Improve drag and drop to look and feel better

WIP: Change rendering of drag and drop based on alt-modifier
This commit is contained in:
Mikayla 2023-09-19 14:48:23 -07:00
parent 9bff3b6916
commit f3b91082a6
No known key found for this signature in database
5 changed files with 102 additions and 21 deletions

View file

@ -33,7 +33,7 @@ use gpui::{
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}, },
impl_actions, impl_actions,
platform::{CursorStyle, MouseButton, PromptLevel}, platform::{CursorStyle, ModifiersChangedEvent, MouseButton, PromptLevel},
serde_json, AnyElement, AppContext, AsyncAppContext, Element, Entity, FontCache, ModelHandle, serde_json, AnyElement, AppContext, AsyncAppContext, Element, Entity, FontCache, ModelHandle,
Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
}; };
@ -1669,7 +1669,7 @@ impl CollabPanel {
let mut is_dragged_over = false; let mut is_dragged_over = false;
if cx if cx
.global::<DragAndDrop<Workspace>>() .global::<DragAndDrop<Workspace>>()
.currently_dragged::<Channel>(cx.window()) .currently_dragged::<DraggedChannel>(cx.window())
.is_some() .is_some()
&& self && self
.dragged_channel_target .dragged_channel_target
@ -1771,7 +1771,9 @@ impl CollabPanel {
) )
}) })
.on_click(MouseButton::Left, move |_, this, cx| { .on_click(MouseButton::Left, move |_, this, cx| {
this.join_channel_chat(channel_id, cx); if this.dragged_channel_target.take().is_none() {
this.join_channel_chat(channel_id, cx);
}
}) })
.on_click(MouseButton::Right, { .on_click(MouseButton::Right, {
let path = path.clone(); let path = path.clone();
@ -1817,16 +1819,32 @@ impl CollabPanel {
.currently_dragged::<DraggedChannel>(cx.window()) .currently_dragged::<DraggedChannel>(cx.window())
.is_some() .is_some()
{ {
this.dragged_channel_target = Some((channel.clone(), path.clone())); if let Some(dragged_channel_target) = &this.dragged_channel_target {
if dragged_channel_target.0 != channel || dragged_channel_target.1 != path {
this.dragged_channel_target = Some((channel.clone(), path.clone()));
cx.notify();
}
} else {
this.dragged_channel_target = Some((channel.clone(), path.clone()));
cx.notify();
}
} }
} }
}) })
.as_draggable( .as_draggable(
(channel.clone(), path.parent_id()), (channel.clone(), path.parent_id()),
move |(channel, _), cx: &mut ViewContext<Workspace>| { move |e, (channel, _), cx: &mut ViewContext<Workspace>| {
let theme = &theme::current(cx).collab_panel; let theme = &theme::current(cx).collab_panel;
Flex::<Workspace>::row() Flex::<Workspace>::row()
.with_children(e.alt.then(|| {
Svg::new("icons/plus.svg")
.with_color(theme.channel_hash.color)
.constrained()
.with_width(theme.channel_hash.width)
.aligned()
.left()
}))
.with_child( .with_child(
Svg::new("icons/hash.svg") Svg::new("icons/hash.svg")
.with_color(theme.channel_hash.color) .with_color(theme.channel_hash.color)
@ -1840,11 +1858,17 @@ impl CollabPanel {
.contained() .contained()
.with_style(theme.channel_name.container) .with_style(theme.channel_name.container)
.aligned() .aligned()
.left() .left(),
.flex(1., true),
) )
.align_children_center() .align_children_center()
.contained() .contained()
.with_background_color(
theme
.container
.background_color
.unwrap_or(gpui::color::Color::transparent_black()),
)
.contained()
.with_padding_left( .with_padding_left(
theme.channel_row.default_style().padding.left theme.channel_row.default_style().padding.left
+ theme.channel_indent * depth as f32, + theme.channel_indent * depth as f32,
@ -2816,6 +2840,19 @@ impl View for CollabPanel {
self.has_focus = false; self.has_focus = false;
} }
fn modifiers_changed(&mut self, _: &ModifiersChangedEvent, cx: &mut ViewContext<Self>) -> bool {
if cx
.global::<DragAndDrop<Workspace>>()
.currently_dragged::<DraggedChannel>(cx.window())
.is_some()
{
cx.notify();
true
} else {
false
}
}
fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> { fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> {
let theme = &theme::current(cx).collab_panel; let theme = &theme::current(cx).collab_panel;

View file

@ -4,7 +4,7 @@ use collections::HashSet;
use gpui::{ use gpui::{
elements::{Empty, MouseEventHandler, Overlay}, elements::{Empty, MouseEventHandler, Overlay},
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
platform::{CursorStyle, MouseButton}, platform::{CursorStyle, Modifiers, MouseButton},
scene::{MouseDown, MouseDrag}, scene::{MouseDown, MouseDrag},
AnyElement, AnyWindowHandle, Element, View, ViewContext, WeakViewHandle, WindowContext, AnyElement, AnyWindowHandle, Element, View, ViewContext, WeakViewHandle, WindowContext,
}; };
@ -21,12 +21,13 @@ enum State<V> {
region: RectF, region: RectF,
}, },
Dragging { Dragging {
modifiers: Modifiers,
window: AnyWindowHandle, window: AnyWindowHandle,
position: Vector2F, position: Vector2F,
region_offset: Vector2F, region_offset: Vector2F,
region: RectF, region: RectF,
payload: Rc<dyn Any + 'static>, payload: Rc<dyn Any + 'static>,
render: Rc<dyn Fn(Rc<dyn Any>, &mut ViewContext<V>) -> AnyElement<V>>, render: Rc<dyn Fn(&Modifiers, Rc<dyn Any>, &mut ViewContext<V>) -> AnyElement<V>>,
}, },
Canceled, Canceled,
} }
@ -49,6 +50,7 @@ impl<V> Clone for State<V> {
region, region,
}, },
State::Dragging { State::Dragging {
modifiers,
window, window,
position, position,
region_offset, region_offset,
@ -62,6 +64,7 @@ impl<V> Clone for State<V> {
region: region.clone(), region: region.clone(),
payload: payload.clone(), payload: payload.clone(),
render: render.clone(), render: render.clone(),
modifiers: modifiers.clone(),
}, },
State::Canceled => State::Canceled, State::Canceled => State::Canceled,
} }
@ -111,6 +114,27 @@ impl<V: 'static> DragAndDrop<V> {
}) })
} }
pub fn any_currently_dragged(&self, window: AnyWindowHandle) -> bool {
self.currently_dragged
.as_ref()
.map(|state| {
if let State::Dragging {
window: window_dragged_from,
..
} = state
{
if &window != window_dragged_from {
return false;
}
true
} else {
false
}
})
.unwrap_or(false)
}
pub fn drag_started(event: MouseDown, cx: &mut WindowContext) { pub fn drag_started(event: MouseDown, cx: &mut WindowContext) {
cx.update_global(|this: &mut Self, _| { cx.update_global(|this: &mut Self, _| {
this.currently_dragged = Some(State::Down { this.currently_dragged = Some(State::Down {
@ -124,7 +148,7 @@ impl<V: 'static> DragAndDrop<V> {
event: MouseDrag, event: MouseDrag,
payload: Rc<T>, payload: Rc<T>,
cx: &mut WindowContext, cx: &mut WindowContext,
render: Rc<impl 'static + Fn(&T, &mut ViewContext<V>) -> AnyElement<V>>, render: Rc<impl 'static + Fn(&Modifiers, &T, &mut ViewContext<V>) -> AnyElement<V>>,
) { ) {
let window = cx.window(); let window = cx.window();
cx.update_global(|this: &mut Self, cx| { cx.update_global(|this: &mut Self, cx| {
@ -141,13 +165,14 @@ impl<V: 'static> DragAndDrop<V> {
}) => { }) => {
if (event.position - (region.origin() + region_offset)).length() > DEAD_ZONE { if (event.position - (region.origin() + region_offset)).length() > DEAD_ZONE {
this.currently_dragged = Some(State::Dragging { this.currently_dragged = Some(State::Dragging {
modifiers: event.modifiers,
window, window,
region_offset, region_offset,
region, region,
position: event.position, position: event.position,
payload, payload,
render: Rc::new(move |payload, cx| { render: Rc::new(move |modifiers, payload, cx| {
render(payload.downcast_ref::<T>().unwrap(), cx) render(modifiers, payload.downcast_ref::<T>().unwrap(), cx)
}), }),
}); });
} else { } else {
@ -163,13 +188,14 @@ impl<V: 'static> DragAndDrop<V> {
.. ..
}) => { }) => {
this.currently_dragged = Some(State::Dragging { this.currently_dragged = Some(State::Dragging {
modifiers: event.modifiers,
window, window,
region_offset, region_offset,
region, region,
position: event.position, position: event.position,
payload, payload,
render: Rc::new(move |payload, cx| { render: Rc::new(move |modifiers, payload, cx| {
render(payload.downcast_ref::<T>().unwrap(), cx) render(modifiers, payload.downcast_ref::<T>().unwrap(), cx)
}), }),
}); });
} }
@ -178,6 +204,19 @@ impl<V: 'static> DragAndDrop<V> {
}); });
} }
pub fn update_modifiers(new_modifiers: Modifiers, cx: &mut ViewContext<V>) -> bool {
cx.update_global(|this: &mut Self, _| match &mut this.currently_dragged {
Some(state) => match state {
State::Dragging { modifiers, .. } => {
*modifiers = new_modifiers;
true
}
_ => false,
},
None => false,
})
}
pub fn render(cx: &mut ViewContext<V>) -> Option<AnyElement<V>> { pub fn render(cx: &mut ViewContext<V>) -> Option<AnyElement<V>> {
enum DraggedElementHandler {} enum DraggedElementHandler {}
cx.global::<Self>() cx.global::<Self>()
@ -188,6 +227,7 @@ impl<V: 'static> DragAndDrop<V> {
State::Down { .. } => None, State::Down { .. } => None,
State::DeadZone { .. } => None, State::DeadZone { .. } => None,
State::Dragging { State::Dragging {
modifiers,
window, window,
region_offset, region_offset,
position, position,
@ -205,7 +245,7 @@ impl<V: 'static> DragAndDrop<V> {
MouseEventHandler::new::<DraggedElementHandler, _>( MouseEventHandler::new::<DraggedElementHandler, _>(
0, 0,
cx, cx,
|_, cx| render(payload, cx), |_, cx| render(&modifiers, payload, cx),
) )
.with_cursor_style(CursorStyle::Arrow) .with_cursor_style(CursorStyle::Arrow)
.on_up(MouseButton::Left, |_, _, cx| { .on_up(MouseButton::Left, |_, _, cx| {
@ -295,7 +335,7 @@ pub trait Draggable<V> {
fn as_draggable<D: View, P: Any>( fn as_draggable<D: View, P: Any>(
self, self,
payload: P, payload: P,
render: impl 'static + Fn(&P, &mut ViewContext<D>) -> AnyElement<D>, render: impl 'static + Fn(&Modifiers, &P, &mut ViewContext<D>) -> AnyElement<D>,
) -> Self ) -> Self
where where
Self: Sized; Self: Sized;
@ -305,7 +345,7 @@ impl<V: 'static> Draggable<V> for MouseEventHandler<V> {
fn as_draggable<D: View, P: Any>( fn as_draggable<D: View, P: Any>(
self, self,
payload: P, payload: P,
render: impl 'static + Fn(&P, &mut ViewContext<D>) -> AnyElement<D>, render: impl 'static + Fn(&Modifiers, &P, &mut ViewContext<D>) -> AnyElement<D>,
) -> Self ) -> Self
where where
Self: Sized, Self: Sized,

View file

@ -1485,7 +1485,7 @@ impl ProjectPanel {
.as_draggable(entry_id, { .as_draggable(entry_id, {
let row_container_style = theme.dragged_entry.container; let row_container_style = theme.dragged_entry.container;
move |_, cx: &mut ViewContext<Workspace>| { move |_, _, cx: &mut ViewContext<Workspace>| {
let theme = theme::current(cx).clone(); let theme = theme::current(cx).clone();
Self::render_entry_visual_element( Self::render_entry_visual_element(
&details, &details,

View file

@ -1383,7 +1383,7 @@ impl Pane {
let theme = theme::current(cx).clone(); let theme = theme::current(cx).clone();
let detail = detail.clone(); let detail = detail.clone();
move |dragged_item: &DraggedItem, cx: &mut ViewContext<Workspace>| { move |_, dragged_item: &DraggedItem, cx: &mut ViewContext<Workspace>| {
let tab_style = &theme.workspace.tab_bar.dragged_tab; let tab_style = &theme.workspace.tab_bar.dragged_tab;
Self::render_dragged_tab( Self::render_dragged_tab(
&dragged_item.handle, &dragged_item.handle,

View file

@ -33,8 +33,8 @@ use gpui::{
}, },
impl_actions, impl_actions,
platform::{ platform::{
CursorStyle, MouseButton, PathPromptOptions, Platform, PromptLevel, WindowBounds, CursorStyle, ModifiersChangedEvent, MouseButton, PathPromptOptions, Platform, PromptLevel,
WindowOptions, WindowBounds, WindowOptions,
}, },
AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity,
ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle, ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle,
@ -3807,6 +3807,10 @@ impl View for Workspace {
cx.focus(&self.active_pane); cx.focus(&self.active_pane);
} }
} }
fn modifiers_changed(&mut self, e: &ModifiersChangedEvent, cx: &mut ViewContext<Self>) -> bool {
DragAndDrop::<Workspace>::update_modifiers(e.modifiers, cx)
}
} }
impl ViewId { impl ViewId {