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:
parent
bdf5f7da38
commit
8eff8a33fa
1 changed files with 148 additions and 132 deletions
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue