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
This commit is contained in:
13r0ck 2022-12-21 18:24:02 -07:00 committed by Jeremy Soller
parent bdf5f7da38
commit 8eff8a33fa

View file

@ -2,17 +2,17 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use cosmic::{ use cosmic::{
iced_native,
iced_native::window, iced_native::window,
iced::widget::{column, container, horizontal_space, row, text}, iced::widget::{column, container, horizontal_space, row, text},
iced::{self, Application, Command, Length}, iced::{self, Application, Command, Length, Subscription},
iced_lazy::responsive,
iced_winit::window::{close, drag, toggle_maximize, minimize}, iced_winit::window::{close, drag, toggle_maximize, minimize},
theme::{self, Theme}, theme::{self, Theme},
widget::{icon, list, nav_bar, nav_button, header_bar, settings, scrollable, spin_button::{SpinButtonModel, SpinMessage}}, widget::{icon, list, nav_bar, nav_button, header_bar, settings, scrollable, spin_button::{SpinButtonModel, SpinMessage}},
Element, Element,
ElementExt, ElementExt,
}; };
use std::vec; use std::{vec, sync::atomic::{AtomicU32, Ordering}};
mod bluetooth; mod bluetooth;
@ -115,6 +115,9 @@ impl Default for Page {
} }
} }
static WINDOW_WIDTH: AtomicU32 = AtomicU32::new(0);
const BREAK_POINT: u32 = 900;
#[derive(Default)] #[derive(Default)]
pub struct Window { pub struct Window {
title: String, title: String,
@ -168,7 +171,8 @@ pub enum Message {
Minimize, Minimize,
Maximize, Maximize,
InputChanged, InputChanged,
SpinButton(SpinMessage) SpinButton(SpinMessage),
WindowWidthChanged(u32),
} }
impl Window { impl Window {
@ -179,6 +183,10 @@ impl Window {
).into() ).into()
} }
fn is_condensed(&self) -> bool {
WINDOW_WIDTH.load(Ordering::Relaxed) < BREAK_POINT
}
fn parent_page_button(&self, sub_page: impl SubPage) -> Element<Message> { fn parent_page_button(&self, sub_page: impl SubPage) -> Element<Message> {
let page = sub_page.parent_page(); let page = sub_page.parent_page();
column!( column!(
@ -258,6 +266,22 @@ impl Application for Window {
self.title.clone() self.title.clone()
} }
fn subscription(&self) -> Subscription<Message> {
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<Self::Message> { fn update(&mut self, message: Message) -> iced::Command<Self::Message> {
match message { match message {
Message::Page(page) => { Message::Page(page) => {
@ -282,6 +306,7 @@ impl Application for Window {
Message::RowSelected(row) => println!("Selected row {row}"), Message::RowSelected(row) => println!("Selected row {row}"),
Message::InputChanged => {}, Message::InputChanged => {},
Message::SpinButton(msg) => self.spin_button.update(msg), 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<Message> { fn view(&self) -> Element<Message> {
// TODO: Adding responsive makes this regenerate on every size change, and regeneration let (sidebar_message, sidebar_toggled) = if self.is_condensed() {
// involves allocations for many different items. Ideally, we could only make the nav bar (Message::ToggleSidebarCondensed, self.sidebar_toggled_condensed)
// responsive and leave the content to be sized normally. } else {
responsive(|size| { (Message::ToggleSidebar, self.sidebar_toggled)
//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 condensed { let mut header = header_bar()
(Message::ToggleSidebarCondensed, self.sidebar_toggled_condensed) .title("COSMIC Design System - Iced")
} else { .on_close(Message::Close)
(Message::ToggleSidebar, self.sidebar_toggled) .on_drag(Message::Drag)
}; .start(
nav_button("Settings")
.on_sidebar_toggled(sidebar_message)
.sidebar_active(sidebar_toggled)
.into()
);
let mut header = header_bar() if self.show_maximize {
.title("COSMIC Design System - Iced") header = header.on_maximize(Message::Maximize);
.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 { if self.show_minimize {
header = header.on_maximize(Message::Maximize); header = header.on_minimize(Message::Minimize);
} }
if self.show_minimize { let header = Into::<Element<Message>>::into(header).debug(self.debug);
header = header.on_minimize(Message::Minimize);
}
let header = Into::<Element<Message>>::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 = |page: Page| {
let sidebar_button_complex = |page: Page, active| { sidebar_button_complex(page, self.page == page)
cosmic::nav_button!( };
page.icon_name(),
page.title(),
active
)
.on_press(Message::Page(page))
};
let sidebar_button = |page: Page| { let mut sidebar = container(scrollable(column!(
sidebar_button_complex(page, self.page == page) 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!( if ! self.is_condensed() {
sidebar_button(Page::Demo), sidebar = sidebar.max_width(300)
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 ! condensed { let sidebar: Element<_> = sidebar.into();
sidebar = sidebar.max_width(300) widgets.push(sidebar.debug(self.debug));
} }
let sidebar: Element<_> = sidebar.into(); if ! (self.is_condensed() && sidebar_toggled) {
widgets.push(sidebar.debug(self.debug)); 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) { widgets.push(
let content: Element<_> = match self.page { scrollable(row![
Page::Demo => self.view_demo(), horizontal_space(Length::Fill),
Page::Networking(None) => settings::view_column(vec![ content.debug(self.debug),
self.page_title(self.page), horizontal_space(Length::Fill),
column!( ])
self.sub_page_button(NetworkingPage::Wired), .into(),
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( let content = container(row(widgets))
scrollable(row![ .padding([0, 8, 8, 8])
horizontal_space(Length::Fill), .width(Length::Fill)
content.debug(self.debug), .height(Length::Fill)
horizontal_space(Length::Fill), .into();
])
.into(),
);
}
let content = container(row(widgets)) column(vec![header, content]).into()
.padding([0, 8, 8, 8])
.width(Length::Fill)
.height(Length::Fill)
.into();
column(vec![header, content]).into()
})
.into()
} }
fn theme(&self) -> Theme { fn theme(&self) -> Theme {