1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-25 05:52: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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,324 +12,353 @@ import qs.Services
import qs.Widgets
DankModal {
id: processListModal
id: processListModal
property int currentTab: 0
property var tabNames: ["Processes", "Performance", "System"]
property int currentTab: 0
property var tabNames: ["Processes", "Performance", "System"]
function show() {
if (!DgopService.dgopAvailable) {
console.warn("ProcessListModal: dgop is not available")
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
function show() {
if (!DgopService.dgopAvailable) {
console.warn("ProcessListModal: dgop is not available");
return ;
}
}
open();
UserInfoService.getUptime();
}
// Show error message when dgop is not available
Rectangle {
anchors.centerIn: parent
width: 400
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
function hide() {
close();
if (processContextMenu.visible)
processContextMenu.close();
Column {
anchors.centerIn: parent
spacing: Theme.spacingL
}
DankIcon {
name: "error"
size: 48
color: Theme.error
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
}
function toggle() {
if (!DgopService.dgopAvailable) {
console.warn("ProcessListModal: dgop is not available");
return ;
}
}
if (shouldBeVisible)
hide();
else
show();
}
ColumnLayout {
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingL
visible: DgopService.dgopAvailable
width: 900
height: 680
visible: false
backgroundColor: Theme.popupBackground()
cornerRadius: Theme.cornerRadius
enableShadow: true
onBackgroundClicked: hide()
RowLayout {
Layout.fillWidth: true
height: 40
Component {
id: processesTabComponent
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
}
ProcessesTab {
contextMenu: processContextMenu
}
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.margins: 4
spacing: 2
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;
}
}
Repeater {
model: tabNames
Rectangle {
width: (parent.width - (tabNames.length - 1) * 2) / tabNames.length
height: 44
// Show error message when dgop is not available
Rectangle {
anchors.centerIn: parent
width: 400
height: 200
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
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.1)
border.color: Theme.error
border.width: 2
visible: !DgopService.dgopAvailable
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
Column {
anchors.centerIn: parent
spacing: Theme.spacingL
DankIcon {
name: {
switch (index) {
case 0:
return "list_alt"
case 1:
return "analytics"
case 2:
return "settings"
default:
return "tab"
}
DankIcon {
name: "error"
size: 48
color: Theme.error
anchors.horizontalCenter: parent.horizontalCenter
}
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: "System Monitor Unavailable"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Bold
color: Theme.error
anchors.horizontalCenter: parent.horizontalCenter
}
}
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
}
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
}
}
}
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 {
id: settingsModal
objectName: "settingsModal"
signal closingModal
function show() {
settingsModal.visible = true
open()
}
function hide() {
settingsModal.visible = false
close()
}
function toggle() {
if (settingsModal.visible)
if (shouldBeVisible)
hide()
else
show()
@@ -31,19 +32,12 @@ DankModal {
width: 750
height: 750
visible: false
keyboardFocus: "ondemand"
onBackgroundClicked: hide()
property Component settingsContent: Component {
Item {
anchors.fill: parent
focus: true
Keys.onPressed: function (event) {
if (event.key === Qt.Key_Escape) {
settingsModal.hide()
event.accepted = true
}
}
Column {
anchors.fill: parent
@@ -128,7 +122,9 @@ DankModal {
visible: active
asynchronous: true
sourceComponent: Component {
PersonalizationTab {}
PersonalizationTab {
parentModal: settingsModal
}
}
}

View File

@@ -15,6 +15,92 @@ DankModal {
property bool spotlightOpen: false
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 {
Item {
id: spotlightKeyHandler
@@ -111,7 +197,7 @@ DankModal {
textColor: Theme.surfaceText
font.pixelSize: Theme.fontSizeLarge
enabled: spotlightOpen
placeholderText: "Search applications..."
placeholderText: ""
ignoreLeftRightKeys: true
keyForwardTargets: [spotlightKeyHandler]
text: appLauncher.searchQuery
@@ -132,8 +218,6 @@ DankModal {
event.accepted = false;
}
}
}
Row {
@@ -357,6 +441,7 @@ DankModal {
onEntered: {
if (resultsList.hoverUpdatesSelection && !resultsList.keyboardNavigationActive)
resultsList.currentIndex = index;
}
onPositionChanged: {
resultsList.keyboardNavigationReset();
@@ -365,8 +450,8 @@ DankModal {
if (mouse.button === Qt.LeftButton) {
resultsList.itemClicked(index, model);
} else if (mouse.button === Qt.RightButton) {
var globalPos = mapToGlobal(mouse.x, mouse.y);
resultsList.itemRightClicked(index, model, globalPos.x, globalPos.y);
var modalPos = mapToItem(spotlightKeyHandler, mouse.x, mouse.y);
resultsList.itemRightClicked(index, model, modalPos.x, modalPos.y);
}
}
}
@@ -521,6 +606,7 @@ DankModal {
onEntered: {
if (resultsGrid.hoverUpdatesSelection && !resultsGrid.keyboardNavigationActive)
resultsGrid.currentIndex = index;
}
onPositionChanged: {
resultsGrid.keyboardNavigationReset();
@@ -529,8 +615,8 @@ DankModal {
if (mouse.button === Qt.LeftButton) {
resultsGrid.itemClicked(index, model);
} else if (mouse.button === Qt.RightButton) {
var globalPos = mapToGlobal(mouse.x, mouse.y);
resultsGrid.itemRightClicked(index, model, globalPos.x, globalPos.y);
var modalPos = mapToItem(spotlightKeyHandler, mouse.x, mouse.y);
resultsGrid.itemRightClicked(index, model, modalPos.x, modalPos.y);
}
}
}
@@ -543,274 +629,225 @@ DankModal {
}
}
Rectangle {
id: contextMenu
}
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
}
property var currentApp: null
property bool menuVisible: false
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 {
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();
}
if (finalY + menuHeight > spotlightKeyHandler.height) {
finalY = y - menuHeight - 8
}
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 {
width: parent.width - Theme.spacingS * 2
height: 5
anchors.horizontalCenter: parent.horizontalCenter
color: "transparent"
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
}
Column {
id: menuColumn
anchors.fill: parent
anchors.margins: Theme.spacingS
spacing: 1
Rectangle {
anchors.centerIn: parent
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
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 || !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 {
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
}
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
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 {
id: root
property bool wifiPasswordModalVisible: false
property string wifiPasswordSSID: ""
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
height: 230
keyboardFocus: "exclusive"
onVisibleChanged: {
if (!visible)
onShouldBeVisibleChanged: {
if (!shouldBeVisible)
wifiPasswordInput = ""
}
onOpened: {
Qt.callLater(function() {
if (contentLoader.item && contentLoader.item.passwordInput) {
contentLoader.item.passwordInput.forceActiveFocus()
}
})
}
onBackgroundClicked: {
wifiPasswordModalVisible = false
close()
wifiPasswordInput = ""
}
@@ -30,7 +46,7 @@ DankModal {
&& NetworkService.connectingSSID !== "") {
wifiPasswordSSID = NetworkService.connectingSSID
wifiPasswordInput = ""
wifiPasswordModalVisible = true
open()
NetworkService.passwordDialogShouldReopen = false
}
}
@@ -40,8 +56,16 @@ DankModal {
content: Component {
FocusScope {
id: wifiContent
property alias passwordInput: passwordInput
anchors.fill: parent
focus: true
Keys.onEscapePressed: function(event) {
close()
wifiPasswordInput = ""
event.accepted = true
}
Column {
anchors.centerIn: parent
@@ -77,7 +101,7 @@ DankModal {
iconColor: Theme.surfaceText
hoverColor: Theme.errorHover
onClicked: {
wifiPasswordModalVisible = false
close()
wifiPasswordInput = ""
}
}
@@ -91,6 +115,13 @@ DankModal {
border.color: passwordInput.activeFocus ? Theme.primary : Theme.outlineStrong
border.width: passwordInput.activeFocus ? 2 : 1
MouseArea {
anchors.fill: parent
onClicked: {
passwordInput.forceActiveFocus()
}
}
DankTextField {
id: passwordInput
@@ -99,42 +130,45 @@ DankModal {
textColor: Theme.surfaceText
text: wifiPasswordInput
echoMode: showPasswordCheckbox.checked ? TextInput.Normal : TextInput.Password
placeholderText: "Enter password"
placeholderText: ""
backgroundColor: "transparent"
focus: true
enabled: root.shouldBeVisible
onTextEdited: {
wifiPasswordInput = text
}
onAccepted: {
NetworkService.connectToWifiWithPassword(wifiPasswordSSID,
passwordInput.text)
wifiPasswordModalVisible = false
close()
wifiPasswordInput = ""
passwordInput.text = ""
}
Timer {
id: focusTimer
interval: 50
onTriggered: passwordInput.forceActiveFocus()
Component.onCompleted: {
if (root.shouldBeVisible) {
focusDelayTimer.start()
}
}
Component.onCompleted: {
focusTimer.start()
Timer {
id: focusDelayTimer
interval: 100
repeat: false
onTriggered: {
if (root.shouldBeVisible) {
passwordInput.forceActiveFocus()
}
}
}
Connections {
function onOpened() {
focusTimer.start()
}
function onVisibleChanged() {
if (root.visible) {
focusTimer.start()
target: root
function onShouldBeVisibleChanged() {
if (root.shouldBeVisible) {
focusDelayTimer.start()
}
}
target: root
}
}
}
@@ -214,7 +248,7 @@ DankModal {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
wifiPasswordModalVisible = false
close()
wifiPasswordInput = ""
}
}
@@ -249,7 +283,7 @@ DankModal {
onClicked: {
NetworkService.connectToWifiWithPassword(wifiPasswordSSID,
passwordInput.text)
wifiPasswordModalVisible = false
close()
wifiPasswordInput = ""
passwordInput.text = ""
}