diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ffd3d0686b..b8d824470b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -141,7 +141,7 @@ jobs: strategy: fail-fast: false matrix: - backend: [ "macOS", "windows", "linux", "android", "iOS" ] + backend: [ "macOS", "windows", "linux", "android", "iOS", "web" ] include: - pre-command: briefcase-run-prefix: @@ -193,6 +193,11 @@ jobs: - backend: android runs-on: macos-12 briefcase-run-args: " -d '{\"avd\":\"beePhone\"}' --Xemulator=-no-window --Xemulator=-no-snapshot --Xemulator=-no-audio --Xemulator=-no-boot-anim --shutdown-on-exit" + + - backend: web + runs-on: ubuntu-22.04 + briefcase-run-args: " --no-browser" + steps: - uses: actions/checkout@v4.1.1 with: diff --git a/examples/tutorial3/tutorial/app.py b/examples/tutorial3/tutorial/app.py index d6dfa21851..ed5e808a2d 100644 --- a/examples/tutorial3/tutorial/app.py +++ b/examples/tutorial3/tutorial/app.py @@ -1,5 +1,5 @@ import toga -from toga.style.pack import CENTER, COLUMN, ROW, Pack +from toga.style.pack import COLUMN, ROW, Pack class Graze(toga.App): @@ -7,10 +7,12 @@ def startup(self): self.main_window = toga.MainWindow() self.webview = toga.WebView( - on_webview_load=self.on_webview_loaded, style=Pack(flex=1) + on_webview_load=self.on_webview_loaded, + style=Pack(flex=1), ) self.url_input = toga.TextInput( - value="https://beeware.org/", style=Pack(flex=1) + value="https://beeware.org/", + style=Pack(flex=1), ) box = toga.Box( @@ -24,11 +26,7 @@ def startup(self): style=Pack(width=50, padding_left=5), ), ], - style=Pack( - direction=ROW, - alignment=CENTER, - padding=5, - ), + style=Pack(direction=ROW, padding=5), ), self.webview, ], diff --git a/testbed/pyproject.toml b/testbed/pyproject.toml index 9107fb175b..c2740e20e1 100644 --- a/testbed/pyproject.toml +++ b/testbed/pyproject.toml @@ -105,3 +105,5 @@ android.defaultConfig.python { requires = [ "../web" ] +# FIXME: needed for test purposes to use a pre-release template. +template_branch = "web-test" diff --git a/web/setup.cfg b/web/setup.cfg index 3221e804e8..fd41719062 100644 --- a/web/setup.cfg +++ b/web/setup.cfg @@ -51,6 +51,7 @@ toga.backends = [options.package_data] toga_web = + inserts/** static/** [options.packages.find] diff --git a/web/src/toga_web/app.py b/web/src/toga_web/app.py index 839892acc1..1afc9883dc 100644 --- a/web/src/toga_web/app.py +++ b/web/src/toga_web/app.py @@ -1,3 +1,5 @@ +import asyncio + import toga from toga.command import GROUP_BREAK, SECTION_BREAK from toga_web.libs import create_element, js @@ -13,6 +15,7 @@ class App: def __init__(self, interface): self.interface = interface self.interface._impl = self + self.loop = asyncio.get_event_loop() def create(self): # self.resource_path = os.path.dirname(os.path.dirname(NSBundle.mainBundle.bundlePath)) diff --git a/web/src/toga_web/factory.py b/web/src/toga_web/factory.py index 153c845462..836217c58c 100644 --- a/web/src/toga_web/factory.py +++ b/web/src/toga_web/factory.py @@ -34,7 +34,8 @@ from .widgets.textinput import TextInput # from .widgets.tree import Tree -# from .widgets.webview import WebView +from .widgets.webview import WebView + # from .window import Window @@ -77,7 +78,7 @@ def not_implemented(feature): # 'Table', "TextInput", # 'Tree', - # 'WebView', + "WebView", # 'Window', ] diff --git a/web/src/toga_web/inserts/index.html:header b/web/src/toga_web/inserts/index.html:header new file mode 100644 index 0000000000..9418e45fe3 --- /dev/null +++ b/web/src/toga_web/inserts/index.html:header @@ -0,0 +1,4 @@ + + diff --git a/web/src/toga_web/static/toga.css b/web/src/toga_web/inserts/toga.css similarity index 96% rename from web/src/toga_web/static/toga.css rename to web/src/toga_web/inserts/toga.css index 6cc3929e3c..50d34f31d2 100644 --- a/web/src/toga_web/static/toga.css +++ b/web/src/toga_web/inserts/toga.css @@ -10,6 +10,9 @@ html, body { height: 100%; margin: 0; + display: flex; + flex-direction: column; + font-family: sans-serif; } /* If a custom element hasn't been defined yet, hide it from rendering */ @@ -79,6 +82,7 @@ main.toga.window { overflow: hidden; display: flex; flex-direction: column; + flex-grow: 1; } main.toga.window > .container { diff --git a/web/src/toga_web/widgets/webview.py b/web/src/toga_web/widgets/webview.py new file mode 100644 index 0000000000..47babc8e26 --- /dev/null +++ b/web/src/toga_web/widgets/webview.py @@ -0,0 +1,43 @@ +from travertino.size import at_least + +from toga.widgets.webview import JavaScriptResult + +from .base import Widget + + +class WebView(Widget): + def create(self): + self.native = self._create_native_widget("iframe") + + def get_url(self): + url = str(self.native.src) + return None if url == "about:blank" else url + + def set_url(self, value, future=None): + if value: + self.native.src = value + else: + self.native.src = "about:blank" + + self.loaded_future = future + + def set_content(self, root_url, content): + pass + # self.native.loadHTMLString(content, baseURL=NSURL.URLWithString(root_url)) + + def get_user_agent(self): + # return str(self.native.valueForKey("userAgent")) + return "user agent?" + + def set_user_agent(self, value): + # self.native.customUserAgent = value + pass + + def evaluate_javascript(self, javascript: str, on_result=None) -> str: + result = JavaScriptResult() + + return result + + def rehint(self): + self.interface.intrinsic.width = at_least(self.interface._MIN_WIDTH) + self.interface.intrinsic.height = at_least(self.interface._MIN_HEIGHT)