adding base for drag n drop

This commit is contained in:
Chris Cochrun 2025-02-18 16:43:39 -06:00
parent f79e61f2ed
commit 1ce365fc04
8 changed files with 139 additions and 49 deletions

View file

@ -1,6 +1,7 @@
use super::kinds::ServiceItemKind; use super::{kinds::ServiceItemKind, service_items::ServiceItem};
pub trait Content { pub trait Content {
fn title(&self) -> String; fn title(&self) -> String;
fn kind(&self) -> ServiceItemKind; fn kind(&self) -> ServiceItemKind;
fn to_service_item(&self) -> ServiceItem;
} }

View file

@ -30,6 +30,10 @@ impl Content for Image {
fn kind(&self) -> ServiceItemKind { fn kind(&self) -> ServiceItemKind {
ServiceItemKind::Image(self.clone()) ServiceItemKind::Image(self.clone())
} }
fn to_service_item(&self) -> super::service_items::ServiceItem {
self.into()
}
} }
impl From<Value> for Image { impl From<Value> for Image {

View file

@ -44,6 +44,10 @@ impl Content for Presentation {
fn kind(&self) -> ServiceItemKind { fn kind(&self) -> ServiceItemKind {
ServiceItemKind::Presentation(self.clone()) ServiceItemKind::Presentation(self.clone())
} }
fn to_service_item(&self) -> super::service_items::ServiceItem {
self.into()
}
} }
impl From<Value> for Presentation { impl From<Value> for Presentation {

View file

@ -1,7 +1,9 @@
use std::borrow::Cow;
use std::ops::Deref; use std::ops::Deref;
use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
use crisp::types::{Keyword, Symbol, Value}; use crisp::types::{Keyword, Symbol, Value};
use miette::Result; use miette::{miette, Result};
use tracing::{debug, error}; use tracing::{debug, error};
use crate::Slide; use crate::Slide;
@ -22,6 +24,48 @@ pub struct ServiceItem {
// pub item: Box<dyn ServiceTrait>, // pub item: Box<dyn ServiceTrait>,
} }
impl TryFrom<(Vec<u8>, String)> for ServiceItem {
type Error = miette::Error;
fn try_from(
value: (Vec<u8>, String),
) -> std::result::Result<Self, Self::Error> {
let sto = value.0.to_owned();
let song = Song {
title: "Death Was Arrested".to_string(),
..Default::default()
};
debug!(?value);
Ok(Self::from(&song))
}
}
impl AllowedMimeTypes for ServiceItem {
fn allowed() -> Cow<'static, [String]> {
Cow::from(vec!["application/service-item".to_string()])
}
}
impl AsMimeTypes for ServiceItem {
fn available(&self) -> std::borrow::Cow<'static, [String]> {
debug!(?self);
Cow::from(vec!["application/service-item".to_string()])
}
fn as_bytes(
&self,
mime_type: &str,
) -> Option<std::borrow::Cow<'static, [u8]>> {
todo!();
debug!(?self);
debug!(mime_type);
Some(Cow::from(
r#"(slide :background (image :source "~/pics/frodo.jpg" :fit fill)
(text "This is frodo" :font-size 70))"#.to_string().into_bytes()
))
}
}
impl ServiceItem { impl ServiceItem {
pub fn title(&self) -> String { pub fn title(&self) -> String {
self.title.clone() self.title.clone()

View file

@ -44,6 +44,10 @@ impl Content for Song {
fn kind(&self) -> ServiceItemKind { fn kind(&self) -> ServiceItemKind {
ServiceItemKind::Song(self.clone()) ServiceItemKind::Song(self.clone())
} }
fn to_service_item(&self) -> super::service_items::ServiceItem {
self.into()
}
} }
impl ServiceTrait for Song { impl ServiceTrait for Song {

View file

@ -35,6 +35,10 @@ impl Content for Video {
fn kind(&self) -> ServiceItemKind { fn kind(&self) -> ServiceItemKind {
ServiceItemKind::Video(self.clone()) ServiceItemKind::Video(self.clone())
} }
fn to_service_item(&self) -> super::service_items::ServiceItem {
self.into()
}
} }
impl From<Value> for Video { impl From<Value> for Video {

View file

@ -2,6 +2,7 @@ use clap::{command, Parser};
use core::service_items::{ServiceItem, ServiceItemModel}; use core::service_items::{ServiceItem, ServiceItemModel};
use cosmic::app::context_drawer::ContextDrawer; use cosmic::app::context_drawer::ContextDrawer;
use cosmic::app::{Core, Settings, Task}; use cosmic::app::{Core, Settings, Task};
use cosmic::iced::clipboard::dnd::DndAction;
use cosmic::iced::keyboard::{Key, Modifiers}; use cosmic::iced::keyboard::{Key, Modifiers};
use cosmic::iced::window::{Mode, Position}; use cosmic::iced::window::{Mode, Position};
use cosmic::iced::{self, event, window, Length, Padding, Point}; use cosmic::iced::{self, event, window, Length, Padding, Point};
@ -9,7 +10,9 @@ use cosmic::iced_futures::Subscription;
use cosmic::iced_widget::{column, row}; use cosmic::iced_widget::{column, row};
use cosmic::prelude::ElementExt; use cosmic::prelude::ElementExt;
use cosmic::prelude::*; use cosmic::prelude::*;
use cosmic::widget::dnd_destination::DragId;
use cosmic::widget::nav_bar::nav_bar_style; use cosmic::widget::nav_bar::nav_bar_style;
use cosmic::widget::segmented_button::Entity;
use cosmic::widget::tooltip::Position as TPosition; use cosmic::widget::tooltip::Position as TPosition;
use cosmic::widget::{ use cosmic::widget::{
button, nav_bar, text, tooltip, DndSource, Id, Space, button, nav_bar, text, tooltip, DndSource, Id, Space,
@ -100,8 +103,8 @@ enum Message {
Present(presenter::Message), Present(presenter::Message),
Library(library::Message), Library(library::Message),
File(PathBuf), File(PathBuf),
DndEnter(ServiceItem), DndEnter(Entity, Vec<String>),
DndDrop(ServiceItem), DndDrop(Entity, Option<ServiceItem>, DndAction),
OpenWindow, OpenWindow,
CloseWindow(Option<window::Id>), CloseWindow(Option<window::Id>),
WindowOpened(window::Id, Option<Point>), WindowOpened(window::Id, Option<Point>),
@ -110,6 +113,7 @@ enum Message {
Quit, Quit,
Key(Key, Modifiers), Key(Key, Modifiers),
None, None,
DndLeave(Entity),
} }
impl cosmic::Application for App { impl cosmic::Application for App {
@ -217,9 +221,21 @@ impl cosmic::Application for App {
cosmic::app::cosmic::Message::NavBar(id), cosmic::app::cosmic::Message::NavBar(id),
) )
}) })
// .on_dnd_drop(|entity, data, idk| { .on_dnd_drop(|entity, data, action| {
// cosmic::app::Message::Cosmic(Message::DndDrop(entity)) debug!(?entity);
// }) debug!(?data);
debug!(?action);
cosmic::app::Message::App(Message::DndDrop(
entity, data, action,
))
})
.on_dnd_enter(|entity, data| {
cosmic::app::Message::App(Message::DndEnter(entity, data))
})
.on_dnd_leave(|entity| {
cosmic::app::Message::App(Message::DndLeave(entity))
})
.drag_id(DragId::new())
.on_context(|id| { .on_context(|id| {
cosmic::app::Message::Cosmic( cosmic::app::Message::Cosmic(
cosmic::app::cosmic::Message::NavBarContext(id), cosmic::app::cosmic::Message::NavBarContext(id),
@ -253,7 +269,7 @@ impl cosmic::Application for App {
id: nav_bar::Id, id: nav_bar::Id,
) -> Task<Self::Message> { ) -> Task<Self::Message> {
self.nav_model.activate(id); self.nav_model.activate(id);
debug!(?id); // debug!(?id);
self.update_title() self.update_title()
} }
@ -503,13 +519,26 @@ impl cosmic::Application for App {
} }
} }
Message::Quit => cosmic::iced::exit(), Message::Quit => cosmic::iced::exit(),
Message::DndEnter(service_item) => todo!(), Message::DndEnter(entity, data) => {
Message::DndDrop(service_item) => todo!(), debug!(?entity);
debug!(?data);
Task::none()
}
Message::DndDrop(entity, service_item, action) => {
debug!(?entity);
debug!(?service_item);
debug!(?action);
Task::none()
}
Message::AddLibrary(library) => { Message::AddLibrary(library) => {
self.library = Some(library); self.library = Some(library);
Task::none() Task::none()
} }
Message::None => Task::none(), Message::None => Task::none(),
Message::DndLeave(entity) => {
// debug!(?entity);
Task::none()
}
} }
} }

View file

@ -3,7 +3,7 @@ use cosmic::{
iced_widget::{column, row as rowm, text as textm}, iced_widget::{column, row as rowm, text as textm},
widget::{ widget::{
container, horizontal_space, icon, mouse_area, responsive, container, horizontal_space, icon, mouse_area, responsive,
row, scrollable, text, Container, Space, row, scrollable, text, Container, DndSource, Space,
}, },
Element, Task, Element, Task,
}; };
@ -14,6 +14,7 @@ use crate::core::{
images::Image, images::Image,
model::{get_db, LibraryKind, Model}, model::{get_db, LibraryKind, Model},
presentations::Presentation, presentations::Presentation,
service_items::ServiceItem,
songs::Song, songs::Song,
videos::Video, videos::Video,
}; };
@ -204,54 +205,53 @@ impl Library {
.on_enter(Message::HoverLibrary(Some(model.kind))) .on_enter(Message::HoverLibrary(Some(model.kind)))
.on_exit(Message::HoverLibrary(None)); .on_exit(Message::HoverLibrary(None));
let mut dragged_item = None; let mut dragged_item = None;
let lib_container = if self.library_open == Some(model.kind) { let lib_container =
let items = scrollable( if self.library_open == Some(model.kind) {
column({ let items = scrollable(
model.items.iter().enumerate().map( column({
model.items.iter().enumerate().map(
|(index, item)| { |(index, item)| {
mouse_area( let service_item = item.to_service_item();
self.single_item(index, item, model), DndSource::<Message, ServiceItem>::new(
) mouse_area(
.on_drag({ self.single_item(
dragged_item =
Some(self.single_item(
index, item, model, index, item, model,
)); ),
Message::DragItem(Some(( )
.on_enter(Message::HoverItem(Some((
model.kind, model.kind,
index as i32, index as i32,
))) ))))
.on_exit(Message::HoverItem(None))
.on_press(Message::SelectItem(Some(
(model.kind, index as i32),
))),
)
.drag_content(move || {
service_item.to_owned()
}) })
.on_enter(Message::HoverItem(Some(( .on_start(Some(Message::DragItem(Some(
model.kind, (model.kind, index as i32),
index as i32,
))))
.on_exit(Message::HoverItem(None))
.on_press(Message::SelectItem(Some((
model.kind,
index as i32,
)))) ))))
.into() .into()
}, },
) )
}) })
.spacing(2) .spacing(2)
.width(Length::Fill), .width(Length::Fill),
); );
Container::new(items).padding(5).style(|t| { Container::new(items).padding(5).style(|t| {
container::Style::default() container::Style::default()
.background(Background::Color( .background(Background::Color(
t.cosmic().primary.base.into(), t.cosmic().primary.base.into(),
)) ))
.border( .border(Border::default().rounded(
Border::default().rounded(
t.cosmic().corner_radii.radius_m, t.cosmic().corner_radii.radius_m,
), ))
) })
}) } else {
} else { Container::new(Space::new(0, 0))
Container::new(Space::new(0, 0)) };
};
(column![button, lib_container].into(), dragged_item) (column![button, lib_container].into(), dragged_item)
} }