Skip to content

Commit

Permalink
Merge branch 'main' into patch-20
Browse files Browse the repository at this point in the history
  • Loading branch information
proneon267 committed Jun 17, 2024
2 parents afdbb30 + b4c0c91 commit 37e1add
Show file tree
Hide file tree
Showing 55 changed files with 959 additions and 538 deletions.
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,14 @@ updates:
interval: "weekly"
day: "sunday"
time: "20:00"

- package-ecosystem: "pip"
directory: "/testbed"
ignore:
# pin PIL for Android and iOS
- dependency-name: "pillow"
schedule:
# Check for updates on Sunday, 8PM UTC
interval: "weekly"
day: "sunday"
time: "20:00"
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ jobs:
python-version: "3.13-dev"
steps:
- name: Checkout
uses: actions/checkout@v4.1.6
uses: actions/checkout@v4.1.7
with:
fetch-depth: 0

Expand Down Expand Up @@ -146,7 +146,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.1.6
uses: actions/checkout@v4.1.7
with:
fetch-depth: 0

Expand Down Expand Up @@ -278,7 +278,7 @@ jobs:
uses: smorimoto/tune-github-hosted-runner-network@v1.0.0

- name: Checkout
uses: actions/checkout@v4.1.6
uses: actions/checkout@v4.1.7
with:
fetch-depth: 0

Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ repos:
- id: black
language_version: python3
- repo: https://github.com/PyCQA/flake8
rev: 7.0.0
rev: 7.1.0
hooks:
- id: flake8
- repo: https://github.com/codespell-project/codespell
Expand Down
4 changes: 3 additions & 1 deletion android/src/toga_android/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from org.beeware.android import IPythonApp, MainActivity

from toga.command import Command, Group, Separator
from toga.handlers import simple_handler

from .libs import events
from .screens import Screen as ScreenImpl
Expand Down Expand Up @@ -207,9 +208,10 @@ def create_app_commands(self):
self.interface.commands.add(
# About should be the last item in the menu, in a section on its own.
Command(
lambda _: self.interface.about(),
simple_handler(self.interface.about),
f"About {self.interface.formal_name}",
section=sys.maxsize,
id=Command.ABOUT,
),
)

Expand Down
1 change: 1 addition & 0 deletions changes/2023.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The initial position of each newly created window is now different, cascading down the screen as windows are created.
1 change: 1 addition & 0 deletions changes/2382.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The Testbed tests were updated to use the latest testing dependencies.
1 change: 1 addition & 0 deletions changes/2636.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Commands can now be retrieved by ID. System-installed commands (such as "About" and "Visit Homepage") are installed using a known ID that can be used at runtime to manipulate those commands.
1 change: 1 addition & 0 deletions changes/2647.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The "live docs" tox environments were updated to allow a grace period for ``sphinx-autobuild`` to shutdown the HTTP server. Without waiting, the HTTP server may have been left running when tox exited.
1 change: 1 addition & 0 deletions changes/2648.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The testbed testing strategy for MapView and WebView were updated to avoid Python crashes from WebKit.
1 change: 1 addition & 0 deletions changes/2654.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Updated actions/checkout from 4.1.6 to 4.1.7.
1 change: 1 addition & 0 deletions changes/2657.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Dependency specifications for the testbed were revised to avoid issues with Dependabot.
1 change: 1 addition & 0 deletions changes/2660.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The ``pre-commit`` hook for ``flake8`` was updated to its latest version.
1 change: 1 addition & 0 deletions changes/90.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
An app can now define a ``preferences()`` method to hook into the default menu item generated by Toga.
71 changes: 36 additions & 35 deletions cocoa/src/toga_cocoa/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
from rubicon.objc.eventloop import CocoaLifecycle, EventLoopPolicy

import toga
from toga.command import Separator
from toga.handlers import NativeHandler
from toga.app import overridden
from toga.command import Command, Separator
from toga.handlers import NativeHandler, simple_handler

from .keys import cocoa_key
from .libs import (
Expand Down Expand Up @@ -162,9 +163,6 @@ def create(self):
# Commands and menus
######################################################################

def _menu_about(self, command, **kwargs):
self.interface.about()

def _menu_close_all_windows(self, command, **kwargs):
# Convert to a list to so that we're not altering a set while iterating
for window in list(self.interface.windows):
Expand All @@ -178,74 +176,76 @@ def _menu_minimize(self, command, **kwargs):
if self.interface.current_window:
self.interface.current_window._impl.native.miniaturize(None)

def _menu_quit(self, command, **kwargs):
self.interface.on_exit()

def _menu_visit_homepage(self, command, **kwargs):
self.interface.visit_homepage()

def create_app_commands(self):
formal_name = self.interface.formal_name
self.interface.commands.add(
# ---- App menu -----------------------------------
toga.Command(
self._menu_about,
Command(
simple_handler(self.interface.about),
"About " + formal_name,
group=toga.Group.APP,
id=Command.ABOUT,
),
toga.Command(
None,
# Include a preferences menu item; but only enable it if the user has
# overridden it in their App class.
Command(
simple_handler(self.interface.preferences),
"Settings\u2026",
shortcut=toga.Key.MOD_1 + ",",
group=toga.Group.APP,
section=20,
enabled=overridden(self.interface.preferences),
id=Command.PREFERENCES,
),
toga.Command(
Command(
NativeHandler(SEL("hide:")),
"Hide " + formal_name,
shortcut=toga.Key.MOD_1 + "h",
group=toga.Group.APP,
order=0,
section=sys.maxsize - 1,
),
toga.Command(
Command(
NativeHandler(SEL("hideOtherApplications:")),
"Hide Others",
shortcut=toga.Key.MOD_1 + toga.Key.MOD_2 + "h",
group=toga.Group.APP,
order=1,
section=sys.maxsize - 1,
),
toga.Command(
Command(
NativeHandler(SEL("unhideAllApplications:")),
"Show All",
group=toga.Group.APP,
order=2,
section=sys.maxsize - 1,
),
# Quit should always be the last item, in a section on its own
toga.Command(
self._menu_quit,
"Quit " + formal_name,
# Quit should always be the last item, in a section on its own. Invoke
# `on_exit` rather than `exit`, because we want to trigger the "OK to exit?"
# logic. It's already a bound handler, so we can use it directly.
Command(
self.interface.on_exit,
f"Quit {formal_name}",
shortcut=toga.Key.MOD_1 + "q",
group=toga.Group.APP,
section=sys.maxsize,
id=Command.EXIT,
),
# ---- File menu ----------------------------------
# This is a bit of an oddity. Apple HIG apps that don't have tabs as
# part of their interface (so, Preview and Numbers, but not Safari)
# have a "Close" item that becomes "Close All" when you press Option
# (MOD_2). That behavior isn't something we're currently set up to
# implement, so we live with a separate menu item for now.
toga.Command(
Command(
self._menu_close_window,
"Close",
shortcut=toga.Key.MOD_1 + "w",
group=toga.Group.FILE,
order=1,
section=50,
),
toga.Command(
Command(
self._menu_close_all_windows,
"Close All",
shortcut=toga.Key.MOD_2 + toga.Key.MOD_1 + "w",
Expand All @@ -254,60 +254,60 @@ def create_app_commands(self):
section=50,
),
# ---- Edit menu ----------------------------------
toga.Command(
Command(
NativeHandler(SEL("undo:")),
"Undo",
shortcut=toga.Key.MOD_1 + "z",
group=toga.Group.EDIT,
order=10,
),
toga.Command(
Command(
NativeHandler(SEL("redo:")),
"Redo",
shortcut=toga.Key.SHIFT + toga.Key.MOD_1 + "z",
group=toga.Group.EDIT,
order=20,
),
toga.Command(
Command(
NativeHandler(SEL("cut:")),
"Cut",
shortcut=toga.Key.MOD_1 + "x",
group=toga.Group.EDIT,
section=10,
order=10,
),
toga.Command(
Command(
NativeHandler(SEL("copy:")),
"Copy",
shortcut=toga.Key.MOD_1 + "c",
group=toga.Group.EDIT,
section=10,
order=20,
),
toga.Command(
Command(
NativeHandler(SEL("paste:")),
"Paste",
shortcut=toga.Key.MOD_1 + "v",
group=toga.Group.EDIT,
section=10,
order=30,
),
toga.Command(
Command(
NativeHandler(SEL("pasteAsPlainText:")),
"Paste and Match Style",
shortcut=toga.Key.MOD_2 + toga.Key.SHIFT + toga.Key.MOD_1 + "v",
group=toga.Group.EDIT,
section=10,
order=40,
),
toga.Command(
Command(
NativeHandler(SEL("delete:")),
"Delete",
group=toga.Group.EDIT,
section=10,
order=50,
),
toga.Command(
Command(
NativeHandler(SEL("selectAll:")),
"Select All",
shortcut=toga.Key.MOD_1 + "a",
Expand All @@ -316,18 +316,19 @@ def create_app_commands(self):
order=60,
),
# ---- Window menu ----------------------------------
toga.Command(
Command(
self._menu_minimize,
"Minimize",
shortcut=toga.Key.MOD_1 + "m",
group=toga.Group.WINDOW,
),
# ---- Help menu ----------------------------------
toga.Command(
self._menu_visit_homepage,
Command(
simple_handler(self.interface.visit_homepage),
"Visit homepage",
enabled=self.interface.home_page is not None,
group=toga.Group.HELP,
id=Command.VISIT_HOMEPAGE,
),
)

Expand Down
3 changes: 2 additions & 1 deletion cocoa/src/toga_cocoa/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from toga.command import Command, Separator
from toga.types import Position, Size
from toga.window import _initial_position
from toga_cocoa.container import Container
from toga_cocoa.libs import (
SEL,
Expand Down Expand Up @@ -161,7 +162,7 @@ def __init__(self, interface, title, position, size):

self.set_title(title)
self.set_size(size)
self.set_position(position)
self.set_position(position if position is not None else _initial_position())

self.native.delegate = self.native

Expand Down
34 changes: 32 additions & 2 deletions core/src/toga/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def __init__(
self,
id: str | None = None,
title: str | None = None,
position: PositionT = Position(100, 100),
position: PositionT | None = None,
size: SizeT = Size(640, 480),
resizable: bool = True,
minimizable: bool = True,
Expand Down Expand Up @@ -300,6 +300,21 @@ def _default_title(self) -> str:
return self.doc.path.name


def overridable(method):
"""Decorate the method as being user-overridable"""
method._overridden = True
return method


def overridden(coroutine_or_method):
"""Has the user overridden this method?
This is based on the method *not* having a ``_overridden`` attribute. Overridable
default methods have this attribute; user-defined method will not.
"""
return not hasattr(coroutine_or_method, "_overridden")


class App:
#: The currently running :class:`~toga.App`. Since there can only be one running
#: Toga app in a process, this is available as a class property via ``toga.App.app``.
Expand Down Expand Up @@ -746,10 +761,25 @@ def beep(self) -> None:
"""Play the default system notification sound."""
self._impl.beep()

@overridable
def preferences(self) -> None:
"""Open a preferences panel for the app.
By default, this will do nothing, and the Preferences/Settings menu item
will be disabled. However, if you override this method in your App class,
the menu item will be enabled, and this method will be invoked when the
menu item is selected.
"""
# Default implementation won't ever be invoked, because the menu item
# isn't enabled unless it's overridden.
pass # pragma: no cover

def visit_homepage(self) -> None:
"""Open the application's :any:`home_page` in the default browser.
If the :any:`home_page` is ``None``, this is a no-op.
This method is invoked as a handler by the "Visit homepage" default menu item.
If the :any:`home_page` is ``None``, this is a no-op, and the default menu item
will be disabled.
"""
if self.home_page is not None:
webbrowser.open(self.home_page)
Expand Down
Loading

0 comments on commit 37e1add

Please sign in to comment.