@@ -148,23 +148,36 @@ def create_main_panel(self):
148148 editor_frame = ttk .Frame (left_frame )
149149 editor_frame .pack (fill = tk .BOTH , expand = True )
150150
151+ # Common font for alignment
152+ self .editor_font = ('Consolas' , 11 )
153+
151154 # Line numbers
152155 self .line_numbers = tk .Text (editor_frame , width = 4 , padx = 3 , pady = 3 , takefocus = 0 ,
153- border = 0 , background = 'lightgray' , state = tk .DISABLED )
156+ border = 0 , background = 'lightgray' , state = tk .DISABLED ,
157+ font = self .editor_font )
154158 self .line_numbers .pack (side = tk .LEFT , fill = tk .Y )
155159
156160 # Breakpoint indicators
157- self .breakpoint_canvas = tk .Canvas (editor_frame , width = 20 , bg = 'lightgray' )
161+ self .breakpoint_canvas = tk .Canvas (editor_frame , width = 20 , bg = 'lightgray' , highlightthickness = 0 )
158162 self .breakpoint_canvas .pack (side = tk .LEFT , fill = tk .Y )
159163
160164 # Main editor
161- self .editor = scrolledtext .ScrolledText (editor_frame , wrap = tk .NONE , undo = True , font = ( 'Consolas' , 10 ) )
165+ self .editor = scrolledtext .ScrolledText (editor_frame , wrap = tk .NONE , undo = True , font = self . editor_font )
162166 self .editor .pack (side = tk .LEFT , fill = tk .BOTH , expand = True )
163167
164- # Bind events
168+ # Bind events for alignment and updates
165169 self .editor .bind ('<KeyRelease>' , self .on_text_change )
166170 self .editor .bind ('<Button-1>' , self .on_click )
167171 self .editor .bind ('<Control-Button-1>' , self .toggle_breakpoint )
172+ self .editor .bind ('<Configure>' , lambda e : self .update_line_numbers ())
173+
174+ # Sync scrolling
175+ def sync_scroll (* args ):
176+ self .line_numbers .yview_moveto (args [0 ])
177+ self .update_breakpoint_indicators ()
178+
179+ # We need to hook into the underlying text widget's scroll
180+ self .editor .vbar .config (command = self .on_scroll )
168181
169182 # Bind breakpoint canvas clicks
170183 self .breakpoint_canvas .bind ('<Button-1>' , self .on_breakpoint_click )
@@ -263,30 +276,38 @@ def create_status_bar(self):
263276 self .debug_status_label = ttk .Label (self .status_bar , text = "Not Debugging" )
264277 self .debug_status_label .pack (side = tk .RIGHT , padx = 5 )
265278
279+ def on_scroll (self , * args ):
280+ self .editor .yview (* args )
281+ self .line_numbers .yview_moveto (self .editor .yview ()[0 ])
282+ self .update_breakpoint_indicators ()
283+
266284 def update_line_numbers (self ):
267285 self .line_numbers .config (state = tk .NORMAL )
268286 self .line_numbers .delete (1.0 , tk .END )
269287
270288 line_count = int (self .editor .index ('end-1c' ).split ('.' )[0 ])
271- for i in range (1 , line_count + 1 ):
272- self .line_numbers .insert (tk . END , f" { i } \n " )
289+ lines = " \n " . join ( str ( i ) for i in range (1 , line_count + 1 ))
290+ self .line_numbers .insert (1.0 , lines )
273291
274292 self .line_numbers .config (state = tk .DISABLED )
293+ self .line_numbers .yview_moveto (self .editor .yview ()[0 ])
275294
276295 # Update breakpoint indicators
277296 self .update_breakpoint_indicators ()
278297
279298 def update_breakpoint_indicators (self ):
280299 self .breakpoint_canvas .delete ("all" )
281300
282- line_count = int (self .editor .index ('end-1c' ).split ('.' )[0 ])
283- line_height = 21 # approximate line height
284-
285301 for line_num in self .breakpoints :
286- if line_num <= line_count :
287- y = (line_num - 1 ) * line_height + 10
288- # Draw a red circle for breakpoint
289- self .breakpoint_canvas .create_oval (5 , y - 5 , 15 , y + 5 , fill = 'red' , outline = 'darkred' )
302+ # Use dlineinfo to get the exact y-coordinate of the line on screen
303+ try :
304+ dinfo = self .editor .dlineinfo (f"{ line_num } .0" )
305+ if dinfo :
306+ y = dinfo [1 ] + (dinfo [3 ] // 2 )
307+ # Draw a red circle for breakpoint
308+ self .breakpoint_canvas .create_oval (5 , y - 5 , 15 , y + 5 , fill = 'red' , outline = 'darkred' )
309+ except tk .TclError :
310+ continue
290311
291312 def on_text_change (self , event = None ):
292313 self .update_line_numbers ()
@@ -298,12 +319,25 @@ def on_breakpoint_click(self, event=None):
298319 if not self .current_file :
299320 return
300321
301- line_height = 20
302- line_num = (event .y // line_height ) + 1
303- if line_num in self .breakpoints :
304- self .remove_breakpoint (line_num )
305- else :
306- self .add_breakpoint (line_num )
322+ # Find which line was clicked by comparing y-coordinate with dlineinfo
323+ line_count = int (self .editor .index ('end-1c' ).split ('.' )[0 ])
324+ clicked_line = None
325+
326+ for i in range (1 , line_count + 1 ):
327+ dinfo = self .editor .dlineinfo (f"{ i } .0" )
328+ if dinfo :
329+ y_start = dinfo [1 ]
330+ y_end = dinfo [1 ] + dinfo [3 ]
331+ if y_start <= event .y <= y_end :
332+ clicked_line = i
333+ break
334+
335+ if clicked_line :
336+ if clicked_line in self .breakpoints :
337+ self .remove_breakpoint (clicked_line )
338+ else :
339+ self .add_breakpoint (clicked_line )
340+ self .update_breakpoint_indicators ()
307341
308342 def toggle_breakpoint (self , event = None ):
309343 if not self .current_file :
0 commit comments