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
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<Message> {
let page = sub_page.parent_page();
column!(
@ -258,6 +266,22 @@ impl Application for Window {
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> {
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<Message> {
// 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::<Element<Message>>::into(header).debug(self.debug);
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_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 {