Fix remaining compile errors and runtime panics

This commit is contained in:
Antonio Scandurra 2024-02-27 10:33:20 +01:00
parent 7c6e6971da
commit 5183dbb5be
11 changed files with 293 additions and 216 deletions

View file

@ -107,6 +107,21 @@ impl<M: ManagedView> PopoverMenu<M> {
}
})
}
fn with_element_state<R>(
&mut self,
cx: &mut ElementContext,
f: impl FnOnce(&mut Self, &mut PopoverMenuElementState<M>, &mut ElementContext) -> R,
) -> R {
cx.with_element_state::<PopoverMenuElementState<M>, _>(
self.element_id(),
|element_state, cx| {
let mut element_state = element_state.unwrap().unwrap_or_default();
let result = f(self, &mut element_state, cx);
(result, Some(element_state))
},
)
}
}
/// Creates a [`PopoverMenu`]
@ -121,104 +136,116 @@ pub fn popover_menu<M: ManagedView>(id: impl Into<ElementId>) -> PopoverMenu<M>
}
}
pub struct PopoverMenuState<M> {
pub struct PopoverMenuElementState<M> {
menu: Rc<RefCell<Option<View<M>>>>,
child_bounds: Option<Bounds<Pixels>>,
}
impl<M> Clone for PopoverMenuElementState<M> {
fn clone(&self) -> Self {
Self {
menu: Rc::clone(&self.menu),
child_bounds: self.child_bounds,
}
}
}
impl<M> Default for PopoverMenuElementState<M> {
fn default() -> Self {
Self {
menu: Rc::default(),
child_bounds: None,
}
}
}
pub struct PopoverMenuFrameState {
child_layout_id: Option<LayoutId>,
child_element: Option<AnyElement>,
child_bounds: Option<Bounds<Pixels>>,
menu_element: Option<AnyElement>,
menu: Rc<RefCell<Option<View<M>>>>,
}
impl<M: ManagedView> Element for PopoverMenu<M> {
type FrameState = PopoverMenuState<M>;
type FrameState = PopoverMenuFrameState;
fn request_layout(
&mut self,
element_state: Option<Self::FrameState>,
cx: &mut ElementContext,
) -> (gpui::LayoutId, Self::FrameState) {
let mut menu_layout_id = None;
fn request_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::FrameState) {
self.with_element_state(cx, |this, element_state, cx| {
let mut menu_layout_id = None;
let (menu, child_bounds) = if let Some(element_state) = element_state {
(element_state.menu, element_state.child_bounds)
} else {
(Rc::default(), None)
};
let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| {
let mut overlay = overlay().snap_to_window().anchor(this.anchor);
let menu_element = menu.borrow_mut().as_mut().map(|menu| {
let mut overlay = overlay().snap_to_window().anchor(self.anchor);
if let Some(child_bounds) = element_state.child_bounds {
overlay = overlay.position(
this.resolved_attach().corner(child_bounds) + this.resolved_offset(cx),
);
}
if let Some(child_bounds) = child_bounds {
overlay = overlay.position(
self.resolved_attach().corner(child_bounds) + self.resolved_offset(cx),
);
}
let mut element = overlay.child(menu.clone()).into_any();
menu_layout_id = Some(element.request_layout(cx));
element
});
let mut element = overlay.child(menu.clone()).into_any();
menu_layout_id = Some(element.request_layout(cx));
element
});
let mut child_element = this.child_builder.take().map(|child_builder| {
(child_builder)(element_state.menu.clone(), this.menu_builder.clone())
});
let mut child_element = self
.child_builder
.take()
.map(|child_builder| (child_builder)(menu.clone(), self.menu_builder.clone()));
let child_layout_id = child_element
.as_mut()
.map(|child_element| child_element.request_layout(cx));
let child_layout_id = child_element
.as_mut()
.map(|child_element| child_element.request_layout(cx));
let layout_id = cx.request_layout(
&gpui::Style::default(),
menu_layout_id.into_iter().chain(child_layout_id),
);
let layout_id = cx.request_layout(
&gpui::Style::default(),
menu_layout_id.into_iter().chain(child_layout_id),
);
(
layout_id,
PopoverMenuState {
menu,
child_element,
child_layout_id,
menu_element,
child_bounds,
},
)
(
layout_id,
PopoverMenuFrameState {
child_element,
child_layout_id,
menu_element,
},
)
})
}
fn paint(
&mut self,
_: Bounds<gpui::Pixels>,
element_state: &mut Self::FrameState,
frame_state: &mut Self::FrameState,
cx: &mut ElementContext,
) {
if let Some(mut child) = element_state.child_element.take() {
child.paint(cx);
}
if let Some(child_layout_id) = element_state.child_layout_id.take() {
element_state.child_bounds = Some(cx.layout_bounds(child_layout_id));
}
if let Some(mut menu) = element_state.menu_element.take() {
menu.paint(cx);
if let Some(child_bounds) = element_state.child_bounds {
let interactive_bounds = InteractiveBounds {
bounds: child_bounds,
stacking_order: cx.stacking_order().clone(),
};
// Mouse-downing outside the menu dismisses it, so we don't
// want a click on the toggle to re-open it.
cx.on_mouse_event(move |e: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble
&& interactive_bounds.visibly_contains(&e.position, cx)
{
cx.stop_propagation()
}
})
self.with_element_state(cx, |_this, element_state, cx| {
if let Some(mut child) = frame_state.child_element.take() {
child.paint(cx);
}
}
if let Some(child_layout_id) = frame_state.child_layout_id.take() {
element_state.child_bounds = Some(cx.layout_bounds(child_layout_id));
}
if let Some(mut menu) = frame_state.menu_element.take() {
menu.paint(cx);
if let Some(child_bounds) = element_state.child_bounds {
let interactive_bounds = InteractiveBounds {
bounds: child_bounds,
stacking_order: cx.stacking_order().clone(),
};
// Mouse-downing outside the menu dismisses it, so we don't
// want a click on the toggle to re-open it.
cx.on_mouse_event(move |e: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble
&& interactive_bounds.visibly_contains(&e.position, cx)
{
cx.stop_propagation()
}
})
}
}
})
}
}