@@ -139,6 +139,9 @@ def _main():
139
139
# Label with status text shown at the bottom of the main window
140
140
# ("Modified", "Saved to ...", etc.)
141
141
#
142
+ # _stats_label:
143
+ # Label showing statistics (symbol count, changed count) in the status bar
144
+ #
142
145
# _id_to_node:
143
146
# We can't use Node objects directly as Treeview item IDs, so we use their
144
147
# id()s instead. This dictionary maps Node id()s back to Nodes. (The keys
@@ -182,10 +185,12 @@ def menuconfig(kconf):
182
185
global _minconf_filename
183
186
global _jump_to_tree
184
187
global _cur_menu
188
+ global _tree_row_index
185
189
186
190
_kconf = kconf
187
191
188
192
_jump_to_tree = None
193
+ _tree_row_index = 0
189
194
190
195
_create_id_to_node ()
191
196
@@ -315,6 +320,7 @@ def _create_ui():
315
320
_fix_treeview_issues ()
316
321
317
322
_create_top_widgets ()
323
+ _create_menubar ()
318
324
# Create the pane with the Kconfig tree and description text
319
325
panedwindow , _tree = _create_kconfig_tree_and_desc (_root )
320
326
panedwindow .grid (column = 0 , row = 1 , sticky = "nsew" )
@@ -482,45 +488,109 @@ def _init_misc_ui():
482
488
style .theme_use ("clam" )
483
489
484
490
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
+
485
529
def _create_top_widgets ():
486
530
# Creates the controls above the Kconfig tree in the main window
531
+ # Also initializes the option variables used by the menu bar
487
532
488
533
global _show_all_var
489
534
global _show_name_var
490
535
global _single_menu_var
491
536
global _menupath
492
537
global _backbutton
493
538
539
+ # Initialize option variables first (needed by menubar)
540
+ _show_name_var = BooleanVar ()
541
+ _show_all_var = BooleanVar ()
542
+ _single_menu_var = BooleanVar ()
543
+
494
544
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" )
496
551
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 "
499
554
)
500
555
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"
503
558
)
504
559
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"
507
566
)
508
567
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" )
510
571
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 "
513
574
)
514
575
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
+
516
580
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" )
519
587
520
- _show_all_var = BooleanVar ()
521
588
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" )
524
594
525
595
# Allow the show-all and single-menu status to be queried via plain global
526
596
# Python variables, which is faster and simpler
@@ -532,40 +602,37 @@ def show_all_updated(*_):
532
602
_trace_write (_show_all_var , show_all_updated )
533
603
_show_all_var .set (False )
534
604
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" )
542
608
543
609
_backbutton = ttk .Button (
544
- topframe , text = "<-- " , command = _leave_menu , state = "disabled"
610
+ path_frame , text = "\u25c0 Back " , command = _leave_menu , state = "disabled" , width = 8
545
611
)
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
547
614
548
615
def tree_mode_updated (* _ ):
549
616
global _single_menu
550
617
_single_menu = _single_menu_var .get ()
551
618
552
619
if _single_menu :
553
- _backbutton .grid ()
620
+ _backbutton .pack (
621
+ side = "left" , padx = 2 , pady = 2 , before = path_frame .winfo_children ()[1 ]
622
+ )
554
623
else :
555
- _backbutton .grid_remove ()
624
+ _backbutton .pack_forget ()
556
625
557
626
_trace_write (_single_menu_var , tree_mode_updated )
558
627
_single_menu_var .set (False )
559
628
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 )
567
631
)
568
632
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
+
569
636
570
637
def _create_kconfig_tree_and_desc (parent ):
571
638
# Creates a Panedwindow with a Treeview that shows Kconfig nodes and a Text
@@ -617,6 +684,11 @@ def _create_kconfig_tree(parent):
617
684
frame = ttk .Frame (parent )
618
685
619
686
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
+
620
692
tree .heading ("#0" , text = "Option" , anchor = "w" )
621
693
tree .heading ("name" , text = "Name" , anchor = "w" )
622
694
@@ -634,7 +706,17 @@ def _create_kconfig_tree(parent):
634
706
tree .tag_configure ("not-selected" , image = _not_selected_img )
635
707
tree .tag_configure ("selected" , image = _selected_img )
636
708
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" )
638
720
639
721
tree .grid (column = 0 , row = 0 , sticky = "nsew" )
640
722
@@ -700,12 +782,23 @@ def yscrollcommand(first, last):
700
782
701
783
702
784
def _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
704
786
705
787
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" )
706
792
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 ()
709
802
710
803
711
804
def _set_status (s ):
@@ -714,6 +807,19 @@ def _set_status(s):
714
807
_status_label ["text" ] = s
715
808
716
809
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
+
717
823
def _set_conf_changed (changed ):
718
824
# Updates the status re. whether there are unsaved changes
719
825
@@ -722,6 +828,7 @@ def _set_conf_changed(changed):
722
828
_conf_changed = changed
723
829
if changed :
724
830
_set_status ("Modified" )
831
+ _update_stats ()
725
832
726
833
727
834
def _update_tree ():
@@ -740,6 +847,10 @@ def _update_tree():
740
847
# luckily.
741
848
_tree .detach (* _id_to_node .keys ())
742
849
850
+ # Reset row counter for alternating colors
851
+ global _tree_row_index
852
+ _tree_row_index = 0
853
+
743
854
if _single_menu :
744
855
_build_menu_tree ()
745
856
else :
@@ -855,22 +966,32 @@ def _add_to_tree(node, top):
855
966
# the nodes linearly to get the correct order. 'top' holds the menu that
856
967
# corresponds to the top-level menu, and can vary in single-menu mode.
857
968
969
+ global _tree_row_index
970
+
858
971
parent = node .parent
859
972
_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
+
860
983
_tree .item (
861
984
id (node ),
862
985
text = _node_str (node ),
863
986
# The _show_all test avoids showing invisible items in red outside
864
987
# show-all mode, which could look confusing/broken. Invisible symbols
865
988
# are shown outside show-all mode if an invisible symbol has visible
866
989
# 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 ,
872
991
)
873
992
993
+ _tree_row_index += 1
994
+
874
995
875
996
def _get_force_info (sym ):
876
997
# Returns a string indicating what's forcing a symbol's value, or None
0 commit comments