1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-26 06:22:50 -05:00

mecha refactoring

This commit is contained in:
bbedward
2025-07-23 15:15:51 -04:00
parent 19adcf3578
commit c01da89311
37 changed files with 431 additions and 396 deletions

View File

@@ -6,7 +6,7 @@ import qs.Services
import qs.Widgets
Column {
id: calendarWidget
id: calendarGrid
property date displayDate: new Date()
property date selectedDate: new Date()

View File

@@ -7,7 +7,7 @@ import qs.Widgets
// Events widget for selected date - Material Design 3 style
Rectangle {
id: eventsWidget
id: events
property date selectedDate: new Date()
property var selectedDateEvents: []
@@ -17,7 +17,7 @@ Rectangle {
function updateSelectedDateEvents() {
if (CalendarService && CalendarService.khalAvailable) {
let events = CalendarService.getEventsForDate(selectedDate);
console.log("EventsWidget: Updating events for", Qt.formatDate(selectedDate, "yyyy-MM-dd"), "found", events.length, "events");
console.log("Events: Updating events for", Qt.formatDate(selectedDate, "yyyy-MM-dd"), "found", events.length, "events");
selectedDateEvents = events;
} else {
selectedDateEvents = [];
@@ -25,7 +25,7 @@ Rectangle {
}
onSelectedDateEventsChanged: {
console.log("EventsWidget: selectedDateEvents changed, count:", selectedDateEvents.length);
console.log("Events: selectedDateEvents changed, count:", selectedDateEvents.length);
eventsList.model = selectedDateEvents;
}
width: parent.width

View File

@@ -8,7 +8,7 @@ import qs.Services
import qs.Widgets
Rectangle {
id: mediaPlayerWidget
id: mediaPlayer
property MprisPlayer activePlayer: MprisController.activePlayer
property string lastValidTitle: ""

View File

@@ -6,7 +6,7 @@ import qs.Services
import qs.Widgets
Rectangle {
id: weatherWidget
id: weather
width: parent.width
height: parent.height

View File

@@ -1,591 +0,0 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
import qs.Widgets
DankModal {
// Don't hide the interface, just show toast
id: clipboardHistoryModal
property bool isVisible: false
property int totalCount: 0
property var activeTheme: Theme
property bool showClearConfirmation: false
property var clipboardEntries: []
property string searchText: ""
function updateFilteredModel() {
filteredClipboardModel.clear();
for (let i = 0; i < clipboardModel.count; i++) {
const entry = clipboardModel.get(i).entry;
if (searchText.trim().length === 0) {
filteredClipboardModel.append({
"entry": entry
});
} else {
const content = getEntryPreview(entry).toLowerCase();
if (content.includes(searchText.toLowerCase()))
filteredClipboardModel.append({
"entry": entry
});
}
}
clipboardHistoryModal.totalCount = filteredClipboardModel.count;
}
function toggle() {
if (isVisible)
hide();
else
show();
}
function show() {
clipboardHistoryModal.isVisible = true;
refreshClipboard();
console.log("ClipboardHistoryModal: Opening and refreshing");
}
function hide() {
clipboardHistoryModal.isVisible = false;
clipboardHistoryModal.searchText = "";
cleanupTempFiles();
}
function cleanupTempFiles() {
cleanupProcess.command = ["sh", "-c", "rm -f /tmp/clipboard_preview_*.png"];
cleanupProcess.running = true;
}
function refreshClipboard() {
clipboardProcess.running = true;
}
function copyEntry(entry) {
const entryId = entry.split('\t')[0];
copyProcess.command = ["sh", "-c", `cliphist decode ${entryId} | wl-copy`];
copyProcess.running = true;
console.log("ClipboardHistoryModal: Entry copied, showing toast");
ToastService.showInfo("Copied to clipboard");
}
function deleteEntry(entry) {
console.log("Deleting entry:", entry);
deleteProcess.command = ["sh", "-c", `echo '${entry.replace(/'/g, "'\\''")}' | cliphist delete`];
deleteProcess.running = true;
}
function clearAll() {
clearProcess.running = true;
}
function getEntryPreview(entry) {
let content = entry.replace(/^\s*\d+\s+/, "");
if (content.includes("image/") || content.includes("binary data") || /\.(png|jpg|jpeg|gif|bmp|webp)/i.test(content)) {
const dimensionMatch = content.match(/(\d+)x(\d+)/);
if (dimensionMatch)
return `Image ${dimensionMatch[1]}×${dimensionMatch[2]}`;
const typeMatch = content.match(/\b(png|jpg|jpeg|gif|bmp|webp)\b/i);
if (typeMatch)
return `Image (${typeMatch[1].toUpperCase()})`;
return "Image";
}
if (content.length > 100)
return content.substring(0, 100) + "...";
return content;
}
function getEntryType(entry) {
if (entry.includes("image/") || entry.includes("binary data") || /\.(png|jpg|jpeg|gif|bmp|webp)/i.test(entry) || /\b(png|jpg|jpeg|gif|bmp|webp)\b/i.test(entry))
return "image";
if (entry.length > 200)
return "long_text";
return "text";
}
// DankModal configuration
visible: isVisible
width: 650
height: 550
keyboardFocus: "ondemand"
onBackgroundClicked: {
hide();
}
// Clear confirmation dialog
DankModal {
id: clearConfirmDialog
visible: showClearConfirmation
width: 350
height: 150
keyboardFocus: "ondemand"
onBackgroundClicked: {
showClearConfirmation = false;
}
content: Component {
Item {
anchors.fill: parent
Column {
anchors.centerIn: parent
width: parent.width - Theme.spacingM * 2
spacing: Theme.spacingM
Text {
text: "Clear All History?"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
text: "This will permanently delete all clipboard history."
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
anchors.horizontalCenter: parent.horizontalCenter
wrapMode: Text.WordWrap
width: parent.width
horizontalAlignment: Text.AlignHCenter
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingM
Rectangle {
width: 100
height: 40
radius: Theme.cornerRadius
color: cancelClearButton.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
Text {
text: "Cancel"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
anchors.centerIn: parent
}
MouseArea {
id: cancelClearButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: showClearConfirmation = false
}
}
Rectangle {
width: 100
height: 40
radius: Theme.cornerRadius
color: confirmClearButton.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.9) : Theme.error
Text {
text: "Clear All"
font.pixelSize: Theme.fontSizeMedium
color: Theme.primaryText
font.weight: Font.Medium
anchors.centerIn: parent
}
MouseArea {
id: confirmClearButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
clearAll();
showClearConfirmation = false;
hide();
}
}
}
}
}
}
}
}
// Data models
ListModel {
id: clipboardModel
}
ListModel {
id: filteredClipboardModel
}
// Processes
Process {
id: clipboardProcess
command: ["cliphist", "list"]
running: false
stdout: StdioCollector {
onStreamFinished: {
clipboardModel.clear();
const lines = text.trim().split('\n');
for (const line of lines) {
if (line.trim().length > 0)
clipboardModel.append({
"entry": line
});
}
updateFilteredModel();
}
}
}
Process {
id: copyProcess
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.error("Copy failed with exit code:", exitCode);
}
}
Process {
id: deleteProcess
running: false
onExited: (exitCode) => {
if (exitCode === 0)
refreshClipboard();
else
console.error("Delete failed with exit code:", exitCode);
}
}
Process {
id: clearProcess
command: ["cliphist", "wipe"]
running: false
onExited: (exitCode) => {
if (exitCode === 0) {
clipboardModel.clear();
filteredClipboardModel.clear();
totalCount = 0;
} else {
console.error("Clear failed with exit code:", exitCode);
}
}
}
Process {
id: cleanupProcess
running: false
}
IpcHandler {
function open() {
console.log("ClipboardHistoryModal: IPC open() called");
clipboardHistoryModal.show();
return "CLIPBOARD_OPEN_SUCCESS";
}
function close() {
console.log("ClipboardHistoryModal: IPC close() called");
clipboardHistoryModal.hide();
return "CLIPBOARD_CLOSE_SUCCESS";
}
function toggle() {
console.log("ClipboardHistoryModal: IPC toggle() called");
clipboardHistoryModal.toggle();
return "CLIPBOARD_TOGGLE_SUCCESS";
}
target: "clipboard"
}
content: Component {
Column {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingS
// Header with search
Rectangle {
width: parent.width
height: 40
color: "transparent"
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: "content_paste"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: `Clipboard History (${totalCount})`
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankActionButton {
iconName: "delete_sweep"
iconSize: Theme.iconSize
iconColor: Theme.error
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
onClicked: {
showClearConfirmation = true;
}
}
DankActionButton {
iconName: "close"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
onClicked: hide()
}
}
}
// Search field
DankTextField {
id: searchField
width: parent.width
placeholderText: "Search clipboard history..."
leftIconName: "search"
showClearButton: true
onTextChanged: {
clipboardHistoryModal.searchText = text;
updateFilteredModel();
}
Connections {
function onOpened() {
searchField.forceActiveFocus();
}
function onDialogClosed() {
searchField.clearFocus();
}
target: clipboardHistoryModal
}
}
// Clipboard entries list
Rectangle {
width: parent.width
height: parent.height - 110
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.1)
clip: true
ScrollView {
anchors.fill: parent
anchors.margins: Theme.spacingS
clip: true
ScrollBar.vertical.policy: ScrollBar.AsNeeded
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ListView {
id: clipboardListView
width: parent.availableWidth
model: filteredClipboardModel
spacing: Theme.spacingXS
Text {
text: "No clipboard entries found"
anchors.centerIn: parent
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
visible: filteredClipboardModel.count === 0
}
delegate: Rectangle {
property string entryType: getEntryType(model.entry)
property string entryPreview: getEntryPreview(model.entry)
property int entryIndex: index + 1
width: clipboardListView.width
height: Math.max(60, contentText.contentHeight + Theme.spacingL)
radius: Theme.cornerRadius
color: mouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.04)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 1
Row {
anchors.fill: parent
anchors.margins: Theme.spacingM
anchors.rightMargin: Theme.spacingS // Reduced right margin
spacing: Theme.spacingL
// Index number
Rectangle {
width: 24
height: 24
radius: 12
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
anchors.verticalCenter: parent.verticalCenter
Text {
anchors.centerIn: parent
text: entryIndex.toString()
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Bold
color: Theme.primary
}
}
// Content icon and text
Row {
anchors.verticalCenter: parent.verticalCenter
width: parent.width - 68 // Account for index (24) + spacing (16) + delete button (32) - small margin
spacing: Theme.spacingM
DankIcon {
name: {
if (entryType === "image")
return "image";
if (entryType === "long_text")
return "subject";
return "content_copy";
}
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Column {
anchors.verticalCenter: parent.verticalCenter
width: parent.width - Theme.iconSize - Theme.spacingM
spacing: Theme.spacingXS
Text {
text: {
switch (entryType) {
case "image":
return "Image • " + entryPreview;
case "long_text":
return "Long Text";
default:
return "Text";
}
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.primary
font.weight: Font.Medium
width: parent.width
elide: Text.ElideRight
}
Text {
id: contentText
text: entryPreview
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
width: parent.width
wrapMode: Text.WordWrap
maximumLineCount: entryType === "long_text" ? 3 : 1
elide: Text.ElideRight
}
}
}
}
// Delete button
DankActionButton {
anchors.right: parent.right
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
iconName: "dangerous"
iconSize: Theme.iconSize - 4
iconColor: Theme.error
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
onClicked: {
console.log("Delete clicked for entry:", model.entry);
deleteEntry(model.entry);
}
}
// Main click area - explicitly excludes delete button area
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.rightMargin: 40 // Enough space to avoid delete button (32 + 8 margin)
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: copyEntry(model.entry)
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
}
}
}
}
}
}
}
}
}

View File

@@ -1,183 +0,0 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Widgets
DankModal {
id: root
property bool networkInfoDialogVisible: false
property string networkSSID: ""
property var networkData: null
property string networkDetails: ""
function showNetworkInfo(ssid, data) {
networkSSID = ssid;
networkData = data;
networkInfoDialogVisible = true;
WifiService.fetchNetworkInfo(ssid);
}
function hideDialog() {
networkInfoDialogVisible = false;
networkSSID = "";
networkData = null;
networkDetails = "";
}
visible: networkInfoDialogVisible
width: 600
height: 500
enableShadow: true
onBackgroundClicked: {
hideDialog();
}
onVisibleChanged: {
if (!visible) {
networkSSID = "";
networkData = null;
networkDetails = "";
}
}
content: Component {
Item {
anchors.fill: parent
Column {
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingL
// Header
Row {
width: parent.width
Column {
width: parent.width - 40
spacing: Theme.spacingXS
Text {
text: "Network Information"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
Text {
text: "Details for \"" + networkSSID + "\""
font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
width: parent.width
elide: Text.ElideRight
}
}
DankActionButton {
iconName: "close"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
onClicked: {
root.hideDialog();
}
}
}
// Network Details
ScrollView {
width: parent.width
height: parent.height - 140
clip: true
ScrollBar.vertical.policy: ScrollBar.AsNeeded
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
Flickable {
contentWidth: parent.width
contentHeight: detailsRect.height
Rectangle {
id: detailsRect
width: parent.width
height: Math.max(parent.parent.height, detailsText.contentHeight + Theme.spacingM * 2)
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 1
Text {
id: detailsText
anchors.fill: parent
anchors.margins: Theme.spacingM
text: WifiService.networkInfoDetails.replace(/\\n/g, '\n') || "No information available"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
wrapMode: Text.WordWrap
lineHeight: 1.5
}
}
}
}
// Close Button
Item {
width: parent.width
height: 40
Rectangle {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: Math.max(70, closeText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: closeArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
Text {
id: closeText
anchors.centerIn: parent
text: "Close"
font.pixelSize: Theme.fontSizeMedium
color: Theme.background
font.weight: Font.Medium
}
MouseArea {
id: closeArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.hideDialog();
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
}
}
}
}

View File

@@ -13,8 +13,8 @@ Rectangle {
property var networkData: null
property bool menuVisible: false
property var parentItem
property var wifiPasswordDialogRef
property var networkInfoDialogRef
property var wifiPasswordModalRef
property var networkInfoModalRef
function show(x, y) {
const menuWidth = 160;
@@ -123,10 +123,10 @@ Rectangle {
if (wifiContextMenuWindow.networkData.saved) {
WifiService.connectToWifi(wifiContextMenuWindow.networkData.ssid);
} else if (wifiContextMenuWindow.networkData.secured) {
if (wifiPasswordDialogRef) {
wifiPasswordDialogRef.wifiPasswordSSID = wifiContextMenuWindow.networkData.ssid;
wifiPasswordDialogRef.wifiPasswordInput = "";
wifiPasswordDialogRef.wifiPasswordDialogVisible = true;
if (wifiPasswordModalRef) {
wifiPasswordModalRef.wifiPasswordSSID = wifiContextMenuWindow.networkData.ssid;
wifiPasswordModalRef.wifiPasswordInput = "";
wifiPasswordModalRef.wifiPasswordModalVisible = true;
}
} else {
WifiService.connectToWifi(wifiContextMenuWindow.networkData.ssid);
@@ -248,8 +248,8 @@ Rectangle {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (wifiContextMenuWindow.networkData && networkInfoDialogRef) {
networkInfoDialogRef.showNetworkInfo(wifiContextMenuWindow.networkData.ssid, wifiContextMenuWindow.networkData);
if (wifiContextMenuWindow.networkData && networkInfoModalRef) {
networkInfoModalRef.showNetworkInfo(wifiContextMenuWindow.networkData.ssid, wifiContextMenuWindow.networkData);
}
wifiContextMenuWindow.hide();
}

View File

@@ -12,7 +12,7 @@ Column {
property var wifiContextMenuWindow
property var sortedWifiNetworks
property var wifiPasswordDialogRef
property var wifiPasswordModalRef
function getWiFiSignalIcon(signalStrength) {
switch (signalStrength) {
@@ -255,10 +255,10 @@ Column {
if (modelData.saved) {
WifiService.connectToWifi(modelData.ssid);
} else if (modelData.secured) {
if (wifiPasswordDialogRef) {
wifiPasswordDialogRef.wifiPasswordSSID = modelData.ssid;
wifiPasswordDialogRef.wifiPasswordInput = "";
wifiPasswordDialogRef.wifiPasswordDialogVisible = true;
if (wifiPasswordModalRef) {
wifiPasswordModalRef.wifiPasswordSSID = modelData.ssid;
wifiPasswordModalRef.wifiPasswordInput = "";
wifiPasswordModalRef.wifiPasswordModalVisible = true;
}
} else {
WifiService.connectToWifi(modelData.ssid);

View File

@@ -11,8 +11,8 @@ import qs.Modules.ControlCenter.Network
Item {
id: networkTab
property var wifiPasswordDialogRef: wifiPasswordDialog
property var networkInfoDialogRef: networkInfoDialog
property var wifiPasswordModalRef: wifiPasswordModal
property var networkInfoModalRef: networkInfoModal
// Properly sorted WiFi networks with connected networks first
property var sortedWifiNetworks: {
@@ -188,7 +188,7 @@ Item {
WiFiNetworksList {
wifiContextMenuWindow: wifiContextMenuWindow
sortedWifiNetworks: networkTab.sortedWifiNetworks
wifiPasswordDialogRef: networkTab.wifiPasswordDialogRef
wifiPasswordModalRef: networkTab.wifiPasswordModalRef
}
// Timer for refreshing network status after WiFi toggle
@@ -312,8 +312,8 @@ Item {
WiFiContextMenu {
id: wifiContextMenuWindow
parentItem: networkTab
wifiPasswordDialogRef: networkTab.wifiPasswordDialogRef
networkInfoDialogRef: networkTab.networkInfoDialogRef
wifiPasswordModalRef: networkTab.wifiPasswordModalRef
networkInfoModalRef: networkTab.networkInfoModalRef
}
// Background MouseArea to close the context menu

View File

@@ -12,7 +12,7 @@ import qs.Widgets
PanelWindow {
// For recents, use the recent apps from Prefs and filter out non-existent ones
id: launcher
id: appDrawerPopout
property bool isVisible: false
// App management
@@ -125,9 +125,9 @@ PanelWindow {
Prefs.addRecentApp(selectedApp.desktopEntry);
selectedApp.desktopEntry.execute();
} else {
launcher.launchApp(selectedApp.exec);
appDrawerPopout.launchApp(selectedApp.exec);
}
launcher.hide();
appDrawerPopout.hide();
}
}
@@ -145,7 +145,7 @@ PanelWindow {
}
function show() {
launcher.isVisible = true;
appDrawerPopout.isVisible = true;
searchField.enabled = true;
searchDebounceTimer.stop(); // Stop any pending search
updateFilteredModel();
@@ -153,14 +153,14 @@ PanelWindow {
function hide() {
searchField.enabled = false; // Disable before hiding to prevent Wayland warnings
launcher.isVisible = false;
appDrawerPopout.isVisible = false;
searchDebounceTimer.stop(); // Stop any pending search
searchField.text = "";
showCategories = false;
}
function toggle() {
if (launcher.isVisible)
if (appDrawerPopout.isVisible)
hide();
else
show();
@@ -207,13 +207,13 @@ PanelWindow {
Rectangle {
anchors.fill: parent
color: Qt.rgba(0, 0, 0, 0.3)
opacity: launcher.isVisible ? 1 : 0
visible: launcher.isVisible
opacity: appDrawerPopout.isVisible ? 1 : 0
visible: appDrawerPopout.isVisible
MouseArea {
anchors.fill: parent
enabled: launcher.isVisible
onClicked: launcher.hide()
enabled: appDrawerPopout.isVisible
onClicked: appDrawerPopout.hide()
}
Behavior on opacity {
@@ -273,66 +273,15 @@ PanelWindow {
height: 600
color: Theme.popupBackground()
radius: Theme.cornerRadiusXLarge
opacity: launcher.isVisible ? 1 : 0
// Animated entrance with spring effect
transform: [
Scale {
id: scaleTransform
origin.x: 0
origin.y: 0
xScale: launcher.isVisible ? 1 : 0.92
yScale: launcher.isVisible ? 1 : 0.92
Behavior on xScale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Easing.OutBack
easing.overshoot: 1.2
}
}
Behavior on yScale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Easing.OutBack
easing.overshoot: 1.2
}
}
},
Translate {
id: translateTransform
x: launcher.isVisible ? 0 : -30
y: launcher.isVisible ? 0 : -15
Behavior on x {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on y {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
opacity: appDrawerPopout.isVisible ? 1 : 0
x: appDrawerPopout.isVisible ? Theme.spacingL : Theme.spacingL - Anims.slidePx
y: Theme.barHeight + Theme.spacingXS
Behavior on x {
NumberAnimation {
duration: Anims.durMed
easing.type: Easing.OutCubic
}
]
anchors {
top: parent.top
left: parent.left
topMargin: Theme.barHeight + Theme.spacingXS
leftMargin: Theme.spacingL
}
// Material 3 elevation with multiple layers
@@ -370,14 +319,14 @@ PanelWindow {
anchors.fill: parent
focus: true
Component.onCompleted: {
if (launcher.isVisible)
if (appDrawerPopout.isVisible)
forceActiveFocus();
}
// Handle keyboard shortcuts
Keys.onPressed: function(event) {
if (event.key === Qt.Key_Escape) {
launcher.hide();
appDrawerPopout.hide();
event.accepted = true;
} else if (event.key === Qt.Key_Down) {
selectNext();
@@ -452,7 +401,7 @@ PanelWindow {
leftIconFocusedColor: Theme.primary
showClearButton: true
font.pixelSize: Theme.fontSizeLarge
enabled: launcher.isVisible
enabled: appDrawerPopout.isVisible
placeholderText: "Search applications..."
onTextEdited: {
searchDebounceTimer.restart();
@@ -465,9 +414,9 @@ PanelWindow {
Prefs.addRecentApp(firstApp.desktopEntry);
firstApp.desktopEntry.execute();
} else {
launcher.launchApp(firstApp.exec);
appDrawerPopout.launchApp(firstApp.exec);
}
launcher.hide();
appDrawerPopout.hide();
event.accepted = true;
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up || (event.key === Qt.Key_Left && viewMode === "grid") || (event.key === Qt.Key_Right && viewMode === "grid") || ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length === 0)) {
// Pass navigation keys and enter (when not searching) to main handler
@@ -477,13 +426,13 @@ PanelWindow {
Connections {
function onVisibleChanged() {
if (launcher.visible)
if (appDrawerPopout.visible)
searchField.forceActiveFocus();
else
searchField.clearFocus();
}
target: launcher
target: appDrawerPopout
}
}
@@ -621,9 +570,9 @@ PanelWindow {
Prefs.addRecentApp(modelData.desktopEntry);
modelData.desktopEntry.execute();
} else {
launcher.launchApp(modelData.exec);
appDrawerPopout.launchApp(modelData.exec);
}
launcher.hide();
appDrawerPopout.hide();
}
onItemHovered: function(index) {
selectedIndex = index;
@@ -646,9 +595,9 @@ PanelWindow {
Prefs.addRecentApp(modelData.desktopEntry);
modelData.desktopEntry.execute();
} else {
launcher.launchApp(modelData.exec);
appDrawerPopout.launchApp(modelData.exec);
}
launcher.hide();
appDrawerPopout.hide();
}
onItemHovered: function(index) {
selectedIndex = index;
@@ -768,10 +717,9 @@ PanelWindow {
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
duration: Anims.durShort
easing.type: Easing.OutCubic
}
}
}

View File

@@ -54,16 +54,18 @@ PanelWindow {
}
Rectangle {
width: Math.min(380, parent.width - Theme.spacingL * 2)
height: Math.min(450, parent.height - Theme.barHeight - Theme.spacingS * 2)
x: Math.max(Theme.spacingL, parent.width - width - Theme.spacingL)
readonly property real targetWidth: Math.min(380, Screen.width - Theme.spacingL * 2)
readonly property real targetHeight: Math.min(450, Screen.height - Theme.barHeight - Theme.spacingS * 2)
width: targetWidth
height: targetHeight
property real normalX: Math.max(Theme.spacingL, Screen.width - targetWidth - Theme.spacingL)
x: batteryPopupVisible ? normalX : normalX + Anims.slidePx
y: Theme.barHeight + Theme.spacingS
color: Theme.popupBackground()
radius: Theme.cornerRadiusLarge
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
opacity: batteryPopupVisible ? 1 : 0
scale: batteryPopupVisible ? 1 : 0.85
// Prevent click-through to background
MouseArea {
@@ -352,8 +354,8 @@ PanelWindow {
width: parent.width
height: 50
radius: Theme.cornerRadius
color: profileArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (batteryControlPopup.isActiveProfile(modelData) ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : "transparent"
color: profileArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (batteryPopout.isActiveProfile(modelData) ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: batteryPopout.isActiveProfile(modelData) ? Theme.primary : "transparent"
border.width: 2
Row {
@@ -365,7 +367,7 @@ PanelWindow {
DankIcon {
name: Theme.getPowerProfileIcon(modelData)
size: Theme.iconSize
color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
color: batteryPopout.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
@@ -376,8 +378,8 @@ PanelWindow {
Text {
text: Theme.getPowerProfileLabel(modelData)
font.pixelSize: Theme.fontSizeMedium
color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
font.weight: batteryControlPopup.isActiveProfile(modelData) ? Font.Medium : Font.Normal
color: batteryPopout.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
font.weight: batteryPopout.isActiveProfile(modelData) ? Font.Medium : Font.Normal
}
Text {
@@ -397,7 +399,7 @@ PanelWindow {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
batteryControlPopup.setProfile(modelData);
batteryPopout.setProfile(modelData);
}
}
@@ -461,18 +463,16 @@ PanelWindow {
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
duration: Anims.durShort
easing.type: Easing.OutCubic
}
}
Behavior on scale {
Behavior on x {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
duration: Anims.durMed
easing.type: Easing.OutCubic
}
}
}

View File

@@ -7,19 +7,33 @@ import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Modules.CentcomCenter
PanelWindow {
id: root
readonly property bool hasActiveMedia: MprisController.activePlayer !== null
property bool calendarVisible: false
property bool internalVisible: false
visible: calendarVisible
visible: internalVisible
onCalendarVisibleChanged: {
if (calendarVisible) {
internalVisible = true
Qt.callLater(() => {
// This ensures opacity changes after window is visible
internalVisible = true // Force re-trigger if needed
})
} else {
internalVisible = false
}
}
onVisibleChanged: {
if (visible && CalendarService)
CalendarService.loadCurrentMonth();
}
implicitWidth: 480
implicitHeight: 600
WlrLayershell.layer: WlrLayershell.Overlay
@@ -57,24 +71,31 @@ PanelWindow {
contentHeight += mainRowHeight + Theme.spacingM;
// Add events widget height - use calculated height instead of actual
if (CalendarService && CalendarService.khalAvailable) {
let hasEvents = eventsWidget.selectedDateEvents && eventsWidget.selectedDateEvents.length > 0;
let eventsHeight = hasEvents ? Math.min(300, 80 + eventsWidget.selectedDateEvents.length * 60) : 120;
let hasEvents = events.selectedDateEvents && events.selectedDateEvents.length > 0;
let eventsHeight = hasEvents ? Math.min(300, 80 + events.selectedDateEvents.length * 60) : 120;
contentHeight += eventsHeight;
}
return Math.min(contentHeight, parent.height * 0.9);
}
width: calculateWidth()
readonly property real targetWidth: Math.min(Screen.width * 0.9, 600)
width: targetWidth
height: calculateHeight()
x: (parent.width - width) / 2
y: Theme.barHeight + 4
color: Theme.surfaceContainer
radius: Theme.cornerRadiusLarge
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
layer.enabled: true
opacity: calendarVisible ? 1 : 0
scale: calendarVisible ? 1 : 0.92
x: (Screen.width - targetWidth) / 2
y: Theme.barHeight + 4
Behavior on opacity {
NumberAnimation {
duration: Anims.durShort
easing.type: Easing.OutCubic
}
}
Rectangle {
anchors.fill: parent
@@ -101,7 +122,6 @@ PanelWindow {
}
// Update height when calendar service events change
Connections {
function onEventsByDateChanged() {
mainContainer.height = mainContainer.calculateHeight();
@@ -115,16 +135,16 @@ PanelWindow {
enabled: CalendarService !== null
}
// Update height when events widget's selectedDateEvents changes
Connections {
function onSelectedDateEventsChanged() {
mainContainer.height = mainContainer.calculateHeight();
}
target: eventsWidget
enabled: eventsWidget !== null
target: events
enabled: events !== null
}
Column {
anchors.fill: parent
anchors.margins: Theme.spacingM
@@ -153,13 +173,13 @@ PanelWindow {
visible: hasAnyWidgets
anchors.top: parent.top
MediaPlayerWidget {
MediaPlayer {
visible: true // Always visible - shows placeholder when no media
width: parent.width
height: 160
}
WeatherWidget {
Weather {
visible: true // Always visible - shows placeholder when no weather
width: parent.width
height: 140
@@ -168,8 +188,8 @@ PanelWindow {
}
// Right section for calendar
CalendarWidget {
id: calendarWidget
CalendarGrid {
id: calendarGrid
width: leftWidgets.hasAnyWidgets ? parent.width * 0.55 - Theme.spacingL : parent.width
height: parent.height
@@ -178,11 +198,11 @@ PanelWindow {
}
// Full-width events widget below
EventsWidget {
id: eventsWidget
Events {
id: events
width: parent.width
selectedDate: calendarWidget.selectedDate
selectedDate: calendarGrid.selectedDate
}
}
@@ -196,35 +216,14 @@ PanelWindow {
shadowOpacity: 0.15
}
Behavior on opacity {
NumberAnimation {
duration: Theme.longDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on scale {
NumberAnimation {
duration: Theme.longDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on height {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
}
}
MouseArea {
anchors.fill: parent
z: -1
enabled: calendarVisible
onClicked: {
calendarVisible = false;
}

View File

@@ -1,4 +1,3 @@
import "../../Widgets"
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
@@ -10,6 +9,7 @@ import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.ControlCenter
PanelWindow {
id: root
@@ -48,86 +48,24 @@ PanelWindow {
}
Rectangle {
width: Math.min(600, Screen.width - Theme.spacingL * 2)
readonly property real targetWidth: Math.min(600, Screen.width - Theme.spacingL * 2)
width: targetWidth
height: root.powerOptionsExpanded ? 570 : 500
x: Math.max(Theme.spacingL, Screen.width - width - Theme.spacingL)
y: Theme.barHeight + Theme.spacingXS
color: Theme.popupBackground()
radius: Theme.cornerRadiusLarge
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
opacity: controlCenterVisible ? 1 : 0
// TopBar dropdown animation - optimized for performance
transform: [
Scale {
id: scaleTransform
property real normalX: Math.max(Theme.spacingL, Screen.width - targetWidth - Theme.spacingL)
x: controlCenterVisible ? normalX : normalX + Anims.slidePx
origin.x: 600 // Use fixed width since popup is max 600px wide
origin.y: 0
xScale: controlCenterVisible ? 1 : 0.95
yScale: controlCenterVisible ? 1 : 0.8
},
Translate {
id: translateTransform
x: controlCenterVisible ? 0 : 15 // Slide slightly left when hidden
y: controlCenterVisible ? 0 : -30
Behavior on x {
NumberAnimation {
duration: Anims.durMed
easing.type: Easing.OutCubic
}
]
// Single coordinated animation for better performance
states: [
State {
name: "visible"
when: controlCenterVisible
PropertyChanges {
target: scaleTransform
xScale: 1
yScale: 1
}
PropertyChanges {
target: translateTransform
x: 0
y: 0
}
},
State {
name: "hidden"
when: !controlCenterVisible
PropertyChanges {
target: scaleTransform
xScale: 0.95
yScale: 0.8
}
PropertyChanges {
target: translateTransform
x: 15
y: -30
}
}
]
transitions: [
Transition {
from: "*"
to: "*"
ParallelAnimation {
NumberAnimation {
targets: [scaleTransform, translateTransform]
properties: "xScale,yScale,x,y"
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
]
}
ColumnLayout {
anchors.fill: parent
@@ -720,10 +658,9 @@ PanelWindow {
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
duration: Anims.durShort
easing.type: Easing.OutCubic
}
}
}

View File

@@ -9,9 +9,10 @@ import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.ProcessList
PanelWindow {
id: processListDropdown
id: processListPopout
property bool isVisible: false
property var parentWidget: null
@@ -53,73 +54,33 @@ PanelWindow {
MouseArea {
anchors.fill: parent
onClicked: processListDropdown.hide()
onClicked: processListPopout.hide()
}
Rectangle {
id: dropdownContent
width: Math.min(600, Screen.width - Theme.spacingL * 2)
height: Math.min(600, Screen.height - Theme.barHeight - Theme.spacingS * 2)
x: Math.max(Theme.spacingL, Screen.width - width - Theme.spacingL)
readonly property real targetWidth: Math.min(600, Screen.width - Theme.spacingL * 2)
readonly property real targetHeight: Math.min(600, Screen.height - Theme.barHeight - Theme.spacingS * 2)
width: targetWidth
height: targetHeight
y: Theme.barHeight + Theme.spacingXS
radius: Theme.cornerRadiusLarge
color: Theme.popupBackground()
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
clip: true
opacity: processListDropdown.isVisible ? 1 : 0
opacity: processListPopout.isVisible ? 1 : 0
layer.enabled: true
transform: [
Scale {
id: scaleTransform
origin.x: dropdownContent.width * 0.85
origin.y: 0
xScale: processListDropdown.isVisible ? 1 : 0.95
yScale: processListDropdown.isVisible ? 1 : 0.8
Behavior on xScale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on yScale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
},
Translate {
id: translateTransform
x: processListDropdown.isVisible ? 0 : 20
y: processListDropdown.isVisible ? 0 : -30
Behavior on x {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on y {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
property real normalX: Math.max(Theme.spacingL, Screen.width - targetWidth - Theme.spacingL)
x: processListPopout.isVisible ? normalX : normalX + Anims.slidePx
Behavior on x {
NumberAnimation {
duration: Anims.durMed
easing.type: Easing.OutCubic
}
]
}
MouseArea {
@@ -157,15 +118,14 @@ PanelWindow {
shadowVerticalOffset: 8
shadowBlur: 1
shadowColor: Qt.rgba(0, 0, 0, 0.15)
shadowOpacity: processListDropdown.isVisible ? 0.15 : 0
shadowOpacity: processListPopout.isVisible ? 0.15 : 0
}
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
duration: Anims.durShort
easing.type: Easing.OutCubic
}
}
}

View File

@@ -1,185 +0,0 @@
import QtQuick
import QtQuick.Controls
import Quickshell.Io
import qs.Common
import qs.Widgets
DankModal {
id: root
property bool powerConfirmVisible: false
property string powerConfirmAction: ""
property string powerConfirmTitle: ""
property string powerConfirmMessage: ""
function executePowerAction(action) {
console.log("Executing power action:", action);
let command = [];
switch (action) {
case "logout":
command = ["niri", "msg", "action", "quit", "-s"];
break;
case "suspend":
command = ["systemctl", "suspend"];
break;
case "reboot":
command = ["systemctl", "reboot"];
break;
case "poweroff":
command = ["systemctl", "poweroff"];
break;
}
if (command.length > 0) {
powerActionProcess.command = command;
powerActionProcess.running = true;
}
}
// DankModal configuration
visible: powerConfirmVisible
width: 350
height: 160
keyboardFocus: "ondemand"
enableShadow: false
onBackgroundClicked: {
powerConfirmVisible = false;
}
Process {
id: powerActionProcess
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.error("Power action failed with exit code:", exitCode);
}
}
content: Component {
Item {
anchors.fill: parent
Column {
anchors.centerIn: parent
width: parent.width - Theme.spacingM * 2
spacing: Theme.spacingM
// Title
Text {
text: powerConfirmTitle
font.pixelSize: Theme.fontSizeLarge
color: {
switch (powerConfirmAction) {
case "poweroff":
return Theme.error;
case "reboot":
return Theme.warning;
default:
return Theme.surfaceText;
}
}
font.weight: Font.Medium
width: parent.width
horizontalAlignment: Text.AlignHCenter
}
// Message
Text {
text: powerConfirmMessage
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
width: parent.width
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
}
Item {
height: Theme.spacingS
}
// Buttons
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingM
// Cancel button
Rectangle {
width: 120
height: 40
radius: Theme.cornerRadius
color: cancelButton.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
Text {
text: "Cancel"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
anchors.centerIn: parent
}
MouseArea {
id: cancelButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
powerConfirmVisible = false;
}
}
}
// Confirm button
Rectangle {
width: 120
height: 40
radius: Theme.cornerRadius
color: {
let baseColor;
switch (powerConfirmAction) {
case "poweroff":
baseColor = Theme.error;
break;
case "reboot":
baseColor = Theme.warning;
break;
default:
baseColor = Theme.primary;
break;
}
return confirmButton.containsMouse ? Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 0.9) : baseColor;
}
Text {
text: "Confirm"
font.pixelSize: Theme.fontSizeMedium
color: Theme.primaryText
font.weight: Font.Medium
anchors.centerIn: parent
}
MouseArea {
id: confirmButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
powerConfirmVisible = false;
executePowerAction(powerConfirmAction);
}
}
}
}
}
}
}
}

View File

@@ -1,318 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Widgets
DankModal {
id: processListModal
property int currentTab: 0
property var tabNames: ["Processes", "Performance", "System"]
function show() {
processListModal.visible = true;
ProcessMonitorService.updateSystemInfo();
ProcessMonitorService.updateProcessList();
SystemMonitorService.enableDetailedMonitoring(true);
SystemMonitorService.updateSystemInfo();
UserInfoService.getUptime();
}
function hide() {
processListModal.visible = false;
SystemMonitorService.enableDetailedMonitoring(false);
}
function toggle() {
if (processListModal.visible)
hide();
else
show();
}
width: 900
height: 680
visible: false
keyboardFocus: "exclusive"
backgroundColor: Theme.popupBackground()
cornerRadius: Theme.cornerRadiusXLarge
enableShadow: true
onVisibleChanged: {
ProcessMonitorService.enableMonitoring(visible);
}
onBackgroundClicked: hide()
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;
}
}
ColumnLayout {
anchors.fill: parent
anchors.margins: Theme.spacingXL
spacing: Theme.spacingL
Row {
Layout.fillWidth: true
height: 40
spacing: Theme.spacingM
Text {
anchors.verticalCenter: parent.verticalCenter
text: "System Monitor"
font.pixelSize: Theme.fontSizeLarge + 4
font.weight: Font.Bold
color: Theme.surfaceText
}
Item {
width: parent.width - 280
height: 1
}
Text {
anchors.verticalCenter: parent.verticalCenter
text: ProcessMonitorService.processes.length + " processes"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
width: Math.min(implicitWidth, 120)
elide: Text.ElideRight
}
}
Rectangle {
Layout.fillWidth: true
height: 52
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.04)
radius: Theme.cornerRadiusLarge
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.06)
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.cornerRadiusLarge
color: currentTab === index ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : (tabMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent")
border.color: currentTab === index ? Theme.primary : "transparent"
border.width: currentTab === index ? 1 : 0
Row {
anchors.centerIn: parent
spacing: Theme.spacingS
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
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
}
}
}
Text {
text: modelData
font.pixelSize: Theme.fontSizeLarge
font.weight: currentTab === index ? Font.Bold : Font.Medium
color: currentTab === index ? Theme.primary : Theme.surfaceText
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
}
}
}
}
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
Loader {
id: processesTab
anchors.fill: parent
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
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
visible: currentTab === 2
opacity: currentTab === 2 ? 1 : 0
sourceComponent: systemTabComponent
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
}
}
}
}
Component {
id: processesTabComponent
ProcessesTab {
contextMenu: processContextMenuWindow
}
}
Component {
id: performanceTabComponent
PerformanceTab {}
}
Component {
id: systemTabComponent
SystemTab {}
}
ProcessContextMenu {
id: processContextMenuWindow
}
IpcHandler {
function open() {
processListModal.show();
return "PROCESSLIST_OPEN_SUCCESS";
}
function close() {
processListModal.hide();
return "PROCESSLIST_CLOSE_SUCCESS";
}
function toggle() {
processListModal.toggle();
return "PROCESSLIST_TOGGLE_SUCCESS";
}
target: "processlist"
}
}

View File

@@ -16,13 +16,16 @@ ScrollView {
Rectangle {
width: parent.width
height: 200
height: systemInfoColumn.implicitHeight + 2 * Theme.spacingL
radius: Theme.cornerRadiusLarge
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.6)
border.width: 0
Column {
anchors.fill: parent
id: systemInfoColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: Theme.spacingL
spacing: Theme.spacingL
@@ -95,7 +98,8 @@ ScrollView {
Text {
text: SystemMonitorService.motherboard
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
font.weight: Font.Medium
color: Theme.surfaceText
width: parent.width
elide: Text.ElideRight
}
@@ -118,7 +122,8 @@ ScrollView {
Text {
text: "BIOS " + SystemMonitorService.biosVersion
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
font.weight: Font.Medium
color: Theme.surfaceText
width: parent.width
elide: Text.ElideRight
}
@@ -134,13 +139,16 @@ ScrollView {
Rectangle {
width: parent.width
height: Math.max(180, diskMountRepeater.count * 28 + 100)
height: storageColumn.implicitHeight + 2 * Theme.spacingL
radius: Theme.cornerRadiusLarge
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.6)
border.width: 0
Column {
anchors.fill: parent
id: storageColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: Theme.spacingL
spacing: Theme.spacingS
@@ -173,16 +181,9 @@ ScrollView {
elide: Text.ElideRight
}
ScrollView {
Column {
width: parent.width
height: Math.max(120, diskMountRepeater.count * 28 + 50)
clip: true
ScrollBar.vertical.policy: ScrollBar.AsNeeded
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
Column {
width: parent.width
spacing: 2
spacing: 2
Row {
width: parent.width
@@ -338,8 +339,6 @@ ScrollView {
}
}
}
}

View File

@@ -1,163 +0,0 @@
import QtQuick
import QtQuick.Controls
import qs.Common
import qs.Widgets
DankModal {
id: settingsModal
property bool settingsVisible: false
signal closingModal()
onVisibleChanged: {
if (!visible)
closingModal();
}
// DankModal configuration
visible: settingsVisible
width: 650
height: 750
keyboardFocus: "ondemand"
enableShadow: true
onBackgroundClicked: {
settingsVisible = false;
}
// Keyboard focus and shortcuts
FocusScope {
anchors.fill: parent
focus: settingsModal.settingsVisible
Keys.onEscapePressed: settingsModal.settingsVisible = false
}
content: Component {
Column {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingS
// Header
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "settings"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: "Settings"
font.pixelSize: Theme.fontSizeXLarge
color: Theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: parent.width - 175 // Spacer to push close button to the right
height: 1
}
// Close button
DankActionButton {
circular: false
iconName: "close"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
onClicked: settingsModal.settingsVisible = false
}
}
// Settings sections
ScrollView {
id: settingsScrollView
width: parent.width
height: parent.height - 50
clip: true
ScrollBar.vertical.policy: ScrollBar.AsNeeded
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
Column {
id: settingsColumn
width: settingsScrollView.width - 20
spacing: Theme.spacingL
bottomPadding: Theme.spacingL
// Profile Settings
SettingsSection {
title: "Profile"
iconName: "person"
content: ProfileTab {
}
}
// Clock Settings
SettingsSection {
title: "Clock & Time"
iconName: "schedule"
content: ClockTab {
}
}
// Weather Settings
SettingsSection {
title: "Weather"
iconName: "wb_sunny"
content: WeatherTab {
}
}
// Widget Visibility Settings
SettingsSection {
title: "Top Bar Widgets"
iconName: "widgets"
content: WidgetsTab {
}
}
// Workspace Settings
SettingsSection {
title: "Workspaces"
iconName: "tab"
content: WorkspaceTab {
}
}
// Display Settings
SettingsSection {
title: "Display & Appearance"
iconName: "palette"
content: DisplayTab {
}
}
}
}
}
}
}

View File

@@ -1,579 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
import qs.Widgets
DankModal {
id: spotlightLauncher
property bool spotlightOpen: false
property var filteredApps: []
property int selectedIndex: 0
property int maxResults: 50
property var categories: {
var allCategories = AppSearchService.getAllCategories().filter((cat) => {
return cat !== "Education" && cat !== "Science";
});
// Insert "Recents" after "All"
var result = ["All", "Recents"];
return result.concat(allCategories.filter((cat) => {
return cat !== "All";
}));
}
property string selectedCategory: "All"
property string viewMode: Prefs.spotlightLauncherViewMode // "list" or "grid"
property int gridColumns: 4
function show() {
console.log("SpotlightLauncher: show() called");
spotlightOpen = true;
console.log("SpotlightLauncher: spotlightOpen set to", spotlightOpen);
searchDebounceTimer.stop(); // Stop any pending search
updateFilteredApps(); // Immediate update when showing
}
function hide() {
spotlightOpen = false;
searchDebounceTimer.stop(); // Stop any pending search
searchQuery = "";
selectedIndex = 0;
selectedCategory = "All";
updateFilteredApps();
}
function toggle() {
if (spotlightOpen)
hide();
else
show();
}
property string searchQuery: ""
function updateFilteredApps() {
filteredApps = [];
selectedIndex = 0;
var apps = [];
if (searchQuery.length === 0) {
// Show apps from category
if (selectedCategory === "All") {
// For "All" category, show all available apps
apps = AppSearchService.applications || [];
} else if (selectedCategory === "Recents") {
// For "Recents" category, get recent apps from Prefs and filter out non-existent ones
var recentApps = Prefs.getRecentApps();
apps = recentApps.map((recentApp) => {
return AppSearchService.getAppByExec(recentApp.exec);
}).filter((app) => {
return app !== null && !app.noDisplay;
});
} else {
// For specific categories, limit results
var categoryApps = AppSearchService.getAppsInCategory(selectedCategory);
apps = categoryApps.slice(0, maxResults);
}
} else {
// Search with category filter
if (selectedCategory === "All") {
// For "All" category, search all apps without limit
apps = AppSearchService.searchApplications(searchQuery);
} else if (selectedCategory === "Recents") {
// For "Recents" category, search within recent apps
var recentApps = Prefs.getRecentApps();
var recentDesktopEntries = recentApps.map((recentApp) => {
return AppSearchService.getAppByExec(recentApp.exec);
}).filter((app) => {
return app !== null && !app.noDisplay;
});
if (recentDesktopEntries.length > 0) {
var allSearchResults = AppSearchService.searchApplications(searchQuery);
var recentNames = new Set(recentDesktopEntries.map((app) => {
return app.name;
}));
// Filter search results to only include recent apps
apps = allSearchResults.filter((searchApp) => {
return recentNames.has(searchApp.name);
});
} else {
apps = [];
}
} else {
// For specific categories, filter search results by category
var categoryApps = AppSearchService.getAppsInCategory(selectedCategory);
if (categoryApps.length > 0) {
var allSearchResults = AppSearchService.searchApplications(searchQuery);
var categoryNames = new Set(categoryApps.map((app) => {
return app.name;
}));
// Filter search results to only include apps from the selected category
apps = allSearchResults.filter((searchApp) => {
return categoryNames.has(searchApp.name);
}).slice(0, maxResults);
} else {
apps = [];
}
}
}
// Convert to our format - batch operations for better performance
filteredApps = apps.map((app) => {
return ({
"name": app.name,
"exec": app.execString || "",
"icon": app.icon || "application-x-executable",
"comment": app.comment || "",
"categories": app.categories || [],
"desktopEntry": app
});
});
// Clear and repopulate model efficiently
filteredModel.clear();
filteredApps.forEach((app) => {
return filteredModel.append(app);
});
}
function launchApp(app) {
Prefs.addRecentApp(app);
if (app.desktopEntry) {
app.desktopEntry.execute();
} else {
var cleanExec = app.exec.replace(/%[fFuU]/g, "").trim();
console.log("Spotlight: Launching app directly:", cleanExec);
Quickshell.execDetached(["sh", "-c", cleanExec]);
}
hide();
}
function selectNext() {
if (filteredModel.count > 0) {
if (viewMode === "grid") {
// Grid navigation: move DOWN by one row (gridColumns positions)
var columnsCount = gridColumns;
var newIndex = Math.min(selectedIndex + columnsCount, filteredModel.count - 1);
selectedIndex = newIndex;
} else {
// List navigation: next item
selectedIndex = (selectedIndex + 1) % filteredModel.count;
}
}
}
function selectPrevious() {
if (filteredModel.count > 0) {
if (viewMode === "grid") {
// Grid navigation: move UP by one row (gridColumns positions)
var columnsCount = gridColumns;
var newIndex = Math.max(selectedIndex - columnsCount, 0);
selectedIndex = newIndex;
} else {
// List navigation: previous item
selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : filteredModel.count - 1;
}
}
}
function selectNextInRow() {
if (filteredModel.count > 0 && viewMode === "grid") {
// Grid navigation: move RIGHT by one position
selectedIndex = Math.min(selectedIndex + 1, filteredModel.count - 1);
}
}
function selectPreviousInRow() {
if (filteredModel.count > 0 && viewMode === "grid") {
// Grid navigation: move LEFT by one position
selectedIndex = Math.max(selectedIndex - 1, 0);
}
}
function launchSelected() {
if (filteredModel.count > 0 && selectedIndex >= 0 && selectedIndex < filteredModel.count) {
var selectedApp = filteredModel.get(selectedIndex);
launchApp(selectedApp);
}
}
// DankModal configuration
visible: spotlightOpen
width: 550
height: 600
keyboardFocus: "ondemand"
backgroundColor: Theme.popupBackground()
cornerRadius: Theme.cornerRadiusXLarge
borderColor: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
borderWidth: 1
enableShadow: true
onVisibleChanged: {
console.log("SpotlightLauncher visibility changed to:", visible);
if (visible && !spotlightOpen) {
show();
}
}
onBackgroundClicked: {
spotlightOpen = false;
}
Component.onCompleted: {
console.log("SpotlightLauncher: Component.onCompleted called - component loaded successfully!");
var allCategories = AppSearchService.getAllCategories().filter((cat) => {
return cat !== "Education" && cat !== "Science";
});
// Insert "Recents" after "All"
var result = ["All", "Recents"];
categories = result.concat(allCategories.filter((cat) => {
return cat !== "All";
}));
}
// Search debouncing
Timer {
id: searchDebounceTimer
interval: 50
repeat: false
onTriggered: updateFilteredApps()
}
ListModel {
id: filteredModel
}
content: Component {
Item {
anchors.fill: parent
focus: true
// Handle keyboard shortcuts
Keys.onPressed: function(event) {
if (event.key === Qt.Key_Escape) {
hide();
event.accepted = true;
} else if (event.key === Qt.Key_Down) {
selectNext();
event.accepted = true;
} else if (event.key === Qt.Key_Up) {
selectPrevious();
event.accepted = true;
} else if (event.key === Qt.Key_Right && viewMode === "grid") {
selectNextInRow();
event.accepted = true;
} else if (event.key === Qt.Key_Left && viewMode === "grid") {
selectPreviousInRow();
event.accepted = true;
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
launchSelected();
event.accepted = true;
} else if (event.text && event.text.length > 0 && event.text.match(/[a-zA-Z0-9\\s]/)) {
searchField.text = event.text;
searchField.forceActiveFocus();
event.accepted = true;
}
}
Column {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingM
// Combined row for categories and view mode toggle
Column {
width: parent.width
spacing: Theme.spacingM
visible: categories.length > 1 || filteredModel.count > 0
// Categories organized in 2 rows: 4 + 5
Column {
width: parent.width
spacing: Theme.spacingS
// Top row: All, Development, Graphics, Internet (4 items)
Row {
property var topRowCategories: ["All", "Recents", "Development", "Graphics"]
width: parent.width
spacing: Theme.spacingS
Repeater {
model: parent.topRowCategories.filter((cat) => {
return categories.includes(cat);
})
Rectangle {
height: 36
width: (parent.width - (parent.topRowCategories.length - 1) * Theme.spacingS) / parent.topRowCategories.length
radius: Theme.cornerRadiusLarge
color: selectedCategory === modelData ? Theme.primary : "transparent"
border.color: selectedCategory === modelData ? "transparent" : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
Text {
anchors.centerIn: parent
text: modelData
color: selectedCategory === modelData ? Theme.surface : Theme.surfaceText
font.pixelSize: Theme.fontSizeMedium
font.weight: selectedCategory === modelData ? Font.Medium : Font.Normal
elide: Text.ElideRight
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
selectedCategory = modelData;
updateFilteredApps();
}
}
}
}
}
// Bottom row: Media, Office, Settings, System, Utilities (5 items)
Row {
property var bottomRowCategories: ["Internet", "Media", "Office", "Settings", "System"]
width: parent.width
spacing: Theme.spacingS
Repeater {
model: parent.bottomRowCategories.filter((cat) => {
return categories.includes(cat);
})
Rectangle {
height: 36
width: (parent.width - (parent.bottomRowCategories.length - 1) * Theme.spacingS) / parent.bottomRowCategories.length
radius: Theme.cornerRadiusLarge
color: selectedCategory === modelData ? Theme.primary : "transparent"
border.color: selectedCategory === modelData ? "transparent" : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
Text {
anchors.centerIn: parent
text: modelData
color: selectedCategory === modelData ? Theme.surface : Theme.surfaceText
font.pixelSize: Theme.fontSizeMedium
font.weight: selectedCategory === modelData ? Font.Medium : Font.Normal
elide: Text.ElideRight
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
selectedCategory = modelData;
updateFilteredApps();
}
}
}
}
}
}
}
// Search field with view toggle buttons
Row {
width: parent.width
spacing: Theme.spacingM
DankTextField {
id: searchField
width: parent.width - 80 - Theme.spacingM // Leave space for view toggle buttons
height: 56
cornerRadius: Theme.cornerRadiusLarge
backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.7)
normalBorderColor: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
focusedBorderColor: Theme.primary
leftIconName: "search"
leftIconSize: Theme.iconSize
leftIconColor: Theme.surfaceVariantText
leftIconFocusedColor: Theme.primary
showClearButton: true
textColor: Theme.surfaceText
font.pixelSize: Theme.fontSizeLarge
enabled: spotlightOpen
placeholderText: "Search applications..."
text: searchQuery
onTextEdited: {
searchQuery = text;
searchDebounceTimer.restart();
}
Connections {
target: spotlightLauncher
function onOpened() {
searchField.forceActiveFocus();
}
function onDialogClosed() {
searchField.clearFocus();
}
}
Keys.onPressed: (event) => {
if (event.key === Qt.Key_Escape) {
hide();
event.accepted = true;
} else if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && searchQuery.length > 0) {
// Launch first app when typing in search field
if (filteredApps.length > 0) {
launchApp(filteredApps[0]);
}
event.accepted = true;
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up ||
(event.key === Qt.Key_Left && viewMode === "grid") ||
(event.key === Qt.Key_Right && viewMode === "grid") ||
((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && searchQuery.length === 0)) {
// Pass navigation keys and enter (when not searching) to main handler
event.accepted = false;
}
}
}
// View mode toggle buttons next to search bar
Row {
spacing: Theme.spacingXS
visible: filteredModel.count > 0
anchors.verticalCenter: parent.verticalCenter
// List view button
Rectangle {
width: 36
height: 36
radius: Theme.cornerRadiusLarge
color: viewMode === "list" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : listViewArea.containsMouse ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08) : "transparent"
border.color: viewMode === "list" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) : "transparent"
border.width: 1
DankIcon {
anchors.centerIn: parent
name: "view_list"
size: 18
color: viewMode === "list" ? Theme.primary : Theme.surfaceText
}
MouseArea {
id: listViewArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
viewMode = "list";
Prefs.setSpotlightLauncherViewMode("list");
}
}
}
// Grid view button
Rectangle {
width: 36
height: 36
radius: Theme.cornerRadiusLarge
color: viewMode === "grid" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : gridViewArea.containsMouse ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08) : "transparent"
border.color: viewMode === "grid" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) : "transparent"
border.width: 1
DankIcon {
anchors.centerIn: parent
name: "grid_view"
size: 18
color: viewMode === "grid" ? Theme.primary : Theme.surfaceText
}
MouseArea {
id: gridViewArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
viewMode = "grid";
Prefs.setSpotlightLauncherViewMode("grid");
}
}
}
}
}
// Results container
Rectangle {
id: resultsContainer
width: parent.width
height: parent.height - y // Use remaining space
color: "transparent"
// List view
DankListView {
id: resultsList
anchors.fill: parent
visible: viewMode === "list"
model: filteredModel
currentIndex: selectedIndex
itemHeight: 60
iconSize: 40
showDescription: true
hoverUpdatesSelection: false
onItemClicked: function(index, modelData) {
launchApp(modelData);
}
onItemHovered: function(index) {
selectedIndex = index;
}
}
// Grid view
DankGridView {
id: resultsGrid
anchors.fill: parent
visible: viewMode === "grid"
model: filteredModel
columns: 4
adaptiveColumns: false
minCellWidth: 120
maxCellWidth: 160
iconSizeRatio: 0.55
maxIconSize: 48
currentIndex: selectedIndex
hoverUpdatesSelection: false
onItemClicked: function(index, modelData) {
launchApp(modelData);
}
onItemHovered: function(index) {
selectedIndex = index;
}
}
}
}
}
}
IpcHandler {
function open() {
console.log("SpotlightLauncher: IPC open() called");
spotlightLauncher.show();
return "SPOTLIGHT_OPEN_SUCCESS";
}
function close() {
console.log("SpotlightLauncher: IPC close() called");
spotlightLauncher.hide();
return "SPOTLIGHT_CLOSE_SUCCESS";
}
function toggle() {
console.log("SpotlightLauncher: IPC toggle() called");
spotlightLauncher.toggle();
return "SPOTLIGHT_TOGGLE_SUCCESS";
}
target: "spotlight"
}
}

View File

@@ -5,7 +5,7 @@ import qs.Services
import qs.Widgets
Rectangle {
id: batteryWidget
id: battery
property bool batteryPopupVisible: false

View File

@@ -6,7 +6,7 @@ import qs.Services
import qs.Widgets
Rectangle {
id: cpuWidget
id: cpuMonitor
property bool showPercentage: true
property bool showIcon: true
@@ -24,7 +24,7 @@ Rectangle {
cursorShape: Qt.PointingHandCursor
onClicked: {
ProcessMonitorService.setSortBy("cpu");
processListDropdown.toggle();
processListPopout.toggle();
}
}

View File

@@ -6,7 +6,7 @@ import qs.Services
import qs.Widgets
Rectangle {
id: ramWidget
id: ramMonitor
property bool showPercentage: true
property bool showIcon: true
@@ -24,7 +24,7 @@ Rectangle {
cursorShape: Qt.PointingHandCursor
onClicked: {
ProcessMonitorService.setSortBy("memory");
processListDropdown.toggle();
processListPopout.toggle();
}
}

View File

@@ -133,9 +133,9 @@ PanelWindow {
LauncherButton {
anchors.verticalCenter: parent.verticalCenter
isActive: appLauncher.isVisible
isActive: appDrawerPopout.isVisible
onClicked: {
appLauncher.toggle();
appDrawerPopout.toggle();
}
}
@@ -144,41 +144,41 @@ PanelWindow {
screenName: root.screenName
}
FocusedAppWidget {
FocusedApp {
anchors.verticalCenter: parent.verticalCenter
visible: Prefs.showFocusedWindow
}
}
ClockWidget {
id: clockWidget
Clock {
id: clock
anchors.centerIn: parent
onClockClicked: {
centcomCenter.calendarVisible = !centcomCenter.calendarVisible;
centcomPopout.calendarVisible = !centcomPopout.calendarVisible;
}
}
MediaWidget {
Media {
anchors.verticalCenter: parent.verticalCenter
anchors.right: clockWidget.left
anchors.rightMargin: Theme.spacingS
visible: Prefs.showMusic && MprisController.activePlayer
onClicked: {
centcomCenter.calendarVisible = !centcomCenter.calendarVisible;
centcomPopout.calendarVisible = !centcomPopout.calendarVisible;
}
}
WeatherWidget {
id: weatherWidget
Weather {
id: weather
anchors.verticalCenter: parent.verticalCenter
anchors.left: clockWidget.right
anchors.left: clock.right
anchors.leftMargin: Theme.spacingS
visible: Prefs.showWeather && WeatherService.weather.available && WeatherService.weather.temp > 0 && WeatherService.weather.tempF > 0
onClicked: {
centcomCenter.calendarVisible = !centcomCenter.calendarVisible;
centcomPopout.calendarVisible = !centcomPopout.calendarVisible;
}
}
@@ -190,7 +190,7 @@ PanelWindow {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
SystemTrayWidget {
SystemTrayBar {
anchors.verticalCenter: parent.verticalCenter
visible: Prefs.showSystemTray
onMenuRequested: (menu, item, x, y) => {
@@ -240,12 +240,12 @@ PanelWindow {
}
// System Monitor Widgets
CpuMonitorWidget {
CpuMonitor {
anchors.verticalCenter: parent.verticalCenter
visible: Prefs.showSystemResources
}
RamMonitorWidget {
RamMonitor {
anchors.verticalCenter: parent.verticalCenter
visible: Prefs.showSystemResources
}
@@ -260,11 +260,11 @@ PanelWindow {
}
// Battery Widget
BatteryWidget {
Battery {
anchors.verticalCenter: parent.verticalCenter
batteryPopupVisible: batteryControlPopup.batteryPopupVisible
batteryPopupVisible: batteryPopout.batteryPopupVisible
onToggleBatteryPopup: {
batteryControlPopup.batteryPopupVisible = !batteryControlPopup.batteryPopupVisible;
batteryPopout.batteryPopupVisible = !batteryPopout.batteryPopupVisible;
}
}
@@ -272,10 +272,10 @@ PanelWindow {
// Bluetooth devices are automatically updated via signals
anchors.verticalCenter: parent.verticalCenter
isActive: controlCenter.controlCenterVisible
isActive: controlCenterPopout.controlCenterVisible
onClicked: {
controlCenter.controlCenterVisible = !controlCenter.controlCenterVisible;
if (controlCenter.controlCenterVisible) {
controlCenterPopout.controlCenterVisible = !controlCenterPopout.controlCenterVisible;
if (controlCenterPopout.controlCenterVisible) {
if (NetworkService.wifiEnabled)
WifiService.scanWifi();

View File

@@ -1,275 +0,0 @@
import QtQuick
import QtQuick.Controls
import qs.Common
import qs.Services
import qs.Widgets
DankModal {
id: root
property bool wifiPasswordDialogVisible: false
property string wifiPasswordSSID: ""
property string wifiPasswordInput: ""
visible: wifiPasswordDialogVisible
width: 420
height: 230
keyboardFocus: "ondemand"
onVisibleChanged: {
if (!visible)
wifiPasswordInput = "";
}
onBackgroundClicked: {
wifiPasswordDialogVisible = false;
wifiPasswordInput = "";
}
// Auto-reopen dialog on invalid password
Connections {
function onPasswordDialogShouldReopenChanged() {
if (WifiService.passwordDialogShouldReopen && WifiService.connectingSSID !== "") {
wifiPasswordSSID = WifiService.connectingSSID;
wifiPasswordInput = "";
wifiPasswordDialogVisible = true;
WifiService.passwordDialogShouldReopen = false;
}
}
target: WifiService
}
content: Component {
Item {
anchors.fill: parent
Column {
anchors.centerIn: parent
width: parent.width - Theme.spacingM * 2
spacing: Theme.spacingM
// Header
Row {
width: parent.width
Column {
width: parent.width - 40
spacing: Theme.spacingXS
Text {
text: "Connect to Wi-Fi"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
Text {
text: "Enter password for \"" + wifiPasswordSSID + "\""
font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
width: parent.width
elide: Text.ElideRight
}
}
DankActionButton {
iconName: "close"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
onClicked: {
wifiPasswordDialogVisible = false;
wifiPasswordInput = "";
}
}
}
// Password input
Rectangle {
width: parent.width
height: 50
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
border.color: passwordInput.activeFocus ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: passwordInput.activeFocus ? 2 : 1
DankTextField {
id: passwordInput
anchors.fill: parent
font.pixelSize: Theme.fontSizeMedium
textColor: Theme.surfaceText
text: wifiPasswordInput
echoMode: showPasswordCheckbox.checked ? TextInput.Normal : TextInput.Password
placeholderText: "Enter password"
backgroundColor: "transparent"
normalBorderColor: "transparent"
focusedBorderColor: "transparent"
onTextEdited: {
wifiPasswordInput = text;
}
onAccepted: {
WifiService.connectToWifiWithPassword(wifiPasswordSSID, passwordInput.text);
wifiPasswordDialogVisible = false;
wifiPasswordInput = "";
passwordInput.text = "";
}
Connections {
function onOpened() {
passwordInput.forceActiveFocus();
}
function onDialogClosed() {
passwordInput.clearFocus();
}
target: root
}
}
}
// Show password checkbox
Row {
spacing: Theme.spacingS
Rectangle {
id: showPasswordCheckbox
property bool checked: false
width: 20
height: 20
radius: 4
color: checked ? Theme.primary : "transparent"
border.color: checked ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.5)
border.width: 2
DankIcon {
anchors.centerIn: parent
name: "check"
size: 12
color: Theme.background
visible: parent.checked
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
showPasswordCheckbox.checked = !showPasswordCheckbox.checked;
}
}
}
Text {
text: "Show password"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
// Buttons
Item {
width: parent.width
height: 40
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
Rectangle {
width: Math.max(70, cancelText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: cancelArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 1
Text {
id: cancelText
anchors.centerIn: parent
text: "Cancel"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
}
MouseArea {
id: cancelArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
wifiPasswordDialogVisible = false;
wifiPasswordInput = "";
}
}
}
Rectangle {
width: Math.max(80, connectText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
enabled: passwordInput.text.length > 0
opacity: enabled ? 1 : 0.5
Text {
id: connectText
anchors.centerIn: parent
text: "Connect"
font.pixelSize: Theme.fontSizeMedium
color: Theme.background
font.weight: Font.Medium
}
MouseArea {
id: connectArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
enabled: parent.enabled
onClicked: {
WifiService.connectToWifiWithPassword(wifiPasswordSSID, passwordInput.text);
wifiPasswordDialogVisible = false;
wifiPasswordInput = "";
passwordInput.text = "";
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
}
}
}
}
}