From ec7a085a91fde334841231290578ae79af811956 Mon Sep 17 00:00:00 2001 From: Access Date: Fri, 6 Dec 2024 18:57:52 +0900 Subject: [PATCH] feat: support record mainwindow (#101) * feat: support record mainwindow * feat: change the macro name * fix: unit test * fix: remove shell maybe cause panic * chore: add some documents * fix(doc): the document cannot be rendered --- .../counter_mulit_pattern/src/main.rs | 10 +- iced_examples/counter_muti/src/main.rs | 4 +- iced_layershell/src/actions.rs | 9 + iced_layershell/src/build_pattern.rs | 8 + .../src/build_pattern/application.md | 160 ++++++++++ iced_layershell/src/build_pattern/daemon.md | 283 ++++++++++++++++++ iced_layershell/src/build_pattern/daemon.rs | 8 +- iced_layershell/src/lib.rs | 6 +- iced_layershell/src/multi_window.rs | 55 ++-- iced_layershell/tests/test_macro.rs | 12 +- iced_layershell_macros/src/lib.rs | 42 ++- 11 files changed, 565 insertions(+), 32 deletions(-) create mode 100644 iced_layershell/src/build_pattern/application.md create mode 100644 iced_layershell/src/build_pattern/daemon.md diff --git a/iced_examples/counter_mulit_pattern/src/main.rs b/iced_examples/counter_mulit_pattern/src/main.rs index ad1b756..4b8249e 100644 --- a/iced_examples/counter_mulit_pattern/src/main.rs +++ b/iced_examples/counter_mulit_pattern/src/main.rs @@ -10,7 +10,7 @@ use iced_runtime::{task, Action}; use iced_layershell::build_pattern::{daemon, MainSettings}; use iced_layershell::reexport::{Anchor, KeyboardInteractivity, Layer, NewLayerShellSettings}; use iced_layershell::settings::{LayerShellSettings, StartMode}; -use iced_layershell::{to_layer_message, LayerSingleton}; +use iced_layershell::{to_layer_message, WindowInfoMarker}; pub fn main() -> Result<(), iced_layershell::Error> { tracing_subscriber::fmt().init(); @@ -43,13 +43,15 @@ struct Counter { ids: HashMap, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, LayerSingleton)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, WindowInfoMarker)] enum WindowInfo { #[singleton] Left, #[singleton] Right, PopUp, + #[main] + Main, } #[derive(Debug, Clone, Copy)] @@ -101,6 +103,10 @@ impl Counter { } fn set_id_info(&mut self, id: iced::window::Id, info: WindowInfo) { + if let WindowInfo::Main = info { + println!("it is main window: {id}"); + return; + } self.ids.insert(id, info); } diff --git a/iced_examples/counter_muti/src/main.rs b/iced_examples/counter_muti/src/main.rs index 8474e8f..fa4a415 100644 --- a/iced_examples/counter_muti/src/main.rs +++ b/iced_examples/counter_muti/src/main.rs @@ -10,7 +10,7 @@ use iced_runtime::{task, Action}; use iced_layershell::reexport::{Anchor, KeyboardInteractivity, Layer, NewLayerShellSettings}; use iced_layershell::settings::{LayerShellSettings, Settings, StartMode}; use iced_layershell::MultiApplication; -use iced_layershell::{to_layer_message, LayerSingleton}; +use iced_layershell::{to_layer_message, WindowInfoMarker}; pub fn main() -> Result<(), iced_layershell::Error> { tracing_subscriber::fmt().init(); @@ -32,7 +32,7 @@ struct Counter { ids: HashMap, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, LayerSingleton)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, WindowInfoMarker)] enum WindowInfo { #[singleton] Left, diff --git a/iced_layershell/src/actions.rs b/iced_layershell/src/actions.rs index dfddc4b..df9d3fb 100644 --- a/iced_layershell/src/actions.rs +++ b/iced_layershell/src/actions.rs @@ -22,6 +22,15 @@ pub trait IsSingleton { } } +pub struct MainWindowInfo; + +impl TryFrom for () { + type Error = (); + fn try_from(_: MainWindowInfo) -> Result<(), Self::Error> { + Err(()) + } +} + impl IsSingleton for () {} #[derive(Debug, PartialEq, Eq, Clone, Copy)] diff --git a/iced_layershell/src/build_pattern.rs b/iced_layershell/src/build_pattern.rs index b879258..9c0e360 100644 --- a/iced_layershell/src/build_pattern.rs +++ b/iced_layershell/src/build_pattern.rs @@ -1,3 +1,6 @@ +//! The build_pattern allow you to create application just with callback functions. +//! Similar with the one of origin iced. + mod application; mod daemon; use std::borrow::Cow; @@ -11,6 +14,8 @@ pub trait Renderer: iced_core::text::Renderer + iced_graphics::compositor::Defau impl Renderer for T where T: iced_core::text::Renderer + iced_graphics::compositor::Default {} +/// MainSettings for iced_layershell +/// different from [`crate::Settings`], it does not contain the field of flags #[derive(Debug)] pub struct MainSettings { /// The identifier of the application. @@ -62,5 +67,8 @@ impl Default for MainSettings { } } +#[doc = include_str!("./build_pattern/application.md")] pub use application::application; + +#[doc = include_str!("./build_pattern/daemon.md")] pub use daemon::daemon; diff --git a/iced_layershell/src/build_pattern/application.md b/iced_layershell/src/build_pattern/application.md new file mode 100644 index 0000000..85a3d34 --- /dev/null +++ b/iced_layershell/src/build_pattern/application.md @@ -0,0 +1,160 @@ +# The application function allow you to create the application with just some functions + +```rust, no_run + +use iced::widget::{button, column, row, text, text_input}; +use iced::{event, Alignment, Color, Element, Event, Length, Task as Command}; +use iced_layershell::build_pattern::{application, MainSettings}; +use iced_layershell::reexport::Anchor; +use iced_layershell::settings::{LayerShellSettings, StartMode}; +use iced_layershell::to_layer_message; + +pub fn main() -> Result<(), iced_layershell::Error> { + let args: Vec = std::env::args().collect(); + + let mut binded_output_name = None; + if args.len() >= 2 { + binded_output_name = Some(args[1].to_string()) + } + + let start_mode = match binded_output_name { + Some(output) => StartMode::TargetScreen(output), + None => StartMode::Active, + }; + + application(namespace, update, view) + .style(style) + .subscription(subscription) + .settings(MainSettings { + layer_settings: LayerShellSettings { + size: Some((0, 400)), + exclusive_zone: 400, + anchor: Anchor::Bottom | Anchor::Left | Anchor::Right, + start_mode, + ..Default::default() + }, + ..Default::default() + }) + .run() +} + +#[derive(Default)] +struct Counter { + value: i32, + text: String, +} + +#[derive(Debug, Clone, Copy)] +enum WindowDirection { + Top, + Left, + Right, + Bottom, +} + +#[to_layer_message] +#[derive(Debug, Clone)] +enum Message { + IncrementPressed, + DecrementPressed, + TextInput(String), + Direction(WindowDirection), + IcedEvent(Event), +} + +fn namespace(_: &Counter) -> String { + String::from("Counter - Iced") +} + +fn subscription(_: &Counter) -> iced::Subscription { + event::listen().map(Message::IcedEvent) +} + +fn update(counter: &mut Counter, message: Message) -> Command { + match message { + Message::IcedEvent(event) => { + println!("hello {event:?}"); + Command::none() + } + Message::IncrementPressed => { + counter.value += 1; + Command::none() + } + Message::DecrementPressed => { + counter.value -= 1; + Command::none() + } + Message::TextInput(text) => { + counter.text = text; + Command::none() + } + + Message::Direction(direction) => match direction { + WindowDirection::Left => Command::done(Message::AnchorSizeChange( + Anchor::Left | Anchor::Top | Anchor::Bottom, + (400, 0), + )), + WindowDirection::Right => Command::done(Message::AnchorSizeChange( + Anchor::Right | Anchor::Top | Anchor::Bottom, + (400, 0), + )), + WindowDirection::Bottom => Command::done(Message::AnchorSizeChange( + Anchor::Bottom | Anchor::Left | Anchor::Right, + (0, 400), + )), + WindowDirection::Top => Command::done(Message::AnchorSizeChange( + Anchor::Top | Anchor::Left | Anchor::Right, + (0, 400), + )), + }, + _ => unreachable!(), + } +} + +fn view(counter: &Counter) -> Element { + let center = column![ + button("Increment").on_press(Message::IncrementPressed), + text(counter.value).size(50), + button("Decrement").on_press(Message::DecrementPressed) + ] + .align_x(Alignment::Center) + .padding(20) + .width(Length::Fill) + .height(Length::Fill); + row![ + button("left") + .on_press(Message::Direction(WindowDirection::Left)) + .height(Length::Fill), + column![ + button("top") + .on_press(Message::Direction(WindowDirection::Top)) + .width(Length::Fill), + center, + text_input("hello", &counter.text) + .on_input(Message::TextInput) + .padding(10), + button("bottom") + .on_press(Message::Direction(WindowDirection::Bottom)) + .width(Length::Fill), + ] + .width(Length::Fill), + button("right") + .on_press(Message::Direction(WindowDirection::Right)) + .height(Length::Fill), + ] + .padding(20) + .spacing(10) + .width(Length::Fill) + .height(Length::Fill) + .into() +} + +fn style(_counter: &Counter, theme: &iced::Theme) -> iced_layershell::Appearance { + use iced_layershell::Appearance; + Appearance { + background_color: Color::TRANSPARENT, + text_color: theme.palette().text, + } +} + +``` diff --git a/iced_layershell/src/build_pattern/daemon.md b/iced_layershell/src/build_pattern/daemon.md new file mode 100644 index 0000000..2d01185 --- /dev/null +++ b/iced_layershell/src/build_pattern/daemon.md @@ -0,0 +1,283 @@ +# the daemon function allows you to pass just the function to create the program. + + +```rust, no_run +use std::collections::HashMap; + +use iced::widget::{button, column, container, row, text, text_input}; +use iced::window::Id; +use iced::{event, Alignment, Element, Event, Length, Task as Command}; +use iced_layershell::actions::{IcedNewMenuSettings, MenuDirection}; +use iced_runtime::window::Action as WindowAction; +use iced_runtime::{task, Action}; + +use iced_layershell::build_pattern::{daemon, MainSettings}; +use iced_layershell::reexport::{Anchor, KeyboardInteractivity, Layer, NewLayerShellSettings}; +use iced_layershell::settings::{LayerShellSettings, StartMode}; +use iced_layershell::{to_layer_message, WindowInfoMarker}; + +pub fn main() -> Result<(), iced_layershell::Error> { + daemon( + Counter::namespace, + Counter::update, + Counter::view, + Counter::id_info, + Counter::set_id_info, + Counter::remove_id, + ) + .subscription(Counter::subscription) + .settings(MainSettings { + layer_settings: LayerShellSettings { + size: Some((0, 400)), + exclusive_zone: 400, + anchor: Anchor::Bottom | Anchor::Left | Anchor::Right, + start_mode: StartMode::AllScreens, + ..Default::default() + }, + ..Default::default() + }) + .run_with(|| Counter::new("Hello")) +} + +#[derive(Debug, Default)] +struct Counter { + value: i32, + text: String, + ids: HashMap, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, WindowInfoMarker)] +enum WindowInfo { + #[singleton] + Left, + #[singleton] + Right, + PopUp, + #[main] + Main, +} + +#[derive(Debug, Clone, Copy)] +enum WindowDirection { + Top(Id), + Left(Id), + Right(Id), + Bottom(Id), +} + +#[to_layer_message(multi, info_name = "WindowInfo")] +#[derive(Debug, Clone)] +enum Message { + IncrementPressed, + DecrementPressed, + NewWindowLeft, + NewWindowRight, + Close(Id), + TextInput(String), + Direction(WindowDirection), + IcedEvent(Event), +} + +impl Counter { + fn window_id(&self, info: &WindowInfo) -> Option<&iced::window::Id> { + for (k, v) in self.ids.iter() { + if info == v { + return Some(k); + } + } + None + } +} + +impl Counter { + fn new(text: &str) -> (Self, Command) { + ( + Self { + value: 0, + text: text.to_string(), + ids: HashMap::new(), + }, + Command::none(), + ) + } + + fn id_info(&self, id: iced::window::Id) -> Option { + self.ids.get(&id).cloned() + } + + fn set_id_info(&mut self, id: iced::window::Id, info: WindowInfo) { + if let WindowInfo::Main = info { + println!("it is main window: {id}"); + return; + } + self.ids.insert(id, info); + } + + fn remove_id(&mut self, id: iced::window::Id) { + self.ids.remove(&id); + } + + fn namespace(&self) -> String { + String::from("Counter - Iced") + } + + fn subscription(&self) -> iced::Subscription { + event::listen().map(Message::IcedEvent) + } + + fn update(&mut self, message: Message) -> Command { + use iced::keyboard; + use iced::keyboard::key::Named; + use iced::Event; + match message { + Message::IcedEvent(event) => { + match event { + Event::Keyboard(keyboard::Event::KeyPressed { + key: keyboard::Key::Named(Named::Escape), + .. + }) => { + if let Some(id) = self.window_id(&WindowInfo::Left) { + return iced_runtime::task::effect(Action::Window( + WindowAction::Close(*id), + )); + } + } + Event::Mouse(iced::mouse::Event::ButtonPressed(iced::mouse::Button::Right)) => { + return Command::done(Message::NewMenu { + settings: IcedNewMenuSettings { + size: (100, 100), + direction: MenuDirection::Up, + }, + info: WindowInfo::PopUp, + }); + } + _ => {} + } + Command::none() + } + Message::IncrementPressed => { + self.value += 1; + Command::none() + } + Message::DecrementPressed => { + self.value -= 1; + Command::none() + } + Message::TextInput(text) => { + self.text = text; + Command::none() + } + Message::Direction(direction) => match direction { + WindowDirection::Left(id) => Command::done(Message::AnchorSizeChange { + id, + anchor: Anchor::Top | Anchor::Left | Anchor::Bottom, + size: (400, 0), + }), + WindowDirection::Right(id) => Command::done(Message::AnchorSizeChange { + id, + anchor: Anchor::Top | Anchor::Right | Anchor::Bottom, + size: (400, 0), + }), + WindowDirection::Bottom(id) => Command::done(Message::AnchorSizeChange { + id, + anchor: Anchor::Left | Anchor::Right | Anchor::Bottom, + size: (0, 400), + }), + WindowDirection::Top(id) => Command::done(Message::AnchorSizeChange { + id, + anchor: Anchor::Left | Anchor::Right | Anchor::Top, + size: (0, 400), + }), + }, + Message::NewWindowLeft => Command::done(Message::NewLayerShell { + settings: NewLayerShellSettings { + size: Some((100, 100)), + exclusive_zone: None, + anchor: Anchor::Left | Anchor::Bottom, + layer: Layer::Top, + margin: None, + keyboard_interactivity: KeyboardInteractivity::Exclusive, + use_last_output: false, + ..Default::default() + }, + info: WindowInfo::Left, + }), + Message::NewWindowRight => Command::done(Message::NewLayerShell { + settings: NewLayerShellSettings { + size: Some((100, 100)), + exclusive_zone: None, + anchor: Anchor::Right | Anchor::Bottom, + layer: Layer::Top, + margin: None, + keyboard_interactivity: KeyboardInteractivity::Exclusive, + use_last_output: false, + ..Default::default() + }, + info: WindowInfo::Right, + }), + Message::Close(id) => task::effect(Action::Window(WindowAction::Close(id))), + _ => unreachable!(), + } + } + + fn view(&self, id: iced::window::Id) -> Element { + if let Some(WindowInfo::Left) = self.id_info(id) { + return button("close left").on_press(Message::Close(id)).into(); + } + if let Some(WindowInfo::Right) = self.id_info(id) { + return button("close right").on_press(Message::Close(id)).into(); + } + if let Some(WindowInfo::PopUp) = self.id_info(id) { + return container(button("close PopUp").on_press(Message::Close(id))) + .center_x(Length::Fill) + .center_y(Length::Fill) + .style(|_theme| container::Style { + background: Some(iced::Color::new(0., 0.5, 0.7, 0.6).into()), + ..Default::default() + }) + //.style(Container::Custom(Box::new(BlackMenu))) + .width(Length::Fill) + .height(Length::Fill) + .into(); + } + let center = column![ + button("Increment").on_press(Message::IncrementPressed), + button("Decrement").on_press(Message::DecrementPressed), + text(self.value).size(50), + button("newwindowLeft").on_press(Message::NewWindowLeft), + button("newwindowRight").on_press(Message::NewWindowRight), + ] + .align_x(Alignment::Center) + .padding(20) + .width(Length::Fill) + .height(Length::Fill); + row![ + button("left") + .on_press(Message::Direction(WindowDirection::Left(id))) + .height(Length::Fill), + column![ + button("top") + .on_press(Message::Direction(WindowDirection::Top(id))) + .width(Length::Fill), + center, + text_input("hello", &self.text) + .on_input(Message::TextInput) + .padding(10), + button("bottom") + .on_press(Message::Direction(WindowDirection::Bottom(id))) + .width(Length::Fill), + ] + .width(Length::Fill), + button("right") + .on_press(Message::Direction(WindowDirection::Right(id))) + .height(Length::Fill), + ] + .padding(20) + .spacing(10) + //.align_items(Alignment::Center) + .width(Length::Fill) + .height(Length::Fill) + .into() + } +} +``` diff --git a/iced_layershell/src/build_pattern/daemon.rs b/iced_layershell/src/build_pattern/daemon.rs index 0a82e20..dd0a10b 100644 --- a/iced_layershell/src/build_pattern/daemon.rs +++ b/iced_layershell/src/build_pattern/daemon.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use iced::Font; use iced::{Element, Task}; -use crate::actions::{IsSingleton, LayershellCustomActionsWithIdAndInfo}; +use crate::actions::{IsSingleton, LayershellCustomActionsWithIdAndInfo, MainWindowInfo}; use crate::settings::LayerShellSettings; use crate::DefaultStyle; @@ -27,7 +27,7 @@ pub trait Program: Sized { type State; type Renderer: Renderer; - type WindowInfo: Clone + PartialEq + IsSingleton; + type WindowInfo: Clone + PartialEq + IsSingleton + TryFrom; /// The type of __messages__ your [`Application`] will produce. type Message: std::fmt::Debug + Send @@ -361,7 +361,7 @@ pub fn daemon( ) -> Daemon> where State: 'static, - WindowInfo: Clone + PartialEq + IsSingleton, + WindowInfo: Clone + PartialEq + IsSingleton + TryFrom, Message: 'static + TryInto, Error = Message> + Send @@ -418,7 +418,7 @@ where WindowInfo, > where - WindowInfo: Clone + PartialEq + IsSingleton, + WindowInfo: Clone + PartialEq + IsSingleton + TryFrom, Message: 'static + TryInto, Error = Message> + Send diff --git a/iced_layershell/src/lib.rs b/iced_layershell/src/lib.rs index 3496aef..d1e0704 100644 --- a/iced_layershell/src/lib.rs +++ b/iced_layershell/src/lib.rs @@ -20,13 +20,14 @@ pub mod reexport { pub use layershellev::NewLayerShellSettings; } +use actions::MainWindowInfo; use actions::{IsSingleton, LayershellCustomActions, LayershellCustomActionsWithIdAndInfo}; use settings::Settings; use iced_runtime::Task; pub use iced_layershell_macros::to_layer_message; -pub use iced_layershell_macros::LayerSingleton; +pub use iced_layershell_macros::WindowInfoMarker; pub use error::Error; @@ -358,7 +359,8 @@ pub trait MultiApplication: Sized { fn run(settings: Settings) -> Result where Self: 'static, - ::WindowInfo: Clone + PartialEq + IsSingleton, + ::WindowInfo: + Clone + PartialEq + IsSingleton + TryFrom, Self::Message: 'static + TryInto, Error = Self::Message>, { diff --git a/iced_layershell/src/multi_window.rs b/iced_layershell/src/multi_window.rs index 9772d92..739d062 100644 --- a/iced_layershell/src/multi_window.rs +++ b/iced_layershell/src/multi_window.rs @@ -2,7 +2,8 @@ mod state; use crate::{ actions::{ IcedNewMenuSettings, IcedNewPopupSettings, IsSingleton, LayerShellActionVec, - LayershellCustomActionsWithIdAndInfo, LayershellCustomActionsWithIdInner, MenuDirection, + LayershellCustomActionsWithIdAndInfo, LayershellCustomActionsWithIdInner, MainWindowInfo, + MenuDirection, }, multi_window::window_manager::WindowManager, settings::VirtualKeyboardSettings, @@ -156,7 +157,8 @@ where E: Executor + 'static, C: Compositor + 'static, A::Theme: DefaultStyle, - ::WindowInfo: Clone + PartialEq + IsSingleton, + ::WindowInfo: + Clone + PartialEq + IsSingleton + TryFrom, A::Message: 'static + TryInto, Error = A::Message>, { @@ -489,7 +491,7 @@ async fn run_instance( E: Executor + 'static, C: Compositor + 'static, A::Theme: DefaultStyle, - A::WindowInfo: Clone + PartialEq + IsSingleton, + A::WindowInfo: Clone + PartialEq + IsSingleton + TryFrom, A::Message: 'static + TryInto, Error = A::Message>, { @@ -663,19 +665,34 @@ async fn run_instance( } } - if is_created && is_new_window { - let cached_interfaces: HashMap = - ManuallyDrop::into_inner(user_interfaces) - .drain() - .map(|(id, ui)| (id, ui.into_cache())) - .collect(); - application.set_id_info(id, info.unwrap().clone()); - user_interfaces = ManuallyDrop::new(build_user_interfaces( - &application, - &mut debug, - &mut window_manager, - cached_interfaces, - )); + if is_new_window { + if is_created { + let cached_interfaces: HashMap = + ManuallyDrop::into_inner(user_interfaces) + .drain() + .map(|(id, ui)| (id, ui.into_cache())) + .collect(); + application.set_id_info(id, info.unwrap().clone()); + user_interfaces = ManuallyDrop::new(build_user_interfaces( + &application, + &mut debug, + &mut window_manager, + cached_interfaces, + )); + } else if let Ok(info) = MainWindowInfo.try_into() { + let cached_interfaces: HashMap = + ManuallyDrop::into_inner(user_interfaces) + .drain() + .map(|(id, ui)| (id, ui.into_cache())) + .collect(); + application.set_id_info(id, info); + user_interfaces = ManuallyDrop::new(build_user_interfaces( + &application, + &mut debug, + &mut window_manager, + cached_interfaces, + )); + } } } MultiWindowIcedLayerEvent(None, IcedLayerEvent::Window(event)) => { @@ -1014,7 +1031,11 @@ pub(crate) fn run_action( } let option_id = if let LayershellCustomActionsWithInfo::RemoveWindow(id) = action.1 { - window_manager.get_layer_id(id) + let option_id = window_manager.get_layer_id(id); + if option_id.is_none() { + return; + } + option_id } else { None }; diff --git a/iced_layershell/tests/test_macro.rs b/iced_layershell/tests/test_macro.rs index eaf65f6..b837549 100644 --- a/iced_layershell/tests/test_macro.rs +++ b/iced_layershell/tests/test_macro.rs @@ -1,5 +1,5 @@ -use iced_layershell::actions::IsSingleton; -use iced_layershell::{to_layer_message, LayerSingleton}; +use iced_layershell::actions::{IsSingleton, MainWindowInfo}; +use iced_layershell::{to_layer_message, WindowInfoMarker}; #[test] fn test_layer_message_macro() { @@ -15,7 +15,7 @@ fn test_layer_message_macro() { #[test] fn test_layersingleton_derive() { #[allow(unused)] - #[derive(LayerSingleton)] + #[derive(WindowInfoMarker)] enum SingleToneTest { #[singleton] SingleTon, @@ -26,9 +26,15 @@ fn test_layersingleton_derive() { }, #[singleton] SingleTonThird(i32), + #[main] + Main, } assert!(SingleToneTest::SingleTon.is_singleton()); assert!(!SingleToneTest::NotSingleTon.is_singleton()); assert!(SingleToneTest::SingleTonTwo { field: false }.is_singleton()); assert!(SingleToneTest::SingleTonThird(10).is_singleton()); + assert!(matches!( + MainWindowInfo.try_into().unwrap(), + SingleToneTest::Main + )) } diff --git a/iced_layershell_macros/src/lib.rs b/iced_layershell_macros/src/lib.rs index 2845d81..7902d04 100644 --- a/iced_layershell_macros/src/lib.rs +++ b/iced_layershell_macros/src/lib.rs @@ -17,8 +17,17 @@ fn is_singleton_attr(attr: &Attribute) -> bool { attr.path().is_ident("singleton") } -#[proc_macro_derive(LayerSingleton, attributes(singleton))] -pub fn layer_singleton(input: TokenStream) -> TokenStream { +#[inline] +fn is_mainwindow_attr(attr: &Attribute) -> bool { + attr.path().is_ident("main") +} + +/// WindowInfoMarker, it is a derive to mark the WIndowInfo of MultiApplication. +/// There are two attributes: singleton and main +/// Singleton is used to mark the window can only exist once, +/// main is used to get the id of the main window +#[proc_macro_derive(WindowInfoMarker, attributes(singleton, main))] +pub fn window_info_marker(input: TokenStream) -> TokenStream { // Parse the input as a DeriveInput let input = parse_macro_input!(input as DeriveInput); @@ -48,6 +57,29 @@ pub fn layer_singleton(input: TokenStream) -> TokenStream { } }); + let try_from_mainwindow = variants + .iter() + .find(|variant| variant.attrs.iter().any(is_mainwindow_attr)) + .map(|variant| { + let variant_name = &variant.ident; + quote! { + impl TryFrom for #name { + type Error = (); + fn try_from(_val: iced_layershell::actions::MainWindowInfo) -> Result { + Ok(Self::#variant_name) + } + } + } + }) + .unwrap_or(quote! { + impl TryFrom for #name { + type Error = (); + fn try_from(_val: iced_layershell::actions::MainWindowInfo) -> Result { + Err(()) + } + } + }); + // Generate the final implementation let expanded = quote! { impl iced_layershell::actions::IsSingleton for #name { @@ -57,11 +89,17 @@ pub fn layer_singleton(input: TokenStream) -> TokenStream { } } } + #try_from_mainwindow }; TokenStream::from(expanded) } +/// to_layer_message is to convert a normal enum to the enum usable in iced_layershell +/// It impl the try_into trait for the enum and make it can be convert to the actions in +/// layershell. +/// +/// It will automatic add the fields which match the actions in iced_layershell #[manyhow::manyhow] #[proc_macro_attribute] pub fn to_layer_message(attr: TokenStream2, input: TokenStream2) -> manyhow::Result {