mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 05:25:41 -05:00
Add DankModal and SystemLogo widgets
This commit is contained in:
@@ -81,7 +81,9 @@ PanelWindow {
|
||||
if (viewMode === "grid") {
|
||||
// Grid navigation: move by columns
|
||||
var columnsCount = appGrid.columns || 4;
|
||||
selectedIndex = Math.min(selectedIndex + columnsCount, filteredModel.count - 1);
|
||||
var newIndex = Math.min(selectedIndex + columnsCount, filteredModel.count - 1);
|
||||
console.log("Grid navigation DOWN: from", selectedIndex, "to", newIndex, "columns:", columnsCount);
|
||||
selectedIndex = newIndex;
|
||||
} else {
|
||||
// List navigation: next item
|
||||
selectedIndex = (selectedIndex + 1) % filteredModel.count;
|
||||
@@ -94,7 +96,9 @@ PanelWindow {
|
||||
if (viewMode === "grid") {
|
||||
// Grid navigation: move by columns
|
||||
var columnsCount = appGrid.columns || 4;
|
||||
selectedIndex = Math.max(selectedIndex - columnsCount, 0);
|
||||
var newIndex = Math.max(selectedIndex - columnsCount, 0);
|
||||
console.log("Grid navigation UP: from", selectedIndex, "to", newIndex, "columns:", columnsCount);
|
||||
selectedIndex = newIndex;
|
||||
} else {
|
||||
// List navigation: previous item
|
||||
selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : filteredModel.count - 1;
|
||||
@@ -145,9 +149,6 @@ PanelWindow {
|
||||
searchField.enabled = true;
|
||||
searchDebounceTimer.stop(); // Stop any pending search
|
||||
updateFilteredModel();
|
||||
Qt.callLater(function() {
|
||||
searchField.forceActiveFocus();
|
||||
});
|
||||
}
|
||||
|
||||
function hide() {
|
||||
@@ -368,6 +369,12 @@ PanelWindow {
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
|
||||
Component.onCompleted: {
|
||||
if (launcher.isVisible) {
|
||||
forceActiveFocus();
|
||||
}
|
||||
}
|
||||
// Handle keyboard shortcuts
|
||||
Keys.onPressed: function(event) {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
@@ -388,6 +395,11 @@ PanelWindow {
|
||||
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
launchSelected();
|
||||
event.accepted = true;
|
||||
} else if (event.text && event.text.length > 0 && event.text.match(/[a-zA-Z0-9\s]/)) {
|
||||
// User started typing, focus search field and pass the character
|
||||
searchField.forceActiveFocus();
|
||||
searchField.text = event.text;
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,14 +453,14 @@ PanelWindow {
|
||||
leftIconFocusedColor: Theme.primary
|
||||
showClearButton: true
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
focus: launcher.isVisible
|
||||
enabled: launcher.isVisible
|
||||
placeholderText: "Search applications..."
|
||||
onTextEdited: {
|
||||
searchDebounceTimer.restart();
|
||||
}
|
||||
Keys.onPressed: function(event) {
|
||||
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && filteredModel.count) {
|
||||
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && filteredModel.count && text.length > 0) {
|
||||
// Launch first app when typing in search field
|
||||
var firstApp = filteredModel.get(0);
|
||||
if (firstApp.desktopEntry) {
|
||||
Prefs.addRecentApp(firstApp.desktopEntry);
|
||||
@@ -458,9 +470,12 @@ PanelWindow {
|
||||
}
|
||||
launcher.hide();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Escape) {
|
||||
launcher.hide();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up ||
|
||||
(event.key === Qt.Key_Left && viewMode === "grid") ||
|
||||
(event.key === Qt.Key_Right && viewMode === "grid") ||
|
||||
((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length === 0)) {
|
||||
// Pass navigation keys and enter (when not searching) to main handler
|
||||
event.accepted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -592,6 +607,7 @@ PanelWindow {
|
||||
itemHeight: 72
|
||||
iconSize: 56
|
||||
showDescription: true
|
||||
hoverUpdatesSelection: false
|
||||
onItemClicked: function(index, modelData) {
|
||||
if (modelData.desktopEntry) {
|
||||
Prefs.addRecentApp(modelData.desktopEntry);
|
||||
@@ -616,6 +632,7 @@ PanelWindow {
|
||||
columns: 4
|
||||
adaptiveColumns: false
|
||||
currentIndex: selectedIndex
|
||||
hoverUpdatesSelection: false
|
||||
onItemClicked: function(index, modelData) {
|
||||
if (modelData.desktopEntry) {
|
||||
Prefs.addRecentApp(modelData.desktopEntry);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,9 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
DankModal {
|
||||
id: inputDialog
|
||||
|
||||
property bool dialogVisible: false
|
||||
@@ -39,298 +36,118 @@ PanelWindow {
|
||||
}
|
||||
|
||||
visible: dialogVisible
|
||||
WlrLayershell.layer: WlrLayershell.Overlay
|
||||
WlrLayershell.exclusiveZone: -1
|
||||
WlrLayershell.keyboardFocus: dialogVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||
color: "transparent"
|
||||
size: "medium"
|
||||
keyboardFocus: "exclusive"
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
textInput.enabled = true;
|
||||
Qt.callLater(function() {
|
||||
textInput.forceActiveFocus();
|
||||
textInput.text = inputValue;
|
||||
});
|
||||
} else {
|
||||
textInput.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
onBackgroundClicked: {
|
||||
hideDialog();
|
||||
cancelled();
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(0, 0, 0, 0.5)
|
||||
opacity: dialogVisible ? 1 : 0
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
textInput.enabled = false; // Disable before hiding to prevent Wayland warnings
|
||||
inputDialog.cancelled();
|
||||
hideDialog();
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: Math.min(400, parent.width - Theme.spacingL * 2)
|
||||
height: Math.min(250, parent.height - Theme.spacingL * 2)
|
||||
anchors.centerIn: parent
|
||||
color: Theme.surfaceContainer
|
||||
radius: Theme.cornerRadiusLarge
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
border.width: 1
|
||||
opacity: dialogVisible ? 1 : 0
|
||||
scale: dialogVisible ? 1 : 0.9
|
||||
|
||||
content: Component {
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Theme.spacingL * 2
|
||||
spacing: Theme.spacingL
|
||||
|
||||
// Header
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
Column {
|
||||
width: parent.width - 40
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: dialogTitle
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
Text {
|
||||
text: dialogSubtitle
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 2
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
iconName: "close"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: Theme.surfaceText
|
||||
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||
onClicked: {
|
||||
inputDialog.cancelled();
|
||||
hideDialog();
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: dialogTitle
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
// Text input
|
||||
Rectangle {
|
||||
Text {
|
||||
text: dialogSubtitle
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
|
||||
border.color: textInput.activeFocus ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
border.width: textInput.activeFocus ? 2 : 1
|
||||
|
||||
DankTextField {
|
||||
id: textInput
|
||||
|
||||
anchors.fill: parent
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
textColor: Theme.surfaceText
|
||||
echoMode: isPassword && !showPasswordCheckbox.checked ? TextInput.Password : TextInput.Normal
|
||||
enabled: dialogVisible
|
||||
placeholderText: inputPlaceholder
|
||||
backgroundColor: "transparent"
|
||||
normalBorderColor: "transparent"
|
||||
focusedBorderColor: "transparent"
|
||||
onTextEdited: {
|
||||
inputValue = text;
|
||||
}
|
||||
onAccepted: {
|
||||
inputDialog.confirmed(inputValue);
|
||||
hideDialog();
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if (dialogVisible)
|
||||
forceActiveFocus();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: textInput
|
||||
width: parent.width
|
||||
placeholderText: inputPlaceholder
|
||||
text: inputValue
|
||||
echoMode: isPassword ? TextInput.Password : TextInput.Normal
|
||||
onTextChanged: inputValue = text
|
||||
onAccepted: {
|
||||
hideDialog();
|
||||
confirmed(text);
|
||||
}
|
||||
}
|
||||
|
||||
// Show password checkbox (only visible for password inputs)
|
||||
Row {
|
||||
spacing: Theme.spacingS
|
||||
visible: isPassword
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Rectangle {
|
||||
id: showPasswordCheckbox
|
||||
width: 120
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: cancelButton.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||
|
||||
property bool checked: false
|
||||
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 4
|
||||
color: checked ? Theme.primary : "transparent"
|
||||
border.color: checked ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.5)
|
||||
border.width: 2
|
||||
|
||||
DankIcon {
|
||||
Text {
|
||||
text: cancelButtonText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.centerIn: parent
|
||||
name: "check"
|
||||
size: 12
|
||||
color: Theme.background
|
||||
visible: parent.checked
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: cancelButton
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
showPasswordCheckbox.checked = !showPasswordCheckbox.checked;
|
||||
hideDialog();
|
||||
cancelled();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Show password"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Buttons
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 40
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Rectangle {
|
||||
width: Math.max(70, cancelText.contentWidth + Theme.spacingM * 2)
|
||||
height: 36
|
||||
radius: Theme.cornerRadius
|
||||
color: cancelArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||
border.width: 1
|
||||
|
||||
Text {
|
||||
id: cancelText
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: cancelButtonText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: cancelArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
textInput.enabled = false; // Disable before hiding to prevent Wayland warnings
|
||||
inputDialog.cancelled();
|
||||
hideDialog();
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
width: 120
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: confirmButton.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.9) : Theme.primary
|
||||
|
||||
Text {
|
||||
text: confirmButtonText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.primaryText
|
||||
font.weight: Font.Medium
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: Math.max(80, confirmText.contentWidth + Theme.spacingM * 2)
|
||||
height: 36
|
||||
radius: Theme.cornerRadius
|
||||
color: confirmArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||
enabled: inputValue.length > 0
|
||||
opacity: enabled ? 1 : 0.5
|
||||
|
||||
Text {
|
||||
id: confirmText
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: confirmButtonText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.background
|
||||
font.weight: Font.Medium
|
||||
MouseArea {
|
||||
id: confirmButton
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
hideDialog();
|
||||
confirmed(textInput.text);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: confirmArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: parent.enabled
|
||||
onClicked: {
|
||||
inputDialog.confirmed(inputValue);
|
||||
hideDialog();
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
DankModal {
|
||||
id: root
|
||||
|
||||
property bool powerConfirmVisible: false
|
||||
@@ -37,173 +35,13 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// DankModal configuration
|
||||
visible: powerConfirmVisible
|
||||
implicitWidth: 400
|
||||
implicitHeight: 300
|
||||
WlrLayershell.layer: WlrLayershell.Overlay
|
||||
WlrLayershell.exclusiveZone: -1
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
// Darkened background
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
opacity: 0.5
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: Math.min(400, parent.width - Theme.spacingL * 2)
|
||||
height: Math.min(200, parent.height - Theme.spacingL * 2)
|
||||
anchors.centerIn: parent
|
||||
color: Theme.surfaceContainer
|
||||
radius: Theme.cornerRadiusLarge
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
border.width: 1
|
||||
opacity: powerConfirmVisible ? 1 : 0
|
||||
scale: powerConfirmVisible ? 1 : 0.9
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Theme.spacingL * 2
|
||||
spacing: Theme.spacingL
|
||||
|
||||
// Title
|
||||
Text {
|
||||
text: powerConfirmTitle
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: {
|
||||
switch (powerConfirmAction) {
|
||||
case "poweroff":
|
||||
return Theme.error;
|
||||
case "reboot":
|
||||
return Theme.warning;
|
||||
default:
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
}
|
||||
font.weight: Font.Medium
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
// Message
|
||||
Text {
|
||||
text: powerConfirmMessage
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Item {
|
||||
height: Theme.spacingL
|
||||
}
|
||||
|
||||
// Buttons
|
||||
Row {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
// Cancel button
|
||||
Rectangle {
|
||||
width: 120
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: cancelButton.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||
|
||||
Text {
|
||||
text: "Cancel"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: cancelButton
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
powerConfirmVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Confirm button
|
||||
Rectangle {
|
||||
width: 120
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
let baseColor;
|
||||
switch (powerConfirmAction) {
|
||||
case "poweroff":
|
||||
baseColor = Theme.error;
|
||||
break;
|
||||
case "reboot":
|
||||
baseColor = Theme.warning;
|
||||
break;
|
||||
default:
|
||||
baseColor = Theme.primary;
|
||||
break;
|
||||
}
|
||||
return confirmButton.containsMouse ? Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 0.9) : baseColor;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Confirm"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.primaryText
|
||||
font.weight: Font.Medium
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: confirmButton
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
powerConfirmVisible = false;
|
||||
executePowerAction(powerConfirmAction);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
size: "small"
|
||||
keyboardFocus: "ondemand"
|
||||
enableShadow: false
|
||||
onBackgroundClicked: {
|
||||
powerConfirmVisible = false;
|
||||
}
|
||||
|
||||
Process {
|
||||
@@ -217,4 +55,130 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
|
||||
content: Component {
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Theme.spacingL * 2
|
||||
spacing: Theme.spacingL
|
||||
|
||||
// Title
|
||||
Text {
|
||||
text: powerConfirmTitle
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: {
|
||||
switch (powerConfirmAction) {
|
||||
case "poweroff":
|
||||
return Theme.error;
|
||||
case "reboot":
|
||||
return Theme.warning;
|
||||
default:
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
}
|
||||
font.weight: Font.Medium
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
// Message
|
||||
Text {
|
||||
text: powerConfirmMessage
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Item {
|
||||
height: Theme.spacingL
|
||||
}
|
||||
|
||||
// Buttons
|
||||
Row {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
// Cancel button
|
||||
Rectangle {
|
||||
width: 120
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: cancelButton.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||
|
||||
Text {
|
||||
text: "Cancel"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: cancelButton
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
powerConfirmVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Confirm button
|
||||
Rectangle {
|
||||
width: 120
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
let baseColor;
|
||||
switch (powerConfirmAction) {
|
||||
case "poweroff":
|
||||
baseColor = Theme.error;
|
||||
break;
|
||||
case "reboot":
|
||||
baseColor = Theme.warning;
|
||||
break;
|
||||
default:
|
||||
baseColor = Theme.primary;
|
||||
break;
|
||||
}
|
||||
return confirmButton.containsMouse ? Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 0.9) : baseColor;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Confirm"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.primaryText
|
||||
font.weight: Font.Medium
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: confirmButton
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
powerConfirmVisible = false;
|
||||
executePowerAction(powerConfirmAction);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2121,4 +2121,4 @@ PanelWindow {
|
||||
target: "processlist"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,73 +1,35 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
import qs.Modules.Settings
|
||||
|
||||
PanelWindow {
|
||||
DankModal {
|
||||
id: settingsPopup
|
||||
|
||||
property bool settingsVisible: false
|
||||
|
||||
signal closingPopup()
|
||||
|
||||
onSettingsVisibleChanged: {
|
||||
if (!settingsVisible) {
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
closingPopup();
|
||||
// Hide any open dropdown when settings close
|
||||
if (typeof globalDropdownWindow !== 'undefined') {
|
||||
globalDropdownWindow.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DankModal configuration
|
||||
visible: settingsVisible
|
||||
implicitWidth: 600
|
||||
implicitHeight: 700
|
||||
WlrLayershell.layer: WlrLayershell.Overlay
|
||||
WlrLayershell.exclusiveZone: -1
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
color: "transparent"
|
||||
size: "extra-large"
|
||||
keyboardFocus: "ondemand"
|
||||
enableShadow: true
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
onBackgroundClicked: {
|
||||
settingsVisible = false;
|
||||
}
|
||||
|
||||
// Darkened background
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
opacity: 0.5
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: settingsPopup.settingsVisible = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Main settings panel - spotlight-like centered appearance
|
||||
Rectangle {
|
||||
id: mainPanel
|
||||
|
||||
width: Math.min(600, parent.width - Theme.spacingXL * 2)
|
||||
height: Math.min(700, parent.height - Theme.spacingXL * 2)
|
||||
anchors.centerIn: parent
|
||||
color: Theme.popupBackground()
|
||||
radius: Theme.cornerRadiusLarge
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
// Simple opacity and scale control tied directly to settingsVisible
|
||||
opacity: settingsPopup.settingsVisible ? 1 : 0
|
||||
scale: settingsPopup.settingsVisible ? 1 : 0.95
|
||||
// Add shadow effect
|
||||
layer.enabled: true
|
||||
|
||||
content: Component {
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
@@ -111,40 +73,19 @@ PanelWindow {
|
||||
}
|
||||
|
||||
// Settings sections
|
||||
Flickable {
|
||||
ScrollView {
|
||||
id: settingsScrollView
|
||||
width: parent.width
|
||||
height: parent.height - 80
|
||||
height: parent.height - 50
|
||||
clip: true
|
||||
contentHeight: settingsColumn.height
|
||||
boundsBehavior: Flickable.DragAndOvershootBounds
|
||||
flickDeceleration: 8000
|
||||
maximumFlickVelocity: 15000
|
||||
|
||||
property real wheelStepSize: 60
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
z: -1
|
||||
onWheel: (wheel) => {
|
||||
var delta = wheel.angleDelta.y
|
||||
var steps = delta / 120
|
||||
settingsScrollView.contentY -= steps * settingsScrollView.wheelStepSize
|
||||
|
||||
// Keep within bounds
|
||||
if (settingsScrollView.contentY < 0)
|
||||
settingsScrollView.contentY = 0
|
||||
else if (settingsScrollView.contentY > settingsScrollView.contentHeight - settingsScrollView.height)
|
||||
settingsScrollView.contentY = Math.max(0, settingsScrollView.contentHeight - settingsScrollView.height)
|
||||
}
|
||||
}
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
Column {
|
||||
id: settingsColumn
|
||||
width: parent.width
|
||||
width: settingsScrollView.width - 20
|
||||
spacing: Theme.spacingL
|
||||
bottomPadding: Theme.spacingL
|
||||
|
||||
// Profile Settings
|
||||
SettingsSection {
|
||||
@@ -193,32 +134,6 @@ PanelWindow {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: true
|
||||
shadowHorizontalOffset: 0
|
||||
shadowVerticalOffset: 8
|
||||
shadowBlur: 1
|
||||
shadowColor: Qt.rgba(0, 0, 0, 0.3)
|
||||
shadowOpacity: 0.3
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Keyboard focus and shortcuts
|
||||
|
||||
@@ -59,4 +59,13 @@ Column {
|
||||
return Prefs.setShowSystemTray(checked);
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
text: "Use OS Logo for App Launcher"
|
||||
description: "Display operating system logo instead of apps icon"
|
||||
checked: Prefs.useOSLogo
|
||||
onToggled: (checked) => {
|
||||
return Prefs.setUseOSLogo(checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,11 @@ import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
DankModal {
|
||||
id: spotlightLauncher
|
||||
|
||||
property bool spotlightOpen: false
|
||||
@@ -28,26 +26,20 @@ PanelWindow {
|
||||
}
|
||||
property string selectedCategory: "All"
|
||||
property string viewMode: Prefs.spotlightLauncherViewMode // "list" or "grid"
|
||||
property int gridColumns: 4
|
||||
|
||||
// ...existing code...
|
||||
function show() {
|
||||
console.log("SpotlightLauncher: show() called");
|
||||
spotlightOpen = true;
|
||||
searchField.enabled = true;
|
||||
console.log("SpotlightLauncher: spotlightOpen set to", spotlightOpen);
|
||||
searchDebounceTimer.stop(); // Stop any pending search
|
||||
updateFilteredApps(); // Immediate update when showing
|
||||
Qt.callLater(function() {
|
||||
searchField.forceActiveFocus();
|
||||
searchField.selectAll();
|
||||
});
|
||||
}
|
||||
|
||||
function hide() {
|
||||
searchField.enabled = false; // Disable before hiding to prevent Wayland warnings
|
||||
spotlightOpen = false;
|
||||
searchDebounceTimer.stop(); // Stop any pending search
|
||||
searchField.text = "";
|
||||
searchQuery = "";
|
||||
selectedIndex = 0;
|
||||
selectedCategory = "All";
|
||||
updateFilteredApps();
|
||||
@@ -60,11 +52,13 @@ PanelWindow {
|
||||
show();
|
||||
}
|
||||
|
||||
property string searchQuery: ""
|
||||
property bool shouldFocusSearch: false
|
||||
|
||||
function updateFilteredApps() {
|
||||
filteredApps = [];
|
||||
selectedIndex = 0;
|
||||
var apps = [];
|
||||
var searchQuery = searchField.text;
|
||||
if (searchQuery.length === 0) {
|
||||
// Show apps from category
|
||||
if (selectedCategory === "All") {
|
||||
@@ -156,58 +150,95 @@ PanelWindow {
|
||||
}
|
||||
|
||||
function selectNext() {
|
||||
if (filteredApps.length > 0) {
|
||||
if (filteredModel.count > 0) {
|
||||
if (viewMode === "grid") {
|
||||
// Grid navigation: move by columns
|
||||
var columnsCount = resultsGrid.columns || 6;
|
||||
selectedIndex = Math.min(selectedIndex + columnsCount, filteredApps.length - 1);
|
||||
// Grid navigation: move DOWN by one row (gridColumns positions)
|
||||
var columnsCount = gridColumns;
|
||||
var newIndex = Math.min(selectedIndex + columnsCount, filteredModel.count - 1);
|
||||
selectedIndex = newIndex;
|
||||
} else {
|
||||
// List navigation: next item
|
||||
selectedIndex = (selectedIndex + 1) % filteredApps.length;
|
||||
selectedIndex = (selectedIndex + 1) % filteredModel.count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function selectPrevious() {
|
||||
if (filteredApps.length > 0) {
|
||||
if (filteredModel.count > 0) {
|
||||
if (viewMode === "grid") {
|
||||
// Grid navigation: move by columns
|
||||
var columnsCount = resultsGrid.columns || 6;
|
||||
selectedIndex = Math.max(selectedIndex - columnsCount, 0);
|
||||
// Grid navigation: move UP by one row (gridColumns positions)
|
||||
var columnsCount = gridColumns;
|
||||
var newIndex = Math.max(selectedIndex - columnsCount, 0);
|
||||
selectedIndex = newIndex;
|
||||
} else {
|
||||
// List navigation: previous item
|
||||
selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : filteredApps.length - 1;
|
||||
selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : filteredModel.count - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function selectNextInRow() {
|
||||
if (filteredApps.length > 0 && viewMode === "grid")
|
||||
selectedIndex = Math.min(selectedIndex + 1, filteredApps.length - 1);
|
||||
|
||||
if (filteredModel.count > 0 && viewMode === "grid") {
|
||||
// Grid navigation: move RIGHT by one position
|
||||
selectedIndex = Math.min(selectedIndex + 1, filteredModel.count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
function selectPreviousInRow() {
|
||||
if (filteredApps.length > 0 && viewMode === "grid")
|
||||
if (filteredModel.count > 0 && viewMode === "grid") {
|
||||
// Grid navigation: move LEFT by one position
|
||||
selectedIndex = Math.max(selectedIndex - 1, 0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function launchSelected() {
|
||||
if (filteredApps.length > 0 && selectedIndex >= 0 && selectedIndex < filteredApps.length)
|
||||
launchApp(filteredApps[selectedIndex]);
|
||||
|
||||
if (filteredModel.count > 0 && selectedIndex >= 0 && selectedIndex < filteredModel.count) {
|
||||
var selectedApp = filteredModel.get(selectedIndex);
|
||||
launchApp(selectedApp);
|
||||
}
|
||||
}
|
||||
|
||||
WlrLayershell.layer: WlrLayershell.Overlay
|
||||
WlrLayershell.exclusiveZone: -1
|
||||
WlrLayershell.keyboardFocus: spotlightOpen ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||
WlrLayershell.namespace: "quickshell-spotlight"
|
||||
// DankModal configuration
|
||||
visible: spotlightOpen
|
||||
size: "custom"
|
||||
customWidth: 600
|
||||
customHeight: {
|
||||
// Fixed height to prevent shrinking - consistent experience
|
||||
let baseHeight = Theme.spacingXL * 2 + Theme.spacingL * 3;
|
||||
// Add category section height if visible
|
||||
if (categories.length > 1 || filteredModel.count > 0)
|
||||
baseHeight += 36 * 2 + Theme.spacingS + Theme.spacingM;
|
||||
// Add search field height
|
||||
baseHeight += 56;
|
||||
// Add fixed results height for consistent size
|
||||
let fixedResultsHeight = 400;
|
||||
// Always same height regardless of content
|
||||
baseHeight += fixedResultsHeight;
|
||||
// Ensure reasonable bounds
|
||||
return Math.min(Math.max(baseHeight, 500), 800);
|
||||
}
|
||||
keyboardFocus: "exclusive"
|
||||
backgroundColor: Theme.popupBackground()
|
||||
cornerRadius: Theme.cornerRadiusXLarge
|
||||
borderColor: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
borderWidth: 1
|
||||
enableShadow: true
|
||||
|
||||
onVisibleChanged: {
|
||||
console.log("SpotlightLauncher visibility changed to:", visible);
|
||||
if (visible && !spotlightOpen) {
|
||||
show();
|
||||
}
|
||||
}
|
||||
color: "transparent"
|
||||
|
||||
onOpened: {
|
||||
shouldFocusSearch = true;
|
||||
}
|
||||
|
||||
onBackgroundClicked: {
|
||||
spotlightOpen = false;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("SpotlightLauncher: Component.onCompleted called - component loaded successfully!");
|
||||
var allCategories = AppSearchService.getAllCategories().filter((cat) => {
|
||||
@@ -223,19 +254,11 @@ PanelWindow {
|
||||
// Search debouncing
|
||||
Timer {
|
||||
id: searchDebounceTimer
|
||||
|
||||
interval: 50
|
||||
repeat: false
|
||||
onTriggered: updateFilteredApps()
|
||||
}
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: filteredModel
|
||||
}
|
||||
@@ -253,72 +276,59 @@ PanelWindow {
|
||||
}));
|
||||
if (spotlightOpen)
|
||||
updateFilteredApps();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
target: AppSearchService
|
||||
}
|
||||
|
||||
// Dimmed overlay background
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(0, 0, 0, 0.4)
|
||||
opacity: spotlightOpen ? 1 : 0
|
||||
|
||||
MouseArea {
|
||||
content: Component {
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
enabled: spotlightOpen
|
||||
onClicked: hide()
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
focus: true
|
||||
|
||||
Component.onCompleted: {
|
||||
forceActiveFocus();
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle keyboard shortcuts
|
||||
Keys.onPressed: function(event) {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
hide();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Down) {
|
||||
selectNext();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Up) {
|
||||
selectPrevious();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Right && viewMode === "grid") {
|
||||
selectNextInRow();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Left && viewMode === "grid") {
|
||||
selectPreviousInRow();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
launchSelected();
|
||||
event.accepted = true;
|
||||
} else if (event.text && event.text.length > 0 && event.text.match(/[a-zA-Z0-9\\s]/)) {
|
||||
// User started typing, focus search field and pass the character
|
||||
searchField.forceActiveFocus();
|
||||
searchField.text = event.text;
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Main container with search and results
|
||||
Rectangle {
|
||||
// Margins and spacing
|
||||
// Categories (2 rows)
|
||||
|
||||
id: mainContainer
|
||||
|
||||
width: 600
|
||||
height: {
|
||||
// Fixed height to prevent shrinking - consistent experience
|
||||
let baseHeight = Theme.spacingXL * 2 + Theme.spacingL * 3;
|
||||
// Add category section height if visible
|
||||
if (categories.length > 1 || filteredModel.count > 0)
|
||||
baseHeight += 36 * 2 + Theme.spacingS + Theme.spacingM;
|
||||
|
||||
// Add search field height
|
||||
baseHeight += 56;
|
||||
// Add fixed results height for consistent size
|
||||
let fixedResultsHeight = 400;
|
||||
// Always same height regardless of content
|
||||
baseHeight += fixedResultsHeight;
|
||||
// Ensure reasonable bounds
|
||||
return Math.min(Math.max(baseHeight, 500), parent.height - 40);
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
color: Theme.popupBackground()
|
||||
radius: Theme.cornerRadiusXLarge
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
layer.enabled: true
|
||||
opacity: spotlightOpen ? 1 : 0
|
||||
scale: spotlightOpen ? 1 : 0.96
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingXL
|
||||
spacing: Theme.spacingL
|
||||
|
||||
|
||||
// Combined row for categories and view mode toggle
|
||||
Column {
|
||||
@@ -368,11 +378,8 @@ PanelWindow {
|
||||
updateFilteredApps();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Bottom row: Media, Office, Settings, System, Utilities (5 items)
|
||||
@@ -412,15 +419,10 @@ PanelWindow {
|
||||
updateFilteredApps();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Search field with view toggle buttons
|
||||
@@ -444,31 +446,51 @@ PanelWindow {
|
||||
showClearButton: true
|
||||
textColor: Theme.surfaceText
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
focus: spotlightOpen
|
||||
focus: false
|
||||
enabled: spotlightOpen
|
||||
placeholderText: "Search applications..."
|
||||
text: searchQuery
|
||||
onTextEdited: {
|
||||
searchQuery = text;
|
||||
searchDebounceTimer.restart();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: spotlightLauncher
|
||||
function onShouldFocusSearchChanged() {
|
||||
if (shouldFocusSearch) {
|
||||
Qt.callLater(function() {
|
||||
searchField.forceActiveFocus();
|
||||
searchField.selectAll();
|
||||
shouldFocusSearch = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (!activeFocus && searchQuery.length === 0) {
|
||||
// If search field loses focus and there's no search text, give focus back to main handler
|
||||
parent.parent.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
hide();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
launchSelected();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Down) {
|
||||
selectNext();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Up) {
|
||||
selectPrevious();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Right && viewMode === "grid") {
|
||||
selectNextInRow();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Left && viewMode === "grid") {
|
||||
selectPreviousInRow();
|
||||
} else if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && searchQuery.length > 0) {
|
||||
// Launch first app when typing in search field
|
||||
if (filteredApps.length > 0) {
|
||||
launchApp(filteredApps[0]);
|
||||
}
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up ||
|
||||
(event.key === Qt.Key_Left && viewMode === "grid") ||
|
||||
(event.key === Qt.Key_Right && viewMode === "grid") ||
|
||||
((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && searchQuery.length === 0)) {
|
||||
// Pass navigation keys and enter (when not searching) to main handler
|
||||
event.accepted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -506,7 +528,6 @@ PanelWindow {
|
||||
Prefs.setSpotlightLauncherViewMode("list");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Grid view button
|
||||
@@ -536,11 +557,8 @@ PanelWindow {
|
||||
Prefs.setSpotlightLauncherViewMode("grid");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Results container
|
||||
@@ -562,6 +580,7 @@ PanelWindow {
|
||||
itemHeight: 60
|
||||
iconSize: 40
|
||||
showDescription: true
|
||||
hoverUpdatesSelection: false
|
||||
onItemClicked: function(index, modelData) {
|
||||
launchApp(modelData);
|
||||
}
|
||||
@@ -577,13 +596,14 @@ PanelWindow {
|
||||
anchors.fill: parent
|
||||
visible: viewMode === "grid"
|
||||
model: filteredModel
|
||||
columns: 6
|
||||
adaptiveColumns: true
|
||||
columns: 4
|
||||
adaptiveColumns: false
|
||||
minCellWidth: 120
|
||||
maxCellWidth: 160
|
||||
iconSizeRatio: 0.55
|
||||
maxIconSize: 48
|
||||
currentIndex: selectedIndex
|
||||
hoverUpdatesSelection: false
|
||||
onItemClicked: function(index, modelData) {
|
||||
launchApp(modelData);
|
||||
}
|
||||
@@ -591,37 +611,9 @@ PanelWindow {
|
||||
selectedIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: true
|
||||
shadowHorizontalOffset: 0
|
||||
shadowVerticalOffset: 8
|
||||
shadowBlur: 1 // radius/32
|
||||
shadowColor: Qt.rgba(0, 0, 0, 0.3)
|
||||
shadowOpacity: 0.3
|
||||
}
|
||||
// Center-screen fade with subtle scale
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
@@ -645,5 +637,4 @@ PanelWindow {
|
||||
|
||||
target: "spotlight"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -10,25 +10,21 @@ Rectangle {
|
||||
|
||||
property bool isActive: false
|
||||
|
||||
readonly property bool nerdFontAvailable: Qt.fontFamilies()
|
||||
.indexOf("Symbols Nerd Font") !== -1
|
||||
|
||||
width: 40
|
||||
height: 30
|
||||
radius: Theme.cornerRadius
|
||||
color: launcherArea.containsMouse || isActive ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
||||
|
||||
Text {
|
||||
visible: nerdFontAvailable && OSDetectorService.osLogo
|
||||
SystemLogo {
|
||||
visible: Prefs.useOSLogo
|
||||
anchors.centerIn: parent
|
||||
text: OSDetectorService.osLogo
|
||||
font.family: "Symbols Nerd Font"
|
||||
font.pixelSize: Theme.iconSize - 6
|
||||
width: Theme.iconSize - 6
|
||||
height: Theme.iconSize - 6
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
visible: !nerdFontAvailable || !OSDetectorService.osLogo
|
||||
visible: !Prefs.useOSLogo
|
||||
anchors.centerIn: parent
|
||||
name: "apps"
|
||||
size: Theme.iconSize - 6
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
DankModal {
|
||||
id: root
|
||||
|
||||
property bool wifiPasswordDialogVisible: false
|
||||
@@ -15,75 +12,31 @@ PanelWindow {
|
||||
property string wifiPasswordInput: ""
|
||||
|
||||
visible: wifiPasswordDialogVisible
|
||||
WlrLayershell.layer: WlrLayershell.Overlay
|
||||
WlrLayershell.exclusiveZone: -1
|
||||
WlrLayershell.keyboardFocus: wifiPasswordDialogVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||
color: "transparent"
|
||||
size: "medium"
|
||||
keyboardFocus: "exclusive"
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
passwordInput.enabled = true;
|
||||
Qt.callLater(function() {
|
||||
passwordInput.forceActiveFocus();
|
||||
});
|
||||
} else {
|
||||
passwordInput.enabled = false;
|
||||
if (!visible) {
|
||||
wifiPasswordInput = "";
|
||||
}
|
||||
}
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
onBackgroundClicked: {
|
||||
wifiPasswordDialogVisible = false;
|
||||
wifiPasswordInput = "";
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(0, 0, 0, 0.5)
|
||||
opacity: wifiPasswordDialogVisible ? 1 : 0
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
passwordInput.enabled = false; // Disable before hiding to prevent Wayland warnings
|
||||
wifiPasswordDialogVisible = false;
|
||||
wifiPasswordInput = "";
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: Math.min(400, parent.width - Theme.spacingL * 2)
|
||||
height: Math.min(250, parent.height - Theme.spacingL * 2)
|
||||
anchors.centerIn: parent
|
||||
color: Theme.surfaceContainer
|
||||
radius: Theme.cornerRadiusLarge
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
border.width: 1
|
||||
opacity: wifiPasswordDialogVisible ? 1 : 0
|
||||
scale: wifiPasswordDialogVisible ? 1 : 0.9
|
||||
|
||||
// Prevent clicks inside dialog from closing it
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
// Do nothing - prevent propagation to background
|
||||
}
|
||||
}
|
||||
|
||||
content: Component {
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingL
|
||||
|
||||
Component.onCompleted: {
|
||||
Qt.callLater(function() {
|
||||
passwordInput.forceActiveFocus();
|
||||
});
|
||||
}
|
||||
|
||||
// Header
|
||||
Row {
|
||||
@@ -107,7 +60,6 @@ PanelWindow {
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
@@ -116,12 +68,10 @@ PanelWindow {
|
||||
iconColor: Theme.surfaceText
|
||||
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||
onClicked: {
|
||||
passwordInput.enabled = false; // Disable before hiding to prevent Wayland warnings
|
||||
wifiPasswordDialogVisible = false;
|
||||
wifiPasswordInput = "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Password input
|
||||
@@ -135,13 +85,11 @@ PanelWindow {
|
||||
|
||||
DankTextField {
|
||||
id: passwordInput
|
||||
|
||||
anchors.fill: parent
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
textColor: Theme.surfaceText
|
||||
text: wifiPasswordInput
|
||||
echoMode: showPasswordCheckbox.checked ? TextInput.Normal : TextInput.Password
|
||||
enabled: wifiPasswordDialogVisible
|
||||
placeholderText: "Enter password"
|
||||
backgroundColor: "transparent"
|
||||
normalBorderColor: "transparent"
|
||||
@@ -150,20 +98,12 @@ PanelWindow {
|
||||
wifiPasswordInput = text;
|
||||
}
|
||||
onAccepted: {
|
||||
WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput);
|
||||
// Close dialog immediately after pressing Enter
|
||||
passwordInput.enabled = false;
|
||||
WifiService.connectToWifiWithPassword(wifiPasswordSSID, passwordInput.text);
|
||||
wifiPasswordDialogVisible = false;
|
||||
wifiPasswordInput = "";
|
||||
passwordInput.text = "";
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if (wifiPasswordDialogVisible)
|
||||
forceActiveFocus();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Show password checkbox
|
||||
@@ -172,9 +112,7 @@ PanelWindow {
|
||||
|
||||
Rectangle {
|
||||
id: showPasswordCheckbox
|
||||
|
||||
property bool checked: false
|
||||
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 4
|
||||
@@ -198,7 +136,6 @@ PanelWindow {
|
||||
showPasswordCheckbox.checked = !showPasswordCheckbox.checked;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
@@ -207,7 +144,6 @@ PanelWindow {
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Buttons
|
||||
@@ -230,7 +166,6 @@ PanelWindow {
|
||||
|
||||
Text {
|
||||
id: cancelText
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "Cancel"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
@@ -240,17 +175,14 @@ PanelWindow {
|
||||
|
||||
MouseArea {
|
||||
id: cancelArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
passwordInput.enabled = false; // Disable before hiding to prevent Wayland warnings
|
||||
wifiPasswordDialogVisible = false;
|
||||
wifiPasswordInput = "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -258,12 +190,11 @@ PanelWindow {
|
||||
height: 36
|
||||
radius: Theme.cornerRadius
|
||||
color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||
enabled: wifiPasswordInput.length > 0
|
||||
enabled: passwordInput.text.length > 0
|
||||
opacity: enabled ? 1 : 0.5
|
||||
|
||||
Text {
|
||||
id: connectText
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "Connect"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
@@ -273,17 +204,15 @@ PanelWindow {
|
||||
|
||||
MouseArea {
|
||||
id: connectArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: parent.enabled
|
||||
onClicked: {
|
||||
WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput);
|
||||
// Close dialog immediately after clicking connect
|
||||
passwordInput.enabled = false;
|
||||
WifiService.connectToWifiWithPassword(wifiPasswordSSID, passwordInput.text);
|
||||
wifiPasswordDialogVisible = false;
|
||||
wifiPasswordInput = "";
|
||||
passwordInput.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,33 +221,11 @@ PanelWindow {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Auto-reopen dialog on invalid password
|
||||
@@ -333,5 +240,4 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user