refactor: libcosmic rebase with Application API

This commit is contained in:
Michael Aaron Murphy 2023-09-19 16:54:50 +02:00 committed by Michael Murphy
parent d243e45094
commit 454894d82f
18 changed files with 1081 additions and 1198 deletions

1128
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,6 @@ git = "https://github.com/pop-os/libcosmic"
[workspace.dependencies.libcosmic] [workspace.dependencies.libcosmic]
git = "https://github.com/pop-os/libcosmic" git = "https://github.com/pop-os/libcosmic"
default-features = false
features = ["debug", "wayland", "tokio"] features = ["debug", "wayland", "tokio"]
[workspace.dependencies.cosmic-config] [workspace.dependencies.cosmic-config]
@ -24,10 +23,10 @@ git = "https://github.com/pop-os/cosmic-comp"
git = "https://github.com/pop-os/cosmic-panel" git = "https://github.com/pop-os/cosmic-panel"
[patch."https://github.com/pop-os/libcosmic"] [patch."https://github.com/pop-os/libcosmic"]
# libcosmic = { path = "../libcosmic" } # libcosmic = { path = "../../libcosmic" }
# cosmic-config = { path = "../libcosmic/cosmic-config" } # cosmic-config = { path = "../../libcosmic/cosmic-config" }
# libcosmic = { git = "https://github.com/pop-os/libcosmic?rev=master", branch = "flexalloc" } # libcosmic = { git = "https://github.com/pop-os/libcosmic?rev=master", branch = "improv" }
# cosmic-config = { git = "https://github.com/pop-os/libcosmic?rev=master", branch = "flexalloc" } # cosmic-config = { git = "https://github.com/pop-os/libcosmic?rev=master", branch = "improv" }
[profile.release] [profile.release]
opt-level = 3 opt-level = 3

View file

@ -1,159 +1,96 @@
// Copyright 2023 System76 <info@system76.com> // Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use apply::Apply; use cosmic::iced::Subscription;
use cosmic::{
app::{Command, Core},
cosmic_config::config_subscription,
iced::{self, event::wayland, event::PlatformSpecific, subscription, window, Length},
prelude::*,
widget::{
column, container, icon, nav_bar, navigation, scrollable, search, segmented_button,
settings,
},
Element,
};
use cosmic_panel_config::CosmicPanelConfig; use cosmic_panel_config::CosmicPanelConfig;
use cosmic_settings_page::{self as page, section}; use cosmic_settings_page::{self as page, section};
use cosmic::{ use crate::config::Config;
cosmic_config::config_subscription,
iced::{ use crate::pages::desktop::{
self,
dock::{self, applets::ADD_DOCK_APPLET_DIALOGUE_ID},
panel::{
self, self,
event::wayland::{self, WindowEvent, WindowState}, applets_inner::{self, AppletsPage, APPLET_DND_ICON_ID},
event::PlatformSpecific, inner as _panel,
subscription, window, Application, Color, Command, Length, Subscription,
}, },
iced::{
widget::{self, column, container, horizontal_space, row},
window::Mode,
},
iced_sctk::commands::window::{set_mode_window, start_drag_window},
iced_style::application,
keyboard_nav,
theme::{self, theme_subscription, Theme},
widget::{
header_bar, nav_bar, nav_bar_toggle, scrollable, search, segmented_button, settings,
IconSource,
},
Element, ElementExt,
}; };
use page::Page; use crate::pages::input::{self, keyboard};
use crate::pages::{sound, system, time};
use crate::{ use crate::subscription::desktop_files;
config::Config, use crate::widget::{page_title, search_header};
pages::{ use std::borrow::Cow;
desktop::{
self,
dock::{self, applets::ADD_DOCK_APPLET_DIALOGUE_ID},
panel::{
self,
applets_inner::{self, AppletsPage, APPLET_DND_ICON_ID},
inner as _panel,
},
},
input::{self, keyboard},
sound, system, time,
},
subscription::desktop_files,
widget::{page_title, parent_page_button, search_header, sub_page_button},
};
use std::{borrow::Cow, process};
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
#[allow(clippy::module_name_repetitions)] #[allow(clippy::module_name_repetitions)]
pub struct SettingsApp { pub struct SettingsApp {
pub active_page: page::Entity, active_page: page::Entity,
pub config: Config, config: Config,
pub debug: bool, core: Core,
pub nav_bar_toggled_condensed: bool, nav_model: nav_bar::Model,
pub nav_bar_toggled: bool, pages: page::Binder<crate::pages::Message>,
pub nav_bar: segmented_button::SingleSelectModel, search: search::Model,
pub pages: page::Binder<crate::pages::Message>, search_selections: Vec<(page::Entity, section::Entity)>,
pub scaling_factor: f32,
pub search: search::Model,
pub search_selections: Vec<(page::Entity, section::Entity)>,
pub show_maximize: bool,
pub sharp_corners: bool,
pub show_minimize: bool,
pub theme: Theme,
pub title: String,
pub window_width: u32,
} }
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Message { pub enum Message {
Close, DesktopInfo,
Drag,
KeyboardNav(keyboard_nav::Message),
Maximize,
Minimize,
NavBar(segmented_button::Entity),
None,
Page(page::Entity), Page(page::Entity),
PageMessage(crate::pages::Message), PageMessage(crate::pages::Message),
Search(search::Message),
ToggleNavBar,
ToggleNavBarCondensed,
WindowResize(u32, u32),
WindowState(WindowState),
PanelConfig(CosmicPanelConfig), PanelConfig(CosmicPanelConfig),
DesktopInfo, Search(search::Message),
ThemeChanged(Theme), SetWindowTitle,
} }
impl Application for SettingsApp { impl cosmic::Application for SettingsApp {
type Executor = cosmic::executor::single::Executor; type Executor = cosmic::executor::single::Executor;
type Flags = (); type Flags = ();
type Message = Message; type Message = Message;
type Theme = Theme;
fn new(_: Self::Flags) -> (Self, Command<Self::Message>) { const APP_ID: &'static str = "com.system76.CosmicSettings";
fn core(&self) -> &Core {
&self.core
}
fn core_mut(&mut self) -> &mut Core {
&mut self.core
}
fn init(core: Core, _flags: Self::Flags) -> (Self, Command<Self::Message>) {
let mut app = SettingsApp { let mut app = SettingsApp {
sharp_corners: false,
active_page: page::Entity::default(), active_page: page::Entity::default(),
config: Config::new(), config: Config::new(),
debug: false, core,
nav_bar: segmented_button::Model::default(), nav_model: nav_bar::Model::default(),
nav_bar_toggled: true,
nav_bar_toggled_condensed: false,
pages: page::Binder::default(), pages: page::Binder::default(),
title: crate::fl!("app"),
scaling_factor: std::env::var("COSMIC_SCALE")
.ok()
.and_then(|scale| scale.parse::<f32>().ok())
.unwrap_or(1.0),
search: search::Model::default(), search: search::Model::default(),
search_selections: Vec::default(), search_selections: Vec::default(),
show_maximize: true,
show_minimize: true,
window_width: 0,
theme: cosmic::theme::theme(),
}; };
// app.insert_page::<wifi::Page>();
// app.insert_page::<networking::Page>();
// app.insert_page::<bluetooth::Page>();
let desktop_id = app.insert_page::<desktop::Page>().id(); let desktop_id = app.insert_page::<desktop::Page>().id();
// app.insert_page::<panel::Page>();
// app.insert_page::<dock::Page>();
// app.insert_page::<input::Page>();
// app.insert_page::<displays::Page>();
// app.insert_page::<power::Page>();
app.insert_page::<sound::Page>(); app.insert_page::<sound::Page>();
// app.insert_page::<printers::Page>();
// app.insert_page::<privacy::Page>();
app.insert_page::<system::Page>(); app.insert_page::<system::Page>();
app.insert_page::<time::Page>(); app.insert_page::<time::Page>();
// app.insert_page::<accessibility::Page>();
// app.insert_page::<applications::Page>();
//
app.insert_page::<input::Page>(); app.insert_page::<input::Page>();
let active_id = app let active_id = app
.pages .pages
.info .find_page_by_id(&app.config.active_page)
.iter()
.find(|(_id, info)| info.id == *app.config.active_page)
.map_or(desktop_id, |(id, _info)| id); .map_or(desktop_id, |(id, _info)| id);
let command = app.activate_page(active_id); let command = app.activate_page(active_id);
@ -161,19 +98,49 @@ impl Application for SettingsApp {
(app, command) (app, command)
} }
fn title(&self) -> String { fn nav_model(&self) -> Option<&nav_bar::Model> {
self.title.clone() Some(&self.nav_model)
}
fn on_close_requested(&self, id: window::Id) -> Option<Self::Message> {
let message = if id == applets_inner::ADD_PANEL_APPLET_DIALOGUE_ID {
Message::PageMessage(crate::pages::Message::PanelApplet(
applets_inner::Message::ClosedAppletDialogue,
))
} else if id == ADD_DOCK_APPLET_DIALOGUE_ID {
Message::PageMessage(crate::pages::Message::DockApplet(dock::applets::Message(
applets_inner::Message::ClosedAppletDialogue,
)))
} else {
return None;
};
Some(message)
}
fn on_escape(&mut self) -> Command<Self::Message> {
if self.search.is_active() {
self.search.state = search::State::Inactive;
self.search_clear();
}
Command::none()
}
fn on_nav_select(&mut self, id: nav_bar::Id) -> Command<Self::Message> {
if let Some(page) = self.nav_model.data::<page::Entity>(id).copied() {
return self.activate_page(page);
}
Command::none()
}
fn on_search(&mut self) -> Command<Self::Message> {
self.search.focus()
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
let window_break = subscription::events_with(|event, _| match event { let window_break = subscription::events_with(|event, _| match event {
iced::Event::Window(_window_id, window::Event::Resized { width, height }) => {
Some(Message::WindowResize(width, height))
}
iced::Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::Window(
WindowEvent::State(s),
..,
))) => Some(Message::WindowState(s)),
iced::Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::Output( iced::Event::PlatformSpecific(PlatformSpecific::Wayland(wayland::Event::Output(
wayland::OutputEvent::Created(Some(info)), wayland::OutputEvent::Created(Some(info)),
o, o,
@ -191,7 +158,6 @@ impl Application for SettingsApp {
Subscription::batch(vec![ Subscription::batch(vec![
window_break, window_break,
keyboard_nav::subscription().map(Message::KeyboardNav),
desktop_files(0).map(|_| Message::DesktopInfo), desktop_files(0).map(|_| Message::DesktopInfo),
config_subscription(0, "com.system76.CosmicPanel.Panel".into(), 1).map( config_subscription(0, "com.system76.CosmicPanel.Panel".into(), 1).map(
|(_, e)| match e { |(_, e)| match e {
@ -215,64 +181,28 @@ impl Application for SettingsApp {
} }
}, },
), ),
theme_subscription(0).map(Message::ThemeChanged),
]) ])
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn update(&mut self, message: Message) -> iced::Command<Self::Message> { fn update(&mut self, message: Message) -> Command<Self::Message> {
let mut ret = Command::none();
match message { match message {
Message::WindowResize(_width, _height) => {} Message::Page(page) => return self.activate_page(page),
Message::KeyboardNav(message) => match message {
keyboard_nav::Message::Unfocus => ret = keyboard_nav::unfocus(), Message::SetWindowTitle => return self.set_window_title(),
keyboard_nav::Message::FocusNext => ret = widget::focus_next(),
keyboard_nav::Message::FocusPrevious => ret = widget::focus_previous(),
keyboard_nav::Message::Escape => {
if self.search.is_active() {
self.search.state = search::State::Inactive;
self.search_clear();
}
}
keyboard_nav::Message::Search => {
return self.search.focus();
}
},
Message::Page(page) => {
return self.activate_page(page);
}
Message::Drag => return start_drag_window(window::Id(0)),
Message::Close => {
process::exit(0);
}
Message::Minimize => return set_mode_window(window::Id(0), Mode::Hidden),
Message::Maximize => {
if self.sharp_corners {
self.sharp_corners = false;
return set_mode_window(window::Id(0), Mode::Windowed);
}
self.sharp_corners = true;
return set_mode_window(window::Id(0), Mode::Fullscreen);
}
Message::NavBar(key) => {
if let Some(page) = self.nav_bar.data::<page::Entity>(key).copied() {
return self.activate_page(page);
}
}
Message::ToggleNavBar => self.nav_bar_toggled = !self.nav_bar_toggled,
Message::ToggleNavBarCondensed => {
self.nav_bar_toggled_condensed = !self.nav_bar_toggled_condensed;
}
Message::Search(search::Message::Activate) => { Message::Search(search::Message::Activate) => {
return self.search.focus(); return self.search.focus();
} }
Message::Search(search::Message::Changed(phrase)) => { Message::Search(search::Message::Changed(phrase)) => {
self.search_changed(phrase); self.search_changed(phrase);
} }
Message::Search(search::Message::Clear) => { Message::Search(search::Message::Clear) => {
self.search_clear(); self.search_clear();
} }
Message::PageMessage(message) => match message { Message::PageMessage(message) => match message {
crate::pages::Message::About(message) => { crate::pages::Message::About(message) => {
page::update!(self.pages, message, system::about::Page); page::update!(self.pages, message, system::about::Page);
@ -288,7 +218,7 @@ impl Application for SettingsApp {
} }
crate::pages::Message::Input(message) => { crate::pages::Message::Input(message) => {
if let Some(page) = self.pages.page_mut::<input::Page>() { if let Some(page) = self.pages.page_mut::<input::Page>() {
return page.update(message); return page.update(message).map(cosmic::app::Message::App);
} }
} }
crate::pages::Message::External { .. } => { crate::pages::Message::External { .. } => {
@ -302,7 +232,9 @@ impl Application for SettingsApp {
} }
crate::pages::Message::PanelApplet(message) => { crate::pages::Message::PanelApplet(message) => {
if let Some(page) = self.pages.page_mut::<applets_inner::Page>() { if let Some(page) = self.pages.page_mut::<applets_inner::Page>() {
return page.update(message, applets_inner::ADD_PANEL_APPLET_DIALOGUE_ID); return page
.update(message, applets_inner::ADD_PANEL_APPLET_DIALOGUE_ID)
.map(cosmic::app::Message::App);
} }
} }
crate::pages::Message::Dock(message) => { crate::pages::Message::Dock(message) => {
@ -310,13 +242,11 @@ impl Application for SettingsApp {
} }
crate::pages::Message::DockApplet(message) => { crate::pages::Message::DockApplet(message) => {
if let Some(page) = self.pages.page_mut::<dock::applets::Page>() { if let Some(page) = self.pages.page_mut::<dock::applets::Page>() {
return page.update(message); return page.update(message).map(cosmic::app::Message::App);
} }
} }
}, },
Message::WindowState(state) => {
self.sharp_corners = matches!(state, WindowState::Activated);
}
Message::PanelConfig(config) if config.name.to_lowercase().contains("panel") => { Message::PanelConfig(config) if config.name.to_lowercase().contains("panel") => {
page::update!( page::update!(
self.pages, self.pages,
@ -325,12 +255,15 @@ impl Application for SettingsApp {
); );
if let Some(page) = self.pages.page_mut::<applets_inner::Page>() { if let Some(page) = self.pages.page_mut::<applets_inner::Page>() {
return page.update( return page
applets_inner::Message::PanelConfig(config), .update(
applets_inner::ADD_PANEL_APPLET_DIALOGUE_ID, applets_inner::Message::PanelConfig(config),
); applets_inner::ADD_PANEL_APPLET_DIALOGUE_ID,
)
.map(cosmic::app::Message::App);
} }
} }
Message::PanelConfig(config) if config.name.to_lowercase().contains("dock") => { Message::PanelConfig(config) if config.name.to_lowercase().contains("dock") => {
page::update!( page::update!(
self.pages, self.pages,
@ -343,6 +276,7 @@ impl Application for SettingsApp {
dock::applets::Page dock::applets::Page
); );
} }
Message::DesktopInfo => { Message::DesktopInfo => {
let info_list: Vec<_> = freedesktop_desktop_entry::Iter::new( let info_list: Vec<_> = freedesktop_desktop_entry::Iter::new(
freedesktop_desktop_entry::default_paths(), freedesktop_desktop_entry::default_paths(),
@ -356,22 +290,45 @@ impl Application for SettingsApp {
dock::applets::Page dock::applets::Page
); );
if let Some(page) = self.pages.page_mut::<applets_inner::Page>() { if let Some(page) = self.pages.page_mut::<applets_inner::Page>() {
return page.update( return page
applets_inner::Message::Applets(info_list), .update(
applets_inner::ADD_PANEL_APPLET_DIALOGUE_ID, applets_inner::Message::Applets(info_list),
); applets_inner::ADD_PANEL_APPLET_DIALOGUE_ID,
)
.map(cosmic::app::Message::App);
} }
} }
Message::ThemeChanged(theme) => {
self.theme = theme; Message::PanelConfig(_) | Message::Search(_) => {} // Ignored
}
Message::PanelConfig(_) | Message::None | Message::Search(_) => {} // Ignored
} }
ret
Command::none()
}
fn view(&self) -> Element<Message> {
let page_view = if self.search.is_active() {
self.search_view()
} else if let Some(content) = self.pages.content(self.active_page) {
self.page_view(content)
} else if let Some(sub_pages) = self.pages.sub_pages(self.active_page) {
self.sub_page_view(sub_pages)
} else {
panic!("page without sub-pages or content");
};
container(page_view)
.max_width(800)
.width(Length::Fill)
.apply(container)
.center_x()
.padding([0, 64])
.width(Length::Fill)
.apply(scrollable)
.into()
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn view(&self, id: window::Id) -> Element<Message> { fn view_window(&self, id: window::Id) -> Element<Message> {
if let Some(Some(page)) = if let Some(Some(page)) =
(id == APPLET_DND_ICON_ID).then(|| self.pages.page::<applets_inner::Page>()) (id == APPLET_DND_ICON_ID).then(|| self.pages.page::<applets_inner::Page>())
{ {
@ -400,129 +357,13 @@ impl Application for SettingsApp {
return page.special_character_key_view(); return page.special_character_key_view();
} }
cosmic::iced::widget::responsive(|size| { panic!("unknown window ID: {id:?}");
let is_condensed = (600.0 * self.scaling_factor) > size.width;
let narrow_navbar = (700.0 * self.scaling_factor) > size.width;
let (nav_bar_message, nav_bar_toggled) = if is_condensed {
(
Message::ToggleNavBarCondensed,
self.nav_bar_toggled_condensed,
)
} else {
(Message::ToggleNavBar, self.nav_bar_toggled)
};
let mut header = header_bar()
.title("")
.on_close(Message::Close)
.on_drag(Message::Drag)
.start(
iced::widget::row!(
nav_bar_toggle()
.on_nav_bar_toggled(nav_bar_message)
.nav_bar_active(nav_bar_toggled),
search::search(&self.search, Message::Search)
)
.align_items(iced::Alignment::Center)
.into(),
);
if self.show_maximize {
header = header.on_maximize(Message::Maximize);
}
if self.show_minimize {
header = header.on_minimize(Message::Minimize);
}
let header = Into::<Element<Message>>::into(header).debug(self.debug);
let mut widgets = Vec::with_capacity(2);
if nav_bar_toggled {
let mut nav_bar = nav_bar(&self.nav_bar, Message::NavBar);
if !is_condensed {
nav_bar = nav_bar.max_width(if narrow_navbar { 200 } else { 300 });
}
let nav_bar: Element<_> = nav_bar.into();
widgets.push(nav_bar.debug(self.debug));
}
if !(is_condensed && nav_bar_toggled) {
widgets.push(
scrollable(row![
horizontal_space(Length::Fill),
(if self.search.is_active() {
self.search_view()
} else if let Some(content) = self.pages.content(self.active_page) {
self.page_view(content)
} else if let Some(sub_pages) = self.pages.sub_pages(self.active_page) {
self.sub_page_view(sub_pages)
} else {
panic!("page without sub-pages or content");
})
.debug(self.debug),
horizontal_space(Length::Fill),
])
.into(),
);
}
let content = container(row(widgets).spacing(8))
.padding([0, 8, 8, 8])
.width(Length::Fill)
.height(Length::Fill)
.style(theme::Container::Background)
.into();
column(vec![header, content]).into()
})
.into()
}
fn theme(&self) -> Theme {
self.theme.clone()
}
fn scale_factor(&self) -> f64 {
self.scaling_factor as f64
}
fn close_requested(&self, id: window::Id) -> Self::Message {
if id == window::Id(0) {
Message::Close
} else if id == applets_inner::ADD_PANEL_APPLET_DIALOGUE_ID {
Message::PageMessage(crate::pages::Message::PanelApplet(
applets_inner::Message::ClosedAppletDialogue,
))
} else if id == ADD_DOCK_APPLET_DIALOGUE_ID {
Message::PageMessage(crate::pages::Message::DockApplet(dock::applets::Message(
applets_inner::Message::ClosedAppletDialogue,
)))
} else {
Message::None
}
}
fn style(&self) -> <Self::Theme as cosmic::iced_style::application::StyleSheet>::Style {
if self.sharp_corners {
cosmic::theme::Application::default()
} else {
cosmic::theme::Application::Custom(Box::new(|theme| application::Appearance {
background_color: Color::TRANSPARENT,
text_color: theme.cosmic().on_bg_color().into(),
}))
}
} }
} }
impl SettingsApp { impl SettingsApp {
/// Activates a page. /// Activates a page.
fn activate_page(&mut self, page: page::Entity) -> Command<crate::Message> { fn activate_page(&mut self, page: page::Entity) -> Command<crate::Message> {
self.nav_bar_toggled_condensed = false;
let current_page = self.active_page; let current_page = self.active_page;
self.active_page = page; self.active_page = page;
@ -536,10 +377,25 @@ impl SettingsApp {
self.search.state = search::State::Inactive; self.search.state = search::State::Inactive;
self.activate_navbar(page); self.activate_navbar(page);
self.pages let page_command = self
.pages
.page_reload(page) .page_reload(page)
.unwrap_or(Command::none()) .unwrap_or(iced::Command::none())
.map(Message::PageMessage) .map(Message::PageMessage)
.map(cosmic::app::Message::App);
Command::batch(vec![
page_command,
cosmic::command::future(async { Message::SetWindowTitle })
.map(cosmic::app::Message::App),
])
}
fn set_window_title(&self) -> Command<crate::Message> {
cosmic::app::command::set_title(format!(
"{} - COSMIC Settings",
self.pages.info[self.active_page].title
))
} }
/// Activates the navbar item associated with a page. /// Activates the navbar item associated with a page.
@ -549,7 +405,7 @@ impl SettingsApp {
} }
if let Some(nav_id) = self.pages.data(page) { if let Some(nav_id) = self.pages.data(page) {
self.nav_bar.activate(*nav_id); self.nav_model.activate(*nav_id);
} }
} }
@ -569,10 +425,10 @@ impl SettingsApp {
fn navbar_insert(&mut self, id: page::Entity) -> segmented_button::SingleSelectEntityMut { fn navbar_insert(&mut self, id: page::Entity) -> segmented_button::SingleSelectEntityMut {
let page = &self.pages.info[id]; let page = &self.pages.info[id];
self.nav_bar self.nav_model
.insert() .insert()
.text(page.title.clone()) .text(page.title.clone())
.icon(IconSource::from(page.icon_name.clone())) .icon(icon::from_name(&*page.icon_name).into())
.data(id) .data(id)
.with_id(|nav_id| self.pages.data_set(id, nav_id)) .with_id(|nav_id| self.pages.data_set(id, nav_id))
} }
@ -583,9 +439,9 @@ impl SettingsApp {
let mut column_widgets = Vec::with_capacity(1); let mut column_widgets = Vec::with_capacity(1);
if let Some(parent) = page.parent { if let Some(parent) = page.parent {
column_widgets.push(parent_page_button( column_widgets.push(navigation::sub_page_header(
&self.pages.info[parent], page.title.as_str(),
page, self.pages.info[parent].title.as_str(),
Message::Page(parent), Message::Page(parent),
)); ));
} }
@ -600,10 +456,7 @@ impl SettingsApp {
); );
} }
settings::view_column(column_widgets) settings::view_column(column_widgets).padding(0).into()
.max_width(683)
.padding(0)
.into()
} }
fn search_changed(&mut self, phrase: String) { fn search_changed(&mut self, phrase: String) {
@ -667,18 +520,22 @@ impl SettingsApp {
/// Displays the sub-pages view of a page. /// Displays the sub-pages view of a page.
fn sub_page_view(&self, sub_pages: &[page::Entity]) -> cosmic::Element<Message> { fn sub_page_view(&self, sub_pages: &[page::Entity]) -> cosmic::Element<Message> {
let page = &self.pages.info[self.active_page]; let mut page_list = column::with_capacity(sub_pages.len()).spacing(18);
let mut column_widgets = Vec::with_capacity(sub_pages.len());
column_widgets.push(page_title(page));
for entity in sub_pages.iter().copied() { for entity in sub_pages.iter().copied() {
let sub_page = &self.pages.info[entity]; let sub_page = &self.pages.info[entity];
column_widgets.push(sub_page_button(entity, sub_page)); page_list = page_list.push(navigation::page_list_item(
sub_page.title.as_str(),
sub_page.description.as_str(),
&sub_page.icon_name,
entity,
));
} }
settings::view_column(column_widgets) column::with_capacity(2)
.apply(Element::from) .push(page_title(&self.pages.info[self.active_page]))
.map(Message::Page) .push(Element::from(page_list).map(Message::Page))
.spacing(24)
.into()
} }
} }

View file

@ -19,7 +19,7 @@ pub mod widget;
pub mod subscription; pub mod subscription;
use cosmic::{ use cosmic::{
iced::{wayland::actions::window::SctkWindowSettings, Application, Limits}, iced::{wayland::actions::window::SctkWindowSettings, Limits},
iced_sctk::settings::InitialSurface, iced_sctk::settings::InitialSurface,
}; };
use i18n_embed::DesktopLanguageRequester; use i18n_embed::DesktopLanguageRequester;
@ -38,17 +38,10 @@ pub fn main() -> color_eyre::Result<()> {
init_logger(); init_logger();
init_localizer(); init_localizer();
cosmic::settings::set_default_icon_theme("Pop"); let settings = cosmic::app::Settings::default()
let mut settings = cosmic::settings(); .size_limits(Limits::NONE.min_width(400.0).min_height(300.0));
settings.default_text_size = 14.0;
settings.initial_surface = InitialSurface::XdgWindow(SctkWindowSettings {
title: Some(fl!("app")),
size_limits: Limits::NONE.min_width(400.0).min_height(300.0),
app_id: Some("com.system76.CosmicSettings".to_string()),
..Default::default()
});
SettingsApp::run(settings)?; cosmic::app::run::<app::SettingsApp>(settings, ())?;
Ok(()) Ok(())
} }

View file

@ -4,10 +4,9 @@
use super::Message; use super::Message;
use apply::Apply; use apply::Apply;
use cosmic::{ use cosmic::{
iced::widget::{button, container, horizontal_space, row},
iced::Length, iced::Length,
theme, theme,
widget::{icon, list, settings, toggler}, widget::{button, container, horizontal_space, icon, list, row, settings, toggler},
Element, Element,
}; };
@ -117,18 +116,19 @@ pub fn panel_dock_links() -> Section<crate::pages::Message> {
settings = if let Some((panel_entity, panel_info)) = settings = if let Some((panel_entity, panel_info)) =
binder.info.iter().find(|(_, v)| v.id == "panel") binder.info.iter().find(|(_, v)| v.id == "panel")
{ {
let control = row::with_children(vec![
horizontal_space(Length::Fill).into(),
icon::from_name("go-next-symbolic").size(16).into(),
]);
settings.add( settings.add(
settings::item::builder(panel_info.title.clone()) settings::item::builder(panel_info.title.clone())
.description(panel_info.description.clone()) .description(panel_info.description.clone())
.control(row!( .control(control)
horizontal_space(Length::Fill),
icon("go-next-symbolic", 20).style(theme::Svg::Symbolic)
))
.spacing(16) .spacing(16)
.apply(container) .apply(container)
.style(theme::Container::custom(list::column::style)) .style(theme::Container::custom(list::style))
.apply(button) .apply(button)
.padding(0)
.style(theme::Button::Transparent) .style(theme::Button::Transparent)
.on_press(crate::pages::Message::Page(panel_entity)), .on_press(crate::pages::Message::Page(panel_entity)),
) )
@ -139,18 +139,19 @@ pub fn panel_dock_links() -> Section<crate::pages::Message> {
settings = if let Some((dock_entity, dock_info)) = settings = if let Some((dock_entity, dock_info)) =
binder.info.iter().find(|(_, v)| v.id == "dock") binder.info.iter().find(|(_, v)| v.id == "dock")
{ {
let control = row::with_children(vec![
horizontal_space(Length::Fill).into(),
icon::from_name("go-next-symbolic").size(16).into(),
]);
settings.add( settings.add(
settings::item::builder(dock_info.title.clone()) settings::item::builder(dock_info.title.clone())
.description(dock_info.description.clone()) .description(dock_info.description.clone())
.control(row!( .control(control)
horizontal_space(Length::Fill),
icon("go-next-symbolic", 20).style(theme::Svg::Symbolic)
))
.spacing(16) .spacing(16)
.apply(container) .apply(container)
.style(theme::Container::custom(list::column::style)) .style(theme::Container::custom(list::style))
.apply(button) .apply(button)
.padding(0)
.style(theme::Button::Transparent) .style(theme::Button::Transparent)
.on_press(crate::pages::Message::Page(dock_entity)), .on_press(crate::pages::Message::Page(dock_entity)),
) )

View file

@ -1,4 +1,11 @@
use apply::Apply; use button::StyleSheet as ButtonStyleSheet;
use cosmic::iced_style::container::StyleSheet;
use cosmic::iced_widget::text_input::{Icon, Side};
use cosmic::widget::{
button, column, container, header_bar, icon, list_column, row, scrollable, text, text_input,
Column,
};
use cosmic::{ use cosmic::{
cosmic_config::{Config, CosmicConfigEntry}, cosmic_config::{Config, CosmicConfigEntry},
iced::{ iced::{
@ -18,26 +25,18 @@ use cosmic::{
}, },
iced_runtime::{command::platform_specific, core::id::Id, Command}, iced_runtime::{command::platform_specific, core::id::Id, Command},
iced_sctk::commands, iced_sctk::commands,
iced_style::{
button::StyleSheet as ButtonStyleSheet, container::StyleSheet as ContainerStyleSheet,
},
iced_widget::{ iced_widget::{
column, container,
core::{ core::{
layout, renderer, layout, renderer,
widget::{tree, Operation, OperationOutputWrapper, Tree}, widget::{tree, Operation, OperationOutputWrapper, Tree},
Clipboard, Shell, Widget, Clipboard, Shell, Widget,
}, },
graphics::image::image_rs::EncodableLayout, graphics::image::image_rs::EncodableLayout,
row, scrollable, text, text_input,
text_input::{Icon, Side},
Column,
}, },
sctk::reexports::client::protocol::wl_data_device_manager::DndAction, sctk::reexports::client::protocol::wl_data_device_manager::DndAction,
theme, theme, Apply, Element,
widget::{button, header_bar, icon, list_column},
Element,
}; };
use std::{ use std::{
borrow::{Borrow, Cow}, borrow::{Borrow, Cow},
fmt::Debug, fmt::Debug,
@ -244,32 +243,49 @@ impl Page {
} }
has_some = true; has_some = true;
list_column = list_column.add( list_column = list_column.add(
row![ row::with_children(vec![
icon(info.icon.clone(), 32).style(theme::Svg::Symbolic), icon::from_name(&*info.icon)
column![ .size(32)
text(info.name.clone()), .symbolic(true)
text(info.description.clone()).size(10) .icon()
] .into(),
.spacing(4.0) column::with_capacity(2)
.width(Length::Fill), .push(text(info.name.clone()))
cosmic::iced::widget::button(text(fl!("add"))) .push(text(info.description.clone()).size(10))
.style(theme::Button::Custom { .spacing(4.0)
active: Box::new(|theme| { .width(Length::Fill)
let mut style = theme.active(&theme::Button::Text); .into(),
style.text_color = theme.cosmic().accent_color().into(); button(text(fl!("add")))
.style(button::Style::Custom {
active: Box::new(|focused, theme| {
let mut style = theme.active(focused, &button::Style::Text);
style.text_color = Some(theme.cosmic().accent_color().into());
style style
}), }),
hover: Box::new(|theme| { disabled: Box::new(|theme| {
let mut style = theme.hovered(&theme::Button::Text); let mut style = theme.disabled(&button::Style::Text);
style.text_color = theme.cosmic().accent_color().into(); let mut text_color: Color = theme.cosmic().accent_color().into();
text_color.a *= 0.5;
style.text_color = Some(text_color);
style style
}) }),
hovered: Box::new(|focused, theme| {
let mut style = theme.hovered(focused, &theme::Button::Text);
style.text_color = Some(theme.cosmic().accent_color().into());
style
}),
pressed: Box::new(|focused, theme| {
let mut style = theme.pressed(focused, &theme::Button::Text);
style.text_color = Some(theme.cosmic().accent_color().into());
style
}),
}) })
.padding(8.0) .padding(8.0)
.on_press(app::Message::PageMessage(msg_map(Message::AddApplet( .on_press(app::Message::PageMessage(msg_map(Message::AddApplet(
info.clone() info.clone(),
)))), ))))
] .into(),
])
.padding([0, 32, 0, 32]) .padding([0, 32, 0, 32])
.spacing(12) .spacing(12)
.align_items(Alignment::Center), .align_items(Alignment::Center),
@ -282,49 +298,43 @@ impl Page {
.horizontal_alignment(Horizontal::Center), .horizontal_alignment(Horizontal::Center),
); );
} }
column![ column::with_children(vec![
header_bar() header_bar()
.title(fl!("add-applet")) .title(fl!("add-applet"))
.on_close(app::Message::PageMessage(msg_map( .on_close(app::Message::PageMessage(msg_map(
Message::CloseAppletDialogue Message::CloseAppletDialogue,
))) )))
.on_drag(app::Message::PageMessage(msg_map( .on_drag(app::Message::PageMessage(msg_map(
Message::DragAppletDialogue Message::DragAppletDialogue,
))), )))
.into(),
container( container(
scrollable( scrollable(
column![ column::with_children(vec![
text(fl!("add-applet")).size(24).width(Length::Fill), text(fl!("add-applet")).size(24).width(Length::Fill).into(),
text_input(&fl!("search-applets"), &self.search) text_input::search_input(&fl!("search-applets"), &self.search, None)
.style(theme::TextInput::Search)
.padding([8, 24])
.icon(Icon {
font: cosmic::iced::Font::default(),
code_point: '🔍',
size: Some(12.0),
spacing: 12.0,
side: Side::Left,
})
.on_input(move |s| { .on_input(move |s| {
app::Message::PageMessage(msg_map(Message::Search(s))) app::Message::PageMessage(msg_map(Message::Search(s)))
}) })
.on_paste(move |s| { .on_paste(move |s| {
app::Message::PageMessage(msg_map(Message::Search(s))) app::Message::PageMessage(msg_map(Message::Search(s)))
}) })
.width(Length::Fixed(312.0)), .width(Length::Fixed(312.0))
list_column .into(),
] list_column.into(),
])
.padding([0, 64, 32, 64]) .padding([0, 64, 32, 64])
.align_items(Alignment::Center) .align_items(Alignment::Center)
.spacing(8.0) .spacing(8.0),
) )
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill) .height(Length::Fill),
) )
.style(theme::Container::Background) .style(theme::Container::Background)
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill) .height(Length::Fill)
] .into(),
])
.into() .into()
} }
@ -497,33 +507,36 @@ pub fn lists<
); );
}; };
let button = cosmic::iced::widget::button(text(fl!("add-applet"))) let button = button::standard(fl!("add-applet"));
.style(theme::Button::Secondary)
.padding(8.0); column::with_children(vec![
column![ column::with_children(vec![
column![ row::with_children(vec![
row![ text(fl!("applets")).width(Length::Fill).size(24).into(),
text(fl!("applets")).width(Length::Fill).size(24), (if page.has_dialogue {
if page.has_dialogue {
button button
} else { } else {
button.on_press(Message::AddAppletDialogue) button.on_press(Message::AddAppletDialogue)
} })
], .into(),
text(fl!("start-segment")), ])
.into(),
text(fl!("start-segment")).into(),
AppletReorderList::new( AppletReorderList::new(
config config
.plugins_wings .plugins_wings
.as_ref() .as_ref()
.map(|list| list .map(|list| {
.0 list.0
.iter()
.filter_map(|id| page
.available_entries
.iter() .iter()
.find(|e| e.id.as_ref() == id.as_str()) .filter_map(|id| {
.map(Applet::borrowed)) page.available_entries
.collect()) .iter()
.find(|e| e.id.as_ref() == id.as_str())
.map(Applet::borrowed)
})
.collect()
})
.unwrap_or_default(), .unwrap_or_default(),
Some((window::Id(0), APPLET_DND_ICON_ID)), Some((window::Id(0), APPLET_DND_ICON_ID)),
Message::StartDnd, Message::StartDnd,
@ -533,24 +546,28 @@ pub fn lists<
Message::ReorderStart, Message::ReorderStart,
Message::Save, Message::Save,
Message::Cancel, Message::Cancel,
page.reorder_widget_state.dragged_applet().as_ref() page.reorder_widget_state.dragged_applet().as_ref(),
) )
] .into(),
.spacing(8.0), ])
column![ .spacing(8.0)
text(fl!("center-segment")), .into(),
column::with_children(vec![
text(fl!("center-segment")).into(),
AppletReorderList::new( AppletReorderList::new(
config config
.plugins_center .plugins_center
.as_ref() .as_ref()
.map(|list| list .map(|list| {
.iter() list.iter()
.filter_map(|id| page .filter_map(|id| {
.available_entries page.available_entries
.iter() .iter()
.find(|e| e.id.as_ref() == id.as_str()) .find(|e| e.id.as_ref() == id.as_str())
.map(Applet::borrowed)) .map(Applet::borrowed)
.collect()) })
.collect()
})
.unwrap_or_default(), .unwrap_or_default(),
Some((window::Id(0), APPLET_DND_ICON_ID)), Some((window::Id(0), APPLET_DND_ICON_ID)),
Message::StartDnd, Message::StartDnd,
@ -560,25 +577,29 @@ pub fn lists<
Message::ReorderCenter, Message::ReorderCenter,
Message::Save, Message::Save,
Message::Cancel, Message::Cancel,
page.reorder_widget_state.dragged_applet().as_ref() page.reorder_widget_state.dragged_applet().as_ref(),
) )
] .into(),
.spacing(8.0), ])
column![ .spacing(8.0)
text(fl!("end-segment")), .into(),
column::with_children(vec![
text(fl!("end-segment")).into(),
AppletReorderList::new( AppletReorderList::new(
config config
.plugins_wings .plugins_wings
.as_ref() .as_ref()
.map(|list| list .map(|list| {
.1 list.1
.iter()
.filter_map(|id| page
.available_entries
.iter() .iter()
.find(|e| e.id.as_ref() == id.as_str()) .filter_map(|id| {
.map(Applet::borrowed)) page.available_entries
.collect()) .iter()
.find(|e| e.id.as_ref() == id.as_str())
.map(Applet::borrowed)
})
.collect()
})
.unwrap_or_default(), .unwrap_or_default(),
Some((window::Id(0), APPLET_DND_ICON_ID)), Some((window::Id(0), APPLET_DND_ICON_ID)),
Message::StartDnd, Message::StartDnd,
@ -588,11 +609,13 @@ pub fn lists<
Message::ReorderEnd, Message::ReorderEnd,
Message::Save, Message::Save,
Message::Cancel, Message::Cancel,
page.reorder_widget_state.dragged_applet().as_ref() page.reorder_widget_state.dragged_applet().as_ref(),
) )
] .into(),
.spacing(8.0), ])
] .spacing(8.0)
.into(),
])
.padding([0, 16, 0, 16]) .padding([0, 16, 0, 16])
.spacing(12.0) .spacing(12.0)
.apply(Element::from) .apply(Element::from)
@ -700,19 +723,27 @@ impl<'a, Message: 'static + Clone> AppletReorderList<'a, Message> {
let id_clone = info.id.to_string(); let id_clone = info.id.to_string();
let is_dragged = active_dnd.as_ref().map_or(false, |dnd| dnd.id == info.id); let is_dragged = active_dnd.as_ref().map_or(false, |dnd| dnd.id == info.id);
container( container(
row![ row::with_children(vec![
icon("open-menu-symbolic", 16).style(theme::Svg::Symbolic), icon::from_name("open-menu-symbolic")
icon(info.icon, 32).style(theme::Svg::Symbolic), .symbolic(true)
column![text(info.name), text(info.description).size(10)] .size(16)
.into(),
icon::from_name(info.icon).size(32).symbolic(true).into(),
column::with_capacity(2)
.spacing(4.0) .spacing(4.0)
.width(Length::Fill), .width(Length::Fill)
button(theme::Button::Text) .push(text(info.name))
.icon(theme::Svg::Symbolic, "edit-delete-symbolic", 16) .push(text::caption(info.description))
.on_press(on_remove(id_clone.clone())), .into(),
button(theme::Button::Text) button::icon(icon::from_name("edit-delete-symbolic"))
.icon(theme::Svg::Symbolic, "open-menu-symbolic", 16) .extra_small()
.on_press(on_details(id_clone)), .on_press(on_remove(id_clone.clone()))
] .into(),
button::icon(icon::from_name("open-menu-symbolic"))
.extra_small()
.on_press(on_details(id_clone))
.into(),
])
.spacing(12) .spacing(12)
.align_items(Alignment::Center), .align_items(Alignment::Center),
) )
@ -781,23 +812,28 @@ impl<'a, Message: 'static + Clone> AppletReorderList<'a, Message> {
surface_ids: None, surface_ids: None,
inner: if let Some(info) = state.dragged_applet() { inner: if let Some(info) = state.dragged_applet() {
container( container(
row![ row::with_children(vec![
icon("open-menu-symbolic", 16).style(theme::Svg::Symbolic), icon::from_name("open-menu-symbolic")
icon(info.icon.into_owned(), 32).style(theme::Svg::Symbolic), .size(16)
column![text(info.name), text(info.description).size(10)] .symbolic(true)
.into(),
icon::from_name(info.icon.into_owned())
.size(32)
.symbolic(true)
.into(),
column::with_capacity(2)
.spacing(4.0) .spacing(4.0)
.width(Length::Fill), .width(Length::Fill)
button(theme::Button::Text).icon( .push(text(info.name))
theme::Svg::Symbolic, .push(text::caption(info.description))
"edit-delete-symbolic", .into(),
16 button::icon(icon::from_name("edit-delete-symbolic"))
), .extra_small()
button(theme::Button::Text).icon( .into(),
theme::Svg::Symbolic, button::icon(icon::from_name("open-menu-symbolic"))
"open-menu-symbolic", .extra_small()
16 .into(),
), ])
]
.spacing(12) .spacing(12)
.align_items(Alignment::Center), .align_items(Alignment::Center),
) )
@ -939,6 +975,7 @@ where
renderer: &cosmic::Renderer, renderer: &cosmic::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) -> event::Status { ) -> event::Status {
let mut ret = match self.inner.as_widget_mut().on_event( let mut ret = match self.inner.as_widget_mut().on_event(
&mut tree.children[0], &mut tree.children[0],
@ -948,6 +985,7 @@ where
renderer, renderer,
clipboard, clipboard,
shell, shell,
viewport,
) { ) {
event::Status::Captured => return event::Status::Captured, event::Status::Captured => return event::Status::Captured,
event::Status::Ignored => event::Status::Ignored, event::Status::Ignored => event::Status::Ignored,

View file

@ -1,11 +1,12 @@
use cosmic::{ use cosmic::{
cosmic_config::{self, CosmicConfigEntry}, cosmic_config::{self, CosmicConfigEntry},
iced::widget::{button, container, horizontal_space, pick_list, row},
iced::Length, iced::Length,
iced_widget::slider, iced_widget::slider,
sctk::reexports::client::{backend::ObjectId, protocol::wl_output::WlOutput, Proxy}, sctk::reexports::client::{backend::ObjectId, protocol::wl_output::WlOutput, Proxy},
theme, theme,
widget::{icon, list, settings, text, toggler}, widget::{
button, container, horizontal_space, icon, list, pick_list, row, settings, text, toggler,
},
Element, Element,
}; };
@ -14,7 +15,7 @@ use cosmic_panel_config::{
AutoHide, CosmicPanelBackground, CosmicPanelConfig, CosmicPanelContainerConfig, AutoHide, CosmicPanelBackground, CosmicPanelConfig, CosmicPanelContainerConfig,
CosmicPanelOuput, PanelAnchor, PanelSize, CosmicPanelOuput, PanelAnchor, PanelSize,
}; };
use cosmic_settings_page::{self as page, section, Section}; use cosmic_settings_page::{self as page, Section};
use std::{borrow::Cow, collections::HashMap}; use std::{borrow::Cow, collections::HashMap};
pub struct PageInner { pub struct PageInner {
@ -149,8 +150,8 @@ pub(crate) fn style<
.add(settings::item( .add(settings::item(
&descriptions[3], &descriptions[3],
// TODO custom discrete slider variant // TODO custom discrete slider variant
row![ row::with_children(vec![
text(fl!("small")), text(fl!("small")).into(),
slider( slider(
0..=4, 0..=4,
match panel_config.size { match panel_config.size {
@ -173,20 +174,22 @@ pub(crate) fn style<
Message::PanelSize(PanelSize::XL) Message::PanelSize(PanelSize::XL)
} }
}, },
), )
text(fl!("large")) .into(),
] text(fl!("large")).into(),
])
.spacing(12), .spacing(12),
)) ))
.add(settings::item( .add(settings::item(
&descriptions[4], &descriptions[4],
row![ row::with_children(vec![
text(fl!("number", HashMap::from_iter(vec![("number", 0)]))), text(fl!("number", HashMap::from_iter(vec![("number", 0)]))).into(),
slider(0..=100, (panel_config.opacity * 100.0) as i32, |v| { slider(0..=100, (panel_config.opacity * 100.0) as i32, |v| {
Message::Opacity(v as f32 / 100.0) Message::Opacity(v as f32 / 100.0)
},), })
text(fl!("number", HashMap::from_iter(vec![("number", 100)]))), .into(),
] text(fl!("number", HashMap::from_iter(vec![("number", 100)]))).into(),
])
.spacing(12), .spacing(12),
)) ))
.apply(Element::from) .apply(Element::from)
@ -208,17 +211,18 @@ pub(crate) fn configuration<P: page::Page<crate::pages::Message> + PanelPage>(
.iter() .iter()
.find(|(_, v)| v.id == page.applets_page_id()) .find(|(_, v)| v.id == page.applets_page_id())
{ {
let control = row::with_children(vec![
horizontal_space(Length::Fill).into(),
icon::from_name("go-next-symbolic").size(16).into(),
]);
settings.add( settings.add(
settings::item::builder(&descriptions[0]) settings::item::builder(&descriptions[0])
.control(row!( .control(control)
horizontal_space(Length::Fill),
icon("go-next-symbolic", 20).style(theme::Svg::Symbolic)
))
.spacing(16) .spacing(16)
.apply(container) .apply(container)
.style(theme::Container::custom(list::column::style)) .style(theme::Container::custom(list::style))
.apply(button) .apply(button)
.padding(0)
.style(theme::Button::Transparent) .style(theme::Button::Transparent)
.on_press(crate::pages::Message::Page(panel_applets_entity)), .on_press(crate::pages::Message::Page(panel_applets_entity)),
) )

View file

@ -13,7 +13,7 @@ use apply::Apply;
use cosmic::widget::{ use cosmic::widget::{
list_column, list_column,
segmented_button::{self, SingleSelectModel}, segmented_button::{self, SingleSelectModel},
settings, toggler, segmented_selection, settings, text, toggler,
}; };
use cosmic::{iced::Length, Element}; use cosmic::{iced::Length, Element};
use cosmic::{iced_core::alignment, iced_runtime::core::image::Handle as ImageHandle}; use cosmic::{iced_core::alignment, iced_runtime::core::image::Handle as ImageHandle};
@ -591,7 +591,7 @@ pub fn settings() -> Section<crate::pages::Message> {
)); ));
children.push(if page.config.same_on_all { children.push(if page.config.same_on_all {
cosmic::widget::text(fl!("all-displays")) text(fl!("all-displays"))
.font(cosmic::font::FONT_SEMIBOLD) .font(cosmic::font::FONT_SEMIBOLD)
.horizontal_alignment(alignment::Horizontal::Center) .horizontal_alignment(alignment::Horizontal::Center)
.vertical_alignment(alignment::Vertical::Center) .vertical_alignment(alignment::Vertical::Center)
@ -602,7 +602,7 @@ pub fn settings() -> Section<crate::pages::Message> {
.height(Length::Fixed(32.0)) .height(Length::Fixed(32.0))
.into() .into()
} else { } else {
cosmic::widget::horizontal_segmented_selection(&page.outputs) segmented_selection::horizontal(&page.outputs)
.on_activate(Message::Output) .on_activate(Message::Output)
.into() .into()
}); });
@ -669,7 +669,6 @@ pub fn settings() -> Section<crate::pages::Message> {
cosmic::iced::widget::column(children) cosmic::iced::widget::column(children)
.spacing(22) .spacing(22)
.max_width(683)
.apply(Element::from) .apply(Element::from)
.map(crate::pages::Message::DesktopWallpaper) .map(crate::pages::Message::DesktopWallpaper)
}) })

View file

@ -2,10 +2,11 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use super::Message; use super::Message;
use apply::Apply;
use cosmic::iced_core::{self, gradient::Linear, Background, BorderRadius, Color, Degrees}; use cosmic::iced_core::{self, gradient::Linear, Background, BorderRadius, Color, Degrees};
use cosmic::iced_core::{alignment, Length}; use cosmic::iced_core::{alignment, Length};
use cosmic::iced_runtime::core::image::Handle as ImageHandle; use cosmic::iced_runtime::core::image::Handle as ImageHandle;
use cosmic::prelude::*;
use cosmic::widget::{button, container, image, space};
use cosmic::{iced, Element}; use cosmic::{iced, Element};
use cosmic_settings_desktop::wallpaper; use cosmic_settings_desktop::wallpaper;
use slotmap::DefaultKey; use slotmap::DefaultKey;
@ -18,9 +19,9 @@ const ROW_SPACING: u16 = 16;
/// A button for selecting a color or gradient. /// A button for selecting a color or gradient.
pub fn color_button(color: wallpaper::Color) -> Element<'static, Message> { pub fn color_button(color: wallpaper::Color) -> Element<'static, Message> {
iced::widget::button(color_image(color.clone(), COLOR_WIDTH, COLOR_WIDTH, 8.0)) button(color_image(color.clone(), COLOR_WIDTH, COLOR_WIDTH, 8.0))
.padding(0) .padding(0)
.style(cosmic::theme::Button::Transparent) .style(button::Style::IconVertical)
.on_press(Message::ColorSelect(color)) .on_press(Message::ColorSelect(color))
.into() .into()
} }
@ -32,9 +33,10 @@ pub fn color_image(
height: u16, height: u16,
border_radius: f32, border_radius: f32,
) -> Element<'static, Message> { ) -> Element<'static, Message> {
iced::widget::container(iced::widget::space::Space::new(width, height)) container(space::Space::new(width, height))
.style(cosmic::theme::Container::custom(move |_theme| { .style(cosmic::theme::Container::custom(move |_theme| {
iced::widget::container::Appearance { container::Appearance {
icon_color: None,
text_color: None, text_color: None,
background: Some(match &color { background: Some(match &color {
wallpaper::Color::Single([r, g, b]) => { wallpaper::Color::Single([r, g, b]) => {
@ -98,9 +100,9 @@ fn flex_select_row<'a>(
} }
fn wallpaper_button(handle: &ImageHandle, id: DefaultKey) -> Element<Message> { fn wallpaper_button(handle: &ImageHandle, id: DefaultKey) -> Element<Message> {
let image = iced::widget::image(handle.clone()).apply(iced::Element::from); let image = image(handle.clone()).apply(iced::Element::from);
iced::widget::button(image) button(image)
.padding(0) .padding(0)
.style(cosmic::theme::Button::Transparent) .style(cosmic::theme::Button::Transparent)
.on_press(Message::Select(id)) .on_press(Message::Select(id))

View file

@ -6,7 +6,7 @@ use cosmic::{
window, Length, window, Length,
}, },
iced_style, theme, iced_style, theme,
widget::settings, widget::{button, container, icon, radio, settings},
}; };
use cosmic_settings_page::{self as page, section, Section}; use cosmic_settings_page::{self as page, section, Section};
use slotmap::SlotMap; use slotmap::SlotMap;
@ -68,11 +68,11 @@ fn popover_menu_row(label: String) -> cosmic::Element<'static, Message> {
.style(cosmic::theme::Container::custom(|theme| { .style(cosmic::theme::Container::custom(|theme| {
iced_style::container::Appearance { iced_style::container::Appearance {
background: None, background: None,
..cosmic::widget::list::column::style(theme) ..cosmic::widget::list::style(theme)
} }
})) }))
.apply(widget::button) .apply(button)
.style(cosmic::theme::Button::Transparent) .style(theme::Button::Transparent)
.into() .into()
} }
@ -92,7 +92,8 @@ fn popover_menu() -> cosmic::Element<'static, Message> {
.height(Length::Shrink) .height(Length::Shrink)
.apply(cosmic::widget::container) .apply(cosmic::widget::container)
.style(cosmic::theme::Container::custom(|theme| { .style(cosmic::theme::Container::custom(|theme| {
iced_style::container::Appearance { container::Appearance {
icon_color: Some(theme.cosmic().background.on.into()),
text_color: Some(theme.cosmic().background.on.into()), text_color: Some(theme.cosmic().background.on.into()),
background: Some(iced::Color::from(theme.cosmic().background.base).into()), background: Some(iced::Color::from(theme.cosmic().background.base).into()),
border_radius: (12.0).into(), border_radius: (12.0).into(),
@ -104,18 +105,14 @@ fn popover_menu() -> cosmic::Element<'static, Message> {
} }
fn popover_button(input_source: &InputSource, expanded: bool) -> cosmic::Element<'static, Message> { fn popover_button(input_source: &InputSource, expanded: bool) -> cosmic::Element<'static, Message> {
let style = if expanded {
cosmic::theme::Svg::SymbolicActive
} else {
cosmic::theme::Svg::Symbolic
};
let on_press = Message::ExpandInputSourcePopover(if expanded { let on_press = Message::ExpandInputSourcePopover(if expanded {
None None
} else { } else {
Some(input_source.id.clone()) Some(input_source.id.clone())
}); });
let button = cosmic::widget::button(cosmic::theme::Button::Secondary)
.icon(style, "open-menu-symbolic", 20) let button = button::icon(icon::from_name("open-menu-symbolic"))
.extra_small()
.padding(0) .padding(0)
.on_press(on_press); .on_press(on_press);
@ -195,12 +192,9 @@ fn special_char_radio_row<'a>(
value: Option<&'static str>, value: Option<&'static str>,
current_value: Option<&'a str>, current_value: Option<&'a str>,
) -> cosmic::Element<'a, Message> { ) -> cosmic::Element<'a, Message> {
settings::item_row(vec![iced::widget::radio( settings::item_row(vec![radio(desc, value, Some(current_value), |_| {
desc, Message::SpecialCharacterSelect(value)
value, })
Some(current_value),
|_| Message::SpecialCharacterSelect(value),
)
.into()]) .into()])
.into() .into()
} }
@ -312,7 +306,7 @@ fn keyboard_shortcuts() -> Section<crate::pages::Message> {
fn go_next_control<Msg: Clone + 'static>() -> cosmic::Element<'static, Msg> { fn go_next_control<Msg: Clone + 'static>() -> cosmic::Element<'static, Msg> {
widget::row!( widget::row!(
horizontal_space(Length::Fill), horizontal_space(Length::Fill),
cosmic::widget::icon("go-next-symbolic", 20).style(cosmic::theme::Svg::Symbolic) icon::from_name("go-next-symbolic").size(16).icon(),
) )
.into() .into()
} }
@ -321,11 +315,10 @@ fn go_next_item<Msg: Clone + 'static>(description: &str, msg: Msg) -> cosmic::El
settings::item(description, go_next_control()) settings::item(description, go_next_control())
.apply(widget::container) .apply(widget::container)
.style(cosmic::theme::Container::custom( .style(cosmic::theme::Container::custom(
cosmic::widget::list::column::style, cosmic::widget::list::style,
)) ))
.apply(widget::button) .apply(button)
.style(cosmic::theme::Button::Transparent) .style(theme::Button::Transparent)
.padding(0)
.on_press(msg) .on_press(msg)
.into() .into()
} }

View file

@ -1,7 +1,5 @@
use apply::Apply; use cosmic::widget::{column, settings};
use cosmic::iced::widget; use cosmic::{Apply, Element};
use cosmic::widget::settings;
use cosmic::Element;
use cosmic_settings_page::Section; use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section}; use cosmic_settings_page::{self as page, section};
use slotmap::SlotMap; use slotmap::SlotMap;
@ -44,7 +42,8 @@ fn shortcuts() -> Section<crate::pages::Message> {
.apply(Element::from) .apply(Element::from)
.map(crate::pages::Message::Input) .map(crate::pages::Message::Input)
*/ */
widget::column![settings::view_section(&section.title)] column()
.push(settings::view_section(&section.title))
.apply(Element::from) .apply(Element::from)
.map(crate::pages::Message::Input) .map(crate::pages::Message::Input)
}) })

View file

@ -1,6 +1,5 @@
use apply::Apply; use apply::Apply;
use cosmic::iced::widget; use cosmic::widget::{self, settings};
use cosmic::widget::settings;
use cosmic::Element; use cosmic::Element;
use cosmic_comp_config::input::AccelProfile; use cosmic_comp_config::input::AccelProfile;
use cosmic_settings_page::Section; use cosmic_settings_page::Section;

View file

@ -1,6 +1,5 @@
use apply::Apply; use apply::Apply;
use cosmic::iced::widget; use cosmic::widget::{self, settings};
use cosmic::widget::settings;
use cosmic::Element; use cosmic::Element;
use cosmic_comp_config::input::AccelProfile; use cosmic_comp_config::input::AccelProfile;
use cosmic_settings_page::Section; use cosmic_settings_page::Section;

View file

@ -1,7 +1,7 @@
// Copyright 2023 System76 <info@system76.com> // Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use cosmic::{iced, widget::settings}; use cosmic::widget::{settings, text};
use cosmic_settings_page::{self as page, section, Section}; use cosmic_settings_page::{self as page, section, Section};
use slotmap::SlotMap; use slotmap::SlotMap;
@ -39,14 +39,8 @@ fn alerts() -> Section<crate::pages::Message> {
]) ])
.view::<Page>(|_binder, _page, section| { .view::<Page>(|_binder, _page, section| {
settings::view_section(&section.title) settings::view_section(&section.title)
.add(settings::item( .add(settings::item(&section.descriptions[0], text("TODO")))
&section.descriptions[0], .add(settings::item(&section.descriptions[1], text("TODO")))
iced::widget::text("TODO"),
))
.add(settings::item(
&section.descriptions[1],
iced::widget::text("TODO"),
))
.into() .into()
}) })
} }
@ -57,10 +51,7 @@ fn applications() -> Section<crate::pages::Message> {
.descriptions(vec![fl!("sound-applications", "desc")]) .descriptions(vec![fl!("sound-applications", "desc")])
.view::<Page>(|_binder, _page, section| { .view::<Page>(|_binder, _page, section| {
settings::view_section(&section.title) settings::view_section(&section.title)
.add(settings::item( .add(settings::item(&section.descriptions[0], text("TODO")))
&section.descriptions[0],
iced::widget::text("TODO"),
))
.into() .into()
}) })
} }
@ -75,18 +66,9 @@ fn input() -> Section<crate::pages::Message> {
]) ])
.view::<Page>(|_binder, _page, section| { .view::<Page>(|_binder, _page, section| {
settings::view_section(&section.title) settings::view_section(&section.title)
.add(settings::item( .add(settings::item(&section.descriptions[0], text("TODO")))
&section.descriptions[0], .add(settings::item(&section.descriptions[1], text("TODO")))
iced::widget::text("TODO"), .add(settings::item(&section.descriptions[2], text("TODO")))
))
.add(settings::item(
&section.descriptions[1],
iced::widget::text("TODO"),
))
.add(settings::item(
&section.descriptions[2],
iced::widget::text("TODO"),
))
.into() .into()
}) })
} }
@ -103,22 +85,10 @@ fn output() -> Section<crate::pages::Message> {
]) ])
.view::<Page>(|_binder, _page, section| { .view::<Page>(|_binder, _page, section| {
settings::view_section(&section.title) settings::view_section(&section.title)
.add(settings::item( .add(settings::item(&section.descriptions[0], text("TODO")))
&section.descriptions[0], .add(settings::item(&section.descriptions[1], text("TODO")))
iced::widget::text("TODO"), .add(settings::item(&section.descriptions[2], text("TODO")))
)) .add(settings::item(&section.descriptions[3], text("TODO")))
.add(settings::item(
&section.descriptions[1],
iced::widget::text("TODO"),
))
.add(settings::item(
&section.descriptions[2],
iced::widget::text("TODO"),
))
.add(settings::item(
&section.descriptions[3],
iced::widget::text("TODO"),
))
.into() .into()
}) })
} }

View file

@ -80,7 +80,7 @@ fn distributor_logo() -> Section<crate::pages::Message> {
.view::<Page>(|_binder, _page, _section| { .view::<Page>(|_binder, _page, _section| {
row!( row!(
horizontal_space(Length::Fill), horizontal_space(Length::Fill),
icon("distributor-logo", 78), icon::from_name("distributor-logo").size(78).icon(),
horizontal_space(Length::Fill), horizontal_space(Length::Fill),
) )
// Add extra padding to reach 40px from the first section. // Add extra padding to reach 40px from the first section.

View file

@ -5,7 +5,8 @@ use cosmic::{iced_widget::core::BorderRadius, theme};
#[must_use] #[must_use]
pub fn display_container_frame() -> cosmic::theme::Container { pub fn display_container_frame() -> cosmic::theme::Container {
theme::Container::custom(|_theme| cosmic::iced::widget::container::Appearance { theme::Container::custom(|_theme| cosmic::widget::container::Appearance {
icon_color: None,
text_color: None, text_color: None,
background: Some(cosmic::iced::Background::Color(cosmic::iced::Color::WHITE)), background: Some(cosmic::iced::Background::Color(cosmic::iced::Color::WHITE)),
border_color: cosmic::iced::Color::WHITE, border_color: cosmic::iced::Color::WHITE,
@ -16,7 +17,8 @@ pub fn display_container_frame() -> cosmic::theme::Container {
#[must_use] #[must_use]
pub fn display_container_screen() -> cosmic::theme::Container { pub fn display_container_screen() -> cosmic::theme::Container {
theme::Container::custom(|_theme| cosmic::iced::widget::container::Appearance { theme::Container::custom(|_theme| cosmic::widget::container::Appearance {
icon_color: None,
text_color: None, text_color: None,
background: Some(cosmic::iced::Background::Color(cosmic::iced::Color::BLACK)), background: Some(cosmic::iced::Background::Color(cosmic::iced::Color::BLACK)),
border_color: cosmic::iced::Color::BLACK, border_color: cosmic::iced::Color::BLACK,

View file

@ -2,13 +2,11 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use apply::Apply; use apply::Apply;
use cosmic::iced::{ use cosmic::iced::Length;
self, use cosmic::widget::{
widget::{button, column, container, horizontal_space, row, vertical_space, Button}, button, column, container, divider, horizontal_space, row, settings, text, vertical_space,
Length,
}; };
use cosmic::widget::{divider, icon, list, settings, text}; use cosmic::Element;
use cosmic::{theme, Element};
use cosmic_settings_page as page; use cosmic_settings_page as page;
#[must_use] #[must_use]
@ -41,87 +39,19 @@ pub fn search_header<Message>(
column_children.push(vertical_space(Length::Fixed(8.)).into()); column_children.push(vertical_space(Length::Fixed(8.)).into());
column_children.push(divider::horizontal::heavy().into()); column_children.push(divider::horizontal::heavy().into());
column(column_children).into() column::with_children(column_children).into()
} }
#[must_use] #[must_use]
pub fn search_page_link<Message: 'static>(title: &str) -> Button<Message, cosmic::Renderer> { pub fn search_page_link<Message: 'static>(title: &str) -> button::TextButton<Message> {
text(title) button::text(title).style(button::Style::Link)
.size(24)
.horizontal_alignment(iced::alignment::Horizontal::Left)
.apply(button)
.style(cosmic::theme::Button::Link)
} }
#[must_use] #[must_use]
pub fn page_title<Message: 'static>(page: &page::Info) -> Element<Message> { pub fn page_title<Message: 'static>(page: &page::Info) -> Element<Message> {
row!( row::with_capacity(2)
text(page.title.as_str()).size(24), .push(text::title3(page.title.as_str()))
horizontal_space(Length::Fill) .push(horizontal_space(Length::Fill))
)
.into()
}
#[must_use]
pub fn parent_page_button<'a, Message: Clone + 'static>(
parent: &'a page::Info,
sub_page: &'a page::Info,
on_press: Message,
) -> Element<'a, Message> {
column!(
button(row!(
icon("go-previous-symbolic", 20).style(theme::Svg::SymbolicLink),
text(parent.title.as_str()).size(14),
))
.padding(0)
.style(theme::Button::Link)
.on_press(on_press),
row!(
text(sub_page.title.as_str()).size(24),
horizontal_space(Length::Fill),
)
.align_items(iced::alignment::Alignment::Center),
)
.spacing(6)
.into()
}
#[must_use]
pub fn sub_page_button(entity: page::Entity, page: &page::Info) -> Element<page::Entity> {
settings::item::builder(page.title.as_str())
.description(page.description.as_str())
.icon(icon(&*page.icon_name, 20).style(theme::Svg::Symbolic))
.control(row!(
horizontal_space(Length::Fill),
icon("go-next-symbolic", 20).style(theme::Svg::Symbolic)
))
.spacing(16)
.apply(container)
.padding([20, 24])
.style(theme::Container::custom(list::column::style))
.apply(button)
.padding(0)
.style(theme::Button::Transparent)
.on_press(entity)
.into()
}
#[must_use]
pub fn sub_page_section(entity: page::Entity, page: &page::Info) -> Element<page::Entity> {
settings::item::builder(page.title.as_str())
.description(page.description.as_str())
.control(row!(
horizontal_space(Length::Fill),
icon("go-next-symbolic", 20).style(theme::Svg::Symbolic)
))
.spacing(16)
.apply(container)
.padding([20, 24])
.style(theme::Container::custom(list::column::style))
.apply(button)
.padding(0)
.style(theme::Button::Transparent)
.on_press(entity)
.into() .into()
} }
@ -134,15 +64,16 @@ pub fn unimplemented_page<Message: 'static>() -> Element<'static, Message> {
#[must_use] #[must_use]
pub fn display_container<'a, Message: 'a>(widget: Element<'a, Message>) -> Element<'a, Message> { pub fn display_container<'a, Message: 'a>(widget: Element<'a, Message>) -> Element<'a, Message> {
row!( let display = container(widget)
horizontal_space(Length::Fill), .style(crate::theme::display_container_screen())
container(widget) .apply(container)
.style(crate::theme::display_container_screen()) .padding(4)
.apply(container) .style(crate::theme::display_container_frame());
.padding(4)
.style(crate::theme::display_container_frame()), row::with_capacity(3)
horizontal_space(Length::Fill), .push(horizontal_space(Length::Fill))
) .push(display)
.padding([0, 0, 8, 0]) .push(horizontal_space(Length::Fill))
.into() .padding([0, 0, 8, 0])
.into()
} }

View file

@ -85,6 +85,11 @@ impl<Message: 'static> Binder<Message> {
.and_then(|storage| storage.remove(id)); .and_then(|storage| storage.remove(id));
} }
#[must_use]
pub fn find_page_by_id(&self, id: &str) -> Option<(crate::Entity, &Info)> {
self.info.iter().find(|(_id, info)| info.id == id)
}
/// Registers a new page in the settings panel. /// Registers a new page in the settings panel.
pub fn register<P: AutoBind<Message>>(&mut self) -> crate::Insert<Message> { pub fn register<P: AutoBind<Message>>(&mut self) -> crate::Insert<Message> {
let page = P::default(); let page = P::default();