151 lines
7.7 KiB
Rust
151 lines
7.7 KiB
Rust
//! Canonical SVG assets, sizing and semantic tint rules for native Ferese UIs.
|
|
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");
|
|
pub const CHECK: &[u8] = include_bytes!("../assets/icons/check.svg");
|
|
pub const DISPLAY: &[u8] = include_bytes!("../assets/icons/display.svg");
|
|
pub const FERESE: &[u8] = include_bytes!("../assets/icons/ferese.svg");
|
|
pub const OVERVIEW: &[u8] = include_bytes!("../assets/icons/overview.svg");
|
|
pub const AIRPLANE: &[u8] = include_bytes!("../assets/icons/status/airplane.svg");
|
|
pub const BATTERY_25: &[u8] = include_bytes!("../assets/icons/status/battery-25.svg");
|
|
pub const BATTERY_50: &[u8] = include_bytes!("../assets/icons/status/battery-50.svg");
|
|
pub const BATTERY_75: &[u8] = include_bytes!("../assets/icons/status/battery-75.svg");
|
|
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_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");
|
|
pub const CALENDAR: &[u8] = include_bytes!("../assets/icons/status/calendar.svg");
|
|
pub const CHEVRON_DOWN: &[u8] = include_bytes!("../assets/icons/status/chevron-down.svg");
|
|
pub const CHEVRON_UP: &[u8] = include_bytes!("../assets/icons/status/chevron-up.svg");
|
|
pub const CLOSE: &[u8] = include_bytes!("../assets/icons/status/close.svg");
|
|
pub const CONTROL_CENTER: &[u8] = include_bytes!("../assets/icons/status/control-center.svg");
|
|
pub const DND: &[u8] = include_bytes!("../assets/icons/status/dnd.svg");
|
|
pub const ETHERNET: &[u8] = include_bytes!("../assets/icons/status/ethernet.svg");
|
|
pub const LOCK: &[u8] = include_bytes!("../assets/icons/status/lock.svg");
|
|
pub const MIC_MUTE: &[u8] = include_bytes!("../assets/icons/status/mic-mute.svg");
|
|
pub const MIC: &[u8] = include_bytes!("../assets/icons/status/mic.svg");
|
|
pub const MOON: &[u8] = include_bytes!("../assets/icons/status/moon.svg");
|
|
pub const NOTIFICATIONS_OFF: &[u8] = include_bytes!("../assets/icons/status/notifications-off.svg");
|
|
pub const NOTIFICATIONS: &[u8] = include_bytes!("../assets/icons/status/notifications.svg");
|
|
pub const POWER_BALANCED: &[u8] = include_bytes!("../assets/icons/status/power-balanced.svg");
|
|
pub const POWER_OFF: &[u8] = include_bytes!("../assets/icons/status/power-off.svg");
|
|
pub const POWER_PERFORMANCE: &[u8] = include_bytes!("../assets/icons/status/power-performance.svg");
|
|
pub const POWER_SAVER: &[u8] = include_bytes!("../assets/icons/status/power-saver.svg");
|
|
pub const POWER: &[u8] = include_bytes!("../assets/icons/status/power.svg");
|
|
pub const RECORD_CANCEL: &[u8] = include_bytes!("../assets/icons/status/record-cancel.svg");
|
|
pub const RECORD_SAVING: &[u8] = include_bytes!("../assets/icons/status/record-saving.svg");
|
|
pub const RECORD_STOP: &[u8] = include_bytes!("../assets/icons/status/record-stop.svg");
|
|
pub const RECORD: &[u8] = include_bytes!("../assets/icons/status/record.svg");
|
|
pub const RESTART: &[u8] = include_bytes!("../assets/icons/status/restart.svg");
|
|
pub const SETTINGS: &[u8] = include_bytes!("../assets/icons/status/settings.svg");
|
|
pub const SUSPEND: &[u8] = include_bytes!("../assets/icons/status/suspend.svg");
|
|
pub const TRASH: &[u8] = include_bytes!("../assets/icons/status/trash.svg");
|
|
pub const VOLUME_HIGH: &[u8] = include_bytes!("../assets/icons/status/volume-high.svg");
|
|
pub const VOLUME_LOW: &[u8] = include_bytes!("../assets/icons/status/volume-low.svg");
|
|
pub const VOLUME_MEDIUM: &[u8] = include_bytes!("../assets/icons/status/volume-medium.svg");
|
|
pub const VOLUME_MUTE: &[u8] = include_bytes!("../assets/icons/status/volume-mute.svg");
|
|
pub const WIFI_FULL: &[u8] = include_bytes!("../assets/icons/status/wifi-full.svg");
|
|
pub const WIFI_LOW: &[u8] = include_bytes!("../assets/icons/status/wifi-low.svg");
|
|
pub const WIFI_MEDIUM: &[u8] = include_bytes!("../assets/icons/status/wifi-medium.svg");
|
|
pub const WIFI_OFF: &[u8] = include_bytes!("../assets/icons/status/wifi-off.svg");
|
|
pub const VOLUME: &[u8] = include_bytes!("../assets/icons/volume.svg");
|
|
pub const WIFI: &[u8] = include_bytes!("../assets/icons/wifi.svg");
|
|
|
|
pub const APPLICATION: &[u8] = include_bytes!("../../../packaging/icons/ferese.svg");
|
|
|
|
pub fn tinted(source: &'static [u8], size: u16, foreground: Color) -> icon::Icon {
|
|
accented(source, size, foreground, foreground)
|
|
}
|
|
|
|
pub fn symbolic(source: &'static [u8], size: u16) -> icon::Icon {
|
|
icon::from_svg_bytes(source).symbolic(true).icon().size(size)
|
|
}
|
|
|
|
pub fn accented(source: &'static [u8], size: u16, foreground: Color, accent: Color) -> icon::Icon {
|
|
let svg = std::str::from_utf8(source).expect("embedded SVG must be UTF-8");
|
|
// Battery canvases are wider; reserve that space instead of stretching
|
|
// or clipping them into the square slot used by the other status icons.
|
|
let aspect = if svg.contains("viewBox=\"0 0 32 24\"") {
|
|
4.0 / 3.0
|
|
} else {
|
|
1.0
|
|
};
|
|
icon::from_svg_bytes(tint_svg(source, foreground, accent))
|
|
.symbolic(false)
|
|
.icon()
|
|
.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,
|
|
}))
|
|
}
|
|
|
|
pub fn tint_svg(source: &[u8], foreground: Color, accent: Color) -> Vec<u8> {
|
|
let rgba = |foreground: Color| {
|
|
format!(
|
|
"rgba({},{},{},{})",
|
|
(foreground.r * 255.0).round() as u8,
|
|
(foreground.g * 255.0).round() as u8,
|
|
(foreground.b * 255.0).round() as u8,
|
|
foreground.a,
|
|
)
|
|
};
|
|
std::str::from_utf8(source)
|
|
.expect("embedded icons must be UTF-8 SVG")
|
|
.replace("currentColor", &rgba(foreground))
|
|
// The source artwork's blue swatch is the semantic theme accent.
|
|
// Battery warning/success swatches remain status colors.
|
|
.replace("#3d7be6", &rgba(accent))
|
|
.into_bytes()
|
|
}
|
|
|
|
pub fn outline(path: &str, tint: Color, size: u16) -> icon::Icon {
|
|
let svg = format!(
|
|
r##"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<path d="{path}" fill="none" stroke="currentColor" stroke-width="1.6"
|
|
stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>"##,
|
|
);
|
|
icon::from_svg_bytes(tint_svg(svg.as_bytes(), tint, tint))
|
|
.symbolic(false)
|
|
.icon()
|
|
.size(size)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use cosmic::iced::Color;
|
|
#[test]
|
|
fn icons_use_theme_accent_and_preserve_battery_status_colors() {
|
|
for (source, accent) in [
|
|
(super::BATTERY_FULL, "#63c168"),
|
|
(super::BATTERY_50, "#3d7be6"),
|
|
(super::BATTERY_25, "#e0654f"),
|
|
(super::WIFI_FULL, "#3d7be6"),
|
|
(super::CONTROL_CENTER, "#3d7be6"),
|
|
] {
|
|
let svg = String::from_utf8(super::tint_svg(
|
|
source,
|
|
Color::from_rgb8(205, 214, 244),
|
|
Color::from_rgb8(203, 166, 247),
|
|
))
|
|
.unwrap();
|
|
assert!(!svg.contains("currentColor"));
|
|
assert!(svg.contains("rgba(205,214,244,1)"));
|
|
assert!(!svg.contains("#3d7be6"));
|
|
assert!(svg.contains(if accent == "#3d7be6" {
|
|
"rgba(203,166,247,1)"
|
|
} else {
|
|
accent
|
|
}));
|
|
}
|
|
}
|
|
}
|