feat(applets): configurable applet overlap and padding increases

This commit is contained in:
Ashley Wulber 2025-11-11 15:04:09 -05:00 committed by GitHub
parent 2c93a4094f
commit 2296e8e94d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 1135 additions and 61 deletions

View file

@ -1,13 +1,14 @@
#[cfg(feature = "applet-token")]
pub mod token;
use crate::app::cosmic;
use crate::{
Application, Element, Renderer,
app::iced_settings,
cctk::sctk,
iced::{
self, Color, Length, Limits, Rectangle,
alignment::{Horizontal, Vertical},
alignment::{Alignment, Horizontal, Vertical},
widget::Container,
window,
},
@ -16,18 +17,24 @@ 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};
use tracing::info;
use crate::app::cosmic;
pub mod column;
pub mod row;
static AUTOSIZE_ID: LazyLock<iced::id::Id> =
LazyLock::new(|| iced::id::Id::new("cosmic-applet-autosize"));
static AUTOSIZE_MAIN_ID: LazyLock<iced::id::Id> =
@ -46,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<iced::Size>,
/// Ratio of overlap for applet padding.
pub padding_overlap: f32,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@ -104,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,
}
}
@ -124,13 +137,19 @@ impl Context {
#[must_use]
pub fn suggested_window_size(&self) -> (NonZeroU32, NonZeroU32) {
let suggested = self.suggested_size(true);
let applet_padding = self.suggested_padding(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)
};
let configured_width = self
.suggested_bounds
.as_ref()
.and_then(|c| NonZeroU32::new(c.width as u32)) // TODO: should this be physical size instead of logical?
.unwrap_or_else(|| {
NonZeroU32::new(suggested.0 as u32 + applet_padding as u32 * 2).unwrap()
NonZeroU32::new(suggested.0 as u32 + horizontal_padding as u32 * 2).unwrap()
});
let configured_height = self
@ -138,17 +157,20 @@ impl Context {
.as_ref()
.and_then(|c| NonZeroU32::new(c.height as u32))
.unwrap_or_else(|| {
NonZeroU32::new(suggested.1 as u32 + applet_padding as u32 * 2).unwrap()
NonZeroU32::new(suggested.1 as u32 + vertical_padding as u32 * 2).unwrap()
});
info!("{configured_height:?}");
(configured_width, configured_height)
}
#[must_use]
pub fn suggested_padding(&self, is_symbolic: bool) -> u16 {
pub fn suggested_padding(&self, is_symbolic: bool) -> (u16, u16) {
match &self.size {
Size::PanelSize(size) => size.get_applet_padding(is_symbolic),
Size::Hardcoded(_) => 8,
Size::PanelSize(size) => (
size.get_applet_shrinkable_padding(is_symbolic),
size.get_applet_padding(is_symbolic),
),
Size::Hardcoded(_) => (12, 8),
}
}
@ -160,9 +182,15 @@ impl Context {
#[allow(clippy::cast_precision_loss)]
pub fn window_settings(&self) -> crate::app::Settings {
let (width, height) = self.suggested_size(true);
let applet_padding = self.suggested_padding(true);
let width = f32::from(width) + applet_padding as f32 * 2.;
let height = f32::from(height) + applet_padding as f32 * 2.;
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)
};
let width = f32::from(width) + horizontal_padding as f32 * 2.;
let height = f32::from(height) + vertical_padding as f32 * 2.;
let mut settings = crate::app::Settings::default()
.size(iced_core::Size::new(width, height))
.size_limits(Limits::NONE.min_height(height).min_width(width))
@ -187,28 +215,70 @@ impl Context {
icon: widget::icon::Handle,
) -> crate::widget::Button<'a, Message> {
let suggested = self.suggested_size(icon.symbolic);
let applet_padding = self.suggested_padding(icon.symbolic);
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)
};
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<Element<'a, Message>>,
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<Text<'a, crate::Theme, crate::Renderer>>,
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)),
Text::from(text)
.height(Length::Fill)
.align_y(Alignment::Center),
)
.center(Length::Fill),
.center_y(Length::Fixed(f32::from(suggested.1 + 2 * vertical_padding))),
)
.width(Length::Fixed((suggested.0 + 2 * applet_padding) as f32))
.height(Length::Fixed((suggested.1 + 2 * applet_padding) as f32))
.class(Button::AppletIcon)
.on_press_down(message)
.padding([0, horizontal_padding])
.class(crate::theme::Button::AppletIcon)
}
pub fn icon_button<'a, Message: Clone + 'static>(
@ -345,7 +415,12 @@ impl Context {
height_padding: Option<i32>,
) -> SctkPopupSettings {
let (width, height) = self.suggested_size(true);
let applet_padding = self.suggested_padding(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)
};
let pixel_offset = 4;
let (offset, anchor, gravity) = match self.anchor {
PanelAnchor::Left => ((pixel_offset, 0), Anchor::Right, Gravity::Right),
@ -364,8 +439,10 @@ impl Context {
anchor_rect: Rectangle {
x: 0,
y: 0,
width: width_padding.unwrap_or(applet_padding as i32) * 2 + i32::from(width),
height: height_padding.unwrap_or(applet_padding as i32) * 2 + i32::from(height),
width: width_padding.unwrap_or(horizontal_padding as i32) * 2
+ i32::from(width),
height: height_padding.unwrap_or(vertical_padding as i32) * 2
+ i32::from(height),
},
reactive: true,
constraint_adjustment: 15, // slide_y, slide_x, flip_x, flip_y