diff --git a/src/applet/column.rs b/src/applet/column.rs index 992b9421..16362d39 100644 --- a/src/applet/column.rs +++ b/src/applet/column.rs @@ -325,6 +325,7 @@ where .zip(&mut tree.children) .zip(layout.children()) .map(|(((i, child), state), c_layout)| { + let mut cursor_virtual = cursor; if matches!( event, Event::Mouse(mouse::Event::CursorMoved { .. } | mouse::Event::CursorEntered) @@ -335,6 +336,8 @@ where ) && cursor.is_over(c_layout.bounds()) { my_state.hovered = Some(i); + } else if my_state.hovered.is_some_and(|h| i == h) { + cursor_virtual = mouse::Cursor::Unavailable; } child.as_widget_mut().on_event( diff --git a/src/applet/mod.rs b/src/applet/mod.rs index d7f0d721..1b5cfc85 100644 --- a/src/applet/mod.rs +++ b/src/applet/mod.rs @@ -8,7 +8,7 @@ use crate::{ cctk::sctk, iced::{ self, Color, Length, Limits, Rectangle, - alignment::{Horizontal, Vertical}, + alignment::{Alignment, Horizontal, Vertical}, widget::Container, window, }, @@ -17,12 +17,16 @@ use crate::{ widget::{ self, autosize::{self, Autosize, autosize}, - layer_container, + column::Column, + horizontal_space, layer_container, + row::Row, + vertical_space, }, }; pub use cosmic_panel_config; use cosmic_panel_config::{CosmicPanelBackground, PanelAnchor, PanelSize}; use iced_core::{Padding, Shadow}; +use iced_widget::Text; use iced_widget::runtime::platform_specific::wayland::popup::{SctkPopupSettings, SctkPositioner}; use sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{Anchor, Gravity}; use std::{borrow::Cow, num::NonZeroU32, rc::Rc, sync::LazyLock, time::Duration}; @@ -49,6 +53,8 @@ pub struct Context { /// Includes the configured size of the window. /// This can be used by apples to handle overflow themselves. pub suggested_bounds: Option, + /// Ratio of overlap for applet padding. + pub padding_overlap: f32, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -107,6 +113,10 @@ impl Default for Context { .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()), + padding_overlap: str::parse( + &std::env::var("COSMIC_PANEL_PADDING_OVERLAP").unwrap_or_default(), + ) + .unwrap_or(0.0), suggested_bounds: None, } } @@ -212,25 +222,76 @@ impl Context { (applet_padding_minor_axis, applet_padding_major_axis) }; let symbolic = icon.symbolic; + let icon = widget::icon(icon) + .class(if symbolic { + theme::Svg::Custom(Rc::new(|theme| crate::iced_widget::svg::Style { + 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)); + self.button_from_element(icon, symbolic) + } + pub fn button_from_element<'a, Message: Clone + 'static>( + &self, + content: impl Into>, + use_symbolic_size: bool, + ) -> crate::widget::Button<'a, Message> { + let suggested = self.suggested_size(use_symbolic_size); + let (applet_padding_major_axis, applet_padding_minor_axis) = self.suggested_padding(true); + let (horizontal_padding, vertical_padding) = if self.is_horizontal() { + (applet_padding_major_axis, applet_padding_minor_axis) + } else { + (applet_padding_minor_axis, applet_padding_major_axis) + }; + + crate::widget::button::custom(layer_container(content).center(Length::Fill)) + .width(Length::Fixed((suggested.0 + 2 * horizontal_padding) as f32)) + .height(Length::Fixed((suggested.1 + 2 * vertical_padding) as f32)) + .class(Button::AppletIcon) + } + + pub fn text_button<'a, Message: Clone + 'static>( + &self, + text: impl Into>, + message: Message, + ) -> crate::widget::Button<'a, Message> { + let text = text.into(); + let suggested = self.suggested_size(true); + + let (applet_padding_major_axis, applet_padding_minor_axis) = self.suggested_padding(true); + let (horizontal_padding, vertical_padding) = if self.is_horizontal() { + (applet_padding_major_axis, applet_padding_minor_axis) + } else { + (applet_padding_minor_axis, applet_padding_major_axis) + }; crate::widget::button::custom( - layer_container( - widget::icon(icon) - .class(if symbolic { - theme::Svg::Custom(Rc::new(|theme| crate::iced_widget::svg::Style { - 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)), - ) - .center(Length::Fill), + Row::with_children(vec![ + Column::with_children(vec![ + Text::from(text).into(), + horizontal_space() + .width(Length::Fixed( + (suggested.0 as f32 + 2. * horizontal_padding as f32), + )) + .width(Length::Shrink) + .height(Length::Shrink) + .into(), + ]) + .align_x(Alignment::Center) + .into(), + vertical_space() + .height(Length::Fixed( + (suggested.1 as f32 + 2. * vertical_padding as f32), + )) + .into(), + ]) + .align_y(Alignment::Center), ) - .width(Length::Fixed((suggested.0 + 2 * horizontal_padding) as f32)) - .height(Length::Fixed((suggested.1 + 2 * vertical_padding) as f32)) - .class(Button::AppletIcon) + .on_press_down(message) + .class(crate::theme::Button::AppletIcon) } pub fn icon_button<'a, Message: Clone + 'static>( diff --git a/src/widget/color_picker/mod.rs b/src/widget/color_picker/mod.rs index 8dba2e1a..40a4a940 100644 --- a/src/widget/color_picker/mod.rs +++ b/src/widget/color_picker/mod.rs @@ -50,6 +50,24 @@ pub static HSV_RAINBOW: LazyLock> = LazyLock::new(|| { .collect() }); +fn hsv_rainbow(low_hue: f32, high_hue: f32) -> Vec { + let mut colors = Vec::new(); + let steps: u8 = 7; + let step_size = (high_hue - low_hue) / f32::from(steps); + for i in 0..=steps { + let hue = low_hue + step_size * f32::from(i); + colors.push(ColorStop { + color: Color::from(palette::Srgba::from_color(palette::Hsv::new_srgb_const( + RgbHue::new(hue), + 1.0, + 1.0, + ))), + offset: f32::from(i) / f32::from(steps), + }); + } + colors +} + const MAX_RECENT: usize = 20; #[derive(Debug, Clone)] @@ -290,37 +308,9 @@ where copied_to_clipboard_label: T, ) -> ColorPicker<'a, Message> { fn rail_backgrounds(hue: f32) -> (Background, Background) { - let pivot = hue * 7.0 / 360.; + let low_range = hsv_rainbow(0., hue); + let high_range = hsv_rainbow(hue, 360.); - let low_end = pivot.floor() as usize; - let high_start = pivot.ceil() as usize; - let pivot_color = palette::Hsv::new_srgb(RgbHue::new(hue), 1.0, 1.0); - let low_range = HSV_RAINBOW[0..=low_end] - .iter() - .enumerate() - .map(|(i, color)| ColorStop { - color: *color, - offset: i as f32 / pivot.max(0.0001), - }) - .chain(iter::once(ColorStop { - color: iced::Color::from(palette::Srgba::from_color(pivot_color)), - offset: 1., - })) - .collect::>(); - let high_range = iter::once(ColorStop { - color: iced::Color::from(palette::Srgba::from_color(pivot_color)), - offset: 0., - }) - .chain( - HSV_RAINBOW[high_start..] - .iter() - .enumerate() - .map(|(i, color)| ColorStop { - color: *color, - offset: (i as f32 + (1. - pivot.fract())) / (7. - pivot).max(0.0001), - }), - ) - .collect::>(); ( Background::Gradient(iced::Gradient::Linear( Linear::new(Radians(90.0)).add_stops(low_range),