From d75864ff61a547fefbf3a2400112fe94f6271329 Mon Sep 17 00:00:00 2001 From: NB-Group Date: Wed, 29 Jul 2026 11:25:39 +0800 Subject: [PATCH 01/11] asusd: fix Slash mode getter returning the interval as a u8 The Slash mode() getter handed back config.display_interval (a u8) instead of config.display_mode, so the client always came up on Static. --- asusd/src/aura_slash/trait_impls.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asusd/src/aura_slash/trait_impls.rs b/asusd/src/aura_slash/trait_impls.rs index f154597a..6342aeb9 100644 --- a/asusd/src/aura_slash/trait_impls.rs +++ b/asusd/src/aura_slash/trait_impls.rs @@ -138,9 +138,9 @@ impl SlashZbus { } #[zbus(property)] - async fn mode(&self) -> zbus::fdo::Result { + async fn mode(&self) -> zbus::fdo::Result { let config = self.0.lock_config().await; - Ok(config.display_interval) + Ok(config.display_mode) } /// Set interval between slash animations (0-255) From 1e671bcc9759bc655eb19837bc1115297485ec0d Mon Sep 17 00:00:00 2001 From: NB-Group Date: Wed, 29 Jul 2026 11:25:39 +0800 Subject: [PATCH 02/11] rog-control-center: AC-style frontend redesign MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Redoes the UI to match Armoury Crate: a self-drawn component library (rog.slint) on a single Theme global, a dashboard Home page, and AC styling across System / Aura / Slash / Fan / GPU / Battery / App Settings / About. Backend wiring is unchanged — the same Slint global callbacks drive the same asusd proxies — except for a few things that needed real data: Home specs and the GPU clock come off live telemetry, the charge-limit toggle actually reaches asusd (threshold 100 = disabled), the language picker auto-discovers translations and shows native names, setlocale runs before the tokio runtime, and the reload-window bypass is one-shot. Comments are English throughout. --- rog-control-center/Cargo.toml | 1 + rog-control-center/src/config.rs | 4 + rog-control-center/src/main.rs | 38 +- rog-control-center/src/ui/mod.rs | 117 ++ rog-control-center/src/ui/setup_aura.rs | 13 + rog-control-center/src/ui/setup_slash.rs | 27 +- rog-control-center/src/ui/setup_system.rs | 14 + .../zh_CN/LC_MESSAGES/rog-control-center.mo | Bin 11724 -> 14273 bytes .../translations/zh_CN/rog-control-center.po | 1735 ++++++++--------- rog-control-center/ui/globals.slint | 98 +- .../ui/images/effect-breathing.svg | 4 + rog-control-center/ui/images/effect-cycle.svg | 6 + rog-control-center/ui/images/effect-flash.svg | 3 + .../ui/images/effect-static.svg | 4 + rog-control-center/ui/images/ic-battery.svg | 1 + rog-control-center/ui/images/ic-cpu-fill.svg | 1 + rog-control-center/ui/images/ic-cpu.svg | 1 + rog-control-center/ui/images/ic-fan.svg | 1 + .../ui/images/ic-gpu-dedicated.svg | 6 + .../ui/images/ic-gpu-hybrid.svg | 5 + .../ui/images/ic-gpu-integrated.svg | 7 + rog-control-center/ui/images/ic-gpu.svg | 1 + rog-control-center/ui/images/ic-hz.svg | 6 + rog-control-center/ui/images/ic-moon.svg | 3 + rog-control-center/ui/images/ic-speaker.svg | 5 + rog-control-center/ui/images/ic-sync.svg | 6 + rog-control-center/ui/images/ic-temp.svg | 1 + rog-control-center/ui/images/ic-touchpad.svg | 4 + rog-control-center/ui/images/ic-windows.svg | 6 + .../ui/images/laptop-placeholder.svg | 30 + rog-control-center/ui/images/mode-manual.svg | 1 + .../ui/images/mode-performance.svg | 1 + rog-control-center/ui/images/mode-silent.svg | 1 + rog-control-center/ui/images/mode-turbo.svg | 1 + rog-control-center/ui/images/mode-windows.svg | 1 + rog-control-center/ui/images/nav-about.svg | 1 + rog-control-center/ui/images/nav-apps.svg | 1 + rog-control-center/ui/images/nav-battery.svg | 1 + rog-control-center/ui/images/nav-fan.svg | 1 + rog-control-center/ui/images/nav-gpu.svg | 1 + rog-control-center/ui/images/nav-home.svg | 1 + rog-control-center/ui/images/nav-keyboard.svg | 1 + rog-control-center/ui/images/nav-matrix.svg | 1 + rog-control-center/ui/images/nav-slash.svg | 1 + rog-control-center/ui/images/slash-bar.svg | 1 + .../ui/images/slash-device-shell.svg | 37 + rog-control-center/ui/main_window.slint | 135 +- rog-control-center/ui/pages/about.slint | 213 +- rog-control-center/ui/pages/anime.slint | 3 +- .../ui/pages/app_settings.slint | 274 ++- rog-control-center/ui/pages/aura.slint | 838 +++++--- rog-control-center/ui/pages/battery.slint | 227 ++- rog-control-center/ui/pages/fans.slint | 348 ++-- rog-control-center/ui/pages/gpu.slint | 322 ++- rog-control-center/ui/pages/home.slint | 279 +++ rog-control-center/ui/pages/slash.slint | 629 ++++-- rog-control-center/ui/pages/system.slint | 4 + rog-control-center/ui/types/aura_types.slint | 2 + rog-control-center/ui/widgets/common.slint | 8 +- rog-control-center/ui/widgets/graph.slint | 30 +- rog-control-center/ui/widgets/rog.slint | 798 ++++++++ rog-control-center/ui/widgets/sidebar.slint | 246 +-- rog-platform/src/gpu_pci.rs | 52 + 63 files changed, 4594 insertions(+), 2014 deletions(-) create mode 100644 rog-control-center/ui/images/effect-breathing.svg create mode 100644 rog-control-center/ui/images/effect-cycle.svg create mode 100644 rog-control-center/ui/images/effect-flash.svg create mode 100644 rog-control-center/ui/images/effect-static.svg create mode 100644 rog-control-center/ui/images/ic-battery.svg create mode 100644 rog-control-center/ui/images/ic-cpu-fill.svg create mode 100644 rog-control-center/ui/images/ic-cpu.svg create mode 100644 rog-control-center/ui/images/ic-fan.svg create mode 100644 rog-control-center/ui/images/ic-gpu-dedicated.svg create mode 100644 rog-control-center/ui/images/ic-gpu-hybrid.svg create mode 100644 rog-control-center/ui/images/ic-gpu-integrated.svg create mode 100644 rog-control-center/ui/images/ic-gpu.svg create mode 100644 rog-control-center/ui/images/ic-hz.svg create mode 100644 rog-control-center/ui/images/ic-moon.svg create mode 100644 rog-control-center/ui/images/ic-speaker.svg create mode 100644 rog-control-center/ui/images/ic-sync.svg create mode 100644 rog-control-center/ui/images/ic-temp.svg create mode 100644 rog-control-center/ui/images/ic-touchpad.svg create mode 100644 rog-control-center/ui/images/ic-windows.svg create mode 100644 rog-control-center/ui/images/laptop-placeholder.svg create mode 100644 rog-control-center/ui/images/mode-manual.svg create mode 100644 rog-control-center/ui/images/mode-performance.svg create mode 100644 rog-control-center/ui/images/mode-silent.svg create mode 100644 rog-control-center/ui/images/mode-turbo.svg create mode 100644 rog-control-center/ui/images/mode-windows.svg create mode 100644 rog-control-center/ui/images/nav-about.svg create mode 100644 rog-control-center/ui/images/nav-apps.svg create mode 100644 rog-control-center/ui/images/nav-battery.svg create mode 100644 rog-control-center/ui/images/nav-fan.svg create mode 100644 rog-control-center/ui/images/nav-gpu.svg create mode 100644 rog-control-center/ui/images/nav-home.svg create mode 100644 rog-control-center/ui/images/nav-keyboard.svg create mode 100644 rog-control-center/ui/images/nav-matrix.svg create mode 100644 rog-control-center/ui/images/nav-slash.svg create mode 100644 rog-control-center/ui/images/slash-bar.svg create mode 100644 rog-control-center/ui/images/slash-device-shell.svg create mode 100644 rog-control-center/ui/pages/home.slint create mode 100644 rog-control-center/ui/widgets/rog.slint diff --git a/rog-control-center/Cargo.toml b/rog-control-center/Cargo.toml index 49ef1771..a8f2b680 100644 --- a/rog-control-center/Cargo.toml +++ b/rog-control-center/Cargo.toml @@ -38,6 +38,7 @@ log.workspace = true env_logger.workspace = true tokio.workspace = true +libc = "0.2" serde.workspace = true zbus.workspace = true dirs.workspace = true diff --git a/rog-control-center/src/config.rs b/rog-control-center/src/config.rs index 67253f4c..80e3c283 100644 --- a/rog-control-center/src/config.rs +++ b/rog-control-center/src/config.rs @@ -24,6 +24,8 @@ pub struct Config { pub start_fullscreen: bool, pub fullscreen_width: u32, pub fullscreen_height: u32, + #[serde(default)] + pub language: String, // This field must be last pub notifications: EnabledNotifications, } @@ -40,6 +42,7 @@ impl Default for Config { start_fullscreen: false, fullscreen_width: 1920, fullscreen_height: 1080, + language: String::new(), notifications: EnabledNotifications::default(), ac_command: String::new(), bat_command: String::new(), @@ -100,6 +103,7 @@ impl From for Config { start_fullscreen: false, fullscreen_width: 1920, fullscreen_height: 1080, + language: String::new(), notifications: c.enabled_notifications, } } diff --git a/rog-control-center/src/main.rs b/rog-control-center/src/main.rs index b4f11946..72a75ceb 100644 --- a/rog-control-center/src/main.rs +++ b/rog-control-center/src/main.rs @@ -63,8 +63,16 @@ async fn main() -> Result<()> { } } - // Try to open a proxy and check for app state first - { + // Single-instance guard. Skipped when this binary re-spawned itself for a + // "Reload Window" — the child carries ROGCC_NO_SINGLE_INSTANCE so it doesn't + // see the still-registered parent and exit; the parent quits right after + // spawning, freeing the name for future reloads. The bypass is one-shot: + // consume it below so a reload triggered from *this* process can't pass the + // skip on to its own child (otherwise the guard stays off forever). + let skip_single_instance = std::env::var_os("ROGCC_NO_SINGLE_INSTANCE").is_some(); + std::env::remove_var("ROGCC_NO_SINGLE_INSTANCE"); + if !skip_single_instance { + // Try to open a proxy and check for app state first let user_con = zbus::blocking::Connection::session()?; if let Ok(proxy) = ROGCCZbusProxyBlocking::new(&user_con) { if let Ok(state) = proxy.state() { @@ -92,6 +100,24 @@ async fn main() -> Result<()> { // return Ok(()); } + // setlocale is process-global and MT-Unsafe (glibc), so it must run before + // the Tokio worker threads start at Runtime::new. Read just the language. + let startup_language = Config::new().load().language; + if !startup_language.is_empty() { + let locale = format!("{startup_language}.UTF-8"); + env::set_var("LANG", &locale); + env::set_var("LC_ALL", &locale); + env::set_var("LANGUAGE", &startup_language); + if let Ok(c) = std::ffi::CString::new(locale) { + // SAFETY: setlocale(LC_ALL, valid null-terminated string) only mutates + // the C library's global locale — the intent. Run before any worker + // thread exists, so nothing else is touching locale state. + unsafe { + libc::setlocale(libc::LC_ALL, c.as_ptr()); + } + } + } + // start tokio let rt = Runtime::new().expect("Unable to create Runtime"); // Enter the runtime so that `tokio::spawn` is available immediately. @@ -171,6 +197,9 @@ async fn main() -> Result<()> { }; let config = Arc::new(Mutex::new(config)); + // setlocale + LANG/LC_ALL env were applied before Runtime::new (see above); + // gettext picks them up at init_translations below. + // GPU power status channel: written by the dGPU status monitor in // notify.rs, read by the tray to color its icon let (gpu_status_tx, gpu_status_rx) = @@ -185,11 +214,10 @@ async fn main() -> Result<()> { } if std::env::var("RUST_TRANSLATIONS").is_ok() { - // don't care about content - log::debug!("---- Using local-dir translations"); + log::debug!("Using system-installed translations"); slint::init_translations!("/usr/share/locale/"); } else { - log::debug!("Using system installed translations"); + log::debug!("Using local translations"); slint::init_translations!(concat!(env!("CARGO_MANIFEST_DIR"), "/translations/")); } diff --git a/rog-control-center/src/ui/mod.rs b/rog-control-center/src/ui/mod.rs index 6bbcf8bd..85e374ec 100644 --- a/rog-control-center/src/ui/mod.rs +++ b/rog-control-center/src/ui/mod.rs @@ -141,6 +141,7 @@ pub fn setup_window( let ui = MainWindow::new().expect("Couldn't create main window"); // propagate TUF flag to the UI so the sidebar can swap logo branding ui.set_is_tuf(is_tuf); + ui.set_app_version(env!("CARGO_PKG_VERSION").into()); if let Err(e) = ui.window().show() { warn!("Couldn't show main window: {e:?}"); } @@ -202,6 +203,62 @@ fn ui_shortcut_status(status: ShortcutStatus) -> GlobalShortcutStatus { } } +/// Locale codes that have a translation on disk: the source `translations/` +/// tree (every subdir is ours) plus any installed under `/usr/share/locale` +/// that actually ships our catalog. Sorted + deduped, with an "en" fallback so +/// the picker is never empty. The locale *list* is automatic; the native +/// display names live in `language_display_name` (add a line there when a new +/// translation lands — until then it shows the raw code). +fn available_languages() -> Vec { + let mut set: std::collections::BTreeSet = std::collections::BTreeSet::new(); + // English is the source language (no .mo needed) — always present, so a + // fresh config never lands on a translation by default. + set.insert("en".to_owned()); + let dev = concat!(env!("CARGO_MANIFEST_DIR"), "/translations"); + for dir in [dev, "/usr/share/locale"] { + if let Ok(entries) = std::fs::read_dir(dir) { + for entry in entries.flatten() { + let path = entry.path(); + if !path.is_dir() { + continue; + } + // /usr/share/locale holds every app's locales, so only count + // dirs carrying our catalog; the source tree is all ours. + let ours = dir == dev || path.join("LC_MESSAGES/rog-control-center.mo").exists(); + if ours { + if let Some(name) = path.file_name().and_then(|n| n.to_str()) { + set.insert(name.to_string()); + } + } + } + } + } + let mut v: Vec = set.into_iter().map(SharedString::from).collect(); + if v.is_empty() { + v.push(SharedString::from("en")); + } + v +} + +/// Native display name for a locale code — language pickers conventionally +/// show each language in its own tongue. Unknown codes fall back to themselves +/// so a newly added translation still shows up (as its code) until named here. +fn language_display_name(code: &str) -> SharedString { + let name = match code { + "en" => "English", + "zh_CN" => "简体中文", + "fr" => "Français", + "it" => "Italiano", + "ru" => "Русский", + "tr" => "Türkçe", + "uk_UA" => "Українська", + "pt_BR" => "Português (Brasil)", + "az" => "Azərbaycanca", + other => other, + }; + SharedString::from(name) +} + pub fn setup_app_settings_page( ui: &MainWindow, config: Arc>, @@ -365,4 +422,64 @@ pub fn setup_app_settings_page( }); }); } + + // Discover shipped translations at startup so the picker lists every + // language without a hardcoded array. + let codes = available_languages(); + let configured_language = config + .try_lock() + .map(|lock| lock.language.clone()) + .unwrap_or_default(); + // Match the persisted language; fall back to "en" (source language), then + // index 0 — so a stale config like the old "en_US" still lands on English. + let current_idx = codes + .iter() + .position(|l| l.as_str() == configured_language.as_str()) + .or_else(|| codes.iter().position(|l| l.as_str() == "en")) + .unwrap_or(0) as i32; + // The picker shows each language in its own name (standard for language + // selectors); the raw code is what gets persisted, so keep both in lockstep. + let display: Vec = codes + .iter() + .map(|c| language_display_name(c.as_str())) + .collect(); + global.set_available_languages(slint::ModelRc::new(slint::VecModel::from(display))); + global.set_current_language(current_idx); + + let config_copy = config.clone(); + global.on_cb_change_language(move |index: i32| { + if let Some(code) = codes.get(index as usize) { + if let Ok(mut lock) = config_copy.try_lock() { + lock.language = code.to_string(); + lock.write(); + } + log::info!("Language changed to {code}; reload to apply"); + } + }); + + // Reload Window: spawn a fresh instance flagged to skip the single-instance + // guard (ROGCC_NO_SINGLE_INSTANCE), then quit this one. spawn+quit is + // reliable where exec() was not: the old DBus name is released on quit and + // the new image never races the check. The child re-reads config.language + // and re-resolves @tr() in the chosen locale. + global.on_cb_reload_window(move || { + let exe = match std::env::current_exe() { + Ok(e) => e, + Err(e) => { + log::error!("reload: cannot resolve current exe: {e}"); + return; + } + }; + log::info!("reload: spawning {:?}", exe); + match std::process::Command::new(exe) + .env("ROGCC_NO_SINGLE_INSTANCE", "1") + .spawn() + { + Ok(_) => { + slint::quit_event_loop() + .unwrap_or_else(|e| log::error!("reload: quit_event_loop: {e}")); + } + Err(e) => log::error!("reload: spawn failed: {e}"), + } + }); } diff --git a/rog-control-center/src/ui/setup_aura.rs b/rog-control-center/src/ui/setup_aura.rs index 0bc63412..28bc3aac 100644 --- a/rog-control-center/src/ui/setup_aura.rs +++ b/rog-control-center/src/ui/setup_aura.rs @@ -183,6 +183,19 @@ pub fn setup_aura_page( show_toast("LED mode applied".into(), "LED mode failed".into(), t, r); }); }); + + // Speed slider: stamp the new speed onto the active effect, + // mirror it in the UI, then run the existing apply path (which + // also pushes to hardware and toasts) so the write logic stays + // in one place. + let w_speed = weak.clone(); + h.global::().on_cb_speed(move |speed| { + let Some(ui) = w_speed.upgrade() else { return }; + let mut data = ui.global::().get_led_mode_data(); + data.speed = speed; + ui.global::().set_led_mode_data(data); + ui.global::().invoke_apply_led_mode_data(); + }); h.invoke_external_colour_change(); }) .ok(); diff --git a/rog-control-center/src/ui/setup_slash.rs b/rog-control-center/src/ui/setup_slash.rs index 43b42e05..57fef705 100644 --- a/rog-control-center/src/ui/setup_slash.rs +++ b/rog-control-center/src/ui/setup_slash.rs @@ -54,8 +54,29 @@ pub fn setup_slash_page(ui: &MainWindow, _states: Arc>) { set_ui_props_async!(handle, slash, SlashPageData, show_battery_warning); set_ui_props_async!(handle, slash, SlashPageData, show_on_lid_closed); - if let Ok(mode) = slash.mode().await { - let idx = slash_mode_to_index(mode); + // Read the persisted mode, retrying briefly — a single read right + // after (re)start can race with asusd and fail, which would leave + // the UI stuck on the Static default. The mode-changed listener + // below only fires on *changes*, so it won't recover an initial + // read failure on its own. + let mut mode: Option = None; + for attempt in 0..5u32 { + match slash.mode().await { + Ok(m) => { + mode = Some(m); + break; + } + Err(e) => { + log::warn!("slash mode read attempt {} failed: {e}", attempt + 1); + if attempt < 4 { + tokio::time::sleep(std::time::Duration::from_millis(300)).await; + } + } + } + } + if let Some(m) = mode { + let idx = slash_mode_to_index(m); + log::info!("slash mode at startup: {:?} (index {})", m, idx); let choices = slash_modes(); handle .upgrade_in_event_loop(move |handle| { @@ -64,6 +85,8 @@ pub fn setup_slash_page(ui: &MainWindow, _states: Arc>) { global.set_mode(idx); }) .ok(); + } else { + log::error!("slash mode unreadable after retries; UI will show Static"); } handle diff --git a/rog-control-center/src/ui/setup_system.rs b/rog-control-center/src/ui/setup_system.rs index 9f78924d..7d280fd2 100644 --- a/rog-control-center/src/ui/setup_system.rs +++ b/rog-control-center/src/ui/setup_system.rs @@ -87,6 +87,18 @@ pub fn setup_system_page( .set_dgpu_name(dgpu_model.into()); ui.global::().set_has_igpu(has_igpu); + // Real product name from DMI (e.g. "ROG Zephyrus G16 GU605MV") + let real_product_name = match std::fs::read_to_string("/sys/class/dmi/id/product_name") { + Ok(s) => s.trim().to_string(), + Err(e) => { + log::debug!("DMI product_name unreadable: {e}"); + String::new() + } + }; + if !real_product_name.is_empty() { + ui.global::().set_product_name(real_product_name.into()); + } + if let Ok(sys_props) = platform .supported_properties() .map_err(|e| log::error!("Failed to get supported properties: {}", e)) @@ -147,6 +159,7 @@ pub fn setup_system_page( let igpu_temp = rog_platform::gpu_pci::get_igpu_temp(); let (cpu_fan, gpu_fan, mid_fan) = rog_platform::platform::get_fan_rpms(); let cpu_freq = rog_platform::cpu::get_cpu_frequency_mhz(); + let gpu_freq = rog_platform::gpu_pci::get_gpu_frequency_mhz().unwrap_or(-1.0); let ram_usage = rog_platform::cpu::get_ram_usage_pct(); let gpu_usage = rog_platform::gpu_pci::get_gpu_usage_pct(); let igpu_usage = rog_platform::gpu_pci::get_igpu_usage_pct(); @@ -183,6 +196,7 @@ pub fn setup_system_page( data.set_igpu_usage_val(igpu_usage); data.set_ram_usage_val(ram_usage); data.set_cpu_freq_mhz(cpu_freq); + data.set_gpu_freq_mhz(gpu_freq); data.set_cpu_fan_rpm(cpu_fan); data.set_gpu_fan_rpm(gpu_fan); data.set_mid_fan_rpm(mid_fan); diff --git a/rog-control-center/translations/zh_CN/LC_MESSAGES/rog-control-center.mo b/rog-control-center/translations/zh_CN/LC_MESSAGES/rog-control-center.mo index 4f53eae708cf8c87d4701d3b5da084b2e7acb310..595579b51079a98a2583dd2e3d33c12eda11cebe 100644 GIT binary patch literal 14273 zcma)>33OcLb$}m1Kw_385CS3FOGu~<$Q$5*2@T$Hu&^W9kU(AXq zeHtCEzxn_7ulK+A{`Wop`TDYL7JolPK88GVrDffCnPvU_8mYCcsUNhgkHTs2Mpywq z1Z$M5;Ga)mr*JE{V=rQ zoiG9`ps3~~ycP~anfFKVQ}E|-3j94}C~Go<@Y9+NrTiFVE7l750r(9qcf+eGAApy` z0VsMq31xq8LfP-nwEkC6#(58oZiP2O+1GvWjnaaB57Ry=yr$gE2(~zZFomxH!MenDf==TjMdit62*HHTX0m^vS zW5m+#b|~d9KxzLNlyRa^&T|!{i`5Eco%^-?ZOB%vpKJYZ;WElMqFh$nL%1aE`Szyy?W{vC=xUGWLieliq$nFBEeYcZ64 zxlqRWI+SzT1m)b%LOGXTL9wH0Y);lS3re{P%6Zp7vC9tlIP8Vu*Y85H|4-Xyz722% z<-O2_@587V+Dbs_-v!0q`yfYZor0f-KZ2r%2^gE`>n14eZ-=7)`=P97q1Jx|q9W@l zDEr$0rGFok^&f@OehA8Ze+@++Z$laHf1v31N}NRW@=@jOQ0Bc4vNh|AkR!F8f^wd% zQ0CjK^{+x1=g0cohcf@~ApcqK#|fsuyP%9)17&|{t=|CIx^)_Il-5t6tmj?G60Iu< zdNThcd{5m&13J|Ev51l;@W-nQ(&ggUXwgpM=u?Hs$Tg$;#bK_`@?$?6Dn+z3kEY0p&R;<9#2>y8cG%{|?H!f1%I+P3!+l z%fE**-W3EB>3=g6|N5lz9w_>J7)rY*lyPMXyr255P};qv^}kU5H@t@WE6dIE8=&m- zRw(m*9?H0LptO4wVscguly!7K8F#-ve;G>qA+7&0ly<*Vz6)jCYbRq1@I&xw_%M|5 zAA|BdqFkZ%Nhs(4O=X)t->lDfDi1+f&q;m$9p#(KxAgfxLYXhC{EhOmDQ5lGLDAc% zpsZ)PG6JRF3gs%TUk~M6Hz|9xeyehi@&Js|?gf~HH%v9-wZdB|Z_)B;DC2!s%imZ2 z1C;f=4W)e^%6VKn&9d%=_dt0bS7wyYK#4PJp{)NfTnJA=iD$oqkHA}|oA?=pwUnQR z4*Vqy;u=ZA1j>&qU3fj^Mu-ZnwNU0isXPm%{kOFI1Lfap{ZHWgc|HPVUB7{{@5}Bs znB=Yc8?k7YAA8{W+?4uLRnu0lzl9RH$n%>yiLk=P}*;XGVehsapfhg{~nZk z;HU6L_#5Q}oJPjCl~a`uLK$y~mTREQm(u!XEpJio)B0mt{uaEC_CJEx!mB=K?yDQ2 z#Df{i#magp>uyoDLCHV5p{!#7a=iq9UCJ-Q3?g2FCxz)gUANNMZ&-7{5YqD zCfI_ckS}PR;A!LtBEC`lx0XT`^5@8di2Thluqt33BKCO#c?|h7LP)f5V{03-1(}Cj z>`(0PCPdCdY~vPW5AtWoB1HbWkq3fb=AS-V5w^I;Tv7SVJfP4e_E+RJbA!G>IimXQ7K(-?_ zi2S{d3?r`~>yhsvn~>$m$B{op!oS7*h#-Woz)z(9T4WtkiNuhNNceYE_(Lf%D~_}y z;#V^e8@U3JzXs$h$X_5YBM&3t-vNFcM;3+^%IlQ3DR;tJWTuv1g`d&im&0!%cOefU zZOGNgdy#J<;oqn9MHNJLHqG^0(g&Wbz>P$42E|13T zlpAwv($Pe`Y<41%wq<5VikEIznTf}v@wz{zX;pnD9Z59B|ERVx*KI6rm`OTzbwLGH zY1UO^&64U!G^vZ8%jje47P+V%U-O)}3# z9@tJiVlR@>s-5JxR*RzJdtSP6qAoFRgi6;*+6!WGda{C4qw7Y3qbV+Pj?qkmsR@@o zt+Y73w0Ku(@$RBxxIVj}w_wcC`Xb#04<12LLH{M9w*|>Wrm<{Rle1!6WqAB!8&}Cu zkEvW7ijNz=ERx6&<|vLgvMWyi7gntCT*=LTarxBvrbpUKvIC%)9FE}PZZ7+W0{ z=E$(Iurx33RL5{9Z8@J263Nx}T({QA#M1UeCl-?xrY40gmdf0+5FoQVE2A+P!cN4^ zx*FI)6wOvR(fDK0IDS+1r3`yz5mtp8&rB<;TAfO}4fdQwJe^F$g39Tm7Hf;W1a)_n z;l&lMUE!pY(N#g!-DRxPo+H{y1r_(m-q30~jVqg~8D+Do6PdKNxVF|>;?%i8Nrt!Q zyG}Zjq-UXeQMAU5r`(d7_=@5dmG0LvxM_nMPiwUVwX#`}m7>Rp=~+@`&yA)UW6tWr zvV!@7Hb&hze$b+*Gpn_m>fN{3 zX$mf3uiyyM2|HDvXqr+VjktDGU>)|v6%sKW&M~PWnR%<7niX}pWIQry+?W_nZM2S) zl(CZO8V)*%d9N(v2mYh;6)Y z+z^#+D(xiGcG6gOB#1i_iQ;aP9n*>Bq|(Gf+pVqTj+#7vlnf!D_?&E~7P|~=7hfFL zUhFW_cUv!c!0&4_}{=bWIbNEmUtBZ${k2XhHhmReJt`W-^VJ z7{7FE=_Ny4s`*9gA6vL&rd^dFDXP_2gukQY5)%!)G6?L6hQ^qic2)Vr#j@ZsZAe7a zFr;d>lZw{Z^MYei^A4($%s?R4laQ)8iCE~NqeXkp>Y7;5XwoK=w5J&jgw3d#UZ|NH zrY(i4;0hh%8`9a-%hgx3Gz<`9&?}~>K8!4dVXGuEp9eLPQ#LrOq79h_xte7X zeCgh)pS5eayToIIXcSM!R@?5XH1RqN>DWT8n`(68=D@>QjSlA~ou-lvV!r8&(O%ec zsifPLZUZ-OcZ=FgPScq<`9x@B!do$v#!GA zb4gLT<9NI{lWx?TMpiY~EX9tmj7FkPd9db)M3U;F`&k*P$ z-1rOQX}2yJ%v{p5!j%hkv@`bsV{lVrNGZ+zYIxNW9ibo5Yhg2wB+~9o8}lV!OcjTB zjol7+LN%{XV|$3hl5#}?Z8ro9(5&;18)Zo{A!{04w3am{hz=^pPZsY_k%^4n zTJogfQg#?*z7cmNDWhHQNcL-|nxbiZT+&||Z*+sCP$_HyilIo-+YRSM* zX9u=h@F=pcv|Z1;84C;S%Em`hnTAFQVueY*yuhwVRLi@;qIq*ir+%W+tw}VHu95~W zw-?53iniPsciIjfDR0F!%)i=BFkD0QX%<@TkZp?TW>WaKogyW6lF@|mnu2?>4Kd_Y zVFgc^3`0yUcwJb|QKS;Yxfrh$c7-wQ$z=3I0;4LK%}qDCB)Sna7WidwtfOmaN+efE z_F`svh`X{ZaPSzeATFLv;H_z0MDUo9WfPPF4FnZ)%^6LcYV0Ja3tqP>oOp)#GwM&~ z392QPTrJn0o-Tojd3Xy$O{4q2( zBTf6@Q-}n>;7v0VZ;WN?>NLU978r|OV0M#}98Dw4)8LCqL1`v2xy{xbqcr_9Jxr6T z;6^U^UGXz>TL%%UXtU-!Nsye!Q25`+_b;eDPxLr*UA^W$YzEc4LCA_BG+7BzZSa0_V4@!t11HE00Eb z)vPi`Us9cla-l`jtL>#YWSyH#S(S;pV6+M+xuR?ixA_X&2|gvDB)MEyMtK7xsPlCp z_~Oa^!jZ@wZeEXRE$_9pnRxJdcyj50^Osa%mhmVby-FV}a_CS`=Q@X+n$c3OYfI~h zlXk{5b?clOa#M5bm;@tTXEhKXt+gsn4##RR&W{v+Ur(YJt z{&@{d@k(Mo%f(kdDJk)jyt&r7>9XJ!mI{sJ^6FxdpFKtDSiUe){wQZa)-lsgChE$$ z3+0PlIVVaino#LB^7*B_B2^cSl+Vu8rOKBkX4(^$EH1A^7t*bqeUoucpE`X;`P9#q zPoH5=n?7^u)H|l$J9X-WMNTSRzBI{u2zPrTNzd@BNBL6MX^=#f?}$XliB(Zvgt-FT z$fVIWGwrAApPci^go=d~^9rk+HhJm-7;G^O!q1M&JjJg3^Id-XhV1$6xuMPGx4-9Pc5s+a z&-|Ag-ZgTjbL9MPuWf_hdBD4{#cS{MUpeP(>^CF)Yx5e*8{C@R+K&#l`>(aJt>Zyy zeSdy!zu$7eJJ{)M81%Z<<(r#nYh_QJ%bx4g z_Q6*WqgS@;Z)=nP%fpvqZ+*MJb12*QLVo9~R`JSpJ=&dP8yV~py`X|~xx+2q#y<9M z25IyvEAEv@<(+L?P2091O?P?e(;A-sW8W9j`Hbe#bE@ zyY*H7<$WVVd*nx0lTdz(2AsxTQA>u*0{I{NKmaoMeXXh!TyH`Uu;ydw@!%&9Q6DdR?O z^GR{E+B<#HA8y8CWp^k$x8al-Mg2}rC3p}TaZm_q3)<7Z(#7lE%oOjO-;HszIb%%k zoNx0EuQ6JD=X|?&Vz+F4V3Xh0BkK<=*tm=3^$lY5YRKN^^Ip#xE7yL~yYPa-8+tL@ zb==Ay=%t!apg#x&+s}GEn*$RY>naUgw>CEw^!vnI^xX+u}q$OunTTSH*F%FLf}J z*SUY>+@W08X>Vu;${4wDiaruxx3&4t^}cg{6RODX>KwVy<)3*0jdLk@yI#!qo#N5R zg;xB`yKunl2&4CRb}=Tm3SC4|#y@jITY|7y8eOu7*?)iOHz~{PU5!n2=AS<8^|a{- z`8};P2u-ndy?1P&{!jCM&Kh^KA>&EK+J+Js% z&w3|X3ErcQTkzD;cKHMAvfFkA0}<4i=KtwvrsArV87;5vG{FGVA2SGk7JNStwIADw zr(rE}%b26HI0rOWe1iV5=UJ$+4OxsF$2lVTfQi@Mz}k^>N9Fj6jyc%xv4eP+cW?tY zE9Yk}KN&zCj+|R>Ml7}gR!)%i&z$u8wwOaNHVJdSdg6iY=Z4mD$H;JnFFP6)p76W+ zbDcfQZFNVzsXORyESReRpTi=}VH4rA13Ne`4g0-={;uO{>%px(ezh|1==U5GF_zmS zXZx~8PJ~}_n9bjJ*6$q1z0ya>MvcYW#frt2+Ir+D@PM-&_-^r>GJ`EDASvi=<}^nJ zk9b>Jq*^}VnEP5n6iT+`AKsqd|Egui#@;1j1z%n;eKU|5-SW5Kr@iVP@?pg& z*ch3W-@6O9)jUCmHkpblm|2aFhp_ov!UvMVD}NS?zDV!i@*1f zKiGq+!_c_f^0%$e@9woY+WhXVVMy+2BkQ4{w{p>{%&v&ioqqSTvO*dS4zLn5&_x-G z)s0EQDh&pEBu5Uy8b=`6p^3J71SJK{nv9mr?zE2?l-cySik74&Xr?er2oE(#?xzq4 z$Uw90XGJxp|L9_K#}0d&H-=YanADj@h4djT2Lp{RLf;b#Wz#Ghq=Mn$gsRjbO3cAn z7S6y4hw3O!i;YovfwNnF-=S=8NAWr8USxiGX<5UacaGpl5|P`r+wbTz3ALoKyVsew z4Z@(w)^Jvn)`+vpTX1&I=G>81?o-W!{4;0#wr<*+X@fUqf7=OSh2`%!!fSv1*6535z3U7J;*g@lRMQ;ro3mK=mCvk0ps`}vn0Vz>7 A)&Kwi literal 11724 zcmbVRdvqMtc^}HQfRX}%CX|$h8%T&rypm-b0x}R}*|HTRTUZjWl$g=(tahx?%(62p zA=9KQ+j`l08C!ly#+EH(1Ga2qW9wmAr)g-@a?&=G9@;+&E!mmfJtsNjoF>gV&FMM) zefQqct{$eG&he+;+;8sv?!DjZes}%hZGk}r&mPcSpo;-Tc?Vc|6aL_N;q!{}CqNbW zMc@yBe+1kk@sz}A;5Wd35BN>se*?b-{Q4g%$`as{KpM9dcssCP;&F+WCB7^1BOvwv zPv94TH{Pr$e+>LG@aw=8K$7#U3FPxO#fFA%?0d3&zU^W3e zfd_!U0X})1qVz%dyJ-JAgwy!bFfNV%F%Tk^4}iqa0LlKe8Tx(~@LpgMkn~dn{8OMI zaVL=c=oGLR_zsZv^;_Wmz&jz7{G<#>`)mZR01g4kU;Y6|^ZX7-`@b2JU@OW!5?2E0 zdpQtYlxKn59zfD#2axnS2)qw?97yZ`J0SUm2PApaAUoV9@otGrfh6x?Aj#VRB>#OL zNP6jz{QW>$$7?{^{{--P;0M69zz4o8=6xAR@vt9A<9`H%N|k>C(*Cj%|3~t_fW;Pq z{|z90e+qaHa0`(9z86UIUI4BJ{xy)~-2|mm|GRr2>d8E1JX~0;%7AAnET- zAno(7fLMa^p2S}QNzRSf9IgK=KnAP;(mJ*PNsj|Sm{vI^aRT@~wEq!Ec3=7xv5pNu znr{n``fme5Rmwpi16}~q{OJhH-w8ZU{eX{MrzlS|Mfn4?_hFLjfrllI zNIW6&bs+USBk`idDTzM<()hoX_*02LlladP|5f6@N&F9qE|BDBfe3NsSF%k;BmNB% zZrQ}M9PXS5(3qX?BAlr=+;}TmXCM9-C?2|Ys zaaiJUiEl_e40^q$sny(N@_FE;}l|a(Rvp|yH03184x$4jpjSY2hO~no0=*3K zpFX^hAJVf8^h5rpC_5$I2z(y&An5l%t3lPEWgs227DNx+T+u)&w9L=vhl3M zCnZu`&{GC#;cvn>U|RlgJIi;1HW2whC&&P81=WG}fN*^Bx&X(Rf+K78A>XFw1_9j1 zLh_x?M|#Nrn)sWdyabHP_ZNYWf+|4upj{w(R)WHyHJ~jZ27=r3^YnYb?I6lIDrgg^ z22=~8=Q#oVFXdx;D1JT<3V^-@+5n>GagYQ0D(KHa-v!b00{<)i+$!In02b1Fsi`+< zY;|03h&rZb+kxEc!f)%*YazGNYFWsv(!vXw8zT|PtWW5M6Vy#+Ylaqbbju8^wk(Iy za#U(wyWggSY3gRfXX#oKO*mnz#r#Zt4NZ&XcTB`pR-e<0*!w&t-}*cr-}yWq-#GEY zdR%UI4c4WU&gE5V5obPc6Io_Hvo0n$F{?$3v#pk?1)k7a>n$}7t&_|}J}}h`vr3Xy zuf`Yjs?-;bymn`+!CD|;lcvVmI)j{nR$#|8EgZ;cO|)kfCYsQdo@f#S{9P2!Z7!eN zTrsz~GOy_`m*v#RWzO>RR4G2pZi6>BK#g2nHEDHS+)Bg(r7ddX0w#It{Eoh>E#Q+| z&0}sf!ix$f{vxu8U2_dF&tc81#*ZPTTs3Re`ao$cX0-Z^GSv)ehTmFis`Un9Q}!%Z zLyi@1WoxvEnlK!;L^TW+QBC`PzejCUw~505CL&aA)(sNDER#9(tw{|#c+nC@2;25J?!U zA+E-vdPp^xZlZ5Q4QVW5#jy)hZQyC8rmlu<+PH2htF0?v%7dmlZk5IzW~ed8irKXB zO5IGnRLClzj*w$B)rKmdkw`=j=@7lHu7V3CrfMhbkYg~%vJ6{uAkjdK&`dO~t*K20 z(k_c4bX(N8#+(GtZY{bK#oD>tkky3cg_!|07v}AY@u$X0#IcDoE3B2UsN=-!lA@vp z3{TV-Vr0?Am}ah1n=oUUW$I!?@gsLJ8Zkn)I#kHpEgW5pUM)LaBZd!*ga$s~IZs;{P$Y!dEG9y*8rv=wipMZ>EaAlCmzMgo**0T;v<_RC&TM#FuiEBam$5T%2>t zs_XdNyaBW48da!eNiF?AD&x)I>QG`j5R~(IjJ3I3Ri%#9LsEQnRO@!>Ty7_%C&2_L{TTs z&1RAZV>6gjI7A)~LFXidgZf;)HMX|88s?+w19q&9#*yghAhS?*wB}Dn{iqOIjOY8@ zrwq&UuEl!EVw;>%E^n==#T#1ls|Hq$qf~?WphJ{gjaV|=|5KJ+P8ATkx6aW9S$DpM zh__Hf>-|!*$})9S|M^?5DyzdjaQ(5>W??G1?keFp3#B307R+0PxQ0<-UzxO|kPStp z`IA*|tf|Ejj7tSdM^l86>^ZOE$RD+-w2IZ)(CD1)&(jyTDGy|Zd@8+8%AVg94pLf{ zKf@v&Wai56wQvpL#r3V{vH5jov!lo6U2JAc4PN(@216FruB1a7m!TN8LkAEY5{X#M ziX+3Y=d@U~HJ-o)3|cl&ONs4~udPuX)z6rD9(t`FWbbQy3(x1vi{gBC6)ul@kgn0! z{sLEwquQL`;ZqjKJ#l`2k-N?2%}zsXb5t{8fm+MKrAyxIY*~HtikG_qRgx$}s33sa zHau0arlORRA?^q$s-fm))kq-yhUk*pg!&2TGu6L^;htbcm>Lf2bmzpBQPg<6 z7#5xuj;k$&ihn=Z98t~9ywKl_>ku7u0m@>ctE|UmU&!z;DB_-wyCf7cS=p#L-t4aA zrAA&fsP3S>A`8%2Qcr0lVH#Gbk=UrC{BjmaSzO(M72)7h;`&s=3aU2-HzAeNWhw{{ z!ojqB+47aaWe)~%u_|6xvf|s{TDE-IvVuz0c7nBW%!1346)$0RRl!P~J(6g`mGN;W z;i!eW^%%OD4GB17uoib*I^=MB#UdVQ3~NEX_#rbGwnC4iK{w1??-I5(y18saK~+W7 z+T7ZT3zrq(!jGE^W^RpPX%6l&MW_~aa}~ei*qZZbU2S>rpUF1}+CU{^1j})0vrCv6<4ya~;#DX<>C2T$K1)+2o&$oekC=2MseFODDIV{H$16iW(G1NJ;b3_`tV44_~?SZsqtNkd-*lR z?Z1HWJC*F-vGmXZ3GORB>9(`(_-jgfXrFs#K#?u?>a=&XQ*j6Sz);+oHx;k_7@ywj z>4pRpfAVFy|FXh=SxM9Ra{4d$Z$hHC|ES{b9nE&U;%{Q^>gRrwTXK6Q-6Ka7cXDrf z? z8t--c`JikviM~qu!c==_`jb z=eB2dZcC4xPEB2cXhr-+<4q2vrjDnNw*7y9K8xjG`Pt4+82B^&lEb{dF7NPmx3@ntHkP-OGowNp6p(qXP5f}B?JMh=LIF-4)9jkKtcO&w!B(LvOrgy+S zxhp-@m71Qx^1R`sx9=Q8%?goPq~903lLNwIynUV6r8{vc z)A@!wb|^K`Eu7UGdMiCSgSF)Sv@rXN4J3JeC*7_~!c8;Vd*GbpLUeq?W4%|0=Z+L^ z>9(D4Cof~L@N~Dm9sKOU0SHP>op7fvWe2epa$xuD3>;;y408U_$=OZ73%$cLnM>{2 zW4k}O(lxJq`s50&$L+dIO6lvID=Gh%nc1z-e}v5b6wBQ6{qDpdhUIB7o7@JSx##*3 z=g?Vdd@6hFK%RQM%Tw;oqYOu05k;go{yI{o+rLvNG&6lVn>->S(>vOqHxBXXP7Z?S zo*%>x^SfiT5R=zE_hXG%q_BaoM_#wtpKa3J6J&ppjId}ppkS5o%r`k8ytM?GcN{q| zyXUIcccMh*baeL4Z!1x}GskI}z6+KpPVaHMd&RsIUwPsVrw0!r4bm)(rayDw)2ATYE*Jt= z9fkhg(N3H#Zhw#1he&+x&LhZ!G^^N8dgx+i=2azkFZX&n5rH^XegCHVRRm-SDV(Ku zUh$Q@fFK;e`t& zkse44vK#VO2{)F%5Ymrah&VITL%R|G3SDJ|{iRu9JSb6iB|q7J(U;+0SbYaTIm9og zA_THWE>O^k1cRyzN>qd}5r=$9LJ7jX#T;1l!kr*yM!mP&WEcpSl#M)L{3a<^%CC>| zeYVh{V}+kN-rlnaXwuK@eGoO3`|2)AH{g~s91Z*=66Fz@TWBB0MC$5ExT=31cF+GH zw1>mPw^w%RKxS-IaYu1 zxXjhZ6* QZJC*asmUQ>CR)+|0=}TZp#T5? diff --git a/rog-control-center/translations/zh_CN/rog-control-center.po b/rog-control-center/translations/zh_CN/rog-control-center.po index a2431bea..6f379e27 100644 --- a/rog-control-center/translations/zh_CN/rog-control-center.po +++ b/rog-control-center/translations/zh_CN/rog-control-center.po @@ -1,20 +1,20 @@ -# SPDX-FileCopyrightText: 2024 UM-Li +# Chinese (Simplified) translations for rog-control-center +# Copyright (C) 2024-2026 +# This file is distributed under the same license as the rog-control-center package. +# msgid "" msgstr "" -"Project-Id-Version: \n" -"POT-Creation-Date: 2026-07-20 18:55+0000\n" -"PO-Revision-Date: 2024-06-19 10:37+0200\n" -"Last-Translator: UM-Li \n" -"Language-Team: Chinese \n" +"Project-Id-Version: rog-control-center\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-01-01 00:00+0000\n" +"PO-Revision-Date: 2026-07-26 12:00+0800\n" +"Last-Translator: \n" +"Language-Team: Chinese (Simplified)\n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Lokalize 24.05.1\n" -#: rog-control-center/ui/pages/anime.slint:6 -msgctxt "Anime Brightness" msgid "Off" msgstr "关" @@ -73,102 +73,118 @@ msgctxt "AnimePageData" msgid "See Ya" msgstr "" -#: rog-control-center/ui/pages/anime.slint:47 +#: rog-control-center/ui/pages/anime.slint:50 msgctxt "Anime Brightness" msgid "Brightness" msgstr "亮度" -#: rog-control-center/ui/pages/anime.slint:63 +#: rog-control-center/ui/pages/anime.slint:66 msgctxt "PageAnime" msgid "Enable display" msgstr "启用" -#: rog-control-center/ui/pages/anime.slint:71 +#: rog-control-center/ui/pages/anime.slint:74 +#: rog-control-center/ui/pages/anime.slint:97 msgctxt "PageAnime" msgid "Advanced" msgstr "高级" -#: rog-control-center/ui/pages/anime.slint:86 +#: rog-control-center/ui/pages/anime.slint:89 msgctxt "PageAnime" msgid "Use built-in animations" msgstr "使用内置动效" -#: rog-control-center/ui/pages/anime.slint:144 +#: rog-control-center/ui/pages/anime.slint:146 msgctxt "PageAnime" msgid "Set which builtin animations are played" msgstr "选择内置动效" -#: rog-control-center/ui/pages/anime.slint:148 +#: rog-control-center/ui/pages/anime.slint:150 msgctxt "Anime built-in selection" msgid "Boot Animation" msgstr "开机" -#: rog-control-center/ui/pages/anime.slint:158 +#: rog-control-center/ui/pages/anime.slint:160 msgctxt "Anime built-in selection" msgid "Running Animation" msgstr "运行" -#: rog-control-center/ui/pages/anime.slint:168 +#: rog-control-center/ui/pages/anime.slint:170 msgctxt "Anime built-in selection" msgid "Sleep Animation" msgstr "睡眠" -#: rog-control-center/ui/pages/anime.slint:178 +#: rog-control-center/ui/pages/anime.slint:180 msgctxt "Anime built-in selection" msgid "Shutdown Animation" msgstr "关机" -#: rog-control-center/ui/pages/anime.slint:219 +#: rog-control-center/ui/pages/anime.slint:220 msgctxt "PageAnime" msgid "Advanced Display Settings" msgstr "高级显示设置" -#: rog-control-center/ui/pages/anime.slint:224 +#: rog-control-center/ui/pages/anime.slint:225 msgctxt "PageAnime" msgid "Off when lid closed" msgstr "合上盖子时关闭" -#: rog-control-center/ui/pages/anime.slint:233 +#: rog-control-center/ui/pages/anime.slint:234 msgctxt "PageAnime" msgid "Off when suspended" msgstr "睡眠时关闭" -#: rog-control-center/ui/pages/anime.slint:242 +#: rog-control-center/ui/pages/anime.slint:243 msgctxt "PageAnime" msgid "Off when on battery" msgstr "电池供电时关闭" -#: rog-control-center/ui/pages/aura.slint:29 -msgctxt "PageAura" -msgid "Brightness" -msgstr "亮度" +#: rog-control-center/ui/pages/app_settings.slint:26 +msgctxt "PageAppSettings" +msgid "Run in background after closing" +msgstr "保持在后台运行" + +#: rog-control-center/ui/pages/app_settings.slint:34 +msgctxt "PageAppSettings" +msgid "Start app in background (UI closed)" +msgstr "启动至后台" + +#: rog-control-center/ui/pages/app_settings.slint:42 +msgctxt "PageAppSettings" +msgid "Enable system tray icon" +msgstr "启用托盘图标" + +#: rog-control-center/ui/pages/app_settings.slint:50 +msgctxt "PageAppSettings" +msgid "Enable dGPU notifications" +msgstr "启用独显通知" -#: rog-control-center/ui/pages/aura.slint:40 +#: rog-control-center/ui/pages/aura.slint:28 msgctxt "PageAura" msgid "Aura mode" msgstr "Aura 模式" -#: rog-control-center/ui/pages/aura.slint:65 +#: rog-control-center/ui/pages/aura.slint:59 msgctxt "PageAura" msgid "Colour 1" msgstr "颜色 1" -#: rog-control-center/ui/pages/aura.slint:90 +#: rog-control-center/ui/pages/aura.slint:85 msgctxt "PageAura" msgid "Colour 2" msgstr "颜色 2" -#: rog-control-center/ui/pages/aura.slint:120 +#: rog-control-center/ui/pages/aura.slint:119 msgctxt "PageAura" msgid "Zone" msgstr "区域" -#: rog-control-center/ui/pages/aura.slint:141 +#: rog-control-center/ui/pages/aura.slint:142 msgctxt "PageAura" msgid "Direction" msgstr "方向" -#: rog-control-center/ui/pages/aura.slint:162 +#: rog-control-center/ui/pages/aura.slint:164 msgctxt "PageAura" msgid "Speed" msgstr "速度" @@ -178,1078 +194,1001 @@ msgctxt "PageAura" msgid "Power Settings" msgstr "电源设置" -#: rog-control-center/ui/pages/aura.slint:304 +#: rog-control-center/ui/pages/aura.slint:270 msgctxt "PageAura" msgid "Power Zones" msgstr "区域供电" -#: rog-control-center/ui/pages/fans.slint:27 +#: rog-control-center/ui/pages/fans.slint:26 msgctxt "FanTab" msgid "This fan is not avilable on this machine" msgstr "该风扇在本机不可用" -#: rog-control-center/ui/pages/fans.slint:42 +#: rog-control-center/ui/pages/fans.slint:34 msgctxt "FanTab" msgid "Enabled" msgstr "启用" -#: rog-control-center/ui/pages/fans.slint:51 +#: rog-control-center/ui/pages/fans.slint:43 msgctxt "FanTab" msgid "Apply" msgstr "应用" -#: rog-control-center/ui/pages/fans.slint:59 +#: rog-control-center/ui/pages/fans.slint:51 msgctxt "FanTab" msgid "Cancel" msgstr "取消" -#: rog-control-center/ui/pages/fans.slint:67 +#: rog-control-center/ui/pages/fans.slint:59 msgctxt "FanTab" msgid "Factory Default (all fans)" msgstr "全部恢复出厂设置" -#: rog-control-center/ui/pages/fans.slint:80 +#: rog-control-center/ui/pages/fans.slint:72 msgctxt "PageFans" msgid "Balanced" msgstr "平衡" -#: rog-control-center/ui/pages/fans.slint:83 +#: rog-control-center/ui/pages/fans.slint:75 +#: rog-control-center/ui/pages/fans.slint:134 +#: rog-control-center/ui/pages/fans.slint:193 msgctxt "PageFans" msgid "CPU" msgstr "CPU" -#: rog-control-center/ui/pages/fans.slint:101 +#: rog-control-center/ui/pages/fans.slint:93 +#: rog-control-center/ui/pages/fans.slint:152 +#: rog-control-center/ui/pages/fans.slint:211 msgctxt "PageFans" msgid "Mid" msgstr "中部" -#: rog-control-center/ui/pages/fans.slint:119 +#: rog-control-center/ui/pages/fans.slint:111 +#: rog-control-center/ui/pages/fans.slint:170 +#: rog-control-center/ui/pages/fans.slint:229 msgctxt "PageFans" msgid "GPU" msgstr "GPU" -#: rog-control-center/ui/pages/fans.slint:139 +#: rog-control-center/ui/pages/fans.slint:131 msgctxt "PageFans" msgid "Performance" msgstr "性能" -#: rog-control-center/ui/pages/fans.slint:198 +#: rog-control-center/ui/pages/fans.slint:190 msgctxt "PageFans" msgid "Quiet" msgstr "省电" -#: rog-control-center/ui/pages/slash.slint:51 -msgctxt "PageSlash" -msgid "Slash lighting" -msgstr "" - -#: rog-control-center/ui/pages/slash.slint:56 -msgctxt "PageSlash" -msgid "Enable A-cover lighting" -msgstr "" +#: rog-control-center/ui/pages/system.slint:26 +msgctxt "SystemPageData" +msgid "Default" +msgstr "默认" -#: rog-control-center/ui/pages/slash.slint:64 -#, fuzzy -msgctxt "PageSlash" -msgid "Brightness" -msgstr "亮度" +#: rog-control-center/ui/pages/system.slint:31 +msgctxt "SystemPageData" +msgid "BalancePerformance" +msgstr "平衡(侧重性能)" -#: rog-control-center/ui/pages/slash.slint:77 -#, fuzzy -msgctxt "PageSlash" -msgid "Animation speed" -msgstr "开机" +#: rog-control-center/ui/pages/system.slint:32 +msgctxt "SystemPageData" +msgid "BalancePower" +msgstr "平衡(侧重省电)" -#: rog-control-center/ui/pages/slash.slint:90 -#, fuzzy -msgctxt "PageSlash" -msgid "Animation" -msgstr "开机" +#: rog-control-center/ui/pages/system.slint:33 +msgctxt "SystemPageData" +msgid "Power" +msgstr "省电" -#: rog-control-center/ui/pages/slash.slint:110 -msgctxt "PageSlash" -msgid "Visibility triggers" +#: rog-control-center/ui/pages/system.slint:188 +msgctxt "ppt_pl1_spl" +msgid "PL1, sustained power limit" msgstr "" -#: rog-control-center/ui/pages/slash.slint:115 -msgctxt "PageSlash" -msgid "Show on boot" +#: rog-control-center/ui/pages/system.slint:198 +msgctxt "ppt_pl2_sppt" +msgid "PL2, turbo power limit" msgstr "" -#: rog-control-center/ui/pages/slash.slint:123 -#, fuzzy -msgctxt "PageSlash" -msgid "Show on shutdown" -msgstr "关机" - -#: rog-control-center/ui/pages/slash.slint:131 -msgctxt "PageSlash" -msgid "Show on sleep" +#: rog-control-center/ui/pages/system.slint:208 +msgctxt "ppt_fppt" +msgid "FPPT, Fast Power Limit" msgstr "" -#: rog-control-center/ui/pages/slash.slint:139 -#, fuzzy -msgctxt "PageSlash" -msgid "Show on battery" -msgstr "电池供电时关闭" - -#: rog-control-center/ui/pages/slash.slint:147 -msgctxt "PageSlash" -msgid "Show low-battery warning" +#: rog-control-center/ui/pages/system.slint:218 +msgctxt "ppt_apu_sppt" +msgid "SPPT, APU slow power limit" msgstr "" -#: rog-control-center/ui/pages/slash.slint:155 -#, fuzzy -msgctxt "PageSlash" -msgid "Show when lid is closed" -msgstr "合上盖子时关闭" - -#: rog-control-center/ui/pages/slash.slint:170 -msgctxt "Slash_tip" -msgid "" -"Slash Lighting controls the A-cover LED strip found on supported Zephyrus " -"models." -msgstr "Slash 灯效控制支持的 Zephyrus 机型上的 A 面 LED 灯带。" - -#: rog-control-center/ui/pages/battery.slint:24 -#, fuzzy -msgctxt "PageBattery" -msgid "Battery Information" -msgstr "电池信息" - -#: rog-control-center/ui/pages/battery.slint:29 -#, fuzzy -msgctxt "PageBattery" -msgid "Charge limit" -msgstr "充电限制" - -#: rog-control-center/ui/pages/battery.slint:54 -msgctxt "PageBattery" -msgid "Battery Health (Capacity):" -msgstr "电池健康度 (容量):" +#: rog-control-center/ui/pages/system.slint:228 +msgctxt "ppt_platform_sppt" +msgid "Slow package power tracking limit" +msgstr "" -#: rog-control-center/ui/pages/battery.slint:67 +#: rog-control-center/ui/pages/system.slint:238 #, fuzzy -msgctxt "PageBattery" -msgid "Status:" -msgstr "状态:" +msgctxt "nv_dynamic_boost" +msgid "dGPU boost overclock" +msgstr "独显动态增强" -#: rog-control-center/ui/pages/battery.slint:80 -#, fuzzy -msgctxt "PageBattery" -msgid "Power Draw:" -msgstr "功耗:" +#: rog-control-center/ui/pages/system.slint:248 +msgctxt "nv_temp_target" +msgid "dGPU temperature max" +msgstr "独显最高温度" -#: rog-control-center/ui/pages/battery.slint:93 -msgctxt "PageBattery" -msgid "Time to Full:" -msgstr "充满所需时间:" +#: rog-control-center/ui/types/aura_types.slint:49 +msgctxt "Aura power zone" +msgid "Logo" +msgstr "徽标" -#: rog-control-center/ui/pages/battery.slint:93 -msgctxt "PageBattery" -msgid "Time Remaining:" -msgstr "剩余时间:" +#: rog-control-center/ui/types/aura_types.slint:50 +#: rog-control-center/ui/types/aura_types.slint:59 +msgctxt "Aura power zone" +msgid "Keyboard" +msgstr "键盘" -#: rog-control-center/ui/pages/app_settings.slint:27 -msgctxt "PageAppSettings" -msgid "Run in background after closing" -msgstr "保持在后台运行" +#: rog-control-center/ui/types/aura_types.slint:51 +#: rog-control-center/ui/types/aura_types.slint:60 +msgctxt "Aura power zone" +msgid "Lightbar" +msgstr "灯带" -#: rog-control-center/ui/pages/app_settings.slint:35 -msgctxt "PageAppSettings" -msgid "Start app in background (UI closed)" -msgstr "启动至后台" +#: rog-control-center/ui/types/aura_types.slint:52 +msgctxt "Aura power zone" +msgid "Lid" +msgstr "盖子" -#: rog-control-center/ui/pages/app_settings.slint:43 -msgctxt "PageAppSettings" -msgid "Enable system tray icon" -msgstr "启用托盘图标" +#: rog-control-center/ui/types/aura_types.slint:53 +msgctxt "Aura power zone" +msgid "Rear Glow" +msgstr "后灯" -#: rog-control-center/ui/pages/app_settings.slint:51 -msgctxt "PageAppSettings" -msgid "Enable dGPU notifications" -msgstr "启用独显通知" +#: rog-control-center/ui/types/aura_types.slint:54 +#: rog-control-center/ui/types/aura_types.slint:61 +msgctxt "Aura power zone" +msgid "Keyboard and Lightbar" +msgstr "键盘和灯带" -#: rog-control-center/ui/pages/app_settings.slint:59 -msgctxt "PageAppSettings" -msgid "Start app on system startup" -msgstr "系统开机时启动应用" +#: rog-control-center/ui/types/aura_types.slint:64 +msgctxt "Aura brightness" +msgid "Static" +msgstr "恒亮" -#: rog-control-center/ui/pages/app_settings.slint:69 +#: rog-control-center/ui/types/aura_types.slint:73 +#: rog-control-center/ui/types/aura_types.slint:88 #, fuzzy -msgctxt "PageAppSettings" -msgid "Start in background on startup" -msgstr "启动至后台" - -#: rog-control-center/ui/pages/system.slint:21 -msgctxt "SystemPageData" -msgid "Balanced" -msgstr "平衡" - -#: rog-control-center/ui/pages/system.slint:21 -msgctxt "SystemPageData" -msgid "Performance" -msgstr "性能" - -#: rog-control-center/ui/pages/system.slint:21 -msgctxt "SystemPageData" -msgid "Quiet" -msgstr "省电" +msgctxt "Basic aura mode" +msgid "Breathe" +msgstr "呼吸" -#: rog-control-center/ui/pages/system.slint:21 +#: rog-control-center/ui/types/aura_types.slint:74 +#: rog-control-center/ui/types/aura_types.slint:89 #, fuzzy -msgctxt "SystemPageData" -msgid "LowPower" -msgstr "省电" - -#: rog-control-center/ui/pages/system.slint:27 -msgctxt "SystemPageData" -msgid "Default" -msgstr "默认" - -#: rog-control-center/ui/pages/system.slint:29 -msgctxt "SystemPageData" -msgid "BalancePerformance" -msgstr "平衡(侧重性能)" - -#: rog-control-center/ui/pages/system.slint:30 -msgctxt "SystemPageData" -msgid "BalancePower" -msgstr "平衡(侧重省电)" - -#: rog-control-center/ui/pages/system.slint:31 -msgctxt "SystemPageData" -msgid "Power" -msgstr "省电" +msgctxt "Basic aura mode" +msgid "Strobe" +msgstr "频谱" -#. 3-state set per device; the index is the value written over dbus. -#: rog-control-center/ui/pages/system.slint:63 +#: rog-control-center/ui/types/aura_types.slint:75 #, fuzzy -msgctxt "SystemPageData" -msgid "Off" -msgstr "关" +msgctxt "Basic aura mode" +msgid "Rainbow" +msgstr "彩虹" -#. 3-state set per device; the index is the value written over dbus. -#: rog-control-center/ui/pages/system.slint:63 -msgctxt "SystemPageData" -msgid "Multi-zone" -msgstr "多分区" +#: rog-control-center/ui/types/aura_types.slint:76 +#, fuzzy +msgctxt "Basic aura mode" +msgid "Star" +msgstr "星光" -#. 3-state set per device; the index is the value written over dbus. -#: rog-control-center/ui/pages/system.slint:63 -msgctxt "SystemPageData" -msgid "Multi-zone Strong" -msgstr "强效多分区" +#: rog-control-center/ui/types/aura_types.slint:77 +#, fuzzy +msgctxt "Basic aura mode" +msgid "Rain" +msgstr "落雨" -#: rog-control-center/ui/pages/system.slint:207 +#: rog-control-center/ui/types/aura_types.slint:78 #, fuzzy -msgctxt "PageSystem" -msgid "Power settings" -msgstr "电源设置" +msgctxt "Basic aura mode" +msgid "Highlight" +msgstr "高亮" -#: rog-control-center/ui/pages/system.slint:229 -msgctxt "PageSystem" -msgid "Hardware Monitor" -msgstr "硬件监控" +#: rog-control-center/ui/types/aura_types.slint:79 +#, fuzzy +msgctxt "Basic aura mode" +msgid "Laser" +msgstr "激光" -#: rog-control-center/ui/pages/system.slint:242 -msgctxt "PageSystem" -msgid "CPU Status" -msgstr "CPU 状态" +#: rog-control-center/ui/types/aura_types.slint:80 +#, fuzzy +msgctxt "Basic aura mode" +msgid "Ripple" +msgstr "涟漪" -#: rog-control-center/ui/pages/system.slint:251 +#: rog-control-center/ui/types/aura_types.slint:81 #, fuzzy -msgctxt "PageSystem" -msgid "Temperature:" -msgstr "温度:" - -#: rog-control-center/ui/pages/system.slint:264 -msgctxt "PageSystem" -msgid "Usage:" -msgstr "使用率:" - -#: rog-control-center/ui/pages/system.slint:277 -msgctxt "PageSystem" -msgid "Frequency:" -msgstr "频率:" - -#: rog-control-center/ui/pages/system.slint:296 -msgctxt "PageSystem" -msgid "iGPU Status" -msgstr "iGPU 状态" - -#: rog-control-center/ui/pages/system.slint:290 -msgctxt "PageSystem" -msgid "Memory Status" -msgstr "内存状态" - -#: rog-control-center/ui/pages/system.slint:299 -msgctxt "PageSystem" -msgid "RAM Usage:" -msgstr "内存使用率:" - -#: rog-control-center/ui/pages/system.slint:359 -msgctxt "PageSystem" -msgid "dGPU Status" -msgstr "dGPU 状态" - -#: rog-control-center/ui/pages/system.slint:316 -msgctxt "PageSystem" -msgid "GPU Status" -msgstr "GPU 状态" - -#: rog-control-center/ui/pages/system.slint:329 -msgctxt "PageSystem" -msgid "N/A" +msgctxt "Basic aura mode" +msgid "Nothing" msgstr "无" -#: rog-control-center/ui/pages/system.slint:351 +#: rog-control-center/ui/types/aura_types.slint:82 #, fuzzy -msgctxt "PageSystem" -msgid "Fan Speeds" -msgstr "风扇速度" - -#: rog-control-center/ui/pages/system.slint:360 -msgctxt "PageSystem" -msgid "CPU Fan:" -msgstr "CPU 风扇:" - -#: rog-control-center/ui/pages/system.slint:373 -msgctxt "PageSystem" -msgid "GPU Fan:" -msgstr "GPU 风扇:" - -#: rog-control-center/ui/pages/system.slint:386 -msgctxt "PageSystem" -msgid "Mid Fan:" -msgstr "中间风扇:" - -#: rog-control-center/ui/pages/system.slint:403 -msgctxt "PageSystem" -msgid "Platform Profile" -msgstr "" - -#: rog-control-center/ui/pages/system.slint:413 -msgctxt "PageSystem" -msgid "Advanced" -msgstr "高级" - -#: rog-control-center/ui/pages/system.slint:433 -msgctxt "PageSystem" -msgid "Screenpad brightness" -msgstr "" - -#: rog-control-center/ui/pages/system.slint:457 -msgctxt "PageSystem" -msgid "Sync with primary" -msgstr "" +msgctxt "Basic aura mode" +msgid "Pulse" +msgstr "脉冲" -#: rog-control-center/ui/pages/system.slint:477 +#: rog-control-center/ui/types/aura_types.slint:83 #, fuzzy -msgctxt "PageSystem" -msgid "Armoury settings" -msgstr "电源设置" - -#: rog-control-center/ui/pages/system.slint:487 -msgctxt "no_asus_armoury_driver_1" -msgid "The asus-armoury driver is not loaded" -msgstr "" - -#: rog-control-center/ui/pages/system.slint:494 -msgctxt "no_asus_armoury_driver_2" -msgid "For advanced features you will require a kernel with this driver added." -msgstr "" - -#: rog-control-center/ui/pages/system.slint:506 -msgctxt "PageSystem" -msgid "Panel Overdrive" -msgstr "屏幕快显" - -#: rog-control-center/ui/pages/system.slint:514 -msgctxt "PageSystem" -msgid "POST boot sound" -msgstr "POST 开机音效" +msgctxt "Basic aura mode" +msgid "Comet" +msgstr "彗星" -#: rog-control-center/ui/pages/system.slint:522 +#: rog-control-center/ui/types/aura_types.slint:84 #, fuzzy -msgctxt "PageSystem" -msgid "Screen Auto Brightness" -msgstr "亮度" - -#: rog-control-center/ui/pages/system.slint:530 -msgctxt "PageSystem" -msgid "MCU Powersave" -msgstr "MCU 省电模式" +msgctxt "Basic aura mode" +msgid "Flash" +msgstr "闪烁" -#: rog-control-center/ui/pages/system.slint:539 -msgctxt "PageSystem" -msgid "MiniLED Mode" -msgstr "MiniLED 模式" +#: rog-control-center/ui/types/aura_types.slint:96 +msgctxt "Aura zone" +msgid "None" +msgstr "无" -#: rog-control-center/ui/pages/system.slint:557 -msgctxt "ppt_warning" -msgid "The following settings are not applied until the toggle is enabled." -msgstr "" +#: rog-control-center/ui/types/aura_types.slint:97 +msgctxt "Aura zone" +msgid "Key1" +msgstr "按键 1" -#: rog-control-center/ui/pages/system.slint:562 -msgctxt "ppt_group_enabled" -msgid "Enable Tuning" -msgstr "" +#: rog-control-center/ui/types/aura_types.slint:98 +msgctxt "Aura zone" +msgid "Key2" +msgstr "按键 2" -#: rog-control-center/ui/pages/system.slint:585 -msgctxt "requires_fan_curve_warning" -msgid "" -"CPU power limits require an active custom fan curve. Enable a fan curve for " -"the active profile in the Fans tab to unlock these controls." -msgstr "" -"CPU 功耗限制需要启用的自定义风扇曲线。在“风扇”选项卡中为活动配置文件启用风扇" -"曲线以解锁这些控制。" +#: rog-control-center/ui/types/aura_types.slint:99 +msgctxt "Aura zone" +msgid "Key3" +msgstr "按键 3" -#: rog-control-center/ui/pages/system.slint:595 -msgctxt "ppt_pl1_spl" -msgid "CPU Sustained Power Limit" -msgstr "" +#: rog-control-center/ui/types/aura_types.slint:100 +msgctxt "Aura zone" +msgid "Key4" +msgstr "按键 4" -#: rog-control-center/ui/pages/system.slint:597 -msgctxt "ppt_pl1_spl_help" -msgid "" -"Long-term CPU power limit that affects sustained workload performance. " -"Higher values may increase heat and power consumption." -msgstr "" +#: rog-control-center/ui/types/aura_types.slint:101 +msgctxt "Aura zone" +msgid "Lightbar Left" +msgstr "左灯带" -#: rog-control-center/ui/pages/system.slint:613 -msgctxt "ppt_pl2_sppt" -msgid "CPU Turbo Power Limit" -msgstr "" +#: rog-control-center/ui/types/aura_types.slint:103 +msgctxt "Aura zone" +msgid "Lightbar Right" +msgstr "右灯带" -#: rog-control-center/ui/pages/system.slint:615 -msgctxt "ppt_pl2_sppt_help" -msgid "" -"Short-term CPU power limit for boost periods. Controls maximum power during " -"brief high-performance bursts." -msgstr "" +#: rog-control-center/ui/types/aura_types.slint:107 +msgctxt "Aura direction" +msgid "Right" +msgstr "右" -#: rog-control-center/ui/pages/system.slint:631 -msgctxt "ppt_pl3_fppt" -msgid "CPU Fast Burst Power Limit" -msgstr "" +#: rog-control-center/ui/types/aura_types.slint:108 +msgctxt "Aura direction" +msgid "Left" +msgstr "左" -#: rog-control-center/ui/pages/system.slint:633 -msgctxt "ppt_pl3_fppt_help" -msgid "" -"Ultra-short duration power limit for instantaneous CPU bursts. Affects " -"responsiveness during sudden workload spikes." -msgstr "" +#: rog-control-center/ui/types/aura_types.slint:109 +msgctxt "Aura direction" +msgid "Up" +msgstr "上" -#: rog-control-center/ui/pages/system.slint:648 -msgctxt "ppt_fppt" -msgid "Fast Package Power Limit" -msgstr "" +#: rog-control-center/ui/types/aura_types.slint:110 +msgctxt "Aura direction" +msgid "Down" +msgstr "下" -#: rog-control-center/ui/pages/system.slint:650 -msgctxt "ppt_fppt_help" -msgid "" -"Ultra-short duration power limit for system package. Controls maximum power " -"during millisecond-scale load spikes." -msgstr "" +#: rog-control-center/ui/types/aura_types.slint:114 +msgctxt "Aura speed" +msgid "Medium" +msgstr "中" -#: rog-control-center/ui/pages/system.slint:666 -msgctxt "ppt_apu_sppt" -msgid "APU Sustained Power Limit" -msgstr "" +#: rog-control-center/ui/types/aura_types.slint:116 +msgctxt "Aura speed" +msgid "Boot" +msgstr "开机" -#: rog-control-center/ui/pages/system.slint:668 -msgctxt "ppt_apu_sppt_help" -msgid "" -"Long-term power limit for integrated graphics and CPU combined. Affects " -"sustained performance of APU-based workloads." -msgstr "" +#: rog-control-center/ui/widgets/aura_power.slint:43 +msgctxt "AuraPowerGroup" +msgid "Awake" +msgstr "唤醒" -#: rog-control-center/ui/pages/system.slint:684 -msgctxt "ppt_platform_sppt" -msgid "Platform Sustained Power Limit" -msgstr "" +#: rog-control-center/ui/widgets/aura_power.slint:53 +msgctxt "AuraPowerGroup" +msgid "Sleep" +msgstr "睡眠" -#: rog-control-center/ui/pages/system.slint:686 -msgctxt "ppt_platform_sppt_help" -msgid "" -"Overall system power limit for sustained operations. Controls total platform " -"power consumption over extended periods." -msgstr "" +#: rog-control-center/ui/widgets/aura_power.slint:63 +msgctxt "AuraPowerGroup" +msgid "Shutdown" +msgstr "关机" -#: rog-control-center/ui/pages/system.slint:702 -#, fuzzy -msgctxt "nv_dynamic_boost" -msgid "GPU Power Boost" -msgstr "区域供电" +#: rog-control-center/ui/widgets/aura_power.slint:102 +msgctxt "AuraPowerGroupOld" +msgid "Zone Selection" +msgstr "区域" -#: rog-control-center/ui/pages/system.slint:704 -msgctxt "nv_dynamic_boost_help" -msgid "" -"Additional power allocation for GPU dynamic boost. Higher values increase " -"GPU performance but generate more heat." -msgstr "" +#: rog-control-center/ui/widgets/aura_power.slint:114 +msgctxt "AuraPowerGroupOld" +msgid "ROG" +msgstr "ROG" -#: rog-control-center/ui/pages/system.slint:720 -msgctxt "nv_tgp" -msgid "GPU TGP" -msgstr "" +#: rog-control-center/ui/main_window.slint:53 +msgctxt "Menu1" +msgid "System Control" +msgstr "系统设置" -#: rog-control-center/ui/pages/system.slint:722 -msgctxt "nv_tgp_help" -msgid "" -"Total graphics power limit for the dedicated NVIDIA GPU. Higher values can " -"improve performance at the cost of additional heat and power draw." -msgstr "" -"专用 NVIDIA GPU 的总图形功耗限制。较高的值可以提高性能,但会产生额外的热量和" -"功耗。" +#: rog-control-center/ui/main_window.slint:54 +msgctxt "Menu2" +msgid "Keyboard Aura" +msgstr "Aura 键盘灯效" -#: rog-control-center/ui/pages/system.slint:738 -#, fuzzy -msgctxt "nv_temp_target" -msgid "GPU Temperature Limit" -msgstr "独显最高温度" +#: rog-control-center/ui/main_window.slint:55 +msgctxt "Menu3" +msgid "AniMe Matrix" +msgstr "AniMe 光显矩阵" -#: rog-control-center/ui/pages/system.slint:740 -msgctxt "nv_temp_target_help" -msgid "" -"Maximum GPU temperature threshold in Celsius. GPU will throttle to maintain " -"temperature below this limit." -msgstr "" +#: rog-control-center/ui/main_window.slint:56 +msgctxt "Menu4" +msgid "Fan Curves" +msgstr "风扇曲线" -#: rog-control-center/ui/pages/system.slint:794 -msgctxt "PageSystem" -msgid "Energy Performance Preference linked to Throttle Policy" -msgstr "绑定至电源管理方案的 EPP" +msgid "Edit the fan curve for each performance profile and fan — this does not change the active profile." +msgstr "为每个性能模式与风扇编辑风扇曲线——这不会切换当前活动模式。" -#: rog-control-center/ui/pages/system.slint:798 -msgctxt "PageSystem" -msgid "Change EPP based on Throttle Policy" -msgstr "基于电源管理方案更改 EPP" +msgid "This fan is not available on this machine" +msgstr "此风扇在本机上不可用" -#: rog-control-center/ui/pages/system.slint:806 -msgctxt "PageSystem" -msgid "EPP for Balanced Policy" -msgstr "平衡" +#: rog-control-center/ui/main_window.slint:57 +msgctxt "Menu5" +msgid "App Settings" +msgstr "应用设置" -#: rog-control-center/ui/pages/system.slint:816 -msgctxt "PageSystem" -msgid "EPP for Performance Policy" -msgstr "性能" +#: rog-control-center/ui/main_window.slint:58 +msgctxt "Menu6" +msgid "About" +msgstr "关于" -#: rog-control-center/ui/pages/system.slint:826 -msgctxt "PageSystem" -msgid "EPP for " -msgstr "EPP 适用于 " +#: rog-control-center/ui/main_window.slint:70 +msgctxt "MainWindow" +msgid "Quit App" +msgstr "退出" -#: rog-control-center/ui/pages/system.slint:826 -#, fuzzy -msgctxt "PageSystem" -msgid " Policy" -msgstr " 策略" - -#: rog-control-center/ui/pages/system.slint:845 -msgctxt "PageSystem" -msgid "Throttle Policy for power state" -msgstr "基于供电状态的电源管理方案" - -#: rog-control-center/ui/pages/system.slint:851 -msgctxt "PageSystem" -msgid "Throttle Policy on Battery" -msgstr "电池供电" - -#: rog-control-center/ui/pages/system.slint:861 -msgctxt "PageSystem" -msgid "Enabled" -msgstr "启用" +# === New page translations (added 2026-07-27) === +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Home" +msgstr "首页" -#: rog-control-center/ui/pages/system.slint:872 -msgctxt "PageSystem" -msgid "Throttle Policy on AC" -msgstr "交流供电" +#: rog-control-center/ui/pages/aura.slint +msgctxt "PageAura" +msgid "Basic Effects" +msgstr "基本灯效" -#: rog-control-center/ui/pages/about.slint:27 -msgctxt "PageAbout" -msgid "" -"\n" -"A powerful graphical interface for managing ASUS ROG, TUF, and ProArt " -"laptops on Linux. It acts as the official GUI for the asusctl toolset, " -"allowing for seamless hardware tuning without the command line." -msgstr "" -"\n" -"一个在 Linux 上管理华硕 ROG、TUF 和 ProArt 笔记本电脑的强大图形界面。它作为 asusctl 工具集的官方 GUI,实现无需命令行即可无缝调节硬件。" +#: rog-control-center/ui/pages/aura.slint +msgctxt "PageAura" +msgid "Breathing" +msgstr "呼吸" -#: rog-control-center/ui/pages/about.slint:32 -msgctxt "PageAbout" -msgid "Key Features:" -msgstr "主要特性:" +#: rog-control-center/ui/pages/aura.slint +msgctxt "PageAura" +msgid "Color Cycle" +msgstr "色彩循环" -#: rog-control-center/ui/pages/about.slint:36 -msgctxt "PageAbout" -msgid "" -" • Performance: Switch power profiles and customize fan curves.\n" -" • Aura Sync: Control keyboard backlighting and LED effects.\n" -" • Battery Health: Set charge limits to extend battery longevity.\n" -" • Display: Toggle Panel Overdrive and refresh rates.\n" -" • AniMe Matrix: Control AniMe Matrix displays." -msgstr "" -" • 性能:切换电源配置文件并自定义风扇曲线。\n" -" • Aura Sync:控制键盘背光和 LED 效果。\n" -" • 电池健康:设置充电限制以延长电池寿命。\n" -" • 显示:切换面板超频(Panel Overdrive)和刷新率。\n" -" • AniMe Matrix:控制 AniMe Matrix 屏幕。" +#: rog-control-center/ui/pages/aura.slint +msgctxt "PageAura" +msgid "Settings" +msgstr "设置" -#: rog-control-center/ui/pages/about.slint:41 -msgctxt "PageAbout" -msgid "Requirements:" -msgstr "运行要求:" +#: rog-control-center/ui/pages/aura.slint +msgctxt "PageAura" +msgid "Reset Defaults" +msgstr "恢复默认值" -#: rog-control-center/ui/pages/about.slint:46 -msgctxt "PageAbout" -msgid "" -" This software requires kernel version 6.19 or later. However due to " -"active development, the latest kernel is always the recommended one." -msgstr " 此软件需要 6.19 或更高版本的内核。但由于处于活跃开发中,推荐始终使用最新版内核。" +#: rog-control-center/ui/pages/aura.slint +msgctxt "PageAura" +msgid "Color" +msgstr "色彩" -#: rog-control-center/ui/pages/about.slint:51 -msgctxt "PageAbout" -msgid "Work in progress:" -msgstr "正在开发中:" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "This device has no switchable GPU." +msgstr "此设备无可切换 GPU。" -#: rog-control-center/ui/pages/about.slint:55 -msgctxt "PageAbout" -msgid "" -" • Theme the widgets\n" -" • Add a cpu/gpu temp/fan speed info bar\n" -" • Include fan speeds, temps in a bottom bar\n" -" • Screenpad controls\n" -" • ROG Ally specific settings" -msgstr "" -" • 适配小部件主题\n" -" • 添加 CPU/GPU 温度及风扇转速信息栏\n" -" • 在底栏展示风扇转速及温度\n" -" • Screenpad 屏幕键盘控制\n" -" • ROG Ally 特性设置" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Note: Changes take effect after a reboot." +msgstr "注意:更改将在重启后生效。" -#: rog-control-center/ui/pages/about.slint:60 -msgctxt "PageAbout" -msgid "License:" -msgstr "开源协议:" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Disable nvidia-powerd on battery" +msgstr "电池供电时禁用 nvidia-powerd" -#: rog-control-center/ui/pages/about.slint:64 -msgctxt "PageAbout" -msgid "" -" This project is open-source software licensed under the Mozilla Public " -"License 2.0 (MPL-2.0)." -msgstr " 此项目为开源软件,采用 Mozilla Public License 2.0 (MPL-2.0) 授权协议。" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Reserved GPU Memory (iGPU VRAM)" +msgstr "预留 GPU 内存(iGPU 显存)" -#: rog-control-center/ui/pages/about.slint:69 -msgctxt "PageAbout" -msgid "Links:" -msgstr "相关链接:" +#: rog-control-center/ui/pages/battery.slint +msgctxt "PageBattery" +msgid "Battery Information" +msgstr "电池信息" -#: rog-control-center/ui/pages/about.slint:73 -msgctxt "PageAbout" +#: rog-control-center/ui/pages/battery.slint +msgctxt "PageBattery" +msgid "Charge Control" +msgstr "充电控制" + +#: rog-control-center/ui/pages/battery.slint +msgctxt "PageBattery" +msgid "Enable Charge Limit" +msgstr "启用充电限制" + +#: rog-control-center/ui/pages/battery.slint +msgctxt "PageBattery" +msgid "Charge Limit" +msgstr "充电上限" + +#: rog-control-center/ui/pages/battery.slint +msgctxt "PageBattery" msgid "" -" Source Code: https://github.com/OpenGamingCollective/asusctl\n" -" Website: https://asus-linux.org/" -msgstr "" -" 源代码: https://github.com/OpenGamingCollective/asusctl\n" -" 官方网站: https://asus-linux.org/" +"Limiting maximum charge to 80% can significantly extend battery lifespan." +msgstr "限制最大充电量至 80% 可显著延长电池寿命。" -#: rog-control-center/ui/pages/gpu.slint:29 -msgctxt "PageGPU" -msgid "Integrated" -msgstr "集成显卡" +#: rog-control-center/ui/pages/battery.slint +msgctxt "PageBattery" +msgid "Battery Health" +msgstr "电池健康度" -#: rog-control-center/ui/pages/gpu.slint:29 -msgctxt "PageGPU" -msgid "Hybrid" -msgstr "混合显卡" +#: rog-control-center/ui/pages/battery.slint +msgctxt "PageBattery" +msgid "Status" +msgstr "状态" -#: rog-control-center/ui/pages/gpu.slint:29 -msgctxt "PageGPU" -msgid "Ultimate" -msgstr "独显直连" +#: rog-control-center/ui/pages/battery.slint +msgctxt "PageBattery" +msgid "Power Draw" +msgstr "功耗" -#: rog-control-center/ui/pages/gpu.slint:48 -msgctxt "PageGPU" -msgid "Internal/Discrete GPU" -msgstr "内置/独显 GPU" +#: rog-control-center/ui/pages/battery.slint +msgctxt "PageBattery" +msgid "Time Remaining" +msgstr "剩余时间" -#: rog-control-center/ui/pages/gpu.slint:57 -msgctxt "PageGPU" -msgid "This device has no switchable GPU." -msgstr "此设备无双显卡切换功能。" +#: rog-control-center/ui/pages/battery.slint +msgctxt "PageBattery" +msgid "Time to Full" +msgstr "充满时间" -#: rog-control-center/ui/pages/gpu.slint:69 -msgctxt "PageGPU" -msgid "" -"Note: Changes take effect after a reboot. The dropdown always shows the " -"current mode." -msgstr "注意:修改将在重启后生效。下拉菜单始终显示当前模式。" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "General Settings" +msgstr "一般设置" -#: rog-control-center/ui/pages/gpu.slint:76 -msgctxt "PageGPU" -msgid "GPU mode" -msgstr "GPU 模式" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Application Behavior" +msgstr "应用程序行为" -#: rog-control-center/ui/pages/gpu.slint:97 -msgctxt "PageGPU" -msgid "Note: Reserved GPU memory changes take effect after a reboot." -msgstr "注意:保留的 GPU 显存修改将在重启后生效。" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Start app on system startup" +msgstr "开机时启动应用" -#: rog-control-center/ui/pages/gpu.slint:104 -msgctxt "PageGPU" -msgid "Reserved GPU memory" -msgstr "保留的 GPU 显存" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Start in background on startup" +msgstr "启动时后台运行" -#: rog-control-center/ui/pages/gpu.slint:118 -msgctxt "PageGPU" -msgid "XG Mobile LED" -msgstr "XG Mobile 指示灯" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Startup" +msgstr "启动" + +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "WIP: Some features like notifications are not complete." +msgstr "开发中:部分功能(如通知)尚未完成。" + +#: rog-control-center/ui/pages/about.slint +msgctxt "PageAbout" +msgid "Key Features" +msgstr "主要功能" + +#: rog-control-center/ui/pages/about.slint +msgctxt "PageAbout" +msgid "Requirements" +msgstr "系统要求" + +#: rog-control-center/ui/pages/about.slint +msgctxt "PageAbout" +msgid "License" +msgstr "许可证" -#: rog-control-center/ui/pages/gpu.slint:131 +#: rog-control-center/ui/pages/about.slint +msgctxt "PageAbout" +msgid "Links" +msgstr "链接" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Device (0)" +msgstr "设备 (0)" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "No Device Connected" +msgstr "未连接设备" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Ensure your device is properly connected, or visit " +msgstr "确保您的设备已正确连接,或访问 " + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid " for latest products." +msgstr " 获取最新产品。" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Show All" +msgstr "显示全部" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "System Usage" +msgstr "系统使用率" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Frequency" +msgstr "频率" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Usage" +msgstr "使用率" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Temperature" +msgstr "温度" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Fan" +msgstr "风扇" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "CPU Fan" +msgstr "CPU 风扇" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "GPU Fan" +msgstr "GPU 风扇" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "System Fan" +msgstr "系统风扇" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Operation Mode" +msgstr "操作模式" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Edit Manual Mode" +msgstr "编辑手动模式" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Windows" +msgstr "Windows" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Silent" +msgstr "静音" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Turbo" +msgstr " turbo" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Manual" +msgstr "手动" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Max TGP" +msgstr "最大 TGP" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "RAM" +msgstr "RAM" + +#: rog-control-center/ui/pages/home.slint +msgctxt "PageHome" +msgid "Temp" +msgstr "温度" + +#: rog-control-center/ui/widgets/rog.slint +msgctxt "RogSlashMark" +msgid "FPS Monitor" +msgstr "FPS 监视器" + +#: rog-control-center/ui/widgets/rog.slint +msgctxt "RogSlashMark" +msgid "Launch a game to see FPS data" +msgstr "启动游戏以查看 FPS 数据" + +#: rog-control-center/ui/widgets/rog.slint +msgctxt "RogSlashMark" +msgid "Legacy device power zones" +msgstr "旧式设备供电区域" + +#: rog-control-center/ui/widgets/rog.slint +msgctxt "RogSlashMark" +msgid "Click a key on the virtual keyboard below to assign a new function." +msgstr "点击下方虚拟键盘上的按键以分配新功能。" + +#: rog-control-center/ui/pages/gpu.slint msgctxt "PageGPU" -msgid "Disable nvidia-powerd on battery" -msgstr "使用电池时禁用 nvidia-powerd" +msgid "" +"[Recommended] In eco mode, automatically optimizes based on usage scenarios " +"and connected displays." +msgstr "[推荐] 充电模式下,自动为大量显示运算的应用场景自动切换使用独立显卡。" -#: rog-control-center/ui/types/aura_types.slint:54 -msgctxt "Aura power zone" -msgid "Logo" -msgstr "徽标" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Auto-notify when external display is disconnected to save power." +msgstr "当未使用外接显示器情况时允许自动弹出通知,提醒您停止应用程序并节省电量" -#: rog-control-center/ui/types/aura_types.slint:55 -msgctxt "Aura power zone" -msgid "Keyboard" -msgstr "键盘" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Note: Reserved GPU memory changes take effect after a reboot." +msgstr "注意:预留 GPU 内存更改将在重启后生效。" -#: rog-control-center/ui/types/aura_types.slint:56 -msgctxt "Aura power zone" -msgid "Lightbar" -msgstr "灯带" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "No GPU-intensive processes detected." +msgstr "未检测到占用 GPU 的进程。" -#: rog-control-center/ui/types/aura_types.slint:57 -msgctxt "Aura power zone" -msgid "Lid" -msgstr "盖子" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Active Slash Lighting" +msgstr "启用 Slash 灯效" -#: rog-control-center/ui/types/aura_types.slint:58 -msgctxt "Aura power zone" -msgid "Rear Glow" -msgstr "后灯" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Settings" +msgstr "设置" -#: rog-control-center/ui/types/aura_types.slint:59 -msgctxt "Aura power zone" -msgid "Keyboard and Lightbar" -msgstr "键盘和灯带" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Change Theme" +msgstr "变更主题" -#: rog-control-center/ui/types/aura_types.slint:60 -msgctxt "Aura power zone" -msgid "Ally" -msgstr "" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Basic Effects" +msgstr "基本灯效" -#: rog-control-center/ui/types/aura_types.slint:70 -msgctxt "Aura brightness" -msgid "Off" -msgstr "关" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "App Notifications" +msgstr "应用程序通知" -#: rog-control-center/ui/types/aura_types.slint:71 -msgctxt "Aura brightness" -msgid "Low" -msgstr "暗" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Show basic effect on notification" +msgstr "通知时显示基本灯效" -#: rog-control-center/ui/types/aura_types.slint:72 -msgctxt "Aura brightness" -msgid "Med" -msgstr "中" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Select app lighting effect" +msgstr "选择应用灯效" -#: rog-control-center/ui/types/aura_types.slint:73 -msgctxt "Aura brightness" -msgid "High" -msgstr "亮" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "None" +msgstr "无" -#: rog-control-center/ui/types/aura_types.slint:78 -#, fuzzy -msgctxt "Basic aura mode" -msgid "Static" -msgstr "恒亮" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Enable notification sound" +msgstr "开启通知音效" -#: rog-control-center/ui/types/aura_types.slint:79 -#, fuzzy -msgctxt "Basic aura mode" -msgid "Breathe" -msgstr "呼吸" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Get notifications from these sources:" +msgstr "获得来自这些发送来源的通知:" -#: rog-control-center/ui/types/aura_types.slint:80 -#, fuzzy -msgctxt "Basic aura mode" -msgid "Strobe" -msgstr "频谱" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "View Sources" +msgstr "查看发送来源" -#: rog-control-center/ui/types/aura_types.slint:81 -#, fuzzy -msgctxt "Basic aura mode" -msgid "Rainbow" -msgstr "彩虹" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "System Settings" +msgstr "系统设置" -#: rog-control-center/ui/types/aura_types.slint:82 -#, fuzzy -msgctxt "Basic aura mode" -msgid "Star" -msgstr "星光" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Usage Scenarios" +msgstr "使用场景" -#: rog-control-center/ui/types/aura_types.slint:83 -#, fuzzy -msgctxt "Basic aura mode" -msgid "Rain" -msgstr "落雨" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Lighting when unplugged" +msgstr "不插电时亮灯" -#: rog-control-center/ui/types/aura_types.slint:84 -#, fuzzy -msgctxt "Basic aura mode" -msgid "Highlight" -msgstr "高亮" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Keep basic effects on when lid closed" +msgstr "合盖时保持基本灯效启用" -#: rog-control-center/ui/types/aura_types.slint:85 -#, fuzzy -msgctxt "Basic aura mode" -msgid "Laser" -msgstr "激光" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "System Animation Pattern" +msgstr "系统动画模式" -#: rog-control-center/ui/types/aura_types.slint:86 -#, fuzzy -msgctxt "Basic aura mode" -msgid "Ripple" -msgstr "涟漪" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Show boot/shutdown effect" +msgstr "显示开机/关机特效" -#: rog-control-center/ui/types/aura_types.slint:87 -#, fuzzy -msgctxt "Basic aura mode" -msgid "Nothing" -msgstr "无" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Show sleep/wake effect" +msgstr "显示睡眠灯效" -#: rog-control-center/ui/types/aura_types.slint:88 -#, fuzzy -msgctxt "Basic aura mode" -msgid "Pulse" -msgstr "脉冲" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Power & Battery Settings" +msgstr "电源和电池设置" -#: rog-control-center/ui/types/aura_types.slint:89 -#, fuzzy -msgctxt "Basic aura mode" -msgid "Comet" -msgstr "彗星" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Power Saving Mode" +msgstr "省电模式" -#: rog-control-center/ui/types/aura_types.slint:90 -#, fuzzy -msgctxt "Basic aura mode" -msgid "Flash" -msgstr "闪烁" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Power saving mode" +msgstr "省电模式" -#: rog-control-center/ui/types/aura_types.slint:96 -msgctxt "Aura zone" -msgid "None" -msgstr "无" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Battery Animation" +msgstr "电池动画" -#: rog-control-center/ui/types/aura_types.slint:97 -msgctxt "Aura zone" -msgid "Key1" -msgstr "按键 1" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Low battery warning" +msgstr "低电量通知" -#: rog-control-center/ui/types/aura_types.slint:98 -msgctxt "Aura zone" -msgid "Key2" -msgstr "按键 2" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Show battery level" +msgstr "显示电池电量" -#: rog-control-center/ui/types/aura_types.slint:99 -msgctxt "Aura zone" -msgid "Key3" -msgstr "按键 3" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "GPU" +msgstr "GPU" -#: rog-control-center/ui/types/aura_types.slint:100 -msgctxt "Aura zone" -msgid "Key4" -msgstr "按键 4" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Display Mode" +msgstr "显示模式" -#: rog-control-center/ui/types/aura_types.slint:101 -msgctxt "Aura zone" -msgid "Logo" -msgstr "徽标" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Dedicated Output" +msgstr "独显输出" -#: rog-control-center/ui/types/aura_types.slint:102 -msgctxt "Aura zone" -msgid "Lightbar Left" -msgstr "左灯带" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "" +"Uses only the discrete GPU for all rendering. Best performance but higher " +"power consumption." +msgstr "仅使用独立显卡进行渲染,可获得最佳性能,但功耗较高。" -#: rog-control-center/ui/types/aura_types.slint:103 -msgctxt "Aura zone" -msgid "Lightbar Right" -msgstr "右灯带" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Hybrid Output" +msgstr "混合输出" -#: rog-control-center/ui/types/aura_types.slint:106 -msgctxt "Aura direction" -msgid "Right" -msgstr "右" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "" +"[Windows default] Also called MHybrid. Auto-switches between dGPU and iGPU " +"based on workload." +msgstr "" +"[Windows 默认] 也称 MHybrid,根据负载在独立显卡和集成显卡之间自动切换。" -#: rog-control-center/ui/types/aura_types.slint:107 -msgctxt "Aura direction" -msgid "Left" -msgstr "左" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Integrated Mode" +msgstr "集显模式" -#: rog-control-center/ui/types/aura_types.slint:108 -msgctxt "Aura direction" -msgid "Up" -msgstr "上" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "" +"Completely disables discrete GPU for best power saving. Lower temps and " +"noise but significantly reduced performance." +msgstr "" +"完全停用独立显卡以获得最佳节能效果,可降低温度和噪音,但图形性能会明显下降。" -#: rog-control-center/ui/types/aura_types.slint:109 -msgctxt "Aura direction" -msgid "Down" -msgstr "下" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Processes" +msgstr "进程" -#: rog-control-center/ui/types/aura_types.slint:112 -msgctxt "Aura speed" -msgid "Low" -msgstr "慢" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "" +"The following applications are using the GPU. Consider closing them to save " +"power." +msgstr "以下应用程序正在使用 GPU,建议关闭它们以节省电量。" -#: rog-control-center/ui/types/aura_types.slint:113 -msgctxt "Aura speed" -msgid "Medium" -msgstr "中" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Stop All" +msgstr "全部停止" -#: rog-control-center/ui/types/aura_types.slint:114 -msgctxt "Aura speed" -msgid "High" -msgstr "快" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Refresh" +msgstr "刷新" -#: rog-control-center/ui/widgets/aura_power.slint:32 -msgctxt "AuraPowerGroup" -msgid "Boot" -msgstr "开机" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Process" +msgstr "进程" -#: rog-control-center/ui/widgets/aura_power.slint:42 -msgctxt "AuraPowerGroup" -msgid "Awake" -msgstr "唤醒" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "Memory" +msgstr "内存" -#: rog-control-center/ui/widgets/aura_power.slint:52 -msgctxt "AuraPowerGroup" -msgid "Sleep" -msgstr "睡眠" +#: rog-control-center/ui/pages/gpu.slint +msgctxt "PageGPU" +msgid "XG Mobile LED" +msgstr "XG Mobile 灯效" -#: rog-control-center/ui/widgets/aura_power.slint:62 -msgctxt "AuraPowerGroup" -msgid "Shutdown" -msgstr "关机" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Slash Lighting" +msgstr "Slash 灯效" -#: rog-control-center/ui/widgets/aura_power.slint:100 -msgctxt "AuraPowerGroupOld" -msgid "Zone Selection" -msgstr "区域" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Basic Effect" +msgstr "基本灯效" -#: rog-control-center/ui/widgets/aura_power.slint:112 -msgctxt "AuraPowerGroupOld" -msgid "Boot" -msgstr "开机" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Effects" +msgstr "灯效" -#: rog-control-center/ui/widgets/aura_power.slint:122 -msgctxt "AuraPowerGroupOld" -msgid "Awake" -msgstr "唤醒" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Interval" +msgstr "间隔时间" -#: rog-control-center/ui/widgets/aura_power.slint:132 -msgctxt "AuraPowerGroupOld" -msgid "Sleep" -msgstr "睡眠" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Brightness" +msgstr "亮度" -#: rog-control-center/ui/widgets/common.slint:125 -msgctxt "confirm_reset" -msgid "Are you sure you want to reset this?" -msgstr "" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Visibility Triggers" +msgstr "显示时机" -#: rog-control-center/ui/main_window.slint:80 -msgctxt "Menu1" -msgid "System Control" -msgstr "系统设置" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Show on boot" +msgstr "开机时显示" -#: rog-control-center/ui/main_window.slint:81 -msgctxt "Menu2" -msgid "Keyboard Aura" -msgstr "Aura 键盘灯效" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Show on shutdown" +msgstr "关机时显示" -#: rog-control-center/ui/main_window.slint:82 -msgctxt "Menu3" -msgid "AniMe Matrix" -msgstr "AniMe 光显矩阵" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Show on sleep" +msgstr "睡眠时显示" -#: rog-control-center/ui/main_window.slint:83 -msgctxt "Menu4" -msgid "Slash Lighting" -msgstr "Slash 灯带" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Show on battery" +msgstr "电池供电时显示" -#: rog-control-center/ui/main_window.slint:84 -#, fuzzy -msgctxt "Menu5" -msgid "Fan Curves" -msgstr "风扇曲线" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Show low-battery warning" +msgstr "显示低电量警告" -#: rog-control-center/ui/main_window.slint:85 -msgctxt "Menu6" -msgid "GPU Configuration" -msgstr "GPU 配置" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Show when lid is closed" +msgstr "合盖时显示" -#: rog-control-center/ui/main_window.slint:86 -msgctxt "Menu9" -msgid "Battery Info" -msgstr "电池信息" +#: rog-control-center/ui/pages/slash.slint +msgctxt "PageSlash" +msgid "Glitch" +msgstr "故障" -#: rog-control-center/ui/main_window.slint:87 -#, fuzzy -msgctxt "Menu7" -msgid "App Settings" -msgstr "应用设置" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Global Shortcut" +msgstr "全局快捷键" -#: rog-control-center/ui/main_window.slint:88 -#, fuzzy -msgctxt "Menu8" -msgid "About" -msgstr "关于" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Armory/ROG key to show/hide window (keep app running in background)" +msgstr "Armory/ROG 键显示/隐藏窗口(保持应用后台运行)" -#: rog-control-center/ui/main_window.slint:97 -msgctxt "MainWindow" -msgid "Quit App" -msgstr "退出" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Setting up…" +msgstr "设置中…" -#~ msgctxt "PageAppSettings" -#~ msgid "Auto-switch refresh rate on battery" -#~ msgstr "使用电池时自动切换刷新率" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Shortcut active" +msgstr "快捷键已激活" -#, fuzzy -#~ msgctxt "PageSystem" -#~ msgid "Display Settings" -#~ msgstr "显示设置" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "No shortcut assigned" +msgstr "未分配快捷键" -#~ msgctxt "PageSystem" -#~ msgid "Refresh Rate:" -#~ msgstr "刷新率:" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Global shortcuts unavailable" +msgstr "全局快捷键不可用" -#~ msgctxt "PageSystem" -#~ msgid "Base system settings" -#~ msgstr "基本设置" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Set up shortcut" +msgstr "设置快捷键" -#~ msgctxt "PageSystem" -#~ msgid "System performance settings" -#~ msgstr "性能设置" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Configure shortcut" +msgstr "配置快捷键" -#, fuzzy -#~ msgctxt "nv_dynamic_boost" -#~ msgid "dGPU boost overclock" -#~ msgstr "独显动态增强" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Language" +msgstr "语言" -#~ msgctxt "PageSystem" -#~ msgid "EPP for Quiet Policy" -#~ msgstr "省电" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Restart required for the new language to take effect." +msgstr "需要重启后新语言才会生效。" -#~ msgctxt "MainWindow" -#~ msgid "ROG" -#~ msgstr "ROG" +#: rog-control-center/ui/pages/app_settings.slint +msgctxt "PageAppSettings" +msgid "Reload Window" +msgstr "重载窗口" diff --git a/rog-control-center/ui/globals.slint b/rog-control-center/ui/globals.slint index 17367644..602a4cc3 100644 --- a/rog-control-center/ui/globals.slint +++ b/rog-control-center/ui/globals.slint @@ -1,8 +1,98 @@ +// rog-control-center — Armoury Crate-style design tokens (rebuilt from scratch). +// Colour values are reverse-engineered from AC 6.5.7 (SVG stats + dll hex), not eyeballed. + export global AppSize { - out property width: 900px; - out property height: 500px; + out property width: 1000px; + out property height: 640px; } -export global IconImages { - //out property two_t: @image-url("images/parameters/2t.png"); +export global Theme { + // — Brand pair (AC red + blue) — + out property rog-red: #FF1929; // brand / selected / CTA + out property rog-blue: #148AC4; // secondary brand / hover / progress + + // — Icon / interaction states (AC _n/_h/_p/_d) — + out property fg-normal: #F2F2F2; + out property fg-hover: #148AC4; + out property fg-pressed: #FF1929; + out property fg-disabled: #4D4D4D; + + // — Text hierarchy — + out property text-primary: #F2F2F2; + out property text-secondary: #B3B3B3; + out property text-dim: #6A6A6A; + + // — Background layers (AC dark base, lighter cards: window near-black, cards a touch lighter) — + out property bg-base: #0A0A0A; // window base (near-black) + out property bg-panel: #070707; // sidebar / panels (darkest) + out property bg-card: #161616; // cards (lighter than base) + out property bg-elevated: #242424; // raised / inputs / hover + out property bg-preview: #100d0f; // light-bar preview panel (slash page warm-black) + + // — Status colours — + out property state-good: #4CAF50; // green: ok + out property state-warn: #FF9E1B; // orange: warning + out property state-danger: #FF1929; // red: danger + out property state-accent: #E6A81D; // gold: accent + + // — Dark variants (glow / shadow) — + out property red-deep: #4D080C; + out property blue-deep: #08364C; + out property rog-red-glow: #55FF1929; + out property rog-blue-glow: #55148AC4; + + // — Dividers (very subtle) — + out property divider: #2A2A2A; + out property line: #575757; // progress track / strong divider (AC HTML) + out property line-soft: #343434; + + // — Meter gradient (AC meter purple → red) — + out property meter-start: #6437FF; // progress bar purple end + out property meter-end: #FF1929; // progress bar red end (= rog-red) + + // — Chrome (from AC HTML) — + out property titlebar-bg: #181818; + out property sidebar-bg: #161616; + out property sidebar-selected: #5A2025; // selected nav item dark-red fill + + // — Operation-mode selected state — + out property mode-selected: #2A1114; + out property mode-selected-strong: #4D1117; + + // — Legacy compat for common.slint (kept for the transition, not for the new look) — + out property border: #2A2A2A; + out property border-accent: #FF1929; + out property bg-app: #1A1A1A; + out property bg-card-hover: #2E2E2E; + out property accent: #FF1929; + out property accent-soft: #FF5566; + out property accent-glow: #55FF1929; + out property success: #4CAF50; + out property warn: #FF9E1B; + out property danger: #FF1929; + + // — Corner radii — + out property radius-card: 8px; + out property radius-item: 4px; + out property radius-pill: 14px; + + // — Spacing — + out property space-xs: 4px; + out property space-sm: 8px; + out property space: 12px; + out property space-lg: 20px; + out property pad-card: 16px; + + // — Font sizes — + out property font-xs: 11px; + out property font-sm: 13px; + out property font-md: 15px; + out property font-lg: 20px; + out property font-xl: 28px; + out property font-2xl: 38px; // dashboard big number + + // Temperature threshold colour (>80 red / >65 orange / else green) — pure, usable in bindings + public pure function temp-color(temp: float) -> color { + return temp > 80.0 ? #FF1929 : (temp > 65.0 ? #FF9E1B : #4CAF50); + } } diff --git a/rog-control-center/ui/images/effect-breathing.svg b/rog-control-center/ui/images/effect-breathing.svg new file mode 100644 index 00000000..4dd9d15e --- /dev/null +++ b/rog-control-center/ui/images/effect-breathing.svg @@ -0,0 +1,4 @@ + + + + diff --git a/rog-control-center/ui/images/effect-cycle.svg b/rog-control-center/ui/images/effect-cycle.svg new file mode 100644 index 00000000..b63f850f --- /dev/null +++ b/rog-control-center/ui/images/effect-cycle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/rog-control-center/ui/images/effect-flash.svg b/rog-control-center/ui/images/effect-flash.svg new file mode 100644 index 00000000..d0cfc867 --- /dev/null +++ b/rog-control-center/ui/images/effect-flash.svg @@ -0,0 +1,3 @@ + + + diff --git a/rog-control-center/ui/images/effect-static.svg b/rog-control-center/ui/images/effect-static.svg new file mode 100644 index 00000000..a4d6d9af --- /dev/null +++ b/rog-control-center/ui/images/effect-static.svg @@ -0,0 +1,4 @@ + + + + diff --git a/rog-control-center/ui/images/ic-battery.svg b/rog-control-center/ui/images/ic-battery.svg new file mode 100644 index 00000000..d83ac0b6 --- /dev/null +++ b/rog-control-center/ui/images/ic-battery.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/ic-cpu-fill.svg b/rog-control-center/ui/images/ic-cpu-fill.svg new file mode 100644 index 00000000..6016fc67 --- /dev/null +++ b/rog-control-center/ui/images/ic-cpu-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/ic-cpu.svg b/rog-control-center/ui/images/ic-cpu.svg new file mode 100644 index 00000000..a69c58ca --- /dev/null +++ b/rog-control-center/ui/images/ic-cpu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/ic-fan.svg b/rog-control-center/ui/images/ic-fan.svg new file mode 100644 index 00000000..990bcce1 --- /dev/null +++ b/rog-control-center/ui/images/ic-fan.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/ic-gpu-dedicated.svg b/rog-control-center/ui/images/ic-gpu-dedicated.svg new file mode 100644 index 00000000..b9f7957c --- /dev/null +++ b/rog-control-center/ui/images/ic-gpu-dedicated.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/rog-control-center/ui/images/ic-gpu-hybrid.svg b/rog-control-center/ui/images/ic-gpu-hybrid.svg new file mode 100644 index 00000000..a51e4f36 --- /dev/null +++ b/rog-control-center/ui/images/ic-gpu-hybrid.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/rog-control-center/ui/images/ic-gpu-integrated.svg b/rog-control-center/ui/images/ic-gpu-integrated.svg new file mode 100644 index 00000000..b1ac0fa1 --- /dev/null +++ b/rog-control-center/ui/images/ic-gpu-integrated.svg @@ -0,0 +1,7 @@ + + + i + + + + \ No newline at end of file diff --git a/rog-control-center/ui/images/ic-gpu.svg b/rog-control-center/ui/images/ic-gpu.svg new file mode 100644 index 00000000..d660b234 --- /dev/null +++ b/rog-control-center/ui/images/ic-gpu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/ic-hz.svg b/rog-control-center/ui/images/ic-hz.svg new file mode 100644 index 00000000..40def55e --- /dev/null +++ b/rog-control-center/ui/images/ic-hz.svg @@ -0,0 +1,6 @@ + + + + + HZ + diff --git a/rog-control-center/ui/images/ic-moon.svg b/rog-control-center/ui/images/ic-moon.svg new file mode 100644 index 00000000..5729a87b --- /dev/null +++ b/rog-control-center/ui/images/ic-moon.svg @@ -0,0 +1,3 @@ + + + diff --git a/rog-control-center/ui/images/ic-speaker.svg b/rog-control-center/ui/images/ic-speaker.svg new file mode 100644 index 00000000..391c943e --- /dev/null +++ b/rog-control-center/ui/images/ic-speaker.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/rog-control-center/ui/images/ic-sync.svg b/rog-control-center/ui/images/ic-sync.svg new file mode 100644 index 00000000..9776a3af --- /dev/null +++ b/rog-control-center/ui/images/ic-sync.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/rog-control-center/ui/images/ic-temp.svg b/rog-control-center/ui/images/ic-temp.svg new file mode 100644 index 00000000..5c56a9b0 --- /dev/null +++ b/rog-control-center/ui/images/ic-temp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/ic-touchpad.svg b/rog-control-center/ui/images/ic-touchpad.svg new file mode 100644 index 00000000..e1bc2504 --- /dev/null +++ b/rog-control-center/ui/images/ic-touchpad.svg @@ -0,0 +1,4 @@ + + + + diff --git a/rog-control-center/ui/images/ic-windows.svg b/rog-control-center/ui/images/ic-windows.svg new file mode 100644 index 00000000..bac880ae --- /dev/null +++ b/rog-control-center/ui/images/ic-windows.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/rog-control-center/ui/images/laptop-placeholder.svg b/rog-control-center/ui/images/laptop-placeholder.svg new file mode 100644 index 00000000..63c40f95 --- /dev/null +++ b/rog-control-center/ui/images/laptop-placeholder.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rog-control-center/ui/images/mode-manual.svg b/rog-control-center/ui/images/mode-manual.svg new file mode 100644 index 00000000..de5d127c --- /dev/null +++ b/rog-control-center/ui/images/mode-manual.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/mode-performance.svg b/rog-control-center/ui/images/mode-performance.svg new file mode 100644 index 00000000..c1125241 --- /dev/null +++ b/rog-control-center/ui/images/mode-performance.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/mode-silent.svg b/rog-control-center/ui/images/mode-silent.svg new file mode 100644 index 00000000..efb3f089 --- /dev/null +++ b/rog-control-center/ui/images/mode-silent.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/mode-turbo.svg b/rog-control-center/ui/images/mode-turbo.svg new file mode 100644 index 00000000..624d06fb --- /dev/null +++ b/rog-control-center/ui/images/mode-turbo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/mode-windows.svg b/rog-control-center/ui/images/mode-windows.svg new file mode 100644 index 00000000..3aafe8b3 --- /dev/null +++ b/rog-control-center/ui/images/mode-windows.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/nav-about.svg b/rog-control-center/ui/images/nav-about.svg new file mode 100644 index 00000000..edbf7a42 --- /dev/null +++ b/rog-control-center/ui/images/nav-about.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/nav-apps.svg b/rog-control-center/ui/images/nav-apps.svg new file mode 100644 index 00000000..21e598f6 --- /dev/null +++ b/rog-control-center/ui/images/nav-apps.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/nav-battery.svg b/rog-control-center/ui/images/nav-battery.svg new file mode 100644 index 00000000..b687f214 --- /dev/null +++ b/rog-control-center/ui/images/nav-battery.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/nav-fan.svg b/rog-control-center/ui/images/nav-fan.svg new file mode 100644 index 00000000..acd1edd2 --- /dev/null +++ b/rog-control-center/ui/images/nav-fan.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/nav-gpu.svg b/rog-control-center/ui/images/nav-gpu.svg new file mode 100644 index 00000000..6016fc67 --- /dev/null +++ b/rog-control-center/ui/images/nav-gpu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/nav-home.svg b/rog-control-center/ui/images/nav-home.svg new file mode 100644 index 00000000..6fa0dfe6 --- /dev/null +++ b/rog-control-center/ui/images/nav-home.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/nav-keyboard.svg b/rog-control-center/ui/images/nav-keyboard.svg new file mode 100644 index 00000000..765cb466 --- /dev/null +++ b/rog-control-center/ui/images/nav-keyboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/nav-matrix.svg b/rog-control-center/ui/images/nav-matrix.svg new file mode 100644 index 00000000..3bf1cdeb --- /dev/null +++ b/rog-control-center/ui/images/nav-matrix.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/nav-slash.svg b/rog-control-center/ui/images/nav-slash.svg new file mode 100644 index 00000000..c385d374 --- /dev/null +++ b/rog-control-center/ui/images/nav-slash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rog-control-center/ui/images/slash-bar.svg b/rog-control-center/ui/images/slash-bar.svg new file mode 100644 index 00000000..706b3f89 --- /dev/null +++ b/rog-control-center/ui/images/slash-bar.svg @@ -0,0 +1 @@ + diff --git a/rog-control-center/ui/images/slash-device-shell.svg b/rog-control-center/ui/images/slash-device-shell.svg new file mode 100644 index 00000000..e0d7dea7 --- /dev/null +++ b/rog-control-center/ui/images/slash-device-shell.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rog-control-center/ui/main_window.slint b/rog-control-center/ui/main_window.slint index 85b8473a..a82d588c 100644 --- a/rog-control-center/ui/main_window.slint +++ b/rog-control-center/ui/main_window.slint @@ -1,6 +1,7 @@ import { Palette, Button, VerticalBox } from "std-widgets.slint"; -import { AppSize } from "globals.slint"; -import { PageSystem, SystemPageData, AttrMinMax } from "pages/system.slint"; +import { AppSize, Theme } from "globals.slint"; +import { SystemPageData, AttrMinMax } from "pages/system.slint"; +import { PageHome } from "pages/home.slint"; import { SideBar } from "widgets/sidebar.slint"; import { PageAbout } from "pages/about.slint"; import { PageFans } from "pages/fans.slint"; @@ -22,6 +23,7 @@ export { AppSize, AttrMinMax, SystemPageData, AnimePageData, SlashPageData, AppS export component MainWindow inherits Window { title: "ROG Control"; + in property app-version: ""; always-on-top: true; default-font-family: "Noto Sans"; default-font-size: 14px; @@ -38,10 +40,8 @@ export component MainWindow inherits Window { true, // App Settings true, // About ]; - private property show_notif; // TODO: change if TUF on the logo in the menu on the main page in property is_tuf: false; - private property fade_cover; private property toast: false; private property toast_text: "I show when something is waiting"; callback show_toast(string); @@ -57,25 +57,20 @@ export component MainWindow inherits Window { } } callback exit-app(); - callback show_notification(bool); - show_notification(yes) => { - show_notif = yes; - fade_cover = yes; - } callback external_colour_change(); external_colour_change() => { aura.external_colour_change(); - aura.external_colour_change(); } min-height: AppSize.height; min-width: AppSize.width; - background: Palette.alternate-background; + background: Theme.bg-base; HorizontalLayout { padding: 0px; VerticalLayout { padding: 0px; side-bar := SideBar { + quit-clicked => { root.exit-app(); } model: [ @tr("Menu1" => "System Control"), @tr("Menu2" => "Keyboard Aura"), @@ -91,69 +86,80 @@ export component MainWindow inherits Window { is_tuf: root.is_tuf; } - Button { - height: 40px; - width: side-bar.width; - text: @tr("Quit App"); - clicked() => { - root.exit-app(); - } - } + // Quit button lives inside SideBar now (quit-clicked callback). } Rectangle { - background: Palette.background; - if(side-bar.current-item == 0): page := PageSystem { - width: root.width - side-bar.width; - height: root.height + 12px; - } - - /*if(side-bar.current-item == 1):*/ aura := PageAura { + background: Theme.bg-base; + // All pages stay mounted; the active one is driven to opacity 1, + // others to 0, with a crossfade. `visible` keeps a page rendered + // only while it is active or still fading out, so inactive pages + // cost nothing to draw. + page := PageHome { + // Responsive width: fill small windows, cap at 1100px and center on large ones. + width: root.width - side-bar.width > 1100px ? 1100px : root.width - side-bar.width; + height: root.height; + x: root.width - side-bar.width > 1100px ? ((root.width - side-bar.width) - 1100px) / 2 : 0px; + opacity: side-bar.current-item == 0 ? 1 : 0; + visible: side-bar.current-item == 0 || self.opacity > 0; + } + + aura := PageAura { width: root.width - side-bar.width; height: root.height + 12px; - visible: side-bar.current-item == 1; + opacity: side-bar.current-item == 1 ? 1 : 0; + visible: side-bar.current-item == 1 || self.opacity > 0; } - if(side-bar.current-item == 2): PageAnime { + PageAnime { width: root.width - side-bar.width; height: root.height + 12px; - visible: side-bar.current-item == 2; + opacity: side-bar.current-item == 2 ? 1 : 0; + visible: side-bar.current-item == 2 || self.opacity > 0; } - if(side-bar.current-item == 3): PageSlash { + PageSlash { width: root.width - side-bar.width; height: root.height + 12px; - visible: side-bar.current-item == 3; + page-active: side-bar.current-item == 3; + opacity: side-bar.current-item == 3 ? 1 : 0; + visible: side-bar.current-item == 3 || self.opacity > 0; } - if(side-bar.current-item == 4): fans := PageFans { + fans := PageFans { width: root.width - side-bar.width; height: root.height + 12px; - visible: side-bar.current-item == 4; + opacity: side-bar.current-item == 4 ? 1 : 0; + visible: side-bar.current-item == 4 || self.opacity > 0; } - if(side-bar.current-item == 5): PageGPU { + PageGPU { width: root.width - side-bar.width; height: root.height + 12px; - visible: side-bar.current-item == 5; + opacity: side-bar.current-item == 5 ? 1 : 0; + visible: side-bar.current-item == 5 || self.opacity > 0; } - if(side-bar.current-item == 6): PageBattery { + PageBattery { width: root.width - side-bar.width; height: root.height + 12px; - visible: side-bar.current-item == 6; + opacity: side-bar.current-item == 6 ? 1 : 0; + visible: side-bar.current-item == 6 || self.opacity > 0; } - if(side-bar.current-item == 7): PageAppSettings { + PageAppSettings { width: root.width - side-bar.width; height: root.height + 12px; - visible: side-bar.current-item == 7; + opacity: side-bar.current-item == 7 ? 1 : 0; + visible: side-bar.current-item == 7 || self.opacity > 0; } - if(side-bar.current-item == 8): PageAbout { + PageAbout { width: root.width - side-bar.width; height: root.height + 12px; - visible: side-bar.current-item == 8; + app-version: root.app-version; + opacity: side-bar.current-item == 8 ? 1 : 0; + visible: side-bar.current-item == 8 || self.opacity > 0; } if toast: Rectangle { @@ -185,53 +191,6 @@ export component MainWindow inherits Window { } } - if fade_cover: Rectangle { - x: 0px; - y: 0px; - width: root.width; - height: root.height; - background: Colors.rgba(25,33,23,20); - opacity: 0.7; - TouchArea { - height: 100%; - width: 100%; - clicked => { - // toolbar-dropdown.close(); - if (show_notif) { - show_notif = false; - } - fade_cover = false; - } - } - } - - - // // TODO: or use Dialogue - if show_notif: Rectangle { - x: root.width / 8; - y: root.height / 8; - height: (root.height / 8) * 6; - width: (root.width / 8) * 6; - TouchArea { - height: 100%; - width: 100%; - clicked => { - show_notif = false; - exit-app(); - } - } - - // TODO: add properties to display - Rectangle { - height: 100%; - width: 100%; - background: Palette.background; - Text { - text: "Click here to exit"; - } - } - } - if SomeError.error_message != "": Rectangle { x: 0px; y: 0px; diff --git a/rog-control-center/ui/pages/about.slint b/rog-control-center/ui/pages/about.slint index 99f9cf78..4c289a67 100644 --- a/rog-control-center/ui/pages/about.slint +++ b/rog-control-center/ui/pages/about.slint @@ -1,76 +1,171 @@ -import { - AboutSlint, - VerticalBox, - HorizontalBox, - ScrollView, -} from "std-widgets.slint"; +// PageAbout — About page in AC style +// Layout: titlebar + centered info card +// Pattern: alignment:start, no ScrollView, natural height +import { RogCard, RogSlashMark, RogPage } from "../widgets/rog.slint"; +import { Theme } from "../globals.slint"; -export component PageAbout inherits VerticalLayout { - padding: 10px; - spacing: 10px; - ScrollView { +export component PageAbout inherits RogPage { + in property app-version: ""; + background: Theme.bg-base; + clip: true; - HorizontalBox { - alignment: LayoutAlignment.center; - VerticalBox { - alignment: LayoutAlignment.center; + // ====== Titlebar ====== + titlebar := Rectangle { + y: 0px; + height: 52px; + width: 100%; + background: Theme.bg-base; + HorizontalLayout { + spacing: 12px; + x: 24px; + y: (parent.height - 24px) / 2; + height: 24px; + Text { + text: @tr("About"); + color: Theme.text-primary; + font-size: 20px; + font-weight: 700; + vertical-alignment: center; + } + } + } - Text { - vertical-alignment: TextVerticalAlignment.center; - horizontal-alignment: TextHorizontalAlignment.center; - text: "ROG Control Center"; - font-size: 22px; - } + // ====== Content area ====== + content := Rectangle { + y: titlebar.height; + width: 100%; - Text { - wrap: TextWrap.word-wrap; - text: @tr("\nA powerful graphical interface for managing ASUS ROG, TUF, and ProArt laptops on Linux. It acts as the official GUI for the asusctl toolset, allowing for seamless hardware tuning without the command line."); - } + VerticalLayout { + padding-left: 24px; + padding-right: 24px; + padding-top: 12px; + padding-bottom: 24px; + spacing: 20px; + alignment: start; - Text { - font-weight: 900; - text: @tr("Key Features:"); - } + // Centered about card + Rectangle { + width: 100%; + HorizontalLayout { + Rectangle { horizontal-stretch: 1; } + RogCard { + width: min(560px, root.width - 100px); + VerticalLayout { + padding: 28px; + spacing: 16px; + alignment: center; - Text { - text: @tr(" • Performance: Switch power profiles and customize fan curves.\n • Aura Sync: Control keyboard backlighting and LED effects.\n • Battery Health: Set charge limits to extend battery longevity.\n • Display: Toggle Panel Overdrive and refresh rates.\n • AniMe Matrix: Control AniMe Matrix displays."); - } + // App name + version + Text { + text: "ROG Control Center"; + color: Theme.text-primary; + font-size: 24px; + font-weight: 800; + font-family: "Inter Display"; + horizontal-alignment: center; + } - Text { - font-weight: 900; - text: @tr("Requirements:"); - } + Text { + text: root.app-version; + color: Theme.text-dim; + font-size: 12px; + horizontal-alignment: center; + } - Text { - wrap: TextWrap.word-wrap; - text: @tr(" This software requires kernel version 6.19 or later. However due to active development, the latest kernel is always the recommended one."); - } + Rectangle { width: 120px; height: 2px; background: Theme.rog-red; border-radius: 1px; } - Text { - font-weight: 900; - text: @tr("Work in progress:"); - } + // Description + Text { + text: @tr("A powerful graphical interface for managing ASUS ROG, TUF, and ProArt laptops on Linux."); + color: Theme.text-secondary; + font-size: 13px; + horizontal-alignment: center; + wrap: word-wrap; + } - Text { - text: @tr(" • Theme the widgets\n • Add a cpu/gpu temp/fan speed info bar\n • Include fan speeds, temps in a bottom bar\n • Screenpad controls\n • ROG Ally specific settings"); - } + Text { + text: @tr("Official GUI for the asusctl toolset — seamless hardware tuning without the command line."); + color: Theme.text-dim; + font-size: 11px; + horizontal-alignment: center; + wrap: word-wrap; + } - Text { - font-weight: 900; - text: @tr("License:"); - } + Rectangle { width: 100%; height: 1px; background: Theme.line-soft; } - Text { - text: @tr(" This project is open-source software licensed under the Mozilla Public License 2.0 (MPL-2.0)."); - } + // Key Features + Text { + text: @tr("Key Features"); + color: Theme.text-primary; + font-size: 14px; + font-weight: 700; + horizontal-alignment: left; + width: 100%; + } + Text { + text: @tr("Performance: Switch power profiles and customize fan curves.\nAura Sync: Control keyboard backlighting and LED effects.\nBattery Health: Set charge limits to extend battery longevity.\nDisplay: Toggle Panel Overdrive and refresh rates.\nAniMe Matrix: Control AniMe Matrix displays."); + color: Theme.text-secondary; + font-size: 12px; + horizontal-alignment: left; + width: 100%; + wrap: word-wrap; + } - Text { - font-weight: 900; - text: @tr("Links:"); - } + // Requirements + Text { + text: @tr("Requirements"); + color: Theme.text-primary; + font-size: 14px; + font-weight: 700; + horizontal-alignment: left; + width: 100%; + } + Text { + text: @tr("Kernel 6.19+ recommended (latest kernel always preferred). asusctl/asusd daemon required for hardware control."); + color: Theme.text-secondary; + font-size: 12px; + horizontal-alignment: left; + width: 100%; + wrap: word-wrap; + } + + // License + Text { + text: @tr("License"); + color: Theme.text-primary; + font-size: 14px; + font-weight: 700; + horizontal-alignment: left; + width: 100%; + } + Text { + text: @tr("Mozilla Public License 2.0 (MPL-2.0). Open source software by OpenGamingCollective."); + color: Theme.text-secondary; + font-size: 12px; + horizontal-alignment: left; + width: 100%; + wrap: word-wrap; + } - Text { - text: @tr(" Source Code: https://github.com/OpenGamingCollective/asusctl\n Website: https://asus-linux.org/"); + // Links + Text { + text: @tr("Links"); + color: Theme.text-primary; + font-size: 14px; + font-weight: 700; + horizontal-alignment: left; + width: 100%; + } + Text { + text: "github.com/OpenGamingCollective/asusctl\nasus-linux.org"; + color: Theme.rog-blue; + font-size: 12px; + horizontal-alignment: left; + width: 100%; + } + } + } + Rectangle { horizontal-stretch: 1; } } } } diff --git a/rog-control-center/ui/pages/anime.slint b/rog-control-center/ui/pages/anime.slint index 7eb28898..cde37aa9 100644 --- a/rog-control-center/ui/pages/anime.slint +++ b/rog-control-center/ui/pages/anime.slint @@ -1,5 +1,6 @@ import { SystemDropdown, SystemToggle } from "../widgets/common.slint"; import { Palette, GroupBox, VerticalBox, Button, HorizontalBox } from "std-widgets.slint"; +import { RogPage } from "../widgets/rog.slint"; export global AnimePageData { in-out property <[string]> brightness_names: [ @@ -31,7 +32,7 @@ export global AnimePageData { callback cb_builtin_animations(int, int, int, int); } -export component PageAnime inherits Rectangle { +export component PageAnime inherits RogPage { property show_fade_cover: false; property show_display_advanced: false; property show_builtin_advanced: false; diff --git a/rog-control-center/ui/pages/app_settings.slint b/rog-control-center/ui/pages/app_settings.slint index 959bd8ff..0bd59dda 100644 --- a/rog-control-center/ui/pages/app_settings.slint +++ b/rog-control-center/ui/pages/app_settings.slint @@ -1,5 +1,8 @@ -import { Palette, Button } from "std-widgets.slint"; -import { SystemToggle } from "../widgets/common.slint"; +// PageAppSettings — Application Settings (AC style) +// All upstream features preserved (background/tray/autostart toggles, global +// shortcut status machine, language selector), restyled with RogCard/RogToggle. +import { RogCard, RogToggle, RogSlashMark, RogDropdownRow, RogPage } from "../widgets/rog.slint"; +import { Theme } from "../globals.slint"; export enum GlobalShortcutStatus { disabled, @@ -32,114 +35,199 @@ export global AppSettingsPageData { in-out property global_shortcut_configurable; // Routes by status: Bind on first use, Configure when active. callback manage_global_shortcut(); + + // Language selector (index into the language list shown in the UI). + in-out property current_language: 0; + // Locale codes discovered at startup (translations/ + /usr/share/locale), + // so new translations show up in the picker without code changes. + in-out property <[string]> available_languages: ["en"]; + callback cb_change_language(int); + // Re-launch the app so the new language takes effect (@tr is resolved at + // window build time, so the process must restart to re-resolve). + callback cb_reload_window(); } -export component PageAppSettings inherits VerticalLayout { - Rectangle { - clip: true; +export component PageAppSettings inherits RogPage { + background: Theme.bg-base; + clip: true; - mainview := VerticalLayout { - padding: 10px; - spacing: 10px; - SystemToggle { - text: @tr("Run in background after closing"); - checked <=> AppSettingsPageData.run_in_background; - toggled => { - AppSettingsPageData.set_run_in_background(AppSettingsPageData.run_in_background) - } - } - - SystemToggle { - text: @tr("Start app in background (UI closed)"); - checked <=> AppSettingsPageData.startup_in_background; - toggled => { - AppSettingsPageData.set_startup_in_background(AppSettingsPageData.startup_in_background) - } - } - - SystemToggle { - text: @tr("Enable system tray icon"); - checked <=> AppSettingsPageData.enable_tray_icon; - toggled => { - AppSettingsPageData.set_enable_tray_icon(AppSettingsPageData.enable_tray_icon) - } + // ====== Titlebar ====== + titlebar := Rectangle { + y: 0px; + height: 52px; + width: 100%; + background: Theme.bg-base; + HorizontalLayout { + x: 24px; + y: (parent.height - 24px) / 2; + height: 24px; + spacing: 12px; + Text { + text: @tr("App Settings"); + color: Theme.text-primary; + font-size: 20px; + font-weight: 700; + vertical-alignment: center; } + } + } - SystemToggle { - text: @tr("Enable dGPU notifications"); - checked <=> AppSettingsPageData.enable_dgpu_notifications; - toggled => { - AppSettingsPageData.set_enable_dgpu_notifications(AppSettingsPageData.enable_dgpu_notifications) - } - } + // ====== Content ====== + content := Rectangle { + y: titlebar.height; + width: 100%; + height: parent.height - titlebar.height; - SystemToggle { - text: @tr("Start app on system startup"); - checked <=> AppSettingsPageData.enable_autostart; - toggled => { - AppSettingsPageData.set_enable_autostart(AppSettingsPageData.enable_autostart) - } - } + VerticalLayout { + padding-left: 24px; + padding-right: 24px; + padding-top: 12px; + padding-bottom: 24px; + spacing: 16px; + alignment: start; - if AppSettingsPageData.enable_autostart: HorizontalLayout { - padding-left: 20px; - SystemToggle { - text: @tr("Start in background on startup"); - checked <=> AppSettingsPageData.autostart_in_background; - toggled => { - AppSettingsPageData.set_autostart_in_background(AppSettingsPageData.autostart_in_background) + // ---- General toggles ---- + RogCard { + width: 100%; + VerticalLayout { + padding: Theme.pad-card; + spacing: 14px; + RogToggle { + label: @tr("Run in background after closing"); + checked <=> AppSettingsPageData.run_in_background; + toggled(v) => { AppSettingsPageData.set_run_in_background(v); } + } + RogToggle { + label: @tr("Start app in background (UI closed)"); + checked <=> AppSettingsPageData.startup_in_background; + toggled(v) => { AppSettingsPageData.set_startup_in_background(v); } + } + RogToggle { + label: @tr("Enable system tray icon"); + checked <=> AppSettingsPageData.enable_tray_icon; + toggled(v) => { AppSettingsPageData.set_enable_tray_icon(v); } + } + RogToggle { + label: @tr("Enable dGPU notifications"); + checked <=> AppSettingsPageData.enable_dgpu_notifications; + toggled(v) => { AppSettingsPageData.set_enable_dgpu_notifications(v); } + } + RogToggle { + label: @tr("Start app on system startup"); + checked <=> AppSettingsPageData.enable_autostart; + toggled(v) => { AppSettingsPageData.set_enable_autostart(v); } + } + if AppSettingsPageData.enable_autostart: RogToggle { + label: @tr("Start in background on startup"); + checked <=> AppSettingsPageData.autostart_in_background; + toggled(v) => { AppSettingsPageData.set_autostart_in_background(v); } } } } - if AppSettingsPageData.show_global_shortcut_controls: SystemToggle { - enabled: AppSettingsPageData.global_shortcut_status != GlobalShortcutStatus.starting; - text: @tr("Armory/ROG key to show/hide window (keep app running in background)"); - checked <=> AppSettingsPageData.enable_global_shortcut; - toggled => { - AppSettingsPageData.set_enable_global_shortcut(AppSettingsPageData.enable_global_shortcut) - } - } - - if AppSettingsPageData.show_global_shortcut_controls && AppSettingsPageData.enable_global_shortcut: HorizontalLayout { - padding-left: 20px; - spacing: 10px; - - Text { - vertical-alignment: TextVerticalAlignment.center; - color: Palette.control-foreground; - text: { - if AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.starting { - @tr("Setting up…") - } else if AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.listening { - @tr("Shortcut active") - } else if AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.unassigned { - @tr("No shortcut assigned") - } else if AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.unavailable { - @tr("Global shortcuts unavailable") - } else { - "" + // ---- Global shortcut ---- + if AppSettingsPageData.show_global_shortcut_controls: RogCard { + width: 100%; + VerticalLayout { + padding: Theme.pad-card; + spacing: 12px; + HorizontalLayout { + spacing: 8px; + RogSlashMark {} + Text { + text: @tr("Global Shortcut"); + color: Theme.text-primary; + font-size: 14px; + font-weight: 600; + vertical-alignment: center; } } - } - - Button { - visible: AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.unassigned - || (AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.listening - && AppSettingsPageData.global_shortcut_configurable); - height: 30px; - text: AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.unassigned - ? @tr("Set up shortcut") - : @tr("Configure shortcut"); - clicked => { - AppSettingsPageData.manage_global_shortcut() + RogToggle { + enabled: AppSettingsPageData.global_shortcut_status != GlobalShortcutStatus.starting; + label: @tr("Armory/ROG key to show/hide window (keep app running in background)"); + checked <=> AppSettingsPageData.enable_global_shortcut; + toggled(v) => { AppSettingsPageData.set_enable_global_shortcut(v); } + } + if AppSettingsPageData.enable_global_shortcut: HorizontalLayout { + spacing: 12px; + Text { + text: { + if AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.starting { + @tr("Setting up…") + } else if AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.listening { + @tr("Shortcut active") + } else if AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.unassigned { + @tr("No shortcut assigned") + } else if AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.unavailable { + @tr("Global shortcuts unavailable") + } else { + "" + } + } + color: Theme.text-dim; + font-size: 12px; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + if AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.unassigned + || (AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.listening + && AppSettingsPageData.global_shortcut_configurable): Rectangle { + width: 150px; + height: 30px; + border-radius: 3px; + border-width: 1px; + border-color: Theme.rog-red; + Text { + text: AppSettingsPageData.global_shortcut_status == GlobalShortcutStatus.unassigned + ? @tr("Set up shortcut") + : @tr("Configure shortcut"); + color: Theme.rog-red; + font-size: 11px; + horizontal-alignment: center; + vertical-alignment: center; + } + TouchArea { + clicked => { AppSettingsPageData.manage_global_shortcut(); } + } + } } } } - Text { - color: Palette.accent-background; - text: " WIP: some features like notifications are not complete"; + // ---- Language ---- + RogCard { + width: 100%; + VerticalLayout { + padding: Theme.pad-card; + spacing: 12px; + RogDropdownRow { + label: @tr("Language"); + model: AppSettingsPageData.available_languages; + current-index <=> AppSettingsPageData.current_language; + selected(index) => { AppSettingsPageData.cb_change_language(index); } + } + // Reload button: re-launches the app to apply the language. + Rectangle { + width: 150px; + height: 32px; + border-radius: 3px; + border-width: 1px; + border-color: Theme.rog-red; + HorizontalLayout { + alignment: center; + Text { + text: @tr("Reload Window"); + color: Theme.rog-red; + font-size: 12px; + font-weight: 600; + vertical-alignment: center; + } + } + TouchArea { + clicked => { AppSettingsPageData.cb_reload_window(); } + } + } + } } } } diff --git a/rog-control-center/ui/pages/aura.slint b/rog-control-center/ui/pages/aura.slint index e7c3d2a3..01729985 100644 --- a/rog-control-center/ui/pages/aura.slint +++ b/rog-control-center/ui/pages/aura.slint @@ -1,14 +1,22 @@ -import { SystemDropdown, RogItem, SystemToggle, SystemToggleVert } from "../widgets/common.slint"; -import { Palette, Button, ComboBox, VerticalBox, GroupBox } from "std-widgets.slint"; -import { StyleMetrics, Slider, HorizontalBox, TextEdit, SpinBox, LineEdit, ScrollView } from "std-widgets.slint"; +// PageAura — Lighting/Aura page matching AC reference (f007-f009) +// Layout: titlebar + 2-col (effect icons left 360px | settings panel right) +// Pattern: alignment:start, HorizontalLayout, no ScrollView, natural height +// Data: all from AuraPageData global (D-Bus backed), preserves original callbacks +import { RogCard, RogEffectButton, RogSlashMark, RogSlider, RogPage } from "../widgets/rog.slint"; +import { Theme } from "../globals.slint"; import { ColourSlider } from "../widgets/colour_picker.slint"; -import { AuraPageData, AuraDevType, PowerZones, LaptopAuraPower, AuraEffect } from "../types/aura_types.slint"; +import { AuraPageData, AuraDevType } from "../types/aura_types.slint"; import { AuraPowerGroup, AuraPowerGroupOld } from "../widgets/aura_power.slint"; -export component PageAura inherits Rectangle { +export component PageAura inherits RogPage { + background: Theme.bg-base; + clip: true; + + // Modal state property show_fade_cover: false; property show_aura_power: false; callback external_colour_change(); + external_colour_change() => { c1.colourbox = AuraPageData.led_mode_data.colour1; c1.final_colour = AuraPageData.led_mode_data.colour1; @@ -18,66 +26,130 @@ export component PageAura inherits Rectangle { c2.external_colour_change(); } - ScrollView { + // ====== Titlebar ====== + titlebar := Rectangle { + y: 0px; + height: 52px; + width: 100%; + background: Theme.bg-base; + HorizontalLayout { + spacing: 12px; + x: 24px; + y: (parent.height - 24px) / 2; + height: 24px; + Text { + text: @tr("Lighting"); + color: Theme.text-primary; + font-size: 20px; + font-weight: 700; + vertical-alignment: center; + } + } + } + + // ====== Content area ====== + content := Rectangle { + y: titlebar.height; + width: 100%; + VerticalLayout { - padding: 10px; - spacing: 10px; - alignment: LayoutAlignment.start; + padding-left: 24px; + padding-right: 24px; + padding-top: 12px; + padding-bottom: 24px; + spacing: 20px; + alignment: start; + + // ---- Header row: label + action buttons ---- HorizontalLayout { - spacing: 10px; - SystemDropdown { - text: @tr("Brightness"); - current_index <=> AuraPageData.brightness; - current_value: AuraPageData.brightness_names[self.current-index]; - model <=> AuraPageData.brightness_names; - selected => { - AuraPageData.cb_brightness(AuraPageData.brightness) + width: 100%; + spacing: 8px; + RogSlashMark { count: 3; bar-w: 4px; bar-h: 14px; bar-color: Theme.rog-red; } + Text { + text: @tr("Basic Effects"); + color: Theme.text-primary; + font-size: 14px; + font-weight: 600; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + Rectangle { + width: 80px; + height: 30px; + border-radius: 3px; + border-width: 1px; + border-color: Theme.line; + Text { + text: @tr("Settings"); + color: Theme.text-secondary; + font-size: 11px; + horizontal-alignment: center; + vertical-alignment: center; + } + TouchArea { + clicked => { + root.show_fade_cover = true; + root.show_aura_power = true; + } } } - - SystemDropdown { - width: root.width * 1px / 2px; - text: @tr("Aura mode"); - current_index <=> AuraPageData.current_available_mode; - current_value: AuraPageData.available_mode_names[self.current-index]; - model <=> AuraPageData.available_mode_names; - selected => { - AuraPageData.apply_effect({ - mode: AuraPageData.supported_basic_modes[self.current-index], - zone: AuraPageData.led_mode_data.zone, - colour1: AuraPageData.led_mode_data.colour1, - colour2: AuraPageData.led_mode_data.colour2, - speed: AuraPageData.led_mode_data.speed, - direction: AuraPageData.led_mode_data.direction, - }); + Rectangle { + width: 100px; + height: 30px; + border-radius: 3px; + border-width: 1px; + border-color: Theme.line; + Text { + text: @tr("Reset Defaults"); + color: Theme.text-secondary; + font-size: 11px; + horizontal-alignment: center; + vertical-alignment: center; + } + TouchArea { + width: 100%; height: 100%; + mouse-cursor: pointer; + clicked => { + // Factory default: first supported mode (usually Static), + // white, Med speed, High brightness. + if (AuraPageData.supported_basic_modes.length > 0) { + AuraPageData.apply_effect({ + mode: AuraPageData.supported_basic_modes[0], + zone: AuraPageData.led_mode_data.zone, + colour1: #FFFFFF, + colour2: #FFFFFF, + speed: 1, + direction: 0, + }); + AuraPageData.cb_brightness(3); + } + } } } } - RogItem { - min-height: 220px; - max-height: 400px; - HorizontalLayout { - padding-top: 5px; - spacing: 10px; - VerticalBox { - Text { - text: @tr("Colour 1"); - vertical-alignment: TextVerticalAlignment.center; - horizontal-alignment: TextHorizontalAlignment.center; - } - HorizontalBox { - c1 := ColourSlider { - enabled: AuraPageData.colour1_enabled; - final_colour <=> AuraPageData.color1; - colourbox <=> AuraPageData.colorbox1; - set_hex_from_colour(c) => { return AuraPageData.cb_hex_from_colour(c); } - hex_to_colour(s) => { return AuraPageData.cb_hex_to_colour(s); } - released => { + // ---- Main 2-col layout (effects left | settings right) ---- + HorizontalLayout { + spacing: 36px; + + // Left column: Effect selection buttons + VerticalLayout { + width: 360px; + spacing: 16px; + alignment: start; + + HorizontalLayout { + spacing: 24px; + RogEffectButton { + icon: @image-url("../images/effect-static.svg"); + label: @tr("Static"); + selected: AuraPageData.current_available_mode == 0; + clicked => { + if (AuraPageData.supported_basic_modes.length > 0) { AuraPageData.apply_effect({ - mode: AuraPageData.led_mode_data.mode, + mode: AuraPageData.supported_basic_modes[0], zone: AuraPageData.led_mode_data.zone, - colour1: AuraPageData.color1, + colour1: AuraPageData.led_mode_data.colour1, colour2: AuraPageData.led_mode_data.colour2, speed: AuraPageData.led_mode_data.speed, direction: AuraPageData.led_mode_data.direction, @@ -85,256 +157,518 @@ export component PageAura inherits Rectangle { } } } - } - VerticalBox { - Text { text: @tr("Colour 2"); vertical-alignment: TextVerticalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center; } - HorizontalBox { - c2 := ColourSlider { - enabled: AuraPageData.colour2_enabled; - final_colour <=> AuraPageData.color2; - colourbox <=> AuraPageData.colorbox2; - set_hex_from_colour(c) => { return AuraPageData.cb_hex_from_colour(c); } - hex_to_colour(s) => { return AuraPageData.cb_hex_to_colour(s); } - released => { + RogEffectButton { + icon: @image-url("../images/effect-breathing.svg"); + label: @tr("Breathing"); + selected: AuraPageData.current_available_mode == 1; + clicked => { + if (AuraPageData.supported_basic_modes.length > 1) { AuraPageData.apply_effect({ - mode: AuraPageData.led_mode_data.mode, + mode: AuraPageData.supported_basic_modes[1], zone: AuraPageData.led_mode_data.zone, colour1: AuraPageData.led_mode_data.colour1, - colour2: AuraPageData.color2, + colour2: AuraPageData.led_mode_data.colour2, speed: AuraPageData.led_mode_data.speed, direction: AuraPageData.led_mode_data.direction, }); } } } - } - } - } - - HorizontalLayout { - spacing: 10px; - min-height: 80px; - max-height: 90px; - RogItem { - VerticalBox { - Text { text: @tr("Zone"); vertical-alignment: TextVerticalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center; } - ComboBox { - enabled: false; - current_index <=> AuraPageData.zone; - current_value: AuraPageData.zone_names[self.current-index]; - model <=> AuraPageData.zone_names; - selected => { - AuraPageData.apply_effect({ - mode: AuraPageData.led_mode_data.mode, - zone: self.current-index, - colour1: AuraPageData.led_mode_data.colour1, - colour2: AuraPageData.led_mode_data.colour2, - speed: AuraPageData.led_mode_data.speed, - direction: AuraPageData.led_mode_data.direction, - }); + RogEffectButton { + icon: @image-url("../images/effect-flash.svg"); + label: @tr("Flash"); + selected: AuraPageData.current_available_mode == 2; + clicked => { + if (AuraPageData.supported_basic_modes.length > 2) { + AuraPageData.apply_effect({ + mode: AuraPageData.supported_basic_modes[2], + zone: AuraPageData.led_mode_data.zone, + colour1: AuraPageData.led_mode_data.colour1, + colour2: AuraPageData.led_mode_data.colour2, + speed: AuraPageData.led_mode_data.speed, + direction: AuraPageData.led_mode_data.direction, + }); + } } } - } - } - RogItem { - VerticalBox { - Text { text: @tr("Direction"); vertical-alignment: TextVerticalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center; } - ComboBox { - enabled: AuraPageData.direction_enabled; - current_index <=> AuraPageData.direction; - current_value: AuraPageData.direction_names[self.current-index]; - model <=> AuraPageData.direction_names; - selected => { - AuraPageData.apply_effect({ - mode: AuraPageData.led_mode_data.mode, - zone: AuraPageData.led_mode_data.zone, - colour1: AuraPageData.led_mode_data.colour1, - colour2: AuraPageData.led_mode_data.colour2, - speed: AuraPageData.led_mode_data.speed, - direction: self.current-index, - }); + RogEffectButton { + icon: @image-url("../images/effect-cycle.svg"); + label: @tr("Color Cycle"); + selected: AuraPageData.current_available_mode == 3; + clicked => { + if (AuraPageData.supported_basic_modes.length > 3) { + AuraPageData.apply_effect({ + mode: AuraPageData.supported_basic_modes[3], + zone: AuraPageData.led_mode_data.zone, + colour1: AuraPageData.led_mode_data.colour1, + colour2: AuraPageData.led_mode_data.colour2, + speed: AuraPageData.led_mode_data.speed, + direction: AuraPageData.led_mode_data.direction, + }); + } } } } } - RogItem { - VerticalBox { - Text { text: @tr("Speed"); vertical-alignment: TextVerticalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center; } - ComboBox { - enabled: AuraPageData.speed_enabled; - current_index <=> AuraPageData.speed; - current_value: AuraPageData.speed_names[self.current-index]; - model <=> AuraPageData.speed_names; - selected => { - AuraPageData.apply_effect({ - mode: AuraPageData.led_mode_data.mode, - zone: AuraPageData.led_mode_data.zone, - colour1: AuraPageData.led_mode_data.colour1, - colour2: AuraPageData.led_mode_data.colour2, - speed: self.current-index, - direction: AuraPageData.led_mode_data.direction, - }); + + // Right column: Effect settings panel (pushed to right) + Rectangle { horizontal-stretch: 1; } // spacer pushes panel right + VerticalLayout { + spacing: 16px; + alignment: start; + + // Settings card (bordered, not filled background) + Rectangle { + width: 280px; + border-radius: 4px; + border-width: 1px; + border-color: Theme.line; + clip: true; + + VerticalLayout { + padding: 20px; + spacing: 18px; + + // Effect name header + Text { + text: AuraPageData.available_mode_names[AuraPageData.current_available_mode]; + color: Theme.text-primary; + font-size: 15px; + font-weight: 600; } - } - } - } - } - HorizontalLayout { - Button { - text: @tr("Power Settings"); - clicked => { - root.show_fade_cover = true; - root.show_aura_power = true; + Rectangle { height: 1px; background: Theme.line-soft; } + + // Color section + Text { + text: @tr("Color"); + color: Theme.text-secondary; + font-size: 13px; + } + + // Color pickers (Colour 1 + Colour 2 side by side) + HorizontalLayout { + spacing: 12px; + VerticalLayout { + spacing: 6px; + Text { + text: @tr("Colour 1"); + color: Theme.text-dim; + font-size: 11px; + } + c1 := ColourSlider { + width: 120px; + enabled: AuraPageData.colour1_enabled; + final_colour <=> AuraPageData.color1; + colourbox <=> AuraPageData.colorbox1; + set_hex_from_colour(c) => { return AuraPageData.cb_hex_from_colour(c); } + hex_to_colour(s) => { return AuraPageData.cb_hex_to_colour(s); } + released => { + AuraPageData.apply_effect({ + mode: AuraPageData.led_mode_data.mode, + zone: AuraPageData.led_mode_data.zone, + colour1: AuraPageData.color1, + colour2: AuraPageData.led_mode_data.colour2, + speed: AuraPageData.led_mode_data.speed, + direction: AuraPageData.led_mode_data.direction, + }); + } + } + } + VerticalLayout { + visible: AuraPageData.colour2_enabled; + spacing: 6px; + Text { + text: @tr("Colour 2"); + color: Theme.text-dim; + font-size: 11px; + } + c2 := ColourSlider { + width: 120px; + enabled: AuraPageData.colour2_enabled; + final_colour <=> AuraPageData.color2; + colourbox <=> AuraPageData.colorbox2; + set_hex_from_colour(c) => { return AuraPageData.cb_hex_from_colour(c); } + hex_to_colour(s) => { return AuraPageData.cb_hex_to_colour(s); } + released => { + AuraPageData.apply_effect({ + mode: AuraPageData.led_mode_data.mode, + zone: AuraPageData.led_mode_data.zone, + colour1: AuraPageData.led_mode_data.colour1, + colour2: AuraPageData.color2, + speed: AuraPageData.led_mode_data.speed, + direction: AuraPageData.led_mode_data.direction, + }); + } + } + } + } + + // Speed section (when applicable) + if AuraPageData.speed_enabled: VerticalLayout { + spacing: 8px; + Text { + text: @tr("Speed"); + color: Theme.text-secondary; + font-size: 13px; + } + // Speed dots indicator (visual only) + HorizontalLayout { + spacing: 4px; + for si in 3: Rectangle { + width: 5px; + height: 5px; + border-radius: 2.5px; + background: si <= AuraPageData.speed ? Theme.text-dim : #333333; + } + } + // Speed slider + Rectangle { + width: 100%; + height: 24px; + // Track + Rectangle { + x: 0px; y: 10px; + width: 100%; height: 4px; + background: Theme.bg-elevated; + border-radius: 2px; + Rectangle { + x: 0; + width: parent.width * (AuraPageData.speed / 2); + height: 100%; + background: Theme.rog-red; + border-radius: 2px; + } + } + // Handle + Rectangle { + x: parent.width * (AuraPageData.speed / 2) - 7px; y: 5px; + width: 14px; height: 14px; + border-radius: 7px; + background: #FFFFFF; + border-width: 2px; + border-color: Theme.rog-red; + } + ta-speed := TouchArea { + width: 100%; height: 100%; + mouse-cursor: pointer; + pointer-event(e) => { + // Live-update the UI property during the drag for instant + // handle feedback; defer the hardware write + toast to + // pointer-up so one drag = one DBus write, not one per move. + if e.kind == PointerEventKind.down || (e.kind == PointerEventKind.move && self.pressed) { + AuraPageData.speed = Math.round(clamp(self.mouse-x / self.width, 0.0, 1.0) * 2); + } + if e.kind == PointerEventKind.up { + AuraPageData.cb_speed(AuraPageData.speed); + } + } + } + } + } + + // Brightness section + VerticalLayout { + spacing: 8px; + HorizontalLayout { + Text { + text: @tr("Brightness"); + color: Theme.text-secondary; + font-size: 13px; + } + Rectangle { horizontal-stretch: 1; } + Text { + text: AuraPageData.brightness_names[AuraPageData.brightness]; + color: Theme.text-dim; + font-size: 11px; + } + } + // Brightness dots + HorizontalLayout { + spacing: 4px; + for bi in 4: Rectangle { + width: 5px; + height: 5px; + border-radius: 2.5px; + background: bi < AuraPageData.brightness ? Theme.text-dim : #333333; + } + } + // Brightness slider + Rectangle { + width: 100%; + height: 24px; + // Track + fill (brightness 0..3 → 0..1) + Rectangle { + x: 0px; y: 10px; + width: 100%; height: 4px; + background: Theme.bg-elevated; + border-radius: 2px; + Rectangle { + x: 0; + width: parent.width * (AuraPageData.brightness / 3); + height: 100%; + background: Theme.rog-red; + border-radius: 2px; + } + } + // Handle (tracks brightness) + Rectangle { + x: parent.width * (AuraPageData.brightness / 3) - 7px; y: 5px; + width: 14px; height: 14px; + border-radius: 7px; + background: #FFFFFF; + border-width: 2px; + border-color: Theme.rog-red; + } + ta-bright := TouchArea { + width: 100%; height: 100%; + mouse-cursor: pointer; + pointer-event(e) => { + // Live-update during drag; write + toast only on release. + if e.kind == PointerEventKind.down || (e.kind == PointerEventKind.move && self.pressed) { + AuraPageData.brightness = Math.round(clamp(self.mouse-x / self.width, 0.0, 1.0) * 3); + } + if e.kind == PointerEventKind.up { + AuraPageData.cb_brightness(AuraPageData.brightness); + } + } + } + } + } + } } } } } + } - if root.show_fade_cover: Rectangle { - background: Palette.background; - opacity: 0.8; - TouchArea { - height: 100%; - width: 100%; + // ====== Power Settings Modal (kept from original, restyled) ====== + if root.show_fade_cover: Rectangle { + width: 100%; height: 100%; + background: #000000; + opacity: 0.65; + TouchArea { + width: 100%; height: 100%; + clicked => { + root.show_aura_power = false; + root.show_fade_cover = false; } } } if root.show_aura_power && AuraPageData.device_type == AuraDevType.New: Rectangle { - background: Palette.background; - width: 100%; - height: 100%; - opacity: 1; - ScrollView { - VerticalLayout { - padding: 30px; - padding-top: 10px; - spacing: 10px; - alignment: LayoutAlignment.start; + background: Theme.bg-base; + border-radius: Theme.radius-card; + drop-shadow-blur: 24px; + drop-shadow-color: #80000000; + x: root.width * 0.08; + y: root.height * 0.06; + width: root.width * 0.84; + height: root.height * 0.88; + clip: true; - for state[idx] in AuraPageData.led_power.states: zone := AuraPowerGroup { - group-title: AuraPageData.power_zone_names[state.zone_name_idx]; - // TODO: use the zone name - boot_checked: state.boot; - boot_toggled => { - AuraPageData.led_power.states[idx].boot = zone.boot_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); - } - awake_checked: state.awake; - awake_toggled => { - AuraPageData.led_power.states[idx].awake = zone.awake_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); - } - sleep_checked: state.sleep; - sleep_toggled => { - AuraPageData.led_power.states[idx].sleep = zone.sleep_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); + VerticalLayout { + padding: 20px; + spacing: 16px; + HorizontalLayout { + Text { + text: @tr("Power Settings"); + color: Theme.text-primary; + font-size: 17px; + font-weight: 700; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + Rectangle { + width: 28px; + height: 28px; + border-radius: 14px; + background: Theme.bg-elevated; + Text { + text: "✕"; + color: Theme.text-secondary; + font-size: 13px; + horizontal-alignment: center; + vertical-alignment: center; } - shutdown_checked: state.shutdown; - shutdown_toggled => { - AuraPageData.led_power.states[idx].shutdown = zone.shutdown_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); + TouchArea { + clicked => { + root.show_aura_power = false; + root.show_fade_cover = false; + } } } } + + for state[idx] in AuraPageData.led_power.states: zone := AuraPowerGroup { + group-title: AuraPageData.power_zone_names[state.zone_name_idx]; + boot_checked: state.boot; + boot_toggled => { + AuraPageData.led_power.states[idx].boot = zone.boot_checked; + AuraPageData.cb_led_power(AuraPageData.led_power); + } + awake_checked: state.awake; + awake_toggled => { + AuraPageData.led_power.states[idx].awake = zone.awake_checked; + AuraPageData.cb_led_power(AuraPageData.led_power); + } + sleep_checked: state.sleep; + sleep_toggled => { + AuraPageData.led_power.states[idx].sleep = zone.sleep_checked; + AuraPageData.cb_led_power(AuraPageData.led_power); + } + shutdown_checked: state.shutdown; + shutdown_toggled => { + AuraPageData.led_power.states[idx].shutdown = zone.shutdown_checked; + AuraPageData.cb_led_power(AuraPageData.led_power); + } + } } } - // TUF devices: show the same configurable power groups as `New`, since - // `setup_aura` fills `AuraPageData.supported_power_zones` and `led_power` - // appropriately for TUF devices. This ensures the Power Settings panel - // reflects what the firmware reports for TUF models. if root.show_aura_power && AuraPageData.device_type == AuraDevType.Tuf: Rectangle { - background: Palette.background; - width: 100%; - height: 100%; - opacity: 1; - ScrollView { - VerticalLayout { - padding: 30px; - padding-top: 10px; - spacing: 10px; - alignment: LayoutAlignment.start; + background: Theme.bg-base; + border-radius: Theme.radius-card; + drop-shadow-blur: 24px; + drop-shadow-color: #80000000; + x: root.width * 0.08; + y: root.height * 0.06; + width: root.width * 0.84; + height: root.height * 0.88; + clip: true; - for state[idx] in AuraPageData.led_power.states: tuf_zone := AuraPowerGroup { - group-title: AuraPageData.power_zone_names[state.zone_name_idx]; - boot_checked: state.boot; - boot_toggled => { - AuraPageData.led_power.states[idx].boot = tuf_zone.boot_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); - } - awake_checked: state.awake; - awake_toggled => { - AuraPageData.led_power.states[idx].awake = tuf_zone.awake_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); - } - sleep_checked: state.sleep; - sleep_toggled => { - AuraPageData.led_power.states[idx].sleep = tuf_zone.sleep_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); + VerticalLayout { + padding: 20px; + spacing: 16px; + HorizontalLayout { + Text { + text: @tr("Power Settings"); + color: Theme.text-primary; + font-size: 17px; + font-weight: 700; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + Rectangle { + width: 28px; + height: 28px; + border-radius: 14px; + background: Theme.bg-elevated; + Text { + text: "✕"; + color: Theme.text-secondary; + font-size: 13px; + horizontal-alignment: center; + vertical-alignment: center; } - shutdown_checked: state.shutdown; - shutdown_toggled => { - AuraPageData.led_power.states[idx].shutdown = tuf_zone.shutdown_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); + TouchArea { + clicked => { + root.show_aura_power = false; + root.show_fade_cover = false; + } } } } + + for state[idx] in AuraPageData.led_power.states: tuf_zone := AuraPowerGroup { + group-title: AuraPageData.power_zone_names[state.zone_name_idx]; + boot_checked: state.boot; + boot_toggled => { + AuraPageData.led_power.states[idx].boot = tuf_zone.boot_checked; + AuraPageData.cb_led_power(AuraPageData.led_power); + } + awake_checked: state.awake; + awake_toggled => { + AuraPageData.led_power.states[idx].awake = tuf_zone.awake_checked; + AuraPageData.cb_led_power(AuraPageData.led_power); + } + sleep_checked: state.sleep; + sleep_toggled => { + AuraPageData.led_power.states[idx].sleep = tuf_zone.sleep_checked; + AuraPageData.cb_led_power(AuraPageData.led_power); + } + shutdown_checked: state.shutdown; + shutdown_toggled => { + AuraPageData.led_power.states[idx].shutdown = tuf_zone.shutdown_checked; + AuraPageData.cb_led_power(AuraPageData.led_power); + } + } } } if root.show_aura_power && AuraPageData.device_type == AuraDevType.Old: Rectangle { - background: Palette.background; - width: 100%; - height: 100%; - opacity: 1; - ScrollView { - VerticalLayout { - padding: 30px; - padding-top: 10px; - spacing: 10px; - alignment: LayoutAlignment.start; + background: Theme.bg-base; + border-radius: Theme.radius-card; + drop-shadow-blur: 24px; + drop-shadow-color: #80000000; + x: root.width * 0.08; + y: root.height * 0.06; + width: root.width * 0.84; + height: root.height * 0.88; + clip: true; + VerticalLayout { + padding: 20px; + spacing: 16px; + HorizontalLayout { Text { - text: "TODO: In progress"; + text: @tr("Power Settings"); + color: Theme.text-primary; + font-size: 17px; + font-weight: 700; + vertical-alignment: center; } - - for state[idx] in AuraPageData.led_power.states: old_zone := AuraPowerGroupOld { - group-title: @tr("Power Zones"); - zone_strings <=> AuraPageData.power_zone_names_old; - selected_zone => { - AuraPageData.led_power.states[idx].zone = AuraPageData.supported_power_zones[old_zone.current_zone]; - AuraPageData.cb_led_power(AuraPageData.led_power); + Rectangle { horizontal-stretch: 1; } + Rectangle { + width: 28px; + height: 28px; + border-radius: 14px; + background: Theme.bg-elevated; + Text { + text: "✕"; + color: Theme.text-secondary; + font-size: 13px; + horizontal-alignment: center; + vertical-alignment: center; } - boot_checked: state.boot; - boot_toggled => { - AuraPageData.led_power.states[idx].boot = old_zone.boot_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); - } - awake_checked: state.awake; - awake_toggled => { - AuraPageData.led_power.states[idx].awake = old_zone.awake_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); - } - sleep_checked: state.sleep; - sleep_toggled => { - AuraPageData.led_power.states[idx].sleep = old_zone.sleep_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); + TouchArea { + clicked => { + root.show_aura_power = false; + root.show_fade_cover = false; + } } } } - } - } - if root.show_aura_power: Button { - x: root.width - self.width - 6px; - y: 6px; - text: "✕"; - height: 36px; - clicked => { - root.show_aura_power = false; - root.show_fade_cover = false; + Text { + text: @tr("Legacy device power zones"); + color: Theme.text-dim; + font-size: 12px; + } + + for state[idx] in AuraPageData.led_power.states: old_zone := AuraPowerGroupOld { + group-title: @tr("Power Zones"); + zone_strings <=> AuraPageData.power_zone_names_old; + selected_zone => { + AuraPageData.led_power.states[idx].zone = AuraPageData.supported_power_zones[old_zone.current_zone]; + AuraPageData.cb_led_power(AuraPageData.led_power); + } + boot_checked: state.boot; + boot_toggled => { + AuraPageData.led_power.states[idx].boot = old_zone.boot_checked; + AuraPageData.cb_led_power(AuraPageData.led_power); + } + awake_checked: state.awake; + awake_toggled => { + AuraPageData.led_power.states[idx].awake = old_zone.awake_checked; + AuraPageData.cb_led_power(AuraPageData.led_power); + } + sleep_checked: state.sleep; + sleep_toggled => { + AuraPageData.led_power.states[idx].sleep = old_zone.sleep_checked; + AuraPageData.cb_led_power(AuraPageData.led_power); + } + } } } } diff --git a/rog-control-center/ui/pages/battery.slint b/rog-control-center/ui/pages/battery.slint index 0a8d4a76..704560d8 100644 --- a/rog-control-center/ui/pages/battery.slint +++ b/rog-control-center/ui/pages/battery.slint @@ -1,102 +1,177 @@ -import { SystemSlider, RogItem } from "../widgets/common.slint"; -import { Palette, VerticalBox, ScrollView } from "std-widgets.slint"; +// PageBattery — Battery page matching AC style +// Layout: titlebar + content (charge control + battery info card) +// Pattern: alignment:start, no ScrollView, natural height +import { RogCard, RogSlider, RogToggle, RogSlashMark, RogMeter, RogStatRow, RogPage } from "../widgets/rog.slint"; +import { Theme } from "../globals.slint"; import { SystemPageData } from "system.slint"; -export component PageBattery inherits Rectangle { +export component PageBattery inherits RogPage { + background: Theme.bg-base; clip: true; + // Remembers the user's charge threshold while the limit toggle is off + // (asusd has no on/off — "off" is emulated as threshold 100). + property saved-charge-threshold: 80.0; - ScrollView { - VerticalLayout { - padding: 12px; - spacing: 8px; - alignment: LayoutAlignment.start; - - Rectangle { - background: Palette.alternate-background; - border-color: Palette.border; - border-width: 1px; - border-radius: 2px; - height: 36px; - Text { - font-size: 16px; - color: Palette.control-foreground; - horizontal-alignment: TextHorizontalAlignment.center; - text: @tr("Battery Information"); - } + // ====== Titlebar ====== + titlebar := Rectangle { + y: 0px; + height: 52px; + width: 100%; + background: Theme.bg-base; + HorizontalLayout { + spacing: 12px; + x: 24px; + y: (parent.height - 24px) / 2; + height: 24px; + Text { + text: @tr("Battery"); + color: Theme.text-primary; + font-size: 20px; + font-weight: 700; + vertical-alignment: center; } + } + } - if SystemPageData.charge_control_end_threshold != -1: SystemSlider { - text: @tr("Charge limit"); - minimum: 20; - maximum: 100; - has_reset: false; - enabled <=> SystemPageData.charge_control_enabled; - value: SystemPageData.charge_control_end_threshold; - released => { - SystemPageData.charge_control_end_threshold = self.value; - SystemPageData.cb_charge_control_end_threshold(Math.round(SystemPageData.charge_control_end_threshold)) - } - } + // ====== Content area ====== + content := Rectangle { + y: titlebar.height; + width: 100%; + + VerticalLayout { + padding-left: 24px; + padding-right: 24px; + padding-top: 12px; + padding-bottom: 24px; + spacing: 20px; + alignment: start; + + // ---- Charge Control section ---- + if SystemPageData.charge_control_end_threshold != -1: VerticalLayout { + width: 100%; + spacing: 16px; + alignment: start; - if SystemPageData.battery_health != -1: Rectangle { - background: Palette.alternate-background; - border-color: Palette.border; - border-width: 1px; - border-radius: 4px; - - VerticalLayout { - padding: 12px; - spacing: 8px; - + Rectangle { + height: 24px; HorizontalLayout { - alignment: LayoutAlignment.space-between; + spacing: 8px; + RogSlashMark { count: 3; bar-w: 4px; bar-h: 14px; bar-color: Theme.rog-red; } Text { - text: @tr("Battery Health (Capacity):"); - color: Palette.control-foreground; + text: @tr("Charge Control"); + color: Theme.text-primary; + font-size: 14px; + font-weight: 600; + vertical-alignment: center; } + } + } + + RogCard { + width: 100%; + VerticalLayout { + padding: Theme.pad-card; + spacing: 14px; + + RogToggle { + label: @tr("Enable Charge Limit"); + checked <=> SystemPageData.charge_control_enabled; + toggled(v) => { + // asusd exposes no on/off switch — disabling the + // limit means "charge to 100%". + if (v) { + SystemPageData.cb_charge_control_end_threshold(Math.round(root.saved-charge-threshold)); + } else { + root.saved-charge-threshold = SystemPageData.charge_control_end_threshold; + SystemPageData.cb_charge_control_end_threshold(100); + } + } + } + + if SystemPageData.charge_control_enabled: RogSlider { + label: @tr("Charge Limit"); + minimum: 20; maximum: 100; + value <=> SystemPageData.charge_control_end_threshold; + unit: "%"; + released(v) => { SystemPageData.cb_charge_control_end_threshold(Math.round(v)); } + } + Text { - text: SystemPageData.battery_health + "%"; - font-weight: 700; - color: SystemPageData.battery_health > 80 ? #22c55e : (SystemPageData.battery_health > 50 ? #eab308 : #ef4444); + text: @tr("Limiting maximum charge to 80% can significantly extend battery lifespan."); + color: Theme.text-dim; + font-size: 11px; + wrap: word-wrap; } } + } + } + // ---- Battery Information Card ---- + if SystemPageData.battery_health != -1: VerticalLayout { + width: 100%; + spacing: 16px; + alignment: start; + + Rectangle { + height: 24px; HorizontalLayout { - alignment: LayoutAlignment.space-between; - Text { - text: @tr("Status:"); - color: Palette.control-foreground; - } + spacing: 8px; + RogSlashMark { count: 3; bar-w: 4px; bar-h: 14px; bar-color: Theme.rog-red; } Text { - text: SystemPageData.battery_status; - font-weight: 700; - color: Palette.control-foreground; + text: @tr("Battery Information"); + color: Theme.text-primary; + font-size: 14px; + font-weight: 600; + vertical-alignment: center; } } + } - if SystemPageData.battery_power_consumption != -1.0: HorizontalLayout { - alignment: LayoutAlignment.space-between; - Text { - text: @tr("Power Draw:"); - color: Palette.control-foreground; + RogCard { + width: 100%; + VerticalLayout { + padding: Theme.pad-card; + spacing: 14px; + + // Health row (big number) + HorizontalLayout { + spacing: 8px; + Text { + text: @tr("Battery Health"); + color: Theme.text-secondary; + font-size: 13px; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + Text { + text: "\{SystemPageData.battery_health}%"; + color: SystemPageData.battery_health > 80 ? #4CAF50 : (SystemPageData.battery_health > 50 ? #FF9E1B : #FF1929); + font-size: 28px; + font-weight: 800; + font-family: "Inter Display"; + vertical-alignment: center; + } } - Text { - text: Math.round(SystemPageData.battery_power_consumption * 10) / 10 + " W"; - font-weight: 700; - color: SystemPageData.battery_status == "Discharging" ? #ef4444 : #22c55e; + + // Health meter + RogMeter { value: SystemPageData.battery_health / 100.0; } + + Rectangle { height: 1px; background: Theme.line-soft; } + + // Status rows + RogStatRow { + label: @tr("Status"); + value: SystemPageData.battery_status; } - } - if SystemPageData.battery_time_estimate != "": HorizontalLayout { - alignment: LayoutAlignment.space-between; - Text { - text: SystemPageData.battery_status == "Charging" ? @tr("Time to Full:") : @tr("Time Remaining:"); - color: Palette.control-foreground; + if SystemPageData.battery_power_consumption != -1.0: RogStatRow { + label: @tr("Power Draw"); + value: Math.round(SystemPageData.battery_power_consumption * 10) / 10 + " W"; } - Text { - text: SystemPageData.battery_time_estimate; - font-weight: 700; - color: Palette.control-foreground; + + if SystemPageData.battery_time_estimate != "": RogStatRow { + label: SystemPageData.battery_status == "Charging" ? @tr("Time to Full") : @tr("Time Remaining"); + value: SystemPageData.battery_time_estimate; } } } diff --git a/rog-control-center/ui/pages/fans.slint b/rog-control-center/ui/pages/fans.slint index db1d7e3a..d6b8d4fe 100644 --- a/rog-control-center/ui/pages/fans.slint +++ b/rog-control-center/ui/pages/fans.slint @@ -1,253 +1,229 @@ -import { Palette, TabWidget, Button, CheckBox } from "std-widgets.slint"; +// PageFans — Fan curve editor, AC-styled. +// Interaction unchanged from upstream: the profile tab picks WHICH curve to +// edit (not the system mode), the fan selector picks which fan, and the graph +// edits that curve. Only the chrome is restyled (RogTabBar / RogModeSelector / +// RogCard / RogToggle / AC buttons / Theme tokens). All FanPageData bindings +// and callbacks are preserved verbatim. import { Graph, Node } from "../widgets/graph.slint"; -import { SystemToggle } from "../widgets/common.slint"; +import { RogPage, RogCard, RogTabBar, RogModeSelector, RogToggle } from "../widgets/rog.slint"; +import { Theme } from "../globals.slint"; import { Profile, FanType, FanPageData } from "../types/fan_types.slint"; +// One curve editor: graph + enable toggle + apply/cancel/default actions. component FanTab inherits Rectangle { in-out property enabled: false; in-out property tab_enabled: true; - in property profile; - in property fan_type; callback apply(); - callback cancel(); callback default(); callback toggled(); - in property title; in-out property <[Node]> nodes; - + vertical-stretch: 1; VerticalLayout { - padding: 5px; - HorizontalLayout { - if root.tab_enabled: Graph { - nodes <=> root.nodes; - } - if !root.tab_enabled: Rectangle { - Text { - font-size: 16px; - text: @tr("This fan is not avilable on this machine"); - } - } + spacing: 14px; + + // Curve editor, or the "not available" notice for this fan. + if root.tab_enabled: Graph { + nodes <=> root.nodes; + // Lock the curve when disabled (BIOS default takes over). + enabled: root.enabled; + vertical-stretch: 1; + min-height: 220px; } - - Rectangle { - background: Palette.border; - height: 1px; + if !root.tab_enabled: Rectangle { + height: 240px; + Text { + text: @tr("This fan is not available on this machine"); + color: Theme.text-dim; + font-size: 13px; + horizontal-alignment: center; + vertical-alignment: center; + } } + // Enable toggle (left) + action buttons (right). HorizontalLayout { - alignment: LayoutAlignment.end; spacing: 10px; - padding: 10px; - CheckBox { - text: @tr("Enabled"); + RogToggle { + label: @tr("Enabled"); + compact: true; checked <=> root.enabled; enabled <=> root.tab_enabled; - toggled => { - root.toggled(); - } + toggled => { root.toggled(); } } + Rectangle { horizontal-stretch: 1; } - Button { - text: @tr("Apply"); - enabled <=> root.tab_enabled; - clicked => { - root.apply(); + // Apply — primary action (AC red). + Rectangle { + width: 80px; height: 30px; border-radius: 3px; + border-width: 1px; + border-color: root.tab_enabled ? Theme.rog-red : Theme.line; + Text { + text: @tr("Apply"); + color: root.tab_enabled ? Theme.rog-red : Theme.fg-disabled; + font-size: 11px; + horizontal-alignment: center; vertical-alignment: center; } - } - - Button { - text: @tr("Cancel"); - enabled <=> root.tab_enabled; - clicked => { - root.cancel() + TouchArea { + width: 100%; height: 100%; mouse-cursor: pointer; + enabled: root.tab_enabled; + clicked => { root.apply(); } } } - - Button { - text: @tr("Factory Default (all fans)"); - enabled <=> root.tab_enabled; - clicked => { - root.default(); + // Factory default — secondary. + Rectangle { + width: 190px; height: 30px; border-radius: 3px; + border-width: 1px; border-color: Theme.line; + Text { + text: @tr("Factory Default (all fans)"); + color: root.tab_enabled ? Theme.text-secondary : Theme.fg-disabled; + font-size: 11px; + horizontal-alignment: center; vertical-alignment: center; + } + TouchArea { + width: 100%; height: 100%; mouse-cursor: pointer; + enabled: root.tab_enabled; + clicked => { root.default(); } } } } } } -export component PageFans inherits VerticalLayout { - TabWidget { - Tab { - title: @tr("Balanced"); - TabWidget { - Tab { - title: @tr("CPU"); - FanTab { +export component PageFans inherits RogPage { + background: Theme.bg-base; + clip: true; + + // Selection into the 3×3 matrix of curves. 0=Balanced 1=Performance 2=Quiet; + // 0=CPU 1=Mid 2=GPU. + property current-profile: 0; + property current-fan: 0; + + titlebar := Rectangle { + y: 0px; height: 52px; width: 100%; background: Theme.bg-base; + HorizontalLayout { + spacing: 12px; x: 24px; y: (parent.height - 24px) / 2; height: 24px; + Text { + text: @tr("Fan Curves"); + color: Theme.text-primary; font-size: 20px; font-weight: 700; + vertical-alignment: center; + } + } + } + + content := Rectangle { + y: titlebar.height; + width: 100%; + height: root.height - titlebar.height; + + VerticalLayout { + padding-left: 24px; padding-right: 24px; + padding-top: 12px; padding-bottom: 24px; + spacing: 16px; + + // Clarify the interaction: tabs pick which curve to EDIT, they do + // not switch the system power profile. + Text { + text: @tr("Edit the fan curve for each performance profile and fan — this does not change the active profile."); + color: Theme.text-dim; + font-size: 12px; + wrap: word-wrap; + } + + // Profile tab — selects which profile's curve to edit. + RogTabBar { + tabs: [@tr("Balanced"), @tr("Performance"), @tr("Quiet")]; + active <=> root.current-profile; + } + + RogCard { + width: 100%; + vertical-stretch: 1; + VerticalLayout { + padding: 20px; + spacing: 16px; + + // Fan selector within the profile. + RogModeSelector { + modes: ["CPU", "Mid", "GPU"]; + current <=> root.current-fan; + } + + // One FanTab per (profile, fan). Use `if` (not `visible:`) so + // only the selected one is created — `visible:false` still + // claims layout space, which stacked all nine and shrank the + // graph to 1/9 of the card. The two-way bindings initialise + // from FanPageData on creation, so edits survive switches. + if root.current-profile == 0 && root.current-fan == 0: FanTab { tab_enabled <=> FanPageData.cpu_fan_available; enabled <=> FanPageData.balanced_cpu_enabled; nodes <=> FanPageData.balanced_cpu; - apply => { - FanPageData.set_fan_data(FanType.CPU, Profile.Balanced, self.enabled, FanPageData.balanced_cpu); - } - toggled => { - FanPageData.set_fan_data(FanType.CPU, Profile.Balanced, self.enabled, FanPageData.balanced_cpu); - } - default => { - FanPageData.set_profile_default(Profile.Balanced); - } + apply => { FanPageData.set_fan_data(FanType.CPU, Profile.Balanced, self.enabled, FanPageData.balanced_cpu); } + toggled => { FanPageData.set_fan_data(FanType.CPU, Profile.Balanced, self.enabled, FanPageData.balanced_cpu); } + default => { FanPageData.set_profile_default(Profile.Balanced); } } - } - - Tab { - title: @tr("Mid"); - FanTab { + if root.current-profile == 0 && root.current-fan == 1: FanTab { tab_enabled <=> FanPageData.mid_fan_available; enabled <=> FanPageData.balanced_mid_enabled; nodes <=> FanPageData.balanced_mid; - apply => { - FanPageData.set_fan_data(FanType.Middle, Profile.Balanced, self.enabled, FanPageData.balanced_mid); - } - toggled => { - FanPageData.set_fan_data(FanType.Middle, Profile.Balanced, self.enabled, FanPageData.balanced_mid); - } - default => { - FanPageData.set_profile_default(Profile.Balanced); - } + apply => { FanPageData.set_fan_data(FanType.Middle, Profile.Balanced, self.enabled, FanPageData.balanced_mid); } + toggled => { FanPageData.set_fan_data(FanType.Middle, Profile.Balanced, self.enabled, FanPageData.balanced_mid); } + default => { FanPageData.set_profile_default(Profile.Balanced); } } - } - - Tab { - title: @tr("GPU"); - FanTab { + if root.current-profile == 0 && root.current-fan == 2: FanTab { tab_enabled <=> FanPageData.gpu_fan_available; enabled <=> FanPageData.balanced_gpu_enabled; nodes <=> FanPageData.balanced_gpu; - apply => { - FanPageData.set_fan_data(FanType.GPU, Profile.Balanced, self.enabled, FanPageData.balanced_gpu); - } - toggled => { - FanPageData.set_fan_data(FanType.GPU, Profile.Balanced, self.enabled, FanPageData.balanced_gpu); - } - default => { - FanPageData.set_profile_default(Profile.Balanced); - } + apply => { FanPageData.set_fan_data(FanType.GPU, Profile.Balanced, self.enabled, FanPageData.balanced_gpu); } + toggled => { FanPageData.set_fan_data(FanType.GPU, Profile.Balanced, self.enabled, FanPageData.balanced_gpu); } + default => { FanPageData.set_profile_default(Profile.Balanced); } } - } - } - } - - Tab { - title: @tr("Performance"); - TabWidget { - Tab { - title: @tr("CPU"); - FanTab { + if root.current-profile == 1 && root.current-fan == 0: FanTab { tab_enabled <=> FanPageData.cpu_fan_available; enabled <=> FanPageData.performance_cpu_enabled; nodes <=> FanPageData.performance_cpu; - apply => { - FanPageData.set_fan_data(FanType.CPU, Profile.Performance, self.enabled, FanPageData.performance_cpu); - } - toggled => { - FanPageData.set_fan_data(FanType.CPU, Profile.Performance, self.enabled, FanPageData.performance_cpu); - } - default => { - FanPageData.set_profile_default(Profile.Performance); - } + apply => { FanPageData.set_fan_data(FanType.CPU, Profile.Performance, self.enabled, FanPageData.performance_cpu); } + toggled => { FanPageData.set_fan_data(FanType.CPU, Profile.Performance, self.enabled, FanPageData.performance_cpu); } + default => { FanPageData.set_profile_default(Profile.Performance); } } - } - - Tab { - title: @tr("Mid"); - FanTab { + if root.current-profile == 1 && root.current-fan == 1: FanTab { tab_enabled <=> FanPageData.mid_fan_available; enabled <=> FanPageData.performance_mid_enabled; nodes <=> FanPageData.performance_mid; - apply => { - FanPageData.set_fan_data(FanType.Middle, Profile.Performance, self.enabled, FanPageData.performance_mid); - } - toggled => { - FanPageData.set_fan_data(FanType.Middle, Profile.Performance, self.enabled, FanPageData.performance_mid); - } - default => { - FanPageData.set_profile_default(Profile.Performance); - } + apply => { FanPageData.set_fan_data(FanType.Middle, Profile.Performance, self.enabled, FanPageData.performance_mid); } + toggled => { FanPageData.set_fan_data(FanType.Middle, Profile.Performance, self.enabled, FanPageData.performance_mid); } + default => { FanPageData.set_profile_default(Profile.Performance); } } - } - - Tab { - title: @tr("GPU"); - FanTab { + if root.current-profile == 1 && root.current-fan == 2: FanTab { tab_enabled <=> FanPageData.gpu_fan_available; enabled <=> FanPageData.performance_gpu_enabled; nodes <=> FanPageData.performance_gpu; - apply => { - FanPageData.set_fan_data(FanType.GPU, Profile.Performance, self.enabled, FanPageData.performance_gpu); - } - toggled => { - FanPageData.set_fan_data(FanType.GPU, Profile.Performance, self.enabled, FanPageData.performance_gpu); - } - default => { - FanPageData.set_profile_default(Profile.Performance); - } + apply => { FanPageData.set_fan_data(FanType.GPU, Profile.Performance, self.enabled, FanPageData.performance_gpu); } + toggled => { FanPageData.set_fan_data(FanType.GPU, Profile.Performance, self.enabled, FanPageData.performance_gpu); } + default => { FanPageData.set_profile_default(Profile.Performance); } } - } - } - } - - Tab { - title: @tr("Quiet"); - TabWidget { - Tab { - title: @tr("CPU"); - FanTab { + if root.current-profile == 2 && root.current-fan == 0: FanTab { tab_enabled <=> FanPageData.cpu_fan_available; enabled <=> FanPageData.quiet_cpu_enabled; nodes <=> FanPageData.quiet_cpu; - apply => { - FanPageData.set_fan_data(FanType.CPU, Profile.Quiet, self.enabled, FanPageData.quiet_cpu); - } - toggled => { - FanPageData.set_fan_data(FanType.CPU, Profile.Quiet, self.enabled, FanPageData.quiet_cpu); - } - default => { - FanPageData.set_profile_default(Profile.Quiet); - } + apply => { FanPageData.set_fan_data(FanType.CPU, Profile.Quiet, self.enabled, FanPageData.quiet_cpu); } + toggled => { FanPageData.set_fan_data(FanType.CPU, Profile.Quiet, self.enabled, FanPageData.quiet_cpu); } + default => { FanPageData.set_profile_default(Profile.Quiet); } } - } - - Tab { - title: @tr("Mid"); - FanTab { + if root.current-profile == 2 && root.current-fan == 1: FanTab { tab_enabled <=> FanPageData.mid_fan_available; enabled <=> FanPageData.quiet_mid_enabled; nodes <=> FanPageData.quiet_mid; - apply => { - FanPageData.set_fan_data(FanType.Middle, Profile.Quiet, self.enabled, FanPageData.quiet_mid); - } - toggled => { - FanPageData.set_fan_data(FanType.Middle, Profile.Quiet, self.enabled, FanPageData.quiet_mid); - } - default => { - FanPageData.set_profile_default(Profile.Quiet); - } + apply => { FanPageData.set_fan_data(FanType.Middle, Profile.Quiet, self.enabled, FanPageData.quiet_mid); } + toggled => { FanPageData.set_fan_data(FanType.Middle, Profile.Quiet, self.enabled, FanPageData.quiet_mid); } + default => { FanPageData.set_profile_default(Profile.Quiet); } } - } - - Tab { - title: @tr("GPU"); - FanTab { + if root.current-profile == 2 && root.current-fan == 2: FanTab { tab_enabled <=> FanPageData.gpu_fan_available; enabled <=> FanPageData.quiet_gpu_enabled; nodes <=> FanPageData.quiet_gpu; - apply => { - FanPageData.set_fan_data(FanType.GPU, Profile.Quiet, self.enabled, FanPageData.quiet_gpu); - } - toggled => { - FanPageData.set_fan_data(FanType.GPU, Profile.Quiet, self.enabled, FanPageData.quiet_gpu); - } - default => { - FanPageData.set_profile_default(Profile.Quiet); - } + apply => { FanPageData.set_fan_data(FanType.GPU, Profile.Quiet, self.enabled, FanPageData.quiet_gpu); } + toggled => { FanPageData.set_fan_data(FanType.GPU, Profile.Quiet, self.enabled, FanPageData.quiet_gpu); } + default => { FanPageData.set_profile_default(Profile.Quiet); } } } } diff --git a/rog-control-center/ui/pages/gpu.slint b/rog-control-center/ui/pages/gpu.slint index a0d9f358..cf1b002e 100644 --- a/rog-control-center/ui/pages/gpu.slint +++ b/rog-control-center/ui/pages/gpu.slint @@ -1,140 +1,260 @@ -import { Palette, TabWidget, Button, CheckBox, ScrollView } from "std-widgets.slint"; -import { Graph, Node } from "../widgets/graph.slint"; -import { SystemToggle, SystemDropdown } from "../widgets/common.slint"; -import { Profile, FanType, FanPageData } from "../types/fan_types.slint"; +// PageGPU — GPU Configuration page matching AC video frame (f004, 12s mark) +// Layout: titlebar + backend-provided display mode cards + process list +// Pattern: alignment:start, HorizontalLayout, no ScrollView, natural height +// Data: all from GPUPageData global (D-Bus backed) +import { RogCard, RogSlashMark, RogToggle, RogDisplayModeCard, RogPage } from "../widgets/rog.slint"; +import { Theme } from "../globals.slint"; export global GPUPageData { - in-out property gpu_mode_index: 0; // index into gpu_modes - in-out property <[string]> gpu_modes: []; // available modes, in display order - in-out property gpu_switchable: false; // false when no GPU switch exists - in-out property gpu_dropdown_enabled: true; // false when GPU switch is in progress + in-out property gpu_mode_index: 0; + in-out property <[string]> gpu_modes: []; + in-out property gpu_switchable: false; + in-out property gpu_dropdown_enabled: true; callback cb_set_gpu_mode(int); - in-out property has_xgm_led: false; // true when XG Mobile LED is present - in-out property xgm_led_enabled: false; // current LED state + in-out property has_xgm_led: false; + in-out property xgm_led_enabled: false; callback cb_set_xgm_led_enabled(bool); in-out property disable_nvidia_powerd_on_battery: true; callback cb_disable_nvidia_powerd_on_battery(bool); - in-out property apu_mem_index: -1; // -1 when apu_mem is not available - in-out property <[string]> apu_mem_choices: []; // available choices, in display order - in-out property apu_mem_present: false; // false when apu_mem is not available + in-out property apu_mem_index: -1; + in-out property <[string]> apu_mem_choices: []; + in-out property apu_mem_present: false; callback cb_set_apu_mem(int); } -export component PageGPU inherits Rectangle { +export component PageGPU inherits RogPage { + background: Theme.bg-base; clip: true; - property <[string]> modes_all: [@tr("Integrated"), @tr("Hybrid"), @tr("Ultimate")]; - property <[string]> modes_dgpu: [@tr("Integrated"), @tr("Hybrid")]; - property <[string]> modes_mux: [@tr("Hybrid"), @tr("Ultimate")]; + // Backend GpuMode enum (setup_gpu.rs) is exactly: Integrated, Hybrid, Ultimate. + // Unknown values fall through and show their raw backend string — never lie + // about a mode we don't recognize. + function mode-title(mode: string) -> string { + return mode == "Ultimate" ? @tr("Dedicated Output") + : mode == "Hybrid" ? @tr("Hybrid Output") + : mode == "Integrated" ? @tr("Integrated Mode") + : mode; + } - ScrollView { - VerticalLayout { - padding: 12px; - spacing: 8px; + function mode-description(mode: string) -> string { + return mode == "Ultimate" + ? @tr("Uses only the discrete GPU for all rendering. Best performance but higher power consumption.") + : mode == "Hybrid" + ? @tr("[Windows default] Also called MHybrid. Auto-switches between dGPU and iGPU based on workload.") + : mode == "Integrated" + ? @tr("Completely disables discrete GPU for best power saving. Lower temps and noise but significantly reduced performance.") + : ""; + } - Rectangle { - background: Palette.alternate-background; - border-color: Palette.border; - border-width: 1px; - border-radius: 2px; - height: 36px; - Text { - font-size: 16px; - color: Palette.control-foreground; - horizontal-alignment: TextHorizontalAlignment.center; - text: @tr("Internal/Discrete GPU"); - } - } + function mode-icon(mode: string) -> image { + return mode == "Ultimate" ? @image-url("../images/ic-gpu-dedicated.svg") + : mode == "Integrated" ? @image-url("../images/ic-gpu-integrated.svg") + : @image-url("../images/ic-gpu-hybrid.svg"); + } - if !GPUPageData.gpu_switchable: HorizontalLayout { - padding-right: 10px; - padding-left: 10px; - Text { - font-size: 16px; - text: @tr("This device has no switchable GPU."); - } + // ====== Titlebar ====== + titlebar := Rectangle { + y: 0px; + height: 52px; + width: 100%; + background: Theme.bg-base; + HorizontalLayout { + spacing: 12px; + x: 24px; + y: (parent.height - 24px) / 2; + height: 24px; + Text { + text: @tr("GPU"); + color: Theme.text-primary; + font-size: 20px; + font-weight: 700; + vertical-alignment: center; } + } + } + + // ====== Content area ====== + content := Rectangle { + y: titlebar.height; + width: 100%; + + VerticalLayout { + padding-left: 24px; + padding-right: 24px; + padding-top: 12px; + padding-bottom: 24px; + spacing: 20px; + alignment: start; - if GPUPageData.gpu_switchable: HorizontalLayout { - padding-right: 10px; - padding-left: 10px; - alignment: LayoutAlignment.space-between; - Rectangle { - height: 32px; + // ---- Display Mode section ---- + Rectangle { + height: 24px; + HorizontalLayout { + spacing: 8px; + RogSlashMark { count: 3; bar-w: 4px; bar-h: 14px; bar-color: Theme.rog-red; } Text { - font-size: 16px; - text: @tr("Note: Changes take effect after a reboot. The dropdown always shows the current mode."); + text: @tr("Display Mode"); + color: Theme.text-primary; + font-size: 14px; + font-weight: 600; + vertical-alignment: center; } } } - if GPUPageData.gpu_switchable: HorizontalLayout { - SystemDropdown { - text: @tr("GPU mode"); - model: GPUPageData.gpu_modes.length == 3 ? root.modes_all : ( - GPUPageData.gpu_modes.length == 2 && GPUPageData.gpu_modes[0] == "Integrated" ? root.modes_dgpu : root.modes_mux - ); - current_index <=> GPUPageData.gpu_mode_index; - current_value: self.model[self.current_index]; - enabled <=> GPUPageData.gpu_dropdown_enabled; - selected => { - GPUPageData.cb_set_gpu_mode(GPUPageData.gpu_mode_index) + // Explicit geometry keeps every option equal regardless of translated text width. + mode-row := Rectangle { + height: 200px; + clip: true; + + for mode[i] in GPUPageData.gpu_modes: RogDisplayModeCard { + x: i * ((mode-row.width + 16px) / GPUPageData.gpu_modes.length); + width: (mode-row.width - 16px * (GPUPageData.gpu_modes.length - 1)) + / GPUPageData.gpu_modes.length; + height: 200px; + icon: root.mode-icon(mode); + title: root.mode-title(mode); + description: root.mode-description(mode); + selected: i == GPUPageData.gpu_mode_index; + enabled: GPUPageData.gpu_switchable && GPUPageData.gpu_dropdown_enabled; + clicked => { + if i != GPUPageData.gpu_mode_index { + GPUPageData.gpu_mode_index = i; + GPUPageData.cb_set_gpu_mode(i); + } } } - } - if GPUPageData.apu_mem_present: HorizontalLayout { - padding-right: 10px; - padding-left: 10px; - alignment: LayoutAlignment.space-between; - Rectangle { - height: 32px; - Text { - font-size: 16px; - text: @tr("Note: Reserved GPU memory changes take effect after a reboot."); - } + if GPUPageData.gpu_modes.length == 0: Text { + text: @tr("This device has no switchable GPU."); + color: Theme.text-dim; + font-size: 13px; + horizontal-alignment: center; + vertical-alignment: center; } } - if GPUPageData.apu_mem_present: HorizontalLayout { - SystemDropdown { - text: @tr("Reserved GPU memory"); - model <=> GPUPageData.apu_mem_choices; - current_index <=> GPUPageData.apu_mem_index; - current_value: GPUPageData.apu_mem_choices[GPUPageData.apu_mem_index]; - selected => { - GPUPageData.cb_set_apu_mem(GPUPageData.apu_mem_index) + // ---- Process List Section ---- + // Future feature (needs a backend): a live GPU-process viewer. + // asusd exposes no GPU process listing/kill API, so this is + // scaffolding only. To re-enable, build an nvidia-smi-based + // scanner in setup_gpu.rs (populate process-list), then + // uncomment this block AND re-add: the `process-info` struct, + // the `process-list` property on PageGPU, and the + // RogCollapsibleHeader import. + // + // RogCollapsibleHeader { + // title: @tr("Processes"); + // expanded: true; + // } + // RogCard { + // width: 100%; + // VerticalLayout { + // padding: Theme.pad-card; + // spacing: 12px; + // Text { + // text: @tr("The following applications are using the GPU. Consider closing them to save power."); + // color: Theme.text-dim; font-size: 11px; wrap: word-wrap; + // } + // HorizontalLayout { + // Rectangle { horizontal-stretch: 1; } + // Rectangle { + // width: 90px; height: 28px; border-radius: 3px; + // border-width: 1px; border-color: Theme.line; + // Text { + // text: @tr("Stop All"); color: Theme.text-secondary; font-size: 11px; + // horizontal-alignment: center; vertical-alignment: center; + // } + // } + // Rectangle { + // width: 70px; height: 28px; border-radius: 3px; + // border-width: 1px; border-color: Theme.line; + // Text { + // text: @tr("Refresh"); color: Theme.text-secondary; font-size: 11px; + // horizontal-alignment: center; vertical-alignment: center; + // } + // } + // } + // HorizontalLayout { spacing: 0; height: 28px; + // Rectangle { width: 28px; } + // Text { text: @tr("Process"); color: Theme.text-dim; font-size: 11px; vertical-alignment: center; } + // Rectangle { horizontal-stretch: 1; } + // Text { text: @tr("CPU"); color: Theme.text-dim; font-size: 11px; horizontal-alignment: right; vertical-alignment: center; min-width: 60px; } + // Text { text: @tr("Memory"); color: Theme.text-dim; font-size: 11px; horizontal-alignment: right; vertical-alignment: center; min-width: 70px; } + // } + // Rectangle { height: 1px; background: Theme.line-soft; } + // empty-state := Rectangle { + // visible: root.process-list.length == 0; + // height: 80px; + // Text { + // text: @tr("No GPU-intensive processes detected."); + // color: Theme.text-dim; font-size: 12px; + // horizontal-alignment: center; vertical-alignment: center; + // } + // } + // for proc in root.process-list: HorizontalLayout { spacing: 0; height: 36px; + // visible: root.process-list.length > 0; + // Rectangle { width: 28px; + // Rectangle { + // x: 6px; y: 9px; width: 14px; height: 14px; border-radius: 2px; + // border-width: 1px; border-color: #555555; + // } + // } + // Image { + // source: @image-url("../images/ic-cpu-fill.svg"); + // colorize: Theme.text-dim; width: 16px; height: 16px; + // image-fit: contain; vertical-alignment: center; + // } + // Text { text: proc.name; color: Theme.text-secondary; font-size: 12px; vertical-alignment: center; } + // Rectangle { horizontal-stretch: 1; } + // Text { text: proc.cpu; color: Theme.text-secondary; font-size: 11px; horizontal-alignment: right; vertical-alignment: center; min-width: 60px; } + // Text { text: proc.mem; color: Theme.text-secondary; font-size: 11px; horizontal-alignment: right; vertical-alignment: center; min-width: 70px; } + // } + // Rectangle { height: 1px; background: Theme.bg-app; } + // } + // } + + // ---- GPU Settings section ---- + RogCard { + width: 100%; + VerticalLayout { + padding: Theme.pad-card; + spacing: 14px; + + RogToggle { + label: @tr("Disable nvidia-powerd on battery"); + checked <=> GPUPageData.disable_nvidia_powerd_on_battery; + toggled(v) => { GPUPageData.cb_disable_nvidia_powerd_on_battery(v); } } - } - } - if GPUPageData.has_xgm_led: HorizontalLayout { - padding-right: 10px; - padding-left: 10px; - SystemToggle { - text: @tr("XG Mobile LED"); - checked <=> GPUPageData.xgm_led_enabled; - toggled(checked) => { - GPUPageData.cb_set_xgm_led_enabled(checked); + if GPUPageData.has_xgm_led: RogToggle { + label: @tr("XG Mobile LED"); + checked <=> GPUPageData.xgm_led_enabled; + toggled(v) => { GPUPageData.cb_set_xgm_led_enabled(v); } } - } - } - // Disable nvidia-powerd on battery toggle - HorizontalLayout { - padding-right: 10px; - padding-left: 10px; - SystemToggle { - text: @tr("Disable nvidia-powerd on battery"); - checked <=> GPUPageData.disable_nvidia_powerd_on_battery; - toggled(checked) => { - GPUPageData.cb_disable_nvidia_powerd_on_battery(checked); + if GPUPageData.apu_mem_present: VerticalLayout { + spacing: 10px; + HorizontalLayout { + Text { text: @tr("Reserved GPU Memory (iGPU VRAM)"); color: Theme.text-secondary; font-size: 13px; vertical-alignment: center; } + Rectangle { horizontal-stretch: 1; } + Rectangle { + width: 140px; height: 30px; border-radius: 3px; + border-width: 1px; border-color: Theme.line; background: Theme.bg-elevated; + Text { + text: GPUPageData.apu_mem_choices[GPUPageData.apu_mem_index]; + color: Theme.text-secondary; font-size: 12px; + horizontal-alignment: center; vertical-alignment: center; + } + } + } + Text { text: @tr("Note: Reserved GPU memory changes take effect after a reboot."); color: Theme.text-dim; font-size: 11px; } } } } } } -} \ No newline at end of file +} diff --git a/rog-control-center/ui/pages/home.slint b/rog-control-center/ui/pages/home.slint new file mode 100644 index 00000000..90a88c40 --- /dev/null +++ b/rog-control-center/ui/pages/home.slint @@ -0,0 +1,279 @@ +// PageHome — Dashboard page matching AC system-page.html reference +// Layout: HorizontalLayout 2-col (left 360px fixed + right flexible), no ScrollView (avoids binding loops) +// Font: Inter Display for product name +// Data: all from SystemPageData, no hardcoded values +import { RogProductCard, RogSlashMark, RogMetric, RogReading, RogStatRow, RogPage } from "../widgets/rog.slint"; +import { Theme } from "../globals.slint"; +import { SystemPageData } from "system.slint"; + +export component PageHome inherits RogPage { + background: Theme.bg-base; + clip: true; + + // Product name from SystemPageData (backend reads /sys/class/dmi/id/product_name) + + // ====== Titlebar ====== + titlebar := Rectangle { + y: 0px; + height: 52px; + width: 100%; + background: Theme.bg-base; + Text { + text: @tr("Home"); + color: Theme.text-primary; + font-size: 20px; + font-weight: 700; + x: 32px; + y: 13px; + } + } + + // ====== Dashboard — fills remaining window height so the vertical flex + // gaps inside the right column can grow in fullscreen. ====== + dashboard := Rectangle { + y: titlebar.height; + width: 100%; + height: parent.height - titlebar.height; + + VerticalLayout { + padding-left: 24px; + padding-right: 24px; + padding-top: 12px; + padding-bottom: 24px; + spacing: 0px; + + HorizontalLayout { + spacing: 36px; + + // Left column: product card (kept square) + VerticalLayout { + width: 360px; + alignment: start; + + product-card := RogProductCard { + width: 100%; + // Keep the card square: height tracks the column width. + height: parent.width; + name: SystemPageData.product_name != "" ? SystemPageData.product_name : "ROG Laptop"; + // CPU + GPU straight off the backend — no hardcoded specs. + details: [ + SystemPageData.cpu_name, + SystemPageData.has_dgpu ? SystemPageData.dgpu_name : SystemPageData.igpu_name, + ]; + } + } + + // Right column: System Usage + Operation Mode. + // No `alignment` (default distributes free space by stretch). + // Content blocks have vertical-stretch:0 so they keep their + // natural height; only the flex spacers grow in fullscreen. + VerticalLayout { + horizontal-stretch: 1; + spacing: 20px; + + // System Usage header + Rectangle { + height: 24px; + vertical-stretch: 0; + HorizontalLayout { + spacing: 8px; + RogSlashMark {} + Text { + text: @tr("System Usage"); + color: Theme.text-primary; + font-size: 14px; + font-weight: 600; + vertical-alignment: center; + } + } + } + + // Row 1: CPU | GPU — cards capped so they don't over-widen; + // the fixed 36px middle gap stays as it was. + HorizontalLayout { + vertical-stretch: 0; + spacing: 36px; + + cpu-metric := RogMetric { + horizontal-stretch: 1; + max-width: 340px; + title: "CPU"; + RogReading { + label: @tr("Frequency"); + value: SystemPageData.cpu_freq_mhz > 0.0 ? "\{Math.round(SystemPageData.cpu_freq_mhz)} MHz" : "--"; + progress: SystemPageData.cpu_freq_mhz / 5000.0; + } + RogReading { + label: @tr("Usage"); + value: SystemPageData.cpu_usage_val >= 0.0 ? "\{Math.round(SystemPageData.cpu_usage_val)} %" : "--"; + progress: SystemPageData.cpu_usage_val / 100.0; + } + RogStatRow { + label: @tr("Temperature"); + value: SystemPageData.cpu_temp_val > 0.0 ? "\{Math.round(SystemPageData.cpu_temp_val)} °C" : "--"; + } + } + + if SystemPageData.has_dgpu: RogMetric { + horizontal-stretch: 1; + max-width: 340px; + title: "GPU"; + RogReading { + label: @tr("Frequency"); + value: SystemPageData.gpu_freq_mhz > 0.0 ? "\{Math.round(SystemPageData.gpu_freq_mhz)} MHz" : "--"; + progress: SystemPageData.gpu_freq_mhz / 2500.0; + } + RogReading { + label: @tr("Usage"); + value: SystemPageData.gpu_usage_val >= 0.0 ? "\{Math.round(SystemPageData.gpu_usage_val)} %" : "--"; + progress: SystemPageData.gpu_usage_val / 100.0; + } + RogStatRow { + label: @tr("Temperature"); + value: SystemPageData.gpu_temp_val > 0.0 ? "\{Math.round(SystemPageData.gpu_temp_val)} °C" : "--"; + } + RogStatRow { + label: @tr("TGP"); + value: SystemPageData.nv_tgp.current > 0.0 ? "\{Math.round(SystemPageData.nv_tgp.current)} W" : "--"; + } + } + } + + // Vertical flex gap: grows in fullscreen, keeps the rows apart. + Rectangle { vertical-stretch: 1; min-height: 0px; } + + // Row 2: Fan | Memory + HorizontalLayout { + vertical-stretch: 0; + spacing: 36px; + + fan-metric := RogMetric { + horizontal-stretch: 1; + max-width: 340px; + title: @tr("Fan"); + RogReading { + label: @tr("CPU Fan"); + value: SystemPageData.cpu_fan_rpm > 0 ? "\{SystemPageData.cpu_fan_rpm} RPM" : "--"; + progress: SystemPageData.cpu_fan_rpm / 5000.0; + } + RogReading { + label: @tr("GPU Fan"); + value: SystemPageData.gpu_fan_rpm > 0 ? "\{SystemPageData.gpu_fan_rpm} RPM" : "--"; + progress: SystemPageData.gpu_fan_rpm / 5000.0; + } + RogStatRow { + label: @tr("System Fan"); + value: SystemPageData.mid_fan_rpm > 0 ? "\{SystemPageData.mid_fan_rpm} RPM" : "--"; + } + } + + mem-metric := RogMetric { + horizontal-stretch: 1; + max-width: 340px; + title: @tr("Memory"); + RogReading { + label: "RAM"; + value: SystemPageData.ram_usage_val >= 0.0 ? "\{Math.round(SystemPageData.ram_usage_val)} %" : "--"; + progress: SystemPageData.ram_usage_val / 100.0; + } + } + } + + // Vertical flex gap before the mode section. + Rectangle { vertical-stretch: 1; min-height: 0px; } + + // Operation Mode section — switches the real asusd platform + // profile. Buttons are generated from the device's actual + // profiles (platform_profile_indexes), so the count matches + // the hardware instead of a hardcoded AC-style set. ev is the + // profile enum value (passed to cb_platform_profile); p is the + // position (used for layout, label lookup and the active check). + VerticalLayout { + vertical-stretch: 0; + spacing: 12px; + + Rectangle { + height: 24px; + HorizontalLayout { + spacing: 8px; + RogSlashMark {} + Text { + text: @tr("Operation Mode"); + color: Theme.text-primary; + font-size: 14px; + font-weight: 600; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + } + } + + Rectangle { + height: 104px; + width: 100%; + clip: true; + for ev[p] in SystemPageData.platform_profile_indexes: Rectangle { + x: p * (parent.width / SystemPageData.platform_profile_indexes.length); + y: 0px; + width: parent.width / SystemPageData.platform_profile_indexes.length; + height: 100%; + + if SystemPageData.platform_profile == p: Rectangle { + x: 0px; y: 0px; + width: 100%; height: 100%; + background: Theme.mode-selected; + border-radius: 4px; + drop-shadow-blur: 16px; + drop-shadow-color: Theme.rog-red-glow; + } + + Image { + // Icon by power intensity: Quiet/LowPower → silent, + // Balanced → performance, Performance → turbo, Custom → manual. + source: ev == 0 ? @image-url("../images/mode-performance.svg") : + ev == 1 ? @image-url("../images/mode-turbo.svg") : + ev == 2 ? @image-url("../images/mode-silent.svg") : + ev == 3 ? @image-url("../images/mode-silent.svg") : + ev == 4 ? @image-url("../images/mode-manual.svg") : + @image-url("../images/mode-performance.svg"); + colorize: SystemPageData.platform_profile == p ? Theme.text-primary : Theme.text-secondary; + width: 32px; height: 32px; + image-fit: contain; + x: (parent.width - self.width) / 2; + y: 12px; + } + + Text { + text: SystemPageData.platform_profile_choices[p]; + color: SystemPageData.platform_profile == p ? Theme.text-primary : Theme.text-secondary; + font-size: 14px; + width: parent.width; + horizontal-alignment: center; + y: 54px; + } + + if SystemPageData.platform_profile == p: Rectangle { + x: (parent.width - 40px) / 2; + y: parent.height - 6px; + width: 40px; height: 3px; + background: Theme.rog-red; + drop-shadow-blur: 10px; + drop-shadow-offset-y: -4px; + drop-shadow-color: Theme.rog-red-glow; + } + + TouchArea { + width: 100%; height: 100%; + mouse-cursor: pointer; + clicked => { + SystemPageData.cb_platform_profile(ev); + } + } + } + } + } + } + } + } + } +} diff --git a/rog-control-center/ui/pages/slash.slint b/rog-control-center/ui/pages/slash.slint index da151f0d..a07c7d6b 100644 --- a/rog-control-center/ui/pages/slash.slint +++ b/rog-control-center/ui/pages/slash.slint @@ -1,5 +1,354 @@ -import { Palette, VerticalBox, ScrollView } from "std-widgets.slint"; -import { SystemDropdown, SystemSlider, SystemToggle, RogItem } from "../widgets/common.slint"; +import { ComboBox } from "std-widgets.slint"; +import { RogToggle, RogPage } from "../widgets/rog.slint"; +import { Theme } from "../globals.slint"; + +component SlashPanelHeader inherits Rectangle { + in property title; + in property expanded; + in property show-sync: false; + callback clicked(); + callback sync-clicked(); + + height: 44px; + border-radius: 4px; + border-width: 1px; + border-color: Theme.line; + background: Theme.bg-preview; + + HorizontalLayout { + padding-left: 14px; + // Reserve the icon's footprint so a long translated title can't slide + // underneath it. + padding-right: 44px; + spacing: 12px; + + Text { + text: root.expanded ? "▼" : "▶"; + color: root.expanded ? Theme.rog-red : Theme.text-primary; + font-size: 10px; + vertical-alignment: center; + } + Text { + text: root.title; + color: Theme.text-primary; + font-size: 14px; + font-weight: 600; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + } + + // Reset icon: pulled out of the HorizontalLayout and absolutely positioned, + // because a layout child can't reliably center vertically — + // `vertical-alignment` only reflows glyphs inside a Text box, not an Image. + if root.show-sync: Image { + source: @image-url("../images/ic-sync.svg"); + colorize: Theme.text-secondary; + width: 20px; + height: 20px; + image-fit: contain; + x: parent.width - 14px - self.width; + y: (parent.height - self.height) / 2; + } + + // Click anywhere on the header toggles expand. + TouchArea { + mouse-cursor: pointer; + clicked => { root.clicked(); } + } + + // The sync icon is a reset action: its own hit area, layered ON TOP of the + // expand TouchArea so only the icon (right side) triggers it. + if root.show-sync: TouchArea { + x: parent.width - 44px; + width: 44px; + height: 100%; + mouse-cursor: pointer; + clicked => { root.sync-clicked(); } + } +} + +component SlashSwitch inherits Rectangle { + in-out property checked; + in property enabled: true; + callback toggled(bool); + + width: 46px; + height: 24px; + border-radius: 4px; + background: root.enabled ? Theme.fg-normal : Theme.text-dim; + opacity: root.enabled ? 1.0 : 0.55; + + Text { + x: root.checked ? 5px : 24px; + width: 18px; + height: 100%; + text: root.checked ? "ON" : "OFF"; + color: #333333; + font-size: 10px; + horizontal-alignment: center; + vertical-alignment: center; + } + Rectangle { + x: root.checked ? 27px : 3px; + y: 3px; + width: 18px; + height: 18px; + border-radius: 4px; + background: #202020; + animate x { duration: 120ms; easing: ease; } + } + TouchArea { + enabled: root.enabled; + mouse-cursor: pointer; + clicked => { + root.checked = !root.checked; + root.toggled(root.checked); + } + } +} + +component SlashSlider inherits Rectangle { + in property label; + in property value-text; + in-out property value; + in property maximum: 100; + in property enabled: true; + callback released(int); + + private property fraction: clamp(root.value / max(root.maximum, 1), 0.0, 1.0); + height: 70px; + opacity: root.enabled ? 1.0 : 0.45; + + VerticalLayout { + spacing: 8px; + HorizontalLayout { + Text { + text: root.label; + color: Theme.text-secondary; + font-size: 12px; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + Text { + text: root.value-text; + color: Theme.text-secondary; + font-size: 12px; + vertical-alignment: center; + } + } + track-area := Rectangle { + width: 100%; + height: 30px; + + for dot[i] in 6: Rectangle { + x: i * (parent.width - 6px) / 5; + y: 0px; + width: 6px; + height: 6px; + border-radius: 3px; + background: Theme.text-secondary; + } + Rectangle { + y: 18px; + width: 100%; + height: 4px; + border-radius: 2px; + background: Theme.line; + Rectangle { + x: 0; + width: parent.width * root.fraction; + height: 100%; + border-radius: 2px; + background: Theme.rog-red; + } + } + Rectangle { + x: root.fraction * (parent.width - self.width); + y: 14px; + width: 10px; + height: 14px; + border-radius: 2px; + background: Theme.fg-normal; + } + TouchArea { + enabled: root.enabled; + mouse-cursor: pointer; + pointer-event(e) => { + if e.kind == PointerEventKind.down || (e.kind == PointerEventKind.move && self.pressed) { + root.value = Math.round(clamp((self.mouse-x - 5px) / (self.width - 10px), 0.0, 1.0) * root.maximum); + } + if e.kind == PointerEventKind.up { + root.released(root.value); + } + } + } + } + } +} + +component SlashDevicePreview inherits Rectangle { + background: transparent; + in property active: false; + + // asusd's Slash D-Bus interface exposes no LED/zone count — it only sends a + // whole-bar animation mode. This is a PREVIEW fidelity constant, not hardware + // data: how many segments to draw when approximating the bar. The animation + // math (segment-level below) is modular and adapts to any value here. + private property zone-count: 7; + private property last-zone: root.zone-count - 1; + private property bounce-cycle: root.last-zone * 2; + private property animation-step: 0; + + // Segment geometry, derived so the preview fills the same bar regardless of + // zone-count (was hardcoded to a 7-zone step). Plain numbers: these are + // viewbox coordinates (1440x1000), not physical lengths. + private property seg-dx: 1120 / root.zone-count; + private property seg-dy: 710 / root.zone-count; + private property seg-x0: 161; + private property seg-y0: 157; + + private property light-opacity: SlashPageData.enabled + ? 0.38 + 0.62 * SlashPageData.brightness / 255 + : 0.20; + + function segment-level(index: int) -> float { + // The firmware renders the effects and exposes no per-zone frame data. + // Approximate the motion while keeping mode, speed and brightness exact. + if SlashPageData.mode == 0 { + return 1.0; + } + + if SlashPageData.mode == 1 { + // Bounce: a short packet travels in both directions. + return Math.abs(index - (Math.mod(root.animation-step, root.bounce-cycle) < root.zone-count + ? Math.mod(root.animation-step, root.bounce-cycle) + : root.bounce-cycle - Math.mod(root.animation-step, root.bounce-cycle))) < 1 ? 1.0 : 0.08; + } + if SlashPageData.mode == 2 { + // Slash: a sharp one-way sweep. + return index == Math.mod(root.animation-step, root.zone-count) ? 1.0 : 0.04; + } + if SlashPageData.mode == 3 || SlashPageData.mode == 14 { + // Loading and Start progressively fill the strip. + return index <= Math.mod(root.animation-step, root.zone-count + 2) ? 1.0 : 0.06; + } + if SlashPageData.mode == 4 { + // BitStream: staggered digital pulses. + return Math.mod(index * 3 + root.animation-step, 5) < 2 ? 1.0 : 0.08; + } + if SlashPageData.mode == 5 { + // Transmission: two packets move in opposite directions. + return index == Math.mod(root.animation-step, root.zone-count) + || index == root.last-zone - Math.mod(root.animation-step, root.zone-count) ? 1.0 : 0.06; + } + if SlashPageData.mode == 6 { + // Flow: a continuous head with a fading tail. + return 0.12 + 0.88 * Math.mod(index - root.animation-step + root.zone-count * 2, root.zone-count) / root.last-zone; + } + if SlashPageData.mode == 7 { + // Flux: the whole strip breathes together. + return 0.16 + 0.84 * Math.abs(Math.mod(root.animation-step, 32) - 16) / 16; + } + if SlashPageData.mode == 8 { + // Phantom: a broad, soft moving trail. + return max(0.08, 1.0 - Math.abs(index - Math.mod(root.animation-step, root.zone-count + 2)) / 2.5); + } + if SlashPageData.mode == 9 { + // Spectrum: several intensity waves travel along the diffuser. + return 0.20 + 0.80 * Math.abs(Math.mod(index * 2 + root.animation-step, 8) - 4) / 4; + } + if SlashPageData.mode == 10 { + // Hazard: alternating warning bands flash out of phase. + return Math.mod(index + Math.floor(root.animation-step / 3), 2) == 0 ? 1.0 : 0.08; + } + if SlashPageData.mode == 11 { + // Interfacing: dim idle segments behind a precise scan point. + return index == Math.mod(root.animation-step, root.zone-count) ? 1.0 : 0.22; + } + if SlashPageData.mode == 12 { + // Ramp: a moving brightness gradient. + return 0.12 + 0.88 * Math.mod(index + root.animation-step, root.zone-count) / root.last-zone; + } + if SlashPageData.mode == 13 { + // GameOver drains the strip from the far end. + return index < root.zone-count - Math.mod(root.animation-step, root.zone-count + 2) ? 0.72 : 0.03; + } + + // Buzzer: a fast full-strip pulse. + return Math.mod(root.animation-step, 6) < 2 ? 1.0 : 0.05; + } + + // Animate the preview for any non-static mode, even when the hardware is + // off — users should see what an effect looks like before enabling it. + // `enabled` only dims brightness (light-opacity), not whether it animates. + Timer { + running: root.active && SlashPageData.mode != 0; + interval: (70 + SlashPageData.interval * 32) * 1ms; + triggered => { root.animation-step = Math.mod(root.animation-step + 1, 1240); } + } + + device := Rectangle { + width: min(root.width - 8px, (root.height - 8px) * 1.44); + height: self.width / 1.44; + x: (root.width - self.width) / 2; + y: (root.height - self.height) / 2; + clip: true; + + Image { + source: @image-url("../images/slash-device-shell.svg"); + width: 100%; + height: 100%; + image-fit: fill; + } + + // A soft underlay gives the physical diffuser depth without extra effects. + Path { + width: 100%; + height: 100%; + viewbox-width: 1440; + viewbox-height: 1000; + fill: transparent; + stroke: Theme.fg-normal; + stroke-width: 12px; + opacity: SlashPageData.enabled ? root.light-opacity * 0.06 : 0.03; + MoveTo { x: 174; y: 148; } + LineTo { x: 1274; y: 856; } + } + + // The two rails reproduce the outlined Slash light bar on the lid. + Path { + width: 100%; + height: 100%; + viewbox-width: 1440; + viewbox-height: 1000; + fill: transparent; + stroke: Theme.fg-normal; + stroke-width: 3px; + opacity: SlashPageData.enabled ? 0.24 + root.light-opacity * 0.18 : 0.20; + MoveTo { x: 168; y: 148; } + LineTo { x: 1284; y: 856; } + LineTo { x: 1271; y: 874; } + LineTo { x: 155; y: 166; } + LineTo { x: 168; y: 148; } + } + + // Animated segments along the bar. Spacing derives from zone-count above, + // so the preview is never silently coupled to a specific segment count. + for segment[index] in root.zone-count: Path { + width: 100%; + height: 100%; + viewbox-width: 1440; + viewbox-height: 1000; + fill: transparent; + stroke: Theme.fg-normal; + stroke-width: 8px; + opacity: root.light-opacity * root.segment-level(index); + MoveTo { x: root.seg-x0 + index * root.seg-dx; y: root.seg-y0 + index * root.seg-dy; } + LineTo { x: root.seg-x0 + index * root.seg-dx + 145; y: root.seg-y0 + index * root.seg-dy + 92; } + } + } +} export global SlashPageData { in-out property enabled: false; @@ -29,145 +378,179 @@ export global SlashPageData { callback cb_show_on_lid_closed(bool); } -export component PageSlash inherits Rectangle { +export component PageSlash inherits RogPage { + background: Theme.bg-base; clip: true; + in property page-active: false; - ScrollView { - VerticalLayout { - padding: 12px; - spacing: 8px; - alignment: LayoutAlignment.start; - - Rectangle { - background: Palette.alternate-background; - border-color: Palette.border; - border-width: 1px; - border-radius: 2px; - height: 36px; - Text { - font-size: 16px; - color: Palette.control-foreground; - horizontal-alignment: TextHorizontalAlignment.center; - text: @tr("Slash lighting"); - } - } + // Basic-effects panel expanded state (the only collapsible panel left). + property basic-effects-expanded: true; + property show-settings: false; - SystemToggle { - text: @tr("Enable A-cover lighting"); - checked <=> SlashPageData.enabled; - toggled => { - SlashPageData.cb_enabled(SlashPageData.enabled); - } - } + titlebar := Rectangle { + y: 0px; + width: 100%; + height: 52px; + background: Theme.bg-base; + Text { + x: 24px; + y: 13px; + text: @tr("Slash Lighting"); + color: Theme.text-primary; + font-size: 20px; + font-weight: 700; + } + } - SystemSlider { - text: @tr("Brightness"); - minimum: 0; - maximum: 255; - has_reset: false; - enabled <=> SlashPageData.enabled; - value: SlashPageData.brightness; - released => { - SlashPageData.brightness = Math.round(self.value); - SlashPageData.cb_brightness(SlashPageData.brightness); - } - } + content := Rectangle { + y: titlebar.height; + width: 100%; + height: root.height - titlebar.height; - SystemSlider { - text: @tr("Animation speed"); - minimum: 0; - maximum: 5; - has_reset: false; - enabled <=> SlashPageData.enabled; - value: SlashPageData.interval; - released => { - SlashPageData.interval = Math.round(self.value); - SlashPageData.cb_interval(SlashPageData.interval); - } - } + VerticalLayout { + padding-left: 24px; + padding-right: 24px; + padding-top: 12px; + padding-bottom: 24px; + spacing: 14px; - if SlashPageData.mode_choices.length > 0: SystemDropdown { - text: @tr("Animation"); - current_index <=> SlashPageData.mode; - current_value: SlashPageData.mode_choices[SlashPageData.mode]; - model <=> SlashPageData.mode_choices; - enabled <=> SlashPageData.enabled; - selected => { - SlashPageData.cb_mode(SlashPageData.mode); + HorizontalLayout { + height: 32px; + spacing: 12px; + SlashSwitch { + checked <=> SlashPageData.enabled; + toggled(value) => { SlashPageData.cb_enabled(value); } } - } - - Rectangle { - background: Palette.alternate-background; - border-color: Palette.border; - border-width: 1px; - border-radius: 2px; - height: 36px; Text { - font-size: 16px; - color: Palette.control-foreground; - horizontal-alignment: TextHorizontalAlignment.center; - text: @tr("Visibility triggers"); + text: @tr("Active Slash Lighting"); + color: Theme.text-secondary; + font-size: 14px; + vertical-alignment: center; } - } - - SystemToggle { - text: @tr("Show on boot"); - checked <=> SlashPageData.show_on_boot; - toggled => { - SlashPageData.cb_show_on_boot(SlashPageData.show_on_boot); + Rectangle { horizontal-stretch: 1; } + Rectangle { + width: 96px; + height: 30px; + border-radius: 3px; + border-width: 1px; + border-color: root.show-settings ? Theme.rog-red : Theme.text-secondary; + Text { + text: root.show-settings ? @tr("Effects") : @tr("Settings"); + color: root.show-settings ? Theme.rog-red : Theme.text-primary; + font-size: 12px; + horizontal-alignment: center; + vertical-alignment: center; + } + TouchArea { + mouse-cursor: pointer; + clicked => { root.show-settings = !root.show-settings; } + } } } - SystemToggle { - text: @tr("Show on shutdown"); - checked <=> SlashPageData.show_on_shutdown; - toggled => { - SlashPageData.cb_show_on_shutdown(SlashPageData.show_on_shutdown); - } - } + HorizontalLayout { + spacing: 20px; + vertical-stretch: 1; - SystemToggle { - text: @tr("Show on sleep"); - checked <=> SlashPageData.show_on_sleep; - toggled => { - SlashPageData.cb_show_on_sleep(SlashPageData.show_on_sleep); + preview := SlashDevicePreview { + active: root.page-active; + horizontal-stretch: 2; + min-width: 0px; } - } - SystemToggle { - text: @tr("Show on battery"); - checked <=> SlashPageData.show_on_battery; - toggled => { - SlashPageData.cb_show_on_battery(SlashPageData.show_on_battery); - } - } + right-panel := Rectangle { + horizontal-stretch: 1; + min-width: 280px; + max-width: 520px; + clip: true; - SystemToggle { - text: @tr("Show low-battery warning"); - checked <=> SlashPageData.show_battery_warning; - toggled => { - SlashPageData.cb_show_battery_warning(SlashPageData.show_battery_warning); - } - } + if !root.show-settings: VerticalLayout { + spacing: 10px; + alignment: start; - SystemToggle { - text: @tr("Show when lid is closed"); - checked <=> SlashPageData.show_on_lid_closed; - toggled => { - SlashPageData.cb_show_on_lid_closed(SlashPageData.show_on_lid_closed); - } - } + SlashPanelHeader { + title: @tr("Basic Effects"); + expanded: root.basic-effects-expanded; + show-sync: true; + clicked => { root.basic-effects-expanded = !root.basic-effects-expanded; } + // Reset icon: restore animation speed (interval) and + // brightness to their defaults. + sync-clicked => { + SlashPageData.interval = 0; + SlashPageData.brightness = 255; + SlashPageData.cb_interval(0); + SlashPageData.cb_brightness(255); + } + } + if root.basic-effects-expanded: Rectangle { + height: 220px; + border-radius: 4px; + border-width: 1px; + border-color: Theme.line; + background: Theme.bg-preview; + VerticalLayout { + padding-left: 16px; + padding-right: 16px; + padding-top: 14px; + padding-bottom: 10px; + spacing: 8px; + if SlashPageData.mode_choices.length > 0: mode-combo := ComboBox { + height: 34px; + model: SlashPageData.mode_choices; + // One-way display binding: a two-way `<=>` would let the + // freshly-created ComboBox (current-index defaults to 0) + // clobber the persisted mode back to Static on every + // page re-entry. Read the combo's own current-index in + // the callback and write it back explicitly. + current-index: SlashPageData.mode; + enabled: SlashPageData.enabled; + selected => { + SlashPageData.mode = mode-combo.current-index; + SlashPageData.cb_mode(mode-combo.current-index); + } + } + SlashSlider { + label: @tr("Interval"); + value-text: "\{SlashPageData.interval} s"; + value <=> SlashPageData.interval; + maximum: 5; + enabled: SlashPageData.enabled; + released(value) => { SlashPageData.cb_interval(value); } + } + SlashSlider { + label: @tr("Brightness"); + value-text: "\{Math.round(SlashPageData.brightness * 100 / 255)} %"; + value <=> SlashPageData.brightness; + maximum: 255; + enabled: SlashPageData.enabled; + released(value) => { SlashPageData.cb_brightness(value); } + } + } + } + } - RogItem { - min-height: 64px; - max-height: 64px; - Text { - width: parent.width - 20px; - x: 10px; - wrap: TextWrap.word-wrap; - color: Palette.control-foreground; - text: @tr("Slash_tip" => "Slash Lighting controls the A-cover LED strip found on supported Zephyrus models."); + if root.show-settings: Rectangle { + border-radius: 4px; + border-width: 1px; + border-color: Theme.line; + background: Theme.bg-preview; + VerticalLayout { + padding: 16px; + spacing: 8px; + Text { + text: @tr("Visibility Triggers"); + color: Theme.text-primary; + font-size: 15px; + font-weight: 600; + } + RogToggle { height: 34px; label: @tr("Show on boot"); checked <=> SlashPageData.show_on_boot; toggled(value) => { SlashPageData.cb_show_on_boot(value); } } + RogToggle { height: 34px; label: @tr("Show on shutdown"); checked <=> SlashPageData.show_on_shutdown; toggled(value) => { SlashPageData.cb_show_on_shutdown(value); } } + RogToggle { height: 34px; label: @tr("Show on sleep"); checked <=> SlashPageData.show_on_sleep; toggled(value) => { SlashPageData.cb_show_on_sleep(value); } } + RogToggle { height: 34px; label: @tr("Show on battery"); checked <=> SlashPageData.show_on_battery; toggled(value) => { SlashPageData.cb_show_on_battery(value); } } + RogToggle { height: 34px; label: @tr("Show low-battery warning"); checked <=> SlashPageData.show_battery_warning; toggled(value) => { SlashPageData.cb_show_battery_warning(value); } } + RogToggle { height: 34px; label: @tr("Show when lid is closed"); checked <=> SlashPageData.show_on_lid_closed; toggled(value) => { SlashPageData.cb_show_on_lid_closed(value); } } + } + } } } } diff --git a/rog-control-center/ui/pages/system.slint b/rog-control-center/ui/pages/system.slint index 15438306..83453bda 100644 --- a/rog-control-center/ui/pages/system.slint +++ b/rog-control-center/ui/pages/system.slint @@ -163,6 +163,7 @@ export global SystemPageData { in-out property gpu_usage_val: -1.0; in-out property ram_usage_val: -1.0; in-out property cpu_freq_mhz: -1.0; + in-out property gpu_freq_mhz: -1.0; in-out property cpu_fan_rpm: -1; in-out property gpu_fan_rpm: -1; in-out property mid_fan_rpm: -1; @@ -173,6 +174,9 @@ export global SystemPageData { in-out property igpu_temp_val: -1.0; in-out property igpu_usage_val: -1.0; in-out property has_igpu: false; + + // Real product name from DMI (read in setup_system.rs). + in-out property product_name: ""; } export component PageSystem inherits Rectangle { diff --git a/rog-control-center/ui/types/aura_types.slint b/rog-control-center/ui/types/aura_types.slint index e6d4795f..0c5abde4 100644 --- a/rog-control-center/ui/types/aura_types.slint +++ b/rog-control-center/ui/types/aura_types.slint @@ -74,6 +74,8 @@ export global AuraPageData { ]; in-out property brightness; callback cb_brightness(int); + // Set the animation speed (0..2, Low/Medium/High) for the current mode. + callback cb_speed(int); in-out property <[string]> mode_names: [ @tr("Basic aura mode" => "Static"), @tr("Basic aura mode" => "Breathe"), diff --git a/rog-control-center/ui/widgets/common.slint b/rog-control-center/ui/widgets/common.slint index d4743062..90daf50a 100644 --- a/rog-control-center/ui/widgets/common.slint +++ b/rog-control-center/ui/widgets/common.slint @@ -1,9 +1,11 @@ import { Palette, VerticalBox , StandardButton, Button, HorizontalBox, ComboBox, Switch, Slider} from "std-widgets.slint"; +import { Theme } from "../globals.slint"; export component RogItem inherits Rectangle { - border-color: Palette.border; + border-color: Theme.border; border-width: 1px; - border-radius: 2px; + border-radius: Theme.radius-item; + background: Theme.bg-card; min-height: 44px; max-height: 44px; } @@ -325,7 +327,7 @@ export component PopupNotification { } Button { - text: "More Info"; + text: @tr("More Info"); dialog-button-role: action; } } diff --git a/rog-control-center/ui/widgets/graph.slint b/rog-control-center/ui/widgets/graph.slint index 509ba894..1cfd755c 100644 --- a/rog-control-center/ui/widgets/graph.slint +++ b/rog-control-center/ui/widgets/graph.slint @@ -1,9 +1,12 @@ -import { Palette } from "std-widgets.slint"; +import { Theme } from "../globals.slint"; export struct Node { x: length, y: length} export component Graph inherits Rectangle { in-out property <[Node]> nodes; + // Read-only mode (e.g. fan curve disabled → BIOS default): dim and ignore drag. + in property enabled: true; + opacity: root.enabled ? 1.0 : 0.4; in property node_min: { x: 0px, y: 0px }; in property node_max: { x: 100px, y: 255px }; property graph_padding: 40px; @@ -44,7 +47,7 @@ export component Graph inherits Rectangle { for n in 11: Path { viewbox-width: self.width / 1px; viewbox-height: self.height / 1px; - stroke: Palette.alternate-foreground.darker(200%); + stroke: Theme.line-soft; stroke-width: 1px; MoveTo { x: scale_x_to_graph(n * 10px) / 1px; @@ -60,7 +63,7 @@ export component Graph inherits Rectangle { } for n in 11: Text { - color: Palette.accent-background; + color: Theme.text-dim; font-size <=> root.axis_font_size; text: "\{n * 10}c"; x: scale_x_to_graph(n * 10px) - self.width / 3; @@ -70,7 +73,7 @@ export component Graph inherits Rectangle { for n in 11: Path { viewbox-width: self.width / 1px; viewbox-height: self.height / 1px; - stroke: Palette.alternate-foreground.darker(200%); + stroke: Theme.line-soft; stroke-width: 1px; MoveTo { x: 0; @@ -86,7 +89,7 @@ export component Graph inherits Rectangle { } for n in 11: Text { - color: Palette.accent-background; + color: Theme.text-dim; font-size <=> root.axis_font_size; text: "\{n * 10}%"; x: - self.width; @@ -97,7 +100,7 @@ export component Graph inherits Rectangle { if idx + 1 != nodes.length: Path { viewbox-width: self.width / 1px; viewbox-height: self.height / 1px; - stroke: Palette.control-foreground; + stroke: Theme.rog-red; stroke-width: 2px; MoveTo { x: scale_x_to_graph(nodes[idx].x) / 1px; @@ -114,19 +117,19 @@ export component Graph inherits Rectangle { for n[idx] in nodes: Rectangle { states [ pressed when touch.pressed: { - point.background: Palette.selection-background; - tip.background: Palette.selection-background; + point.background: #FFFFFF; + tip.background: #FFFFFF; tip.opacity: 1.0; } hover when touch.has-hover: { - point.background: Palette.accent-background; - tip.background: Palette.accent-background; + point.background: Theme.rog-red; + tip.background: Theme.rog-red; tip.opacity: 1.0; } ] // point := Rectangle { - background: Palette.control-foreground; + background: Theme.rog-red; x: scale_x_to_graph(n.x) - self.width / 2; y: graph.height - scale_y_to_graph(n.y) - self.height / 2; width: 18px; @@ -135,6 +138,7 @@ export component Graph inherits Rectangle { property pad: 1px; touch := TouchArea { + enabled: root.enabled; function check() { if idx + 1 < nodes.length && idx > 0 { if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) > nodes[idx + 1].x { @@ -189,7 +193,7 @@ export component Graph inherits Rectangle { } tip := Rectangle { - background: Palette.control-foreground; + background: Theme.bg-elevated; opacity: 0.3; border-radius: 12px; x: final_x_pos(); @@ -225,7 +229,7 @@ export component Graph inherits Rectangle { } // label := Text { - color: Palette.accent-foreground; + color: Theme.text-primary; font-size: 14px; text: "\{Math.floor(n.x / 1px)}c, \{fan_pct()}%"; } diff --git a/rog-control-center/ui/widgets/rog.slint b/rog-control-center/ui/widgets/rog.slint new file mode 100644 index 00000000..6022b17f --- /dev/null +++ b/rog-control-center/ui/widgets/rog.slint @@ -0,0 +1,798 @@ +// rog-control-center — AC-style component library (redone against 2.5K AC footage) +// RogCard=AC card, RogMetricCard=dashboard quad (icon + big number + red progress), +// RogSlider=hand-drawn AC slider (grey track + red fill + white/red handle), RogToggle=hand-drawn ON-red/OFF-grey, +// RogModeSelector=performance segment, RogDropdownRow=dropdown, RogSectionHeader=heading +import { Theme } from "../globals.slint"; +import { ComboBox } from "std-widgets.slint"; + +// — Page root: declares the opacity transition. The actual opacity value is +// driven by main_window per active page (crossfade); this only provides the curve. — +export component RogPage inherits Rectangle { + animate opacity { duration: 180ms; easing: ease-out; } +} + +// — Card: dark fill + radius 8 + deep shadow, no bright border (matches AC cards) — +export component RogCard inherits Rectangle { + in property radius: Theme.radius-card; + background: Theme.bg-card; + border-radius: root.radius; + drop-shadow-blur: 18px; + drop-shadow-color: #70000000; + drop-shadow-offset-y: 4px; + clip: true; +} + +// — Section heading: text + short red underline — +export component RogSectionHeader inherits VerticalLayout { + in property title; + spacing: 5px; + Text { + text: root.title; + color: Theme.text-primary; + font-size: Theme.font-md; + font-weight: 800; + letter-spacing: 0.5px; + } + Rectangle { + height: 2px; + width: 30px; + background: Theme.rog-red; + border-radius: 1px; + } +} + +// — Metric card: top-left icon + label + big number + unit + red progress bar (matches AC quad) — +export component RogMetricCard inherits RogCard { + in property label; + in property value; + in property unit; + in property progress: 0.0; // 0..1 + in property value-color: Theme.text-primary; + in property icon; + VerticalLayout { + padding: Theme.pad-card; + spacing: 10px; + // icon + label + HorizontalLayout { + spacing: 8px; + if root.icon.width > 0: Image { + source: root.icon; + colorize: Theme.text-secondary; + height: 20px; + vertical-alignment: center; + } + Text { + text: root.label; + color: Theme.text-secondary; + font-size: Theme.font-sm; + letter-spacing: 0.4px; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + } + // big number + unit + HorizontalLayout { + spacing: 3px; + alignment: start; + Text { + text: root.value; + color: root.value-color; + font-size: 32px; + font-weight: 800; + } + Text { + text: root.unit; + color: root.value-color; + font-size: Theme.font-md; + vertical-alignment: bottom; + y: 7px; + } + } + // bottom red progress bar + Rectangle { + height: 4px; + background: Theme.bg-elevated; + border-radius: 2px; + Rectangle { + x: 0; + width: parent.width * clamp(root.progress, 0.0, 1.0); + height: 100%; + background: Theme.rog-red; + border-radius: 2px; + } + } + } +} + +// — Performance mode segmented selector (matches AC ModePivot) — +export component RogModeSelector inherits Rectangle { + in property <[string]> modes; + in-out property current; + callback selected(int); + height: 46px; + background: Theme.bg-panel; + border-radius: Theme.radius-item; + HorizontalLayout { + padding: 4px; + spacing: Theme.space-xs; + for m[i] in root.modes: Rectangle { + vertical-stretch: 1; + border-radius: Theme.radius-item; + background: i == root.current + ? Theme.rog-red + : (ta.has-hover ? Theme.bg-elevated : transparent); + if i == root.current: Rectangle { + background: Theme.rog-red; + border-radius: Theme.radius-item; + drop-shadow-blur: 12px; + drop-shadow-color: Theme.rog-red-glow; + } + ta := TouchArea { + clicked => { + root.current = i; + root.selected(i); + } + } + Text { + text: m; + color: i == root.current ? #FFFFFF : Theme.text-secondary; + font-size: Theme.font-sm; + font-weight: i == root.current ? 800 : 500; + horizontal-alignment: center; + vertical-alignment: center; + } + } + } +} + +// — Hand-drawn AC slider (grey track + red fill + white round red-bordered handle) — +export component RogSlider inherits Rectangle { + in property label; + in-out property value; + in property minimum: 0; + in property maximum: 100; + in property unit: ""; + in property enabled: true; + callback released(float); + background: transparent; + + property range: max(0.0001, root.maximum - root.minimum); + property pct: clamp((root.value - root.minimum) / root.range, 0.0, 1.0); + + VerticalLayout { + padding: Theme.pad-card; + spacing: 10px; + HorizontalLayout { + spacing: 6px; + Text { + text: root.label; + color: Theme.text-secondary; + font-size: Theme.font-sm; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + Text { + text: "\{Math.round(root.value)}" + (root.unit == "" ? "" : " " + root.unit); + color: Theme.rog-red; + font-size: Theme.font-md; + font-weight: 700; + vertical-alignment: center; + } + } + track := Rectangle { + width: 100%; + height: 24px; + // Track + fill positioned absolutely. Do NOT wrap in a layout with + // `alignment: center`: that is non-stretch mode, which centers the + // track (ignoring width:100%) and makes the fill look centered. + Rectangle { + y: (parent.height - self.height) / 2; + width: 100%; + height: 4px; + background: Theme.bg-elevated; + border-radius: 2px; + Rectangle { + x: 0; + width: parent.width * root.pct; + height: 100%; + background: root.enabled ? Theme.rog-red : Theme.fg-disabled; + border-radius: 2px; + } + } + // handle: white circle + red border + Rectangle { + x: root.pct * (parent.width - self.width); + y: (parent.height - self.height) / 2; + width: 16px; + height: 16px; + border-radius: 8px; + background: #FFFFFF; + border-width: 2px; + border-color: root.enabled ? Theme.rog-red : Theme.fg-disabled; + drop-shadow-blur: 4px; + drop-shadow-color: #40000000; + } + ta := TouchArea { + width: 100%; + height: 100%; + enabled: root.enabled; + mouse-cursor: pointer; + pointer-event(e) => { + if e.kind == PointerEventKind.down || (e.kind == PointerEventKind.move && self.pressed) { + root.set-from-x(self.mouse-x); + } + if e.kind == PointerEventKind.up { + root.released(root.value); + } + } + } + } + } + + function set-from-x(x: length) { + // Map the pointer onto the handle's actual travel (track minus the + // 16px handle), centered on the handle — so clicking the handle + // doesn't jump it and both ends are reachable. + root.value = root.minimum + clamp((x - 8px) / (track.width - 16px), 0.0, 1.0) * root.range; + } +} + +// — Hand-drawn AC toggle (ON red / OFF grey, rounded pill) — +export component RogToggle inherits Rectangle { + in property label; + in-out property checked; + in property enabled: true; + // compact: label sits right next to the switch (no stretch spacer), for + // toolbars where the toggle isn't a full-width settings row. + in property compact: false; + callback toggled(bool); + background: transparent; + // compact: take only the content width; otherwise fill the row so the + // switch sits at the far right (settings-row style). + horizontal-stretch: root.compact ? 0 : 1; + HorizontalLayout { + padding-left: root.compact ? 0px : Theme.pad-card; + padding-right: root.compact ? 0px : Theme.pad-card; + spacing: root.compact ? 8px : 0px; + Text { + text: root.label; + color: root.enabled ? Theme.text-primary : Theme.text-dim; + font-size: Theme.font-sm; + vertical-alignment: center; + } + if !root.compact: Rectangle { horizontal-stretch: 1; } + tg := Rectangle { + width: 44px; + height: 24px; + border-radius: 12px; + background: root.checked ? (root.enabled ? Theme.rog-red : Theme.fg-disabled) : #3A3A3A; + ta := TouchArea { + enabled: root.enabled; + clicked => { + root.checked = !root.checked; + root.toggled(root.checked); + } + } + Rectangle { + x: root.checked ? parent.width - self.width - 3px : 3px; + animate x { duration: 120ms; easing: ease-in-out; } + y: 3px; + width: 18px; + height: 18px; + border-radius: 9px; + background: Theme.fg-normal; + } + } + } +} + +// — int switch (for panel_overdrive / boot_sound etc., int 0/1) — +export component RogToggleInt inherits Rectangle { + in property label; + in-out property checked-int; + in property enabled: true; + callback toggled(int); + background: transparent; + HorizontalLayout { + padding-left: Theme.pad-card; + padding-right: Theme.pad-card; + Text { + text: root.label; + color: root.enabled ? Theme.text-primary : Theme.text-dim; + font-size: Theme.font-sm; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + tg := Rectangle { + width: 44px; + height: 24px; + border-radius: 12px; + background: root.checked-int != 0 ? (root.enabled ? Theme.rog-red : Theme.fg-disabled) : #3A3A3A; + ta := TouchArea { + enabled: root.enabled; + clicked => { + root.checked-int = root.checked-int != 0 ? 0 : 1; + root.toggled(root.checked-int); + } + } + Rectangle { + x: root.checked-int != 0 ? parent.width - self.width - 3px : 3px; + y: 3px; + width: 18px; + height: 18px; + border-radius: 9px; + background: #FFFFFF; + } + } + } +} + +// — Dropdown row (MiniLED / EPP) — +export component RogDropdownRow inherits Rectangle { + in property label; + in-out property current-index; + in-out property current-value; + in-out property <[string]> model; + in property combo-min-width: 180px; + callback selected(int); + background: transparent; + HorizontalLayout { + padding-left: Theme.pad-card; + padding-right: Theme.pad-card; + Text { + text: root.label; + color: Theme.text-secondary; + font-size: Theme.font-sm; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + ComboBox { + min-width: root.combo-min-width; + current-index <=> root.current-index; + current-value <=> root.current-value; + model <=> root.model; + selected => { root.selected(root.current-index); } + } + } +} + +// ============================================================ +// AC home (dashboard) components — matched against ac-system-page.html (1:1) +// ============================================================ + +// — AC signature slash accent: N bars at -42° (the red slashes before subheadings) — +export component RogSlashMark inherits HorizontalLayout { + in property bar-color: Theme.rog-red; + in property count: 4; + in property bar-w: 5px; + in property bar-h: 14px; + spacing: 2px; + for i in root.count: Rectangle { + width: 11px; + height: root.bar-h + 4px; + Image { + source: @image-url("../images/slash-bar.svg"); + colorize: root.bar-color; + transform-rotation: -42deg; + width: root.bar-w; + height: root.bar-h; + x: (parent.width - self.width) / 2; + y: (parent.height - self.height) / 2; + } + } +} + +// — Dark slashes (by metric headings, 3 elevated-grey bars) — +export component RogDarkSlashes inherits RogSlashMark { + bar-color: Theme.bg-elevated; + count: 3; + bar-w: 5px; + bar-h: 22px; +} + +// — Purple→red gradient meter (#6437FF→#FF1929; Slint has no native gradient, simulated) — +export component RogMeter inherits Rectangle { + in property value: 0.0; // 0..1 + in property thickness: 4px; + height: root.thickness; + background: Theme.line; + border-radius: 1px; + clip: true; + fill := Rectangle { + x: 0; + width: root.width * clamp(root.value, 0.0, 1.0); + height: 100%; + clip: true; + // Real smooth meter gradient (purple → red), clipped to the fill + // width. Replaces the old 7-band fake gradient. + Rectangle { + x: 0; + width: root.width; + height: 100%; + background: @linear-gradient(90deg, Theme.meter-start, Theme.meter-end); + } + } +} + +// — Single stat row: left label, right value (13px secondary; dim uses text-dim) — +export component RogStatRow inherits Rectangle { + in property label; + in property value; + in property dim: false; + background: transparent; + height: 20px; + HorizontalLayout { + Text { + text: root.label; + color: root.dim ? Theme.text-dim : Theme.text-secondary; + font-size: 13px; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + Text { + text: root.value; + color: root.dim ? Theme.text-dim : Theme.text-secondary; + font-size: 13px; + vertical-alignment: center; + } + } +} + +// — One reading: label+value row + purple-red meter — +export component RogReading inherits VerticalLayout { + in property label; + in property value; + in property progress: 0.0; + spacing: 3px; + HorizontalLayout { + Text { + text: root.label; + color: Theme.text-secondary; + font-size: 13px; + vertical-alignment: center; + } + Rectangle { horizontal-stretch: 1; } + Text { + text: root.value; + color: Theme.text-secondary; + font-size: 13px; + vertical-alignment: center; + } + } + RogMeter { value: root.progress; } +} + +// — Metric block: big heading (30px regular) + dark-slashes + free reading/stat fill (@children) — +export component RogMetric inherits VerticalLayout { + in property title; + spacing: 7px; + alignment: start; + HorizontalLayout { + spacing: 10px; + padding-bottom: 10px; + Text { + text: root.title; + color: Theme.text-primary; + font-size: 30px; + font-weight: 400; + } + Rectangle { horizontal-stretch: 1; } + RogDarkSlashes {} + } + @children +} + +// — Product card: product image/placeholder + model (25px 900) + spec lines — +export component RogProductCard inherits Rectangle { + in property product-image; + in property name; + in property <[string]> details; + background: Theme.bg-card; + border-radius: 4px; + drop-shadow-blur: 18px; + drop-shadow-color: #73000000; + drop-shadow-offset-y: 4px; + clip: true; + VerticalLayout { + // Image area: stretches (grows with card height to keep the square ratio) + Rectangle { + vertical-stretch: 1; + min-height: 160px; + // Gradient background (matches the AC purple-red) + background: #15101e; + + if root.product-image.width > 0: Image { + source: root.product-image; + width: 75%; + height: 85%; + horizontal-alignment: center; + vertical-alignment: center; + image-fit: contain; + } + if root.product-image.width == 0: Image { + source: @image-url("../images/laptop-placeholder.svg"); + width: 85%; + height: 90%; + horizontal-alignment: center; + vertical-alignment: center; + image-fit: contain; + } + + // ROG-style slash accent + Rectangle { + x: parent.width * 0.15; + y: parent.height * 0.2; + width: 30px; + height: 3px; + background: Theme.rog-red; + opacity: 0.3; + transform-rotation: -42deg; + } + Rectangle { + x: parent.width * 0.65; + y: parent.height * 0.6; + width: 24px; + height: 2.5px; + background: #6437FF; + opacity: 0.25; + transform-rotation: -42deg; + } + } + // Text info area + VerticalLayout { + padding-top: 16px; + padding-bottom: 18px; + padding-left: 20px; + padding-right: 20px; + spacing: 4px; + Text { + text: root.name; + color: Theme.text-primary; + font-family: "Inter Display"; + font-size: 22px; + font-weight: 900; + letter-spacing: 0.5px; + horizontal-alignment: center; + } + for d in root.details: Text { + text: d; + color: Theme.text-secondary; + font-size: 11.5px; + horizontal-alignment: center; + wrap: word-wrap; + } + } + } +} + +// ============================================================ +// System Settings Page Components — AC multi-tab layout +// ============================================================ + +// —— Tab bar: horizontal row of tabs with red underline on active —— +export component RogTabBar inherits Rectangle { + in property <[string]> tabs; + in-out property active: 0; + callback tab-changed(int); + height: 44px; + background: transparent; + + HorizontalLayout { + spacing: 0px; + for t[i] in root.tabs: Rectangle { + height: 100%; + min-width: 90px; + vertical-stretch: 1; + background: transparent; + + // Active tab: red bottom border + if i == root.active: Rectangle { + x: 0px; + y: parent.height - 2px; + width: 100%; + height: 2px; + background: Theme.rog-red; + } + + VerticalLayout { + padding-left: 16px; + padding-right: 16px; + alignment: center; + Text { + text: t; + color: i == root.active ? Theme.text-primary : Theme.text-secondary; + font-size: 14px; + font-weight: i == root.active ? 600 : 400; + horizontal-alignment: center; + } + } + + ta := TouchArea { + clicked => { + root.active = i; + root.tab-changed(i); + } + } + + // Hover effect + if ta.has-hover && i != root.active: Rectangle { + y: parent.height - 2px; + width: 100%; + height: 1px; + background: Theme.line-soft; + } + } + Rectangle { horizontal-stretch: 1; } // push tabs left + } +} + +// —— Icon card for system config grid (icon + title + description) —— +export component RogIconCard inherits Rectangle { + in property icon; + in property title; + in property description: ""; + in property selected: false; + callback clicked(); + background: Theme.bg-card; + border-radius: 4px; + clip: true; + + // Red bottom accent line (AC style) + if root.selected: Rectangle { + y: parent.height - 3px; + width: 100%; + height: 3px; + background: Theme.rog-red; + } + + VerticalLayout { + padding-top: 24px; + padding-bottom: 20px; + padding-left: 16px; + padding-right: 16px; + spacing: 8px; + alignment: center; + + Image { + source: root.icon; + colorize: Theme.text-secondary; + width: 48px; + height: 48px; + image-fit: contain; + horizontal-alignment: center; + } + + Text { + text: root.title; + color: Theme.text-primary; + font-size: 14px; + font-weight: 500; + horizontal-alignment: center; + } + + if root.description != "": Text { + text: root.description; + color: Theme.text-dim; + font-size: 11px; + horizontal-alignment: center; + wrap: word-wrap; + max-width: parent.width - 8px; + } + } + + ta := TouchArea { + clicked => { root.clicked(); } + } +} + +// —— Effect icon button for lighting page (large circular icon, radio-style single-select) —— +// IMPORTANT: parent manages 'selected' state — this component only signals clicked() +export component RogEffectButton inherits VerticalLayout { + in property icon; + in property label; + in property selected: false; // read-only from parent, do NOT self-assign + callback clicked(); + spacing: 10px; + alignment: center; + + // Circle background + outer := Rectangle { + width: 72px; + height: 72px; + border-radius: 36px; + background: root.selected ? #2A1114 : #1A1A1A; + border-width: root.selected ? 2px : 1px; + border-color: root.selected ? Theme.rog-red : Theme.line-soft; + + if root.selected: Rectangle { + x: -4px; y: -4px; + width: parent.width + 8px; + height: parent.height + 8px; + border-radius: 40px; + border-width: 1px; + border-color: #33FF1929; + clip: true; + } + + Image { + source: root.icon; + colorize: root.selected ? Theme.rog-red : Theme.text-secondary; + width: 32px; + height: 32px; + image-fit: contain; + horizontal-alignment: center; + vertical-alignment: center; + } + + ta := TouchArea { + clicked => { root.clicked(); } + } + } + + Text { + text: root.label; + color: root.selected ? Theme.text-primary : Theme.text-secondary; + font-size: 13px; + font-weight: root.selected ? 600 : 400; + horizontal-alignment: center; + } +} + +// —— Display mode card for GPU page (icon + title + description) —— +export component RogDisplayModeCard inherits Rectangle { + in property icon; + in property title; + in property description; + in property selected: false; + in property enabled: true; + callback clicked(); + background: Theme.bg-card; + border-radius: 4px; + border-width: 1px; + border-color: root.selected ? Theme.rog-red : (root.enabled ? transparent : Theme.line); + animate border-color { duration: 150ms; } + clip: true; + + VerticalLayout { + padding: 20px; + spacing: 12px; + alignment: center; + + Image { + source: root.icon; + colorize: root.selected ? Theme.rog-red : (root.enabled ? Theme.text-dim : Theme.fg-disabled); + // Full-width box with contain: the SVG scales to 56px tall and + // centers horizontally, regardless of card width. + width: 100%; + height: 56px; + image-fit: contain; + opacity: root.enabled ? 1.0 : 0.4; + } + + Text { + text: root.title; + color: root.selected ? Theme.text-primary : (root.enabled ? Theme.text-secondary : Theme.fg-disabled); + font-size: 15px; + font-weight: 600; + horizontal-alignment: center; + } + + Text { + text: root.description; + color: root.enabled ? Theme.text-dim : Theme.fg-disabled; + font-size: 11px; + horizontal-alignment: center; + wrap: word-wrap; + opacity: root.enabled ? 1.0 : 0.5; + } + } + + ta := TouchArea { + enabled: root.enabled; + clicked => { root.clicked(); } + } +} + +// —— Collapsible section header (title + chevron) —— +// (removed: was only used by the GPU process list, which had no backend and +// was deleted. Re-add from history if a real collapsible section is needed.) diff --git a/rog-control-center/ui/widgets/sidebar.slint b/rog-control-center/ui/widgets/sidebar.slint index 60eab5bb..864eb470 100644 --- a/rog-control-center/ui/widgets/sidebar.slint +++ b/rog-control-center/ui/widgets/sidebar.slint @@ -1,140 +1,166 @@ -// Copyright © SixtyFPS GmbH -// SPDX-License-Identifier: MIT - -import { Palette, HorizontalBox, VerticalBox } from "std-widgets.slint"; - -component SideBarItem inherits Rectangle { - in property selected; - in property has-focus; - in-out property text <=> label.text; - callback clicked <=> touch.clicked; - min-height: self.visible ? l.preferred-height + 10px : 0px; - // min-width: self.visible ? l.preferred-width + 10px : 0px; - states [ - pressed when touch.pressed: { - state.opacity: 0.8; - } - hover when touch.has-hover: { - state.opacity: 0.6; - } - selected when root.selected: { - state.opacity: 1; - } - focused when root.has-focus: { - state.opacity: 0.8; - } - ] +// rog-control-center — AC-style sidebar (matched against the AC f001 screenshot) +// Narrow icon bar (~56px): ROG logo + icon nav + quit button. +// Selected state: #5A2025 dark-red fill + a left red indicator bar. +import { Theme } from "../globals.slint"; - state := Rectangle { - opacity: 0; - background: Palette.selection-background; - animate opacity { duration: 150ms; } - height: l.preferred-height; +// — Single nav item: icon + optional small label + selected red bar — +export component NavItem inherits Rectangle { + in property selected: false; + in property icon; + in property label: ""; + in property enabled: true; + callback clicked(); + width: 56px; + height: 48px; + + // Selected background + bg := Rectangle { + width: 100%; + height: 100%; + background: root.selected ? Theme.sidebar-selected : transparent; + visible: root.selected; } - l := HorizontalBox { - x: 4px; - y: (parent.height - self.height) / 2; - spacing: 0px; - label := Text { - color: Palette.foreground; - vertical-alignment: center; - font-size: 14px; - } + // Left red indicator (only when selected) + if root.selected: Rectangle { + x: 0px; + y: 8px; + width: 3px; + height: 32px; + background: Theme.rog-red; + border-radius: 1.5px; } - touch := TouchArea { + // Icon + label (absolutely positioned, centered) + Image { + source: root.icon; + colorize: root.enabled ? (root.selected ? Theme.text-primary : Theme.text-secondary) : #444444; + width: 22px; + height: 22px; + x: (parent.width - self.width) / 2; + y: (parent.height - self.height) / 2 - (root.label != "" ? 6px : 0px); + image-fit: contain; + } + if root.label != "": Text { + text: root.label; + color: root.selected ? Theme.text-primary : Theme.text-dim; + font-size: 9px; + x: (parent.width - self.width) / 2; + y: parent.height - self.height - 8px; + } + + ta := TouchArea { width: 100%; height: 100%; + enabled: root.enabled; + clicked => { root.clicked(); } } } +// — AC-style sidebar — export component SideBar inherits Rectangle { in property <[string]> model: []; in property <[bool]> available: []; in property is_tuf: false; out property current-item: 0; - out property current-focused: fs.has-focus ? fs.focused-tab : -1; - - // The currently focused tab - width: 180px; - forward-focus: fs; - accessible-role: tab; - accessible-delegate-focus: root.current-focused >= 0 ? root.current-focused : root.current-item; - Rectangle { - background: Palette.alternate-background; - fs := FocusScope { - key-pressed(event) => { - if (event.text == "\n") { - root.current-item = root.current-focused; - return accept; - } - if (event.text == Key.UpArrow) { - self.focused-tab = Math.max(self.focused-tab - 1, 0); - return accept; - } - if (event.text == Key.DownArrow) { - self.focused-tab = Math.min(self.focused-tab + 1, root.model.length - 1); - return accept; - } - return reject; - } - key-released(event) => { - if (event.text == " ") { - root.current-item = root.current-focused; - return accept; - } - return reject; - } - property focused-tab: 0; - x: 0; - width: 0; - // Do not react on clicks - } - } - VerticalBox { - spacing: 4px; - padding: 0px; - alignment: start; - - Image { - height: 100px; - // TODO: change if TUF on the logo in the menu on the main page - // If running on a TUF model, replace the ROG red with TUF orange - // (add data/tuf-control-center.png and switch here when available) - source: @image-url("../../data/rog-control-center.png"); - horizontal-alignment: center; - image-fit: contain; + background: Theme.sidebar-bg; + width: 56px; + clip: true; + + // Nav icon mapping (order matches the model in main_window.slint): + // 0=Home 1=Aura 2=AniMe Matrix 3=Slash Lighting 4=Fan Curves + // 5=GPU Configuration 6=Battery Info 7=App Settings 8=About + property <[image]> nav-icons: [ + @image-url("../images/nav-home.svg"), + @image-url("../images/nav-keyboard.svg"), + @image-url("../images/nav-matrix.svg"), + @image-url("../images/nav-slash.svg"), + @image-url("../images/nav-fan.svg"), + @image-url("../images/nav-gpu.svg"), + @image-url("../images/nav-battery.svg"), + @image-url("../images/nav-apps.svg"), + @image-url("../images/nav-about.svg"), + ]; + + // Small labels (AC style, optional, default empty) + property <[string]> nav-labels: [ + "", "", "", "", "", + "", "", "", "", + ]; + + VerticalLayout { + spacing: 2px; + padding-top: 12px; + padding-bottom: 8px; + alignment: center; + + // — ROG logo — + Rectangle { + height: 64px; + width: 100%; + background: transparent; + Image { + source: @image-url("../../data/rog-control-center.png"); + width: 36px; + height: 36px; + horizontal-alignment: center; + vertical-alignment: center; + image-fit: contain; + } } + // Divider Rectangle { height: 1px; - background: Palette.border; + width: 36px; + background: #222222; } - navigation := VerticalLayout { - spacing: -6px; - alignment: start; - vertical-stretch: 0; - for item[index] in root.model: SideBarItem { - visible: root.available[index]; - clicked => { - root.current-item = index; - } - has-focus: index == root.current-focused; - text: item; + // — Nav item list — + VerticalLayout { + spacing: 2px; + padding-top: 8px; + alignment: center; + for item[index] in root.model: NavItem { + visible: true; + enabled: index < root.available.length && root.available[index]; + icon: root.nav-icons[index]; + label: root.nav-labels[index]; selected: index == root.current-item; + clicked => { root.current-item = index; } } } - VerticalLayout { - bottom := VerticalBox { - padding-left: 0px; - padding-top: 0px; - padding-bottom: 0px; - @children + // Bottom flex space + quit button + Rectangle { vertical-stretch: 1; } + + // Divider + Rectangle { + height: 1px; + width: 36px; + background: #222222; + } + + // Quit button + Rectangle { + height: 44px; + width: 100%; + background: transparent; + Text { + text: @tr("Quit"); + color: Theme.text-dim; + font-size: 10px; + horizontal-alignment: center; + vertical-alignment: center; + } + ta := TouchArea { + width: 100%; + height: 100%; + clicked => { root.quit-clicked(); } } } } + + callback quit-clicked(); } diff --git a/rog-platform/src/gpu_pci.rs b/rog-platform/src/gpu_pci.rs index 277ff0a1..fefedd85 100644 --- a/rog-platform/src/gpu_pci.rs +++ b/rog-platform/src/gpu_pci.rs @@ -716,6 +716,58 @@ pub fn get_gpu_usage_pct() -> f32 { 0.0 } +/// Current dGPU graphics clock in MHz, if a source reads successfully. NVML +/// first (NVIDIA), then the sysfs nodes NVIDIA (`gpu_current_freq`) and amdgpu +/// (`freq1_input`) expose. `None` when no source is available. +pub fn get_gpu_frequency_mhz() -> Option { + if let Ok(nvml) = nvml_wrapper::Nvml::init() { + if let Ok(device) = nvml.device_by_index(0) { + if let Ok(clock) = + device.clock_info(nvml_wrapper::enum_wrappers::device::Clock::Graphics) + { + return Some(clock as f32); + } + } + } + if let Ok(entries) = std::fs::read_dir("/sys/class/drm") { + for entry in entries.flatten() { + let path = entry.path(); + let is_card = path + .file_name() + .map(|n| n.to_string_lossy().starts_with("card")) + .unwrap_or(false); + if is_card { + let nv = path.join("device/gpu_current_freq"); + if nv.exists() { + if let Ok(s) = std::fs::read_to_string(&nv) { + if let Ok(v) = s.trim().parse::() { + return Some(v); + } + } + } + } + } + } + if let Ok(entries) = std::fs::read_dir("/sys/class/hwmon") { + for entry in entries.flatten() { + let path = entry.path(); + if let Ok(name) = std::fs::read_to_string(path.join("name")) { + if name.trim() == "amdgpu" { + if let Ok(s) = std::fs::read_to_string(path.join("freq1_input")) { + if let Ok(v) = s.trim().parse::() { + // amdgpu hwmon freq1_input follows the standard hwmon + // convention (Hz) — convert to MHz. (NVIDIA's + // gpu_current_freq and NVML are already MHz.) + return Some(v / 1_000_000.0); + } + } + } + } + } + } + None +} + #[cfg(test)] mod tests { use std::fs; From e3f6094bd4fa50952a3f7396652ce7d2aa6ce62a Mon Sep 17 00:00:00 2001 From: NB-Group Date: Wed, 29 Jul 2026 12:20:51 +0800 Subject: [PATCH 03/11] rog-control-center: split about feature list; de-link the URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Feature list was one @tr with five features joined by newlines, so translators couldn't reuse them — split into five separate @tr. The GitHub / asus-linux lines were coloured as links but had no handler; drop the link colour so they're not mistaken for clickable. --- rog-control-center/ui/pages/about.slint | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rog-control-center/ui/pages/about.slint b/rog-control-center/ui/pages/about.slint index 4c289a67..aaadbd83 100644 --- a/rog-control-center/ui/pages/about.slint +++ b/rog-control-center/ui/pages/about.slint @@ -102,13 +102,13 @@ export component PageAbout inherits RogPage { horizontal-alignment: left; width: 100%; } - Text { - text: @tr("Performance: Switch power profiles and customize fan curves.\nAura Sync: Control keyboard backlighting and LED effects.\nBattery Health: Set charge limits to extend battery longevity.\nDisplay: Toggle Panel Overdrive and refresh rates.\nAniMe Matrix: Control AniMe Matrix displays."); - color: Theme.text-secondary; - font-size: 12px; - horizontal-alignment: left; - width: 100%; - wrap: word-wrap; + VerticalLayout { + spacing: 4px; + Text { text: @tr("Performance: Switch power profiles and customize fan curves."); color: Theme.text-secondary; font-size: 12px; width: 100%; wrap: word-wrap; } + Text { text: @tr("Aura Sync: Control keyboard backlighting and LED effects."); color: Theme.text-secondary; font-size: 12px; width: 100%; wrap: word-wrap; } + Text { text: @tr("Battery Health: Set charge limits to extend battery longevity."); color: Theme.text-secondary; font-size: 12px; width: 100%; wrap: word-wrap; } + Text { text: @tr("Display: Toggle Panel Overdrive and refresh rates."); color: Theme.text-secondary; font-size: 12px; width: 100%; wrap: word-wrap; } + Text { text: @tr("AniMe Matrix: Control AniMe Matrix displays."); color: Theme.text-secondary; font-size: 12px; width: 100%; wrap: word-wrap; } } // Requirements @@ -158,7 +158,7 @@ export component PageAbout inherits RogPage { } Text { text: "github.com/OpenGamingCollective/asusctl\nasus-linux.org"; - color: Theme.rog-blue; + color: Theme.text-secondary; font-size: 12px; horizontal-alignment: left; width: 100%; From 9e173e2920c2236bdc373d9eb6a92bfdf6ffa333 Mon Sep 17 00:00:00 2001 From: NB-Group Date: Wed, 29 Jul 2026 12:52:32 +0800 Subject: [PATCH 04/11] rog-control-center: set locale via env only, drop libc/setlocale Setting LANG/LC_ALL/LANGUAGE env vars before init_translations is enough: the reload window already restarts the process, and gettext picks up LC_ALL to resolve @tr() in the chosen locale on the next start. Removes the libc::setlocale call, the CString, the unsafe block, and the libc crate dependency. --- rog-control-center/Cargo.toml | 1 - rog-control-center/src/main.rs | 14 ++++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/rog-control-center/Cargo.toml b/rog-control-center/Cargo.toml index a8f2b680..49ef1771 100644 --- a/rog-control-center/Cargo.toml +++ b/rog-control-center/Cargo.toml @@ -38,7 +38,6 @@ log.workspace = true env_logger.workspace = true tokio.workspace = true -libc = "0.2" serde.workspace = true zbus.workspace = true dirs.workspace = true diff --git a/rog-control-center/src/main.rs b/rog-control-center/src/main.rs index 72a75ceb..33f40c07 100644 --- a/rog-control-center/src/main.rs +++ b/rog-control-center/src/main.rs @@ -100,22 +100,16 @@ async fn main() -> Result<()> { // return Ok(()); } - // setlocale is process-global and MT-Unsafe (glibc), so it must run before - // the Tokio worker threads start at Runtime::new. Read just the language. + // Apply the configured UI language through env vars before Runtime::new + // (they're process-global). "Reload Window" restarts the process, which + // re-reads config.language here so gettext resolves @tr() in the chosen + // locale at init_translations — no setlocale / unsafe / libc needed. let startup_language = Config::new().load().language; if !startup_language.is_empty() { let locale = format!("{startup_language}.UTF-8"); env::set_var("LANG", &locale); env::set_var("LC_ALL", &locale); env::set_var("LANGUAGE", &startup_language); - if let Ok(c) = std::ffi::CString::new(locale) { - // SAFETY: setlocale(LC_ALL, valid null-terminated string) only mutates - // the C library's global locale — the intent. Run before any worker - // thread exists, so nothing else is touching locale state. - unsafe { - libc::setlocale(libc::LC_ALL, c.as_ptr()); - } - } } // start tokio From af4a54aa810de886899c346ef0f36308e9b7c3ac Mon Sep 17 00:00:00 2001 From: NB-Group Date: Wed, 29 Jul 2026 23:13:31 +0800 Subject: [PATCH 05/11] rog-control-center: address CodeRabbit review findings Functional: - gpu: restore the reserved-VRAM dropdown (cb_set_apu_mem was orphaned behind a static Text) via RogDropdownRow. - fans: gate profile tabs on balanced/performance/quiet_available so an unsupported profile can't be selected and written. - aura: add a click-swallowing TouchArea to each power modal so clicking blank regions no longer falls through to the fade-cover and dismisses it. - aura: persist the speed/brightness drag on PointerEventKind.cancel, not only on pointer-up. - home: hide the Operation Mode section when platform_profile_indexes is empty. - mod: warn when the language-change config lock is busy instead of silently reporting success. Theming: - sidebar/rog: route leftover hardcoded hex through Theme tokens. - system: use Theme.temp-color for the CPU/iGPU/dGPU temperature bindings. - common: move off Palette.* so control text stays readable on the dark cards. i18n (zh_CN): - drop the stray leading space in "Turbo". - translate PL1/PL2/FPPT/SPPT. Polish: - about/battery/aura: give the content rect an explicit height. - aura: mouse-cursor on the Settings + modal-close buttons; merge the duplicate Tuf modal into the New branch. --- rog-control-center/src/ui/mod.rs | 4 +- .../translations/zh_CN/rog-control-center.po | 10 +-- rog-control-center/ui/globals.slint | 1 + rog-control-center/ui/pages/about.slint | 1 + rog-control-center/ui/pages/aura.slint | 88 ++++--------------- rog-control-center/ui/pages/battery.slint | 1 + rog-control-center/ui/pages/fans.slint | 1 + rog-control-center/ui/pages/gpu.slint | 21 ++--- rog-control-center/ui/pages/home.slint | 4 +- rog-control-center/ui/pages/system.slint | 7 +- rog-control-center/ui/widgets/common.slint | 28 +++--- rog-control-center/ui/widgets/graph.slint | 2 +- rog-control-center/ui/widgets/rog.slint | 12 ++- rog-control-center/ui/widgets/sidebar.slint | 6 +- 14 files changed, 67 insertions(+), 119 deletions(-) diff --git a/rog-control-center/src/ui/mod.rs b/rog-control-center/src/ui/mod.rs index 85e374ec..38dadd43 100644 --- a/rog-control-center/src/ui/mod.rs +++ b/rog-control-center/src/ui/mod.rs @@ -452,8 +452,10 @@ pub fn setup_app_settings_page( if let Ok(mut lock) = config_copy.try_lock() { lock.language = code.to_string(); lock.write(); + log::info!("Language changed to {code}; reload to apply"); + } else { + log::warn!("config lock busy; language change to {code} not saved"); } - log::info!("Language changed to {code}; reload to apply"); } }); diff --git a/rog-control-center/translations/zh_CN/rog-control-center.po b/rog-control-center/translations/zh_CN/rog-control-center.po index 6f379e27..f9f9ea40 100644 --- a/rog-control-center/translations/zh_CN/rog-control-center.po +++ b/rog-control-center/translations/zh_CN/rog-control-center.po @@ -283,22 +283,22 @@ msgstr "省电" #: rog-control-center/ui/pages/system.slint:188 msgctxt "ppt_pl1_spl" msgid "PL1, sustained power limit" -msgstr "" +msgstr "PL1,持续功率限制" #: rog-control-center/ui/pages/system.slint:198 msgctxt "ppt_pl2_sppt" msgid "PL2, turbo power limit" -msgstr "" +msgstr "PL2,峰值功率限制" #: rog-control-center/ui/pages/system.slint:208 msgctxt "ppt_fppt" msgid "FPPT, Fast Power Limit" -msgstr "" +msgstr "FPPT,快速功率限制" #: rog-control-center/ui/pages/system.slint:218 msgctxt "ppt_apu_sppt" msgid "SPPT, APU slow power limit" -msgstr "" +msgstr "SPPT,APU 慢速功率限制" #: rog-control-center/ui/pages/system.slint:228 msgctxt "ppt_platform_sppt" @@ -804,7 +804,7 @@ msgstr "静音" #: rog-control-center/ui/pages/home.slint msgctxt "PageHome" msgid "Turbo" -msgstr " turbo" +msgstr "涡轮" #: rog-control-center/ui/pages/home.slint msgctxt "PageHome" diff --git a/rog-control-center/ui/globals.slint b/rog-control-center/ui/globals.slint index 602a4cc3..b9968347 100644 --- a/rog-control-center/ui/globals.slint +++ b/rog-control-center/ui/globals.slint @@ -27,6 +27,7 @@ export global Theme { out property bg-panel: #070707; // sidebar / panels (darkest) out property bg-card: #161616; // cards (lighter than base) out property bg-elevated: #242424; // raised / inputs / hover + out property toggle-off: #3A3A3A; // toggle switch track (off state) out property bg-preview: #100d0f; // light-bar preview panel (slash page warm-black) // — Status colours — diff --git a/rog-control-center/ui/pages/about.slint b/rog-control-center/ui/pages/about.slint index aaadbd83..ffada41d 100644 --- a/rog-control-center/ui/pages/about.slint +++ b/rog-control-center/ui/pages/about.slint @@ -34,6 +34,7 @@ export component PageAbout inherits RogPage { content := Rectangle { y: titlebar.height; width: 100%; + height: root.height - titlebar.height; VerticalLayout { padding-left: 24px; diff --git a/rog-control-center/ui/pages/aura.slint b/rog-control-center/ui/pages/aura.slint index 01729985..97caa5b4 100644 --- a/rog-control-center/ui/pages/aura.slint +++ b/rog-control-center/ui/pages/aura.slint @@ -51,6 +51,7 @@ export component PageAura inherits RogPage { content := Rectangle { y: titlebar.height; width: 100%; + height: root.height - titlebar.height; VerticalLayout { padding-left: 24px; @@ -87,6 +88,7 @@ export component PageAura inherits RogPage { vertical-alignment: center; } TouchArea { + mouse-cursor: pointer; clicked => { root.show_fade_cover = true; root.show_aura_power = true; @@ -359,7 +361,7 @@ export component PageAura inherits RogPage { if e.kind == PointerEventKind.down || (e.kind == PointerEventKind.move && self.pressed) { AuraPageData.speed = Math.round(clamp(self.mouse-x / self.width, 0.0, 1.0) * 2); } - if e.kind == PointerEventKind.up { + if e.kind == PointerEventKind.up || e.kind == PointerEventKind.cancel { AuraPageData.cb_speed(AuraPageData.speed); } } @@ -428,7 +430,7 @@ export component PageAura inherits RogPage { if e.kind == PointerEventKind.down || (e.kind == PointerEventKind.move && self.pressed) { AuraPageData.brightness = Math.round(clamp(self.mouse-x / self.width, 0.0, 1.0) * 3); } - if e.kind == PointerEventKind.up { + if e.kind == PointerEventKind.up || e.kind == PointerEventKind.cancel { AuraPageData.cb_brightness(AuraPageData.brightness); } } @@ -456,7 +458,7 @@ export component PageAura inherits RogPage { } } - if root.show_aura_power && AuraPageData.device_type == AuraDevType.New: Rectangle { + if root.show_aura_power && (AuraPageData.device_type == AuraDevType.New || AuraPageData.device_type == AuraDevType.Tuf): Rectangle { background: Theme.bg-base; border-radius: Theme.radius-card; drop-shadow-blur: 24px; @@ -467,6 +469,10 @@ export component PageAura inherits RogPage { height: root.height * 0.88; clip: true; + // Swallow clicks on the modal's blank regions so they don't fall + // through to the fade-cover underneath and dismiss the dialog. + TouchArea { width: 100%; height: 100%; } + VerticalLayout { padding: 20px; spacing: 16px; @@ -492,6 +498,7 @@ export component PageAura inherits RogPage { vertical-alignment: center; } TouchArea { + mouse-cursor: pointer; clicked => { root.show_aura_power = false; root.show_fade_cover = false; @@ -526,76 +533,6 @@ export component PageAura inherits RogPage { } } - if root.show_aura_power && AuraPageData.device_type == AuraDevType.Tuf: Rectangle { - background: Theme.bg-base; - border-radius: Theme.radius-card; - drop-shadow-blur: 24px; - drop-shadow-color: #80000000; - x: root.width * 0.08; - y: root.height * 0.06; - width: root.width * 0.84; - height: root.height * 0.88; - clip: true; - - VerticalLayout { - padding: 20px; - spacing: 16px; - HorizontalLayout { - Text { - text: @tr("Power Settings"); - color: Theme.text-primary; - font-size: 17px; - font-weight: 700; - vertical-alignment: center; - } - Rectangle { horizontal-stretch: 1; } - Rectangle { - width: 28px; - height: 28px; - border-radius: 14px; - background: Theme.bg-elevated; - Text { - text: "✕"; - color: Theme.text-secondary; - font-size: 13px; - horizontal-alignment: center; - vertical-alignment: center; - } - TouchArea { - clicked => { - root.show_aura_power = false; - root.show_fade_cover = false; - } - } - } - } - - for state[idx] in AuraPageData.led_power.states: tuf_zone := AuraPowerGroup { - group-title: AuraPageData.power_zone_names[state.zone_name_idx]; - boot_checked: state.boot; - boot_toggled => { - AuraPageData.led_power.states[idx].boot = tuf_zone.boot_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); - } - awake_checked: state.awake; - awake_toggled => { - AuraPageData.led_power.states[idx].awake = tuf_zone.awake_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); - } - sleep_checked: state.sleep; - sleep_toggled => { - AuraPageData.led_power.states[idx].sleep = tuf_zone.sleep_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); - } - shutdown_checked: state.shutdown; - shutdown_toggled => { - AuraPageData.led_power.states[idx].shutdown = tuf_zone.shutdown_checked; - AuraPageData.cb_led_power(AuraPageData.led_power); - } - } - } - } - if root.show_aura_power && AuraPageData.device_type == AuraDevType.Old: Rectangle { background: Theme.bg-base; border-radius: Theme.radius-card; @@ -607,6 +544,10 @@ export component PageAura inherits RogPage { height: root.height * 0.88; clip: true; + // Swallow clicks on the modal's blank regions so they don't fall + // through to the fade-cover underneath and dismiss the dialog. + TouchArea { width: 100%; height: 100%; } + VerticalLayout { padding: 20px; spacing: 16px; @@ -632,6 +573,7 @@ export component PageAura inherits RogPage { vertical-alignment: center; } TouchArea { + mouse-cursor: pointer; clicked => { root.show_aura_power = false; root.show_fade_cover = false; diff --git a/rog-control-center/ui/pages/battery.slint b/rog-control-center/ui/pages/battery.slint index 704560d8..659fa409 100644 --- a/rog-control-center/ui/pages/battery.slint +++ b/rog-control-center/ui/pages/battery.slint @@ -37,6 +37,7 @@ export component PageBattery inherits RogPage { content := Rectangle { y: titlebar.height; width: 100%; + height: root.height - titlebar.height; VerticalLayout { padding-left: 24px; diff --git a/rog-control-center/ui/pages/fans.slint b/rog-control-center/ui/pages/fans.slint index d6b8d4fe..82b0ac1c 100644 --- a/rog-control-center/ui/pages/fans.slint +++ b/rog-control-center/ui/pages/fans.slint @@ -132,6 +132,7 @@ export component PageFans inherits RogPage { // Profile tab — selects which profile's curve to edit. RogTabBar { tabs: [@tr("Balanced"), @tr("Performance"), @tr("Quiet")]; + tab-enabled: [FanPageData.balanced_available, FanPageData.performance_available, FanPageData.quiet_available]; active <=> root.current-profile; } diff --git a/rog-control-center/ui/pages/gpu.slint b/rog-control-center/ui/pages/gpu.slint index cf1b002e..5b62209f 100644 --- a/rog-control-center/ui/pages/gpu.slint +++ b/rog-control-center/ui/pages/gpu.slint @@ -2,7 +2,7 @@ // Layout: titlebar + backend-provided display mode cards + process list // Pattern: alignment:start, HorizontalLayout, no ScrollView, natural height // Data: all from GPUPageData global (D-Bus backed) -import { RogCard, RogSlashMark, RogToggle, RogDisplayModeCard, RogPage } from "../widgets/rog.slint"; +import { RogCard, RogDropdownRow, RogSlashMark, RogToggle, RogDisplayModeCard, RogPage } from "../widgets/rog.slint"; import { Theme } from "../globals.slint"; export global GPUPageData { @@ -238,18 +238,13 @@ export component PageGPU inherits RogPage { if GPUPageData.apu_mem_present: VerticalLayout { spacing: 10px; - HorizontalLayout { - Text { text: @tr("Reserved GPU Memory (iGPU VRAM)"); color: Theme.text-secondary; font-size: 13px; vertical-alignment: center; } - Rectangle { horizontal-stretch: 1; } - Rectangle { - width: 140px; height: 30px; border-radius: 3px; - border-width: 1px; border-color: Theme.line; background: Theme.bg-elevated; - Text { - text: GPUPageData.apu_mem_choices[GPUPageData.apu_mem_index]; - color: Theme.text-secondary; font-size: 12px; - horizontal-alignment: center; vertical-alignment: center; - } - } + RogDropdownRow { + label: @tr("Reserved GPU Memory (iGPU VRAM)"); + combo-min-width: 140px; + current-index <=> GPUPageData.apu_mem_index; + current-value: GPUPageData.apu_mem_choices[GPUPageData.apu_mem_index]; + model: GPUPageData.apu_mem_choices; + selected(i) => { GPUPageData.cb_set_apu_mem(i); } } Text { text: @tr("Note: Reserved GPU memory changes take effect after a reboot."); color: Theme.text-dim; font-size: 11px; } } diff --git a/rog-control-center/ui/pages/home.slint b/rog-control-center/ui/pages/home.slint index 90a88c40..a44a9c52 100644 --- a/rog-control-center/ui/pages/home.slint +++ b/rog-control-center/ui/pages/home.slint @@ -192,7 +192,7 @@ export component PageHome inherits RogPage { vertical-stretch: 0; spacing: 12px; - Rectangle { + if SystemPageData.platform_profile_indexes.length > 0: Rectangle { height: 24px; HorizontalLayout { spacing: 8px; @@ -208,7 +208,7 @@ export component PageHome inherits RogPage { } } - Rectangle { + if SystemPageData.platform_profile_indexes.length > 0: Rectangle { height: 104px; width: 100%; clip: true; diff --git a/rog-control-center/ui/pages/system.slint b/rog-control-center/ui/pages/system.slint index 83453bda..4a1e5808 100644 --- a/rog-control-center/ui/pages/system.slint +++ b/rog-control-center/ui/pages/system.slint @@ -1,6 +1,7 @@ import { SystemSlider, SystemDropdown, SystemToggle, SystemToggleInt, RogItem } from "../widgets/common.slint"; import { Palette, HorizontalBox , VerticalBox, ScrollView, Slider, Button, Switch, ComboBox, GroupBox, StandardButton} from "std-widgets.slint"; import { FanPageData } from "../types/fan_types.slint"; +import { Theme } from "../globals.slint"; export struct AttrMinMax { min: int, @@ -264,7 +265,7 @@ export component PageSystem inherits Rectangle { Text { text: Math.round(SystemPageData.cpu_temp_val) + " °C"; font-weight: 700; - color: SystemPageData.cpu_temp_val > 80 ? #ef4444 : (SystemPageData.cpu_temp_val > 65 ? #eab308 : #22c55e); + color: SystemPageData.cpu_temp_val > 0.0 ? Theme.temp-color(SystemPageData.cpu_temp_val) : Palette.control-foreground; } } @@ -312,7 +313,7 @@ export component PageSystem inherits Rectangle { Text { text: SystemPageData.igpu_temp_val > 0.0 ? (Math.round(SystemPageData.igpu_temp_val) + " °C") : @tr("N/A"); font-weight: 700; - color: SystemPageData.igpu_temp_val > 80 ? #ef4444 : (SystemPageData.igpu_temp_val > 65 ? #eab308 : (SystemPageData.igpu_temp_val > 0.0 ? #22c55e : Palette.control-foreground)); + color: SystemPageData.igpu_temp_val > 0.0 ? Theme.temp-color(SystemPageData.igpu_temp_val) : Palette.control-foreground; } } @@ -375,7 +376,7 @@ export component PageSystem inherits Rectangle { Text { text: SystemPageData.gpu_temp_val > 0.0 ? (Math.round(SystemPageData.gpu_temp_val) + " °C") : @tr("N/A"); font-weight: 700; - color: SystemPageData.gpu_temp_val > 80 ? #ef4444 : (SystemPageData.gpu_temp_val > 65 ? #eab308 : (SystemPageData.gpu_temp_val > 0.0 ? #22c55e : Palette.control-foreground)); + color: SystemPageData.gpu_temp_val > 0.0 ? Theme.temp-color(SystemPageData.gpu_temp_val) : Palette.control-foreground; } } diff --git a/rog-control-center/ui/widgets/common.slint b/rog-control-center/ui/widgets/common.slint index 90daf50a..8c2ed75a 100644 --- a/rog-control-center/ui/widgets/common.slint +++ b/rog-control-center/ui/widgets/common.slint @@ -33,7 +33,7 @@ export component SystemSlider inherits RogItem { Text { font-size: 14px; vertical-alignment: TextVerticalAlignment.center; - color: Palette.control-foreground; + color: Theme.text-primary; text: root.text; } @@ -41,7 +41,7 @@ export component SystemSlider inherits RogItem { font-size: 14px; horizontal-alignment: TextHorizontalAlignment.right; vertical-alignment: TextVerticalAlignment.center; - color: Palette.control-foreground; + color: Theme.text-primary; text: "\{Math.round(root.value)}"; } } @@ -67,8 +67,8 @@ export component SystemSlider inherits RogItem { drop-shadow-blur: 10px; drop-shadow-color: black; border-radius: 10px; - border-color: Palette.accent-background; - background: Palette.background; + border-color: Theme.rog-red; + background: Theme.bg-card; Dialog { title: root.title; VerticalBox { @@ -117,8 +117,8 @@ export component SystemSlider inherits RogItem { drop-shadow-blur: 10px; drop-shadow-color: black; border-radius: 10px; - border-color: Palette.accent-background; - background: Palette.background; + border-color: Theme.rog-red; + background: Theme.bg-card; Dialog { Text { max-width: 420px; @@ -166,7 +166,7 @@ export component SystemToggle inherits RogItem { Text { font-size: 14px; vertical-alignment: TextVerticalAlignment.center; - color: Palette.control-foreground; + color: Theme.text-primary; text: root.text; } } @@ -198,7 +198,7 @@ export component SystemToggleInt inherits RogItem { Text { font-size: 14px; vertical-alignment: TextVerticalAlignment.center; - color: Palette.control-foreground; + color: Theme.text-primary; text: root.text; } } @@ -229,7 +229,7 @@ export component SystemToggleVert inherits RogItem { font-size: 14px; vertical-alignment: TextVerticalAlignment.bottom; horizontal-alignment: TextHorizontalAlignment.center; - color: Palette.control-foreground; + color: Theme.text-primary; text: root.text; } @@ -262,7 +262,7 @@ export component SystemDropdown inherits RogItem { Text { font-size: 14px; vertical-alignment: TextVerticalAlignment.center; - color: Palette.control-foreground; + color: Theme.text-primary; text: root.text; } } @@ -297,8 +297,8 @@ export component PopupNotification { // TODO: add properties to display Rectangle { border-width: 2px; - border-color: Palette.accent-background; - background: Palette.background; + border-color: Theme.rog-red; + background: Theme.bg-card; VerticalLayout { Dialog { @@ -306,14 +306,14 @@ export component PopupNotification { alignment: start; Text { text: heading; - color: Palette.control-foreground; + color: Theme.text-primary; font-size: 32px; font-weight: 900; } Text { text: content; - color: Palette.control-foreground; + color: Theme.text-primary; font-size: 18px; } } diff --git a/rog-control-center/ui/widgets/graph.slint b/rog-control-center/ui/widgets/graph.slint index 1cfd755c..8edf0d81 100644 --- a/rog-control-center/ui/widgets/graph.slint +++ b/rog-control-center/ui/widgets/graph.slint @@ -118,7 +118,7 @@ export component Graph inherits Rectangle { states [ pressed when touch.pressed: { point.background: #FFFFFF; - tip.background: #FFFFFF; + tip.background: Theme.bg-elevated; tip.opacity: 1.0; } hover when touch.has-hover: { diff --git a/rog-control-center/ui/widgets/rog.slint b/rog-control-center/ui/widgets/rog.slint index 6022b17f..05482369 100644 --- a/rog-control-center/ui/widgets/rog.slint +++ b/rog-control-center/ui/widgets/rog.slint @@ -265,7 +265,7 @@ export component RogToggle inherits Rectangle { width: 44px; height: 24px; border-radius: 12px; - background: root.checked ? (root.enabled ? Theme.rog-red : Theme.fg-disabled) : #3A3A3A; + background: root.checked ? (root.enabled ? Theme.rog-red : Theme.fg-disabled) : Theme.toggle-off; ta := TouchArea { enabled: root.enabled; clicked => { @@ -307,7 +307,7 @@ export component RogToggleInt inherits Rectangle { width: 44px; height: 24px; border-radius: 12px; - background: root.checked-int != 0 ? (root.enabled ? Theme.rog-red : Theme.fg-disabled) : #3A3A3A; + background: root.checked-int != 0 ? (root.enabled ? Theme.rog-red : Theme.fg-disabled) : Theme.toggle-off; ta := TouchArea { enabled: root.enabled; clicked => { @@ -572,6 +572,7 @@ export component RogProductCard inherits Rectangle { // —— Tab bar: horizontal row of tabs with red underline on active —— export component RogTabBar inherits Rectangle { in property <[string]> tabs; + in property <[bool]> tab-enabled: []; in-out property active: 0; callback tab-changed(int); height: 44px; @@ -600,7 +601,9 @@ export component RogTabBar inherits Rectangle { alignment: center; Text { text: t; - color: i == root.active ? Theme.text-primary : Theme.text-secondary; + color: (i >= root.tab-enabled.length || root.tab-enabled[i]) + ? (i == root.active ? Theme.text-primary : Theme.text-secondary) + : Theme.fg-disabled; font-size: 14px; font-weight: i == root.active ? 600 : 400; horizontal-alignment: center; @@ -608,6 +611,7 @@ export component RogTabBar inherits Rectangle { } ta := TouchArea { + enabled: i >= root.tab-enabled.length || root.tab-enabled[i]; clicked => { root.active = i; root.tab-changed(i); @@ -700,7 +704,7 @@ export component RogEffectButton inherits VerticalLayout { width: 72px; height: 72px; border-radius: 36px; - background: root.selected ? #2A1114 : #1A1A1A; + background: root.selected ? Theme.mode-selected : Theme.bg-app; border-width: root.selected ? 2px : 1px; border-color: root.selected ? Theme.rog-red : Theme.line-soft; diff --git a/rog-control-center/ui/widgets/sidebar.slint b/rog-control-center/ui/widgets/sidebar.slint index 864eb470..3075383d 100644 --- a/rog-control-center/ui/widgets/sidebar.slint +++ b/rog-control-center/ui/widgets/sidebar.slint @@ -34,7 +34,7 @@ export component NavItem inherits Rectangle { // Icon + label (absolutely positioned, centered) Image { source: root.icon; - colorize: root.enabled ? (root.selected ? Theme.text-primary : Theme.text-secondary) : #444444; + colorize: root.enabled ? (root.selected ? Theme.text-primary : Theme.text-secondary) : Theme.fg-disabled; width: 22px; height: 22px; x: (parent.width - self.width) / 2; @@ -114,7 +114,7 @@ export component SideBar inherits Rectangle { Rectangle { height: 1px; width: 36px; - background: #222222; + background: Theme.divider; } // — Nav item list — @@ -139,7 +139,7 @@ export component SideBar inherits Rectangle { Rectangle { height: 1px; width: 36px; - background: #222222; + background: Theme.divider; } // Quit button From 47cc2fa0483b671abc15592adb18af71ea7f0aa0 Mon Sep 17 00:00:00 2001 From: NB-Group Date: Wed, 29 Jul 2026 23:22:12 +0800 Subject: [PATCH 06/11] rog-control-center(zh_CN): translate ppt_platform_sppt Missed in the earlier power-limit batch (PL1/PL2/FPPT/SPPT were done); caught by CodeRabbit. --- rog-control-center/translations/zh_CN/rog-control-center.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rog-control-center/translations/zh_CN/rog-control-center.po b/rog-control-center/translations/zh_CN/rog-control-center.po index f9f9ea40..4cb914bd 100644 --- a/rog-control-center/translations/zh_CN/rog-control-center.po +++ b/rog-control-center/translations/zh_CN/rog-control-center.po @@ -303,7 +303,7 @@ msgstr "SPPT,APU 慢速功率限制" #: rog-control-center/ui/pages/system.slint:228 msgctxt "ppt_platform_sppt" msgid "Slow package power tracking limit" -msgstr "" +msgstr "慢速封装功率追踪限制" #: rog-control-center/ui/pages/system.slint:238 #, fuzzy From a27df22d0d30f5d8e8547434aaae74eae1996f82 Mon Sep 17 00:00:00 2001 From: NB-Group Date: Wed, 29 Jul 2026 23:51:00 +0800 Subject: [PATCH 07/11] rog-control-center(gpu): guard the APU-memory dropdown index setup_gpu sets apu_mem_present before choices/index land, so the row could briefly index an empty model at -1; require a non-empty model and an in-range index before building it. Caught by CodeRabbit. --- rog-control-center/ui/pages/gpu.slint | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rog-control-center/ui/pages/gpu.slint b/rog-control-center/ui/pages/gpu.slint index 5b62209f..dc67ede1 100644 --- a/rog-control-center/ui/pages/gpu.slint +++ b/rog-control-center/ui/pages/gpu.slint @@ -236,7 +236,13 @@ export component PageGPU inherits RogPage { toggled(v) => { GPUPageData.cb_set_xgm_led_enabled(v); } } - if GPUPageData.apu_mem_present: VerticalLayout { + // Guard the index: apu_mem_index defaults to -1 and the model + // is filled asynchronously, so only build the row once both + // are valid — otherwise choices[index] indexes out of range. + if GPUPageData.apu_mem_present + && GPUPageData.apu_mem_choices.length > 0 + && GPUPageData.apu_mem_index >= 0 + && GPUPageData.apu_mem_index < GPUPageData.apu_mem_choices.length: VerticalLayout { spacing: 10px; RogDropdownRow { label: @tr("Reserved GPU Memory (iGPU VRAM)"); From 147f49dfce31b5d1698e8fc108ca33df95115124 Mon Sep 17 00:00:00 2001 From: NB-Group Date: Wed, 29 Jul 2026 23:52:18 +0800 Subject: [PATCH 08/11] rog-control-center(gpu): refresh apu_mem choices after a write, not just the index set_apu_mem re-read possible_values() but only wrote the index back, so a changed firmware option set would leave the dropdown labels stale. Now writes the labels too, matching the initial setup path. Noted by CodeRabbit. --- rog-control-center/src/ui/setup_gpu.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rog-control-center/src/ui/setup_gpu.rs b/rog-control-center/src/ui/setup_gpu.rs index f1fa87f9..74f50a2a 100644 --- a/rog-control-center/src/ui/setup_gpu.rs +++ b/rog-control-center/src/ui/setup_gpu.rs @@ -183,7 +183,11 @@ fn set_apu_mem(proxy: AsusArmouryProxy<'static>, handle: Weak, value let new_current = p.current_value().await.unwrap_or(value); let choices = p.possible_values().await.unwrap_or_default(); let new_index = choices.iter().position(|v| *v == new_current).unwrap_or(0) as i32; + // Refresh the labels too: the firmware's option set can change after a + // write, so a stale model would leave the dropdown out of sync. + let labels: Vec = choices.iter().map(|v| apu_mem_val_to_label(*v)).collect(); w.upgrade_in_event_loop(move |h| { + h.global::().set_apu_mem_choices(labels.as_slice().into()); h.global::().set_apu_mem_index(new_index); }) .unwrap_or_else(|e| error!("setup_gpu: failed to refresh apu_mem index: {e:?}")); From 98a674e4b6df318c91137378e81e139c9cbbc535 Mon Sep 17 00:00:00 2001 From: NB-Group Date: Thu, 30 Jul 2026 11:39:35 +0800 Subject: [PATCH 09/11] rog-control-center(gpu): drop the Windows-mode reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're not on Windows — the Hybrid description said "[Windows default] Also called MHybrid". Trimmed to just the workload-based switching explanation (en + zh_CN). Also dropped a stale "Windows" msgid that home.slint no longer references. --- .../translations/zh_CN/rog-control-center.po | 11 ++--------- rog-control-center/ui/pages/gpu.slint | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/rog-control-center/translations/zh_CN/rog-control-center.po b/rog-control-center/translations/zh_CN/rog-control-center.po index 4cb914bd..f899ddad 100644 --- a/rog-control-center/translations/zh_CN/rog-control-center.po +++ b/rog-control-center/translations/zh_CN/rog-control-center.po @@ -791,10 +791,6 @@ msgctxt "PageHome" msgid "Edit Manual Mode" msgstr "编辑手动模式" -#: rog-control-center/ui/pages/home.slint -msgctxt "PageHome" -msgid "Windows" -msgstr "Windows" #: rog-control-center/ui/pages/home.slint msgctxt "PageHome" @@ -1017,11 +1013,8 @@ msgstr "混合输出" #: rog-control-center/ui/pages/gpu.slint msgctxt "PageGPU" -msgid "" -"[Windows default] Also called MHybrid. Auto-switches between dGPU and iGPU " -"based on workload." -msgstr "" -"[Windows 默认] 也称 MHybrid,根据负载在独立显卡和集成显卡之间自动切换。" +msgid "Auto-switches between the discrete and integrated GPUs based on workload." +msgstr "根据负载在独立显卡和集成显卡之间自动切换。" #: rog-control-center/ui/pages/gpu.slint msgctxt "PageGPU" diff --git a/rog-control-center/ui/pages/gpu.slint b/rog-control-center/ui/pages/gpu.slint index dc67ede1..780c712b 100644 --- a/rog-control-center/ui/pages/gpu.slint +++ b/rog-control-center/ui/pages/gpu.slint @@ -43,7 +43,7 @@ export component PageGPU inherits RogPage { return mode == "Ultimate" ? @tr("Uses only the discrete GPU for all rendering. Best performance but higher power consumption.") : mode == "Hybrid" - ? @tr("[Windows default] Also called MHybrid. Auto-switches between dGPU and iGPU based on workload.") + ? @tr("Auto-switches between the discrete and integrated GPUs based on workload.") : mode == "Integrated" ? @tr("Completely disables discrete GPU for best power saving. Lower temps and noise but significantly reduced performance.") : ""; From b07b60480e9bd178e1bf3a3ba6a6c2bba8a293f7 Mon Sep 17 00:00:00 2001 From: NB-Group Date: Thu, 30 Jul 2026 11:41:30 +0800 Subject: [PATCH 10/11] rog-control-center: drop the unused Windows-mode SVG icons ic-windows.svg and mode-windows.svg were added with the redesign but nothing references them now that the Hybrid description no longer mentions Windows. --- rog-control-center/ui/images/ic-windows.svg | 6 ------ rog-control-center/ui/images/mode-windows.svg | 1 - 2 files changed, 7 deletions(-) delete mode 100644 rog-control-center/ui/images/ic-windows.svg delete mode 100644 rog-control-center/ui/images/mode-windows.svg diff --git a/rog-control-center/ui/images/ic-windows.svg b/rog-control-center/ui/images/ic-windows.svg deleted file mode 100644 index bac880ae..00000000 --- a/rog-control-center/ui/images/ic-windows.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/rog-control-center/ui/images/mode-windows.svg b/rog-control-center/ui/images/mode-windows.svg deleted file mode 100644 index 3aafe8b3..00000000 --- a/rog-control-center/ui/images/mode-windows.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From f457b7d66b13257e20da866d6e17b2086300bd68 Mon Sep 17 00:00:00 2001 From: NB-Group Date: Thu, 30 Jul 2026 12:11:30 +0800 Subject: [PATCH 11/11] rog-control-center(gpu): remap apu_mem index from current firmware state on_cb_set_apu_mem closed over the initial possible vector, so after a choices refresh a UI index could map to a stale/wrong value. Re-read possible_values() in the callback and map the index there before writing. Noted by CodeRabbit. --- rog-control-center/src/ui/setup_gpu.rs | 34 ++++++++++++++++---------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/rog-control-center/src/ui/setup_gpu.rs b/rog-control-center/src/ui/setup_gpu.rs index 74f50a2a..76ab1d42 100644 --- a/rog-control-center/src/ui/setup_gpu.rs +++ b/rog-control-center/src/ui/setup_gpu.rs @@ -274,20 +274,28 @@ pub fn setup_gpu_page(ui: &MainWindow) { global.set_apu_mem_present(true); global.set_apu_mem_choices(apu_choices.as_slice().into()); global.set_apu_mem_index(apu_index); - let weak_handle = h.as_weak(); global.on_cb_set_apu_mem(move |index| { - let Some(value) = possible.get(index as usize).copied() else { - return; - }; - // Disable the dropdown while applying - weak_handle - .upgrade_in_event_loop(move |h| { - h.global::().set_apu_mem_index(index); - }) - .unwrap_or_else(|e| { - error!("setup_gpu: failed to set apu_mem index: {e:?}") - }); - set_apu_mem(proxy_cb.clone(), handle_cb.clone(), value); + let proxy_cb = proxy_cb.clone(); + let handle_cb = handle_cb.clone(); + // Re-read the firmware option set so the UI index maps to the + // right value even if the list/order changed since startup — + // closing over the initial possible vector would write a + // stale or wrong value otherwise. + tokio::spawn(async move { + let possible: Vec = + proxy_cb.possible_values().await.unwrap_or_default(); + let Some(value) = possible.get(index as usize).copied() else { + return; + }; + handle_cb + .upgrade_in_event_loop(move |h| { + h.global::().set_apu_mem_index(index); + }) + .unwrap_or_else(|e| { + error!("setup_gpu: failed to set apu_mem index: {e:?}") + }); + set_apu_mem(proxy_cb.clone(), handle_cb.clone(), value); + }); }); }) { error!("setup_gpu: failed to wire apu_mem callback: {e:?}");