92 lines
3.2 KiB
Rust
92 lines
3.2 KiB
Rust
//! Иконки вкладок.
|
|
//!
|
|
//! Имена совпадают с названиями Lucide (`file`, `file-code`, `book-open`) —
|
|
//! именно их принимает `app:set_tab_icon(index, name)`. Неизвестное имя не
|
|
//! подменяется молча: API возвращает `false`, а вкладка продолжает показывать
|
|
//! иконку по умолчанию.
|
|
|
|
use std::collections::HashMap;
|
|
use std::sync::LazyLock;
|
|
|
|
use gpui_kit::assets::IconName;
|
|
|
|
/// Иконка вкладки, пока сценарий не назначил другую.
|
|
pub const DEFAULT_TAB_ICON: &str = "file";
|
|
|
|
/// Каталог иконок по каноническому имени.
|
|
static ICONS: LazyLock<HashMap<String, IconName>> = LazyLock::new(|| {
|
|
IconName::ALL
|
|
.iter()
|
|
.map(|icon| (asset_name(*icon), *icon))
|
|
.collect()
|
|
});
|
|
|
|
/// Имя иконки в каталоге: `icons/file-code.svg` → `file-code`.
|
|
fn asset_name(icon: IconName) -> String {
|
|
let path = icon.path();
|
|
let path = path.as_str();
|
|
let stem = path.strip_prefix("icons/").unwrap_or(path);
|
|
stem.strip_suffix(".svg").unwrap_or(stem).to_owned()
|
|
}
|
|
|
|
/// Приводит имя из API к виду каталога.
|
|
///
|
|
/// Регистр не важен; `_` и пробелы считаются дефисом, префикс `icons/` и
|
|
/// расширение `.svg` можно не писать.
|
|
fn normalize(name: &str) -> String {
|
|
let mut normalized = name.trim().to_ascii_lowercase().replace(['_', ' '], "-");
|
|
if let Some(rest) = normalized.strip_prefix("icons/") {
|
|
normalized = rest.to_owned();
|
|
}
|
|
if let Some(rest) = normalized.strip_suffix(".svg") {
|
|
normalized = rest.to_owned();
|
|
}
|
|
normalized
|
|
}
|
|
|
|
/// Каноническое имя известной иконки.
|
|
pub fn canonical_name(name: &str) -> Option<&'static str> {
|
|
let normalized = normalize(name);
|
|
ICONS
|
|
.get_key_value(&normalized)
|
|
.map(|(name, _)| name.as_str())
|
|
}
|
|
|
|
/// Иконка по имени из API; `None`, если такого имени нет в каталоге.
|
|
pub fn from_name(name: &str) -> Option<IconName> {
|
|
let normalized = normalize(name);
|
|
ICONS.get(&normalized).copied()
|
|
}
|
|
|
|
/// Иконка вкладки по умолчанию.
|
|
pub fn default_icon() -> IconName {
|
|
IconName::File
|
|
}
|
|
|
|
/// Иконка вкладки: назначенная сценарием или стандартная.
|
|
pub fn tab_icon(name: Option<&str>) -> IconName {
|
|
name.and_then(from_name).unwrap_or_else(default_icon)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn names_are_normalized_before_lookup() {
|
|
assert_eq!(canonical_name("file"), Some("file"));
|
|
assert_eq!(canonical_name(" File "), Some("file"));
|
|
assert_eq!(canonical_name("file_code"), Some("file-code"));
|
|
assert_eq!(canonical_name("icons/file-code.svg"), Some("file-code"));
|
|
assert_eq!(canonical_name("not-an-icon"), None);
|
|
}
|
|
|
|
#[test]
|
|
fn default_icon_is_in_the_catalog() {
|
|
let icon = default_icon();
|
|
assert_eq!(from_name(DEFAULT_TAB_ICON), Some(icon));
|
|
assert_eq!(tab_icon(None), icon);
|
|
assert_eq!(tab_icon(Some("unknown")), icon);
|
|
}
|
|
}
|