From 8eff8a33facb20f43f17f3e9deea2efae0d032ec Mon Sep 17 00:00:00 2001 From: 13r0ck Date: Wed, 21 Dec 2022 18:24:02 -0700 Subject: [PATCH] More efficently resize at break point Uses iced `events_with()` to only subscribe to window resize events. The window width is stored in a static AtomicU32 so that the subscription can compare to an old window width as to only send messages when the width crosses the break point. fix up --- examples/cosmic/src/window.rs | 280 ++++++++++++++++++---------------- 1 file changed, 148 insertions(+), 132 deletions(-) diff --git a/examples/cosmic/src/window.rs b/examples/cosmic/src/window.rs index 7f839773..9e78de9d 100644 --- a/examples/cosmic/src/window.rs +++ b/examples/cosmic/src/window.rs @@ -2,17 +2,17 @@ // SPDX-License-Identifier: MPL-2.0 use cosmic::{ + iced_native, iced_native::window, iced::widget::{column, container, horizontal_space, row, text}, - iced::{self, Application, Command, Length}, - iced_lazy::responsive, + iced::{self, Application, Command, Length, Subscription}, iced_winit::window::{close, drag, toggle_maximize, minimize}, theme::{self, Theme}, widget::{icon, list, nav_bar, nav_button, header_bar, settings, scrollable, spin_button::{SpinButtonModel, SpinMessage}}, Element, ElementExt, }; -use std::vec; +use std::{vec, sync::atomic::{AtomicU32, Ordering}}; mod bluetooth; @@ -115,6 +115,9 @@ impl Default for Page { } } +static WINDOW_WIDTH: AtomicU32 = AtomicU32::new(0); +const BREAK_POINT: u32 = 900; + #[derive(Default)] pub struct Window { title: String, @@ -168,7 +171,8 @@ pub enum Message { Minimize, Maximize, InputChanged, - SpinButton(SpinMessage) + SpinButton(SpinMessage), + WindowWidthChanged(u32), } impl Window { @@ -179,6 +183,10 @@ impl Window { ).into() } + fn is_condensed(&self) -> bool { + WINDOW_WIDTH.load(Ordering::Relaxed) < BREAK_POINT + } + fn parent_page_button(&self, sub_page: impl SubPage) -> Element { let page = sub_page.parent_page(); column!( @@ -258,6 +266,22 @@ impl Application for Window { self.title.clone() } + fn subscription(&self) -> Subscription { + iced_native::subscription::events_with(|event, _| match event { + cosmic::iced::Event::Window(_window_id, window::Event::Resized{width, height}) => { + let old_width = WINDOW_WIDTH.load(Ordering::Relaxed); + if old_width == 0 + || old_width < BREAK_POINT && width > BREAK_POINT + || old_width > BREAK_POINT && width < BREAK_POINT { + Some(width) + } else { + None + } + } + _ => None + }).map(Message::WindowWidthChanged) + } + fn update(&mut self, message: Message) -> iced::Command { match message { Message::Page(page) => { @@ -282,6 +306,7 @@ impl Application for Window { Message::RowSelected(row) => println!("Selected row {row}"), Message::InputChanged => {}, Message::SpinButton(msg) => self.spin_button.update(msg), + Message::WindowWidthChanged(new_width) => WINDOW_WIDTH.store(new_width, Ordering::Relaxed), } @@ -289,148 +314,139 @@ impl Application for Window { } fn view(&self) -> Element { - // TODO: Adding responsive makes this regenerate on every size change, and regeneration - // involves allocations for many different items. Ideally, we could only make the nav bar - // responsive and leave the content to be sized normally. - responsive(|size| { - //TODO: send a message when this happens instead of having everything be recalculated on resize - let condensed = size.width < 900.0; + let (sidebar_message, sidebar_toggled) = if self.is_condensed() { + (Message::ToggleSidebarCondensed, self.sidebar_toggled_condensed) + } else { + (Message::ToggleSidebar, self.sidebar_toggled) + }; - let (sidebar_message, sidebar_toggled) = if condensed { - (Message::ToggleSidebarCondensed, self.sidebar_toggled_condensed) - } else { - (Message::ToggleSidebar, self.sidebar_toggled) - }; + let mut header = header_bar() + .title("COSMIC Design System - Iced") + .on_close(Message::Close) + .on_drag(Message::Drag) + .start( + nav_button("Settings") + .on_sidebar_toggled(sidebar_message) + .sidebar_active(sidebar_toggled) + .into() + ); - let mut header = header_bar() - .title("COSMIC Design System - Iced") - .on_close(Message::Close) - .on_drag(Message::Drag) - .start( - nav_button("Settings") - .on_sidebar_toggled(sidebar_message) - .sidebar_active(sidebar_toggled) - .into() - ); + if self.show_maximize { + header = header.on_maximize(Message::Maximize); + } - if self.show_maximize { - header = header.on_maximize(Message::Maximize); - } + if self.show_minimize { + header = header.on_minimize(Message::Minimize); + } - if self.show_minimize { - header = header.on_minimize(Message::Minimize); - } + let header = Into::>::into(header).debug(self.debug); - let header = Into::>::into(header).debug(self.debug); + let mut widgets = Vec::with_capacity(2); - let mut widgets = Vec::with_capacity(2); + if sidebar_toggled { + let sidebar_button_complex = |page: Page, active| { + cosmic::nav_button!( + page.icon_name(), + page.title(), + active + ) + .on_press(Message::Page(page)) + }; - if sidebar_toggled { - let sidebar_button_complex = |page: Page, active| { - cosmic::nav_button!( - page.icon_name(), - page.title(), - active - ) - .on_press(Message::Page(page)) - }; + let sidebar_button = |page: Page| { + sidebar_button_complex(page, self.page == page) + }; - let sidebar_button = |page: Page| { - sidebar_button_complex(page, self.page == page) - }; + let mut sidebar = container(scrollable(column!( + sidebar_button(Page::Demo), + sidebar_button(Page::WiFi), + sidebar_button_complex(Page::Networking(None), matches!(self.page, Page::Networking(_))), + sidebar_button(Page::Bluetooth), + sidebar_button_complex(Page::Desktop(None), matches!(self.page, Page::Desktop(_))), + sidebar_button_complex(Page::InputDevices(None), matches!(self.page, Page::InputDevices(_))), + sidebar_button(Page::Displays), + sidebar_button(Page::PowerAndBattery), + sidebar_button(Page::Sound), + sidebar_button(Page::PrintersAndScanners), + sidebar_button(Page::PrivacyAndSecurity), + sidebar_button_complex(Page::SystemAndAccounts(None), matches!(self.page, Page::SystemAndAccounts(_))), + sidebar_button(Page::UpdatesAndRecovery), + sidebar_button_complex(Page::TimeAndLanguage(None), matches!(self.page, Page::TimeAndLanguage(_))), + sidebar_button(Page::Accessibility), + sidebar_button(Page::Applications), + ).spacing(14))) + .height(Length::Fill) + .padding(8) + .style(theme::Container::Custom(nav_bar::nav_bar_sections_style)); - let mut sidebar = container(scrollable(column!( - sidebar_button(Page::Demo), - sidebar_button(Page::WiFi), - sidebar_button_complex(Page::Networking(None), matches!(self.page, Page::Networking(_))), - sidebar_button(Page::Bluetooth), - sidebar_button_complex(Page::Desktop(None), matches!(self.page, Page::Desktop(_))), - sidebar_button_complex(Page::InputDevices(None), matches!(self.page, Page::InputDevices(_))), - sidebar_button(Page::Displays), - sidebar_button(Page::PowerAndBattery), - sidebar_button(Page::Sound), - sidebar_button(Page::PrintersAndScanners), - sidebar_button(Page::PrivacyAndSecurity), - sidebar_button_complex(Page::SystemAndAccounts(None), matches!(self.page, Page::SystemAndAccounts(_))), - sidebar_button(Page::UpdatesAndRecovery), - sidebar_button_complex(Page::TimeAndLanguage(None), matches!(self.page, Page::TimeAndLanguage(_))), - sidebar_button(Page::Accessibility), - sidebar_button(Page::Applications), - ).spacing(14))) - .height(Length::Fill) - .padding(8) - .style(theme::Container::Custom(nav_bar::nav_bar_sections_style)); + if ! self.is_condensed() { + sidebar = sidebar.max_width(300) + } - if ! condensed { - sidebar = sidebar.max_width(300) - } + let sidebar: Element<_> = sidebar.into(); + widgets.push(sidebar.debug(self.debug)); + } - let sidebar: Element<_> = sidebar.into(); - widgets.push(sidebar.debug(self.debug)); - } + if ! (self.is_condensed() && sidebar_toggled) { + let content: Element<_> = match self.page { + Page::Demo => self.view_demo(), + Page::Networking(None) => settings::view_column(vec![ + self.page_title(self.page), + column!( + self.sub_page_button(NetworkingPage::Wired), + self.sub_page_button(NetworkingPage::OnlineAccounts), + ).spacing(16).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::InputDevices(None) => settings::view_column(vec![ + self.page_title(self.page), + column!( + self.sub_page_button(InputDevicesPage::Keyboard), + self.sub_page_button(InputDevicesPage::Touchpad), + self.sub_page_button(InputDevicesPage::Mouse), + ).spacing(16).into() + ]).into(), + Page::InputDevices(Some(sub_page)) => self.view_unimplemented_sub_page(sub_page), + Page::SystemAndAccounts(None) => settings::view_column(vec![ + self.page_title(self.page), + column!( + self.sub_page_button(SystemAndAccountsPage::Users), + self.sub_page_button(SystemAndAccountsPage::About), + self.sub_page_button(SystemAndAccountsPage::Firmware), + ).spacing(16).into() + ]).into(), + Page::SystemAndAccounts(Some(SystemAndAccountsPage::About)) => self.view_system_and_accounts_about(), + Page::SystemAndAccounts(Some(sub_page)) => self.view_unimplemented_sub_page(sub_page), + Page::TimeAndLanguage(None) => settings::view_column(vec![ + self.page_title(self.page), + column!( + self.sub_page_button(TimeAndLanguagePage::DateAndTime), + self.sub_page_button(TimeAndLanguagePage::RegionAndLanguage), + ).spacing(16).into() + ]).into(), + Page::TimeAndLanguage(Some(sub_page)) => self.view_unimplemented_sub_page(sub_page), + _ => self.view_unimplemented_page(self.page), + }; - if ! (condensed && sidebar_toggled) { - let content: Element<_> = match self.page { - Page::Demo => self.view_demo(), - Page::Networking(None) => settings::view_column(vec![ - self.page_title(self.page), - column!( - self.sub_page_button(NetworkingPage::Wired), - self.sub_page_button(NetworkingPage::OnlineAccounts), - ).spacing(16).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::InputDevices(None) => settings::view_column(vec![ - self.page_title(self.page), - column!( - self.sub_page_button(InputDevicesPage::Keyboard), - self.sub_page_button(InputDevicesPage::Touchpad), - self.sub_page_button(InputDevicesPage::Mouse), - ).spacing(16).into() - ]).into(), - Page::InputDevices(Some(sub_page)) => self.view_unimplemented_sub_page(sub_page), - Page::SystemAndAccounts(None) => settings::view_column(vec![ - self.page_title(self.page), - column!( - self.sub_page_button(SystemAndAccountsPage::Users), - self.sub_page_button(SystemAndAccountsPage::About), - self.sub_page_button(SystemAndAccountsPage::Firmware), - ).spacing(16).into() - ]).into(), - Page::SystemAndAccounts(Some(SystemAndAccountsPage::About)) => self.view_system_and_accounts_about(), - Page::SystemAndAccounts(Some(sub_page)) => self.view_unimplemented_sub_page(sub_page), - Page::TimeAndLanguage(None) => settings::view_column(vec![ - self.page_title(self.page), - column!( - self.sub_page_button(TimeAndLanguagePage::DateAndTime), - self.sub_page_button(TimeAndLanguagePage::RegionAndLanguage), - ).spacing(16).into() - ]).into(), - Page::TimeAndLanguage(Some(sub_page)) => self.view_unimplemented_sub_page(sub_page), - _ => self.view_unimplemented_page(self.page), - }; + widgets.push( + scrollable(row![ + horizontal_space(Length::Fill), + content.debug(self.debug), + horizontal_space(Length::Fill), + ]) + .into(), + ); + } - widgets.push( - scrollable(row![ - horizontal_space(Length::Fill), - content.debug(self.debug), - horizontal_space(Length::Fill), - ]) - .into(), - ); - } + let content = container(row(widgets)) + .padding([0, 8, 8, 8]) + .width(Length::Fill) + .height(Length::Fill) + .into(); - let content = container(row(widgets)) - .padding([0, 8, 8, 8]) - .width(Length::Fill) - .height(Length::Fill) - .into(); - - column(vec![header, content]).into() - }) - .into() + column(vec![header, content]).into() } fn theme(&self) -> Theme {