1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// SPDX-License-Identifier: MPL-2.0
// SPDX-FileCopyrightText: 2026 FernTech
//! Tier-3 style protocol for `WebView`. See `docs/styling-system.md`.
//!
//! A `WebView` paints almost nothing itself — the actual page is drawn by a
//! native OS engine subview (WKWebView / WebView2 / WebKitGTK / Servo) that
//! sits *on top of* the wgpu surface. What Teksilo owns is the **overlay
//! chrome** the engine surface sits behind/under: the loading shimmer shown
//! before the first paint arrives, an error banner when navigation fails, and
//! the focus ring. This trait themes that chrome.
//!
//! Because the chrome reacts to the page's lifecycle, the config carries a
//! `Signal<WebViewVisualState>` (the same reactive pattern `DropZone` uses)
//! rather than a static state — `make_body` binds the overlay's appearance to
//! it so it updates without a rebuild.
use std::rc::Rc;
use teksilo_tokens::{BorderRole, SurfaceRole};
use crate::build_context::BuildContext;
use crate::signal::Signal;
use crate::widget_id::WidgetId;
/// Lifecycle/visual state of a web view, driving the overlay chrome. Defined
/// here (not in `teksilo-webview`) so the core style trait and the default
/// recipe can both name it — mirroring `DropZoneVisualState`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WebViewVisualState {
/// The engine is loading the current page — show the loading shimmer.
Loading,
/// The page has finished loading and is displayed by the engine.
/// The overlay is fully transparent; only the focus ring may show.
Ready,
/// Navigation or engine init failed — show the error chrome. Also the
/// state a platform with no engine that can embed lands in (wry under a
/// Wayland session, without Servo).
Error,
}
impl WebViewVisualState {
/// Background surface-tint role for the overlay in this state.
pub fn surface_role(self) -> SurfaceRole {
match self {
Self::Loading => SurfaceRole::Sunken,
Self::Ready => SurfaceRole::Transparent,
Self::Error => SurfaceRole::StatusError,
}
}
/// Border role for the overlay/focus chrome in this state.
pub fn border_role(self) -> BorderRole {
match self {
Self::Loading => BorderRole::Default,
Self::Ready => BorderRole::Default,
Self::Error => BorderRole::Error,
}
}
}
/// Inputs handed to a [`WebViewStyle`] to build the web view's overlay chrome.
#[derive(Clone)]
pub struct WebViewStyleConfig {
/// Reactive lifecycle state — bind overlay surface/border/opacity to it.
pub state: Signal<WebViewVisualState>,
/// Whether the Teksilo-side frame holds keyboard focus. The web view paints
/// no content of its own — the page is the engine's native subview — so the
/// focus ring drawn from this is the *only* thing telling a keyboard user
/// that Tab landed here and Enter will enter the page. Draw it.
pub focused: Signal<bool>,
/// Pre-built overlay content (e.g. a spinner + status label) the chrome
/// centers. The native engine surface is composited by the OS on top of
/// this; the overlay is what shows through before/around the page.
pub content: WidgetId,
}
/// Tier-3 style protocol for [`WebView`](../../teksilo_webview/struct.WebView.html).
/// Produces the overlay body shown behind/around the native engine surface.
pub trait WebViewStyle: 'static {
fn make_body(&self, cfg: &WebViewStyleConfig, ctx: &mut BuildContext) -> WidgetId;
}
/// Shared, theme-installable handle to a [`WebViewStyle`].
pub type SharedWebViewStyle = Rc<dyn WebViewStyle>;