Clarify constructor failures and borrowed API contracts

This commit is contained in:
Samuel Onoja committed 2026-10-22 01:28:03 +01:00
1 parent 23e280a6b4
commit a7fcd69897
55 files changed
+332 -102

No files matched your search

+1
View File
@@ -45,6 +45,7 @@ pub(crate) fn accent_button_style(
}
/// Filled accent buttons and selected rows share this per-state contrast policy.
#[must_use]
pub fn accent_button() -> theme::Button {
theme::Button::Custom {
active: Box::new(|focused, theme| accent_button_style(theme, State::Active, focused)),
+4
View File
@@ -5,6 +5,7 @@ use cosmic::widget::{column, container, row};
use cosmic::{Element, theme};
use jiff::Zoned;
#[must_use]
pub fn format_bar_time(now: &Zoned) -> String {
let months = [
"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sept", "oct", "nov", "dec",
@@ -20,6 +21,7 @@ pub fn format_bar_time(now: &Zoned) -> String {
if hour < 12 { "am" } else { "pm" }
)
}
#[must_use]
pub fn month(today: jiff::civil::Date, offset: i32) -> (jiff::civil::Date, usize, usize) {
let month_index = i32::from(today.year()) * 12 + i32::from(today.month()) - 1 + offset;
let year = month_index.div_euclid(12) as i16;
@@ -30,6 +32,7 @@ pub fn month(today: jiff::civil::Date, offset: i32) -> (jiff::civil::Date, usize
(first, start, days)
}
#[must_use]
pub fn grid<'a, M: Clone + 'a>(
today: jiff::civil::Date,
offset: i32,
@@ -41,6 +44,7 @@ pub fn grid<'a, M: Clone + 'a>(
grid_with_density(today, offset, palette, font, opacity, navigation, false)
}
#[must_use]
pub fn compact_grid<'a, M: Clone + 'a>(
today: jiff::civil::Date,
offset: i32,
+6
View File
@@ -1,5 +1,6 @@
use cosmic::iced::Color;
#[must_use]
pub fn luminance(color: Color) -> f32 {
let linear = |v: f32| {
if v <= 0.04045 {
@@ -11,6 +12,7 @@ pub fn luminance(color: Color) -> f32 {
0.2126 * linear(color.r) + 0.7152 * linear(color.g) + 0.0722 * linear(color.b)
}
#[must_use]
pub fn contrast(a: Color, b: Color) -> f32 {
let (a, b) = (luminance(a), luminance(b));
(a.max(b) + 0.05) / (a.min(b) + 0.05)
@@ -19,6 +21,7 @@ pub fn contrast(a: Color, b: Color) -> f32 {
/// Resolve a control fill against its surface before selecting its foreground.
/// The returned fill is opaque, so wallpaper and translucent panels cannot change
/// the contrast of a small, filled control.
#[must_use]
pub fn composite(fill: Color, surface: Color) -> Color {
let a = fill.a.clamp(0., 1.);
Color::from_rgb(
@@ -30,6 +33,7 @@ pub fn composite(fill: Color, surface: Color) -> Color {
/// Preserve the user's preferred color when it passes normal-text contrast.
/// Otherwise black or white always provides at least 4.5:1 on an opaque sRGB fill.
#[must_use]
pub fn foreground(background: Color, preferred: Color) -> Color {
let preferred = composite(preferred, background);
if contrast(background, preferred) >= 4.5 {
@@ -44,6 +48,7 @@ pub fn foreground(background: Color, preferred: Color) -> Color {
/// Prefer the theme's foreground for filled controls by adjusting the accent's
/// brightness until the text has enough contrast.
#[must_use]
pub fn accent_pair(background: Color, preferred: Color) -> (Color, Color) {
let on = composite(preferred, background);
let shade = if luminance(on) > luminance(background) {
@@ -67,6 +72,7 @@ pub fn accent_pair(background: Color, preferred: Color) -> (Color, Color) {
}
/// Resolve configured accent transparency before passing an opaque color to a toolkit.
#[must_use]
pub fn accent_color(accent: Color, surface: Color) -> cosmic::cosmic_theme::palette::Srgb {
rgba(composite(accent, surface)).color
}
+13
View File
@@ -5,23 +5,28 @@ use cosmic::{theme, widget};
use crate::{Palette, mix};
#[must_use]
pub fn surface(background: Color, radius: f32) -> theme::Container<'static> {
theme::Container::custom(move |_| surface_appearance(background, radius))
}
#[must_use]
pub fn button_style(p: Palette, selected: bool) -> theme::Button {
button_style_with_focus(p, selected, true)
}
#[must_use]
pub fn button_style_with_focus(p: Palette, selected: bool, focus_visible: bool) -> theme::Button {
styled_button(p, selected, false, 1.0, focus_visible)
}
/// Selection cards share native button states while revealing the modal material.
#[must_use]
pub fn material_button_style(p: Palette, selected: bool, material_opacity: f32) -> theme::Button {
material_button_style_with_focus(p, selected, material_opacity, true)
}
#[must_use]
pub fn material_button_style_with_focus(
p: Palette,
selected: bool,
@@ -101,10 +106,12 @@ pub(crate) fn switch_colors(palette: Palette, enabled: bool, hovered: bool) -> (
(track, thumb)
}
#[must_use]
pub fn navigation_style(p: Palette, selected: bool) -> theme::Button {
styled_button(p, selected, true, 1.0, true)
}
#[must_use]
pub fn settings_input(p: Palette) -> theme::TextInput {
let appearance = move |focused: bool, hovered: bool| cosmic::widget::text_input::Appearance {
shape: Some(BorderShape::Continuous),
@@ -229,6 +236,7 @@ fn styled_button(
/// Authentication actions use normal text while enabled and muted text while disabled.
/// Keep the disabled fill neutral so contrast correction cannot make it look active.
#[must_use]
pub fn authentication_button(palette: Palette) -> theme::Button {
let paint = move |enabled: bool, interaction: f32, focused: bool| {
let (fill, foreground) = if enabled {
@@ -260,6 +268,7 @@ pub fn authentication_button(palette: Palette) -> theme::Button {
}
}
#[must_use]
pub fn authentication_input(palette: Palette) -> theme::TextInput {
let text = palette.text;
let muted = palette.muted;
@@ -293,6 +302,7 @@ pub fn authentication_input(palette: Palette) -> theme::TextInput {
}
}
#[must_use]
pub fn lock_input(
radius: f32,
accent: cosmic::iced::Color,
@@ -361,6 +371,7 @@ pub fn shell_button(
}
}
#[must_use]
pub fn surface_appearance(background: Color, radius: f32) -> container::Style {
container::Style {
background: Some(Background::Color(background)),
@@ -373,6 +384,7 @@ pub fn surface_appearance(background: Color, radius: f32) -> container::Style {
}
}
#[must_use]
pub fn notification_button(
foreground: Color,
hover: Color,
@@ -396,6 +408,7 @@ pub fn notification_button(
}
}
#[must_use]
pub fn filled_button(fill: Color, foreground: Color, radius: f32, opacity: f32) -> theme::Button {
let paint = move |outline| button::Style {
shape: Some(BorderShape::Continuous),
+2
View File
@@ -174,6 +174,7 @@ fn desktop(p: &Palette) -> String {
)
}
#[must_use]
pub fn svg(
family: &Family,
variant: Option<Appearance>,
@@ -274,6 +275,7 @@ fn svg_with_radius(
result
}
#[must_use]
pub fn neighbor(index: usize, count: usize, key: &keyboard::key::Named) -> Option<usize> {
use keyboard::key::Named::*;
if count == 0 {
+1
View File
@@ -1,4 +1,5 @@
/// Radius of a circular inset. Squircle insets retain an `Outline` and its distance offset.
#[must_use]
pub fn inner_radius(outer: f32, padding: f32) -> f32 {
(outer - padding).max(0.0)
}
+1
View File
@@ -186,6 +186,7 @@ pub fn accented(source: &'static [u8], size: u16, foreground: Color, accent: Col
}))
}
#[must_use]
pub fn tint_svg(source: &[u8], foreground: Color, accent: Color) -> Vec<u8> {
let rgba = |foreground: Color| {
format!(
+5
View File
@@ -37,6 +37,7 @@ pub fn heading<'a, M: 'a>(
.align_y(Alignment::Center)
}
#[must_use]
pub fn badge<'a, M: 'a>(
content: Element<'a, M>,
accent: Color,
@@ -55,6 +56,7 @@ pub fn badge<'a, M: 'a>(
.into()
}
#[must_use]
pub fn section<'a, M: 'a>(
content: Element<'a, M>,
foreground: Color,
@@ -84,6 +86,7 @@ pub fn section_label<'a>(
.class(cosmic::theme::Text::Color(muted))
}
#[must_use]
pub fn separator<'a, M: 'a>(foreground: Color, opacity: f32) -> Element<'a, M> {
container(cosmic::widget::Space::new().width(Length::Fill).height(1))
.class(crate::controls::surface(
@@ -93,6 +96,7 @@ pub fn separator<'a, M: 'a>(foreground: Color, opacity: f32) -> Element<'a, M> {
.into()
}
#[must_use]
pub fn switch<'a, M: 'a>(enabled: bool, palette: crate::Palette, opacity: f32) -> Element<'a, M> {
let (track, thumb) = crate::controls::switch_colors(palette, enabled, false);
let track_radius = 10.;
@@ -123,6 +127,7 @@ pub fn switch<'a, M: 'a>(enabled: bool, palette: crate::Palette, opacity: f32) -
.into()
}
#[must_use]
pub fn slider(foreground: Color, opacity: f32) -> cosmic::theme::iced::Slider {
let style = std::rc::Rc::new(move |_: &cosmic::Theme| {
use cosmic::iced::widget::slider::{Breakpoint, Handle, HandleShape, Rail, Style};
+7
View File
@@ -19,6 +19,7 @@ pub struct Palette {
}
impl Palette {
#[must_use]
pub fn from_resolved(theme: &ferese_theme_model::ResolvedTheme) -> Self {
let color = |value: &str| parse_color(value).expect("validated resolved theme color");
Self {
@@ -50,6 +51,7 @@ impl Palette {
}
}
#[must_use]
pub fn flat(mut self) -> Self {
self.background = self.sidebar;
self.card = self.sidebar;
@@ -138,6 +140,7 @@ impl Palette {
cosmic::Theme::custom(std::sync::Arc::new(native)).corner_shape(BorderShape::Continuous)
}
#[must_use]
pub fn application_style(self, background: Color) -> cosmic::iced::theme::Style {
cosmic::iced::theme::Style {
background_color: background,
@@ -147,6 +150,7 @@ impl Palette {
}
}
#[must_use]
pub fn parse_color(value: &str) -> Option<Color> {
let hex = value.strip_prefix('#')?;
let packed = u32::from_str_radix(hex, 16).ok()?;
@@ -163,6 +167,7 @@ pub fn parse_color(value: &str) -> Option<Color> {
))
}
#[must_use]
pub fn mix(a: Color, b: Color, t: f32) -> Color {
Color::from_rgb(
a.r + (b.r - a.r) * t,
@@ -171,6 +176,7 @@ pub fn mix(a: Color, b: Color, t: f32) -> Color {
)
}
#[must_use]
pub fn surface_shade(base: Color) -> Color {
if crate::luminance(base) > 0.5 {
Color::BLACK
@@ -179,6 +185,7 @@ pub fn surface_shade(base: Color) -> Color {
}
}
#[must_use]
pub fn material_opacity(theme: &ferese_theme_model::ResolvedTheme) -> f32 {
if theme.tokens.material.style == "translucent" {
theme.tokens.material.opacity as f32
+1 -1
View File
@@ -107,7 +107,7 @@ impl<M> Surface<'_, M> {
&mut collector,
);
let size = layout.bounds().size();
let scene = PixelGrid::new(self.options.scale)
let scene = PixelGrid::try_new(self.options.scale)
.and_then(|grid| {
ferese_panel::scene::derive(
self.options.parameters,
+4
View File
@@ -16,6 +16,7 @@ pub struct Workspace<'a> {
pub visible_elsewhere: bool,
}
#[must_use]
pub fn width(style: WorkspaceStyle) -> f32 {
match style {
WorkspaceStyle::Numbers | WorkspaceStyle::Dots => 24.,
@@ -25,6 +26,7 @@ pub fn width(style: WorkspaceStyle) -> f32 {
}
}
#[must_use]
pub fn tooltip_label(workspace: &Workspace<'_>) -> String {
let name = if workspace.name.is_empty() || workspace.name == workspace.index.to_string() {
String::new()
@@ -44,6 +46,7 @@ pub fn tooltip_label(workspace: &Workspace<'_>) -> String {
)
}
#[must_use]
pub fn indicator<'a, M: 'a>(
style: WorkspaceStyle,
workspace: Workspace<'a>,
@@ -193,6 +196,7 @@ pub fn indicator<'a, M: 'a>(
.into()
}
#[must_use]
pub fn sample<'a, M: 'a>(
style: WorkspaceStyle,
palette: Palette,