From 81ae9dea91506410541ecfc12b304c303a894624 Mon Sep 17 00:00:00 2001
From: Bartolomiv
Date: Thu, 3 Sep 2026 22:03:21 +0300
Subject: [PATCH 1/8] vrgb plasma vidget first iteration(very vibecoded)
---
plasmoid/install.sh | 28 ++
plasmoid/package/contents/ui/ColorPicker.qml | 143 +++++++++
plasmoid/package/contents/ui/main.qml | 312 +++++++++++++++++++
plasmoid/package/metadata.json | 19 ++
4 files changed, 502 insertions(+)
create mode 100755 plasmoid/install.sh
create mode 100644 plasmoid/package/contents/ui/ColorPicker.qml
create mode 100644 plasmoid/package/contents/ui/main.qml
create mode 100644 plasmoid/package/metadata.json
diff --git a/plasmoid/install.sh b/plasmoid/install.sh
new file mode 100755
index 0000000..75bf24e
--- /dev/null
+++ b/plasmoid/install.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+# Install or upgrade the VRGB Plasma 6 applet for the current user.
+set -e
+
+APPLET_ID="org.vrgb.keyboard"
+PACKAGE_DIR="$(cd "$(dirname "$0")" && pwd)/package"
+
+if ! command -v kpackagetool6 >/dev/null 2>&1; then
+ echo "kpackagetool6 not found - this applet requires Plasma 6." >&2
+ exit 1
+fi
+
+if ! command -v vrgb >/dev/null 2>&1 && [ ! -x /usr/local/bin/vrgb ]; then
+ echo "Warning: vrgb was not found. Install it first with ../install.sh" >&2
+fi
+
+if kpackagetool6 --type Plasma/Applet --list 2>/dev/null | grep -qx "$APPLET_ID"; then
+ echo "Upgrading $APPLET_ID..."
+ kpackagetool6 --type Plasma/Applet --upgrade "$PACKAGE_DIR"
+else
+ echo "Installing $APPLET_ID..."
+ kpackagetool6 --type Plasma/Applet --install "$PACKAGE_DIR"
+fi
+
+echo
+echo "Done. Add it with: right-click the panel -> Add Widgets -> \"VRGB Keyboard Lighting\""
+echo "If you upgraded an already-running applet, restart plasmashell to reload it:"
+echo " systemctl --user restart plasma-plasmashell"
diff --git a/plasmoid/package/contents/ui/ColorPicker.qml b/plasmoid/package/contents/ui/ColorPicker.qml
new file mode 100644
index 0000000..cd5f3e0
--- /dev/null
+++ b/plasmoid/package/contents/ui/ColorPicker.qml
@@ -0,0 +1,143 @@
+/*
+ * VRGB - inline HSV colour picker (saturation/value square + hue strip).
+ *
+ * Emits picked() continuously while dragging; the caller is expected to
+ * throttle hardware writes rather than firing one per pixel.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+import QtQuick
+import QtQuick.Layouts
+import org.kde.kirigami as Kirigami
+
+ColumnLayout {
+ id: picker
+
+ signal picked(color c)
+
+ // Hue is kept separately from the emitted colour: greys report hsvHue == -1,
+ // so round-tripping through a colour would make the hue marker jump to red.
+ property real hue: 0
+ property real sat: 1
+ property real val: 1
+
+ readonly property color currentColor: Qt.hsva(hue, sat, val, 1)
+
+ // Seed the picker from outside without emitting picked().
+ function setColor(c) {
+ if (Qt.colorEqual(c, picker.currentColor)) {
+ return;
+ }
+ hue = (c.hsvHue < 0) ? hue : c.hsvHue;
+ sat = c.hsvSaturation;
+ val = c.hsvValue;
+ }
+
+ spacing: Kirigami.Units.smallSpacing
+
+ Rectangle {
+ id: svSquare
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: Kirigami.Units.gridUnit * 7
+
+ radius: 3
+ border.width: 1
+ border.color: Qt.rgba(0, 0, 0, 0.25)
+
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
+ GradientStop { position: 0.0; color: "#ffffff" }
+ GradientStop { position: 1.0; color: Qt.hsva(picker.hue, 1, 1, 1) }
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ radius: parent.radius
+ gradient: Gradient {
+ orientation: Gradient.Vertical
+ GradientStop { position: 0.0; color: "transparent" }
+ GradientStop { position: 1.0; color: "#000000" }
+ }
+ }
+
+ Rectangle {
+ width: Math.round(Kirigami.Units.gridUnit * 0.8)
+ height: width
+ radius: width / 2
+ color: "transparent"
+ border.width: 2
+ // Dark ring on pale areas, light ring everywhere else.
+ border.color: (picker.val > 0.55 && picker.sat < 0.55) ? "#000000" : "#ffffff"
+ x: picker.sat * svSquare.width - width / 2
+ y: (1 - picker.val) * svSquare.height - height / 2
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ function pick(mx, my) {
+ picker.sat = Math.max(0, Math.min(1, mx / width));
+ picker.val = 1 - Math.max(0, Math.min(1, my / height));
+ picker.picked(picker.currentColor);
+ }
+
+ onPressed: mouse => pick(mouse.x, mouse.y)
+ onPositionChanged: mouse => {
+ if (pressed) {
+ pick(mouse.x, mouse.y);
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ id: hueStrip
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: Kirigami.Units.gridUnit
+
+ radius: 3
+ border.width: 1
+ border.color: Qt.rgba(0, 0, 0, 0.25)
+
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
+ GradientStop { position: 0.000; color: "#ff0000" }
+ GradientStop { position: 0.167; color: "#ffff00" }
+ GradientStop { position: 0.333; color: "#00ff00" }
+ GradientStop { position: 0.500; color: "#00ffff" }
+ GradientStop { position: 0.667; color: "#0000ff" }
+ GradientStop { position: 0.833; color: "#ff00ff" }
+ GradientStop { position: 1.000; color: "#ff0000" }
+ }
+
+ Rectangle {
+ width: 3
+ height: parent.height + 4
+ y: -2
+ x: picker.hue * hueStrip.width - width / 2
+ color: "#ffffff"
+ border.width: 1
+ border.color: "#000000"
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ function pick(mx) {
+ // Clamp below 1.0: a hue of exactly 1.0 wraps back to 0.
+ picker.hue = Math.max(0, Math.min(0.9999, mx / width));
+ picker.picked(picker.currentColor);
+ }
+
+ onPressed: mouse => pick(mouse.x)
+ onPositionChanged: mouse => {
+ if (pressed) {
+ pick(mouse.x);
+ }
+ }
+ }
+ }
+}
diff --git a/plasmoid/package/contents/ui/main.qml b/plasmoid/package/contents/ui/main.qml
new file mode 100644
index 0000000..c3d11dc
--- /dev/null
+++ b/plasmoid/package/contents/ui/main.qml
@@ -0,0 +1,312 @@
+/*
+ * VRGB - Plasma 6 applet for ASUS Vivobook HID LampArray keyboards.
+ *
+ * State lives in ~/.config/vrgb/config.json, which the applet reads directly.
+ * Writes always go through the `vrgb` CLI so the config file has a single
+ * owner and the applet never touches hidraw itself.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+import QtQuick
+import QtQuick.Layouts
+import org.kde.plasma.plasmoid
+import org.kde.plasma.components as PlasmaComponents
+import org.kde.plasma.plasma5support as P5Support
+import org.kde.kirigami as Kirigami
+
+PlasmoidItem {
+ id: root
+
+ // Resolved at startup; install.sh puts vrgb in /usr/local/bin, which is not
+ // always on plasmashell's PATH.
+ property string vrgbBin: "vrgb"
+ property color currentColor: "#00aa55"
+ property int brightness: 100
+ property string errorText: ""
+ // Suppresses hardware writes while the UI is being seeded from config.json.
+ property bool loading: true
+
+ readonly property var presets: [
+ "#ff0000", "#ff7f00", "#ffd400", "#7fff00", "#00ff5e", "#00e5ff",
+ "#0080ff", "#2b3cff", "#8b00ff", "#ff00c8", "#ff4d6d", "#ffffff"
+ ]
+
+ readonly property string hexColor: {
+ function ch(v) {
+ return Math.round(Math.max(0, Math.min(1, v)) * 255).toString(16).padStart(2, "0");
+ }
+ return ch(currentColor.r) + ch(currentColor.g) + ch(currentColor.b);
+ }
+
+ // The colour as it should actually appear on the keys.
+ readonly property color litColor: Qt.rgba(currentColor.r * brightness / 100,
+ currentColor.g * brightness / 100,
+ currentColor.b * brightness / 100, 1)
+
+ readonly property string resolveCmd: "command -v vrgb 2>/dev/null || echo /usr/local/bin/vrgb"
+ readonly property string readCfgCmd: "cat \"$HOME/.config/vrgb/config.json\" 2>/dev/null"
+
+ Plasmoid.icon: "input-keyboard"
+ toolTipMainText: i18n("Keyboard Lighting")
+ toolTipSubText: loading ? i18n("Reading configuration…")
+ : i18n("#%1 at %2%", hexColor.toUpperCase(), brightness)
+
+ preferredRepresentation: compactRepresentation
+
+ function applyNow() {
+ if (loading) {
+ return;
+ }
+ applyTimer.stop();
+ // hexColor is generated locally and brightness is an int, so the
+ // command line is safe to build by concatenation.
+ executable.run(vrgbBin + " set " + hexColor + " " + brightness);
+ }
+
+ function scheduleApply() {
+ if (!loading) {
+ applyTimer.restart();
+ }
+ }
+
+ function loadConfig(text) {
+ var cfg = null;
+ if (text.length > 0) {
+ try {
+ cfg = JSON.parse(text);
+ } catch (e) {
+ cfg = null;
+ }
+ }
+ if (cfg) {
+ if (typeof cfg.color === "string" && /^[0-9a-fA-F]{6}$/.test(cfg.color)) {
+ currentColor = "#" + cfg.color;
+ }
+ var p = parseInt(cfg.percent, 10);
+ if (!isNaN(p)) {
+ brightness = Math.max(0, Math.min(100, p));
+ }
+ }
+ loading = false;
+ }
+
+ function handleResult(source, code, out, err) {
+ if (source === resolveCmd) {
+ if (out.length > 0) {
+ vrgbBin = out.split("\n")[0];
+ }
+ executable.run(readCfgCmd);
+ } else if (source === readCfgCmd) {
+ loadConfig(out);
+ } else {
+ errorText = (code === 0) ? ""
+ : (err.length > 0 ? err : i18n("vrgb exited with code %1", code));
+ }
+ }
+
+ Timer {
+ id: applyTimer
+ // vrgb returns in ~25 ms; 60 ms keeps a drag smooth without queueing up
+ // one process per mouse move.
+ interval: 60
+ onTriggered: root.applyNow()
+ }
+
+ P5Support.DataSource {
+ id: executable
+
+ engine: "executable"
+ connectedSources: []
+
+ onNewData: (source, data) => {
+ var code = data["exit code"];
+ var out = (data["stdout"] || "").trim();
+ var err = (data["stderr"] || "").trim();
+ disconnectSource(source);
+ root.handleResult(source, code, out, err);
+ }
+
+ function run(cmd) {
+ // The engine keys sources by command string, so re-running an
+ // identical command needs an explicit disconnect first.
+ if (connectedSources.indexOf(cmd) !== -1) {
+ disconnectSource(cmd);
+ }
+ connectSource(cmd);
+ }
+ }
+
+ Component.onCompleted: executable.run(resolveCmd)
+
+ compactRepresentation: MouseArea {
+ onClicked: root.expanded = !root.expanded
+
+ Kirigami.Icon {
+ anchors.centerIn: parent
+ width: Math.min(parent.width, parent.height)
+ height: width
+ source: "input-keyboard"
+ isMask: true
+ color: root.brightness > 0 ? root.currentColor : Kirigami.Theme.disabledTextColor
+ }
+ }
+
+ fullRepresentation: Item {
+ Layout.minimumWidth: Kirigami.Units.gridUnit * 15
+ Layout.minimumHeight: Kirigami.Units.gridUnit * 21
+ Layout.preferredWidth: Kirigami.Units.gridUnit * 17
+ Layout.preferredHeight: Kirigami.Units.gridUnit * 23
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: Kirigami.Units.largeSpacing
+ spacing: Kirigami.Units.smallSpacing
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Kirigami.Units.largeSpacing
+
+ Rectangle {
+ Layout.preferredWidth: Kirigami.Units.gridUnit * 2.5
+ Layout.preferredHeight: Kirigami.Units.gridUnit * 2.5
+ radius: 4
+ color: root.litColor
+ border.width: 1
+ border.color: Qt.rgba(0, 0, 0, 0.3)
+ }
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: 0
+
+ Kirigami.Heading {
+ Layout.fillWidth: true
+ level: 4
+ elide: Text.ElideRight
+ text: i18n("Keyboard Lighting")
+ }
+
+ PlasmaComponents.Label {
+ Layout.fillWidth: true
+ elide: Text.ElideRight
+ font.family: "monospace"
+ opacity: 0.75
+ text: "#" + root.hexColor.toUpperCase()
+ }
+ }
+ }
+
+ Kirigami.Separator {
+ Layout.fillWidth: true
+ Layout.topMargin: Kirigami.Units.smallSpacing
+ Layout.bottomMargin: Kirigami.Units.smallSpacing
+ }
+
+ GridLayout {
+ Layout.fillWidth: true
+ columns: 6
+ columnSpacing: Kirigami.Units.smallSpacing
+ rowSpacing: Kirigami.Units.smallSpacing
+
+ Repeater {
+ model: root.presets
+
+ delegate: Rectangle {
+ required property string modelData
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: Kirigami.Units.gridUnit * 1.3
+ radius: 3
+ color: modelData
+ border.width: 1
+ border.color: Qt.rgba(0, 0, 0, 0.3)
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ root.currentColor = parent.modelData;
+ picker.setColor(root.currentColor);
+ root.applyNow();
+ }
+ }
+ }
+ }
+ }
+
+ ColorPicker {
+ id: picker
+
+ Layout.fillWidth: true
+ Layout.topMargin: Kirigami.Units.smallSpacing
+
+ Component.onCompleted: setColor(root.currentColor)
+
+ onPicked: c => {
+ root.currentColor = c;
+ root.scheduleApply();
+ }
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ Layout.topMargin: Kirigami.Units.smallSpacing
+
+ PlasmaComponents.Label {
+ text: i18n("Brightness")
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ PlasmaComponents.Label {
+ opacity: 0.75
+ text: root.brightness + "%"
+ }
+ }
+
+ PlasmaComponents.Slider {
+ id: brightnessSlider
+
+ Layout.fillWidth: true
+ from: 0
+ to: 100
+ stepSize: 1
+
+ Component.onCompleted: value = root.brightness
+
+ onMoved: {
+ root.brightness = Math.round(value);
+ root.scheduleApply();
+ }
+ }
+
+ Kirigami.InlineMessage {
+ Layout.fillWidth: true
+ type: Kirigami.MessageType.Error
+ text: root.errorText
+ visible: root.errorText.length > 0
+ }
+
+ Item {
+ Layout.fillHeight: true
+ }
+ }
+
+ // Keep the popup in sync when config.json loads after the popup was built.
+ Connections {
+ target: root
+
+ function onCurrentColorChanged() {
+ picker.setColor(root.currentColor);
+ }
+
+ function onBrightnessChanged() {
+ if (!brightnessSlider.pressed) {
+ brightnessSlider.value = root.brightness;
+ }
+ }
+ }
+ }
+}
diff --git a/plasmoid/package/metadata.json b/plasmoid/package/metadata.json
new file mode 100644
index 0000000..c6f8e07
--- /dev/null
+++ b/plasmoid/package/metadata.json
@@ -0,0 +1,19 @@
+{
+ "KPackageStructure": "Plasma/Applet",
+ "KPlugin": {
+ "Authors": [
+ {
+ "Name": "vrgb-dev"
+ }
+ ],
+ "Category": "Utilities",
+ "Description": "Set the colour and brightness of ASUS Vivobook HID LampArray keyboards",
+ "Icon": "input-keyboard",
+ "Id": "org.vrgb.keyboard",
+ "License": "MIT",
+ "Name": "VRGB Keyboard Lighting",
+ "Version": "0.1.0",
+ "Website": "https://github.com/vrgb-dev/vrgb"
+ },
+ "X-Plasma-API-Minimum-Version": "6.0"
+}
From 586a6e5d961ccedd7155bb3b75b7d8a29dbc200b Mon Sep 17 00:00:00 2001
From: Bartolomiv
Date: Thu, 3 Sep 2026 22:17:18 +0300
Subject: [PATCH 2/8] ukrainian translation
---
plasmoid/build-translations.sh | 31 ++++++++++++++
plasmoid/extract-messages.sh | 32 +++++++++++++++
.../plasma_applet_org.vrgb.keyboard.mo | Bin 0 -> 840 bytes
plasmoid/package/metadata.json | 2 +
.../po/plasma_applet_org.vrgb.keyboard.pot | 38 ++++++++++++++++++
plasmoid/po/uk.po | 38 ++++++++++++++++++
6 files changed, 141 insertions(+)
create mode 100755 plasmoid/build-translations.sh
create mode 100755 plasmoid/extract-messages.sh
create mode 100644 plasmoid/package/contents/locale/uk/LC_MESSAGES/plasma_applet_org.vrgb.keyboard.mo
create mode 100644 plasmoid/po/plasma_applet_org.vrgb.keyboard.pot
create mode 100644 plasmoid/po/uk.po
diff --git a/plasmoid/build-translations.sh b/plasmoid/build-translations.sh
new file mode 100755
index 0000000..e606198
--- /dev/null
+++ b/plasmoid/build-translations.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Compile po/*.po into the applet package.
+#
+# Plasma looks up an applet's catalogue under the domain
+# plasma_applet_, and KPackage adds the installed package's own
+# contents/locale directory to the search path, so the .mo files ship with
+# the widget instead of needing root access to /usr/share/locale.
+set -e
+
+DOMAIN="plasma_applet_org.vrgb.keyboard"
+BASE="$(cd "$(dirname "$0")" && pwd)"
+
+found=0
+for po in "$BASE"/po/*.po; do
+ [ -e "$po" ] || continue
+ found=1
+ lang="$(basename "$po" .po)"
+ dest="$BASE/package/contents/locale/$lang/LC_MESSAGES"
+ mkdir -p "$dest"
+ msgfmt --check -o "$dest/$DOMAIN.mo" "$po"
+ printf '%s -> %s\n' "$lang" "${dest#"$BASE"/}/$DOMAIN.mo"
+done
+
+if [ "$found" -eq 0 ]; then
+ echo "No po/*.po files found. Run ./extract-messages.sh first." >&2
+ exit 1
+fi
+
+echo
+echo "Now reinstall the applet so the catalogues are picked up:"
+echo " ./install.sh"
diff --git a/plasmoid/extract-messages.sh b/plasmoid/extract-messages.sh
new file mode 100755
index 0000000..0a9a01d
--- /dev/null
+++ b/plasmoid/extract-messages.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+# Re-extract translatable strings from the QML sources into po/*.pot,
+# then merge the changes into the existing translations.
+#
+# Run this whenever you add, remove, or reword an i18n() string.
+set -e
+
+DOMAIN="plasma_applet_org.vrgb.keyboard"
+BASE="$(cd "$(dirname "$0")" && pwd)"
+POT="$BASE/po/$DOMAIN.pot"
+
+mkdir -p "$BASE/po"
+
+# -C -kde is the KDE extraction mode; it understands the i18n* call family and
+# parses QML well enough since the call syntax matches C++.
+xgettext --from-code=UTF-8 -C -kde \
+ -ci18n -ki18n:1 -ki18nc:1c,2 -ki18np:1,2 -ki18ncp:1c,2,3 \
+ --package-name="VRGB Keyboard Lighting" \
+ --msgid-bugs-address="https://github.com/vrgb-dev/vrgb/issues" \
+ -o "$POT" \
+ "$BASE"/package/contents/ui/*.qml
+
+echo "Wrote ${POT#"$BASE"/}"
+
+for po in "$BASE"/po/*.po; do
+ [ -e "$po" ] || continue
+ msgmerge --quiet --update --backup=none "$po" "$POT"
+ echo "Merged into ${po#"$BASE"/}"
+done
+
+echo
+echo "Next: translate any new strings in po/*.po, then run ./build-translations.sh"
diff --git a/plasmoid/package/contents/locale/uk/LC_MESSAGES/plasma_applet_org.vrgb.keyboard.mo b/plasmoid/package/contents/locale/uk/LC_MESSAGES/plasma_applet_org.vrgb.keyboard.mo
new file mode 100644
index 0000000000000000000000000000000000000000..34d894521a1665a9aa6ae4a2e8a024b182086a12
GIT binary patch
literal 840
zcmZWnK~EDw6dnaVtS1cz51KqYwmUDRAH=U%of{z3;u*$@_Jq|1H6|inxWCMqER*
z5jH*{9K=?Scag6nejsilOppH}Z(Sm!4{;mw&zA`qK>mqr{lAfikpCeUkO%q*!BWzX
zz_c4zdVEUuPBzd&$laU^TtUvukzy7{HI)b{$qTVkNqH8+wEYw(5h)2CB11Jz7UHOt
zaTTY@pUwxeltmQ~%drw6ypB~3D?$M|m&|7AD^XQ+Dx~uwlUVMNjiF7XNNC{j7}u@O8A^U
zj8vS^B8q$ZP**(f&WDHKyAPE8&oia4BG7z3)ZlE=yB2GjX8D)9)Q_2tNOrn^tRqH@64)MGaKEt&|fYJM8DU^
z`Vi|nM1R!#W({pe`p9eo(zZU-+h!d*ywN*WFdJu^qWA0o{mpdDy5842fD@1PSAA@&
PLGOV+!1FHRyIrFHYwZww
literal 0
HcmV?d00001
diff --git a/plasmoid/package/metadata.json b/plasmoid/package/metadata.json
index c6f8e07..466ef4f 100644
--- a/plasmoid/package/metadata.json
+++ b/plasmoid/package/metadata.json
@@ -8,10 +8,12 @@
],
"Category": "Utilities",
"Description": "Set the colour and brightness of ASUS Vivobook HID LampArray keyboards",
+ "Description[uk]": "Керування кольором і яскравістю клавіатур ASUS Vivobook з HID LampArray",
"Icon": "input-keyboard",
"Id": "org.vrgb.keyboard",
"License": "MIT",
"Name": "VRGB Keyboard Lighting",
+ "Name[uk]": "Підсвічування клавіатури VRGB",
"Version": "0.1.0",
"Website": "https://github.com/vrgb-dev/vrgb"
},
diff --git a/plasmoid/po/plasma_applet_org.vrgb.keyboard.pot b/plasmoid/po/plasma_applet_org.vrgb.keyboard.pot
new file mode 100644
index 0000000..4a775ac
--- /dev/null
+++ b/plasmoid/po/plasma_applet_org.vrgb.keyboard.pot
@@ -0,0 +1,38 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the VRGB Keyboard Lighting package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: VRGB Keyboard Lighting\n"
+"Report-Msgid-Bugs-To: https://github.com/vrgb-dev/vrgb/issues\n"
+"POT-Creation-Date: 2026-09-03 22:05+0300\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: package/contents/ui/main.qml:51 package/contents/ui/main.qml:187
+msgid "Keyboard Lighting"
+msgstr ""
+
+#: package/contents/ui/main.qml:52
+msgid "Reading configuration…"
+msgstr ""
+
+#: package/contents/ui/main.qml:53
+msgid "#%1 at %2%"
+msgstr ""
+
+#: package/contents/ui/main.qml:104
+msgid "vrgb exited with code %1"
+msgstr ""
+
+#: package/contents/ui/main.qml:256
+msgid "Brightness"
+msgstr ""
diff --git a/plasmoid/po/uk.po b/plasmoid/po/uk.po
new file mode 100644
index 0000000..284349e
--- /dev/null
+++ b/plasmoid/po/uk.po
@@ -0,0 +1,38 @@
+# Ukrainian translation for the VRGB Plasma applet.
+# This file is distributed under the same license as the VRGB package.
+# SPDX-License-Identifier: MIT
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: VRGB Keyboard Lighting\n"
+"Report-Msgid-Bugs-To: https://github.com/vrgb-dev/vrgb/issues\n"
+"POT-Creation-Date: 2026-09-03 22:05+0300\n"
+"PO-Revision-Date: 2026-09-03 22:10+0300\n"
+"Last-Translator: vrgb-dev\n"
+"Language-Team: Ukrainian\n"
+"Language: uk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : "
+"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+
+#: package/contents/ui/main.qml:51 package/contents/ui/main.qml:187
+msgid "Keyboard Lighting"
+msgstr "Колір"
+
+#: package/contents/ui/main.qml:52
+msgid "Reading configuration…"
+msgstr "Читання налаштувань…"
+
+#: package/contents/ui/main.qml:53
+msgid "#%1 at %2%"
+msgstr "#%1, яскравість %2%"
+
+#: package/contents/ui/main.qml:104
+msgid "vrgb exited with code %1"
+msgstr "vrgb завершив роботу з кодом %1"
+
+#: package/contents/ui/main.qml:256
+msgid "Brightness"
+msgstr "Яскравість"
From f282e916a9aebf4bb342525fbd5009acfd027963 Mon Sep 17 00:00:00 2001
From: Bartolomiv
Date: Thu, 3 Sep 2026 22:46:22 +0300
Subject: [PATCH 3/8] now brighness binds to KeyboardBrightnessControl from
org.kde.plasma.private.brightnesscontrolplugin so it can be controlled from
shortcut, vrgb brightness is now always 100%
---
plasmoid/package/contents/ui/main.qml | 90 +++++++++++++++++++--------
1 file changed, 63 insertions(+), 27 deletions(-)
diff --git a/plasmoid/package/contents/ui/main.qml b/plasmoid/package/contents/ui/main.qml
index c3d11dc..f066150 100644
--- a/plasmoid/package/contents/ui/main.qml
+++ b/plasmoid/package/contents/ui/main.qml
@@ -2,8 +2,14 @@
* VRGB - Plasma 6 applet for ASUS Vivobook HID LampArray keyboards.
*
* State lives in ~/.config/vrgb/config.json, which the applet reads directly.
- * Writes always go through the `vrgb` CLI so the config file has a single
- * owner and the applet never touches hidraw itself.
+ * Colour writes always go through the `vrgb` CLI so the config file has a
+ * single owner and the applet never touches hidraw itself.
+ *
+ * Brightness is deliberately NOT vrgb's own intensity channel. The keyboard
+ * has two controls that multiply together: the asus::kbd_backlight level the
+ * Fn keys and Plasma drive, and vrgb's LampArray intensity. Driving the latter
+ * left the widget out of step with the Fn keys, so the slider here controls
+ * the Plasma backlight and vrgb's intensity stays pinned wide open.
*
* SPDX-License-Identifier: MIT
*/
@@ -13,6 +19,7 @@ import QtQuick.Layouts
import org.kde.plasma.plasmoid
import org.kde.plasma.components as PlasmaComponents
import org.kde.plasma.plasma5support as P5Support
+import org.kde.plasma.private.brightnesscontrolplugin
import org.kde.kirigami as Kirigami
PlasmoidItem {
@@ -22,11 +29,15 @@ PlasmoidItem {
// always on plasmashell's PATH.
property string vrgbBin: "vrgb"
property color currentColor: "#00aa55"
- property int brightness: 100
property string errorText: ""
// Suppresses hardware writes while the UI is being seeded from config.json.
property bool loading: true
+ // Brightness comes from Plasma so the Fn keys and the widget stay in step.
+ readonly property int backlight: kbdBacklight.brightness
+ readonly property int backlightMax: Math.max(1, kbdBacklight.brightnessMax)
+ readonly property real litFraction: backlight / backlightMax
+
readonly property var presets: [
"#ff0000", "#ff7f00", "#ffd400", "#7fff00", "#00ff5e", "#00e5ff",
"#0080ff", "#2b3cff", "#8b00ff", "#ff00c8", "#ff4d6d", "#ffffff"
@@ -40,9 +51,9 @@ PlasmoidItem {
}
// The colour as it should actually appear on the keys.
- readonly property color litColor: Qt.rgba(currentColor.r * brightness / 100,
- currentColor.g * brightness / 100,
- currentColor.b * brightness / 100, 1)
+ readonly property color litColor: Qt.rgba(currentColor.r * litFraction,
+ currentColor.g * litFraction,
+ currentColor.b * litFraction, 1)
readonly property string resolveCmd: "command -v vrgb 2>/dev/null || echo /usr/local/bin/vrgb"
readonly property string readCfgCmd: "cat \"$HOME/.config/vrgb/config.json\" 2>/dev/null"
@@ -50,7 +61,8 @@ PlasmoidItem {
Plasmoid.icon: "input-keyboard"
toolTipMainText: i18n("Keyboard Lighting")
toolTipSubText: loading ? i18n("Reading configuration…")
- : i18n("#%1 at %2%", hexColor.toUpperCase(), brightness)
+ : i18n("#%1 at %2%", hexColor.toUpperCase(),
+ Math.round(litFraction * 100))
preferredRepresentation: compactRepresentation
@@ -59,9 +71,10 @@ PlasmoidItem {
return;
}
applyTimer.stop();
- // hexColor is generated locally and brightness is an int, so the
- // command line is safe to build by concatenation.
- executable.run(vrgbBin + " set " + hexColor + " " + brightness);
+ // hexColor is generated locally, so the command line is safe to build
+ // by concatenation. The trailing 100 pins vrgb's intensity wide open --
+ // the Plasma backlight level is this applet's brightness control.
+ executable.run(vrgbBin + " set " + hexColor + " 100");
}
function scheduleApply() {
@@ -79,16 +92,24 @@ PlasmoidItem {
cfg = null;
}
}
+ var percent = 100;
if (cfg) {
if (typeof cfg.color === "string" && /^[0-9a-fA-F]{6}$/.test(cfg.color)) {
currentColor = "#" + cfg.color;
}
var p = parseInt(cfg.percent, 10);
if (!isNaN(p)) {
- brightness = Math.max(0, Math.min(100, p));
+ percent = p;
}
}
loading = false;
+
+ // Normalise once if the CLI (or an older build of this applet) left
+ // vrgb's intensity somewhere other than wide open -- otherwise the
+ // backlight slider could never reach full brightness.
+ if (percent !== 100) {
+ applyNow();
+ }
}
function handleResult(source, code, out, err) {
@@ -113,6 +134,13 @@ PlasmoidItem {
onTriggered: root.applyNow()
}
+ KeyboardBrightnessControl {
+ id: kbdBacklight
+ // The popup shows the level itself; a second OSD on every drag step
+ // would just be noise.
+ isSilent: true
+ }
+
P5Support.DataSource {
id: executable
@@ -146,9 +174,11 @@ PlasmoidItem {
anchors.centerIn: parent
width: Math.min(parent.width, parent.height)
height: width
- source: "input-keyboard"
+ // The symbolic variant keeps its key detail when masked; plain
+ // input-keyboard flattens to a solid block.
+ source: "input-keyboard-symbolic"
isMask: true
- color: root.brightness > 0 ? root.currentColor : Kirigami.Theme.disabledTextColor
+ color: root.backlight > 0 ? root.currentColor : Kirigami.Theme.disabledTextColor
}
}
@@ -262,24 +292,36 @@ PlasmoidItem {
PlasmaComponents.Label {
opacity: 0.75
- text: root.brightness + "%"
+ text: root.backlight + " / " + root.backlightMax
}
}
PlasmaComponents.Slider {
- id: brightnessSlider
+ id: backlightSlider
Layout.fillWidth: true
+ enabled: kbdBacklight.isBrightnessAvailable
from: 0
- to: 100
+ to: root.backlightMax
stepSize: 1
+ snapMode: PlasmaComponents.Slider.SnapAlways
- Component.onCompleted: value = root.brightness
+ // Straight to Plasma: this is a D-Bus property rather than a
+ // subprocess, so it needs no throttling.
+ onMoved: kbdBacklight.brightness = Math.round(value)
+ }
- onMoved: {
- root.brightness = Math.round(value);
- root.scheduleApply();
- }
+ // Follows Plasma -- Fn keys, the brightness applet, anything else --
+ // whenever the user is not dragging. Depending on backlightMax as
+ // well as backlight matters: PowerDevil publishes the maximum after
+ // the current value on startup, and without that dependency the
+ // slider can stay clamped against a stale range.
+ Binding {
+ target: backlightSlider
+ property: "value"
+ value: Math.min(root.backlight, root.backlightMax)
+ when: !backlightSlider.pressed
+ restoreMode: Binding.RestoreNone
}
Kirigami.InlineMessage {
@@ -301,12 +343,6 @@ PlasmoidItem {
function onCurrentColorChanged() {
picker.setColor(root.currentColor);
}
-
- function onBrightnessChanged() {
- if (!brightnessSlider.pressed) {
- brightnessSlider.value = root.brightness;
- }
- }
}
}
}
From 6f7c0c0e15587c99ec3f917d65158e0982573eb9 Mon Sep 17 00:00:00 2001
From: Bartolomiv
Date: Thu, 3 Sep 2026 23:10:38 +0300
Subject: [PATCH 4/8] brightness moved to vrgb profiles, basically widget is
now a profile switcher for vrgb
---
plasmoid/extract-messages.sh | 6 +-
plasmoid/package/contents/config/main.xml | 13 +
.../plasma_applet_org.vrgb.keyboard.mo | Bin 840 -> 1139 bytes
plasmoid/package/contents/ui/main.qml | 548 ++++++++++++++----
.../po/plasma_applet_org.vrgb.keyboard.pot | 36 +-
plasmoid/po/uk.po | 36 +-
6 files changed, 501 insertions(+), 138 deletions(-)
create mode 100644 plasmoid/package/contents/config/main.xml
diff --git a/plasmoid/extract-messages.sh b/plasmoid/extract-messages.sh
index 0a9a01d..f70595a 100755
--- a/plasmoid/extract-messages.sh
+++ b/plasmoid/extract-messages.sh
@@ -11,6 +11,10 @@ POT="$BASE/po/$DOMAIN.pot"
mkdir -p "$BASE/po"
+# Run from the package root so the "#:" source references in the .pot stay
+# repo-relative instead of baking in whoever's home directory built it.
+cd "$BASE"
+
# -C -kde is the KDE extraction mode; it understands the i18n* call family and
# parses QML well enough since the call syntax matches C++.
xgettext --from-code=UTF-8 -C -kde \
@@ -18,7 +22,7 @@ xgettext --from-code=UTF-8 -C -kde \
--package-name="VRGB Keyboard Lighting" \
--msgid-bugs-address="https://github.com/vrgb-dev/vrgb/issues" \
-o "$POT" \
- "$BASE"/package/contents/ui/*.qml
+ package/contents/ui/*.qml
echo "Wrote ${POT#"$BASE"/}"
diff --git a/plasmoid/package/contents/config/main.xml b/plasmoid/package/contents/config/main.xml
new file mode 100644
index 0000000..473fd12
--- /dev/null
+++ b/plasmoid/package/contents/config/main.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ false
+
+
+
diff --git a/plasmoid/package/contents/locale/uk/LC_MESSAGES/plasma_applet_org.vrgb.keyboard.mo b/plasmoid/package/contents/locale/uk/LC_MESSAGES/plasma_applet_org.vrgb.keyboard.mo
index 34d894521a1665a9aa6ae4a2e8a024b182086a12..8f9c93e0df4d2c76216e07d9af33578509e65290 100644
GIT binary patch
delta 565
zcmZ{f&nv@m7{{N@Fy=5n5~04*N|Hj#MIr}9h@;b&eateOW)Z0|%0-IVNGVq*+iV%D
zxykYQl%rgnaP$v&KeIS_>ivCvp3nRF@%=oNec8jWHMSTbs-R|Q3#x`*APK`lR0+0%
z4zLrnf&In%0k91DC@6iCpwyY5%v=T=z)ev4qQ(3Sys}W3k~p-$m@Oe{0yjWe7z69T
zOV9~siuaFTBXV8jC)kd>6w{@y3+x34L5wE5c#s523qK)e*+JM}2rItwit?Yd@PZ1Q
zNd=f;p%bGklSsn1Op3RpQ2%Neu;*IUe994uU1!SMEO)m(U{tp3z$K-F)ohR
z@1Y5`{@XP5(`@+7Wku7|O>j!b^^T5+f6Q4F?&^KcNo)F7a1|VPqVi_Rz0b*_-t^r~YSOLUbK)e!4ZvoPRK)fG_C4l%Mlzs)I
zS28j%FaxnHkiC|Pfk6aFp9az({SSb&29SOYq>X{J2r~l%P-#5_3y=e3g8?g)hEX6#
zfdEhs1B0@vp+aJbf~t`!gHusvdPYfJYH=}xcWPx)eqvFIf)7Y2GcTPXC^az!NGl}g
z=cQ$)mlh?KWaj5RYFNflR+OHkkXn&hlA5AWo>`IsRFSfI4dXe+$qSi9IcHticVYL%
QX%`zNv#@AQwr4Q`0OT7v9RL6T
diff --git a/plasmoid/package/contents/ui/main.qml b/plasmoid/package/contents/ui/main.qml
index f066150..bea095f 100644
--- a/plasmoid/package/contents/ui/main.qml
+++ b/plasmoid/package/contents/ui/main.qml
@@ -1,15 +1,17 @@
/*
* VRGB - Plasma 6 applet for ASUS Vivobook HID LampArray keyboards.
*
- * State lives in ~/.config/vrgb/config.json, which the applet reads directly.
- * Colour writes always go through the `vrgb` CLI so the config file has a
- * single owner and the applet never touches hidraw itself.
+ * The swatch grid is backed by vrgb profiles rather than hardcoded colours:
+ * one click loads a profile, a double click opens it in the editor, and "+"
+ * creates a new one. State is read straight out of ~/.config/vrgb/config.json;
+ * every write goes through the `vrgb` CLI so the config file has a single
+ * owner and the applet never touches hidraw itself.
*
- * Brightness is deliberately NOT vrgb's own intensity channel. The keyboard
- * has two controls that multiply together: the asus::kbd_backlight level the
- * Fn keys and Plasma drive, and vrgb's LampArray intensity. Driving the latter
- * left the widget out of step with the Fn keys, so the slider here controls
- * the Plasma backlight and vrgb's intensity stays pinned wide open.
+ * Two different brightnesses are in play, and they multiply:
+ * - asus::kbd_backlight (0..3), driven by the Fn keys and Plasma. The slider
+ * on the grid page is bound to it, so the widget and the Fn keys agree.
+ * - vrgb's own LampArray intensity (0..100), stored per profile. That one
+ * lives in the editor, since it is part of what a profile *is*.
*
* SPDX-License-Identifier: MIT
*/
@@ -29,31 +31,44 @@ PlasmoidItem {
// always on plasmashell's PATH.
property string vrgbBin: "vrgb"
property color currentColor: "#00aa55"
+ property int currentPercent: 100
property string errorText: ""
// Suppresses hardware writes while the UI is being seeded from config.json.
property bool loading: true
+ // [{ name, color, percent }], sorted by name.
+ property var profiles: []
+
+ // "grid" or "editor"
+ property string page: "grid"
+ property string editorName: ""
+ property bool editorNew: false
+ property color editorColor: "#ffffff"
+ property int editorPercent: 100
+ // Shown in the header instead of the hex while a swatch is hovered.
+ property string hoveredName: ""
+
// Brightness comes from Plasma so the Fn keys and the widget stay in step.
readonly property int backlight: kbdBacklight.brightness
readonly property int backlightMax: Math.max(1, kbdBacklight.brightnessMax)
readonly property real litFraction: backlight / backlightMax
- readonly property var presets: [
- "#ff0000", "#ff7f00", "#ffd400", "#7fff00", "#00ff5e", "#00e5ff",
- "#0080ff", "#2b3cff", "#8b00ff", "#ff00c8", "#ff4d6d", "#ffffff"
+ readonly property var defaultPalette: [
+ { name: "Red", color: "ff0000" }, { name: "Orange", color: "ff7f00" },
+ { name: "Amber", color: "ffd400" }, { name: "Lime", color: "7fff00" },
+ { name: "Green", color: "00ff5e" }, { name: "Cyan", color: "00e5ff" },
+ { name: "Azure", color: "0080ff" }, { name: "Blue", color: "2b3cff" },
+ { name: "Violet", color: "8b00ff" }, { name: "Magenta", color: "ff00c8" },
+ { name: "Rose", color: "ff4d6d" }, { name: "White", color: "ffffff" }
]
- readonly property string hexColor: {
- function ch(v) {
- return Math.round(Math.max(0, Math.min(1, v)) * 255).toString(16).padStart(2, "0");
- }
- return ch(currentColor.r) + ch(currentColor.g) + ch(currentColor.b);
- }
+ readonly property string hexColor: toHex(currentColor)
- // The colour as it should actually appear on the keys.
- readonly property color litColor: Qt.rgba(currentColor.r * litFraction,
- currentColor.g * litFraction,
- currentColor.b * litFraction, 1)
+ // What the keys should actually look like: colour x vrgb intensity x backlight.
+ readonly property color litColor: {
+ var f = litFraction * currentPercent / 100;
+ return Qt.rgba(currentColor.r * f, currentColor.g * f, currentColor.b * f, 1);
+ }
readonly property string resolveCmd: "command -v vrgb 2>/dev/null || echo /usr/local/bin/vrgb"
readonly property string readCfgCmd: "cat \"$HOME/.config/vrgb/config.json\" 2>/dev/null"
@@ -66,23 +81,106 @@ PlasmoidItem {
preferredRepresentation: compactRepresentation
- function applyNow() {
+ function toHex(c) {
+ function ch(v) {
+ return Math.round(Math.max(0, Math.min(1, v)) * 255).toString(16).padStart(2, "0");
+ }
+ return ch(c.r) + ch(c.g) + ch(c.b);
+ }
+
+ // Profile names reach a shell, so quote them properly rather than trusting
+ // the field validator alone.
+ function shellQuote(s) {
+ return "'" + String(s).replace(/'/g, "'\\''") + "'";
+ }
+
+ function nameIsValid(name) {
+ var n = String(name).trim();
+ return n.length > 0 && n.length <= 32 && /^[A-Za-z0-9 _-]+$/.test(n);
+ }
+
+ function profileByName(name) {
+ for (var i = 0; i < profiles.length; ++i) {
+ if (profiles[i].name === name) {
+ return profiles[i];
+ }
+ }
+ return null;
+ }
+
+ // --- hardware / CLI ---------------------------------------------------
+
+ // Live preview while the editor is open; snapshotted by "Save".
+ function applyPreview() {
if (loading) {
return;
}
- applyTimer.stop();
- // hexColor is generated locally, so the command line is safe to build
- // by concatenation. The trailing 100 pins vrgb's intensity wide open --
- // the Plasma backlight level is this applet's brightness control.
- executable.run(vrgbBin + " set " + hexColor + " 100");
+ previewTimer.stop();
+ executable.run(vrgbBin + " set " + toHex(editorColor) + " " + editorPercent);
}
- function scheduleApply() {
+ function schedulePreview() {
if (!loading) {
- applyTimer.restart();
+ previewTimer.restart();
}
}
+ function loadProfile(name) {
+ executable.run(vrgbBin + " profile load " + shellQuote(name));
+ }
+
+ function saveProfile(name) {
+ // `profile save` snapshots the current state, so set it first.
+ executable.run(vrgbBin + " set " + toHex(editorColor) + " " + editorPercent
+ + " && " + vrgbBin + " profile save " + shellQuote(name));
+ }
+
+ function deleteProfile(name) {
+ executable.run(vrgbBin + " profile delete " + shellQuote(name));
+ }
+
+ function seedDefaults() {
+ var parts = [];
+ for (var i = 0; i < defaultPalette.length; ++i) {
+ var p = defaultPalette[i];
+ parts.push(vrgbBin + " set " + p.color + " 100");
+ parts.push(vrgbBin + " profile save " + shellQuote(p.name));
+ }
+ // Put the keys back where they were rather than leaving them on the
+ // last seeded colour.
+ parts.push(vrgbBin + " set " + hexColor + " " + currentPercent);
+ executable.run(parts.join(" && "));
+ }
+
+ // --- editor -----------------------------------------------------------
+
+ function openEditor(name) {
+ var p = profileByName(name);
+ if (!p) {
+ return;
+ }
+ editorNew = false;
+ editorName = p.name;
+ editorColor = "#" + p.color;
+ editorPercent = p.percent;
+ page = "editor";
+ }
+
+ function openNewEditor() {
+ editorNew = true;
+ editorName = "";
+ editorColor = currentColor;
+ editorPercent = currentPercent;
+ page = "editor";
+ }
+
+ function closeEditor() {
+ previewTimer.stop();
+ page = "grid";
+ }
+
+ // --- config -----------------------------------------------------------
+
function loadConfig(text) {
var cfg = null;
if (text.length > 0) {
@@ -92,23 +190,41 @@ PlasmoidItem {
cfg = null;
}
}
- var percent = 100;
+
+ var list = [];
if (cfg) {
if (typeof cfg.color === "string" && /^[0-9a-fA-F]{6}$/.test(cfg.color)) {
currentColor = "#" + cfg.color;
}
- var p = parseInt(cfg.percent, 10);
- if (!isNaN(p)) {
- percent = p;
+ var pc = parseInt(cfg.percent, 10);
+ if (!isNaN(pc)) {
+ currentPercent = Math.max(0, Math.min(100, pc));
+ }
+ if (cfg.profiles && typeof cfg.profiles === "object") {
+ for (var name in cfg.profiles) {
+ var p = cfg.profiles[name];
+ if (!p || !/^[0-9a-fA-F]{6}$/.test(String(p.color))) {
+ continue;
+ }
+ var pp = parseInt(p.percent, 10);
+ list.push({
+ name: name,
+ color: String(p.color),
+ percent: isNaN(pp) ? 100 : Math.max(0, Math.min(100, pp))
+ });
+ }
}
}
+ list.sort(function (a, b) { return a.name.localeCompare(b.name); });
+ profiles = list;
loading = false;
- // Normalise once if the CLI (or an older build of this applet) left
- // vrgb's intensity somewhere other than wide open -- otherwise the
- // backlight slider could never reach full brightness.
- if (percent !== 100) {
- applyNow();
+ // First run: turn the old hardcoded palette into real profiles so the
+ // grid is not empty. Guarded by applet config, otherwise deleting every
+ // profile would resurrect them on the next start.
+ if (list.length === 0 && !Plasmoid.configuration.seededDefaults) {
+ Plasmoid.configuration.seededDefaults = true;
+ seedDefaults();
}
}
@@ -118,20 +234,42 @@ PlasmoidItem {
vrgbBin = out.split("\n")[0];
}
executable.run(readCfgCmd);
- } else if (source === readCfgCmd) {
+ return;
+ }
+ if (source === readCfgCmd) {
loadConfig(out);
+ return;
+ }
+ // Any other command was a write.
+ if (code === 0) {
+ errorText = "";
+ executable.run(readCfgCmd);
} else {
- errorText = (code === 0) ? ""
- : (err.length > 0 ? err : i18n("vrgb exited with code %1", code));
+ errorText = err.length > 0 ? err : i18n("vrgb exited with code %1", code);
}
}
Timer {
- id: applyTimer
+ id: previewTimer
// vrgb returns in ~25 ms; 60 ms keeps a drag smooth without queueing up
// one process per mouse move.
interval: 60
- onTriggered: root.applyNow()
+ onTriggered: root.applyPreview()
+ }
+
+ Timer {
+ id: clickTimer
+ property string pendingName: ""
+ // Must be at least the system double-click interval, or the single-click
+ // load would fire before the second click is recognised.
+ interval: (Qt.styleHints && Qt.styleHints.mouseDoubleClickInterval)
+ ? Qt.styleHints.mouseDoubleClickInterval : 400
+ onTriggered: {
+ if (pendingName.length > 0) {
+ root.loadProfile(pendingName);
+ pendingName = "";
+ }
+ }
}
KeyboardBrightnessControl {
@@ -184,20 +322,30 @@ PlasmoidItem {
fullRepresentation: Item {
Layout.minimumWidth: Kirigami.Units.gridUnit * 15
- Layout.minimumHeight: Kirigami.Units.gridUnit * 21
+ Layout.minimumHeight: Kirigami.Units.gridUnit * 19
Layout.preferredWidth: Kirigami.Units.gridUnit * 17
- Layout.preferredHeight: Kirigami.Units.gridUnit * 23
+ Layout.preferredHeight: Kirigami.Units.gridUnit * 22
ColumnLayout {
anchors.fill: parent
anchors.margins: Kirigami.Units.largeSpacing
spacing: Kirigami.Units.smallSpacing
+ // --- header ---------------------------------------------------
RowLayout {
Layout.fillWidth: true
spacing: Kirigami.Units.largeSpacing
+ PlasmaComponents.ToolButton {
+ visible: root.page === "editor"
+ icon.name: "draw-arrow-back"
+ display: PlasmaComponents.ToolButton.IconOnly
+ text: i18n("Back")
+ onClicked: root.closeEditor()
+ }
+
Rectangle {
+ visible: root.page === "grid"
Layout.preferredWidth: Kirigami.Units.gridUnit * 2.5
Layout.preferredHeight: Kirigami.Units.gridUnit * 2.5
radius: 4
@@ -214,15 +362,21 @@ PlasmoidItem {
Layout.fillWidth: true
level: 4
elide: Text.ElideRight
- text: i18n("Keyboard Lighting")
+ text: root.page === "editor"
+ ? (root.editorNew ? i18n("New profile") : root.editorName)
+ : i18n("Keyboard Lighting")
}
PlasmaComponents.Label {
Layout.fillWidth: true
+ visible: root.page === "grid"
elide: Text.ElideRight
- font.family: "monospace"
+ font.family: root.hoveredName.length > 0
+ ? Kirigami.Theme.defaultFont.family : "monospace"
opacity: 0.75
- text: "#" + root.hexColor.toUpperCase()
+ text: root.hoveredName.length > 0
+ ? root.hoveredName
+ : "#" + root.hexColor.toUpperCase()
}
}
}
@@ -233,95 +387,237 @@ PlasmoidItem {
Layout.bottomMargin: Kirigami.Units.smallSpacing
}
- GridLayout {
+ // --- pages ----------------------------------------------------
+ StackLayout {
Layout.fillWidth: true
- columns: 6
- columnSpacing: Kirigami.Units.smallSpacing
- rowSpacing: Kirigami.Units.smallSpacing
-
- Repeater {
- model: root.presets
+ Layout.fillHeight: true
+ currentIndex: root.page === "editor" ? 1 : 0
- delegate: Rectangle {
- required property string modelData
+ // page 0: profile grid
+ ColumnLayout {
+ spacing: Kirigami.Units.smallSpacing
+ GridLayout {
Layout.fillWidth: true
- Layout.preferredHeight: Kirigami.Units.gridUnit * 1.3
- radius: 3
- color: modelData
- border.width: 1
- border.color: Qt.rgba(0, 0, 0, 0.3)
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- root.currentColor = parent.modelData;
- picker.setColor(root.currentColor);
- root.applyNow();
+ columns: 6
+ columnSpacing: Kirigami.Units.smallSpacing
+ rowSpacing: Kirigami.Units.smallSpacing
+
+ Repeater {
+ model: root.profiles
+
+ delegate: Rectangle {
+ id: swatch
+
+ required property var modelData
+ // Marks whichever profile matches the live colour.
+ // Colours need colorEqual; === compares wrappers.
+ readonly property bool isCurrent:
+ Qt.colorEqual(root.currentColor, "#" + modelData.color)
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: Kirigami.Units.gridUnit * 1.3
+ radius: 3
+ color: "#" + modelData.color
+ border.width: isCurrent ? 2 : 1
+ border.color: isCurrent ? Kirigami.Theme.highlightColor
+ : Qt.rgba(0, 0, 0, 0.3)
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: root.hoveredName = swatch.modelData.name
+ onExited: {
+ if (root.hoveredName === swatch.modelData.name) {
+ root.hoveredName = "";
+ }
+ }
+ // A double click also emits two clicks, so
+ // defer the load and cancel it if a second
+ // click arrives.
+ onClicked: {
+ clickTimer.pendingName = swatch.modelData.name;
+ clickTimer.restart();
+ }
+ onDoubleClicked: {
+ clickTimer.stop();
+ clickTimer.pendingName = "";
+ root.openEditor(swatch.modelData.name);
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.preferredHeight: Kirigami.Units.gridUnit * 1.3
+ radius: 3
+ color: "transparent"
+ border.width: 1
+ border.color: Kirigami.Theme.disabledTextColor
+
+ Kirigami.Icon {
+ anchors.centerIn: parent
+ width: Kirigami.Units.iconSizes.small
+ height: width
+ source: "list-add"
+ isMask: true
+ color: Kirigami.Theme.textColor
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: root.hoveredName = i18n("Add profile")
+ onExited: root.hoveredName = ""
+ onClicked: root.openNewEditor()
}
}
}
- }
- }
- ColorPicker {
- id: picker
+ Item {
+ Layout.fillHeight: true
+ }
- Layout.fillWidth: true
- Layout.topMargin: Kirigami.Units.smallSpacing
+ RowLayout {
+ Layout.fillWidth: true
- Component.onCompleted: setColor(root.currentColor)
+ PlasmaComponents.Label {
+ text: i18n("Brightness")
+ }
- onPicked: c => {
- root.currentColor = c;
- root.scheduleApply();
- }
- }
+ Item {
+ Layout.fillWidth: true
+ }
- RowLayout {
- Layout.fillWidth: true
- Layout.topMargin: Kirigami.Units.smallSpacing
+ PlasmaComponents.Label {
+ opacity: 0.75
+ text: root.backlight + " / " + root.backlightMax
+ }
+ }
- PlasmaComponents.Label {
- text: i18n("Brightness")
- }
+ PlasmaComponents.Slider {
+ id: backlightSlider
- Item {
- Layout.fillWidth: true
- }
+ Layout.fillWidth: true
+ enabled: kbdBacklight.isBrightnessAvailable
+ from: 0
+ to: root.backlightMax
+ stepSize: 1
+ snapMode: PlasmaComponents.Slider.SnapAlways
+
+ // Straight to Plasma: this is a D-Bus property rather
+ // than a subprocess, so it needs no throttling.
+ onMoved: kbdBacklight.brightness = Math.round(value)
+ }
- PlasmaComponents.Label {
- opacity: 0.75
- text: root.backlight + " / " + root.backlightMax
+ // Follows Plasma -- Fn keys, the brightness applet, anything
+ // else -- whenever the user is not dragging. Depending on
+ // backlightMax as well as backlight matters: PowerDevil
+ // publishes the maximum after the current value on startup,
+ // and without that dependency the slider can stay clamped
+ // against a stale range.
+ Binding {
+ target: backlightSlider
+ property: "value"
+ value: Math.min(root.backlight, root.backlightMax)
+ when: !backlightSlider.pressed
+ restoreMode: Binding.RestoreNone
+ }
}
- }
- PlasmaComponents.Slider {
- id: backlightSlider
+ // page 1: profile editor
+ ColumnLayout {
+ spacing: Kirigami.Units.smallSpacing
+
+ PlasmaComponents.TextField {
+ id: nameField
- Layout.fillWidth: true
- enabled: kbdBacklight.isBrightnessAvailable
- from: 0
- to: root.backlightMax
- stepSize: 1
- snapMode: PlasmaComponents.Slider.SnapAlways
-
- // Straight to Plasma: this is a D-Bus property rather than a
- // subprocess, so it needs no throttling.
- onMoved: kbdBacklight.brightness = Math.round(value)
- }
+ Layout.fillWidth: true
+ visible: root.editorNew
+ placeholderText: i18n("Profile name")
+ maximumLength: 32
+ // Seeded by onPageChanged, kept one-way so typing does
+ // not fight a binding.
+ onTextChanged: root.editorName = text
+ }
+
+ ColorPicker {
+ id: picker
+
+ Layout.fillWidth: true
+
+ onPicked: c => {
+ root.editorColor = c;
+ root.schedulePreview();
+ }
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ Layout.topMargin: Kirigami.Units.smallSpacing
+
+ PlasmaComponents.Label {
+ text: i18n("Brightness")
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ PlasmaComponents.Label {
+ opacity: 0.75
+ text: root.editorPercent + "%"
+ }
+ }
+
+ PlasmaComponents.Slider {
+ id: percentSlider
- // Follows Plasma -- Fn keys, the brightness applet, anything else --
- // whenever the user is not dragging. Depending on backlightMax as
- // well as backlight matters: PowerDevil publishes the maximum after
- // the current value on startup, and without that dependency the
- // slider can stay clamped against a stale range.
- Binding {
- target: backlightSlider
- property: "value"
- value: Math.min(root.backlight, root.backlightMax)
- when: !backlightSlider.pressed
- restoreMode: Binding.RestoreNone
+ Layout.fillWidth: true
+ from: 0
+ to: 100
+ stepSize: 1
+
+ onMoved: {
+ root.editorPercent = Math.round(value);
+ root.schedulePreview();
+ }
+ }
+
+ Item {
+ Layout.fillHeight: true
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Kirigami.Units.smallSpacing
+
+ PlasmaComponents.Button {
+ visible: !root.editorNew
+ icon.name: "edit-delete"
+ text: i18n("Delete")
+ onClicked: {
+ root.deleteProfile(root.editorName);
+ root.closeEditor();
+ }
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ PlasmaComponents.Button {
+ icon.name: "document-save"
+ text: i18n("Save")
+ enabled: root.nameIsValid(root.editorName)
+ onClicked: {
+ root.saveProfile(root.editorName.trim());
+ root.closeEditor();
+ }
+ }
+ }
+ }
}
Kirigami.InlineMessage {
@@ -330,18 +626,20 @@ PlasmoidItem {
text: root.errorText
visible: root.errorText.length > 0
}
-
- Item {
- Layout.fillHeight: true
- }
}
- // Keep the popup in sync when config.json loads after the popup was built.
+ // Seed the editor widgets whenever a profile is opened. They live in a
+ // StackLayout page that is built once, so this cannot be a
+ // Component.onCompleted.
Connections {
target: root
- function onCurrentColorChanged() {
- picker.setColor(root.currentColor);
+ function onPageChanged() {
+ if (root.page === "editor") {
+ picker.setColor(root.editorColor);
+ percentSlider.value = root.editorPercent;
+ nameField.text = root.editorName;
+ }
}
}
}
diff --git a/plasmoid/po/plasma_applet_org.vrgb.keyboard.pot b/plasmoid/po/plasma_applet_org.vrgb.keyboard.pot
index 4a775ac..c451254 100644
--- a/plasmoid/po/plasma_applet_org.vrgb.keyboard.pot
+++ b/plasmoid/po/plasma_applet_org.vrgb.keyboard.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VRGB Keyboard Lighting\n"
"Report-Msgid-Bugs-To: https://github.com/vrgb-dev/vrgb/issues\n"
-"POT-Creation-Date: 2026-09-03 22:05+0300\n"
+"POT-Creation-Date: 2026-09-03 22:58+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -17,22 +17,46 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: package/contents/ui/main.qml:51 package/contents/ui/main.qml:187
+#: package/contents/ui/main.qml:77 package/contents/ui/main.qml:367
msgid "Keyboard Lighting"
msgstr ""
-#: package/contents/ui/main.qml:52
+#: package/contents/ui/main.qml:78
msgid "Reading configuration…"
msgstr ""
-#: package/contents/ui/main.qml:53
+#: package/contents/ui/main.qml:79
msgid "#%1 at %2%"
msgstr ""
-#: package/contents/ui/main.qml:104
+#: package/contents/ui/main.qml:248
msgid "vrgb exited with code %1"
msgstr ""
-#: package/contents/ui/main.qml:256
+#: package/contents/ui/main.qml:343
+msgid "Back"
+msgstr ""
+
+#: package/contents/ui/main.qml:366
+msgid "New profile"
+msgstr ""
+
+#: package/contents/ui/main.qml:471
+msgid "Add profile"
+msgstr ""
+
+#: package/contents/ui/main.qml:486 package/contents/ui/main.qml:561
msgid "Brightness"
msgstr ""
+
+#: package/contents/ui/main.qml:538
+msgid "Profile name"
+msgstr ""
+
+#: package/contents/ui/main.qml:599
+msgid "Delete"
+msgstr ""
+
+#: package/contents/ui/main.qml:612
+msgid "Save"
+msgstr ""
diff --git a/plasmoid/po/uk.po b/plasmoid/po/uk.po
index 284349e..21080e0 100644
--- a/plasmoid/po/uk.po
+++ b/plasmoid/po/uk.po
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VRGB Keyboard Lighting\n"
"Report-Msgid-Bugs-To: https://github.com/vrgb-dev/vrgb/issues\n"
-"POT-Creation-Date: 2026-09-03 22:05+0300\n"
+"POT-Creation-Date: 2026-09-03 22:58+0300\n"
"PO-Revision-Date: 2026-09-03 22:10+0300\n"
"Last-Translator: vrgb-dev\n"
"Language-Team: Ukrainian\n"
@@ -17,22 +17,46 @@ msgstr ""
"Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : "
"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-#: package/contents/ui/main.qml:51 package/contents/ui/main.qml:187
+#: package/contents/ui/main.qml:77 package/contents/ui/main.qml:367
msgid "Keyboard Lighting"
msgstr "Колір"
-#: package/contents/ui/main.qml:52
+#: package/contents/ui/main.qml:78
msgid "Reading configuration…"
msgstr "Читання налаштувань…"
-#: package/contents/ui/main.qml:53
+#: package/contents/ui/main.qml:79
msgid "#%1 at %2%"
msgstr "#%1, яскравість %2%"
-#: package/contents/ui/main.qml:104
+#: package/contents/ui/main.qml:248
msgid "vrgb exited with code %1"
msgstr "vrgb завершив роботу з кодом %1"
-#: package/contents/ui/main.qml:256
+#: package/contents/ui/main.qml:343
+msgid "Back"
+msgstr "Назад"
+
+#: package/contents/ui/main.qml:366
+msgid "New profile"
+msgstr "Новий профіль"
+
+#: package/contents/ui/main.qml:471
+msgid "Add profile"
+msgstr "Додати профіль"
+
+#: package/contents/ui/main.qml:486 package/contents/ui/main.qml:561
msgid "Brightness"
msgstr "Яскравість"
+
+#: package/contents/ui/main.qml:538
+msgid "Profile name"
+msgstr "Назва профілю"
+
+#: package/contents/ui/main.qml:599
+msgid "Delete"
+msgstr "Вилучити"
+
+#: package/contents/ui/main.qml:612
+msgid "Save"
+msgstr "Зберегти"
From 8fa173f335104cf0800ab82b3577a059c543663b Mon Sep 17 00:00:00 2001
From: Bartolomiv
Date: Thu, 3 Sep 2026 23:20:12 +0300
Subject: [PATCH 5/8] fixed gap from colour selection grid to brighness slider
---
plasmoid/package/contents/ui/main.qml | 43 +++++++++++++++++++--------
1 file changed, 30 insertions(+), 13 deletions(-)
diff --git a/plasmoid/package/contents/ui/main.qml b/plasmoid/package/contents/ui/main.qml
index bea095f..1bb7875 100644
--- a/plasmoid/package/contents/ui/main.qml
+++ b/plasmoid/package/contents/ui/main.qml
@@ -321,14 +321,29 @@ PlasmoidItem {
}
fullRepresentation: Item {
+ id: fullRep
+
+ readonly property int pad: Kirigami.Units.largeSpacing
+ // Height follows the content rather than a fixed guess, so the grid
+ // page is not padded out to the (much taller) editor page. Anchoring
+ // the content left/right/top only -- never filling -- keeps
+ // content.implicitHeight independent of this item's own height, so the
+ // binding cannot become circular.
+ readonly property int contentHeight: content.implicitHeight + 2 * pad
+
Layout.minimumWidth: Kirigami.Units.gridUnit * 15
- Layout.minimumHeight: Kirigami.Units.gridUnit * 19
Layout.preferredWidth: Kirigami.Units.gridUnit * 17
- Layout.preferredHeight: Kirigami.Units.gridUnit * 22
+ Layout.minimumHeight: contentHeight
+ Layout.preferredHeight: contentHeight
+ Layout.maximumHeight: contentHeight
ColumnLayout {
- anchors.fill: parent
- anchors.margins: Kirigami.Units.largeSpacing
+ id: content
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.margins: fullRep.pad
spacing: Kirigami.Units.smallSpacing
// --- header ---------------------------------------------------
@@ -390,11 +405,17 @@ PlasmoidItem {
// --- pages ----------------------------------------------------
StackLayout {
Layout.fillWidth: true
- Layout.fillHeight: true
+ // A StackLayout's own implicit height is the tallest page, which
+ // is what left dead space under the grid. Report just the page
+ // actually on screen.
+ Layout.preferredHeight: root.page === "editor" ? editorPage.implicitHeight
+ : gridPage.implicitHeight
currentIndex: root.page === "editor" ? 1 : 0
// page 0: profile grid
ColumnLayout {
+ id: gridPage
+
spacing: Kirigami.Units.smallSpacing
GridLayout {
@@ -475,12 +496,9 @@ PlasmoidItem {
}
}
- Item {
- Layout.fillHeight: true
- }
-
RowLayout {
Layout.fillWidth: true
+ Layout.topMargin: Kirigami.Units.smallSpacing
PlasmaComponents.Label {
text: i18n("Brightness")
@@ -528,6 +546,8 @@ PlasmoidItem {
// page 1: profile editor
ColumnLayout {
+ id: editorPage
+
spacing: Kirigami.Units.smallSpacing
PlasmaComponents.TextField {
@@ -585,12 +605,9 @@ PlasmoidItem {
}
}
- Item {
- Layout.fillHeight: true
- }
-
RowLayout {
Layout.fillWidth: true
+ Layout.topMargin: Kirigami.Units.smallSpacing
spacing: Kirigami.Units.smallSpacing
PlasmaComponents.Button {
From d223c55ca7f7633d9352f0b2385f2a07bc4c7593 Mon Sep 17 00:00:00 2001
From: Bartolomiv
Date: Thu, 3 Sep 2026 23:25:37 +0300
Subject: [PATCH 6/8] Made naming a bit more right for my opinion
---
plasmoid/po/plasma_applet_org.vrgb.keyboard.pot | 2 +-
plasmoid/po/uk.po | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/plasmoid/po/plasma_applet_org.vrgb.keyboard.pot b/plasmoid/po/plasma_applet_org.vrgb.keyboard.pot
index c451254..3dbdfef 100644
--- a/plasmoid/po/plasma_applet_org.vrgb.keyboard.pot
+++ b/plasmoid/po/plasma_applet_org.vrgb.keyboard.pot
@@ -18,7 +18,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
#: package/contents/ui/main.qml:77 package/contents/ui/main.qml:367
-msgid "Keyboard Lighting"
+msgid "Colour"
msgstr ""
#: package/contents/ui/main.qml:78
diff --git a/plasmoid/po/uk.po b/plasmoid/po/uk.po
index 21080e0..00f5461 100644
--- a/plasmoid/po/uk.po
+++ b/plasmoid/po/uk.po
@@ -18,7 +18,7 @@ msgstr ""
"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
#: package/contents/ui/main.qml:77 package/contents/ui/main.qml:367
-msgid "Keyboard Lighting"
+msgid "Colour"
msgstr "Колір"
#: package/contents/ui/main.qml:78
From 790e0e099b097ca14dc9802773cc3e59116194a3 Mon Sep 17 00:00:00 2001
From: Bartolomiv
Date: Thu, 3 Sep 2026 23:34:27 +0300
Subject: [PATCH 7/8] uninstall.sh for widget
---
README.md | 10 +++++++
assets/plasma-widget-demo.png | Bin 0 -> 91319 bytes
plasmoid/uninstall.sh | 48 ++++++++++++++++++++++++++++++++++
3 files changed, 58 insertions(+)
create mode 100644 assets/plasma-widget-demo.png
create mode 100755 plasmoid/uninstall.sh
diff --git a/README.md b/README.md
index 30ed883..7ca046b 100644
--- a/README.md
+++ b/README.md
@@ -63,6 +63,10 @@ Current Stable Release: v0.3.5
+
+
+
+
## Features
@@ -77,6 +81,7 @@ Current Stable Release: v0.3.5
- Installer and uninstaller included
- Non-root daily usage via udev permissions
- Optional KDE autostart restore
+- Optional KDE widget
@@ -162,6 +167,11 @@ Clone the repository and run the installer.
After installation log out and log back in so group permissions apply.
+If want to install optional KDE widget
+
+ cd plasmoid
+ chmod +x install.sh
+ ./install.sh
**Note:**
Keyboard color persists on reboot, but may reset to firmware default after a full power cycle.
diff --git a/assets/plasma-widget-demo.png b/assets/plasma-widget-demo.png
new file mode 100644
index 0000000000000000000000000000000000000000..7d0923b975963831e3bdecf015bea50785de5b9f
GIT binary patch
literal 91319
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