diff --git a/examples/cosmic/Cargo.toml b/examples/cosmic/Cargo.toml index 53bf385..c29e3f1 100644 --- a/examples/cosmic/Cargo.toml +++ b/examples/cosmic/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" publish = false [dependencies] +apply = "0.3.0" libcosmic = { path = "../..", default-features = false, features = ["debug", "wgpu", "winit"] } diff --git a/examples/cosmic/src/window.rs b/examples/cosmic/src/window.rs index 1d49c5f..946c3bd 100644 --- a/examples/cosmic/src/window.rs +++ b/examples/cosmic/src/window.rs @@ -128,13 +128,10 @@ pub struct Window { page: Page, debug: bool, theme: Theme, - slider_value: f32, - demo_view_switcher: segmented_button::State, - demo_selection: segmented_button::State<()>, - spin_button: SpinButtonModel, - checkbox_value: bool, - toggler_value: bool, - pick_list_selected: Option<&'static str>, + bluetooth: bluetooth::State, + demo: demo::State, + desktop: desktop::State, + system_and_accounts: system_and_accounts::State, sidebar_toggled: bool, sidebar_toggled_condensed: bool, show_minimize: bool, @@ -161,30 +158,28 @@ impl Window { #[allow(dead_code)] #[derive(Clone, Copy, Debug)] pub enum Message { - ButtonPressed, - CheckboxToggled(bool), Close, CondensedViewToggle(()), - Debug(bool), - DemoTabActivate(segmented_button::Key), - DemoSelectionActivate(segmented_button::Key), + Bluetooth(bluetooth::Message), + Demo(demo::Message), + Desktop(desktop::Message), Drag, InputChanged, Maximize, Minimize, Page(Page), - PickListSelected(&'static str), - RowSelected(usize), - SliderChanged(f32), - SpinButton(SpinMessage), - ThemeChanged(Theme), - TogglerToggled(bool), ToggleSidebar, ToggleSidebarCondensed, } +impl From for Message { + fn from(page: Page) -> Message { + Message::Page(page) + } +} + impl Window { - fn page_title(&self, page: Page) -> Element { + fn page_title(&self, page: Page) -> Element { row!(text(page.title()).size(30), horizontal_space(Length::Fill),).into() } @@ -192,7 +187,12 @@ impl Window { WINDOW_WIDTH.load(Ordering::Relaxed) < BREAK_POINT } - fn parent_page_button(&self, sub_page: impl SubPage) -> Element { + fn page(&mut self, page: Page) { + self.sidebar_toggled_condensed = false; + self.page = page; + } + + fn parent_page_button + 'static>(&self, sub_page: impl SubPage) -> Element { let page = sub_page.parent_page(); column!( iced::widget::Button::new(row!( @@ -201,7 +201,7 @@ impl Window { )) .padding(0) .style(theme::Button::Link) - .on_press(Message::Page(page)), + .on_press(Message::from(page)), row!( text(sub_page.title()).size(30), horizontal_space(Length::Fill), @@ -211,7 +211,7 @@ impl Window { .into() } - fn sub_page_button(&self, sub_page: impl SubPage) -> Element { + fn sub_page_button + 'static>(&self, sub_page: impl SubPage) -> Element { iced::widget::Button::new( container( settings::item_row(vec![ @@ -236,18 +236,18 @@ impl Window { ) .padding(0) .style(theme::Button::Transparent) - .on_press(Message::Page(sub_page.into_page())) + .on_press(Message::from(sub_page.into_page())) .into() } - fn view_unimplemented_page(&self, page: Page) -> Element { + fn view_unimplemented_page(&self, page: Page) -> Element { settings::view_column(vec![ self.page_title(page), text("We haven't created that panel yet, and/or it is using a similar idea as current Pop! designs.").into(), ]).into() } - fn view_unimplemented_sub_page(&self, sub_page: impl SubPage) -> Element { + fn view_unimplemented_sub_page<'a, Message: Clone + From + 'static>(&'a self, sub_page: impl SubPage) -> Element<'a, Message> { settings::view_column(vec![ self.parent_page_button(sub_page), text("We haven't created that panel yet, and/or it is using a similar idea as current Pop! designs.").into(), @@ -266,24 +266,25 @@ impl Application for Window { .sidebar_toggled(true) .show_maximize(true) .show_minimize(true); - window.slider_value = 50.0; + window.demo.slider_value = 50.0; // window.theme = Theme::Light; - window.pick_list_selected = Some("Option 1"); + window.demo.pick_list_selected = Some("Option 1"); window.title = String::from("COSMIC Design System - Iced"); - window.spin_button.min = -10; - window.spin_button.max = 10; + window.demo.spin_button.min = -10; + window.demo.spin_button.max = 10; // Configures the demo view switcher. - let key = window.demo_view_switcher.insert("Tab A", DemoView::TabA); - window.demo_view_switcher.activate(key); - window.demo_view_switcher.insert("Tab B", DemoView::TabB); - window.demo_view_switcher.insert("Tab C", DemoView::TabC); + let key = window.demo.view_switcher.insert("Controls", DemoView::TabA); + window.demo.view_switcher.activate(key); + window.demo.view_switcher.insert("Segmented Button", DemoView::TabB); + window.demo.view_switcher.insert("Tab C", DemoView::TabC); // Configures the demo selection button. - let key = window.demo_selection.insert("Choice A", ()); - window.demo_selection.activate(key); - window.demo_selection.insert("Choice B", ()); - window.demo_selection.insert("Choice C", ()); + let key = window.demo.selection.insert("Choice A", ()); + window.demo.selection.activate(key); + window.demo.selection.insert("Choice B", ()); + window.demo.selection.insert("Choice C", ()); + (window, Command::none()) } @@ -316,19 +317,23 @@ impl Application for Window { fn update(&mut self, message: Message) -> iced::Command { match message { - Message::Page(page) => { - self.sidebar_toggled_condensed = false; - self.page = page; + Message::Page(page) => self.page(page), + Message::Bluetooth(message) => { + self.bluetooth.update(message); } - Message::Debug(debug) => self.debug = debug, - Message::ThemeChanged(theme) => self.theme = theme, - Message::ButtonPressed => {} - Message::SliderChanged(value) => self.slider_value = value, - Message::CheckboxToggled(value) => { - self.checkbox_value = value; + Message::Demo(message) => { + match self.demo.update(message) { + Some(demo::Output::Debug(debug)) => self.debug = debug, + Some(demo::Output::ThemeChanged(theme)) => self.theme = theme, + None => (), + } + } + Message::Desktop(message) => { + match self.desktop.update(message) { + Some(desktop::Output::Page(page)) => self.page(page), + None => (), + } } - Message::TogglerToggled(value) => self.toggler_value = value, - Message::PickListSelected(value) => self.pick_list_selected = Some(value), Message::ToggleSidebar => self.sidebar_toggled = !self.sidebar_toggled, Message::ToggleSidebarCondensed => { self.sidebar_toggled_condensed = !self.sidebar_toggled_condensed @@ -337,12 +342,11 @@ impl Application for Window { Message::Close => return close(window::Id::new(0)), Message::Minimize => return minimize(window::Id::new(0), true), Message::Maximize => return toggle_maximize(window::Id::new(0)), - Message::RowSelected(row) => println!("Selected row {row}"), + Message::InputChanged => {} - Message::SpinButton(msg) => self.spin_button.update(msg), + Message::CondensedViewToggle(_) => {} - Message::DemoTabActivate(key) => self.demo_view_switcher.activate(key), - Message::DemoSelectionActivate(key) => self.demo_selection.activate(key), + } Command::none() @@ -439,7 +443,7 @@ impl Application for Window { if !(self.is_condensed() && sidebar_toggled) { let content: Element<_> = match self.page { - Page::Demo => self.view_demo(), + Page::Demo => self.demo.view(self).map(Message::Demo), Page::Networking(None) => settings::view_column(vec![ self.page_title(self.page), column!( @@ -450,9 +454,11 @@ impl Application for Window { .into(), ]) .into(), - Page::Networking(Some(sub_page)) => self.view_unimplemented_sub_page(sub_page), - Page::Bluetooth => self.view_bluetooth(), - Page::Desktop(desktop_page_opt) => self.view_desktop(desktop_page_opt), + Page::Networking(Some(sub_page)) => { + self.view_unimplemented_sub_page(sub_page) + } + Page::Bluetooth => self.bluetooth.view(self).map(Message::Bluetooth), + Page::Desktop(desktop_page_opt) => self.desktop.view(self, desktop_page_opt).map(Message::Desktop), Page::InputDevices(None) => settings::view_column(vec![ self.page_title(self.page), column!( @@ -477,7 +483,7 @@ impl Application for Window { ]) .into(), Page::SystemAndAccounts(Some(SystemAndAccountsPage::About)) => { - self.view_system_and_accounts_about() + self.system_and_accounts.view(self) } Page::SystemAndAccounts(Some(sub_page)) => { self.view_unimplemented_sub_page(sub_page) diff --git a/examples/cosmic/src/window/bluetooth.rs b/examples/cosmic/src/window/bluetooth.rs index 51967fe..cd89681 100644 --- a/examples/cosmic/src/window/bluetooth.rs +++ b/examples/cosmic/src/window/bluetooth.rs @@ -3,16 +3,32 @@ use cosmic::{ iced::widget::{column, text}, widget::{list_column, settings, toggler}, }; -use super::{Message, Page, Window}; +use super::{Page, Window}; -impl Window { - pub(super) fn view_bluetooth(&self) -> Element { +#[derive(Clone, Copy, Debug)] +pub enum Message { + Enable(bool) +} + +#[derive(Default)] +pub struct State { + enabled: bool +} + +impl State { + pub(super) fn update(&mut self, message: Message) { + match message { + Message::Enable(value) => self.enabled = value, + } + } + + pub(super) fn view<'a>(&'a self, window: &'a Window) -> Element<'a, Message> { settings::view_column(vec![ - self.page_title(Page::Bluetooth), + window.page_title(Page::Bluetooth), column!( list_column() - .add(settings::item("Bluetooth", toggler(None, self.toggler_value, Message::TogglerToggled))), + .add(settings::item("Bluetooth", toggler(None, self.enabled, Message::Enable))), text("Now visible as \"TODO\", just kidding") ).spacing(8).into(), diff --git a/examples/cosmic/src/window/demo.rs b/examples/cosmic/src/window/demo.rs index 5209fb8..512046d 100644 --- a/examples/cosmic/src/window/demo.rs +++ b/examples/cosmic/src/window/demo.rs @@ -1,12 +1,12 @@ use cosmic::{ iced::widget::{checkbox, pick_list, progress_bar, radio, row, slider}, iced::{Alignment, Length}, - theme::{self, Button as ButtonTheme, Theme}, - widget::{button, segmented_button::cosmic::{view_switcher, segmented_selection}, settings, toggler}, + theme::{Button as ButtonTheme, Theme}, + widget::{button, segmented_button::{self, cosmic::{view_switcher, segmented_selection}}, settings, toggler, Orientation, spin_button::{SpinButtonModel, SpinMessage}}, Element, }; -use super::{Message, Page, Window}; +use super::{Page, Window}; pub enum DemoView { TabA, @@ -14,33 +14,82 @@ pub enum DemoView { TabC, } -impl Window { - pub(super) fn view_demo(&self) -> Element { +#[derive(Clone, Copy, Debug)] +pub enum Message { + ButtonPressed, + CheckboxToggled(bool), + Debug(bool), + PickListSelected(&'static str), + RowSelected(usize), + Selection(segmented_button::Key), + SliderChanged(f32), + SpinButton(SpinMessage), + ThemeChanged(Theme), + TogglerToggled(bool), + ViewSwitcher(segmented_button::Key), +} + +pub enum Output { + Debug(bool), + ThemeChanged(Theme) +} + +#[derive(Default)] +pub struct State { + pub checkbox_value: bool, + pub pick_list_selected: Option<&'static str>, + pub selection: segmented_button::State<()>, + pub slider_value: f32, + pub spin_button: SpinButtonModel, + pub toggler_value: bool, + pub view_switcher: segmented_button::State, +} + +impl State { + pub(super) fn update(&mut self, message: Message) -> Option { + match message { + Message::ButtonPressed => (), + Message::CheckboxToggled(value) => self.checkbox_value = value, + Message::Debug(value) => return Some(Output::Debug(value)), + Message::PickListSelected(value) => self.pick_list_selected = Some(value), + Message::RowSelected(row) => println!("Selected row {row}"), + Message::Selection(key) => self.selection.activate(key), + Message::SliderChanged(value) => self.slider_value = value, + Message::SpinButton(msg) => self.spin_button.update(msg), + Message::ThemeChanged(theme) => return Some(Output::ThemeChanged(theme)), + Message::TogglerToggled(value) => self.toggler_value = value, + Message::ViewSwitcher(key) => self.view_switcher.activate(key), + } + + None + } + + pub(super) fn view<'a>(&'a self, window: &'a Window) -> Element<'a, Message> { let choose_theme = [Theme::Light, Theme::Dark].iter().fold( row![].spacing(10).align_items(Alignment::Center), |row, theme| { row.push(radio( format!("{:?}", theme), *theme, - Some(self.theme), + Some(window.theme), Message::ThemeChanged, )) }, ); settings::view_column(vec![ - self.page_title(Page::Demo), - view_switcher(&self.demo_view_switcher) - .on_activate(Message::DemoTabActivate) + window.page_title(Page::Demo), + view_switcher(&self.view_switcher) + .on_activate(Message::ViewSwitcher) .into(), - match self.demo_view_switcher.active_data() { + match self.view_switcher.active_data() { None => panic!("no tab is active"), Some(DemoView::TabA) => settings::view_column(vec![ settings::view_section("Debug") .add(settings::item("Debug theme", choose_theme)) .add(settings::item( "Debug layout", - toggler(None, self.debug, Message::Debug), + toggler(None, window.debug, Message::Debug), )) .into(), settings::view_section("Buttons") @@ -118,12 +167,59 @@ impl Window { .into(), Some(DemoView::TabB) => { settings::view_column(vec![ - cosmic::iced::widget::text("SegmentedButton::Selection") + cosmic::iced::widget::text("Selection") .font(cosmic::font::FONT_SEMIBOLD) .into(), - segmented_selection(&self.demo_selection) - .on_activate(Message::DemoSelectionActivate) - .into() + segmented_selection(&self.selection) + .on_activate(Message::Selection) + .into(), + segmented_selection(&self.selection) + .on_activate(Message::Selection) + .orientation(Orientation::Vertical) + .into(), + cosmic::iced::widget::row(vec![ + segmented_selection(&self.selection) + .on_activate(Message::Selection) + .orientation(Orientation::Vertical) + .width(Length::FillPortion(1)) + .into(), + segmented_selection(&self.selection) + .on_activate(Message::Selection) + .orientation(Orientation::Vertical) + .width(Length::FillPortion(1)) + .into(), + segmented_selection(&self.selection) + .on_activate(Message::Selection) + .orientation(Orientation::Vertical) + .width(Length::FillPortion(1)) + .into(), + ]) + .spacing(12) + .width(Length::Fill) + .into(), + cosmic::iced::widget::text("ViewSwitcher") + .font(cosmic::font::FONT_SEMIBOLD) + .into(), + cosmic::iced::widget::row(vec![ + view_switcher(&self.selection) + .on_activate(Message::Selection) + .orientation(Orientation::Vertical) + .width(Length::FillPortion(1)) + .into(), + view_switcher(&self.selection) + .on_activate(Message::Selection) + .orientation(Orientation::Vertical) + .width(Length::FillPortion(1)) + .into(), + view_switcher(&self.selection) + .on_activate(Message::Selection) + .orientation(Orientation::Vertical) + .width(Length::FillPortion(1)) + .into(), + ]) + .spacing(12) + .width(Length::Fill) + .into() ]) .padding(0) .into() diff --git a/examples/cosmic/src/window/desktop.rs b/examples/cosmic/src/window/desktop.rs index 88b2691..2abcd29 100644 --- a/examples/cosmic/src/window/desktop.rs +++ b/examples/cosmic/src/window/desktop.rs @@ -6,7 +6,7 @@ use cosmic::{ widget::{list_column, settings, toggler}, }; -use super::{Message, Page, SubPage, Window}; +use super::{Page, SubPage, Window}; #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum DesktopPage { @@ -18,6 +18,39 @@ pub enum DesktopPage { Notifications, } +#[derive(Debug, Default)] +pub struct State { + pub top_left_hot_corner: bool, + pub show_workspaces_button: bool, + pub show_applications_button: bool, + pub show_minimize_button: bool, + pub show_maximize_button: bool, + pub slideshow: bool, + pub same_background: bool, +} + +#[derive(Clone, Copy, Debug)] +pub enum Message { + Page(Page), + Slideshow(bool), + SameBackground(bool), + ShowWorkspacesButton(bool), + ShowApplicationsButton(bool), + ShowMinimizeButton(bool), + ShowMaximizeButton(bool), + TopLeftHotCorner(bool), +} + +impl From for Message { + fn from(page: Page) -> Message { + Message::Page(page) + } +} + +pub enum Output { + Page(Page), +} + impl SubPage for DesktopPage { //TODO: translate fn title(&self) -> &'static str { @@ -66,30 +99,45 @@ impl SubPage for DesktopPage { } } -impl Window { - pub(super) fn view_desktop(&self, desktop_page_opt: Option) -> Element { +impl State { + pub(super) fn update(&mut self, message: Message) -> Option { + match message { + Message::Page(page) => return Some(Output::Page(page)), + Message::SameBackground(value) => self.same_background = value, + Message::ShowApplicationsButton(value) => self.show_applications_button = value, + Message::ShowMaximizeButton(value) => self.show_maximize_button = value, + Message::ShowMinimizeButton(value) => self.show_maximize_button = value, + Message::ShowWorkspacesButton(value) => self.show_workspaces_button = value, + Message::Slideshow(value) => self.slideshow = value, + Message::TopLeftHotCorner(value) => self.top_left_hot_corner = value, + } + None + } + + pub(super) fn view<'a>(&'a self, window: &'a Window, desktop_page_opt: Option) -> Element<'a, Message> { match desktop_page_opt { None => settings::view_column(vec![ - self.page_title(self.page), + window.page_title(window.page), column!( - self.sub_page_button(DesktopPage::DesktopOptions), - self.sub_page_button(DesktopPage::Wallpaper), - self.sub_page_button(DesktopPage::Appearance), - self.sub_page_button(DesktopPage::DockAndTopPanel), - self.sub_page_button(DesktopPage::Workspaces), - self.sub_page_button(DesktopPage::Notifications), + window.sub_page_button(DesktopPage::DesktopOptions), + window.sub_page_button(DesktopPage::Wallpaper), + window.sub_page_button(DesktopPage::Appearance), + window.sub_page_button(DesktopPage::DockAndTopPanel), + window.sub_page_button(DesktopPage::Workspaces), + window.sub_page_button(DesktopPage::Notifications), ).spacing(16).into() - ]).into(), - Some(DesktopPage::DesktopOptions) => self.view_desktop_options(), - Some(DesktopPage::Wallpaper) => self.view_desktop_wallpaper(), - Some(DesktopPage::Workspaces) => self.view_desktop_workspaces(), - Some(sub_page) => self.view_unimplemented_sub_page(sub_page), + ]) + .into(), + Some(DesktopPage::DesktopOptions) => self.view_desktop_options(window), + Some(DesktopPage::Wallpaper) => self.view_desktop_wallpaper(window), + Some(DesktopPage::Workspaces) => self.view_desktop_workspaces(window), + Some(sub_page) => window.view_unimplemented_sub_page(sub_page), } } - fn view_desktop_options(&self) -> Element { + fn view_desktop_options<'a>(&'a self, window: &'a Window) -> Element<'a, Message> { settings::view_column(vec![ - self.parent_page_button(DesktopPage::DesktopOptions), + window.parent_page_button(DesktopPage::DesktopOptions), settings::view_section("Super Key Action") .add(settings::item("Launcher", horizontal_space(Length::Fill))) @@ -98,22 +146,22 @@ impl Window { .into(), settings::view_section("Hot Corner") - .add(settings::item("Enable top-left hot corner for Workspaces", toggler(None, self.toggler_value, Message::TogglerToggled))) + .add(settings::item("Enable top-left hot corner for Workspaces", toggler(None, self.top_left_hot_corner, Message::TopLeftHotCorner))) .into(), settings::view_section("Top Panel") - .add(settings::item("Show Workspaces Button", toggler(None, self.toggler_value, Message::TogglerToggled))) - .add(settings::item("Show Applications Button", toggler(None, self.toggler_value, Message::TogglerToggled))) + .add(settings::item("Show Workspaces Button", toggler(None, self.show_workspaces_button, Message::ShowWorkspacesButton))) + .add(settings::item("Show Applications Button", toggler(None, self.show_applications_button, Message::ShowApplicationsButton))) .into(), settings::view_section("Window Controls") - .add(settings::item("Show Minimize Button", toggler(None, self.toggler_value, Message::TogglerToggled))) - .add(settings::item("Show Maximize Button", toggler(None, self.toggler_value, Message::TogglerToggled))) + .add(settings::item("Show Minimize Button", toggler(None, self.show_minimize_button, Message::ShowMinimizeButton))) + .add(settings::item("Show Maximize Button", toggler(None, self.show_maximize_button, Message::ShowMaximizeButton))) .into(), ]).into() } - fn view_desktop_wallpaper(&self) -> Element { + fn view_desktop_wallpaper<'a>(&'a self, window: &'a Window) -> Element<'a, Message> { let mut image_paths: Vec = Vec::new(); /* //TODO: load image paths, do this asynchronously somehow @@ -150,7 +198,7 @@ impl Window { } settings::view_column(vec![ - self.parent_page_button(DesktopPage::Wallpaper), + window.parent_page_button(DesktopPage::Wallpaper), row!( horizontal_space(Length::Fill), @@ -165,18 +213,18 @@ impl Window { ).into(), list_column() - .add(settings::item("Same background on all displays", toggler(None, self.toggler_value, Message::TogglerToggled))) + .add(settings::item("Same background on all displays", toggler(None, self.same_background, Message::SameBackground))) .add(settings::item("Background fit", text("TODO"))) - .add(settings::item("Slideshow", toggler(None, self.toggler_value, Message::TogglerToggled))) + .add(settings::item("Slideshow", toggler(None, self.slideshow, Message::Slideshow))) .into(), column(image_column).spacing(16).into(), ]).into() } - fn view_desktop_workspaces(&self) -> Element { + fn view_desktop_workspaces<'a>(&'a self, window: &'a Window) -> Element<'a, Message> { settings::view_column(vec![ - self.parent_page_button(DesktopPage::Wallpaper), + window.parent_page_button(DesktopPage::Wallpaper), settings::view_section("Workspace Behavior") .add(settings::item("Dynamic workspaces", horizontal_space(Length::Fill))) diff --git a/examples/cosmic/src/window/system_and_accounts.rs b/examples/cosmic/src/window/system_and_accounts.rs index 2a28791..2f44d5d 100644 --- a/examples/cosmic/src/window/system_and_accounts.rs +++ b/examples/cosmic/src/window/system_and_accounts.rs @@ -7,6 +7,9 @@ use cosmic::{ use super::{Message, Page, SubPage, Window}; +#[derive(Default)] +pub struct State {} + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum SystemAndAccountsPage { Users, @@ -53,10 +56,10 @@ impl SubPage for SystemAndAccountsPage { } } -impl Window { - pub(super) fn view_system_and_accounts_about(&self) -> Element { +impl State { + pub(super) fn view<'a>(&'a self, window: &'a Window) -> Element<'a, Message> { settings::view_column(vec![ - self.parent_page_button(SystemAndAccountsPage::About), + window.parent_page_button(SystemAndAccountsPage::About), row!( horizontal_space(Length::Fill),