Context menu, Dragon Drop, for collab panel (#3441)
Release Notes: - N/A
This commit is contained in:
commit
fb377aed73
11 changed files with 490 additions and 478 deletions
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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::*;
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue