1414# You should have received a copy of the GNU General Public License
1515# along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
17+ from bisect import bisect_left
18+
1719from PyQt6 .QtCore import QPoint
1820
1921
@@ -41,6 +43,11 @@ def __init__(self, previewFrame,
4143 self ._updating_preview = False
4244 self ._updating_editor = False
4345
46+ # Cached orderings for mapping between preview positions and source lines
47+ self ._posmap_lines = []
48+ self ._preview_posmap = []
49+ self ._preview_positions = []
50+
4451 self .frame .contentsSizeChanged .connect (self ._handlePreviewResized )
4552 self .frame .loadStarted .connect (self ._handleLoadStarted )
4653 self .frame .loadFinished .connect (self ._handleLoadFinished )
@@ -115,11 +122,11 @@ def _updatePreviewScrollPosition(self):
115122 # Do a binary search through the posmap to find the nearest line above
116123 # and below the line to scroll to for which the rendered position is
117124 # known.
118- posmap_lines = [ 0 ] + sorted ( self .posmap . keys ())
125+ posmap_lines = self ._posmap_lines
119126 min_index = 0
120127 max_index = len (posmap_lines ) - 1
121128 while max_index - min_index > 1 :
122- current_index = int (( min_index + max_index ) // 2 )
129+ current_index = ( min_index + max_index ) // 2
123130 if posmap_lines [current_index ] > line_to_scroll_to :
124131 max_index = current_index
125132 else :
@@ -159,6 +166,15 @@ def _setPositionMap(self, posmap):
159166 self .posmap = posmap
160167 if posmap :
161168 self .posmap [0 ] = 0
169+ if self .posmap :
170+ self ._posmap_lines = sorted (self .posmap .keys ())
171+ preview_sorted = sorted (self .posmap .items (), key = lambda item : item [1 ])
172+ self ._preview_posmap = [(preview , line ) for line , preview in preview_sorted ]
173+ self ._preview_positions = [preview for preview , _ in self ._preview_posmap ]
174+ else :
175+ self ._posmap_lines = []
176+ self ._preview_posmap = []
177+ self ._preview_positions = []
162178
163179 def handlePreviewScrolled (self , previewScrollPosition ):
164180 """
@@ -180,34 +196,40 @@ def handlePreviewScrolled(self, previewScrollPosition):
180196 except AttributeError :
181197 preview_y = float (previewScrollPosition )
182198
183- # Binary search using line numbers to find nearest posmap values
184- posmap_lines = [0 ] + sorted (self .posmap .keys ())
185- min_index = 0
186- max_index = len (posmap_lines ) - 1
187- while max_index - min_index > 1 :
188- current_index = int ((min_index + max_index ) // 2 )
189- current_line = posmap_lines [current_index ]
190- if self .posmap [current_line ] > preview_y :
191- max_index = current_index
192- else :
193- min_index = current_index
199+ if not self ._preview_posmap :
200+ return
194201
195- min_line = posmap_lines [min_index ]
196- max_line = posmap_lines [max_index ]
202+ if len (self ._preview_posmap ) == 1 :
203+ _ , line = self ._preview_posmap [0 ]
204+ editor_scroll_to = self .sourceLineToEditorPosition (line )
205+ else :
206+ index = bisect_left (self ._preview_positions , preview_y )
207+ if index <= 0 :
208+ min_preview_pos , min_line = self ._preview_posmap [0 ]
209+ max_preview_pos , max_line = self ._preview_posmap [1 ]
210+ elif index >= len (self ._preview_posmap ):
211+ min_preview_pos , min_line = self ._preview_posmap [- 2 ]
212+ max_preview_pos , max_line = self ._preview_posmap [- 1 ]
213+ else :
214+ min_preview_pos , min_line = self ._preview_posmap [index - 1 ]
215+ max_preview_pos , max_line = self ._preview_posmap [index ]
197216
198- min_preview_pos = self .posmap [ min_line ]
199- max_preview_pos = self .posmap [ max_line ]
217+ min_textedit_pos = self .sourceLineToEditorPosition ( min_line )
218+ max_textedit_pos = self .sourceLineToEditorPosition ( max_line )
200219
201- min_textedit_pos = self .sourceLineToEditorPosition (min_line )
202- max_textedit_pos = self .sourceLineToEditorPosition (max_line )
220+ editor_scroll_to = self ._linearScale (
221+ preview_y ,
222+ min_preview_pos ,
223+ max_preview_pos ,
224+ min_textedit_pos ,
225+ max_textedit_pos ,
226+ )
203227
204- editor_scroll_to = self ._linearScale (preview_y ,
205- min_preview_pos , max_preview_pos ,
206- min_textedit_pos , max_textedit_pos )
228+ editor_scroll_value = int (editor_scroll_to )
207229
208230 # Apply editor scroll with guard to avoid triggering editor→preview update
209231 self ._updating_editor = True
210232 try :
211- self ._setEditorScrollValue (int ( editor_scroll_to ) )
233+ self ._setEditorScrollValue (editor_scroll_value )
212234 finally :
213235 self ._updating_editor = False
0 commit comments