diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/corporation.svg b/dots/.config/quickshell/ii/assets/icons/fluent/corporation.svg new file mode 100644 index 000000000..0ed1a0319 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/corporation.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/start-here-pressed.svg b/dots/.config/quickshell/ii/assets/icons/fluent/start-here-pressed.svg index e6b950eca..2efc81c45 100644 --- a/dots/.config/quickshell/ii/assets/icons/fluent/start-here-pressed.svg +++ b/dots/.config/quickshell/ii/assets/icons/fluent/start-here-pressed.svg @@ -1,24 +1,120 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg b/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg index 708d5a71b..9f7b4a177 100644 --- a/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg +++ b/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg @@ -1,24 +1,120 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 1716aa2ef..6331df0b1 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -153,6 +153,7 @@ Singleton { property JsonObject apps: JsonObject { property string bluetooth: "kcmshell6 kcm_bluetooth" + property string manageUser: "kcmshell6 kcm_users" property string network: "kitty -1 fish -c nmtui" property string networkEthernet: "kcmshell6 kcm_networkmanagement" property string taskManager: "plasma-systemmonitor --page-name Processes" diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml index 1d24170bc..7fe121165 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml @@ -7,6 +7,10 @@ import qs.services Singleton { id: root + function pathForName(iconName) { + return Quickshell.shellPath(`assets/icons/fluent/${iconName}.svg`); + } + function wifiIconForStrength(strength) { if (strength > 75) return "wifi-1"; diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml new file mode 100644 index 000000000..a208f355b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml @@ -0,0 +1,83 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Hyprland +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +Menu { + id: root + + property bool downDirection: false + property bool hasIcons: false // TODO: implement + + implicitWidth: background.implicitWidth + root.padding * 2 + implicitHeight: background.implicitHeight + root.padding * 2 + padding: 3 + property real sourceEdgeMargin: -implicitHeight + clip: true + + enter: Transition { + NumberAnimation { + property: "sourceEdgeMargin" + from: -root.implicitHeight + to: root.padding + duration: 200 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + } + exit: Transition { + NumberAnimation { + property: "sourceEdgeMargin" + from: root.padding + to: -root.implicitHeight + duration: 150 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut + } + } + + background: WPane { + anchors { + left: parent.left + right: parent.right + top: root.downDirection ? parent.top : undefined + bottom: root.downDirection ? undefined : parent.bottom + margins: root.padding + topMargin: root.downDirection ? root.sourceEdgeMargin : root.padding + bottomMargin: root.downDirection ? root.padding : root.sourceEdgeMargin + } + contentItem: Rectangle { + color: Looks.colors.bg1Base + implicitWidth: menuListView.implicitWidth + root.padding * 2 + implicitHeight: root.contentItem.implicitHeight + root.padding * 2 + } + } + + contentItem: ListView { + id: menuListView + anchors { + left: parent.left + right: parent.right + top: root.downDirection ? parent.top : undefined + bottom: root.downDirection ? undefined : parent.bottom + margins: root.padding * 2 + topMargin: root.downDirection ? root.sourceEdgeMargin : root.padding + bottomMargin: root.downDirection ? root.padding : root.sourceEdgeMargin + } + implicitHeight: contentHeight + implicitWidth: Array.from({ + length: count + }, (_, i) => itemAtIndex(i)?.implicitWidth ?? 0).reduce((a, b) => a > b ? a : b) + + model: root.contentModel + } + + delegate: WMenuItem { + id: menuItemDelegate + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml new file mode 100644 index 000000000..3030bc122 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml @@ -0,0 +1,93 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Hyprland +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +MenuItem { + id: root + + property color colBackground: ColorUtils.transparentize(Looks.colors.bg1) + property color colBackgroundHover: Looks.colors.bg2Hover + property color colBackgroundActive: Looks.colors.bg2Active + property color colBackgroundToggled: Looks.colors.accent + property color colBackgroundToggledHover: Looks.colors.accentHover + property color colBackgroundToggledActive: Looks.colors.accentActive + property color colForeground: Looks.colors.fg + property color colForegroundToggled: Looks.colors.accentFg + property color colForegroundDisabled: ColorUtils.transparentize(Looks.colors.subfg, 0.4) + property color color: { + if (!root.enabled) + return colBackground; + if (root.checked) { + if (root.down) { + return root.colBackgroundToggledActive; + } else if (root.hovered) { + return root.colBackgroundToggledHover; + } else { + return root.colBackgroundToggled; + } + } + if (root.down) { + return root.colBackgroundActive; + } else if (root.hovered) { + return root.colBackgroundHover; + } else { + return root.colBackground; + } + } + property color fgColor: { + if (root.checked) + return root.colForegroundToggled; + if (root.enabled) + return root.colForeground; + return root.colForegroundDisabled; + } + + property real inset: 2 + topInset: inset + bottomInset: inset + leftInset: inset + rightInset: inset + horizontalPadding: 11 + + background: Rectangle { + id: backgroundRect + radius: Looks.radius.medium + color: root.color + Behavior on color { + animation: Looks.transition.color.createObject(this) + } + } + + implicitHeight: Math.max(28, contentItem.implicitHeight) + topInset + bottomInset + implicitWidth: contentItem.implicitWidth + leftInset + rightInset + leftPadding + rightPadding + + contentItem: RowLayout { + id: contentLayout + spacing: 12 + FluentIcon { + id: buttonIcon + monochrome: true + implicitSize: 20 + Layout.fillWidth: false + Layout.alignment: Qt.AlignVCenter + color: root.fgColor + visible: root.icon.name !== ""; + icon: root.icon.name + } + WText { + id: buttonText + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + text: root.text + horizontalAlignment: Text.AlignLeft + font.pixelSize: Looks.font.pixelSize.large + color: root.fgColor + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml index be75cc30a..f281a8b7c 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml @@ -10,7 +10,7 @@ import qs.modules.waffle.looks Item { id: root - required property Item contentItem + property Item contentItem property real radius: Looks.radius.large property alias border: borderRect property alias borderColor: borderRect.border.color diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml index 3c8d20d26..7368836cc 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml @@ -25,6 +25,8 @@ StyledToolTip { verticalPadding: 8 horizontalPadding: 10 + delay: 400 + contentItem: WToolTipContent { id: tooltipContent realContentItem: root.realContentItem diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml index 13c78fc7e..b0a400df0 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml @@ -6,6 +6,7 @@ Item { id: root anchors.centerIn: parent required property Item realContentItem + property alias radius: realContent.radius property real verticalPadding: 8 property real horizontalPadding: 10 implicitWidth: realContent.implicitWidth + 2 * 2 diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WUserAvatar.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WUserAvatar.qml new file mode 100644 index 000000000..07499d51b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WUserAvatar.qml @@ -0,0 +1,27 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +StyledImage { + id: avatar + Layout.alignment: Qt.AlignTop + sourceSize: Qt.size(32, 32) + source: Directories.userAvatarPathAccountsService + fallbacks: [Directories.userAvatarPathRicersAndWeirdSystems, Directories.userAvatarPathRicersAndWeirdSystems2] + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Circle { + diameter: avatar.height + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml index 122469771..658bc03ae 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml @@ -7,6 +7,7 @@ import qs.modules.common.widgets import qs.modules.common.functions import qs.modules.waffle.looks +// TODO: Swipe to dismiss MouseArea { id: root diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml index 36e605187..eb3c96a80 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml @@ -4,7 +4,6 @@ import QtQuick.Controls import QtQuick.Layouts import Qt5Compat.GraphicalEffects import Quickshell -import org.kde.kirigami as Kirigami import qs import qs.services import qs.modules.common @@ -60,20 +59,8 @@ WPanelPageColumn { anchors.centerIn: parent spacing: 12 - StyledImage { - id: avatar - // Use this for free fallback because I'm lazy - Layout.alignment: Qt.AlignTop + WUserAvatar { sourceSize: Qt.size(32, 32) - source: Directories.userAvatarPathAccountsService - fallbacks: [Directories.userAvatarPathRicersAndWeirdSystems, Directories.userAvatarPathRicersAndWeirdSystems2] - - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Circle { - diameter: avatar.height - } - } } WText { Layout.alignment: Qt.AlignVCenter @@ -81,9 +68,116 @@ WPanelPageColumn { } } } + + onClicked: { + userMenu.open(); + } + + WToolTip { + text: SystemInfo.username + } + + Popup { + id: userMenu + x: -51 + y: -userMenu.implicitHeight + userButton.implicitHeight / 2 - 10 + + background: null + + WToolTipContent { + id: popupContent + horizontalPadding: 10 + verticalPadding: 7 + radius: Looks.radius.large + realContentItem: Item { + implicitWidth: userMenuContentLayout.implicitWidth + implicitHeight: userMenuContentLayout.implicitHeight + + ColumnLayout { + id: userMenuContentLayout + anchors { + fill: parent + leftMargin: popupContent.horizontalPadding + rightMargin: popupContent.horizontalPadding + topMargin: popupContent.verticalPadding + bottomMargin: popupContent.verticalPadding + } + spacing: 5 + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 6 + FluentIcon { + Layout.alignment: Qt.AlignVCenter + implicitSize: 22 + icon: "corporation" + monochrome: false + } + WText { + Layout.alignment: Qt.AlignVCenter + text: "Megahard" + font.pixelSize: Looks.font.pixelSize.large + font.weight: Looks.font.weight.strong + } + Item { Layout.fillWidth: true } + WBorderlessButton { + Layout.alignment: Qt.AlignVCenter + implicitHeight: 36 + implicitWidth: textItem.implicitWidth + 10 * 2 + contentItem: WText { + id: textItem + text: Translation.tr("Sign out") + font.pixelSize: Looks.font.pixelSize.large + } + onClicked: Session.logout() + } + } + Item { // Force min width 360 (using min on the item somehow doesn't work) + implicitWidth: 334 + } + RowLayout { + Layout.fillWidth: true + Layout.bottomMargin: 7 + Layout.leftMargin: 6 + spacing: 12 + WUserAvatar { + sourceSize: Qt.size(58, 58) + } + ColumnLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + spacing: 2 + WText { + text: SystemInfo.username + font.pixelSize: Looks.font.pixelSize.larger + font.weight: Looks.font.weight.strong + } + WText { + color: Looks.colors.fg1 + text: Translation.tr("Local account") + } + WText { + color: Looks.colors.accent + text: Translation.tr("Manage my account") + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Quickshell.execDetached(["bash", "-c", Config.options.apps.manageUser]) + GlobalStates.searchOpen = false; + } + } + } + } + } + } + } + } + } } component PowerButton: WBorderlessButton { + id: powerButton implicitWidth: 40 implicitHeight: 40 @@ -94,5 +188,40 @@ WPanelPageColumn { implicitSize: 20 } } + + WToolTip { + extraVisibleCondition: !powerMenu.visible + text: qsTr("Power") + } + + onClicked: { + powerMenu.open() + } + + WMenu { + id: powerMenu + x: -powerMenu.implicitWidth / 2 + powerButton.implicitWidth / 2 + y: -powerMenu.implicitHeight - 4 + Action { + icon.name: "lock-closed" + text: Translation.tr("Lock") + onTriggered: Session.lock() + } + Action { + icon.name: "weather-moon" + text: Translation.tr("Sleep") + onTriggered: Session.suspend() + } + Action { + icon.name: "power" + text: Translation.tr("Shut down") + onTriggered: Session.poweroff() + } + Action { + icon.name: "arrow-counterclockwise" + text: Translation.tr("Restart") + onTriggered: Session.reboot() + } + } } }