1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-29 07:52:50 -05:00

niri: embed spotlight to same window as overview layer

This commit is contained in:
bbedward
2025-11-20 16:28:26 -05:00
parent 2dbfec0307
commit d9da88ceb5

View File

@@ -3,26 +3,47 @@ import QtQuick.Controls
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import qs.Common import qs.Common
import qs.Modals.Spotlight
import qs.Services import qs.Services
Scope { Scope {
id: niriOverviewScope id: niriOverviewScope
// Only show overlay when in overview and spotlight is not open property bool searchActive: false
property string searchActiveScreen: ""
property bool overlayActive: NiriService.inOverview && !(PopoutService.spotlightModal?.spotlightOpen ?? false) property bool overlayActive: NiriService.inOverview && !(PopoutService.spotlightModal?.spotlightOpen ?? false)
function showSpotlight(screenName) {
searchActive = true
searchActiveScreen = screenName
}
function hideSpotlight() {
searchActive = false
searchActiveScreen = ""
}
Connections { Connections {
target: NiriService target: NiriService
function onInOverviewChanged() { function onInOverviewChanged() {
if (!NiriService.inOverview && PopoutService.spotlightModal?.openedFromOverview) { if (!NiriService.inOverview) {
PopoutService.spotlightModal.hide() hideSpotlight()
} else {
searchActive = false
searchActiveScreen = ""
}
}
function onCurrentOutputChanged() {
if (NiriService.inOverview && searchActive && searchActiveScreen !== "" && searchActiveScreen !== NiriService.currentOutput) {
hideSpotlight()
} }
} }
} }
Loader { Loader {
id: niriOverlayLoader id: niriOverlayLoader
active: NiriService.inOverview active: overlayActive
asynchronous: false asynchronous: false
sourceComponent: Variants { sourceComponent: Variants {
@@ -33,92 +54,159 @@ Scope {
id: overlayWindow id: overlayWindow
required property var modelData required property var modelData
readonly property real dpr: CompositorService.getScreenScale(screen)
readonly property bool isActiveScreen: screen.name === NiriService.currentOutput
readonly property bool shouldShowSpotlight: niriOverviewScope.searchActive && screen.name === niriOverviewScope.searchActiveScreen
screen: modelData screen: modelData
visible: niriOverviewScope.overlayActive visible: NiriService.inOverview
color: "transparent" color: "transparent"
WlrLayershell.namespace: "dms:niri-overview-overlay" WlrLayershell.namespace: "dms:niri-overview-spotlight"
WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.exclusiveZone: -1 WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: niriOverviewScope.overlayActive ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None WlrLayershell.keyboardFocus: {
if (!NiriService.inOverview) return WlrKeyboardFocus.None
if (!isActiveScreen) return WlrKeyboardFocus.None
return WlrKeyboardFocus.Exclusive
}
implicitWidth: 0 onShouldShowSpotlightChanged: {
implicitHeight: 0 if (!shouldShowSpotlight && isActiveScreen) {
Qt.callLater(() => keyboardFocusScope.forceActiveFocus())
}
}
anchors { anchors {
top: true top: true
left: true left: true
right: false right: true
bottom: false bottom: true
} }
FocusScope { FocusScope {
id: keyboardFocusScope id: keyboardFocusScope
anchors.fill: parent anchors.fill: parent
visible: niriOverviewScope.overlayActive focus: true
focus: niriOverviewScope.overlayActive
Keys.onPressed: event => { Keys.onPressed: event => {
if (!overlayWindow.shouldShowSpotlight) {
if ([Qt.Key_Escape, Qt.Key_Return].includes(event.key)) {
NiriService.toggleOverview()
event.accepted = true
return
}
// Handle arrow keys and escape for navigation, mimicking niri's harcoded keybinds if (event.key === Qt.Key_Left) {
if ([Qt.Key_Escape, Qt.Key_Return].includes(event.key)) { NiriService.moveColumnLeft()
NiriService.toggleOverview() event.accepted = true
event.accepted = true return
return }
}
if (event.key === Qt.Key_Left) { if (event.key === Qt.Key_Right) {
NiriService.moveColumnLeft() NiriService.moveColumnRight()
event.accepted = true event.accepted = true
return return
} }
if (event.key === Qt.Key_Right) { if (event.key === Qt.Key_Up) {
NiriService.moveColumnRight() NiriService.moveWorkspaceUp()
event.accepted = true event.accepted = true
return return
} }
if (event.key === Qt.Key_Up) { if (event.key === Qt.Key_Down) {
NiriService.moveWorkspaceUp() NiriService.moveWorkspaceDown()
event.accepted = true event.accepted = true
return return
} }
if (event.key === Qt.Key_Down) { if (event.modifiers & (Qt.ControlModifier | Qt.MetaModifier) || [Qt.Key_Delete, Qt.Key_Backspace].includes(event.key)) {
NiriService.moveWorkspaceDown() event.accepted = false
event.accepted = true return
return }
}
// Allowing delete and backspace will produce a broken text if (!event.isAutoRepeat && event.text) {
if (event.modifiers & (Qt.ControlModifier | Qt.MetaModifier) || [Qt.Key_Delete, Qt.Key_Backspace].includes(event.key)) { niriOverviewScope.showSpotlight(overlayWindow.screen.name)
event.accepted = false if (spotlightContent?.searchField) {
return spotlightContent.searchField.text = event.text.trim()
} if (spotlightContent.appLauncher) {
spotlightContent.appLauncher.searchQuery = event.text.trim()
// For any other key (printable characters), open spotlight
if (!event.isAutoRepeat) {
Qt.callLater(() => {
if (PopoutService.spotlightModal) {
if (event.text) {
PopoutService.spotlightModal.openedFromOverview = true
PopoutService.spotlightModal.showWithQuery(event.text.trim())
}
}
})
event.accepted = true
}
} }
Qt.callLater(() => spotlightContent.searchField.forceActiveFocus())
}
event.accepted = true
}
}
}
}
Item {
id: spotlightContainer
x: Theme.snap((parent.width - width) / 2, overlayWindow.dpr)
y: Theme.snap((parent.height - height) / 2, overlayWindow.dpr)
width: Theme.px(500, overlayWindow.dpr)
height: Theme.px(600, overlayWindow.dpr)
property real scaleValue: 0.96
scale: scaleValue
opacity: overlayWindow.shouldShowSpotlight ? 1 : 0
layer.enabled: true
layer.smooth: false
layer.textureSize: Qt.size(Math.round(width * overlayWindow.dpr), Math.round(height * overlayWindow.dpr))
Connections { Connections {
target: niriOverviewScope target: overlayWindow
function onOverlayActiveChanged() { function onShouldShowSpotlightChanged() {
if (niriOverviewScope.overlayActive) { spotlightContainer.scaleValue = overlayWindow.shouldShowSpotlight ? 1.0 : 0.96
Qt.callLater(() => keyboardFocusScope.forceActiveFocus()) }
}
Behavior on scaleValue {
NumberAnimation {
duration: Theme.expressiveDurations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: niriOverviewScope.searchActive ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.expressiveDurations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: niriOverviewScope.searchActive ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
}
}
Rectangle {
anchors.fill: parent
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
radius: Theme.cornerRadius
border.color: Theme.outlineMedium
border.width: 1
}
SpotlightContent {
id: spotlightContent
anchors.fill: parent
anchors.margins: 0
property var fakeParentModal: QtObject {
property bool spotlightOpen: overlayWindow.shouldShowSpotlight
function hide() {
niriOverviewScope.hideSpotlight()
if (overlayWindow.isActiveScreen) {
Qt.callLater(() => keyboardFocusScope.forceActiveFocus())
}
} }
} }
Component.onCompleted: {
parentModal = fakeParentModal
}
} }
} }
} }