mirror of
https://github.com/theupriser/steamify-cachyos.git
synced 2026-10-03 17:41:58 +02:00
refactor(ui): One QML file per screen; the HDMI boost's setup screens and backend commands removed
This commit is contained in:
1 parent
7bef834b16
commit
e699a3f8eb
29 files changed
+1050
-1832
No files matched your search
@@ -0,0 +1,265 @@
|
||||
// State and logic of the app: menu -> review -> password -> applying -> done,
|
||||
// and the BIOS update (check -> warning 1 -> warning 2 -> flash). The
|
||||
// screens only show this and call its functions.
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: app
|
||||
required property var backend
|
||||
required property var gamepad
|
||||
|
||||
property string screen: "menu" // menu review password applying done bios1 bios2
|
||||
property double remoteAt: 0
|
||||
property var want: ({})
|
||||
property string boot: "gamescope"
|
||||
property int sel: 0 // row index; rows.length = the Apply button
|
||||
property bool reapply: false
|
||||
property var plan: [] // [{id, action, isNew}]
|
||||
property var steps: ({}) // id -> "wait"|"run"|"ok"|"fail"
|
||||
property var failed: []
|
||||
property bool restartNeeded: false
|
||||
property string runError: ""
|
||||
property bool wrongPassword: false
|
||||
property bool typing: false // a text field has the keys
|
||||
property int doneCount: 0
|
||||
property string pending: "apply" // what the password is for: apply | bios
|
||||
property string sessionPassword: "" // kept between the BIOS steps only
|
||||
property var biosInfo: ({})
|
||||
property bool biosRun: false
|
||||
property bool biosChecking: false // the invisible check before the warnings
|
||||
property int biosFocus: 0 // warning 1: 0 = Cancel, 1 = continue
|
||||
property real holdProgress: 0 // warning 2: hold A/OK for 5 s
|
||||
property bool holding: false
|
||||
readonly property alias log: logModel
|
||||
|
||||
readonly property var status: backend.status || ({})
|
||||
readonly property var items: status.items || []
|
||||
readonly property var rows: items.filter(function (i) { return !i.parent || want[i.parent]; })
|
||||
readonly property var bios: status.bios || null
|
||||
|
||||
signal passwordRequested()
|
||||
signal passwordRejected()
|
||||
signal passwordSubmitRequested() // A/OK on the password screen
|
||||
|
||||
ListModel { id: logModel }
|
||||
|
||||
function nowOn(id) {
|
||||
for (var i = 0; i < items.length; i++) if (items[i].id === id) return items[i].on;
|
||||
return false;
|
||||
}
|
||||
function label(item) { return Texts.label(item.id, item.label); }
|
||||
function biosHint() {
|
||||
if (!bios) return "";
|
||||
if (!bios.newest) return "Now " + bios.current + ", newest unknown (offline?)";
|
||||
if (bios.current === bios.newest) return bios.current + " is up to date";
|
||||
return "Now " + bios.current + ", newest " + bios.newest + " · at your own risk";
|
||||
}
|
||||
function syncFromStatus() {
|
||||
reapply = false;
|
||||
var w = {};
|
||||
for (var i = 0; i < items.length; i++) w[items[i].id] = items[i].kind === "action" ? false : items[i].wanted;
|
||||
want = w;
|
||||
boot = nowOn("boot") ? "desktop" : "gamescope";
|
||||
if (sel > rows.length) sel = 0;
|
||||
}
|
||||
Connections { target: backend; function onStatusChanged() { if (app.screen === "menu" || app.screen === "done") app.syncFromStatus(); } }
|
||||
|
||||
// --- Menu ---
|
||||
function toggle(id) {
|
||||
var cur = items.find(function (i) { return i.id === id; });
|
||||
if (cur && cur.kind === "toggle" && cur.selectable === false) return;
|
||||
var w = Object.assign({}, want);
|
||||
w[id] = !w[id];
|
||||
// The same rules as the terminal menu (toggle_component).
|
||||
if (id === "gaming" && !w.gaming) w.single = false;
|
||||
if (id === "single" && w.single) w.gaming = true;
|
||||
if (id === "machine") w.poweroff = w.machine;
|
||||
if (id === "poweroff" && w.poweroff) w.machine = true;
|
||||
if (id === "machine" && !w.machine) w.kpin = false;
|
||||
if (id === "kpin" && w.kpin) w.machine = true;
|
||||
want = w;
|
||||
}
|
||||
function computePlan() {
|
||||
var p = [];
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var it = items[i];
|
||||
if (it.kind !== "toggle") continue;
|
||||
if (it.parent && !want[it.parent]) { if (it.on) p.push({ id: it.id, action: "off" }); continue; }
|
||||
if (want[it.id] && !it.on) p.push({ id: it.id, action: "on", isNew: !!it["new"] });
|
||||
else if (!want[it.id] && it.on) p.push({ id: it.id, action: "off" });
|
||||
else if (want[it.id] && (reapply || it.update)) p.push({ id: it.id, action: reapply ? "again" : "update" });
|
||||
}
|
||||
var bootNow = nowOn("boot") ? "desktop" : "gamescope";
|
||||
if (want.gaming && boot !== bootNow) p.push({ id: "boot", action: boot === "desktop" ? "desktop" : "gaming" });
|
||||
return p;
|
||||
}
|
||||
function goReview(again) { reapply = again; plan = computePlan(); screen = "review"; }
|
||||
function wantedIds() {
|
||||
var ids = [];
|
||||
for (var i = 0; i < items.length; i++)
|
||||
if (items[i].kind === "toggle" && want[items[i].id] && (!items[i].parent || want[items[i].parent])) ids.push(items[i].id);
|
||||
return ids;
|
||||
}
|
||||
|
||||
// --- Password and apply ---
|
||||
function resetRun() { steps = {}; failed = []; restartNeeded = false; runError = ""; doneCount = 0; logModel.clear(); }
|
||||
function startApply(password) {
|
||||
resetRun();
|
||||
var s = {};
|
||||
for (var i = 0; i < plan.length; i++) s[plan[i].id] = "wait";
|
||||
steps = s;
|
||||
screen = "applying";
|
||||
backend.apply(wantedIds(), want.gaming ? boot : "", reapply, password || "");
|
||||
}
|
||||
function askPassword(forWhat) {
|
||||
pending = forWhat;
|
||||
if (!backend.needsPassword()) return false;
|
||||
wrongPassword = false; screen = "password"; passwordRequested();
|
||||
return true;
|
||||
}
|
||||
function onApplyPressed() {
|
||||
if (plan.length === 0) return;
|
||||
if (!askPassword("apply")) startApply("");
|
||||
}
|
||||
function submitPassword(p) {
|
||||
if (!backend.checkPassword(p)) { wrongPassword = true; passwordRejected(); return; }
|
||||
if (pending === "bios") { sessionPassword = p; biosCheck(); }
|
||||
else startApply(p);
|
||||
}
|
||||
function passwordBack() { screen = pending === "bios" ? "menu" : "review"; }
|
||||
|
||||
// --- BIOS ---
|
||||
function startBios() {
|
||||
if (!bios || !bios.selectable) return;
|
||||
biosInfo = {}; sessionPassword = "";
|
||||
if (!askPassword("bios")) biosCheck();
|
||||
}
|
||||
function biosCheck() {
|
||||
// Invisible: stay on the menu (the BIOS row says "Checking…"),
|
||||
// then the first warning.
|
||||
failed = []; runError = ""; logModel.clear();
|
||||
plan = [{ id: "bios", action: "check" }];
|
||||
biosRun = true; biosChecking = true; screen = "menu";
|
||||
backend.biosPrepare(sessionPassword);
|
||||
}
|
||||
function biosCancel() { sessionPassword = ""; holdProgress = 0; holding = false; biosRun = false; syncFromStatus(); screen = "menu"; }
|
||||
function biosContinue() { holdProgress = 0; screen = "bios2"; }
|
||||
function biosFlash() {
|
||||
holdProgress = 0; holding = false;
|
||||
resetRun();
|
||||
plan = [{ id: "bios", action: "flash" }];
|
||||
steps = { bios: "wait" };
|
||||
biosRun = true; screen = "applying";
|
||||
backend.biosFlash(sessionPassword);
|
||||
}
|
||||
Timer {
|
||||
interval: 50; repeat: true; running: app.screen === "bios2" && app.holding
|
||||
onTriggered: { app.holdProgress = Math.min(1, app.holdProgress + 0.01); if (app.holdProgress >= 1) app.biosFlash(); }
|
||||
}
|
||||
|
||||
function backToMenu() { biosRun = false; syncFromStatus(); screen = "menu"; }
|
||||
|
||||
// --- Backend events ---
|
||||
function errorText(e) {
|
||||
return e === "wrong-password" ? "The password didn't work." : (e === "start" ? "Steamify couldn't start the changes." : (e ? "sudo isn't available." : ""));
|
||||
}
|
||||
Connections {
|
||||
target: app.backend
|
||||
function onEvent(ev) {
|
||||
var s = Object.assign({}, app.steps);
|
||||
if (ev.event === "start") { s[ev.id] = "run"; app.steps = s; }
|
||||
else if (ev.event === "done") { s[ev.id] = ev.ok ? "ok" : "fail"; app.steps = s; app.doneCount++; }
|
||||
else if (ev.event === "log") { logModel.append({ line: ev.line }); if (logModel.count > 400) logModel.remove(0); }
|
||||
else if (ev.event === "bios-ready") { app.biosChecking = false; app.biosInfo = ev; app.biosFocus = 0; app.holdProgress = 0; app.screen = "bios1"; }
|
||||
else if (ev.event === "finished" && app.biosChecking) {
|
||||
// The check found nothing to do or a problem: say so.
|
||||
app.biosChecking = false; app.sessionPassword = "";
|
||||
app.failed = ev.failed || []; app.restartNeeded = false;
|
||||
app.runError = ev.nothing && !ev.error ? "The BIOS is already the newest version." : app.errorText(ev.error);
|
||||
app.screen = "done";
|
||||
}
|
||||
else if (ev.event === "finished") {
|
||||
// A successful BIOS check ends with bios-ready instead, keeping
|
||||
// the password for the flash.
|
||||
app.sessionPassword = "";
|
||||
app.failed = ev.failed || []; app.restartNeeded = !!ev.restart;
|
||||
app.runError = app.errorText(ev.error);
|
||||
app.screen = "done";
|
||||
}
|
||||
}
|
||||
function onRemotePressed() { app.remoteAt = Date.now(); Input.type = "remote"; }
|
||||
}
|
||||
|
||||
// --- Input: one set of actions for controller, keyboard and remote ---
|
||||
function act(a) {
|
||||
if (screen === "menu") {
|
||||
if (biosChecking) return;
|
||||
if (a === "up") sel = Math.max(0, sel - 1);
|
||||
else if (a === "down") sel = Math.min(rows.length, sel + 1);
|
||||
else if (a === "accept") {
|
||||
if (sel === rows.length) { goReview(false); return; }
|
||||
var r = rows[sel];
|
||||
if (r.kind === "action" && r.id === "bios") startBios();
|
||||
else if (r.kind === "choice") boot = boot === "gamescope" ? "desktop" : "gamescope";
|
||||
else if (r.kind === "toggle") toggle(r.id);
|
||||
}
|
||||
else if ((a === "left" || a === "right") && sel < rows.length && rows[sel].kind === "choice")
|
||||
boot = a === "left" ? "gamescope" : "desktop";
|
||||
else if (a === "apply") goReview(false);
|
||||
else if (a === "reapply") goReview(true);
|
||||
else if (a === "back") Qt.quit();
|
||||
} else if (screen === "review") {
|
||||
if (a === "accept" || a === "apply") onApplyPressed();
|
||||
else if (a === "back") { reapply = false; screen = "menu"; }
|
||||
} else if (screen === "password") {
|
||||
if (a === "accept" || a === "apply") passwordSubmitRequested();
|
||||
else if (a === "back") passwordBack();
|
||||
} else if (screen === "bios1") {
|
||||
if (a === "left") biosFocus = 0;
|
||||
else if (a === "right") biosFocus = 1;
|
||||
else if (a === "accept") { if (biosFocus === 1) biosContinue(); else biosCancel(); }
|
||||
else if (a === "back") biosCancel();
|
||||
} else if (screen === "bios2") {
|
||||
if (a === "back") biosCancel();
|
||||
else if (a === "hold") holding = true;
|
||||
else if (a === "release") { holding = false; if (holdProgress < 1) holdProgress = 0; }
|
||||
} else if (screen === "done") {
|
||||
if (a === "accept" && restartNeeded) backend.restart();
|
||||
else if (a === "back" || a === "accept") backToMenu();
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: app.gamepad
|
||||
function onButton(b) {
|
||||
if (b === "a_up") { if (app.screen === "bios2") app.act("release"); return; }
|
||||
if (b.slice(-3) === "_up") return;
|
||||
Input.type = "controller";
|
||||
if (b === "a" && app.screen === "bios2") { app.act("hold"); return; }
|
||||
var map = { a: "accept", b: "back", x: "apply", y: "reapply", start: "apply", up: "up", down: "down", left: "left", right: "right" };
|
||||
if (map[b]) app.act(map[b]);
|
||||
}
|
||||
}
|
||||
function keyPressed(e) {
|
||||
// A text field handles its own keys, except Escape and Enter.
|
||||
if (typing && e.key !== Qt.Key_Escape && e.key !== Qt.Key_Return && e.key !== Qt.Key_Enter) return;
|
||||
var fromRemote = Date.now() - remoteAt < 400;
|
||||
if (screen === "bios2" && Input.type !== "keyboard" && (e.key === Qt.Key_Return || e.key === Qt.Key_Enter)) {
|
||||
if (!e.isAutoRepeat) act("hold"); e.accepted = true; return;
|
||||
}
|
||||
if (!fromRemote) Input.type = "keyboard";
|
||||
var k = e.key;
|
||||
if (k === Qt.Key_Up) act("up");
|
||||
else if (k === Qt.Key_Down) act("down");
|
||||
else if (k === Qt.Key_Left) act("left");
|
||||
else if (k === Qt.Key_Right) act("right");
|
||||
else if (k === Qt.Key_Space) act("accept");
|
||||
else if (k === Qt.Key_Return || k === Qt.Key_Enter) act(Input.type === "remote" || screen !== "menu" ? "accept" : "apply");
|
||||
else if (k === Qt.Key_Escape || k === Qt.Key_Back || k === Qt.Key_Backspace) { if (!e.isAutoRepeat) act("back"); }
|
||||
else if (k === Qt.Key_R) act("reapply");
|
||||
else return;
|
||||
e.accepted = true;
|
||||
}
|
||||
function keyReleased(e) {
|
||||
if (screen === "bios2" && !e.isAutoRepeat && (e.key === Qt.Key_Return || e.key === Qt.Key_Enter)) act("release");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user