1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-28 15:32:50 -05:00

refactor all modals and popouts so they retain animations on exit

This commit is contained in:
bbedward
2025-08-19 15:44:43 -04:00
parent 5fba96f345
commit 2a28f99831
36 changed files with 1499 additions and 1452 deletions

14
Common/ModalManager.qml Normal file
View File

@@ -0,0 +1,14 @@
import QtQuick
pragma Singleton
QtObject {
id: modalManager
signal closeAllModalsExcept(var excludedModal)
function openModal(modal) {
if (!modal.allowStacking) {
closeAllModalsExcept(modal)
}
}
}

View File

@@ -38,7 +38,6 @@ DankModal {
} }
} }
clipboardHistoryModal.totalCount = filteredClipboardModel.count; clipboardHistoryModal.totalCount = filteredClipboardModel.count;
// Clamp selectedIndex to valid range // Clamp selectedIndex to valid range
if (filteredClipboardModel.count === 0) { if (filteredClipboardModel.count === 0) {
keyboardNavigationActive = false; keyboardNavigationActive = false;
@@ -46,34 +45,35 @@ DankModal {
} else if (selectedIndex >= filteredClipboardModel.count) { } else if (selectedIndex >= filteredClipboardModel.count) {
selectedIndex = filteredClipboardModel.count - 1; selectedIndex = filteredClipboardModel.count - 1;
} }
} }
function toggle() { function toggle() {
if (visible) if (shouldBeVisible)
hide(); hide();
else else
show(); show();
} }
function show() { function show() {
clipboardHistoryModal.visible = true; open();
clipboardHistoryModal.searchText = ""; clipboardHistoryModal.searchText = "";
if (typeof searchField !== 'undefined' && searchField) {
searchField.text = "";
}
initializeThumbnailSystem(); initializeThumbnailSystem();
refreshClipboard(); refreshClipboard();
keyboardController.reset(); keyboardController.reset();
Qt.callLater(function() {
if (contentLoader.item && contentLoader.item.searchField) {
contentLoader.item.searchField.text = "";
contentLoader.item.searchField.forceActiveFocus();
}
});
} }
function hide() { function hide() {
clipboardHistoryModal.visible = false; close();
clipboardHistoryModal.searchText = ""; clipboardHistoryModal.searchText = "";
if (typeof searchField !== 'undefined' && searchField) {
searchField.text = "";
}
updateFilteredModel(); updateFilteredModel();
keyboardController.reset(); keyboardController.reset();
cleanupTempFiles(); cleanupTempFiles();
@@ -97,7 +97,7 @@ DankModal {
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("Copied to clipboard"); ToastService.showInfo("Copied to clipboard");
clipboardHistoryModal.hide(); hide();
} }
function deleteEntry(entry) { function deleteEntry(entry) {
@@ -143,7 +143,6 @@ DankModal {
visible: false visible: false
width: 650 width: 650
height: 550 height: 550
keyboardFocus: "ondemand"
backgroundColor: Theme.popupBackground() backgroundColor: Theme.popupBackground()
cornerRadius: Theme.cornerRadius cornerRadius: Theme.cornerRadius
borderColor: Theme.outlineMedium borderColor: Theme.outlineMedium
@@ -201,7 +200,6 @@ DankModal {
deleteEntry(selectedEntry); deleteEntry(selectedEntry);
} }
function handleKey(event) { function handleKey(event) {
if (event.key === Qt.Key_Escape) { if (event.key === Qt.Key_Escape) {
if (keyboardNavigationActive) { if (keyboardNavigationActive) {
@@ -271,8 +269,7 @@ DankModal {
visible: showClearConfirmation visible: showClearConfirmation
width: 350 width: 350
height: 150 height: 150
keyboardFocus: "ondemand" onBackgroundClicked: {
onBackgroundClicked: {
showClearConfirmation = false; showClearConfirmation = false;
} }
@@ -406,6 +403,7 @@ DankModal {
id: deleteProcess id: deleteProcess
property string deletedEntry: "" property string deletedEntry: ""
running: false running: false
onExited: (exitCode) => { onExited: (exitCode) => {
if (exitCode === 0) { if (exitCode === 0) {
@@ -423,7 +421,6 @@ DankModal {
} }
} }
clipboardHistoryModal.totalCount = filteredClipboardModel.count; clipboardHistoryModal.totalCount = filteredClipboardModel.count;
// Clamp selectedIndex to valid range // Clamp selectedIndex to valid range
if (filteredClipboardModel.count === 0) { if (filteredClipboardModel.count === 0) {
keyboardNavigationActive = false; keyboardNavigationActive = false;
@@ -459,7 +456,7 @@ DankModal {
} }
function close() { function close() {
clipboardHistoryModal.hide(); hide();
return "CLIPBOARD_CLOSE_SUCCESS"; return "CLIPBOARD_CLOSE_SUCCESS";
} }
@@ -473,6 +470,9 @@ DankModal {
clipboardContent: Component { clipboardContent: Component {
Item { Item {
id: clipboardContent
property alias searchField: searchField
anchors.fill: parent anchors.fill: parent
Column { Column {
@@ -548,7 +548,7 @@ DankModal {
id: searchField id: searchField
width: parent.width width: parent.width
placeholderText: "Search clipboard history..." placeholderText: ""
leftIconName: "search" leftIconName: "search"
showClearButton: true showClearButton: true
focus: true focus: true
@@ -559,7 +559,7 @@ DankModal {
updateFilteredModel(); updateFilteredModel();
} }
Keys.onEscapePressed: function(event) { Keys.onEscapePressed: function(event) {
clipboardHistoryModal.hide(); hide();
event.accepted = true; event.accepted = true;
} }
Component.onCompleted: { Component.onCompleted: {
@@ -567,6 +567,15 @@ DankModal {
forceActiveFocus(); forceActiveFocus();
}); });
} }
Connections {
target: clipboardHistoryModal
function onOpened() {
Qt.callLater(function() {
searchField.forceActiveFocus();
});
}
}
} }
Rectangle { Rectangle {
@@ -581,6 +590,19 @@ DankModal {
DankListView { DankListView {
id: clipboardListView id: clipboardListView
function ensureVisible(index) {
if (index < 0 || index >= count)
return ;
var itemHeight = 72 + spacing;
var itemY = index * itemHeight;
var itemBottom = itemY + itemHeight;
if (itemY < contentY)
contentY = itemY;
else if (itemBottom > contentY + height)
contentY = itemBottom - height;
}
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingS anchors.margins: Theme.spacingS
clip: true clip: true
@@ -594,27 +616,11 @@ DankModal {
boundsMovement: Flickable.FollowBoundsBehavior boundsMovement: Flickable.FollowBoundsBehavior
pressDelay: 0 pressDelay: 0
flickableDirection: Flickable.VerticalFlick flickableDirection: Flickable.VerticalFlick
function ensureVisible(index) {
if (index < 0 || index >= count)
return;
var itemHeight = 72 + spacing;
var itemY = index * itemHeight;
var itemBottom = itemY + itemHeight;
if (itemY < contentY)
contentY = itemY;
else if (itemBottom > contentY + height)
contentY = itemBottom - height;
}
onCurrentIndexChanged: { onCurrentIndexChanged: {
if (keyboardNavigationActive && currentIndex >= 0) { if (keyboardNavigationActive && currentIndex >= 0)
ensureVisible(currentIndex); ensureVisible(currentIndex);
}
} }
StyledText { StyledText {
text: "No clipboard entries found" text: "No clipboard entries found"

View File

@@ -17,7 +17,6 @@ PanelWindow {
property real backgroundOpacity: 0.5 property real backgroundOpacity: 0.5
property string positioning: "center" property string positioning: "center"
property point customPosition: Qt.point(0, 0) property point customPosition: Qt.point(0, 0)
property string keyboardFocus: "ondemand"
property bool closeOnEscapeKey: true property bool closeOnEscapeKey: true
property bool closeOnBackgroundClick: true property bool closeOnBackgroundClick: true
property string animationType: "scale" property string animationType: "scale"
@@ -30,36 +29,50 @@ PanelWindow {
property bool enableShadow: false property bool enableShadow: false
// Expose the focusScope for external access // Expose the focusScope for external access
property alias modalFocusScope: focusScope property alias modalFocusScope: focusScope
property bool shouldBeVisible: false
property bool shouldHaveFocus: shouldBeVisible
property bool allowFocusOverride: false
property bool allowStacking: false
signal opened() signal opened()
signal dialogClosed() signal dialogClosed()
signal backgroundClicked() signal backgroundClicked()
Connections {
target: ModalManager
function onCloseAllModalsExcept(excludedModal) {
if (excludedModal !== root && !allowStacking && shouldBeVisible) {
close()
}
}
}
function open() { function open() {
ModalManager.openModal(root)
closeTimer.stop();
shouldBeVisible = true;
visible = true; visible = true;
focusScope.forceActiveFocus();
} }
function close() { function close() {
visible = false; shouldBeVisible = false;
closeTimer.restart();
} }
function toggle() { function toggle() {
visible = !visible; if (shouldBeVisible)
close();
else
open();
} }
visible: shouldBeVisible
color: "transparent" color: "transparent"
WlrLayershell.layer: WlrLayershell.Overlay WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1 WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: { WlrLayershell.keyboardFocus: shouldHaveFocus ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
switch (root.keyboardFocus) {
case "exclusive":
return WlrKeyboardFocus.Exclusive;
case "none":
return WlrKeyboardFocus.None;
default:
return WlrKeyboardFocus.OnDemand;
}
}
onVisibleChanged: { onVisibleChanged: {
if (root.visible) { if (root.visible) {
opened(); opened();
@@ -72,6 +85,17 @@ PanelWindow {
} }
} }
Timer {
id: closeTimer
interval: animationDuration + 50
onTriggered: {
visible = false;
}
}
anchors { anchors {
top: true top: true
left: true left: true
@@ -84,7 +108,7 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
color: "black" color: "black"
opacity: root.showBackground ? (root.visible ? root.backgroundOpacity : 0) : 0 opacity: root.showBackground ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
visible: root.showBackground visible: root.showBackground
MouseArea { MouseArea {
@@ -133,10 +157,10 @@ PanelWindow {
border.color: root.borderColor border.color: root.borderColor
border.width: root.borderWidth border.width: root.borderWidth
layer.enabled: root.enableShadow layer.enabled: root.enableShadow
opacity: root.visible ? 1 : 0 opacity: root.shouldBeVisible ? 1 : 0
scale: { scale: {
if (root.animationType === "scale") if (root.animationType === "scale")
return root.visible ? 1 : 0.9; return root.shouldBeVisible ? 1 : 0.9;
return 1; return 1;
} }
@@ -145,8 +169,8 @@ PanelWindow {
Translate { Translate {
id: slideTransform id: slideTransform
x: root.visible ? 0 : 15 x: root.shouldBeVisible ? 0 : 15
y: root.visible ? 0 : -30 y: root.shouldBeVisible ? 0 : -30
} }
Loader { Loader {
@@ -194,17 +218,29 @@ PanelWindow {
visible: root.visible // Only active when the modal is visible visible: root.visible // Only active when the modal is visible
focus: root.visible focus: root.visible
Keys.onEscapePressed: (event) => { Keys.onEscapePressed: (event) => {
if (root.closeOnEscapeKey) { console.log("DankModal escape pressed - shouldHaveFocus:", shouldHaveFocus, "closeOnEscapeKey:", root.closeOnEscapeKey, "objectName:", root.objectName || "unnamed");
root.visible = false; if (root.closeOnEscapeKey && shouldHaveFocus) {
console.log("DankModal handling escape");
root.close();
event.accepted = true; event.accepted = true;
} }
} }
onVisibleChanged: { onVisibleChanged: {
if (visible) if (visible && shouldHaveFocus)
Qt.callLater(function() { Qt.callLater(function() {
focusScope.forceActiveFocus(); focusScope.forceActiveFocus();
}); });
}
Connections {
target: root
function onShouldHaveFocusChanged() {
if (shouldHaveFocus && visible) {
Qt.callLater(function() {
focusScope.forceActiveFocus();
});
}
}
} }
} }

View File

@@ -10,6 +10,8 @@ import qs.Widgets
DankModal { DankModal {
id: fileBrowserModal id: fileBrowserModal
objectName: "fileBrowserModal"
allowStacking: true
signal fileSelected(string path) signal fileSelected(string path)
@@ -67,11 +69,10 @@ DankModal {
width: 800 width: 800
height: 600 height: 600
keyboardFocus: "ondemand"
enableShadow: true enableShadow: true
visible: false visible: false
onBackgroundClicked: visible = false onBackgroundClicked: close()
onVisibleChanged: { onVisibleChanged: {
if (visible) { if (visible) {
@@ -145,7 +146,7 @@ DankModal {
iconSize: Theme.iconSize - 4 iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
hoverColor: Theme.errorHover hoverColor: Theme.errorHover
onClicked: fileBrowserModal.visible = false onClicked: fileBrowserModal.close()
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }

View File

@@ -19,11 +19,13 @@ DankModal {
networkSSID = ssid networkSSID = ssid
networkData = data networkData = data
networkInfoModalVisible = true networkInfoModalVisible = true
open()
NetworkService.fetchNetworkInfo(ssid) NetworkService.fetchNetworkInfo(ssid)
} }
function hideDialog() { function hideDialog() {
networkInfoModalVisible = false networkInfoModalVisible = false
close()
networkSSID = "" networkSSID = ""
networkData = null networkData = null
networkDetails = "" networkDetails = ""

View File

@@ -15,7 +15,6 @@ DankModal {
width: 500 width: 500
height: 700 height: 700
visible: false visible: false
keyboardFocus: "ondemand"
onBackgroundClicked: hide() onBackgroundClicked: hide()
onDialogClosed: { onDialogClosed: {
notificationModalOpen = false notificationModalOpen = false
@@ -40,7 +39,7 @@ DankModal {
function show() { function show() {
notificationModalOpen = true notificationModalOpen = true
visible = true open()
modalKeyboardController.reset() modalKeyboardController.reset()
if (modalKeyboardController && notificationListRef) { if (modalKeyboardController && notificationListRef) {
@@ -51,12 +50,12 @@ DankModal {
function hide() { function hide() {
notificationModalOpen = false notificationModalOpen = false
visible = false close()
modalKeyboardController.reset() modalKeyboardController.reset()
} }
function toggle() { function toggle() {
if (notificationModalOpen) if (shouldBeVisible)
hide() hide()
else else
show() show()

View File

@@ -9,11 +9,17 @@ import qs.Widgets
DankModal { DankModal {
id: root id: root
property bool powerConfirmVisible: false
property string powerConfirmAction: "" property string powerConfirmAction: ""
property string powerConfirmTitle: "" property string powerConfirmTitle: ""
property string powerConfirmMessage: "" property string powerConfirmMessage: ""
function show(action, title, message) {
powerConfirmAction = action
powerConfirmTitle = title
powerConfirmMessage = message
open()
}
function executePowerAction(action) { function executePowerAction(action) {
switch (action) { switch (action) {
case "logout": case "logout":
@@ -31,13 +37,12 @@ DankModal {
} }
} }
visible: powerConfirmVisible shouldBeVisible: false
width: 350 width: 350
height: 160 height: 160
keyboardFocus: "ondemand"
enableShadow: false enableShadow: false
onBackgroundClicked: { onBackgroundClicked: {
powerConfirmVisible = false close()
} }
content: Component { content: Component {
@@ -105,7 +110,7 @@ DankModal {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
powerConfirmVisible = false close()
} }
} }
} }
@@ -148,7 +153,7 @@ DankModal {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
powerConfirmVisible = false close()
executePowerAction(powerConfirmAction) executePowerAction(powerConfirmAction)
} }
} }

View File

@@ -12,324 +12,353 @@ import qs.Services
import qs.Widgets import qs.Widgets
DankModal { DankModal {
id: processListModal id: processListModal
property int currentTab: 0 property int currentTab: 0
property var tabNames: ["Processes", "Performance", "System"] property var tabNames: ["Processes", "Performance", "System"]
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 ;
}
processListModal.visible = true
UserInfoService.getUptime()
}
function hide() {
processListModal.visible = false
if (processContextMenu.visible)
processContextMenu.close()
}
function toggle() {
if (!DgopService.dgopAvailable) {
console.warn("ProcessListModal: dgop is not available")
return
}
if (processListModal.visible)
hide()
else
show()
}
width: 900
height: 680
visible: false
keyboardFocus: "exclusive"
backgroundColor: Theme.popupBackground()
cornerRadius: Theme.cornerRadius
enableShadow: true
onBackgroundClicked: hide()
Component {
id: processesTabComponent
ProcessesTab {
contextMenu: processContextMenu
}
}
Component {
id: performanceTabComponent
PerformanceTab {}
}
Component {
id: systemTabComponent
SystemTab {}
}
ProcessContextMenu {
id: processContextMenu
}
content: Component {
Item {
anchors.fill: parent
focus: true
Keys.onPressed: function (event) {
if (event.key === Qt.Key_Escape) {
processListModal.hide()
event.accepted = true
} else if (event.key === Qt.Key_1) {
currentTab = 0
event.accepted = true
} else if (event.key === Qt.Key_2) {
currentTab = 1
event.accepted = true
} else if (event.key === Qt.Key_3) {
currentTab = 2
event.accepted = true
} }
} open();
UserInfoService.getUptime();
}
// Show error message when dgop is not available function hide() {
Rectangle { close();
anchors.centerIn: parent if (processContextMenu.visible)
width: 400 processContextMenu.close();
height: 200
radius: Theme.cornerRadius
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.1)
border.color: Theme.error
border.width: 2
visible: !DgopService.dgopAvailable
Column { }
anchors.centerIn: parent
spacing: Theme.spacingL
DankIcon { function toggle() {
name: "error" if (!DgopService.dgopAvailable) {
size: 48 console.warn("ProcessListModal: dgop is not available");
color: Theme.error return ;
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
text: "System Monitor Unavailable"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Bold
color: Theme.error
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
text: "The 'dgop' tool is required for system monitoring.\nPlease install dgop to use this feature."
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
}
} }
} if (shouldBeVisible)
hide();
else
show();
}
ColumnLayout { width: 900
anchors.fill: parent height: 680
anchors.margins: Theme.spacingL visible: false
spacing: Theme.spacingL backgroundColor: Theme.popupBackground()
visible: DgopService.dgopAvailable cornerRadius: Theme.cornerRadius
enableShadow: true
onBackgroundClicked: hide()
RowLayout { Component {
Layout.fillWidth: true id: processesTabComponent
height: 40
StyledText { ProcessesTab {
text: "System Monitor" contextMenu: processContextMenu
font.pixelSize: Theme.fontSizeLarge + 4
font.weight: Font.Bold
color: Theme.surfaceText
Layout.alignment: Qt.AlignVCenter
}
Item {
Layout.fillWidth: true
}
DankActionButton {
circular: false
iconName: "close"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
hoverColor: Theme.errorHover
onClicked: processListModal.hide()
Layout.alignment: Qt.AlignVCenter
}
} }
Rectangle { }
Layout.fillWidth: true
height: 52
color: Theme.surfaceSelected
radius: Theme.cornerRadius
border.color: Theme.outlineLight
border.width: 1
Row { Component {
id: performanceTabComponent
PerformanceTab {
}
}
Component {
id: systemTabComponent
SystemTab {
}
}
ProcessContextMenu {
id: processContextMenu
}
content: Component {
Item {
anchors.fill: parent anchors.fill: parent
anchors.margins: 4 focus: true
spacing: 2 Keys.onPressed: function(event) {
if (event.key === Qt.Key_Escape) {
processListModal.hide();
event.accepted = true;
} else if (event.key === Qt.Key_1) {
currentTab = 0;
event.accepted = true;
} else if (event.key === Qt.Key_2) {
currentTab = 1;
event.accepted = true;
} else if (event.key === Qt.Key_3) {
currentTab = 2;
event.accepted = true;
}
}
Repeater { // Show error message when dgop is not available
model: tabNames Rectangle {
anchors.centerIn: parent
Rectangle { width: 400
width: (parent.width - (tabNames.length - 1) * 2) / tabNames.length height: 200
height: 44
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent") color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.1)
border.color: currentTab === index ? Theme.primary : "transparent" border.color: Theme.error
border.width: currentTab === index ? 1 : 0 border.width: 2
visible: !DgopService.dgopAvailable
Row { Column {
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingXS spacing: Theme.spacingL
DankIcon { DankIcon {
name: { name: "error"
switch (index) { size: 48
case 0: color: Theme.error
return "list_alt" anchors.horizontalCenter: parent.horizontalCenter
case 1:
return "analytics"
case 2:
return "settings"
default:
return "tab"
}
} }
size: Theme.iconSize - 2
color: currentTab === index ? Theme.primary : Theme.surfaceText
opacity: currentTab === index ? 1 : 0.7
anchors.verticalCenter: parent.verticalCenter
Behavior on color { StyledText {
ColorAnimation { text: "System Monitor Unavailable"
duration: Theme.shortDuration font.pixelSize: Theme.fontSizeLarge
} font.weight: Font.Bold
color: Theme.error
anchors.horizontalCenter: parent.horizontalCenter
} }
}
StyledText { StyledText {
text: modelData text: "The 'dgop' tool is required for system monitoring.\nPlease install dgop to use this feature."
font.pixelSize: Theme.fontSizeLarge font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium color: Theme.surfaceText
color: currentTab === index ? Theme.primary : Theme.surfaceText anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter horizontalAlignment: Text.AlignHCenter
anchors.verticalCenterOffset: -1 wrapMode: Text.WordWrap
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
}
} }
}
} }
MouseArea {
id: tabMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
currentTab = index
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
}
}
Behavior on border.color {
ColorAnimation {
duration: Theme.shortDuration
}
}
}
} }
}
ColumnLayout {
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingL
visible: DgopService.dgopAvailable
RowLayout {
Layout.fillWidth: true
height: 40
StyledText {
text: "System Monitor"
font.pixelSize: Theme.fontSizeLarge + 4
font.weight: Font.Bold
color: Theme.surfaceText
Layout.alignment: Qt.AlignVCenter
}
Item {
Layout.fillWidth: true
}
DankActionButton {
circular: false
iconName: "close"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
hoverColor: Theme.errorHover
onClicked: processListModal.hide()
Layout.alignment: Qt.AlignVCenter
}
}
Rectangle {
Layout.fillWidth: true
height: 52
color: Theme.surfaceSelected
radius: Theme.cornerRadius
border.color: Theme.outlineLight
border.width: 1
Row {
anchors.fill: parent
anchors.margins: 4
spacing: 2
Repeater {
model: tabNames
Rectangle {
width: (parent.width - (tabNames.length - 1) * 2) / tabNames.length
height: 44
radius: Theme.cornerRadius
color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent")
border.color: currentTab === index ? Theme.primary : "transparent"
border.width: currentTab === index ? 1 : 0
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
name: {
switch (index) {
case 0:
return "list_alt";
case 1:
return "analytics";
case 2:
return "settings";
default:
return "tab";
}
}
size: Theme.iconSize - 2
color: currentTab === index ? Theme.primary : Theme.surfaceText
opacity: currentTab === index ? 1 : 0.7
anchors.verticalCenter: parent.verticalCenter
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
}
}
}
StyledText {
text: modelData
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: currentTab === index ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: -1
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
}
}
}
}
MouseArea {
id: tabMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
currentTab = index;
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
}
}
Behavior on border.color {
ColorAnimation {
duration: Theme.shortDuration
}
}
}
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
radius: Theme.cornerRadius
color: Theme.surfaceLight
border.color: Theme.outlineLight
border.width: 1
Loader {
id: processesTab
anchors.fill: parent
anchors.margins: Theme.spacingS
active: processListModal.visible && currentTab === 0
visible: currentTab === 0
opacity: currentTab === 0 ? 1 : 0
sourceComponent: processesTabComponent
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
Loader {
id: performanceTab
anchors.fill: parent
anchors.margins: Theme.spacingS
active: processListModal.visible && currentTab === 1
visible: currentTab === 1
opacity: currentTab === 1 ? 1 : 0
sourceComponent: performanceTabComponent
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
Loader {
id: systemTab
anchors.fill: parent
anchors.margins: Theme.spacingS
active: processListModal.visible && currentTab === 2
visible: currentTab === 2
opacity: currentTab === 2 ? 1 : 0
sourceComponent: systemTabComponent
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
}
}
} }
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
radius: Theme.cornerRadius
color: Theme.surfaceLight
border.color: Theme.outlineLight
border.width: 1
Loader {
id: processesTab
anchors.fill: parent
anchors.margins: Theme.spacingS
active: processListModal.visible && currentTab === 0
visible: currentTab === 0
opacity: currentTab === 0 ? 1 : 0
sourceComponent: processesTabComponent
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
Loader {
id: performanceTab
anchors.fill: parent
anchors.margins: Theme.spacingS
active: processListModal.visible && currentTab === 1
visible: currentTab === 1
opacity: currentTab === 1 ? 1 : 0
sourceComponent: performanceTabComponent
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
Loader {
id: systemTab
anchors.fill: parent
anchors.margins: Theme.spacingS
active: processListModal.visible && currentTab === 2
visible: currentTab === 2
opacity: currentTab === 2 ? 1 : 0
sourceComponent: systemTabComponent
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
}
}
} }
}
} }

View File

@@ -10,19 +10,20 @@ import qs.Widgets
DankModal { DankModal {
id: settingsModal id: settingsModal
objectName: "settingsModal"
signal closingModal signal closingModal
function show() { function show() {
settingsModal.visible = true open()
} }
function hide() { function hide() {
settingsModal.visible = false close()
} }
function toggle() { function toggle() {
if (settingsModal.visible) if (shouldBeVisible)
hide() hide()
else else
show() show()
@@ -31,19 +32,12 @@ DankModal {
width: 750 width: 750
height: 750 height: 750
visible: false visible: false
keyboardFocus: "ondemand"
onBackgroundClicked: hide() onBackgroundClicked: hide()
property Component settingsContent: Component { property Component settingsContent: Component {
Item { Item {
anchors.fill: parent anchors.fill: parent
focus: true focus: true
Keys.onPressed: function (event) {
if (event.key === Qt.Key_Escape) {
settingsModal.hide()
event.accepted = true
}
}
Column { Column {
anchors.fill: parent anchors.fill: parent
@@ -128,7 +122,9 @@ DankModal {
visible: active visible: active
asynchronous: true asynchronous: true
sourceComponent: Component { sourceComponent: Component {
PersonalizationTab {} PersonalizationTab {
parentModal: settingsModal
}
} }
} }

View File

@@ -15,6 +15,92 @@ DankModal {
property bool spotlightOpen: false property bool spotlightOpen: false
property Component spotlightContent property Component spotlightContent
function show() {
spotlightOpen = true;
open();
if (contentLoader.item && contentLoader.item.appLauncher)
contentLoader.item.appLauncher.searchQuery = "";
Qt.callLater(function() {
if (contentLoader.item && contentLoader.item.searchField)
contentLoader.item.searchField.forceActiveFocus();
});
}
function hide() {
spotlightOpen = false;
close();
if (contentLoader.item && contentLoader.item.appLauncher) {
contentLoader.item.appLauncher.searchQuery = "";
contentLoader.item.appLauncher.selectedIndex = 0;
contentLoader.item.appLauncher.setCategory("All");
}
}
function toggle() {
if (spotlightOpen)
hide();
else
show();
}
shouldBeVisible: spotlightOpen
Connections {
target: ModalManager
function onCloseAllModalsExcept(excludedModal) {
if (excludedModal !== spotlightModal && !allowStacking && spotlightOpen) {
spotlightOpen = false
}
}
}
width: 550
height: 600
backgroundColor: Theme.popupBackground()
cornerRadius: Theme.cornerRadius
borderColor: Theme.outlineMedium
borderWidth: 1
enableShadow: true
onVisibleChanged: {
if (visible && !spotlightOpen)
show();
if (visible && contentLoader.item)
Qt.callLater(function() {
if (contentLoader.item.searchField)
contentLoader.item.searchField.forceActiveFocus();
});
}
onBackgroundClicked: {
hide();
}
Component.onCompleted: {
}
content: spotlightContent
IpcHandler {
function open() {
spotlightModal.show();
return "SPOTLIGHT_OPEN_SUCCESS";
}
function close() {
spotlightModal.hide();
return "SPOTLIGHT_CLOSE_SUCCESS";
}
function toggle() {
spotlightModal.toggle();
return "SPOTLIGHT_TOGGLE_SUCCESS";
}
target: "spotlight"
}
spotlightContent: Component { spotlightContent: Component {
Item { Item {
id: spotlightKeyHandler id: spotlightKeyHandler
@@ -111,7 +197,7 @@ DankModal {
textColor: Theme.surfaceText textColor: Theme.surfaceText
font.pixelSize: Theme.fontSizeLarge font.pixelSize: Theme.fontSizeLarge
enabled: spotlightOpen enabled: spotlightOpen
placeholderText: "Search applications..." placeholderText: ""
ignoreLeftRightKeys: true ignoreLeftRightKeys: true
keyForwardTargets: [spotlightKeyHandler] keyForwardTargets: [spotlightKeyHandler]
text: appLauncher.searchQuery text: appLauncher.searchQuery
@@ -132,8 +218,6 @@ DankModal {
event.accepted = false; event.accepted = false;
} }
} }
} }
Row { Row {
@@ -357,6 +441,7 @@ DankModal {
onEntered: { onEntered: {
if (resultsList.hoverUpdatesSelection && !resultsList.keyboardNavigationActive) if (resultsList.hoverUpdatesSelection && !resultsList.keyboardNavigationActive)
resultsList.currentIndex = index; resultsList.currentIndex = index;
} }
onPositionChanged: { onPositionChanged: {
resultsList.keyboardNavigationReset(); resultsList.keyboardNavigationReset();
@@ -365,8 +450,8 @@ DankModal {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
resultsList.itemClicked(index, model); resultsList.itemClicked(index, model);
} else if (mouse.button === Qt.RightButton) { } else if (mouse.button === Qt.RightButton) {
var globalPos = mapToGlobal(mouse.x, mouse.y); var modalPos = mapToItem(spotlightKeyHandler, mouse.x, mouse.y);
resultsList.itemRightClicked(index, model, globalPos.x, globalPos.y); resultsList.itemRightClicked(index, model, modalPos.x, modalPos.y);
} }
} }
} }
@@ -521,6 +606,7 @@ DankModal {
onEntered: { onEntered: {
if (resultsGrid.hoverUpdatesSelection && !resultsGrid.keyboardNavigationActive) if (resultsGrid.hoverUpdatesSelection && !resultsGrid.keyboardNavigationActive)
resultsGrid.currentIndex = index; resultsGrid.currentIndex = index;
} }
onPositionChanged: { onPositionChanged: {
resultsGrid.keyboardNavigationReset(); resultsGrid.keyboardNavigationReset();
@@ -529,8 +615,8 @@ DankModal {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
resultsGrid.itemClicked(index, model); resultsGrid.itemClicked(index, model);
} else if (mouse.button === Qt.RightButton) { } else if (mouse.button === Qt.RightButton) {
var globalPos = mapToGlobal(mouse.x, mouse.y); var modalPos = mapToItem(spotlightKeyHandler, mouse.x, mouse.y);
resultsGrid.itemRightClicked(index, model, globalPos.x, globalPos.y); resultsGrid.itemRightClicked(index, model, modalPos.x, modalPos.y);
} }
} }
} }
@@ -543,274 +629,225 @@ DankModal {
} }
} Rectangle {
id: contextMenu
} property var currentApp: null
property bool menuVisible: false
function show() {
spotlightOpen = true;
if (contentLoader.item && contentLoader.item.appLauncher)
contentLoader.item.appLauncher.searchQuery = "";
Qt.callLater(function() {
if (contentLoader.item && contentLoader.item.searchField) {
contentLoader.item.searchField.forceActiveFocus();
}
});
}
function hide() {
spotlightOpen = false;
if (contentLoader.item && contentLoader.item.appLauncher) {
contentLoader.item.appLauncher.searchQuery = "";
contentLoader.item.appLauncher.selectedIndex = 0;
contentLoader.item.appLauncher.setCategory("All");
}
}
function toggle() {
if (spotlightOpen)
hide();
else
show();
}
visible: spotlightOpen
width: 550
height: 600
keyboardFocus: "ondemand"
backgroundColor: Theme.popupBackground()
cornerRadius: Theme.cornerRadius
borderColor: Theme.outlineMedium
borderWidth: 1
enableShadow: true
onVisibleChanged: {
if (visible && !spotlightOpen)
show();
if (visible && contentLoader.item) {
Qt.callLater(function() {
if (contentLoader.item.searchField) {
contentLoader.item.searchField.forceActiveFocus();
}
});
}
}
onBackgroundClicked: {
spotlightOpen = false;
}
Component.onCompleted: {
}
content: spotlightContent
IpcHandler {
function open() {
spotlightModal.show();
return "SPOTLIGHT_OPEN_SUCCESS";
}
function close() {
spotlightModal.hide();
return "SPOTLIGHT_CLOSE_SUCCESS";
}
function toggle() {
spotlightModal.toggle();
return "SPOTLIGHT_TOGGLE_SUCCESS";
}
target: "spotlight"
}
Popup {
id: contextMenu
property var currentApp: null
function show(x, y, app) {
currentApp = app;
if (!contextMenu.parent && typeof Overlay !== "undefined" && Overlay.overlay)
contextMenu.parent = Overlay.overlay;
const menuWidth = 180;
const menuHeight = menuColumn.implicitHeight + Theme.spacingS * 2;
const screenWidth = Screen.width;
const screenHeight = Screen.height;
let finalX = x;
let finalY = y;
if (x + menuWidth > screenWidth - 20)
finalX = x - menuWidth;
if (y + menuHeight > screenHeight - 20)
finalY = y - menuHeight;
contextMenu.x = Math.max(20, finalX);
contextMenu.y = Math.max(20, finalY);
open();
}
width: 180
height: menuColumn.implicitHeight + Theme.spacingS * 2
padding: 0
modal: false
closePolicy: Popup.CloseOnEscape
onClosed: {
closePolicy = Popup.CloseOnEscape;
}
onOpened: {
outsideClickTimer.start();
}
Timer {
id: outsideClickTimer
interval: 100
onTriggered: {
contextMenu.closePolicy = Popup.CloseOnEscape | Popup.CloseOnPressOutside;
}
}
background: Rectangle {
color: "transparent"
}
contentItem: Rectangle {
color: Theme.popupBackground()
radius: Theme.cornerRadius
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
Column {
id: menuColumn
anchors.fill: parent
anchors.margins: Theme.spacingS
spacing: 1
Rectangle {
width: parent.width
height: 32
radius: Theme.cornerRadius
color: pinMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankIcon {
name: {
if (!contextMenu.currentApp || !contextMenu.currentApp.desktopEntry)
return "push_pin";
var appId = contextMenu.currentApp.desktopEntry.id || contextMenu.currentApp.desktopEntry.execString || "";
return SessionData.isPinnedApp(appId) ? "keep_off" : "push_pin";
}
size: Theme.iconSize - 2
color: Theme.surfaceText
opacity: 0.7
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: {
if (!contextMenu.currentApp || !contextMenu.currentApp.desktopEntry)
return "Pin to Dock";
var appId = contextMenu.currentApp.desktopEntry.id || contextMenu.currentApp.desktopEntry.execString || "";
return SessionData.isPinnedApp(appId) ? "Unpin from Dock" : "Pin to Dock";
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
anchors.verticalCenter: parent.verticalCenter
}
function show(x, y, app) {
currentApp = app
const menuWidth = 180
const menuHeight = menuColumn.implicitHeight + Theme.spacingS * 2
let finalX = x + 8
let finalY = y + 8
if (finalX + menuWidth > spotlightKeyHandler.width) {
finalX = x - menuWidth - 8
} }
MouseArea { if (finalY + menuHeight > spotlightKeyHandler.height) {
id: pinMouseArea finalY = y - menuHeight - 8
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (!contextMenu.currentApp || !contextMenu.currentApp.desktopEntry)
return ;
var appId = contextMenu.currentApp.desktopEntry.id || contextMenu.currentApp.desktopEntry.execString || "";
if (SessionData.isPinnedApp(appId))
SessionData.removePinnedApp(appId);
else
SessionData.addPinnedApp(appId);
contextMenu.close();
}
} }
finalX = Math.max(8, Math.min(finalX, spotlightKeyHandler.width - menuWidth - 8))
finalY = Math.max(8, Math.min(finalY, spotlightKeyHandler.height - menuHeight - 8))
contextMenu.x = finalX
contextMenu.y = finalY
contextMenu.visible = true
contextMenu.menuVisible = true
} }
function close() {
contextMenu.menuVisible = false
Qt.callLater(() => {
contextMenu.visible = false
})
}
visible: false
width: 180
height: menuColumn.implicitHeight + Theme.spacingS * 2
radius: Theme.cornerRadius
color: Theme.popupBackground()
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
z: 1000
opacity: menuVisible ? 1 : 0
scale: menuVisible ? 1 : 0.85
Rectangle { Rectangle {
width: parent.width - Theme.spacingS * 2 anchors.fill: parent
height: 5 anchors.topMargin: 4
anchors.horizontalCenter: parent.horizontalCenter anchors.leftMargin: 2
color: "transparent" anchors.rightMargin: -2
anchors.bottomMargin: -4
radius: parent.radius
color: Qt.rgba(0, 0, 0, 0.15)
z: parent.z - 1
}
Column {
id: menuColumn
anchors.fill: parent
anchors.margins: Theme.spacingS
spacing: 1
Rectangle { Rectangle {
anchors.centerIn: parent
width: parent.width width: parent.width
height: 1 height: 32
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2) radius: Theme.cornerRadius
color: pinMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankIcon {
name: {
if (!contextMenu.currentApp || !contextMenu.currentApp.desktopEntry)
return "push_pin"
var appId = contextMenu.currentApp.desktopEntry.id || contextMenu.currentApp.desktopEntry.execString || ""
return SessionData.isPinnedApp(appId) ? "keep_off" : "push_pin"
}
size: Theme.iconSize - 2
color: Theme.surfaceText
opacity: 0.7
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: {
if (!contextMenu.currentApp || !contextMenu.currentApp.desktopEntry)
return "Pin to Dock"
var appId = contextMenu.currentApp.desktopEntry.id || contextMenu.currentApp.desktopEntry.execString || ""
return SessionData.isPinnedApp(appId) ? "Unpin from Dock" : "Pin to Dock"
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: pinMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (!contextMenu.currentApp || !contextMenu.currentApp.desktopEntry)
return
var appId = contextMenu.currentApp.desktopEntry.id || contextMenu.currentApp.desktopEntry.execString || ""
if (SessionData.isPinnedApp(appId))
SessionData.removePinnedApp(appId)
else
SessionData.addPinnedApp(appId)
contextMenu.close()
}
}
} }
Rectangle {
width: parent.width - Theme.spacingS * 2
height: 5
anchors.horizontalCenter: parent.horizontalCenter
color: "transparent"
Rectangle {
anchors.centerIn: parent
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
}
}
Rectangle {
width: parent.width
height: 32
radius: Theme.cornerRadius
color: launchMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankIcon {
name: "launch"
size: Theme.iconSize - 2
color: Theme.surfaceText
opacity: 0.7
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Launch"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: launchMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (contextMenu.currentApp)
appLauncher.launchApp(contextMenu.currentApp)
contextMenu.close()
}
}
}
} }
Rectangle { Behavior on opacity {
width: parent.width NumberAnimation {
height: 32 duration: Theme.mediumDuration
radius: Theme.cornerRadius easing.type: Theme.emphasizedEasing
color: launchMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankIcon {
name: "launch"
size: Theme.iconSize - 2
color: Theme.surfaceText
opacity: 0.7
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Launch"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
anchors.verticalCenter: parent.verticalCenter
}
} }
MouseArea {
id: launchMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (contextMenu.currentApp && contentLoader.item && contentLoader.item.appLauncher)
contentLoader.item.appLauncher.launchApp(contextMenu.currentApp);
contextMenu.close();
}
}
} }
Behavior on scale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
MouseArea {
anchors.fill: parent
visible: contextMenu.visible
z: 999
onClicked: {
contextMenu.close()
}
MouseArea {
x: contextMenu.x
y: contextMenu.y
width: contextMenu.width
height: contextMenu.height
onClicked: {
// Prevent closing when clicking on the menu itself
}
}
} }
} }

View File

@@ -7,20 +7,36 @@ import qs.Widgets
DankModal { DankModal {
id: root id: root
property bool wifiPasswordModalVisible: false
property string wifiPasswordSSID: "" property string wifiPasswordSSID: ""
property string wifiPasswordInput: "" property string wifiPasswordInput: ""
visible: wifiPasswordModalVisible function show(ssid) {
wifiPasswordSSID = ssid
wifiPasswordInput = ""
open()
Qt.callLater(function() {
if (contentLoader.item && contentLoader.item.passwordInput) {
contentLoader.item.passwordInput.forceActiveFocus()
}
})
}
shouldBeVisible: false
width: 420 width: 420
height: 230 height: 230
keyboardFocus: "exclusive" onShouldBeVisibleChanged: {
onVisibleChanged: { if (!shouldBeVisible)
if (!visible)
wifiPasswordInput = "" wifiPasswordInput = ""
} }
onOpened: {
Qt.callLater(function() {
if (contentLoader.item && contentLoader.item.passwordInput) {
contentLoader.item.passwordInput.forceActiveFocus()
}
})
}
onBackgroundClicked: { onBackgroundClicked: {
wifiPasswordModalVisible = false close()
wifiPasswordInput = "" wifiPasswordInput = ""
} }
@@ -30,7 +46,7 @@ DankModal {
&& NetworkService.connectingSSID !== "") { && NetworkService.connectingSSID !== "") {
wifiPasswordSSID = NetworkService.connectingSSID wifiPasswordSSID = NetworkService.connectingSSID
wifiPasswordInput = "" wifiPasswordInput = ""
wifiPasswordModalVisible = true open()
NetworkService.passwordDialogShouldReopen = false NetworkService.passwordDialogShouldReopen = false
} }
} }
@@ -40,8 +56,16 @@ DankModal {
content: Component { content: Component {
FocusScope { FocusScope {
id: wifiContent
property alias passwordInput: passwordInput
anchors.fill: parent anchors.fill: parent
focus: true focus: true
Keys.onEscapePressed: function(event) {
close()
wifiPasswordInput = ""
event.accepted = true
}
Column { Column {
anchors.centerIn: parent anchors.centerIn: parent
@@ -77,7 +101,7 @@ DankModal {
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
hoverColor: Theme.errorHover hoverColor: Theme.errorHover
onClicked: { onClicked: {
wifiPasswordModalVisible = false close()
wifiPasswordInput = "" wifiPasswordInput = ""
} }
} }
@@ -91,6 +115,13 @@ DankModal {
border.color: passwordInput.activeFocus ? Theme.primary : Theme.outlineStrong border.color: passwordInput.activeFocus ? Theme.primary : Theme.outlineStrong
border.width: passwordInput.activeFocus ? 2 : 1 border.width: passwordInput.activeFocus ? 2 : 1
MouseArea {
anchors.fill: parent
onClicked: {
passwordInput.forceActiveFocus()
}
}
DankTextField { DankTextField {
id: passwordInput id: passwordInput
@@ -99,42 +130,45 @@ DankModal {
textColor: Theme.surfaceText textColor: Theme.surfaceText
text: wifiPasswordInput text: wifiPasswordInput
echoMode: showPasswordCheckbox.checked ? TextInput.Normal : TextInput.Password echoMode: showPasswordCheckbox.checked ? TextInput.Normal : TextInput.Password
placeholderText: "Enter password" placeholderText: ""
backgroundColor: "transparent" backgroundColor: "transparent"
focus: true focus: true
enabled: root.shouldBeVisible
onTextEdited: { onTextEdited: {
wifiPasswordInput = text wifiPasswordInput = text
} }
onAccepted: { onAccepted: {
NetworkService.connectToWifiWithPassword(wifiPasswordSSID, NetworkService.connectToWifiWithPassword(wifiPasswordSSID,
passwordInput.text) passwordInput.text)
wifiPasswordModalVisible = false close()
wifiPasswordInput = "" wifiPasswordInput = ""
passwordInput.text = "" passwordInput.text = ""
} }
Timer { Component.onCompleted: {
id: focusTimer if (root.shouldBeVisible) {
interval: 50 focusDelayTimer.start()
onTriggered: passwordInput.forceActiveFocus() }
} }
Component.onCompleted: { Timer {
focusTimer.start() id: focusDelayTimer
interval: 100
repeat: false
onTriggered: {
if (root.shouldBeVisible) {
passwordInput.forceActiveFocus()
}
}
} }
Connections { Connections {
function onOpened() { target: root
focusTimer.start() function onShouldBeVisibleChanged() {
} if (root.shouldBeVisible) {
focusDelayTimer.start()
function onVisibleChanged() {
if (root.visible) {
focusTimer.start()
} }
} }
target: root
} }
} }
} }
@@ -214,7 +248,7 @@ DankModal {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
wifiPasswordModalVisible = false close()
wifiPasswordInput = "" wifiPasswordInput = ""
} }
} }
@@ -249,7 +283,7 @@ DankModal {
onClicked: { onClicked: {
NetworkService.connectToWifiWithPassword(wifiPasswordSSID, NetworkService.connectToWifiWithPassword(wifiPasswordSSID,
passwordInput.text) passwordInput.text)
wifiPasswordModalVisible = false close()
wifiPasswordInput = "" wifiPasswordInput = ""
passwordInput.text = "" passwordInput.text = ""
} }

View File

@@ -10,18 +10,14 @@ import qs.Modules.AppDrawer
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
PanelWindow { DankPopout {
id: appDrawerPopout id: appDrawerPopout
property bool isVisible: false
property real triggerX: Theme.spacingL
property real triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
property real triggerWidth: 40
property string triggerSection: "left" property string triggerSection: "left"
property var triggerScreen: null property var triggerScreen: null
function show() { function show() {
appDrawerPopout.isVisible = true open()
appLauncher.searchQuery = "" appLauncher.searchQuery = ""
appLauncher.selectedIndex = 0 appLauncher.selectedIndex = 0
appLauncher.setCategory("All") appLauncher.setCategory("All")
@@ -29,14 +25,7 @@ PanelWindow {
} }
function hide() { function hide() {
appDrawerPopout.isVisible = false close()
}
function toggle() {
if (appDrawerPopout.isVisible)
hide()
else
show()
} }
function setTriggerPosition(x, y, width, section, screen) { function setTriggerPosition(x, y, width, section, screen) {
@@ -47,21 +36,15 @@ PanelWindow {
triggerScreen = screen triggerScreen = screen
} }
WlrLayershell.layer: WlrLayershell.Overlay popupWidth: 520
WlrLayershell.exclusiveZone: -1 popupHeight: 600
WlrLayershell.keyboardFocus: isVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None triggerX: Theme.spacingL
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
triggerWidth: 40
positioning: "center"
WlrLayershell.namespace: "quickshell-launcher" WlrLayershell.namespace: "quickshell-launcher"
visible: isVisible
color: "transparent"
screen: triggerScreen screen: triggerScreen
anchors {
top: true
left: true
right: true
bottom: true
}
AppLauncher { AppLauncher {
id: appLauncher id: appLauncher
@@ -73,67 +56,8 @@ PanelWindow {
} }
} }
MouseArea { content: Component {
anchors.fill: parent Rectangle {
enabled: appDrawerPopout.isVisible
onClicked: function (mouse) {
var localPos = mapToItem(launcherLoader, mouse.x, mouse.y)
if (localPos.x < 0 || localPos.x > launcherLoader.width || localPos.y < 0
|| localPos.y > launcherLoader.height)
appDrawerPopout.hide()
}
}
Loader {
id: launcherLoader
readonly property real popupWidth: 520
readonly property real popupHeight: 600
readonly property real screenWidth: appDrawerPopout.screen ? appDrawerPopout.screen.width : Screen.width
readonly property real screenHeight: appDrawerPopout.screen ? appDrawerPopout.screen.height : Screen.height
readonly property real calculatedX: {
var centerX = appDrawerPopout.triggerX + (appDrawerPopout.triggerWidth / 2) - (popupWidth / 2)
if (centerX >= Theme.spacingM
&& centerX + popupWidth <= screenWidth - Theme.spacingM)
return centerX
if (centerX < Theme.spacingM)
return Theme.spacingM
if (centerX + popupWidth > screenWidth - Theme.spacingM)
return screenWidth - popupWidth - Theme.spacingM
return centerX
}
readonly property real calculatedY: appDrawerPopout.triggerY
asynchronous: true
active: appDrawerPopout.isVisible
width: popupWidth
height: popupHeight
x: calculatedX
y: calculatedY
opacity: appDrawerPopout.isVisible ? 1 : 0
scale: appDrawerPopout.isVisible ? 1 : 0.9
Behavior on opacity {
NumberAnimation {
duration: Anims.durMed
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasized
}
}
Behavior on scale {
NumberAnimation {
duration: Anims.durMed
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasized
}
}
sourceComponent: Rectangle {
id: launcherPanel id: launcherPanel
color: Theme.popupBackground() color: Theme.popupBackground()
@@ -177,12 +101,12 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
focus: true focus: true
Component.onCompleted: { Component.onCompleted: {
if (appDrawerPopout.isVisible) if (appDrawerPopout.shouldBeVisible)
forceActiveFocus() forceActiveFocus()
} }
Keys.onPressed: function (event) { Keys.onPressed: function (event) {
if (event.key === Qt.Key_Escape) { if (event.key === Qt.Key_Escape) {
appDrawerPopout.hide() appDrawerPopout.close()
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_Down) { } else if (event.key === Qt.Key_Down) {
appLauncher.selectNext() appLauncher.selectNext()
@@ -262,15 +186,17 @@ PanelWindow {
leftIconFocusedColor: Theme.primary leftIconFocusedColor: Theme.primary
showClearButton: true showClearButton: true
font.pixelSize: Theme.fontSizeLarge font.pixelSize: Theme.fontSizeLarge
enabled: appDrawerPopout.isVisible enabled: appDrawerPopout.shouldBeVisible
placeholderText: "Search applications..."
ignoreLeftRightKeys: true ignoreLeftRightKeys: true
keyForwardTargets: [keyHandler] keyForwardTargets: [keyHandler]
onTextEdited: { onTextEdited: {
appLauncher.searchQuery = text appLauncher.searchQuery = text
} }
Keys.onPressed: function (event) { Keys.onPressed: function (event) {
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) if (event.key === Qt.Key_Escape) {
appDrawerPopout.close()
event.accepted = true
} else if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
&& text.length > 0) { && text.length > 0) {
if (appLauncher.keyboardNavigationActive if (appLauncher.keyboardNavigationActive
&& appLauncher.model.count > 0) { && appLauncher.model.count > 0) {
@@ -288,13 +214,13 @@ PanelWindow {
} }
} }
Component.onCompleted: { Component.onCompleted: {
if (appDrawerPopout.isVisible) if (appDrawerPopout.shouldBeVisible)
searchField.forceActiveFocus() searchField.forceActiveFocus()
} }
Connections { Connections {
function onIsVisibleChanged() { function onShouldBeVisibleChanged() {
if (appDrawerPopout.isVisible) if (appDrawerPopout.shouldBeVisible)
Qt.callLater(function () { Qt.callLater(function () {
searchField.forceActiveFocus() searchField.forceActiveFocus()
}) })
@@ -560,10 +486,10 @@ PanelWindow {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
appList.itemClicked(index, model) appList.itemClicked(index, model)
} else if (mouse.button === Qt.RightButton) { } else if (mouse.button === Qt.RightButton) {
var globalPos = mapToGlobal(mouse.x, mouse.y) var panelPos = mapToItem(contextMenu.parent, mouse.x, mouse.y)
appList.itemRightClicked(index, model, appList.itemRightClicked(index, model,
globalPos.x, panelPos.x,
globalPos.y) panelPos.y)
} }
} }
} }
@@ -737,10 +663,10 @@ PanelWindow {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
appGrid.itemClicked(index, model) appGrid.itemClicked(index, model)
} else if (mouse.button === Qt.RightButton) { } else if (mouse.button === Qt.RightButton) {
var globalPos = mapToGlobal(mouse.x, mouse.y) var panelPos = mapToItem(contextMenu.parent, mouse.x, mouse.y)
appGrid.itemRightClicked(index, model, appGrid.itemRightClicked(index, model,
globalPos.x, panelPos.x,
globalPos.y) panelPos.y)
} }
} }
} }
@@ -752,205 +678,237 @@ PanelWindow {
} }
} }
Popup { Rectangle {
id: contextMenu id: contextMenu
property var currentApp: null property var currentApp: null
property bool menuVisible: false
function show(x, y, app) { function show(x, y, app) {
currentApp = app currentApp = app
if (!contextMenu.parent && typeof Overlay !== "undefined"
&& Overlay.overlay)
contextMenu.parent = Overlay.overlay
const menuWidth = 180 const menuWidth = 180
const menuHeight = menuColumn.implicitHeight + Theme.spacingS * 2 const menuHeight = menuColumn.implicitHeight + Theme.spacingS * 2
const screenWidth = Screen.width
const screenHeight = Screen.height let finalX = x + 8
let finalX = x let finalY = y + 8
let finalY = y
if (x + menuWidth > screenWidth - 20) if (finalX + menuWidth > appDrawerPopout.popupWidth) {
finalX = x - menuWidth finalX = x - menuWidth - 8
}
if (y + menuHeight > screenHeight - 20)
finalY = y - menuHeight if (finalY + menuHeight > appDrawerPopout.popupHeight) {
finalY = y - menuHeight - 8
contextMenu.x = Math.max(20, finalX) }
contextMenu.y = Math.max(20, finalY)
open() finalX = Math.max(8, Math.min(finalX, appDrawerPopout.popupWidth - menuWidth - 8))
finalY = Math.max(8, Math.min(finalY, appDrawerPopout.popupHeight - menuHeight - 8))
contextMenu.x = finalX
contextMenu.y = finalY
contextMenu.visible = true
contextMenu.menuVisible = true
} }
function close() {
contextMenu.menuVisible = false
Qt.callLater(() => {
contextMenu.visible = false
})
}
visible: false
width: 180 width: 180
height: menuColumn.implicitHeight + Theme.spacingS * 2 height: menuColumn.implicitHeight + Theme.spacingS * 2
padding: 0 radius: Theme.cornerRadius
modal: false color: Theme.popupBackground()
closePolicy: Popup.CloseOnEscape border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
onClosed: { border.width: 1
closePolicy = Popup.CloseOnEscape z: 1000
} opacity: menuVisible ? 1 : 0
onOpened: { scale: menuVisible ? 1 : 0.85
outsideClickTimer.start()
Rectangle {
anchors.fill: parent
anchors.topMargin: 4
anchors.leftMargin: 2
anchors.rightMargin: -2
anchors.bottomMargin: -4
radius: parent.radius
color: Qt.rgba(0, 0, 0, 0.15)
z: parent.z - 1
} }
Timer { Column {
id: outsideClickTimer id: menuColumn
interval: 100 anchors.fill: parent
onTriggered: { anchors.margins: Theme.spacingS
contextMenu.closePolicy = Popup.CloseOnEscape | Popup.CloseOnPressOutside spacing: 1
}
}
background: Rectangle { Rectangle {
color: "transparent" width: parent.width
} height: 32
radius: Theme.cornerRadius
color: pinMouseArea.containsMouse ? Qt.rgba(Theme.primary.r,
Theme.primary.g,
Theme.primary.b,
0.12) : "transparent"
contentItem: Rectangle { Row {
color: Theme.popupBackground() anchors.left: parent.left
radius: Theme.cornerRadius anchors.leftMargin: Theme.spacingS
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, anchors.verticalCenter: parent.verticalCenter
Theme.outline.b, 0.08) spacing: Theme.spacingS
border.width: 1
Column { DankIcon {
id: menuColumn name: {
anchors.fill: parent
anchors.margins: Theme.spacingS
spacing: 1
Rectangle {
width: parent.width
height: 32
radius: Theme.cornerRadius
color: pinMouseArea.containsMouse ? Qt.rgba(Theme.primary.r,
Theme.primary.g,
Theme.primary.b,
0.12) : "transparent"
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankIcon {
name: {
if (!contextMenu.currentApp
|| !contextMenu.currentApp.desktopEntry)
return "push_pin"
var appId = contextMenu.currentApp.desktopEntry.id
|| contextMenu.currentApp.desktopEntry.execString || ""
return SessionData.isPinnedApp(appId) ? "keep_off" : "push_pin"
}
size: Theme.iconSize - 2
color: Theme.surfaceText
opacity: 0.7
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: {
if (!contextMenu.currentApp
|| !contextMenu.currentApp.desktopEntry)
return "Pin to Dock"
var appId = contextMenu.currentApp.desktopEntry.id
|| contextMenu.currentApp.desktopEntry.execString || ""
return SessionData.isPinnedApp(
appId) ? "Unpin from Dock" : "Pin to Dock"
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: pinMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (!contextMenu.currentApp if (!contextMenu.currentApp
|| !contextMenu.currentApp.desktopEntry) || !contextMenu.currentApp.desktopEntry)
return return "push_pin"
var appId = contextMenu.currentApp.desktopEntry.id var appId = contextMenu.currentApp.desktopEntry.id
|| contextMenu.currentApp.desktopEntry.execString || "" || contextMenu.currentApp.desktopEntry.execString || ""
if (SessionData.isPinnedApp(appId)) return SessionData.isPinnedApp(appId) ? "keep_off" : "push_pin"
SessionData.removePinnedApp(appId)
else
SessionData.addPinnedApp(appId)
contextMenu.close()
} }
} size: Theme.iconSize - 2
} color: Theme.surfaceText
opacity: 0.7
Rectangle {
width: parent.width - Theme.spacingS * 2
height: 5
anchors.horizontalCenter: parent.horizontalCenter
color: "transparent"
Rectangle {
anchors.centerIn: parent
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
}
}
Rectangle {
width: parent.width
height: 32
radius: Theme.cornerRadius
color: launchMouseArea.containsMouse ? Qt.rgba(Theme.primary.r,
Theme.primary.g,
Theme.primary.b,
0.12) : "transparent"
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankIcon {
name: "launch"
size: Theme.iconSize - 2
color: Theme.surfaceText
opacity: 0.7
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Launch"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
anchors.verticalCenter: parent.verticalCenter
}
} }
MouseArea { StyledText {
id: launchMouseArea text: {
if (!contextMenu.currentApp
|| !contextMenu.currentApp.desktopEntry)
return "Pin to Dock"
anchors.fill: parent var appId = contextMenu.currentApp.desktopEntry.id
hoverEnabled: true || contextMenu.currentApp.desktopEntry.execString || ""
cursorShape: Qt.PointingHandCursor return SessionData.isPinnedApp(
onClicked: { appId) ? "Unpin from Dock" : "Pin to Dock"
if (contextMenu.currentApp)
appLauncher.launchApp(contextMenu.currentApp)
contextMenu.close()
} }
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
anchors.verticalCenter: parent.verticalCenter
} }
} }
MouseArea {
id: pinMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (!contextMenu.currentApp
|| !contextMenu.currentApp.desktopEntry)
return
var appId = contextMenu.currentApp.desktopEntry.id
|| contextMenu.currentApp.desktopEntry.execString || ""
if (SessionData.isPinnedApp(appId))
SessionData.removePinnedApp(appId)
else
SessionData.addPinnedApp(appId)
contextMenu.close()
}
}
}
Rectangle {
width: parent.width - Theme.spacingS * 2
height: 5
anchors.horizontalCenter: parent.horizontalCenter
color: "transparent"
Rectangle {
anchors.centerIn: parent
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
}
}
Rectangle {
width: parent.width
height: 32
radius: Theme.cornerRadius
color: launchMouseArea.containsMouse ? Qt.rgba(Theme.primary.r,
Theme.primary.g,
Theme.primary.b,
0.12) : "transparent"
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankIcon {
name: "launch"
size: Theme.iconSize - 2
color: Theme.surfaceText
opacity: 0.7
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Launch"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: launchMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (contextMenu.currentApp)
appLauncher.launchApp(contextMenu.currentApp)
contextMenu.close()
}
}
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on scale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
MouseArea {
anchors.fill: parent
visible: contextMenu.visible
z: 999
onClicked: {
contextMenu.close()
}
MouseArea {
x: contextMenu.x
y: contextMenu.y
width: contextMenu.width
height: contextMenu.height
onClicked: {
// Prevent closing when clicking on the menu itself
} }
} }
} }

View File

@@ -14,6 +14,7 @@ PanelWindow {
readonly property bool hasActiveMedia: MprisController.activePlayer !== null readonly property bool hasActiveMedia: MprisController.activePlayer !== null
property bool calendarVisible: false property bool calendarVisible: false
property bool shouldBeVisible: false
property real triggerX: (Screen.width - 480) / 2 property real triggerX: (Screen.width - 480) / 2
property real triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + 4 property real triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + 4
property real triggerWidth: 80 property real triggerWidth: 80
@@ -28,13 +29,29 @@ PanelWindow {
triggerScreen = screen triggerScreen = screen
} }
visible: calendarVisible visible: calendarVisible || closeTimer.running
screen: triggerScreen screen: triggerScreen
onCalendarVisibleChanged: { onCalendarVisibleChanged: {
if (calendarVisible) { if (calendarVisible) {
closeTimer.stop()
shouldBeVisible = true
visible = true
Qt.callLater(() => { Qt.callLater(() => {
calendarGrid.loadEventsForMonth() calendarGrid.loadEventsForMonth()
}) })
} else {
shouldBeVisible = false
closeTimer.restart()
}
}
Timer {
id: closeTimer
interval: Theme.mediumDuration + 50
onTriggered: {
if (!shouldBeVisible) {
visible = false
}
} }
} }
onVisibleChanged: { onVisibleChanged: {
@@ -45,7 +62,7 @@ PanelWindow {
implicitHeight: 600 implicitHeight: 600
WlrLayershell.layer: WlrLayershell.Overlay WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1 WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: calendarVisible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None WlrLayershell.keyboardFocus: shouldBeVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
color: "transparent" color: "transparent"
anchors { anchors {
@@ -123,8 +140,8 @@ PanelWindow {
Theme.outline.b, 0.08) Theme.outline.b, 0.08)
border.width: 1 border.width: 1
layer.enabled: true layer.enabled: true
opacity: calendarVisible ? 1 : 0 opacity: shouldBeVisible ? 1 : 0
scale: calendarVisible ? 1 : 0.9 scale: shouldBeVisible ? 1 : 0.9
x: calculatedX x: calculatedX
y: root.triggerY y: root.triggerY
onOpacityChanged: { onOpacityChanged: {
@@ -166,7 +183,7 @@ PanelWindow {
radius: parent.radius radius: parent.radius
SequentialAnimation on opacity { SequentialAnimation on opacity {
running: calendarVisible running: shouldBeVisible
loops: Animation.Infinite loops: Animation.Infinite
NumberAnimation { NumberAnimation {
@@ -296,7 +313,7 @@ PanelWindow {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
z: -1 z: -1
enabled: calendarVisible enabled: shouldBeVisible
onClicked: function (mouse) { onClicked: function (mouse) {
var localPos = mapToItem(mainContainer, mouse.x, mouse.y) var localPos = mapToItem(mainContainer, mouse.x, mouse.y)
if (localPos.x < 0 || localPos.x > mainContainer.width || localPos.y < 0 if (localPos.x < 0 || localPos.x > mainContainer.width || localPos.y < 0

View File

@@ -11,17 +11,13 @@ import qs.Modules.ControlCenter
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
PanelWindow { DankPopout {
id: root id: root
property bool controlCenterVisible: false
property string currentTab: "network" property string currentTab: "network"
property bool powerOptionsExpanded: false property bool powerOptionsExpanded: false
property var triggerScreen: null
property real triggerX: Screen.width - 600 - Theme.spacingL
property real triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
property real triggerWidth: 80
property string triggerSection: "right" property string triggerSection: "right"
property var triggerScreen: null
function setTriggerPosition(x, y, width, section, screen) { function setTriggerPosition(x, y, width, section, screen) {
triggerX = x triggerX = x
@@ -34,82 +30,42 @@ PanelWindow {
signal powerActionRequested(string action, string title, string message) signal powerActionRequested(string action, string title, string message)
signal lockRequested signal lockRequested
visible: controlCenterVisible popupWidth: 600
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : (powerOptionsExpanded ? 570 : 500)
triggerX: Screen.width - 600 - Theme.spacingL
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
triggerWidth: 80
positioning: "center"
WlrLayershell.namespace: "quickshell-controlcenter"
screen: triggerScreen screen: triggerScreen
onVisibleChanged: { shouldBeVisible: false
NetworkService.autoRefreshEnabled = visible && NetworkService.wifiEnabled visible: shouldBeVisible
if (!visible && BluetoothService.adapter
&& BluetoothService.adapter.discovering)
BluetoothService.adapter.discovering = false
if (visible && UserInfoService) onShouldBeVisibleChanged: {
UserInfoService.getUptime() if (shouldBeVisible) {
} NetworkService.autoRefreshEnabled = NetworkService.wifiEnabled
implicitWidth: 600 if (UserInfoService)
implicitHeight: 500 UserInfoService.getUptime()
WlrLayershell.layer: WlrLayershell.Overlay } else {
WlrLayershell.exclusiveZone: -1 NetworkService.autoRefreshEnabled = false
WlrLayershell.keyboardFocus: controlCenterVisible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None if (BluetoothService.adapter && BluetoothService.adapter.discovering)
color: "transparent" BluetoothService.adapter.discovering = false
}
anchors {
top: true
left: true
right: true
bottom: true
} }
Loader { content: Component {
id: contentLoader Rectangle {
id: controlContent
readonly property real screenWidth: root.screen ? root.screen.width : Screen.width
readonly property real screenHeight: root.screen ? root.screen.height : Screen.height implicitHeight: {
readonly property real targetWidth: Math.min( let baseHeight = Theme.spacingL * 2
600, screenWidth - Theme.spacingL * 2) baseHeight += 90 // user header
baseHeight += (powerOptionsExpanded ? 60 : 0) + Theme.spacingL // power options
asynchronous: true baseHeight += 52 + Theme.spacingL // tab bar
active: controlCenterVisible baseHeight += 280 // tab content area
width: targetWidth return baseHeight
height: root.powerOptionsExpanded ? 570 : 500
y: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
x: {
var centerX = root.triggerX + (root.triggerWidth / 2) - (targetWidth / 2)
if (centerX >= Theme.spacingM
&& centerX + targetWidth <= screenWidth - Theme.spacingM) {
return centerX
} }
if (centerX < Theme.spacingM) {
return Theme.spacingM
}
if (centerX + targetWidth > screenWidth - Theme.spacingM) {
return screenWidth - targetWidth - Theme.spacingM
}
return centerX
}
opacity: controlCenterVisible ? 1 : 0
scale: controlCenterVisible ? 1 : 0.9
Behavior on opacity {
NumberAnimation {
duration: Anims.durMed
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasized
}
}
Behavior on scale {
NumberAnimation {
duration: Anims.durMed
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasized
}
}
sourceComponent: Rectangle {
color: Theme.popupBackground() color: Theme.popupBackground()
radius: Theme.cornerRadius radius: Theme.cornerRadius
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
@@ -118,13 +74,15 @@ PanelWindow {
antialiasing: true antialiasing: true
smooth: true smooth: true
focus: true focus: true
Component.onCompleted: { Component.onCompleted: {
if (controlCenterVisible) if (root.shouldBeVisible)
forceActiveFocus() forceActiveFocus()
} }
Keys.onPressed: function (event) { Keys.onPressed: function (event) {
if (event.key === Qt.Key_Escape) { if (event.key === Qt.Key_Escape) {
controlCenterVisible = false root.close()
event.accepted = true event.accepted = true
} else { } else {
event.accepted = false event.accepted = false
@@ -132,10 +90,10 @@ PanelWindow {
} }
Connections { Connections {
function onControlCenterVisibleChanged() { function onShouldBeVisibleChanged() {
if (controlCenterVisible) if (root.shouldBeVisible)
Qt.callLater(function () { Qt.callLater(function () {
parent.forceActiveFocus() controlContent.forceActiveFocus()
}) })
} }
target: root target: root
@@ -294,7 +252,7 @@ PanelWindow {
hoverColor: Qt.rgba(Theme.primary.r, Theme.primary.g, hoverColor: Qt.rgba(Theme.primary.r, Theme.primary.g,
Theme.primary.b, 0.12) Theme.primary.b, 0.12)
onClicked: { onClicked: {
controlCenterVisible = false root.close()
root.lockRequested() root.lockRequested()
} }
} }
@@ -363,7 +321,7 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
root.powerOptionsExpanded = !root.powerOptionsExpanded root.powerOptionsExpanded = !root.powerOptionsExpanded
} }
} }
@@ -387,7 +345,7 @@ PanelWindow {
hoverColor: Qt.rgba(Theme.primary.r, Theme.primary.g, hoverColor: Qt.rgba(Theme.primary.r, Theme.primary.g,
Theme.primary.b, 0.12) Theme.primary.b, 0.12)
onClicked: { onClicked: {
controlCenterVisible = false root.close()
settingsModal.show() settingsModal.show()
} }
} }
@@ -451,8 +409,9 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
root.powerOptionsExpanded = false root.powerOptionsExpanded = false
root.close()
root.powerActionRequested( root.powerActionRequested(
"logout", "Logout", "logout", "Logout",
"Are you sure you want to logout?") "Are you sure you want to logout?")
@@ -506,8 +465,9 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
root.powerOptionsExpanded = false root.powerOptionsExpanded = false
root.close()
root.powerActionRequested( root.powerActionRequested(
"reboot", "Restart", "reboot", "Restart",
"Are you sure you want to restart?") "Are you sure you want to restart?")
@@ -561,8 +521,9 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
root.powerOptionsExpanded = false root.powerOptionsExpanded = false
root.close()
root.powerActionRequested( root.powerActionRequested(
"suspend", "Suspend", "suspend", "Suspend",
"Are you sure you want to suspend?") "Are you sure you want to suspend?")
@@ -616,8 +577,9 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
root.powerOptionsExpanded = false root.powerOptionsExpanded = false
root.close()
root.powerActionRequested( root.powerActionRequested(
"poweroff", "Shutdown", "poweroff", "Shutdown",
"Are you sure you want to shutdown?") "Are you sure you want to shutdown?")
@@ -768,23 +730,12 @@ PanelWindow {
} }
} }
Behavior on height { Behavior on implicitHeight {
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration // Faster for height changes duration: Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
} }
} }
} }
}
MouseArea {
anchors.fill: parent
z: -1
onClicked: function (mouse) {
var localPos = mapToItem(contentLoader, mouse.x, mouse.y)
if (localPos.x < 0 || localPos.x > contentLoader.width || localPos.y < 0
|| localPos.y > contentLoader.height)
controlCenterVisible = false
}
}
}

View File

@@ -131,9 +131,7 @@ Rectangle {
wifiContextMenuWindow.networkData.ssid) wifiContextMenuWindow.networkData.ssid)
} else if (wifiContextMenuWindow.networkData.secured) { } else if (wifiContextMenuWindow.networkData.secured) {
if (wifiPasswordModalRef) { if (wifiPasswordModalRef) {
wifiPasswordModalRef.wifiPasswordSSID = wifiContextMenuWindow.networkData.ssid wifiPasswordModalRef.show(wifiContextMenuWindow.networkData.ssid)
wifiPasswordModalRef.wifiPasswordInput = ""
wifiPasswordModalRef.wifiPasswordModalVisible = true
} }
} else { } else {
NetworkService.connectToWifi( NetworkService.connectToWifi(

View File

@@ -303,9 +303,7 @@ Column {
NetworkService.connectToWifi(modelData.ssid) NetworkService.connectToWifi(modelData.ssid)
} else if (modelData.secured) { } else if (modelData.secured) {
if (wifiPasswordModalRef) { if (wifiPasswordModalRef) {
wifiPasswordModalRef.wifiPasswordSSID = modelData.ssid wifiPasswordModalRef.show(modelData.ssid)
wifiPasswordModalRef.wifiPasswordInput = ""
wifiPasswordModalRef.wifiPasswordModalVisible = true
} }
} else { } else {
NetworkService.connectToWifi(modelData.ssid) NetworkService.connectToWifi(modelData.ssid)

View File

@@ -11,10 +11,7 @@ PanelWindow {
id: root id: root
property bool powerMenuVisible: false property bool powerMenuVisible: false
property bool powerConfirmVisible: false signal powerActionRequested(string action, string title, string message)
property string powerConfirmAction: ""
property string powerConfirmTitle: ""
property string powerConfirmMessage: ""
visible: powerMenuVisible visible: powerMenuVisible
implicitWidth: 400 implicitWidth: 400
@@ -137,10 +134,7 @@ PanelWindow {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
powerMenuVisible = false powerMenuVisible = false
root.powerConfirmAction = "logout" root.powerActionRequested("logout", "Log Out", "Are you sure you want to log out?")
root.powerConfirmTitle = "Log Out"
root.powerConfirmMessage = "Are you sure you want to log out?"
root.powerConfirmVisible = true
} }
} }
} }
@@ -187,10 +181,7 @@ PanelWindow {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
powerMenuVisible = false powerMenuVisible = false
root.powerConfirmAction = "suspend" root.powerActionRequested("suspend", "Suspend", "Are you sure you want to suspend the system?")
root.powerConfirmTitle = "Suspend"
root.powerConfirmMessage = "Are you sure you want to suspend the system?"
root.powerConfirmVisible = true
} }
} }
} }
@@ -237,10 +228,7 @@ PanelWindow {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
powerMenuVisible = false powerMenuVisible = false
root.powerConfirmAction = "reboot" root.powerActionRequested("reboot", "Reboot", "Are you sure you want to reboot the system?")
root.powerConfirmTitle = "Reboot"
root.powerConfirmMessage = "Are you sure you want to reboot the system?"
root.powerConfirmVisible = true
} }
} }
} }
@@ -287,10 +275,7 @@ PanelWindow {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
powerMenuVisible = false powerMenuVisible = false
root.powerConfirmAction = "poweroff" root.powerActionRequested("poweroff", "Power Off", "Are you sure you want to power off the system?")
root.powerConfirmTitle = "Power Off"
root.powerConfirmMessage = "Are you sure you want to power off the system?"
root.powerConfirmVisible = true
} }
} }
} }

View File

@@ -187,8 +187,7 @@ PanelWindow {
} }
Rectangle { Rectangle {
visible: root.appData && root.appData.windows visible: !!(root.appData && root.appData.windows && root.appData.windows.count > 0)
&& root.appData.windows.count > 0
width: parent.width width: parent.width
height: 1 height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2) color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
@@ -236,16 +235,14 @@ PanelWindow {
} }
Rectangle { Rectangle {
visible: root.appData && root.appData.windows visible: !!(root.appData && root.appData.windows && root.appData.windows.count > 1)
&& root.appData.windows.count > 1
width: parent.width width: parent.width
height: 1 height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2) color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
} }
Rectangle { Rectangle {
visible: root.appData && root.appData.windows visible: !!(root.appData && root.appData.windows && root.appData.windows.count > 1)
&& root.appData.windows.count > 1
width: parent.width width: parent.width
height: 28 height: 28
radius: Theme.cornerRadius radius: Theme.cornerRadius

View File

@@ -9,14 +9,12 @@ import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.Notifications.Center import qs.Modules.Notifications.Center
PanelWindow { DankPopout {
id: root id: root
property bool notificationHistoryVisible: false property bool notificationHistoryVisible: false
property real triggerX: Screen.width - 400 - Theme.spacingL
property real triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
property real triggerWidth: 40
property string triggerSection: "right" property string triggerSection: "right"
property var triggerScreen: null
NotificationKeyboardController { NotificationKeyboardController {
id: keyboardController id: keyboardController
@@ -25,185 +23,143 @@ PanelWindow {
onClose: function() { notificationHistoryVisible = false } onClose: function() { notificationHistoryVisible = false }
} }
NotificationKeyboardHints {
id: keyboardHints
anchors.bottom: mainRect.bottom
anchors.left: mainRect.left
anchors.right: mainRect.right
anchors.margins: Theme.spacingL
showHints: keyboardController.showKeyboardHints
z: 200
}
function setTriggerPosition(x, y, width, section) { function setTriggerPosition(x, y, width, section, screen) {
triggerX = x triggerX = x
triggerY = y triggerY = y
triggerWidth = width triggerWidth = width
triggerSection = section triggerSection = section
triggerScreen = screen
} }
visible: notificationHistoryVisible popupWidth: 400
popupHeight: {
let baseHeight = Theme.spacingL * 2
baseHeight += 40 // notificationHeader height estimate
baseHeight += 0 // notificationSettings when collapsed
baseHeight += Theme.spacingM * 2
let listHeight = 200 // default list height
baseHeight += Math.min(listHeight, 600)
return Math.max(300, Math.min(baseHeight, Screen.height * 0.8))
}
triggerX: Screen.width - 400 - Theme.spacingL
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
triggerWidth: 40
positioning: "center"
WlrLayershell.namespace: "quickshell-notifications"
screen: triggerScreen
shouldBeVisible: notificationHistoryVisible
visible: shouldBeVisible
onNotificationHistoryVisibleChanged: { onNotificationHistoryVisibleChanged: {
NotificationService.disablePopups(notificationHistoryVisible) if (notificationHistoryVisible) {
} open()
implicitWidth: 400 NotificationService.disablePopups(true)
implicitHeight: Math.min(Screen.height * 0.8, 400) } else {
WlrLayershell.layer: WlrLayershell.Overlay close()
WlrLayershell.exclusiveZone: -1 NotificationService.disablePopups(false)
WlrLayershell.keyboardFocus: notificationHistoryVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
color: "transparent"
anchors {
top: true
left: true
right: true
bottom: true
}
MouseArea {
anchors.fill: parent
onClicked: {
notificationHistoryVisible = false
} }
} }
Rectangle { content: Component {
id: mainRect Rectangle {
id: notificationContent
readonly property real popupWidth: 400
readonly property real calculatedX: {
var centerX = root.triggerX + (root.triggerWidth / 2) - (popupWidth / 2)
if (centerX >= Theme.spacingM
&& centerX + popupWidth <= Screen.width - Theme.spacingM) {
return centerX
}
if (centerX < Theme.spacingM) {
return Theme.spacingM
}
if (centerX + popupWidth > Screen.width - Theme.spacingM) {
return Screen.width - popupWidth - Theme.spacingM
}
return centerX
}
width: popupWidth
height: {
let baseHeight = Theme.spacingL * 2
baseHeight += notificationHeader.height
// Use the final content height when expanded, not the animating height
baseHeight += (notificationSettings.expanded ? notificationSettings.contentHeight : 0)
baseHeight += Theme.spacingM * 2
let listHeight = notificationList.listContentHeight
if (NotificationService.groupedNotifications.length === 0)
listHeight = 200
baseHeight += Math.min(listHeight, 600)
return Math.max(300, Math.min(baseHeight, Screen.height * 0.8))
}
x: calculatedX
y: root.triggerY
color: Theme.popupBackground()
radius: Theme.cornerRadius
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.08)
border.width: 1
opacity: notificationHistoryVisible ? 1 : 0
scale: notificationHistoryVisible ? 1 : 0.9
MouseArea {
anchors.fill: parent
onClicked: {
}
}
FocusScope {
id: contentColumn
anchors.fill: parent
anchors.margins: Theme.spacingL
focus: true
implicitHeight: {
let baseHeight = Theme.spacingL * 2
baseHeight += notificationHeader.height
baseHeight += (notificationSettings.expanded ? notificationSettings.contentHeight : 0)
baseHeight += Theme.spacingM * 2
let listHeight = notificationList.listContentHeight
if (NotificationService.groupedNotifications.length === 0)
listHeight = 200
baseHeight += Math.min(listHeight, 600)
return Math.max(300, Math.min(baseHeight, root.screen ? root.screen.height * 0.8 : Screen.height * 0.8))
}
color: Theme.popupBackground()
radius: Theme.cornerRadius
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.08)
border.width: 1
focus: true
Component.onCompleted: { Component.onCompleted: {
if (notificationHistoryVisible) if (root.shouldBeVisible)
forceActiveFocus() forceActiveFocus()
} }
Keys.onPressed: function(event) { Keys.onPressed: function(event) {
keyboardController.handleKey(event) keyboardController.handleKey(event)
} }
Column {
id: contentColumnInner
anchors.fill: parent
spacing: Theme.spacingM
Connections { Connections {
function onNotificationHistoryVisibleChanged() { function onShouldBeVisibleChanged() {
if (notificationHistoryVisible) if (root.shouldBeVisible)
Qt.callLater(function () { Qt.callLater(function () {
contentColumn.forceActiveFocus() notificationContent.forceActiveFocus()
}) })
else else
contentColumn.focus = false notificationContent.focus = false
} }
target: root target: root
} }
NotificationHeader { FocusScope {
id: notificationHeader id: contentColumn
keyboardController: keyboardController
}
NotificationSettings {
id: notificationSettings
expanded: notificationHeader.showSettings
}
KeyboardNavigatedNotificationList { anchors.fill: parent
id: notificationList anchors.margins: Theme.spacingL
focus: true
width: parent.width
height: parent.height - notificationHeader.height - notificationSettings.height - contentColumnInner.spacing * 2
Component.onCompleted: { Column {
if (keyboardController && notificationList) { id: contentColumnInner
keyboardController.listView = notificationList anchors.fill: parent
notificationList.keyboardController = keyboardController spacing: Theme.spacingM
NotificationHeader {
id: notificationHeader
keyboardController: keyboardController
}
NotificationSettings {
id: notificationSettings
expanded: notificationHeader.showSettings
}
KeyboardNavigatedNotificationList {
id: notificationList
width: parent.width
height: parent.height - notificationHeader.height - notificationSettings.height - contentColumnInner.spacing * 2
Component.onCompleted: {
if (keyboardController && notificationList) {
keyboardController.listView = notificationList
notificationList.keyboardController = keyboardController
}
}
} }
} }
} }
NotificationKeyboardHints {
id: keyboardHints
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: Theme.spacingL
showHints: keyboardController.showKeyboardHints
z: 200
} }
}
Behavior on implicitHeight {
Behavior on height { NumberAnimation {
NumberAnimation { duration: Anims.durShort
duration: Anims.durShort easing.type: Easing.BezierSpline
easing.type: Easing.BezierSpline easing.bezierCurve: Anims.emphasized
easing.bezierCurve: Anims.emphasized }
}
}
Behavior on opacity {
NumberAnimation {
duration: Anims.durMed
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasized
}
}
Behavior on scale {
NumberAnimation {
duration: Anims.durMed
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasized
} }
} }
} }
} }

View File

@@ -11,14 +11,10 @@ import qs.Modules.ProcessList
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
PanelWindow { DankPopout {
id: processListPopout id: processListPopout
property bool isVisible: false
property var parentWidget: null property var parentWidget: null
property real triggerX: Screen.width - 600 - Theme.spacingL
property real triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
property real triggerWidth: 55
property string triggerSection: "right" property string triggerSection: "right"
property var triggerScreen: null property var triggerScreen: null
@@ -31,109 +27,33 @@ PanelWindow {
} }
function hide() { function hide() {
isVisible = false close()
if (processContextMenu.visible) if (processContextMenu.visible)
processContextMenu.close() processContextMenu.close()
} }
function show() { function show() {
isVisible = true open()
} }
function toggle() { popupWidth: 600
if (isVisible) popupHeight: 600
hide() triggerX: Screen.width - 600 - Theme.spacingL
else triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
show() triggerWidth: 55
} positioning: "center"
WlrLayershell.namespace: "quickshell-processlist"
visible: isVisible
screen: triggerScreen screen: triggerScreen
implicitWidth: 600 visible: shouldBeVisible
implicitHeight: 600 shouldBeVisible: false
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: isVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
color: "transparent"
Ref { Ref {
service: DgopService service: DgopService
} }
anchors { content: Component {
top: true Rectangle {
left: true id: processListContent
right: true
bottom: true
}
MouseArea {
anchors.fill: parent
onClicked: function (mouse) {
var localPos = mapToItem(contentLoader, mouse.x, mouse.y)
if (localPos.x < 0 || localPos.x > contentLoader.width || localPos.y < 0
|| localPos.y > contentLoader.height)
processListPopout.hide()
}
}
Loader {
id: contentLoader
readonly property real screenWidth: processListPopout.screen ? processListPopout.screen.width : Screen.width
readonly property real screenHeight: processListPopout.screen ? processListPopout.screen.height : Screen.height
readonly property real targetWidth: Math.min(
600, screenWidth - Theme.spacingL * 2)
readonly property real targetHeight: Math.min(
600,
screenHeight - Theme.barHeight - Theme.spacingS * 2)
readonly property real calculatedX: {
var centerX = processListPopout.triggerX + (processListPopout.triggerWidth
/ 2) - (targetWidth / 2)
if (centerX >= Theme.spacingM
&& centerX + targetWidth <= screenWidth - Theme.spacingM) {
return centerX
}
if (centerX < Theme.spacingM) {
return Theme.spacingM
}
if (centerX + targetWidth > screenWidth - Theme.spacingM) {
return screenWidth - targetWidth - Theme.spacingM
}
return centerX
}
asynchronous: true
active: processListPopout.isVisible
width: targetWidth
height: targetHeight
y: processListPopout.triggerY
x: calculatedX
opacity: processListPopout.isVisible ? 1 : 0
scale: processListPopout.isVisible ? 1 : 0.9
Behavior on opacity {
NumberAnimation {
duration: Anims.durMed
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasized
}
}
Behavior on scale {
NumberAnimation {
duration: Anims.durMed
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasized
}
}
sourceComponent: Rectangle {
id: dropdownContent
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.popupBackground() color: Theme.popupBackground()
@@ -144,22 +64,24 @@ PanelWindow {
antialiasing: true antialiasing: true
smooth: true smooth: true
focus: true focus: true
Component.onCompleted: { Component.onCompleted: {
if (processListPopout.isVisible) if (processListPopout.shouldBeVisible)
forceActiveFocus() forceActiveFocus()
} }
Keys.onPressed: function (event) { Keys.onPressed: function (event) {
if (event.key === Qt.Key_Escape) { if (event.key === Qt.Key_Escape) {
processListPopout.hide() processListPopout.close()
event.accepted = true event.accepted = true
} }
} }
Connections { Connections {
function onIsVisibleChanged() { function onShouldBeVisibleChanged() {
if (processListPopout.isVisible) if (processListPopout.shouldBeVisible)
Qt.callLater(function () { Qt.callLater(function () {
dropdownContent.forceActiveFocus() processListContent.forceActiveFocus()
}) })
} }
target: processListPopout target: processListPopout
@@ -211,4 +133,4 @@ PanelWindow {
ProcessContextMenu { ProcessContextMenu {
id: processContextMenu id: processContextMenu
} }
} }

View File

@@ -12,6 +12,7 @@ Item {
property alias profileBrowser: profileBrowser property alias profileBrowser: profileBrowser
property alias wallpaperBrowser: wallpaperBrowser property alias wallpaperBrowser: wallpaperBrowser
property var parentModal: null
Component.onCompleted: { Component.onCompleted: {
// Access WallpaperCyclingService to ensure it's initialized // Access WallpaperCyclingService to ensure it's initialized
@@ -238,7 +239,11 @@ Item {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
profileBrowser.visible = true; if (parentModal) {
parentModal.allowFocusOverride = true;
parentModal.shouldHaveFocus = false;
}
profileBrowser.open();
} }
} }
@@ -437,7 +442,11 @@ Item {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
wallpaperBrowser.visible = true; if (parentModal) {
parentModal.allowFocusOverride = true;
parentModal.shouldHaveFocus = false;
}
wallpaperBrowser.open();
} }
} }
@@ -915,7 +924,13 @@ Item {
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"] fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
onFileSelected: (path) => { onFileSelected: (path) => {
PortalService.setProfileImage(path); PortalService.setProfileImage(path);
visible = false; close();
}
onDialogClosed: {
if (parentModal) {
parentModal.allowFocusOverride = false;
parentModal.shouldHaveFocus = Qt.binding(() => parentModal.shouldBeVisible);
}
} }
} }
@@ -928,7 +943,13 @@ Item {
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"] fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
onFileSelected: (path) => { onFileSelected: (path) => {
SessionData.setWallpaper(path); SessionData.setWallpaper(path);
visible = false; close();
}
onDialogClosed: {
if (parentModal) {
parentModal.allowFocusOverride = false;
parentModal.shouldHaveFocus = Qt.binding(() => parentModal.shouldBeVisible);
}
} }
} }

View File

@@ -125,7 +125,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
var globalPos = mapToGlobal(0, 0) var globalPos = mapToGlobal(0, 0)
var currentScreen = parentScreen || Screen var currentScreen = parentScreen || Screen

View File

@@ -8,13 +8,9 @@ import qs.Common
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
PanelWindow { DankPopout {
id: root id: root
property bool batteryPopupVisible: false
property real triggerX: Screen.width - 380 - Theme.spacingL
property real triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingS
property real triggerWidth: 70
property string triggerSection: "right" property string triggerSection: "right"
property var triggerScreen: null property var triggerScreen: null
@@ -43,84 +39,22 @@ PanelWindow {
ToastService.showError("Failed to set power profile") ToastService.showError("Failed to set power profile")
} }
visible: batteryPopupVisible popupWidth: 400
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 400
triggerX: Screen.width - 380 - Theme.spacingL
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingS
triggerWidth: 70
positioning: "center"
WlrLayershell.namespace: "quickshell-battery"
screen: triggerScreen screen: triggerScreen
implicitWidth: 400 shouldBeVisible: false
implicitHeight: 300 visible: shouldBeVisible
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: batteryPopupVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
color: "transparent"
anchors {
top: true
left: true
right: true
bottom: true
}
MouseArea { content: Component {
anchors.fill: parent Rectangle {
onClicked: function (mouse) { id: batteryContent
var localPos = mapToItem(contentLoader, mouse.x, mouse.y)
if (localPos.x < 0 || localPos.x > contentLoader.width || localPos.y < 0
|| localPos.y > contentLoader.height)
batteryPopupVisible = false
}
}
Loader {
id: contentLoader
readonly property real screenWidth: root.screen ? root.screen.width : Screen.width
readonly property real screenHeight: root.screen ? root.screen.height : Screen.height
readonly property real targetWidth: Math.min(
380, screenWidth - Theme.spacingL * 2)
readonly property real calculatedX: {
var centerX = root.triggerX + (root.triggerWidth / 2) - (targetWidth / 2)
if (centerX >= Theme.spacingM
&& centerX + targetWidth <= screenWidth - Theme.spacingM) {
return centerX
}
if (centerX < Theme.spacingM) {
return Theme.spacingM
}
if (centerX + targetWidth > screenWidth - Theme.spacingM) {
return screenWidth - targetWidth - Theme.spacingM
}
return centerX
}
asynchronous: true
active: batteryPopupVisible
width: targetWidth
height: item ? item.implicitHeight : 0
x: calculatedX
y: root.triggerY
opacity: batteryPopupVisible ? 1 : 0
scale: batteryPopupVisible ? 1 : 0.9
Behavior on opacity {
NumberAnimation {
duration: Anims.durMed
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasized
}
}
Behavior on scale {
NumberAnimation {
duration: Anims.durMed
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasized
}
}
sourceComponent: Rectangle {
implicitHeight: contentColumn.height + Theme.spacingL * 2 implicitHeight: contentColumn.height + Theme.spacingL * 2
color: Theme.popupBackground() color: Theme.popupBackground()
radius: Theme.cornerRadius radius: Theme.cornerRadius
@@ -129,22 +63,24 @@ PanelWindow {
antialiasing: true antialiasing: true
smooth: true smooth: true
focus: true focus: true
Component.onCompleted: { Component.onCompleted: {
if (batteryPopupVisible) if (root.shouldBeVisible)
forceActiveFocus() forceActiveFocus()
} }
Keys.onPressed: function (event) { Keys.onPressed: function (event) {
if (event.key === Qt.Key_Escape) { if (event.key === Qt.Key_Escape) {
batteryPopupVisible = false root.close()
event.accepted = true event.accepted = true
} }
} }
Connections { Connections {
function onBatteryPopupVisibleChanged() { function onShouldBeVisibleChanged() {
if (batteryPopupVisible) if (root.shouldBeVisible)
Qt.callLater(function () { Qt.callLater(function () {
parent.forceActiveFocus() batteryContent.forceActiveFocus()
}) })
} }
target: root target: root
@@ -220,8 +156,8 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
batteryPopupVisible = false root.close()
} }
} }
} }
@@ -542,7 +478,7 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
root.setProfile(modelData) root.setProfile(modelData)
} }
} }
@@ -599,4 +535,4 @@ PanelWindow {
} }
} }
} }
} }

View File

@@ -69,7 +69,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
var globalPos = mapToGlobal(0, 0); var globalPos = mapToGlobal(0, 0);
var currentScreen = parentScreen || Screen; var currentScreen = parentScreen || Screen;

View File

@@ -132,7 +132,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
var globalPos = mapToGlobal(0, 0) var globalPos = mapToGlobal(0, 0)
var currentScreen = parentScreen || Screen var currentScreen = parentScreen || Screen

View File

@@ -35,7 +35,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
var globalPos = mapToGlobal(0, 0) var globalPos = mapToGlobal(0, 0)
var currentScreen = parentScreen || Screen var currentScreen = parentScreen || Screen

View File

@@ -35,7 +35,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
var globalPos = mapToGlobal(0, 0) var globalPos = mapToGlobal(0, 0)
var currentScreen = parentScreen || Screen var currentScreen = parentScreen || Screen

View File

@@ -74,7 +74,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
var globalPos = mapToGlobal(0, 0) var globalPos = mapToGlobal(0, 0)
var currentScreen = parentScreen || Screen var currentScreen = parentScreen || Screen

View File

@@ -21,11 +21,9 @@ Item {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
z: 1000 acceptedButtons: Qt.LeftButton
preventStealing: true
propagateComposedEvents: false
onClicked: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
var globalPos = mapToGlobal(0, 0) var globalPos = mapToGlobal(0, 0)
var currentScreen = parentScreen || Screen var currentScreen = parentScreen || Screen
@@ -39,15 +37,12 @@ Item {
} }
} }
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: { color: Qt.rgba(Theme.surfaceTextHover.r, Theme.surfaceTextHover.g, Theme.surfaceTextHover.b,
const baseColor = launcherArea.containsMouse Theme.surfaceTextHover.a * Theme.widgetTransparency)
|| isActive ? Theme.surfaceTextPressed : Theme.surfaceTextHover
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b,
baseColor.a * Theme.widgetTransparency)
}
SystemLogo { SystemLogo {
visible: SettingsData.useOSLogo visible: SettingsData.useOSLogo
@@ -67,11 +62,5 @@ Item {
color: Theme.surfaceText color: Theme.surfaceText
} }
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
} }
} }

View File

@@ -187,7 +187,7 @@ Rectangle {
enabled: root.playerAvailable && root.opacity > 0 && root.width > 0 && textContainer.visible enabled: root.playerAvailable && root.opacity > 0 && root.width > 0 && textContainer.visible
hoverEnabled: enabled hoverEnabled: enabled
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: { onPressed: {
if (root.popupTarget && root.popupTarget.setTriggerPosition) { if (root.popupTarget && root.popupTarget.setTriggerPosition) {
var globalPos = mapToGlobal(0, 0) var globalPos = mapToGlobal(0, 0)
var currentScreen = root.parentScreen || Screen var currentScreen = root.parentScreen || Screen

View File

@@ -49,7 +49,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
var globalPos = mapToGlobal(0, 0) var globalPos = mapToGlobal(0, 0)
var currentScreen = parentScreen || Screen var currentScreen = parentScreen || Screen

View File

@@ -35,7 +35,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
var globalPos = mapToGlobal(0, 0) var globalPos = mapToGlobal(0, 0)
var currentScreen = parentScreen || Screen var currentScreen = parentScreen || Screen

View File

@@ -617,7 +617,7 @@ PanelWindow {
id: launcherButtonComponent id: launcherButtonComponent
LauncherButton { LauncherButton {
isActive: appDrawerLoader.item ? appDrawerLoader.item.isVisible : false isActive: false
section: { section: {
if (parent && parent.parent) { if (parent && parent.parent) {
if (parent.parent === leftSection) if (parent.parent === leftSection)
@@ -921,7 +921,7 @@ PanelWindow {
id: batteryComponent id: batteryComponent
Battery { Battery {
batteryPopupVisible: batteryPopoutLoader.item ? batteryPopoutLoader.item.batteryPopupVisible : false batteryPopupVisible: batteryPopoutLoader.item ? batteryPopoutLoader.item.shouldBeVisible : false
section: { section: {
if (parent && parent.parent === leftSection) if (parent && parent.parent === leftSection)
return "left" return "left"
@@ -939,7 +939,7 @@ PanelWindow {
onToggleBatteryPopup: { onToggleBatteryPopup: {
batteryPopoutLoader.active = true batteryPopoutLoader.active = true
if (batteryPopoutLoader.item) { if (batteryPopoutLoader.item) {
batteryPopoutLoader.item.batteryPopupVisible = !batteryPopoutLoader.item.batteryPopupVisible batteryPopoutLoader.item.toggle()
} }
} }
} }
@@ -949,7 +949,7 @@ PanelWindow {
id: controlCenterButtonComponent id: controlCenterButtonComponent
ControlCenterButton { ControlCenterButton {
isActive: controlCenterLoader.item ? controlCenterLoader.item.controlCenterVisible : false isActive: controlCenterLoader.item ? controlCenterLoader.item.shouldBeVisible : false
section: { section: {
if (parent && parent.parent === leftSection) if (parent && parent.parent === leftSection)
return "left" return "left"
@@ -968,8 +968,8 @@ PanelWindow {
controlCenterLoader.active = true controlCenterLoader.active = true
if (controlCenterLoader.item) { if (controlCenterLoader.item) {
controlCenterLoader.item.triggerScreen = root.screen controlCenterLoader.item.triggerScreen = root.screen
controlCenterLoader.item.controlCenterVisible = !controlCenterLoader.item.controlCenterVisible controlCenterLoader.item.toggle()
if (controlCenterLoader.item.controlCenterVisible) { if (controlCenterLoader.item.shouldBeVisible) {
if (NetworkService.wifiEnabled) if (NetworkService.wifiEnabled)
NetworkService.scanWifi() NetworkService.scanWifi()
} }

View File

@@ -60,7 +60,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
var globalPos = mapToGlobal(0, 0) var globalPos = mapToGlobal(0, 0)
var currentScreen = parentScreen || Screen var currentScreen = parentScreen || Screen

156
Widgets/DankPopout.qml Normal file
View File

@@ -0,0 +1,156 @@
import QtQuick
import QtQuick.Effects
import Quickshell
import Quickshell.Wayland
import qs.Common
PanelWindow {
id: root
property alias content: contentLoader.sourceComponent
property alias contentLoader: contentLoader
property real popupWidth: 400
property real popupHeight: 300
property real triggerX: 0
property real triggerY: 0
property real triggerWidth: 40
property string positioning: "center"
property int animationDuration: Theme.mediumDuration
property var animationEasing: Theme.emphasizedEasing
property bool shouldBeVisible: false
signal opened()
signal popoutClosed()
signal backgroundClicked()
function open() {
closeTimer.stop();
shouldBeVisible = true;
visible = true;
opened();
}
function close() {
shouldBeVisible = false;
closeTimer.restart();
}
function toggle() {
if (shouldBeVisible)
close();
else
open();
}
Timer {
id: closeTimer
interval: animationDuration + 50
onTriggered: {
if (!shouldBeVisible) {
visible = false;
popoutClosed();
}
}
}
color: "transparent"
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: shouldBeVisible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
anchors {
top: true
left: true
right: true
bottom: true
}
MouseArea {
anchors.fill: parent
enabled: shouldBeVisible
onClicked: (mouse) => {
var localPos = mapToItem(contentContainer, mouse.x, mouse.y);
if (localPos.x < 0 || localPos.x > contentContainer.width ||
localPos.y < 0 || localPos.y > contentContainer.height) {
backgroundClicked();
close();
}
}
}
Item {
id: contentContainer
readonly property real screenWidth: root.screen ? root.screen.width : 1920
readonly property real screenHeight: root.screen ? root.screen.height : 1080
readonly property real calculatedX: {
if (positioning === "center") {
var centerX = triggerX + (triggerWidth / 2) - (popupWidth / 2);
if (centerX >= Theme.spacingM && centerX + popupWidth <= screenWidth - Theme.spacingM)
return centerX;
if (centerX < Theme.spacingM)
return Theme.spacingM;
if (centerX + popupWidth > screenWidth - Theme.spacingM)
return screenWidth - popupWidth - Theme.spacingM;
return centerX;
} else if (positioning === "left") {
return Math.max(Theme.spacingM, triggerX);
} else if (positioning === "right") {
return Math.min(screenWidth - popupWidth - Theme.spacingM, triggerX + triggerWidth - popupWidth);
}
return triggerX;
}
readonly property real calculatedY: triggerY
width: popupWidth
height: popupHeight
x: calculatedX
y: calculatedY
opacity: shouldBeVisible ? 1 : 0
scale: shouldBeVisible ? 1 : 0.9
Behavior on opacity {
NumberAnimation {
duration: animationDuration
easing.type: animationEasing
}
}
Behavior on scale {
NumberAnimation {
duration: animationDuration
easing.type: animationEasing
}
}
Loader {
id: contentLoader
anchors.fill: parent
active: root.visible
asynchronous: false
}
}
FocusScope {
anchors.fill: parent
visible: shouldBeVisible
focus: shouldBeVisible
Keys.onEscapePressed: (event) => {
close();
event.accepted = true;
}
onVisibleChanged: {
if (visible) {
Qt.callLater(function() {
forceActiveFocus();
});
}
}
}
}

View File

@@ -52,12 +52,13 @@ ShellRoot {
} }
} }
LazyLoader { Loader {
id: centcomPopoutLoader id: centcomPopoutLoader
active: false active: false
sourceComponent: Component {
CentcomPopout { CentcomPopout {
id: centcomPopout id: centcomPopout
}
} }
} }
@@ -106,10 +107,7 @@ ShellRoot {
onPowerActionRequested: (action, title, message) => { onPowerActionRequested: (action, title, message) => {
powerConfirmModalLoader.active = true powerConfirmModalLoader.active = true
if (powerConfirmModalLoader.item) { if (powerConfirmModalLoader.item) {
powerConfirmModalLoader.item.powerConfirmAction = action powerConfirmModalLoader.item.show(action, title, message)
powerConfirmModalLoader.item.powerConfirmTitle = title
powerConfirmModalLoader.item.powerConfirmMessage = message
powerConfirmModalLoader.item.powerConfirmVisible = true
} }
} }
onLockRequested: { onLockRequested: {
@@ -151,6 +149,12 @@ ShellRoot {
PowerMenu { PowerMenu {
id: powerMenu id: powerMenu
onPowerActionRequested: (action, title, message) => {
powerConfirmModalLoader.active = true
if (powerConfirmModalLoader.item) {
powerConfirmModalLoader.item.show(action, title, message)
}
}
} }
} }