Skip to content

Commit

Permalink
[Cocoa] fix menu
Browse files Browse the repository at this point in the history
  • Loading branch information
r0x0r committed Dec 10, 2024
1 parent dddaa76 commit 7807d95
Showing 1 changed file with 91 additions and 82 deletions.
173 changes: 91 additions & 82 deletions webview/platforms/cocoa.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class BrowserView:
instances = {}
app = AppKit.NSApplication.sharedApplication()
app.setActivationPolicy_(0)
app_menu_list = None

cascade_loc = Foundation.NSMakePoint(100.0, 0.0)

Expand Down Expand Up @@ -619,6 +620,7 @@ def first_show(self):
self._clear_main_menu()
self._add_app_menu()
self._add_view_menu()
self._add_user_menu()

BrowserView.app.activateIgnoringOtherApps_(Foundation.YES)
AppHelper.installMachInterrupt()
Expand Down Expand Up @@ -864,6 +866,92 @@ def create_dialog(*args):

return self._file_name


def _add_user_menu(self):
"""
Create a custom menu for the app menu (MacOS bar menu)
Args:
app_menu_list ([webview.menu.Menu])
"""

if BrowserView.app_menu_list is None:
return

# From https://github.com/r0x0r/pywebview/issues/500
class InternalMenu:
def __init__(self, title, parent):
self.m = AppKit.NSMenu.alloc().init()
self.item = AppKit.NSMenuItem.alloc().init()
self.item.setSubmenu_(self.m)
if not isinstance(parent, self.__class__):
self.m.setTitle_(title)
parent.addItem_(self.item)
else:
self.item.setTitle_(title)
parent.m.addItem_(self.item)

def action(self, title: str, action: Callable, command: str | None = None):
InternalAction(self, title, action, command)
return self

def separator(self):
self.m.addItem_(AppKit.NSMenuItem.separatorItem())
return self

def sub_menu(self, title: str):
return self.__class__(title, parent=self)

class InternalAction:
def __init__(self, parent: InternalMenu, title: str, action: callable, command=None):
self.action = action
s = selector(self._call_action, signature=b'v@:')
if command:
item = parent.m.addItemWithTitle_action_keyEquivalent_(title, s, command)
else:
item = AppKit.NSMenuItem.alloc().init()
item.setAction_(s)
item.setTitle_(title)
parent.m.addItem_(item)
item.setTarget_(self)

def _call_action(self):
# Don't run action function on main thread
Thread(target=self.action).start()

def create_submenu(title, line_items, supermenu):
m = InternalMenu(title, parent=supermenu)
for menu_line_item in line_items:
if isinstance(menu_line_item, MenuSeparator):
m = m.separator()
elif isinstance(menu_line_item, MenuAction):
m = m.action(menu_line_item.title, menu_line_item.function)
elif isinstance(menu_line_item, Menu):
create_submenu(menu_line_item.title, menu_line_item.items, m)

os_bar_menu = BrowserView.app.mainMenu()
if os_bar_menu is None:
os_bar_menu = AppKit.NSMenu.alloc().init()
BrowserView.app.setMainMenu_(os_bar_menu)

for app_menu in BrowserView.app_menu_list:
create_submenu(app_menu.title, app_menu.items, os_bar_menu)


def get_active_window():
active_window = BrowserView.app.keyWindow()
if active_window is None:
return None

active_window_number = active_window.windowNumber()

for uid, browser_view_instance in BrowserView.instances.items():
if browser_view_instance.window.windowNumber() == active_window_number:
return browser_view_instance.pywebview_window

return None


def _clear_main_menu(self):
"""
Remove all items from the main menu.
Expand Down Expand Up @@ -1092,6 +1180,9 @@ def setup_app():
# MUST be called before create_window and set_app_menu
pass

def set_app_menu(app_menu_list):
BrowserView.app_menu_list = app_menu_list


def create_window(window):
def create():
Expand Down Expand Up @@ -1157,88 +1248,6 @@ def load_html(content, base_uri, uid):
i.load_html(content, base_uri)


def set_app_menu(app_menu_list):
"""
Create a custom menu for the app menu (MacOS bar menu)
Args:
app_menu_list ([webview.menu.Menu])
"""

# From https://github.com/r0x0r/pywebview/issues/500
class InternalMenu:
def __init__(self, title, parent):
self.m = AppKit.NSMenu.alloc().init()
self.item = AppKit.NSMenuItem.alloc().init()
self.item.setSubmenu_(self.m)
if not isinstance(parent, self.__class__):
self.m.setTitle_(title)
parent.addItem_(self.item)
else:
self.item.setTitle_(title)
parent.m.addItem_(self.item)

def action(self, title: str, action: Callable, command: str | None = None):
InternalAction(self, title, action, command)
return self

def separator(self):
self.m.addItem_(AppKit.NSMenuItem.separatorItem())
return self

def sub_menu(self, title: str):
return self.__class__(title, parent=self)

class InternalAction:
def __init__(self, parent: InternalMenu, title: str, action: callable, command=None):
self.action = action
s = selector(self._call_action, signature=b'v@:')
if command:
item = parent.m.addItemWithTitle_action_keyEquivalent_(title, s, command)
else:
item = AppKit.NSMenuItem.alloc().init()
item.setAction_(s)
item.setTitle_(title)
parent.m.addItem_(item)
item.setTarget_(self)

def _call_action(self):
# Don't run action function on main thread
Thread(target=self.action).start()

def create_submenu(title, line_items, supermenu):
m = InternalMenu(title, parent=supermenu)
for menu_line_item in line_items:
if isinstance(menu_line_item, MenuSeparator):
m = m.separator()
elif isinstance(menu_line_item, MenuAction):
m = m.action(menu_line_item.title, menu_line_item.function)
elif isinstance(menu_line_item, Menu):
create_submenu(menu_line_item.title, menu_line_item.items, m)

os_bar_menu = BrowserView.app.mainMenu()
if os_bar_menu is None:
os_bar_menu = AppKit.NSMenu.alloc().init()
BrowserView.app.setMainMenu_(os_bar_menu)

for app_menu in app_menu_list:
create_submenu(app_menu.title, app_menu.items, os_bar_menu)


def get_active_window():
active_window = BrowserView.app.keyWindow()
if active_window is None:
return None

active_window_number = active_window.windowNumber()

for uid, browser_view_instance in BrowserView.instances.items():
if browser_view_instance.window.windowNumber() == active_window_number:
return browser_view_instance.pywebview_window

return None


def destroy_window(uid):
i = BrowserView.instances.get(uid)
if i:
Expand Down

0 comments on commit 7807d95

Please sign in to comment.