From 9fc966ff7726d01267a6220483fb005c0efaa9c0 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sun, 31 Aug 2025 12:12:23 +0200 Subject: ... --- .../shell/quickshell/ActiveWindowDisplay.qml | 93 ++++ accounts/gkleen@sif/shell/quickshell/Bar.qml | 507 +-------------------- accounts/gkleen@sif/shell/quickshell/Clock.qml | 186 ++++++++ .../gkleen@sif/shell/quickshell/KeyboardLayout.qml | 78 ++++ .../gkleen@sif/shell/quickshell/SystemTray.qml | 94 ++++ .../shell/quickshell/WorkspaceSwitcher.qml | 71 +++ 6 files changed, 528 insertions(+), 501 deletions(-) create mode 100644 accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml create mode 100644 accounts/gkleen@sif/shell/quickshell/Clock.qml create mode 100644 accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml create mode 100644 accounts/gkleen@sif/shell/quickshell/SystemTray.qml create mode 100644 accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml diff --git a/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml b/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml new file mode 100644 index 00000000..d7e8e7c5 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml @@ -0,0 +1,93 @@ +import QtQuick +import qs.Services +import Quickshell +import Quickshell.Widgets + +Item { + id: activeWindowDisplay + + required property int maxWidth + + property var activeWindow: { + let currWindowId = Array.from(NiriService.workspaces).find(ws => { + return ws.output === bar.screen.name && ws.is_active; + })?.active_window_id; + + return currWindowId ? Array.from(NiriService.windows).find(win => win.id == currWindowId) : null; + } + property var windowEntry: activeWindow ? DesktopEntries.heuristicLookup(activeWindow.app_id) : null + + anchors.verticalCenter: parent.verticalCenter + width: activeWindowDisplayContent.width + height: parent.height + + Row { + id: activeWindowDisplayContent + + width: childrenRect.width + height: parent.height + anchors.verticalCenter: parent.verticalCenter + spacing: 8 + + IconImage { + id: activeWindowIcon + + height: 14 + width: 14 + + anchors.verticalCenter: parent.verticalCenter + + source: { + let icon = activeWindowDisplay.windowEntry?.icon + if (typeof icon === 'string' || icon instanceof String) { + if (icon.includes("?path=")) { + const split = icon.split("?path=") + if (split.length !== 2) + return icon + const name = split[0] + const path = split[1] + const fileName = name.substring( + name.lastIndexOf("/") + 1) + return `file://${path}/${fileName}` + } else + icon = Quickshell.iconPath(icon); + return icon + } + return "" + } + asynchronous: true + smooth: true + mipmap: true + } + + Text { + id: windowTitle + + width: Math.min(implicitWidth, activeWindowDisplay.maxWidth - activeWindowIcon.width - activeWindowDisplayContent.spacing) + + property var appAliases: { "Firefox": "Mozilla Firefox", "mpv Media Player": "mpv", "Thunderbird": "Mozilla Thunderbird", "Thunderbird (LMU)": "Mozilla Thunderbird" } + + elide: Text.ElideRight + maximumLineCount: 1 + color: "white" + anchors.verticalCenter: parent.verticalCenter + text: { + if (!activeWindowDisplay.activeWindow) + return ""; + + var title = activeWindowDisplay.activeWindow.title; + var appName = activeWindowDisplay.windowEntry?.name; + if (appAliases[appName]) + appName = appAliases[appName]; + if (appName && title.endsWith(appName)) { + const oldTitle = title; + title = title.substring(0, title.length - appName.length); + title = title.replace(/\s*(—|-)\s*$/, ""); + if (!title) + title = oldTitle; + } + return title; + } + } + } +} diff --git a/accounts/gkleen@sif/shell/quickshell/Bar.qml b/accounts/gkleen@sif/shell/quickshell/Bar.qml index 9cb58b93..2f323858 100644 --- a/accounts/gkleen@sif/shell/quickshell/Bar.qml +++ b/accounts/gkleen@sif/shell/quickshell/Bar.qml @@ -1,13 +1,5 @@ import Quickshell -import Quickshell.Io -import Quickshell.Services.SystemTray -import Quickshell.Widgets -import Custom as Custom import QtQuick -import qs.Services -import QtQuick.Controls -import QtQuick.Layouts -import QtQml PanelWindow { @@ -46,74 +38,7 @@ PanelWindow { anchors.verticalCenter: parent.verticalCenter spacing: 8 - Row { - id: workspaces - - property var ignoreWorkspaces: @ignore_workspaces@ - - height: parent.height - anchors.verticalCenter: parent.verticalCenter - spacing: 0 - - Repeater { - model: { - let currWorkspaces = NiriService.workspaces; - const ignoreWorkspaces = Array.from(workspaces.ignoreWorkspaces); - currWorkspaces = currWorkspaces.filter(ws => ws.is_active || ignoreWorkspaces.every(iws => iws !== ws.name)); - currWorkspaces.sort((a, b) => { - if (NiriService.outputs?.[a.output]?.logical?.x !== NiriService.outputs?.[b.output]?.logical?.x) - return NiriService.outputs?.[a.output]?.logical?.x - NiriService.outputs?.[b.output]?.logical?.x - if (NiriService.outputs?.[a.output]?.logical?.y !== NiriService.outputs?.[b.output]?.logical?.y) - return NiriService.outputs?.[a.output]?.logical?.y - NiriService.outputs?.[b.output]?.logical?.y - return a.idx - b.idx; - }); - return currWorkspaces; - } - - Rectangle { - property var workspaceData: modelData - - width: wsLabel.contentWidth + 8 - color: { - if (mouseArea.containsMouse) { - return "#33808080"; - } - return "transparent"; - } - height: parent.height - anchors.verticalCenter: parent.verticalCenter - - MouseArea { - id: mouseArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - enabled: true - onClicked: { - NiriService.sendCommand({ "Action": { "FocusWorkspace": { "reference": { "Id": workspaceData.id } } } }, _ => {}) - } - } - - Text { - id: wsLabel - - font.pointSize: 10 - font.family: "Fira Sans" - color: { - if (workspaceData.is_active) - return "#23fd00"; - if (workspaceData.active_window_id === null) - return "#555"; - return "white"; - } - anchors.centerIn: parent - - text: workspaceData.name ? workspaceData.name : workspaceData.idx - } - } - } - } + WorkspaceSwitcher {} } Row { @@ -124,91 +49,8 @@ PanelWindow { anchors.centerIn: parent spacing: 5 - Item { - id: activeWindowDisplay - - property var activeWindow: { - let currWindowId = Array.from(NiriService.workspaces).find(ws => { - return ws.output === bar.screen.name && ws.is_active; - })?.active_window_id; - - return currWindowId ? Array.from(NiriService.windows).find(win => win.id == currWindowId) : null; - } - property var windowEntry: activeWindow ? DesktopEntries.heuristicLookup(activeWindow.app_id) : null - - anchors.verticalCenter: parent.verticalCenter - width: activeWindowDisplayContent.width - height: parent.height - - Row { - id: activeWindowDisplayContent - - width: childrenRect.width - height: parent.height - anchors.verticalCenter: parent.verticalCenter - spacing: 8 - - IconImage { - id: activeWindowIcon - - height: 14 - width: 14 - - anchors.verticalCenter: parent.verticalCenter - - source: { - let icon = activeWindowDisplay.windowEntry?.icon - if (typeof icon === 'string' || icon instanceof String) { - if (icon.includes("?path=")) { - const split = icon.split("?path=") - if (split.length !== 2) - return icon - const name = split[0] - const path = split[1] - const fileName = name.substring( - name.lastIndexOf("/") + 1) - return `file://${path}/${fileName}` - } else - icon = Quickshell.iconPath(icon); - return icon - } - return "" - } - asynchronous: true - smooth: true - mipmap: true - } - - Text { - id: windowTitle - - width: Math.min(implicitWidth, bar.width - 2*Math.max(left.width, right.width) - 2*8 - activeWindowIcon.width - activeWindowDisplayContent.spacing) - - property var appAliases: { "Firefox": "Mozilla Firefox", "mpv Media Player": "mpv", "Thunderbird": "Mozilla Thunderbird", "Thunderbird (LMU)": "Mozilla Thunderbird" } - - elide: Text.ElideRight - maximumLineCount: 1 - color: "white" - anchors.verticalCenter: parent.verticalCenter - text: { - if (!activeWindowDisplay.activeWindow) - return ""; - - var title = activeWindowDisplay.activeWindow.title; - var appName = activeWindowDisplay.windowEntry?.name; - if (appAliases[appName]) - appName = appAliases[appName]; - if (appName && title.endsWith(appName)) { - const oldTitle = title; - title = title.substring(0, title.length - appName.length); - title = title.replace(/\s*(—|-)\s*$/, ""); - if (!title) - title = oldTitle; - } - return title; - } - } - } + ActiveWindowDisplay { + maxWidth: bar.width - 2*Math.max(left.width, right.width) - 2*8 } } @@ -222,357 +64,20 @@ PanelWindow { anchors.verticalCenter: parent.verticalCenter spacing: 0 - Item { - anchors.verticalCenter: parent.verticalCenter - width: systemTrayRow.childrenRect.width - height: parent.height - clip: true - - Row { - id: systemTrayRow - anchors.centerIn: parent - width: childrenRect.width - height: parent.height - spacing: 0 - - Repeater { - model: SystemTray.items.values - - delegate: Item { - property var trayItem: modelData - property string iconSource: { - let icon = trayItem && trayItem.icon - if (typeof icon === 'string' || icon instanceof String) { - if (icon.includes("?path=")) { - const split = icon.split("?path=") - if (split.length !== 2) - return icon - const name = split[0] - const path = split[1] - const fileName = name.substring( - name.lastIndexOf("/") + 1) - return `file://${path}/${fileName}` - } - return icon - } - return "" - } - - width: 16 - height: parent.height - anchors.verticalCenter: parent.verticalCenter - - IconImage { - anchors.centerIn: parent - width: parent.width - height: parent.width - source: parent.iconSource - asynchronous: true - smooth: true - mipmap: true - } - - MouseArea { - id: trayItemArea - - anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: mouse => { - if (!trayItem) - return - - if (mouse.button === Qt.LeftButton - && !trayItem.onlyMenu) { - trayItem.activate() - return - } - - if (trayItem.hasMenu) { - var globalPos = mapToGlobal(0, 0) - var currentScreen = screen || Screen - var screenX = currentScreen.x || 0 - var relativeX = globalPos.x - screenX - menuAnchor.menu = trayItem.menu - menuAnchor.anchor.window = bar - menuAnchor.anchor.rect = Qt.rect( - relativeX, - 21, - parent.width, 1) - menuAnchor.open() - } - } - } - } - } - } - QsMenuAnchor { - id: menuAnchor - } - } + SystemTray {} Item { height: parent.height width: 4 } - Rectangle { - id: kbdWidget - - property var keyboardAbbrev: { "English (programmer Dvorak)": "dvp", "English (US)": "us" } - - width: kbdLabel.contentWidth + 8 - color: { - if (kbdMouseArea.containsMouse) { - return "#33808080"; - } - return "transparent"; - } - height: parent.height - anchors.verticalCenter: parent.verticalCenter - - MouseArea { - id: kbdMouseArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - enabled: true - onClicked: { - NiriService.sendCommand({ "Action": { "SwitchLayout": { "layout": "Next" } } }, _ => {}) - } - } - - Text { - id: kbdLabel - - font.pointSize: 10 - font.family: "Fira Sans" - color: { - if (NiriService.keyboardLayouts?.current_idx === 0) - return "#555"; - return "white"; - } - anchors.centerIn: parent - - text: { - const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx]; - if (!currentLayout) - return ""; - return kbdWidget.keyboardAbbrev[currentLayout] ? kbdWidget.keyboardAbbrev[currentLayout] : currentLayout; - } - } - - PopupWindow { - anchor { - item: kbdMouseArea - edges: Edges.Bottom - } - visible: kbdMouseArea.containsMouse - - implicitWidth: kbdTooltipText.contentWidth + 4 - implicitHeight: kbdTooltipText.contentHeight + 4 - color: "black" - - Text { - id: kbdTooltipText - - anchors.centerIn: parent - - font.pointSize: 10 - font.family: "Fira Sans" - color: "white" - - text: { - const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx]; - return currentLayout || ""; - } - } - } - } + KeyboardLayout {} Item { height: parent.height width: 4 } - Item { - width: clock.contentWidth - height: parent.height - anchors.verticalCenter: parent.verticalCenter - - MouseArea { - id: clockMouseArea - - anchors.fill: parent - hoverEnabled: true - enabled: true - } - - Text { - id: clock - color: "white" - - anchors.verticalCenter: parent.verticalCenter - - Custom.Chrono { - id: chrono - format: "W{0:%V-%u} {0:%F} {0:%H:%M:%S%Ez}" - } - - text: chrono.date - - font.pointSize: 10 - font.family: "Fira Sans" - font.features: { "tnum": 1 } - } - - PopupWindow { - anchor { - item: clockMouseArea - edges: Edges.Bottom - } - visible: clockMouseArea.containsMouse - - implicitWidth: yearCalendar.implicitWidth + 16 - implicitHeight: yearCalendar.implicitHeight + 16 - color: "black" - - GridLayout { - property int year: { const d = new Date(); return d.getFullYear(); } - - id: yearCalendar - - columns: 3 - columnSpacing: 16 - rowSpacing: 16 - - anchors.centerIn: parent - - Repeater { - model: 12 - - Column { - required property int index - property int month: index - - id: monthCalendar - - width: parent.width - - Text { - width: parent.width - - horizontalAlignment: Text.AlignHCenter - - font.pointSize: 10 - font.family: "Fira Sans" - - text: { - const date = Date.fromLocaleDateString(Qt.locale(), `${yearCalendar.year}-${monthCalendar.month + 1}-01`, "yyyy-M-dd"); - return date.toLocaleString(Qt.locale("en_DK"), "MMMM") - } - - color: "#ffead3" - } - - GridLayout { - columns: 2 - - DayOfWeekRow { - locale: grid.locale - - Layout.column: 1 - Layout.fillWidth: true - - delegate: Text { - required property string shortName - - font.pointSize: 10 - font.family: "Fira Mono" - - text: shortName - color: "#ffcc66" - - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - } - } - - WeekNumberColumn { - month: grid.month - year: grid.year - locale: grid.locale - - Layout.fillHeight: true - - delegate: Text { - required property int weekNumber - - opacity: { - const simple = new Date(weekNumber == 1 && monthCalendar.month == 12 ? yearCalendar.year + 1 : yearCalendar.year, 0, 1 + (weekNumber - 1) * 7); - const dayOfWeek = simple.getDay(); - const isoWeekStart = simple; - - isoWeekStart.setDate(simple.getDate() - dayOfWeek + 1); - if (dayOfWeek > 4) { - isoWeekStart.setDate(isoWeekStart.getDate() + 7); - } - - for (let i = 0; i < 7; i++) { - const dayInWeek = isoWeekStart; - dayInWeek.setDate(dayInWeek.getDate() + i); - if (dayInWeek.getMonth() == monthCalendar.month) - return 1; - } - - return 0; - } - - font.pointSize: 10 - font.family: "Fira Sans" - font.features: { "tnum": 1 } - - text: weekNumber - color: "#99ffdd" - - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - } - } - - MonthGrid { - id: grid - - year: yearCalendar.year - month: monthCalendar.month - locale: Qt.locale("en_DK") - - Layout.fillWidth: true - Layout.fillHeight: true - - delegate: Text { - required property var model - - opacity: model.month === monthCalendar.month ? 1 : 0 - - font.pointSize: 10 - font.family: "Fira Sans" - font.features: { "tnum": 1 } - - text: model.day - color: model.today ? "#ff6699" : "white" - - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - } - } - } - } - } - } - } - } + Clock {} } } \ No newline at end of file diff --git a/accounts/gkleen@sif/shell/quickshell/Clock.qml b/accounts/gkleen@sif/shell/quickshell/Clock.qml new file mode 100644 index 00000000..56f8d41f --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Clock.qml @@ -0,0 +1,186 @@ +import QtQml +import QtQuick +import Quickshell +import Custom as Custom +import QtQuick.Controls +import QtQuick.Layouts + +Item { + width: clock.contentWidth + height: parent.height + anchors.verticalCenter: parent.verticalCenter + + MouseArea { + id: clockMouseArea + + anchors.fill: parent + hoverEnabled: true + enabled: true + } + + Text { + id: clock + color: "white" + + anchors.verticalCenter: parent.verticalCenter + + Custom.Chrono { + id: chrono + format: "W{0:%V-%u} {0:%F} {0:%H:%M:%S%Ez}" + } + + text: chrono.date + + font.pointSize: 10 + font.family: "Fira Sans" + font.features: { "tnum": 1 } + } + + PopupWindow { + anchor { + item: clockMouseArea + edges: Edges.Bottom + } + visible: clockMouseArea.containsMouse + + implicitWidth: yearCalendar.implicitWidth + 16 + implicitHeight: yearCalendar.implicitHeight + 16 + color: "black" + + GridLayout { + property int year: { const d = new Date(); return d.getFullYear(); } + + id: yearCalendar + + columns: 3 + columnSpacing: 16 + rowSpacing: 16 + + anchors.centerIn: parent + + Repeater { + model: 12 + + Column { + required property int index + property int month: index + + id: monthCalendar + + width: parent.width + + Layout.alignment: Qt.AlignTop | Qt.AlignRight + + Text { + width: parent.width + + horizontalAlignment: Text.AlignHCenter + + font.pointSize: 10 + font.family: "Fira Sans" + + text: { + const date = Date.fromLocaleDateString(Qt.locale(), `${yearCalendar.year}-${monthCalendar.month + 1}-01`, "yyyy-M-dd"); + return date.toLocaleString(Qt.locale("en_DK"), "MMMM") + } + + color: "#ffead3" + } + + GridLayout { + columns: 2 + + DayOfWeekRow { + locale: grid.locale + + Layout.column: 1 + Layout.fillWidth: true + + delegate: Text { + required property string shortName + + font.pointSize: 10 + font.family: "Fira Mono" + + text: shortName + color: "#ffcc66" + + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + } + } + + WeekNumberColumn { + month: grid.month + year: grid.year + locale: grid.locale + + Layout.fillHeight: true + + delegate: Text { + required property int weekNumber + + opacity: { + const simple = new Date(weekNumber == 1 && monthCalendar.month == 12 ? yearCalendar.year + 1 : yearCalendar.year, 0, 1 + (weekNumber - 1) * 7); + const dayOfWeek = simple.getDay(); + const isoWeekStart = simple; + + isoWeekStart.setDate(simple.getDate() - dayOfWeek + 1); + if (dayOfWeek > 4) { + isoWeekStart.setDate(isoWeekStart.getDate() + 7); + } + + for (let i = 0; i < 7; i++) { + const dayInWeek = isoWeekStart; + dayInWeek.setDate(dayInWeek.getDate() + i); + if (dayInWeek.getMonth() == monthCalendar.month) + return 1; + } + + return 0; + } + + font.pointSize: 10 + font.family: "Fira Sans" + font.features: { "tnum": 1 } + + text: weekNumber + color: "#99ffdd" + + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + } + } + + MonthGrid { + id: grid + + year: yearCalendar.year + month: monthCalendar.month + locale: Qt.locale("en_DK") + + Layout.fillWidth: true + Layout.fillHeight: true + + delegate: Text { + required property var model + + opacity: model.month === monthCalendar.month ? 1 : 0 + + font.pointSize: 10 + font.family: "Fira Sans" + font.features: { "tnum": 1 } + + text: model.day + color: model.today ? "#ff6699" : "white" + + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml b/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml new file mode 100644 index 00000000..4d3bd180 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml @@ -0,0 +1,78 @@ +import Quickshell +import QtQuick +import qs.Services + +Rectangle { + id: kbdWidget + + property var keyboardAbbrev: { "English (programmer Dvorak)": "dvp", "English (US)": "us" } + + width: kbdLabel.contentWidth + 8 + color: { + if (kbdMouseArea.containsMouse) { + return "#33808080"; + } + return "transparent"; + } + height: parent.height + anchors.verticalCenter: parent.verticalCenter + + MouseArea { + id: kbdMouseArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + enabled: true + onClicked: { + NiriService.sendCommand({ "Action": { "SwitchLayout": { "layout": "Next" } } }, _ => {}) + } + } + + Text { + id: kbdLabel + + font.pointSize: 10 + font.family: "Fira Sans" + color: { + if (NiriService.keyboardLayouts?.current_idx === 0) + return "#555"; + return "white"; + } + anchors.centerIn: parent + + text: { + const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx]; + if (!currentLayout) + return ""; + return kbdWidget.keyboardAbbrev[currentLayout] ? kbdWidget.keyboardAbbrev[currentLayout] : currentLayout; + } + } + + PopupWindow { + anchor { + item: kbdMouseArea + edges: Edges.Bottom + } + visible: kbdMouseArea.containsMouse + + implicitWidth: kbdTooltipText.contentWidth + 4 + implicitHeight: kbdTooltipText.contentHeight + 4 + color: "black" + + Text { + id: kbdTooltipText + + anchors.centerIn: parent + + font.pointSize: 10 + font.family: "Fira Sans" + color: "white" + + text: { + const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx]; + return currentLayout || ""; + } + } + } +} diff --git a/accounts/gkleen@sif/shell/quickshell/SystemTray.qml b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml new file mode 100644 index 00000000..a02a81fd --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml @@ -0,0 +1,94 @@ +import QtQuick +import Quickshell +import Quickshell.Widgets +import Quickshell.Services.SystemTray + +Item { + anchors.verticalCenter: parent.verticalCenter + width: systemTrayRow.childrenRect.width + height: parent.height + clip: true + + Row { + id: systemTrayRow + anchors.centerIn: parent + width: childrenRect.width + height: parent.height + spacing: 0 + + Repeater { + model: SystemTray.items.values + + delegate: Item { + property var trayItem: modelData + property string iconSource: { + let icon = trayItem && trayItem.icon + if (typeof icon === 'string' || icon instanceof String) { + if (icon.includes("?path=")) { + const split = icon.split("?path=") + if (split.length !== 2) + return icon + const name = split[0] + const path = split[1] + const fileName = name.substring( + name.lastIndexOf("/") + 1) + return `file://${path}/${fileName}` + } + return icon + } + return "" + } + + width: 16 + height: parent.height + anchors.verticalCenter: parent.verticalCenter + + IconImage { + anchors.centerIn: parent + width: parent.width + height: parent.width + source: parent.iconSource + asynchronous: true + smooth: true + mipmap: true + } + + MouseArea { + id: trayItemArea + + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: mouse => { + if (!trayItem) + return + + if (mouse.button === Qt.LeftButton + && !trayItem.onlyMenu) { + trayItem.activate() + return + } + + if (trayItem.hasMenu) { + var globalPos = mapToGlobal(0, 0) + var currentScreen = screen || Screen + var screenX = currentScreen.x || 0 + var relativeX = globalPos.x - screenX + menuAnchor.menu = trayItem.menu + menuAnchor.anchor.window = bar + menuAnchor.anchor.rect = Qt.rect( + relativeX, + 21, + parent.width, 1) + menuAnchor.open() + } + } + } + } + } + } + QsMenuAnchor { + id: menuAnchor + } +} diff --git a/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml b/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml new file mode 100644 index 00000000..153c56bb --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml @@ -0,0 +1,71 @@ +import QtQuick +import qs.Services + +Row { + id: workspaces + + property var ignoreWorkspaces: @ignore_workspaces@ + + height: parent.height + anchors.verticalCenter: parent.verticalCenter + spacing: 0 + + Repeater { + model: { + let currWorkspaces = NiriService.workspaces; + const ignoreWorkspaces = Array.from(workspaces.ignoreWorkspaces); + currWorkspaces = currWorkspaces.filter(ws => ws.is_active || ignoreWorkspaces.every(iws => iws !== ws.name)); + currWorkspaces.sort((a, b) => { + if (NiriService.outputs?.[a.output]?.logical?.x !== NiriService.outputs?.[b.output]?.logical?.x) + return NiriService.outputs?.[a.output]?.logical?.x - NiriService.outputs?.[b.output]?.logical?.x + if (NiriService.outputs?.[a.output]?.logical?.y !== NiriService.outputs?.[b.output]?.logical?.y) + return NiriService.outputs?.[a.output]?.logical?.y - NiriService.outputs?.[b.output]?.logical?.y + return a.idx - b.idx; + }); + return currWorkspaces; + } + + Rectangle { + property var workspaceData: modelData + + width: wsLabel.contentWidth + 8 + color: { + if (mouseArea.containsMouse) { + return "#33808080"; + } + return "transparent"; + } + height: parent.height + anchors.verticalCenter: parent.verticalCenter + + MouseArea { + id: mouseArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + enabled: true + onClicked: { + NiriService.sendCommand({ "Action": { "FocusWorkspace": { "reference": { "Id": workspaceData.id } } } }, _ => {}) + } + } + + Text { + id: wsLabel + + font.pointSize: 10 + font.family: "Fira Sans" + color: { + if (workspaceData.is_active) + return "#23fd00"; + if (workspaceData.active_window_id === null) + return "#555"; + return "white"; + } + anchors.centerIn: parent + + text: workspaceData.name ? workspaceData.name : workspaceData.idx + } + } + } +} \ No newline at end of file -- cgit v1.2.3