The pervious version has keep on [gpui2](https://github.com/longbridge/gpui-component/tree/gpui2) branch, if there need to use. Ref https://github.com/zed-industries/zed/pull/22632 ## Prepare ### Generate index.scip ```bash rust-analyzer scip ./ > index.scip ``` ### Get [refactor](https://github.com/zed-industries/refactor) ```bash https://github.com/zed-industries/refactor.git ``` ## Refactor exist code ```bash cd refactor cargo run -- ~/work/gpui-component ``` Then will get result if successed: ``` Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.03s Running `target/debug/refactor /Users/jason/work/gpui-component` Loaded 115 SCIP documents Processing files starting with focus path: "" Changed 90 files ``` ------------- ## Changes ```diff - View<T> - Model<T> + Entity<T> - WeakView<T> + WeakEntity<T> - FocusableView + Focusable - cx.bounds() + window.bounds() - cx.refresh() + window.refresh() - cx.focused() + window.focused(cx) - cx.with_optional_element_state - window.with_optional_element_state ``` ```diff - &mut WindowContext + &mut Window, &mut App - cx: &mut WindowContext + window: &mut Window, cx: &mut App - &mut ViewContext< + &mut Window, &mut Context< - cx: &mut ViewContext< + window: &mut Window, cx: &mut Context< ``` ```diff - cx.spawn(|_, mut cx| async move { + cx.spawn_in(window, |_, mut cx| async move { - cx.dispatch_action(Box::new(...)) + window.dispatch_action(Box::new(...), cx) ``` `cx.spawn` ```diff - cx.spawn(|view, mut cx| async move { + cx.spawn_in(window, |view, mut window| async move { + _ = view.update_in(&mut window, move |view, window, cx| { ```
205 lines
5.2 KiB
Rust
205 lines
5.2 KiB
Rust
use std::{ops::Deref, rc::Rc};
|
|
|
|
use wry::{
|
|
dpi::{self, LogicalSize},
|
|
Rect,
|
|
};
|
|
|
|
use gpui::{
|
|
canvas, div, App, Bounds, ContentMask, DismissEvent, Element, ElementId, Entity, EventEmitter,
|
|
FocusHandle, Focusable, GlobalElementId, Hitbox, InteractiveElement, IntoElement, LayoutId,
|
|
MouseDownEvent, ParentElement as _, Pixels, Render, Size, Style, Styled as _, Window,
|
|
};
|
|
|
|
pub struct WebView {
|
|
focus_handle: FocusHandle,
|
|
webview: Rc<wry::WebView>,
|
|
visible: bool,
|
|
bounds: Bounds<Pixels>,
|
|
}
|
|
|
|
impl Drop for WebView {
|
|
fn drop(&mut self) {
|
|
self.hide();
|
|
}
|
|
}
|
|
|
|
impl WebView {
|
|
pub fn new(webview: wry::WebView, _: &mut Window, cx: &mut App) -> Self {
|
|
let _ = webview.set_bounds(Rect::default());
|
|
|
|
Self {
|
|
focus_handle: cx.focus_handle(),
|
|
visible: true,
|
|
bounds: Bounds::default(),
|
|
webview: Rc::new(webview),
|
|
}
|
|
}
|
|
|
|
pub fn show(&mut self) {
|
|
let _ = self.webview.set_visible(true);
|
|
}
|
|
|
|
pub fn hide(&mut self) {
|
|
_ = self.webview.focus_parent();
|
|
_ = self.webview.set_visible(false);
|
|
}
|
|
|
|
pub fn visible(&self) -> bool {
|
|
self.visible
|
|
}
|
|
|
|
pub fn bounds(&self) -> Bounds<Pixels> {
|
|
self.bounds
|
|
}
|
|
|
|
/// Go back in the webview history.
|
|
pub fn back(&mut self) -> anyhow::Result<()> {
|
|
Ok(self.webview.evaluate_script("history.back();")?)
|
|
}
|
|
|
|
pub fn load_url(&mut self, url: &str) {
|
|
self.webview.load_url(url).unwrap();
|
|
}
|
|
}
|
|
|
|
impl Deref for WebView {
|
|
type Target = wry::WebView;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.webview
|
|
}
|
|
}
|
|
|
|
impl Focusable for WebView {
|
|
fn focus_handle(&self, _cx: &gpui::App) -> FocusHandle {
|
|
self.focus_handle.clone()
|
|
}
|
|
}
|
|
|
|
impl EventEmitter<DismissEvent> for WebView {}
|
|
|
|
impl Render for WebView {
|
|
fn render(
|
|
&mut self,
|
|
window: &mut gpui::Window,
|
|
cx: &mut gpui::Context<Self>,
|
|
) -> impl IntoElement {
|
|
let view = cx.model().clone();
|
|
|
|
div()
|
|
.track_focus(&self.focus_handle)
|
|
.size_full()
|
|
.child({
|
|
let view = cx.model().clone();
|
|
canvas(
|
|
move |bounds, _, cx| view.update(cx, |r, _| r.bounds = bounds),
|
|
|_, _, _, _| {},
|
|
)
|
|
.absolute()
|
|
.size_full()
|
|
})
|
|
.child(WebViewElement::new(self.webview.clone(), view, window, cx))
|
|
}
|
|
}
|
|
|
|
/// A webview element can display a wry webview.
|
|
pub struct WebViewElement {
|
|
parent: Entity<WebView>,
|
|
view: Rc<wry::WebView>,
|
|
}
|
|
|
|
impl WebViewElement {
|
|
/// Create a new webview element from a wry WebView.
|
|
pub fn new(
|
|
view: Rc<wry::WebView>,
|
|
parent: Entity<WebView>,
|
|
_window: &mut Window,
|
|
_cx: &mut App,
|
|
) -> Self {
|
|
Self { view, parent }
|
|
}
|
|
}
|
|
|
|
impl IntoElement for WebViewElement {
|
|
type Element = WebViewElement;
|
|
|
|
fn into_element(self) -> Self::Element {
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Element for WebViewElement {
|
|
type RequestLayoutState = ();
|
|
type PrepaintState = Option<Hitbox>;
|
|
|
|
fn id(&self) -> Option<ElementId> {
|
|
None
|
|
}
|
|
|
|
fn request_layout(
|
|
&mut self,
|
|
_: Option<&GlobalElementId>,
|
|
window: &mut Window,
|
|
cx: &mut App,
|
|
) -> (LayoutId, Self::RequestLayoutState) {
|
|
let mut style = Style::default();
|
|
style.flex_grow = 0.0;
|
|
style.flex_shrink = 1.;
|
|
style.size = Size::full();
|
|
// If the parent view is no longer visible, we don't need to layout the webview
|
|
|
|
let id = window.request_layout(style, [], cx);
|
|
(id, ())
|
|
}
|
|
|
|
fn prepaint(
|
|
&mut self,
|
|
_: Option<&GlobalElementId>,
|
|
bounds: Bounds<Pixels>,
|
|
_: &mut Self::RequestLayoutState,
|
|
window: &mut Window,
|
|
cx: &mut App,
|
|
) -> Self::PrepaintState {
|
|
if !self.parent.read(cx).visible() {
|
|
return None;
|
|
}
|
|
|
|
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();
|
|
|
|
// Create a hitbox to handle mouse event
|
|
Some(window.insert_hitbox(bounds, false))
|
|
}
|
|
|
|
fn paint(
|
|
&mut self,
|
|
_: Option<&GlobalElementId>,
|
|
bounds: Bounds<Pixels>,
|
|
_: &mut Self::RequestLayoutState,
|
|
hitbox: &mut Self::PrepaintState,
|
|
window: &mut Window,
|
|
_: &mut App,
|
|
) {
|
|
let bounds = hitbox.clone().map(|h| h.bounds).unwrap_or(bounds);
|
|
window.with_content_mask(Some(ContentMask { bounds }), |window| {
|
|
let webview = self.view.clone();
|
|
window.on_mouse_event(move |event: &MouseDownEvent, _, _, _| {
|
|
if !bounds.contains(&event.position) {
|
|
// Click white space to blur the input focus
|
|
let _ = webview.focus_parent();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|