Coverage for collectra/gui/native_menu.py: 75.00%

24 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-16 09:56 +0000

1"""Native menu entries delegate to the same actions as the viewer toolbar.""" 

2 

3import json 

4import platform 

5 

6from webview.menu import Menu, MenuAction, MenuSeparator 

7 

8 

9def put_file_menu_first(menu): 

10 """Keep the app menu at index zero, then File before Cocoa's Edit/View.""" 

11 if menu is None: 

12 return 

13 for index in range(1, menu.numberOfItems()): 

14 item = menu.itemAtIndex_(index) 

15 submenu = item.submenu() 

16 if submenu is not None and submenu.title() == "File": 

17 if index != 1: 

18 menu.removeItemAtIndex_(index) 

19 menu.insertItem_atIndex_(item, 1) 

20 return 

21 

22 

23def arrange_native_menu(): 

24 if platform.system() != "Darwin": 

25 return 

26 from AppKit import NSApplication 

27 from PyObjCTools import AppHelper 

28 

29 # pywebview's startup callback runs on a worker; AppKit requires the UI thread. 

30 AppHelper.callAfter(lambda: put_file_menu_first(NSApplication.sharedApplication().mainMenu())) 

31 

32 

33def build_native_menu(window): 

34 def action(title, name): 

35 return MenuAction(title, lambda: window.evaluate_js( 

36 f"runNativeMenuAction({json.dumps(name)})" 

37 )) 

38 

39 return [ 

40 Menu("File", [ 

41 action("Open Results…", "open"), 

42 action("Show Current Results in File Manager…", "reveal"), 

43 MenuSeparator(), 

44 action("Close Current Tab", "close-tab"), 

45 ]), 

46 Menu("Navigate", [ 

47 action("Previous File", "previous-file"), 

48 action("Next File", "next-file"), 

49 MenuSeparator(), 

50 action("Find File…", "find-file"), 

51 action("Find Artefacts…", "find-artefacts"), 

52 action("Previous Search Match", "previous-match"), 

53 action("Next Search Match", "next-match"), 

54 ]), 

55 Menu("Panels", [ 

56 action("Toggle Sidebar", "sidebar"), 

57 action("Open Pipeline Graph", "graph"), 

58 action("Show Artefacts", "artefacts"), 

59 action("Show Tasks", "tasks"), 

60 MenuSeparator(), 

61 action("Fit Pipeline Graph", "fit-graph"), 

62 action("Fit Image", "fit-image"), 

63 MenuSeparator(), 

64 Menu("Split Panel", [action(f"Split {direction.title()}", f"split-{direction}") 

65 for direction in ("left", "right", "above", "below")]), 

66 action("Toggle Current Panel", "collapse-panel"), 

67 action("Remove Current Panel", "remove-panel"), 

68 ]), 

69 Menu("Layouts", [ 

70 action("Choose Layout…", "choose-layout"), 

71 action("Save Current Layout…", "save-layout"), 

72 ]), 

73 Menu("Tools", [ 

74 action("Check for Updates…", "check-updates"), 

75 MenuSeparator(), 

76 action("Source Control…", "source-control"), 

77 action("Statistics…", "statistics"), 

78 action("Load Details…", "load-details"), 

79 ]), 

80 ]