@@ -139,6 +139,9 @@ def _main():
139139# Label with status text shown at the bottom of the main window
140140# ("Modified", "Saved to ...", etc.)
141141#
142+ # _stats_label:
143+ # Label showing statistics (symbol count, changed count) in the status bar
144+ #
142145# _id_to_node:
143146# We can't use Node objects directly as Treeview item IDs, so we use their
144147# id()s instead. This dictionary maps Node id()s back to Nodes. (The keys
@@ -182,10 +185,12 @@ def menuconfig(kconf):
182185 global _minconf_filename
183186 global _jump_to_tree
184187 global _cur_menu
188+ global _tree_row_index
185189
186190 _kconf = kconf
187191
188192 _jump_to_tree = None
193+ _tree_row_index = 0
189194
190195 _create_id_to_node ()
191196
@@ -315,6 +320,7 @@ def _create_ui():
315320 _fix_treeview_issues ()
316321
317322 _create_top_widgets ()
323+ _create_menubar ()
318324 # Create the pane with the Kconfig tree and description text
319325 panedwindow , _tree = _create_kconfig_tree_and_desc (_root )
320326 panedwindow .grid (column = 0 , row = 1 , sticky = "nsew" )
@@ -482,45 +488,109 @@ def _init_misc_ui():
482488 style .theme_use ("clam" )
483489
484490
491+ def _create_menubar ():
492+ # Creates the menu bar at the top of the window
493+ # Note: This is called after _create_top_widgets() initializes the variables
494+
495+ menubar = Menu (_root )
496+ _root .config (menu = menubar )
497+
498+ # File menu
499+ file_menu = Menu (menubar , tearoff = 0 )
500+ menubar .add_cascade (label = "File" , menu = file_menu )
501+ file_menu .add_command (label = "Save" , command = _save )
502+ file_menu .add_command (label = "Save As..." , command = _save_as )
503+ file_menu .add_command (label = "Save Minimal..." , command = _save_minimal )
504+ file_menu .add_separator ()
505+ file_menu .add_command (label = "Open..." , command = _open )
506+ file_menu .add_separator ()
507+ file_menu .add_command (label = "Quit" , command = _on_quit )
508+
509+ # Options menu
510+ options_menu = Menu (menubar , tearoff = 0 )
511+ menubar .add_cascade (label = "Options" , menu = options_menu )
512+ options_menu .add_checkbutton (
513+ label = "Show Name" ,
514+ variable = _show_name_var ,
515+ command = _do_showname ,
516+ )
517+ options_menu .add_checkbutton (
518+ label = "Show All" ,
519+ variable = _show_all_var ,
520+ command = _do_showall ,
521+ )
522+ options_menu .add_checkbutton (
523+ label = "Single-Menu Mode" ,
524+ variable = _single_menu_var ,
525+ command = _do_tree_mode ,
526+ )
527+
528+
485529def _create_top_widgets ():
486530 # Creates the controls above the Kconfig tree in the main window
531+ # Also initializes the option variables used by the menu bar
487532
488533 global _show_all_var
489534 global _show_name_var
490535 global _single_menu_var
491536 global _menupath
492537 global _backbutton
493538
539+ # Initialize option variables first (needed by menubar)
540+ _show_name_var = BooleanVar ()
541+ _show_all_var = BooleanVar ()
542+ _single_menu_var = BooleanVar ()
543+
494544 topframe = ttk .Frame (_root )
495- topframe .grid (column = 0 , row = 0 , sticky = "ew" )
545+ topframe .grid (column = 0 , row = 0 , sticky = "ew" , padx = ".1c" , pady = ".1c" )
546+
547+ # Create button groups with separators
548+ # File operations group
549+ file_group = ttk .LabelFrame (topframe , text = "File Operations" , padding = "5" )
550+ file_group .grid (column = 0 , row = 0 , sticky = "ew" , padx = "0 .1c" )
496551
497- ttk .Button (topframe , text = "Save" , command = _save ).grid (
498- column = 0 , row = 0 , sticky = "ew" , padx = ".05c " , pady = ".05c "
552+ ttk .Button (file_group , text = "Save" , command = _save , width = 12 ).grid (
553+ column = 0 , row = 0 , sticky = "ew" , padx = "2 " , pady = "2 "
499554 )
500555
501- ttk .Button (topframe , text = "Save as..." , command = _save_as ).grid (
502- column = 1 , row = 0 , sticky = "ew"
556+ ttk .Button (file_group , text = "Save as..." , command = _save_as , width = 12 ).grid (
557+ column = 1 , row = 0 , sticky = "ew" , padx = "2" , pady = "2"
503558 )
504559
505- ttk .Button (topframe , text = "Save minimal (advanced)..." , command = _save_minimal ).grid (
506- column = 2 , row = 0 , sticky = "ew" , padx = ".05c"
560+ ttk .Button (
561+ file_group , text = "Save minimal..." , command = _save_minimal , width = 12
562+ ).grid (column = 2 , row = 0 , sticky = "ew" , padx = "2" , pady = "2" )
563+
564+ ttk .Button (file_group , text = "Open..." , command = _open , width = 12 ).grid (
565+ column = 3 , row = 0 , sticky = "ew" , padx = "2" , pady = "2"
507566 )
508567
509- ttk .Button (topframe , text = "Open..." , command = _open ).grid (column = 3 , row = 0 )
568+ # Navigation group
569+ nav_group = ttk .LabelFrame (topframe , text = "Navigation" , padding = "5" )
570+ nav_group .grid (column = 1 , row = 0 , sticky = "ew" )
510571
511- ttk .Button (topframe , text = "Jump to..." , command = _jump_to_dialog ).grid (
512- column = 4 , row = 0 , padx = ".05c "
572+ ttk .Button (nav_group , text = "Jump to..." , command = _jump_to_dialog , width = 12 ).grid (
573+ column = 0 , row = 0 , sticky = "ew" , padx = "2" , pady = "2 "
513574 )
514575
515- _show_name_var = BooleanVar ()
576+ # View options group
577+ options_group = ttk .LabelFrame (topframe , text = "View Options" , padding = "5" )
578+ options_group .grid (column = 0 , row = 1 , columnspan = 2 , sticky = "ew" , pady = ".1c 0" )
579+
516580 ttk .Checkbutton (
517- topframe , text = "Show name" , command = _do_showname , variable = _show_name_var
518- ).grid (column = 0 , row = 1 , sticky = "nsew" , padx = ".05c" , pady = "0 .05c" , ipady = ".2c" )
581+ options_group , text = "Show name" , command = _do_showname , variable = _show_name_var
582+ ).grid (column = 0 , row = 0 , sticky = "w" , padx = "5" , pady = "2" )
583+
584+ ttk .Checkbutton (
585+ options_group , text = "Show all" , command = _do_showall , variable = _show_all_var
586+ ).grid (column = 1 , row = 0 , sticky = "w" , padx = "5" , pady = "2" )
519587
520- _show_all_var = BooleanVar ()
521588 ttk .Checkbutton (
522- topframe , text = "Show all" , command = _do_showall , variable = _show_all_var
523- ).grid (column = 1 , row = 1 , sticky = "nsew" , pady = "0 .05c" )
589+ options_group ,
590+ text = "Single-menu mode" ,
591+ command = _do_tree_mode ,
592+ variable = _single_menu_var ,
593+ ).grid (column = 2 , row = 0 , sticky = "w" , padx = "5" , pady = "2" )
524594
525595 # Allow the show-all and single-menu status to be queried via plain global
526596 # Python variables, which is faster and simpler
@@ -532,40 +602,37 @@ def show_all_updated(*_):
532602 _trace_write (_show_all_var , show_all_updated )
533603 _show_all_var .set (False )
534604
535- _single_menu_var = BooleanVar ()
536- ttk .Checkbutton (
537- topframe ,
538- text = "Single-menu mode" ,
539- command = _do_tree_mode ,
540- variable = _single_menu_var ,
541- ).grid (column = 2 , row = 1 , sticky = "nsew" , padx = ".05c" , pady = "0 .05c" )
605+ # Create integrated menu path bar with back button
606+ path_frame = ttk .Frame (topframe , relief = "groove" , borderwidth = 1 )
607+ path_frame .grid (column = 0 , row = 2 , columnspan = 2 , sticky = "ew" , pady = ".1c 0" )
542608
543609 _backbutton = ttk .Button (
544- topframe , text = "<-- " , command = _leave_menu , state = "disabled"
610+ path_frame , text = "\u25c0 Back " , command = _leave_menu , state = "disabled" , width = 8
545611 )
546- _backbutton .grid (column = 0 , row = 4 , sticky = "nsew" , padx = ".05c" , pady = "0 .05c" )
612+ _backbutton .pack (side = "left" , padx = 2 , pady = 2 )
613+ _backbutton .pack_forget () # Initially hidden
547614
548615 def tree_mode_updated (* _ ):
549616 global _single_menu
550617 _single_menu = _single_menu_var .get ()
551618
552619 if _single_menu :
553- _backbutton .grid ()
620+ _backbutton .pack (
621+ side = "left" , padx = 2 , pady = 2 , before = path_frame .winfo_children ()[1 ]
622+ )
554623 else :
555- _backbutton .grid_remove ()
624+ _backbutton .pack_forget ()
556625
557626 _trace_write (_single_menu_var , tree_mode_updated )
558627 _single_menu_var .set (False )
559628
560- # Column to the right of the buttons that the menu path extends into, so
561- # that it can grow wider than the buttons
562- topframe .columnconfigure (5 , weight = 1 )
563-
564- _menupath = ttk .Label (topframe )
565- _menupath .grid (
566- column = 0 , row = 3 , columnspan = 6 , sticky = "w" , padx = "0.05c" , pady = "0 .05c"
629+ ttk .Label (path_frame , text = "Path:" , font = ("TkDefaultFont" , 9 , "bold" )).pack (
630+ side = "left" , padx = (10 , 2 )
567631 )
568632
633+ _menupath = ttk .Label (path_frame , anchor = "w" , relief = "flat" , padding = "2 4" )
634+ _menupath .pack (side = "left" , fill = "x" , expand = True , padx = 2 , pady = 2 )
635+
569636
570637def _create_kconfig_tree_and_desc (parent ):
571638 # Creates a Panedwindow with a Treeview that shows Kconfig nodes and a Text
@@ -617,6 +684,11 @@ def _create_kconfig_tree(parent):
617684 frame = ttk .Frame (parent )
618685
619686 tree = ttk .Treeview (frame , selectmode = "browse" , height = 20 , columns = ("name" ,))
687+
688+ # Configure column widths and headers
689+ tree .column ("#0" , width = 400 , minwidth = 200 , stretch = True )
690+ tree .column ("name" , width = 200 , minwidth = 100 , stretch = True )
691+
620692 tree .heading ("#0" , text = "Option" , anchor = "w" )
621693 tree .heading ("name" , text = "Name" , anchor = "w" )
622694
@@ -634,7 +706,17 @@ def _create_kconfig_tree(parent):
634706 tree .tag_configure ("not-selected" , image = _not_selected_img )
635707 tree .tag_configure ("selected" , image = _selected_img )
636708 tree .tag_configure ("edit" , image = _edit_img )
637- tree .tag_configure ("invisible" , foreground = "red" )
709+
710+ # Enhanced semantic color tags
711+ tree .tag_configure ("invisible" , foreground = "#cc0000" ) # Red for invisible items
712+ tree .tag_configure ("new-item" , foreground = "#0066cc" ) # Blue for NEW items
713+ tree .tag_configure (
714+ "menu-item" , foreground = "#006600"
715+ ) # Dark green for menu/choice items
716+
717+ # Alternating row colors (zebra striping) for better readability
718+ tree .tag_configure ("oddrow" , background = "#f0f0f0" )
719+ tree .tag_configure ("evenrow" , background = "#ffffff" )
638720
639721 tree .grid (column = 0 , row = 0 , sticky = "nsew" )
640722
@@ -700,12 +782,23 @@ def yscrollcommand(first, last):
700782
701783
702784def _create_status_bar ():
703- # Creates the status bar at the bottom of the main window
785+ # Creates an enhanced status bar at the bottom of the main window
704786
705787 global _status_label
788+ global _stats_label
789+
790+ status_frame = ttk .Frame (_root , relief = "sunken" , borderwidth = 1 )
791+ status_frame .grid (column = 0 , row = 3 , sticky = "ew" )
706792
707- _status_label = ttk .Label (_root , anchor = "e" , padding = "0 0 0.4c 0" )
708- _status_label .grid (column = 0 , row = 3 , sticky = "ew" )
793+ # Left side: Status message
794+ _status_label = ttk .Label (status_frame , anchor = "w" , padding = "2 2" )
795+ _status_label .pack (side = "left" , fill = "x" , expand = True )
796+
797+ # Right side: Statistics
798+ _stats_label = ttk .Label (status_frame , anchor = "e" , padding = "2 2" )
799+ _stats_label .pack (side = "right" )
800+
801+ _update_stats ()
709802
710803
711804def _set_status (s ):
@@ -714,6 +807,19 @@ def _set_status(s):
714807 _status_label ["text" ] = s
715808
716809
810+ def _update_stats ():
811+ # Updates the statistics label in the status bar
812+
813+ if not _kconf :
814+ return
815+
816+ total_syms = len (_kconf .unique_defined_syms )
817+ changed_syms = sum (
818+ 1 for sym in _kconf .unique_defined_syms if sym .user_value is not None
819+ )
820+ _stats_label ["text" ] = "Symbols: {} | Changed: {}" .format (total_syms , changed_syms )
821+
822+
717823def _set_conf_changed (changed ):
718824 # Updates the status re. whether there are unsaved changes
719825
@@ -722,6 +828,7 @@ def _set_conf_changed(changed):
722828 _conf_changed = changed
723829 if changed :
724830 _set_status ("Modified" )
831+ _update_stats ()
725832
726833
727834def _update_tree ():
@@ -740,6 +847,10 @@ def _update_tree():
740847 # luckily.
741848 _tree .detach (* _id_to_node .keys ())
742849
850+ # Reset row counter for alternating colors
851+ global _tree_row_index
852+ _tree_row_index = 0
853+
743854 if _single_menu :
744855 _build_menu_tree ()
745856 else :
@@ -855,22 +966,32 @@ def _add_to_tree(node, top):
855966 # the nodes linearly to get the correct order. 'top' holds the menu that
856967 # corresponds to the top-level menu, and can vary in single-menu mode.
857968
969+ global _tree_row_index
970+
858971 parent = node .parent
859972 _tree .move (id (node ), "" if parent is top else id (parent ), "end" )
973+
974+ # Build tags: base tags + row color + optional invisible
975+ base_tags = _img_tag (node )
976+ row_tag = "oddrow" if _tree_row_index % 2 else "evenrow"
977+
978+ if _visible (node ) or not _show_all :
979+ tags = base_tags + " " + row_tag
980+ else :
981+ tags = base_tags + " invisible " + row_tag
982+
860983 _tree .item (
861984 id (node ),
862985 text = _node_str (node ),
863986 # The _show_all test avoids showing invisible items in red outside
864987 # show-all mode, which could look confusing/broken. Invisible symbols
865988 # are shown outside show-all mode if an invisible symbol has visible
866989 # children in an implicit menu.
867- tags = (
868- _img_tag (node )
869- if _visible (node ) or not _show_all
870- else _img_tag (node ) + " invisible"
871- ),
990+ tags = tags ,
872991 )
873992
993+ _tree_row_index += 1
994+
874995
875996def _get_force_info (sym ):
876997 # Returns a string indicating what's forcing a symbol's value, or None
0 commit comments