Menus · ContextMenu manual: opens on the owner's numbered request at a point, not on a right click, for rows that come after the press
This commit is contained in:
2 files changed
+56
-7
No files matched your search
+24
-6
@@ -2,8 +2,8 @@ use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
AnyElement, App, ClickEvent, Div, ElementId, Entity, InteractiveElement, IntoElement,
|
||||
MouseButton, ParentElement, RenderOnce, SharedString, Stateful, Styled, Window, div,
|
||||
prelude::*,
|
||||
MouseButton, ParentElement, Pixels, Point, RenderOnce, SharedString, Stateful, Styled, Window,
|
||||
div, prelude::*,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
@@ -226,6 +226,7 @@ impl RenderOnce for SplitButton {
|
||||
pub struct ContextMenu {
|
||||
id: ElementId,
|
||||
menu: Menu,
|
||||
request: Option<Option<(u64, Point<Pixels>)>>,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
|
||||
@@ -234,9 +235,16 @@ impl ContextMenu {
|
||||
Self {
|
||||
id: id.into(),
|
||||
menu,
|
||||
request: None,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens on the owner's numbered request, not on a right click: for rows that come after the press.
|
||||
pub fn manual(mut self, request: Option<(u64, Point<Pixels>)>) -> Self {
|
||||
self.request = Some(request);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for ContextMenu {
|
||||
@@ -248,14 +256,24 @@ impl ParentElement for ContextMenu {
|
||||
impl RenderOnce for ContextMenu {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let state = state(&self.id, window, cx);
|
||||
if let Some(Some((number, at))) = self.request {
|
||||
let asked = window.use_keyed_state((self.id.clone(), "asked"), cx, |_, _| None);
|
||||
if *asked.read(cx) != Some(number) {
|
||||
asked.update(cx, |asked, _| *asked = Some(number));
|
||||
log::info!("context menu {:?}: asked at {at:?}", self.id);
|
||||
Open::show(&state, &self.menu, Spot::At(at), false, cx);
|
||||
}
|
||||
}
|
||||
let (open, shut, menu) = (state.clone(), state.clone(), self.menu.clone());
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.relative()
|
||||
.on_mouse_down(MouseButton::Right, move |event, window, cx| {
|
||||
window.prevent_default();
|
||||
log::info!("context menu: at {:?}", event.position);
|
||||
Open::show(&open, &menu, Spot::At(event.position), false, cx);
|
||||
.when(self.request.is_none(), |host| {
|
||||
host.on_mouse_down(MouseButton::Right, move |event, window, cx| {
|
||||
window.prevent_default();
|
||||
log::info!("context menu: at {:?}", event.position);
|
||||
Open::show(&open, &menu, Spot::At(event.position), false, cx);
|
||||
})
|
||||
})
|
||||
.on_mouse_down(MouseButton::Left, move |_, window, cx| {
|
||||
if shut.read(cx).is_open() {
|
||||
|
||||
+32
-1
@@ -18,6 +18,7 @@ struct Desk {
|
||||
view_rows: bool,
|
||||
enters_above: usize,
|
||||
shift: bool,
|
||||
asked: Option<(u64, gpui::Point<gpui::Pixels>)>,
|
||||
}
|
||||
|
||||
impl Desk {
|
||||
@@ -83,7 +84,15 @@ impl Render for Desk {
|
||||
ContextMenu::new("area", menu.clone())
|
||||
.child(div().w(px(200.0)).h(px(120.0))),
|
||||
)
|
||||
.child(SearchableMenu::new("find", "Find", menu)),
|
||||
.child(SearchableMenu::new("find", "Find", menu.clone()))
|
||||
.child(
|
||||
ContextMenu::new("late", menu).manual(self.asked).child(
|
||||
div()
|
||||
.debug_selector(|| "late".into())
|
||||
.w(px(200.0))
|
||||
.h(px(40.0)),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -117,6 +126,7 @@ fn desk(cx: &mut TestAppContext) -> (gpui::Entity<Desk>, &mut VisualTestContext)
|
||||
view_rows: true,
|
||||
enters_above: 0,
|
||||
shift: false,
|
||||
asked: None,
|
||||
});
|
||||
settle(cx);
|
||||
(view, cx)
|
||||
@@ -209,6 +219,27 @@ fn a_right_click_opens_the_context_menu_where_it_lands(cx: &mut TestAppContext)
|
||||
assert_eq!(ran(&view, cx), ["cut"]);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn a_manual_context_menu_opens_only_from_its_owner(cx: &mut TestAppContext) {
|
||||
let (view, cx) = desk(cx);
|
||||
let at = cx.debug_bounds("late").expect("the host").center();
|
||||
cx.simulate_mouse_move(at, None, Modifiers::none());
|
||||
cx.simulate_mouse_down(at, MouseButton::Right, Modifiers::none());
|
||||
cx.simulate_mouse_up(at, MouseButton::Right, Modifiers::none());
|
||||
settle(cx);
|
||||
press("down", cx);
|
||||
press("enter", cx);
|
||||
assert!(ran(&view, cx).is_empty(), "a right click opens nothing");
|
||||
view.update(cx, |desk, cx| {
|
||||
desk.asked = Some((1, point(px(60.0), px(80.0))));
|
||||
cx.notify();
|
||||
});
|
||||
settle(cx);
|
||||
press("down", cx);
|
||||
press("enter", cx);
|
||||
assert_eq!(ran(&view, cx), ["cut"]);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn the_trigger_toggles_and_a_press_outside_closes(cx: &mut TestAppContext) {
|
||||
let (view, cx) = desk(cx);
|
||||
|
||||
Reference in new issue
Block a user