mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-13 14:59:06 +00:00
* Themes: Soft Dark and Soft Light (#4368) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: regenerate scorecard after merging main Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
28 files changed
+260
-17
No files matched your search
@@ -54,6 +54,7 @@ next to them.
|
||||
| `packaging/macos/dmg/` (all files) | PhotoCraft DMG window background and layout | @XusBadia, from the PhotoCraft app icon | Original work, see [`packaging/macos/dmg/README.md`](packaging/macos/dmg/README.md) | MIT OR Apache-2.0, like the code |
|
||||
| `crates/ui-egui/src/theme.rs` (Solarized Dark palette) | Solarized colour scheme used by the Solarized Dark theme | Ethan Schoonover | <https://ethanschoonover.com/solarized/> | MIT, [`LICENSE-solarized.txt`](crates/ui-egui/src/LICENSE-solarized.txt) |
|
||||
| `crates/ui-egui/src/theme.rs` (Breeze Light and Breeze Dark themes) | The Breeze Light and Breeze Dark themes are original colour values on PhotoCraft's Studio layout, chosen to look close to KDE Plasma's Breeze (cool blue-greys, blue accent, 5 px frame radius) so the app blends into a Plasma desktop. None of Breeze's colour values, files or code are copied; the values are a few levels away from its schemes and tuned for text contrast | PhotoCraft contributors | Inspired by the look of KDE Breeze, <https://invent.kde.org/plasma/breeze> (its colour schemes are LGPL-2.0-or-later) | Original work |
|
||||
| `crates/ui-egui/src/theme.rs` (Soft Dark and Soft Light themes) | Original colour values and radii for two low-contrast themes on PhotoCraft's Studio layout; no third-party palette, file or code is used | PhotoCraft contributors | Original | Original work |
|
||||
|
||||
### Optional build input: craft-fonts (not files in this repo)
|
||||
|
||||
|
||||
@@ -309,7 +309,7 @@ pub struct UiSetParams {
|
||||
/// Fields for the control method `ui.set`: tool, panels, dock, dockTabs, dockWidth, colorPanel
|
||||
/// ({background: bool} picks which swatch the Color panel edits), maskTarget,
|
||||
/// vectorMaskTarget, selectionMode, zoom, center, rotation (view angle in degrees), fit, theme (pro, proMedium, studio,
|
||||
/// studioLight, classic, solarizedDark, adwaita, adwaitaDark, breezeLight, breezeDark), brushSection, brushTab,
|
||||
/// studioLight, classic, solarizedDark, adwaita, adwaitaDark, breezeLight, breezeDark, softLight, softDark), brushSection, brushTab,
|
||||
/// brushesView, brushPicker ([x, y] opens the Brush Preset picker there, null closes it),
|
||||
/// brushPickerName, brushPickerStroke, brushPickerTip (the picker card's parts, at least one
|
||||
/// on), brushPickerScale (the picker footer's card-scale slider, 0.15 to 2, 1 the standard),
|
||||
|
||||
@@ -55,10 +55,10 @@ choice!(TypeUnit { Points = "points", Pixels = "pixels", Millimeters = "mm" } de
|
||||
choice!(PointSize { PostScript = "postScript", Traditional = "traditional" } default PostScript);
|
||||
choice!(Interpolation { BicubicAutomatic = "bicubicAutomatic", Nearest = "nearestNeighbor", Bilinear = "bilinear", Bicubic = "bicubic", BicubicSmoother = "bicubicSmoother", BicubicSharper = "bicubicSharper", PreserveDetails = "preserveDetails" } default BicubicAutomatic);
|
||||
choice!(ColorPicker { Adobe = "adobe", System = "system" } default Adobe);
|
||||
choice!(Theme { Pro = "pro", ProMedium = "proMedium", ProLightGray = "proLightGray", ProLight = "proLight", Studio = "studio", StudioLight = "studioLight", Classic = "classic", SolarizedDark = "solarizedDark", Adwaita = "adwaita", AdwaitaDark = "adwaitaDark", BreezeLight = "breezeLight", BreezeDark = "breezeDark" } default ProMedium);
|
||||
choice!(Theme { Pro = "pro", ProMedium = "proMedium", ProLightGray = "proLightGray", ProLight = "proLight", Studio = "studio", StudioLight = "studioLight", Classic = "classic", SolarizedDark = "solarizedDark", Adwaita = "adwaita", AdwaitaDark = "adwaitaDark", BreezeLight = "breezeLight", BreezeDark = "breezeDark", SoftDark = "softDark", SoftLight = "softLight" } default ProMedium);
|
||||
choice!(AppearanceMode { Auto = "auto", Dark = "dark", Light = "light" } default Dark);
|
||||
choice!(DarkTheme { Pro = "pro", ProMedium = "proMedium", Studio = "studio", SolarizedDark = "solarizedDark", AdwaitaDark = "adwaitaDark", BreezeDark = "breezeDark" } default ProMedium);
|
||||
choice!(LightTheme { StudioLight = "studioLight", ProLightGray = "proLightGray", ProLight = "proLight", Classic = "classic", Adwaita = "adwaita", BreezeLight = "breezeLight" } default StudioLight);
|
||||
choice!(DarkTheme { Pro = "pro", ProMedium = "proMedium", Studio = "studio", SolarizedDark = "solarizedDark", AdwaitaDark = "adwaitaDark", BreezeDark = "breezeDark", SoftDark = "softDark" } default ProMedium);
|
||||
choice!(LightTheme { StudioLight = "studioLight", ProLightGray = "proLightGray", ProLight = "proLight", Classic = "classic", Adwaita = "adwaita", BreezeLight = "breezeLight", SoftLight = "softLight" } default StudioLight);
|
||||
choice!(CanvasColor { Default = "default", Black = "black", DarkGray = "darkGray", MediumGray = "mediumGray", LightGray = "lightGray", Custom = "custom" } default Default);
|
||||
choice!(CanvasBorder { DropShadow = "dropShadow", Line = "line", None = "none" } default DropShadow);
|
||||
choice!(UiScale { Auto = "auto", P75 = "75", P80 = "80", P85 = "85", P90 = "90", P95 = "95", P100 = "100", P125 = "125", P150 = "150", P175 = "175", P200 = "200", P250 = "250", P300 = "300" } default Auto);
|
||||
@@ -1542,8 +1542,12 @@ impl Session {
|
||||
&& let Some(theme) = interface.get("theme").and_then(Value::as_str).and_then(Theme::parse)
|
||||
{
|
||||
let (mode, slot) = match theme {
|
||||
Theme::Pro | Theme::ProMedium | Theme::Studio | Theme::SolarizedDark | Theme::AdwaitaDark | Theme::BreezeDark => ("dark", "darkTheme"),
|
||||
Theme::StudioLight | Theme::ProLightGray | Theme::ProLight | Theme::Classic | Theme::Adwaita | Theme::BreezeLight => ("light", "lightTheme"),
|
||||
Theme::Pro | Theme::ProMedium | Theme::Studio | Theme::SolarizedDark | Theme::AdwaitaDark | Theme::BreezeDark | Theme::SoftDark => {
|
||||
("dark", "darkTheme")
|
||||
}
|
||||
Theme::StudioLight | Theme::ProLightGray | Theme::ProLight | Theme::Classic | Theme::Adwaita | Theme::BreezeLight | Theme::SoftLight => {
|
||||
("light", "lightTheme")
|
||||
}
|
||||
};
|
||||
interface.insert("appearanceMode".into(), json!(mode));
|
||||
interface.insert(slot.into(), json!(theme.name()));
|
||||
@@ -1666,6 +1670,14 @@ impl Session {
|
||||
next.interface.light_theme = LightTheme::BreezeLight;
|
||||
next.interface.appearance_mode = AppearanceMode::Light;
|
||||
}
|
||||
Theme::SoftDark => {
|
||||
next.interface.dark_theme = DarkTheme::SoftDark;
|
||||
next.interface.appearance_mode = AppearanceMode::Dark;
|
||||
}
|
||||
Theme::SoftLight => {
|
||||
next.interface.light_theme = LightTheme::SoftLight;
|
||||
next.interface.appearance_mode = AppearanceMode::Light;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.prefs.edit(|p| *p = next);
|
||||
|
||||
@@ -735,6 +735,10 @@
|
||||
Breeze Dark Theme Tmavý motiv Breeze
|
||||
Breeze light Breeze světlý
|
||||
Breeze Light Theme Světlý motiv Breeze
|
||||
Soft dark Jemný tmavý
|
||||
Soft light Jemný světlý
|
||||
Soft Dark Theme Jemné tmavé téma
|
||||
Soft Light Theme Jemné světlé téma
|
||||
Next Theme Další motiv
|
||||
Actions Akce
|
||||
Brush Settings Nastavení štětce
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -721,6 +721,10 @@
|
||||
Breeze Dark Theme Breeze-Design (Dunkel)
|
||||
Breeze light Breeze hell
|
||||
Breeze Light Theme Breeze-Design (Hell)
|
||||
Soft dark Weich dunkel
|
||||
Soft light Weich hell
|
||||
Soft Dark Theme Weiches Design (Dunkel)
|
||||
Soft Light Theme Weiches Design (Hell)
|
||||
Next Theme Nächstes Design
|
||||
Actions Aktionen
|
||||
Brush Settings Pinseleinstellungen
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2132,6 +2132,10 @@
|
||||
Breeze Dark Theme Σκούρο θέμα Breeze
|
||||
Breeze light Breeze φωτεινό
|
||||
Breeze Light Theme Φωτεινό θέμα Breeze
|
||||
Soft dark Απαλό σκούρο
|
||||
Soft light Απαλό ανοιχτό
|
||||
Soft Dark Theme Απαλό σκούρο θέμα
|
||||
Soft Light Theme Απαλό ανοιχτό θέμα
|
||||
Clear Canvas Guides Απαλοιφή οδηγών καμβά
|
||||
Clear Override Απαλοιφή παράκαμψης
|
||||
Clear Samples Απαλοιφή δειγμάτων
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 1005 and column 18.
|
@@ -728,6 +728,10 @@
|
||||
Breeze Dark Theme Tema Breeze oscuro
|
||||
Breeze light Breeze claro
|
||||
Breeze Light Theme Tema Breeze claro
|
||||
Soft dark Suave oscuro
|
||||
Soft light Suave claro
|
||||
Soft Dark Theme Tema suave oscuro
|
||||
Soft Light Theme Tema suave claro
|
||||
Next Theme Tema siguiente
|
||||
Actions Acciones
|
||||
Brush Settings Ajustes del pincel
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
@@ -718,6 +718,10 @@
|
||||
Breeze Dark Theme Thème Breeze sombre
|
||||
Breeze light Breeze clair
|
||||
Breeze Light Theme Thème Breeze clair
|
||||
Soft dark Doux sombre
|
||||
Soft light Doux clair
|
||||
Soft Dark Theme Thème doux sombre
|
||||
Soft Light Theme Thème doux clair
|
||||
Next Theme Thème suivant
|
||||
Actions Actions
|
||||
Brush Settings Paramètres du pinceau
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -443,6 +443,10 @@
|
||||
Box Blur… Dobozelmosás…
|
||||
Breeze Dark Theme Breeze sötét téma
|
||||
Breeze Light Theme Breeze világos téma
|
||||
Soft dark Lágy sötét
|
||||
Soft light Lágy világos
|
||||
Soft Dark Theme Lágy sötét téma
|
||||
Soft Light Theme Lágy világos téma
|
||||
Brush Preview Ecset előnézete
|
||||
Brush Settings Ecsetbeállítások
|
||||
Brushes Ecsetek
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -711,6 +711,10 @@
|
||||
Breeze Dark Theme Tema Breeze Gelap
|
||||
Breeze light Breeze terang
|
||||
Breeze Light Theme Tema Breeze Terang
|
||||
Soft dark Lembut gelap
|
||||
Soft light Lembut terang
|
||||
Soft Dark Theme Tema lembut gelap
|
||||
Soft Light Theme Tema lembut terang
|
||||
Next Theme Tema Berikutnya
|
||||
Actions Action
|
||||
Brush Settings Pengaturan Kuas
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
@@ -726,6 +726,10 @@
|
||||
Breeze Dark Theme Tema Breeze scuro
|
||||
Breeze light Breeze chiaro
|
||||
Breeze Light Theme Tema Breeze chiaro
|
||||
Soft dark Morbido scuro
|
||||
Soft light Morbido chiaro
|
||||
Soft Dark Theme Tema morbido scuro
|
||||
Soft Light Theme Tema morbido chiaro
|
||||
Next Theme Tema successivo
|
||||
Actions Azioni
|
||||
Brush Settings Impostazioni pennello
|
||||
|
||||
|
@@ -728,6 +728,10 @@
|
||||
Breeze Dark Theme Breeze ダークテーマ
|
||||
Breeze light Breeze ライト
|
||||
Breeze Light Theme Breeze ライトテーマ
|
||||
Soft dark ソフトダーク
|
||||
Soft light ソフトライト
|
||||
Soft Dark Theme ソフトダークテーマ
|
||||
Soft Light Theme ソフトライトテーマ
|
||||
Next Theme 次のテーマ
|
||||
Actions アクション
|
||||
Brush Settings ブラシ設定
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -702,6 +702,10 @@
|
||||
Breeze Dark Theme Breeze 어두운 테마
|
||||
Breeze light Breeze 밝게
|
||||
Breeze Light Theme Breeze 밝은 테마
|
||||
Soft dark 소프트 다크
|
||||
Soft light 소프트 라이트
|
||||
Soft Dark Theme 소프트 다크 테마
|
||||
Soft Light Theme 소프트 라이트 테마
|
||||
Next Theme 다음 테마
|
||||
Actions 액션
|
||||
Brush Settings 브러시 설정
|
||||
|
||||
|
@@ -707,6 +707,10 @@
|
||||
Breeze Dark Theme Breeze-thema donker
|
||||
Breeze light Breeze licht
|
||||
Breeze Light Theme Breeze-thema licht
|
||||
Soft dark Zacht donker
|
||||
Soft light Zacht licht
|
||||
Soft Dark Theme Zacht donker thema
|
||||
Soft Light Theme Zacht licht thema
|
||||
Adwaita Light Theme Adwaita-thema licht
|
||||
Adwaita Dark Theme Adwaita-thema donker
|
||||
Adwaita Adwaita
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 34.
|
@@ -727,6 +727,10 @@
|
||||
Breeze Dark Theme Ciemny motyw Breeze
|
||||
Breeze light Breeze jasny
|
||||
Breeze Light Theme Jasny motyw Breeze
|
||||
Soft dark Łagodny ciemny
|
||||
Soft light Łagodny jasny
|
||||
Soft Dark Theme Łagodny ciemny motyw
|
||||
Soft Light Theme Łagodny jasny motyw
|
||||
Next Theme Następny motyw
|
||||
Actions Akcje
|
||||
Brush Settings Ustawienia pędzla
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -713,6 +713,10 @@
|
||||
Breeze Dark Theme Tema Breeze Escuro
|
||||
Breeze light Breeze claro
|
||||
Breeze Light Theme Tema Breeze Claro
|
||||
Soft dark Suave escuro
|
||||
Soft light Suave claro
|
||||
Soft Dark Theme Tema suave escuro
|
||||
Soft Light Theme Tema suave claro
|
||||
Next Theme Próximo tema
|
||||
Actions Ações
|
||||
Brush Settings Configurações do pincel
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1433,6 +1433,10 @@
|
||||
Breeze Dark Theme Тёмная тема Breeze
|
||||
Breeze light Breeze светлая
|
||||
Breeze Light Theme Светлая тема Breeze
|
||||
Soft dark Мягкая тёмная
|
||||
Soft light Мягкая светлая
|
||||
Soft Dark Theme Мягкая тёмная тема
|
||||
Soft Light Theme Мягкая светлая тема
|
||||
Studio Theme Тема Studio
|
||||
Studio Light Theme Светлая тема Studio
|
||||
Adwaita Light Theme Светлая тема Adwaita
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -702,6 +702,10 @@
|
||||
Breeze Dark Theme Esinti Koyu Tema
|
||||
Breeze light Esinti ışığı
|
||||
Breeze Light Theme Esinti Işık Teması
|
||||
Soft dark Yumuşak koyu
|
||||
Soft light Yumuşak açık
|
||||
Soft Dark Theme Yumuşak koyu tema
|
||||
Soft Light Theme Yumuşak açık tema
|
||||
Adwaita Light Theme Adwaita Işık Teması
|
||||
Adwaita Dark Theme Adwaita Karanlık Tema
|
||||
Adwaita Adwaita
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 34.
|
@@ -1412,6 +1412,10 @@
|
||||
Breeze Dark Theme Темна тема Breeze
|
||||
Breeze light Breeze світла
|
||||
Breeze Light Theme Світла тема Breeze
|
||||
Soft dark М'яка темна
|
||||
Soft light М'яка світла
|
||||
Soft Dark Theme М'яка темна тема
|
||||
Soft Light Theme М'яка світла тема
|
||||
Adwaita Light Theme Світла тема Adwaita
|
||||
Adwaita Dark Theme Темна тема Adwaita
|
||||
Adwaita Adwaita
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 1362 and column 18.
|
@@ -2353,6 +2353,10 @@ shape Appearance Diện mạo
|
||||
Bottom: Dưới:
|
||||
Breeze Dark Theme Chủ đề Breeze tối
|
||||
Breeze Light Theme Chủ đề Breeze sáng
|
||||
Soft dark Dịu tối
|
||||
Soft light Dịu sáng
|
||||
Soft Dark Theme Giao diện dịu tối
|
||||
Soft Light Theme Giao diện dịu sáng
|
||||
Caps Đầu nét
|
||||
Center Columns Căn giữa các cột
|
||||
Change Panel Group Đổi nhóm bảng
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -730,6 +730,10 @@
|
||||
Breeze Dark Theme Breeze 深色主题
|
||||
Breeze light Breeze 浅色
|
||||
Breeze Light Theme Breeze 浅色主题
|
||||
Soft dark 柔和深色
|
||||
Soft light 柔和浅色
|
||||
Soft Dark Theme 柔和深色主题
|
||||
Soft Light Theme 柔和浅色主题
|
||||
Next Theme 下一代主题
|
||||
Actions 动作
|
||||
Brush Settings 画笔设置
|
||||
|
||||
|
@@ -735,6 +735,10 @@
|
||||
Breeze Dark Theme Breeze 深色主題
|
||||
Breeze light Breeze 淺色
|
||||
Breeze Light Theme Breeze 淺色主題
|
||||
Soft dark 柔和深色
|
||||
Soft light 柔和淺色
|
||||
Soft Dark Theme 柔和深色主題
|
||||
Soft Light Theme 柔和淺色主題
|
||||
Next Theme 下一個主題
|
||||
Actions 動作
|
||||
Brush Settings 筆刷設定
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -89,6 +89,8 @@ pub const UI_COMMANDS: &[(&str, &str, &[&str], Option<&str>)] = &[
|
||||
("window.theme.adwaitaDark", "Adwaita Dark Theme", &["Window", "Theme"], None),
|
||||
("window.theme.breezeLight", "Breeze Light Theme", &["Window", "Theme"], None),
|
||||
("window.theme.breezeDark", "Breeze Dark Theme", &["Window", "Theme"], None),
|
||||
("window.theme.softLight", "Soft Light Theme", &["Window", "Theme"], None),
|
||||
("window.theme.softDark", "Soft Dark Theme", &["Window", "Theme"], None),
|
||||
("window.theme.system", "Sync with system", &["Window", "Theme"], None),
|
||||
("edit.search", "Search…", &["Edit"], Some("Cmd+F")),
|
||||
("help.discord", "Join the ArtCraft Discord…", &["Help"], None),
|
||||
@@ -374,7 +376,9 @@ pub(crate) fn invoke_unguarded(app: &mut PhotocraftApp, ctx: &egui::Context, id:
|
||||
| "window.theme.adwaita"
|
||||
| "window.theme.adwaitaDark"
|
||||
| "window.theme.breezeLight"
|
||||
| "window.theme.breezeDark" => {
|
||||
| "window.theme.breezeDark"
|
||||
| "window.theme.softLight"
|
||||
| "window.theme.softDark" => {
|
||||
let k = crate::theme::ThemeKind::from_name(&id["window.theme.".len()..]).unwrap_or_default();
|
||||
app.set_theme(ctx, k);
|
||||
Ok(Value::Null)
|
||||
|
||||
@@ -97,6 +97,7 @@ fn selected_theme(interface: &prefs::Interface, system: Option<egui::Theme>) ->
|
||||
LightTheme::Classic => ThemeKind::Classic,
|
||||
LightTheme::Adwaita => ThemeKind::Adwaita,
|
||||
LightTheme::BreezeLight => ThemeKind::BreezeLight,
|
||||
LightTheme::SoftLight => ThemeKind::SoftLight,
|
||||
}
|
||||
} else {
|
||||
match interface.dark_theme {
|
||||
@@ -106,6 +107,7 @@ fn selected_theme(interface: &prefs::Interface, system: Option<egui::Theme>) ->
|
||||
DarkTheme::SolarizedDark => ThemeKind::SolarizedDark,
|
||||
DarkTheme::AdwaitaDark => ThemeKind::AdwaitaDark,
|
||||
DarkTheme::BreezeDark => ThemeKind::BreezeDark,
|
||||
DarkTheme::SoftDark => ThemeKind::SoftDark,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,6 +135,7 @@ const LIGHT_THEME_OPTIONS: &[(&str, ThemeKind)] = &[
|
||||
("classic", ThemeKind::Classic),
|
||||
("adwaita", ThemeKind::Adwaita),
|
||||
("breezeLight", ThemeKind::BreezeLight),
|
||||
("softLight", ThemeKind::SoftLight),
|
||||
];
|
||||
const DARK_THEME_OPTIONS: &[(&str, ThemeKind)] = &[
|
||||
("proMedium", ThemeKind::ProMedium),
|
||||
@@ -141,6 +144,7 @@ const DARK_THEME_OPTIONS: &[(&str, ThemeKind)] = &[
|
||||
("solarizedDark", ThemeKind::SolarizedDark),
|
||||
("adwaitaDark", ThemeKind::AdwaitaDark),
|
||||
("breezeDark", ThemeKind::BreezeDark),
|
||||
("softDark", ThemeKind::SoftDark),
|
||||
];
|
||||
|
||||
fn favorite_theme_groups(favorites: &[SavedTheme]) -> (Vec<ThemeKind>, Vec<ThemeKind>) {
|
||||
@@ -2089,7 +2093,12 @@ mod tests {
|
||||
fn breeze_themes_are_chosen_by_preference_and_fix_the_matching_mode() {
|
||||
let ctx = egui::Context::default();
|
||||
let mut app = PhotocraftApp::new(photocraft_engine::Session::new(), crate::Services::default());
|
||||
for (id, kind, mode) in [("breezeLight", ThemeKind::BreezeLight, AppearanceMode::Light), ("breezeDark", ThemeKind::BreezeDark, AppearanceMode::Dark)] {
|
||||
for (id, kind, mode) in [
|
||||
("breezeLight", ThemeKind::BreezeLight, AppearanceMode::Light),
|
||||
("breezeDark", ThemeKind::BreezeDark, AppearanceMode::Dark),
|
||||
("softLight", ThemeKind::SoftLight, AppearanceMode::Light),
|
||||
("softDark", ThemeKind::SoftDark, AppearanceMode::Dark),
|
||||
] {
|
||||
app.run("prefs.set", json!({"path": "interface.theme", "value": id})).unwrap();
|
||||
tick(&mut app, &ctx);
|
||||
let interface = app.session.prefs().interface.clone();
|
||||
@@ -2098,8 +2107,8 @@ mod tests {
|
||||
assert_eq!(app.ui.theme, kind, "{id}");
|
||||
}
|
||||
// The two slots are independent: choosing the dark one left the light choice alone.
|
||||
assert_eq!(app.session.prefs().interface.light_theme, LightTheme::BreezeLight);
|
||||
assert_eq!(app.session.prefs().interface.dark_theme, DarkTheme::BreezeDark);
|
||||
assert_eq!(app.session.prefs().interface.light_theme, LightTheme::SoftLight);
|
||||
assert_eq!(app.session.prefs().interface.dark_theme, DarkTheme::SoftDark);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+141
-2
@@ -48,10 +48,14 @@ pub enum ThemeKind {
|
||||
BreezeLight,
|
||||
/// An adaptation of KDE Plasma's Breeze Dark look.
|
||||
BreezeDark,
|
||||
/// A low-contrast dark theme: muted violet-grey surfaces, softened text and a periwinkle accent.
|
||||
SoftDark,
|
||||
/// A low-contrast light theme: pale violet-grey surfaces, softened dark text and a periwinkle accent.
|
||||
SoftLight,
|
||||
}
|
||||
|
||||
impl ThemeKind {
|
||||
pub const ALL: [ThemeKind; 12] = [
|
||||
pub const ALL: [ThemeKind; 14] = [
|
||||
ThemeKind::Pro,
|
||||
ThemeKind::ProMedium,
|
||||
ThemeKind::ProLightGray,
|
||||
@@ -64,6 +68,8 @@ impl ThemeKind {
|
||||
ThemeKind::AdwaitaDark,
|
||||
ThemeKind::BreezeLight,
|
||||
ThemeKind::BreezeDark,
|
||||
ThemeKind::SoftDark,
|
||||
ThemeKind::SoftLight,
|
||||
];
|
||||
pub fn label(self) -> &'static str {
|
||||
match self {
|
||||
@@ -79,6 +85,8 @@ impl ThemeKind {
|
||||
ThemeKind::SolarizedDark => "Solarized Dark",
|
||||
ThemeKind::BreezeLight => "Breeze Light",
|
||||
ThemeKind::BreezeDark => "Breeze Dark",
|
||||
ThemeKind::SoftDark => "Soft Dark",
|
||||
ThemeKind::SoftLight => "Soft Light",
|
||||
}
|
||||
}
|
||||
/// The canonical name: `ui.set {theme}` accepts it and the Window › Theme commands are
|
||||
@@ -97,6 +105,8 @@ impl ThemeKind {
|
||||
ThemeKind::SolarizedDark => "solarizedDark",
|
||||
ThemeKind::BreezeLight => "breezeLight",
|
||||
ThemeKind::BreezeDark => "breezeDark",
|
||||
ThemeKind::SoftDark => "softDark",
|
||||
ThemeKind::SoftLight => "softLight",
|
||||
}
|
||||
}
|
||||
/// Photoshop-grammar layout (tab strips, flat panels): the four Pro brightness levels.
|
||||
@@ -121,6 +131,8 @@ impl ThemeKind {
|
||||
"solarizeddark" | "solarized" => Some(ThemeKind::SolarizedDark),
|
||||
"breezelight" | "breeze" | "kde" | "kdelight" | "plasma" | "plasmalight" => Some(ThemeKind::BreezeLight),
|
||||
"breezedark" | "kdedark" | "plasmadark" => Some(ThemeKind::BreezeDark),
|
||||
"softdark" => Some(ThemeKind::SoftDark),
|
||||
"softlight" => Some(ThemeKind::SoftLight),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -602,6 +614,91 @@ impl Tokens {
|
||||
caption_close_text: Color32::WHITE,
|
||||
scrim: Color32::from_black_alpha(110),
|
||||
},
|
||||
// Soft Dark: our own low-contrast palette on the Studio layout. Muted violet-grey
|
||||
// surfaces (never near-black), softened off-white text and a periwinkle accent; the
|
||||
// text colours still hold 4.5:1 on every surface (checked by the tests).
|
||||
ThemeKind::SoftDark => Tokens {
|
||||
kind,
|
||||
chrome: Color32::from_rgb(58, 58, 68),
|
||||
canvas: Color32::from_rgb(40, 40, 48),
|
||||
canvas_dot: Color32::from_rgb(40, 40, 48),
|
||||
dock: Color32::from_rgb(47, 47, 56),
|
||||
card: Color32::from_rgb(54, 54, 64),
|
||||
card_border: Color32::from_rgb(70, 70, 82),
|
||||
field: Color32::from_rgb(64, 64, 75),
|
||||
field_border: Color32::from_rgb(84, 84, 98),
|
||||
hover: Color32::from_rgb(68, 68, 80),
|
||||
pressed: Color32::from_rgb(74, 74, 88),
|
||||
text: Color32::from_rgb(238, 238, 246),
|
||||
text_dim: Color32::from_rgb(212, 212, 224),
|
||||
text_faint: Color32::from_rgb(190, 190, 206),
|
||||
icon: Color32::from_rgb(214, 214, 226),
|
||||
accent: Color32::from_rgb(138, 154, 232),
|
||||
accent_soft: Color32::from_rgba_unmultiplied(138, 154, 232, 48),
|
||||
accent_border: Color32::from_rgba_unmultiplied(138, 154, 232, 150),
|
||||
accent_text: Color32::from_rgb(190, 200, 252),
|
||||
separator: Color32::from_rgb(36, 36, 44),
|
||||
shadow: Color32::from_black_alpha(110),
|
||||
primary_bg: Color32::from_rgb(86, 104, 196),
|
||||
primary_text: Color32::WHITE,
|
||||
danger: Color32::from_rgb(250, 160, 172),
|
||||
warning: Color32::from_rgb(240, 200, 130),
|
||||
radius_sm: 7.0,
|
||||
radius: 10.0,
|
||||
radius_lg: 14.0,
|
||||
bevel: false,
|
||||
pro: false,
|
||||
round_chips: true,
|
||||
tab_strip: Color32::TRANSPARENT,
|
||||
row_selected: Color32::TRANSPARENT,
|
||||
histogram_bg: Color32::from_rgb(40, 40, 48),
|
||||
histogram_level: 220,
|
||||
caption_close: Color32::from_rgb(200, 72, 86),
|
||||
caption_close_text: Color32::WHITE,
|
||||
scrim: Color32::from_black_alpha(100),
|
||||
},
|
||||
// Soft Light: pale violet-grey surfaces, softened dark-violet text (not black) and a
|
||||
// periwinkle accent, with the same generous radii as Soft Dark.
|
||||
ThemeKind::SoftLight => Tokens {
|
||||
kind,
|
||||
chrome: Color32::from_rgb(232, 230, 236),
|
||||
canvas: Color32::from_rgb(218, 216, 224),
|
||||
canvas_dot: Color32::from_rgb(218, 216, 224),
|
||||
dock: Color32::from_rgb(226, 224, 231),
|
||||
card: Color32::from_rgb(240, 238, 243),
|
||||
card_border: Color32::from_rgb(208, 205, 215),
|
||||
field: Color32::from_rgb(250, 249, 252),
|
||||
field_border: Color32::from_rgb(190, 187, 201),
|
||||
hover: Color32::from_rgb(228, 226, 234),
|
||||
pressed: Color32::from_rgb(212, 209, 221),
|
||||
text: Color32::from_rgb(44, 42, 56),
|
||||
text_dim: Color32::from_rgb(62, 60, 76),
|
||||
text_faint: Color32::from_rgb(80, 78, 96),
|
||||
icon: Color32::from_rgb(64, 62, 80),
|
||||
accent: Color32::from_rgb(110, 128, 216),
|
||||
accent_soft: Color32::from_rgba_unmultiplied(110, 128, 216, 44),
|
||||
accent_border: Color32::from_rgba_unmultiplied(110, 128, 216, 170),
|
||||
accent_text: Color32::from_rgb(46, 58, 146),
|
||||
separator: Color32::from_rgb(210, 207, 218),
|
||||
shadow: Color32::from_black_alpha(40),
|
||||
primary_bg: Color32::from_rgb(80, 98, 190),
|
||||
primary_text: Color32::WHITE,
|
||||
danger: Color32::from_rgb(166, 44, 62),
|
||||
warning: Color32::from_rgb(140, 72, 0),
|
||||
radius_sm: 7.0,
|
||||
radius: 10.0,
|
||||
radius_lg: 14.0,
|
||||
bevel: false,
|
||||
pro: false,
|
||||
round_chips: true,
|
||||
tab_strip: Color32::TRANSPARENT,
|
||||
row_selected: Color32::TRANSPARENT,
|
||||
histogram_bg: Color32::from_rgb(44, 42, 56),
|
||||
histogram_level: 240,
|
||||
caption_close: Color32::from_rgb(200, 60, 72),
|
||||
caption_close_text: Color32::WHITE,
|
||||
scrim: Color32::from_black_alpha(90),
|
||||
},
|
||||
// Ethan Schoonover's Solarized Dark palette: base03 canvas, base02 surfaces, base0
|
||||
// body text and the Solarized blue accent. https://ethanschoonover.com/solarized/
|
||||
// (MIT; see LICENSE-solarized.txt)
|
||||
@@ -714,7 +811,13 @@ impl Tokens {
|
||||
pub fn dark(&self) -> bool {
|
||||
matches!(
|
||||
self.kind,
|
||||
ThemeKind::Studio | ThemeKind::Pro | ThemeKind::ProMedium | ThemeKind::SolarizedDark | ThemeKind::AdwaitaDark | ThemeKind::BreezeDark
|
||||
ThemeKind::Studio
|
||||
| ThemeKind::Pro
|
||||
| ThemeKind::ProMedium
|
||||
| ThemeKind::SolarizedDark
|
||||
| ThemeKind::AdwaitaDark
|
||||
| ThemeKind::BreezeDark
|
||||
| ThemeKind::SoftDark
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1168,6 +1271,42 @@ mod tests {
|
||||
assert_eq!(ThemeKind::from_name("gnome dark"), Some(ThemeKind::AdwaitaDark));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn soft_themes_parse_and_hold_text_contrast() {
|
||||
for (kind, alias) in [(ThemeKind::SoftDark, "soft dark"), (ThemeKind::SoftLight, "Soft_Light")] {
|
||||
assert_eq!(ThemeKind::from_name(kind.id()), Some(kind));
|
||||
assert_eq!(ThemeKind::from_name(kind.label()), Some(kind));
|
||||
assert_eq!(ThemeKind::from_name(alias), Some(kind));
|
||||
assert!(ThemeKind::ALL.contains(&kind));
|
||||
let t = Tokens::for_kind(kind);
|
||||
assert_eq!(t.kind, kind);
|
||||
assert_eq!(t.dark(), kind == ThemeKind::SoftDark);
|
||||
assert!(!t.pro && !t.bevel);
|
||||
// Soft means no extremes: surfaces stay off black and white.
|
||||
for c in [t.chrome, t.canvas, t.dock, t.card, t.field] {
|
||||
let (hi, lo) = (c.r().max(c.g()).max(c.b()), c.r().min(c.g()).min(c.b()));
|
||||
assert!(if t.dark() { (32..96).contains(&hi) } else { hi < 255 && lo > 200 }, "{kind:?}: {c:?}");
|
||||
}
|
||||
let tint = |under: Color32| {
|
||||
let k = f32::from(t.accent_soft.a()) / 255.0;
|
||||
let ch = |over: u8, under: u8| (f32::from(over) * k + f32::from(under) * (1.0 - k)).round() as u8;
|
||||
Color32::from_rgb(ch(t.accent.r(), under.r()), ch(t.accent.g(), under.g()), ch(t.accent.b(), under.b()))
|
||||
};
|
||||
let surfaces = [t.chrome, t.canvas, t.dock, t.card, t.field, t.hover, t.pressed, tint(t.dock), tint(t.card)];
|
||||
for (i, surface) in surfaces.into_iter().enumerate() {
|
||||
for (name, colour) in [("text", t.text), ("dim", t.text_dim), ("faint", t.text_faint), ("accent text", t.accent_text)] {
|
||||
assert!(contrast(colour, surface) >= 4.5, "{kind:?}: {name} on {surface:?}");
|
||||
}
|
||||
// Status colours are marks on hovered or selected surfaces (3:1), text on resting ones.
|
||||
let floor = if i < 5 { 4.5 } else { 3.0 };
|
||||
assert!(contrast(t.danger, surface) >= floor && contrast(t.warning, surface) >= floor, "{kind:?}: status on {surface:?}");
|
||||
}
|
||||
for k in [1.0, 0.93, 0.85] {
|
||||
assert!(contrast(t.primary_text, t.primary_bg.gamma_multiply(k)) >= 4.5, "{kind:?}: primary at {k}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn breeze_themes_parse_and_meet_text_contrast() {
|
||||
for (kind, aliases) in [
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ Generated by `cargo xtask scorecard` from `perf/budgets.toml`, `perf/baseline.js
|
||||
| [Performance](#performance) | 3 | 22 | 22 | 56 scenarios: 3 within budget, 22 over budget or failing, 9 tracked only, 17 not run, 5 not measurable yet (baseline 2026-10-05) |
|
||||
| [File compatibility](#file-compatibility) | 6 | 13 | 8 | io corpus: oracle ≥ 146/170, round trip ≥ 169/170; psd-tools corpus: oracle ≥ 236/309, round trip ≥ 308/309; photoshop oracles: oracle ≥ 133/256, round trip ≥ 258/256 |
|
||||
| [Tools](#tools) | 28 | 14 | 1 | |
|
||||
| [Workspace and UI](#workspace-and-ui) | 13 | 5 | 5 | settings that do nothing: 26 of 156 (target 0) |
|
||||
| [Workspace and UI](#workspace-and-ui) | 13 | 6 | 4 | settings that do nothing: 26 of 156 (target 0) |
|
||||
| [Type](#type) | 4 | 2 | 3 | |
|
||||
| [Automation](#automation) | 5 | 2 | 4 | 631 of 631 menu items live (100.0%) |
|
||||
| [Reliability](#reliability) | 5 | 2 | 0 | never-crash lints on 28 of 28 crates; panic_hunt present |
|
||||
@@ -192,7 +192,7 @@ Unread: `general.color_picker`, `general.animated_zoom`, `general.zoom_resizes_w
|
||||
| UI-217-8 | Tool-specific canvas right-click menu; right-click spelling suggestions in text fields | partial | [#217](https://github.com/storytold/photocraft/issues/217) | paint_mouse.rs brush picker and canvas_tool_menu.rs selection actions; text_field_menu.rs gives every text field its right-click Undo/Cut/Copy/Paste/Delete/Select All menu (#2921); other tools missing; the Type tool's edit menu starts with spelling suggestions and Add to Dictionary over a misspelled word (#4370); Ignore / Ignore All, suggestions in the non-editing type menu and in generic text fields are not there yet |
|
||||
| UI-217-9 | UI scale presets 100/115/125/150/200 % through zoom_factor, remembered | done | [#217](https://github.com/storytold/photocraft/issues/217) | prefs::UiScale offers Auto plus 75 to 300 percent (13 presets, more than the five this row lists); prefs_ui.rs display_scale resolves it (Auto follows the system scale, fractional and unscaled 4K included, #1072/#1278) and sync_display_scale applies it with ctx.set_zoom_factor. It is a preference, so it is remembered. The zoom_factor assert in input_tests.rs is about canvas zoom not scaling the interface, not about this setting |
|
||||
| UI-217-10 | Appearance: animation toggle, canvas pattern toggle per theme, start-screen recents | partial | [#217](https://github.com/storytold/photocraft/issues/217) | interface.enableAnimations (prefs_ui.rs sync_animations, #3273) zeroes egui animation_time and scroll animation in both styles; the Home screen lists recent files (canvas.rs home_recent, #2691); no canvas pattern toggle yet (#2264) and no option to hide the Home screen recents |
|
||||
| UI-217-11 | Soft dark and soft light themes with picker previews; outlined knobs in light themes | missing | [#217](https://github.com/storytold/photocraft/issues/217) | theme.rs ThemeKind has twelve themes (the four Pro brightness levels, Studio and Studio Light, Classic, Adwaita and Adwaita Dark, Solarized Dark, Breeze Light and Breeze Dark); no Soft dark or Soft light, and no outlined knobs in the light themes |
|
||||
| UI-217-11 | Soft dark and soft light themes with picker previews; outlined knobs in light themes | partial | [#217](https://github.com/storytold/photocraft/issues/217) | theme.rs ThemeKind has fourteen themes: the twelve earlier ones plus Soft Dark and Soft Light (low-contrast Studio-layout palettes, text at 4.5:1, selectable in Preferences with the existing theme-card previews, ui.set {theme}, Window > Theme); outlined knobs in the light themes are still missing |
|
||||
| UI-217-12 | Save custom New Document presets; a real Recent category | done | [#217](https://github.com/storytold/photocraft/issues/217) | document_preset_cmds.rs and new_doc_ui.rs: named presets save/select/delete through preferences; Recent lists Clipboard, the last 20 documents created in the dialog (document.recent.*, persisted with the presets, newest first, deduplicated) and the uncovered defaults (#3131); created_documents_fill_the_recent_category |
|
||||
| UI-217-13 | Numeric fields accept arithmetic and units; scrubby labels; double-click resets a slider | done | [#217](https://github.com/storytold/photocraft/issues/217) | widgets::value_field: arithmetic plus units (1in, 5mm, 72pt, 50%) converted at the document resolution (#3099); widgets::scrub_label makes options-bar, slider-row and Character labels scrubby (Shift x10, Alt fine); slider rows with a known default reset on double-click |
|
||||
| UI-217-14 | Layers panel: wheel steps blend modes, drop on New/Trash duplicates/deletes, checker behind art | done | [#217](https://github.com/storytold/photocraft/issues/217) | panels.rs: the wheel steps the blend-mode dropdown (widgets::dropdown_wheel_hovered, #1747); rows dropped on New Layer/Group/Delete duplicate/group/delete them (footer_drop, #736), effect rows dropped on Delete delete that effect or the whole style (fx_trash_drop, #3102); pixel thumbnails draw widgets::checker behind the art; Shift+Plus/Minus step the layer's or the painting tool's blend mode (blend_keys.rs, #3102) |
|
||||
|
||||
+3
-1
@@ -14,6 +14,8 @@
|
||||
| Solarized Dark | Ethan Schoonover's Solarized palette: base03 canvas, base02 panels, base0 text, Solarized blue accent |
|
||||
| Breeze Light | An adaptation of KDE Plasma's Breeze Light on the Studio layout: cool light blue-grey surfaces a few levels from Breeze's so the app blends into a Plasma desktop, Breeze-style blue accent, 5 px radii, red close button on hover; text and status colours are darker than Breeze's for contrast |
|
||||
| Breeze Dark | The same for Breeze Dark: cool dark blue-grey surfaces (darkest canvas, then dock, cards, chrome and controls) near Breeze's, blue accent, 5 px radii; secondary text and status colours are lighter than Breeze's for contrast |
|
||||
| Soft Dark | Low-contrast dark on the Studio layout: muted violet-grey surfaces (never near-black), softened off-white text, periwinkle accent, generous 7/10/14 px radii; text still holds 4.5:1 on every surface (`theme.rs` tests) |
|
||||
| Soft Light | The light counterpart: pale violet-grey surfaces, softened dark-violet text instead of black, periwinkle accent, the same radii |
|
||||
|
||||
Both Breeze themes are our own values close to Plasma's (not a copy of its colour schemes) and put text readability first: every text colour holds 4.5:1 on every surface and on hovered, pressed and selected states (`theme.rs` tests), so their secondary text, accent and status colours are darker (Light) or lighter (Dark) than Breeze's own, and the primary button is a deeper blue with white text.
|
||||
|
||||
@@ -21,7 +23,7 @@ Edit → Preferences → Interface shows an Appearance Mode selector (Sync with
|
||||
|
||||
Window → Theme and macOS PhotoCraft → Appearance also offer Sync with system. Its checkmark follows the saved Auto mode rather than the displayed palette; choosing a manual theme selects its fixed appearance mode.
|
||||
|
||||
The toolbar's foreground and background colour chips follow the `Tokens::round_chips` flag. Pro and Classic leave it off and draw Photoshop's overlapping squares with Default Colors and Switch Colors above them. Studio, Studio Light, Solarized Dark, Adwaita, Adwaita Dark, Breeze Light and Breeze Dark (the Studio-layout themes) turn it on and draw large round chips with a curved Switch Colors arrow centred right under them, its heads touching the chips: stacked vertically in one tool column, with Default Colors at their top-right, and side by side in two tool columns, with Default Colors at the toolbar's left edge. Another non-Pro theme can opt in by setting the flag.
|
||||
The toolbar's foreground and background colour chips follow the `Tokens::round_chips` flag. Pro and Classic leave it off and draw Photoshop's overlapping squares with Default Colors and Switch Colors above them. Studio, Studio Light, Solarized Dark, Adwaita, Adwaita Dark, Breeze Light, Breeze Dark, Soft Dark and Soft Light (the Studio-layout themes) turn it on and draw large round chips with a curved Switch Colors arrow centred right under them, its heads touching the chips: stacked vertically in one tool column, with Default Colors at their top-right, and side by side in two tool columns, with Default Colors at the toolbar's left edge. Another non-Pro theme can opt in by setting the flag.
|
||||
|
||||
## Rules
|
||||
|
||||
|
||||
+2
-2
@@ -78,9 +78,9 @@ note = "interface.enableAnimations (prefs_ui.rs sync_animations, #3273) zeroes e
|
||||
[[item]]
|
||||
id = "UI-217-11"
|
||||
target = "Soft dark and soft light themes with picker previews; outlined knobs in light themes"
|
||||
status = "missing"
|
||||
status = "partial"
|
||||
issue = 217
|
||||
note = "theme.rs ThemeKind has twelve themes (the four Pro brightness levels, Studio and Studio Light, Classic, Adwaita and Adwaita Dark, Solarized Dark, Breeze Light and Breeze Dark); no Soft dark or Soft light, and no outlined knobs in the light themes"
|
||||
note = "theme.rs ThemeKind has fourteen themes: the twelve earlier ones plus Soft Dark and Soft Light (low-contrast Studio-layout palettes, text at 4.5:1, selectable in Preferences with the existing theme-card previews, ui.set {theme}, Window > Theme); outlined knobs in the light themes are still missing"
|
||||
|
||||
[[item]]
|
||||
id = "UI-217-12"
|
||||
|
||||
Reference in new issue
Block a user