diff --git a/src/cocoa/toga_cocoa/widgets/icon.py b/src/cocoa/toga_cocoa/widgets/icon.py index 0cf222373a..d7ef5985e3 100644 --- a/src/cocoa/toga_cocoa/widgets/icon.py +++ b/src/cocoa/toga_cocoa/widgets/icon.py @@ -1,8 +1,30 @@ +import os + +from toga import Icon as toga_Icon from toga_cocoa.libs import NSImage class Icon: def __init__(self, interface): self.interface = interface - interface.__impl = self - self.native = NSImage.alloc().initWithContentsOfFile(interface.filename) + self.interface._impl = self + file_path, file_extension = os.path.splitext(self.interface.filename) + valid_icon_extensions = ('.png', '.bmp', '.ico') + + if file_extension == '.icns': + self.native = NSImage.alloc().initWithContentsOfFile(self.interface.filename) + elif os.path.isfile(file_path + '.icns'): + self.native = NSImage.alloc().initWithContentsOfFile(file_path + '.icns') + elif file_extension in valid_icon_extensions: + self.native = NSImage.alloc().initWithContentsOfFile(self.interface.filename) + elif os.path.isfile(file_path + '.png'): + self.native = NSImage.alloc().initWithContentsOfFile(file_path + '.png') + elif os.path.isfile(file_path + '.bmp'): + self.native = NSImage.alloc().initWithContentsOfFile(file_path + '.bmp') + else: + print("[Cocoa] No valid icon format available for {}; " + "fall back on Tiberius instead".format( + self.interface.filename)) + tiberius_file = toga_Icon.TIBERIUS_ICON.filename + '.icns' + self.interface.icon = toga_Icon.TIBERIUS_ICON + self.native = NSImage.alloc().initWithContentsOfFile(tiberius_file) diff --git a/src/core/tests/test_app.py b/src/core/tests/test_app.py index 8fa72c84df..9a97871537 100644 --- a/src/core/tests/test_app.py +++ b/src/core/tests/test_app.py @@ -34,7 +34,7 @@ def test_app_icon(self): self.assertEqual(self.app.icon, self.app.default_icon) # Set the icon to a different resource - self.app.icon = "other" + self.app.icon = "other.icns" self.assertEqual(self.app.icon.path, "other.icns") def test_app_app_id(self): diff --git a/src/core/toga/command.py b/src/core/toga/command.py index bbaccb0935..88216ddc4f 100644 --- a/src/core/toga/command.py +++ b/src/core/toga/command.py @@ -76,6 +76,8 @@ def enabled(self, value): self._enabled = value for widget in self._widgets: widget.enabled = value + if self._impl is not None: + self._impl.enabled = value GROUP_BREAK = object() diff --git a/src/core/toga/resources/tiberius.ico b/src/core/toga/resources/tiberius.ico new file mode 100644 index 0000000000..aa3492f61b Binary files /dev/null and b/src/core/toga/resources/tiberius.ico differ diff --git a/src/core/toga/widgets/icon.py b/src/core/toga/widgets/icon.py index 81de7af6b2..72c63dfa16 100644 --- a/src/core/toga/widgets/icon.py +++ b/src/core/toga/widgets/icon.py @@ -26,10 +26,7 @@ class Icon: """ def __init__(self, path, system=False): - if os.path.splitext(path)[1] in ('.png', '.icns', '.bmp'): - self.path = path - else: - self.path = path + '.icns' + self.path = path self.system = system diff --git a/src/winforms/toga_winforms/app.py b/src/winforms/toga_winforms/app.py index d8663659de..bb89f65204 100644 --- a/src/winforms/toga_winforms/app.py +++ b/src/winforms/toga_winforms/app.py @@ -1,4 +1,8 @@ -from .libs import Threading, WinForms +import sys + +import toga + +from .libs import Threading, WinForms, add_handler from .window import Window @@ -17,21 +21,55 @@ def __init__(self, interface): def create(self): self.native = WinForms.Application - # self.native.setApplicationIconImage_(self.icon.native) - - # Set the menu for the app. - # self.native.setMainMenu_(self.menu) + self.interface.commands.add( + toga.Command(None, 'About ' + self.interface.name, group=toga.Group.HELP), + toga.Command(None, 'Preferences', group=toga.Group.FILE), + # Quit should always be the last item, in a section on it's own + toga.Command(lambda s: self.exit(), 'Exit ' + self.interface.name, shortcut='q', group=toga.Group.FILE, + section=sys.maxsize), + toga.Command(None, 'Visit homepage', group=toga.Group.HELP) + ) # Call user code to populate the main window self.interface.startup() + self._menu_items = {} + self.create_menus() + self.interface.main_window._impl.native.Icon = \ + self.interface.icon.bind(self.interface.factory).native + + def create_menus(self): + toga.Group.FILE.order = 0 + # Only create the menu if the menu item index has been created. + if hasattr(self, '_menu_items'): + menubar = WinForms.MenuStrip() + submenu = None + for cmd in self.interface.commands: + if cmd == toga.GROUP_BREAK: + menubar.Items.Add(submenu) + submenu = None + elif cmd == toga.SECTION_BREAK: + submenu.DropDownItems.Add('-') + else: + if submenu is None: + submenu = WinForms.ToolStripMenuItem(cmd.group.label) + item = WinForms.ToolStripMenuItem(cmd.label) + if cmd.action: + item.Click += add_handler(cmd) + else: + item.Enabled = False + cmd._widgets.append(item) + self._menu_items[item] = cmd + submenu.DropDownItems.Add(item) + if submenu: + menubar.Items.Add(submenu) + self.interface.main_window._impl.native.Controls.Add(menubar) + self.interface.main_window._impl.native.MainMenuStrip = menubar + self.interface.main_window.content.refresh() def open_document(self, fileURL): '''Add a new document to this app.''' print("STUB: If you want to handle opening documents, implement App.open_document(fileURL)") - def create_menus(self): - self.interface.factory.not_implemented('App.create_menus()') - def run_app(self): self.create() self.native.Run(self.interface.main_window._impl.native) @@ -43,4 +81,4 @@ def main_loop(self): thread.Join() def exit(self): - self.interface.factory.not_implemented('App.exit()') + self.native.Exit() diff --git a/src/winforms/toga_winforms/command.py b/src/winforms/toga_winforms/command.py index 3bbbe7d62c..4fd4d2c588 100644 --- a/src/winforms/toga_winforms/command.py +++ b/src/winforms/toga_winforms/command.py @@ -1,10 +1,14 @@ -#from .widgets.icon import Icon +from toga.widgets.icon import Icon as CoreIcon class Command: def __init__(self, interface): self.interface = interface - - # if self.interface.icon_id: - # self.icon = Icon.load(self.interface.icon_id) - # else: - # self.icon = None + self.native = None + if self.interface.icon_id: + # If icon_id is an icon, not a filepath + if type(self.interface.icon_id) is not str: + self.interface.icon = self.interface.icon_id + else: + self.interface.icon = CoreIcon(self.interface.icon_id) + else: + self.interface.icon = None diff --git a/src/winforms/toga_winforms/libs.py b/src/winforms/toga_winforms/libs.py index f1bdd2721d..fe71ffa4de 100644 --- a/src/winforms/toga_winforms/libs.py +++ b/src/winforms/toga_winforms/libs.py @@ -8,7 +8,8 @@ from System import Convert from System import Threading from System import Uri -from System.Drawing import Size, Point, Color, ContentAlignment +from System.Drawing import Size, Point, Color, ContentAlignment, Bitmap +from System.Drawing import Icon as WinIcon def TextAlignment(value): @@ -18,3 +19,10 @@ def TextAlignment(value): CENTER: ContentAlignment.MiddleCenter, JUSTIFY: ContentAlignment.MiddleLeft, }[value] + + +def add_handler(cmd): + action = cmd.action + def handler(sender, event): + return action(None) + return handler diff --git a/src/winforms/toga_winforms/widgets/icon.py b/src/winforms/toga_winforms/widgets/icon.py index bce10585bf..5c5f8bff26 100644 --- a/src/winforms/toga_winforms/widgets/icon.py +++ b/src/winforms/toga_winforms/widgets/icon.py @@ -1,8 +1,41 @@ -# from ..libs import NSImage +import os + +from toga import Icon as toga_Icon +from toga_winforms.libs import Bitmap, WinIcon class Icon: def __init__(self, interface): + + def create_icon_from_file(filename): + icon_bitmap = Bitmap(self.interface.filename) + icon_handle = icon_bitmap.GetHicon() + return WinIcon.FromHandle(icon_handle) + self.interface = interface - interface._impl = self - # self.native = NSImage.alloc().initWithContentsOfFile(interface.filename) + self.interface._impl = self + valid_icon_extensions = ('.png', '.bmp', '.ico') + file_path, file_extension = os.path.splitext(self.interface.filename) + + if file_extension == '.ico': + self.native = WinIcon(self.interface.filename) + + elif os.path.isfile(file_path + '.ico'): + self.native = WinIcon(file_path + '.ico') + + elif file_extension in valid_icon_extensions: + self.native = create_icon_from_file(self.interface.filename) + + elif os.path.isfile(file_path + '.png'): + self.native = create_icon_from_file(file_path + '.png') + + elif os.path.isfile(file_path + '.bmp'): + self.native = create_icon_from_file(file_path + '.bmp') + + else: + print("[Winforms] No valid icon format available for {}; " + "fall back on Tiberius instead".format( + self.interface.filename)) + tiberius_file = toga_Icon.TIBERIUS_ICON.filename + '.ico' + self.interface.icon = toga_Icon.TIBERIUS_ICON + self.native = WinIcon(tiberius_file) diff --git a/src/winforms/toga_winforms/widgets/webview.py b/src/winforms/toga_winforms/widgets/webview.py index b139ec796b..e1253c0c50 100644 --- a/src/winforms/toga_winforms/widgets/webview.py +++ b/src/winforms/toga_winforms/widgets/webview.py @@ -1,6 +1,6 @@ from travertino.size import at_least -from toga_winforms.libs import WinForms +from toga_winforms.libs import WinForms, Uri from .base import Widget diff --git a/src/winforms/toga_winforms/window.py b/src/winforms/toga_winforms/window.py index bcae2ac4a2..667d60966d 100644 --- a/src/winforms/toga_winforms/window.py +++ b/src/winforms/toga_winforms/window.py @@ -1,7 +1,7 @@ from toga import GROUP_BREAK, SECTION_BREAK from travertino.layout import Viewport -from .libs import WinForms, Size +from .libs import WinForms, Size, add_handler class WinFormsViewport: @@ -36,14 +36,21 @@ def create(self): self.toolbar_items = None def create_toolbar(self): - self.toolbar_native = WinForms.ToolStrip() + self.toolbar_native = WinForms.MenuStrip() for cmd in self.interface.toolbar: if cmd == GROUP_BREAK: item = WinForms.ToolStripSeparator() elif cmd == SECTION_BREAK: item = WinForms.ToolStripSeparator() else: - item = WinForms.ToolStripButton() + cmd.native = cmd.bind(self.interface.factory) + native_icon = cmd.icon.bind(self.interface.factory).native + if cmd.icon is not None: + item = WinForms.ToolStripMenuItem(cmd.label, native_icon.ToBitmap()) + else: + item = WinForms.ToolStripMenuItem(cmd.label) + + item.Click += add_handler(cmd) self.toolbar_native.Items.Add(item) def set_position(self, position): @@ -58,15 +65,22 @@ def set_app(self, app): @property def vertical_shift(self): # vertical shift is the toolbar height or 0 + result = 0 try: - return self.native.interface._impl.toolbar_native.Height + result += self.native.interface._impl.toolbar_native.Height except AttributeError: - return 0 + pass + try: + result += self.native.interface._impl.native.MainMenuStrip.Height + except AttributeError: + pass + return result def set_content(self, widget): if self.toolbar_native: self.native.Controls.Add(self.toolbar_native) - + # Create the lookup table of menu items, + # then force the creation of the menus. self.native.Controls.Add(widget.native) # Set the widget's viewport to be based on the window's content.