use shared focusgrab for most stuff (makes osk usable w/ other panels)

This commit is contained in:
end-4
2026-01-01 16:36:50 +01:00
parent 171cf6059f
commit 7238b2b15c
10 changed files with 168 additions and 75 deletions
@@ -1,3 +1,5 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
@@ -60,6 +62,7 @@ Scope {
}
color: "transparent"
// Positioning
anchors {
top: !Config.options.bar.bottom
bottom: Config.options.bar.bottom
@@ -72,6 +75,14 @@ Scope {
bottom: (Config.options.interactions.deadPixelWorkaround.enable && barRoot.anchors.bottom) * -1
}
// Include in focus grab
Component.onCompleted: {
GlobalFocusGrab.addPersistent(barRoot);
}
Component.onDestruction: {
GlobalFocusGrab.removePersistent(barRoot);
}
MouseArea {
id: hoverRegion
hoverEnabled: true
@@ -54,13 +54,16 @@ Scope { // Scope
item: cheatsheetBackground
}
HyprlandFocusGrab { // Click outside to close
id: grab
windows: [cheatsheetRoot]
active: cheatsheetRoot.visible
onCleared: () => {
if (!active)
cheatsheetRoot.hide();
Component.onCompleted: {
GlobalFocusGrab.addDismissable(cheatsheetRoot);
}
Component.onDestruction: {
GlobalFocusGrab.removeDismissable(cheatsheetRoot);
}
Connections {
target: GlobalFocusGrab
function onDismissed() {
cheatsheetRoot.hide();
}
}
@@ -81,7 +81,7 @@ Scope {
}
sourceComponent: PanelWindow {
id: mediaControlsRoot
id: panelWindow
visible: true
exclusionMode: ExclusionMode.Ignore
@@ -98,9 +98,9 @@ Scope {
right: Config.options.bar.vertical && Config.options.bar.bottom
}
margins {
top: Config.options.bar.vertical ? ((mediaControlsRoot.screen.height / 2) - widgetHeight * 1.5) : Appearance.sizes.barHeight
top: Config.options.bar.vertical ? ((panelWindow.screen.height / 2) - widgetHeight * 1.5) : Appearance.sizes.barHeight
bottom: Appearance.sizes.barHeight
left: Config.options.bar.vertical ? Appearance.sizes.barHeight : ((mediaControlsRoot.screen.width / 2) - (osdWidth / 2) - widgetWidth)
left: Config.options.bar.vertical ? Appearance.sizes.barHeight : ((panelWindow.screen.width / 2) - (osdWidth / 2) - widgetWidth)
right: Appearance.sizes.barHeight
}
@@ -108,13 +108,16 @@ Scope {
item: playerColumnLayout
}
HyprlandFocusGrab {
windows: [mediaControlsRoot]
active: mediaControlsLoader.active
onCleared: () => {
if (!active) {
GlobalStates.mediaControlsOpen = false;
}
Component.onCompleted: {
GlobalFocusGrab.addDismissable(panelWindow);
}
Component.onDestruction: {
GlobalFocusGrab.removeDismissable(panelWindow);
}
Connections {
target: GlobalFocusGrab
function onDismissed() {
GlobalStates.mediaControlsOpen = false;
}
}
@@ -137,10 +140,13 @@ Scope {
}
}
Item { // No player placeholder
Item {
// No player placeholder
Layout.alignment: {
if (mediaControlsRoot.anchors.left) return Qt.AlignLeft;
if (mediaControlsRoot.anchors.right) return Qt.AlignRight;
if (panelWindow.anchors.left)
return Qt.AlignLeft;
if (panelWindow.anchors.right)
return Qt.AlignRight;
return Qt.AlignHCenter;
}
Layout.leftMargin: Appearance.sizes.hyprlandGapsOut
@@ -153,7 +159,7 @@ Scope {
target: placeholderBackground
}
Rectangle {
Rectangle {
id: placeholderBackground
anchors.centerIn: parent
color: Appearance.colors.colLayer0
@@ -57,6 +57,13 @@ Scope { // Scope
item: oskBackground
}
// Make it usable with other panels
Component.onCompleted: {
GlobalFocusGrab.addPersistent(oskRoot);
}
Component.onDestruction: {
GlobalFocusGrab.removePersistent(oskRoot);
}
// Background
StyledRectangularShadow {
@@ -23,7 +23,7 @@ Scope {
visible: GlobalStates.overviewOpen
WlrLayershell.namespace: "quickshell:overview"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.layer: WlrLayer.Top
// WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
color: "transparent"
@@ -38,43 +38,28 @@ Scope {
right: true
}
HyprlandFocusGrab {
id: grab
windows: [panelWindow]
property bool canBeActive: panelWindow.monitorIsFocused
active: false
onCleared: () => {
if (!active)
GlobalStates.overviewOpen = false;
}
}
Connections {
target: GlobalStates
function onOverviewOpenChanged() {
if (!GlobalStates.overviewOpen) {
searchWidget.disableExpandAnimation();
overviewScope.dontAutoCancelSearch = false;
GlobalFocusGrab.dismiss();
} else {
if (!overviewScope.dontAutoCancelSearch) {
searchWidget.cancelSearch();
}
delayedGrabTimer.start();
GlobalFocusGrab.addDismissable(panelWindow);
}
}
}
Timer {
id: delayedGrabTimer
interval: Config.options.hacks.arbitraryRaceConditionDelay
repeat: false
onTriggered: {
if (!grab.canBeActive)
return;
grab.active = GlobalStates.overviewOpen;
Connections {
target: GlobalFocusGrab
function onDismissed() {
GlobalStates.overviewOpen = false;
}
}
implicitWidth: columnLayout.implicitWidth
implicitHeight: columnLayout.implicitHeight
@@ -84,11 +84,11 @@ Scope { // Scope
active: true
sourceComponent: PanelWindow { // Window
id: sidebarRoot
id: panelWindow
visible: GlobalStates.sidebarLeftOpen
property bool extend: false
property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth
property real sidebarWidth: panelWindow.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth
property var contentParent: sidebarLeftBackground
function hide() {
@@ -113,15 +113,17 @@ Scope { // Scope
item: sidebarLeftBackground
}
HyprlandFocusGrab { // Click outside to close
id: grab
windows: [ sidebarRoot ]
active: sidebarRoot.visible && !root.pin
onActiveChanged: { // Focus the selected tab
if (active) sidebarLeftBackground.children[0].focusActiveItem()
onVisibleChanged: {
if (visible) {
GlobalFocusGrab.addDismissable(panelWindow);
} else {
GlobalFocusGrab.removeDismissable(panelWindow);
}
onCleared: () => {
if (!active) sidebarRoot.hide()
}
Connections {
target: GlobalFocusGrab
function onDismissed() {
panelWindow.hide();
}
}
@@ -136,7 +138,7 @@ Scope { // Scope
anchors.left: parent.left
anchors.topMargin: Appearance.sizes.hyprlandGapsOut
anchors.leftMargin: Appearance.sizes.hyprlandGapsOut
width: sidebarRoot.sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
width: panelWindow.sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
color: Appearance.colors.colLayer0
border.width: 1
@@ -149,11 +151,11 @@ Scope { // Scope
Keys.onPressed: (event) => {
if (event.key === Qt.Key_Escape) {
sidebarRoot.hide();
panelWindow.hide();
}
if (event.modifiers === Qt.ControlModifier) {
if (event.key === Qt.Key_O) {
sidebarRoot.extend = !sidebarRoot.extend;
panelWindow.extend = !panelWindow.extend;
} else if (event.key === Qt.Key_D) {
root.toggleDetach();
} else if (event.key === Qt.Key_P) {
@@ -12,11 +12,11 @@ Scope {
property int sidebarWidth: Appearance.sizes.sidebarWidth
PanelWindow {
id: sidebarRoot
id: panelWindow
visible: GlobalStates.sidebarRightOpen
function hide() {
GlobalStates.sidebarRightOpen = false
GlobalStates.sidebarRightOpen = false;
}
exclusiveZone: 0
@@ -32,12 +32,17 @@ Scope {
bottom: true
}
HyprlandFocusGrab {
id: grab
windows: [ sidebarRoot ]
active: GlobalStates.sidebarRightOpen
onCleared: () => {
if (!active) sidebarRoot.hide()
onVisibleChanged: {
if (visible) {
GlobalFocusGrab.addDismissable(panelWindow);
} else {
GlobalFocusGrab.removeDismissable(panelWindow);
}
}
Connections {
target: GlobalFocusGrab
function onDismissed() {
panelWindow.hide();
}
}
@@ -53,16 +58,14 @@ Scope {
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
focus: GlobalStates.sidebarRightOpen
Keys.onPressed: (event) => {
Keys.onPressed: event => {
if (event.key === Qt.Key_Escape) {
sidebarRoot.hide();
panelWindow.hide();
}
}
sourceComponent: SidebarRightContent {}
}
}
IpcHandler {
@@ -105,5 +108,4 @@ Scope {
GlobalStates.sidebarRightOpen = false;
}
}
}
@@ -66,6 +66,7 @@ Scope {
}
color: "transparent"
// Positioning
anchors {
left: !Config.options.bar.bottom
right: Config.options.bar.bottom
@@ -73,6 +74,14 @@ Scope {
bottom: true
}
// Include in focus grab
Component.onCompleted: {
GlobalFocusGrab.addPersistent(barRoot);
}
Component.onDestruction: {
GlobalFocusGrab.removePersistent(barRoot);
}
MouseArea {
id: hoverRegion
hoverEnabled: true
@@ -39,12 +39,16 @@ Scope {
implicitHeight: Appearance.sizes.wallpaperSelectorHeight
implicitWidth: Appearance.sizes.wallpaperSelectorWidth
HyprlandFocusGrab { // Click outside to close
id: grab
windows: [ panelWindow ]
active: wallpaperSelectorLoader.active
onCleared: () => {
if (!active) GlobalStates.wallpaperSelectorOpen = false;
Component.onCompleted: {
GlobalFocusGrab.addDismissable(panelWindow);
}
Component.onDestruction: {
GlobalFocusGrab.removeDismissable(panelWindow);
}
Connections {
target: GlobalFocusGrab
function onDismissed() {
GlobalStates.wallpaperSelectorOpen = false;
}
}
@@ -0,0 +1,64 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Hyprland
/**
* Manages a HyprlandFocusGrab that's to be shared by all windows.
* "Persistent" is for windows that should always be included but not closed on dismiss, like bar and onscreen keyboard.
* "Dismissable" is for stuff like sidebars
*/
Singleton {
id: root
signal dismissed()
property list<var> persistent: []
property list<var> dismissable: []
function dismiss() {
root.dismissable = [];
root.dismissed();
}
Component.onCompleted: {
console.log("[GlobalFocusGrab] Initialized");
}
function addPersistent(window) {
if (root.persistent.indexOf(window) === -1) {
root.persistent.push(window);
}
}
function removePersistent(window) {
var index = root.persistent.indexOf(window);
if (index !== -1) {
root.persistent.splice(index, 1);
}
}
function addDismissable(window) {
if (root.dismissable.indexOf(window) === -1) {
root.dismissable.push(window);
}
}
function removeDismissable(window) {
var index = root.dismissable.indexOf(window);
if (index !== -1) {
root.dismissable.splice(index, 1);
}
}
HyprlandFocusGrab {
id: grab
windows: [...root.persistent, ...root.dismissable]
active: root.dismissable.length > 0
onCleared: () => {
root.dismiss();
}
}
}