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
« 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."""
3import json
4import platform
6from webview.menu import Menu, MenuAction, MenuSeparator
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
23def arrange_native_menu():
24 if platform.system() != "Darwin":
25 return
26 from AppKit import NSApplication
27 from PyObjCTools import AppHelper
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()))
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 ))
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 ]