Progress on ContextMenu
This commit is contained in:
parent
9456f716c2
commit
074a221e0f
5 changed files with 247 additions and 158 deletions
|
@ -1,8 +1,9 @@
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
use taffy::style::Position;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
point, AnyElement, BorrowWindow, Bounds, Component, Element, LayoutId, ParentComponent, Pixels,
|
point, px, AbsoluteLength, AnyElement, BorrowWindow, Bounds, Component, Element, LayoutId,
|
||||||
Point, Size, Style,
|
ParentComponent, Pixels, Point, Size, Style,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct OverlayState {
|
pub struct OverlayState {
|
||||||
|
@ -72,8 +73,9 @@ impl<V: 'static> Element<V> for Overlay<V> {
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|child| child.layout(view_state, cx))
|
.map(|child| child.layout(view_state, cx))
|
||||||
.collect::<SmallVec<_>>();
|
.collect::<SmallVec<_>>();
|
||||||
|
|
||||||
let mut overlay_style = Style::default();
|
let mut overlay_style = Style::default();
|
||||||
overlay_style.position = crate::Position::Absolute;
|
overlay_style.position = Position::Absolute;
|
||||||
|
|
||||||
let layout_id = cx.request_layout(&overlay_style, child_layout_ids.iter().copied());
|
let layout_id = cx.request_layout(&overlay_style, child_layout_ids.iter().copied());
|
||||||
|
|
||||||
|
@ -106,6 +108,7 @@ impl<V: 'static> Element<V> for Overlay<V> {
|
||||||
origin: Point::zero(),
|
origin: Point::zero(),
|
||||||
size: cx.viewport_size(),
|
size: cx.viewport_size(),
|
||||||
};
|
};
|
||||||
|
dbg!(bounds, desired, limits);
|
||||||
|
|
||||||
match self.fit_mode {
|
match self.fit_mode {
|
||||||
OverlayFitMode::SnapToWindow => {
|
OverlayFitMode::SnapToWindow => {
|
||||||
|
|
|
@ -87,7 +87,7 @@ pub struct TerminalView {
|
||||||
has_new_content: bool,
|
has_new_content: bool,
|
||||||
//Currently using iTerm bell, show bell emoji in tab until input is received
|
//Currently using iTerm bell, show bell emoji in tab until input is received
|
||||||
has_bell: bool,
|
has_bell: bool,
|
||||||
context_menu: Option<ContextMenu>,
|
context_menu: Option<View<ContextMenu>>,
|
||||||
blink_state: bool,
|
blink_state: bool,
|
||||||
blinking_on: bool,
|
blinking_on: bool,
|
||||||
blinking_paused: bool,
|
blinking_paused: bool,
|
||||||
|
@ -302,10 +302,14 @@ impl TerminalView {
|
||||||
position: gpui::Point<Pixels>,
|
position: gpui::Point<Pixels>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
self.context_menu = Some(ContextMenu::new(vec![
|
self.context_menu = Some(cx.build_view(|cx| {
|
||||||
ContextMenuItem::entry(Label::new("Clear"), Clear),
|
ContextMenu::new(cx)
|
||||||
ContextMenuItem::entry(Label::new("Close"), CloseActiveItem { save_intent: None }),
|
.entry(Label::new("Clear"), Box::new(Clear))
|
||||||
]));
|
.entry(
|
||||||
|
Label::new("Close"),
|
||||||
|
Box::new(CloseActiveItem { save_intent: None }),
|
||||||
|
)
|
||||||
|
}));
|
||||||
dbg!(&position);
|
dbg!(&position);
|
||||||
// todo!()
|
// todo!()
|
||||||
// self.context_menu
|
// self.context_menu
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
use crate::{prelude::*, ListItemVariant};
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::{h_stack, prelude::*, ListItemVariant};
|
||||||
use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHeader};
|
use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHeader};
|
||||||
|
use gpui::{
|
||||||
|
overlay, px, Action, AnyElement, Bounds, DispatchPhase, Div, EventEmitter, FocusHandle,
|
||||||
|
Focusable, FocusableView, LayoutId, MouseButton, MouseDownEvent, Overlay, Render, View,
|
||||||
|
};
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
pub enum ContextMenuItem {
|
pub enum ContextMenuItem {
|
||||||
Header(SharedString),
|
Header(SharedString),
|
||||||
|
@ -19,12 +27,12 @@ impl Clone for ContextMenuItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ContextMenuItem {
|
impl ContextMenuItem {
|
||||||
fn to_list_item<V: 'static>(self) -> ListItem {
|
fn to_list_item(self) -> ListItem {
|
||||||
match self {
|
match self {
|
||||||
ContextMenuItem::Header(label) => ListSubHeader::new(label).into(),
|
ContextMenuItem::Header(label) => ListSubHeader::new(label).into(),
|
||||||
ContextMenuItem::Entry(label, action) => ListEntry::new(label)
|
ContextMenuItem::Entry(label, action) => ListEntry::new(label)
|
||||||
.variant(ListItemVariant::Inset)
|
.variant(ListItemVariant::Inset)
|
||||||
.on_click(action)
|
.action(action)
|
||||||
.into(),
|
.into(),
|
||||||
ContextMenuItem::Separator => ListSeparator::new().into(),
|
ContextMenuItem::Separator => ListSeparator::new().into(),
|
||||||
}
|
}
|
||||||
|
@ -43,40 +51,196 @@ impl ContextMenuItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Clone)]
|
|
||||||
pub struct ContextMenu {
|
pub struct ContextMenu {
|
||||||
items: Vec<ContextMenuItem>,
|
items: Vec<ListItem>,
|
||||||
|
focus_handle: FocusHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum MenuEvent {
|
||||||
|
Dismissed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventEmitter<MenuEvent> for ContextMenu {}
|
||||||
|
impl FocusableView for ContextMenu {
|
||||||
|
fn focus_handle(&self, cx: &gpui::AppContext) -> FocusHandle {
|
||||||
|
self.focus_handle.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextMenu {
|
impl ContextMenu {
|
||||||
pub fn new(items: impl IntoIterator<Item = ContextMenuItem>) -> Self {
|
pub fn new(cx: &mut WindowContext) -> Self {
|
||||||
Self {
|
Self {
|
||||||
items: items.into_iter().collect(),
|
items: Default::default(),
|
||||||
}
|
focus_handle: cx.focus_handle(),
|
||||||
}
|
}
|
||||||
// todo!()
|
}
|
||||||
// cx.add_action(ContextMenu::select_first);
|
|
||||||
// cx.add_action(ContextMenu::select_last);
|
pub fn header(mut self, title: impl Into<SharedString>) -> Self {
|
||||||
// cx.add_action(ContextMenu::select_next);
|
self.items.push(ListItem::Header(ListSubHeader::new(title)));
|
||||||
// cx.add_action(ContextMenu::select_prev);
|
self
|
||||||
// cx.add_action(ContextMenu::confirm);
|
}
|
||||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
|
||||||
v_stack()
|
pub fn separator(mut self) -> Self {
|
||||||
.flex()
|
self.items.push(ListItem::Separator(ListSeparator));
|
||||||
.bg(cx.theme().colors().elevated_surface_background)
|
self
|
||||||
.border()
|
}
|
||||||
.border_color(cx.theme().colors().border)
|
|
||||||
.child(List::new(
|
pub fn entry(mut self, label: Label, action: Box<dyn Action>) -> Self {
|
||||||
self.items
|
self.items.push(ListEntry::new(label).action(action).into());
|
||||||
.into_iter()
|
self
|
||||||
.map(ContextMenuItem::to_list_item::<V>)
|
}
|
||||||
.collect(),
|
|
||||||
))
|
pub fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
|
||||||
.on_mouse_down_out(|_, _, cx| cx.dispatch_action(Box::new(menu::Cancel)))
|
// todo!()
|
||||||
|
cx.emit(MenuEvent::Dismissed);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
|
||||||
|
cx.emit(MenuEvent::Dismissed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for ContextMenu {
|
||||||
|
type Element = Overlay<Self>;
|
||||||
|
// todo!()
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||||
|
overlay().child(
|
||||||
|
div().elevation_2(cx).flex().flex_row().child(
|
||||||
|
v_stack()
|
||||||
|
.min_w(px(200.))
|
||||||
|
.track_focus(&self.focus_handle)
|
||||||
|
.on_mouse_down_out(|this: &mut Self, _, cx| {
|
||||||
|
this.cancel(&Default::default(), cx)
|
||||||
|
})
|
||||||
|
// .on_action(ContextMenu::select_first)
|
||||||
|
// .on_action(ContextMenu::select_last)
|
||||||
|
// .on_action(ContextMenu::select_next)
|
||||||
|
// .on_action(ContextMenu::select_prev)
|
||||||
|
.on_action(ContextMenu::confirm)
|
||||||
|
.on_action(ContextMenu::cancel)
|
||||||
|
.flex_none()
|
||||||
|
// .bg(cx.theme().colors().elevated_surface_background)
|
||||||
|
// .border()
|
||||||
|
// .border_color(cx.theme().colors().border)
|
||||||
|
.child(List::new(self.items.clone())),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MenuHandle<V: 'static> {
|
||||||
|
id: ElementId,
|
||||||
|
children: SmallVec<[AnyElement<V>; 2]>,
|
||||||
|
builder: Rc<dyn Fn(&mut V, &mut ViewContext<V>) -> View<ContextMenu> + 'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> ParentComponent<V> for MenuHandle<V> {
|
||||||
|
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
||||||
|
&mut self.children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> MenuHandle<V> {
|
||||||
|
pub fn new(
|
||||||
|
id: impl Into<ElementId>,
|
||||||
|
builder: impl Fn(&mut V, &mut ViewContext<V>) -> View<ContextMenu> + 'static,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
id: id.into(),
|
||||||
|
children: SmallVec::new(),
|
||||||
|
builder: Rc::new(builder),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MenuHandleState<V> {
|
||||||
|
menu: Rc<RefCell<Option<View<ContextMenu>>>>,
|
||||||
|
menu_element: Option<AnyElement<V>>,
|
||||||
|
}
|
||||||
|
impl<V: 'static> Element<V> for MenuHandle<V> {
|
||||||
|
type ElementState = MenuHandleState<V>;
|
||||||
|
|
||||||
|
fn element_id(&self) -> Option<gpui::ElementId> {
|
||||||
|
Some(self.id.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&mut self,
|
||||||
|
view_state: &mut V,
|
||||||
|
element_state: Option<Self::ElementState>,
|
||||||
|
cx: &mut crate::ViewContext<V>,
|
||||||
|
) -> (gpui::LayoutId, Self::ElementState) {
|
||||||
|
let mut child_layout_ids = self
|
||||||
|
.children
|
||||||
|
.iter_mut()
|
||||||
|
.map(|child| child.layout(view_state, cx))
|
||||||
|
.collect::<SmallVec<[LayoutId; 2]>>();
|
||||||
|
|
||||||
|
let menu = if let Some(element_state) = element_state {
|
||||||
|
element_state.menu
|
||||||
|
} else {
|
||||||
|
Rc::new(RefCell::new(None))
|
||||||
|
};
|
||||||
|
|
||||||
|
let menu_element = menu.borrow_mut().as_mut().map(|menu| {
|
||||||
|
let mut view = menu.clone().render();
|
||||||
|
child_layout_ids.push(view.layout(view_state, cx));
|
||||||
|
view
|
||||||
|
});
|
||||||
|
|
||||||
|
let layout_id = cx.request_layout(&gpui::Style::default(), child_layout_ids.into_iter());
|
||||||
|
|
||||||
|
(layout_id, MenuHandleState { menu, menu_element })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(
|
||||||
|
&mut self,
|
||||||
|
bounds: Bounds<gpui::Pixels>,
|
||||||
|
view_state: &mut V,
|
||||||
|
element_state: &mut Self::ElementState,
|
||||||
|
cx: &mut crate::ViewContext<V>,
|
||||||
|
) {
|
||||||
|
for child in &mut self.children {
|
||||||
|
child.paint(view_state, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(menu) = element_state.menu_element.as_mut() {
|
||||||
|
menu.paint(view_state, cx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let menu = element_state.menu.clone();
|
||||||
|
let builder = self.builder.clone();
|
||||||
|
cx.on_mouse_event(move |view_state, event: &MouseDownEvent, phase, cx| {
|
||||||
|
if phase == DispatchPhase::Bubble
|
||||||
|
&& event.button == MouseButton::Right
|
||||||
|
&& bounds.contains_point(&event.position)
|
||||||
|
{
|
||||||
|
cx.stop_propagation();
|
||||||
|
cx.prevent_default();
|
||||||
|
|
||||||
|
let new_menu = (builder)(view_state, cx);
|
||||||
|
let menu2 = menu.clone();
|
||||||
|
cx.subscribe(&new_menu, move |this, modal, e, cx| match e {
|
||||||
|
MenuEvent::Dismissed => {
|
||||||
|
*menu2.borrow_mut() = None;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
*menu.borrow_mut() = Some(new_menu);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> Component<V> for MenuHandle<V> {
|
||||||
|
fn render(self) -> AnyElement<V> {
|
||||||
|
AnyElement::new(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use gpui::Action;
|
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
pub use stories::*;
|
pub use stories::*;
|
||||||
|
|
||||||
|
@ -84,7 +248,7 @@ pub use stories::*;
|
||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::story::Story;
|
use crate::story::Story;
|
||||||
use gpui::{action, Div, Render};
|
use gpui::{action, Div, Render, VisualContext};
|
||||||
|
|
||||||
pub struct ContextMenuStory;
|
pub struct ContextMenuStory;
|
||||||
|
|
||||||
|
@ -97,17 +261,25 @@ mod stories {
|
||||||
|
|
||||||
Story::container(cx)
|
Story::container(cx)
|
||||||
.child(Story::title_for::<_, ContextMenu>(cx))
|
.child(Story::title_for::<_, ContextMenu>(cx))
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(ContextMenu::new([
|
|
||||||
ContextMenuItem::header("Section header"),
|
|
||||||
ContextMenuItem::Separator,
|
|
||||||
ContextMenuItem::entry(Label::new("Print current time"), PrintCurrentDate {}),
|
|
||||||
]))
|
|
||||||
.on_action(|_, _: &PrintCurrentDate, _| {
|
.on_action(|_, _: &PrintCurrentDate, _| {
|
||||||
if let Ok(unix_time) = std::time::UNIX_EPOCH.elapsed() {
|
if let Ok(unix_time) = std::time::UNIX_EPOCH.elapsed() {
|
||||||
println!("Current Unix time is {:?}", unix_time.as_secs());
|
println!("Current Unix time is {:?}", unix_time.as_secs());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.child(
|
||||||
|
MenuHandle::new("test", move |_, cx| {
|
||||||
|
cx.build_view(|cx| {
|
||||||
|
ContextMenu::new(cx)
|
||||||
|
.header("Section header")
|
||||||
|
.separator()
|
||||||
|
.entry(
|
||||||
|
Label::new("Print current time"),
|
||||||
|
PrintCurrentDate {}.boxed_clone(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.child(Label::new("RIGHT CLICK ME")),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ impl ListHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Clone)]
|
||||||
pub struct ListSubHeader {
|
pub struct ListSubHeader {
|
||||||
label: SharedString,
|
label: SharedString,
|
||||||
left_icon: Option<Icon>,
|
left_icon: Option<Icon>,
|
||||||
|
@ -172,7 +172,7 @@ pub enum ListEntrySize {
|
||||||
Medium,
|
Medium,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Clone)]
|
||||||
pub enum ListItem {
|
pub enum ListItem {
|
||||||
Entry(ListEntry),
|
Entry(ListEntry),
|
||||||
Separator(ListSeparator),
|
Separator(ListSeparator),
|
||||||
|
@ -234,6 +234,24 @@ pub struct ListEntry {
|
||||||
on_click: Option<Box<dyn Action>>,
|
on_click: Option<Box<dyn Action>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for ListEntry {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
disabled: self.disabled,
|
||||||
|
// TODO: Reintroduce this
|
||||||
|
// disclosure_control_style: DisclosureControlVisibility,
|
||||||
|
indent_level: self.indent_level,
|
||||||
|
label: self.label.clone(),
|
||||||
|
left_slot: self.left_slot.clone(),
|
||||||
|
overflow: self.overflow,
|
||||||
|
size: self.size,
|
||||||
|
toggle: self.toggle,
|
||||||
|
variant: self.variant,
|
||||||
|
on_click: self.on_click.as_ref().map(|opt| opt.boxed_clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ListEntry {
|
impl ListEntry {
|
||||||
pub fn new(label: Label) -> Self {
|
pub fn new(label: Label) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -249,7 +267,7 @@ impl ListEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_click(mut self, action: impl Into<Box<dyn Action>>) -> Self {
|
pub fn action(mut self, action: impl Into<Box<dyn Action>>) -> Self {
|
||||||
self.on_click = Some(action.into());
|
self.on_click = Some(action.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,10 @@ use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||||
use ui::{h_stack, IconButton, InteractionState, Label, Tooltip};
|
use ui::{
|
||||||
|
h_stack, ContextMenu, ContextMenuItem, IconButton, InteractionState, Label, MenuEvent,
|
||||||
|
MenuHandle, Tooltip,
|
||||||
|
};
|
||||||
|
|
||||||
pub enum PanelEvent {
|
pub enum PanelEvent {
|
||||||
ChangePosition,
|
ChangePosition,
|
||||||
|
@ -659,117 +662,6 @@ impl PanelButtons {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub struct MenuHandle<V: 'static> {
|
|
||||||
id: ElementId,
|
|
||||||
children: SmallVec<[AnyElement<V>; 2]>,
|
|
||||||
builder: Rc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> ParentComponent<V> for MenuHandle<V> {
|
|
||||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
|
||||||
&mut self.children
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> MenuHandle<V> {
|
|
||||||
fn new(
|
|
||||||
id: impl Into<ElementId>,
|
|
||||||
builder: impl Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
id: id.into(),
|
|
||||||
children: SmallVec::new(),
|
|
||||||
builder: Rc::new(builder),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MenuState<V> {
|
|
||||||
open: Rc<RefCell<bool>>,
|
|
||||||
menu: Option<AnyElement<V>>,
|
|
||||||
}
|
|
||||||
// Here be dragons
|
|
||||||
impl<V: 'static> Element<V> for MenuHandle<V> {
|
|
||||||
type ElementState = MenuState<V>;
|
|
||||||
|
|
||||||
fn element_id(&self) -> Option<gpui::ElementId> {
|
|
||||||
Some(self.id.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
view_state: &mut V,
|
|
||||||
element_state: Option<Self::ElementState>,
|
|
||||||
cx: &mut crate::ViewContext<V>,
|
|
||||||
) -> (gpui::LayoutId, Self::ElementState) {
|
|
||||||
let mut child_layout_ids = self
|
|
||||||
.children
|
|
||||||
.iter_mut()
|
|
||||||
.map(|child| child.layout(view_state, cx))
|
|
||||||
.collect::<SmallVec<[LayoutId; 2]>>();
|
|
||||||
|
|
||||||
let open = if let Some(element_state) = element_state {
|
|
||||||
element_state.open
|
|
||||||
} else {
|
|
||||||
Rc::new(RefCell::new(false))
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut menu = None;
|
|
||||||
if *open.borrow() {
|
|
||||||
let mut view = (self.builder)(view_state, cx).render();
|
|
||||||
child_layout_ids.push(view.layout(view_state, cx));
|
|
||||||
menu.replace(view);
|
|
||||||
}
|
|
||||||
let layout_id = cx.request_layout(&gpui::Style::default(), child_layout_ids.into_iter());
|
|
||||||
|
|
||||||
(layout_id, MenuState { open, menu })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
bounds: crate::Bounds<gpui::Pixels>,
|
|
||||||
view_state: &mut V,
|
|
||||||
element_state: &mut Self::ElementState,
|
|
||||||
cx: &mut crate::ViewContext<V>,
|
|
||||||
) {
|
|
||||||
for child in &mut self.children {
|
|
||||||
child.paint(view_state, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(mut menu) = element_state.menu.as_mut() {
|
|
||||||
menu.paint(view_state, cx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let open = element_state.open.clone();
|
|
||||||
cx.on_mouse_event(move |view_state, event: &MouseDownEvent, phase, cx| {
|
|
||||||
dbg!(&event, &phase);
|
|
||||||
if phase == DispatchPhase::Bubble
|
|
||||||
&& event.button == MouseButton::Right
|
|
||||||
&& bounds.contains_point(&event.position)
|
|
||||||
{
|
|
||||||
*open.borrow_mut() = true;
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> Component<V> for MenuHandle<V> {
|
|
||||||
fn render(self) -> AnyElement<V> {
|
|
||||||
AnyElement::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestMenu {}
|
|
||||||
impl Render for TestMenu {
|
|
||||||
type Element = Div<Self>;
|
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
|
||||||
div().child("0MG!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// here be kittens
|
// here be kittens
|
||||||
impl Render for PanelButtons {
|
impl Render for PanelButtons {
|
||||||
type Element = Div<Self>;
|
type Element = Div<Self>;
|
||||||
|
@ -807,7 +699,7 @@ impl Render for PanelButtons {
|
||||||
Some(
|
Some(
|
||||||
MenuHandle::new(
|
MenuHandle::new(
|
||||||
SharedString::from(format!("{} tooltip", name)),
|
SharedString::from(format!("{} tooltip", name)),
|
||||||
move |_, cx| Tooltip::text("HELLOOOOOOOOOOOOOO", cx),
|
move |_, cx| cx.build_view(|cx| ContextMenu::new(cx).header("SECTION")),
|
||||||
)
|
)
|
||||||
.child(button),
|
.child(button),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue