feat(rog-control-center): Major UI/UX improvements and new features

- Add software RGB animations for static-only keyboards (rainbow, color cycle)
- Add custom fan curve control via direct sysfs for unsupported laptops
- Add real-time system status bar (CPU/GPU temps, fan speeds, power draw)
- Add tray icon tooltip with live system stats
- Add power profile change notifications (Fn+F5)
- Add dGPU status notifications
- Add ROG theme with dark palette and accent colors
- Add Screenpad, Slash, and SuperGFX page stubs
- Improve fan curve graph UI
- Various UI refinements and fixes

Co-Authored-By: Gemini <noreply@google.com>
This commit is contained in:
mihai2mn
2026-01-15 20:09:40 +01:00
parent 5303bfc1ad
commit 3d0caa39e1
40 changed files with 2790 additions and 692 deletions
+12 -9
View File
@@ -1,12 +1,14 @@
import { Palette, VerticalBox, HorizontalBox, GroupBox } from "std-widgets.slint";
import { VerticalBox, HorizontalBox, GroupBox } from "std-widgets.slint";
import { SystemToggleVert, SystemDropdown } from "common.slint";
import { PowerZones } from "../types/aura_types.slint";
import { RogPalette } from "../themes/rog_theme.slint";
export component AuraPowerGroup inherits Rectangle {
min-width: row.min-width;
border-radius: 20px;
background: Palette.alternate-background;
opacity: 0.9;
border-radius: 8px;
background: RogPalette.control-background;
border-width: 1px;
border-color: RogPalette.control-border;
in-out property <string> group-title;
in-out property <bool> boot_checked;
in-out property <bool> awake_checked;
@@ -20,7 +22,7 @@ export component AuraPowerGroup inherits Rectangle {
spacing: 10px;
Text {
font-size: 18px;
color: Palette.alternate-foreground;
color: RogPalette.text-primary;
horizontal-alignment: TextHorizontalAlignment.center;
text <=> root.group-title;
}
@@ -72,9 +74,10 @@ export component AuraPowerGroup inherits Rectangle {
export component AuraPowerGroupOld inherits Rectangle {
min-width: row.min-width;
border-radius: 20px;
background: Palette.alternate-background;
opacity: 0.9;
border-radius: 8px;
background: RogPalette.control-background;
border-width: 1px;
border-color: RogPalette.control-border;
in-out property <int> current_zone;
in-out property <[int]> zones;
in-out property <[string]> zone_strings;
@@ -90,7 +93,7 @@ export component AuraPowerGroupOld inherits Rectangle {
spacing: 10px;
Text {
font-size: 18px;
color: Palette.alternate-foreground;
color: RogPalette.text-primary;
horizontal-alignment: TextHorizontalAlignment.center;
text <=> root.group-title;
}
+33 -27
View File
@@ -1,12 +1,15 @@
import { Palette, VerticalBox , StandardButton, Button, HorizontalBox, ComboBox, Switch, Slider} from "std-widgets.slint";
import { VerticalBox , StandardButton, Button, HorizontalBox, ComboBox, Switch, Slider} from "std-widgets.slint";
import { RogPalette } from "../themes/rog_theme.slint";
export component RogItem inherits Rectangle {
background: Palette.control-background;
border-color: Palette.border;
border-width: 3px;
border-radius: 10px;
in property <bool> enabled: true;
background: root.enabled ? RogPalette.control-background : RogPalette.control-background.darker(0.5);
border-color: root.enabled ? RogPalette.control-border : RogPalette.control-border.darker(0.3);
border-width: 1px; // Thinner border for modern look
border-radius: RogPalette.border-radius;
min-height: 48px;
max-height: 56px;
opacity: root.enabled ? 1.0 : 0.6;
}
export component SystemSlider inherits RogItem {
@@ -18,7 +21,6 @@ export component SystemSlider inherits RogItem {
callback released(float);
in property <string> help_text;
in property <bool> enabled: true;
in property <bool> has_reset: false;
callback cb_do_reset();
@@ -32,7 +34,7 @@ export component SystemSlider inherits RogItem {
Text {
font-size: 16px;
vertical-alignment: TextVerticalAlignment.center;
color: Palette.control-foreground;
color: RogPalette.text-primary;
text: root.text;
}
@@ -40,7 +42,7 @@ export component SystemSlider inherits RogItem {
font-size: 16px;
horizontal-alignment: TextHorizontalAlignment.right;
vertical-alignment: TextVerticalAlignment.center;
color: Palette.control-foreground;
color: RogPalette.accent;
text: "\{Math.round(root.value)}";
}
}
@@ -64,10 +66,11 @@ export component SystemSlider inherits RogItem {
y: help.y - self.height + help.height - 10px;
Rectangle {
drop-shadow-blur: 10px;
drop-shadow-color: black;
border-radius: 10px;
border-color: Palette.accent-background;
background: Palette.background;
drop-shadow-color: Colors.black;
border-radius: RogPalette.border-radius;
border-width: 1px;
border-color: RogPalette.accent;
background: RogPalette.control-background;
Dialog {
title: root.title;
VerticalBox {
@@ -77,12 +80,12 @@ export component SystemSlider inherits RogItem {
wrap: TextWrap.word-wrap;
horizontal-alignment: TextHorizontalAlignment.center;
text: root.title;
color: RogPalette.text-primary;
}
Rectangle {
height: 1px;
border-color: black;
border-width: 1px;
background: RogPalette.control-border;
}
Text {
@@ -90,6 +93,7 @@ export component SystemSlider inherits RogItem {
font-size: 16px;
wrap: TextWrap.word-wrap;
text: root.help_text;
color: RogPalette.text-secondary;
}
}
@@ -114,16 +118,18 @@ export component SystemSlider inherits RogItem {
y: reset.y - self.height + reset.height;
Rectangle {
drop-shadow-blur: 10px;
drop-shadow-color: black;
border-radius: 10px;
border-color: Palette.accent-background;
background: Palette.background;
drop-shadow-color: Colors.black;
border-radius: RogPalette.border-radius;
border-width: 1px;
border-color: RogPalette.accent;
background: RogPalette.control-background;
Dialog {
Text {
max-width: 420px;
font-size: 16px;
wrap: TextWrap.word-wrap;
text: @tr("confirm_reset" => "Are you sure you want to reset this?");
color: RogPalette.text-primary;
}
StandardButton {
@@ -164,7 +170,7 @@ export component SystemToggle inherits RogItem {
Text {
font-size: 16px;
vertical-alignment: TextVerticalAlignment.center;
color: Palette.control-foreground;
color: RogPalette.text-primary;
text: root.text;
}
}
@@ -195,7 +201,7 @@ export component SystemToggleInt inherits RogItem {
Text {
font-size: 16px;
vertical-alignment: TextVerticalAlignment.center;
color: Palette.control-foreground;
color: RogPalette.text-primary;
text: root.text;
}
}
@@ -226,7 +232,7 @@ export component SystemToggleVert inherits RogItem {
font-size: 16px;
vertical-alignment: TextVerticalAlignment.bottom;
horizontal-alignment: TextHorizontalAlignment.center;
color: Palette.control-foreground;
color: RogPalette.text-primary;
text: root.text;
}
@@ -256,7 +262,7 @@ export component SystemDropdown inherits RogItem {
Text {
font-size: 16px;
vertical-alignment: TextVerticalAlignment.center;
color: Palette.control-foreground;
color: RogPalette.text-primary;
text: root.text;
}
}
@@ -288,9 +294,9 @@ export component PopupNotification {
height: root.height;
// TODO: add properties to display
Rectangle {
border-width: 2px;
border-color: Palette.accent-background;
background: Palette.background;
border-width: 1px;
border-color: RogPalette.accent;
background: RogPalette.background;
// TODO: drop shadows slow
// drop-shadow-offset-x: 7px;
// drop-shadow-offset-y: 7px;
@@ -302,14 +308,14 @@ export component PopupNotification {
alignment: start;
Text {
text: heading;
color: Palette.control-foreground;
color: RogPalette.text-primary;
font-size: 32px;
font-weight: 900;
}
Text {
text: content;
color: Palette.control-foreground;
color: RogPalette.text-secondary;
font-size: 18px;
}
}
+29 -18
View File
@@ -1,4 +1,5 @@
import { Palette } from "std-widgets.slint";
import { RogPalette } from "../themes/rog_theme.slint";
export struct Node { x: length, y: length}
@@ -44,7 +45,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: RogPalette.control-border;
stroke-width: 1px;
MoveTo {
x: scale_x_to_graph(n * 10px) / 1px;
@@ -60,7 +61,7 @@ export component Graph inherits Rectangle {
}
for n in 11: Text {
color: Palette.accent-background;
color: RogPalette.text-secondary;
font-size <=> root.axis_font_size;
text: "\{n * 10}c";
x: scale_x_to_graph(n * 10px) - self.width / 3;
@@ -70,7 +71,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: RogPalette.control-border;
stroke-width: 1px;
MoveTo {
x: 0;
@@ -86,7 +87,7 @@ export component Graph inherits Rectangle {
}
for n in 11: Text {
color: Palette.accent-background;
color: RogPalette.text-secondary;
font-size <=> root.axis_font_size;
text: "\{n * 10}%";
x: - self.width;
@@ -97,7 +98,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: RogPalette.accent;
stroke-width: 2px;
MoveTo {
x: scale_x_to_graph(nodes[idx].x) / 1px;
@@ -114,19 +115,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: RogPalette.accent;
tip.background: RogPalette.accent;
tip.opacity: 1.0;
}
hover when touch.has-hover: {
point.background: Palette.accent-background;
tip.background: Palette.accent-background;
point.background: RogPalette.accent;
tip.background: RogPalette.accent;
tip.opacity: 1.0;
}
]
//
point := Rectangle {
background: Palette.control-foreground;
background: RogPalette.text-primary;
x: scale_x_to_graph(n.x) - self.width / 2;
y: graph.height - scale_y_to_graph(n.y) - self.height / 2;
width: 18px;
@@ -142,10 +143,14 @@ export component Graph inherits Rectangle {
} else if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) < nodes[idx - 1].x {
n.x = nodes[idx - 1].x + pad;
}
// Y-Axis: Monotonic Non-Decreasing
if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) > nodes[idx + 1].y {
n.y = nodes[idx + 1].y - pad;
n.y = nodes[idx + 1].y; // Allow equality
} else if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) < nodes[idx - 1].y {
n.y = nodes[idx - 1].y + pad;
n.y = nodes[idx - 1].y; // Allow equality
} else if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) < 0.0 {
n.y = 0px;
}
} else if idx == 0 {
if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) < 0.0 {
@@ -153,10 +158,12 @@ export component Graph inherits Rectangle {
} else if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) > nodes[idx + 1].x {
n.x = nodes[idx + 1].x - pad;
}
// Y-Axis: <= Next Point
if n.y - scale_y_to_node(self.mouse-y - self.pressed-y) < 0.0 {
n.y = 1px;
n.y = 0px; // Allow 0 RPM
} else if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) > nodes[idx + 1].y {
n.y = nodes[idx + 1].y - pad;
n.y = nodes[idx + 1].y; // Allow equality
}
} else if idx == nodes.length - 1 {
if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) > scale_x_to_node(graph.width) {
@@ -164,10 +171,14 @@ export component Graph inherits Rectangle {
} else if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) < nodes[idx - 1].x {
n.x = nodes[idx - 1].x + pad;
}
// Y-Axis: >= Previous Point
if n.y - scale_y_to_node(self.mouse-y - self.pressed-y) > scale_y_to_node(graph.height) {
n.y = scale_y_to_node(graph.height - 1px);
n.y = scale_y_to_node(graph.height);
} else if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) < nodes[idx - 1].y {
n.y = nodes[idx - 1].y + pad;
n.y = nodes[idx - 1].y; // Allow equality
} else if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) < 0.0 {
n.y = 0px;
}
}
}
@@ -189,7 +200,7 @@ export component Graph inherits Rectangle {
}
tip := Rectangle {
background: Palette.control-foreground;
background: RogPalette.control-background;
opacity: 0.3;
x: final_x_pos();
y: final_y_pos();
@@ -224,7 +235,7 @@ export component Graph inherits Rectangle {
}
//
label := Text {
color: Palette.accent-foreground;
color: RogPalette.text-primary;
font-size: 16px;
text: "\{Math.floor(n.x / 1px)}c, \{fan_pct()}%";
}
+30 -12
View File
@@ -1,7 +1,8 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT
import { Palette, HorizontalBox, VerticalBox } from "std-widgets.slint";
import { HorizontalBox, VerticalBox } from "std-widgets.slint";
import { RogPalette } from "../themes/rog_theme.slint";
component SideBarItem inherits Rectangle {
// padding only has effect on layout elements
@@ -28,12 +29,21 @@ component SideBarItem inherits Rectangle {
]
state := Rectangle {
opacity: 0;
border-width: 2px;
border-radius: 10px;
border-color: Palette.accent-background;
background: Palette.alternate-background;
border-width: 0px; // Modern look: no full border, maybe just a left bar?
// Or keep the ROG style border
border-color: RogPalette.accent;
background: root.selected ? RogPalette.control-background : RogPalette.alternate-background;
// Add a red indicator line on the left for selected items
Rectangle {
x: 0;
width: 4px;
height: 100%;
background: root.selected ? RogPalette.accent : Colors.transparent;
}
animate opacity { duration: 150ms; }
animate border-width { duration: 150ms; }
// animate border-width { duration: 150ms; }
height: l.preferred-height;
}
@@ -41,9 +51,10 @@ component SideBarItem inherits Rectangle {
y: (parent.height - self.height) / 2;
spacing: 0px;
label := Text {
color: Palette.foreground;
color: root.selected ? RogPalette.accent : RogPalette.text-primary;
vertical-alignment: center;
font-size: 14px;
font-weight: root.selected ? 700 : 400;
}
}
@@ -66,10 +77,10 @@ export component SideBar inherits Rectangle {
accessible-role: tab;
accessible-delegate-focus: root.current-focused >= 0 ? root.current-focused : root.current-item;
Rectangle {
border-width: 2px;
border-color: Palette.accent-background;
border-width: 0px;
// border-color: RogPalette.accent;
border-radius: 0px;
background: Palette.background.darker(0.2);
background: RogPalette.alternate-background; // Darker sidebar
fs := FocusScope {
key-pressed(event) => {
if (event.text == "\n") {
@@ -104,12 +115,19 @@ export component SideBar inherits Rectangle {
spacing: 4px;
alignment: start;
label := Text {
font-size: 16px;
font-size: 24px; // Larger brand text
font-weight: 800;
horizontal-alignment: center;
color: RogPalette.accent; // ROG Red brand text
}
// Spacer after brand text
Rectangle {
height: 20px;
}
navigation := VerticalLayout {
spacing: -6px;
spacing: 4px; // Spacing between items
alignment: start;
vertical-stretch: 0;
for item[index] in root.model: SideBarItem {
@@ -0,0 +1,58 @@
import { RogPalette } from "../themes/rog_theme.slint";
export global SystemStatus {
in property <int> cpu_temp: 0;
in property <int> gpu_temp: 0;
in property <int> cpu_fan: 0;
in property <int> gpu_fan: 0;
in property <string> power_w: "--";
in property <string> power_avg_w: "--";
}
component StatusItem inherits Rectangle {
in property <string> label;
in property <string> value;
in property <string> unit;
HorizontalLayout {
spacing: 5px;
Text { text: label; color: RogPalette.text-secondary; font-weight: 700; vertical-alignment: center; }
Text { text: value; color: RogPalette.text-primary; vertical-alignment: center; }
Text { text: unit; color: RogPalette.text-secondary; font-size: 12px; vertical-alignment: center; }
}
}
export component StatusBar inherits Rectangle {
background: RogPalette.control-background;
height: 30px;
// Simulated top border
Rectangle {
y: 0px;
x: 0px;
width: parent.width;
height: 1px;
background: RogPalette.control-border;
}
HorizontalLayout {
padding-left: 20px;
padding-right: 20px;
spacing: 20px;
alignment: space-between;
HorizontalLayout {
spacing: 20px;
StatusItem { label: "CPU"; value: SystemStatus.cpu_temp; unit: "°C"; }
StatusItem { label: "GPU"; value: SystemStatus.gpu_temp; unit: "°C"; }
}
HorizontalLayout {
spacing: 20px;
StatusItem { label: "PWR"; value: SystemStatus.power_w; unit: "W"; }
StatusItem { label: "AVG"; value: SystemStatus.power_avg_w; unit: "W"; }
StatusItem { label: "CPU Fan"; value: SystemStatus.cpu_fan; unit: "RPM"; }
StatusItem { label: "GPU Fan"; value: SystemStatus.gpu_fan; unit: "RPM"; }
}
}
}