chore: Upgrade to GPUI3 (#6)

* wip: gpui3

* wip: gpui3

* chore: fix clippy
This commit is contained in:
reya
2025-01-28 08:25:49 +07:00
committed by GitHub
parent 3c15e74e56
commit 72a6d79bc5
62 changed files with 2572 additions and 2511 deletions

View File

@@ -1,10 +1,10 @@
use crate::{Selectable, StyledExt as _};
use gpui::{
actions, anchored, deferred, div, prelude::FluentBuilder as _, px, AnyElement, AppContext,
Bounds, Corner, DismissEvent, DispatchPhase, Element, ElementId, EventEmitter, FocusHandle,
FocusableView, GlobalElementId, Hitbox, InteractiveElement as _, IntoElement, KeyBinding,
LayoutId, ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Render,
Style, StyleRefinement, Styled, View, ViewContext, VisualContext, WindowContext,
actions, anchored, deferred, div, prelude::FluentBuilder as _, px, AnyElement, App, Bounds,
Context, Corner, DismissEvent, DispatchPhase, Element, ElementId, Entity, EventEmitter,
FocusHandle, Focusable, GlobalElementId, Hitbox, InteractiveElement as _, IntoElement,
KeyBinding, LayoutId, ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point,
Render, Style, StyleRefinement, Styled, Window,
};
use std::{cell::RefCell, rc::Rc};
@@ -12,22 +12,20 @@ const CONTEXT: &str = "Popover";
actions!(popover, [Escape]);
pub fn init(cx: &mut AppContext) {
pub fn init(cx: &mut App) {
cx.bind_keys([KeyBinding::new("escape", Escape, Some(CONTEXT))])
}
type Content<T> = Rc<dyn Fn(&mut ViewContext<T>) -> AnyElement>;
pub struct PopoverContent {
focus_handle: FocusHandle,
content: Content<Self>,
content: Rc<dyn Fn(&mut Window, &mut Context<Self>) -> AnyElement>,
max_width: Option<Pixels>,
}
impl PopoverContent {
pub fn new<B>(cx: &mut WindowContext, content: B) -> Self
pub fn new<B>(_window: &mut Window, cx: &mut App, content: B) -> Self
where
B: Fn(&mut ViewContext<Self>) -> AnyElement + 'static,
B: Fn(&mut Window, &mut Context<Self>) -> AnyElement + 'static,
{
let focus_handle = cx.focus_handle();
@@ -46,32 +44,29 @@ impl PopoverContent {
impl EventEmitter<DismissEvent> for PopoverContent {}
impl FocusableView for PopoverContent {
fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
impl Focusable for PopoverContent {
fn focus_handle(&self, _cx: &App) -> FocusHandle {
self.focus_handle.clone()
}
}
impl Render for PopoverContent {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.track_focus(&self.focus_handle)
.key_context(CONTEXT)
.on_action(cx.listener(|_, _: &Escape, cx| cx.emit(DismissEvent)))
.on_action(cx.listener(|_, _: &Escape, _, cx| cx.emit(DismissEvent)))
.p_2()
.when_some(self.max_width, |this, v| this.max_w(v))
.child(self.content.clone()(cx))
.child(self.content.clone()(window, cx))
}
}
type Trigger = Option<Box<dyn FnOnce(bool, &WindowContext) -> AnyElement + 'static>>;
type ViewContent<M> = Option<Rc<dyn Fn(&mut WindowContext) -> View<M> + 'static>>;
pub struct Popover<M: ManagedView> {
id: ElementId,
anchor: Corner,
trigger: Trigger,
content: ViewContent<M>,
trigger: Option<Box<dyn FnOnce(bool, &Window, &App) -> AnyElement + 'static>>,
content: Option<Rc<dyn Fn(&mut Window, &mut App) -> Entity<M> + 'static>>,
/// Style for trigger element.
/// This is used for hotfix the trigger element style to support w_full.
trigger_style: Option<StyleRefinement>,
@@ -111,7 +106,7 @@ where
where
T: Selectable + IntoElement + 'static,
{
self.trigger = Some(Box::new(|is_open, _| {
self.trigger = Some(Box::new(|is_open, _, _| {
trigger.selected(is_open).into_any_element()
}));
self
@@ -121,13 +116,12 @@ where
self.trigger_style = Some(style);
self
}
/// Set the content of the popover.
///
/// The `content` is a closure that returns an `AnyElement`.
pub fn content<C>(mut self, content: C) -> Self
where
C: Fn(&mut WindowContext) -> View<M> + 'static,
C: Fn(&mut Window, &mut App) -> Entity<M> + 'static,
{
self.content = Some(Rc::new(content));
self
@@ -144,12 +138,12 @@ where
self
}
fn render_trigger(&mut self, is_open: bool, cx: &mut WindowContext) -> AnyElement {
fn render_trigger(&mut self, is_open: bool, window: &mut Window, cx: &mut App) -> AnyElement {
let Some(trigger) = self.trigger.take() else {
return div().into_any_element();
};
(trigger)(is_open, cx)
(trigger)(is_open, window, cx)
}
fn resolved_corner(&self, bounds: Bounds<Pixels>) -> Point<Pixels> {
@@ -164,14 +158,15 @@ where
fn with_element_state<R>(
&mut self,
id: &GlobalElementId,
cx: &mut WindowContext,
f: impl FnOnce(&mut Self, &mut PopoverElementState<M>, &mut WindowContext) -> R,
window: &mut Window,
cx: &mut App,
f: impl FnOnce(&mut Self, &mut PopoverElementState<M>, &mut Window, &mut App) -> R,
) -> R {
cx.with_optional_element_state::<PopoverElementState<M>, _>(
window.with_optional_element_state::<PopoverElementState<M>, _>(
Some(id),
|element_state, cx| {
|element_state, window| {
let mut element_state = element_state.unwrap().unwrap_or_default();
let result = f(self, &mut element_state, cx);
let result = f(self, &mut element_state, window, cx);
(result, Some(element_state))
},
)
@@ -194,7 +189,7 @@ pub struct PopoverElementState<M> {
popover_layout_id: Option<LayoutId>,
popover_element: Option<AnyElement>,
trigger_element: Option<AnyElement>,
content_view: Rc<RefCell<Option<View<M>>>>,
content_view: Rc<RefCell<Option<Entity<M>>>>,
/// Trigger bounds for positioning the popover.
trigger_bounds: Option<Bounds<Pixels>>,
}
@@ -229,7 +224,8 @@ impl<M: ManagedView> Element for Popover<M> {
fn request_layout(
&mut self,
id: Option<&gpui::GlobalElementId>,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
let mut style = Style::default();
@@ -246,73 +242,81 @@ impl<M: ManagedView> Element for Popover<M> {
}
}
self.with_element_state(id.unwrap(), cx, |view, element_state, cx| {
let mut popover_layout_id = None;
let mut popover_element = None;
let mut is_open = false;
self.with_element_state(
id.unwrap(),
window,
cx,
|view, element_state, window, cx| {
let mut popover_layout_id = None;
let mut popover_element = None;
let mut is_open = false;
if let Some(content_view) = element_state.content_view.borrow_mut().as_mut() {
is_open = true;
if let Some(content_view) = element_state.content_view.borrow_mut().as_mut() {
is_open = true;
let mut anchored = anchored()
.snap_to_window_with_margin(px(8.))
.anchor(view.anchor);
if let Some(trigger_bounds) = element_state.trigger_bounds {
anchored = anchored.position(view.resolved_corner(trigger_bounds));
let mut anchored = anchored()
.snap_to_window_with_margin(px(8.))
.anchor(view.anchor);
if let Some(trigger_bounds) = element_state.trigger_bounds {
anchored = anchored.position(view.resolved_corner(trigger_bounds));
}
let mut element = {
let content_view_mut = element_state.content_view.clone();
let anchor = view.anchor;
let no_style = view.no_style;
deferred(
anchored.child(
div()
.size_full()
.occlude()
.when(!no_style, |this| this.popover_style(cx))
.map(|this| match anchor {
Corner::TopLeft | Corner::TopRight => this.top_1p5(),
Corner::BottomLeft | Corner::BottomRight => {
this.bottom_1p5()
}
})
.child(content_view.clone())
.when(!no_style, |this| {
this.on_mouse_down_out(move |_, window, _| {
// Update the element_state.content_view to `None`,
// so that the `paint`` method will not paint it.
*content_view_mut.borrow_mut() = None;
window.refresh();
})
}),
),
)
.with_priority(1)
.into_any()
};
popover_layout_id = Some(element.request_layout(window, cx));
popover_element = Some(element);
}
let mut element = {
let content_view_mut = element_state.content_view.clone();
let anchor = view.anchor;
let no_style = view.no_style;
deferred(
anchored.child(
div()
.size_full()
.occlude()
.when(!no_style, |this| this.popover_style(cx))
.map(|this| match anchor {
Corner::TopLeft | Corner::TopRight => this.top_1p5(),
Corner::BottomLeft | Corner::BottomRight => this.bottom_1p5(),
})
.child(content_view.clone())
.when(!no_style, |this| {
this.on_mouse_down_out(move |_, cx| {
// Update the element_state.content_view to `None`,
// so that the `paint`` method will not paint it.
*content_view_mut.borrow_mut() = None;
cx.refresh();
})
}),
),
)
.with_priority(1)
.into_any()
};
let mut trigger_element = view.render_trigger(is_open, window, cx);
let trigger_layout_id = trigger_element.request_layout(window, cx);
popover_layout_id = Some(element.request_layout(cx));
popover_element = Some(element);
}
let layout_id = window.request_layout(
style,
Some(trigger_layout_id).into_iter().chain(popover_layout_id),
cx,
);
let mut trigger_element = view.render_trigger(is_open, cx);
let trigger_layout_id = trigger_element.request_layout(cx);
let layout_id = cx.request_layout(
style,
Some(trigger_layout_id).into_iter().chain(popover_layout_id),
);
(
layout_id,
PopoverElementState {
trigger_layout_id: Some(trigger_layout_id),
popover_layout_id,
popover_element,
trigger_element: Some(trigger_element),
..Default::default()
},
)
})
(
layout_id,
PopoverElementState {
trigger_layout_id: Some(trigger_layout_id),
popover_layout_id,
popover_element,
trigger_element: Some(trigger_element),
..Default::default()
},
)
},
)
}
fn prepaint(
@@ -320,25 +324,26 @@ impl<M: ManagedView> Element for Popover<M> {
_id: Option<&gpui::GlobalElementId>,
_bounds: gpui::Bounds<gpui::Pixels>,
request_layout: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> Self::PrepaintState {
if let Some(element) = &mut request_layout.trigger_element {
element.prepaint(cx);
element.prepaint(window, cx);
}
if let Some(element) = &mut request_layout.popover_element {
element.prepaint(cx);
element.prepaint(window, cx);
}
let trigger_bounds = request_layout
.trigger_layout_id
.map(|id| cx.layout_bounds(id));
.map(|id| window.layout_bounds(id));
// Prepare the popover, for get the bounds of it for open window size.
let _ = request_layout
.popover_layout_id
.map(|id| cx.layout_bounds(id));
.map(|id| window.layout_bounds(id));
let hitbox = cx.insert_hitbox(trigger_bounds.unwrap_or_default(), false);
let hitbox = window.insert_hitbox(trigger_bounds.unwrap_or_default(), false);
PrepaintState {
trigger_bounds,
@@ -352,57 +357,70 @@ impl<M: ManagedView> Element for Popover<M> {
_bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) {
self.with_element_state(id.unwrap(), cx, |this, element_state, cx| {
element_state.trigger_bounds = prepaint.trigger_bounds;
self.with_element_state(
id.unwrap(),
window,
cx,
|this, element_state, window, cx| {
element_state.trigger_bounds = prepaint.trigger_bounds;
if let Some(mut element) = request_layout.trigger_element.take() {
element.paint(cx);
}
if let Some(mut element) = request_layout.popover_element.take() {
element.paint(cx);
return;
}
// When mouse click down in the trigger bounds, open the popover.
let Some(content_build) = this.content.take() else {
return;
};
let old_content_view = element_state.content_view.clone();
let hitbox_id = prepaint.hitbox.id;
let mouse_button = this.mouse_button;
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble
&& event.button == mouse_button
&& hitbox_id.is_hovered(cx)
{
cx.stop_propagation();
cx.prevent_default();
let new_content_view = (content_build)(cx);
let old_content_view1 = old_content_view.clone();
let previous_focus_handle = cx.focused();
cx.subscribe(&new_content_view, move |modal, _: &DismissEvent, cx| {
if modal.focus_handle(cx).contains_focused(cx) {
if let Some(previous_focus_handle) = previous_focus_handle.as_ref() {
cx.focus(previous_focus_handle);
}
}
*old_content_view1.borrow_mut() = None;
cx.refresh();
})
.detach();
cx.focus_view(&new_content_view);
*old_content_view.borrow_mut() = Some(new_content_view);
cx.refresh();
if let Some(mut element) = request_layout.trigger_element.take() {
element.paint(window, cx);
}
});
});
if let Some(mut element) = request_layout.popover_element.take() {
element.paint(window, cx);
return;
}
// When mouse click down in the trigger bounds, open the popover.
let Some(content_build) = this.content.take() else {
return;
};
let old_content_view = element_state.content_view.clone();
let hitbox_id = prepaint.hitbox.id;
let mouse_button = this.mouse_button;
window.on_mouse_event(move |event: &MouseDownEvent, phase, window, cx| {
if phase == DispatchPhase::Bubble
&& event.button == mouse_button
&& hitbox_id.is_hovered(window)
{
cx.stop_propagation();
window.prevent_default();
let new_content_view = (content_build)(window, cx);
let old_content_view1 = old_content_view.clone();
let previous_focus_handle = window.focused(cx);
window
.subscribe(
&new_content_view,
cx,
move |modal, _: &DismissEvent, window, cx| {
if modal.focus_handle(cx).contains_focused(window, cx) {
if let Some(previous_focus_handle) =
previous_focus_handle.as_ref()
{
window.focus(previous_focus_handle);
}
}
*old_content_view1.borrow_mut() = None;
window.refresh();
},
)
.detach();
window.focus(&new_content_view.focus_handle(cx));
*old_content_view.borrow_mut() = Some(new_content_view);
window.refresh();
}
});
},
);
}
}