mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-26 06:22:50 -05:00
mecha refactoring
This commit is contained in:
@@ -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()
|
||||
@@ -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
|
||||
@@ -8,7 +8,7 @@ import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: mediaPlayerWidget
|
||||
id: mediaPlayer
|
||||
|
||||
property MprisPlayer activePlayer: MprisController.activePlayer
|
||||
property string lastValidTitle: ""
|
||||
@@ -6,7 +6,7 @@ import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: weatherWidget
|
||||
id: weather
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: batteryWidget
|
||||
id: battery
|
||||
|
||||
property bool batteryPopupVisible: false
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user