Something went wrong. Try again.
a servo-backed webview component for gpui
Something went wrong. Try again.
4.7 kB · 154 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155use gpui::{ App, AppContext as _, Bounds, Context, Entity, Focusable, Render, Stateful, Window, WindowBounds, WindowOptions, div, prelude::*, px, rgb, size,};use gpui_platform::application;use gpui_servo::{ServoConfig, ServoEngine, WebView};use url::Url;
fn parse_url(input: &str) -> Option<Url> { let input = input.trim(); if input.is_empty() { return None; } Url::parse(input) .or_else(|_| Url::parse(&format!("https://{input}"))) .ok()}
fn nav_button(label: &'static str, id: &'static str, enabled: bool) -> Stateful<gpui::Div> { div() .px_2() .py_1() .rounded_md() .text_sm() .text_color(if enabled { rgb(0xe4e4e7) } else { rgb(0x52525b) }) .when(enabled, |el| { el.hover(|el| el.bg(rgb(0x27272a))).cursor_pointer() }) .child(label) .id(id)}
fn main() { let start = std::env::args() .nth(1) .and_then(|arg| parse_url(&arg)) .unwrap_or_else(|| { Url::parse("https://en.wikipedia.org/wiki/Meridian").expect("valid url") });
application().run(move |cx: &mut App| { gpui_servo::init(cx);
let bounds = Bounds::centered(None, size(px(1100.), px(720.)), cx); cx.open_window( WindowOptions { window_bounds: Some(WindowBounds::Windowed(bounds)), ..Default::default() }, move |window, cx| { let engine = cx.new(|cx| { ServoEngine::new(Some(start.clone()), ServoConfig::default(), window, cx) }); cx.new(|cx| BrowserView::new(engine, window, cx)) }, ) .expect("failed to open the browser window");
cx.activate(true); });}
struct BrowserView { engine: Entity<ServoEngine>, webview: Entity<WebView>,}
impl BrowserView { fn new(engine: Entity<ServoEngine>, window: &mut Window, cx: &mut Context<Self>) -> Self { let webview = cx.new(|cx| WebView::new(engine.clone(), cx).keyboard(true)); // hand keyboard focus to the page window.focus(&webview.read(cx).focus_handle(cx), cx); Self { engine, webview } }}
impl Render for BrowserView { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { let engine = self.engine.read(cx); let url = engine.url().map(|url| url.to_string()).unwrap_or_default(); let loading = engine.is_loading(); let can_back = engine.can_go_back(); let can_forward = engine.can_go_forward();
let back_engine = self.engine.clone(); let forward_engine = self.engine.clone(); let reload_engine = self.engine.clone();
div() .size_full() .flex() .flex_col() .bg(rgb(0x18181b)) .child( div() .flex() .flex_row() .items_center() .gap_2() .px_3() .py_2() .bg(rgb(0x09090b)) .border_b_1() .border_color(rgb(0x27272a)) .child( nav_button("Back", "back", can_back).on_click(move |_, _, cx| { back_engine.update(cx, |engine, _| engine.go_back()); }), ) .child(nav_button("Forward", "forward", can_forward).on_click( move |_, _, cx| { forward_engine.update(cx, |engine, _| engine.go_forward()); }, )) .child( nav_button("Reload", "reload", true).on_click(move |_, _, cx| { reload_engine.update(cx, |engine, _| engine.reload()); }), ) .child( div() .flex_1() .min_w_0() .text_sm() .text_color(if loading { rgb(0x7a7df3) } else { rgb(0x6b7280) }) .line_clamp(1) .child(url), ), ) .child(div().flex_1().min_h_0().child(self.webview.clone())) }}