Merge pull request #8391 from horizon-rust/feat/launcher-glass-accent

🤖 Agent: the accent now takes any #rrggbb as well as the named swatches, and a typed colour survives settings.json (test a_typed_colour_is_worn_as_is_and_survives_settings_json).
This commit is contained in:
Ludovic Marie authored and GitHub committed 2026-11-03 18:06:22 +01:00
commit 49a6e02ee3
28 files changed
+307 -44

No files matched your search

+1 -1
View File
@@ -222,7 +222,7 @@ MCP: `account_skin`.
## `horizon-launcher settings`
Show the launcher's settings, or set one (the Settings page): selected-version TAG, minecraft-dir PATH, server-dir PATH ("default" resets a folder), install-new-versions on|off, prune-old-versions on|off (delete old versions after an install, keeping the selected one, the ones instances use and the newest few), keep-versions N (how many of the newest to keep; "default" is 2), opening-animation on|off, proxy http://host:port ("default" removes it), while-playing stay|minimize|hide (what the window does while the game runs), language system|en|fr|ru|de|zh|es|uk|pl (the window's language; system follows the computer's), accent violet|blue|teal|green|amber|red|pink, background PATH (a picture behind Home; "default" shows Horizon's landscape), native-profile performance|dev|release (the profile native mods build with: performance is incremental, dev rebuilds a mod quickest for a slow game, release is slowest to build and slightly faster to play).
Show the launcher's settings, or set one (the Settings page): selected-version TAG, minecraft-dir PATH, server-dir PATH ("default" resets a folder), install-new-versions on|off, prune-old-versions on|off (delete old versions after an install, keeping the selected one, the ones instances use and the newest few), keep-versions N (how many of the newest to keep; "default" is 2), opening-animation on|off, proxy http://host:port ("default" removes it), while-playing stay|minimize|hide (what the window does while the game runs), language system|en|fr|ru|de|zh|es|uk|pl (the window's language; system follows the computer's), accent violet|blue|teal|green|amber|red|pink|#rrggbb|DEGREES (a named accent, any colour, or any hue), background PATH (a picture behind Home; "default" shows Horizon's landscape), native-profile performance|dev|release (the profile native mods build with: performance is incremental, dev rebuilds a mod quickest for a slow game, release is slowest to build and slightly faster to play).
horizon-launcher settings [KEY] [VALUE]
+8 -6
View File
@@ -153,12 +153,14 @@ The opening, then Play ([clip](docs/opening.webm)):
[NAME]`, `account select <name>` (an account or an offline name), `account forget-offline
<name>`.
- **Accent and Home background**: on top of Dark, Light or System, Settings > Launcher picks an
accent (Horizon's violet, blue, teal, green, amber, red or pink) and a picture of the player's
own behind Home (PNG, JPEG or WebP, cropped to fill; **Landscape** brings Horizon's back). An
accent turns the violet tokens to its hue, then darkens or lightens them just enough to keep
white on the Play button at 4.5:1 and the accent at 3:1 on every background (a test holds every
accent in both looks to it). The picture is copied into the data folder.
`horizon-launcher settings accent NAME`, `settings background PATH|default`.
accent (Horizon's violet, blue, teal, green, amber, red or pink, any hue, or any colour typed as
hex in the dark look) and a picture of the player's own behind Home (PNG, JPEG or WebP, cropped
to fill; **Landscape** brings Horizon's back). The dark look wears the colour as is, as the
mockup does; in the light look an accent turns the violet tokens to its hue, then darkens or
lightens them just enough to keep white on the Play button at 4.5:1 and the accent at 3:1 on
every background (a test holds every accent in both looks to it). The picture is copied into the
data folder. `horizon-launcher settings accent NAME|#rrggbb|DEGREES`,
`settings background PATH|default`.
- **Language**: English, French, Russian, German, Simplified Chinese, Spanish, Ukrainian and Polish.
The window follows the system's language unless the player picks one in Settings > Launcher
(each named in itself); a string a table lacks shows in English. The tables are
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "من 3 إلى 16 حرفًا أو رقمًا أو شرطة سفلية. يمكنك تبديل الأسماء من الشريط العلوي أيضًا. الخوادم عبر الإنترنت لا تقبل الأسماء دون اتصال.",
"Play online with your Microsoft account": "العب عبر الإنترنت بحساب Microsoft",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "سجّل الدخول بالحساب الذي يملك Minecraft: Java Edition. يرافقك اسمك ومظهرك إلى الخوادم عبر الإنترنت، كما في المشغلات الأخرى.",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "تُفتح صفحة Microsoft نفسها في متصفحك. لا يرى Horizon كلمة مرورك أبدًا."
"Microsoft's own page opens in your browser. Horizon never sees your password.": "تُفتح صفحة Microsoft نفسها في متصفحك. لا يرى Horizon كلمة مرورك أبدًا.",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "يلوّن الأزرار والمفاتيح والسماء. اختر لونًا، أو اكتب أي لون بصيغة hex."
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "3 bis 16 Buchstaben, Ziffern oder Unterstriche. Namen wechselst du auch in der oberen Leiste. Online-Server nehmen keine Offline-Namen an.",
"Play online with your Microsoft account": "Mit deinem Microsoft-Konto online spielen",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "Melde dich mit dem Konto an, dem Minecraft: Java Edition gehört. Dein Name und Skin kommen mit auf Online-Server, wie in anderen Launchern.",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsofts eigene Seite öffnet sich in deinem Browser. Horizon sieht dein Passwort nie."
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsofts eigene Seite öffnet sich in deinem Browser. Horizon sieht dein Passwort nie.",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "Färbt die Schaltflächen, die Schalter und den Himmel. Wähle eine, oder tippe eine beliebige Hex-Farbe."
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "De 3 a 16 letras, números o guiones bajos. También puedes cambiar de nombre en la barra superior. Los servidores en línea no aceptan nombres sin conexión.",
"Play online with your Microsoft account": "Juega en línea con tu cuenta de Microsoft",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "Inicia sesión con la cuenta que tiene Minecraft: Java Edition. Tu nombre y tu skin te acompañan a los servidores en línea, como en otros launchers.",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "La propia página de Microsoft se abre en tu navegador. Horizon nunca ve tu contraseña."
"Microsoft's own page opens in your browser. Horizon never sees your password.": "La propia página de Microsoft se abre en tu navegador. Horizon nunca ve tu contraseña.",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "Colorea los botones, los interruptores y el cielo. Elige uno, o escribe cualquier color hex."
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "3 à 16 lettres, chiffres ou tirets bas. Change aussi de nom depuis la barre du haut. Les serveurs en ligne n'acceptent pas les noms hors ligne.",
"Play online with your Microsoft account": "Joue en ligne avec ton compte Microsoft",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "Connecte-toi avec le compte qui possède Minecraft: Java Edition. Ton nom et ton skin te suivent sur les serveurs en ligne, comme dans les autres launchers.",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "La page de Microsoft s'ouvre dans ton navigateur. Horizon ne voit jamais ton mot de passe."
"Microsoft's own page opens in your browser. Horizon never sees your password.": "La page de Microsoft s'ouvre dans ton navigateur. Horizon ne voit jamais ton mot de passe.",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "Colore les boutons, les interrupteurs et le ciel. Choisis-en une, ou tape n'importe quelle couleur hex."
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "3 से 16 अक्षर, अंक या अंडरस्कोर। नाम ऊपर की पट्टी से भी बदलें। ऑनलाइन सर्वर ऑफ़लाइन नाम नहीं लेते।",
"Play online with your Microsoft account": "अपने Microsoft खाते से ऑनलाइन खेलें",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "उस खाते से साइन इन करें जिसके पास Minecraft: Java Edition है। आपका नाम और स्किन ऑनलाइन सर्वरों पर साथ आते हैं, दूसरे लॉन्चरों की तरह।",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsoft का अपना पेज आपके ब्राउज़र में खुलता है। Horizon आपका पासवर्ड कभी नहीं देखता।"
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsoft का अपना पेज आपके ब्राउज़र में खुलता है। Horizon आपका पासवर्ड कभी नहीं देखता।",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "बटन, स्विच और आसमान को रंगता है। एक चुनें, या कोई भी hex रंग लिखें।"
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "3 do 16 slova, brojeva ili podvlaka. Imena mijenjaj i na gornjoj traci. Mrežni poslužitelji ne primaju imena izvan mreže.",
"Play online with your Microsoft account": "Igraj na mreži sa svojim Microsoft računom",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "Prijavi se računom koji posjeduje Minecraft: Java Edition. Tvoje ime i skin idu s tobom na mrežne poslužitelje, kao u drugim pokretačima.",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsoftova stranica otvara se u tvom pregledniku. Horizon nikad ne vidi tvoju lozinku."
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsoftova stranica otvara se u tvom pregledniku. Horizon nikad ne vidi tvoju lozinku.",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "Boja gumbe, prekidače i nebo. Odaberi jednu ili upiši bilo koju hex boju."
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "3 sampai 16 huruf, angka, atau garis bawah. Ganti nama juga dari bilah atas. Server online tidak menerima nama offline.",
"Play online with your Microsoft account": "Main online dengan akun Microsoft-mu",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "Masuk dengan akun yang memiliki Minecraft: Java Edition. Nama dan skin-mu ikut ke server online, seperti di launcher lain.",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Halaman Microsoft sendiri terbuka di browser-mu. Horizon tidak pernah melihat kata sandimu."
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Halaman Microsoft sendiri terbuka di browser-mu. Horizon tidak pernah melihat kata sandimu.",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "Mewarnai tombol, sakelar, dan langit. Pilih satu, atau ketik warna hex apa pun."
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "3〜16 文字の英数字またはアンダースコア。名前は上部のバーからも切り替えられます。オンラインサーバーではオフラインの名前は使えません。",
"Play online with your Microsoft account": "Microsoft アカウントでオンラインプレイ",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "Minecraft: Java Edition を所有するアカウントでサインインしてください。名前とスキンは、ほかのランチャーと同じようにオンラインサーバーでも使われます。",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsoft 自身のページがブラウザで開きます。Horizon がパスワードを見ることはありません。"
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsoft 自身のページがブラウザで開きます。Horizon がパスワードを見ることはありません。",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "ボタン、スイッチ、空に色を付けます。選ぶか、任意の16進カラーを入力してください。"
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "영문자, 숫자, 밑줄 3~16자. 위쪽 막대에서도 이름을 바꿀 수 있습니다. 온라인 서버는 오프라인 이름을 받지 않습니다.",
"Play online with your Microsoft account": "Microsoft 계정으로 온라인 플레이",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "Minecraft: Java Edition을 소유한 계정으로 로그인하세요. 다른 런처처럼 이름과 스킨이 온라인 서버에서도 함께합니다.",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsoft의 자체 페이지가 브라우저에서 열립니다. Horizon은 비밀번호를 절대 보지 않습니다."
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsoft의 자체 페이지가 브라우저에서 열립니다. Horizon은 비밀번호를 절대 보지 않습니다.",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "버튼, 스위치, 하늘에 색을 입힙니다. 하나를 고르거나 원하는 hex 색을 입력하세요."
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "Od 3 do 16 liter, cyfr lub podkreśleń. Nazwę zmienisz też na górnym pasku. Serwery online nie przyjmują nazw offline.",
"Play online with your Microsoft account": "Graj online na koncie Microsoft",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "Zaloguj się kontem, które ma Minecraft: Java Edition. Twoja nazwa i skin trafią z tobą na serwery online, jak w innych launcherach.",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Strona Microsoftu otwiera się w przeglądarce. Horizon nigdy nie widzi twojego hasła."
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Strona Microsoftu otwiera się w przeglądarce. Horizon nigdy nie widzi twojego hasła.",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "Koloruje przyciski, przełączniki i niebo. Wybierz jeden albo wpisz dowolny kolor hex."
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "De 3 a 16 letras, números ou sublinhados. Troque de nome também pela barra superior. Servidores online não aceitam nomes offline.",
"Play online with your Microsoft account": "Jogue online com sua conta Microsoft",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "Entre com a conta que tem o Minecraft: Java Edition. Seu nome e sua skin vão junto para os servidores online, como em outros launchers.",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "A própria página da Microsoft abre no seu navegador. O Horizon nunca vê sua senha."
"Microsoft's own page opens in your browser. Horizon never sees your password.": "A própria página da Microsoft abre no seu navegador. O Horizon nunca vê sua senha.",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "Colore os botões, os interruptores e o céu. Escolha uma, ou digite qualquer cor hex."
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "De 3 a 16 letras, números ou sublinhados. Mude de nome também na barra superior. Os servidores online não aceitam nomes offline.",
"Play online with your Microsoft account": "Joga online com a tua conta Microsoft",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "Inicia sessão com a conta que tem o Minecraft: Java Edition. O teu nome e a tua skin vão contigo para os servidores online, como noutros launchers.",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "A própria página da Microsoft abre no teu navegador. O Horizon nunca vê a tua palavra-passe."
"Microsoft's own page opens in your browser. Horizon never sees your password.": "A própria página da Microsoft abre no teu navegador. O Horizon nunca vê a tua palavra-passe.",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "Dá cor aos botões, aos interruptores e ao céu. Escolhe uma, ou escreve qualquer cor hex."
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "От 3 до 16 букв, цифр или подчёркиваний. Имя можно сменить и в верхней панели. Онлайн-серверы не принимают офлайн-имена.",
"Play online with your Microsoft account": "Играйте онлайн с учётной записью Microsoft",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "Войдите в учётную запись, которой принадлежит Minecraft: Java Edition. Имя и скин будут с вами на онлайн-серверах, как в других лаунчерах.",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Страница Microsoft открывается в вашем браузере. Horizon никогда не видит ваш пароль."
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Страница Microsoft открывается в вашем браузере. Horizon никогда не видит ваш пароль.",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "Окрашивает кнопки, переключатели и небо. Выберите цвет или введите любой hex-цвет."
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "3 ila 16 harf, rakam veya alt çizgi. Adları üst çubuktan da değiştirebilirsin. Çevrim içi sunucular çevrim dışı adları kabul etmez.",
"Play online with your Microsoft account": "Microsoft hesabınla çevrim içi oyna",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "Minecraft: Java Edition'a sahip hesapla giriş yap. Adın ve görünümün, diğer başlatıcılarda olduğu gibi çevrim içi sunuculara da gelir.",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsoft'un kendi sayfası tarayıcında açılır. Horizon parolanı asla görmez."
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsoft'un kendi sayfası tarayıcında açılır. Horizon parolanı asla görmez.",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "Düğmeleri, anahtarları ve gökyüzünü renklendirir. Birini seç ya da herhangi bir hex rengi yaz."
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "Від 3 до 16 літер, цифр або підкреслень. Ім'я можна змінити й у верхній панелі. Онлайн-сервери не приймають офлайн-імена.",
"Play online with your Microsoft account": "Грайте онлайн з обліковим записом Microsoft",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "Увійдіть в обліковий запис, якому належить Minecraft: Java Edition. Ім'я та скін будуть з вами на онлайн-серверах, як в інших лаунчерах.",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Сторінка Microsoft відкривається у вашому браузері. Horizon ніколи не бачить ваш пароль."
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Сторінка Microsoft відкривається у вашому браузері. Horizon ніколи не бачить ваш пароль.",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "Фарбує кнопки, перемикачі та небо. Виберіть колір або введіть будь-який hex-колір."
}
+2 -1
View File
@@ -1033,5 +1033,6 @@
"3 to 16 letters, numbers or underscores. Switch names from the top bar too. Online servers don't take offline names.": "3 到 16 个字母、数字或下划线。也可以在顶栏切换名称。在线服务器不接受离线名称。",
"Play online with your Microsoft account": "用 Microsoft 账户在线游玩",
"Sign in with the account that owns Minecraft: Java Edition. Your name and skin come along to online servers, like in other launchers.": "使用拥有 Minecraft: Java Edition 的账户登录。和其他启动器一样,你的名称和皮肤会随你进入在线服务器。",
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsoft 自己的页面会在浏览器中打开。Horizon 永远看不到你的密码。"
"Microsoft's own page opens in your browser. Horizon never sees your password.": "Microsoft 自己的页面会在浏览器中打开。Horizon 永远看不到你的密码。",
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.": "为按钮、开关和天空着色。选一个,或输入任意十六进制颜色。"
}
+3 -2
View File
@@ -835,7 +835,8 @@ enum Cmd {
/// newest to keep; "default" is 2), opening-animation on|off, proxy http://host:port
/// ("default" removes it), while-playing stay|minimize|hide (what the window does while the game runs), language
/// system|en|fr|ru|de|zh|es|uk|pl (the window's language; system follows the computer's), accent
/// violet|blue|teal|green|amber|red|pink, background PATH (a picture behind Home; "default"
/// violet|blue|teal|green|amber|red|pink|#rrggbb|DEGREES (a named accent, any colour, or any
/// hue), background PATH (a picture behind Home; "default"
/// shows Horizon's landscape), native-profile performance|dev|release (the profile native
/// mods build with: performance is incremental, dev rebuilds a mod quickest for a slow game,
/// release is slowest to build and slightly faster to play)
@@ -1039,7 +1040,7 @@ fn set_setting(s: &mut backend::Settings, key: &str, value: &str) -> Result<(),
s.accent = Accent::parse(value).ok_or_else(|| {
let names: Vec<_> = Accent::ALL.iter().map(|a| a.name()).collect();
format!(
"{value}: say {} or a hue in degrees (0 to 360)",
"{value}: say {}, a colour like #2563eb or a hue in degrees (0 to 360)",
names.join(", ")
)
})?
+10
View File
@@ -60,6 +60,16 @@ pub fn set_backdrop(model: &Model) {
});
}
/// The player's accent (and so the plates' tint) changed: pieces are painted again.
pub fn set_look(look: Glass) {
PIECES.with_borrow_mut(|p| {
if p.look != look {
p.look = look;
p.generation += 1;
}
});
}
/// The launcher's pages are being built: pieces nothing drew in the last build are freed (call
/// at the start of the launcher's render).
pub fn begin_build(window: &mut Window) {
+1 -1
View File
@@ -186,7 +186,7 @@ impl Launcher {
Some(b.appearance),
&t,
))
.child(self.accent_row(cx))
.child(self.glass_accent_row(cx))
.child(self.home_background_row(cx))
.child(self.language_row(cx))
.child(self.while_playing_row(n, cx))
+5
View File
@@ -78,6 +78,11 @@ pub fn set_on(on: bool) {
ON.set(on);
}
/// Set as each frame's pages start being built: the colours for the player's accent.
pub fn set_look(look: Glass) {
LOOK.set(look);
}
/// The look's colours.
pub fn look() -> Glass {
LOOK.get()
+6
View File
@@ -257,6 +257,8 @@ struct Launcher {
native_log: native_log_page::NativeLog,
confirm_remove: Option<String>,
picker_open: bool,
/// Settings' any-colour swatch shows the hue strip (the glass look).
accent_strip: bool,
/// Where the accent picker's hue strip is on screen (`themes_page.rs`), to map a click to a
/// hue.
hue_area: std::rc::Rc<std::cell::Cell<Option<Bounds<gpui::Pixels>>>>,
@@ -556,6 +558,7 @@ impl Launcher {
native_log: Default::default(),
confirm_remove: None,
picker_open: false,
accent_strip: false,
hue_area: std::rc::Rc::new(std::cell::Cell::new(None)),
hue_dragging: false,
language_menu: false,
@@ -4877,6 +4880,9 @@ impl Render for Launcher {
self.theme = self.settings_theme(window);
}
glass_ui::set_on(self.theme.dark);
self.look = self.theme.look;
glass_ui::set_look(self.look);
glass::set_look(self.look);
self.scale = window.scale_factor();
self.perf.frame();
if self.bench.as_ref().is_some_and(perf::Bench::scrolling) {
+14 -2
View File
@@ -61,10 +61,12 @@ pub(super) enum Field {
/// Settings' new offline name (`switch_page.rs`).
OfflineName,
DeleteServer,
/// Settings' accent as hex (`themes_page.rs`).
AccentHex,
}
impl Field {
const ALL: [Field; 23] = [
const ALL: [Field; 24] = [
Field::Name,
Field::Seed,
Field::Players,
@@ -88,6 +90,7 @@ impl Field {
Field::MemoryLimit,
Field::OfflineName,
Field::DeleteServer,
Field::AccentHex,
];
}
@@ -966,6 +969,14 @@ impl Launcher {
.bg(crate::glass_ui::white(if focused { 0.1 } else { 0.06 }))
},
)
// The accent's hex: `h-9 w-[96px] rounded-[10px] px-3 text-[13px] font-medium`.
.when(f == Field::AccentHex, |d| {
d.h(px(36.))
.px(px(12.))
.rounded(px(10.))
.text_size(px(13.))
.font_weight(FontWeight::MEDIUM)
})
// The friend field sits in its chip, on the chip's fill.
.when(matches!(f, Field::Players | Field::OfflineName), |d| {
d.h_full().px(px(14.)).bg(gpui::transparent_black())
@@ -1098,7 +1109,8 @@ impl Launcher {
| Field::InstanceVersion
| Field::InstanceModsFilter
| Field::ConsoleSearch
| Field::DeleteServer => {}
| Field::DeleteServer
| Field::AccentHex => {}
Field::PreLaunch | Field::Wrapper | Field::LaunchEnv | Field::PostExit => {
self.save_launch()
}
+8 -4
View File
@@ -39,6 +39,8 @@ pub const STAGGER: Duration = tokens::STAGGER;
#[derive(Clone, Copy)]
pub struct Theme {
/// The glass look's colours for the accent (the dark appearance draws it).
pub look: tokens::glass::Glass,
/// The page behind everything the sky doesn't cover.
pub page: Hsla,
/// Hover plates, fields and the console: the raised panel colour.
@@ -112,7 +114,7 @@ impl Theme {
let night = if dark { night::NIGHT } else { night::DAWN };
let theme = Self::from(accent.apply(night.blocks()), night);
if dark {
theme.glass()
theme.glass(accent)
} else {
theme
}
@@ -121,11 +123,11 @@ impl Theme {
/// The dark appearance draws the glass look ("Glass" in `docs/design-system.md`): what the
/// pages not rebuilt for it yet draw with these tokens comes out as the mockup's parts do,
/// white text and fills at a few opacities, no borders, no bevels.
fn glass(self) -> Self {
use horizon_theme::glass::{fill, ink, Glass, DANGER, LIVE, WARN};
fn glass(self, accent: Accent) -> Self {
use horizon_theme::glass::{fill, ink, Glass, BLUR, DANGER, LIVE, TINT, WARN};
let white = |a: f32| gpui::white().opacity(a);
let none = gpui::transparent_black();
let look = Glass::default_look();
let look = Glass::new(accent.rgb(), TINT, BLUR);
Self {
plate_soft: white(fill::ROW_HOVER),
plate: white(fill::BUTTON),
@@ -150,12 +152,14 @@ impl Theme {
face_bevel: none,
face_lip: none,
on_face: c(look.on_accent),
look,
..self
}
}
fn from(p: tokens::Blocks, night: Night) -> Self {
Self {
look: tokens::glass::Glass::default_look(),
page: c(p.page),
plate_soft: c(p.raised),
plate: c(p.button),
+82 -10
View File
@@ -10,24 +10,52 @@ use horizon_theme::{contrast, Blocks};
use std::path::{Path, PathBuf};
/// An accent: a hue in degrees, or none for Horizon's violet, whose tokens are the palette's own
/// and stay untouched.
/// and stay untouched; and for a colour typed as hex, that colour (the glass look wears it as
/// is, the light look takes its hue).
#[derive(Clone, Copy, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(try_from = "String", into = "String")]
pub struct Accent {
hue: Option<f32>,
rgb: Option<u32>,
}
/// The glass look's colour for each of [`Accent::ALL`]: the mockup's swatches.
pub const SWATCHES: [u32; 7] = [
0x6b4bff, 0x2563eb, 0x0f766e, 0x15803d, 0xc2410c, 0xdc2626, 0xbe185d,
];
/// [`Accent::ALL`]'s names, in its order.
const NAMES: [&str; 7] = ["violet", "blue", "teal", "green", "amber", "red", "pink"];
impl Accent {
pub const VIOLET: Accent = Accent { hue: None };
pub const BLUE: Accent = Accent { hue: Some(212.) };
pub const TEAL: Accent = Accent { hue: Some(176.) };
pub const GREEN: Accent = Accent { hue: Some(140.) };
pub const AMBER: Accent = Accent { hue: Some(36.) };
pub const RED: Accent = Accent { hue: Some(356.) };
pub const PINK: Accent = Accent { hue: Some(322.) };
pub const VIOLET: Accent = Accent {
hue: None,
rgb: None,
};
pub const BLUE: Accent = Accent {
hue: Some(212.),
rgb: None,
};
pub const TEAL: Accent = Accent {
hue: Some(176.),
rgb: None,
};
pub const GREEN: Accent = Accent {
hue: Some(140.),
rgb: None,
};
pub const AMBER: Accent = Accent {
hue: Some(36.),
rgb: None,
};
pub const RED: Accent = Accent {
hue: Some(356.),
rgb: None,
};
pub const PINK: Accent = Accent {
hue: Some(322.),
rgb: None,
};
pub const ALL: [Accent; 7] = [
Self::VIOLET,
@@ -43,11 +71,49 @@ impl Accent {
pub fn custom(hue: f32) -> Self {
Self {
hue: Some(hue.rem_euclid(360.)),
rgb: None,
}
}
/// Its name in `settings.json` and on the command line: a named hue's, else its degrees.
/// A colour (`0xrrggbb`): one of the swatches is its named accent.
pub fn from_rgb(rgb: u32) -> Self {
let rgb = rgb & 0xff_ffff;
match SWATCHES.iter().position(|&c| c == rgb) {
Some(i) => Self::ALL[i],
None => Self {
hue: Some(hsl(rgb).0),
rgb: Some(rgb),
},
}
}
/// The colour the glass look wears: the one typed, a named accent's swatch, else the hue at
/// the violet's saturation and lightness.
pub fn rgb(self) -> u32 {
if let Some(rgb) = self.rgb {
return rgb;
}
match (Self::ALL.iter().position(|a| *a == self), self.hue) {
(Some(i), _) => SWATCHES[i],
(None, Some(hue)) => {
let (_, s, l) = hsl(SWATCHES[0]);
from_hsl(hue, s, l)
}
(None, None) => SWATCHES[0],
}
}
/// [`rgb`](Self::rgb) as `#rrggbb`.
pub fn hex(self) -> String {
format!("#{:06x}", self.rgb())
}
/// Its name in `settings.json` and on the command line: a named hue's, a typed colour's
/// `#rrggbb`, else its degrees.
pub fn name(self) -> String {
if self.rgb.is_some() {
return self.hex();
}
let named = Self::ALL
.iter()
.position(|a| *a == self)
@@ -58,9 +124,15 @@ impl Accent {
})
}
/// A name, or a hue in degrees.
/// A name, `#rrggbb`, or a hue in degrees.
pub fn parse(name: &str) -> Option<Self> {
let name = name.trim().to_ascii_lowercase();
if let Some(hex) = name.strip_prefix('#') {
return (hex.len() == 6 && hex.bytes().all(|b| b.is_ascii_hexdigit()))
.then(|| u32::from_str_radix(hex, 16).ok())
.flatten()
.map(Self::from_rgb);
}
let name = if name == "purple" || name == "default" {
"violet"
} else {
+23
View File
@@ -122,3 +122,26 @@ fn a_picked_hue_is_any_degree_and_survives_settings_json() {
Accent::custom(217.5)
);
}
#[test]
fn a_typed_colour_is_worn_as_is_and_survives_settings_json() {
// The mockup's swatches are the named accents.
assert_eq!(Accent::parse("#2563EB"), Some(Accent::BLUE));
assert_eq!(Accent::BLUE.hex(), "#2563eb");
assert_eq!(Accent::VIOLET.hex(), "#6b4bff");
// Any other colour keeps its exact value for the glass look, and its hue for the light one.
let typed = Accent::parse("#ff8800").unwrap();
assert_eq!(typed.rgb(), 0xff8800);
assert!((typed.hue().unwrap() - 32.).abs() < 0.5);
let saved = serde_json::to_string(&typed).unwrap();
assert_eq!(saved, "\"#ff8800\"");
assert_eq!(serde_json::from_str::<Accent>(&saved).unwrap(), typed);
// Half-typed or not hex: no accent.
for wrong in ["#ff88", "#ff88001", "#gg8800", "#"] {
assert_eq!(Accent::parse(wrong), None, "{wrong}");
}
// A picked hue wears the violet's saturation and lightness.
let (h, s, l) = hsl(Accent::custom(120.).rgb());
let (_, vs, vl) = hsl(0x6b4bff);
assert!((h - 120.).abs() < 1. && (s - vs).abs() < 0.02 && (l - vl).abs() < 0.02);
}
+114 -2
View File
@@ -1,5 +1,7 @@
//! Settings' accent colour and Home background rows (`horizon_launcher::themes`).
use super::*;
use crate::glass_ui as ui;
use crate::servers_page::Field;
use gpui::{
canvas, fill, point, size, Bounds, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
Pixels, Point,
@@ -17,14 +19,20 @@ impl Launcher {
/// The player's look; a launcher theme worn in the Wardrobe brings its own sky and accent.
pub(super) fn settings_theme(&self, window: &Window) -> Theme {
let worn = self.worn_theme();
let accent = worn.map_or(self.settings.accent, |(_, hue)| Accent::custom(hue));
let theme = Theme::for_appearance(self.settings.appearance, accent, window.appearance());
let theme =
Theme::for_appearance(self.settings.appearance, self.accent(), window.appearance());
match worn {
Some((sky, _)) => theme.with_sky(sky),
None => theme,
}
}
/// The accent worn: the player's, or a launcher theme's from the Wardrobe.
fn accent(&self) -> Accent {
self.worn_theme()
.map_or(self.settings.accent, |(_, hue)| Accent::custom(hue))
}
/// The picture behind Home, if the player keeps one.
pub(super) fn home_background(&self) -> Option<PathBuf> {
themes::background(&backend::data_dir(), self.settings.background.as_deref())
@@ -92,6 +100,110 @@ impl Launcher {
)
}
/// The glass look's accent row (the mockup's): its seven swatches, the any-colour swatch
/// (a click shows the hue strip under them, the browser's colour picker in the mockup) and
/// the hex field, which takes the colour as soon as it is one.
pub(super) fn glass_accent_row(&mut self, cx: &mut Context<Self>) -> Div {
let t = self.theme;
self.sync_accent_hex(cx);
let current = self.settings.accent;
let custom = !Accent::ALL.contains(&current);
let swatch = |id: SharedString, on: bool| {
keyboard(
div()
.id(id)
.relative()
.flex()
.flex_none()
.items_center()
.justify_center()
.size(px(36.))
.rounded(px(10.))
.cursor_pointer()
.when(on, |d| d.bg(ui::white(0.18)))
.when(!on, |d| d.hover(|s| s.bg(ui::white(0.08)))),
&t,
)
};
let chip = |rgb: u32| div().size(px(24.)).rounded(px(7.)).bg(gpui::rgb(rgb));
let mut row = div().flex().flex_wrap().items_center().gap(px(4.));
for (accent, rgb) in Accent::ALL.into_iter().zip(themes::SWATCHES) {
row = row.child(
swatch(
SharedString::from(format!("accent-{}", accent.name())),
current == accent,
)
.child(chip(rgb))
.on_click(cx.listener(move |this, _: &ClickEvent, _, cx| {
this.settings.accent = accent;
this.save_settings();
this.restyle = true;
cx.notify();
})),
);
}
row = row
.child(
swatch("accent-any".into(), custom)
.child(if custom {
chip(current.rgb()).into_any_element()
} else {
ui::icon("color-picker", 18., ui::white(0.7)).into_any_element()
})
.on_click(cx.listener(|this, _: &ClickEvent, _, cx| {
this.accent_strip = !this.accent_strip;
cx.notify();
})),
)
.child(self.field(Field::AccentHex, "#6b4bff", 96., cx).ml(px(4.)));
let control = div()
.flex()
.flex_col()
.items_end()
.gap(px(10.))
.child(row)
.when(self.accent_strip, |d| {
d.child(
self.hue_strip(&t, cx)
.w(px(212.))
.h(px(24.))
.rounded(px(6.))
.overflow_hidden(),
)
});
ui::setting_row(
tr("Accent colour"),
Some(
div()
.child(tr(
"Colours the buttons, the switches and the sky. Pick one, or type any hex colour.",
))
.into_any_element(),
),
Some(control.into_any_element()),
)
}
/// The hex field shows the accent, except while typed in: then each whole colour typed is
/// the accent at once, as in the mockup.
fn sync_accent_hex(&mut self, cx: &mut Context<Self>) {
let shown = self.settings.accent.hex();
if !self.servers.has_focus(Field::AccentHex) {
if self.servers.value(Field::AccentHex) != shown {
self.servers.text(Field::AccentHex).set(shown);
}
return;
}
let typed = self.servers.value(Field::AccentHex).trim();
let hex = format!("#{}", typed.trim_start_matches('#'));
if let Some(accent) = Accent::parse(&hex).filter(|a| *a != self.settings.accent) {
self.settings.accent = accent;
self.save_settings();
self.restyle = true;
cx.notify();
}
}
/// A strip of every hue: a click or a drag picks any of them, the swatches being shortcuts.
fn hue_strip(&self, t: &Theme, cx: &mut Context<Self>) -> Div {
let current = self.settings.accent.hue();