satisfactory positioning for wayland switchover
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Io
|
||||
|
||||
// monitor layout picker for $DOTWRYN/config/desktop/hyprland/config.lua.d/02-monitor-layouts.lua;
|
||||
// shown with 'qs ipc call -- displays show' (without '--', 'show' parses as 'qs ipc show')
|
||||
Scope {
|
||||
id: picker
|
||||
|
||||
property bool shown: false
|
||||
property var screen: null
|
||||
|
||||
property var layouts: []
|
||||
property var resolutions: []
|
||||
|
||||
property int selectedLayout: 0
|
||||
property int selectedResolution: -1 // -1 : the modes configured in the layout
|
||||
|
||||
function show(): void {
|
||||
selectedLayout = 0
|
||||
selectedResolution = -1
|
||||
screen = Quickshell.screens.find(screen => screen.name === Hyprland.focusedMonitor?.name) ?? null
|
||||
Quickshell.execDetached(["hyprctl", "eval", "dotwryn_publish_monitor_layouts()"])
|
||||
shown = true
|
||||
}
|
||||
|
||||
function moveLayout(offset: int): void {
|
||||
if (layouts.length > 0) selectedLayout = (selectedLayout + offset + layouts.length) % layouts.length
|
||||
}
|
||||
|
||||
function moveResolution(offset: int): void {
|
||||
const count = resolutions.length + 1
|
||||
selectedResolution = ((selectedResolution + 1 + offset + count) % count) - 1
|
||||
}
|
||||
|
||||
// hyprland 0.56.2 segfaults if a focused layer surface is destroyed by a
|
||||
// monitor change, so the picker must be gone before the layout is applied
|
||||
function applyLayout(): void {
|
||||
if (selectedLayout >= layouts.length) return
|
||||
|
||||
const resolution = selectedResolution < 0 ? "nil" : JSON.stringify(resolutions[selectedResolution])
|
||||
evaluation.command = ["hyprctl", "eval", `dotwryn_set_monitor_layout(${JSON.stringify(layouts[selectedLayout])}, ${resolution})`]
|
||||
|
||||
shown = false
|
||||
applyAfterHidden.restart()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: applyAfterHidden
|
||||
interval: 250
|
||||
onTriggered: evaluation.running = true
|
||||
}
|
||||
|
||||
Process {
|
||||
id: evaluation
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const result = text.trim()
|
||||
if (result !== "ok") Quickshell.execDetached(["notify-send", "displays", result.replace(/^.*:\d+: /, "")])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "displays"
|
||||
|
||||
function show(): void { picker.show() }
|
||||
function hide(): void { picker.shown = false }
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Hyprland
|
||||
|
||||
function onRawEvent(event) {
|
||||
const prefix = "dotwryn-monitor-layouts "
|
||||
if (event.name !== "custom" || !event.data.startsWith(prefix)) return
|
||||
|
||||
const published = JSON.parse(event.data.slice(prefix.length))
|
||||
picker.layouts = published.layouts
|
||||
picker.resolutions = published.resolutions
|
||||
}
|
||||
}
|
||||
|
||||
component PickerText: Text {
|
||||
color: Colors.foreground
|
||||
font.family: Colors.font
|
||||
font.pointSize: Colors.fontSize
|
||||
}
|
||||
|
||||
PanelWindow {
|
||||
visible: picker.shown
|
||||
screen: picker.screen
|
||||
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
|
||||
implicitWidth: content.implicitWidth + 24
|
||||
implicitHeight: content.implicitHeight + 24
|
||||
color: "transparent"
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Colors.background
|
||||
border.width: 1
|
||||
border.color: Colors.primary
|
||||
|
||||
focus: true
|
||||
Keys.onPressed: event => {
|
||||
switch (event.key) {
|
||||
case Qt.Key_J: case Qt.Key_Down: picker.moveLayout(1); break
|
||||
case Qt.Key_K: case Qt.Key_Up: picker.moveLayout(-1); break
|
||||
case Qt.Key_L: case Qt.Key_Right: picker.moveResolution(1); break
|
||||
case Qt.Key_H: case Qt.Key_Left: picker.moveResolution(-1); break
|
||||
|
||||
case Qt.Key_Return: case Qt.Key_Enter: picker.applyLayout(); break
|
||||
|
||||
case Qt.Key_Escape: case Qt.Key_Q: picker.shown = false; break
|
||||
default: return
|
||||
}
|
||||
event.accepted = true
|
||||
}
|
||||
|
||||
Column {
|
||||
id: content
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
|
||||
PickerText {
|
||||
text: "monitor layout"
|
||||
font.bold: true
|
||||
color: Colors.secondary
|
||||
}
|
||||
|
||||
PickerText {
|
||||
visible: picker.layouts.length === 0
|
||||
text: "no monitor layouts for this host"
|
||||
color: Colors.disabled
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: picker.layouts
|
||||
|
||||
Rectangle {
|
||||
id: layoutRow
|
||||
|
||||
required property int index
|
||||
required property string modelData
|
||||
|
||||
readonly property bool selected: picker.selectedLayout === index
|
||||
|
||||
implicitWidth: Math.max(layoutLabel.implicitWidth + 24, 280)
|
||||
implicitHeight: layoutLabel.implicitHeight + 8
|
||||
color: selected ? Colors.primary : "transparent"
|
||||
|
||||
PickerText {
|
||||
id: layoutLabel
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 12
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: layoutRow.modelData
|
||||
font.bold: layoutRow.selected
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: picker.selectedLayout = layoutRow.index
|
||||
onClicked: picker.applyLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
visible: picker.layouts.length > 0
|
||||
spacing: 8
|
||||
topPadding: 8
|
||||
|
||||
PickerText {
|
||||
text: "resolution"
|
||||
color: Colors.secondary
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ["configured"].concat(picker.resolutions)
|
||||
|
||||
PickerText {
|
||||
required property int index
|
||||
required property string modelData
|
||||
|
||||
readonly property bool selected: picker.selectedResolution === index - 1
|
||||
|
||||
text: selected ? `[${modelData}]` : ` ${modelData} `
|
||||
color: selected ? Colors.foreground : Colors.disabled
|
||||
font.bold: selected
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: picker.selectedResolution = parent.index - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,4 +19,6 @@ ShellRoot {
|
||||
VolumeOsd {}
|
||||
|
||||
LogoutPrompt {}
|
||||
|
||||
DisplayPicker {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user