diff --git a/Cargo.toml b/Cargo.toml index 8930129e..6913c990 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,7 +158,7 @@ phf = { version = "0.13.1", features = ["macros"] } [dependencies.icetron_assets] git = "ssh://git@bitbucket.org/playtron-one/icetron.git" -rev = "a1bf042" +rev = "6db4fa7" [dependencies.cosmic-theme] path = "cosmic-theme" diff --git a/src/theme/style/button.rs b/src/theme/style/button.rs index e6ae8ff6..17dac774 100644 --- a/src/theme/style/button.rs +++ b/src/theme/style/button.rs @@ -32,6 +32,7 @@ pub enum Button { MenuItem, MenuRoot, NavToggle, + Secondary, #[default] Standard, Suggested, @@ -52,14 +53,32 @@ pub fn appearance( let mut appearance = Style::new(); let hc = theme.theme_type.is_high_contrast(); match style { - Button::Standard - | Button::Text - | Button::Suggested + Button::Standard => { + corner_radii = &cosmic.corner_radii.radius_m; + appearance.background = Some(Background::Color(crate::theme::STATE_DEFAULT_COLOR)); + appearance.text_color = Some(Color::WHITE); + appearance.icon_color = Some(Color::WHITE); + } + + Button::Secondary => { + corner_radii = &cosmic.corner_radii.radius_m; + appearance.background = Some(Background::Color(Color::from_rgb8(224, 224, 224))); + appearance.text_color = Some(Color::BLACK); + appearance.icon_color = Some(Color::BLACK); + } + + Button::Text => { + let (background, _, _) = color(&cosmic.text_button); + appearance.background = Some(Background::Color(background)); + appearance.text_color = Some(crate::theme::STATE_DEFAULT_COLOR); + appearance.icon_color = Some(crate::theme::STATE_DEFAULT_COLOR); + corner_radii = &cosmic.corner_radii.radius_m; + } + + Button::Suggested | Button::Destructive | Button::Transparent => { let style_component = match style { - Button::Standard => &cosmic.button, - Button::Text => &cosmic.text_button, Button::Suggested => &cosmic.accent_button, Button::Destructive => &cosmic.destructive_button, Button::Transparent => &TRANSPARENT_COMPONENT, @@ -68,13 +87,8 @@ pub fn appearance( let (background, text, icon) = color(style_component); appearance.background = Some(Background::Color(background)); - if !matches!(style, Button::Standard) { - appearance.text_color = text; - appearance.icon_color = icon; - } else if hc { - appearance.border_color = style_component.border.into(); - appearance.border_width = 1.; - } + appearance.text_color = text; + appearance.icon_color = icon; } Button::Icon | Button::IconVertical | Button::HeaderBar | Button::NavToggle => { @@ -254,12 +268,18 @@ impl Catalog for crate::Theme { Some(component.on.into()) }; - if matches!(style, Button::ListItem) { + if matches!(style, Button::ListItem | Button::Text) { ( crate::theme::STATE_DEFAULT_BG, text_color, text_color, ) + } else if matches!(style, Button::MenuItem | Button::MenuFolder) { + ( + Color::from_rgb8(230, 230, 230), + text_color, + text_color, + ) } else { (component.hover.into(), text_color, text_color) } @@ -283,12 +303,18 @@ impl Catalog for crate::Theme { Some(component.on.into()) }; - if matches!(style, Button::ListItem) { + if matches!(style, Button::ListItem | Button::Text) { ( crate::theme::STATE_DEFAULT_BG, text_color, text_color, ) + } else if matches!(style, Button::MenuItem | Button::MenuFolder) { + ( + Color::from_rgb8(220, 220, 220), + text_color, + text_color, + ) } else { (component.pressed.into(), text_color, text_color) } diff --git a/src/theme/style/iced.rs b/src/theme/style/iced.rs index 40bec8d7..ce2824e6 100644 --- a/src/theme/style/iced.rs +++ b/src/theme/style/iced.rs @@ -654,16 +654,16 @@ impl iced_container::Catalog for Theme { Container::Dialog => iced_container::Style { icon_color: Some(Color::from(cosmic.primary.on)), text_color: Some(Color::from(cosmic.primary.on)), - background: Some(iced::Background::Color(cosmic.primary.base.into())), + background: Some(iced::Background::Color(Color::from_rgb8(245, 245, 245))), border: Border { - color: cosmic.primary.divider.into(), - width: 1.0, + color: Color::TRANSPARENT, + width: 0.0, radius: cosmic.corner_radii.radius_m.into(), }, shadow: Shadow { - color: cosmic.shade.into(), - offset: Vector::new(0.0, 4.0), - blur_radius: 16.0, + color: Color::from_rgba(0.0, 0.0, 0.0, 0.25), + offset: Vector::new(0.0, 8.0), + blur_radius: 32.0, }, }, } @@ -698,7 +698,7 @@ impl slider::Catalog for Theme { Slider::Standard => //TODO: no way to set rail thickness { - let empty_track: Color = Color::from_rgb8(224, 224, 224); + let empty_track: Color = Color::from_rgb8(240, 240, 240); slider::Style { rail: Rail { backgrounds: ( @@ -715,7 +715,7 @@ impl slider::Catalog for Theme { handle: slider::Handle { shape: slider::HandleShape::Circle { - radius: 8.0, + radius: 6.0, }, border_color: Color::from_rgba8(0, 0, 0, 0.12), border_width: 1.0, @@ -739,7 +739,7 @@ impl slider::Catalog for Theme { slider::Status::Hovered => match class { Slider::Standard => { appearance.handle.shape = slider::HandleShape::Circle { - radius: 10.0, + radius: 7.0, }; appearance.handle.border_width = 1.0; appearance.handle.border_color = Color::from_rgba8(0, 0, 0, 0.12); @@ -755,7 +755,7 @@ impl slider::Catalog for Theme { slider::Status::Dragged => match class { Slider::Standard => { appearance.handle.shape = slider::HandleShape::Circle { - radius: 10.0, + radius: 7.0, }; appearance.handle.border_width = 1.0; appearance.handle.border_color = Color::from_rgba8(0, 0, 0, 0.12); @@ -897,17 +897,22 @@ impl toggler::Catalog for Theme { fn style(&self, class: &Self::Class<'_>, status: toggler::Status) -> toggler::Style { let cosmic = self.cosmic(); const HANDLE_MARGIN: f32 = 2.0; - let neutral_10 = cosmic.palette.neutral_10.with_alpha(0.1); - let mut active = toggler::Style { - background: if matches!(status, toggler::Status::Active { is_toggled: true }) { - cosmic.accent.base.into() - } else if cosmic.is_dark { - cosmic.palette.neutral_6.into() - } else { - cosmic.palette.neutral_5.into() - }, - foreground: cosmic.palette.neutral_2.into(), + let is_toggled = matches!( + status, + toggler::Status::Active { is_toggled: true } + | toggler::Status::Hovered { is_toggled: true } + ); + + let track_color = if is_toggled { + crate::theme::STATE_DEFAULT_COLOR + } else { + Color::from_rgb8(224, 224, 224) + }; + + let mut style = toggler::Style { + background: track_color, + foreground: Color::WHITE, border_radius: cosmic.radius_xl().into(), handle_radius: cosmic .radius_xl() @@ -920,30 +925,10 @@ impl toggler::Catalog for Theme { foreground_border_color: Color::TRANSPARENT, }; match status { - toggler::Status::Active { is_toggled } => active, - toggler::Status::Hovered { is_toggled } => { - let is_active = matches!(status, toggler::Status::Hovered { is_toggled: true }); - toggler::Style { - background: if is_active { - over(neutral_10, cosmic.accent_color()) - } else { - over( - neutral_10, - if cosmic.is_dark { - cosmic.palette.neutral_6 - } else { - cosmic.palette.neutral_5 - }, - ) - } - .into(), - ..active - } - } + toggler::Status::Active { .. } | toggler::Status::Hovered { .. } => style, toggler::Status::Disabled => { - active.background.a /= 2.; - active.foreground.a /= 2.; - active + style.background.a /= 2.; + style } } } @@ -1334,7 +1319,6 @@ impl text_input::Catalog for Theme { fn style(&self, class: &Self::Class<'_>, status: text_input::Status) -> text_input::Style { let palette = self.cosmic(); - let bg = self.current_container().small_widget.with_alpha(0.25); let neutral_9 = palette.palette.neutral_9; let value = neutral_9.into(); @@ -1343,7 +1327,7 @@ impl text_input::Catalog for Theme { let mut appearance = match class { TextInput::Default => text_input::Style { - background: Color::from(bg).into(), + background: Color::WHITE.into(), border: Border { radius: palette.corner_radii.radius_s.into(), width: 1.0, @@ -1355,7 +1339,7 @@ impl text_input::Catalog for Theme { selection, }, TextInput::Search => text_input::Style { - background: Color::from(bg).into(), + background: Color::WHITE.into(), border: Border { radius: palette.corner_radii.radius_m.into(), ..Default::default() @@ -1370,11 +1354,9 @@ impl text_input::Catalog for Theme { match status { text_input::Status::Active => appearance, text_input::Status::Hovered => { - let bg = self.current_container().small_widget.with_alpha(0.25); - match class { TextInput::Default => text_input::Style { - background: Color::from(bg).into(), + background: Color::WHITE.into(), border: Border { radius: palette.corner_radii.radius_s.into(), width: 1.0, @@ -1386,7 +1368,7 @@ impl text_input::Catalog for Theme { selection, }, TextInput::Search => text_input::Style { - background: Color::from(bg).into(), + background: Color::WHITE.into(), border: Border { radius: palette.corner_radii.radius_m.into(), ..Default::default() @@ -1399,11 +1381,9 @@ impl text_input::Catalog for Theme { } } text_input::Status::Focused => { - let bg = self.current_container().small_widget.with_alpha(0.25); - match class { TextInput::Default => text_input::Style { - background: Color::from(bg).into(), + background: Color::WHITE.into(), border: Border { radius: palette.corner_radii.radius_s.into(), width: 1.0, @@ -1415,7 +1395,7 @@ impl text_input::Catalog for Theme { selection, }, TextInput::Search => text_input::Style { - background: Color::from(bg).into(), + background: Color::WHITE.into(), border: Border { radius: palette.corner_radii.radius_m.into(), ..Default::default() diff --git a/src/theme/style/segmented_button.rs b/src/theme/style/segmented_button.rs index 6f62e5d6..6f5b79b7 100644 --- a/src/theme/style/segmented_button.rs +++ b/src/theme/style/segmented_button.rs @@ -289,7 +289,7 @@ mod horizontal { let rad_0 = cosmic.corner_radii.radius_0; ItemStatusAppearance { background: Some(Background::Color( - cosmic.palette.neutral_5.with_alpha(0.2).into(), + cosmic.palette.neutral_5.with_alpha(0.05).into(), )), first: ItemAppearance { border: Border { @@ -381,7 +381,7 @@ mod vertical { pub fn tab_bar_active(cosmic: &cosmic_theme::Theme) -> ItemStatusAppearance { ItemStatusAppearance { background: Some(Background::Color( - cosmic.palette.neutral_5.with_alpha(0.2).into(), + cosmic.palette.neutral_5.with_alpha(0.05).into(), )), first: ItemAppearance { border: Border { diff --git a/src/theme/style/text_input.rs b/src/theme/style/text_input.rs index 8085a48d..9c6242fb 100644 --- a/src/theme/style/text_input.rs +++ b/src/theme/style/text_input.rs @@ -6,7 +6,6 @@ use crate::ext::ColorExt; use crate::widget::text_input::{Appearance, StyleSheet}; use iced_core::Color; -use palette::WithAlpha; #[derive(Default)] pub enum TextInput { @@ -32,13 +31,11 @@ impl StyleSheet for crate::Theme { let palette = self.cosmic(); let container = self.current_container(); - let background: Color = container.small_widget.with_alpha(0.25).into(); - let corner = palette.corner_radii; let label_color = palette.palette.neutral_9; match style { TextInput::Default => Appearance { - background: background.into(), + background: Color::WHITE.into(), border_radius: corner.radius_s.into(), border_width: 2.0, border_offset: None, @@ -47,7 +44,7 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), @@ -63,7 +60,7 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), @@ -79,14 +76,14 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), label_color: label_color.into(), }, TextInput::Search => Appearance { - background: background.into(), + background: Color::WHITE.into(), border_radius: corner.radius_xl.into(), border_width: 2.0, border_offset: None, @@ -95,7 +92,7 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), @@ -111,7 +108,7 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), @@ -125,15 +122,12 @@ impl StyleSheet for crate::Theme { let palette = self.cosmic(); let container = self.current_container(); - let mut background: Color = container.small_widget.into(); - background.a = 0.25; - let corner = palette.corner_radii; let label_color = palette.palette.neutral_9; match style { TextInput::Default => Appearance { - background: background.into(), + background: Color::WHITE.into(), border_radius: corner.radius_s.into(), border_width: 2.0, border_offset: Some(2.0), @@ -142,14 +136,14 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), label_color: label_color.into(), }, TextInput::Search | TextInput::ExpandableSearch => Appearance { - background: background.into(), + background: Color::WHITE.into(), border_radius: corner.radius_xl.into(), border_width: 0.0, border_offset: None, @@ -158,7 +152,7 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), @@ -174,7 +168,7 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), @@ -188,15 +182,12 @@ impl StyleSheet for crate::Theme { let palette = self.cosmic(); let container = self.current_container(); - let mut background: Color = container.small_widget.into(); - background.a = 0.25; - let corner = palette.corner_radii; let label_color = palette.palette.neutral_9; match style { TextInput::Default => Appearance { - background: background.into(), + background: Color::WHITE.into(), border_radius: corner.radius_s.into(), border_width: 2.0, border_offset: None, @@ -205,14 +196,14 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), label_color: label_color.into(), }, TextInput::Search => Appearance { - background: background.into(), + background: Color::WHITE.into(), border_radius: corner.radius_xl.into(), border_offset: None, border_width: 2.0, @@ -221,14 +212,14 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), label_color: label_color.into(), }, TextInput::ExpandableSearch => Appearance { - background: background.into(), + background: Color::WHITE.into(), border_radius: corner.radius_xl.into(), border_offset: None, border_width: 0.0, @@ -237,7 +228,7 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), @@ -253,7 +244,7 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), @@ -269,7 +260,7 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), @@ -283,15 +274,12 @@ impl StyleSheet for crate::Theme { let palette = self.cosmic(); let container = self.current_container(); - let mut background: Color = container.small_widget.into(); - background.a = 0.25; - let corner = palette.corner_radii; let label_color = palette.palette.neutral_9; match style { TextInput::Default => Appearance { - background: background.into(), + background: Color::WHITE.into(), border_radius: corner.radius_s.into(), border_width: 2.0, border_offset: Some(2.0), @@ -300,14 +288,14 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), label_color: label_color.into(), }, TextInput::Search | TextInput::ExpandableSearch => Appearance { - background: background.into(), + background: Color::WHITE.into(), border_radius: corner.radius_xl.into(), border_width: 2.0, border_offset: Some(2.0), @@ -316,7 +304,7 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), @@ -332,7 +320,7 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), @@ -348,7 +336,7 @@ impl StyleSheet for crate::Theme { text_color: None, placeholder_color: { let color: Color = container.on.into(); - color.blend_alpha(background, 0.7) + color.blend_alpha(Color::WHITE, 0.7) }, selected_text_color: palette.on_accent_color().into(), selected_fill: palette.accent_color().into(), diff --git a/src/widget/about.rs b/src/widget/about.rs index 384aee4a..d7282083 100644 --- a/src/widget/about.rs +++ b/src/widget/about.rs @@ -101,7 +101,7 @@ pub fn about<'a, Message: Clone + 'static>( .push(widget::text(name)) .push(horizontal_space()) .push_maybe( - (!url.is_empty()).then_some(crate::widget::icon::from_name("link-symbolic").icon()), + (!url.is_empty()).then_some(crate::widget::icon::from_svg_bytes(icetron_assets::icons::system::EXTERNAL_LINK_LINE).icon()), ) .align_y(Alignment::Center) .apply(widget::button::custom) diff --git a/src/widget/button/link.rs b/src/widget/button/link.rs index 9ce81268..bfb4fcaf 100644 --- a/src/widget/button/link.rs +++ b/src/widget/button/link.rs @@ -61,7 +61,7 @@ impl<'a, Message> Button<'a, Message> { #[inline(never)] pub fn icon() -> Handle { - icon::from_svg_bytes(&include_bytes!("external-link.svg")[..]).symbolic(true) + icon::from_svg_bytes(icetron_assets::icons::system::EXTERNAL_LINK_LINE).symbolic(true) } impl<'a, Message: Clone + 'static> From> for Element<'a, Message> { diff --git a/src/widget/button/mod.rs b/src/widget/button/mod.rs index f5975d39..ebea9f0c 100644 --- a/src/widget/button/mod.rs +++ b/src/widget/button/mod.rs @@ -32,7 +32,7 @@ mod text; #[doc(inline)] pub use text::Button as TextButton; #[doc(inline)] -pub use text::{destructive, standard, suggested, text}; +pub use text::{destructive, secondary, standard, suggested, text}; mod widget; #[doc(inline)] diff --git a/src/widget/button/text.rs b/src/widget/button/text.rs index bcdd02ba..116d4ac0 100644 --- a/src/widget/button/text.rs +++ b/src/widget/button/text.rs @@ -35,6 +35,13 @@ pub fn text<'a, Message>(label: impl Into>) -> Button<'a, Message> .class(ButtonClass::Text) } +/// A text button with the secondary style +pub fn secondary<'a, Message>(label: impl Into>) -> Button<'a, Message> { + Button::new(Text::new()) + .label(label) + .class(ButtonClass::Secondary) +} + /// The text variant of a button. pub struct Text { pub(super) leading_icon: Option, diff --git a/src/widget/button/widget.rs b/src/widget/button/widget.rs index 87233330..8bb83ba7 100644 --- a/src/widget/button/widget.rs +++ b/src/widget/button/widget.rs @@ -105,14 +105,7 @@ impl<'a, Message: Clone + 'a> Button<'a, Message> { style: crate::theme::Button::default(), variant: Variant::Image { on_remove, - close_icon: crate::widget::icon::from_name("window-close-symbolic") - .size(8) - .icon() - .into_svg_handle() - .unwrap_or_else(|| { - let bytes: &'static [u8] = &[]; - iced_core::svg::Handle::from_memory(bytes) - }), + close_icon: iced_core::svg::Handle::from_memory(icetron_assets::icons::system::CLOSE_LINE), }, } } diff --git a/src/widget/calendar.rs b/src/widget/calendar.rs index 7ee06204..c7eb0855 100644 --- a/src/widget/calendar.rs +++ b/src/widget/calendar.rs @@ -161,12 +161,12 @@ where let month_controls = row::with_capacity(2) .push( - icon::from_name("go-previous-symbolic") + icon::from_svg_bytes(icetron_assets::icons::system::ARROW_LEFT_S_LINE) .apply(button::icon) .on_press((this.on_prev)()), ) .push( - icon::from_name("go-next-symbolic") + icon::from_svg_bytes(icetron_assets::icons::system::ARROW_RIGHT_S_LINE) .apply(button::icon) .on_press((this.on_next)()), ); diff --git a/src/widget/color_picker/mod.rs b/src/widget/color_picker/mod.rs index 40a4a940..fed00ad7 100644 --- a/src/widget/color_picker/mod.rs +++ b/src/widget/color_picker/mod.rs @@ -30,7 +30,7 @@ use iced_widget::{Row, canvas, column, horizontal_space, row, scrollable, vertic use palette::{FromColor, RgbHue}; use super::divider::horizontal; -use super::icon::{self, from_name}; +use super::icon; use super::segmented_button::{self, SingleSelect}; use super::{Icon, button, segmented_control, text, text_input, tooltip}; @@ -404,8 +404,8 @@ where // TODO copy paste input contents .trailing_icon({ let button = button::custom(crate::widget::icon( - from_name("edit-copy-symbolic").size(spacing.space_s).into(), - )) + icon::from_svg_bytes(icetron_assets::icons::document::FILE_COPY_LINE), + ).size(spacing.space_s)) .on_press(on_update(ColorPickerUpdate::Copied(Instant::now()))) .class(Button::Text); @@ -819,9 +819,8 @@ pub fn color_button<'a, Message: Clone + 'static>( row![ horizontal_space().width(Length::FillPortion(6)), Icon::from( - icon::from_name("list-add-symbolic") - .prefer_svg(true) - .symbolic(true) + icon::from_svg_bytes(icetron_assets::icons::system::ADD_LINE) + .icon() .size(64) ) .width(icon_portion) diff --git a/src/widget/common.rs b/src/widget/common.rs index fc1363e1..1c86fd09 100644 --- a/src/widget/common.rs +++ b/src/widget/common.rs @@ -1,18 +1,10 @@ use crate::widget::svg; use std::sync::OnceLock; -/// Static `svg::Handle` to the `object-select-symbolic` icon. pub fn object_select() -> &'static svg::Handle { static SELECTION_ICON: OnceLock = OnceLock::new(); SELECTION_ICON.get_or_init(|| { - crate::widget::icon::from_name("object-select-symbolic") - .size(16) - .icon() - .into_svg_handle() - .unwrap_or_else(|| { - let bytes: &'static [u8] = &[]; - iced_core::svg::Handle::from_memory(bytes) - }) + iced_core::svg::Handle::from_memory(icetron_assets::icons::system::CHECK_LINE) }) } diff --git a/src/widget/context_drawer/widget.rs b/src/widget/context_drawer/widget.rs index 5366832f..f4ae2412 100644 --- a/src/widget/context_drawer/widget.rs +++ b/src/widget/context_drawer/widget.rs @@ -71,7 +71,7 @@ impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> { let header_row = row::with_capacity(2).push(actions_slot).push( button::text(fl!("close")) - .trailing_icon(icon::from_name("go-next-symbolic")) + .trailing_icon(icon::from_svg_bytes(icetron_assets::icons::system::ARROW_RIGHT_S_LINE)) .on_press(on_close), ); let header = column::with_capacity(3) diff --git a/src/widget/dropdown/multi/widget.rs b/src/widget/dropdown/multi/widget.rs index 458cf5e6..b1215684 100644 --- a/src/widget/dropdown/multi/widget.rs +++ b/src/widget/dropdown/multi/widget.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: MPL-2.0 AND MIT use super::menu::{self, Menu}; -use crate::widget::icon; use derive_setters::Setters; use iced_core::event::{self, Event}; use iced_core::text::{self, Paragraph, Text}; @@ -229,10 +228,7 @@ impl State { /// Creates a new [`State`] for a [`Dropdown`]. pub fn new() -> Self { Self { - icon: match icon::from_name("pan-down-symbolic").size(16).handle().data { - icon::Data::Svg(handle) => Some(handle), - icon::Data::Image(_) => None, - }, + icon: Some(iced_core::svg::Handle::from_memory(icetron_assets::icons::system::ARROW_DOWN_S_LINE)), menu: menu::State::default(), keyboard_modifiers: keyboard::Modifiers::default(), is_open: false, diff --git a/src/widget/dropdown/widget.rs b/src/widget/dropdown/widget.rs index 03be4eb3..5fbedaff 100644 --- a/src/widget/dropdown/widget.rs +++ b/src/widget/dropdown/widget.rs @@ -413,10 +413,7 @@ impl State { /// Creates a new [`State`] for a [`Dropdown`]. pub fn new() -> Self { Self { - icon: match icon::from_name("pan-down-symbolic").size(16).handle().data { - icon::Data::Svg(handle) => Some(handle), - icon::Data::Image(_) => None, - }, + icon: Some(iced_core::svg::Handle::from_memory(icetron_assets::icons::system::ARROW_DOWN_S_LINE)), menu: menu::State::default(), keyboard_modifiers: keyboard::Modifiers::default(), is_open: Arc::new(AtomicBool::new(false)), diff --git a/src/widget/header_bar.rs b/src/widget/header_bar.rs index f63a207e..e7b9340f 100644 --- a/src/widget/header_bar.rs +++ b/src/widget/header_bar.rs @@ -552,10 +552,10 @@ impl<'a, Message: Clone + 'static> HeaderBar<'a, Message> { /// Creates the widget for window controls. fn window_controls(&mut self) -> Element<'a, Message> { - const ICON_MINIMIZE: &[u8] = include_bytes!("../../res/icons/window-minimize.svg"); - const ICON_MAXIMIZE: &[u8] = include_bytes!("../../res/icons/window-maximize.svg"); - const ICON_RESTORE: &[u8] = include_bytes!("../../res/icons/window-restore.svg"); - const ICON_CLOSE: &[u8] = include_bytes!("../../res/icons/window-close.svg"); + const ICON_MINIMIZE: &[u8] = icetron_assets::icons::system::SUBTRACT_LINE; + const ICON_MAXIMIZE: &[u8] = icetron_assets::icons::system::CHECKBOX_BLANK_LINE; + const ICON_RESTORE: &[u8] = icetron_assets::icons::system::CHECKBOX_MULTIPLE_BLANK_LINE; + const ICON_CLOSE: &[u8] = icetron_assets::icons::system::CLOSE_LINE; macro_rules! wc_icon { ($svg_bytes:expr, $size:expr, $on_press:expr, $is_close:expr) => {{ diff --git a/src/widget/icon/handle.rs b/src/widget/icon/handle.rs index 7e0bab02..a4ddd364 100644 --- a/src/widget/icon/handle.rs +++ b/src/widget/icon/handle.rs @@ -94,7 +94,7 @@ pub fn from_raster_pixels( /// Create a SVG handle from memory. pub fn from_svg_bytes(bytes: impl Into>) -> Handle { Handle { - symbolic: false, + symbolic: true, data: Data::Svg(svg::Handle::from_memory(bytes)), } } diff --git a/src/widget/menu/menu_tree.rs b/src/widget/menu/menu_tree.rs index 15dd5810..466cf381 100644 --- a/src/widget/menu/menu_tree.rs +++ b/src/widget/menu/menu_tree.rs @@ -290,9 +290,9 @@ pub fn menu_items< let key = find_key(&action, key_binds); let mut items = vec![ if value { - widget::icon::from_name("object-select-symbolic") - .size(16) + widget::icon::from_svg_bytes(icetron_assets::icons::system::CHECK_LINE) .icon() + .size(16) .class(theme::Svg::Custom(Rc::new(|theme| { iced_widget::svg::Style { color: Some(theme.cosmic().accent_text_color().into()), @@ -326,9 +326,9 @@ pub fn menu_items< menu_button::<'static, _>(vec![ widget::text(l.clone()).into(), widget::horizontal_space().into(), - widget::icon::from_name("pan-end-symbolic") - .size(16) + widget::icon::from_svg_bytes(icetron_assets::icons::system::ARROW_RIGHT_S_LINE) .icon() + .size(16) .into(), ]) .class( diff --git a/src/widget/popover.rs b/src/widget/popover.rs index ddc31455..e5f866a9 100644 --- a/src/widget/popover.rs +++ b/src/widget/popover.rs @@ -343,7 +343,12 @@ where position.x = position.x.round(); position.y = position.y.round(); - node.move_to(position) + if self.modal { + let child = node.move_to(Point::new(position.x, position.y)); + layout::Node::with_children(bounds, vec![child]) + } else { + node.move_to(position) + } } fn operate( @@ -352,9 +357,14 @@ where renderer: &Renderer, operation: &mut dyn Operation<()>, ) { + let content_layout = if self.modal { + layout.children().next().unwrap() + } else { + layout + }; self.content .as_widget() - .operate(self.tree, layout, renderer, operation); + .operate(self.tree, content_layout, renderer, operation); } fn on_event( @@ -366,9 +376,15 @@ where clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { + let content_layout = if self.modal { + layout.children().next().unwrap() + } else { + layout + }; + if self.modal && matches!(event, Event::Mouse(_) | Event::Touch(_)) - && !cursor_position.is_over(layout.bounds()) + && !cursor_position.is_over(content_layout.bounds()) { return event::Status::Captured; } @@ -376,12 +392,12 @@ where self.content.as_widget_mut().on_event( self.tree, event, - layout, + content_layout, cursor_position, renderer, clipboard, shell, - &layout.bounds(), + &content_layout.bounds(), ) } @@ -392,13 +408,19 @@ where viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { - if self.modal && !cursor_position.is_over(layout.bounds()) { + let content_layout = if self.modal { + layout.children().next().unwrap() + } else { + layout + }; + + if self.modal && !cursor_position.is_over(content_layout.bounds()) { return mouse::Interaction::None; } self.content.as_widget().mouse_interaction( self.tree, - layout, + content_layout, cursor_position, viewport, renderer, @@ -413,16 +435,42 @@ where layout: Layout<'_>, cursor_position: mouse::Cursor, ) { - let bounds = layout.bounds(); - self.content.as_widget().draw( - self.tree, - renderer, - theme, - style, - layout, - cursor_position, - &bounds, - ); + if self.modal { + // Draw over the full window (layout covers entire window) + let full_bounds = layout.bounds(); + renderer.fill_quad( + renderer::Quad { + bounds: full_bounds, + border: iced_core::Border::default(), + shadow: iced_core::Shadow::default(), + }, + iced_core::Background::Color(iced_core::Color::from_rgba(0.0, 0.0, 0.0, 0.4)), + ); + + // Draw dialog content from the child node + let content_layout = layout.children().next().unwrap(); + let content_bounds = content_layout.bounds(); + self.content.as_widget().draw( + self.tree, + renderer, + theme, + style, + content_layout, + cursor_position, + &content_bounds, + ); + } else { + let bounds = layout.bounds(); + self.content.as_widget().draw( + self.tree, + renderer, + theme, + style, + layout, + cursor_position, + &bounds, + ); + } } fn overlay<'c>( @@ -430,9 +478,14 @@ where layout: Layout<'_>, renderer: &Renderer, ) -> Option> { + let content_layout = if self.modal { + layout.children().next().unwrap() + } else { + layout + }; self.content .as_widget_mut() - .overlay(self.tree, layout, renderer, Default::default()) + .overlay(self.tree, content_layout, renderer, Default::default()) } } diff --git a/src/widget/responsive_menu_bar.rs b/src/widget/responsive_menu_bar.rs index 5f855260..1aa0b241 100644 --- a/src/widget/responsive_menu_bar.rs +++ b/src/widget/responsive_menu_bar.rs @@ -124,7 +124,7 @@ impl ResponsiveMenuBar { id_container( menu::bar(vec![menu::Tree::<_>::with_children( Element::from( - button::icon(icon::from_name("open-menu-symbolic")) + button::icon(icon::from_svg_bytes(icetron_assets::icons::system::MENU_LINE)) .padding([4, 12]) .class(crate::theme::Button::MenuRoot), ), diff --git a/src/widget/segmented_button/widget.rs b/src/widget/segmented_button/widget.rs index 622537b0..f8e58ece 100644 --- a/src/widget/segmented_button/widget.rs +++ b/src/widget/segmented_button/widget.rs @@ -204,7 +204,7 @@ where Self { model, id: Id::unique(), - close_icon: icon::from_name("window-close-symbolic").size(16).icon(), + close_icon: icon::from_svg_bytes(icetron_assets::icons::system::CLOSE_LINE).icon().size(16), scrollable_focus: false, show_close_icon_on_hover: false, button_alignment: Alignment::Start, @@ -855,6 +855,7 @@ where tab_drag_candidate: None, dragging_tab: None, drop_hint: None, + close_hovered: None, offer_mimes: Vec::new(), }) } @@ -1009,6 +1010,7 @@ where "offer leave id={my_id:?} entity={entity:?}" ); state.hovered = Item::None; + state.close_hovered = None; for key in self.model.order.iter().copied() { self.update_entity_paragraph(state, key); } @@ -1063,6 +1065,7 @@ where } } else if entity.is_some() { state.hovered = Item::None; + state.close_hovered = None; for key in self.model.order.iter().copied() { self.update_entity_paragraph(state, key); } @@ -1153,6 +1156,7 @@ where if let Some(event) = pending_reorder { state.focused_item = Item::Tab(event.dragged); state.hovered = Item::None; + state.close_hovered = None; for key in self.model.order.iter().copied() { self.update_entity_paragraph(state, key); } @@ -1241,6 +1245,13 @@ where let over_close_button = self.model.items[key].closable && cursor_position.is_over(close_button_bounds); + // Track close button hover state + state.close_hovered = if over_close_button { + Some(key) + } else { + None + }; + // If marked as closable, show a close icon. if self.model.items[key].closable { // Emit close message if the close button is pressed. @@ -1353,6 +1364,7 @@ where break; } else if state.hovered == Item::Tab(key) { state.hovered = Item::None; + state.close_hovered = None; self.update_entity_paragraph(state, key); } } @@ -2030,6 +2042,28 @@ where if show_close_button { let close_button_bounds = close_bounds(original_bounds, close_icon_width); + // Draw a red background on the close button when hovered + if state.close_hovered == Some(key) { + let padding = 4.0; + let bg_bounds = Rectangle { + x: close_button_bounds.x - padding, + y: close_button_bounds.y - padding, + width: close_button_bounds.width + padding * 2.0, + height: close_button_bounds.height + padding * 2.0, + }; + renderer.fill_quad( + renderer::Quad { + bounds: bg_bounds, + border: Border { + radius: (bg_bounds.width / 2.0).into(), + ..Default::default() + }, + shadow: Shadow::default(), + }, + Background::Color(iced_core::Color::from_rgba(1.0, 0.0, 0.0, 0.1)), + ); + } + draw_icon::( renderer, theme, @@ -2338,6 +2372,8 @@ pub struct LocalState { dragging_tab: Option, /// Current drop hint for drag-and-drop indicator drop_hint: Option, + /// Track whether the close button is hovered on a specific entity + close_hovered: Option, } #[derive(Debug, Default, PartialEq)] @@ -2465,6 +2501,7 @@ mod tests { tab_drag_candidate: None, dragging_tab: Some(dragging), drop_hint: None, + close_hovered: None, offer_mimes: Vec::new(), }; state.buttons_visible = len; diff --git a/src/widget/table/widget/standard.rs b/src/widget/table/widget/standard.rs index c0207f06..3003fc6d 100644 --- a/src/widget/table/widget/standard.rs +++ b/src/widget/table/widget/standard.rs @@ -103,8 +103,8 @@ where .spacing(val.icon_spacing) .push(widget::text::heading(category.to_string())) .push_maybe(match sort_state { - 1 => Some(widget::icon::from_name("pan-up-symbolic").icon()), - 2 => Some(widget::icon::from_name("pan-down-symbolic").icon()), + 1 => Some(widget::icon::from_svg_bytes(icetron_assets::icons::system::ARROW_UP_S_LINE).icon()), + 2 => Some(widget::icon::from_svg_bytes(icetron_assets::icons::system::ARROW_DOWN_S_LINE).icon()), _ => None, }) .apply(container) diff --git a/src/widget/text_input/input.rs b/src/widget/text_input/input.rs index 7dd92e12..15e57b45 100644 --- a/src/widget/text_input/input.rs +++ b/src/widget/text_input/input.rs @@ -96,7 +96,8 @@ where .padding([0, spacing]) .style(crate::theme::TextInput::Search) .leading_icon( - crate::widget::icon::from_name("system-search-symbolic") + crate::widget::icon::from_svg_bytes(icetron_assets::icons::system::SEARCH_LINE) + .icon() .size(16) .apply(crate::widget::container) .padding(8) @@ -120,7 +121,8 @@ where .padding([0, spacing]) .style(crate::theme::TextInput::Default) .leading_icon( - crate::widget::icon::from_name("system-lock-screen-symbolic") + crate::widget::icon::from_svg_bytes(icetron_assets::icons::system::LOCK_LINE) + .icon() .size(16) .apply(crate::widget::container) .padding(8) @@ -131,11 +133,12 @@ where } if let Some(msg) = on_visible_toggle { input.trailing_icon( - crate::widget::icon::from_name(if hidden { - "document-properties-symbolic" + crate::widget::icon::from_svg_bytes(if hidden { + icetron_assets::icons::system::EYE_LINE } else { - "image-red-eye-symbolic" + icetron_assets::icons::system::EYE_OFF_LINE }) + .icon() .size(16) .apply(crate::widget::button::custom) .class(crate::theme::Button::Icon) @@ -537,7 +540,8 @@ where pub fn on_clear(self, on_clear: Message) -> Self { self.trailing_icon( - crate::widget::icon::from_name("edit-clear-symbolic") + crate::widget::icon::from_svg_bytes(icetron_assets::icons::system::CLOSE_CIRCLE_LINE) + .icon() .size(16) .apply(crate::widget::button::custom) .class(crate::theme::Button::Icon) diff --git a/src/widget/toaster/mod.rs b/src/widget/toaster/mod.rs index efd93a9d..104d672f 100644 --- a/src/widget/toaster/mod.rs +++ b/src/widget/toaster/mod.rs @@ -42,7 +42,7 @@ pub fn toaster<'a, Message: Clone + 'static>( button::text(&action.description).on_press((action.message)(id)) })) .push( - button::icon(icon::from_name("window-close-symbolic")) + button::icon(icon::from_svg_bytes(icetron_assets::icons::system::CLOSE_LINE)) .on_press((toasts.on_close)(id)), ) .align_y(iced::Alignment::Center) diff --git a/src/widget/warning.rs b/src/widget/warning.rs index 942ffb8b..37976bb9 100644 --- a/src/widget/warning.rs +++ b/src/widget/warning.rs @@ -33,8 +33,7 @@ impl<'a, Message: 'static + Clone> Warning<'a, Message> { pub fn into_widget(self) -> widget::Container<'a, Message, crate::Theme, Renderer> { let label = widget::container(crate::widget::text(self.message)).width(Length::Fill); - let close_button = icon::from_name("window-close-symbolic") - .size(16) + let close_button = icon::from_svg_bytes(icetron_assets::icons::system::CLOSE_LINE) .apply(widget::button::icon) .on_press_maybe(self.on_close);