fix(wallpapers): use context drawer for color picker

This commit is contained in:
Michael Aaron Murphy 2024-08-22 02:16:23 +02:00
parent 6df4650ff9
commit 544da2bdc8
No known key found for this signature in database
GPG key ID: B2732D4240C9212C
4 changed files with 157 additions and 173 deletions

View file

@ -603,12 +603,6 @@ impl cosmic::Application for SettingsApp {
return page.dnd_icon(); return page.dnd_icon();
} }
if let Some(page) = self.pages.page::<desktop::wallpaper::Page>() {
if id == page.color_dialog {
return page.show_color_dialog();
}
}
panic!("unknown window ID: {id:?}"); panic!("unknown window ID: {id:?}");
} }

View file

@ -33,6 +33,7 @@ use slotmap::SlotMap;
use tokio::io::AsyncBufReadExt; use tokio::io::AsyncBufReadExt;
use crate::app; use crate::app;
use crate::widget::color_picker_context_view;
use super::wallpaper::widgets::color_image; use super::wallpaper::widgets::color_image;
@ -423,40 +424,6 @@ impl Page {
Ok(()) Ok(())
} }
fn color_picker_context_view(
&self,
description: Option<Cow<'static, str>>,
reset: Cow<'static, str>,
on_update: fn(ColorPickerUpdate) -> Message,
model: impl Fn(&Self) -> &ColorPickerModel,
) -> Element<'_, crate::pages::Message> {
cosmic::widget::column()
.push_maybe(description.map(|description| text::body(description).width(Length::Fill)))
.push(
model(self)
.builder(on_update)
.reset_label(reset)
.height(Length::Fixed(158.0))
.build(
fl!("recent-colors"),
fl!("copy-to-clipboard"),
fl!("copied-to-clipboard"),
)
.apply(container)
.width(Length::Fixed(248.0))
.align_x(alignment::Horizontal::Center)
.apply(container)
.width(Length::Fill)
.align_x(alignment::Horizontal::Center),
)
.padding(self.theme_builder.spacing.space_l)
.align_items(cosmic::iced_core::Alignment::Center)
.spacing(self.theme_builder.spacing.space_m)
.width(Length::Fill)
.apply(Element::from)
.map(crate::pages::Message::Appearance)
}
fn experimental_context_view(&self) -> Element<'_, crate::pages::Message> { fn experimental_context_view(&self) -> Element<'_, crate::pages::Message> {
let active = self.icon_theme_active; let active = self.icon_theme_active;
let theme = cosmic::theme::active(); let theme = cosmic::theme::active();
@ -1084,49 +1051,55 @@ impl page::Page<crate::pages::Message> for Page {
fn context_drawer(&self) -> Option<Element<'_, crate::pages::Message>> { fn context_drawer(&self) -> Option<Element<'_, crate::pages::Message>> {
let view = match self.context_view? { let view = match self.context_view? {
ContextView::AccentWindowHint => self.color_picker_context_view( ContextView::AccentWindowHint => color_picker_context_view(
None, None,
RESET_TO_DEFAULT.as_str().into(), RESET_TO_DEFAULT.as_str().into(),
Message::AccentWindowHint, Message::AccentWindowHint,
|this| &this.accent_window_hint, &self.accent_window_hint,
), )
.map(crate::pages::Message::Appearance),
ContextView::ApplicationBackground => self.color_picker_context_view( ContextView::ApplicationBackground => color_picker_context_view(
None, None,
RESET_TO_DEFAULT.as_str().into(), RESET_TO_DEFAULT.as_str().into(),
Message::ApplicationBackground, Message::ApplicationBackground,
|this| &this.application_background, &self.application_background,
), )
.map(crate::pages::Message::Appearance),
ContextView::ContainerBackground => self.color_picker_context_view( ContextView::ContainerBackground => color_picker_context_view(
Some(fl!("container-background", "desc-detail").into()), Some(fl!("container-background", "desc-detail").into()),
fl!("container-background", "reset").into(), fl!("container-background", "reset").into(),
Message::ContainerBackground, Message::ContainerBackground,
|this| &this.container_background, &self.container_background,
), )
.map(crate::pages::Message::Appearance),
ContextView::ControlComponent => self.color_picker_context_view( ContextView::ControlComponent => color_picker_context_view(
None, None,
RESET_TO_DEFAULT.as_str().into(), RESET_TO_DEFAULT.as_str().into(),
Message::ControlComponent, Message::ControlComponent,
|this| &this.control_component, &self.control_component,
), )
.map(crate::pages::Message::Appearance),
ContextView::CustomAccent => self.color_picker_context_view( ContextView::CustomAccent => color_picker_context_view(
None, None,
RESET_TO_DEFAULT.as_str().into(), RESET_TO_DEFAULT.as_str().into(),
Message::CustomAccent, Message::CustomAccent,
|this| &this.custom_accent, &self.custom_accent,
), )
.map(crate::pages::Message::Appearance),
ContextView::Experimental => self.experimental_context_view(), ContextView::Experimental => self.experimental_context_view(),
ContextView::InterfaceText => self.color_picker_context_view( ContextView::InterfaceText => color_picker_context_view(
None, None,
RESET_TO_DEFAULT.as_str().into(), RESET_TO_DEFAULT.as_str().into(),
Message::InterfaceText, Message::InterfaceText,
|this| &this.interface_text, &self.interface_text,
), )
.map(crate::pages::Message::Appearance),
}; };
Some(view) Some(view)

View file

@ -87,16 +87,14 @@ pub enum Message {
ChangeCategory(Category), ChangeCategory(Category),
/// Changes the displayed images in the wallpaper view. /// Changes the displayed images in the wallpaper view.
ChangeFolder(Context), ChangeFolder(Context),
/// Creates a color dialog
ColorAddDialog,
/// Handles messages from the color dialog. /// Handles messages from the color dialog.
ColorDialogUpdate(ColorPickerUpdate), ColorAdd(ColorPickerUpdate),
/// Creates a color context drawer
ColorAddContext,
/// Removes a custom color from the color view. /// Removes a custom color from the color view.
ColorRemove(wallpaper::Color), ColorRemove(wallpaper::Color),
/// Selects a color in the color view. /// Selects a color in the color view.
ColorSelect(wallpaper::Color), ColorSelect(wallpaper::Color),
/// Handles the drag message in the color dialog.
DragColorDialog,
/// Sets the wallpaper fit parameter. /// Sets the wallpaper fit parameter.
Fit(usize), Fit(usize),
/// Adds a new custom image to the wallpaper view. /// Adds a new custom image to the wallpaper view.
@ -141,8 +139,16 @@ pub enum Category {
Wallpapers, Wallpapers,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum ContextView {
AddColor,
}
/// The page struct for the wallpaper view. /// The page struct for the wallpaper view.
pub struct Page { pub struct Page {
/// Whether to show a context drawer.
context_view: Option<ContextView>,
/// Whether to show the tab_bar or not. /// Whether to show the tab_bar or not.
show_tab_bar: bool, show_tab_bar: bool,
@ -160,9 +166,6 @@ pub struct Page {
/// Model for the category dropdown, which has categories and recent folders. /// Model for the category dropdown, which has categories and recent folders.
categories: dropdown::multi::Model<String, Category>, categories: dropdown::multi::Model<String, Category>,
/// The window ID of the color dialog.
pub color_dialog: window::Id,
/// The color model updated by the color dialog. /// The color model updated by the color dialog.
color_model: ColorPickerModel, color_model: ColorPickerModel,
@ -252,6 +255,18 @@ impl page::Page<crate::pages::Message> for Page {
}))) })))
}) })
} }
fn context_drawer(&self) -> Option<Element<'_, crate::pages::Message>> {
self.context_view.map(|view| match view {
ContextView::AddColor => crate::widget::color_picker_context_view(
None,
fl!("reset-to-default").into(),
Message::ColorAdd,
&self.color_model,
)
.map(crate::pages::Message::DesktopWallpaper),
})
}
} }
impl page::AutoBind<crate::pages::Message> for Page {} impl page::AutoBind<crate::pages::Message> for Page {}
@ -259,6 +274,7 @@ impl page::AutoBind<crate::pages::Message> for Page {}
impl Default for Page { impl Default for Page {
fn default() -> Self { fn default() -> Self {
let mut page = Page { let mut page = Page {
context_view: None,
show_tab_bar: false, show_tab_bar: false,
active_output: None, active_output: None,
cached_display_handle: None, cached_display_handle: None,
@ -290,7 +306,6 @@ impl Default for Page {
categories categories
}, },
wallpaper_service_config: wallpaper::Config::default(), wallpaper_service_config: wallpaper::Config::default(),
color_dialog: window::Id::unique(),
color_model: ColorPickerModel::new(fl!("hex"), fl!("rgb"), None, Some(Color::WHITE)), color_model: ColorPickerModel::new(fl!("hex"), fl!("rgb"), None, Some(Color::WHITE)),
config: Config::new(), config: Config::new(),
fit_options: vec![fl!("fill"), fl!("fit-to-screen")], fit_options: vec![fl!("fill"), fl!("fit-to-screen")],
@ -669,46 +684,8 @@ impl Page {
} }
} }
Message::DragColorDialog => {
return cosmic::iced_sctk::commands::window::start_drag_window(self.color_dialog)
}
Message::CacheDisplayImage => self.cache_display_image(), Message::CacheDisplayImage => self.cache_display_image(),
Message::ColorDialogUpdate(update) => {
let cmd = match update {
ColorPickerUpdate::AppliedColor
| ColorPickerUpdate::Cancel
| ColorPickerUpdate::Reset => {
if let Some(color) = self.color_model.get_applied_color() {
let color = wallpaper::Color::Single([color.r, color.g, color.b]);
if let Err(why) = self.config.add_custom_color(color.clone()) {
tracing::error!(?why, "could not set custom color");
}
self.selection.add_custom_color(color);
}
close_window(self.color_dialog)
}
ColorPickerUpdate::ActionFinished => {
let _res = self
.color_model
.update::<crate::app::Message>(ColorPickerUpdate::AppliedColor);
Command::none()
}
_ => Command::none(),
};
return Command::batch(vec![
cmd,
self.color_model.update::<crate::app::Message>(update),
]);
}
Message::ChangeFolder(mut context) => { Message::ChangeFolder(mut context) => {
// Reassign custom colors and images to the new context. // Reassign custom colors and images to the new context.
std::mem::swap(&mut context, &mut self.selection); std::mem::swap(&mut context, &mut self.selection);
@ -730,8 +707,38 @@ impl Page {
self.select_first_wallpaper(); self.select_first_wallpaper();
} }
Message::ColorAddDialog => { Message::ColorAdd(message) => {
return get_window(color_picker_window_settings(self.color_dialog)); match message {
ColorPickerUpdate::ActionFinished => {
let _res = self
.color_model
.update::<crate::app::Message>(ColorPickerUpdate::AppliedColor);
if let Some(color) = self.color_model.get_applied_color() {
let color = wallpaper::Color::Single([color.r, color.g, color.b]);
if let Err(why) = self.config.add_custom_color(color.clone()) {
tracing::error!(?why, "could not set custom color");
}
self.selection.add_custom_color(color.clone());
self.selection.active = Choice::Color(color);
self.cached_display_handle = None;
self.context_view = None;
}
}
_ => (),
};
return self.color_model.update::<crate::app::Message>(message);
}
Message::ColorAddContext => {
self.context_view = Some(ContextView::AddColor);
return cosmic::command::message(crate::app::Message::OpenContextDrawer(
fl!("color-picker").into(),
));
} }
Message::ColorRemove(color) => { Message::ColorRemove(color) => {
@ -1053,15 +1060,6 @@ impl Page {
self.cache_display_image(); self.cache_display_image();
} }
pub fn show_color_dialog(&self) -> Element<crate::app::Message> {
color_picker_view(
&self.color_model,
Message::DragColorDialog,
Message::ColorDialogUpdate,
)
.map(|m| crate::app::Message::PageMessage(crate::pages::Message::DesktopWallpaper(m)))
}
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -1264,7 +1262,7 @@ pub fn settings() -> Section<crate::pages::Message> {
let add_button = let add_button =
if let Some(Category::Colors | Category::Wallpapers) = page.categories.selected { if let Some(Category::Colors | Category::Wallpapers) = page.categories.selected {
let (text, message) = if Some(Category::Colors) == page.categories.selected { let (text, message) = if Some(Category::Colors) == page.categories.selected {
(fl!("add-color"), Message::ColorAddDialog) (fl!("add-color"), Message::ColorAddContext)
} else { } else {
(fl!("add-image"), Message::ImageAddDialog) (fl!("add-image"), Message::ImageAddDialog)
}; };
@ -1329,62 +1327,42 @@ pub fn settings() -> Section<crate::pages::Message> {
}) })
} }
fn color_picker_window_settings(window_id: window::Id) -> SctkWindowSettings { // // TODO: Reuse with the appearance page
SctkWindowSettings { // pub fn color_picker_view<Message: Clone + 'static>(
window_id, // model: &ColorPickerModel,
app_id: Some("com.system76.CosmicSettings".to_string()), // on_drag: Message,
title: Some(fl!("color-picker")), // on_message: fn(ColorPickerUpdate) -> Message,
parent: Some(window::Id::MAIN), // ) -> Element<Message> {
autosize: false, // let header = cosmic::widget::header_bar()
size_limits: layout::Limits::NONE // .title(fl!("color-picker"))
.min_width(300.0) // .on_close(on_message(ColorPickerUpdate::AppliedColor))
.max_width(800.0) // .on_drag(on_drag);
.min_height(520.0)
.max_height(520.0),
size: (300, 520),
resizable: Some(8.0),
client_decorations: true,
transparent: true,
..Default::default()
}
}
// TODO: Reuse with the appearance page // let content = cosmic::widget::container(
pub fn color_picker_view<Message: Clone + 'static>( // model
model: &ColorPickerModel, // .builder(on_message)
on_drag: Message, // .width(Length::Fixed(254.0))
on_message: fn(ColorPickerUpdate) -> Message, // .height(Length::Fixed(174.0))
) -> Element<Message> { // .reset_label(fl!("reset-to-default"))
let header = cosmic::widget::header_bar() // .build(
.title(fl!("color-picker")) // fl!("recent-colors"),
.on_close(on_message(ColorPickerUpdate::AppliedColor)) // fl!("copy-to-clipboard"),
.on_drag(on_drag); // fl!("copied-to-clipboard"),
// ),
// )
// .width(Length::Fill)
// .height(Length::Fill)
// .center_x()
// .style(cosmic::theme::style::Container::Background);
let content = cosmic::widget::container( // cosmic::widget::column::with_capacity(2)
model // .push(header)
.builder(on_message) // .push(content)
.width(Length::Fixed(254.0)) // .width(Length::Fill)
.height(Length::Fixed(174.0)) // .height(Length::Fill)
.reset_label(fl!("reset-to-default")) // .align_items(cosmic::iced_core::Alignment::Center)
.build( // .apply(Element::from)
fl!("recent-colors"), // }
fl!("copy-to-clipboard"),
fl!("copied-to-clipboard"),
),
)
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.style(cosmic::theme::style::Container::Background);
cosmic::widget::column::with_capacity(2)
.push(header)
.push(content)
.width(Length::Fill)
.height(Length::Fill)
.align_items(cosmic::iced_core::Alignment::Center)
.apply(Element::from)
}
enum DialogResponse { enum DialogResponse {
Error(String), Error(String),

View file

@ -1,15 +1,54 @@
// 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::Length; use std::borrow::Cow;
use cosmic::iced::{alignment, Length};
use cosmic::iced_core::text::Wrap; use cosmic::iced_core::text::Wrap;
use cosmic::prelude::CollectionWidget;
use cosmic::widget::color_picker::ColorPickerUpdate;
use cosmic::widget::{ use cosmic::widget::{
self, button, column, container, divider, horizontal_space, icon, row, settings, text, self, button, column, container, divider, horizontal_space, icon, row, settings, text,
vertical_space, vertical_space, ColorPickerModel,
}; };
use cosmic::{theme, Apply, Element}; use cosmic::{theme, Apply, Element};
use cosmic_settings_page as page; use cosmic_settings_page as page;
pub fn color_picker_context_view<'a, Message: Clone + 'static>(
description: Option<Cow<'static, str>>,
reset: Cow<'static, str>,
on_update: fn(ColorPickerUpdate) -> Message,
model: &'a ColorPickerModel,
) -> Element<'a, Message> {
let theme = cosmic::theme::active();
let spacing = &theme.cosmic().spacing;
cosmic::widget::column()
.push_maybe(description.map(|description| text(description).width(Length::Fill)))
.push(
model
.builder(on_update)
.reset_label(reset)
.height(Length::Fixed(158.0))
.build(
fl!("recent-colors"),
fl!("copy-to-clipboard"),
fl!("copied-to-clipboard"),
)
.apply(container)
.width(Length::Fixed(248.0))
.align_x(alignment::Horizontal::Center)
.apply(container)
.width(Length::Fill)
.align_x(alignment::Horizontal::Center),
)
.padding(spacing.space_l)
.align_items(cosmic::iced_core::Alignment::Center)
.spacing(spacing.space_m)
.width(Length::Fill)
.apply(Element::from)
}
#[must_use] #[must_use]
pub fn search_header<Message>( pub fn search_header<Message>(
pages: &page::Binder<Message>, pages: &page::Binder<Message>,