shell: quick toggle types come from the chooser, not a second list
The hand-written allowlist in AndroidQuickPanel had drifted from it, so the phone rendered 12 of its 17 configured toggles and said nothing about the rest. Adds the missing fullScreenshot and waydroidTerminate, and a deploy guard so the phone chooser can't fall behind the base one again.
This commit is contained in:
parent
e4e659483b
commit
e6e3dd3b2a
8 changed files with 248 additions and 1 deletions
|
|
@ -433,6 +433,30 @@ if (( qmldir_broken )); then
|
|||
echo "manifest above, or remove its qmldir line." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The phone's quick-toggle chooser is a WHOLE-FILE override of the base one, so
|
||||
# a choice added to the base never reaches the phone unless it is added there
|
||||
# too. That drift is silent by construction: the panel derives what it can build
|
||||
# from the chooser, so a missing choice is not an error, it is a toggle that
|
||||
# quietly stops existing on one device. Base must be a subset of phone.
|
||||
#
|
||||
# This is the same class as the qmldir check above and it has already cost a
|
||||
# session: five configured toggles rendered nothing for a day because a second
|
||||
# enumeration had drifted from this one.
|
||||
chooser_rel="modules/ii/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml"
|
||||
base_chooser="$SRC/ii-base/$chooser_rel"
|
||||
phone_chooser="$SRC/ii-phone/$chooser_rel"
|
||||
if [[ -f "$base_chooser" && -f "$phone_chooser" ]]; then
|
||||
role_values() { grep -o 'roleValue: "[A-Za-z]*"' "$1" | sed 's/.*"\(.*\)"/\1/' | sort -u; }
|
||||
missing_on_phone="$(comm -23 <(role_values "$base_chooser") <(role_values "$phone_chooser") | tr '\n' ' ')"
|
||||
if [[ -n "${missing_on_phone// /}" ]]; then
|
||||
echo "BROKEN toggle chooser overlay: ii-phone is missing base choices: $missing_on_phone" >&2
|
||||
echo "The phone chooser overrides the base file wholesale — add the same" >&2
|
||||
echo "DelegateChoice blocks to ii-phone/$chooser_rel or those toggles will" >&2
|
||||
echo "silently not exist on the phone." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if grep -q 'qsConfig", "ii"' "$HOME/.config/hypr/hyprland/variables.lua" 2>/dev/null; then
|
||||
echo "NOTE: hyprland qsConfig is still 'ii' — flip variables.lua to 'souveraine' to switch."
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
|
||||
/**
|
||||
* Whole-screen capture, as opposed to ScreenSnipToggle's region select.
|
||||
*
|
||||
* `fullScreenshot` had been listed in the phone's config for long enough that
|
||||
* everyone assumed a component existed; none ever did, in any tree or any
|
||||
* commit. It rendered nothing because there was nothing to render.
|
||||
*/
|
||||
QuickToggleModel {
|
||||
id: root
|
||||
name: Translation.tr("Screenshot")
|
||||
hasStatusText: false
|
||||
toggled: false
|
||||
icon: "screenshot_monitor"
|
||||
|
||||
// Close the panel first, or the screenshot is a picture of the panel.
|
||||
// Same 300 ms the region snip uses — the close animation has to finish.
|
||||
mainAction: () => {
|
||||
GlobalStates.sidebarRightOpen = false;
|
||||
captureDelay.start();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: captureDelay
|
||||
interval: 300
|
||||
repeat: false
|
||||
onTriggered: captureProc.running = true
|
||||
}
|
||||
|
||||
// To a file AND the clipboard: on the phone there is rarely anywhere to
|
||||
// paste it right away, and on the laptop the file is the annoying half.
|
||||
Process {
|
||||
id: captureProc
|
||||
command: ["bash", "-c",
|
||||
"mkdir -p \"$HOME/Pictures\"; "
|
||||
+ "f=\"$HOME/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png\"; "
|
||||
+ "grim \"$f\" || { notify-send Screenshot 'Capture failed.' -a Shell; exit 1; }; "
|
||||
+ "wl-copy -t image/png < \"$f\"; "
|
||||
+ "notify-send Screenshot \"${f##*/} — saved and copied\" -a Shell"]
|
||||
}
|
||||
|
||||
tooltipText: Translation.tr("Whole screen to ~/Pictures and the clipboard")
|
||||
}
|
||||
|
|
@ -29,7 +29,52 @@ AbstractQuickPanel {
|
|||
readonly property real baseCellHeight: 56
|
||||
|
||||
// Toggles
|
||||
readonly property list<string> availableToggleTypes: ["network", "bluetooth", "idleInhibitor", "easyEffects", "nightLight", "darkMode", "cloudflareWarp", "gameMode", "screenSnip", "colorPicker", "onScreenKeyboard", "mic", "audio", "notifications", "powerProfile","musicRecognition", "antiFlashbang"]
|
||||
//
|
||||
// The one enumeration of what a toggle can be is the chooser's own list of
|
||||
// choices. This used to be a second, hand-written array here, and it drifted
|
||||
// — as a duplicated list always eventually does. Five configured toggles
|
||||
// (vpn, autoRotate, wallpaperShuffle, fullScreenshot, waydroidTerminate)
|
||||
// were silently dropped by validToggles below as "ghosts" while their
|
||||
// models and their android delegates sat complete in the tree: the phone
|
||||
// rendered 12 of the 17 in its config, with no error in the journal and
|
||||
// none in quickshell's own log, because a filtered entry is silent by
|
||||
// design. Asking the chooser is the only arrangement in which the two
|
||||
// cannot disagree — and it gets the phone's extra choices right for free,
|
||||
// since ii-phone/ overrides the chooser and this file is shared.
|
||||
//
|
||||
// Read once at completion rather than bound: DelegateChooser.choices is a
|
||||
// CONSTANT property with no change signal, so a binding that happened to
|
||||
// evaluate before the probe's choices were parented would never re-run.
|
||||
property list<string> availableToggleTypes: []
|
||||
|
||||
AndroidToggleDelegateChooser {
|
||||
id: toggleTypeProbe
|
||||
// Never renders anything — it exists to be asked what it can build.
|
||||
// DelegateChoice holds a Component; nothing is instantiated until a
|
||||
// view actually selects it.
|
||||
baseCellWidth: 0
|
||||
baseCellHeight: 0
|
||||
spacing: 0
|
||||
startingIndex: 0
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
const types = [];
|
||||
for (let i = 0; i < toggleTypeProbe.choices.length; i++) {
|
||||
const value = toggleTypeProbe.choices[i].roleValue;
|
||||
if (value !== undefined && value !== null)
|
||||
types.push(String(value));
|
||||
}
|
||||
if (types.length === 0) {
|
||||
// Never silently. An empty list here filters away every configured
|
||||
// toggle and leaves a blank panel, which is a worse failure than
|
||||
// the one this replaced.
|
||||
console.log("[quickToggles] chooser reported NO types — the panel will be empty");
|
||||
} else {
|
||||
console.log("[quickToggles] " + types.length + " types from chooser: " + types.join(", "));
|
||||
}
|
||||
root.availableToggleTypes = types;
|
||||
}
|
||||
readonly property int columns: Config.options.sidebar.quickToggles.android.columns
|
||||
readonly property list<var> toggles: Config.ready ? Config.options.sidebar.quickToggles.android.toggles : []
|
||||
// Filter out ghost items (config entries with types that have no matching delegate).
|
||||
|
|
@ -43,6 +88,13 @@ AbstractQuickPanel {
|
|||
if (t && availableToggleTypes.includes(t.type) && !seen.has(t.type)) {
|
||||
result.push({ type: t.type, size: t.size, _configIndex: i })
|
||||
seen.add(t.type)
|
||||
} else if (t && availableToggleTypes.length > 0 && !seen.has(t.type)) {
|
||||
// Say it out loud. A dropped entry used to vanish without a
|
||||
// trace — that silence is what made vpn/autoRotate/
|
||||
// wallpaperShuffle look like a rendering bug for a whole
|
||||
// session, when the panel was simply refusing to build them.
|
||||
console.log("[quickToggles] config lists '" + t.type
|
||||
+ "' but the chooser cannot build it — skipping")
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
import qs.modules.common
|
||||
import qs.modules.common.models.quickToggles
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
AndroidQuickToggleButton {
|
||||
toggleModel: FullScreenshotToggle {}
|
||||
}
|
||||
|
|
@ -292,4 +292,18 @@ DelegateChooser {
|
|||
cellSpacing: root.spacing
|
||||
cellSize: modelData.size
|
||||
} }
|
||||
|
||||
DelegateChoice { roleValue: "fullScreenshot"; AndroidFullScreenshotToggle {
|
||||
required property int index
|
||||
required property var modelData
|
||||
buttonIndex: root.startingIndex + index
|
||||
buttonData: modelData
|
||||
editMode: root.editMode
|
||||
dragIndex: root.dragIndex
|
||||
expandedSize: modelData.size > 1
|
||||
baseCellWidth: root.baseCellWidth
|
||||
baseCellHeight: root.baseCellHeight
|
||||
cellSpacing: root.spacing
|
||||
cellSize: modelData.size
|
||||
} }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
// Pixel3Arch addition: stop the Waydroid session. Phone-only — waydroid is
|
||||
// installed on the phone and not on the laptop, so this lives in the overlay.
|
||||
// Deploy to modules/common/models/quickToggles/.
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
|
||||
/**
|
||||
* Terminate the Android container's session.
|
||||
*
|
||||
* `waydroidTerminate` sat in the phone's config with no component behind it in
|
||||
* any tree or commit — a configured verb that did nothing, silently.
|
||||
*/
|
||||
QuickToggleModel {
|
||||
id: root
|
||||
|
||||
/// True while a stop is in flight, so a double tap cannot race itself.
|
||||
property bool busy: false
|
||||
|
||||
name: Translation.tr("Stop Android")
|
||||
hasStatusText: false
|
||||
toggled: false
|
||||
icon: "android"
|
||||
|
||||
// Deliberate, and always pressable. There is deliberately NO status poll:
|
||||
// `waydroid status` talks to the container and can block for seconds, and
|
||||
// a button that greys itself out on a stale read is a verb the owner
|
||||
// cannot reach. Press it; it reports what actually happened.
|
||||
mainAction: () => {
|
||||
if (root.busy)
|
||||
return;
|
||||
root.busy = true;
|
||||
stopProc.running = true;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: stopProc
|
||||
command: ["bash", "-c",
|
||||
"waydroid session stop >/dev/null 2>&1; "
|
||||
+ "waydroid status 2>/dev/null | grep -q 'Session:.*RUNNING' && echo running || echo stopped"]
|
||||
stdout: StdioCollector {
|
||||
id: stopResult
|
||||
onStreamFinished: {
|
||||
root.busy = false;
|
||||
const stillRunning = stopResult.text.trim() === "running";
|
||||
Quickshell.execDetached(["notify-send", Translation.tr("Android"),
|
||||
stillRunning
|
||||
? Translation.tr("Session did not stop.")
|
||||
: Translation.tr("Session stopped."),
|
||||
"-a", "Shell"]);
|
||||
}
|
||||
}
|
||||
onExited: root.busy = false
|
||||
}
|
||||
|
||||
tooltipText: Translation.tr("Stop the Waydroid session")
|
||||
}
|
||||
|
|
@ -322,4 +322,32 @@ DelegateChooser {
|
|||
cellSpacing: root.spacing
|
||||
cellSize: modelData.size
|
||||
} }
|
||||
|
||||
DelegateChoice { roleValue: "fullScreenshot"; AndroidFullScreenshotToggle {
|
||||
required property int index
|
||||
required property var modelData
|
||||
buttonIndex: root.startingIndex + index
|
||||
buttonData: modelData
|
||||
editMode: root.editMode
|
||||
dragIndex: root.dragIndex
|
||||
expandedSize: modelData.size > 1
|
||||
baseCellWidth: root.baseCellWidth
|
||||
baseCellHeight: root.baseCellHeight
|
||||
cellSpacing: root.spacing
|
||||
cellSize: modelData.size
|
||||
} }
|
||||
|
||||
DelegateChoice { roleValue: "waydroidTerminate"; AndroidWaydroidTerminateToggle {
|
||||
required property int index
|
||||
required property var modelData
|
||||
buttonIndex: root.startingIndex + index
|
||||
buttonData: modelData
|
||||
editMode: root.editMode
|
||||
dragIndex: root.dragIndex
|
||||
expandedSize: modelData.size > 1
|
||||
baseCellWidth: root.baseCellWidth
|
||||
baseCellHeight: root.baseCellHeight
|
||||
cellSpacing: root.spacing
|
||||
cellSize: modelData.size
|
||||
} }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
// Pixel3Arch addition: phone-only, waydroid is not installed on the laptop.
|
||||
import qs.modules.common
|
||||
import qs.modules.common.models.quickToggles
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
AndroidQuickToggleButton {
|
||||
toggleModel: WaydroidTerminateToggle {}
|
||||
}
|
||||
Loading…
Reference in a new issue