mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-05 21:15:38 -05:00
modals: single window optimization
This commit is contained in:
373
quickshell/Common/DankModalWindow.qml
Normal file
373
quickshell/Common/DankModalWindow.qml
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var activeModal: null
|
||||||
|
property bool windowsVisible: false
|
||||||
|
property var targetScreen: null
|
||||||
|
readonly property bool hasActiveModal: activeModal !== null
|
||||||
|
readonly property bool shouldShowModal: hasActiveModal
|
||||||
|
readonly property var screen: backgroundWindow.screen
|
||||||
|
readonly property real dpr: screen ? CompositorService.getScreenScale(screen) : 1
|
||||||
|
readonly property real shadowBuffer: 5
|
||||||
|
|
||||||
|
property bool wantsToHide: false
|
||||||
|
|
||||||
|
property real cachedModalWidth: 400
|
||||||
|
property real cachedModalHeight: 300
|
||||||
|
property real cachedModalX: 0
|
||||||
|
property real cachedModalY: 0
|
||||||
|
property var cachedModal: null
|
||||||
|
property int cachedAnimationDuration: Theme.shortDuration
|
||||||
|
property var cachedEnterCurve: Theme.expressiveCurves.expressiveFastSpatial
|
||||||
|
property var cachedExitCurve: Theme.expressiveCurves.expressiveFastSpatial
|
||||||
|
property real cachedScaleCollapsed: 0.96
|
||||||
|
|
||||||
|
readonly property real modalWidth: cachedModalWidth
|
||||||
|
readonly property real modalHeight: cachedModalHeight
|
||||||
|
readonly property real modalX: cachedModalX
|
||||||
|
readonly property real modalY: cachedModalY
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root.cachedModal
|
||||||
|
function onModalWidthChanged() {
|
||||||
|
if (!root.hasActiveModal)
|
||||||
|
return;
|
||||||
|
root.cachedModalWidth = Theme.px(root.cachedModal.modalWidth, root.dpr);
|
||||||
|
root.cachedModalX = root.calculateX(root.cachedModal);
|
||||||
|
}
|
||||||
|
function onModalHeightChanged() {
|
||||||
|
if (!root.hasActiveModal)
|
||||||
|
return;
|
||||||
|
root.cachedModalHeight = Theme.px(root.cachedModal.modalHeight, root.dpr);
|
||||||
|
root.cachedModalY = root.calculateY(root.cachedModal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onScreenChanged: {
|
||||||
|
if (!cachedModal || !screen)
|
||||||
|
return;
|
||||||
|
cachedModalWidth = Theme.px(cachedModal.modalWidth, dpr);
|
||||||
|
cachedModalHeight = Theme.px(cachedModal.modalHeight, dpr);
|
||||||
|
cachedModalX = calculateX(cachedModal);
|
||||||
|
cachedModalY = calculateY(cachedModal);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showModal(modal) {
|
||||||
|
wantsToHide = false;
|
||||||
|
targetScreen = CompositorService.focusedScreen;
|
||||||
|
|
||||||
|
activeModal = modal;
|
||||||
|
cachedModal = modal;
|
||||||
|
windowsVisible = true;
|
||||||
|
cachedModalWidth = Theme.px(modal.modalWidth, dpr);
|
||||||
|
cachedModalHeight = Theme.px(modal.modalHeight, dpr);
|
||||||
|
cachedModalX = calculateX(modal);
|
||||||
|
cachedModalY = calculateY(modal);
|
||||||
|
cachedAnimationDuration = modal.animationDuration ?? Theme.shortDuration;
|
||||||
|
cachedEnterCurve = modal.animationEnterCurve ?? Theme.expressiveCurves.expressiveFastSpatial;
|
||||||
|
cachedExitCurve = modal.animationExitCurve ?? Theme.expressiveCurves.expressiveFastSpatial;
|
||||||
|
cachedScaleCollapsed = modal.animationScaleCollapsed ?? 0.96;
|
||||||
|
|
||||||
|
if (modal.directContent)
|
||||||
|
Qt.callLater(focusDirectContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function focusDirectContent() {
|
||||||
|
if (!hasActiveModal)
|
||||||
|
return;
|
||||||
|
if (!cachedModal?.directContent)
|
||||||
|
return;
|
||||||
|
cachedModal.directContent.forceActiveFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideModal() {
|
||||||
|
wantsToHide = true;
|
||||||
|
Qt.callLater(completeHide);
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeHide() {
|
||||||
|
if (!wantsToHide)
|
||||||
|
return;
|
||||||
|
activeModal = null;
|
||||||
|
wantsToHide = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideModalInstant() {
|
||||||
|
wantsToHide = false;
|
||||||
|
activeModal = null;
|
||||||
|
windowsVisible = false;
|
||||||
|
cleanupInputMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCloseAnimationFinished() {
|
||||||
|
if (hasActiveModal)
|
||||||
|
return;
|
||||||
|
if (cachedModal && typeof cachedModal.onFullyClosed === "function")
|
||||||
|
cachedModal.onFullyClosed();
|
||||||
|
cleanupInputMethod();
|
||||||
|
windowsVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupInputMethod() {
|
||||||
|
if (!Qt.inputMethod)
|
||||||
|
return;
|
||||||
|
Qt.inputMethod.hide();
|
||||||
|
Qt.inputMethod.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateX(m) {
|
||||||
|
const screen = backgroundWindow.screen;
|
||||||
|
if (!screen)
|
||||||
|
return 0;
|
||||||
|
const w = Theme.px(m.modalWidth, dpr);
|
||||||
|
switch (m.positioning) {
|
||||||
|
case "center":
|
||||||
|
return Theme.snap((screen.width - w) / 2, dpr);
|
||||||
|
case "top-right":
|
||||||
|
return Theme.snap(Math.max(Theme.spacingL, screen.width - w - Theme.spacingL), dpr);
|
||||||
|
case "custom":
|
||||||
|
return Theme.snap(m.customPosition.x, dpr);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateY(m) {
|
||||||
|
const screen = backgroundWindow.screen;
|
||||||
|
if (!screen)
|
||||||
|
return 0;
|
||||||
|
const h = Theme.px(m.modalHeight, dpr);
|
||||||
|
switch (m.positioning) {
|
||||||
|
case "center":
|
||||||
|
return Theme.snap((screen.height - h) / 2, dpr);
|
||||||
|
case "top-right":
|
||||||
|
return Theme.snap(Theme.barHeight + Theme.spacingXS, dpr);
|
||||||
|
case "custom":
|
||||||
|
return Theme.snap(m.customPosition.y, dpr);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: backgroundWindow
|
||||||
|
visible: root.windowsVisible
|
||||||
|
screen: root.targetScreen
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
WlrLayershell.namespace: "dms:modal:background"
|
||||||
|
WlrLayershell.layer: WlrLayer.Top
|
||||||
|
WlrLayershell.exclusiveZone: -1
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
bottom: true
|
||||||
|
}
|
||||||
|
|
||||||
|
mask: Region {
|
||||||
|
item: backgroundMaskRect
|
||||||
|
intersection: Intersection.Xor
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: backgroundMaskRect
|
||||||
|
x: root.shouldShowModal ? root.modalX : 0
|
||||||
|
y: root.shouldShowModal ? root.modalY : 0
|
||||||
|
width: root.shouldShowModal ? root.modalWidth : (backgroundWindow.screen?.width ?? 1920)
|
||||||
|
height: root.shouldShowModal ? root.modalHeight : (backgroundWindow.screen?.height ?? 1080)
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: root.windowsVisible
|
||||||
|
onClicked: mouse => {
|
||||||
|
if (!root.cachedModal || !root.shouldShowModal)
|
||||||
|
return;
|
||||||
|
if (!(root.cachedModal.closeOnBackgroundClick ?? true))
|
||||||
|
return;
|
||||||
|
const outside = mouse.x < root.modalX || mouse.x > root.modalX + root.modalWidth || mouse.y < root.modalY || mouse.y > root.modalY + root.modalHeight;
|
||||||
|
if (!outside)
|
||||||
|
return;
|
||||||
|
root.cachedModal.backgroundClicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "black"
|
||||||
|
opacity: root.shouldShowModal && SettingsData.modalDarkenBackground ? (root.cachedModal?.backgroundOpacity ?? 0.5) : 0
|
||||||
|
visible: SettingsData.modalDarkenBackground
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.cachedAnimationDuration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.shouldShowModal ? root.cachedEnterCurve : root.cachedExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: contentWindow
|
||||||
|
visible: root.windowsVisible
|
||||||
|
screen: root.targetScreen
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
WlrLayershell.namespace: root.cachedModal?.layerNamespace ?? "dms:modal"
|
||||||
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
|
WlrLayershell.exclusiveZone: -1
|
||||||
|
WlrLayershell.keyboardFocus: {
|
||||||
|
if (!root.hasActiveModal)
|
||||||
|
return WlrKeyboardFocus.None;
|
||||||
|
if (root.cachedModal?.customKeyboardFocus !== null && root.cachedModal?.customKeyboardFocus !== undefined)
|
||||||
|
return root.cachedModal.customKeyboardFocus;
|
||||||
|
if (CompositorService.isHyprland)
|
||||||
|
return WlrKeyboardFocus.OnDemand;
|
||||||
|
return WlrKeyboardFocus.Exclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: true
|
||||||
|
top: true
|
||||||
|
}
|
||||||
|
|
||||||
|
WlrLayershell.margins {
|
||||||
|
left: Math.max(0, Theme.snap(root.modalX - root.shadowBuffer, root.dpr))
|
||||||
|
top: Math.max(0, Theme.snap(root.modalY - root.shadowBuffer, root.dpr))
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitWidth: root.modalWidth + (root.shadowBuffer * 2)
|
||||||
|
implicitHeight: root.modalHeight + (root.shadowBuffer * 2)
|
||||||
|
|
||||||
|
mask: Region {
|
||||||
|
item: contentMaskRect
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contentMaskRect
|
||||||
|
x: root.shadowBuffer
|
||||||
|
y: root.shadowBuffer
|
||||||
|
width: root.shouldShowModal ? root.modalWidth : 0
|
||||||
|
height: root.shouldShowModal ? root.modalHeight : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
windows: [contentWindow]
|
||||||
|
active: CompositorService.isHyprland && root.hasActiveModal && (root.cachedModal?.shouldHaveFocus ?? false)
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contentContainer
|
||||||
|
x: root.shadowBuffer
|
||||||
|
y: root.shadowBuffer
|
||||||
|
width: root.modalWidth
|
||||||
|
height: root.modalHeight
|
||||||
|
|
||||||
|
readonly property bool hasDirectContent: root.cachedModal ? (root.cachedModal.directContent !== null && root.cachedModal.directContent !== undefined) : false
|
||||||
|
|
||||||
|
opacity: root.shouldShowModal ? 1 : 0
|
||||||
|
scale: root.shouldShowModal ? 1 : root.cachedScaleCollapsed
|
||||||
|
|
||||||
|
onHasDirectContentChanged: {
|
||||||
|
if (!hasDirectContent)
|
||||||
|
return;
|
||||||
|
const dc = root.cachedModal.directContent;
|
||||||
|
if (dc.parent === directContentWrapper)
|
||||||
|
return;
|
||||||
|
dc.parent = directContentWrapper;
|
||||||
|
dc.anchors.fill = directContentWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
id: opacityAnimation
|
||||||
|
duration: root.cachedAnimationDuration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.shouldShowModal ? root.cachedEnterCurve : root.cachedExitCurve
|
||||||
|
onRunningChanged: {
|
||||||
|
if (running || root.shouldShowModal)
|
||||||
|
return;
|
||||||
|
root.onCloseAnimationFinished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
id: scaleAnimation
|
||||||
|
duration: root.cachedAnimationDuration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.shouldShowModal ? root.cachedEnterCurve : root.cachedExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankRectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: root.cachedModal?.backgroundColor ?? Theme.surfaceContainer
|
||||||
|
borderColor: root.cachedModal?.borderColor ?? Theme.outlineMedium
|
||||||
|
borderWidth: root.cachedModal?.borderWidth ?? 1
|
||||||
|
radius: root.cachedModal?.cornerRadius ?? Theme.cornerRadius
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusScope {
|
||||||
|
id: modalFocusScope
|
||||||
|
anchors.fill: parent
|
||||||
|
focus: root.hasActiveModal
|
||||||
|
|
||||||
|
Keys.onEscapePressed: event => {
|
||||||
|
if (!root.cachedModal?.closeOnEscapeKey)
|
||||||
|
return;
|
||||||
|
root.cachedModal.close();
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.forwardTo: contentContainer.hasDirectContent ? [directContentWrapper] : (contentLoader.item ? [contentLoader.item] : [])
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: directContentWrapper
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: contentContainer.hasDirectContent
|
||||||
|
focus: contentContainer.hasDirectContent && root.hasActiveModal
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: contentLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
active: !contentContainer.hasDirectContent && root.windowsVisible
|
||||||
|
asynchronous: false
|
||||||
|
sourceComponent: root.cachedModal?.content ?? null
|
||||||
|
visible: !contentContainer.hasDirectContent
|
||||||
|
focus: !contentContainer.hasDirectContent && root.hasActiveModal
|
||||||
|
onLoaded: {
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
if (root.cachedModal)
|
||||||
|
root.cachedModal.loadedContent = item;
|
||||||
|
if (root.hasActiveModal)
|
||||||
|
item.forceActiveFocus();
|
||||||
|
}
|
||||||
|
onActiveChanged: {
|
||||||
|
if (active || !root.cachedModal)
|
||||||
|
return;
|
||||||
|
root.cachedModal.loadedContent = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,8 @@ import qs.Services
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
readonly property bool _forceDisplayService: DisplayService.brightnessAvailable !== undefined
|
||||||
|
|
||||||
Instantiator {
|
Instantiator {
|
||||||
id: daemonPluginInstantiator
|
id: daemonPluginInstantiator
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Hyprland
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -10,11 +9,6 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:bluetooth-pairing"
|
layerNamespace: "dms:bluetooth-pairing"
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [root.contentWindow]
|
|
||||||
active: CompositorService.isHyprland && root.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
property string deviceName: ""
|
property string deviceName: ""
|
||||||
property string deviceAddress: ""
|
property string deviceAddress: ""
|
||||||
property string requestType: ""
|
property string requestType: ""
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
import qs.Modals.Clipboard
|
import qs.Modals.Clipboard
|
||||||
|
|
||||||
Item {
|
FocusScope {
|
||||||
id: clipboardContent
|
id: clipboardContent
|
||||||
|
|
||||||
required property var modal
|
required property var modal
|
||||||
@@ -15,6 +14,7 @@ Item {
|
|||||||
property alias clipboardListView: clipboardListView
|
property alias clipboardListView: clipboardListView
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
focus: true
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -31,14 +31,13 @@ Item {
|
|||||||
onKeyboardHintsToggled: modal.showKeyboardHints = !modal.showKeyboardHints
|
onKeyboardHintsToggled: modal.showKeyboardHints = !modal.showKeyboardHints
|
||||||
onClearAllClicked: {
|
onClearAllClicked: {
|
||||||
clearConfirmDialog.show(I18n.tr("Clear All History?"), I18n.tr("This will permanently delete all clipboard history."), function () {
|
clearConfirmDialog.show(I18n.tr("Clear All History?"), I18n.tr("This will permanently delete all clipboard history."), function () {
|
||||||
modal.clearAll()
|
modal.clearAll();
|
||||||
modal.hide()
|
modal.hide();
|
||||||
}, function () {})
|
}, function () {});
|
||||||
}
|
}
|
||||||
onCloseClicked: modal.hide()
|
onCloseClicked: modal.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search Field
|
|
||||||
DankTextField {
|
DankTextField {
|
||||||
id: searchField
|
id: searchField
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -47,27 +46,24 @@ Item {
|
|||||||
showClearButton: true
|
showClearButton: true
|
||||||
focus: true
|
focus: true
|
||||||
ignoreTabKeys: true
|
ignoreTabKeys: true
|
||||||
keyForwardTargets: [modal.modalFocusScope]
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
modal.searchText = text
|
modal.searchText = text;
|
||||||
modal.updateFilteredModel()
|
modal.updateFilteredModel();
|
||||||
}
|
}
|
||||||
Keys.onEscapePressed: function (event) {
|
Keys.onPressed: event => {
|
||||||
modal.hide()
|
if (event.key === Qt.Key_Escape) {
|
||||||
event.accepted = true
|
modal.hide();
|
||||||
}
|
event.accepted = true;
|
||||||
Component.onCompleted: {
|
return;
|
||||||
Qt.callLater(function () {
|
}
|
||||||
forceActiveFocus()
|
modal.keyboardController?.handleKey(event);
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
Component.onCompleted: Qt.callLater(() => forceActiveFocus())
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: modal
|
target: modal
|
||||||
function onOpened() {
|
function onOpened() {
|
||||||
Qt.callLater(function () {
|
Qt.callLater(() => searchField.forceActiveFocus());
|
||||||
searchField.forceActiveFocus()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,21 +93,21 @@ Item {
|
|||||||
|
|
||||||
function ensureVisible(index) {
|
function ensureVisible(index) {
|
||||||
if (index < 0 || index >= count) {
|
if (index < 0 || index >= count) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const itemHeight = ClipboardConstants.itemHeight + spacing
|
const itemHeight = ClipboardConstants.itemHeight + spacing;
|
||||||
const itemY = index * itemHeight
|
const itemY = index * itemHeight;
|
||||||
const itemBottom = itemY + itemHeight
|
const itemBottom = itemY + itemHeight;
|
||||||
if (itemY < contentY) {
|
if (itemY < contentY) {
|
||||||
contentY = itemY
|
contentY = itemY;
|
||||||
} else if (itemBottom > contentY + height) {
|
} else if (itemBottom > contentY + height) {
|
||||||
contentY = itemBottom - height
|
contentY = itemBottom - height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCurrentIndexChanged: {
|
onCurrentIndexChanged: {
|
||||||
if (clipboardContent.modal && clipboardContent.modal.keyboardNavigationActive && currentIndex >= 0) {
|
if (clipboardContent.modal && clipboardContent.modal.keyboardNavigationActive && currentIndex >= 0) {
|
||||||
ensureVisible(currentIndex)
|
ensureVisible(currentIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
@@ -13,11 +12,6 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:clipboard"
|
layerNamespace: "dms:clipboard"
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [clipboardHistoryModal.contentWindow]
|
|
||||||
active: CompositorService.isHyprland && clipboardHistoryModal.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
property int totalCount: 0
|
property int totalCount: 0
|
||||||
property var clipboardEntries: []
|
property var clipboardEntries: []
|
||||||
property string searchText: ""
|
property string searchText: ""
|
||||||
@@ -148,11 +142,10 @@ DankModal {
|
|||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
enableShadow: true
|
enableShadow: true
|
||||||
onBackgroundClicked: hide()
|
onBackgroundClicked: hide()
|
||||||
modalFocusScope.Keys.onPressed: function (event) {
|
|
||||||
keyboardController.handleKey(event);
|
|
||||||
}
|
|
||||||
content: clipboardContent
|
content: clipboardContent
|
||||||
|
|
||||||
|
property alias keyboardController: keyboardController
|
||||||
|
|
||||||
ClipboardKeyboardController {
|
ClipboardKeyboardController {
|
||||||
id: keyboardController
|
id: keyboardController
|
||||||
modal: clipboardHistoryModal
|
modal: clipboardHistoryModal
|
||||||
@@ -165,13 +158,11 @@ DankModal {
|
|||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
clipboardHistoryModal.shouldHaveFocus = false;
|
clipboardHistoryModal.shouldHaveFocus = false;
|
||||||
} else if (clipboardHistoryModal.shouldBeVisible) {
|
return;
|
||||||
clipboardHistoryModal.shouldHaveFocus = true;
|
|
||||||
clipboardHistoryModal.modalFocusScope.forceActiveFocus();
|
|
||||||
if (clipboardHistoryModal.contentLoader.item && clipboardHistoryModal.contentLoader.item.searchField) {
|
|
||||||
clipboardHistoryModal.contentLoader.item.searchField.forceActiveFocus();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (!clipboardHistoryModal.shouldBeVisible)
|
||||||
|
return;
|
||||||
|
clipboardHistoryModal.shouldHaveFocus = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,29 +61,21 @@ DankModal {
|
|||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
allowStacking: true
|
allowStacking: true
|
||||||
modalWidth: 350
|
modalWidth: 350
|
||||||
modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 160
|
modalHeight: 160
|
||||||
enableShadow: true
|
enableShadow: true
|
||||||
shouldHaveFocus: true
|
shouldHaveFocus: true
|
||||||
onBackgroundClicked: {
|
onBackgroundClicked: {
|
||||||
close();
|
close();
|
||||||
if (onCancel) {
|
if (onCancel)
|
||||||
onCancel();
|
onCancel();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
onOpened: {
|
|
||||||
Qt.callLater(function () {
|
function handleKey(event) {
|
||||||
modalFocusScope.forceActiveFocus();
|
|
||||||
modalFocusScope.focus = true;
|
|
||||||
shouldHaveFocus = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
modalFocusScope.Keys.onPressed: function (event) {
|
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case Qt.Key_Escape:
|
case Qt.Key_Escape:
|
||||||
close();
|
close();
|
||||||
if (onCancel) {
|
if (onCancel)
|
||||||
onCancel();
|
onCancel();
|
||||||
}
|
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
break;
|
break;
|
||||||
case Qt.Key_Left:
|
case Qt.Key_Left:
|
||||||
@@ -99,46 +91,46 @@ DankModal {
|
|||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
break;
|
break;
|
||||||
case Qt.Key_N:
|
case Qt.Key_N:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (!(event.modifiers & Qt.ControlModifier))
|
||||||
keyboardNavigation = true;
|
return;
|
||||||
selectedButton = (selectedButton + 1) % 2;
|
keyboardNavigation = true;
|
||||||
event.accepted = true;
|
selectedButton = (selectedButton + 1) % 2;
|
||||||
}
|
event.accepted = true;
|
||||||
break;
|
break;
|
||||||
case Qt.Key_P:
|
case Qt.Key_P:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (!(event.modifiers & Qt.ControlModifier))
|
||||||
keyboardNavigation = true;
|
return;
|
||||||
selectedButton = selectedButton === -1 ? 1 : (selectedButton - 1 + 2) % 2;
|
keyboardNavigation = true;
|
||||||
event.accepted = true;
|
selectedButton = selectedButton === -1 ? 1 : (selectedButton - 1 + 2) % 2;
|
||||||
}
|
event.accepted = true;
|
||||||
break;
|
break;
|
||||||
case Qt.Key_J:
|
case Qt.Key_J:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (!(event.modifiers & Qt.ControlModifier))
|
||||||
keyboardNavigation = true;
|
return;
|
||||||
selectedButton = 1;
|
keyboardNavigation = true;
|
||||||
event.accepted = true;
|
selectedButton = 1;
|
||||||
}
|
event.accepted = true;
|
||||||
break;
|
break;
|
||||||
case Qt.Key_K:
|
case Qt.Key_K:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (!(event.modifiers & Qt.ControlModifier))
|
||||||
keyboardNavigation = true;
|
return;
|
||||||
selectedButton = 0;
|
keyboardNavigation = true;
|
||||||
event.accepted = true;
|
selectedButton = 0;
|
||||||
}
|
event.accepted = true;
|
||||||
break;
|
break;
|
||||||
case Qt.Key_H:
|
case Qt.Key_H:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (!(event.modifiers & Qt.ControlModifier))
|
||||||
keyboardNavigation = true;
|
return;
|
||||||
selectedButton = 0;
|
keyboardNavigation = true;
|
||||||
event.accepted = true;
|
selectedButton = 0;
|
||||||
}
|
event.accepted = true;
|
||||||
break;
|
break;
|
||||||
case Qt.Key_L:
|
case Qt.Key_L:
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (!(event.modifiers & Qt.ControlModifier))
|
||||||
keyboardNavigation = true;
|
return;
|
||||||
selectedButton = 1;
|
keyboardNavigation = true;
|
||||||
event.accepted = true;
|
selectedButton = 1;
|
||||||
}
|
event.accepted = true;
|
||||||
break;
|
break;
|
||||||
case Qt.Key_Tab:
|
case Qt.Key_Tab:
|
||||||
keyboardNavigation = true;
|
keyboardNavigation = true;
|
||||||
@@ -147,9 +139,9 @@ DankModal {
|
|||||||
break;
|
break;
|
||||||
case Qt.Key_Return:
|
case Qt.Key_Return:
|
||||||
case Qt.Key_Enter:
|
case Qt.Key_Enter:
|
||||||
if (selectedButton !== -1) {
|
if (selectedButton !== -1)
|
||||||
selectButton();
|
selectButton();
|
||||||
} else {
|
else {
|
||||||
selectedButton = 1;
|
selectedButton = 1;
|
||||||
selectButton();
|
selectButton();
|
||||||
}
|
}
|
||||||
@@ -159,10 +151,13 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
Item {
|
FocusScope {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
focus: true
|
||||||
implicitHeight: mainColumn.implicitHeight
|
implicitHeight: mainColumn.implicitHeight
|
||||||
|
|
||||||
|
Keys.onPressed: event => root.handleKey(event)
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: mainColumn
|
id: mainColumn
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
|||||||
@@ -1,24 +1,19 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string layerNamespace: "dms:modal"
|
property string layerNamespace: "dms:modal"
|
||||||
property alias content: contentLoader.sourceComponent
|
property Component content: null
|
||||||
property alias contentLoader: contentLoader
|
|
||||||
property Item directContent: null
|
property Item directContent: null
|
||||||
|
property Item loadedContent: null
|
||||||
|
readonly property var contentLoader: QtObject {
|
||||||
|
readonly property var item: root.directContent ?? root.loadedContent
|
||||||
|
}
|
||||||
property real modalWidth: 400
|
property real modalWidth: 400
|
||||||
property real modalHeight: 300
|
property real modalHeight: 300
|
||||||
property var targetScreen
|
property var targetScreen: null
|
||||||
readonly property var effectiveScreen: targetScreen || contentWindow.screen
|
|
||||||
readonly property real screenWidth: effectiveScreen?.width
|
|
||||||
readonly property real screenHeight: effectiveScreen?.height
|
|
||||||
readonly property real dpr: effectiveScreen ? CompositorService.getScreenScale(effectiveScreen) : 1
|
|
||||||
property bool showBackground: true
|
property bool showBackground: true
|
||||||
property real backgroundOpacity: 0.5
|
property real backgroundOpacity: 0.5
|
||||||
property string positioning: "center"
|
property string positioning: "center"
|
||||||
@@ -36,7 +31,6 @@ Item {
|
|||||||
property real borderWidth: 1
|
property real borderWidth: 1
|
||||||
property real cornerRadius: Theme.cornerRadius
|
property real cornerRadius: Theme.cornerRadius
|
||||||
property bool enableShadow: false
|
property bool enableShadow: false
|
||||||
property alias modalFocusScope: focusScope
|
|
||||||
property bool shouldBeVisible: false
|
property bool shouldBeVisible: false
|
||||||
property bool shouldHaveFocus: shouldBeVisible
|
property bool shouldHaveFocus: shouldBeVisible
|
||||||
property bool allowFocusOverride: false
|
property bool allowFocusOverride: false
|
||||||
@@ -45,48 +39,41 @@ Item {
|
|||||||
property bool keepPopoutsOpen: false
|
property bool keepPopoutsOpen: false
|
||||||
property var customKeyboardFocus: null
|
property var customKeyboardFocus: null
|
||||||
property bool useOverlayLayer: false
|
property bool useOverlayLayer: false
|
||||||
readonly property alias contentWindow: contentWindow
|
|
||||||
readonly property alias backgroundWindow: backgroundWindow
|
|
||||||
|
|
||||||
signal opened
|
signal opened
|
||||||
signal dialogClosed
|
signal dialogClosed
|
||||||
signal backgroundClicked
|
signal backgroundClicked
|
||||||
|
|
||||||
property bool animationsEnabled: true
|
onBackgroundClicked: {
|
||||||
readonly property bool useBackgroundWindow: true
|
if (closeOnBackgroundClick)
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
function open() {
|
function open() {
|
||||||
ModalManager.openModal(root);
|
ModalManager.openModal(root);
|
||||||
closeTimer.stop();
|
|
||||||
shouldBeVisible = true;
|
shouldBeVisible = true;
|
||||||
contentWindow.visible = false;
|
shouldHaveFocus = true;
|
||||||
if (useBackgroundWindow)
|
DankModalWindow.showModal(root);
|
||||||
backgroundWindow.visible = true;
|
opened();
|
||||||
Qt.callLater(() => {
|
}
|
||||||
contentWindow.visible = true;
|
|
||||||
shouldHaveFocus = false;
|
function openCentered() {
|
||||||
Qt.callLater(() => {
|
positioning = "center";
|
||||||
shouldHaveFocus = Qt.binding(() => shouldBeVisible);
|
open();
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
shouldBeVisible = false;
|
shouldBeVisible = false;
|
||||||
shouldHaveFocus = false;
|
shouldHaveFocus = false;
|
||||||
closeTimer.restart();
|
DankModalWindow.hideModal();
|
||||||
|
dialogClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
function instantClose() {
|
function instantClose() {
|
||||||
animationsEnabled = false;
|
|
||||||
shouldBeVisible = false;
|
shouldBeVisible = false;
|
||||||
shouldHaveFocus = false;
|
shouldHaveFocus = false;
|
||||||
closeTimer.stop();
|
DankModalWindow.hideModalInstant();
|
||||||
contentWindow.visible = false;
|
|
||||||
if (useBackgroundWindow)
|
|
||||||
backgroundWindow.visible = false;
|
|
||||||
dialogClosed();
|
dialogClosed();
|
||||||
Qt.callLater(() => animationsEnabled = true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
@@ -96,322 +83,9 @@ Item {
|
|||||||
Connections {
|
Connections {
|
||||||
target: ModalManager
|
target: ModalManager
|
||||||
function onCloseAllModalsExcept(excludedModal) {
|
function onCloseAllModalsExcept(excludedModal) {
|
||||||
if (excludedModal !== root && !allowStacking && shouldBeVisible) {
|
if (excludedModal === root || allowStacking || !shouldBeVisible)
|
||||||
close();
|
return;
|
||||||
}
|
close();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: closeTimer
|
|
||||||
interval: animationDuration + 120
|
|
||||||
onTriggered: {
|
|
||||||
if (!shouldBeVisible) {
|
|
||||||
contentWindow.visible = false;
|
|
||||||
if (useBackgroundWindow)
|
|
||||||
backgroundWindow.visible = false;
|
|
||||||
dialogClosed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property real shadowBuffer: 5
|
|
||||||
readonly property real alignedWidth: Theme.px(modalWidth, dpr)
|
|
||||||
readonly property real alignedHeight: Theme.px(modalHeight, dpr)
|
|
||||||
|
|
||||||
readonly property real alignedX: Theme.snap((() => {
|
|
||||||
switch (positioning) {
|
|
||||||
case "center":
|
|
||||||
return (screenWidth - alignedWidth) / 2;
|
|
||||||
case "top-right":
|
|
||||||
return Math.max(Theme.spacingL, screenWidth - alignedWidth - Theme.spacingL);
|
|
||||||
case "custom":
|
|
||||||
return customPosition.x;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
})(), dpr)
|
|
||||||
|
|
||||||
readonly property real alignedY: Theme.snap((() => {
|
|
||||||
switch (positioning) {
|
|
||||||
case "center":
|
|
||||||
return (screenHeight - alignedHeight) / 2;
|
|
||||||
case "top-right":
|
|
||||||
return Theme.barHeight + Theme.spacingXS;
|
|
||||||
case "custom":
|
|
||||||
return customPosition.y;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
})(), dpr)
|
|
||||||
|
|
||||||
PanelWindow {
|
|
||||||
id: backgroundWindow
|
|
||||||
visible: false
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
WlrLayershell.namespace: root.layerNamespace + ":background"
|
|
||||||
WlrLayershell.layer: WlrLayershell.Top
|
|
||||||
WlrLayershell.exclusiveZone: -1
|
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
left: true
|
|
||||||
right: true
|
|
||||||
bottom: true
|
|
||||||
}
|
|
||||||
|
|
||||||
mask: Region {
|
|
||||||
item: Rectangle {
|
|
||||||
x: root.alignedX
|
|
||||||
y: root.alignedY
|
|
||||||
width: root.shouldBeVisible ? root.alignedWidth : 0
|
|
||||||
height: root.shouldBeVisible ? root.alignedHeight : 0
|
|
||||||
}
|
|
||||||
intersection: Intersection.Xor
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: root.closeOnBackgroundClick && root.shouldBeVisible
|
|
||||||
onClicked: mouse => {
|
|
||||||
const clickX = mouse.x;
|
|
||||||
const clickY = mouse.y;
|
|
||||||
const outsideContent = clickX < root.alignedX || clickX > root.alignedX + root.alignedWidth || clickY < root.alignedY || clickY > root.alignedY + root.alignedHeight;
|
|
||||||
|
|
||||||
if (!outsideContent)
|
|
||||||
return;
|
|
||||||
root.backgroundClicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: background
|
|
||||||
anchors.fill: parent
|
|
||||||
color: "black"
|
|
||||||
opacity: root.showBackground && SettingsData.modalDarkenBackground ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
|
|
||||||
visible: root.showBackground && SettingsData.modalDarkenBackground
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
enabled: root.animationsEnabled
|
|
||||||
NumberAnimation {
|
|
||||||
duration: root.animationDuration
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PanelWindow {
|
|
||||||
id: contentWindow
|
|
||||||
visible: false
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
WlrLayershell.namespace: root.layerNamespace
|
|
||||||
WlrLayershell.layer: {
|
|
||||||
if (root.useOverlayLayer)
|
|
||||||
return WlrLayershell.Overlay;
|
|
||||||
switch (Quickshell.env("DMS_MODAL_LAYER")) {
|
|
||||||
case "bottom":
|
|
||||||
console.error("DankModal: 'bottom' layer is not valid for modals. Defaulting to 'top' layer.");
|
|
||||||
return WlrLayershell.Top;
|
|
||||||
case "background":
|
|
||||||
console.error("DankModal: 'background' layer is not valid for modals. Defaulting to 'top' layer.");
|
|
||||||
return WlrLayershell.Top;
|
|
||||||
case "overlay":
|
|
||||||
return WlrLayershell.Overlay;
|
|
||||||
default:
|
|
||||||
return WlrLayershell.Top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WlrLayershell.exclusiveZone: -1
|
|
||||||
WlrLayershell.keyboardFocus: {
|
|
||||||
if (customKeyboardFocus !== null)
|
|
||||||
return customKeyboardFocus;
|
|
||||||
if (!shouldHaveFocus)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (CompositorService.isHyprland)
|
|
||||||
return WlrKeyboardFocus.OnDemand;
|
|
||||||
return WlrKeyboardFocus.Exclusive;
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
left: true
|
|
||||||
top: true
|
|
||||||
}
|
|
||||||
|
|
||||||
WlrLayershell.margins {
|
|
||||||
left: Math.max(0, Theme.snap(root.alignedX - shadowBuffer, dpr))
|
|
||||||
top: Math.max(0, Theme.snap(root.alignedY - shadowBuffer, dpr))
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: root.alignedWidth + (shadowBuffer * 2)
|
|
||||||
implicitHeight: root.alignedHeight + (shadowBuffer * 2)
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible) {
|
|
||||||
opened();
|
|
||||||
} else {
|
|
||||||
if (Qt.inputMethod) {
|
|
||||||
Qt.inputMethod.hide();
|
|
||||||
Qt.inputMethod.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: modalContainer
|
|
||||||
x: shadowBuffer
|
|
||||||
y: shadowBuffer
|
|
||||||
width: root.alignedWidth
|
|
||||||
height: root.alignedHeight
|
|
||||||
|
|
||||||
readonly property bool slide: root.animationType === "slide"
|
|
||||||
readonly property real offsetX: slide ? 15 : 0
|
|
||||||
readonly property real offsetY: slide ? -30 : root.animationOffset
|
|
||||||
|
|
||||||
property real animX: 0
|
|
||||||
property real animY: 0
|
|
||||||
property real scaleValue: root.animationScaleCollapsed
|
|
||||||
|
|
||||||
onOffsetXChanged: animX = Theme.snap(root.shouldBeVisible ? 0 : offsetX, root.dpr)
|
|
||||||
onOffsetYChanged: animY = Theme.snap(root.shouldBeVisible ? 0 : offsetY, root.dpr)
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onShouldBeVisibleChanged() {
|
|
||||||
modalContainer.animX = Theme.snap(root.shouldBeVisible ? 0 : modalContainer.offsetX, root.dpr);
|
|
||||||
modalContainer.animY = Theme.snap(root.shouldBeVisible ? 0 : modalContainer.offsetY, root.dpr);
|
|
||||||
modalContainer.scaleValue = root.shouldBeVisible ? 1.0 : root.animationScaleCollapsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on animX {
|
|
||||||
enabled: root.animationsEnabled
|
|
||||||
NumberAnimation {
|
|
||||||
duration: root.animationDuration
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on animY {
|
|
||||||
enabled: root.animationsEnabled
|
|
||||||
NumberAnimation {
|
|
||||||
duration: root.animationDuration
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scaleValue {
|
|
||||||
enabled: root.animationsEnabled
|
|
||||||
NumberAnimation {
|
|
||||||
duration: root.animationDuration
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: contentContainer
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: parent.width
|
|
||||||
height: parent.height
|
|
||||||
clip: false
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: animatedContent
|
|
||||||
anchors.fill: parent
|
|
||||||
clip: false
|
|
||||||
opacity: root.shouldBeVisible ? 1 : 0
|
|
||||||
scale: modalContainer.scaleValue
|
|
||||||
x: Theme.snap(modalContainer.animX, root.dpr) + (parent.width - width) * (1 - modalContainer.scaleValue) * 0.5
|
|
||||||
y: Theme.snap(modalContainer.animY, root.dpr) + (parent.height - height) * (1 - modalContainer.scaleValue) * 0.5
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
enabled: root.animationsEnabled
|
|
||||||
NumberAnimation {
|
|
||||||
duration: animationDuration
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DankRectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: root.backgroundColor
|
|
||||||
borderColor: root.borderColor
|
|
||||||
borderWidth: root.borderWidth
|
|
||||||
radius: root.cornerRadius
|
|
||||||
}
|
|
||||||
|
|
||||||
FocusScope {
|
|
||||||
anchors.fill: parent
|
|
||||||
focus: root.shouldBeVisible
|
|
||||||
clip: false
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: directContentWrapper
|
|
||||||
anchors.fill: parent
|
|
||||||
visible: root.directContent !== null
|
|
||||||
focus: true
|
|
||||||
clip: false
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
if (root.directContent) {
|
|
||||||
root.directContent.parent = directContentWrapper;
|
|
||||||
root.directContent.anchors.fill = directContentWrapper;
|
|
||||||
Qt.callLater(() => root.directContent.forceActiveFocus());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onDirectContentChanged() {
|
|
||||||
if (root.directContent) {
|
|
||||||
root.directContent.parent = directContentWrapper;
|
|
||||||
root.directContent.anchors.fill = directContentWrapper;
|
|
||||||
Qt.callLater(() => root.directContent.forceActiveFocus());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: contentLoader
|
|
||||||
anchors.fill: parent
|
|
||||||
active: root.directContent === null && (root.keepContentLoaded || root.shouldBeVisible || contentWindow.visible)
|
|
||||||
asynchronous: false
|
|
||||||
focus: true
|
|
||||||
clip: false
|
|
||||||
visible: root.directContent === null
|
|
||||||
|
|
||||||
onLoaded: {
|
|
||||||
if (item) {
|
|
||||||
Qt.callLater(() => item.forceActiveFocus());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FocusScope {
|
|
||||||
id: focusScope
|
|
||||||
objectName: "modalFocusScope"
|
|
||||||
anchors.fill: parent
|
|
||||||
visible: root.shouldBeVisible || contentWindow.visible
|
|
||||||
focus: root.shouldBeVisible
|
|
||||||
Keys.onEscapePressed: event => {
|
|
||||||
if (root.closeOnEscapeKey && shouldHaveFocus) {
|
|
||||||
root.close();
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -11,11 +10,6 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:color-picker"
|
layerNamespace: "dms:color-picker"
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [root.contentWindow]
|
|
||||||
active: CompositorService.isHyprland && root.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
property string pickerTitle: I18n.tr("Choose Color")
|
property string pickerTitle: I18n.tr("Choose Color")
|
||||||
property color selectedColor: SessionData.recentColors.length > 0 ? SessionData.recentColors[0] : Theme.primary
|
property color selectedColor: SessionData.recentColors.length > 0 ? SessionData.recentColors[0] : Theme.primary
|
||||||
property var onColorSelectedCallback: null
|
property var onColorSelectedCallback: null
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Hyprland
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -17,12 +16,6 @@ DankModal {
|
|||||||
modalWidth: _maxW
|
modalWidth: _maxW
|
||||||
modalHeight: _maxH
|
modalHeight: _maxH
|
||||||
onBackgroundClicked: close()
|
onBackgroundClicked: close()
|
||||||
onOpened: () => Qt.callLater(() => modalFocusScope.forceActiveFocus())
|
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [root.contentWindow]
|
|
||||||
active: CompositorService.isHyprland && root.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollDown() {
|
function scrollDown() {
|
||||||
if (!root.activeFlickable)
|
if (!root.activeFlickable)
|
||||||
@@ -40,25 +33,35 @@ DankModal {
|
|||||||
root.activeFlickable.contentY = newY;
|
root.activeFlickable.contentY = newY;
|
||||||
}
|
}
|
||||||
|
|
||||||
modalFocusScope.Keys.onPressed: event => {
|
|
||||||
if (event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) {
|
|
||||||
scrollDown();
|
|
||||||
event.accepted = true;
|
|
||||||
} else if (event.key === Qt.Key_K && event.modifiers & Qt.ControlModifier) {
|
|
||||||
scrollUp();
|
|
||||||
event.accepted = true;
|
|
||||||
} else if (event.key === Qt.Key_Down) {
|
|
||||||
scrollDown();
|
|
||||||
event.accepted = true;
|
|
||||||
} else if (event.key === Qt.Key_Up) {
|
|
||||||
scrollUp();
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
Item {
|
FocusScope {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
Keys.onPressed: event => {
|
||||||
|
switch (event.key) {
|
||||||
|
case Qt.Key_J:
|
||||||
|
if (!(event.modifiers & Qt.ControlModifier))
|
||||||
|
return;
|
||||||
|
root.scrollDown();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
|
case Qt.Key_K:
|
||||||
|
if (!(event.modifiers & Qt.ControlModifier))
|
||||||
|
return;
|
||||||
|
root.scrollUp();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
|
case Qt.Key_Down:
|
||||||
|
root.scrollDown();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
|
case Qt.Key_Up:
|
||||||
|
root.scrollUp();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Hyprland
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
@@ -11,11 +10,6 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:notification-center-modal"
|
layerNamespace: "dms:notification-center-modal"
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [notificationModal.contentWindow]
|
|
||||||
active: CompositorService.isHyprland && notificationModal.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool notificationModalOpen: false
|
property bool notificationModalOpen: false
|
||||||
property var notificationListRef: null
|
property var notificationListRef: null
|
||||||
|
|
||||||
@@ -61,9 +55,6 @@ DankModal {
|
|||||||
modalHeight: 700
|
modalHeight: 700
|
||||||
visible: false
|
visible: false
|
||||||
onBackgroundClicked: hide()
|
onBackgroundClicked: hide()
|
||||||
onOpened: () => {
|
|
||||||
Qt.callLater(() => modalFocusScope.forceActiveFocus());
|
|
||||||
}
|
|
||||||
onShouldBeVisibleChanged: shouldBeVisible => {
|
onShouldBeVisibleChanged: shouldBeVisible => {
|
||||||
if (!shouldBeVisible) {
|
if (!shouldBeVisible) {
|
||||||
notificationModalOpen = false;
|
notificationModalOpen = false;
|
||||||
@@ -71,7 +62,6 @@ DankModal {
|
|||||||
NotificationService.onOverlayClose();
|
NotificationService.onOverlayClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
modalFocusScope.Keys.onPressed: event => modalKeyboardController.handleKey(event)
|
|
||||||
|
|
||||||
NotificationKeyboardController {
|
NotificationKeyboardController {
|
||||||
id: modalKeyboardController
|
id: modalKeyboardController
|
||||||
@@ -101,10 +91,12 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
Item {
|
FocusScope {
|
||||||
id: notificationKeyHandler
|
id: notificationKeyHandler
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
Keys.onPressed: event => modalKeyboardController.handleKey(event)
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -13,11 +12,6 @@ DankModal {
|
|||||||
layerNamespace: "dms:power-menu"
|
layerNamespace: "dms:power-menu"
|
||||||
keepPopoutsOpen: true
|
keepPopoutsOpen: true
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [root.contentWindow]
|
|
||||||
active: CompositorService.isHyprland && root.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
property int selectedIndex: 0
|
property int selectedIndex: 0
|
||||||
property int selectedRow: 0
|
property int selectedRow: 0
|
||||||
property int selectedCol: 0
|
property int selectedCol: 0
|
||||||
@@ -275,34 +269,11 @@ DankModal {
|
|||||||
} else {
|
} else {
|
||||||
selectedIndex = defaultIndex;
|
selectedIndex = defaultIndex;
|
||||||
}
|
}
|
||||||
Qt.callLater(() => modalFocusScope.forceActiveFocus());
|
|
||||||
}
|
}
|
||||||
onDialogClosed: () => {
|
onDialogClosed: () => {
|
||||||
cancelHold();
|
cancelHold();
|
||||||
}
|
}
|
||||||
Component.onCompleted: updateVisibleActions()
|
Component.onCompleted: updateVisibleActions()
|
||||||
modalFocusScope.Keys.onPressed: event => {
|
|
||||||
if (event.isAutoRepeat) {
|
|
||||||
event.accepted = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (SettingsData.powerMenuGridLayout) {
|
|
||||||
handleGridNavigation(event, true);
|
|
||||||
} else {
|
|
||||||
handleListNavigation(event, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modalFocusScope.Keys.onReleased: event => {
|
|
||||||
if (event.isAutoRepeat) {
|
|
||||||
event.accepted = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (SettingsData.powerMenuGridLayout) {
|
|
||||||
handleGridNavigation(event, false);
|
|
||||||
} else {
|
|
||||||
handleListNavigation(event, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleListNavigation(event, isPressed) {
|
function handleListNavigation(event, isPressed) {
|
||||||
if (!isPressed) {
|
if (!isPressed) {
|
||||||
@@ -481,10 +452,33 @@ DankModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
Item {
|
FocusScope {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
focus: true
|
||||||
implicitHeight: (SettingsData.powerMenuGridLayout ? buttonGrid.implicitHeight : buttonColumn.implicitHeight) + Theme.spacingL * 2 + (root.needsConfirmation ? hintRow.height + Theme.spacingM : 0)
|
implicitHeight: (SettingsData.powerMenuGridLayout ? buttonGrid.implicitHeight : buttonColumn.implicitHeight) + Theme.spacingL * 2 + (root.needsConfirmation ? hintRow.height + Theme.spacingM : 0)
|
||||||
|
|
||||||
|
Keys.onPressed: event => {
|
||||||
|
if (event.isAutoRepeat) {
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (SettingsData.powerMenuGridLayout)
|
||||||
|
root.handleGridNavigation(event, true);
|
||||||
|
else
|
||||||
|
root.handleListNavigation(event, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onReleased: event => {
|
||||||
|
if (event.isAutoRepeat) {
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (SettingsData.powerMenuGridLayout)
|
||||||
|
root.handleGridNavigation(event, false);
|
||||||
|
else
|
||||||
|
root.handleListNavigation(event, false);
|
||||||
|
}
|
||||||
|
|
||||||
Grid {
|
Grid {
|
||||||
id: buttonGrid
|
id: buttonGrid
|
||||||
visible: SettingsData.powerMenuGridLayout
|
visible: SettingsData.powerMenuGridLayout
|
||||||
|
|||||||
@@ -51,7 +51,21 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
clip: false
|
clip: false
|
||||||
|
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
if (!activeFocus)
|
||||||
|
return;
|
||||||
|
if (!searchField)
|
||||||
|
return;
|
||||||
|
searchField.forceActiveFocus();
|
||||||
|
}
|
||||||
Keys.onPressed: event => {
|
Keys.onPressed: event => {
|
||||||
|
const menu = usePopupContextMenu ? popupContextMenu : layerContextMenuLoader.item;
|
||||||
|
if (menu?.visible) {
|
||||||
|
menu.handleKey(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (event.key === Qt.Key_Escape) {
|
if (event.key === Qt.Key_Escape) {
|
||||||
if (parentModal)
|
if (parentModal)
|
||||||
parentModal.hide();
|
parentModal.hide();
|
||||||
@@ -197,7 +211,6 @@ Item {
|
|||||||
|
|
||||||
parent: spotlightKeyHandler
|
parent: spotlightKeyHandler
|
||||||
appLauncher: spotlightKeyHandler.appLauncher
|
appLauncher: spotlightKeyHandler.appLauncher
|
||||||
parentHandler: spotlightKeyHandler
|
|
||||||
searchField: spotlightKeyHandler.searchField
|
searchField: spotlightKeyHandler.searchField
|
||||||
visible: false
|
visible: false
|
||||||
z: 1000
|
z: 1000
|
||||||
@@ -218,8 +231,6 @@ Item {
|
|||||||
sourceComponent: Component {
|
sourceComponent: Component {
|
||||||
SpotlightContextMenu {
|
SpotlightContextMenu {
|
||||||
appLauncher: spotlightKeyHandler.appLauncher
|
appLauncher: spotlightKeyHandler.appLauncher
|
||||||
parentHandler: spotlightKeyHandler
|
|
||||||
parentModal: spotlightKeyHandler.parentModal
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,6 +291,12 @@ Item {
|
|||||||
updateSearchMode();
|
updateSearchMode();
|
||||||
}
|
}
|
||||||
Keys.onPressed: event => {
|
Keys.onPressed: event => {
|
||||||
|
const menu = spotlightKeyHandler.usePopupContextMenu ? popupContextMenu : layerContextMenuLoader.item;
|
||||||
|
if (menu?.visible) {
|
||||||
|
menu.handleKey(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (event.key === Qt.Key_Escape) {
|
if (event.key === Qt.Key_Escape) {
|
||||||
if (parentModal)
|
if (parentModal)
|
||||||
parentModal.hide();
|
parentModal.hide();
|
||||||
@@ -312,7 +329,7 @@ Item {
|
|||||||
Row {
|
Row {
|
||||||
id: viewModeButtons
|
id: viewModeButtons
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
visible: searchMode === "apps" && appLauncher.model.count > 0
|
visible: searchMode === "apps"
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import Quickshell.Widgets
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Spotlight
|
import qs.Modals.Spotlight
|
||||||
|
|
||||||
@@ -11,54 +10,66 @@ PanelWindow {
|
|||||||
WlrLayershell.namespace: "dms:spotlight-context-menu"
|
WlrLayershell.namespace: "dms:spotlight-context-menu"
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
|
||||||
property var appLauncher: null
|
property var appLauncher: null
|
||||||
property var parentHandler: null
|
|
||||||
property var parentModal: null
|
|
||||||
property real menuPositionX: 0
|
property real menuPositionX: 0
|
||||||
property real menuPositionY: 0
|
property real menuPositionY: 0
|
||||||
|
|
||||||
readonly property real shadowBuffer: 5
|
readonly property real shadowBuffer: 5
|
||||||
|
|
||||||
screen: parentModal?.effectiveScreen
|
screen: DankModalWindow.targetScreen
|
||||||
|
|
||||||
function show(x, y, app, fromKeyboard) {
|
function show(x, y, app, fromKeyboard) {
|
||||||
fromKeyboard = fromKeyboard || false;
|
fromKeyboard = fromKeyboard || false;
|
||||||
menuContent.currentApp = app;
|
menuContent.currentApp = app;
|
||||||
|
|
||||||
let screenX = x;
|
let screenX = x;
|
||||||
let screenY = y;
|
let screenY = y;
|
||||||
|
|
||||||
if (parentModal) {
|
const modalX = DankModalWindow.modalX;
|
||||||
if (fromKeyboard) {
|
const modalY = DankModalWindow.modalY;
|
||||||
screenX = x + parentModal.alignedX;
|
|
||||||
screenY = y + parentModal.alignedY;
|
if (fromKeyboard) {
|
||||||
} else {
|
screenX = x + modalX;
|
||||||
screenX = x + (parentModal.alignedX - shadowBuffer);
|
screenY = y + modalY;
|
||||||
screenY = y + (parentModal.alignedY - shadowBuffer);
|
} else {
|
||||||
}
|
screenX = x + (modalX - shadowBuffer);
|
||||||
|
screenY = y + (modalY - shadowBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
menuPositionX = screenX;
|
menuPositionX = screenX;
|
||||||
menuPositionY = screenY;
|
menuPositionY = screenY;
|
||||||
|
|
||||||
menuContent.selectedMenuIndex = fromKeyboard ? 0 : -1;
|
menuContent.selectedMenuIndex = fromKeyboard ? 0 : -1;
|
||||||
menuContent.keyboardNavigation = true;
|
menuContent.keyboardNavigation = true;
|
||||||
visible = true;
|
visible = true;
|
||||||
|
}
|
||||||
if (parentHandler) {
|
|
||||||
parentHandler.enabled = false;
|
function handleKey(event) {
|
||||||
|
switch (event.key) {
|
||||||
|
case Qt.Key_Down:
|
||||||
|
menuContent.selectNext();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
|
case Qt.Key_Up:
|
||||||
|
menuContent.selectPrevious();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
|
case Qt.Key_Return:
|
||||||
|
case Qt.Key_Enter:
|
||||||
|
menuContent.activateSelected();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
|
case Qt.Key_Escape:
|
||||||
|
case Qt.Key_Menu:
|
||||||
|
hide();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
Qt.callLater(() => {
|
|
||||||
menuContent.keyboardHandler.forceActiveFocus();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
if (parentHandler) {
|
|
||||||
parentHandler.enabled = true;
|
|
||||||
}
|
|
||||||
visible = false;
|
visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,11 +82,6 @@ PanelWindow {
|
|||||||
bottom: true
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (!visible && parentHandler) {
|
|
||||||
parentHandler.enabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SpotlightContextMenuContent {
|
SpotlightContextMenuContent {
|
||||||
id: menuContent
|
id: menuContent
|
||||||
|
|||||||
@@ -8,51 +8,57 @@ Popup {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var appLauncher: null
|
property var appLauncher: null
|
||||||
property var parentHandler: null
|
|
||||||
property var searchField: null
|
property var searchField: null
|
||||||
|
|
||||||
function show(x, y, app, fromKeyboard) {
|
function show(x, y, app, fromKeyboard) {
|
||||||
fromKeyboard = fromKeyboard || false;
|
fromKeyboard = fromKeyboard || false;
|
||||||
menuContent.currentApp = app;
|
menuContent.currentApp = app;
|
||||||
|
|
||||||
root.x = x + 4;
|
root.x = x + 4;
|
||||||
root.y = y + 4;
|
root.y = y + 4;
|
||||||
|
|
||||||
menuContent.selectedMenuIndex = fromKeyboard ? 0 : -1;
|
menuContent.selectedMenuIndex = fromKeyboard ? 0 : -1;
|
||||||
menuContent.keyboardNavigation = true;
|
menuContent.keyboardNavigation = true;
|
||||||
|
|
||||||
if (parentHandler) {
|
|
||||||
parentHandler.enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpened: {
|
function handleKey(event) {
|
||||||
Qt.callLater(() => {
|
switch (event.key) {
|
||||||
menuContent.keyboardHandler.forceActiveFocus();
|
case Qt.Key_Down:
|
||||||
});
|
menuContent.selectNext();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
|
case Qt.Key_Up:
|
||||||
|
menuContent.selectPrevious();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
|
case Qt.Key_Return:
|
||||||
|
case Qt.Key_Enter:
|
||||||
|
menuContent.activateSelected();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
|
case Qt.Key_Escape:
|
||||||
|
case Qt.Key_Menu:
|
||||||
|
hide();
|
||||||
|
event.accepted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
if (parentHandler) {
|
|
||||||
parentHandler.enabled = true;
|
|
||||||
}
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
width: menuContent.implicitWidth
|
width: menuContent.implicitWidth
|
||||||
height: menuContent.implicitHeight
|
height: menuContent.implicitHeight
|
||||||
padding: 0
|
padding: 0
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
closePolicy: Popup.CloseOnPressOutside
|
||||||
modal: true
|
modal: true
|
||||||
dim: false
|
dim: false
|
||||||
background: Item {}
|
background: Item {}
|
||||||
|
|
||||||
onClosed: {
|
onClosed: {
|
||||||
if (parentHandler) {
|
|
||||||
parentHandler.enabled = true;
|
|
||||||
}
|
|
||||||
if (searchField) {
|
if (searchField) {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
searchField.forceActiveFocus();
|
searchField.forceActiveFocus();
|
||||||
|
|||||||
@@ -1,20 +1,13 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Hyprland
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
|
||||||
|
|
||||||
DankModal {
|
DankModal {
|
||||||
id: spotlightModal
|
id: spotlightModal
|
||||||
|
|
||||||
layerNamespace: "dms:spotlight"
|
layerNamespace: "dms:spotlight"
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [spotlightModal.contentWindow]
|
|
||||||
active: CompositorService.isHyprland && spotlightModal.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool spotlightOpen: false
|
property bool spotlightOpen: false
|
||||||
property alias spotlightContent: spotlightContentInstance
|
property alias spotlightContent: spotlightContentInstance
|
||||||
property bool openedFromOverview: false
|
property bool openedFromOverview: false
|
||||||
@@ -23,32 +16,18 @@ DankModal {
|
|||||||
openedFromOverview = false;
|
openedFromOverview = false;
|
||||||
spotlightOpen = true;
|
spotlightOpen = true;
|
||||||
open();
|
open();
|
||||||
|
|
||||||
Qt.callLater(() => {
|
|
||||||
if (spotlightContent && spotlightContent.searchField) {
|
|
||||||
spotlightContent.searchField.forceActiveFocus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showWithQuery(query) {
|
function showWithQuery(query) {
|
||||||
if (spotlightContent) {
|
if (spotlightContent) {
|
||||||
if (spotlightContent.appLauncher) {
|
if (spotlightContent.appLauncher)
|
||||||
spotlightContent.appLauncher.searchQuery = query;
|
spotlightContent.appLauncher.searchQuery = query;
|
||||||
}
|
if (spotlightContent.searchField)
|
||||||
if (spotlightContent.searchField) {
|
|
||||||
spotlightContent.searchField.text = query;
|
spotlightContent.searchField.text = query;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spotlightOpen = true;
|
spotlightOpen = true;
|
||||||
open();
|
open();
|
||||||
|
|
||||||
Qt.callLater(() => {
|
|
||||||
if (spotlightContent && spotlightContent.searchField) {
|
|
||||||
spotlightContent.searchField.forceActiveFocus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
@@ -57,23 +36,24 @@ DankModal {
|
|||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
onDialogClosed: {
|
function onFullyClosed() {
|
||||||
if (spotlightContent) {
|
resetContent();
|
||||||
if (spotlightContent.appLauncher) {
|
}
|
||||||
spotlightContent.appLauncher.searchQuery = "";
|
|
||||||
spotlightContent.appLauncher.selectedIndex = 0;
|
function resetContent() {
|
||||||
spotlightContent.appLauncher.setCategory(I18n.tr("All"));
|
if (!spotlightContent)
|
||||||
}
|
return;
|
||||||
if (spotlightContent.fileSearchController) {
|
if (spotlightContent.appLauncher) {
|
||||||
spotlightContent.fileSearchController.reset();
|
spotlightContent.appLauncher.searchQuery = "";
|
||||||
}
|
spotlightContent.appLauncher.selectedIndex = 0;
|
||||||
if (spotlightContent.resetScroll) {
|
spotlightContent.appLauncher.setCategory(I18n.tr("All"));
|
||||||
spotlightContent.resetScroll();
|
|
||||||
}
|
|
||||||
if (spotlightContent.searchField) {
|
|
||||||
spotlightContent.searchField.text = "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (spotlightContent.fileSearchController)
|
||||||
|
spotlightContent.fileSearchController.reset();
|
||||||
|
if (spotlightContent.resetScroll)
|
||||||
|
spotlightContent.resetScroll();
|
||||||
|
if (spotlightContent.searchField)
|
||||||
|
spotlightContent.searchField.text = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
@@ -94,16 +74,10 @@ DankModal {
|
|||||||
enableShadow: true
|
enableShadow: true
|
||||||
keepContentLoaded: true
|
keepContentLoaded: true
|
||||||
onVisibleChanged: () => {
|
onVisibleChanged: () => {
|
||||||
if (visible && !spotlightOpen) {
|
if (!visible)
|
||||||
|
return;
|
||||||
|
if (!spotlightOpen)
|
||||||
show();
|
show();
|
||||||
}
|
|
||||||
if (visible && spotlightContent) {
|
|
||||||
Qt.callLater(() => {
|
|
||||||
if (spotlightContent.searchField) {
|
|
||||||
spotlightContent.searchField.forceActiveFocus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
onBackgroundClicked: () => {
|
onBackgroundClicked: () => {
|
||||||
return hide();
|
return hide();
|
||||||
|
|||||||
@@ -95,10 +95,6 @@ FloatingWindow {
|
|||||||
if (!parentModal)
|
if (!parentModal)
|
||||||
return;
|
return;
|
||||||
parentModal.shouldHaveFocus = Qt.binding(() => parentModal.shouldBeVisible);
|
parentModal.shouldHaveFocus = Qt.binding(() => parentModal.shouldBeVisible);
|
||||||
Qt.callLater(() => {
|
|
||||||
if (parentModal.modalFocusScope)
|
|
||||||
parentModal.modalFocusScope.forceActiveFocus();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
objectName: "pluginBrowser"
|
objectName: "pluginBrowser"
|
||||||
|
|||||||
@@ -82,10 +82,6 @@ FloatingWindow {
|
|||||||
if (!parentModal)
|
if (!parentModal)
|
||||||
return;
|
return;
|
||||||
parentModal.shouldHaveFocus = Qt.binding(() => parentModal.shouldBeVisible);
|
parentModal.shouldHaveFocus = Qt.binding(() => parentModal.shouldBeVisible);
|
||||||
Qt.callLater(() => {
|
|
||||||
if (parentModal && parentModal.modalFocusScope)
|
|
||||||
parentModal.modalFocusScope.forceActiveFocus();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
objectName: "widgetSelectionPopup"
|
objectName: "widgetSelectionPopup"
|
||||||
@@ -112,10 +108,6 @@ FloatingWindow {
|
|||||||
if (!parentModal)
|
if (!parentModal)
|
||||||
return;
|
return;
|
||||||
parentModal.shouldHaveFocus = Qt.binding(() => parentModal.shouldBeVisible);
|
parentModal.shouldHaveFocus = Qt.binding(() => parentModal.shouldBeVisible);
|
||||||
Qt.callLater(() => {
|
|
||||||
if (parentModal && parentModal.modalFocusScope)
|
|
||||||
parentModal.modalFocusScope.forceActiveFocus();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.I3
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
@@ -23,6 +24,31 @@ Singleton {
|
|||||||
readonly property string labwcPid: Quickshell.env("LABWC_PID")
|
readonly property string labwcPid: Quickshell.env("LABWC_PID")
|
||||||
property bool useNiriSorting: isNiri && NiriService
|
property bool useNiriSorting: isNiri && NiriService
|
||||||
|
|
||||||
|
readonly property string focusedScreenName: {
|
||||||
|
if (isHyprland && Hyprland.focusedMonitor)
|
||||||
|
return Hyprland.focusedMonitor.name;
|
||||||
|
if (isNiri && NiriService.currentOutput)
|
||||||
|
return NiriService.currentOutput;
|
||||||
|
if (isDwl && DwlService.activeOutput)
|
||||||
|
return DwlService.activeOutput;
|
||||||
|
if (isSway) {
|
||||||
|
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true);
|
||||||
|
if (focusedWs?.monitor?.name)
|
||||||
|
return focusedWs.monitor.name;
|
||||||
|
}
|
||||||
|
return Quickshell.screens[0]?.name ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var focusedScreen: {
|
||||||
|
if (!focusedScreenName)
|
||||||
|
return Quickshell.screens[0] ?? null;
|
||||||
|
for (const s of Quickshell.screens) {
|
||||||
|
if (s.name === focusedScreenName)
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return Quickshell.screens[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
property var sortedToplevels: []
|
property var sortedToplevels: []
|
||||||
property bool _sortScheduled: false
|
property bool _sortScheduled: false
|
||||||
|
|
||||||
|
|||||||
@@ -666,11 +666,7 @@ Singleton {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SessionData.nightModeAutoEnabled) {
|
evaluateNightMode();
|
||||||
startAutomation();
|
|
||||||
} else {
|
|
||||||
applyNightModeDirectly();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -680,7 +676,7 @@ Singleton {
|
|||||||
Timer {
|
Timer {
|
||||||
id: restartTimer
|
id: restartTimer
|
||||||
property string nextAction: ""
|
property string nextAction: ""
|
||||||
interval: 100
|
interval: 250
|
||||||
repeat: false
|
repeat: false
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
|||||||
Reference in New Issue
Block a user