diff options
| author | Aargh Rai <aargh.rai+git@gmail.com> | 2025-12-08 18:38:07 +0530 |
|---|---|---|
| committer | Aargh Rai <aargh.rai+git@gmail.com> | 2025-12-08 18:38:07 +0530 |
| commit | c8ed34b938b589a682287ea98f19b341abddba13 (patch) | |
| tree | 1bdbaee6c46ee4272d50fdbb49c0c6ee396b3da0 /quickshell | |
| parent | bb1549a29921abdd75950b264333a80627e5b158 (diff) | |
added calendar and fixed app launcher bugs
Diffstat (limited to 'quickshell')
| -rw-r--r-- | quickshell/AppLauncher.qml | 402 | ||||
| -rw-r--r-- | quickshell/Calendar.qml | 143 | ||||
| -rw-r--r-- | quickshell/shell.qml | 66 |
3 files changed, 373 insertions, 238 deletions
diff --git a/quickshell/AppLauncher.qml b/quickshell/AppLauncher.qml index a1b5ab2..91deb1e 100644 --- a/quickshell/AppLauncher.qml +++ b/quickshell/AppLauncher.qml @@ -4,245 +4,207 @@ import Quickshell import Quickshell.Io Rectangle { - id: appLauncher - - width: 400 - height: 600 - color: shellRoot.black - radius: 16 - border.width: 3 - border.color: shellRoot.blue - antialiasing: true - - property bool isVisible: false - property int selectedIndex: -1 - property int hoverIndex: -1 - - signal requestClose() - - focus: true - - Keys.onEscapePressed: requestClose() - - Keys.onUpPressed: { - hoverIndex = -1 - if (selectedIndex === -1) { - selectedIndex = appListModel.count - 1 - } else if (selectedIndex > 0) { - selectedIndex-- - } - listView.positionViewAtIndex(selectedIndex, ListView.Contain) + id: appLauncher + + width: 400 + height: 600 + color: shellRoot.black + antialiasing: true + + property bool isVisible: false + property int hoverIndex: 0 + property bool cleared: true + + signal requestClose() + + focus: true + + Keys.onEscapePressed: requestClose() + + Keys.onUpPressed: { + hoverIndex-- + if (hoverIndex < 0) hoverIndex += appListModel.count + listView.positionViewAtIndex(hoverIndex, ListView.Contain) + } + + Keys.onDownPressed: { + hoverIndex = (hoverIndex + 1) % appListModel.count + listView.positionViewAtIndex(hoverIndex, ListView.Contain) + } + + Keys.onReturnPressed: { + if (hoverIndex >= 0 && hoverIndex < appListModel.count) { + launchApp(appListModel.get(hoverIndex).appCommand) } - - Keys.onDownPressed: { - hoverIndex = -1 - if (selectedIndex === -1) { - selectedIndex = 0 - } else if (selectedIndex < appListModel.count - 1) { - selectedIndex++ - } - listView.positionViewAtIndex(selectedIndex, ListView.Contain) + } + + Keys.onEnterPressed: { + if (hoverIndex >= 0 && hoverIndex < appListModel.count) { + launchApp(appListModel.get(hoverIndex).appCommand) } + } + + onIsVisibleChanged: { + if (!isVisible) return; + hoverIndex = -1 + appLauncher.forceActiveFocus() + loadApps() + } + + Process { + id: appLoader + running: false + command: ["sh", "-c", "~/.config/quickshell/scripts/list-apps.sh"] - Keys.onReturnPressed: { - if (selectedIndex >= 0 && selectedIndex < appListModel.count) { - launchApp(appListModel.get(selectedIndex).appCommand) + stdout: SplitParser { + onRead: data => { + if (!cleared) { + appListModel.clear() + cleared = true; } - } - - Keys.onEnterPressed: { - if (selectedIndex >= 0 && selectedIndex < appListModel.count) { - launchApp(appListModel.get(selectedIndex).appCommand) + + const lines = data.split('\n') + for (const line of lines) { + if (line.trim().length === 0) continue + + const parts = line.split('|') + if (parts.length < 4) continue; + appListModel.append({ + appName: parts[0], + appDescription: parts[1], + appIcon: parts[2], + appCommand: parts[3] + }) } + } } - // Reset selection when widget becomes visible - onIsVisibleChanged: { - if (isVisible) { - selectedIndex = -1 // Start with nothing selected - hoverIndex = -1 // Reset hover on open - appLauncher.forceActiveFocus() - // Reload apps every time the launcher opens - loadApps() - } + onRunningChanged: { + if (running) return; + appLoader.running = false } + } + + function loadApps() { + cleared = false; + appLoader.running = true + } + + Component.onCompleted: { + loadApps() + } + + Column { + anchors.fill: parent + anchors.margins: 12 + spacing: 8 - // Process to load apps - Process { - id: appLoader - running: false // Don't auto-start, we'll trigger it manually - command: ["sh", "-c", "~/.config/quickshell/scripts/list-apps.sh"] + Rectangle { + width: parent.width + height: 36 + color: "transparent" + + Text { + anchors.centerIn: parent + text: "Applications" + font.family: shellRoot.fontFamily + font.pixelSize: 20 + font.weight: Font.Medium + color: shellRoot.white + } + + Rectangle { + width: 28 + height: 28 + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + radius: 6 + color: closeMouseArea.containsMouse ? shellRoot.red : "transparent" - stdout: SplitParser { - onRead: data => { - const lines = data.split('\n') - for (const line of lines) { - if (line.trim().length === 0) continue - - const parts = line.split('|') - if (parts.length >= 4) { - appListModel.append({ - appName: parts[0], - appDescription: parts[1], - appIcon: parts[2], - appCommand: parts[3] - }) - } - } - console.log("Loaded", appListModel.count, "applications") - } + Text { + anchors.centerIn: parent + text: "✕" + font.family: shellRoot.fontFamily + font.pixelSize: 16 + font.weight: Font.Bold + color: closeMouseArea.containsMouse ? shellRoot.white : shellRoot.red } - onRunningChanged: { - // When process finishes, reset it for next run - if (!running) { - appLoader.running = false - } + MouseArea { + id: closeMouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: appLauncher.requestClose() } + } } - // Function to load/reload applications - function loadApps() { - // Clear existing apps - appListModel.clear() - // Start the process to reload - appLoader.running = true - } - - // Load apps initially when component is created - Component.onCompleted: { - loadApps() - } - - Column { - anchors.fill: parent - anchors.margins: 12 - spacing: 8 - - // Header - Rectangle { - width: parent.width - height: 36 - color: "transparent" - - Text { - anchors.centerIn: parent - text: "Applications" - font.family: shellRoot.fontFamily - font.pixelSize: 14 - font.weight: Font.Medium - color: shellRoot.white - } - - // Close button - Rectangle { - width: 28 - height: 28 - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - radius: 6 - color: closeMouseArea.containsMouse ? shellRoot.red : "transparent" - - Text { - anchors.centerIn: parent - text: "✕" - font.family: shellRoot.fontFamily - font.pixelSize: 16 - font.weight: Font.Bold - color: closeMouseArea.containsMouse ? shellRoot.red : shellRoot.orange - } - - MouseArea { - id: closeMouseArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: appLauncher.requestClose() - } - } + ListView { + id: listView + width: parent.width + height: parent.height - 44 + spacing: 8 + clip: true + + model: ListModel { + id: appListModel + } + + delegate: Rectangle { + width: listView.width + height: 30 + color: { + if (appLauncher.hoverIndex === index) return shellRoot.lightgray + return shellRoot.gray } - // Apps list - ListView { - id: listView - width: parent.width - height: parent.height - 44 - spacing: 8 - clip: true - - model: ListModel { - id: appListModel - // Apps will be loaded dynamically from the script - } - - delegate: Rectangle { - width: listView.width - height: 30 - color: { - if (appLauncher.hoverIndex === index) return shellRoot.lightgray - if (appLauncher.hoverIndex === -1 && index === shellRoot.selectedIndex) return shellRoot.lightgray - return shellRoot.gray - } - radius: 12 - border.width: 2 - border.color: { - if (appLauncher.hoverIndex === index || (shellRoot.hoverIndex === -1 && index === shellRoot.selectedIndex)) return shellRoot.blue - return "transparent" - } - - Row { - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 8 - anchors.bottomMargin: 8 - spacing: 12 - - Text { - width: parent.width - 62 - anchors.centerIn: parent - text: model.appName - font.family: shellRoot.fontFamily - font.pixelSize: 14 - font.weight: Font.Medium - color: { - if (appLauncher.hoverIndex === index) return shellRoot.blue - if (appLauncher.hoverIndex === -1 && index === shellRoot.selectedIndex) return shellRoot.blue - return shellRoot.purple - } - elide: Text.ElideRight - } - } - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - - onEntered: { - appLauncher.hoverIndex = index - } - onExited: { - appLauncher.hoverIndex = -1 - } - - onClicked: { - launchApp(model.appCommand) - } - } + Row { + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 8 + anchors.bottomMargin: 8 + spacing: 12 + + Text { + width: parent.width - 62 + anchors.centerIn: parent + text: model.appName + font.family: shellRoot.fontFamily + font.pixelSize: 14 + font.weight: Font.Medium + color: { + return shellRoot.white; } + elide: Text.ElideRight + } } - } - - function launchApp(command) { - console.log("Launching app:", command) - // Close the launcher first - appLauncher.requestClose() - // Use a small delay before launching to ensure the window closes properly - Qt.callLater(() => { - Quickshell.execDetached(["sh", "-c", command + " &"]) - }) + MouseArea { + id:ouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + + onEntered: { + appLauncher.hoverIndex = index + } + + onClicked: { + launchApp(model.appCommand) + } + } + } } + } + + function launchApp(command) { + appLauncher.requestClose() + + // Use a small delay before launching to ensure the window closes properly + Qt.callLater(() => { + Quickshell.execDetached(["sh", "-c", command + " &"]) + }) + } } diff --git a/quickshell/Calendar.qml b/quickshell/Calendar.qml new file mode 100644 index 0000000..0b79553 --- /dev/null +++ b/quickshell/Calendar.qml @@ -0,0 +1,143 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Io + +Rectangle { + id: calendarRoot + + color: shellRoot.black + antialiasing: true + focus: true + + property bool isVisible: false + property int monthOffset: 0 + + signal requestClose() + Keys.onEscapePressed: { + monthOffset = 0; + requestClose() + } + + Column { + anchors.fill: parent + spacing: 1 + Rectangle { + width: parent.width + height: parent.height + color: shellRoot.black + + Column { + anchors.fill: parent + anchors.margins: 5 + spacing: 6 + + RowLayout { + width: parent.width + Text { + text: " <" + font.pixelSize: 20 + font.family: shellRoot.fontFamily + color: shellRoot.white + MouseArea { + anchors.fill: parent + onClicked: monthOffset--; + } + } + Item { Layout.fillWidth: true } + Text { + text: { + const now = new Date() + const monthNames = ["January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"] + let shiftedMonth = now.getMonth() + monthOffset; + let year = now.getFullYear(); + year += Math.floor(shiftedMonth / 12); + shiftedMonth %= 12; + return monthNames[shiftedMonth] + " " + year + } + font.family: shellRoot.fontFamily + font.pixelSize: 20 + font.weight: Font.Medium + color: shellRoot.white + horizontalAlignment: Text.AlignHCenter + } + Item { Layout.fillWidth: true } + Text { + text: "> " + font.pixelSize: 20 + font.family: shellRoot.fontFamily + color: shellRoot.white + MouseArea { + anchors.fill: parent + onClicked: monthOffset++; + } + } + } + Grid { + width: parent.width + columns: 7 + columnSpacing: 1 + rowSpacing: 1 + + Repeater { + model: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"] + + Text { + text: modelData + font.family: shellRoot.fontFamily + font.pixelSize: 15 + font.weight: Font.Medium + color: shellRoot.white + width: 50 + height: 20 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + Repeater { + model: 35 + + Rectangle { + width: 60 + height: 60 + + property var now: new Date() + property int currentDay: now.getDate() + property int currentMonth: (now.getMonth() + monthOffset) % 12 + property int currentYear: now.getFullYear() + Math.floor((now.getMonth() + monthOffset) / 12) + + // Calculate what day this cell represents + property var firstDay: new Date(currentYear, currentMonth, 1) + property int startOffset: firstDay.getDay() // 0 = Sunday + property int dayNumber: index - startOffset + 1 + property var lastDay: new Date(currentYear, currentMonth + 1, 0) + property int daysInMonth: lastDay.getDate() + property bool isCurrentDay: dayNumber === currentDay + property bool isValidDay: dayNumber >= 1 && dayNumber <= daysInMonth + + color: { + if (!isValidDay) return "transparent" + if (monthOffset == 0 && isCurrentDay) return shellRoot.green + return shellRoot.gray + } + + Text { + anchors.centerIn: parent + text: parent.isValidDay ? parent.dayNumber : "" + font.family: shellRoot.fontFamily + font.pixelSize: 20 + color: { + if (parent.isValidDay && parent.isCurrentDay) return shellRoot.white + if (!parent.isValidDay) return shellRoot.white + return shellRoot.white + } + font.weight: parent.isValidDay && parent.isCurrentDay ? Font.DemiBold : Font.Normal + } + } + } + } + } + } + } +} diff --git a/quickshell/shell.qml b/quickshell/shell.qml index 307dece..f311380 100644 --- a/quickshell/shell.qml +++ b/quickshell/shell.qml @@ -25,25 +25,8 @@ ShellRoot { property int fontSize: 18 property bool appLauncherVisible: false + property bool calendarVisible: false - // focus: true - // Keys.onPressed: (event) => { - // if (event.key === Qt.Key_R && event.modifiers & Qt.MetaModifier) { - // Quickshell.execDetached(["notify-send", "ok"]) - // appLauncherVisible = true; - // } - // } - // Component.onCompleted: { - // Hyprland.rawSignals = true; - // } - // Connections { - // target: Hyprland - // - // function onEvent(sig) { - // Quickshell.execDetached(["notify-send", "ok"]) - // console.log("Raw = ", sig); - // } - // } GlobalShortcut { id: launcherShortcut name: "toggleLauncher" @@ -51,6 +34,13 @@ ShellRoot { appLauncherVisible = true } } + GlobalShortcut { + id: calendarShortcut + name: "toggleCalendar" + onPressed: { + calendarVisible = true + } + } Variants { model: Quickshell.screens @@ -197,4 +187,44 @@ ShellRoot { } } } + + Variants { + model: Quickshell.screens + + PanelWindow { + visible: shellRoot.calendarVisible + + anchors { + top: true + left: true + } + + margins { + top: 5 + left: Screen.width - 435 - 40 + } + + implicitWidth: 435 + implicitHeight: 370 + + color: "transparent" + exclusiveZone: 0 + + WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + + Behavior on height { + NumberAnimation { duration: 200; easing.type: Easing.OutCubic } + } + + Calendar { + anchors.fill: parent + isVisible: shellRoot.calendarVisible + + onRequestClose: { + shellRoot.calendarVisible = false + } + } + } + } } |
