Context menu, Dragon Drop, for collab panel (#3441)

Release Notes:

- N/A
This commit is contained in:
Conrad Irwin 2023-11-29 13:40:46 -07:00 committed by GitHub
commit fb377aed73
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 490 additions and 478 deletions

File diff suppressed because it is too large Load diff

View file

@ -824,7 +824,6 @@ impl Interactivity {
.and_then(|group_hover| GroupBounds::get(&group_hover.group, cx)); .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
if let Some(group_bounds) = hover_group_bounds { if let Some(group_bounds) = hover_group_bounds {
// todo!() needs cx.was_top_layer
let hovered = group_bounds.contains_point(&cx.mouse_position()); let hovered = group_bounds.contains_point(&cx.mouse_position());
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
if phase == DispatchPhase::Capture { if phase == DispatchPhase::Capture {
@ -836,13 +835,13 @@ impl Interactivity {
} }
if self.hover_style.is_some() if self.hover_style.is_some()
|| (cx.active_drag.is_some() && !self.drag_over_styles.is_empty()) || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
{ {
let interactive_bounds = interactive_bounds.clone(); let bounds = bounds.intersect(&cx.content_mask().bounds);
let hovered = interactive_bounds.visibly_contains(&cx.mouse_position(), cx); let hovered = bounds.contains_point(&cx.mouse_position());
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
if phase == DispatchPhase::Capture { if phase == DispatchPhase::Capture {
if interactive_bounds.visibly_contains(&event.position, cx) != hovered { if bounds.contains_point(&event.position) != hovered {
cx.notify(); cx.notify();
} }
} }
@ -1143,7 +1142,9 @@ impl Interactivity {
let mouse_position = cx.mouse_position(); let mouse_position = cx.mouse_position();
if let Some(group_hover) = self.group_hover_style.as_ref() { if let Some(group_hover) = self.group_hover_style.as_ref() {
if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) { if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
if group_bounds.contains_point(&mouse_position) { if group_bounds.contains_point(&mouse_position)
&& cx.was_top_layer(&mouse_position, cx.stacking_order())
{
style.refine(&group_hover.style); style.refine(&group_hover.style);
} }
} }
@ -1162,7 +1163,6 @@ impl Interactivity {
for (state_type, group_drag_style) in &self.group_drag_over_styles { for (state_type, group_drag_style) in &self.group_drag_over_styles {
if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) { if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
if *state_type == drag.view.entity_type() if *state_type == drag.view.entity_type()
// todo!() needs to handle cx.content_mask() and cx.is_top()
&& group_bounds.contains_point(&mouse_position) && group_bounds.contains_point(&mouse_position)
{ {
style.refine(&group_drag_style.style); style.refine(&group_drag_style.style);
@ -1175,7 +1175,6 @@ impl Interactivity {
&& bounds && bounds
.intersect(&cx.content_mask().bounds) .intersect(&cx.content_mask().bounds)
.contains_point(&mouse_position) .contains_point(&mouse_position)
&& cx.was_top_layer(&mouse_position, cx.stacking_order())
{ {
style.refine(drag_over_style); style.refine(drag_over_style);
} }

View file

@ -740,7 +740,7 @@ impl<T> Copy for Corners<T> where T: Copy + Clone + Default + Debug {}
Deserialize, Deserialize,
)] )]
#[repr(transparent)] #[repr(transparent)]
pub struct Pixels(pub(crate) f32); pub struct Pixels(pub f32);
impl std::ops::Div for Pixels { impl std::ops::Div for Pixels {
type Output = f32; type Output = f32;

View file

@ -1480,7 +1480,7 @@ impl Render for ProjectPanel {
.children(self.context_menu.as_ref().map(|(menu, position, _)| { .children(self.context_menu.as_ref().map(|(menu, position, _)| {
overlay() overlay()
.position(*position) .position(*position)
.anchor(gpui::AnchorCorner::BottomLeft) .anchor(gpui::AnchorCorner::TopLeft)
.child(menu.clone()) .child(menu.clone())
})) }))
} else { } else {

View file

@ -3,29 +3,22 @@ use std::rc::Rc;
use gpui::ClickEvent; use gpui::ClickEvent;
use crate::prelude::*; use crate::prelude::*;
use crate::{Color, Icon, IconButton, IconSize, ToggleState, Toggleable}; use crate::{Color, Icon, IconButton, IconSize};
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct Disclosure { pub struct Disclosure {
state: ToggleState, is_open: bool,
on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>, on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
} }
impl Disclosure { impl Disclosure {
pub fn new(state: ToggleState) -> Self { pub fn new(is_open: bool) -> Self {
Self { Self {
state, is_open,
on_toggle: None, on_toggle: None,
} }
} }
pub fn from_toggleable(toggleable: Toggleable) -> Option<Self> {
match toggleable {
Toggleable::Toggleable(state) => Some(Self::new(state)),
Toggleable::NotToggleable => None,
}
}
pub fn on_toggle( pub fn on_toggle(
mut self, mut self,
handler: impl Into<Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>>, handler: impl Into<Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>>,
@ -41,9 +34,10 @@ impl RenderOnce for Disclosure {
fn render(self, _cx: &mut WindowContext) -> Self::Rendered { fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
IconButton::new( IconButton::new(
"toggle", "toggle",
match self.state { if self.is_open {
ToggleState::Toggled => Icon::ChevronDown, Icon::ChevronDown
ToggleState::NotToggled => Icon::ChevronRight, } else {
Icon::ChevronRight
}, },
) )
.color(Color::Muted) .color(Color::Muted)

View file

@ -7,7 +7,7 @@ use gpui::{AnyElement, Div};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::prelude::*; use crate::prelude::*;
use crate::{v_stack, Label, ToggleState, Toggleable}; use crate::{v_stack, Label};
pub use list_header::*; pub use list_header::*;
pub use list_item::*; pub use list_item::*;
@ -20,7 +20,7 @@ pub struct List {
/// Defaults to "No items" /// Defaults to "No items"
empty_message: SharedString, empty_message: SharedString,
header: Option<ListHeader>, header: Option<ListHeader>,
toggle: Toggleable, toggle: Option<bool>,
children: SmallVec<[AnyElement; 2]>, children: SmallVec<[AnyElement; 2]>,
} }
@ -29,7 +29,7 @@ impl List {
Self { Self {
empty_message: "No items".into(), empty_message: "No items".into(),
header: None, header: None,
toggle: Toggleable::NotToggleable, toggle: None,
children: SmallVec::new(), children: SmallVec::new(),
} }
} }
@ -44,7 +44,7 @@ impl List {
self self
} }
pub fn toggle(mut self, toggle: Toggleable) -> Self { pub fn toggle(mut self, toggle: Option<bool>) -> Self {
self.toggle = toggle; self.toggle = toggle;
self self
} }
@ -66,7 +66,7 @@ impl RenderOnce for List {
.children(self.header.map(|header| header)) .children(self.header.map(|header| header))
.map(|this| match (self.children.is_empty(), self.toggle) { .map(|this| match (self.children.is_empty(), self.toggle) {
(false, _) => this.children(self.children), (false, _) => this.children(self.children),
(true, Toggleable::Toggleable(ToggleState::NotToggled)) => this, (true, Some(false)) => this,
(true, _) => this.child(Label::new(self.empty_message.clone()).color(Color::Muted)), (true, _) => this.child(Label::new(self.empty_message.clone()).color(Color::Muted)),
}) })
} }

View file

@ -3,7 +3,7 @@ use std::rc::Rc;
use gpui::{ClickEvent, Div}; use gpui::{ClickEvent, Div};
use crate::prelude::*; use crate::prelude::*;
use crate::{h_stack, Disclosure, Icon, IconButton, IconElement, IconSize, Label, Toggleable}; use crate::{h_stack, Disclosure, Icon, IconButton, IconElement, IconSize, Label};
pub enum ListHeaderMeta { pub enum ListHeaderMeta {
Tools(Vec<IconButton>), Tools(Vec<IconButton>),
@ -17,7 +17,7 @@ pub struct ListHeader {
label: SharedString, label: SharedString,
left_icon: Option<Icon>, left_icon: Option<Icon>,
meta: Option<ListHeaderMeta>, meta: Option<ListHeaderMeta>,
toggle: Toggleable, toggle: Option<bool>,
on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>, on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
inset: bool, inset: bool,
selected: bool, selected: bool,
@ -30,13 +30,13 @@ impl ListHeader {
left_icon: None, left_icon: None,
meta: None, meta: None,
inset: false, inset: false,
toggle: Toggleable::NotToggleable, toggle: None,
on_toggle: None, on_toggle: None,
selected: false, selected: false,
} }
} }
pub fn toggle(mut self, toggle: Toggleable) -> Self { pub fn toggle(mut self, toggle: Option<bool>) -> Self {
self.toggle = toggle; self.toggle = toggle;
self self
} }
@ -114,8 +114,8 @@ impl RenderOnce for ListHeader {
.child(Label::new(self.label.clone()).color(Color::Muted)), .child(Label::new(self.label.clone()).color(Color::Muted)),
) )
.children( .children(
Disclosure::from_toggleable(self.toggle) self.toggle
.map(|disclosure| disclosure.on_toggle(self.on_toggle)), .map(|is_open| Disclosure::new(is_open).on_toggle(self.on_toggle)),
), ),
) )
.child(meta), .child(meta),

View file

@ -6,7 +6,7 @@ use gpui::{
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::prelude::*; use crate::prelude::*;
use crate::{Avatar, Disclosure, GraphicSlot, Icon, IconElement, IconSize, Toggleable}; use crate::{Avatar, Disclosure, GraphicSlot, Icon, IconElement, IconSize};
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct ListItem { pub struct ListItem {
@ -17,7 +17,7 @@ pub struct ListItem {
indent_level: usize, indent_level: usize,
indent_step_size: Pixels, indent_step_size: Pixels,
left_slot: Option<GraphicSlot>, left_slot: Option<GraphicSlot>,
toggle: Toggleable, toggle: Option<bool>,
inset: bool, inset: bool,
on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>, on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>, on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
@ -33,7 +33,7 @@ impl ListItem {
indent_level: 0, indent_level: 0,
indent_step_size: px(12.), indent_step_size: px(12.),
left_slot: None, left_slot: None,
toggle: Toggleable::NotToggleable, toggle: None,
inset: false, inset: false,
on_click: None, on_click: None,
on_secondary_mouse_down: None, on_secondary_mouse_down: None,
@ -70,7 +70,7 @@ impl ListItem {
self self
} }
pub fn toggle(mut self, toggle: Toggleable) -> Self { pub fn toggle(mut self, toggle: Option<bool>) -> Self {
self.toggle = toggle; self.toggle = toggle;
self self
} }
@ -151,8 +151,8 @@ impl RenderOnce for ListItem {
.items_center() .items_center()
.relative() .relative()
.children( .children(
Disclosure::from_toggleable(self.toggle) self.toggle
.map(|disclosure| disclosure.on_toggle(self.on_toggle)), .map(|is_open| Disclosure::new(is_open).on_toggle(self.on_toggle)),
) )
.map(|this| match self.left_slot { .map(|this| match self.left_slot {
Some(GraphicSlot::Icon(i)) => this.child( Some(GraphicSlot::Icon(i)) => this.child(

View file

@ -2,7 +2,7 @@ use gpui::{Div, Render};
use story::Story; use story::Story;
use crate::prelude::*; use crate::prelude::*;
use crate::{Disclosure, ToggleState}; use crate::Disclosure;
pub struct DisclosureStory; pub struct DisclosureStory;
@ -13,8 +13,8 @@ impl Render for DisclosureStory {
Story::container() Story::container()
.child(Story::title_for::<Disclosure>()) .child(Story::title_for::<Disclosure>())
.child(Story::label("Toggled")) .child(Story::label("Toggled"))
.child(Disclosure::new(ToggleState::Toggled)) .child(Disclosure::new(true))
.child(Story::label("Not Toggled")) .child(Story::label("Not Toggled"))
.child(Disclosure::new(ToggleState::NotToggled)) .child(Disclosure::new(false))
} }
} }

View file

@ -1,38 +0,0 @@
/// Whether an element is able to be toggled.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub enum Toggleable {
Toggleable(ToggleState),
NotToggleable,
}
/// The current state of a [`Toggleable`] element.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub enum ToggleState {
Toggled,
NotToggled,
}
impl ToggleState {
/// Returns whether an entry is toggled.
pub fn is_toggled(&self) -> bool {
match self {
ToggleState::Toggled => true,
ToggleState::NotToggled => false,
}
}
}
impl From<bool> for ToggleState {
fn from(toggled: bool) -> Self {
match toggled {
true => Self::Toggled,
false => Self::NotToggled,
}
}
}
impl From<ToggleState> for bool {
fn from(value: ToggleState) -> Self {
value.is_toggled()
}
}

View file

@ -21,7 +21,6 @@ mod selectable;
mod slot; mod slot;
mod styled_ext; mod styled_ext;
mod styles; mod styles;
mod toggleable;
pub mod utils; pub mod utils;
pub use clickable::*; pub use clickable::*;
@ -33,4 +32,3 @@ pub use selectable::*;
pub use slot::*; pub use slot::*;
pub use styled_ext::*; pub use styled_ext::*;
pub use styles::*; pub use styles::*;
pub use toggleable::*;