//! What an icon is, independent of how it gets drawn.
use crate::palette::NamedColor;
/// Background construction. Only `DiagonalSplit` is attested by the icons the
/// original studio shipped (the five cbz apps all use it, two-tone Casabeza
/// green over teal); the rest are reconstructions of the "gradients, patterns
/// and other things" the tool offered.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Background {
Solid,
DiagonalSplit,
LinearGradient,
RadialGradient,
ConicGradient,
Stripes,
Checks,
Dots,
Rays,
CornerWedge,
}
impl Background {
pub const ALL: &'static [Background] = &[
Background::Solid,
Background::DiagonalSplit,
Background::LinearGradient,
Background::RadialGradient,
Background::ConicGradient,
Background::Stripes,
Background::Checks,
Background::Dots,
Background::Rays,
Background::CornerWedge,
];
/// Stable identifier for documents. Distinct from [`label`], which is
/// display text and may be reworded without breaking saved files.
pub fn id(self) -> &'static str {
match self {
Background::Solid => "solid",
Background::DiagonalSplit => "diagonal-split",
Background::LinearGradient => "linear-gradient",
Background::RadialGradient => "radial-gradient",
Background::ConicGradient => "conic-gradient",
Background::Stripes => "stripes",
Background::Checks => "checks",
Background::Dots => "dots",
Background::Rays => "rays",
Background::CornerWedge => "corner-wedge",
}
}
pub fn from_id(id: &str) -> Option<Background> {
Background::ALL.iter().copied().find(|b| b.id() == id)
}
pub fn label(self) -> &'static str {
match self {
Background::Solid => "Solid",
Background::DiagonalSplit => "Diagonal split",
Background::LinearGradient => "Linear gradient",
Background::RadialGradient => "Radial gradient",
Background::ConicGradient => "Conic gradient",
Background::Stripes => "Stripes",
Background::Checks => "Checks",
Background::Dots => "Dots",
Background::Rays => "Rays",
Background::CornerWedge => "Corner wedge",
}
}
/// Whether the second colour does anything. Solid ignores it, and the grid
/// uses this to avoid rendering duplicate cells.
pub fn uses_second_color(self) -> bool {
self != Background::Solid
}
}
#[derive(Clone, PartialEq, Debug)]
pub enum Foreground {
/// A Material Symbols glyph.
Icon(char),
/// Free text, e.g. the "1234" the numerics app shipped.
Text(String),
}
/// An extra glyph stacked on the main mark, for badge shapes like a lock
/// inside a shield. Layers carry the same controls as the mark itself: `size`,
/// `rotation`, `offset`, `filled` and `color` here correspond to the `mark_*`
/// fields, [`IconSpec::filled`] and [`IconSpec::fg_color`].
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Layer {
pub glyph: char,
/// `None` follows [`IconSpec::fg_color`], including later changes to it;
/// a layer only keeps a colour of its own once one is picked for it.
pub color: Option<NamedColor>,
/// Size relative to [`IconSpec::scale`], so the overall scale resizes the
/// whole stack without changing its proportions.
pub size: f32,
/// Degrees, clockwise, about the layer's own centre.
pub rotation: f32,
/// Shift from the shared centre, in units of [`IconSpec::scale`]; +y is
/// down.
pub offset: [f32; 2],
pub filled: bool,
}
#[derive(Clone, PartialEq, Debug)]
pub struct IconSpec {
pub foreground: Foreground,
/// Optional ribbon across the lower third. No shipped icon uses one, so
/// the layout here is a reconstruction of the description, not of output.
pub sub_banner: String,
pub fg_color: NamedColor,
pub bg_a: NamedColor,
pub bg_b: NamedColor,
pub background: Background,
/// Rotation of the pattern in degrees; the shipped split runs corner to
/// corner, which is 45.
pub angle: f32,
/// Overall size of the combined mark and layers, as a fraction of the
/// canvas.
pub scale: f32,
/// The main mark's own size relative to `scale`, like [`Layer::size`].
/// 1.0 for a lone mark, which then fills `scale` exactly as before layers
/// existed.
pub mark_size: f32,
/// The main mark's rotation, like [`Layer::rotation`].
pub mark_rotation: f32,
/// The main mark's shift from centre, like [`Layer::offset`].
pub mark_offset: [f32; 2],
/// Draw the Android adaptive-icon safe zone and mask guides.
pub show_guides: bool,
pub filled: bool,
/// Further glyphs drawn over the main mark, bottom to top.
pub layers: Vec<Layer>,
}
impl IconSpec {
/// The colour a layer actually draws in.
pub fn layer_color(&self, layer: &Layer) -> NamedColor {
layer.color.unwrap_or(self.fg_color)
}
}
impl Default for IconSpec {
fn default() -> Self {
Self {
foreground: Foreground::Icon('\u{e897}'), // lock
sub_banner: String::new(),
fg_color: NamedColor {
role: "base-200",
rgb: [0x0D, 0x2A, 0x2C],
},
bg_a: NamedColor {
role: "primary",
rgb: [0x00, 0xC9, 0xD4],
},
bg_b: NamedColor {
role: "accent",
rgb: [0xAA, 0xD4, 0x00],
},
background: Background::DiagonalSplit,
angle: 45.0,
scale: 0.58,
mark_size: 1.0,
mark_rotation: 0.0,
mark_offset: [0.0, 0.0],
show_guides: false,
filled: false,
layers: Vec::new(),
}
}
}
neiam
/icon-studio
public · Issues · Pulls · Labels · Forks · Compare · Actions failure · Badges · Packages
⭐
Log in to mark this repository.
5.8 KiB · text
History
6811748