Skip to content

Commit 96cc393

Browse files
committed
feat: drag source
1 parent 020e76a commit 96cc393

File tree

2 files changed

+90
-24
lines changed

2 files changed

+90
-24
lines changed

cosmic-app-list/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ edition = "2021"
77
[dependencies]
88
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit" }
99
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", default-features = false, features = ["client"] }
10-
libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["wayland", "applet", "tokio"] }
10+
# libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["wayland", "applet", "tokio"] }
11+
libcosmic = { path = "../../libcosmic", default-features = false, features = ["wayland", "applet", "tokio"] }
1112
ron = "0.8"
1213
futures = "0.3"
1314
futures-util = "0.3"
@@ -27,3 +28,4 @@ freedesktop-icons = "0.2.2"
2728
i18n-embed = { version = "0.13", features = ["fluent-system", "desktop-requester"] }
2829
i18n-embed-fl = "0.6"
2930
rust-embed = "6.3"
31+
url = "2.3.1"

cosmic-app-list/src/app.rs

Lines changed: 87 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,25 @@ use crate::toplevel_subscription::ToplevelRequest;
1010
use crate::toplevel_subscription::ToplevelUpdate;
1111
use calloop::channel::Sender;
1212
use cctk::toplevel_info::ToplevelInfo;
13+
use cctk::wayland_client::protocol::wl_data_device_manager::DndAction;
1314
use cctk::wayland_client::protocol::wl_seat::WlSeat;
1415
use cosmic::applet::cosmic_panel_config::PanelAnchor;
1516
use cosmic::applet::CosmicAppletHelper;
1617
use cosmic::iced;
18+
use cosmic::iced::wayland::actions::data_device::DataFromMimeType;
19+
use cosmic::iced::wayland::actions::data_device::DndIcon;
1720
use cosmic::iced::wayland::actions::window::SctkWindowSettings;
1821
use cosmic::iced::wayland::popup::destroy_popup;
1922
use cosmic::iced::wayland::popup::get_popup;
23+
use cosmic::iced::widget::dnd_source;
2024
use cosmic::iced::widget::mouse_listener;
2125
use cosmic::iced::widget::{column, row};
2226
use cosmic::iced::Settings;
2327
use cosmic::iced::{window, Application, Command, Subscription};
2428
use cosmic::iced_native::alignment::Horizontal;
2529
use cosmic::iced_native::subscription::events_with;
2630
use cosmic::iced_native::widget::vertical_space;
31+
use cosmic::iced_sctk::commands::data_device::start_drag;
2732
use cosmic::iced_sctk::layout::Limits;
2833
use cosmic::iced_sctk::settings::InitialSurface;
2934
use cosmic::iced_sctk::widget::vertical_rule;
@@ -42,6 +47,9 @@ use iced::Alignment;
4247
use iced::Background;
4348
use iced::Length;
4449
use itertools::Itertools;
50+
use url::Url;
51+
52+
static MIME_TYPE: &str = "text/uri-list";
4553

4654
pub fn run() -> cosmic::iced::Result {
4755
let helper = CosmicAppletHelper::default();
@@ -79,6 +87,22 @@ struct DockItem {
7987
desktop_info: DesktopInfo,
8088
}
8189

90+
impl DataFromMimeType for DockItem {
91+
fn from_mime_type(&self, mime_type: &str) -> Option<Vec<u8>> {
92+
if mime_type == MIME_TYPE {
93+
Some(
94+
Url::from_file_path(self.desktop_info.path.clone())
95+
.ok()?
96+
.to_string()
97+
.as_bytes()
98+
.to_vec(),
99+
)
100+
} else {
101+
None
102+
}
103+
}
104+
}
105+
82106
impl DockItem {
83107
fn new(
84108
id: usize,
@@ -92,7 +116,12 @@ impl DockItem {
92116
}
93117
}
94118

95-
fn as_icon(&self, applet_helper: &CosmicAppletHelper, rectangle_tracker: Option<&RectangleTracker<usize>>, has_popup: bool) -> Element<'_, Message> {
119+
fn as_icon(
120+
&self,
121+
applet_helper: &CosmicAppletHelper,
122+
rectangle_tracker: Option<&RectangleTracker<usize>>,
123+
has_popup: bool,
124+
) -> Element<'_, Message> {
96125
let DockItem {
97126
toplevels,
98127
desktop_info,
@@ -143,6 +172,7 @@ impl DockItem {
143172
.spacing(4)
144173
.into(),
145174
};
175+
146176
let mut icon_button = cosmic::widget::button(Button::Text)
147177
.custom(vec![icon_wrapper])
148178
.padding(8);
@@ -156,8 +186,13 @@ impl DockItem {
156186
}
157187

158188
// TODO tooltip on hover
159-
let icon_button = mouse_listener(icon_button.width(Length::Shrink).height(Length::Shrink))
160-
.on_right_release(Message::Popup(desktop_info.id.clone()));
189+
let icon_button = dnd_source(
190+
mouse_listener(icon_button.width(Length::Shrink).height(Length::Shrink))
191+
.on_right_release(Message::Popup(desktop_info.id.clone())),
192+
)
193+
.on_drag(Message::StartDrag(*id))
194+
.on_cancelled(Message::DragFinished)
195+
.on_finished(Message::DragFinished);
161196
if let Some(tracker) = rectangle_tracker {
162197
tracker.container(*id, icon_button).into()
163198
} else {
@@ -174,6 +209,7 @@ struct CosmicAppList {
174209
subscription_ctr: u32,
175210
active_list: Vec<DockItem>,
176211
favorite_list: Vec<DockItem>,
212+
dnd_source: Option<(window::Id, DockItem, DndAction)>,
177213
dnd_preview: Option<(bool, DockItem)>, // TODO allow non-toplevels to be dragged
178214
config: AppListConfig,
179215
toplevel_sender: Option<Sender<ToplevelRequest>>,
@@ -198,6 +234,8 @@ enum Message {
198234
NewSeat(WlSeat),
199235
RemovedSeat(WlSeat),
200236
Rectangle(RectangleUpdate<usize>),
237+
StartDrag(usize), // id of the DockItem
238+
DragFinished
201239
}
202240

203241
#[derive(Debug, Clone, Default)]
@@ -206,6 +244,7 @@ struct DesktopInfo {
206244
icon: PathBuf,
207245
exec: String,
208246
name: String,
247+
path: PathBuf,
209248
}
210249

211250
fn desktop_info_for_app_ids(mut app_ids: Vec<String>) -> Vec<DesktopInfo> {
@@ -226,6 +265,7 @@ fn desktop_info_for_app_ids(mut app_ids: Vec<String>) -> Vec<DesktopInfo> {
226265
icon: buf,
227266
exec: de.exec().unwrap_or_default().to_string(),
228267
name: de.name(None).unwrap_or_default().to_string(),
268+
path: path.clone(),
229269
})
230270
} else {
231271
None
@@ -373,6 +413,28 @@ impl Application for CosmicAppList {
373413
}
374414
}
375415
}
416+
Message::StartDrag(id) => {
417+
if let Some(toplevel_group) = self
418+
.active_list
419+
.iter()
420+
.chain(self.favorite_list.iter())
421+
.find(|t| t.id == id)
422+
{
423+
self.surface_id_ctr += 1;
424+
let icon_id = window::Id::new(self.surface_id_ctr);
425+
self.dnd_source = Some((icon_id, toplevel_group.clone(), DndAction::empty()));
426+
return start_drag(
427+
vec![MIME_TYPE.to_string()],
428+
DndAction::all(),
429+
window::Id::new(0),
430+
Some(DndIcon::Custom(icon_id)),
431+
Box::new(toplevel_group.clone()),
432+
);
433+
}
434+
}
435+
Message::DragFinished => {
436+
self.dnd_source = None;
437+
}
376438
Message::Toplevel(event) => {
377439
match event {
378440
ToplevelUpdate::AddToplevel(handle, info) => {
@@ -477,8 +539,15 @@ impl Application for CosmicAppList {
477539
}
478540

479541
fn view(&self, id: window::Id) -> Element<Message> {
542+
if let Some((_, item, _)) = self.dnd_source.as_ref().filter(|s| s.0 == id) {
543+
return cosmic::widget::icon(
544+
Path::new(&item.desktop_info.icon),
545+
self.applet_helper.suggested_size().0,
546+
)
547+
.into();
548+
}
480549
if let Some((
481-
popup_id,
550+
_popup_id,
482551
DockItem {
483552
toplevels,
484553
desktop_info,
@@ -539,35 +608,30 @@ impl Application for CosmicAppList {
539608
.on_press(Message::Quit(desktop_info.id.clone())),
540609
),
541610
};
542-
// return Container::new(Container::new(content.width(Length::Shrink).height(Length::Shrink)).style(
543-
// cosmic::Container::Custom(|theme| container::Appearance {
544-
// text_color: Some(theme.cosmic().on_bg_color().into()),
545-
// background: Some(theme.extended_palette().background.base.color.into()),
546-
// border_radius: 12.0,
547-
// border_width: 0.0,
548-
// border_color: Color::TRANSPARENT,
549-
// }),
550-
// )).into();
551611
return self.applet_helper.popup_container(content).into();
552612
}
553613

554614
let favorites = self
555615
.favorite_list
556616
.iter()
557-
.map(
558-
|dock_item| {
559-
dock_item.as_icon(&self.applet_helper, self.rectangle_tracker.as_ref(), self.popup.is_some())
560-
},
561-
)
617+
.map(|dock_item| {
618+
dock_item.as_icon(
619+
&self.applet_helper,
620+
self.rectangle_tracker.as_ref(),
621+
self.popup.is_some(),
622+
)
623+
})
562624
.collect();
563625
let active = self
564626
.active_list
565627
.iter()
566-
.map(
567-
|dock_item| {
568-
dock_item.as_icon(&self.applet_helper, self.rectangle_tracker.as_ref(), self.popup.is_some())
569-
},
570-
)
628+
.map(|dock_item| {
629+
dock_item.as_icon(
630+
&self.applet_helper,
631+
self.rectangle_tracker.as_ref(),
632+
self.popup.is_some(),
633+
)
634+
})
571635
.collect();
572636

573637
let (w, h) = match self.applet_helper.anchor {

0 commit comments

Comments
 (0)