@@ -194,33 +194,64 @@ def __init__(self, root, templates_dir):
194194 self ._apply_theme ()
195195 self ._setup_focus_follows_mouse ()
196196
197+ def _make_toolbar_btn (self , parent , text , command , enabled = True ):
198+ """Create a Label-based button with reliable click handling on all platforms."""
199+ lbl = tk .Label (parent , text = f' { text } ' , relief = tk .RAISED , borderwidth = 1 ,
200+ padx = 6 , pady = 3 , cursor = 'hand2' ,
201+ background = '#e8e8e8' , foreground = '#000000' )
202+ if enabled :
203+ lbl .bind ('<Button-1>' , lambda e : command ())
204+ lbl .bind ('<ButtonRelease-1>' ,
205+ lambda e : lbl .config (relief = tk .RAISED ))
206+ lbl .bind ('<ButtonPress-1>' ,
207+ lambda e : lbl .config (relief = tk .SUNKEN ))
208+ else :
209+ lbl .config (foreground = '#aaaaaa' , cursor = 'arrow' )
210+ lbl ._command = command
211+ lbl ._enabled = enabled
212+ return lbl
213+
214+ def _set_btn_enabled (self , btn , enabled ):
215+ """Enable or disable a Label-based toolbar button."""
216+ btn ._enabled = enabled
217+ if enabled :
218+ btn .config (foreground = '#000000' , cursor = 'hand2' )
219+ btn .bind ('<Button-1>' , lambda e : btn ._command ())
220+ btn .bind ('<ButtonPress-1>' , lambda e : btn .config (relief = tk .SUNKEN ))
221+ btn .bind ('<ButtonRelease-1>' , lambda e : btn .config (relief = tk .RAISED ))
222+ else :
223+ btn .config (foreground = '#aaaaaa' , cursor = 'arrow' )
224+ btn .bind ('<Button-1>' , lambda e : None )
225+ btn .bind ('<ButtonPress-1>' , lambda e : None )
226+ btn .bind ('<ButtonRelease-1>' , lambda e : None )
227+
197228 def _build_ui (self ):
198229 """Create all UI elements."""
199- # --- Top toolbar using grid for reliable sizing on macOS ---
230+ # --- Top toolbar using grid for reliable sizing ---
200231 toolbar = tk .Frame (self .root , padx = 5 , pady = 5 )
201232 toolbar .pack (fill = tk .X )
202233
203234 col = 0
204235 pad = dict (padx = 3 , pady = 2 )
205236
206- ttk . Button (toolbar , text = 'Open .nml' , command = self .open_file ).grid (
237+ self . _make_toolbar_btn (toolbar , 'Open .nml' , self .open_file ).grid (
207238 row = 0 , column = col , ** pad ); col += 1
208- ttk . Button (toolbar , text = f'Save .nml ({ MOD_LABEL } +S)' ,
209- command = self .save_file ).grid (row = 0 , column = col , ** pad ); col += 1
239+ self . _make_toolbar_btn (toolbar , f'Save .nml ({ MOD_LABEL } +S)' ,
240+ self .save_file ).grid (row = 0 , column = col , ** pad ); col += 1
210241
211- ttk . Separator (toolbar , orient = tk . VERTICAL ).grid (
212- row = 0 , column = col , sticky = 'ns' , padx = 6 ); col += 1
242+ tk . Label (toolbar , text = ' | ' , foreground = '#999999' ).grid (
243+ row = 0 , column = col ); col += 1
213244
214- ttk . Button (toolbar , text = 'Set Work Dir' ,
215- command = self .set_work_dir ).grid (row = 0 , column = col , ** pad ); col += 1
245+ self . _make_toolbar_btn (toolbar , 'Set Work Dir' ,
246+ self .set_work_dir ).grid (row = 0 , column = col , ** pad ); col += 1
216247
217- ttk . Separator (toolbar , orient = tk . VERTICAL ).grid (
218- row = 0 , column = col , sticky = 'ns' , padx = 6 ); col += 1
248+ tk . Label (toolbar , text = ' | ' , foreground = '#999999' ).grid (
249+ row = 0 , column = col ); col += 1
219250
220- self .run_btn = ttk .Button (toolbar , text = 'Run Program' , width = 14 ,
221- command = self ._on_run_clicked )
251+ # Run button — starts disabled
252+ self .run_btn = self ._make_toolbar_btn (toolbar , 'Run Program' ,
253+ self ._on_run_clicked , enabled = False )
222254 self .run_btn .grid (row = 0 , column = col , ** pad ); col += 1
223- self .run_btn .state (['disabled' ])
224255 self ._shift_held = False
225256 self .root .bind ('<Shift_L>' , lambda e : setattr (self , '_shift_held' , True ))
226257 self .root .bind ('<Shift_R>' , lambda e : setattr (self , '_shift_held' , True ))
@@ -230,23 +261,23 @@ def _build_ui(self):
230261 'Click: run program with current .nml file\n '
231262 'Shift+Click: choose a different .nml file to run' )
232263
233- self .stop_btn = ttk .Button (toolbar , text = 'Stop' , width = 8 ,
234- command = self .stop_program )
264+ # Stop button — starts disabled
265+ self .stop_btn = self ._make_toolbar_btn (toolbar , 'Stop' ,
266+ self .stop_program , enabled = False )
235267 self .stop_btn .grid (row = 0 , column = col , ** pad ); col += 1
236- self .stop_btn .state (['disabled' ])
237268
238269 # Spacer to push font controls to the right
239270 toolbar .columnconfigure (col , weight = 1 ); col += 1
240271
241272 # Font size controls
242273 tk .Label (toolbar , text = 'Font:' ).grid (row = 0 , column = col ); col += 1
243- ttk . Button (toolbar , text = '\u2212 ' , width = 2 ,
244- command = self ._font_smaller ).grid (row = 0 , column = col , padx = 1 ); col += 1
274+ self . _make_toolbar_btn (toolbar , '\u2212 ' ,
275+ self ._font_smaller ).grid (row = 0 , column = col , padx = 1 ); col += 1
245276 self .font_size_var = tk .StringVar (value = str (self .editor_font [1 ]))
246277 tk .Label (toolbar , textvariable = self .font_size_var , width = 3 ,
247278 anchor = tk .CENTER ).grid (row = 0 , column = col ); col += 1
248- ttk . Button (toolbar , text = '+' , width = 2 ,
249- command = self ._font_larger ).grid (row = 0 , column = col , padx = 1 ); col += 1
279+ self . _make_toolbar_btn (toolbar , '+' ,
280+ self ._font_larger ).grid (row = 0 , column = col , padx = 1 ); col += 1
250281
251282 # --- Main paned layout: template list on left, editor+output on right ---
252283 h_paned = ttk .PanedWindow (self .root , orient = tk .HORIZONTAL )
@@ -336,8 +367,8 @@ def _build_ui(self):
336367 output_header .pack (fill = tk .X )
337368 tk .Label (output_header , text = 'Program Output:' ,
338369 font = ('TkDefaultFont' , 10 , 'bold' )).pack (side = tk .LEFT )
339- ttk . Button (output_header , text = 'Clear' , command = self .clear_output ).pack (side = tk .RIGHT , padx = 2 )
340- ttk . Button (output_header , text = 'Save .log' , command = self .save_log ).pack (side = tk .RIGHT , padx = 2 )
370+ self . _make_toolbar_btn (output_header , 'Clear' , self .clear_output ).pack (side = tk .RIGHT , padx = 2 )
371+ self . _make_toolbar_btn (output_header , 'Save .log' , self .save_log ).pack (side = tk .RIGHT , padx = 2 )
341372
342373 # Output text widget
343374 output_frame = ttk .Frame (output_container )
@@ -734,7 +765,7 @@ def _on_run_clicked(self):
734765
735766 def _run_with_file_chooser (self , event = None ):
736767 """Shift+Click handler: choose a .nml file to run instead of the default."""
737- if 'disabled' in self .run_btn .state () :
768+ if not self .run_btn ._enabled :
738769 return
739770
740771 filepath = filedialog .askopenfilename (
0 commit comments