1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

modals: apply same pattern of multi-window

- fixes excessive repaints
fixes #716
This commit is contained in:
bbedward
2025-11-23 12:07:45 -05:00
parent 62845b470c
commit 42de6fd074
15 changed files with 1282 additions and 1279 deletions

View File

@@ -1,5 +1,4 @@
import QtQuick import QtQuick
import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
import qs.Common import qs.Common
import qs.Modals.Common import qs.Modals.Common
@@ -25,38 +24,38 @@ DankModal {
property string passkeyInput: "" property string passkeyInput: ""
function show(pairingData) { function show(pairingData) {
console.log("BluetoothPairingModal.show() called:", JSON.stringify(pairingData)) console.log("BluetoothPairingModal.show() called:", JSON.stringify(pairingData));
token = pairingData.token || "" token = pairingData.token || "";
deviceName = pairingData.deviceName || "" deviceName = pairingData.deviceName || "";
deviceAddress = pairingData.deviceAddr || "" deviceAddress = pairingData.deviceAddr || "";
requestType = pairingData.requestType || "" requestType = pairingData.requestType || "";
passkey = pairingData.passkey || 0 passkey = pairingData.passkey || 0;
pinInput = "" pinInput = "";
passkeyInput = "" passkeyInput = "";
console.log("BluetoothPairingModal: Calling open()") console.log("BluetoothPairingModal: Calling open()");
open() open();
Qt.callLater(() => { Qt.callLater(() => {
if (contentLoader.item) { if (contentLoader.item) {
if (requestType === "pin" && contentLoader.item.pinInputField) { if (requestType === "pin" && contentLoader.item.pinInputField) {
contentLoader.item.pinInputField.forceActiveFocus() contentLoader.item.pinInputField.forceActiveFocus();
} else if (requestType === "passkey" && contentLoader.item.passkeyInputField) { } else if (requestType === "passkey" && contentLoader.item.passkeyInputField) {
contentLoader.item.passkeyInputField.forceActiveFocus() contentLoader.item.passkeyInputField.forceActiveFocus();
} }
} }
}) });
} }
shouldBeVisible: false shouldBeVisible: false
allowStacking: true allowStacking: true
keepPopoutsOpen: true keepPopoutsOpen: true
width: 420 modalWidth: 420
height: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 240 modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 240
onShouldBeVisibleChanged: () => { onShouldBeVisibleChanged: () => {
if (!shouldBeVisible) { if (!shouldBeVisible) {
pinInput = "" pinInput = "";
passkeyInput = "" passkeyInput = "";
} }
} }
@@ -64,22 +63,22 @@ DankModal {
Qt.callLater(() => { Qt.callLater(() => {
if (contentLoader.item) { if (contentLoader.item) {
if (requestType === "pin" && contentLoader.item.pinInputField) { if (requestType === "pin" && contentLoader.item.pinInputField) {
contentLoader.item.pinInputField.forceActiveFocus() contentLoader.item.pinInputField.forceActiveFocus();
} else if (requestType === "passkey" && contentLoader.item.passkeyInputField) { } else if (requestType === "passkey" && contentLoader.item.passkeyInputField) {
contentLoader.item.passkeyInputField.forceActiveFocus() contentLoader.item.passkeyInputField.forceActiveFocus();
} }
} }
}) });
} }
onBackgroundClicked: () => { onBackgroundClicked: () => {
if (token) { if (token) {
DMSService.bluetoothCancelPairing(token) DMSService.bluetoothCancelPairing(token);
} }
close() close();
token = "" token = "";
pinInput = "" pinInput = "";
passkeyInput = "" passkeyInput = "";
} }
content: Component { content: Component {
@@ -95,13 +94,13 @@ DankModal {
Keys.onEscapePressed: event => { Keys.onEscapePressed: event => {
if (token) { if (token) {
DMSService.bluetoothCancelPairing(token) DMSService.bluetoothCancelPairing(token);
} }
close() close();
token = "" token = "";
pinInput = "" pinInput = "";
passkeyInput = "" passkeyInput = "";
event.accepted = true event.accepted = true;
} }
Column { Column {
@@ -129,19 +128,19 @@ DankModal {
text: { text: {
switch (requestType) { switch (requestType) {
case "confirm": case "confirm":
return I18n.tr("Confirm passkey for ") + deviceName return I18n.tr("Confirm passkey for ") + deviceName;
case "display-passkey": case "display-passkey":
return I18n.tr("Enter this passkey on ") + deviceName return I18n.tr("Enter this passkey on ") + deviceName;
case "authorize": case "authorize":
return I18n.tr("Authorize pairing with ") + deviceName return I18n.tr("Authorize pairing with ") + deviceName;
case "pin": case "pin":
return I18n.tr("Enter PIN for ") + deviceName return I18n.tr("Enter PIN for ") + deviceName;
case "passkey": case "passkey":
return I18n.tr("Enter passkey for ") + deviceName return I18n.tr("Enter passkey for ") + deviceName;
default: default:
if (requestType.startsWith("authorize-service")) if (requestType.startsWith("authorize-service"))
return I18n.tr("Authorize service for ") + deviceName return I18n.tr("Authorize service for ") + deviceName;
return deviceName return deviceName;
} }
} }
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
@@ -163,7 +162,7 @@ DankModal {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: () => { onClicked: () => {
pinInputField.forceActiveFocus() pinInputField.forceActiveFocus();
} }
} }
@@ -178,10 +177,10 @@ DankModal {
backgroundColor: "transparent" backgroundColor: "transparent"
enabled: root.shouldBeVisible enabled: root.shouldBeVisible
onTextEdited: () => { onTextEdited: () => {
pinInput = text pinInput = text;
} }
onAccepted: () => { onAccepted: () => {
submitPairing() submitPairing();
} }
} }
} }
@@ -198,7 +197,7 @@ DankModal {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: () => { onClicked: () => {
passkeyInputField.forceActiveFocus() passkeyInputField.forceActiveFocus();
} }
} }
@@ -213,10 +212,10 @@ DankModal {
backgroundColor: "transparent" backgroundColor: "transparent"
enabled: root.shouldBeVisible enabled: root.shouldBeVisible
onTextEdited: () => { onTextEdited: () => {
passkeyInput = text passkeyInput = text;
} }
onAccepted: () => { onAccepted: () => {
submitPairing() submitPairing();
} }
} }
} }
@@ -284,12 +283,12 @@ DankModal {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: () => { onClicked: () => {
if (token) { if (token) {
DMSService.bluetoothCancelPairing(token) DMSService.bluetoothCancelPairing(token);
} }
close() close();
token = "" token = "";
pinInput = "" pinInput = "";
passkeyInput = "" passkeyInput = "";
} }
} }
} }
@@ -301,10 +300,10 @@ DankModal {
color: pairArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary color: pairArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
enabled: { enabled: {
if (requestType === "pin") if (requestType === "pin")
return pinInput.length > 0 return pinInput.length > 0;
if (requestType === "passkey") if (requestType === "passkey")
return passkeyInput.length === 6 return passkeyInput.length === 6;
return true return true;
} }
opacity: enabled ? 1 : 0.5 opacity: enabled ? 1 : 0.5
@@ -316,13 +315,13 @@ DankModal {
switch (requestType) { switch (requestType) {
case "confirm": case "confirm":
case "display-passkey": case "display-passkey":
return I18n.tr("Confirm") return I18n.tr("Confirm");
case "authorize": case "authorize":
return I18n.tr("Authorize") return I18n.tr("Authorize");
default: default:
if (requestType.startsWith("authorize-service")) if (requestType.startsWith("authorize-service"))
return I18n.tr("Authorize") return I18n.tr("Authorize");
return I18n.tr("Pair") return I18n.tr("Pair");
} }
} }
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
@@ -338,7 +337,7 @@ DankModal {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
enabled: parent.enabled enabled: parent.enabled
onClicked: () => { onClicked: () => {
submitPairing() submitPairing();
} }
} }
@@ -363,48 +362,48 @@ DankModal {
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
onClicked: () => { onClicked: () => {
if (token) { if (token) {
DMSService.bluetoothCancelPairing(token) DMSService.bluetoothCancelPairing(token);
} }
close() close();
token = "" token = "";
pinInput = "" pinInput = "";
passkeyInput = "" passkeyInput = "";
} }
} }
} }
} }
function submitPairing() { function submitPairing() {
const secrets = {} const secrets = {};
switch (requestType) { switch (requestType) {
case "pin": case "pin":
secrets["pin"] = pinInput secrets["pin"] = pinInput;
break break;
case "passkey": case "passkey":
secrets["passkey"] = passkeyInput secrets["passkey"] = passkeyInput;
break break;
case "confirm": case "confirm":
case "display-passkey": case "display-passkey":
case "authorize": case "authorize":
secrets["decision"] = "yes" secrets["decision"] = "yes";
break break;
default: default:
if (requestType.startsWith("authorize-service")) { if (requestType.startsWith("authorize-service")) {
secrets["decision"] = "yes" secrets["decision"] = "yes";
} }
break break;
} }
DMSService.bluetoothSubmitPairing(token, secrets, true, response => { DMSService.bluetoothSubmitPairing(token, secrets, true, response => {
if (response.error) { if (response.error) {
ToastService.showError(I18n.tr("Pairing failed"), response.error) ToastService.showError(I18n.tr("Pairing failed"), response.error);
} }
}) });
close() close();
token = "" token = "";
pinInput = "" pinInput = "";
passkeyInput = "" passkeyInput = "";
} }
} }

View File

@@ -1,14 +1,12 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Controls
import Quickshell import Quickshell
import Quickshell.Hyprland 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 import qs.Services
import qs.Widgets
DankModal { DankModal {
id: clipboardHistoryModal id: clipboardHistoryModal
@@ -31,119 +29,119 @@ DankModal {
readonly property int maxConcurrentLoads: 3 readonly property int maxConcurrentLoads: 3
function updateFilteredModel() { function updateFilteredModel() {
filteredClipboardModel.clear() filteredClipboardModel.clear();
for (var i = 0; i < clipboardModel.count; i++) { for (var i = 0; i < clipboardModel.count; i++) {
const entry = clipboardModel.get(i).entry const entry = clipboardModel.get(i).entry;
if (searchText.trim().length === 0) { if (searchText.trim().length === 0) {
filteredClipboardModel.append({ filteredClipboardModel.append({
"entry": entry "entry": entry
}) });
} else { } else {
const content = getEntryPreview(entry).toLowerCase() const content = getEntryPreview(entry).toLowerCase();
if (content.includes(searchText.toLowerCase())) { if (content.includes(searchText.toLowerCase())) {
filteredClipboardModel.append({ filteredClipboardModel.append({
"entry": entry "entry": entry
}) });
} }
} }
} }
clipboardHistoryModal.totalCount = filteredClipboardModel.count clipboardHistoryModal.totalCount = filteredClipboardModel.count;
if (filteredClipboardModel.count === 0) { if (filteredClipboardModel.count === 0) {
keyboardNavigationActive = false keyboardNavigationActive = false;
selectedIndex = 0 selectedIndex = 0;
} else if (selectedIndex >= filteredClipboardModel.count) { } else if (selectedIndex >= filteredClipboardModel.count) {
selectedIndex = filteredClipboardModel.count - 1 selectedIndex = filteredClipboardModel.count - 1;
} }
} }
function toggle() { function toggle() {
if (shouldBeVisible) { if (shouldBeVisible) {
hide() hide();
} else { } else {
show() show();
} }
} }
function show() { function show() {
open() open();
clipboardHistoryModal.searchText = "" clipboardHistoryModal.searchText = "";
clipboardHistoryModal.activeImageLoads = 0 clipboardHistoryModal.activeImageLoads = 0;
clipboardHistoryModal.shouldHaveFocus = true clipboardHistoryModal.shouldHaveFocus = true;
refreshClipboard() refreshClipboard();
keyboardController.reset() keyboardController.reset();
Qt.callLater(function () { Qt.callLater(function () {
if (contentLoader.item && contentLoader.item.searchField) { if (contentLoader.item && contentLoader.item.searchField) {
contentLoader.item.searchField.text = "" contentLoader.item.searchField.text = "";
contentLoader.item.searchField.forceActiveFocus() contentLoader.item.searchField.forceActiveFocus();
} }
}) });
} }
function hide() { function hide() {
close() close();
clipboardHistoryModal.searchText = "" clipboardHistoryModal.searchText = "";
clipboardHistoryModal.activeImageLoads = 0 clipboardHistoryModal.activeImageLoads = 0;
updateFilteredModel() updateFilteredModel();
keyboardController.reset() keyboardController.reset();
cleanupTempFiles() cleanupTempFiles();
} }
function cleanupTempFiles() { function cleanupTempFiles() {
Quickshell.execDetached(["sh", "-c", "rm -f /tmp/clipboard_*.png"]) Quickshell.execDetached(["sh", "-c", "rm -f /tmp/clipboard_*.png"]);
} }
function refreshClipboard() { function refreshClipboard() {
clipboardProcesses.refresh() clipboardProcesses.refresh();
} }
function copyEntry(entry) { function copyEntry(entry) {
const entryId = entry.split('\t')[0] const entryId = entry.split('\t')[0];
Quickshell.execDetached(["sh", "-c", `cliphist decode ${entryId} | wl-copy`]) Quickshell.execDetached(["sh", "-c", `cliphist decode ${entryId} | wl-copy`]);
ToastService.showInfo(I18n.tr("Copied to clipboard")) ToastService.showInfo(I18n.tr("Copied to clipboard"));
hide() hide();
} }
function deleteEntry(entry) { function deleteEntry(entry) {
clipboardProcesses.deleteEntry(entry) clipboardProcesses.deleteEntry(entry);
} }
function clearAll() { function clearAll() {
clipboardProcesses.clearAll() clipboardProcesses.clearAll();
} }
function getEntryPreview(entry) { function getEntryPreview(entry) {
let content = entry.replace(/^\s*\d+\s+/, "") let content = entry.replace(/^\s*\d+\s+/, "");
if (content.includes("image/") || content.includes("binary data") || /\.(png|jpg|jpeg|gif|bmp|webp)/i.test(content)) { if (content.includes("image/") || content.includes("binary data") || /\.(png|jpg|jpeg|gif|bmp|webp)/i.test(content)) {
const dimensionMatch = content.match(/(\d+)x(\d+)/) const dimensionMatch = content.match(/(\d+)x(\d+)/);
if (dimensionMatch) { if (dimensionMatch) {
return `Image ${dimensionMatch[1]}×${dimensionMatch[2]}` return `Image ${dimensionMatch[1]}×${dimensionMatch[2]}`;
} }
const typeMatch = content.match(/\b(png|jpg|jpeg|gif|bmp|webp)\b/i) const typeMatch = content.match(/\b(png|jpg|jpeg|gif|bmp|webp)\b/i);
if (typeMatch) { if (typeMatch) {
return `Image (${typeMatch[1].toUpperCase()})` return `Image (${typeMatch[1].toUpperCase()})`;
} }
return "Image" return "Image";
} }
if (content.length > ClipboardConstants.previewLength) { if (content.length > ClipboardConstants.previewLength) {
return content.substring(0, ClipboardConstants.previewLength) + "..." return content.substring(0, ClipboardConstants.previewLength) + "...";
} }
return content return content;
} }
function getEntryType(entry) { function getEntryType(entry) {
if (entry.includes("image/") || entry.includes("binary data") || /\.(png|jpg|jpeg|gif|bmp|webp)/i.test(entry) || /\b(png|jpg|jpeg|gif|bmp|webp)\b/i.test(entry)) { if (entry.includes("image/") || entry.includes("binary data") || /\.(png|jpg|jpeg|gif|bmp|webp)/i.test(entry) || /\b(png|jpg|jpeg|gif|bmp|webp)\b/i.test(entry)) {
return "image" return "image";
} }
if (entry.length > ClipboardConstants.longTextThreshold) { if (entry.length > ClipboardConstants.longTextThreshold) {
return "long_text" return "long_text";
} }
return "text" return "text";
} }
visible: false visible: false
width: ClipboardConstants.modalWidth modalWidth: ClipboardConstants.modalWidth
height: ClipboardConstants.modalHeight modalHeight: ClipboardConstants.modalHeight
backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
cornerRadius: Theme.cornerRadius cornerRadius: Theme.cornerRadius
borderColor: Theme.outlineMedium borderColor: Theme.outlineMedium
@@ -151,7 +149,7 @@ DankModal {
enableShadow: true enableShadow: true
onBackgroundClicked: hide() onBackgroundClicked: hide()
modalFocusScope.Keys.onPressed: function (event) { modalFocusScope.Keys.onPressed: function (event) {
keyboardController.handleKey(event) keyboardController.handleKey(event);
} }
content: clipboardContent content: clipboardContent
@@ -166,12 +164,12 @@ DankModal {
confirmButtonColor: Theme.primary confirmButtonColor: Theme.primary
onVisibleChanged: { onVisibleChanged: {
if (visible) { if (visible) {
clipboardHistoryModal.shouldHaveFocus = false clipboardHistoryModal.shouldHaveFocus = false;
} else if (clipboardHistoryModal.shouldBeVisible) { } else if (clipboardHistoryModal.shouldBeVisible) {
clipboardHistoryModal.shouldHaveFocus = true clipboardHistoryModal.shouldHaveFocus = true;
clipboardHistoryModal.modalFocusScope.forceActiveFocus() clipboardHistoryModal.modalFocusScope.forceActiveFocus();
if (clipboardHistoryModal.contentLoader.item && clipboardHistoryModal.contentLoader.item.searchField) { if (clipboardHistoryModal.contentLoader.item && clipboardHistoryModal.contentLoader.item.searchField) {
clipboardHistoryModal.contentLoader.item.searchField.forceActiveFocus() clipboardHistoryModal.contentLoader.item.searchField.forceActiveFocus();
} }
} }
} }
@@ -198,18 +196,18 @@ DankModal {
IpcHandler { IpcHandler {
function open(): string { function open(): string {
clipboardHistoryModal.show() clipboardHistoryModal.show();
return "CLIPBOARD_OPEN_SUCCESS" return "CLIPBOARD_OPEN_SUCCESS";
} }
function close(): string { function close(): string {
clipboardHistoryModal.hide() clipboardHistoryModal.hide();
return "CLIPBOARD_CLOSE_SUCCESS" return "CLIPBOARD_CLOSE_SUCCESS";
} }
function toggle(): string { function toggle(): string {
clipboardHistoryModal.toggle() clipboardHistoryModal.toggle();
return "CLIPBOARD_TOGGLE_SUCCESS" return "CLIPBOARD_TOGGLE_SUCCESS";
} }
target: "clipboard" target: "clipboard"

View File

@@ -19,141 +19,141 @@ DankModal {
property bool keyboardNavigation: false property bool keyboardNavigation: false
function show(title, message, onConfirmCallback, onCancelCallback) { function show(title, message, onConfirmCallback, onCancelCallback) {
confirmTitle = title || "" confirmTitle = title || "";
confirmMessage = message || "" confirmMessage = message || "";
confirmButtonText = "Confirm" confirmButtonText = "Confirm";
cancelButtonText = "Cancel" cancelButtonText = "Cancel";
confirmButtonColor = Theme.primary confirmButtonColor = Theme.primary;
onConfirm = onConfirmCallback || (() => {}) onConfirm = onConfirmCallback || (() => {});
onCancel = onCancelCallback || (() => {}) onCancel = onCancelCallback || (() => {});
selectedButton = -1 selectedButton = -1;
keyboardNavigation = false keyboardNavigation = false;
open() open();
} }
function showWithOptions(options) { function showWithOptions(options) {
confirmTitle = options.title || "" confirmTitle = options.title || "";
confirmMessage = options.message || "" confirmMessage = options.message || "";
confirmButtonText = options.confirmText || "Confirm" confirmButtonText = options.confirmText || "Confirm";
cancelButtonText = options.cancelText || "Cancel" cancelButtonText = options.cancelText || "Cancel";
confirmButtonColor = options.confirmColor || Theme.primary confirmButtonColor = options.confirmColor || Theme.primary;
onConfirm = options.onConfirm || (() => {}) onConfirm = options.onConfirm || (() => {});
onCancel = options.onCancel || (() => {}) onCancel = options.onCancel || (() => {});
selectedButton = -1 selectedButton = -1;
keyboardNavigation = false keyboardNavigation = false;
open() open();
} }
function selectButton() { function selectButton() {
close() close();
if (selectedButton === 0) { if (selectedButton === 0) {
if (onCancel) { if (onCancel) {
onCancel() onCancel();
} }
} else { } else {
if (onConfirm) { if (onConfirm) {
onConfirm() onConfirm();
} }
} }
} }
shouldBeVisible: false shouldBeVisible: false
allowStacking: true allowStacking: true
width: 350 modalWidth: 350
height: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 160 modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 160
enableShadow: true enableShadow: true
shouldHaveFocus: true shouldHaveFocus: true
onBackgroundClicked: { onBackgroundClicked: {
close() close();
if (onCancel) { if (onCancel) {
onCancel() onCancel();
} }
} }
onOpened: { onOpened: {
Qt.callLater(function () { Qt.callLater(function () {
modalFocusScope.forceActiveFocus() modalFocusScope.forceActiveFocus();
modalFocusScope.focus = true modalFocusScope.focus = true;
shouldHaveFocus = true shouldHaveFocus = true;
}) });
} }
modalFocusScope.Keys.onPressed: function (event) { 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:
case Qt.Key_Up: case Qt.Key_Up:
keyboardNavigation = true keyboardNavigation = true;
selectedButton = 0 selectedButton = 0;
event.accepted = true event.accepted = true;
break break;
case Qt.Key_Right: case Qt.Key_Right:
case Qt.Key_Down: case Qt.Key_Down:
keyboardNavigation = true keyboardNavigation = true;
selectedButton = 1 selectedButton = 1;
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 keyboardNavigation = true;
selectedButton = (selectedButton + 1) % 2 selectedButton = (selectedButton + 1) % 2;
event.accepted = true 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 keyboardNavigation = true;
selectedButton = selectedButton === -1 ? 1 : (selectedButton - 1 + 2) % 2 selectedButton = selectedButton === -1 ? 1 : (selectedButton - 1 + 2) % 2;
event.accepted = true 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 keyboardNavigation = true;
selectedButton = 1 selectedButton = 1;
event.accepted = true 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 keyboardNavigation = true;
selectedButton = 0 selectedButton = 0;
event.accepted = true 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 keyboardNavigation = true;
selectedButton = 0 selectedButton = 0;
event.accepted = true 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 keyboardNavigation = true;
selectedButton = 1 selectedButton = 1;
event.accepted = true event.accepted = true;
} }
break break;
case Qt.Key_Tab: case Qt.Key_Tab:
keyboardNavigation = true keyboardNavigation = true;
selectedButton = selectedButton === -1 ? 0 : (selectedButton + 1) % 2 selectedButton = selectedButton === -1 ? 0 : (selectedButton + 1) % 2;
event.accepted = true event.accepted = true;
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();
} }
event.accepted = true event.accepted = true;
break break;
} }
} }
@@ -210,11 +210,11 @@ DankModal {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: { color: {
if (keyboardNavigation && selectedButton === 0) { if (keyboardNavigation && selectedButton === 0) {
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
} else if (cancelButton.containsMouse) { } else if (cancelButton.containsMouse) {
return Theme.surfacePressed return Theme.surfacePressed;
} else { } else {
return Theme.surfaceVariantAlpha return Theme.surfaceVariantAlpha;
} }
} }
border.color: (keyboardNavigation && selectedButton === 0) ? Theme.primary : "transparent" border.color: (keyboardNavigation && selectedButton === 0) ? Theme.primary : "transparent"
@@ -235,8 +235,8 @@ DankModal {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
selectedButton = 0 selectedButton = 0;
selectButton() selectButton();
} }
} }
} }
@@ -246,13 +246,13 @@ DankModal {
height: 40 height: 40
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: { color: {
const baseColor = confirmButtonColor const baseColor = confirmButtonColor;
if (keyboardNavigation && selectedButton === 1) { if (keyboardNavigation && selectedButton === 1) {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 1) return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 1);
} else if (confirmButton.containsMouse) { } else if (confirmButton.containsMouse) {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 0.9) return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 0.9);
} else { } else {
return baseColor return baseColor;
} }
} }
border.color: (keyboardNavigation && selectedButton === 1) ? "white" : "transparent" border.color: (keyboardNavigation && selectedButton === 1) ? "white" : "transparent"
@@ -273,8 +273,8 @@ DankModal {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
selectedButton = 1 selectedButton = 1;
selectButton() selectButton();
} }
} }
} }

View File

@@ -1,25 +1,23 @@
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Hyprland
import Quickshell.Wayland import Quickshell.Wayland
import qs.Common import qs.Common
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
PanelWindow { Item {
id: root id: root
property string layerNamespace: "dms:modal" property string layerNamespace: "dms:modal"
WlrLayershell.namespace: layerNamespace
property alias content: contentLoader.sourceComponent property alias content: contentLoader.sourceComponent
property alias contentLoader: contentLoader property alias contentLoader: contentLoader
property Item directContent: null property Item directContent: null
property real width: 400 property real modalWidth: 400
property real height: 300 property real modalHeight: 300
readonly property real screenWidth: screen ? screen.width : 1920 property var targetScreen: Quickshell.screens[0]
readonly property real screenHeight: screen ? screen.height : 1080 readonly property real screenWidth: targetScreen ? targetScreen.width : 1920
readonly property real dpr: CompositorService.getScreenScale(screen) readonly property real screenHeight: targetScreen ? targetScreen.height : 1080
readonly property real dpr: targetScreen ? CompositorService.getScreenScale(targetScreen) : 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"
@@ -44,290 +42,342 @@ PanelWindow {
property bool allowStacking: false property bool allowStacking: false
property bool keepContentLoaded: false property bool keepContentLoaded: false
property bool keepPopoutsOpen: false property bool keepPopoutsOpen: false
property var customKeyboardFocus: null
signal opened signal opened
signal dialogClosed signal dialogClosed
signal backgroundClicked signal backgroundClicked
readonly property bool useBackgroundWindow: {
const layerOverride = Quickshell.env("DMS_MODAL_LAYER");
return !layerOverride || layerOverride === "overlay";
}
function open() { function open() {
ModalManager.openModal(root) ModalManager.openModal(root);
closeTimer.stop() closeTimer.stop();
shouldBeVisible = true shouldBeVisible = true;
visible = true if (useBackgroundWindow)
shouldHaveFocus = false backgroundWindow.visible = true;
contentWindow.visible = true;
shouldHaveFocus = false;
Qt.callLater(() => { Qt.callLater(() => {
shouldHaveFocus = Qt.binding(() => shouldBeVisible) shouldHaveFocus = Qt.binding(() => shouldBeVisible);
}) });
} }
function close() { function close() {
shouldBeVisible = false shouldBeVisible = false;
shouldHaveFocus = false shouldHaveFocus = false;
closeTimer.restart() closeTimer.restart();
} }
function toggle() { function toggle() {
if (shouldBeVisible) { shouldBeVisible ? close() : open();
close()
} else {
open()
}
}
visible: shouldBeVisible
color: "transparent"
WlrLayershell.layer: {
switch (Quickshell.env("DMS_MODAL_LAYER")) {
case "bottom":
return WlrLayershell.Bottom
case "overlay":
return WlrLayershell.Overlay
case "background":
return WlrLayershell.Background
default:
return WlrLayershell.Top
}
}
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: {
if (!shouldHaveFocus) return WlrKeyboardFocus.None
if (CompositorService.isHyprland) return WlrKeyboardFocus.OnDemand
return WlrKeyboardFocus.Exclusive
}
onVisibleChanged: {
if (root.visible) {
opened()
} else {
if (Qt.inputMethod) {
Qt.inputMethod.hide()
Qt.inputMethod.reset()
}
dialogClosed()
}
} }
Connections { Connections {
target: ModalManager
function onCloseAllModalsExcept(excludedModal) { function onCloseAllModalsExcept(excludedModal) {
if (excludedModal !== root && !allowStacking && shouldBeVisible) { if (excludedModal !== root && !allowStacking && shouldBeVisible) {
close() close();
} }
} }
target: ModalManager
} }
Timer { Timer {
id: closeTimer id: closeTimer
interval: animationDuration + 120 interval: animationDuration + 120
onTriggered: { onTriggered: {
visible = false if (!shouldBeVisible) {
} contentWindow.visible = false;
} if (useBackgroundWindow)
backgroundWindow.visible = false;
anchors { dialogClosed();
top: true
left: true
right: true
bottom: true
}
MouseArea {
anchors.fill: parent
enabled: root.closeOnBackgroundClick && root.shouldBeVisible
onClicked: mouse => {
const localPos = mapToItem(contentContainer, mouse.x, mouse.y)
if (localPos.x < 0 || localPos.x > contentContainer.width || localPos.y < 0 || localPos.y > contentContainer.height) {
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 {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
} }
} }
} }
Item { readonly property real shadowBuffer: 5
id: modalContainer readonly property real alignedWidth: Theme.px(modalWidth, dpr)
readonly property real alignedHeight: Theme.px(modalHeight, dpr)
width: Theme.px(root.width, dpr) readonly property real alignedX: Theme.snap((() => {
height: Theme.px(root.height, dpr) switch (positioning) {
x: { case "center":
if (positioning === "center") { return (screenWidth - alignedWidth) / 2;
return Theme.snap((root.screenWidth - width) / 2, dpr) case "top-right":
} else if (positioning === "top-right") { return Math.max(Theme.spacingL, screenWidth - alignedWidth - Theme.spacingL);
return Theme.px(Math.max(Theme.spacingL, root.screenWidth - width - Theme.spacingL), dpr) case "custom":
} else if (positioning === "custom") { return customPosition.x;
return Theme.snap(root.customPosition.x, dpr) default:
return 0;
} }
return 0 })(), dpr)
}
y: { readonly property real alignedY: Theme.snap((() => {
if (positioning === "center") { switch (positioning) {
return Theme.snap((root.screenHeight - height) / 2, dpr) case "center":
} else if (positioning === "top-right") { return (screenHeight - alignedHeight) / 2;
return Theme.px(Theme.barHeight + Theme.spacingXS, dpr) case "top-right":
} else if (positioning === "custom") { return Theme.barHeight + Theme.spacingXS;
return Theme.snap(root.customPosition.y, dpr) case "custom":
return customPosition.y;
default:
return 0;
} }
return 0 })(), dpr)
PanelWindow {
id: backgroundWindow
screen: root.targetScreen
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
} }
readonly property bool slide: root.animationType === "slide" MouseArea {
readonly property real offsetX: slide ? 15 : 0 anchors.fill: parent
readonly property real offsetY: slide ? -30 : root.animationOffset 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;
property real animX: 0 if (!outsideContent)
property real animY: 0 return;
property real scaleValue: root.animationScaleCollapsed root.backgroundClicked();
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 { Rectangle {
NumberAnimation { id: background
duration: root.animationDuration anchors.fill: parent
easing.type: Easing.BezierSpline color: "black"
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve opacity: root.showBackground && SettingsData.modalDarkenBackground ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
visible: root.showBackground && SettingsData.modalDarkenBackground
Behavior on opacity {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
}
} }
} }
}
Behavior on animY { PanelWindow {
NumberAnimation { id: contentWindow
duration: root.animationDuration screen: root.targetScreen
easing.type: Easing.BezierSpline visible: false
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve color: "transparent"
WlrLayershell.namespace: root.layerNamespace
WlrLayershell.layer: {
switch (Quickshell.env("DMS_MODAL_LAYER")) {
case "bottom":
return WlrLayershell.Bottom;
case "top":
return WlrLayershell.Top;
case "background":
return WlrLayershell.Background;
default:
return WlrLayershell.Overlay;
} }
} }
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: {
if (customKeyboardFocus !== null)
return customKeyboardFocus;
if (!shouldHaveFocus)
return WlrKeyboardFocus.None;
if (CompositorService.isHyprland)
return WlrKeyboardFocus.OnDemand;
return WlrKeyboardFocus.Exclusive;
}
Behavior on scaleValue { anchors {
NumberAnimation { left: true
duration: root.animationDuration top: true
easing.type: Easing.BezierSpline }
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
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 { Item {
id: contentContainer id: modalContainer
x: shadowBuffer
y: shadowBuffer
width: root.alignedWidth
height: root.alignedHeight
anchors.centerIn: parent readonly property bool slide: root.animationType === "slide"
width: parent.width readonly property real offsetX: slide ? 15 : 0
height: parent.height readonly property real offsetY: slide ? -30 : root.animationOffset
clip: false
layer.enabled: true
layer.smooth: false
layer.textureSize: Qt.size(Math.round(width * root.dpr), Math.round(height * root.dpr))
opacity: root.shouldBeVisible ? 1 : 0
scale: modalContainer.scaleValue
x: Theme.snap(modalContainer.animX + (parent.width - width) * (1 - modalContainer.scaleValue) * 0.5, root.dpr)
y: Theme.snap(modalContainer.animY + (parent.height - height) * (1 - modalContainer.scaleValue) * 0.5, root.dpr)
Behavior on opacity { 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 {
NumberAnimation { NumberAnimation {
duration: animationDuration duration: root.animationDuration
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
} }
} }
DankRectangle { Behavior on animY {
anchors.fill: parent NumberAnimation {
color: root.backgroundColor duration: root.animationDuration
borderColor: root.borderColor easing.type: Easing.BezierSpline
borderWidth: root.borderWidth easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
radius: root.cornerRadius }
} }
FocusScope { Behavior on scaleValue {
anchors.fill: parent NumberAnimation {
focus: root.shouldBeVisible 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 clip: false
Item { Item {
id: directContentWrapper id: animatedContent
anchors.fill: parent anchors.fill: parent
visible: root.directContent !== null
focus: true
clip: false 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
Component.onCompleted: { Behavior on opacity {
if (root.directContent) { NumberAnimation {
root.directContent.parent = directContentWrapper duration: animationDuration
root.directContent.anchors.fill = directContentWrapper easing.type: Easing.BezierSpline
Qt.callLater(() => root.directContent.forceActiveFocus()) easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
} }
} }
Connections { DankRectangle {
function onDirectContentChanged() { anchors.fill: parent
if (root.directContent) { color: root.backgroundColor
root.directContent.parent = directContentWrapper borderColor: root.borderColor
root.directContent.anchors.fill = directContentWrapper borderWidth: root.borderWidth
Qt.callLater(() => root.directContent.forceActiveFocus()) 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());
}
}
} }
} }
target: root 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
Loader { onLoaded: {
id: contentLoader if (item) {
Qt.callLater(() => item.forceActiveFocus());
anchors.fill: parent }
active: root.directContent === null && (root.keepContentLoaded || root.shouldBeVisible || root.visible) }
asynchronous: false
focus: true
clip: false
visible: root.directContent === null
onLoaded: {
if (item) {
Qt.callLater(() => item.forceActiveFocus())
} }
} }
} }
} }
} }
}
FocusScope { FocusScope {
id: focusScope id: focusScope
objectName: "modalFocusScope"
objectName: "modalFocusScope" anchors.fill: parent
anchors.fill: parent visible: root.shouldBeVisible || contentWindow.visible
visible: root.shouldBeVisible || root.visible focus: root.shouldBeVisible
focus: root.shouldBeVisible Keys.onEscapePressed: event => {
Keys.onEscapePressed: event => { if (root.closeOnEscapeKey && shouldHaveFocus) {
if (root.closeOnEscapeKey && shouldHaveFocus) { root.close();
root.close() event.accepted = true;
event.accepted = true }
} }
} }
} }
} }

View File

@@ -1,8 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Controls
import Quickshell import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
import Quickshell.Io
import qs.Common import qs.Common
import qs.Modals.Common import qs.Modals.Common
import qs.Services import qs.Services
@@ -32,87 +30,79 @@ DankModal {
property real gradientX: 0 property real gradientX: 0
property real gradientY: 0 property real gradientY: 0
readonly property var standardColors: [ readonly property var standardColors: ["#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4", "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", "#d32f2f", "#c2185b", "#7b1fa2", "#512da8", "#303f9f", "#1976d2", "#0288d1", "#0097a7", "#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19", "#c62828", "#ad1457", "#6a1b9a", "#4527a0", "#283593", "#1565c0", "#0277bd", "#00838f", "#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315", "#ffffff", "#9e9e9e", "#212121"]
"#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4",
"#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722",
"#d32f2f", "#c2185b", "#7b1fa2", "#512da8", "#303f9f", "#1976d2", "#0288d1", "#0097a7",
"#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19",
"#c62828", "#ad1457", "#6a1b9a", "#4527a0", "#283593", "#1565c0", "#0277bd", "#00838f",
"#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315",
"#ffffff", "#9e9e9e", "#212121"
]
function show() { function show() {
currentColor = selectedColor currentColor = selectedColor;
updateFromColor(currentColor) updateFromColor(currentColor);
open() open();
} }
function hide() { function hide() {
onColorSelectedCallback = null onColorSelectedCallback = null;
close() close();
} }
function hideInstant() { function hideInstant() {
onColorSelectedCallback = null onColorSelectedCallback = null;
shouldBeVisible = false shouldBeVisible = false;
visible = false visible = false;
} }
onColorSelected: (color) => { onColorSelected: color => {
if (onColorSelectedCallback) { if (onColorSelectedCallback) {
onColorSelectedCallback(color) onColorSelectedCallback(color);
} }
} }
function copyColorToClipboard(colorValue) { function copyColorToClipboard(colorValue) {
Quickshell.execDetached(["sh", "-c", `echo -n "${colorValue}" | wl-copy`]) Quickshell.execDetached(["sh", "-c", `echo -n "${colorValue}" | wl-copy`]);
ToastService.showInfo(`Color ${colorValue} copied`) ToastService.showInfo(`Color ${colorValue} copied`);
SessionData.addRecentColor(currentColor) SessionData.addRecentColor(currentColor);
} }
function updateFromColor(color) { function updateFromColor(color) {
hue = color.hsvHue hue = color.hsvHue;
saturation = color.hsvSaturation saturation = color.hsvSaturation;
value = color.hsvValue value = color.hsvValue;
alpha = color.a alpha = color.a;
gradientX = saturation gradientX = saturation;
gradientY = 1 - value gradientY = 1 - value;
} }
function updateColor() { function updateColor() {
currentColor = Qt.hsva(hue, saturation, value, alpha) currentColor = Qt.hsva(hue, saturation, value, alpha);
} }
function updateColorFromGradient(x, y) { function updateColorFromGradient(x, y) {
saturation = Math.max(0, Math.min(1, x)) saturation = Math.max(0, Math.min(1, x));
value = Math.max(0, Math.min(1, 1 - y)) value = Math.max(0, Math.min(1, 1 - y));
updateColor() updateColor();
selectedColor = currentColor selectedColor = currentColor;
} }
function pickColorFromScreen() { function pickColorFromScreen() {
hideInstant() hideInstant();
Proc.runCommand("hyprpicker", ["hyprpicker", "--format=hex"], (output, errorCode) => { Proc.runCommand("hyprpicker", ["hyprpicker", "--format=hex"], (output, errorCode) => {
if (errorCode !== 0) { if (errorCode !== 0) {
console.warn("hyprpicker exited with code:", errorCode) console.warn("hyprpicker exited with code:", errorCode);
root.show() root.show();
return return;
} }
const colorStr = output.trim() const colorStr = output.trim();
if (colorStr.length >= 7 && colorStr.startsWith('#')) { if (colorStr.length >= 7 && colorStr.startsWith('#')) {
const pickedColor = Qt.color(colorStr) const pickedColor = Qt.color(colorStr);
root.selectedColor = pickedColor root.selectedColor = pickedColor;
root.currentColor = pickedColor root.currentColor = pickedColor;
root.updateFromColor(pickedColor) root.updateFromColor(pickedColor);
copyColorToClipboard(colorStr) copyColorToClipboard(colorStr);
root.show() root.show();
} }
}) });
} }
width: 680 modalWidth: 680
height: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 680 modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 680
backgroundColor: Theme.surfaceContainer backgroundColor: Theme.surfaceContainer
cornerRadius: Theme.cornerRadius cornerRadius: Theme.cornerRadius
borderColor: Theme.outlineMedium borderColor: Theme.outlineMedium
@@ -133,8 +123,8 @@ DankModal {
focus: true focus: true
Keys.onEscapePressed: event => { Keys.onEscapePressed: event => {
root.hide() root.hide();
event.accepted = true event.accepted = true;
} }
Column { Column {
@@ -172,7 +162,7 @@ DankModal {
iconSize: Theme.iconSize - 4 iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
onClicked: () => { onClicked: () => {
root.pickColorFromScreen() root.pickColorFromScreen();
} }
} }
@@ -181,7 +171,7 @@ DankModal {
iconSize: Theme.iconSize - 4 iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
onClicked: () => { onClicked: () => {
root.hide() root.hide();
} }
} }
} }
@@ -207,8 +197,14 @@ DankModal {
anchors.fill: parent anchors.fill: parent
gradient: Gradient { gradient: Gradient {
orientation: Gradient.Horizontal orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: "#ffffff" } GradientStop {
GradientStop { position: 1.0; color: "transparent" } position: 0.0
color: "#ffffff"
}
GradientStop {
position: 1.0
color: "transparent"
}
} }
} }
@@ -216,8 +212,14 @@ DankModal {
anchors.fill: parent anchors.fill: parent
gradient: Gradient { gradient: Gradient {
orientation: Gradient.Vertical orientation: Gradient.Vertical
GradientStop { position: 0.0; color: "transparent" } GradientStop {
GradientStop { position: 1.0; color: "#000000" } position: 0.0
color: "transparent"
}
GradientStop {
position: 1.0
color: "#000000"
}
} }
} }
} }
@@ -248,19 +250,19 @@ DankModal {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.CrossCursor cursorShape: Qt.CrossCursor
onPressed: mouse => { onPressed: mouse => {
const x = Math.max(0, Math.min(1, mouse.x / width)) const x = Math.max(0, Math.min(1, mouse.x / width));
const y = Math.max(0, Math.min(1, mouse.y / height)) const y = Math.max(0, Math.min(1, mouse.y / height));
root.gradientX = x root.gradientX = x;
root.gradientY = y root.gradientY = y;
root.updateColorFromGradient(x, y) root.updateColorFromGradient(x, y);
} }
onPositionChanged: mouse => { onPositionChanged: mouse => {
if (pressed) { if (pressed) {
const x = Math.max(0, Math.min(1, mouse.x / width)) const x = Math.max(0, Math.min(1, mouse.x / width));
const y = Math.max(0, Math.min(1, mouse.y / height)) const y = Math.max(0, Math.min(1, mouse.y / height));
root.gradientX = x root.gradientX = x;
root.gradientY = y root.gradientY = y;
root.updateColorFromGradient(x, y) root.updateColorFromGradient(x, y);
} }
} }
} }
@@ -276,13 +278,34 @@ DankModal {
gradient: Gradient { gradient: Gradient {
orientation: Gradient.Vertical orientation: Gradient.Vertical
GradientStop { position: 0.00; color: "#ff0000" } GradientStop {
GradientStop { position: 0.17; color: "#ffff00" } position: 0.00
GradientStop { position: 0.33; color: "#00ff00" } color: "#ff0000"
GradientStop { position: 0.50; color: "#00ffff" } }
GradientStop { position: 0.67; color: "#0000ff" } GradientStop {
GradientStop { position: 0.83; color: "#ff00ff" } position: 0.17
GradientStop { position: 1.00; color: "#ff0000" } color: "#ffff00"
}
GradientStop {
position: 0.33
color: "#00ff00"
}
GradientStop {
position: 0.50
color: "#00ffff"
}
GradientStop {
position: 0.67
color: "#0000ff"
}
GradientStop {
position: 0.83
color: "#ff00ff"
}
GradientStop {
position: 1.00
color: "#ff0000"
}
} }
Rectangle { Rectangle {
@@ -299,17 +322,17 @@ DankModal {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.SizeVerCursor cursorShape: Qt.SizeVerCursor
onPressed: mouse => { onPressed: mouse => {
const h = Math.max(0, Math.min(1, mouse.y / height)) const h = Math.max(0, Math.min(1, mouse.y / height));
root.hue = h root.hue = h;
root.updateColor() root.updateColor();
root.selectedColor = root.currentColor root.selectedColor = root.currentColor;
} }
onPositionChanged: mouse => { onPositionChanged: mouse => {
if (pressed) { if (pressed) {
const h = Math.max(0, Math.min(1, mouse.y / height)) const h = Math.max(0, Math.min(1, mouse.y / height));
root.hue = h root.hue = h;
root.updateColor() root.updateColor();
root.selectedColor = root.currentColor root.selectedColor = root.currentColor;
} }
} }
} }
@@ -348,10 +371,10 @@ DankModal {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: () => { onClicked: () => {
const pickedColor = Qt.color(modelData) const pickedColor = Qt.color(modelData);
root.selectedColor = pickedColor root.selectedColor = pickedColor;
root.currentColor = pickedColor root.currentColor = pickedColor;
root.updateFromColor(pickedColor) root.updateFromColor(pickedColor);
} }
} }
} }
@@ -393,9 +416,9 @@ DankModal {
color: { color: {
if (index < SessionData.recentColors.length) { if (index < SessionData.recentColors.length) {
return SessionData.recentColors[index] return SessionData.recentColors[index];
} }
return Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) return Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency);
} }
opacity: index < SessionData.recentColors.length ? 1.0 : 0.3 opacity: index < SessionData.recentColors.length ? 1.0 : 0.3
@@ -406,10 +429,10 @@ DankModal {
enabled: index < SessionData.recentColors.length enabled: index < SessionData.recentColors.length
onClicked: () => { onClicked: () => {
if (index < SessionData.recentColors.length) { if (index < SessionData.recentColors.length) {
const pickedColor = SessionData.recentColors[index] const pickedColor = SessionData.recentColors[index];
root.selectedColor = pickedColor root.selectedColor = pickedColor;
root.currentColor = pickedColor root.currentColor = pickedColor;
root.updateFromColor(pickedColor) root.updateFromColor(pickedColor);
} }
} }
} }
@@ -435,10 +458,10 @@ DankModal {
minimum: 0 minimum: 0
maximum: 100 maximum: 100
showValue: false showValue: false
onSliderValueChanged: (newValue) => { onSliderValueChanged: newValue => {
root.alpha = newValue / 100 root.alpha = newValue / 100;
root.updateColor() root.updateColor();
root.selectedColor = root.currentColor root.selectedColor = root.currentColor;
} }
} }
} }
@@ -485,9 +508,10 @@ DankModal {
text: root.currentColor.toString() text: root.currentColor.toString()
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
textColor: { textColor: {
if (text.length === 0) return Theme.surfaceText if (text.length === 0)
const hexPattern = /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/ return Theme.surfaceText;
return hexPattern.test(text) ? Theme.surfaceText : Theme.error const hexPattern = /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/;
return hexPattern.test(text) ? Theme.surfaceText : Theme.error;
} }
placeholderText: "#000000" placeholderText: "#000000"
backgroundColor: Theme.surfaceHover backgroundColor: Theme.surfaceHover
@@ -496,13 +520,14 @@ DankModal {
topPadding: Theme.spacingS topPadding: Theme.spacingS
bottomPadding: Theme.spacingS bottomPadding: Theme.spacingS
onAccepted: () => { onAccepted: () => {
const hexPattern = /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/ const hexPattern = /^#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/;
if (!hexPattern.test(text)) return if (!hexPattern.test(text))
const color = Qt.color(text) return;
const color = Qt.color(text);
if (color) { if (color) {
root.selectedColor = color root.selectedColor = color;
root.currentColor = color root.currentColor = color;
root.updateFromColor(color) root.updateFromColor(color);
} }
} }
} }
@@ -514,7 +539,7 @@ DankModal {
buttonSize: 36 buttonSize: 36
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
onClicked: () => { onClicked: () => {
root.copyColorToClipboard(hexInput.text) root.copyColorToClipboard(hexInput.text);
} }
} }
} }
@@ -546,14 +571,14 @@ DankModal {
StyledText { StyledText {
anchors.centerIn: parent anchors.centerIn: parent
text: { text: {
const r = Math.round(root.currentColor.r * 255) const r = Math.round(root.currentColor.r * 255);
const g = Math.round(root.currentColor.g * 255) const g = Math.round(root.currentColor.g * 255);
const b = Math.round(root.currentColor.b * 255) const b = Math.round(root.currentColor.b * 255);
if (root.alpha < 1) { if (root.alpha < 1) {
const a = Math.round(root.alpha * 255) const a = Math.round(root.alpha * 255);
return `${r}, ${g}, ${b}, ${a}` return `${r}, ${g}, ${b}, ${a}`;
} }
return `${r}, ${g}, ${b}` return `${r}, ${g}, ${b}`;
} }
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText color: Theme.surfaceText
@@ -567,18 +592,18 @@ DankModal {
buttonSize: 36 buttonSize: 36
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
onClicked: () => { onClicked: () => {
const r = Math.round(root.currentColor.r * 255) const r = Math.round(root.currentColor.r * 255);
const g = Math.round(root.currentColor.g * 255) const g = Math.round(root.currentColor.g * 255);
const b = Math.round(root.currentColor.b * 255) const b = Math.round(root.currentColor.b * 255);
let rgbString let rgbString;
if (root.alpha < 1) { if (root.alpha < 1) {
const a = Math.round(root.alpha * 255) const a = Math.round(root.alpha * 255);
rgbString = `rgba(${r}, ${g}, ${b}, ${a})` rgbString = `rgba(${r}, ${g}, ${b}, ${a})`;
} else { } else {
rgbString = `rgb(${r}, ${g}, ${b})` rgbString = `rgb(${r}, ${g}, ${b})`;
} }
Quickshell.execDetached(["sh", "-c", `echo -n "${rgbString}" | wl-copy`]) Quickshell.execDetached(["sh", "-c", `echo -n "${rgbString}" | wl-copy`]);
ToastService.showInfo(`${rgbString} copied`) ToastService.showInfo(`${rgbString} copied`);
} }
} }
} }
@@ -610,14 +635,14 @@ DankModal {
StyledText { StyledText {
anchors.centerIn: parent anchors.centerIn: parent
text: { text: {
const h = Math.round(root.hue * 360) const h = Math.round(root.hue * 360);
const s = Math.round(root.saturation * 100) const s = Math.round(root.saturation * 100);
const v = Math.round(root.value * 100) const v = Math.round(root.value * 100);
if (root.alpha < 1) { if (root.alpha < 1) {
const a = Math.round(root.alpha * 100) const a = Math.round(root.alpha * 100);
return `${h}°, ${s}%, ${v}%, ${a}%` return `${h}°, ${s}%, ${v}%, ${a}%`;
} }
return `${h}°, ${s}%, ${v}%` return `${h}°, ${s}%, ${v}%`;
} }
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText color: Theme.surfaceText
@@ -631,18 +656,18 @@ DankModal {
buttonSize: 36 buttonSize: 36
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
onClicked: () => { onClicked: () => {
const h = Math.round(root.hue * 360) const h = Math.round(root.hue * 360);
const s = Math.round(root.saturation * 100) const s = Math.round(root.saturation * 100);
const v = Math.round(root.value * 100) const v = Math.round(root.value * 100);
let hsvString let hsvString;
if (root.alpha < 1) { if (root.alpha < 1) {
const a = Math.round(root.alpha * 100) const a = Math.round(root.alpha * 100);
hsvString = `${h}, ${s}, ${v}, ${a}` hsvString = `${h}, ${s}, ${v}, ${a}`;
} else { } else {
hsvString = `${h}, ${s}, ${v}` hsvString = `${h}, ${s}, ${v}`;
} }
Quickshell.execDetached(["sh", "-c", `echo -n "${hsvString}" | wl-copy`]) Quickshell.execDetached(["sh", "-c", `echo -n "${hsvString}" | wl-copy`]);
ToastService.showInfo(`HSV ${hsvString} copied`) ToastService.showInfo(`HSV ${hsvString} copied`);
} }
} }
} }
@@ -658,9 +683,9 @@ DankModal {
textColor: Theme.background textColor: Theme.background
anchors.right: parent.right anchors.right: parent.right
onClicked: { onClicked: {
SessionData.addRecentColor(root.currentColor) SessionData.addRecentColor(root.currentColor);
root.colorSelected(root.currentColor) root.colorSelected(root.currentColor);
root.hide() root.hide();
} }
} }
} }

View File

@@ -1,7 +1,6 @@
import QtQuick import QtQuick
import qs.Common import qs.Common
import qs.Modals.Common import qs.Modals.Common
import qs.Services
import qs.Widgets import qs.Widgets
DankModal { DankModal {
@@ -15,8 +14,8 @@ DankModal {
shouldBeVisible: false shouldBeVisible: false
allowStacking: true allowStacking: true
width: 420 modalWidth: 420
height: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 200 modalHeight: contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 200
Timer { Timer {
id: countdownTimer id: countdownTimer
@@ -24,20 +23,20 @@ DankModal {
repeat: true repeat: true
running: root.shouldBeVisible running: root.shouldBeVisible
onTriggered: { onTriggered: {
countdown-- countdown--;
if (countdown <= 0) { if (countdown <= 0) {
revert() revert();
} }
} }
} }
onOpened: { onOpened: {
countdown = 15 countdown = 15;
countdownTimer.start() countdownTimer.start();
} }
onClosed: { onClosed: {
countdownTimer.stop() countdownTimer.stop();
} }
onBackgroundClicked: revert onBackgroundClicked: revert
@@ -51,13 +50,13 @@ DankModal {
implicitHeight: mainColumn.implicitHeight implicitHeight: mainColumn.implicitHeight
Keys.onEscapePressed: event => { Keys.onEscapePressed: event => {
revert() revert();
event.accepted = true event.accepted = true;
} }
Keys.onReturnPressed: event => { Keys.onReturnPressed: event => {
confirm() confirm();
event.accepted = true event.accepted = true;
} }
Column { Column {
@@ -235,12 +234,12 @@ DankModal {
} }
function confirm() { function confirm() {
displaysTab.confirmChanges() displaysTab.confirmChanges();
close() close();
} }
function revert() { function revert() {
displaysTab.revertChanges() displaysTab.revertChanges();
close() close();
} }
} }

View File

@@ -1,5 +1,4 @@
import QtQuick import QtQuick
import QtQuick.Controls
import qs.Common import qs.Common
import qs.Modals.Common import qs.Modals.Common
import qs.Services import qs.Services
@@ -10,34 +9,36 @@ DankModal {
layerNamespace: "dms:network-info" layerNamespace: "dms:network-info"
keepPopoutsOpen: true
property bool networkInfoModalVisible: false property bool networkInfoModalVisible: false
property string networkSSID: "" property string networkSSID: ""
property var networkData: null property var networkData: null
function showNetworkInfo(ssid, data) { function showNetworkInfo(ssid, data) {
networkSSID = ssid networkSSID = ssid;
networkData = data networkData = data;
networkInfoModalVisible = true networkInfoModalVisible = true;
open() open();
NetworkService.fetchNetworkInfo(ssid) NetworkService.fetchNetworkInfo(ssid);
} }
function hideDialog() { function hideDialog() {
networkInfoModalVisible = false networkInfoModalVisible = false;
close() close();
networkSSID = "" networkSSID = "";
networkData = null networkData = null;
} }
visible: networkInfoModalVisible visible: networkInfoModalVisible
width: 600 modalWidth: 600
height: 500 modalHeight: 500
enableShadow: true enableShadow: true
onBackgroundClicked: hideDialog() onBackgroundClicked: hideDialog()
onVisibleChanged: { onVisibleChanged: {
if (!visible) { if (!visible) {
networkSSID = "" networkSSID = "";
networkData = null networkData = null;
} }
} }
@@ -71,7 +72,6 @@ DankModal {
width: parent.width width: parent.width
elide: Text.ElideRight elide: Text.ElideRight
} }
} }
DankActionButton { DankActionButton {
@@ -80,7 +80,6 @@ DankModal {
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
onClicked: root.hideDialog() onClicked: root.hideDialog()
} }
} }
Rectangle { Rectangle {
@@ -109,7 +108,6 @@ DankModal {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }
} }
} }
Item { Item {
@@ -148,17 +146,10 @@ DankModal {
duration: Theme.shortDuration duration: Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
} }
} }
} }
} }
} }
} }
} }

View File

@@ -1,5 +1,4 @@
import QtQuick import QtQuick
import QtQuick.Controls
import qs.Common import qs.Common
import qs.Modals.Common import qs.Modals.Common
import qs.Services import qs.Services
@@ -10,34 +9,36 @@ DankModal {
layerNamespace: "dms:network-info-wired" layerNamespace: "dms:network-info-wired"
keepPopoutsOpen: true
property bool networkWiredInfoModalVisible: false property bool networkWiredInfoModalVisible: false
property string networkID: "" property string networkID: ""
property var networkData: null property var networkData: null
function showNetworkInfo(id, data) { function showNetworkInfo(id, data) {
networkID = id networkID = id;
networkData = data networkData = data;
networkWiredInfoModalVisible = true networkWiredInfoModalVisible = true;
open() open();
NetworkService.fetchWiredNetworkInfo(data.uuid) NetworkService.fetchWiredNetworkInfo(data.uuid);
} }
function hideDialog() { function hideDialog() {
networkWiredInfoModalVisible = false networkWiredInfoModalVisible = false;
close() close();
networkID = "" networkID = "";
networkData = null networkData = null;
} }
visible: networkWiredInfoModalVisible visible: networkWiredInfoModalVisible
width: 600 modalWidth: 600
height: 500 modalHeight: 500
enableShadow: true enableShadow: true
onBackgroundClicked: hideDialog() onBackgroundClicked: hideDialog()
onVisibleChanged: { onVisibleChanged: {
if (!visible) { if (!visible) {
networkID = "" networkID = "";
networkData = null networkData = null;
} }
} }
@@ -71,7 +72,6 @@ DankModal {
width: parent.width width: parent.width
elide: Text.ElideRight elide: Text.ElideRight
} }
} }
DankActionButton { DankActionButton {
@@ -80,7 +80,6 @@ DankModal {
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
onClicked: root.hideDialog() onClicked: root.hideDialog()
} }
} }
Rectangle { Rectangle {
@@ -109,7 +108,6 @@ DankModal {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }
} }
} }
Item { Item {
@@ -148,17 +146,10 @@ DankModal {
duration: Theme.shortDuration duration: Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
} }
} }
} }
} }
} }
} }
} }

View File

@@ -1,12 +1,10 @@
import QtQuick import QtQuick
import Quickshell
import Quickshell.Hyprland 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.Modules.Notifications.Center import qs.Modules.Notifications.Center
import qs.Services import qs.Services
import qs.Widgets
DankModal { DankModal {
id: notificationModal id: notificationModal
@@ -22,58 +20,58 @@ DankModal {
property var notificationListRef: null property var notificationListRef: null
function show() { function show() {
notificationModalOpen = true notificationModalOpen = true;
NotificationService.onOverlayOpen() NotificationService.onOverlayOpen();
open() open();
modalKeyboardController.reset() modalKeyboardController.reset();
if (modalKeyboardController && notificationListRef) { if (modalKeyboardController && notificationListRef) {
modalKeyboardController.listView = notificationListRef modalKeyboardController.listView = notificationListRef;
modalKeyboardController.rebuildFlatNavigation() modalKeyboardController.rebuildFlatNavigation();
Qt.callLater(() => { Qt.callLater(() => {
modalKeyboardController.keyboardNavigationActive = true modalKeyboardController.keyboardNavigationActive = true;
modalKeyboardController.selectedFlatIndex = 0 modalKeyboardController.selectedFlatIndex = 0;
modalKeyboardController.updateSelectedIdFromIndex() modalKeyboardController.updateSelectedIdFromIndex();
if (notificationListRef) { if (notificationListRef) {
notificationListRef.keyboardActive = true notificationListRef.keyboardActive = true;
notificationListRef.currentIndex = 0 notificationListRef.currentIndex = 0;
} }
modalKeyboardController.selectionVersion++ modalKeyboardController.selectionVersion++;
modalKeyboardController.ensureVisible() modalKeyboardController.ensureVisible();
}) });
} }
} }
function hide() { function hide() {
notificationModalOpen = false notificationModalOpen = false;
NotificationService.onOverlayClose() NotificationService.onOverlayClose();
close() close();
modalKeyboardController.reset() modalKeyboardController.reset();
} }
function toggle() { function toggle() {
if (shouldBeVisible) { if (shouldBeVisible) {
hide() hide();
} else { } else {
show() show();
} }
} }
width: 500 modalWidth: 500
height: 700 modalHeight: 700
visible: false visible: false
onBackgroundClicked: hide() onBackgroundClicked: hide()
onOpened: () => { onOpened: () => {
Qt.callLater(() => modalFocusScope.forceActiveFocus()); Qt.callLater(() => modalFocusScope.forceActiveFocus());
} }
onShouldBeVisibleChanged: (shouldBeVisible) => { onShouldBeVisibleChanged: shouldBeVisible => {
if (!shouldBeVisible) { if (!shouldBeVisible) {
notificationModalOpen = false notificationModalOpen = false;
modalKeyboardController.reset() modalKeyboardController.reset();
NotificationService.onOverlayClose() NotificationService.onOverlayClose();
} }
} }
modalFocusScope.Keys.onPressed: (event) => modalKeyboardController.handleKey(event) modalFocusScope.Keys.onPressed: event => modalKeyboardController.handleKey(event)
NotificationKeyboardController { NotificationKeyboardController {
id: modalKeyboardController id: modalKeyboardController
@@ -132,14 +130,13 @@ DankModal {
height: parent.height - y height: parent.height - y
keyboardController: modalKeyboardController keyboardController: modalKeyboardController
Component.onCompleted: { Component.onCompleted: {
notificationModal.notificationListRef = notificationList notificationModal.notificationListRef = notificationList;
if (modalKeyboardController) { if (modalKeyboardController) {
modalKeyboardController.listView = notificationList modalKeyboardController.listView = notificationList;
modalKeyboardController.rebuildFlatNavigation() modalKeyboardController.rebuildFlatNavigation();
} }
} }
} }
} }
NotificationKeyboardHints { NotificationKeyboardHints {
@@ -151,9 +148,6 @@ DankModal {
anchors.margins: Theme.spacingL anchors.margins: Theme.spacingL
showHints: modalKeyboardController.showKeyboardHints showHints: modalKeyboardController.showKeyboardHints
} }
} }
} }
} }

View File

@@ -1,5 +1,4 @@
import QtQuick import QtQuick
import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
import qs.Common import qs.Common
import qs.Modals.Common import qs.Modals.Common
@@ -22,27 +21,27 @@ DankModal {
property real minHeight: 240 property real minHeight: 240
function show() { function show() {
passwordInput = "" passwordInput = "";
isLoading = false isLoading = false;
open() open();
Qt.callLater(() => { Qt.callLater(() => {
if (contentLoader.item && contentLoader.item.passwordField) { if (contentLoader.item && contentLoader.item.passwordField) {
contentLoader.item.passwordField.forceActiveFocus() contentLoader.item.passwordField.forceActiveFocus();
} }
}) });
} }
shouldBeVisible: false shouldBeVisible: false
width: 420 modalWidth: 420
height: Math.max(minHeight, contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 240) modalHeight: Math.max(minHeight, contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 240)
Connections { Connections {
target: contentLoader.item target: contentLoader.item
function onImplicitHeightChanged() { function onImplicitHeightChanged() {
if (shouldBeVisible && contentLoader.item) { if (shouldBeVisible && contentLoader.item) {
const newHeight = contentLoader.item.implicitHeight + Theme.spacingM * 2 const newHeight = contentLoader.item.implicitHeight + Theme.spacingM * 2;
if (newHeight > minHeight) { if (newHeight > minHeight) {
minHeight = newHeight minHeight = newHeight;
} }
} }
} }
@@ -51,19 +50,19 @@ DankModal {
onOpened: { onOpened: {
Qt.callLater(() => { Qt.callLater(() => {
if (contentLoader.item && contentLoader.item.passwordField) { if (contentLoader.item && contentLoader.item.passwordField) {
contentLoader.item.passwordField.forceActiveFocus() contentLoader.item.passwordField.forceActiveFocus();
} }
}) });
} }
onClosed: { onDialogClosed: {
passwordInput = "" passwordInput = "";
isLoading = false isLoading = false;
} }
onBackgroundClicked: () => { onBackgroundClicked: () => {
if (currentFlow && !isLoading) { if (currentFlow && !isLoading) {
currentFlow.cancelAuthenticationRequest() currentFlow.cancelAuthenticationRequest();
} }
} }
@@ -72,12 +71,12 @@ DankModal {
enabled: PolkitService.polkitAvailable enabled: PolkitService.polkitAvailable
function onAuthenticationRequestStarted() { function onAuthenticationRequestStarted() {
show() show();
} }
function onIsActiveChanged() { function onIsActiveChanged() {
if (!(PolkitService.agent?.isActive ?? false)) { if (!(PolkitService.agent?.isActive ?? false)) {
close() close();
} }
} }
} }
@@ -88,24 +87,24 @@ DankModal {
function onIsResponseRequiredChanged() { function onIsResponseRequiredChanged() {
if (currentFlow.isResponseRequired) { if (currentFlow.isResponseRequired) {
isLoading = false isLoading = false;
passwordInput = "" passwordInput = "";
if (contentLoader.item && contentLoader.item.passwordField) { if (contentLoader.item && contentLoader.item.passwordField) {
contentLoader.item.passwordField.forceActiveFocus() contentLoader.item.passwordField.forceActiveFocus();
} }
} }
} }
function onAuthenticationSucceeded() { function onAuthenticationSucceeded() {
close() close();
} }
function onAuthenticationFailed() { function onAuthenticationFailed() {
isLoading = false isLoading = false;
} }
function onAuthenticationRequestCancelled() { function onAuthenticationRequestCancelled() {
close() close();
} }
} }
@@ -121,9 +120,9 @@ DankModal {
Keys.onEscapePressed: event => { Keys.onEscapePressed: event => {
if (currentFlow && !isLoading) { if (currentFlow && !isLoading) {
currentFlow.cancelAuthenticationRequest() currentFlow.cancelAuthenticationRequest();
} }
event.accepted = true event.accepted = true;
} }
Row { Row {
@@ -178,7 +177,7 @@ DankModal {
opacity: enabled ? 1 : 0.5 opacity: enabled ? 1 : 0.5
onClicked: () => { onClicked: () => {
if (currentFlow) { if (currentFlow) {
currentFlow.cancelAuthenticationRequest() currentFlow.cancelAuthenticationRequest();
} }
} }
} }
@@ -215,7 +214,7 @@ DankModal {
anchors.fill: parent anchors.fill: parent
enabled: !isLoading enabled: !isLoading
onClicked: () => { onClicked: () => {
passwordField.forceActiveFocus() passwordField.forceActiveFocus();
} }
} }
@@ -231,13 +230,13 @@ DankModal {
backgroundColor: "transparent" backgroundColor: "transparent"
enabled: !isLoading enabled: !isLoading
onTextEdited: () => { onTextEdited: () => {
passwordInput = text passwordInput = text;
} }
onAccepted: () => { onAccepted: () => {
if (passwordInput.length > 0 && currentFlow && !isLoading) { if (passwordInput.length > 0 && currentFlow && !isLoading) {
isLoading = true isLoading = true;
currentFlow.submit(passwordInput) currentFlow.submit(passwordInput);
passwordInput = "" passwordInput = "";
} }
} }
} }
@@ -310,7 +309,7 @@ DankModal {
enabled: parent.enabled enabled: parent.enabled
onClicked: () => { onClicked: () => {
if (currentFlow) { if (currentFlow) {
currentFlow.cancelAuthenticationRequest() currentFlow.cancelAuthenticationRequest();
} }
} }
} }
@@ -343,9 +342,9 @@ DankModal {
enabled: parent.enabled enabled: parent.enabled
onClicked: () => { onClicked: () => {
if (currentFlow && !isLoading) { if (currentFlow && !isLoading) {
isLoading = true isLoading = true;
currentFlow.submit(passwordInput) currentFlow.submit(passwordInput);
passwordInput = "" passwordInput = "";
} }
} }
} }

View File

@@ -29,64 +29,64 @@ DankModal {
signal lockRequested signal lockRequested
function openCentered() { function openCentered() {
parentBounds = Qt.rect(0, 0, 0, 0) parentBounds = Qt.rect(0, 0, 0, 0);
parentScreen = null parentScreen = null;
backgroundOpacity = 0.5 backgroundOpacity = 0.5;
open() open();
} }
function openFromControlCenter(bounds, targetScreen) { function openFromControlCenter(bounds, targetScreen) {
parentBounds = bounds parentBounds = bounds;
parentScreen = targetScreen parentScreen = targetScreen;
backgroundOpacity = 0 backgroundOpacity = 0;
keepPopoutsOpen = true keepPopoutsOpen = true;
open() open();
keepPopoutsOpen = false keepPopoutsOpen = false;
} }
function updateVisibleActions() { function updateVisibleActions() {
const allActions = SettingsData.powerMenuActions || ["reboot", "logout", "poweroff", "lock", "suspend", "restart"] const allActions = SettingsData.powerMenuActions || ["reboot", "logout", "poweroff", "lock", "suspend", "restart"];
visibleActions = allActions.filter(action => { visibleActions = allActions.filter(action => {
if (action === "hibernate" && !SessionService.hibernateSupported) if (action === "hibernate" && !SessionService.hibernateSupported)
return false return false;
return true return true;
}) });
if (!SettingsData.powerMenuGridLayout) return if (!SettingsData.powerMenuGridLayout)
return;
const count = visibleActions.length const count = visibleActions.length;
if (count === 0) { if (count === 0) {
gridColumns = 1 gridColumns = 1;
gridRows = 1 gridRows = 1;
return return;
} }
if (count <= 3) { if (count <= 3) {
gridColumns = 1 gridColumns = 1;
gridRows = count gridRows = count;
return return;
} }
if (count === 4) { if (count === 4) {
gridColumns = 2 gridColumns = 2;
gridRows = 2 gridRows = 2;
return return;
} }
gridColumns = 3 gridColumns = 3;
gridRows = Math.ceil(count / 3) gridRows = Math.ceil(count / 3);
} }
function getDefaultActionIndex() { function getDefaultActionIndex() {
const defaultAction = SettingsData.powerMenuDefaultAction || "logout" const defaultAction = SettingsData.powerMenuDefaultAction || "logout";
const index = visibleActions.indexOf(defaultAction) const index = visibleActions.indexOf(defaultAction);
return index >= 0 ? index : 0 return index >= 0 ? index : 0;
} }
function getActionAtIndex(index) { function getActionAtIndex(index) {
if (index < 0 || index >= visibleActions.length) if (index < 0 || index >= visibleActions.length)
return "" return "";
return visibleActions[index] return visibleActions[index];
} }
function getActionData(action) { function getActionData(action) {
@@ -96,64 +96,64 @@ DankModal {
"icon": "restart_alt", "icon": "restart_alt",
"label": I18n.tr("Reboot"), "label": I18n.tr("Reboot"),
"key": "R" "key": "R"
} };
case "logout": case "logout":
return { return {
"icon": "logout", "icon": "logout",
"label": I18n.tr("Log Out"), "label": I18n.tr("Log Out"),
"key": "X" "key": "X"
} };
case "poweroff": case "poweroff":
return { return {
"icon": "power_settings_new", "icon": "power_settings_new",
"label": I18n.tr("Power Off"), "label": I18n.tr("Power Off"),
"key": "P" "key": "P"
} };
case "lock": case "lock":
return { return {
"icon": "lock", "icon": "lock",
"label": I18n.tr("Lock"), "label": I18n.tr("Lock"),
"key": "L" "key": "L"
} };
case "suspend": case "suspend":
return { return {
"icon": "bedtime", "icon": "bedtime",
"label": I18n.tr("Suspend"), "label": I18n.tr("Suspend"),
"key": "S" "key": "S"
} };
case "hibernate": case "hibernate":
return { return {
"icon": "ac_unit", "icon": "ac_unit",
"label": I18n.tr("Hibernate"), "label": I18n.tr("Hibernate"),
"key": "H" "key": "H"
} };
case "restart": case "restart":
return { return {
"icon": "refresh", "icon": "refresh",
"label": I18n.tr("Restart DMS"), "label": I18n.tr("Restart DMS"),
"key": "D" "key": "D"
} };
default: default:
return { return {
"icon": "help", "icon": "help",
"label": action, "label": action,
"key": "?" "key": "?"
} };
} }
} }
function selectOption(action) { function selectOption(action) {
if (action === "lock") { if (action === "lock") {
close() close();
lockRequested() lockRequested();
return return;
} }
if (action === "restart") { if (action === "restart") {
close() close();
Quickshell.execDetached(["dms", "restart"]) Quickshell.execDetached(["dms", "restart"]);
return return;
} }
close() close();
const actions = { const actions = {
"logout": { "logout": {
"title": I18n.tr("Log Out"), "title": I18n.tr("Log Out"),
@@ -175,227 +175,223 @@ DankModal {
"title": I18n.tr("Power Off"), "title": I18n.tr("Power Off"),
"message": I18n.tr("Are you sure you want to power off the system?") "message": I18n.tr("Are you sure you want to power off the system?")
} }
} };
const selected = actions[action] const selected = actions[action];
if (selected) { if (selected) {
root.powerActionRequested(action, selected.title, selected.message) root.powerActionRequested(action, selected.title, selected.message);
} }
} }
shouldBeVisible: false shouldBeVisible: false
width: SettingsData.powerMenuGridLayout modalWidth: SettingsData.powerMenuGridLayout ? Math.min(550, gridColumns * 180 + Theme.spacingS * (gridColumns - 1) + Theme.spacingL * 2) : 400
? Math.min(550, gridColumns * 180 + Theme.spacingS * (gridColumns - 1) + Theme.spacingL * 2) modalHeight: contentLoader.item ? contentLoader.item.implicitHeight : 300
: 400
height: contentLoader.item ? contentLoader.item.implicitHeight : 300
enableShadow: true enableShadow: true
screen: parentScreen targetScreen: parentScreen
positioning: parentBounds.width > 0 ? "custom" : "center" positioning: parentBounds.width > 0 ? "custom" : "center"
customPosition: { customPosition: {
if (parentBounds.width > 0) { if (parentBounds.width > 0) {
const effectiveBarThickness = Math.max(26 + (SettingsData.barConfigs[0]?.innerPadding ?? 4) * 0.6 + (SettingsData.barConfigs[0]?.innerPadding ?? 4) + 4, Theme.barHeight - 4 - (8 - (SettingsData.barConfigs[0]?.innerPadding ?? 4))) const effectiveBarThickness = Math.max(26 + (SettingsData.barConfigs[0]?.innerPadding ?? 4) * 0.6 + (SettingsData.barConfigs[0]?.innerPadding ?? 4) + 4, Theme.barHeight - 4 - (8 - (SettingsData.barConfigs[0]?.innerPadding ?? 4)));
const barExclusionZone = effectiveBarThickness + (SettingsData.barConfigs[0]?.spacing ?? 4) + (SettingsData.barConfigs[0]?.bottomGap ?? 0) const barExclusionZone = effectiveBarThickness + (SettingsData.barConfigs[0]?.spacing ?? 4) + (SettingsData.barConfigs[0]?.bottomGap ?? 0);
const screenW = parentScreen?.width ?? 1920 const screenW = parentScreen?.width ?? 1920;
const screenH = parentScreen?.height ?? 1080 const screenH = parentScreen?.height ?? 1080;
const margin = Theme.spacingL const margin = Theme.spacingL;
let targetX = parentBounds.x + (parentBounds.width - width) / 2 let targetX = parentBounds.x + (parentBounds.width - modalWidth) / 2;
let targetY = parentBounds.y + (parentBounds.height - height) / 2 let targetY = parentBounds.y + (parentBounds.height - modalHeight) / 2;
const minY = (SettingsData.barConfigs[0]?.position ?? SettingsData.Position.Top) === SettingsData.Position.Top ? barExclusionZone + margin : margin const minY = (SettingsData.barConfigs[0]?.position ?? SettingsData.Position.Top) === SettingsData.Position.Top ? barExclusionZone + margin : margin;
const maxY = (SettingsData.barConfigs[0]?.position ?? SettingsData.Position.Top) === SettingsData.Position.Bottom ? screenH - height - barExclusionZone - margin : screenH - height - margin const maxY = (SettingsData.barConfigs[0]?.position ?? SettingsData.Position.Top) === SettingsData.Position.Bottom ? screenH - modalHeight - barExclusionZone - margin : screenH - modalHeight - margin;
targetY = Math.max(minY, Math.min(maxY, targetY)) targetY = Math.max(minY, Math.min(maxY, targetY));
return Qt.point(targetX, targetY) return Qt.point(targetX, targetY);
} }
return Qt.point(0, 0) return Qt.point(0, 0);
} }
onBackgroundClicked: () => close() onBackgroundClicked: () => close()
onOpened: () => { onOpened: () => {
updateVisibleActions() updateVisibleActions();
const defaultIndex = getDefaultActionIndex() const defaultIndex = getDefaultActionIndex();
if (SettingsData.powerMenuGridLayout) { if (SettingsData.powerMenuGridLayout) {
selectedRow = Math.floor(defaultIndex / gridColumns) selectedRow = Math.floor(defaultIndex / gridColumns);
selectedCol = defaultIndex % gridColumns selectedCol = defaultIndex % gridColumns;
selectedIndex = defaultIndex selectedIndex = defaultIndex;
} else { } else {
selectedIndex = defaultIndex selectedIndex = defaultIndex;
} }
Qt.callLater(() => modalFocusScope.forceActiveFocus()) Qt.callLater(() => modalFocusScope.forceActiveFocus());
} }
Component.onCompleted: updateVisibleActions() Component.onCompleted: updateVisibleActions()
modalFocusScope.Keys.onPressed: event => { modalFocusScope.Keys.onPressed: event => {
if (SettingsData.powerMenuGridLayout) { if (SettingsData.powerMenuGridLayout) {
handleGridNavigation(event) handleGridNavigation(event);
} else { } else {
handleListNavigation(event) handleListNavigation(event);
} }
} }
function handleListNavigation(event) { function handleListNavigation(event) {
switch (event.key) { switch (event.key) {
case Qt.Key_Up: case Qt.Key_Up:
case Qt.Key_Backtab: case Qt.Key_Backtab:
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length;
event.accepted = true event.accepted = true;
break break;
case Qt.Key_Down: case Qt.Key_Down:
case Qt.Key_Tab: case Qt.Key_Tab:
selectedIndex = (selectedIndex + 1) % visibleActions.length selectedIndex = (selectedIndex + 1) % visibleActions.length;
event.accepted = true event.accepted = true;
break break;
case Qt.Key_Return: case Qt.Key_Return:
case Qt.Key_Enter: case Qt.Key_Enter:
selectOption(getActionAtIndex(selectedIndex)) selectOption(getActionAtIndex(selectedIndex));
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) {
selectedIndex = (selectedIndex + 1) % visibleActions.length selectedIndex = (selectedIndex + 1) % visibleActions.length;
event.accepted = true event.accepted = true;
} }
break break;
case Qt.Key_P: case Qt.Key_P:
if (!(event.modifiers & Qt.ControlModifier)) { if (!(event.modifiers & Qt.ControlModifier)) {
selectOption("poweroff") selectOption("poweroff");
event.accepted = true event.accepted = true;
} else { } else {
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length;
event.accepted = true event.accepted = true;
} }
break break;
case Qt.Key_J: case Qt.Key_J:
if (event.modifiers & Qt.ControlModifier) { if (event.modifiers & Qt.ControlModifier) {
selectedIndex = (selectedIndex + 1) % visibleActions.length selectedIndex = (selectedIndex + 1) % visibleActions.length;
event.accepted = true event.accepted = true;
} }
break break;
case Qt.Key_K: case Qt.Key_K:
if (event.modifiers & Qt.ControlModifier) { if (event.modifiers & Qt.ControlModifier) {
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length;
event.accepted = true event.accepted = true;
} }
break break;
case Qt.Key_R: case Qt.Key_R:
selectOption("reboot") selectOption("reboot");
event.accepted = true event.accepted = true;
break break;
case Qt.Key_X: case Qt.Key_X:
selectOption("logout") selectOption("logout");
event.accepted = true event.accepted = true;
break break;
case Qt.Key_L: case Qt.Key_L:
selectOption("lock") selectOption("lock");
event.accepted = true event.accepted = true;
break break;
case Qt.Key_S: case Qt.Key_S:
selectOption("suspend") selectOption("suspend");
event.accepted = true event.accepted = true;
break break;
case Qt.Key_H: case Qt.Key_H:
selectOption("hibernate") selectOption("hibernate");
event.accepted = true event.accepted = true;
break break;
case Qt.Key_D: case Qt.Key_D:
selectOption("restart") selectOption("restart");
event.accepted = true event.accepted = true;
break break;
} }
} }
function handleGridNavigation(event) { function handleGridNavigation(event) {
switch (event.key) { switch (event.key) {
case Qt.Key_Left: case Qt.Key_Left:
selectedCol = (selectedCol - 1 + gridColumns) % gridColumns selectedCol = (selectedCol - 1 + gridColumns) % gridColumns;
selectedIndex = selectedRow * gridColumns + selectedCol selectedIndex = selectedRow * gridColumns + selectedCol;
event.accepted = true event.accepted = true;
break break;
case Qt.Key_Right: case Qt.Key_Right:
selectedCol = (selectedCol + 1) % gridColumns selectedCol = (selectedCol + 1) % gridColumns;
selectedIndex = selectedRow * gridColumns + selectedCol selectedIndex = selectedRow * gridColumns + selectedCol;
event.accepted = true event.accepted = true;
break break;
case Qt.Key_Up: case Qt.Key_Up:
case Qt.Key_Backtab: case Qt.Key_Backtab:
selectedRow = (selectedRow - 1 + gridRows) % gridRows selectedRow = (selectedRow - 1 + gridRows) % gridRows;
selectedIndex = selectedRow * gridColumns + selectedCol selectedIndex = selectedRow * gridColumns + selectedCol;
event.accepted = true event.accepted = true;
break break;
case Qt.Key_Down: case Qt.Key_Down:
case Qt.Key_Tab: case Qt.Key_Tab:
selectedRow = (selectedRow + 1) % gridRows selectedRow = (selectedRow + 1) % gridRows;
selectedIndex = selectedRow * gridColumns + selectedCol selectedIndex = selectedRow * gridColumns + selectedCol;
event.accepted = true event.accepted = true;
break break;
case Qt.Key_Return: case Qt.Key_Return:
case Qt.Key_Enter: case Qt.Key_Enter:
selectOption(getActionAtIndex(selectedIndex)) selectOption(getActionAtIndex(selectedIndex));
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) {
selectedCol = (selectedCol + 1) % gridColumns selectedCol = (selectedCol + 1) % gridColumns;
selectedIndex = selectedRow * gridColumns + selectedCol selectedIndex = selectedRow * gridColumns + selectedCol;
event.accepted = true event.accepted = true;
} }
break break;
case Qt.Key_P: case Qt.Key_P:
if (!(event.modifiers & Qt.ControlModifier)) { if (!(event.modifiers & Qt.ControlModifier)) {
selectOption("poweroff") selectOption("poweroff");
event.accepted = true event.accepted = true;
} else { } else {
selectedCol = (selectedCol - 1 + gridColumns) % gridColumns selectedCol = (selectedCol - 1 + gridColumns) % gridColumns;
selectedIndex = selectedRow * gridColumns + selectedCol selectedIndex = selectedRow * gridColumns + selectedCol;
event.accepted = true event.accepted = true;
} }
break break;
case Qt.Key_J: case Qt.Key_J:
if (event.modifiers & Qt.ControlModifier) { if (event.modifiers & Qt.ControlModifier) {
selectedRow = (selectedRow + 1) % gridRows selectedRow = (selectedRow + 1) % gridRows;
selectedIndex = selectedRow * gridColumns + selectedCol selectedIndex = selectedRow * gridColumns + selectedCol;
event.accepted = true event.accepted = true;
} }
break break;
case Qt.Key_K: case Qt.Key_K:
if (event.modifiers & Qt.ControlModifier) { if (event.modifiers & Qt.ControlModifier) {
selectedRow = (selectedRow - 1 + gridRows) % gridRows selectedRow = (selectedRow - 1 + gridRows) % gridRows;
selectedIndex = selectedRow * gridColumns + selectedCol selectedIndex = selectedRow * gridColumns + selectedCol;
event.accepted = true event.accepted = true;
} }
break break;
case Qt.Key_R: case Qt.Key_R:
selectOption("reboot") selectOption("reboot");
event.accepted = true event.accepted = true;
break break;
case Qt.Key_X: case Qt.Key_X:
selectOption("logout") selectOption("logout");
event.accepted = true event.accepted = true;
break break;
case Qt.Key_L: case Qt.Key_L:
selectOption("lock") selectOption("lock");
event.accepted = true event.accepted = true;
break break;
case Qt.Key_S: case Qt.Key_S:
selectOption("suspend") selectOption("suspend");
event.accepted = true event.accepted = true;
break break;
case Qt.Key_H: case Qt.Key_H:
selectOption("hibernate") selectOption("hibernate");
event.accepted = true event.accepted = true;
break break;
case Qt.Key_D: case Qt.Key_D:
selectOption("restart") selectOption("restart");
event.accepted = true event.accepted = true;
break break;
} }
} }
content: Component { content: Component {
Item { Item {
anchors.fill: parent anchors.fill: parent
implicitHeight: SettingsData.powerMenuGridLayout implicitHeight: SettingsData.powerMenuGridLayout ? buttonGrid.implicitHeight + Theme.spacingL * 2 : buttonColumn.implicitHeight + Theme.spacingL * 2
? buttonGrid.implicitHeight + Theme.spacingL * 2
: buttonColumn.implicitHeight + Theme.spacingL * 2
Grid { Grid {
id: buttonGrid id: buttonGrid
@@ -416,15 +412,15 @@ DankModal {
readonly property bool isSelected: root.selectedIndex === index readonly property bool isSelected: root.selectedIndex === index
readonly property bool showWarning: modelData === "reboot" || modelData === "poweroff" readonly property bool showWarning: modelData === "reboot" || modelData === "poweroff"
width: (root.width - Theme.spacingL * 2 - Theme.spacingS * (root.gridColumns - 1)) / root.gridColumns width: (root.modalWidth - Theme.spacingL * 2 - Theme.spacingS * (root.gridColumns - 1)) / root.gridColumns
height: 100 height: 100
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: { color: {
if (isSelected) if (isSelected)
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
if (mouseArea.containsMouse) if (mouseArea.containsMouse)
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08);
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08) return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08);
} }
border.color: isSelected ? Theme.primary : "transparent" border.color: isSelected ? Theme.primary : "transparent"
border.width: isSelected ? 2 : 0 border.width: isSelected ? 2 : 0
@@ -438,9 +434,9 @@ DankModal {
size: Theme.iconSize + 8 size: Theme.iconSize + 8
color: { color: {
if (parent.parent.showWarning && mouseArea.containsMouse) { if (parent.parent.showWarning && mouseArea.containsMouse) {
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning;
} }
return Theme.surfaceText return Theme.surfaceText;
} }
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
@@ -450,9 +446,9 @@ DankModal {
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: { color: {
if (parent.parent.showWarning && mouseArea.containsMouse) { if (parent.parent.showWarning && mouseArea.containsMouse) {
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning;
} }
return Theme.surfaceText return Theme.surfaceText;
} }
font.weight: Font.Medium font.weight: Font.Medium
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
@@ -481,9 +477,9 @@ DankModal {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.selectedRow = Math.floor(index / root.gridColumns) root.selectedRow = Math.floor(index / root.gridColumns);
root.selectedCol = index % root.gridColumns root.selectedCol = index % root.gridColumns;
root.selectOption(modelData) root.selectOption(modelData);
} }
} }
} }
@@ -518,10 +514,10 @@ DankModal {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: { color: {
if (isSelected) if (isSelected)
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
if (listMouseArea.containsMouse) if (listMouseArea.containsMouse)
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08);
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08) return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08);
} }
border.color: isSelected ? Theme.primary : "transparent" border.color: isSelected ? Theme.primary : "transparent"
border.width: isSelected ? 2 : 0 border.width: isSelected ? 2 : 0
@@ -541,9 +537,9 @@ DankModal {
size: Theme.iconSize + 4 size: Theme.iconSize + 4
color: { color: {
if (parent.parent.showWarning && listMouseArea.containsMouse) { if (parent.parent.showWarning && listMouseArea.containsMouse) {
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning;
} }
return Theme.surfaceText return Theme.surfaceText;
} }
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
@@ -553,9 +549,9 @@ DankModal {
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: { color: {
if (parent.parent.showWarning && listMouseArea.containsMouse) { if (parent.parent.showWarning && listMouseArea.containsMouse) {
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning;
} }
return Theme.surfaceText return Theme.surfaceText;
} }
font.weight: Font.Medium font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@@ -588,8 +584,8 @@ DankModal {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.selectedIndex = index root.selectedIndex = index;
root.selectOption(modelData) root.selectOption(modelData);
} }
} }
} }

View File

@@ -17,7 +17,7 @@ DankModal {
function show() { function show() {
if (!DgopService.dgopAvailable) { if (!DgopService.dgopAvailable) {
console.warn("ProcessListModal: dgop is not available"); console.warn("ProcessListModal: dgop is not available");
return ; return;
} }
open(); open();
UserInfoService.getUptime(); UserInfoService.getUptime();
@@ -28,13 +28,12 @@ DankModal {
if (processContextMenu.visible) { if (processContextMenu.visible) {
processContextMenu.close(); processContextMenu.close();
} }
} }
function toggle() { function toggle() {
if (!DgopService.dgopAvailable) { if (!DgopService.dgopAvailable) {
console.warn("ProcessListModal: dgop is not available"); console.warn("ProcessListModal: dgop is not available");
return ; return;
} }
if (shouldBeVisible) { if (shouldBeVisible) {
hide(); hide();
@@ -43,9 +42,8 @@ DankModal {
} }
} }
width: 900 modalWidth: 900
height: 680 modalHeight: 680
visible: false
backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
cornerRadius: Theme.cornerRadius cornerRadius: Theme.cornerRadius
enableShadow: true enableShadow: true
@@ -59,23 +57,18 @@ DankModal {
ProcessesTab { ProcessesTab {
contextMenu: processContextMenu contextMenu: processContextMenu
} }
} }
Component { Component {
id: performanceTabComponent id: performanceTabComponent
PerformanceTab { PerformanceTab {}
}
} }
Component { Component {
id: systemTabComponent id: systemTabComponent
SystemTab { SystemTab {}
}
} }
ProcessContextMenu { ProcessContextMenu {
@@ -86,7 +79,7 @@ DankModal {
Item { Item {
anchors.fill: parent anchors.fill: parent
focus: true focus: true
Keys.onPressed: (event) => { Keys.onPressed: event => {
if (event.key === Qt.Key_Escape) { if (event.key === Qt.Key_Escape) {
processListModal.hide(); processListModal.hide();
event.accepted = true; event.accepted = true;
@@ -140,9 +133,7 @@ DankModal {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }
} }
} }
ColumnLayout { ColumnLayout {
@@ -177,7 +168,6 @@ DankModal {
} }
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
} }
} }
Rectangle { Rectangle {
@@ -222,9 +212,7 @@ DankModal {
ColorAnimation { ColorAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration
} }
} }
} }
StyledText { StyledText {
@@ -239,11 +227,8 @@ DankModal {
ColorAnimation { ColorAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration
} }
} }
} }
} }
MouseArea { MouseArea {
@@ -261,22 +246,16 @@ DankModal {
ColorAnimation { ColorAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration
} }
} }
Behavior on border.color { Behavior on border.color {
ColorAnimation { ColorAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration
} }
} }
} }
} }
} }
} }
Rectangle { Rectangle {
@@ -292,7 +271,7 @@ DankModal {
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingS anchors.margins: Theme.spacingS
active: processListModal.visible && currentTab === 0 active: processListModal.shouldBeVisible && currentTab === 0
visible: currentTab === 0 visible: currentTab === 0
opacity: currentTab === 0 ? 1 : 0 opacity: currentTab === 0 ? 1 : 0
sourceComponent: processesTabComponent sourceComponent: processesTabComponent
@@ -302,9 +281,7 @@ DankModal {
duration: Theme.mediumDuration duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing easing.type: Theme.emphasizedEasing
} }
} }
} }
Loader { Loader {
@@ -312,7 +289,7 @@ DankModal {
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingS anchors.margins: Theme.spacingS
active: processListModal.visible && currentTab === 1 active: processListModal.shouldBeVisible && currentTab === 1
visible: currentTab === 1 visible: currentTab === 1
opacity: currentTab === 1 ? 1 : 0 opacity: currentTab === 1 ? 1 : 0
sourceComponent: performanceTabComponent sourceComponent: performanceTabComponent
@@ -322,9 +299,7 @@ DankModal {
duration: Theme.mediumDuration duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing easing.type: Theme.emphasizedEasing
} }
} }
} }
Loader { Loader {
@@ -332,7 +307,7 @@ DankModal {
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingS anchors.margins: Theme.spacingS
active: processListModal.visible && currentTab === 2 active: processListModal.shouldBeVisible && currentTab === 2
visible: currentTab === 2 visible: currentTab === 2
opacity: currentTab === 2 ? 1 : 0 opacity: currentTab === 2 ? 1 : 0
sourceComponent: systemTabComponent sourceComponent: systemTabComponent
@@ -342,17 +317,10 @@ DankModal {
duration: Theme.mediumDuration duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing easing.type: Theme.emphasizedEasing
} }
} }
} }
} }
} }
} }
} }
} }

View File

@@ -1,14 +1,9 @@
import QtQuick import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
import Quickshell.Io import Quickshell.Io
import Quickshell.Widgets
import qs.Common import qs.Common
import qs.Modals.Common import qs.Modals.Common
import qs.Modules.AppDrawer
import qs.Services import qs.Services
import qs.Widgets
DankModal { DankModal {
id: spotlightModal id: spotlightModal
@@ -25,73 +20,73 @@ DankModal {
property bool openedFromOverview: false property bool openedFromOverview: false
function show() { function show() {
openedFromOverview = false openedFromOverview = false;
spotlightOpen = true spotlightOpen = true;
open() open();
Qt.callLater(() => { Qt.callLater(() => {
if (spotlightContent && spotlightContent.searchField) { if (spotlightContent && spotlightContent.searchField) {
spotlightContent.searchField.forceActiveFocus() 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(() => { Qt.callLater(() => {
if (spotlightContent && spotlightContent.searchField) { if (spotlightContent && spotlightContent.searchField) {
spotlightContent.searchField.forceActiveFocus() spotlightContent.searchField.forceActiveFocus();
} }
}) });
} }
function hide() { function hide() {
openedFromOverview = false openedFromOverview = false;
spotlightOpen = false spotlightOpen = false;
close() close();
} }
onDialogClosed: { onDialogClosed: {
if (spotlightContent) { if (spotlightContent) {
if (spotlightContent.appLauncher) { if (spotlightContent.appLauncher) {
spotlightContent.appLauncher.searchQuery = "" spotlightContent.appLauncher.searchQuery = "";
spotlightContent.appLauncher.selectedIndex = 0 spotlightContent.appLauncher.selectedIndex = 0;
spotlightContent.appLauncher.setCategory(I18n.tr("All")) spotlightContent.appLauncher.setCategory(I18n.tr("All"));
} }
if (spotlightContent.fileSearchController) { if (spotlightContent.fileSearchController) {
spotlightContent.fileSearchController.reset() spotlightContent.fileSearchController.reset();
} }
if (spotlightContent.resetScroll) { if (spotlightContent.resetScroll) {
spotlightContent.resetScroll() spotlightContent.resetScroll();
} }
if (spotlightContent.searchField) { if (spotlightContent.searchField) {
spotlightContent.searchField.text = "" spotlightContent.searchField.text = "";
} }
} }
} }
function toggle() { function toggle() {
if (spotlightOpen) { if (spotlightOpen) {
hide() hide();
} else { } else {
show() show();
} }
} }
shouldBeVisible: spotlightOpen shouldBeVisible: spotlightOpen
width: 500 modalWidth: 500
height: 600 modalHeight: 600
backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
cornerRadius: Theme.cornerRadius cornerRadius: Theme.cornerRadius
borderColor: Theme.outlineMedium borderColor: Theme.outlineMedium
@@ -99,25 +94,25 @@ DankModal {
enableShadow: true enableShadow: true
keepContentLoaded: true keepContentLoaded: true
onVisibleChanged: () => { onVisibleChanged: () => {
if (visible && !spotlightOpen) { if (visible && !spotlightOpen) {
show() show();
} }
if (visible && spotlightContent) { if (visible && spotlightContent) {
Qt.callLater(() => { Qt.callLater(() => {
if (spotlightContent.searchField) { if (spotlightContent.searchField) {
spotlightContent.searchField.forceActiveFocus() spotlightContent.searchField.forceActiveFocus();
} }
}) });
} }
} }
onBackgroundClicked: () => { onBackgroundClicked: () => {
return hide() return hide();
} }
Connections { Connections {
function onCloseAllModalsExcept(excludedModal) { function onCloseAllModalsExcept(excludedModal) {
if (excludedModal !== spotlightModal && !allowStacking && spotlightOpen) { if (excludedModal !== spotlightModal && !allowStacking && spotlightOpen) {
spotlightOpen = false spotlightOpen = false;
} }
} }
@@ -126,32 +121,32 @@ DankModal {
IpcHandler { IpcHandler {
function open(): string { function open(): string {
spotlightModal.show() spotlightModal.show();
return "SPOTLIGHT_OPEN_SUCCESS" return "SPOTLIGHT_OPEN_SUCCESS";
} }
function close(): string { function close(): string {
spotlightModal.hide() spotlightModal.hide();
return "SPOTLIGHT_CLOSE_SUCCESS" return "SPOTLIGHT_CLOSE_SUCCESS";
} }
function toggle(): string { function toggle(): string {
spotlightModal.toggle() spotlightModal.toggle();
return "SPOTLIGHT_TOGGLE_SUCCESS" return "SPOTLIGHT_TOGGLE_SUCCESS";
} }
function openQuery(query: string): string { function openQuery(query: string): string {
spotlightModal.showWithQuery(query) spotlightModal.showWithQuery(query);
return "SPOTLIGHT_OPEN_QUERY_SUCCESS" return "SPOTLIGHT_OPEN_QUERY_SUCCESS";
} }
function toggleQuery(query: string): string { function toggleQuery(query: string): string {
if (spotlightModal.spotlightOpen) { if (spotlightModal.spotlightOpen) {
spotlightModal.hide() spotlightModal.hide();
} else { } else {
spotlightModal.showWithQuery(query) spotlightModal.showWithQuery(query);
} }
return "SPOTLIGHT_TOGGLE_QUERY_SUCCESS" return "SPOTLIGHT_TOGGLE_QUERY_SUCCESS";
} }
target: "spotlight" target: "spotlight"

View File

@@ -1,5 +1,4 @@
import QtQuick import QtQuick
import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
import qs.Common import qs.Common
import qs.Modals.Common import qs.Modals.Common
@@ -37,122 +36,124 @@ DankModal {
property string connectionType: "" property string connectionType: ""
function show(ssid) { function show(ssid) {
wifiPasswordSSID = ssid wifiPasswordSSID = ssid;
wifiPasswordInput = "" wifiPasswordInput = "";
wifiUsernameInput = "" wifiUsernameInput = "";
wifiAnonymousIdentityInput = "" wifiAnonymousIdentityInput = "";
wifiDomainInput = "" wifiDomainInput = "";
isPromptMode = false isPromptMode = false;
promptToken = "" promptToken = "";
promptReason = "" promptReason = "";
promptFields = [] promptFields = [];
promptSetting = "" promptSetting = "";
isVpnPrompt = false isVpnPrompt = false;
connectionName = "" connectionName = "";
vpnServiceType = "" vpnServiceType = "";
connectionType = "" connectionType = "";
const network = NetworkService.wifiNetworks.find(n => n.ssid === ssid) const network = NetworkService.wifiNetworks.find(n => n.ssid === ssid);
requiresEnterprise = network?.enterprise || false requiresEnterprise = network?.enterprise || false;
open() open();
Qt.callLater(() => { Qt.callLater(() => {
if (contentLoader.item) { if (contentLoader.item) {
if (requiresEnterprise && contentLoader.item.usernameInput) { if (requiresEnterprise && contentLoader.item.usernameInput) {
contentLoader.item.usernameInput.forceActiveFocus() contentLoader.item.usernameInput.forceActiveFocus();
} else if (contentLoader.item.passwordInput) { } else if (contentLoader.item.passwordInput) {
contentLoader.item.passwordInput.forceActiveFocus() contentLoader.item.passwordInput.forceActiveFocus();
} }
} }
}) });
} }
function showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService) { function showFromPrompt(token, ssid, setting, fields, hints, reason, connType, connName, vpnService) {
isPromptMode = true isPromptMode = true;
promptToken = token promptToken = token;
promptReason = reason promptReason = reason;
promptFields = fields || [] promptFields = fields || [];
promptSetting = setting || "802-11-wireless-security" promptSetting = setting || "802-11-wireless-security";
connectionType = connType || "802-11-wireless" connectionType = connType || "802-11-wireless";
connectionName = connName || ssid || "" connectionName = connName || ssid || "";
vpnServiceType = vpnService || "" vpnServiceType = vpnService || "";
isVpnPrompt = (connectionType === "vpn" || connectionType === "wireguard") isVpnPrompt = (connectionType === "vpn" || connectionType === "wireguard");
wifiPasswordSSID = isVpnPrompt ? connectionName : ssid wifiPasswordSSID = isVpnPrompt ? connectionName : ssid;
requiresEnterprise = setting === "802-1x" requiresEnterprise = setting === "802-1x";
if (reason === "wrong-password") { if (reason === "wrong-password") {
wifiPasswordInput = "" wifiPasswordInput = "";
wifiUsernameInput = "" wifiUsernameInput = "";
} else { } else {
wifiPasswordInput = "" wifiPasswordInput = "";
wifiUsernameInput = "" wifiUsernameInput = "";
wifiAnonymousIdentityInput = "" wifiAnonymousIdentityInput = "";
wifiDomainInput = "" wifiDomainInput = "";
} }
open() open();
Qt.callLater(() => { Qt.callLater(() => {
if (contentLoader.item) { if (contentLoader.item) {
if (reason === "wrong-password" && contentLoader.item.passwordInput) { if (reason === "wrong-password" && contentLoader.item.passwordInput) {
contentLoader.item.passwordInput.text = "" contentLoader.item.passwordInput.text = "";
contentLoader.item.passwordInput.forceActiveFocus() contentLoader.item.passwordInput.forceActiveFocus();
} else if (requiresEnterprise && contentLoader.item.usernameInput) { } else if (requiresEnterprise && contentLoader.item.usernameInput) {
contentLoader.item.usernameInput.forceActiveFocus() contentLoader.item.usernameInput.forceActiveFocus();
} else if (contentLoader.item.passwordInput) { } else if (contentLoader.item.passwordInput) {
contentLoader.item.passwordInput.forceActiveFocus() contentLoader.item.passwordInput.forceActiveFocus();
} }
} }
}) });
} }
shouldBeVisible: false shouldBeVisible: false
width: 420 modalWidth: 420
height: { modalHeight: {
if (requiresEnterprise) return 430 if (requiresEnterprise)
if (isVpnPrompt) return 260 return 430;
return 230 if (isVpnPrompt)
return 260;
return 230;
} }
onShouldBeVisibleChanged: () => { onShouldBeVisibleChanged: () => {
if (!shouldBeVisible) { if (!shouldBeVisible) {
wifiPasswordInput = "" wifiPasswordInput = "";
wifiUsernameInput = "" wifiUsernameInput = "";
wifiAnonymousIdentityInput = "" wifiAnonymousIdentityInput = "";
wifiDomainInput = "" wifiDomainInput = "";
} }
} }
onOpened: { onOpened: {
Qt.callLater(() => { Qt.callLater(() => {
if (contentLoader.item) { if (contentLoader.item) {
if (requiresEnterprise && contentLoader.item.usernameInput) { if (requiresEnterprise && contentLoader.item.usernameInput) {
contentLoader.item.usernameInput.forceActiveFocus() contentLoader.item.usernameInput.forceActiveFocus();
} else if (contentLoader.item.passwordInput) { } else if (contentLoader.item.passwordInput) {
contentLoader.item.passwordInput.forceActiveFocus() contentLoader.item.passwordInput.forceActiveFocus();
} }
} }
}) });
} }
onBackgroundClicked: () => { onBackgroundClicked: () => {
if (isPromptMode) { if (isPromptMode) {
NetworkService.cancelCredentials(promptToken) NetworkService.cancelCredentials(promptToken);
} }
close() close();
wifiPasswordInput = "" wifiPasswordInput = "";
wifiUsernameInput = "" wifiUsernameInput = "";
wifiAnonymousIdentityInput = "" wifiAnonymousIdentityInput = "";
wifiDomainInput = "" wifiDomainInput = "";
} }
Connections { Connections {
target: NetworkService target: NetworkService
function onPasswordDialogShouldReopenChanged() { function onPasswordDialogShouldReopenChanged() {
if (NetworkService.passwordDialogShouldReopen && NetworkService.connectingSSID !== "") { if (NetworkService.passwordDialogShouldReopen && NetworkService.connectingSSID !== "") {
wifiPasswordSSID = NetworkService.connectingSSID wifiPasswordSSID = NetworkService.connectingSSID;
wifiPasswordInput = "" wifiPasswordInput = "";
open() open();
NetworkService.passwordDialogShouldReopen = false NetworkService.passwordDialogShouldReopen = false;
} }
} }
} }
@@ -167,16 +168,16 @@ DankModal {
anchors.fill: parent anchors.fill: parent
focus: true focus: true
Keys.onEscapePressed: event => { Keys.onEscapePressed: event => {
if (isPromptMode) { if (isPromptMode) {
NetworkService.cancelCredentials(promptToken) NetworkService.cancelCredentials(promptToken);
} }
close() close();
wifiPasswordInput = "" wifiPasswordInput = "";
wifiUsernameInput = "" wifiUsernameInput = "";
wifiAnonymousIdentityInput = "" wifiAnonymousIdentityInput = "";
wifiDomainInput = "" wifiDomainInput = "";
event.accepted = true event.accepted = true;
} }
Column { Column {
anchors.centerIn: parent anchors.centerIn: parent
@@ -193,9 +194,9 @@ DankModal {
StyledText { StyledText {
text: { text: {
if (isVpnPrompt) { if (isVpnPrompt) {
return I18n.tr("Connect to VPN") return I18n.tr("Connect to VPN");
} }
return I18n.tr("Connect to Wi-Fi") return I18n.tr("Connect to Wi-Fi");
} }
font.pixelSize: Theme.fontSizeLarge font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText color: Theme.surfaceText
@@ -209,10 +210,10 @@ DankModal {
StyledText { StyledText {
text: { text: {
if (isVpnPrompt) { if (isVpnPrompt) {
return I18n.tr("Enter password for ") + wifiPasswordSSID return I18n.tr("Enter password for ") + wifiPasswordSSID;
} }
const prefix = requiresEnterprise ? I18n.tr("Enter credentials for ") : I18n.tr("Enter password for ") const prefix = requiresEnterprise ? I18n.tr("Enter credentials for ") : I18n.tr("Enter password for ");
return prefix + wifiPasswordSSID return prefix + wifiPasswordSSID;
} }
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceTextMedium color: Theme.surfaceTextMedium
@@ -235,15 +236,15 @@ DankModal {
iconSize: Theme.iconSize - 4 iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
onClicked: () => { onClicked: () => {
if (isPromptMode) { if (isPromptMode) {
NetworkService.cancelCredentials(promptToken) NetworkService.cancelCredentials(promptToken);
} }
close() close();
wifiPasswordInput = "" wifiPasswordInput = "";
wifiUsernameInput = "" wifiUsernameInput = "";
wifiAnonymousIdentityInput = "" wifiAnonymousIdentityInput = "";
wifiDomainInput = "" wifiDomainInput = "";
} }
} }
} }
@@ -259,8 +260,8 @@ DankModal {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: () => { onClicked: () => {
usernameInput.forceActiveFocus() usernameInput.forceActiveFocus();
} }
} }
DankTextField { DankTextField {
@@ -274,13 +275,13 @@ DankModal {
backgroundColor: "transparent" backgroundColor: "transparent"
enabled: root.shouldBeVisible enabled: root.shouldBeVisible
onTextEdited: () => { onTextEdited: () => {
wifiUsernameInput = text wifiUsernameInput = text;
} }
onAccepted: () => { onAccepted: () => {
if (passwordInput) { if (passwordInput) {
passwordInput.forceActiveFocus() passwordInput.forceActiveFocus();
} }
} }
} }
} }
@@ -295,8 +296,8 @@ DankModal {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: () => { onClicked: () => {
passwordInput.forceActiveFocus() passwordInput.forceActiveFocus();
} }
} }
DankTextField { DankTextField {
@@ -312,43 +313,42 @@ DankModal {
focus: !requiresEnterprise focus: !requiresEnterprise
enabled: root.shouldBeVisible enabled: root.shouldBeVisible
onTextEdited: () => { onTextEdited: () => {
wifiPasswordInput = text wifiPasswordInput = text;
} }
onAccepted: () => { onAccepted: () => {
if (isPromptMode) { if (isPromptMode) {
const secrets = {} const secrets = {};
if (isVpnPrompt) { if (isVpnPrompt) {
if (passwordInput.text) secrets["password"] = passwordInput.text if (passwordInput.text)
} else if (promptSetting === "802-11-wireless-security") { secrets["password"] = passwordInput.text;
secrets["psk"] = passwordInput.text } else if (promptSetting === "802-11-wireless-security") {
} else if (promptSetting === "802-1x") { secrets["psk"] = passwordInput.text;
if (usernameInput.text) secrets["identity"] = usernameInput.text } else if (promptSetting === "802-1x") {
if (passwordInput.text) secrets["password"] = passwordInput.text if (usernameInput.text)
if (wifiAnonymousIdentityInput) secrets["anonymous-identity"] = wifiAnonymousIdentityInput secrets["identity"] = usernameInput.text;
} if (passwordInput.text)
NetworkService.submitCredentials(promptToken, secrets, savePasswordCheckbox.checked) secrets["password"] = passwordInput.text;
} else { if (wifiAnonymousIdentityInput)
const username = requiresEnterprise ? usernameInput.text : "" secrets["anonymous-identity"] = wifiAnonymousIdentityInput;
NetworkService.connectToWifi( }
wifiPasswordSSID, NetworkService.submitCredentials(promptToken, secrets, savePasswordCheckbox.checked);
passwordInput.text, } else {
username, const username = requiresEnterprise ? usernameInput.text : "";
wifiAnonymousIdentityInput, NetworkService.connectToWifi(wifiPasswordSSID, passwordInput.text, username, wifiAnonymousIdentityInput, wifiDomainInput);
wifiDomainInput }
) close();
} wifiPasswordInput = "";
close() wifiUsernameInput = "";
wifiPasswordInput = "" wifiAnonymousIdentityInput = "";
wifiUsernameInput = "" wifiDomainInput = "";
wifiAnonymousIdentityInput = "" passwordInput.text = "";
wifiDomainInput = "" if (requiresEnterprise)
passwordInput.text = "" usernameInput.text = "";
if (requiresEnterprise) usernameInput.text = "" }
}
Component.onCompleted: () => { Component.onCompleted: () => {
if (root.shouldBeVisible && !requiresEnterprise) if (root.shouldBeVisible && !requiresEnterprise)
focusDelayTimer.start() focusDelayTimer.start();
} }
Timer { Timer {
id: focusDelayTimer id: focusDelayTimer
@@ -356,14 +356,14 @@ DankModal {
interval: 100 interval: 100
repeat: false repeat: false
onTriggered: () => { onTriggered: () => {
if (root.shouldBeVisible) { if (root.shouldBeVisible) {
if (requiresEnterprise && usernameInput) { if (requiresEnterprise && usernameInput) {
usernameInput.forceActiveFocus() usernameInput.forceActiveFocus();
} else { } else {
passwordInput.forceActiveFocus() passwordInput.forceActiveFocus();
} }
} }
} }
} }
Connections { Connections {
@@ -371,7 +371,7 @@ DankModal {
function onShouldBeVisibleChanged() { function onShouldBeVisibleChanged() {
if (root.shouldBeVisible) if (root.shouldBeVisible)
focusDelayTimer.start() focusDelayTimer.start();
} }
} }
} }
@@ -389,8 +389,8 @@ DankModal {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: () => { onClicked: () => {
anonInput.forceActiveFocus() anonInput.forceActiveFocus();
} }
} }
DankTextField { DankTextField {
@@ -404,8 +404,8 @@ DankModal {
backgroundColor: "transparent" backgroundColor: "transparent"
enabled: root.shouldBeVisible enabled: root.shouldBeVisible
onTextEdited: () => { onTextEdited: () => {
wifiAnonymousIdentityInput = text wifiAnonymousIdentityInput = text;
} }
} }
} }
@@ -421,8 +421,8 @@ DankModal {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: () => { onClicked: () => {
domainMatchInput.forceActiveFocus() domainMatchInput.forceActiveFocus();
} }
} }
DankTextField { DankTextField {
@@ -436,8 +436,8 @@ DankModal {
backgroundColor: "transparent" backgroundColor: "transparent"
enabled: root.shouldBeVisible enabled: root.shouldBeVisible
onTextEdited: () => { onTextEdited: () => {
wifiDomainInput = text wifiDomainInput = text;
} }
} }
} }
@@ -473,8 +473,8 @@ DankModal {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: () => { onClicked: () => {
showPasswordCheckbox.checked = !showPasswordCheckbox.checked showPasswordCheckbox.checked = !showPasswordCheckbox.checked;
} }
} }
} }
@@ -515,8 +515,8 @@ DankModal {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: () => { onClicked: () => {
savePasswordCheckbox.checked = !savePasswordCheckbox.checked savePasswordCheckbox.checked = !savePasswordCheckbox.checked;
} }
} }
} }
@@ -563,15 +563,15 @@ DankModal {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: () => { onClicked: () => {
if (isPromptMode) { if (isPromptMode) {
NetworkService.cancelCredentials(promptToken) NetworkService.cancelCredentials(promptToken);
} }
close() close();
wifiPasswordInput = "" wifiPasswordInput = "";
wifiUsernameInput = "" wifiUsernameInput = "";
wifiAnonymousIdentityInput = "" wifiAnonymousIdentityInput = "";
wifiDomainInput = "" wifiDomainInput = "";
} }
} }
} }
@@ -582,9 +582,9 @@ DankModal {
color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
enabled: { enabled: {
if (isVpnPrompt) { if (isVpnPrompt) {
return passwordInput.text.length > 0 return passwordInput.text.length > 0;
} }
return requiresEnterprise ? (usernameInput.text.length > 0 && passwordInput.text.length > 0) : passwordInput.text.length > 0 return requiresEnterprise ? (usernameInput.text.length > 0 && passwordInput.text.length > 0) : passwordInput.text.length > 0;
} }
opacity: enabled ? 1 : 0.5 opacity: enabled ? 1 : 0.5
@@ -606,36 +606,35 @@ DankModal {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
enabled: parent.enabled enabled: parent.enabled
onClicked: () => { onClicked: () => {
if (isPromptMode) { if (isPromptMode) {
const secrets = {} const secrets = {};
if (isVpnPrompt) { if (isVpnPrompt) {
if (passwordInput.text) secrets["password"] = passwordInput.text if (passwordInput.text)
} else if (promptSetting === "802-11-wireless-security") { secrets["password"] = passwordInput.text;
secrets["psk"] = passwordInput.text } else if (promptSetting === "802-11-wireless-security") {
} else if (promptSetting === "802-1x") { secrets["psk"] = passwordInput.text;
if (usernameInput.text) secrets["identity"] = usernameInput.text } else if (promptSetting === "802-1x") {
if (passwordInput.text) secrets["password"] = passwordInput.text if (usernameInput.text)
if (wifiAnonymousIdentityInput) secrets["anonymous-identity"] = wifiAnonymousIdentityInput secrets["identity"] = usernameInput.text;
} if (passwordInput.text)
NetworkService.submitCredentials(promptToken, secrets, savePasswordCheckbox.checked) secrets["password"] = passwordInput.text;
} else { if (wifiAnonymousIdentityInput)
const username = requiresEnterprise ? usernameInput.text : "" secrets["anonymous-identity"] = wifiAnonymousIdentityInput;
NetworkService.connectToWifi( }
wifiPasswordSSID, NetworkService.submitCredentials(promptToken, secrets, savePasswordCheckbox.checked);
passwordInput.text, } else {
username, const username = requiresEnterprise ? usernameInput.text : "";
wifiAnonymousIdentityInput, NetworkService.connectToWifi(wifiPasswordSSID, passwordInput.text, username, wifiAnonymousIdentityInput, wifiDomainInput);
wifiDomainInput }
) close();
} wifiPasswordInput = "";
close() wifiUsernameInput = "";
wifiPasswordInput = "" wifiAnonymousIdentityInput = "";
wifiUsernameInput = "" wifiDomainInput = "";
wifiAnonymousIdentityInput = "" passwordInput.text = "";
wifiDomainInput = "" if (requiresEnterprise)
passwordInput.text = "" usernameInput.text = "";
if (requiresEnterprise) usernameInput.text = "" }
}
} }
Behavior on color { Behavior on color {

View File

@@ -154,9 +154,8 @@ DankPopout {
if (powerMenuModalLoader) { if (powerMenuModalLoader) {
powerMenuModalLoader.active = true; powerMenuModalLoader.active = true;
if (powerMenuModalLoader.item) { if (powerMenuModalLoader.item) {
const popoutPos = controlContent.mapToItem(null, 0, 0); const bounds = Qt.rect(root.alignedX, root.alignedY, root.popupWidth, root.popupHeight);
const bounds = Qt.rect(popoutPos.x, popoutPos.y, controlContent.width, controlContent.height); powerMenuModalLoader.item.openFromControlCenter(bounds, root.screen);
powerMenuModalLoader.item.openFromControlCenter(bounds, root.triggerScreen);
} }
} }
} }