Skip to content

Commit

Permalink
Add WebView example
Browse files Browse the repository at this point in the history
  • Loading branch information
huacnlee committed Jul 3, 2024
1 parent 19930e4 commit f33c1ee
Show file tree
Hide file tree
Showing 10 changed files with 1,038 additions and 458 deletions.
1,218 changes: 764 additions & 454 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ default-members = ["crates/app"]
resolver = "2"

[workspace.dependencies]
gpui = { git = "https://github.com/zed-industries/zed.git" }
gpui = { git = "https://github.com/huacnlee/zed.git", branch = "export-platform-window" }
ui = { path = "crates/ui" }
story = { path = "crates/story" }
workspace = { path = "crates/workspace" }
Expand Down
1 change: 1 addition & 0 deletions crates/app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use anyhow::Result;
use app_state::AppState;
use assets::Assets;
use gpui::{App, AppContext};
use ui::webview;

mod app_state;
mod assets;
Expand Down
10 changes: 9 additions & 1 deletion crates/app/src/story_workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use gpui::*;
use prelude::FluentBuilder as _;
use story::{
ButtonStory, CheckboxStory, DropdownStory, ImageStory, InputStory, ListStory, PickerStory,
PopoverStory, StoryContainer, SwitchStory, TooltipStory,
PopoverStory, StoryContainer, SwitchStory, TooltipStory, WebViewStory,
};
use workspace::{TitleBar, Workspace};

Expand Down Expand Up @@ -126,6 +126,14 @@ impl StoryWorkspace {
ImageStory::view(cx).into(),
workspace.clone(),
cx,
);

StoryContainer::open(
"WebView",
"A webview element can display a wry webview.",
WebViewStory::view(cx).into(),
workspace.clone(),
cx,
)
.detach();

Expand Down
2 changes: 1 addition & 1 deletion crates/story/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fake = "2.9.2"
anyhow = "1"
workspace.workspace = true
charts-rs = "0.3"
image = "0.25.1"
wry = "0"

[lints]
workspace = true
2 changes: 2 additions & 0 deletions crates/story/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod picker_story;
mod popover_story;
mod switch_story;
mod tooltip_story;
mod webview_story;

pub use button_story::ButtonStory;
pub use checkbox_story::CheckboxStory;
Expand All @@ -19,6 +20,7 @@ pub use picker_story::PickerStory;
pub use popover_story::PopoverStory;
pub use switch_story::SwitchStory;
pub use tooltip_story::TooltipStory;
pub use webview_story::WebViewStory;

use gpui::{
div, prelude::FluentBuilder as _, px, AnyElement, AnyView, AppContext, Div, EventEmitter,
Expand Down
83 changes: 83 additions & 0 deletions crates/story/src/webview_story.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use gpui::{
div, px, FocusHandle, FocusableView, ParentElement as _, Render, Styled as _, View,
VisualContext as _, WindowContext,
};
use ui::{
input::{TextEvent, TextInput},
theme::ActiveTheme,
v_flex,
webview::WebViewContainer,
StyledExt,
};

pub struct WebViewStory {
focus_handle: FocusHandle,
webview: View<WebViewContainer>,
address_input: View<TextInput>,
}

impl WebViewStory {
pub fn view(cx: &mut WindowContext) -> View<Self> {
let focus_handle = cx.focus_handle();

let webview = cx.new_view(|cx| WebViewContainer::new(cx));

let address_input = cx.new_view(|cx| {
let mut input = TextInput::new(cx);
input.set_text("https://google.com", cx);
input
});

let url = address_input.read(cx).text();
webview.update(cx, |view, cx| {
view.load_url(&url);
});

cx.new_view(|cx| {
let this = WebViewStory {
focus_handle,
webview,
address_input: address_input.clone(),
};

cx.subscribe(
&address_input,
|this: &mut Self, input, event: &TextEvent, cx| match event {
TextEvent::PressEnter => {
let url = input.read(cx).text();
this.webview.update(cx, |view, cx| {
view.load_url(&url);
});
}
_ => {}
},
)
.detach();

this
})
}
}

impl FocusableView for WebViewStory {
fn focus_handle(&self, _cx: &gpui::AppContext) -> FocusHandle {
self.focus_handle.clone()
}
}

impl Render for WebViewStory {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl gpui::IntoElement {
v_flex()
.p_2()
.gap_3()
.size_full()
.child(self.address_input.clone())
.child(
div()
.size_full()
.border_1()
.border_color(cx.theme().border)
.child(self.webview.clone()),
)
}
}
4 changes: 3 additions & 1 deletion crates/ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]
gpui = { git = "https://github.com/zed-industries/zed.git" }
gpui = { git = "https://github.com/huacnlee/zed.git", branch = "export-platform-window" }
anyhow = "1"
log = "0.4"
serde = "1.0.203"
Expand All @@ -16,6 +16,8 @@ unicode-segmentation = "1.11.0"
image = "0.25.1"
resvg = { version = "0.41.0", default-features = false }
usvg = { version = "0.41.0", default-features = false }
raw-window-handle = "0.6.2"
winit = "0.30.3"

[lints]
workspace = true
2 changes: 2 additions & 0 deletions crates/ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod empty;
pub mod label;
pub mod prelude;
pub mod theme;
pub mod webview;
pub use styled_ext::StyledExt;
pub mod divider;
pub mod dropdown;
Expand All @@ -40,4 +41,5 @@ pub fn init(cx: &mut gpui::AppContext) {
picker::init(cx);
dropdown::init(cx);
popover::init(cx);
webview::init(cx)
}
172 changes: 172 additions & 0 deletions crates/ui/src/webview.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
use std::rc::Rc;

use wry::{
dpi::{self, LogicalSize},
Rect, WebViewExtMacOS,
};

use gpui::{
div, AppContext, Bounds, DismissEvent, Element, ElementId, EventEmitter, FocusHandle,
FocusableView, GlobalElementId, Hitbox, IntoElement, LayoutId, ParentElement as _, Pixels,
Render, Size, Style, Styled as _, View, WindowContext,
};

pub fn init(_cx: &AppContext) {}

pub struct WebViewContainer {
focus_handle: FocusHandle,
webview: Rc<wry::WebView>,
visable: bool,
}

impl WebViewContainer {
pub fn new(cx: &mut WindowContext) -> Self {
let focus_handle = cx.focus_handle();
let window_handle = cx.raw_window_handle();
let webview = wry::WebView::new_as_child(&window_handle).unwrap();

Self {
focus_handle,
visable: true,
webview: Rc::new(webview),
}
}

pub fn show(&mut self) {
let _ = self.webview.set_visible(true);
}

pub fn hide(&mut self) {
let _ = self.webview.set_visible(false);
}

pub fn visible(&self) -> bool {
self.visable
}

pub fn load_url(&mut self, url: &str) {
self.webview.load_url(url).unwrap();
}
}

impl FocusableView for WebViewContainer {
fn focus_handle(&self, _cx: &gpui::AppContext) -> FocusHandle {
self.focus_handle.clone()
}
}

impl EventEmitter<DismissEvent> for WebViewContainer {}

impl Render for WebViewContainer {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
let view = cx.view().clone();

div()
.size_full()
.debug()
.child(WebView::new(self.webview.clone(), view, cx))
}
}

/// A webview element can display a wry webview.
pub struct WebView {
focus_handle: FocusHandle,
parent: View<WebViewContainer>,
view: Rc<wry::WebView>,
}

impl WebView {
/// Create a new webview element from a wry WebView.
pub fn new(
view: Rc<wry::WebView>,
parent: View<WebViewContainer>,
cx: &mut WindowContext,
) -> Self {
let focus_handle = cx.focus_handle();

Self {
view,
parent,
focus_handle,
}
}
}

impl IntoElement for WebView {
type Element = WebView;

fn into_element(self) -> Self::Element {
self
}
}

impl Element for WebView {
type RequestLayoutState = ();
type PrepaintState = Option<Hitbox>;

fn id(&self) -> Option<ElementId> {
None
}

fn request_layout(
&mut self,
id: Option<&GlobalElementId>,
cx: &mut WindowContext,
) -> (LayoutId, Self::RequestLayoutState) {
let mut style = Style::default();
style.flex_grow = 0.0;
style.flex_shrink = 1.;
style.size = Size::full();

dbg!("---- request_layout");
// If the parent view is no longer visible, we don't need to layout the webview

let id = cx.request_layout(style, []);
(id, ())
}

fn prepaint(
&mut self,
_: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
layout_state: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) -> Self::PrepaintState {
if !self.parent.read(cx).visible() {
return None;
}

if bounds.top() > cx.viewport_size().height || bounds.bottom() < Pixels::ZERO {
dbg!("---------------- hidden");
self.view.set_visible(false).unwrap();
} else {
dbg!("---------------- show", &bounds);
self.view.set_visible(true).unwrap();

self.view
.set_bounds(Rect {
size: dpi::Size::Logical(LogicalSize {
width: (bounds.size.width.0).into(),
height: (bounds.size.height.0).into(),
}),
position: dpi::Position::Logical(dpi::LogicalPosition::new(
bounds.origin.x.into(),
bounds.origin.y.into(),
)),
})
.unwrap();
};

None
}

fn paint(
&mut self,
_: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
layout_state: &mut Self::RequestLayoutState,
hitbox: &mut Self::PrepaintState,
cx: &mut WindowContext,
) {
}
}

0 comments on commit f33c1ee

Please sign in to comment.