use std::any::{self, Any};
use std::{
marker::PhantomData,
sync::{Arc, Mutex},
};
use crate::{WidgetId, WindowId};
pub(crate) type SelectorSymbol = &'static str;
#[derive(Debug, PartialEq, Eq)]
pub struct Selector<T = ()>(SelectorSymbol, PhantomData<*const T>);
#[derive(Debug, Clone)]
pub struct Command {
symbol: SelectorSymbol,
payload: Arc<dyn Any>,
}
pub struct SingleUse<T>(Mutex<Option<T>>);
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Target {
Global,
Window(WindowId),
Widget(WidgetId),
}
pub mod sys {
use std::any::Any;
use super::Selector;
use crate::{FileDialogOptions, FileInfo, SingleUse};
pub const QUIT_APP: Selector = Selector::new("druid-builtin.quit-app");
pub const HIDE_APPLICATION: Selector = Selector::new("druid-builtin.menu-hide-application");
pub const HIDE_OTHERS: Selector = Selector::new("druid-builtin.menu-hide-others");
pub(crate) const NEW_WINDOW: Selector<SingleUse<Box<dyn Any>>> =
Selector::new("druid-builtin.new-window");
pub const CLOSE_WINDOW: Selector = Selector::new("druid-builtin.close-window");
pub const CLOSE_ALL_WINDOWS: Selector = Selector::new("druid-builtin.close-all-windows");
pub const SHOW_WINDOW: Selector = Selector::new("druid-builtin.show-window");
pub(crate) const SHOW_CONTEXT_MENU: Selector<Box<dyn Any>> =
Selector::new("druid-builtin.show-context-menu");
pub(crate) const SET_MENU: Selector<Box<dyn Any>> = Selector::new("druid-builtin.set-menu");
pub const SHOW_PREFERENCES: Selector = Selector::new("druid-builtin.menu-show-preferences");
pub const SHOW_ABOUT: Selector = Selector::new("druid-builtin.menu-show-about");
pub const SHOW_ALL: Selector = Selector::new("druid-builtin.menu-show-all");
pub const NEW_FILE: Selector = Selector::new("druid-builtin.menu-file-new");
pub const SHOW_OPEN_PANEL: Selector<FileDialogOptions> =
Selector::new("druid-builtin.menu-file-open");
pub const OPEN_FILE: Selector<FileInfo> = Selector::new("druid-builtin.open-file-path");
pub const SHOW_SAVE_PANEL: Selector<FileDialogOptions> =
Selector::new("druid-builtin.menu-file-save-as");
pub const SAVE_FILE: Selector<Option<FileInfo>> = Selector::new("druid-builtin.menu-file-save");
pub const PRINT_SETUP: Selector = Selector::new("druid-builtin.menu-file-print-setup");
pub const PRINT: Selector = Selector::new("druid-builtin.menu-file-print");
pub const PRINT_PREVIEW: Selector = Selector::new("druid-builtin.menu-file-print");
pub const CUT: Selector = Selector::new("druid-builtin.menu-cut");
pub const COPY: Selector = Selector::new("druid-builtin.menu-copy");
pub const PASTE: Selector = Selector::new("druid-builtin.menu-paste");
pub const UNDO: Selector = Selector::new("druid-builtin.menu-undo");
pub const REDO: Selector = Selector::new("druid-builtin.menu-redo");
}
impl Selector<()> {
pub const NOOP: Selector = Selector::new("");
}
impl<T> Selector<T> {
pub const fn new(s: &'static str) -> Selector<T> {
Selector(s, PhantomData)
}
pub(crate) const fn symbol(self) -> SelectorSymbol {
self.0
}
}
impl<T: Any> Selector<T> {
pub fn with(self, payload: T) -> Command {
Command::new(self, payload)
}
}
impl Command {
pub fn new<T: Any>(selector: Selector<T>, payload: T) -> Self {
Command {
symbol: selector.symbol(),
payload: Arc::new(payload),
}
}
pub(crate) fn from_ext(symbol: SelectorSymbol, payload: Box<dyn Any>) -> Self {
Command {
symbol,
payload: payload.into(),
}
}
pub fn is<T>(&self, selector: Selector<T>) -> bool {
self.symbol == selector.symbol()
}
pub fn get<T: Any>(&self, selector: Selector<T>) -> Option<&T> {
if self.symbol == selector.symbol() {
Some(self.payload.downcast_ref().unwrap_or_else(|| {
panic!(
"The selector \"{}\" exists twice with different types. See druid::Command::get for more information",
selector.symbol()
)
}))
} else {
None
}
}
pub fn get_unchecked<T: Any>(&self, selector: Selector<T>) -> &T {
self.get(selector).unwrap_or_else(|| {
panic!(
"Expected selector \"{}\" but the command was \"{}\".",
selector.symbol(),
self.symbol
)
})
}
}
impl<T: Any> SingleUse<T> {
pub fn new(data: T) -> Self {
SingleUse(Mutex::new(Some(data)))
}
pub fn take(&self) -> Option<T> {
self.0.lock().unwrap().take()
}
}
impl From<Selector> for Command {
fn from(selector: Selector) -> Command {
Command {
symbol: selector.symbol(),
payload: Arc::new(()),
}
}
}
impl<T> std::fmt::Display for Selector<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Selector(\"{}\", {})", self.0, any::type_name::<T>())
}
}
impl<T> Copy for Selector<T> {}
impl<T> Clone for Selector<T> {
fn clone(&self) -> Self {
*self
}
}
impl From<WindowId> for Target {
fn from(id: WindowId) -> Target {
Target::Window(id)
}
}
impl From<WidgetId> for Target {
fn from(id: WidgetId) -> Target {
Target::Widget(id)
}
}
impl Into<Option<Target>> for WindowId {
fn into(self) -> Option<Target> {
Some(Target::Window(self))
}
}
impl Into<Option<Target>> for WidgetId {
fn into(self) -> Option<Target> {
Some(Target::Widget(self))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get_payload() {
let sel = Selector::new("my-selector");
let payload = vec![0, 1, 2];
let command = Command::new(sel, payload);
assert_eq!(command.get(sel), Some(&vec![0, 1, 2]));
}
}