From 01b764de0b75e7f9ed93cbca4ecee242324b697a Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Mon, 16 Sep 2024 09:15:29 -0700 Subject: [PATCH] File drop events --- CHANGELOG.md | 2 ++ src/window.rs | 97 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 88 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8da87a6cf..32d7b2cbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -159,6 +159,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 initialized. - `Window::on_init` is a new callback that is invoked before the winit window is initialized. +- `Window::on_file_drop` is a new callback that is invoked when file drop events + occur for the window. [139]: https://github.com/khonsulabs/cushy/issues/139 diff --git a/src/window.rs b/src/window.rs index 3977b4122..874b8176f 100644 --- a/src/window.rs +++ b/src/window.rs @@ -9,7 +9,7 @@ use std::io; use std::marker::PhantomData; use std::num::{NonZeroU32, TryFromIntError}; use std::ops::{Deref, DerefMut, Not}; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::string::ToString; use std::sync::{mpsc, Arc, OnceLock}; use std::time::{Duration, Instant}; @@ -61,8 +61,8 @@ use crate::value::{ Destination, Dynamic, DynamicReader, IntoDynamic, IntoValue, Source, Tracked, Value, }; use crate::widget::{ - EventHandling, MakeWidget, MountedWidget, OnceCallback, RootBehavior, SharedCallback, WidgetId, - WidgetInstance, HANDLED, IGNORED, + Callback, EventHandling, MakeWidget, MountedWidget, OnceCallback, RootBehavior, SharedCallback, + WidgetId, WidgetInstance, HANDLED, IGNORED, }; use crate::widgets::shortcuts::{ShortcutKey, ShortcutMap}; use crate::window::sealed::WindowCommand; @@ -565,6 +565,7 @@ where enabled_buttons: Option>, fullscreen: Option>>, shortcuts: Value, + on_file_drop: Option>, } impl Default for Window @@ -662,6 +663,7 @@ where fullscreen: None, shortcuts: Value::default(), on_init: None, + on_file_drop: None, } } @@ -1015,6 +1017,15 @@ where self } + /// Invokes `on_file_drop` when a file is hovered or dropped on this window. + pub fn on_file_drop(mut self, on_file_drop: Function) -> Self + where + Function: FnMut(FileDrop) + Send + 'static, + { + self.on_file_drop = Some(Callback::new(on_file_drop)); + self + } + /// Sets the window's title. pub fn titled(mut self, title: impl IntoValue) -> Self { self.title = title.into_value(); @@ -1159,6 +1170,7 @@ where .unwrap_or(Value::Constant(WindowButtons::all())), fullscreen: this.fullscreen.unwrap_or_default(), shortcuts: this.shortcuts, + on_file_drop: this.on_file_drop, }), pending: this.pending, }, @@ -1233,6 +1245,35 @@ where } } +/// A file drop event for a window. +pub struct FileDrop { + /// The handle to the window the file drop event is for. + pub window: WindowHandle, + /// The file drop event. + pub drop: DropEvent, +} + +/// A drop event. +pub enum DropEvent { + /// The payload is being hovered over the container. + Hover(T), + /// The payload has been dropped on the container. + Dropped(T), + /// The payload previously hovered has been cancelled. + Cancelled, +} + +impl DropEvent { + /// Returns the payload of this event. + #[must_use] + pub fn payload(&self) -> Option<&T> { + match self { + Self::Hover(t) | Self::Dropped(t) => Some(t), + Self::Cancelled => None, + } + } +} + /// The behavior of a Cushy window. pub trait WindowBehavior: Sized + 'static { /// The type that is provided when initializing this window. @@ -1328,6 +1369,7 @@ struct OpenWindow { fullscreen: Tracked>>, modifiers: Dynamic, shortcuts: Value, + on_file_drop: Option>, } impl OpenWindow @@ -1768,6 +1810,7 @@ where enabled_buttons: Tracked::from(settings.enabled_buttons).ignoring_first(), fullscreen: Tracked::from(settings.fullscreen).ignoring_first(), shortcuts: settings.shortcuts, + on_file_drop: settings.on_file_drop, }; this.synchronize_platform_window(&mut window); @@ -2416,6 +2459,19 @@ where } } } + + fn handle_drop( + &mut self, + drop: DropEvent, + window: &kludgine::app::Window<'_, WindowCommand>, + ) { + if let Some(on_file_drop) = &mut self.on_file_drop { + on_file_drop.invoke(FileDrop { + window: WindowHandle::new(window.handle(), self.redraw_status.clone()), + drop, + }); + } + } } #[derive(Clone, Copy, Eq, PartialEq, Debug)] @@ -2634,13 +2690,31 @@ where self.moved(window.inner_position(), window.outer_position()); } - // fn theme_changed(&mut self, window: kludgine::app::Window<'_, ()>) {} - - // fn dropped_file(&mut self, window: kludgine::app::Window<'_, ()>, path: std::path::PathBuf) {} + fn dropped_file( + &mut self, + window: kludgine::app::Window<'_, WindowCommand>, + _kludgine: &mut Kludgine, + path: PathBuf, + ) { + self.handle_drop(DropEvent::Dropped(path), &window); + } - // fn hovered_file(&mut self, window: kludgine::app::Window<'_, ()>, path: std::path::PathBuf) {} + fn hovered_file( + &mut self, + window: kludgine::app::Window<'_, WindowCommand>, + _kludgine: &mut Kludgine, + path: PathBuf, + ) { + self.handle_drop(DropEvent::Hover(path), &window); + } - // fn hovered_file_cancelled(&mut self, window: kludgine::app::Window<'_, ()>) {} + fn hovered_file_cancelled( + &mut self, + window: kludgine::app::Window<'_, WindowCommand>, + _kludgine: &mut Kludgine, + ) { + self.handle_drop(DropEvent::Cancelled, &window); + } // fn received_character(&mut self, window: kludgine::app::Window<'_, ()>, char: char) {} @@ -2886,16 +2960,15 @@ pub(crate) mod sealed { use kludgine::app::winit::window::{Fullscreen, UserAttentionType, WindowButtons, WindowLevel}; use kludgine::Color; - use super::{PendingWindow, WindowHandle}; use crate::app::Cushy; use crate::context::sealed::InvalidationStatus; use crate::context::EventContext; use crate::fonts::FontCollection; use crate::styles::{FontFamilyList, ThemePair}; use crate::value::{Dynamic, Value}; - use crate::widget::{OnceCallback, SharedCallback}; + use crate::widget::{Callback, OnceCallback, SharedCallback}; use crate::widgets::shortcuts::ShortcutMap; - use crate::window::{ThemeMode, WindowAttributes}; + use crate::window::{FileDrop, PendingWindow, ThemeMode, WindowAttributes, WindowHandle}; pub struct Context { pub user: C, @@ -2947,6 +3020,7 @@ pub(crate) mod sealed { pub enabled_buttons: Value, pub fullscreen: Value>, pub shortcuts: Value, + pub on_file_drop: Option>, } pub struct WindowExecute(Box); @@ -3629,6 +3703,7 @@ impl StandaloneWindowBuilder { fullscreen: Value::default(), shortcuts: Value::default(), on_init: None, + on_file_drop: None, }, );