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)