diff --git a/quickshell/.editorconfig b/quickshell/.editorconfig new file mode 100644 index 0000000..35eb2cb --- /dev/null +++ b/quickshell/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{yml,yaml,json,qml}] +indent_size = 2 diff --git a/quickshell/components/Bar/Bar.qml b/quickshell/components/Bar/Bar.qml new file mode 100644 index 0000000..55fa817 --- /dev/null +++ b/quickshell/components/Bar/Bar.qml @@ -0,0 +1,45 @@ +import Quickshell +import QtQuick.Layouts +import QtQuick +import "../" + +PanelWindow { + SystemPalette { id: pal } + property bool top: true + + property real marginRight: 0 + property real marginLeft: 0 + + property alias left: leftContainer.data + property alias center: centerContainer.data + property alias right: rightContainer.data + + id: root + implicitHeight: 32 + + color: pal.window + + anchors { + top: root.top + bottom: !root.top + left: true + right: true + } + + BarSection { + id: leftContainer + anchors.left: parent.left + anchors.leftMargin: root.marginLeft + } + + BarSection { + id: centerContainer + anchors.horizontalCenter: parent.horizontalCenter + } + + BarSection { + id: rightContainer + anchors.right: parent.right + anchors.rightMargin: root.marginRight + } +} diff --git a/quickshell/components/Bar/BarSection.qml b/quickshell/components/Bar/BarSection.qml new file mode 100644 index 0000000..ed5d207 --- /dev/null +++ b/quickshell/components/Bar/BarSection.qml @@ -0,0 +1,7 @@ +import QtQuick +import QtQuick.Layouts + +RowLayout { + anchors.top: parent.top + anchors.bottom: parent.bottom +} diff --git a/quickshell/components/Button.qml b/quickshell/components/Button.qml new file mode 100644 index 0000000..4b8dd58 --- /dev/null +++ b/quickshell/components/Button.qml @@ -0,0 +1,72 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell.Widgets + +Rectangle { + property int paddingY: 4 + property int paddingX: 8 + property string text: "" + property string icon: "" + property string tooltip: "" + property string tooltipDescription: "" + property alias textColor: label.color + property alias acceptedButtons: mouseArea.acceptedButtons + readonly property alias hovered: mouseArea.containsMouse + property color backgroundColor: pal.button + property color backgroundHoverColor: pal.mid + + SystemPalette { id: pal } + + signal clicked(int button) + + id: root + implicitWidth: row.implicitWidth + (paddingX * 2) + implicitHeight: row.implicitHeight + (paddingY * 2) + + color: mouseArea.containsMouse + ? root.backgroundHoverColor + : root.backgroundColor + + Behavior on color { + ColorAnimation { + duration: 150 + } + } + + RowLayout { + id: row + anchors.centerIn: parent + + IconImage { + visible: root.icon.length > 0 + source: root.icon + implicitWidth: implicitHeight + implicitHeight: row.implicitHeight > 0 ? row.implicitHeight : 20 + } + + Text { + id: label + visible: root.text.length > 0 + text: root.text + } + } + + Loader { + active: tooltip.length > 0 || tooltipDescription.length > 0 + sourceComponent: Tooltip { + item: root + visible: mouseArea.containsMouse + title: tooltip + description: tooltipDescription + } + } + + MouseArea { + id: mouseArea + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + anchors.fill: root + acceptedButtons: Qt.LeftButton + onClicked: mouse => root.clicked(mouse.button) + } +} diff --git a/quickshell/components/Clock/Clock.qml b/quickshell/components/Clock/Clock.qml new file mode 100644 index 0000000..d3574b1 --- /dev/null +++ b/quickshell/components/Clock/Clock.qml @@ -0,0 +1,31 @@ +import QtQuick +import Quickshell + +Rectangle { + property int paddingY: 4 + property int paddingX: 4 + property string format: "hh:mm:ss\nyyyy-MM-dd" + property alias textColor: label.color + + id: root + width: label.implicitWidth + root.paddingX + height: label.implicitHeight + root.paddingY + color: "transparent" + + SystemPalette { id: pal } + + SystemClock { + id: clock + precision: SystemClock.Seconds + } + + Text { + id: label + anchors.centerIn: parent + text: Qt.formatDateTime(clock.date, root.format) + textFormat: Text.PlainText + horizontalAlignment: Text.AlignRight + color: pal.text + font.pointSize: 11 + } +} diff --git a/quickshell/components/Tooltip.qml b/quickshell/components/Tooltip.qml new file mode 100644 index 0000000..d674a38 --- /dev/null +++ b/quickshell/components/Tooltip.qml @@ -0,0 +1,47 @@ +import Quickshell +import QtQuick +import QtQuick.Layouts + +PopupWindow { + required property Item item + property alias title: title.text + property alias description: description.text + property int paddingX: 16 + property int paddingY: 8 + + id: root + + anchor { + item: root.item + gravity: Edges.Top + adjustment: PopupAdjustment.All + } + + color: root.visible ? pal.dark : "transparent" + implicitHeight: textContainer.implicitHeight + (root.paddingY * 2) + implicitWidth: textContainer.implicitWidth + (root.paddingX * 2) + SystemPalette { id: pal } + + Behavior on color { + ColorAnimation { + duration: 150 + } + } + + ColumnLayout { + id: textContainer + anchors.centerIn: parent + Text { + id: title + visible: text.length > 0 + color: root.visible ? pal.text : "transparent" + font.weight: Font.Bold + } + + Text { + id: description + visible: text.length > 0 + color: root.visible ? pal.text : "transparent" + } + } +} diff --git a/quickshell/components/Tray/Tray.qml b/quickshell/components/Tray/Tray.qml new file mode 100644 index 0000000..27232aa --- /dev/null +++ b/quickshell/components/Tray/Tray.qml @@ -0,0 +1,76 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Widgets +import Quickshell.Services.SystemTray +import ".." + +RowLayout { + id: root + spacing: 4 + property color backgroundColor: "transparent" + property color backgroundHoverColor: "transparent" + + Repeater { + model: SystemTray.items + TrayItem { + id: entry + required property SystemTrayItem modelData + readonly property var tooltipData: getTooltip(modelData) + + function getTooltip(item) { + // Quick hack for nextcloud + if (item.id == "Nextcloud") { + return [ + "Nextcloud", + item.tooltipTitle + ] + } + return [ + item.tooltipTitle || item.title, + item.tooltipDescription || "" + ] + } + + icon: modelData.icon + tooltip: tooltipData[0] + tooltipDescription: tooltipData[1] + backgroundColor: root.backgroundColor + backgroundHoverColor: root.backgroundHoverColor + + onClicked: button => { + switch (button) { + case Qt.LeftButton: + if (modelData.onlyMenu) { + menuAnchor.open() + } else { + modelData.activate() + } + break + + case Qt.RightButton: + if (modelData.hasMenu) { + menuAnchor.open() + } else { + modelData.secondaryActivate() + } + break + } + } + + QsMenuAnchor { + id: menuAnchor + menu: entry.modelData.menu + + anchor { + item: entry + + edges: Edges.Top | Edges.Left + gravity: Edges.Top | Edges.Right + + adjustment: PopupAdjustment.All + } + } + } + } +} diff --git a/quickshell/components/Tray/TrayItem.qml b/quickshell/components/Tray/TrayItem.qml new file mode 100644 index 0000000..1edfda6 --- /dev/null +++ b/quickshell/components/Tray/TrayItem.qml @@ -0,0 +1,8 @@ +import QtQuick +import "../../components" + +Button { + paddingY: 4 + paddingX: 4 + acceptedButtons: Qt.LeftButton | Qt.RightButton +} diff --git a/quickshell/components/WindowTitle/WindowTitle.qml b/quickshell/components/WindowTitle/WindowTitle.qml new file mode 100644 index 0000000..e4a2340 --- /dev/null +++ b/quickshell/components/WindowTitle/WindowTitle.qml @@ -0,0 +1,29 @@ +import Quickshell +import Quickshell.Hyprland +import QtQuick + +Item { + property int paddingY: 4 + property int paddingX: 4 + property alias bgColor: background.color + property alias color: label.color + id: root + + implicitWidth: label.implicitWidth + root.paddingX + implicitHeight: label.implicitHeight + root.paddingY + + SystemPalette { id: pal } + + Rectangle { + id: background + color: "transparent" + anchors.fill: parent + + Text { + id: label + anchors.centerIn: parent + text: Hyprland.activeToplevel?.title ?? "" + color: pal.text + } + } +} diff --git a/quickshell/components/Workspace/WorkspaceButton.qml b/quickshell/components/Workspace/WorkspaceButton.qml new file mode 100644 index 0000000..8c0be3e --- /dev/null +++ b/quickshell/components/Workspace/WorkspaceButton.qml @@ -0,0 +1,21 @@ +import QtQuick +import QtQuick.Layouts +import ".." + +Button { + property bool indicatorTop: true + property int indicatorHeight: 2 + property color indicatorColor + + id: root + + Rectangle { + anchors { + top: root.indicatorTop ? parent.top : undefined + bottom: !root.indicatorTop ? parent.buttom : undefined + } + implicitHeight: parent.indicatorHeight + implicitWidth: parent.implicitWidth + color: root.indicatorColor + } +} diff --git a/quickshell/components/Workspace/WorkspaceList.qml b/quickshell/components/Workspace/WorkspaceList.qml new file mode 100644 index 0000000..69524ce --- /dev/null +++ b/quickshell/components/Workspace/WorkspaceList.qml @@ -0,0 +1,41 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell.Hyprland + +RowLayout { + SystemPalette { id: pal } + + property color buttonTextColor: pal.text + property color buttonBackgroundColor: "transparent" + property color buttonIndicatorColor: "transparent" + property color buttonIndicatorFocusColor: pal.accent + property color buttonIndicatorUrgentColor: pal.highlightedText + + id: root + spacing: 0 + + Repeater { + model: Hyprland.workspaces + WorkspaceButton { + required property HyprlandWorkspace modelData + + Layout.fillHeight: true + + indicatorTop: false + text: modelData.name + implicitWidth: root.height + + color: root.buttonBackgroundColor + textColor: root.buttonTextColor + indicatorColor: modelData.urgent + ? root.buttonIndicatorUrgentColor + : ( + modelData.focused + ? root.buttonIndicatorFocusColor + : root.buttonIndicatorColor + ) + + onClicked: modelData.activate() + } + } +} diff --git a/quickshell/shell.qml b/quickshell/shell.qml new file mode 100644 index 0000000..3d2ed2e --- /dev/null +++ b/quickshell/shell.qml @@ -0,0 +1,46 @@ +//@ pragma UseQApplication + +import Quickshell +import QtQuick +import QtQuick.Layouts +import "./components/Bar" +import "./components/Workspace" +import "./components/Clock" +import "./components/WindowTitle" +import "./components/Tray" +import "./components" + +Scope { + id: root + + // Output names for the bar; use [] for all connected screens. + property list barScreens: ["DP-2"] + + Variants { + model: Quickshell.screens.filter(screen => + root.barScreens.length === 0 || root.barScreens.indexOf(screen.name) !== -1) + + Bar { + required property ShellScreen modelData + + id: bar + screen: modelData + top: false + implicitHeight: 40 + marginRight: 10 + + left: [ + WorkspaceList { } + ] + + center: [ + WindowTitle { } + ] + + right: [ + Tray { }, + Clock { } + ] + } + } +}