Skip to main content

gpui_cn/
webview.rs

1//! An embedded web page, on `gpui-webview`.
2
3use gpui_kit::{
4    AnyElement, App, ElementId, Entity, InteractiveElement as _, IntoElement, ParentElement,
5    RenderOnce, StyleRefinement, Styled, TestSupportExt as _, Window, base::StyledExt as _, div,
6};
7
8mod browser;
9
10pub use browser::{BrowserEvent, BrowserState};
11pub use gpui_webview::{WebView, WebViewHandle};
12
13use crate::ActiveTheme as _;
14
15/// The frame of an embedded web page: a rounded box with a hairline
16/// border on the theme's window surface.
17///
18/// The page itself is a native view that the operating system owns, built
19/// with `WebView::build` from a `wry::WebViewBuilder` and kept in an
20/// `Entity`. The frame lays that view out, clips it to the rounded
21/// corners, and tells it whether the theme is dark or light, so the
22/// page's `prefers-color-scheme` follows the app on every render.
23///
24/// ```ignore
25/// let page = cx.new(|cx| {
26///     WebView::build(wry::WebViewBuilder::new().with_url("https://example.com"), window, cx)
27///         .expect("a native web view")
28/// });
29/// let _ = WebFrame::new("docs").webview(page).h_96();
30/// ```
31///
32/// Without the `gpui-fast` feature the native view covers GPUI content in
33/// its bounds, so a popover or a dialog cannot open above the page and the
34/// view is not clipped to the corners. The feature is on by default; see
35/// `gpui_webview::WebView` for the platform setup, such as Windows'
36/// `GPUI_DISABLE_DIRECT_COMPOSITION` and Linux's X11 window.
37#[derive(IntoElement)]
38pub struct WebFrame {
39    id: ElementId,
40    style: StyleRefinement,
41    webview: Option<Entity<WebView>>,
42    children: Vec<AnyElement>,
43}
44
45impl WebFrame {
46    /// A frame with a stable id.
47    pub fn new(id: impl Into<ElementId>) -> Self {
48        Self {
49            id: id.into(),
50            style: StyleRefinement::default(),
51            webview: None,
52            children: Vec::new(),
53        }
54    }
55
56    /// The page the frame shows. It fills the frame.
57    pub fn webview(mut self, webview: Entity<WebView>) -> Self {
58        self.webview = Some(webview);
59        self
60    }
61
62    /// The page the frame shows, if any.
63    pub fn page(&self) -> Option<&Entity<WebView>> {
64        self.webview.as_ref()
65    }
66}
67
68impl Styled for WebFrame {
69    fn style(&mut self) -> &mut StyleRefinement {
70        &mut self.style
71    }
72}
73
74impl ParentElement for WebFrame {
75    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
76        self.children.extend(elements);
77    }
78}
79
80impl RenderOnce for WebFrame {
81    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
82        let (fill, border, radius, dark) = {
83            let theme = cx.theme();
84            (
85                theme.background(),
86                theme.border(),
87                theme.radius_lg(),
88                theme.is_dark(),
89            )
90        };
91        if let Some(webview) = &self.webview {
92            let scheme = if dark {
93                wry::Theme::Dark
94            } else {
95                wry::Theme::Light
96            };
97            let _ = webview.read(cx).set_theme(scheme);
98        }
99        div()
100            .id(self.id)
101            .test_support()
102            .relative()
103            .overflow_hidden()
104            .border_1()
105            .border_color(border)
106            .rounded(radius)
107            .bg(fill)
108            .refine_style(&self.style)
109            .children(self.webview)
110            .children(self.children)
111    }
112}