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:
parent
9bff3b6916
commit
f3b91082a6
5 changed files with 102 additions and 21 deletions
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue