From 9bff3b6916c274672c783508abf30f2163be0866 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Tue, 19 Sep 2023 11:20:01 -0700 Subject: [PATCH] Add basic drag and drop support --- Cargo.lock | 1 + crates/channel/src/channel.rs | 2 +- crates/channel/src/channel_store.rs | 4 +- crates/collab_ui/Cargo.toml | 1 + crates/collab_ui/src/collab_panel.rs | 107 ++++++++++++++++++++++++++- 5 files changed, 108 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e927ae5bf97162c52ed3c41060411c5242a4f1fc..de0a6c1e92e865ca67973e1f8c34620f7af7d906 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1537,6 +1537,7 @@ dependencies = [ "collections", "context_menu", "db", + "drag_and_drop", "editor", "feature_flags", "feedback", diff --git a/crates/channel/src/channel.rs b/crates/channel/src/channel.rs index d3e2f8956435727502f83f00517fdb29ac61eb45..724ff75d60f01d75371b1c1286a4e46cca1b298a 100644 --- a/crates/channel/src/channel.rs +++ b/crates/channel/src/channel.rs @@ -5,7 +5,7 @@ mod channel_store; pub use channel_buffer::{ChannelBuffer, ChannelBufferEvent}; pub use channel_chat::{ChannelChat, ChannelChatEvent, ChannelMessage, ChannelMessageId}; pub use channel_store::{ - Channel, ChannelEvent, ChannelId, ChannelMembership, ChannelPath, ChannelStore, + Channel, ChannelData, ChannelEvent, ChannelId, ChannelMembership, ChannelPath, ChannelStore, }; use client::Client; diff --git a/crates/channel/src/channel_store.rs b/crates/channel/src/channel_store.rs index 02eddb9900f9e56efa49475a212303d1ef06c7a7..5e2e10516538003c4e072e653848aac778d6e5f9 100644 --- a/crates/channel/src/channel_store.rs +++ b/crates/channel/src/channel_store.rs @@ -47,6 +47,8 @@ pub struct ChannelStore { _update_channels: Task<()>, } +pub type ChannelData = (Channel, ChannelPath); + #[derive(Clone, Debug, PartialEq)] pub struct Channel { pub id: ChannelId, @@ -758,8 +760,6 @@ impl ChannelStore { payload: proto::UpdateChannels, cx: &mut ModelContext, ) -> Option>> { - dbg!(self.client.user_id(), &payload); - if !payload.remove_channel_invitations.is_empty() { self.channel_invitations .retain(|channel| !payload.remove_channel_invitations.contains(&channel.id)); diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index 0a52b9a19fe9df7c070e33de1d8c719b0dd8dae2..b6e45471f14a0691f2188d3f0c5f20d003128b86 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -30,6 +30,7 @@ channel = { path = "../channel" } clock = { path = "../clock" } collections = { path = "../collections" } context_menu = { path = "../context_menu" } +drag_and_drop = { path = "../drag_and_drop" } editor = { path = "../editor" } feedback = { path = "../feedback" } fuzzy = { path = "../fuzzy" } diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 70137407a10945d86672ebbae9db808392d6df10..b4920175ca4cf356c256ed34744c449d3be06ca0 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -9,12 +9,13 @@ use crate::{ }; use anyhow::Result; use call::ActiveCall; -use channel::{Channel, ChannelEvent, ChannelId, ChannelPath, ChannelStore}; +use channel::{Channel, ChannelData, ChannelEvent, ChannelId, ChannelPath, ChannelStore}; use channel_modal::ChannelModal; use client::{proto::PeerId, Client, Contact, User, UserStore}; use contact_finder::ContactFinder; use context_menu::{ContextMenu, ContextMenuItem}; use db::kvp::KEY_VALUE_STORE; +use drag_and_drop::{DragAndDrop, Draggable}; use editor::{Cancel, Editor}; use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt}; use futures::StreamExt; @@ -116,6 +117,8 @@ struct UnlinkChannel { parent_id: ChannelId, } +type DraggedChannel = (Channel, Option); + actions!( collab_panel, [ @@ -260,6 +263,7 @@ pub struct CollabPanel { subscriptions: Vec, collapsed_sections: Vec
, collapsed_channels: Vec, + dragged_channel_target: Option, workspace: WeakViewHandle, context_menu_on_selected: bool, } @@ -525,6 +529,7 @@ impl CollabPanel { workspace: workspace.weak_handle(), client: workspace.app_state().client.clone(), context_menu_on_selected: true, + dragged_channel_target: None, list_state, }; @@ -1661,6 +1666,20 @@ impl CollabPanel { enum ChannelCall {} + let mut is_dragged_over = false; + if cx + .global::>() + .currently_dragged::(cx.window()) + .is_some() + && self + .dragged_channel_target + .as_ref() + .filter(|(_, dragged_path)| path.starts_with(dragged_path)) + .is_some() + { + is_dragged_over = true; + } + MouseEventHandler::new::(path.unique_id() as usize, cx, |state, cx| { let row_hovered = state.hovered(); @@ -1741,7 +1760,11 @@ impl CollabPanel { .constrained() .with_height(theme.row_height) .contained() - .with_style(*theme.channel_row.style_for(is_selected || is_active, state)) + .with_style( + *theme + .channel_row + .style_for(is_selected || is_active || is_dragged_over, state), + ) .with_padding_left( theme.channel_row.default_style().padding.left + theme.channel_indent * depth as f32, @@ -1750,9 +1773,85 @@ impl CollabPanel { .on_click(MouseButton::Left, move |_, this, cx| { this.join_channel_chat(channel_id, cx); }) - .on_click(MouseButton::Right, move |e, this, cx| { - this.deploy_channel_context_menu(Some(e.position), &path, cx); + .on_click(MouseButton::Right, { + let path = path.clone(); + move |e, this, cx| { + this.deploy_channel_context_menu(Some(e.position), &path, cx); + } + }) + .on_up(MouseButton::Left, move |e, this, cx| { + if let Some((_, dragged_channel)) = cx + .global::>() + .currently_dragged::(cx.window()) + { + if e.modifiers.alt { + this.channel_store.update(cx, |channel_store, cx| { + channel_store + .link_channel(dragged_channel.0.id, channel_id, cx) + .detach_and_log_err(cx) + }) + } else { + this.channel_store.update(cx, |channel_store, cx| { + match dragged_channel.1 { + Some(parent_id) => channel_store.move_channel( + dragged_channel.0.id, + parent_id, + channel_id, + cx, + ), + None => { + channel_store.link_channel(dragged_channel.0.id, channel_id, cx) + } + } + .detach_and_log_err(cx) + }) + } + } + }) + .on_move({ + let channel = channel.clone(); + let path = path.clone(); + move |_, this, cx| { + if cx + .global::>() + .currently_dragged::(cx.window()) + .is_some() + { + this.dragged_channel_target = Some((channel.clone(), path.clone())); + } + } }) + .as_draggable( + (channel.clone(), path.parent_id()), + move |(channel, _), cx: &mut ViewContext| { + let theme = &theme::current(cx).collab_panel; + + Flex::::row() + .with_child( + Svg::new("icons/hash.svg") + .with_color(theme.channel_hash.color) + .constrained() + .with_width(theme.channel_hash.width) + .aligned() + .left(), + ) + .with_child( + Label::new(channel.name.clone(), theme.channel_name.text.clone()) + .contained() + .with_style(theme.channel_name.container) + .aligned() + .left() + .flex(1., true), + ) + .align_children_center() + .contained() + .with_padding_left( + theme.channel_row.default_style().padding.left + + theme.channel_indent * depth as f32, + ) + .into_any() + }, + ) .with_cursor_style(CursorStyle::PointingHand) .into_any() }