From 1adeadacf60c7f7381ec270710fcdab706d4175e Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Mon, 22 Apr 2024 23:51:32 +0200 Subject: [PATCH 001/784] fix(theme): dark theme generation affected by previous commit --- cosmic-theme/src/steps.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cosmic-theme/src/steps.rs b/cosmic-theme/src/steps.rs index c129ea7c..1c02836f 100644 --- a/cosmic-theme/src/steps.rs +++ b/cosmic-theme/src/steps.rs @@ -41,7 +41,7 @@ pub fn get_surface_color( ) -> Srgba { assert!(step_array.len() == 100); - is_dark = !is_dark && base_index < 88; + is_dark = is_dark || base_index < 88; get_index(base_index, steps, step_array.len(), is_dark) .and_then(|i| step_array.get(i).cloned()) From 9a0c3388762c31e0188cc91058bb1349f92d7c75 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Mon, 22 Apr 2024 16:04:06 -0600 Subject: [PATCH 002/784] WIP: segmented_button::Model custom elements --- src/app/mod.rs | 2 +- src/widget/color_picker/mod.rs | 14 ++++++------- src/widget/nav_bar.rs | 6 +++--- src/widget/segmented_button/horizontal.rs | 6 +++--- src/widget/segmented_button/model/builder.rs | 18 ++++++++-------- src/widget/segmented_button/model/entity.rs | 8 +++---- src/widget/segmented_button/model/mod.rs | 21 +++++++++++-------- .../segmented_button/model/selection.rs | 8 +++---- src/widget/segmented_button/vertical.rs | 6 +++--- src/widget/segmented_button/widget.rs | 12 +++++------ src/widget/segmented_control.rs | 8 +++---- src/widget/tab_bar.rs | 8 +++---- 12 files changed, 60 insertions(+), 57 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 93a91a70..64aafbd8 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -478,7 +478,7 @@ where } /// Allows COSMIC to integrate with your application's [`nav_bar::Model`]. - fn nav_model(&self) -> Option<&nav_bar::Model> { + fn nav_model(&self) -> Option<&nav_bar::Model>> { None } diff --git a/src/widget/color_picker/mod.rs b/src/widget/color_picker/mod.rs index 7ff646b8..30c8bcf8 100644 --- a/src/widget/color_picker/mod.rs +++ b/src/widget/color_picker/mod.rs @@ -62,9 +62,9 @@ pub enum ColorPickerUpdate { } #[derive(Setters)] -pub struct ColorPickerModel { +pub struct ColorPickerModel { #[setters(skip)] - segmented_model: Model, + segmented_model: Model, #[setters(skip)] active_color: palette::Hsv, #[setters(skip)] @@ -86,7 +86,7 @@ pub struct ColorPickerModel { copied_at: Option, } -impl ColorPickerModel { +impl ColorPickerModel { #[must_use] pub fn new( hex: impl Into> + Clone, @@ -118,7 +118,7 @@ impl ColorPickerModel { /// Get a color picker button that displays the applied color /// - pub fn picker_button<'a, Message: 'static, T: Fn(ColorPickerUpdate) -> Message>( + pub fn picker_button<'a, T: Fn(ColorPickerUpdate) -> Message>( &self, f: T, icon_portion: Option, @@ -130,7 +130,7 @@ impl ColorPickerModel { ) } - pub fn update(&mut self, update: ColorPickerUpdate) -> Command { + pub fn update(&mut self, update: ColorPickerUpdate) -> Command { match update { ColorPickerUpdate::ActiveColor(c) => { self.must_clear_cache.store(true, Ordering::SeqCst); @@ -222,7 +222,7 @@ impl ColorPickerModel { } #[must_use] - pub fn builder( + pub fn builder( &self, on_update: fn(ColorPickerUpdate) -> Message, ) -> ColorPickerBuilder { @@ -246,7 +246,7 @@ impl ColorPickerModel { #[derive(Setters, Clone)] pub struct ColorPickerBuilder<'a, Message> { #[setters(skip)] - model: &'a Model, + model: &'a Model, #[setters(skip)] active_color: palette::Hsv, #[setters(skip)] diff --git a/src/widget/nav_bar.rs b/src/widget/nav_bar.rs index 764f8050..27a72403 100644 --- a/src/widget/nav_bar.rs +++ b/src/widget/nav_bar.rs @@ -18,13 +18,13 @@ use crate::{theme, Theme}; use super::dnd_destination::DragId; pub type Id = segmented_button::Entity; -pub type Model = segmented_button::SingleSelectModel; +pub type Model = segmented_button::SingleSelectModel; /// Navigation side panel for switching between views. /// /// For details on the model, see the [`segmented_button`] module for more details. pub fn nav_bar( - model: &segmented_button::SingleSelectModel, + model: &Model, on_activate: fn(segmented_button::Entity) -> Message, ) -> NavBar { NavBar { @@ -35,7 +35,7 @@ pub fn nav_bar( /// Navigation side panel for switching between views. /// Can receive drag and drop events. pub fn nav_bar_dnd( - model: &segmented_button::SingleSelectModel, + model: &Model, on_activate: fn(segmented_button::Entity) -> Message, on_dnd_enter: impl Fn(segmented_button::Entity, Vec) -> Message + 'static, on_dnd_leave: impl Fn(segmented_button::Entity) -> Message + 'static, diff --git a/src/widget/segmented_button/horizontal.rs b/src/widget/segmented_button/horizontal.rs index 36decb28..e2c838a4 100644 --- a/src/widget/segmented_button/horizontal.rs +++ b/src/widget/segmented_button/horizontal.rs @@ -22,10 +22,10 @@ pub struct Horizontal; /// /// For details on the model, see the [`segmented_button`](super) module for more details. pub fn horizontal( - model: &Model, + model: &Model, ) -> SegmentedButton where - Model: Selectable, + Model: Selectable, { SegmentedButton::new(model) } @@ -33,7 +33,7 @@ where impl<'a, SelectionMode, Message> SegmentedVariant for SegmentedButton<'a, Horizontal, SelectionMode, Message> where - Model: Selectable, + Model: Selectable, SelectionMode: Default, { fn variant_appearance( diff --git a/src/widget/segmented_button/model/builder.rs b/src/widget/segmented_button/model/builder.rs index 87d25adb..2554b792 100644 --- a/src/widget/segmented_button/model/builder.rs +++ b/src/widget/segmented_button/model/builder.rs @@ -9,37 +9,37 @@ use std::borrow::Cow; /// A builder for a [`Model`]. #[derive(Default)] -pub struct ModelBuilder(Model); +pub struct ModelBuilder(Model); /// Constructs a new item for the [`ModelBuilder`]. -pub struct BuilderEntity { - model: ModelBuilder, +pub struct BuilderEntity { + model: ModelBuilder, id: Entity, } -impl ModelBuilder +impl ModelBuilder where - Model: Selectable, + Model: Selectable, { /// Inserts a new item and its associated data into the model. #[must_use] pub fn insert( mut self, - builder: impl Fn(BuilderEntity) -> BuilderEntity, + builder: impl Fn(BuilderEntity) -> BuilderEntity, ) -> Self { let id = self.0.insert().id(); builder(BuilderEntity { model: self, id }).model } /// Consumes the builder and returns the model. - pub fn build(self) -> Model { + pub fn build(self) -> Model { self.0 } } -impl BuilderEntity +impl BuilderEntity where - Model: Selectable, + Model: Selectable, { /// Activates the newly-inserted item. #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] diff --git a/src/widget/segmented_button/model/entity.rs b/src/widget/segmented_button/model/entity.rs index c9f0997d..0a9ba2e6 100644 --- a/src/widget/segmented_button/model/entity.rs +++ b/src/widget/segmented_button/model/entity.rs @@ -10,14 +10,14 @@ use crate::widget::Icon; use super::{Entity, Model, Selectable}; /// A newly-inserted item which may have additional actions applied to it. -pub struct EntityMut<'a, SelectionMode: Default> { +pub struct EntityMut<'a, SelectionMode: Default, Message> { pub(super) id: Entity, - pub(super) model: &'a mut Model, + pub(super) model: &'a mut Model, } -impl<'a, SelectionMode: Default> EntityMut<'a, SelectionMode> +impl<'a, SelectionMode: Default, Message> EntityMut<'a, SelectionMode, Message> where - Model: Selectable, + Model: Selectable, { /// Activates the newly-inserted item. /// diff --git a/src/widget/segmented_button/model/mod.rs b/src/widget/segmented_button/model/mod.rs index 49003239..9dde8c10 100644 --- a/src/widget/segmented_button/model/mod.rs +++ b/src/widget/segmented_button/model/mod.rs @@ -10,7 +10,7 @@ pub use self::entity::EntityMut; mod selection; pub use self::selection::{MultiSelect, Selectable, SingleSelect}; -use crate::widget::Icon; +use crate::{Element, widget::Icon}; use slotmap::{SecondaryMap, SlotMap}; use std::any::{Any, TypeId}; use std::borrow::Cow; @@ -37,16 +37,16 @@ impl Default for Settings { } /// A model for single-select button selection. -pub type SingleSelectModel = Model; +pub type SingleSelectModel = Model; /// Single-select variant of an [`EntityMut`]. -pub type SingleSelectEntityMut<'a> = EntityMut<'a, SingleSelect>; +pub type SingleSelectEntityMut<'a, Message> = EntityMut<'a, SingleSelect, Message>; /// A model for multi-select button selection. -pub type MultiSelectModel = Model; +pub type MultiSelectModel = Model; /// Multi-select variant of an [`EntityMut`]. -pub type MultiSelectEntityMut<'a> = EntityMut<'a, MultiSelect>; +pub type MultiSelectEntityMut<'a, Message> = EntityMut<'a, MultiSelect, Message>; /// The portion of the model used only by the application. #[derive(Debug, Default)] @@ -54,10 +54,13 @@ pub(super) struct Storage(HashMap>>); /// The model held by the application, containing the unique IDs and data of each inserted item. #[derive(Default)] -pub struct Model { +pub struct Model { /// The content used for drawing segmented items. pub(super) items: SlotMap, + /// Elements optionally-defined for each item. + pub(super) elements: SecondaryMap>, + /// Icons optionally-defined for each item. pub(super) icons: SecondaryMap, @@ -77,7 +80,7 @@ pub struct Model { pub(super) storage: Storage, } -impl Model +impl Model where Self: Selectable, { @@ -110,7 +113,7 @@ where /// .build(); /// ``` #[must_use] - pub fn builder() -> ModelBuilder { + pub fn builder() -> ModelBuilder { ModelBuilder::default() } @@ -259,7 +262,7 @@ where /// let id = model.insert().text("Item A").icon("custom-icon").id(); /// ``` #[must_use] - pub fn insert(&mut self) -> EntityMut { + pub fn insert(&mut self) -> EntityMut { let id = self.items.insert(Settings::default()); self.order.push_back(id); EntityMut { model: self, id } diff --git a/src/widget/segmented_button/model/selection.rs b/src/widget/segmented_button/model/selection.rs index 1366c18c..1b10b851 100644 --- a/src/widget/segmented_button/model/selection.rs +++ b/src/widget/segmented_button/model/selection.rs @@ -24,7 +24,7 @@ pub struct SingleSelect { pub active: Entity, } -impl Selectable for Model { +impl Selectable for Model { fn activate(&mut self, id: Entity) { if !self.items.contains_key(id) { return; @@ -44,7 +44,7 @@ impl Selectable for Model { } } -impl Model { +impl Model { /// Get an immutable reference to the data associated with the active item. #[must_use] pub fn active_data(&self) -> Option<&Data> { @@ -75,7 +75,7 @@ pub struct MultiSelect { pub active: HashSet, } -impl Selectable for Model { +impl Selectable for Model { fn activate(&mut self, id: Entity) { if !self.items.contains_key(id) { return; @@ -95,7 +95,7 @@ impl Selectable for Model { } } -impl Model { +impl Model { /// Deactivates the item in the model. pub fn deactivate(&mut self, id: Entity) { Selectable::deactivate(self, id); diff --git a/src/widget/segmented_button/vertical.rs b/src/widget/segmented_button/vertical.rs index 5b5d9ace..dc7cd015 100644 --- a/src/widget/segmented_button/vertical.rs +++ b/src/widget/segmented_button/vertical.rs @@ -21,10 +21,10 @@ pub type VerticalSegmentedButton<'a, SelectionMode, Message> = /// /// For details on the model, see the [`segmented_button`](super) module for more details. pub fn vertical( - model: &Model, + model: &Model, ) -> SegmentedButton where - Model: Selectable, + Model: Selectable, SelectionMode: Default, { SegmentedButton::new(model) @@ -33,7 +33,7 @@ where impl<'a, SelectionMode, Message> SegmentedVariant for SegmentedButton<'a, Vertical, SelectionMode, Message> where - Model: Selectable, + Model: Selectable, SelectionMode: Default, { fn variant_appearance( diff --git a/src/widget/segmented_button/widget.rs b/src/widget/segmented_button/widget.rs index 73f7b365..0560f93f 100644 --- a/src/widget/segmented_button/widget.rs +++ b/src/widget/segmented_button/widget.rs @@ -71,12 +71,12 @@ pub trait SegmentedVariant { #[must_use] pub struct SegmentedButton<'a, Variant, SelectionMode, Message> where - Model: Selectable, + Model: Selectable, SelectionMode: Default, { /// The model borrowed from the application create this widget. #[setters(skip)] - pub(super) model: &'a Model, + pub(super) model: &'a Model, /// iced widget ID pub(super) id: Id, /// The icon used for the close button. @@ -151,10 +151,10 @@ where impl<'a, Variant, SelectionMode, Message> SegmentedButton<'a, Variant, SelectionMode, Message> where Self: SegmentedVariant, - Model: Selectable, + Model: Selectable, SelectionMode: Default, { - pub fn new(model: &'a Model) -> Self { + pub fn new(model: &'a Model) -> Self { Self { model, id: Id::unique(), @@ -536,7 +536,7 @@ impl<'a, Variant, SelectionMode, Message> Widget where Self: SegmentedVariant, - Model: Selectable, + Model: Selectable, SelectionMode: Default, Message: 'static + Clone, { @@ -1555,7 +1555,7 @@ impl<'a, Variant, SelectionMode, Message> From: SegmentedVariant, Variant: 'static, - Model: Selectable, + Model: Selectable, SelectionMode: Default, Message: 'static + Clone, { diff --git a/src/widget/segmented_control.rs b/src/widget/segmented_control.rs index 7ba7a40b..2aa6ec20 100644 --- a/src/widget/segmented_control.rs +++ b/src/widget/segmented_control.rs @@ -15,10 +15,10 @@ use super::segmented_button::{ /// /// For details on the model, see the [`segmented_button`] module for more details. pub fn horizontal( - model: &Model, + model: &Model, ) -> HorizontalSegmentedButton where - Model: Selectable, + Model: Selectable, { let theme = crate::theme::active(); let space_s = theme.cosmic().space_s(); @@ -40,10 +40,10 @@ where /// /// For details on the model, see the [`segmented_button`] module for more details. pub fn vertical( - model: &Model, + model: &Model, ) -> VerticalSegmentedButton where - Model: Selectable, + Model: Selectable, SelectionMode: Default, { let theme = crate::theme::active(); diff --git a/src/widget/tab_bar.rs b/src/widget/tab_bar.rs index bcbb14d7..807b58b8 100644 --- a/src/widget/tab_bar.rs +++ b/src/widget/tab_bar.rs @@ -15,10 +15,10 @@ use super::segmented_button::{ /// /// For details on the model, see the [`segmented_button`] module for more details. pub fn horizontal( - model: &Model, + model: &Model, ) -> HorizontalSegmentedButton where - Model: Selectable, + Model: Selectable, { let theme = crate::theme::active(); let space_s = theme.cosmic().space_s(); @@ -38,10 +38,10 @@ where /// The data for the widget comes from a model that is maintained the application. /// For details on the model, see the [`segmented_button`] module for more details. pub fn vertical( - model: &Model, + model: &Model, ) -> VerticalSegmentedButton where - Model: Selectable, + Model: Selectable, SelectionMode: Default, { let theme = crate::theme::active(); From 950a1a54f5f5895a7124ad5e0c329f1c1021813d Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Tue, 23 Apr 2024 10:56:19 +0200 Subject: [PATCH 003/784] fix(theme): increase dark theme lightness threshold to 91 so that the default theme matches designs --- cosmic-theme/src/steps.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cosmic-theme/src/steps.rs b/cosmic-theme/src/steps.rs index 1c02836f..ef4d39ad 100644 --- a/cosmic-theme/src/steps.rs +++ b/cosmic-theme/src/steps.rs @@ -41,7 +41,7 @@ pub fn get_surface_color( ) -> Srgba { assert!(step_array.len() == 100); - is_dark = is_dark || base_index < 88; + is_dark = is_dark || base_index < 91; get_index(base_index, steps, step_array.len(), is_dark) .and_then(|i| step_array.get(i).cloned()) From f081161d9723e56ca73254c67d8e843e6b8df710 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 23 Apr 2024 09:25:13 -0600 Subject: [PATCH 004/784] Work around issues with derive(Default) --- src/widget/color_picker/mod.rs | 2 +- src/widget/segmented_button/model/builder.rs | 8 +++++++- src/widget/segmented_button/model/mod.rs | 17 ++++++++++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/widget/color_picker/mod.rs b/src/widget/color_picker/mod.rs index 30c8bcf8..f3366b3b 100644 --- a/src/widget/color_picker/mod.rs +++ b/src/widget/color_picker/mod.rs @@ -122,7 +122,7 @@ impl ColorPickerModel { &self, f: T, icon_portion: Option, - ) -> crate::widget::Button<'a, Message, crate::Theme, crate::Renderer> { + ) -> crate::widget::Button<'a, Message, crate::Theme, crate::Renderer> where Message: 'static { color_button( Some(f(ColorPickerUpdate::ToggleColorPicker)), self.applied_color, diff --git a/src/widget/segmented_button/model/builder.rs b/src/widget/segmented_button/model/builder.rs index 2554b792..00085c8a 100644 --- a/src/widget/segmented_button/model/builder.rs +++ b/src/widget/segmented_button/model/builder.rs @@ -8,9 +8,15 @@ use crate::widget::icon::Icon; use std::borrow::Cow; /// A builder for a [`Model`]. -#[derive(Default)] pub struct ModelBuilder(Model); +//TODO: Default derive ends up requiring Message to implement Default +impl Default for ModelBuilder { + fn default() -> Self { + Self(Model::default()) + } +} + /// Constructs a new item for the [`ModelBuilder`]. pub struct BuilderEntity { model: ModelBuilder, diff --git a/src/widget/segmented_button/model/mod.rs b/src/widget/segmented_button/model/mod.rs index 9dde8c10..b743e76b 100644 --- a/src/widget/segmented_button/model/mod.rs +++ b/src/widget/segmented_button/model/mod.rs @@ -53,7 +53,6 @@ pub type MultiSelectEntityMut<'a, Message> = EntityMut<'a, MultiSelect, Message> pub(super) struct Storage(HashMap>>); /// The model held by the application, containing the unique IDs and data of each inserted item. -#[derive(Default)] pub struct Model { /// The content used for drawing segmented items. pub(super) items: SlotMap, @@ -80,6 +79,22 @@ pub struct Model { pub(super) storage: Storage, } +//TODO: Default derive ends up requiring Message to implement Default +impl Default for Model { + fn default() -> Self { + Self { + items: SlotMap::default(), + elements: SecondaryMap::default(), + icons: SecondaryMap::default(), + indents: SecondaryMap::default(), + text: SecondaryMap::default(), + order: VecDeque::default(), + selection: SelectionMode::default(), + storage: Storage::default(), + } + } +} + impl Model where Self: Selectable, From 14bd633356a4fe4ef7d1d2f1634f6466bd003fe5 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Wed, 24 Apr 2024 03:22:45 +0200 Subject: [PATCH 005/784] improv(theme): theme generation improvements --- cosmic-theme/src/model/derivation.rs | 12 +++- cosmic-theme/src/model/theme.rs | 71 ++++++++++++++----- cosmic-theme/src/steps.rs | 23 +++--- src/theme/style/button.rs | 17 +++-- src/theme/style/iced.rs | 11 +-- src/theme/style/segmented_button.rs | 100 ++++++++++++++++----------- 6 files changed, 146 insertions(+), 88 deletions(-) diff --git a/cosmic-theme/src/model/derivation.rs b/cosmic-theme/src/model/derivation.rs index 7d857c31..994ee35a 100644 --- a/cosmic-theme/src/model/derivation.rs +++ b/cosmic-theme/src/model/derivation.rs @@ -15,18 +15,28 @@ pub struct Container { pub divider: Srgba, /// the color of text in the container pub on: Srgba, + /// the color of @small_widget_container + pub small_widget: Srgba, } impl Container { - pub(crate) fn new(component: Component, base: Srgba, on: Srgba) -> Self { + pub(crate) fn new( + component: Component, + base: Srgba, + on: Srgba, + mut small_widget: Srgba, + ) -> Self { let mut divider_c = on; divider_c.alpha = 0.2; + small_widget.alpha = 0.25; + Self { base, component, divider: over(divider_c, base), on, + small_widget, } } } diff --git a/cosmic-theme/src/model/theme.rs b/cosmic-theme/src/model/theme.rs index 62ed20a9..93c3bde3 100644 --- a/cosmic-theme/src/model/theme.rs +++ b/cosmic-theme/src/model/theme.rs @@ -5,7 +5,7 @@ use crate::{ DARK_PALETTE, LIGHT_PALETTE, NAME, }; use cosmic_config::{Config, CosmicConfigEntry}; -use palette::{IntoColor, Oklcha, Srgb, Srgba}; +use palette::{rgb::Rgb, IntoColor, Oklcha, Srgb, Srgba}; use serde::{Deserialize, Serialize}; use std::num::NonZeroUsize; @@ -182,13 +182,6 @@ impl Theme { self.warning.base } - #[must_use] - #[allow(clippy::doc_markdown)] - /// get @small_container_widget - pub fn small_container_widget(&self) -> Srgba { - self.palette.gray_2 - } - #[must_use] #[allow(clippy::doc_markdown)] /// get @small_widget_divider @@ -774,6 +767,11 @@ impl ThemeBuilder { let p_ref = palette.as_ref(); + let neutral_steps = steps( + neutral_tint.unwrap_or(Rgb::new(0.0, 0.0, 0.0)), + NonZeroUsize::new(100).unwrap(), + ); + let bg = if let Some(bg_color) = bg_color { bg_color } else { @@ -783,10 +781,14 @@ impl ThemeBuilder { let step_array = steps(bg, NonZeroUsize::new(100).unwrap()); let bg_index = color_index(bg, step_array.len()); - let mut component_hovered_overlay = p_ref.neutral_0; + let mut component_hovered_overlay = if bg_index < 91 { + p_ref.neutral_10 + } else { + p_ref.neutral_0 + }; component_hovered_overlay.alpha = 0.1; - let mut component_pressed_overlay = p_ref.neutral_0; + let mut component_pressed_overlay = component_hovered_overlay; component_pressed_overlay.alpha = 0.2; // Standard button background is neutral 7 with 25% opacity @@ -805,7 +807,6 @@ impl ThemeBuilder { let on_bg_component = get_text( color_index(bg_component, step_array.len()), &step_array, - is_dark, &p_ref.neutral_8, text_steps_array.as_ref(), ); @@ -831,10 +832,16 @@ impl ThemeBuilder { get_text( bg_index, &step_array, - is_dark, &p_ref.neutral_8, text_steps_array.as_ref(), ), + get_surface_color( + bg_index, + 5, + &neutral_steps, + bg_index <= 65, + &p_ref.neutral_6, + ), ), primary: { let container_bg = if let Some(primary_container_bg_color) = primary_container_bg { @@ -843,10 +850,20 @@ impl ThemeBuilder { get_surface_color(bg_index, 5, &step_array, is_dark, &p_ref.neutral_1) }; - let base_index = color_index(container_bg, step_array.len()); + let base_index: usize = color_index(container_bg, step_array.len()); let component_base = get_surface_color(base_index, 6, &step_array, is_dark, &p_ref.neutral_3); + component_hovered_overlay = if base_index < 91 { + p_ref.neutral_10 + } else { + p_ref.neutral_0 + }; + component_hovered_overlay.alpha = 0.1; + + component_pressed_overlay = component_hovered_overlay; + component_pressed_overlay.alpha = 0.2; + let container = Container::new( Component::component( component_base, @@ -854,7 +871,6 @@ impl ThemeBuilder { get_text( color_index(component_base, step_array.len()), &step_array, - is_dark, &p_ref.neutral_8, text_steps_array.as_ref(), ), @@ -867,10 +883,16 @@ impl ThemeBuilder { get_text( base_index, &step_array, - is_dark, &p_ref.neutral_8, text_steps_array.as_ref(), ), + get_surface_color( + base_index, + 5, + &neutral_steps, + base_index <= 65, + &p_ref.neutral_6, + ), ); container @@ -886,6 +908,16 @@ impl ThemeBuilder { let secondary_component = get_surface_color(base_index, 3, &step_array, is_dark, &p_ref.neutral_4); + component_hovered_overlay = if base_index < 91 { + p_ref.neutral_10 + } else { + p_ref.neutral_0 + }; + component_hovered_overlay.alpha = 0.1; + + component_pressed_overlay = component_hovered_overlay; + component_pressed_overlay.alpha = 0.2; + Container::new( Component::component( secondary_component, @@ -893,7 +925,6 @@ impl ThemeBuilder { get_text( color_index(secondary_component, step_array.len()), &step_array, - is_dark, &p_ref.neutral_8, text_steps_array.as_ref(), ), @@ -906,10 +937,16 @@ impl ThemeBuilder { get_text( base_index, &step_array, - is_dark, &p_ref.neutral_8, text_steps_array.as_ref(), ), + get_surface_color( + base_index, + 5, + &neutral_steps, + base_index <= 65, + &p_ref.neutral_6, + ), ) }, accent: Component::colored_component( diff --git a/cosmic-theme/src/steps.rs b/cosmic-theme/src/steps.rs index ef4d39ad..2c306e3c 100644 --- a/cosmic-theme/src/steps.rs +++ b/cosmic-theme/src/steps.rs @@ -43,16 +43,15 @@ pub fn get_surface_color( is_dark = is_dark || base_index < 91; - get_index(base_index, steps, step_array.len(), is_dark) - .and_then(|i| step_array.get(i).cloned()) - .unwrap_or_else(|| fallback.to_owned()) + *get_index(base_index, steps, step_array.len(), is_dark) + .and_then(|i| step_array.get(i)) + .unwrap_or(fallback) } /// get text color given a base background color pub fn get_text( base_index: usize, step_array: &Vec, - is_dark: bool, fallback: &Srgba, tint_array: Option<&Vec>, ) -> Srgba { @@ -63,16 +62,14 @@ pub fn get_text( } else { step_array }; - let Some(index) = get_index(base_index, 70, step_array.len(), is_dark) - .or_else(|| get_index(base_index, 50, step_array.len(), is_dark)) - else { - return fallback.to_owned(); - }; - step_array - .get(index) - .cloned() - .unwrap_or_else(|| fallback.to_owned()) + let is_dark = base_index < 60; + + let index = get_index(base_index, 70, step_array.len(), is_dark) + .or_else(|| get_index(base_index, 50, step_array.len(), is_dark)) + .unwrap_or_else(|| if is_dark { 99 } else { 0 }); + + *step_array.get(index).unwrap_or(fallback) } /// get the index into the steps array for a given color diff --git a/src/theme/style/button.rs b/src/theme/style/button.rs index b2e33689..d3fdcb88 100644 --- a/src/theme/style/button.rs +++ b/src/theme/style/button.rs @@ -64,8 +64,10 @@ pub fn appearance( let (background, text, icon) = color(style_component); appearance.background = Some(Background::Color(background)); - appearance.text_color = text; - appearance.icon_color = icon; + if !matches!(style, Button::Standard | Button::Text) { + appearance.text_color = text; + appearance.icon_color = icon; + } } Button::Icon | Button::IconVertical | Button::HeaderBar => { @@ -152,15 +154,14 @@ impl StyleSheet for crate::Theme { if let Button::Custom { active, .. } = style { return active(focused, self); } - let accent = self.cosmic().accent_color(); - appearance(self, focused, selected, style, |component| { + appearance(self, focused, selected, style, move |component| { let text_color = if matches!( style, Button::Icon | Button::IconVertical | Button::HeaderBar ) && selected { - Some(accent.into()) + Some(self.cosmic().accent_color().into()) } else if matches!(style, Button::HeaderBar) && !selected { let mut c = Color::from(component.on); c.a = 0.8; @@ -197,7 +198,6 @@ impl StyleSheet for crate::Theme { if let Button::Custom { hovered, .. } = style { return hovered(focused, self); } - let accent = self.cosmic().accent_button.hover; appearance( self, @@ -210,7 +210,7 @@ impl StyleSheet for crate::Theme { Button::Icon | Button::IconVertical | Button::HeaderBar ) && selected { - Some(accent.into()) + Some(self.cosmic().accent_color().into()) } else if matches!(style, Button::HeaderBar) && !selected { let mut c = Color::from(component.on); c.a = 0.8; @@ -228,7 +228,6 @@ impl StyleSheet for crate::Theme { if let Button::Custom { pressed, .. } = style { return pressed(focused, self); } - let accent = self.cosmic().accent_button.pressed; appearance(self, focused, selected, style, |component| { let text_color = if matches!( @@ -236,7 +235,7 @@ impl StyleSheet for crate::Theme { Button::Icon | Button::IconVertical | Button::HeaderBar ) && selected { - Some(accent.into()) + Some(self.cosmic().accent_color().into()) } else if matches!(style, Button::HeaderBar) && !selected { let mut c = Color::from(component.on); c.a = 0.8; diff --git a/src/theme/style/iced.rs b/src/theme/style/iced.rs index 31fd74f9..af6ee0ef 100644 --- a/src/theme/style/iced.rs +++ b/src/theme/style/iced.rs @@ -436,6 +436,7 @@ impl container::StyleSheet for Theme { #[allow(clippy::too_many_lines)] fn appearance(&self, style: &Self::Style) -> container::Appearance { let cosmic = self.cosmic(); + match style { Container::Transparent => container::Appearance::default(), @@ -459,13 +460,7 @@ impl container::StyleSheet for Theme { }, Container::List => { - // TODO: The primary component has the wrong color on the light theme. - let component = if cosmic.is_dark { - &self.current_container().component - } else { - &cosmic.background.component - }; - + let component = &self.current_container().component; container::Appearance { icon_color: Some(component.on.into()), text_color: Some(component.on.into()), @@ -479,7 +474,7 @@ impl container::StyleSheet for Theme { } Container::HeaderBar => container::Appearance { - icon_color: Some(Color::from(cosmic.accent.base)), + icon_color: Some(Color::from(cosmic.background.on)), text_color: Some(Color::from(cosmic.background.on)), background: Some(iced::Background::Color(cosmic.background.base.into())), border: Border { diff --git a/src/theme/style/segmented_button.rs b/src/theme/style/segmented_button.rs index 170db8fc..3fc4ae1e 100644 --- a/src/theme/style/segmented_button.rs +++ b/src/theme/style/segmented_button.rs @@ -5,6 +5,7 @@ use crate::widget::segmented_button::{Appearance, ItemAppearance, StyleSheet}; use crate::{theme::Theme, widget::segmented_button::ItemStatusAppearance}; +use cosmic_theme::{Component, Container}; use iced_core::{border::Radius, Background}; #[derive(Default)] @@ -23,6 +24,8 @@ impl StyleSheet for Theme { #[allow(clippy::too_many_lines)] fn horizontal(&self, style: &Self::Style) -> Appearance { + let container = &self.current_container(); + match style { SegmentedButton::TabBar => { let cosmic = self.cosmic(); @@ -46,25 +49,24 @@ impl StyleSheet for Theme { border_bottom: Some((1.0, cosmic.accent.base.into())), ..Default::default() }, - text_color: cosmic.on_bg_color().into(), + text_color: container.component.on.into(), }, - hover: hover(cosmic, &active), - focus: focus(cosmic, &active), + hover: hover(cosmic, &container.component, &active), + focus: focus(cosmic, container, &active), active, ..Default::default() } } SegmentedButton::Control => { let cosmic = self.cosmic(); - let active = horizontal::selection_active(cosmic); - let mut neutral_5 = cosmic.palette.neutral_5; - neutral_5.alpha = 0.2; + let active = horizontal::selection_active(cosmic, &container.component); let rad_m = cosmic.corner_radii.radius_m; let rad_0 = cosmic.corner_radii.radius_0; Appearance { - border_radius: cosmic.corner_radii.radius_0.into(), + background: Some(Background::Color(container.small_widget.into())), + border_radius: rad_m.into(), inactive: ItemStatusAppearance { - background: Some(Background::Color(cosmic.small_container_widget().into())), + background: None, first: ItemAppearance { border_radius: Radius::from([rad_m[0], rad_0[1], rad_0[2], rad_m[3]]), ..Default::default() @@ -77,10 +79,10 @@ impl StyleSheet for Theme { border_radius: Radius::from([rad_0[0], rad_m[1], rad_m[2], rad_0[3]]), ..Default::default() }, - text_color: cosmic.on_bg_color().into(), + text_color: container.component.on.into(), }, - hover: hover(cosmic, &active), - focus: focus(cosmic, &active), + hover: hover(cosmic, &container.component, &active), + focus: focus(cosmic, container, &active), active, ..Default::default() } @@ -96,28 +98,29 @@ impl StyleSheet for Theme { let rad_0 = cosmic.corner_radii.radius_0; match style { SegmentedButton::TabBar => { + let container = &self.cosmic().primary; let active = vertical::tab_bar_active(cosmic); Appearance { border_radius: cosmic.corner_radii.radius_0.into(), inactive: ItemStatusAppearance { background: None, - text_color: cosmic.on_bg_color().into(), + text_color: container.component.on.into(), ..active }, - hover: hover(cosmic, &active), - focus: focus(cosmic, &active), + hover: hover(cosmic, &container.component, &active), + focus: focus(cosmic, container, &active), active, ..Default::default() } } SegmentedButton::Control => { - let active = vertical::selection_active(cosmic); - let mut neutral_5 = cosmic.palette.neutral_5; - neutral_5.alpha = 0.2; + let container = self.current_container(); + let active = vertical::selection_active(cosmic, &container.component); Appearance { - border_radius: cosmic.corner_radii.radius_0.into(), + background: Some(Background::Color(container.small_widget.into())), + border_radius: rad_m.into(), inactive: ItemStatusAppearance { - background: Some(Background::Color(neutral_5.into())), + background: None, first: ItemAppearance { border_radius: Radius::from([rad_m[0], rad_m[1], rad_0[0], rad_0[0]]), ..Default::default() @@ -130,10 +133,10 @@ impl StyleSheet for Theme { border_radius: Radius::from([rad_0[0], rad_0[1], rad_m[2], rad_m[3]]), ..Default::default() }, - text_color: cosmic.on_bg_color().into(), + text_color: container.component.on.into(), }, - hover: hover(cosmic, &active), - focus: focus(cosmic, &active), + hover: hover(cosmic, &container.component, &active), + focus: focus(cosmic, container, &active), active, ..Default::default() } @@ -145,15 +148,21 @@ impl StyleSheet for Theme { mod horizontal { use crate::widget::segmented_button::{ItemAppearance, ItemStatusAppearance}; + use cosmic_theme::Component; use iced_core::{border::Radius, Background}; - pub fn selection_active(cosmic: &cosmic_theme::Theme) -> ItemStatusAppearance { - let mut neutral_5 = cosmic.palette.neutral_5; - neutral_5.alpha = 0.2; + pub fn selection_active( + cosmic: &cosmic_theme::Theme, + component: &Component, + ) -> ItemStatusAppearance { + let mut color = cosmic.palette.neutral_5; + color.alpha = 0.2; + let rad_m = cosmic.corner_radii.radius_m; let rad_0 = cosmic.corner_radii.radius_0; + ItemStatusAppearance { - background: Some(Background::Color(neutral_5.into())), + background: Some(Background::Color(color.into())), first: ItemAppearance { border_radius: Radius::from([rad_m[0], rad_0[1], rad_0[2], rad_m[3]]), ..Default::default() @@ -197,23 +206,28 @@ mod horizontal { } } -pub fn focus(cosmic: &cosmic_theme::Theme, default: &ItemStatusAppearance) -> ItemStatusAppearance { - // TODO: This is a hack to make the hover color lighter than the selected color - // I'm not sure why the alpha is being applied differently here than in figma - let mut neutral_5 = cosmic.palette.neutral_5; - neutral_5.alpha = 0.2; +pub fn focus( + cosmic: &cosmic_theme::Theme, + container: &Container, + default: &ItemStatusAppearance, +) -> ItemStatusAppearance { + let color = container.small_widget; ItemStatusAppearance { - background: Some(Background::Color(neutral_5.into())), + background: Some(Background::Color(color.into())), text_color: cosmic.accent.base.into(), ..*default } } -pub fn hover(cosmic: &cosmic_theme::Theme, default: &ItemStatusAppearance) -> ItemStatusAppearance { - let mut neutral_10 = cosmic.palette.neutral_10; - neutral_10.alpha = 0.1; +pub fn hover( + cosmic: &cosmic_theme::Theme, + component: &Component, + default: &ItemStatusAppearance, +) -> ItemStatusAppearance { + let mut color = cosmic.palette.neutral_8; + color.alpha = 0.2; ItemStatusAppearance { - background: Some(Background::Color(neutral_10.into())), + background: Some(Background::Color(color.into())), text_color: cosmic.accent.base.into(), ..*default } @@ -221,15 +235,21 @@ pub fn hover(cosmic: &cosmic_theme::Theme, default: &ItemStatusAppearance) -> It mod vertical { use crate::widget::segmented_button::{ItemAppearance, ItemStatusAppearance}; + use cosmic_theme::Component; use iced_core::{border::Radius, Background}; - pub fn selection_active(cosmic: &cosmic_theme::Theme) -> ItemStatusAppearance { - let mut neutral_5 = cosmic.palette.neutral_5; - neutral_5.alpha = 0.2; + pub fn selection_active( + cosmic: &cosmic_theme::Theme, + component: &Component, + ) -> ItemStatusAppearance { + let mut color = component.selected_state_color(); + color.alpha = 0.3; + let rad_0 = cosmic.corner_radii.radius_0; let rad_m = cosmic.corner_radii.radius_m; + ItemStatusAppearance { - background: Some(Background::Color(neutral_5.into())), + background: Some(Background::Color(color.into())), first: ItemAppearance { border_radius: Radius::from([rad_m[0], rad_m[1], rad_0[2], rad_0[3]]), ..Default::default() From a0ed887b7d73ebb69c2aff7bb230c43d9754c742 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 24 Apr 2024 12:39:46 -0700 Subject: [PATCH 006/784] Update `iced` (#410) --- iced | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iced b/iced index 3fe3a619..900bad58 160000 --- a/iced +++ b/iced @@ -1 +1 @@ -Subproject commit 3fe3a619b04786bdb4b3b67fa354b96ef693f017 +Subproject commit 900bad58c37c2ffce85ee73a8beabcd1ca63a3a7 From d138c86a11af3993e4f3c2136dd5100e7b2bc51a Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 24 Apr 2024 13:57:41 -0600 Subject: [PATCH 007/784] nav_bar: allow setting close_icon and on_close --- src/widget/nav_bar.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/widget/nav_bar.rs b/src/widget/nav_bar.rs index 764f8050..3aa45818 100644 --- a/src/widget/nav_bar.rs +++ b/src/widget/nav_bar.rs @@ -12,7 +12,7 @@ use iced::{ }; use iced_core::{Border, Color, Shadow}; -use crate::widget::{container, menu, scrollable, segmented_button, Container}; +use crate::widget::{container, menu, scrollable, segmented_button, Container, Icon}; use crate::{theme, Theme}; use super::dnd_destination::DragId; @@ -62,6 +62,11 @@ pub struct NavBar<'a, Message> { } impl<'a, Message: Clone + 'static> NavBar<'a, Message> { + pub fn close_icon(mut self, close_icon: Icon) -> Self { + self.segmented_button = self.segmented_button.close_icon(close_icon); + self + } + pub fn context_menu(mut self, context_menu: Option>>) -> Self { self.segmented_button = self.segmented_button.context_menu(context_menu); self @@ -78,6 +83,15 @@ impl<'a, Message: Clone + 'static> NavBar<'a, Message> { Container::from(self) } + /// Emitted when a tab close button is pressed. + pub fn on_close(mut self, on_close: T) -> Self + where + T: Fn(Id) -> Message + 'static, + { + self.segmented_button = self.segmented_button.on_close(on_close); + self + } + /// Emitted when a button is right-clicked. pub fn on_context(mut self, on_context: T) -> Self where From 084ce30478430fa33aed891235b795dcadcb38f0 Mon Sep 17 00:00:00 2001 From: Eleven <114903054+elevenhsoft@users.noreply.github.com> Date: Fri, 26 Apr 2024 16:38:32 +0200 Subject: [PATCH 008/784] feat(theme): GNOME sessions get dark theme preference from gsettings --- cosmic-theme/src/model/theme.rs | 36 ++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/cosmic-theme/src/model/theme.rs b/cosmic-theme/src/model/theme.rs index 93c3bde3..ad4bd6fa 100644 --- a/cosmic-theme/src/model/theme.rs +++ b/cosmic-theme/src/model/theme.rs @@ -101,7 +101,7 @@ pub struct Theme { impl Default for Theme { fn default() -> Self { - Self::dark_default() + Self::preferred_theme() } } @@ -513,6 +513,40 @@ impl Theme { builder = builder.accent(adjusted_c); builder.build() } + + /// choose default color palette based on preferred GTK color scheme + pub fn gtk_prefer_colorscheme() -> Self { + let gsettings = "/usr/bin/gsettings"; + + let cmd = std::process::Command::new(gsettings) + .arg("get") + .arg("org.gnome.desktop.interface") + .arg("color-scheme") + .output(); + + if let Ok(cmd) = cmd { + let color_scheme = String::from_utf8_lossy(&cmd.stdout); + + if color_scheme.trim().contains("light") { + return Self::light_default(); + } + }; + + Self::dark_default() + } + + /// check current desktop environment and preferred color scheme and set it as default + pub fn preferred_theme() -> Self { + let current_desktop = std::env::var("XDG_CURRENT_DESKTOP"); + + if let Ok(desktop) = current_desktop { + if desktop.trim().to_lowercase().contains("gnome") { + return Self::gtk_prefer_colorscheme(); + } + } + + Self::dark_default() + } } impl From for Theme { From 8c8dbd14208f9dfe22388e34c81340a1ef2cbc25 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 26 Apr 2024 14:51:45 +0200 Subject: [PATCH 009/784] fix(text_input): inherit text and icon colors from container --- src/theme/style/text_input.rs | 77 ++++++++++++++++------------------ src/widget/text_input/input.rs | 25 +++++++---- src/widget/text_input/style.rs | 6 +-- 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/src/theme/style/text_input.rs b/src/theme/style/text_input.rs index 74297c20..b8939216 100644 --- a/src/theme/style/text_input.rs +++ b/src/theme/style/text_input.rs @@ -43,8 +43,8 @@ impl StyleSheet for crate::Theme { border_width: 1.0, border_offset: None, border_color: container.component.divider.into(), - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -59,8 +59,8 @@ impl StyleSheet for crate::Theme { border_width: 0.0, border_offset: None, border_color: Color::TRANSPARENT, - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -75,8 +75,8 @@ impl StyleSheet for crate::Theme { border_width: 0.0, border_offset: None, border_color: Color::TRANSPARENT, - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -91,8 +91,8 @@ impl StyleSheet for crate::Theme { border_width: 1.0, border_offset: None, border_color: container.component.divider.into(), - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -107,8 +107,8 @@ impl StyleSheet for crate::Theme { border_width: 0.0, border_offset: None, border_color: Color::TRANSPARENT, - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -138,8 +138,8 @@ impl StyleSheet for crate::Theme { border_width: 1.0, border_offset: Some(2.0), border_color: Color::from(palette.destructive_color()), - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -154,8 +154,8 @@ impl StyleSheet for crate::Theme { border_width: 0.0, border_offset: None, border_color: Color::TRANSPARENT, - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -170,8 +170,8 @@ impl StyleSheet for crate::Theme { border_width: 0.0, border_offset: None, border_color: Color::TRANSPARENT, - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -201,8 +201,8 @@ impl StyleSheet for crate::Theme { border_width: 1.0, border_offset: None, border_color: palette.accent.base.into(), - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -217,8 +217,8 @@ impl StyleSheet for crate::Theme { border_offset: None, border_width: 1.0, border_color: palette.accent.base.into(), - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -233,8 +233,8 @@ impl StyleSheet for crate::Theme { border_offset: None, border_width: 0.0, border_color: Color::TRANSPARENT, - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -249,8 +249,8 @@ impl StyleSheet for crate::Theme { border_width: 0.0, border_offset: None, border_color: Color::TRANSPARENT, - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -265,8 +265,8 @@ impl StyleSheet for crate::Theme { border_width: 0.0, border_offset: None, border_color: Color::TRANSPARENT, - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -296,8 +296,8 @@ impl StyleSheet for crate::Theme { border_width: 1.0, border_offset: Some(2.0), border_color: palette.accent.base.into(), - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -312,8 +312,8 @@ impl StyleSheet for crate::Theme { border_width: 1.0, border_offset: Some(2.0), border_color: palette.accent.base.into(), - icon_color: container.on.into(), - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -328,10 +328,10 @@ impl StyleSheet for crate::Theme { border_width: 0.0, border_offset: None, border_color: Color::TRANSPARENT, - icon_color: container.on.into(), // TODO use regular text color here after text rendering handles multiple colors // in this case, for selected and unselected text - text_color: container.on.into(), + icon_color: None, + text_color: None, placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -346,10 +346,10 @@ impl StyleSheet for crate::Theme { border_width: 0.0, border_offset: None, border_color: Color::TRANSPARENT, - icon_color: container.on.into(), + icon_color: Some(palette.on_accent_color().into()), // TODO use regular text color here after text rendering handles multiple colors // in this case, for selected and unselected text - text_color: palette.on_accent_color().into(), + text_color: Some(palette.on_accent_color().into()), placeholder_color: { let color: Color = container.on.into(); color.blend_alpha(background, 0.7) @@ -367,13 +367,6 @@ impl StyleSheet for crate::Theme { return disabled(self); } - let mut appearance = self.active(style); - - // TODO: iced will not render alpha itself on text or icon colors. - let background: Color = self.current_container().component.base.into(); - appearance.text_color = appearance.text_color.blend_alpha(background, 0.5); - appearance.icon_color = appearance.icon_color.blend_alpha(background, 0.5); - - appearance + self.active(style) } } diff --git a/src/widget/text_input/input.rs b/src/widget/text_input/input.rs index 0bd6cbc3..552d1247 100644 --- a/src/widget/text_input/input.rs +++ b/src/widget/text_input/input.rs @@ -7,6 +7,7 @@ //! A [`TextInput`] has some local [`State`]. use std::borrow::Cow; +use crate::ext::ColorExt; use crate::theme::THEME; use super::cursor; @@ -2017,6 +2018,16 @@ pub fn draw<'a, Message>( theme.active(style) }; + let mut icon_color = appearance.icon_color.unwrap_or(renderer_style.icon_color); + let mut text_color = appearance.text_color.unwrap_or(renderer_style.text_color); + + // TODO: iced will not render alpha itself on text or icon colors. + if is_disabled { + let background = theme.current_container().component.base.into(); + icon_color = icon_color.blend_alpha(background, 0.5); + text_color = text_color.blend_alpha(background, 0.5); + } + // draw background and its border if let Some(border_offset) = appearance.border_offset { let offset_bounds = Rectangle { @@ -2104,8 +2115,8 @@ pub fn draw<'a, Message>( renderer, theme, &renderer::Style { - icon_color: appearance.icon_color, - text_color: appearance.text_color, + icon_color, + text_color, scale_factor: renderer_style.scale_factor, }, icon_layout, @@ -2156,7 +2167,7 @@ pub fn draw<'a, Message>( blur_radius: 0.0, }, }, - appearance.text_color, + text_color, )), offset, ) @@ -2233,7 +2244,7 @@ pub fn draw<'a, Message>( let color = if text.is_empty() { appearance.placeholder_color } else { - appearance.text_color + text_color }; renderer.fill_text( @@ -2268,8 +2279,8 @@ pub fn draw<'a, Message>( renderer, theme, &renderer::Style { - icon_color: renderer_style.icon_color, - text_color: appearance.text_color, + icon_color, + text_color, scale_factor: renderer_style.scale_factor, }, icon_layout, @@ -2292,7 +2303,7 @@ pub fn draw<'a, Message>( shaping: text::Shaping::Advanced, }, helper_text_layout.bounds().position(), - appearance.text_color, + text_color, *viewport, ); } diff --git a/src/widget/text_input/style.rs b/src/widget/text_input/style.rs index 5f152f52..aa25c86e 100644 --- a/src/widget/text_input/style.rs +++ b/src/widget/text_input/style.rs @@ -19,16 +19,16 @@ pub struct Appearance { pub border_width: f32, /// The border [`Color`] of the text input. pub border_color: Color, - /// The [`Color`] of symbolic icons. - pub icon_color: Color, /// The label [`Color`] of the text input. pub label_color: Color, /// The placeholder text [`Color`]. pub placeholder_color: Color, /// The text [`Color`] of the text input. pub selected_text_color: Color, + /// The icon [`Color`] of the text input. + pub icon_color: Option, /// The text [`Color`] of the text input. - pub text_color: Color, + pub text_color: Option, /// The selected fill [`Color`] of the text input. pub selected_fill: Color, } From fe3f9464f7df51df774ebd0c87d43c688d3f6359 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 26 Apr 2024 14:53:11 +0200 Subject: [PATCH 010/784] fix(headerbar): container style should use accent color for icons --- src/theme/style/iced.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/theme/style/iced.rs b/src/theme/style/iced.rs index af6ee0ef..5cf9b6c4 100644 --- a/src/theme/style/iced.rs +++ b/src/theme/style/iced.rs @@ -474,7 +474,7 @@ impl container::StyleSheet for Theme { } Container::HeaderBar => container::Appearance { - icon_color: Some(Color::from(cosmic.background.on)), + icon_color: Some(Color::from(cosmic.accent.base)), text_color: Some(Color::from(cosmic.background.on)), background: Some(iced::Background::Color(cosmic.background.base.into())), border: Border { From c091988af4a2c3b7073d63a21ab543576e8c4fa0 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 26 Apr 2024 15:16:16 +0200 Subject: [PATCH 011/784] fix(button): icon styles should inherit text and icon colors from container --- src/theme/style/button.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/theme/style/button.rs b/src/theme/style/button.rs index d3fdcb88..110e25f5 100644 --- a/src/theme/style/button.rs +++ b/src/theme/style/button.rs @@ -75,10 +75,8 @@ pub fn appearance( corner_radii = &cosmic.corner_radii.radius_m; } - let (background, text, icon) = color(&cosmic.icon_button); + let (background, _text, _icon) = color(&cosmic.icon_button); appearance.background = Some(Background::Color(background)); - appearance.text_color = text; - appearance.icon_color = icon; } Button::Image => { From e5561489427d784cf7a63310c0d505a105d4e68c Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 26 Apr 2024 16:47:28 +0200 Subject: [PATCH 012/784] fix(context-drawer): close button should use link button style --- src/widget/context_drawer/widget.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/widget/context_drawer/widget.rs b/src/widget/context_drawer/widget.rs index eb72b550..18a5b88e 100644 --- a/src/widget/context_drawer/widget.rs +++ b/src/widget/context_drawer/widget.rs @@ -57,6 +57,7 @@ impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> { button::text("Close") .trailing_icon(icon::from_name("go-next-symbolic")) .on_press(on_close) + .style(crate::theme::Button::Link) .apply(container) .width(Length::FillPortion(1)) .height(Length::Fill) From 42cfdbf5856d8f343440d24771f7b1d1e0cddbb9 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 26 Apr 2024 10:22:51 -0600 Subject: [PATCH 013/784] fix(app): restore on_context_drawer functionality that was removed in 59a913c --- src/app/cosmic.rs | 1 + src/app/mod.rs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/app/cosmic.rs b/src/app/cosmic.rs index 8bcd98c2..76438aa3 100644 --- a/src/app/cosmic.rs +++ b/src/app/cosmic.rs @@ -369,6 +369,7 @@ impl Cosmic { Message::ContextDrawer(show) => { self.app.core_mut().window.show_context = show; + return self.app.on_context_drawer(); } Message::Drag => return command::drag(Some(self.app.main_window_id())), diff --git a/src/app/mod.rs b/src/app/mod.rs index 93a91a70..1487d72c 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -490,6 +490,11 @@ where None } + // Called when context drawer is toggled + fn on_context_drawer(&mut self) -> iced::Command> { + iced::Command::none() + } + /// Called when the escape key is pressed. fn on_escape(&mut self) -> iced::Command> { iced::Command::none() From 9797df2b50d088c86f505b26b25b733bce5c142a Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Fri, 26 Apr 2024 14:08:56 -0400 Subject: [PATCH 014/784] fix: update iced fixes damage in tiny_skia & improves scrollbar handling of events. --- iced | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iced b/iced index 900bad58..46b43746 160000 --- a/iced +++ b/iced @@ -1 +1 @@ -Subproject commit 900bad58c37c2ffce85ee73a8beabcd1ca63a3a7 +Subproject commit 46b43746dbed3aea595d7c42892500c810ef606d From 8cbf6ad56356af3209e8fb335298072ced6cdcbc Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 30 Apr 2024 12:18:15 -0600 Subject: [PATCH 015/784] Update iced --- iced | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iced b/iced index 46b43746..08fc97c7 160000 --- a/iced +++ b/iced @@ -1 +1 @@ -Subproject commit 46b43746dbed3aea595d7c42892500c810ef606d +Subproject commit 08fc97c7209e4acc34b05fdc43c6fb64eb4fa4d2 From 2ebf7e8e0ad522627e1dac44523695b27ea346fe Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 30 Apr 2024 16:17:07 -0700 Subject: [PATCH 016/784] Update `text_input` widget for iced_sctk drag-and-drop change --- src/widget/text_input/input.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widget/text_input/input.rs b/src/widget/text_input/input.rs index 552d1247..fd4a8f4f 100644 --- a/src/widget/text_input/input.rs +++ b/src/widget/text_input/input.rs @@ -1256,10 +1256,10 @@ where .collect(), actions: DndAction::Move, origin_id: window_id, - icon_id: Some(DndIcon::Widget( + icon_id: Some((DndIcon::Widget( icon_id, Box::new(state_clone.clone()), - )), + ), iced::Vector::ZERO)), data: Box::new(TextInputString(text.clone())), } }))); From ef635acb2be8f0ecb278dccc5d327ff84b088d15 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 1 May 2024 12:34:49 -0400 Subject: [PATCH 017/784] chore: update iced fixes dnd surface cleanup for multi-window apps --- iced | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iced b/iced index 08fc97c7..1869fe32 160000 --- a/iced +++ b/iced @@ -1 +1 @@ -Subproject commit 08fc97c7209e4acc34b05fdc43c6fb64eb4fa4d2 +Subproject commit 1869fe32ecf13caf36568c2ce8fc2acad33b33f0 From 7a964772c2970d9e6976cbc86df260931de2e83a Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 1 May 2024 12:50:14 -0400 Subject: [PATCH 018/784] cargo fmt --- src/widget/text_input/input.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/widget/text_input/input.rs b/src/widget/text_input/input.rs index fd4a8f4f..0ddbd567 100644 --- a/src/widget/text_input/input.rs +++ b/src/widget/text_input/input.rs @@ -1256,10 +1256,10 @@ where .collect(), actions: DndAction::Move, origin_id: window_id, - icon_id: Some((DndIcon::Widget( - icon_id, - Box::new(state_clone.clone()), - ), iced::Vector::ZERO)), + icon_id: Some(( + DndIcon::Widget(icon_id, Box::new(state_clone.clone())), + iced::Vector::ZERO, + )), data: Box::new(TextInputString(text.clone())), } }))); From 3927a553fba7ecf0dbd66fffaaa96836e0428973 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 1 May 2024 15:10:29 -0400 Subject: [PATCH 019/784] chore: update window clipboard --- iced | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iced b/iced index 1869fe32..927a8525 160000 --- a/iced +++ b/iced @@ -1 +1 @@ -Subproject commit 1869fe32ecf13caf36568c2ce8fc2acad33b33f0 +Subproject commit 927a85251c6dc56cb827711f842a8b87590f2ad4 From e9aa969e61ba812416dceeb15bc7926986e1e793 Mon Sep 17 00:00:00 2001 From: Ryan Brue Date: Mon, 6 May 2024 15:56:13 -0500 Subject: [PATCH 020/784] feat(applet): add panel type attribute to applet context This will allow applets to know whether they are on a Panel, Dock, or other named panel. --- src/applet/mod.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/applet/mod.rs b/src/applet/mod.rs index 8d6acde0..d89535a2 100644 --- a/src/applet/mod.rs +++ b/src/applet/mod.rs @@ -32,6 +32,7 @@ pub struct Context { pub anchor: PanelAnchor, pub background: CosmicPanelBackground, pub output_name: String, + pub panel_type: PanelType, } #[derive(Clone, Debug)] @@ -41,6 +42,23 @@ pub enum Size { Hardcoded((u16, u16)), } +#[derive(Clone, Debug, PartialEq)] +pub enum PanelType { + Panel, + Dock, + Other(String), +} + +impl From for PanelType { + fn from(value: String) -> Self { + match value.as_str() { + "Panel" => PanelType::Panel, + "Dock" => PanelType::Dock, + other => PanelType::Other(other.to_string()), + } + } +} + impl Default for Context { fn default() -> Self { Self { @@ -59,6 +77,7 @@ impl Default for Context { .and_then(|size| ron::from_str(size.as_str()).ok()) .unwrap_or(CosmicPanelBackground::ThemeDefault), output_name: std::env::var("COSMIC_PANEL_OUTPUT").unwrap_or_default(), + panel_type: PanelType::from(std::env::var("COSMIC_PANEL_NAME").unwrap_or_default()), } } } From fec71fdda4c5d48741e5ff8cdb66d9b3d2d52b9b Mon Sep 17 00:00:00 2001 From: Ryan Brue Date: Mon, 6 May 2024 16:09:35 -0500 Subject: [PATCH 021/784] feat(applet): impl ToString for PanelType --- src/applet/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/applet/mod.rs b/src/applet/mod.rs index d89535a2..35bf42d4 100644 --- a/src/applet/mod.rs +++ b/src/applet/mod.rs @@ -49,6 +49,16 @@ pub enum PanelType { Other(String), } +impl ToString for PanelType { + fn to_string(&self) -> String { + match self { + Self::Panel => "Panel".to_string(), + Self::Dock => "Dock".to_string(), + Self::Other(other) => other.clone(), + } + } +} + impl From for PanelType { fn from(value: String) -> Self { match value.as_str() { From 23f6fc8358e9e117bac372468f230f493f3cea5b Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 6 May 2024 18:49:32 -0400 Subject: [PATCH 022/784] fix(applet): set the svg style to default if the icon is not symbolic --- src/applet/mod.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/applet/mod.rs b/src/applet/mod.rs index 35bf42d4..61e9cef9 100644 --- a/src/applet/mod.rs +++ b/src/applet/mod.rs @@ -151,15 +151,18 @@ impl Context { &self, icon: widget::icon::Handle, ) -> crate::widget::Button<'a, Message, crate::Theme, Renderer> { - let suggested = self.suggested_size(icon.symbolic); - let applet_padding = self.suggested_padding(icon.symbolic); + let symbolic = icon.symbolic; + let suggested = self.suggested_size(symbolic); + let applet_padding = self.suggested_padding(symbolic); crate::widget::button( widget::icon(icon) - .style(theme::Svg::Custom(Rc::new(|theme| { - crate::iced_style::svg::Appearance { + .style(if symbolic { + theme::Svg::Custom(Rc::new(|theme| crate::iced_style::svg::Appearance { color: Some(theme.cosmic().background.on.into()), - } - }))) + })) + } else { + theme::Svg::default() + }) .width(Length::Fixed(suggested.0 as f32)) .height(Length::Fixed(suggested.1 as f32)), ) From 3cfc5c16a306d7d38a507554d46f4fe5857cab78 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Tue, 7 May 2024 01:50:54 +0200 Subject: [PATCH 023/784] fix: avoid setting WGPU_POWER_PREF=low if system is desktop --- src/app/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/app/mod.rs b/src/app/mod.rs index 1487d72c..391e2f22 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -159,11 +159,23 @@ pub fn run(settings: Settings, flags: App::Flags) -> iced::Res /// Default to rendering the application with the low power GPU preference. #[cfg(feature = "wgpu")] fn wgpu_power_pref() { + fn is_desktop() -> bool { + let chassis = std::fs::read_to_string("/sys/class/dmi/id/chassis_type").unwrap_or_default(); + + chassis.trim() == "3" + } + + // Ignore if the system is a desktop. + if is_desktop() { + return; + } + // Ignore if requested to run on NVIDIA GPU if std::env::var("__NV_PRIME_RENDER_OFFLOAD").ok().as_deref() == Some("1") { return; } + #[allow(clippy::items_after_statements)] const VAR: &str = "WGPU_POWER_PREF"; if std::env::var(VAR).is_err() { std::env::set_var(VAR, "low"); From 6a974352631713d3986e7b73be74a0934831c1a2 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Sun, 12 May 2024 14:17:43 -0400 Subject: [PATCH 024/784] fix(cosmic-config): attempt to reconnect to the settings daemon --- Cargo.toml | 9 ++- cosmic-config/Cargo.toml | 5 +- cosmic-config/src/dbus.rs | 110 +++++++++++++++++++++++++----- cosmic-config/src/subscription.rs | 16 ++--- examples/application/Cargo.toml | 2 +- 5 files changed, 114 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1a72d908..3cba6ce9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,8 +34,15 @@ serde-keycode = ["iced_core/serde"] single-instance = ["dep:zbus", "serde", "ron"] # smol async runtime smol = ["iced/smol", "zbus?/async-io"] +tokio = [ + "dep:tokio", + "ashpd?/tokio", + "iced/tokio", + "rfd?/tokio", + "zbus?/tokio", + "cosmic-config/tokio", +] # Tokio async runtime -tokio = ["dep:tokio", "ashpd?/tokio", "iced/tokio", "rfd?/tokio", "zbus?/tokio"] # Wayland window support wayland = [ "ashpd?/wayland", diff --git a/cosmic-config/Cargo.toml b/cosmic-config/Cargo.toml index 3f7e3210..7dcf2c21 100644 --- a/cosmic-config/Cargo.toml +++ b/cosmic-config/Cargo.toml @@ -17,12 +17,15 @@ notify = "6.0.0" ron = "0.8.0" serde = "1.0.152" cosmic-config-derive = { path = "../cosmic-config-derive/", optional = true } -iced = { path = "../iced/", default-features = false, optional = true } +iced = { path = "../iced/", default-features = false, optional = true } iced_futures = { path = "../iced/futures/", default-features = false, optional = true } once_cell = "1.19.0" cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings", branch = "cosmic-settings-daemon", optional = true } futures-util = { version = "0.3", optional = true } dirs.workspace = true +tokio = { version = "1.0", optional = true, features = ["time"] } +async-std = { version = "1.10", optional = true } +tracing = "0.1" [target.'cfg(unix)'.dependencies] xdg = "2.1" diff --git a/cosmic-config/src/dbus.rs b/cosmic-config/src/dbus.rs index 4977672b..28ac8844 100644 --- a/cosmic-config/src/dbus.rs +++ b/cosmic-config/src/dbus.rs @@ -1,9 +1,10 @@ use std::ops::Deref; use crate::{CosmicConfigEntry, Update}; -use cosmic_settings_daemon::{ConfigProxy, CosmicSettingsDaemonProxy}; +use cosmic_settings_daemon::{Changed, ConfigProxy, CosmicSettingsDaemonProxy}; use futures_util::SinkExt; -use iced_futures::futures::{future::pending, StreamExt}; +use iced_futures::futures::{self, future::pending, StreamExt}; + pub async fn settings_daemon_proxy() -> zbus::Result> { let conn = zbus::Connection::session().await?; CosmicSettingsDaemonProxy::new(&conn).await @@ -51,11 +52,17 @@ impl Watcher { } } +#[allow(clippy::too_many_lines)] pub fn watcher_subscription( settings_daemon: CosmicSettingsDaemonProxy<'static>, config_id: &'static str, is_state: bool, ) -> iced_futures::Subscription> { + enum Change { + Changes(Changed), + OwnerChanged(bool), + } + let id = std::any::TypeId::of::(); iced_futures::subscription::channel((is_state, config_id, id), 5, move |mut tx| async move { let version = T::VERSION; @@ -68,6 +75,7 @@ pub fn watcher_subscription().await; unreachable!(); }; + let mut config = match T::get_entry(&cosmic_config) { Ok(config) => config, Err((errors, default)) => { @@ -77,6 +85,7 @@ pub fn watcher_subscription().await; - unreachable!(); - }; + let mut attempts = 0; loop { - let Ok(mut changes) = watcher.receive_changed().await else { - pending::<()>().await; - unreachable!(); + let watcher = if is_state { + Watcher::new_state(&settings_daemon, config_id, version).await + } else { + Watcher::new_config(&settings_daemon, config_id, version).await }; - while let Some(change) = changes.next().await { + let Ok(watcher) = watcher else { + tracing::error!("Failed to create watcher for {config_id}"); + + #[cfg(feature = "tokio")] + ::tokio::time::sleep(::tokio::time::Duration::from_secs(2_u64.pow(attempts))).await; + #[cfg(feature = "async-std")] + async_std::task::sleep(std::time::Duration::from_secs(2_u64.pow(attempts))).await; + #[cfg(not(any(feature = "tokio", feature = "async-std")))] + { + pending::<()>().await; + unreachable!(); + } + attempts += 1; + // The settings daemon has exited + continue; + }; + let Ok(changes) = watcher.receive_changed().await else { + tracing::error!("Failed to listen for changes for {config_id}"); + + #[cfg(feature = "tokio")] + ::tokio::time::sleep(::tokio::time::Duration::from_secs(2_u64.pow(attempts))).await; + #[cfg(feature = "async-std")] + async_std::task::sleep(std::time::Duration::from_secs(2_u64.pow(attempts))).await; + #[cfg(not(any(feature = "tokio", feature = "async-std")))] + { + pending::<()>().await; + unreachable!(); + } + attempts += 1; + // The settings daemon has exited + continue; + }; + + let mut changes = changes.map(Change::Changes).fuse(); + + let Ok(owner_changed) = watcher.receive_owner_changed().await else { + tracing::error!("Failed to listen for owner changes for {config_id}"); + #[cfg(feature = "tokio")] + ::tokio::time::sleep(::tokio::time::Duration::from_secs(2_u64.pow(attempts))).await; + #[cfg(feature = "async-std")] + async_std::task::sleep(std::time::Duration::from_secs(2_u64.pow(attempts))).await; + #[cfg(not(any(feature = "tokio", feature = "async-std")))] + { + pending::<()>().await; + unreachable!(); + } + attempts += 1; + // The settings daemon has exited + continue; + }; + let mut owner_changed = owner_changed + .map(|c| Change::OwnerChanged(c.is_some())) + .fuse(); + loop { + let change: Changed = futures::select! { + c = changes.next() => { + let Some(Change::Changes(c)) = c else { + break; + }; + c + } + c = owner_changed.next() => { + let Some(Change::OwnerChanged(cont)) = c else { + break; + }; + if cont { + continue; + } else { + // The settings daemon has exited + break; + } + }, + }; + + // Reset the attempts counter if we received a change + attempts = 0; let Ok(args) = change.args() else { - continue; + // The settings daemon has exited + break; }; let (errors, keys) = config.update_keys(&cosmic_config, &[args.key]); if !keys.is_empty() { diff --git a/cosmic-config/src/subscription.rs b/cosmic-config/src/subscription.rs index e1e4f7ac..ef88866c 100644 --- a/cosmic-config/src/subscription.rs +++ b/cosmic-config/src/subscription.rs @@ -71,20 +71,18 @@ async fn start_listening< match state { ConfigState::Init(config_id, version, is_state) => { let (tx, rx) = mpsc::channel(100); - let config = match if is_state { + let Ok(config) = (if is_state { Config::new_state(&config_id, version) } else { Config::new(&config_id, version) - } { - Ok(c) => c, - Err(_) => return ConfigState::Failed, + }) else { + return ConfigState::Failed; }; - let watcher = match config.watch(move |_helper, keys| { + let Ok(watcher) = config.watch(move |_helper, keys| { let mut tx = tx.clone(); let _ = tx.try_send(keys.to_vec()); - }) { - Ok(w) => w, - Err(_) => return ConfigState::Failed, + }) else { + return ConfigState::Failed; }; match T::get_entry(&config) { @@ -115,7 +113,7 @@ async fn start_listening< if !changed.is_empty() { _ = output .send(crate::Update { - errors: errors, + errors, keys: changed, config: conf_data.clone(), }) diff --git a/examples/application/Cargo.toml b/examples/application/Cargo.toml index e19f1d34..2c69b9c3 100644 --- a/examples/application/Cargo.toml +++ b/examples/application/Cargo.toml @@ -11,4 +11,4 @@ tracing-log = "0.2.0" [dependencies.libcosmic] path = "../../" default-features = false -features = ["debug", "winit", "tokio", "xdg-portal"] +features = ["debug", "winit", "tokio", "xdg-portal", "dbus-config"] From 05a9e7639fabdcfdf4d9827755a9f06a7bc48645 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 13 May 2024 11:15:24 -0400 Subject: [PATCH 025/784] feat: update the headerbar to match figma i don't think the alpha is actually working for the icon, but the color should match --- src/app/mod.rs | 7 ++++--- src/theme/style/button.rs | 8 +++++--- src/widget/nav_bar_toggle.rs | 9 +++++++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 391e2f22..b8d7a70d 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -676,8 +676,7 @@ impl ApplicationExt for App { let is_condensed = core.is_condensed(); let focused = core .focused_window() - .map(|i| i == self.main_window_id()) - .unwrap_or_default(); + .is_some_and(|i| i == self.main_window_id()); let content_row = crate::widget::row::with_children({ let mut widgets = Vec::with_capacity(2); @@ -731,11 +730,13 @@ impl ApplicationExt for App { if self.nav_model().is_some() { let toggle = crate::widget::nav_bar_toggle() .active(core.nav_bar_active()) + .selected(focused) .on_toggle(if is_condensed { Message::Cosmic(cosmic::Message::ToggleNavBarCondensed) } else { Message::Cosmic(cosmic::Message::ToggleNavBar) - }); + }) + .style(crate::theme::Button::HeaderBar); header = header.start(toggle); } diff --git a/src/theme/style/button.rs b/src/theme/style/button.rs index 110e25f5..e1898eee 100644 --- a/src/theme/style/button.rs +++ b/src/theme/style/button.rs @@ -75,7 +75,9 @@ pub fn appearance( corner_radii = &cosmic.corner_radii.radius_m; } - let (background, _text, _icon) = color(&cosmic.icon_button); + let (background, text, icon) = color(&cosmic.icon_button); + appearance.text_color = text; + appearance.icon_color = icon; appearance.background = Some(Background::Color(background)); } @@ -161,8 +163,8 @@ impl StyleSheet for crate::Theme { { Some(self.cosmic().accent_color().into()) } else if matches!(style, Button::HeaderBar) && !selected { - let mut c = Color::from(component.on); - c.a = 0.8; + let mut c = Color::from(self.cosmic().background.on); + c.a = 0.75; Some(c) } else { Some(component.on.into()) diff --git a/src/widget/nav_bar_toggle.rs b/src/widget/nav_bar_toggle.rs index 64e991f0..cd1ec67f 100644 --- a/src/widget/nav_bar_toggle.rs +++ b/src/widget/nav_bar_toggle.rs @@ -13,6 +13,8 @@ pub struct NavBarToggle { active: bool, #[setters(strip_option)] on_toggle: Option, + style: crate::theme::Button, + selected: bool, } #[must_use] @@ -20,6 +22,8 @@ pub fn nav_bar_toggle() -> NavBarToggle { NavBarToggle { active: false, on_toggle: None, + style: crate::theme::Button::Text, + selected: false, } } @@ -37,10 +41,11 @@ impl<'a, Message: 'static + Clone> From> for Element<'a, M .symbolic(true) }; - widget::button::text("") - .leading_icon(icon) + widget::button::icon(icon) .padding([8, 16, 8, 16]) .on_press_maybe(nav_bar_toggle.on_toggle) + .selected(nav_bar_toggle.selected) + .style(nav_bar_toggle.style) .apply(widget::container) .center_y() .height(Length::Fill) From c1cfa024d6424d964841345b951cb6035ce54b65 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Sun, 12 May 2024 22:16:53 -0400 Subject: [PATCH 026/784] feat: basic vscode theme export support --- cosmic-theme/Cargo.toml | 19 +- cosmic-theme/src/image.rs | 1 - cosmic-theme/src/lib.rs | 2 + cosmic-theme/src/output/gtk4_output.rs | 14 +- cosmic-theme/src/output/mod.rs | 45 +++- cosmic-theme/src/output/vs_code.rs | 320 +++++++++++++++++++++++++ 6 files changed, 378 insertions(+), 23 deletions(-) delete mode 100644 cosmic-theme/src/image.rs create mode 100644 cosmic-theme/src/output/vs_code.rs diff --git a/cosmic-theme/Cargo.toml b/cosmic-theme/Cargo.toml index 5acc85ab..0e6c5c20 100644 --- a/cosmic-theme/Cargo.toml +++ b/cosmic-theme/Cargo.toml @@ -10,20 +10,23 @@ features = ["test_all_features"] rustdoc-args = ["--cfg", "docsrs"] [features] -default = [] -gtk4-output = [] +default = ["export"] +export = ["serde_json"] no-default = [] -theme-from-image = ["kmeans_colors", "image"] [dependencies] -palette = {version = "0.7.3", features = ["serializing"] } +palette = { version = "0.7.3", features = ["serializing"] } almost = "0.2" -kmeans_colors = { version = "0.5", features = ["palette_color"], default-features = false, optional = true } -image = {version = "0.24.1", optional = true } serde = { version = "1.0.129", features = ["derive"] } +serde_json = { version = "1.0.64", optional = true, features = [ + "preserve_order", +] } ron = "0.8" lazy_static = "1.4.0" -csscolorparser = {version = "0.6.2", features = ["serde"]} -cosmic-config = { path = "../cosmic-config/", default-features = false, features = ["subscription", "macro"] } +csscolorparser = { version = "0.6.2", features = ["serde"] } +cosmic-config = { path = "../cosmic-config/", default-features = false, features = [ + "subscription", + "macro", +] } dirs.workspace = true thiserror = "1.0.5" diff --git a/cosmic-theme/src/image.rs b/cosmic-theme/src/image.rs deleted file mode 100644 index 52a319ea..00000000 --- a/cosmic-theme/src/image.rs +++ /dev/null @@ -1 +0,0 @@ -// TODO theme from image diff --git a/cosmic-theme/src/lib.rs b/cosmic-theme/src/lib.rs index 6c46cb30..c30234b1 100644 --- a/cosmic-theme/src/lib.rs +++ b/cosmic-theme/src/lib.rs @@ -9,6 +9,8 @@ pub use model::*; mod model; + +#[cfg(feature = "export")] mod output; /// composite colors in srgb diff --git a/cosmic-theme/src/output/gtk4_output.rs b/cosmic-theme/src/output/gtk4_output.rs index ccfbf2ed..db21b8d1 100644 --- a/cosmic-theme/src/output/gtk4_output.rs +++ b/cosmic-theme/src/output/gtk4_output.rs @@ -5,15 +5,8 @@ use std::{ io::Write, num::NonZeroUsize, }; -use thiserror::Error; -#[derive(Error, Debug)] -pub enum OutputError { - #[error("IO Error: {0}")] - Io(std::io::Error), - #[error("Missing config directory")] - MissingConfigDir, -} +use super::{to_hex, OutputError}; impl Theme { #[must_use] @@ -249,11 +242,6 @@ fn component_gtk4_css(prefix: &str, c: &Component) -> String { ) } -fn to_hex(c: Srgba) -> String { - let c_u8: Rgba = c.into_format(); - format!("{:02x}{:02x}{:02x}", c_u8.red, c_u8.green, c_u8.blue) -} - fn color_css(prefix: &str, c_3: Srgba) -> String { let oklch: palette::Oklch = c_3.into_color(); let c_2: Srgba = oklch.lighten(0.1).into_color(); diff --git a/cosmic-theme/src/output/mod.rs b/cosmic-theme/src/output/mod.rs index d2ea7853..31b0f773 100644 --- a/cosmic-theme/src/output/mod.rs +++ b/cosmic-theme/src/output/mod.rs @@ -1,3 +1,46 @@ -#[cfg(feature = "gtk4-output")] +use palette::{rgb::Rgba, Srgba}; +use thiserror::Error; + +use crate::Theme; + /// Module for outputting the Cosmic gtk4 theme type as CSS pub mod gtk4_output; + +pub mod vs_code; + +#[derive(Error, Debug)] +pub enum OutputError { + #[error("IO Error: {0}")] + Io(std::io::Error), + #[error("Missing config directory")] + MissingConfigDir, +} + +impl Theme { + pub fn apply_exports(&self) -> Result<(), OutputError> { + let gtk_res = Theme::apply_gtk(self.is_dark); + let vs_res = self.clone().apply_vs_code(); + gtk_res?; + vs_res?; + Ok(()) + } + + pub fn write_exports(&self) -> Result<(), OutputError> { + let gtk_res = self.write_gtk4(); + gtk_res?; + Ok(()) + } + + pub fn reset_exports() -> Result<(), OutputError> { + let gtk_res = Theme::reset_gtk(); + let vs_res = Theme::reset_vs_code(); + gtk_res?; + vs_res?; + Ok(()) + } +} + +pub fn to_hex(c: Srgba) -> String { + let c_u8: Rgba = c.into_format(); + format!("{:02x}{:02x}{:02x}", c_u8.red, c_u8.green, c_u8.blue) +} diff --git a/cosmic-theme/src/output/vs_code.rs b/cosmic-theme/src/output/vs_code.rs new file mode 100644 index 00000000..7557b0cd --- /dev/null +++ b/cosmic-theme/src/output/vs_code.rs @@ -0,0 +1,320 @@ +use serde::{Deserialize, Serialize}; + +use crate::Theme; + +use super::{to_hex, OutputError}; + +/// Represents the workbench.colorCustomizations section of a VS Code settings.json file +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VsTheme { + #[serde(rename = "editor.background")] + editor_background: String, + #[serde(rename = "sideBar.background")] + sidebar_background: String, + #[serde(rename = "activityBar.background")] + activity_bar_background: String, + #[serde(rename = "notificationCenterHeader.background")] + notification_center_header_background: String, + #[serde(rename = "notifications.background")] + notifications_background: String, + #[serde(rename = "activityBarTop.activeBackground")] + activity_bar_top_active_background: String, + #[serde(rename = "editorGroupHeader.tabsBackground")] + editor_group_header_tabs_background: String, + #[serde(rename = "editorGroupHeader.noTabsBackground")] + editor_group_header_no_tabs_background: String, + #[serde(rename = "titleBar.activeBackground")] + title_bar_active_background: String, + #[serde(rename = "titleBar.inactiveBackground")] + title_bar_inactive_background: String, + #[serde(rename = "statusBar.background")] + status_bar_background: String, + #[serde(rename = "statusBar.noFolderBackground")] + status_bar_no_folder_background: String, + #[serde(rename = "statusBar.debuggingBackground")] + status_bar_debugging_background: String, + #[serde(rename = "tab.activeBackground")] + tab_active_background: String, + #[serde(rename = "tab.activeBorder")] + tab_active_border: String, + #[serde(rename = "tab.activeBorderTop")] + tab_active_border_top: String, + #[serde(rename = "tab.hoverBackground")] + tab_hover_background: String, + #[serde(rename = "quickInput.background")] + quick_input_background: String, + #[serde(rename = "tab.inactiveBackground")] + tab_inactive_background: String, + #[serde(rename = "sideBarSectionHeader.background")] + side_bar_section_header_background: String, + #[serde(rename = "list.focusOutline")] + list_focus_outline: String, + #[serde(rename = "banner.background")] + banner_background: String, + #[serde(rename = "breadcrumb.background")] + breadcrumb_background: String, + #[serde(rename = "commandCenter.background")] + command_center_background: String, + #[serde(rename = "terminal.background")] + terminal_background: String, + #[serde(rename = "menu.background")] + menu_background: String, + #[serde(rename = "panel.background")] + panel_background: String, + #[serde(rename = "peekViewEditorGutter.background")] + peek_view_editor_gutter_background: String, + #[serde(rename = "peekViewResult.background")] + peek_view_result_background: String, + #[serde(rename = "peekViewTitle.background")] + peek_view_title_background: String, + #[serde(rename = "peekViewEditor.background")] + peek_view_editor_background: String, + #[serde(rename = "peekViewResult.selectionBackground")] + peek_view_result_selection_background: String, + #[serde(rename = "editorWidget.background")] + editor_widget_background: String, + #[serde(rename = "editorSuggestWidget.background")] + editor_suggest_widget_background: String, + #[serde(rename = "editorHoverWidget.background")] + editor_hover_widget_background: String, + #[serde(rename = "input.background")] + input_background: String, + #[serde(rename = "dropdown.background")] + dropdown_background: String, + #[serde(rename = "settings.checkboxBackground")] + settings_checkbox_background: String, + #[serde(rename = "settings.textInputBackground")] + settings_text_input_background: String, + #[serde(rename = "settings.numberInputBackground")] + settings_number_input_background: String, + #[serde(rename = "settings.dropdownBackground")] + settings_dropdown_background: String, + #[serde(rename = "sideBar.dropBackground")] + side_bar_drop_background: String, + #[serde(rename = "list.activeSelectionBackground")] + list_active_selection_background: String, + #[serde(rename = "list.inactiveSelectionBackground")] + list_inactive_selection_background: String, + #[serde(rename = "list.focusBackground")] + list_focus_background: String, + #[serde(rename = "list.hoverBackground")] + list_hover_background: String, + + // text colors + #[serde(rename = "editor.foreground")] + editor_foreground: String, + #[serde(rename = "editorLineNumber.foreground")] + editor_line_number_foreground: String, + #[serde(rename = "editorCursor.foreground")] + editor_cursor_foreground: String, + #[serde(rename = "sideBar.foreground")] + side_bar_foreground: String, + #[serde(rename = "activityBar.foreground")] + activity_bar_foreground: String, + #[serde(rename = "statusBar.foreground")] + status_bar_foreground: String, + #[serde(rename = "tab.activeForeground")] + tab_active_foreground: String, + #[serde(rename = "tab.inactiveForeground")] + tab_inactive_foreground: String, + #[serde(rename = "editorGroupHeader.tabsForeground")] + editor_group_header_tabs_foreground: String, + #[serde(rename = "sideBarSectionHeader.foreground")] + side_bar_section_header_foreground: String, + #[serde(rename = "statusBar.debuggingForeground")] + status_bar_debugging_foreground: String, + #[serde(rename = "statusBar.noFolderForeground")] + status_bar_no_folder_foreground: String, + #[serde(rename = "editorWidget.foreground")] + editor_widget_foreground: String, + #[serde(rename = "editorSuggestWidget.foreground")] + editor_suggest_widget_foreground: String, + #[serde(rename = "editorHoverWidget.foreground")] + editor_hover_widget_foreground: String, + #[serde(rename = "input.foreground")] + input_foreground: String, + #[serde(rename = "dropdown.foreground")] + dropdown_foreground: String, + #[serde(rename = "terminal.foreground")] + terminal_foreground: String, + #[serde(rename = "menu.foreground")] + menu_foreground: String, + #[serde(rename = "panel.foreground")] + panel_foreground: String, + #[serde(rename = "peekViewEditorGutter.foreground")] + peek_view_editor_gutter_foreground: String, + #[serde(rename = "peekViewResult.selectionForeground")] + peek_view_result_selection_foreground: String, + #[serde(rename = "inputOption.activeBorder")] + input_option_active_border: String, + + // accent colors + #[serde(rename = "activityBarBadge.background")] + activity_bar_badge_background: String, + #[serde(rename = "statusBar.debuggingBorder")] + status_bar_debugging_border: String, + #[serde(rename = "button.background")] + button_background: String, + #[serde(rename = "button.hoverBackground")] + button_hover_background: String, + #[serde(rename = "statusBarItem.remoteBackground")] + status_bar_item_remote_background: String, + + + // accent fg colors + #[serde(rename = "activityBarBadge.foreground")] + activity_bar_badge_foreground: String, + #[serde(rename = "button.foreground")] + button_foreground: String, + #[serde(rename = "textLink.foreground")] + text_link_foreground: String, + #[serde(rename = "textLink.activeForeground")] + text_link_active_foreground: String, + #[serde(rename = "peekView.border")] + peek_view_border: String, + #[serde(rename = "settings.checkboxForeground")] + settings_checkbox_foreground: String, +} + +impl From for VsTheme { + fn from(theme: Theme) -> Self { + Self { + editor_background: format!("#{}", to_hex(theme.background.base)), + sidebar_background: format!("#{}", to_hex(theme.primary.base)), + activity_bar_background: format!("#{}", to_hex(theme.primary.base)), + notification_center_header_background: format!("#{}", to_hex(theme.background.base)), + notifications_background: format!("#{}", to_hex(theme.background.base)), + activity_bar_top_active_background: format!("#{}", to_hex(theme.primary.base)), + editor_group_header_tabs_background: format!("#{}", to_hex(theme.background.base)), + editor_group_header_no_tabs_background: format!("#{}", to_hex(theme.background.base)), + title_bar_active_background: format!("#{}", to_hex(theme.background.component.base)), + title_bar_inactive_background: format!( + "#{}", + to_hex( + theme + .background + .component.disabled + ) + ), + status_bar_background: format!("#{}", to_hex(theme.background.base)), + status_bar_no_folder_background: format!("#{}", to_hex(theme.background.base)), + status_bar_debugging_background: format!("#{}", to_hex(theme.background.base)), + tab_active_background: format!("#{}", to_hex(theme.primary.component.pressed)), + tab_active_border: format!("#{}", to_hex(theme.accent.base)), + tab_active_border_top: format!("#{}", to_hex(theme.accent.base)), + tab_hover_background: format!("#{}", to_hex(theme.primary.component.hover)), + tab_inactive_background: format!( + "#{}", + to_hex( + theme + .primary + .component + .base + ) + ), + quick_input_background: format!("#{}", to_hex(theme.primary.base)), + side_bar_section_header_background: format!("#{}", to_hex(theme.primary.base)), + banner_background: format!("#{}", to_hex(theme.primary.base)), + breadcrumb_background: format!("#{}", to_hex(theme.primary.base)), + command_center_background: format!("#{}", to_hex(theme.primary.base)), + terminal_background: format!("#{}", to_hex(theme.primary.base)), + menu_background: format!("#{}", to_hex(theme.primary.base)), + panel_background: format!("#{}", to_hex(theme.primary.base)), + peek_view_editor_gutter_background: format!("#{}", to_hex(theme.background.base)), + peek_view_result_background: format!("#{}", to_hex(theme.background.base)), + peek_view_title_background: format!("#{}", to_hex(theme.background.base)), + peek_view_editor_background: format!("#{}", to_hex(theme.background.base)), + peek_view_result_selection_background: format!("#{}", to_hex(theme.background.base)), + editor_widget_background: format!("#{}", to_hex(theme.background.base)), + editor_suggest_widget_background: format!("#{}", to_hex(theme.background.base)), + editor_hover_widget_background: format!("#{}", to_hex(theme.background.base)), + input_background: format!("#{}", to_hex(theme.background.base)), + dropdown_background: format!("#{}", to_hex(theme.background.base)), + settings_checkbox_background: format!("#{}", to_hex(theme.background.base)), + settings_text_input_background: format!("#{}", to_hex(theme.background.base)), + settings_number_input_background: format!("#{}", to_hex(theme.background.base)), + settings_dropdown_background: format!("#{}", to_hex(theme.background.base)), + side_bar_drop_background: format!("#{}", to_hex(theme.background.base)), + list_active_selection_background: format!("#{}", to_hex(theme.primary.base)), + list_inactive_selection_background: format!("#{}", to_hex(theme.primary.base)), + list_focus_background: format!("#{}", to_hex(theme.primary.base)), + list_hover_background: format!("#{}", to_hex(theme.primary.base)), + editor_foreground: format!("#{}", to_hex(theme.background.on)), + editor_line_number_foreground: format!("#{}", to_hex(theme.background.on)), + editor_cursor_foreground: format!("#{}", to_hex(theme.background.on)), + side_bar_foreground: format!("#{}", to_hex(theme.primary.on)), + activity_bar_foreground: format!("#{}", to_hex(theme.primary.on)), + status_bar_foreground: format!("#{}", to_hex(theme.primary.on)), + tab_active_foreground: format!("#{}", to_hex(theme.primary.on)), + tab_inactive_foreground: format!("#{}", to_hex(theme.primary.on)), + editor_group_header_tabs_foreground: format!("#{}", to_hex(theme.primary.on)), + side_bar_section_header_foreground: format!("#{}", to_hex(theme.primary.on)), + status_bar_debugging_foreground: format!("#{}", to_hex(theme.primary.on)), + status_bar_no_folder_foreground: format!("#{}", to_hex(theme.primary.on)), + editor_widget_foreground: format!("#{}", to_hex(theme.primary.on)), + editor_suggest_widget_foreground: format!("#{}", to_hex(theme.primary.on)), + editor_hover_widget_foreground: format!("#{}", to_hex(theme.primary.on)), + input_foreground: format!("#{}", to_hex(theme.primary.on)), + dropdown_foreground: format!("#{}", to_hex(theme.primary.on)), + terminal_foreground: format!("#{}", to_hex(theme.primary.on)), + menu_foreground: format!("#{}", to_hex(theme.primary.on)), + panel_foreground: format!("#{}", to_hex(theme.primary.on)), + peek_view_editor_gutter_foreground: format!("#{}", to_hex(theme.primary.on)), + peek_view_result_selection_foreground: format!("#{}", to_hex(theme.primary.on)), + input_option_active_border: format!("#{}", to_hex(theme.accent.base)), + activity_bar_badge_background: format!("#{}", to_hex(theme.accent.base)), + activity_bar_badge_foreground: format!("#{}", to_hex(theme.accent.on)), + status_bar_debugging_border: format!("#{}", to_hex(theme.accent.base)), + list_focus_outline: format!("#{}", to_hex(theme.accent.base)), + button_background: format!("#{}", to_hex(theme.accent_button.base)), + button_hover_background: format!("#{}", to_hex(theme.accent_button.hover)), + status_bar_item_remote_background: format!("#{}", to_hex(theme.accent.base)), + button_foreground: format!("#{}", to_hex(theme.accent_button.on)), + text_link_foreground: format!("#{}", to_hex(theme.accent.base)), + text_link_active_foreground: format!("#{}", to_hex(theme.accent.base)), + peek_view_border: format!("#{}", to_hex(theme.accent.base)), + settings_checkbox_foreground: format!("#{}", to_hex(theme.accent.base)), + } + } +} + +impl Theme { + pub fn apply_vs_code(self) -> Result<(), OutputError> { + let vs_theme = VsTheme::from(self); + let config_dir = dirs::config_dir().ok_or(OutputError::MissingConfigDir)?; + let vs_code_dir = config_dir.join("Code").join("User"); + if !vs_code_dir.exists() { + std::fs::create_dir_all(&vs_code_dir).map_err(OutputError::Io)?; + } + + // just add the json entry for workbench.colorCustomizations + let settings_file = vs_code_dir.join("settings.json"); + let settings = std::fs::read_to_string(&settings_file).unwrap_or_default(); + let mut settings: serde_json::Value = serde_json::from_str(&settings).unwrap_or_default(); + settings["workbench.colorCustomizations"] = serde_json::to_value(vs_theme).unwrap(); + std::fs::write( + &settings_file, + serde_json::to_string_pretty(&settings).unwrap(), + ) + .map_err(OutputError::Io)?; + + Ok(()) + } + + pub fn reset_vs_code() -> Result<(), OutputError> { + let config_dir = dirs::config_dir().ok_or(OutputError::MissingConfigDir)?; + let vs_code_dir = config_dir.join("Code").join("User"); + let settings_file = vs_code_dir.join("settings.json"); + // just remove the json entry for workbench.colorCustomizations + let settings = std::fs::read_to_string(&settings_file).unwrap_or_default(); + let mut settings: serde_json::Value = serde_json::from_str(&settings).unwrap_or_default(); + settings["workbench.colorCustomizations"] = serde_json::Value::Null; + std::fs::write( + &settings_file, + serde_json::to_string_pretty(&settings).unwrap(), + ) + .map_err(OutputError::Io)?; + + Ok(()) + } +} From 0ffe3f3a3eb0956a03a111589ce190e9470a2e05 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Sun, 12 May 2024 22:25:49 -0400 Subject: [PATCH 027/784] cargo fmt --- cosmic-theme/src/output/vs_code.rs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/cosmic-theme/src/output/vs_code.rs b/cosmic-theme/src/output/vs_code.rs index 7557b0cd..b8e43a17 100644 --- a/cosmic-theme/src/output/vs_code.rs +++ b/cosmic-theme/src/output/vs_code.rs @@ -160,7 +160,6 @@ pub struct VsTheme { #[serde(rename = "statusBarItem.remoteBackground")] status_bar_item_remote_background: String, - // accent fg colors #[serde(rename = "activityBarBadge.foreground")] activity_bar_badge_foreground: String, @@ -190,11 +189,7 @@ impl From for VsTheme { title_bar_active_background: format!("#{}", to_hex(theme.background.component.base)), title_bar_inactive_background: format!( "#{}", - to_hex( - theme - .background - .component.disabled - ) + to_hex(theme.background.component.disabled) ), status_bar_background: format!("#{}", to_hex(theme.background.base)), status_bar_no_folder_background: format!("#{}", to_hex(theme.background.base)), @@ -203,15 +198,7 @@ impl From for VsTheme { tab_active_border: format!("#{}", to_hex(theme.accent.base)), tab_active_border_top: format!("#{}", to_hex(theme.accent.base)), tab_hover_background: format!("#{}", to_hex(theme.primary.component.hover)), - tab_inactive_background: format!( - "#{}", - to_hex( - theme - .primary - .component - .base - ) - ), + tab_inactive_background: format!("#{}", to_hex(theme.primary.component.base)), quick_input_background: format!("#{}", to_hex(theme.primary.base)), side_bar_section_header_background: format!("#{}", to_hex(theme.primary.base)), banner_background: format!("#{}", to_hex(theme.primary.base)), From cac3d0b5df9097eb3d521aefe61fc26a790e7121 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 13 May 2024 08:58:49 -0400 Subject: [PATCH 028/784] chore(theme): set window.autoDetectColorScheme --- cosmic-theme/src/output/vs_code.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cosmic-theme/src/output/vs_code.rs b/cosmic-theme/src/output/vs_code.rs index b8e43a17..a0b4ba7d 100644 --- a/cosmic-theme/src/output/vs_code.rs +++ b/cosmic-theme/src/output/vs_code.rs @@ -279,6 +279,7 @@ impl Theme { let settings = std::fs::read_to_string(&settings_file).unwrap_or_default(); let mut settings: serde_json::Value = serde_json::from_str(&settings).unwrap_or_default(); settings["workbench.colorCustomizations"] = serde_json::to_value(vs_theme).unwrap(); + settings["window.autoDetectColorScheme"] = serde_json::Value::Bool(true); std::fs::write( &settings_file, serde_json::to_string_pretty(&settings).unwrap(), @@ -296,6 +297,7 @@ impl Theme { let settings = std::fs::read_to_string(&settings_file).unwrap_or_default(); let mut settings: serde_json::Value = serde_json::from_str(&settings).unwrap_or_default(); settings["workbench.colorCustomizations"] = serde_json::Value::Null; + std::fs::write( &settings_file, serde_json::to_string_pretty(&settings).unwrap(), From ac95b571ecd6a70cdbf81a1c88150cea1427fe8a Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Mon, 13 May 2024 09:33:47 -0600 Subject: [PATCH 029/784] feat: allow on_app_exit to override closing the application --- src/app/cosmic.rs | 6 ++++-- src/app/mod.rs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/app/cosmic.rs b/src/app/cosmic.rs index 76438aa3..559eb463 100644 --- a/src/app/cosmic.rs +++ b/src/app/cosmic.rs @@ -449,8 +449,10 @@ impl Cosmic { } Message::Close => { - self.app.on_app_exit(); - return self.close(); + return match self.app.on_app_exit() { + Some(message) => self.app.update(message), + None => self.close(), + }; } Message::SystemThemeModeChange(keys, mode) => { let mut cmds = vec![self.app.system_theme_mode_update(&keys, &mode)]; diff --git a/src/app/mod.rs b/src/app/mod.rs index b8d7a70d..ec5c5f96 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -494,8 +494,10 @@ where None } - /// Called before closing the application. - fn on_app_exit(&mut self) {} + /// Called before closing the application. Returning a message will override closing windows. + fn on_app_exit(&mut self) -> Option { + None + } /// Called when a window requests to be closed. fn on_close_requested(&self, id: window::Id) -> Option { From 84d77281cba84e19853c57acd5ae919b862502c0 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 13 May 2024 13:57:51 -0400 Subject: [PATCH 030/784] fix: context-drawer border & radius --- src/theme/style/iced.rs | 2 +- src/widget/context_drawer/widget.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/theme/style/iced.rs b/src/theme/style/iced.rs index 5cf9b6c4..87058bc5 100644 --- a/src/theme/style/iced.rs +++ b/src/theme/style/iced.rs @@ -495,7 +495,7 @@ impl container::StyleSheet for Theme { appearance.border = Border { color: cosmic.primary.divider.into(), - width: 1.0, + width: 0.0, radius: cosmic.corner_radii.radius_s.into(), }; diff --git a/src/widget/context_drawer/widget.rs b/src/widget/context_drawer/widget.rs index 18a5b88e..0674a616 100644 --- a/src/widget/context_drawer/widget.rs +++ b/src/widget/context_drawer/widget.rs @@ -88,8 +88,8 @@ impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> { // XXX this is a hack to get around that drawer: container( LayerContainer::new(pane) - .style(crate::style::Container::ContextDrawer) .layer(cosmic_theme::Layer::Primary) + .style(crate::style::Container::ContextDrawer) .width(Length::Fill) .height(Length::Fill) .max_width(480.0), From 92920046425515645d7715310ce3648a34b47b2c Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 13 May 2024 22:11:59 -0400 Subject: [PATCH 031/784] feat: allow preference for dark or light system theme also converts applet to use preferred system theme --- src/app/cosmic.rs | 75 ++++++++++++++++++++++++++++++++++------------- src/applet/mod.rs | 14 +++++++-- src/theme/mod.rs | 24 +++++++++++---- 3 files changed, 85 insertions(+), 28 deletions(-) diff --git a/src/app/cosmic.rs b/src/app/cosmic.rs index 559eb463..1560c56f 100644 --- a/src/app/cosmic.rs +++ b/src/app/cosmic.rs @@ -207,11 +207,22 @@ where }), self.app .core() - .watch_config::(if self.app.core().system_theme_mode.is_dark { - cosmic_theme::DARK_THEME_ID - } else { - cosmic_theme::LIGHT_THEME_ID - }) + .watch_config::( + if THEME + .with(|t| { + if let ThemeType::System { prefer_dark, .. } = t.borrow().theme_type { + prefer_dark + } else { + None + } + }) + .unwrap_or_else(|| self.app.core().system_theme_mode.is_dark) + { + cosmic_theme::DARK_THEME_ID + } else { + cosmic_theme::LIGHT_THEME_ID + }, + ) .map(|update| { for why in update.errors { tracing::error!(?why, "cosmic theme config update error"); @@ -398,7 +409,7 @@ impl Cosmic { Message::AppThemeChange(mut theme) => { // Apply last-known system theme if the system theme is preferred. - if let ThemeType::System(_) = theme.theme_type { + if let ThemeType::System { theme: _, .. } = theme.theme_type { self.app.core_mut().theme_sub_counter += 1; theme = self.app.core().system_theme.clone(); @@ -426,8 +437,12 @@ impl Cosmic { let mut cosmic_theme = t.borrow_mut(); // Only apply update if the theme is set to load a system theme - if let ThemeType::System(_) = cosmic_theme.theme_type { - let new_theme = if let Some(a) = portal_accent { + if let ThemeType::System { + theme: _, + prefer_dark, + } = cosmic_theme.theme_type + { + let mut new_theme = if let Some(a) = portal_accent { let t_inner = theme.cosmic(); if a.distance_squared(*t_inner.accent_color()) > 0.00001 { Theme::system(Arc::new(t_inner.with_accent(a))) @@ -437,6 +452,8 @@ impl Cosmic { } else { theme }; + new_theme.theme_type.prefer_dark(prefer_dark); + cosmic_theme.set_theme(new_theme.theme_type); } }); @@ -455,6 +472,15 @@ impl Cosmic { }; } Message::SystemThemeModeChange(keys, mode) => { + if THEME.with(|t| match t.borrow().theme_type { + ThemeType::System { + theme: _, + prefer_dark, + } => prefer_dark.is_some(), + _ => false, + }) { + return iced::Command::none(); + } let mut cmds = vec![self.app.system_theme_mode_update(&keys, &mode)]; let core = self.app.core_mut(); @@ -486,9 +512,8 @@ impl Cosmic { core.system_theme = new_theme.clone(); THEME.with(move |t| { let mut cosmic_theme = t.borrow_mut(); - // Only apply update if the theme is set to load a system theme - if let ThemeType::System(_) = cosmic_theme.theme_type { + if let ThemeType::System { theme: _, .. } = cosmic_theme.theme_type { cosmic_theme.set_theme(new_theme.theme_type); } }); @@ -515,6 +540,15 @@ impl Cosmic { #[cfg(feature = "xdg-portal")] Message::DesktopSettings(crate::theme::portal::Desktop::ColorScheme(s)) => { use ashpd::desktop::settings::ColorScheme; + if THEME.with(|t| match t.borrow().theme_type { + ThemeType::System { + theme: _, + prefer_dark, + } => prefer_dark.is_some(), + _ => false, + }) { + return iced::Command::none(); + } let is_dark = match s { ColorScheme::NoPreference => None, ColorScheme::PreferDark => Some(true), @@ -537,7 +571,7 @@ impl Cosmic { let mut cosmic_theme = t.borrow_mut(); // Only apply update if the theme is set to load a system theme - if let ThemeType::System(_) = cosmic_theme.theme_type { + if let ThemeType::System { theme: _, .. } = cosmic_theme.theme_type { cosmic_theme.set_theme(new_theme.theme_type); } }); @@ -546,7 +580,6 @@ impl Cosmic { #[cfg(feature = "xdg-portal")] Message::DesktopSettings(crate::theme::portal::Desktop::Accent(c)) => { use palette::Srgba; - let c = Srgba::new(c.red() as f32, c.green() as f32, c.blue() as f32, 1.0); let core = self.app.core_mut(); core.portal_accent = Some(c); @@ -561,8 +594,15 @@ impl Cosmic { let mut cosmic_theme = t.borrow_mut(); // Only apply update if the theme is set to load a system theme - if let ThemeType::System(t) = cosmic_theme.theme_type.clone() { - cosmic_theme.set_theme(ThemeType::System(Arc::new(t.with_accent(c)))); + if let ThemeType::System { + theme: t, + prefer_dark, + } = cosmic_theme.theme_type.clone() + { + cosmic_theme.set_theme(ThemeType::System { + theme: Arc::new(t.with_accent(c)), + prefer_dark, + }); } }); } @@ -588,12 +628,7 @@ impl Cosmic { Message::Unfocus(id) => { let core = self.app.core_mut(); - if core - .focused_window - .as_ref() - .map(|cur| *cur == id) - .unwrap_or_default() - { + if core.focused_window.as_ref().is_some_and(|cur| *cur == id) { core.focused_window = None; } } diff --git a/src/applet/mod.rs b/src/applet/mod.rs index 61e9cef9..0dbce82e 100644 --- a/src/applet/mod.rs +++ b/src/applet/mod.rs @@ -11,7 +11,7 @@ use crate::{ window, Color, Length, Limits, Rectangle, }, iced_style, iced_widget, - theme::{self, Button, THEME}, + theme::{self, system_dark, system_light, Button, THEME}, widget, Application, Element, Renderer, }; pub use cosmic_panel_config; @@ -265,8 +265,16 @@ impl Context { #[must_use] pub fn theme(&self) -> Option { match self.background { - CosmicPanelBackground::Dark => Some(theme::Theme::dark()), - CosmicPanelBackground::Light => Some(theme::Theme::light()), + CosmicPanelBackground::Dark => { + let mut theme = system_dark(); + theme.theme_type.prefer_dark(Some(true)); + Some(theme) + } + CosmicPanelBackground::Light => { + let mut theme = system_light(); + theme.theme_type.prefer_dark(Some(false)); + Some(theme) + } _ => Some(theme::system_preference()), } } diff --git a/src/theme/mod.rs b/src/theme/mod.rs index 0f72efb1..72a2c218 100644 --- a/src/theme/mod.rs +++ b/src/theme/mod.rs @@ -151,7 +151,10 @@ pub enum ThemeType { HighContrastDark, HighContrastLight, Custom(Arc), - System(Arc), + System { + prefer_dark: Option, + theme: Arc, + }, } impl ThemeType { @@ -161,7 +164,7 @@ impl ThemeType { match self { Self::Dark | Self::HighContrastDark => true, Self::Light | Self::HighContrastLight => false, - Self::Custom(theme) | Self::System(theme) => theme.is_dark, + Self::Custom(theme) | Self::System { theme, .. } => theme.is_dark, } } @@ -171,7 +174,15 @@ impl ThemeType { match self { Self::Dark | Self::Light => false, Self::HighContrastDark | Self::HighContrastLight => true, - Self::Custom(theme) | Self::System(theme) => theme.is_high_contrast, + Self::Custom(theme) | Self::System { theme, .. } => theme.is_high_contrast, + } + } + + /// Prefer dark or light theme. + /// If `None`, the system preference is used. + pub fn prefer_dark(&mut self, new_prefer_dark: Option) { + if let Self::System { prefer_dark, .. } = self { + *prefer_dark = new_prefer_dark; } } } @@ -190,7 +201,7 @@ impl Theme { ThemeType::Light => &COSMIC_LIGHT, ThemeType::HighContrastDark => &COSMIC_HC_DARK, ThemeType::HighContrastLight => &COSMIC_HC_LIGHT, - ThemeType::Custom(ref t) | ThemeType::System(ref t) => t.as_ref(), + ThemeType::Custom(ref t) | ThemeType::System { theme: ref t, .. } => t.as_ref(), } } @@ -231,7 +242,10 @@ impl Theme { pub fn system(theme: Arc) -> Self { Self { - theme_type: ThemeType::System(theme), + theme_type: ThemeType::System { + theme, + prefer_dark: None, + }, ..Default::default() } } From a2e2711061d739e9aef3231beca63d3ee465ee9f Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 14 May 2024 08:04:09 -0600 Subject: [PATCH 032/784] fix(settings): set iced window exit_on_close_request --- src/app/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/mod.rs b/src/app/mod.rs index ec5c5f96..a1c635c6 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -92,6 +92,7 @@ pub(crate) fn iced_settings( iced.default_font = settings.default_font; iced.default_text_size = iced::Pixels(settings.default_text_size); iced.exit_on_close_request = settings.exit_on_close; + iced.window.exit_on_close_request = settings.exit_on_close; iced.id = Some(App::APP_ID.to_owned()); #[cfg(all(not(feature = "wayland"), target_os = "linux"))] { From 9677b74b89d128196719a534a81c9b3ab0ac0dcd Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 15 May 2024 14:33:42 -0400 Subject: [PATCH 033/784] feat(applet): scale text based on panel setting --- src/applet/mod.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/applet/mod.rs b/src/applet/mod.rs index 0dbce82e..d985a796 100644 --- a/src/applet/mod.rs +++ b/src/applet/mod.rs @@ -16,13 +16,15 @@ use crate::{ }; pub use cosmic_panel_config; use cosmic_panel_config::{CosmicPanelBackground, PanelAnchor, PanelSize}; +use cosmic_theme::Theme; +use iced::Pixels; use iced_core::{Padding, Shadow}; use iced_style::container::Appearance; use iced_widget::runtime::command::platform_specific::wayland::popup::{ SctkPopupSettings, SctkPositioner, }; use sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{Anchor, Gravity}; -use std::rc::Rc; +use std::{borrow::Cow, rc::Rc}; use crate::app::cosmic; @@ -278,6 +280,23 @@ impl Context { _ => Some(theme::system_preference()), } } + + pub fn text<'a>(&self, msg: impl Into>) -> crate::widget::Text<'a, crate::Theme> { + let msg = msg.into(); + let t = match self.size { + Size::PanelSize(PanelSize::XL) => crate::widget::text::title1, + Size::PanelSize(PanelSize::L) => crate::widget::text::title2, + Size::PanelSize(PanelSize::M) => crate::widget::text::title3, + Size::PanelSize(PanelSize::S) => crate::widget::text::title4, + Size::PanelSize(PanelSize::XS) => crate::widget::text::body, + Size::Hardcoded(_) => todo!(), + }; + t(msg) + .style(crate::theme::Text::Color(Color::from( + THEME.with(|t| t.borrow().cosmic().on_bg_color()), + ))) + .font(crate::font::FONT) + } } /// Launch the application with the given settings. From 1b63c9e38b0291415d50ccf7fe692b6ea18c2af5 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 15 May 2024 14:37:05 -0400 Subject: [PATCH 034/784] chore: update libcosmic --- iced | 2 +- src/app/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/iced b/iced index 927a8525..7c58b8ed 160000 --- a/iced +++ b/iced @@ -1 +1 @@ -Subproject commit 927a85251c6dc56cb827711f842a8b87590f2ad4 +Subproject commit 7c58b8eda9885083e019a5ea14edf4652740e154 diff --git a/src/app/mod.rs b/src/app/mod.rs index a1c635c6..ba9d6231 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -92,7 +92,7 @@ pub(crate) fn iced_settings( iced.default_font = settings.default_font; iced.default_text_size = iced::Pixels(settings.default_text_size); iced.exit_on_close_request = settings.exit_on_close; - iced.window.exit_on_close_request = settings.exit_on_close; + // iced.window.exit_on_close_request = settings.exit_on_close; iced.id = Some(App::APP_ID.to_owned()); #[cfg(all(not(feature = "wayland"), target_os = "linux"))] { From 4bbb6c3cab3d3edc50f892e54f326e17f1ff58d5 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 15 May 2024 14:37:51 -0400 Subject: [PATCH 035/784] fix: cosmic example --- examples/cosmic/src/window/demo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cosmic/src/window/demo.rs b/examples/cosmic/src/window/demo.rs index 20025441..713ec5c5 100644 --- a/examples/cosmic/src/window/demo.rs +++ b/examples/cosmic/src/window/demo.rs @@ -40,7 +40,7 @@ impl From<&ThemeType> for ThemeVariant { ThemeType::HighContrastDark => ThemeVariant::HighContrastDark, ThemeType::HighContrastLight => ThemeVariant::HighContrastLight, ThemeType::Custom(_) => ThemeVariant::Custom, - ThemeType::System(_) => ThemeVariant::System, + ThemeType::System { .. } => ThemeVariant::System, } } } From be329403c11e2366a1b264d71aeeca6fbd554a83 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 15 May 2024 14:39:30 -0400 Subject: [PATCH 036/784] cleanup(applet): replace todo for text size --- src/applet/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applet/mod.rs b/src/applet/mod.rs index d985a796..bad203f5 100644 --- a/src/applet/mod.rs +++ b/src/applet/mod.rs @@ -289,7 +289,7 @@ impl Context { Size::PanelSize(PanelSize::M) => crate::widget::text::title3, Size::PanelSize(PanelSize::S) => crate::widget::text::title4, Size::PanelSize(PanelSize::XS) => crate::widget::text::body, - Size::Hardcoded(_) => todo!(), + Size::Hardcoded(_) => crate::widget::text, }; t(msg) .style(crate::theme::Text::Color(Color::from( From 54eb5487b4d57d3203c5e9af33fbcebcecf04a00 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 15 May 2024 14:50:21 -0400 Subject: [PATCH 037/784] fix(applet): remove text color style --- src/applet/mod.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/applet/mod.rs b/src/applet/mod.rs index bad203f5..fcd04d8c 100644 --- a/src/applet/mod.rs +++ b/src/applet/mod.rs @@ -291,11 +291,7 @@ impl Context { Size::PanelSize(PanelSize::XS) => crate::widget::text::body, Size::Hardcoded(_) => crate::widget::text, }; - t(msg) - .style(crate::theme::Text::Color(Color::from( - THEME.with(|t| t.borrow().cosmic().on_bg_color()), - ))) - .font(crate::font::FONT) + t(msg).font(crate::font::FONT) } } From 78a137214d9d3534c10940d335d08960563c3c80 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 16 May 2024 15:36:53 -0400 Subject: [PATCH 038/784] chore: update zbus --- Cargo.toml | 10 +++++----- cosmic-config/Cargo.toml | 4 ++-- cosmic-config/src/dbus.rs | 6 +++--- src/app/mod.rs | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3cba6ce9..8b7a2b3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,21 +65,21 @@ xdg-portal = ["ashpd"] [dependencies] apply = "0.3.0" -ashpd = { version = "0.7.0", default-features = false, optional = true } +ashpd = { version = "0.8.1", default-features = false, optional = true } async-fs = { version = "2.1", optional = true } cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "e4e6f8c", optional = true } chrono = "0.4.35" cosmic-config = { path = "cosmic-config" } -cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings", branch = "cosmic-settings-daemon", optional = true } +cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true } css-color = "0.2.5" derive_setters = "0.1.5" fraction = "0.14.0" -image = { version = "0.24.6", optional = true } +image = { version = "0.25.1", optional = true } lazy_static = "1.4.0" mime = { version = "0.3.17", optional = true } nix = { version = "0.27", features = ["process"], optional = true } palette = "0.7.3" -rfd = { version = "0.13.0", optional = true } +rfd = { version = "0.14.0", optional = true } serde = { version = "1.0.180", optional = true } slotmap = "1.0.6" thiserror = "1.0.44" @@ -87,7 +87,7 @@ tokio = { version = "1.24.2", optional = true } tracing = "0.1" unicode-segmentation = "1.6" url = "2.4.0" -zbus = { version = "3.14.1", default-features = false, optional = true } +zbus = { version = "4.2.1", default-features = false, optional = true } [target.'cfg(unix)'.dependencies] freedesktop-icons = "0.2.5" diff --git a/cosmic-config/Cargo.toml b/cosmic-config/Cargo.toml index 7dcf2c21..3699e889 100644 --- a/cosmic-config/Cargo.toml +++ b/cosmic-config/Cargo.toml @@ -10,7 +10,8 @@ macro = ["cosmic-config-derive"] subscription = ["iced_futures"] [dependencies] -zbus = { version = "3.14.1", default-features = false, optional = true } +cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true } +zbus = { version = "4.2.1", default-features = false, optional = true } atomicwrites = { git = "https://github.com/jackpot51/rust-atomicwrites" } calloop = { version = "0.13.0", optional = true } notify = "6.0.0" @@ -20,7 +21,6 @@ cosmic-config-derive = { path = "../cosmic-config-derive/", optional = true } iced = { path = "../iced/", default-features = false, optional = true } iced_futures = { path = "../iced/futures/", default-features = false, optional = true } once_cell = "1.19.0" -cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings", branch = "cosmic-settings-daemon", optional = true } futures-util = { version = "0.3", optional = true } dirs.workspace = true tokio = { version = "1.0", optional = true, features = ["time"] } diff --git a/cosmic-config/src/dbus.rs b/cosmic-config/src/dbus.rs index 28ac8844..a36d49db 100644 --- a/cosmic-config/src/dbus.rs +++ b/cosmic-config/src/dbus.rs @@ -29,7 +29,7 @@ impl Watcher { version: u64, ) -> zbus::Result { let (path, name) = settings_daemon_proxy.watch_config(id, version).await?; - ConfigProxy::builder(settings_daemon_proxy.connection()) + ConfigProxy::builder(settings_daemon_proxy.inner().connection()) .path(path)? .destination(name)? .build() @@ -43,7 +43,7 @@ impl Watcher { version: u64, ) -> zbus::Result { let (path, name) = settings_daemon_proxy.watch_state(id, version).await?; - ConfigProxy::builder(settings_daemon_proxy.connection()) + ConfigProxy::builder(settings_daemon_proxy.inner().connection()) .path(path)? .destination(name)? .build() @@ -140,7 +140,7 @@ pub fn watcher_subscription( @@ -221,7 +221,7 @@ impl DbusActivation { } #[cfg(feature = "single-instance")] -#[dbus_proxy(interface = "org.freedesktop.DbusActivation", assume_defaults = true)] +#[proxy(interface = "org.freedesktop.DbusActivation", assume_defaults = true)] pub trait DbusActivationInterface { /// Activate the application. fn activate(&mut self, platform_data: HashMap<&str, Value<'_>>) -> zbus::Result<()>; @@ -243,7 +243,7 @@ pub trait DbusActivationInterface { } #[cfg(feature = "single-instance")] -#[dbus_interface(name = "org.freedesktop.DbusActivation")] +#[interface(name = "org.freedesktop.DbusActivation")] impl DbusActivation { async fn activate(&mut self, platform_data: HashMap<&str, Value<'_>>) { if let Some(tx) = &mut self.0 { From 11a73354cae950a471b19ce61e8bc502491d7b13 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 16 May 2024 19:10:33 -0400 Subject: [PATCH 039/784] fix(text-input): avoid jumping to start of text as cursor blinks, and clipping text inside the input --- src/widget/text_input/input.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/widget/text_input/input.rs b/src/widget/text_input/input.rs index 0ddbd567..a3c8dbd8 100644 --- a/src/widget/text_input/input.rs +++ b/src/widget/text_input/input.rs @@ -2151,7 +2151,7 @@ pub fn draw<'a, Message>( Some(( renderer::Quad { bounds: Rectangle { - x: text_bounds.x + text_value_width, + x: text_bounds.x + text_value_width - offset, y: text_bounds.y, width: 1.0, height: text_bounds.height, @@ -2173,7 +2173,7 @@ pub fn draw<'a, Message>( ) } } else { - (None, 0.0) + (None, offset) } } cursor::State::Selection { start, end } => { @@ -2237,6 +2237,7 @@ pub fn draw<'a, Message>( } let bounds = Rectangle { + x: text_bounds.x - offset, y: text_bounds.center_y(), width: f32::INFINITY, ..text_bounds @@ -2264,9 +2265,7 @@ pub fn draw<'a, Message>( ); }; - renderer.with_layer(text_bounds, |renderer| { - renderer.with_translation(Vector::new(-offset, 0.0), render); - }); + renderer.with_layer(text_bounds, render); let trailing_icon_tree = children.get(child_index); From c7dc5ab1edad2a231aeb6468f34a65eba267cfca Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 17 May 2024 19:07:42 +0200 Subject: [PATCH 040/784] fix(button): icon buttons should inherit text and icon color from container --- src/theme/style/button.rs | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/theme/style/button.rs b/src/theme/style/button.rs index e1898eee..0509d62f 100644 --- a/src/theme/style/button.rs +++ b/src/theme/style/button.rs @@ -13,27 +13,27 @@ use crate::{ #[derive(Default)] pub enum Button { + AppletIcon, Custom { active: Box Appearance>, disabled: Box Appearance>, hovered: Box Appearance>, pressed: Box Appearance>, }, + AppletMenu, Destructive, - Link, - Icon, HeaderBar, + Icon, IconVertical, Image, + Link, + MenuItem, + MenuRoot, #[default] Standard, Suggested, Text, Transparent, - AppletMenu, - AppletIcon, - MenuRoot, - MenuItem, } pub fn appearance( @@ -76,8 +76,6 @@ pub fn appearance( } let (background, text, icon) = color(&cosmic.icon_button); - appearance.text_color = text; - appearance.icon_color = icon; appearance.background = Some(Background::Color(background)); } @@ -162,10 +160,6 @@ impl StyleSheet for crate::Theme { ) && selected { Some(self.cosmic().accent_color().into()) - } else if matches!(style, Button::HeaderBar) && !selected { - let mut c = Color::from(self.cosmic().background.on); - c.a = 0.75; - Some(c) } else { Some(component.on.into()) }; @@ -211,10 +205,6 @@ impl StyleSheet for crate::Theme { ) && selected { Some(self.cosmic().accent_color().into()) - } else if matches!(style, Button::HeaderBar) && !selected { - let mut c = Color::from(component.on); - c.a = 0.8; - Some(c) } else { Some(component.on.into()) }; @@ -236,10 +226,6 @@ impl StyleSheet for crate::Theme { ) && selected { Some(self.cosmic().accent_color().into()) - } else if matches!(style, Button::HeaderBar) && !selected { - let mut c = Color::from(component.on); - c.a = 0.8; - Some(c) } else { Some(component.on.into()) }; From 135558872350f55d9225cf1eea3552642c29c5b2 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 17 May 2024 19:08:43 +0200 Subject: [PATCH 041/784] improv(button): specialize button for cosmic, and apply alpha to header buttons --- src/applet/mod.rs | 6 +- src/widget/button/link.rs | 51 +++++----- src/widget/button/mod.rs | 17 +--- src/widget/button/text.rs | 35 ++++--- src/widget/button/widget.rs | 181 +++++++++++++++------------------ src/widget/calendar.rs | 2 +- src/widget/color_picker/mod.rs | 4 +- src/widget/menu/menu_tree.rs | 6 +- 8 files changed, 138 insertions(+), 164 deletions(-) diff --git a/src/applet/mod.rs b/src/applet/mod.rs index fcd04d8c..5b7e72d8 100644 --- a/src/applet/mod.rs +++ b/src/applet/mod.rs @@ -152,7 +152,7 @@ impl Context { pub fn icon_button_from_handle<'a, Message: 'static>( &self, icon: widget::icon::Handle, - ) -> crate::widget::Button<'a, Message, crate::Theme, Renderer> { + ) -> crate::widget::Button<'a, Message> { let symbolic = icon.symbolic; let suggested = self.suggested_size(symbolic); let applet_padding = self.suggested_padding(symbolic); @@ -176,7 +176,7 @@ impl Context { pub fn icon_button<'a, Message: 'static>( &self, icon_name: &'a str, - ) -> crate::widget::Button<'a, Message, crate::Theme, Renderer> { + ) -> crate::widget::Button<'a, Message> { self.icon_button_from_handle( widget::icon::from_name(icon_name) .symbolic(true) @@ -371,7 +371,7 @@ pub fn style() -> ::Style { pub fn menu_button<'a, Message>( content: impl Into>, -) -> crate::widget::Button<'a, Message, crate::Theme, crate::Renderer> { +) -> crate::widget::Button<'a, Message> { crate::widget::Button::new(content) .style(Button::AppletMenu) .padding(menu_control_padding()) diff --git a/src/widget/button/link.rs b/src/widget/button/link.rs index 6843269c..d1d43e90 100644 --- a/src/widget/button/link.rs +++ b/src/widget/button/link.rs @@ -58,33 +58,32 @@ pub fn icon() -> Handle { impl<'a, Message: Clone + 'static> From> for Element<'a, Message> { fn from(mut builder: Button<'a, Message>) -> Element<'a, Message> { - let button: super::Button<'a, Message, crate::Theme, crate::Renderer> = - row::with_capacity(2) - .push({ - let mut font = crate::font::DEFAULT; - font.weight = builder.font_weight; + let button: super::Button<'a, Message> = row::with_capacity(2) + .push({ + let mut font = crate::font::DEFAULT; + font.weight = builder.font_weight; - // TODO: Avoid allocation - crate::widget::text(builder.label.to_string()) - .size(builder.font_size) - .line_height(LineHeight::Absolute(builder.line_height.into())) - .font(font) - }) - .push_maybe(if builder.variant.trailing_icon { - Some(icon().icon().size(builder.icon_size)) - } else { - None - }) - .padding(builder.padding) - .width(builder.width) - .height(builder.height) - .spacing(builder.spacing) - .align_items(Alignment::Center) - .apply(button) - .padding(0) - .id(builder.id) - .on_press_maybe(builder.on_press.take()) - .style(builder.style); + // TODO: Avoid allocation + crate::widget::text(builder.label.to_string()) + .size(builder.font_size) + .line_height(LineHeight::Absolute(builder.line_height.into())) + .font(font) + }) + .push_maybe(if builder.variant.trailing_icon { + Some(icon().icon().size(builder.icon_size)) + } else { + None + }) + .padding(builder.padding) + .width(builder.width) + .height(builder.height) + .spacing(builder.spacing) + .align_items(Alignment::Center) + .apply(button) + .padding(0) + .id(builder.id) + .on_press_maybe(builder.on_press.take()) + .style(builder.style); if builder.tooltip.is_empty() { button.into() diff --git a/src/widget/button/mod.rs b/src/widget/button/mod.rs index b0d1afad..9ba5dc2f 100644 --- a/src/widget/button/mod.rs +++ b/src/widget/button/mod.rs @@ -26,28 +26,19 @@ pub use text::{destructive, standard, suggested, text}; mod widget; pub use widget::{draw, focus, layout, mouse_interaction, Button}; -use crate::iced::Element; use iced_core::font::Weight; use iced_core::widget::Id; use iced_core::{Length, Padding}; use std::borrow::Cow; -pub fn button<'a, Message, Theme>( - content: impl Into>, -) -> Button<'a, Message, Theme, crate::Renderer> -where - Theme: style::StyleSheet, -{ +pub fn button<'a, Message>(content: impl Into>) -> Button<'a, Message> { Button::new(content) } -pub fn custom_image_button<'a, Message, Theme>( - content: impl Into>, +pub fn custom_image_button<'a, Message>( + content: impl Into>, on_remove: Option, -) -> Button<'a, Message, Theme, crate::Renderer> -where - Theme: style::StyleSheet, -{ +) -> Button<'a, Message> { Button::new_image(content, on_remove) } diff --git a/src/widget/button/text.rs b/src/widget/button/text.rs index d8f339ff..134e0623 100644 --- a/src/widget/button/text.rs +++ b/src/widget/button/text.rs @@ -109,24 +109,23 @@ impl<'a, Message: Clone + 'static> From> for Element<'a, Mes .into() }); - let button: super::Button<'a, Message, crate::Theme, crate::Renderer> = - row::with_capacity(3) - // Optional icon to place before label. - .push_maybe(leading_icon) - // Optional label between icons. - .push_maybe(label) - // Optional icon to place behind the label. - .push_maybe(trailing_icon) - .padding(builder.padding) - .width(builder.width) - .height(builder.height) - .spacing(builder.spacing) - .align_items(Alignment::Center) - .apply(button) - .padding(0) - .id(builder.id) - .on_press_maybe(builder.on_press.take()) - .style(builder.style); + let button: super::Button<'a, Message> = row::with_capacity(3) + // Optional icon to place before label. + .push_maybe(leading_icon) + // Optional label between icons. + .push_maybe(label) + // Optional icon to place behind the label. + .push_maybe(trailing_icon) + .padding(builder.padding) + .width(builder.width) + .height(builder.height) + .spacing(builder.spacing) + .align_items(Alignment::Center) + .apply(button) + .padding(0) + .id(builder.id) + .on_press_maybe(builder.on_press.take()) + .style(builder.style); if builder.tooltip.is_empty() { button.into() diff --git a/src/widget/button/widget.rs b/src/widget/button/widget.rs index d17dd623..2bea46d1 100644 --- a/src/widget/button/widget.rs +++ b/src/widget/button/widget.rs @@ -10,7 +10,7 @@ use iced_runtime::core::widget::Id; use iced_runtime::{keyboard, Command}; use iced_core::event::{self, Event}; -use iced_core::renderer::{self, Quad}; +use iced_core::renderer::{self, Quad, Renderer}; use iced_core::touch; use iced_core::widget::tree::{self, Tree}; use iced_core::widget::Operation; @@ -18,8 +18,7 @@ use iced_core::{layout, svg}; use iced_core::{mouse, Border}; use iced_core::{overlay, Shadow}; use iced_core::{ - Background, Clipboard, Color, Element, Layout, Length, Padding, Point, Rectangle, Shell, - Vector, Widget, + Background, Clipboard, Color, Layout, Length, Padding, Point, Rectangle, Shell, Vector, Widget, }; use iced_renderer::core::widget::{operation, OperationOutputWrapper}; @@ -39,11 +38,7 @@ enum Variant { /// A generic button which emits a message when pressed. #[allow(missing_debug_implementations)] #[must_use] -pub struct Button<'a, Message, Theme, Renderer> -where - Renderer: iced_core::Renderer, - Theme: super::style::StyleSheet, -{ +pub struct Button<'a, Message> { id: Id, #[cfg(feature = "a11y")] name: Option>, @@ -51,23 +46,19 @@ where description: Option>, #[cfg(feature = "a11y")] label: Option>, - content: Element<'a, Message, Theme, Renderer>, + content: crate::Element<'a, Message>, on_press: Option, width: Length, height: Length, padding: Padding, selected: bool, - style: ::Style, + style: crate::theme::Button, variant: Variant, } -impl<'a, Message, Theme, Renderer> Button<'a, Message, Theme, Renderer> -where - Renderer: iced_core::Renderer, - Theme: super::style::StyleSheet, -{ +impl<'a, Message> Button<'a, Message> { /// Creates a new [`Button`] with the given content. - pub fn new(content: impl Into>) -> Self { + pub fn new(content: impl Into>) -> Self { Self { id: Id::unique(), #[cfg(feature = "a11y")] @@ -82,13 +73,13 @@ where height: Length::Shrink, padding: Padding::new(5.0), selected: false, - style: ::Style::default(), + style: crate::theme::Button::default(), variant: Variant::Normal, } } pub fn new_image( - content: impl Into>, + content: impl Into>, on_remove: Option, ) -> Self { Self { @@ -105,7 +96,7 @@ where height: Length::Shrink, padding: Padding::new(5.0), selected: false, - style: ::Style::default(), + style: crate::theme::Button::default(), variant: Variant::Image { on_remove, close_icon: crate::widget::icon::from_name("window-close-symbolic") @@ -171,7 +162,7 @@ where } /// Sets the style variant of this [`Button`]. - pub fn style(mut self, style: ::Style) -> Self { + pub fn style(mut self, style: crate::theme::Button) -> Self { self.style = style; self } @@ -207,12 +198,8 @@ where } } -impl<'a, Message, Theme, Renderer> Widget - for Button<'a, Message, Theme, Renderer> -where - Message: 'a + Clone, - Renderer: 'a + iced_core::Renderer + svg::Renderer, - Theme: super::style::StyleSheet, +impl<'a, Message: 'a + Clone> Widget + for Button<'a, Message> { fn tag(&self) -> tree::Tag { tree::Tag::of::() @@ -237,7 +224,7 @@ where fn layout( &self, tree: &mut Tree, - renderer: &Renderer, + renderer: &crate::Renderer, limits: &layout::Limits, ) -> layout::Node { layout( @@ -258,7 +245,7 @@ where &self, tree: &mut Tree, layout: Layout<'_>, - renderer: &Renderer, + renderer: &crate::Renderer, operation: &mut dyn Operation>, ) { operation.container(None, layout.bounds(), &mut |operation| { @@ -279,7 +266,7 @@ where event: Event, layout: Layout<'_>, cursor: mouse::Cursor, - renderer: &Renderer, + renderer: &crate::Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, @@ -330,11 +317,12 @@ where ) } + #[allow(clippy::too_many_lines)] fn draw( &self, tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, + renderer: &mut crate::Renderer, + theme: &crate::Theme, renderer_style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, @@ -343,31 +331,60 @@ where let bounds = layout.bounds(); let content_layout = layout.children().next().unwrap(); - let styling = draw::<_, Theme>( - renderer, - bounds, - cursor, - self.on_press.is_some(), - self.selected, - theme, - &self.style, - || tree.state.downcast_ref::(), - |renderer, styling| { - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - &renderer::Style { - icon_color: styling.icon_color.unwrap_or(renderer_style.icon_color), - text_color: styling.text_color.unwrap_or(renderer_style.icon_color), - scale_factor: renderer_style.scale_factor, - }, - content_layout, - cursor, - &bounds, - ); - }, - ); + let mut headerbar_alpha = None; + + let is_enabled = self.on_press.is_some(); + let is_mouse_over = cursor.position().is_some_and(|p| bounds.contains(p)); + + let state = tree.state.downcast_ref::(); + + let styling = if !is_enabled { + theme.disabled(&self.style) + } else if is_mouse_over { + if state.is_pressed { + if !self.selected && matches!(self.style, crate::theme::Button::HeaderBar) { + headerbar_alpha = Some(0.8); + } + + theme.pressed(state.is_focused, self.selected, &self.style) + } else { + if !self.selected && matches!(self.style, crate::theme::Button::HeaderBar) { + headerbar_alpha = Some(0.8); + } + + theme.hovered(state.is_focused, self.selected, &self.style) + } + } else { + if !self.selected && matches!(self.style, crate::theme::Button::HeaderBar) { + headerbar_alpha = Some(0.75); + } + + theme.active(state.is_focused, self.selected, &self.style) + }; + + let mut icon_color = styling.icon_color.unwrap_or(renderer_style.icon_color); + let mut text_color = styling.text_color.unwrap_or(renderer_style.text_color); + + if let Some(alpha) = headerbar_alpha { + icon_color.a = alpha; + text_color.a = alpha; + } + + draw::<_, crate::Theme>(renderer, bounds, &styling, |renderer, _styling| { + self.content.as_widget().draw( + &tree.children[0], + renderer, + theme, + &renderer::Style { + icon_color, + text_color, + scale_factor: renderer_style.scale_factor, + }, + content_layout, + cursor, + &bounds, + ); + }); if let Variant::Image { close_icon, @@ -405,7 +422,7 @@ where iced_core::svg::Renderer::draw( renderer, crate::widget::common::object_select().clone(), - styling.icon_color, + Some(icon_color), Rectangle { width: 16.0, height: 16.0, @@ -434,7 +451,7 @@ where iced_core::svg::Renderer::draw( renderer, close_icon.clone(), - styling.icon_color, + Some(icon_color), Rectangle { width: 16.0, height: 16.0, @@ -454,7 +471,7 @@ where layout: Layout<'_>, cursor: mouse::Cursor, _viewport: &Rectangle, - _renderer: &Renderer, + _renderer: &crate::Renderer, ) -> mouse::Interaction { mouse_interaction(layout, cursor, self.on_press.is_some()) } @@ -463,8 +480,8 @@ where &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { + renderer: &crate::Renderer, + ) -> Option> { self.content.as_widget_mut().overlay( &mut tree.children[0], layout.children().next().unwrap(), @@ -547,14 +564,8 @@ where } } -impl<'a, Message, Theme, Renderer> From> - for Element<'a, Message, Theme, Renderer> -where - Message: Clone + 'a, - Renderer: iced_core::Renderer + svg::Renderer + 'a, - Theme: super::style::StyleSheet + 'a, -{ - fn from(button: Button<'a, Message, Theme, Renderer>) -> Self { +impl<'a, Message: Clone + 'a> From> for crate::Element<'a, Message> { + fn from(button: Button<'a, Message>) -> Self { Self::new(button) } } @@ -674,36 +685,14 @@ pub fn update<'a, Message: Clone>( } #[allow(clippy::too_many_arguments)] -pub fn draw<'a, Renderer: iced_core::Renderer, Theme>( +pub fn draw( renderer: &mut Renderer, bounds: Rectangle, - cursor: mouse::Cursor, - is_enabled: bool, - is_selected: bool, - style_sheet: &dyn StyleSheet