Skip to content

Commit

Permalink
Merge pull request #2259 from freakboy3742/ios-optioncontainer
Browse files Browse the repository at this point in the history
Add OptionContainer for iOS
  • Loading branch information
freakboy3742 authored Dec 13, 2023
2 parents 36f1c75 + b1cedde commit f8957f4
Show file tree
Hide file tree
Showing 38 changed files with 1,167 additions and 120 deletions.
1 change: 1 addition & 0 deletions changes/2259.feature.1.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
An OptionContainer widget was added for iOS.
1 change: 1 addition & 0 deletions changes/2259.feature.2.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OptionContainer content can now be constructed using ``toga.OptionItem`` objects.
1 change: 1 addition & 0 deletions changes/2259.removal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
When inserting or appending a tab to an OptionContainer, the ``enabled`` argument must now be provided as a keyword argument.
12 changes: 10 additions & 2 deletions cocoa/src/toga_cocoa/widgets/optioncontainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ def set_bounds(self, x, y, width, height):

def content_refreshed(self, container):
container.min_width = container.content.interface.layout.min_width
container.min_height = container.content.interface.layout.min_width
container.min_height = container.content.interface.layout.min_height

def add_content(self, index, text, widget):
def add_content(self, index, text, widget, icon):
# Create the container for the widget
container = Container(on_refresh=self.content_refreshed)
container.content = widget
Expand Down Expand Up @@ -117,6 +117,14 @@ def set_option_text(self, index, value):
tabview = self.native.tabViewItemAtIndex(index)
tabview.label = value

def set_option_icon(self, index, value):
# Icons aren't supported
pass

def get_option_icon(self, index):
# Icons aren't supported
return None

def get_option_text(self, index):
tabview = self.native.tabViewItemAtIndex(index)
return tabview.label
Expand Down
2 changes: 1 addition & 1 deletion cocoa/src/toga_cocoa/widgets/splitcontainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def set_bounds(self, x, y, width, height):

def content_refreshed(self, container):
container.min_width = container.content.interface.layout.min_width
container.min_height = container.content.interface.layout.min_width
container.min_height = container.content.interface.layout.min_height

def set_content(self, content, flex):
# Clear any existing content
Expand Down
4 changes: 4 additions & 0 deletions cocoa/tests_backend/widgets/optioncontainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,7 @@ def tab_enabled(self, index):
# property lookup mechanism. Invoke it by passing the message directly.
item = self.native.tabViewItemAtIndex(index)
return send_message(item, SEL("_isTabEnabled"), restype=bool, argtypes=[])

def assert_tab_icon(self, index, expected):
# No tab icons, so if anything is returned, that's an error
assert self.widget.content[index].icon is None
2 changes: 2 additions & 0 deletions core/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ dev = [
"pytest-freezer == 0.4.8",
"setuptools-scm == 8.0.4",
"tox == 4.11.4",
# typing-extensions needed for TypeAlias added in Py 3.10
"typing-extensions == 4.9.0 ; python_version < '3.10'"
]
docs = [
"furo == 2023.9.10",
Expand Down
3 changes: 2 additions & 1 deletion core/src/toga/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from .widgets.label import Label
from .widgets.multilinetextinput import MultilineTextInput
from .widgets.numberinput import NumberInput
from .widgets.optioncontainer import OptionContainer
from .widgets.optioncontainer import OptionContainer, OptionItem
from .widgets.passwordinput import PasswordInput
from .widgets.progressbar import ProgressBar
from .widgets.scrollcontainer import ScrollContainer
Expand Down Expand Up @@ -71,6 +71,7 @@
"MultilineTextInput",
"NumberInput",
"OptionContainer",
"OptionItem",
"PasswordInput",
"ProgressBar",
"ScrollContainer",
Expand Down
25 changes: 15 additions & 10 deletions core/src/toga/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import webbrowser
from collections.abc import Collection, Iterator, Mapping, MutableSet
from email.message import Message
from typing import Any, Protocol
from typing import TYPE_CHECKING, Any, Protocol
from warnings import warn

from toga.command import Command, CommandSet
Expand All @@ -20,6 +20,9 @@
from toga.widgets.base import Widget, WidgetRegistry
from toga.window import Window

if TYPE_CHECKING:
from toga.icons import IconContent

# Make sure deprecation warnings are shown by default
warnings.filterwarnings("default", category=DeprecationWarning)

Expand Down Expand Up @@ -246,7 +249,7 @@ def __init__(
app_id: str | None = None,
app_name: str | None = None,
*,
icon: Icon | str | None = None,
icon: IconContent = None,
author: str | None = None,
version: str | None = None,
home_page: str | None = None,
Expand All @@ -262,8 +265,8 @@ def __init__(
:meth:`~toga.App.main_loop()` method, which will start the event loop of your
App.
:param formal_name: The human-readable name of the app. If not provided,
the metadata key ``Formal-Name`` must be present.
:param formal_name: The human-readable name of the app. If not provided, the
metadata key ``Formal-Name`` must be present.
:param app_id: The unique application identifier. This will usually be a
reversed domain name, e.g. ``org.beeware.myapp``. If not provided, the
metadata key ``App-ID`` must be present.
Expand All @@ -277,10 +280,10 @@ def __init__(
For example, an ``app_id`` of ``com.example.my-app`` would yield a
distribution name of ``my-app``.
#. As a last resort, the name ``toga``.
:param icon: The :any:`Icon` for the app. If not provided, Toga will attempt to
load an icon from ``resources/app_name``, where ``app_name`` is defined
above. If no resource matching this name can be found, a warning will be
printed, and the app will fall back to a default icon.
:param icon: The :any:`icon <IconContent>` for the app. If not provided, Toga
will attempt to load an icon from ``resources/app_name``, where ``app_name``
is defined above. If no resource matching this name can be found, a warning
will be printed, and the app will fall back to a default icon.
:param author: The person or organization to be credited as the author of the
app. If not provided, the metadata key ``Author`` will be used.
:param version: The version number of the app. If not provided, the metadata
Expand Down Expand Up @@ -500,13 +503,15 @@ def id(self) -> str:
def icon(self) -> Icon:
"""The Icon for the app.
Can be specified as any valid :any:`icon content <IconContent>`.
When setting the icon, you can provide either an :any:`Icon` instance, or a
path that will be passed to the ``Icon`` constructor.
"""
return self._icon

@icon.setter
def icon(self, icon_or_name: Icon | str) -> None:
def icon(self, icon_or_name: IconContent) -> None:
if isinstance(icon_or_name, Icon):
self._icon = icon_or_name
else:
Expand Down Expand Up @@ -711,7 +716,7 @@ def __init__(
app_id: str | None = None,
app_name: str | None = None,
*,
icon: str | None = None,
icon: IconContent = None,
author: str | None = None,
version: str | None = None,
home_page: str | None = None,
Expand Down
10 changes: 5 additions & 5 deletions core/src/toga/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

if TYPE_CHECKING:
from toga.app import App
from toga.icons import IconContent


class Group:
Expand Down Expand Up @@ -166,7 +167,7 @@ def __init__(
*,
shortcut: str | Key | None = None,
tooltip: str | None = None,
icon: str | Icon | None = None,
icon: IconContent = None,
group: Group = Group.COMMANDS,
section: int = 0,
order: int = 0,
Expand All @@ -183,8 +184,8 @@ def __init__(
:param text: A label for the command.
:param shortcut: A key combination that can be used to invoke the command.
:param tooltip: A short description of what the command will do.
:param icon: The icon, or icon resource, that can be used to decorate the
command if the platform requires.
:param icon: The :any:`icon content <IconContent>` that can be used to decorate
the command if the platform requires.
:param group: The group to which this command belongs.
:param section: The section where the command should appear within its group.
:param order: The position where the command should appear within its section.
Expand Down Expand Up @@ -232,8 +233,7 @@ def enabled(self, value: bool):
def icon(self) -> Icon | None:
"""The Icon for the command.
When setting the icon, you can provide either an :any:`Icon` instance, or a
path that will be passed to the ``Icon`` constructor.
Can be specified as any valid :any:`icon content <IconContent>`.
"""
return self._icon

Expand Down
14 changes: 14 additions & 0 deletions core/src/toga/icons.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
from __future__ import annotations

import sys
from pathlib import Path
from typing import TYPE_CHECKING

import toga
from toga.platform import get_platform_factory

if TYPE_CHECKING:
if sys.version_info < (3, 10):
from typing_extensions import TypeAlias
else:
from typing import TypeAlias

IconContent: TypeAlias = str | Path | toga.Icon | None


class cachedicon:
def __init__(self, f):
Expand Down Expand Up @@ -36,6 +46,10 @@ def TOGA_ICON(cls) -> Icon:
def DEFAULT_ICON(cls) -> Icon:
return Icon("resources/toga", system=True)

@cachedicon
def OPTION_CONTAINER_DEFAULT_TAB_ICON(cls) -> Icon:
return Icon("resources/optioncontainer-tab", system=True)

def __init__(
self,
path: str | Path,
Expand Down
Binary file added core/src/toga/resources/optioncontainer-tab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit f8957f4

Please sign in to comment.