Fix compositor output routing and pointer hints; format imports
This commit is contained in:
1 parent
88e4f3c135
commit
46509f880a
108 files changed
+3682
-8371
No files matched your search
@@ -1,9 +1,8 @@
|
||||
use cosmic::iced::{Background, Color};
|
||||
use cosmic::theme;
|
||||
use cosmic::widget::button;
|
||||
|
||||
use crate::{composite, foreground};
|
||||
use cosmic::{
|
||||
iced::{Background, Color},
|
||||
theme,
|
||||
widget::button,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum State {
|
||||
@@ -13,11 +12,7 @@ pub(crate) enum State {
|
||||
Disabled,
|
||||
}
|
||||
|
||||
pub(crate) fn accent_button_style(
|
||||
theme: &cosmic::Theme,
|
||||
state: State,
|
||||
focused: bool,
|
||||
) -> button::Style {
|
||||
pub(crate) fn accent_button_style(theme: &cosmic::Theme, state: State, focused: bool) -> button::Style {
|
||||
let native = theme.cosmic();
|
||||
let component = &native.accent_button;
|
||||
let fill: Color = match state {
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
//! Date/time labels and a calendar grid; callers own navigation and popup behavior.
|
||||
use cosmic::{
|
||||
Element,
|
||||
iced::{Alignment, Background, Border, Length},
|
||||
theme,
|
||||
widget::{column, container, row},
|
||||
};
|
||||
use cosmic::iced::{Alignment, Background, Border, Length};
|
||||
use cosmic::widget::{column, container, row};
|
||||
use cosmic::{Element, theme};
|
||||
use jiff::Zoned;
|
||||
|
||||
pub fn format_bar_time(now: &Zoned) -> String {
|
||||
@@ -41,10 +38,7 @@ pub fn stacked_digits(label: &str) -> Option<[String; 2]> {
|
||||
let (hour, minute) = time.split_once(':')?;
|
||||
if !(1..=2).contains(&hour.len())
|
||||
|| minute.len() != 2
|
||||
|| !hour
|
||||
.bytes()
|
||||
.chain(minute.bytes())
|
||||
.all(|digit| digit.is_ascii_digit())
|
||||
|| !hour.bytes().chain(minute.bytes()).all(|digit| digit.is_ascii_digit())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
@@ -104,13 +98,9 @@ pub fn grid<'a, M: Clone + 'a>(
|
||||
let mut weekdays = row::with_capacity(7).spacing(3);
|
||||
for label in ["M", "T", "W", "T", "F", "S", "S"] {
|
||||
weekdays = weekdays.push(
|
||||
container(
|
||||
crate::text(label, font)
|
||||
.size(11)
|
||||
.class(theme::Text::Color(muted)),
|
||||
)
|
||||
.width(30)
|
||||
.center_x(30),
|
||||
container(crate::text(label, font).size(11).class(theme::Text::Color(muted)))
|
||||
.width(30)
|
||||
.center_x(30),
|
||||
);
|
||||
}
|
||||
grid = grid.push(weekdays);
|
||||
@@ -118,26 +108,21 @@ pub fn grid<'a, M: Clone + 'a>(
|
||||
let mut dates = row::with_capacity(7).spacing(3);
|
||||
for weekday in 0..7 {
|
||||
let cell = week * 7 + weekday;
|
||||
let day = cell
|
||||
.checked_sub(start)
|
||||
.map(|day| day + 1)
|
||||
.filter(|day| *day <= days);
|
||||
let is_today =
|
||||
year == today.year() && month == today.month() && day == Some(today.day() as usize);
|
||||
let day = cell.checked_sub(start).map(|day| day + 1).filter(|day| *day <= days);
|
||||
let is_today = year == today.year() && month == today.month() && day == Some(today.day() as usize);
|
||||
let label = day.map_or(String::new(), |day| day.to_string());
|
||||
dates = dates.push(
|
||||
container(
|
||||
crate::text(label, font)
|
||||
.size(12)
|
||||
.class(theme::Text::Color(if is_today { primary } else { muted })),
|
||||
)
|
||||
container(crate::text(label, font).size(12).class(theme::Text::Color(if is_today {
|
||||
primary
|
||||
} else {
|
||||
muted
|
||||
})))
|
||||
.width(30)
|
||||
.height(25)
|
||||
.center_x(30)
|
||||
.center_y(25)
|
||||
.class(theme::Container::custom(move |_| container::Style {
|
||||
background: is_today
|
||||
.then_some(Background::Color(palette.accent.scale_alpha(0.2 * opacity))),
|
||||
background: is_today.then_some(Background::Color(palette.accent.scale_alpha(0.2 * opacity))),
|
||||
border: Border {
|
||||
radius: palette.radius.min(12.).into(),
|
||||
..Default::default()
|
||||
@@ -155,14 +140,8 @@ pub fn grid<'a, M: Clone + 'a>(
|
||||
mod tests {
|
||||
#[test]
|
||||
fn pixel_clock_stacks_only_hour_and_minute_formats() {
|
||||
assert_eq!(
|
||||
super::stacked_digits("2:45 pm"),
|
||||
Some(["02".into(), "45".into()])
|
||||
);
|
||||
assert_eq!(
|
||||
super::stacked_digits("14:45"),
|
||||
Some(["14".into(), "45".into()])
|
||||
);
|
||||
assert_eq!(super::stacked_digits("2:45 pm"), Some(["02".into(), "45".into()]));
|
||||
assert_eq!(super::stacked_digits("14:45"), Some(["14".into(), "45".into()]));
|
||||
assert!(super::stacked_digits("14:45:30").is_none());
|
||||
assert!(super::stacked_digits("Today 14:45").is_none());
|
||||
assert!(super::stacked_digits("14:45 pm extra").is_none());
|
||||
@@ -171,18 +150,9 @@ mod tests {
|
||||
#[test]
|
||||
fn clock_uses_lowercase_date_and_twelve_hour_time() {
|
||||
for (stamp, expected) in [
|
||||
(
|
||||
"2026-09-28T21:32:00+01:00[Africa/Lagos]",
|
||||
"28 sept, 9:32 pm",
|
||||
),
|
||||
(
|
||||
"2026-09-28T00:05:00+01:00[Africa/Lagos]",
|
||||
"28 sept, 12:05 am",
|
||||
),
|
||||
(
|
||||
"2026-09-28T12:00:00+01:00[Africa/Lagos]",
|
||||
"28 sept, 12:00 pm",
|
||||
),
|
||||
("2026-09-28T21:32:00+01:00[Africa/Lagos]", "28 sept, 9:32 pm"),
|
||||
("2026-09-28T00:05:00+01:00[Africa/Lagos]", "28 sept, 12:05 am"),
|
||||
("2026-09-28T12:00:00+01:00[Africa/Lagos]", "28 sept, 12:00 pm"),
|
||||
] {
|
||||
assert_eq!(super::format_bar_time(&stamp.parse().unwrap()), expected);
|
||||
}
|
||||
@@ -194,10 +164,7 @@ mod tests {
|
||||
let (first, start, days) = super::month(february, 0);
|
||||
assert_eq!((first.year(), first.month(), start, days), (2024, 2, 3, 29));
|
||||
let (first, start, days) = super::month(february, -2);
|
||||
assert_eq!(
|
||||
(first.year(), first.month(), start, days),
|
||||
(2023, 12, 4, 31)
|
||||
);
|
||||
assert_eq!((first.year(), first.month(), start, days), (2023, 12, 4, 31));
|
||||
let june = jiff::civil::Date::new(2025, 6, 1).unwrap();
|
||||
let (_, start, days) = super::month(june, 0);
|
||||
assert_eq!((start, days), (6, 30));
|
||||
|
||||
@@ -76,27 +76,12 @@ pub fn apply(native: &mut cosmic::cosmic_theme::Theme, preferred: Color) {
|
||||
let surface: Color = native.primary(false).base.into();
|
||||
for component in [&mut native.accent, &mut native.accent_button] {
|
||||
let (base, on) = accent_pair(composite(component.base.into(), surface), preferred);
|
||||
component.hover = rgba(readable_fill(
|
||||
composite(component.hover.into(), surface),
|
||||
base,
|
||||
on,
|
||||
));
|
||||
component.pressed = rgba(readable_fill(
|
||||
composite(component.pressed.into(), surface),
|
||||
base,
|
||||
on,
|
||||
));
|
||||
component.selected = rgba(readable_fill(
|
||||
composite(component.selected.into(), surface),
|
||||
base,
|
||||
on,
|
||||
));
|
||||
component.hover = rgba(readable_fill(composite(component.hover.into(), surface), base, on));
|
||||
component.pressed = rgba(readable_fill(composite(component.pressed.into(), surface), base, on));
|
||||
component.selected = rgba(readable_fill(composite(component.selected.into(), surface), base, on));
|
||||
component.base = rgba(base);
|
||||
component.on = rgba(on);
|
||||
component.selected_text = rgba(foreground(
|
||||
composite(component.selected.into(), surface),
|
||||
on,
|
||||
));
|
||||
component.selected_text = rgba(foreground(composite(component.selected.into(), surface), on));
|
||||
component.disabled = rgba(composite(Color { a: 0.5, ..base }, surface));
|
||||
component.on_disabled = rgba(foreground(composite(Color { a: 0.5, ..base }, surface), on));
|
||||
}
|
||||
@@ -125,9 +110,10 @@ fn rgba(c: Color) -> cosmic::cosmic_theme::palette::Srgba {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use cosmic::iced::Background;
|
||||
|
||||
use super::*;
|
||||
use crate::button::{State, accent_button_style};
|
||||
use cosmic::iced::Background;
|
||||
#[test]
|
||||
fn reference_contrast_values_use_linear_srgb() {
|
||||
assert!((contrast(Color::BLACK, Color::WHITE) - 21.).abs() < 0.00001);
|
||||
@@ -139,10 +125,7 @@ mod tests {
|
||||
let preferred = Color::from_rgb8(240, 242, 246);
|
||||
assert_eq!(foreground(Color::BLACK, preferred), preferred);
|
||||
assert_eq!(foreground(Color::WHITE, preferred), Color::BLACK);
|
||||
assert_eq!(
|
||||
foreground(Color::from_rgb8(61, 123, 230), preferred),
|
||||
Color::BLACK
|
||||
);
|
||||
assert_eq!(foreground(Color::from_rgb8(61, 123, 230), preferred), Color::BLACK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -170,12 +153,7 @@ mod tests {
|
||||
.build();
|
||||
apply(&mut native, preferred);
|
||||
let theme = cosmic::Theme::custom(std::sync::Arc::new(native));
|
||||
for state in [
|
||||
State::Active,
|
||||
State::Hovered,
|
||||
State::Pressed,
|
||||
State::Disabled,
|
||||
] {
|
||||
for state in [State::Active, State::Hovered, State::Pressed, State::Disabled] {
|
||||
let actual = accent_button_style(&theme, state, false);
|
||||
assert_eq!(actual.text_color, Some(preferred));
|
||||
assert_eq!(actual.icon_color, Some(preferred));
|
||||
@@ -223,20 +201,11 @@ mod tests {
|
||||
for fill in [component.base, component.hover, component.pressed] {
|
||||
assert!(contrast(fill.into(), component.on.into()) >= 4.5);
|
||||
}
|
||||
assert!(
|
||||
contrast(component.selected.into(), component.selected_text.into()) >= 4.5
|
||||
);
|
||||
assert!(
|
||||
contrast(component.disabled.into(), component.on_disabled.into()) >= 4.5
|
||||
);
|
||||
assert!(contrast(component.selected.into(), component.selected_text.into()) >= 4.5);
|
||||
assert!(contrast(component.disabled.into(), component.on_disabled.into()) >= 4.5);
|
||||
}
|
||||
let theme = cosmic::Theme::custom(std::sync::Arc::new(native));
|
||||
for state in [
|
||||
State::Active,
|
||||
State::Hovered,
|
||||
State::Pressed,
|
||||
State::Disabled,
|
||||
] {
|
||||
for state in [State::Active, State::Hovered, State::Pressed, State::Disabled] {
|
||||
let style = accent_button_style(&theme, state, false);
|
||||
let Some(Background::Color(fill)) = style.background else {
|
||||
panic!("No fill")
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use cosmic::iced::{Background, Border, Color, Vector};
|
||||
use cosmic::widget::{button, container};
|
||||
use cosmic::{theme, widget};
|
||||
|
||||
use crate::{Palette, mix};
|
||||
use cosmic::{
|
||||
iced::{Background, Border, Color, Vector},
|
||||
theme,
|
||||
widget::{self, button, container},
|
||||
};
|
||||
|
||||
pub fn surface(background: Color, radius: f32) -> theme::Container<'static> {
|
||||
theme::Container::custom(move |_| surface_appearance(background, radius))
|
||||
@@ -85,11 +84,7 @@ fn styled_button(p: Palette, selected: bool, navigation: bool) -> theme::Button
|
||||
icon_color: Some(on),
|
||||
border_radius: p.radius.min(9.).into(),
|
||||
border_width: if selected { 1. } else { 0. },
|
||||
border_color: if selected {
|
||||
p.accent
|
||||
} else {
|
||||
Color::TRANSPARENT
|
||||
},
|
||||
border_color: if selected { p.accent } else { Color::TRANSPARENT },
|
||||
outline_width: 0.,
|
||||
outline_color: Color::TRANSPARENT,
|
||||
overlay: None,
|
||||
@@ -136,11 +131,7 @@ pub fn authentication_input(palette: Palette) -> theme::TextInput {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock_input(
|
||||
radius: f32,
|
||||
accent: cosmic::iced::Color,
|
||||
surface: cosmic::iced::Color,
|
||||
) -> theme::TextInput {
|
||||
pub fn lock_input(radius: f32, accent: cosmic::iced::Color, surface: cosmic::iced::Color) -> theme::TextInput {
|
||||
let appearance = move |focused: bool| widget::text_input::Appearance {
|
||||
background: cosmic::iced::Color::from_rgba(
|
||||
(surface.r + 1.) * 0.5,
|
||||
@@ -190,9 +181,7 @@ pub fn shell_button(
|
||||
a: if pressed {
|
||||
0.20 * opacity
|
||||
} else {
|
||||
((if selected { 0.14 } else { 0.0 })
|
||||
+ progress * if selected { 0.02 } else { 0.08 })
|
||||
* opacity
|
||||
((if selected { 0.14 } else { 0.0 }) + progress * if selected { 0.02 } else { 0.08 }) * opacity
|
||||
},
|
||||
..foreground
|
||||
})),
|
||||
@@ -211,12 +200,7 @@ pub fn surface_appearance(background: Color, radius: f32) -> container::Style {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notification_button(
|
||||
foreground: Color,
|
||||
hover: Color,
|
||||
radius: f32,
|
||||
filled: bool,
|
||||
) -> theme::Button {
|
||||
pub fn notification_button(foreground: Color, hover: Color, radius: f32, filled: bool) -> theme::Button {
|
||||
let style = move |active: bool| button::Style {
|
||||
text_color: Some(foreground),
|
||||
icon_color: Some(foreground),
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
//! Canonical SVG assets, sizing and semantic tint rules for native Ferese UIs.
|
||||
use cosmic::{
|
||||
iced::{Color, ContentFit, Length},
|
||||
theme,
|
||||
widget::icon,
|
||||
};
|
||||
use cosmic::iced::{Color, ContentFit, Length};
|
||||
use cosmic::theme;
|
||||
use cosmic::widget::icon;
|
||||
|
||||
pub const ARROW: &[u8] = include_bytes!("../assets/icons/arrow.svg");
|
||||
pub const BATTERY: &[u8] = include_bytes!("../assets/icons/battery.svg");
|
||||
@@ -18,8 +16,7 @@ pub const BATTERY_75: &[u8] = include_bytes!("../assets/icons/status/battery-75.
|
||||
pub const BATTERY_CHARGING: &[u8] = include_bytes!("../assets/icons/status/battery-charging.svg");
|
||||
pub const BATTERY_EMPTY: &[u8] = include_bytes!("../assets/icons/status/battery-empty.svg");
|
||||
pub const BATTERY_FULL: &[u8] = include_bytes!("../assets/icons/status/battery-full.svg");
|
||||
pub const BLUETOOTH_CONNECTED: &[u8] =
|
||||
include_bytes!("../assets/icons/status/bluetooth-connected.svg");
|
||||
pub const BLUETOOTH_CONNECTED: &[u8] = include_bytes!("../assets/icons/status/bluetooth-connected.svg");
|
||||
pub const BLUETOOTH_OFF: &[u8] = include_bytes!("../assets/icons/status/bluetooth-off.svg");
|
||||
pub const BLUETOOTH_ON: &[u8] = include_bytes!("../assets/icons/status/bluetooth-on.svg");
|
||||
pub const BRIGHTNESS: &[u8] = include_bytes!("../assets/icons/status/brightness.svg");
|
||||
@@ -67,10 +64,7 @@ pub fn tinted(source: &'static [u8], size: u16, foreground: Color) -> icon::Icon
|
||||
}
|
||||
|
||||
pub fn symbolic(source: &'static [u8], size: u16) -> icon::Icon {
|
||||
icon::from_svg_bytes(source)
|
||||
.symbolic(true)
|
||||
.icon()
|
||||
.size(size)
|
||||
icon::from_svg_bytes(source).symbolic(true).icon().size(size)
|
||||
}
|
||||
|
||||
pub fn accented(source: &'static [u8], size: u16, foreground: Color, accent: Color) -> icon::Icon {
|
||||
@@ -88,8 +82,8 @@ pub fn accented(source: &'static [u8], size: u16, foreground: Color, accent: Col
|
||||
.size(size)
|
||||
.width(Length::Fixed(f32::from(size) * aspect))
|
||||
.content_fit(ContentFit::Contain)
|
||||
.class(theme::Svg::custom(move |_| {
|
||||
cosmic::iced::widget::svg::Style { color: None }
|
||||
.class(theme::Svg::custom(move |_| cosmic::iced::widget::svg::Style {
|
||||
color: None,
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use ferese_protocols::material::v1::client::{
|
||||
ferese_material_manager_v1::FereseMaterialManagerV1,
|
||||
ferese_surface_material_v1::FereseSurfaceMaterialV1,
|
||||
};
|
||||
use wayland_client::{
|
||||
Connection, Dispatch, Proxy, QueueHandle, delegate_noop,
|
||||
globals::{GlobalListContents, registry_queue_init},
|
||||
protocol::{wl_registry, wl_surface},
|
||||
};
|
||||
use ferese_protocols::material::v1::client::ferese_material_manager_v1::FereseMaterialManagerV1;
|
||||
use ferese_protocols::material::v1::client::ferese_surface_material_v1::FereseSurfaceMaterialV1;
|
||||
use wayland_client::globals::{GlobalListContents, registry_queue_init};
|
||||
use wayland_client::protocol::{wl_registry, wl_surface};
|
||||
use wayland_client::{Connection, Dispatch, Proxy, QueueHandle, delegate_noop};
|
||||
|
||||
/// Keeps a compositor-owned dialog material attached for the window lifetime.
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -37,9 +33,8 @@ impl ModalMaterial {
|
||||
return Err("Dialog materials require Wayland".into());
|
||||
};
|
||||
// Iced lends live native handles; this backend borrows its connection.
|
||||
let backend = unsafe {
|
||||
wayland_client::backend::Backend::from_foreign_display(display.display.as_ptr().cast())
|
||||
};
|
||||
let backend =
|
||||
unsafe { wayland_client::backend::Backend::from_foreign_display(display.display.as_ptr().cast()) };
|
||||
let connection = Connection::from_backend(backend);
|
||||
let id = unsafe {
|
||||
wayland_client::backend::ObjectId::from_ptr(
|
||||
@@ -48,8 +43,7 @@ impl ModalMaterial {
|
||||
)
|
||||
}
|
||||
.map_err(|error| error.to_string())?;
|
||||
let surface =
|
||||
wl_surface::WlSurface::from_id(&connection, id).map_err(|error| error.to_string())?;
|
||||
let surface = wl_surface::WlSurface::from_id(&connection, id).map_err(|error| error.to_string())?;
|
||||
let (globals, mut queue) =
|
||||
registry_queue_init::<MaterialState>(&connection).map_err(|error| error.to_string())?;
|
||||
let qh = queue.handle();
|
||||
@@ -63,9 +57,7 @@ impl ModalMaterial {
|
||||
material,
|
||||
};
|
||||
let mut state = MaterialState::default();
|
||||
queue
|
||||
.roundtrip(&mut state)
|
||||
.map_err(|error| error.to_string())?;
|
||||
queue.roundtrip(&mut state).map_err(|error| error.to_string())?;
|
||||
if !state.ready {
|
||||
return Err("Dialog material attachment was not acknowledged".into());
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
//! Shared menu layouts and paint; callers own actions and animation timing.
|
||||
use cosmic::{
|
||||
Element,
|
||||
iced::{Alignment, Color, Length},
|
||||
widget::{button, container, row as iced_row},
|
||||
};
|
||||
use cosmic::Element;
|
||||
use cosmic::iced::{Alignment, Color, Length};
|
||||
use cosmic::widget::{button, container, row as iced_row};
|
||||
|
||||
pub fn button<'a, M: Clone + 'a>(
|
||||
label: impl Into<std::borrow::Cow<'a, str>> + 'a,
|
||||
@@ -20,12 +18,9 @@ pub fn row<'a, M: 'a>(
|
||||
trailing: Element<'a, M>,
|
||||
font: cosmic::font::Font,
|
||||
) -> Element<'a, M> {
|
||||
iced_row![
|
||||
crate::text(label, font).width(Length::Fill).size(14),
|
||||
trailing
|
||||
]
|
||||
.align_y(Alignment::Center)
|
||||
.into()
|
||||
iced_row![crate::text(label, font).width(Length::Fill).size(14), trailing]
|
||||
.align_y(Alignment::Center)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn heading<'a, M: 'a>(
|
||||
@@ -38,12 +33,7 @@ pub fn heading<'a, M: 'a>(
|
||||
.align_y(Alignment::Center)
|
||||
}
|
||||
|
||||
pub fn badge<'a, M: 'a>(
|
||||
content: Element<'a, M>,
|
||||
accent: Color,
|
||||
opacity: f32,
|
||||
radius: f32,
|
||||
) -> Element<'a, M> {
|
||||
pub fn badge<'a, M: 'a>(content: Element<'a, M>, accent: Color, opacity: f32, radius: f32) -> Element<'a, M> {
|
||||
container(content)
|
||||
.width(36)
|
||||
.height(36)
|
||||
@@ -56,12 +46,7 @@ pub fn badge<'a, M: 'a>(
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn section<'a, M: 'a>(
|
||||
content: Element<'a, M>,
|
||||
foreground: Color,
|
||||
opacity: f32,
|
||||
radius: f32,
|
||||
) -> Element<'a, M> {
|
||||
pub fn section<'a, M: 'a>(content: Element<'a, M>, foreground: Color, opacity: f32, radius: f32) -> Element<'a, M> {
|
||||
container(content)
|
||||
.padding(10)
|
||||
.width(Length::Fill)
|
||||
@@ -87,10 +72,7 @@ pub fn section_label<'a>(
|
||||
|
||||
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(
|
||||
foreground.scale_alpha(0.12 * opacity),
|
||||
0.,
|
||||
))
|
||||
.class(crate::controls::surface(foreground.scale_alpha(0.12 * opacity), 0.))
|
||||
.into()
|
||||
}
|
||||
|
||||
@@ -112,10 +94,7 @@ pub fn switch<'a, M: 'a>(enabled: bool, palette: crate::Palette, opacity: f32) -
|
||||
.width(16)
|
||||
.height(16)
|
||||
.class(crate::controls::surface(
|
||||
Color {
|
||||
a: opacity,
|
||||
..thumb
|
||||
},
|
||||
Color { a: opacity, ..thumb },
|
||||
palette.radius.min(8.),
|
||||
));
|
||||
container(knob)
|
||||
|
||||
@@ -102,11 +102,7 @@ pub fn parse_color(value: &str) -> Option<Color> {
|
||||
}
|
||||
|
||||
pub fn mix(a: Color, b: Color, t: f32) -> Color {
|
||||
Color::from_rgb(
|
||||
a.r + (b.r - a.r) * t,
|
||||
a.g + (b.g - a.g) * t,
|
||||
a.b + (b.b - a.b) * t,
|
||||
)
|
||||
Color::from_rgb(a.r + (b.r - a.r) * t, a.g + (b.g - a.g) * t, a.b + (b.b - a.b) * t)
|
||||
}
|
||||
|
||||
pub fn surface_shade(base: Color) -> Color {
|
||||
@@ -179,10 +175,8 @@ mod tests {
|
||||
("translucent", 2., 1.),
|
||||
("translucent", -1., 0.),
|
||||
] {
|
||||
let document = Document::parse(&format!(
|
||||
"theme {{ material {{ style {style}; opacity {opacity}; }} }}"
|
||||
))
|
||||
.unwrap();
|
||||
let document =
|
||||
Document::parse(&format!("theme {{ material {{ style {style}; opacity {opacity}; }} }}")).unwrap();
|
||||
assert_eq!(material_opacity(Some(&document)), expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Mutex, OnceLock},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
pub fn font(family: Option<&str>) -> cosmic::font::Font {
|
||||
let Some(family) = family else {
|
||||
|
||||
Reference in new issue
Block a user