// Config created by Keyitdev https://git.rp1.hu/gabeszm/sddm-rave-theme // Copyright (C) 2022-2025 Keyitdev // Distributed under the GPLv3+ License https://www.gnu.org/licenses/gpl-3.0.html import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Effects 6.5 Item { id: userSwitcher // Csak akkor látható ha több mint 1 felhasználó van visible: userModel.count > 1 width: switchBtn.implicitWidth + root.font.pointSize * 2 height: switchBtn.implicitHeight + root.font.pointSize * 0.6 // Kiválasztott felhasználó indexe (kívülről beállítható) property int selectedIndex: 0 property string selectedName: { if (userModel.count < 1) return "" var n = userModel.data(userModel.index(selectedIndex, 0), Qt.UserRole + 1) return n ? n : "" } signal userSwitched(int idx, string name) // ── Segédfüggvények ─────────────────────────────────────────────────────────────────── function resolveUserIcon(idx) { var list = [] var count = (typeof userModel !== "undefined" && userModel) ? userModel.count : 0 if (count === 0 || idx < 0 || idx >= count) return list var qIdx = userModel.index(idx, 0) var directIcon = userModel.data(qIdx, Qt.UserRole + 4) || "" var home = userModel.data(qIdx, Qt.UserRole + 3) || "" var uName = userModel.data(qIdx, Qt.UserRole + 1) || "" function addCandidate(p) { if (!p || p === "") return var s = p.toString().trim() if (s === "") return var url = (s.startsWith("file://") || s.startsWith("image://") || s.startsWith("qrc://") || s.startsWith("http")) ? s : ("file://" + s) if (list.indexOf(url) === -1) { list.push(url) } } if (directIcon !== "") addCandidate(directIcon) if (home !== "") { addCandidate(home + "/.face.icon") addCandidate(home + "/.face") } if (uName !== "") { addCandidate("/var/lib/AccountsService/icons/" + uName) addCandidate("/var/lib/AccountsService/icons/" + uName + ".png") addCandidate("/home/" + uName + "/.face.icon") addCandidate("/home/" + uName + "/.face") } return list } function getUserName(idx) { return userModel.data(userModel.index(idx, 0), Qt.UserRole + 1) || "" } // ── Gomb (chip stílus) ──────────────────────────────────────────────────── Rectangle { id: switchBtn anchors.centerIn: parent implicitWidth: btnRow.implicitWidth + root.font.pointSize * 1.6 implicitHeight: btnRow.implicitHeight + root.font.pointSize * 0.7 radius: implicitHeight / 2 color: btnArea.pressed ? Qt.rgba(1, 1, 1, 0.18) : btnArea.containsMouse ? Qt.rgba(1, 1, 1, 0.12) : Qt.rgba(1, 1, 1, 0.07) border.color: btnArea.containsMouse ? Qt.rgba(1, 1, 1, 0.45) : Qt.rgba(1, 1, 1, 0.22) border.width: root.font.pointSize * 0.08 Behavior on color { ColorAnimation { duration: 150 } } Behavior on border.color{ ColorAnimation { duration: 150 } } Row { id: btnRow anchors.centerIn: parent spacing: root.font.pointSize * 0.5 // Mini avatar kör Rectangle { id: miniAvatarCircle width: root.font.pointSize * 1.6 height: root.font.pointSize * 1.6 radius: width / 2 clip: true color: Qt.rgba(1, 1, 1, 0.15) anchors.verticalCenter: parent.verticalCenter property var miniCandidates: userSwitcher.resolveUserIcon(userSwitcher.selectedIndex) property int miniAttempt: 0 Connections { target: userSwitcher function onSelectedIndexChanged() { miniAvatarCircle.miniCandidates = userSwitcher.resolveUserIcon(userSwitcher.selectedIndex) miniAvatarCircle.miniAttempt = 0 if (miniAvatarCircle.miniCandidates.length > 0) { miniPhoto.source = miniAvatarCircle.miniCandidates[0] } else { miniPhoto.source = "" } } } Item { id: miniMask anchors.fill: parent visible: false layer.enabled: true Rectangle { anchors.fill: parent radius: parent.width / 2 color: "black" } } Image { id: miniPhoto anchors.fill: parent source: miniAvatarCircle.miniCandidates.length > 0 ? miniAvatarCircle.miniCandidates[0] : "" fillMode: Image.PreserveAspectCrop smooth: true visible: status === Image.Ready layer.enabled: true layer.effect: MultiEffect { maskEnabled: true maskSource: miniMask } onStatusChanged: { if (status === Image.Error && miniAvatarCircle.miniAttempt < miniAvatarCircle.miniCandidates.length - 1) { miniAvatarCircle.miniAttempt++ miniPhoto.source = miniAvatarCircle.miniCandidates[miniAvatarCircle.miniAttempt] } } } Image { anchors.centerIn: parent width: parent.width * 0.70 height: parent.height * 0.70 source: Qt.resolvedUrl("../Assets/User.svg") fillMode: Image.PreserveAspectFit visible: miniPhoto.status !== Image.Ready opacity: 0.80 } } // Felhasználónév Text { anchors.verticalCenter: parent.verticalCenter text: userSwitcher.selectedName color: "#ffffff" style: Text.Raised styleColor: Qt.rgba(0, 0, 0, 0.40) font.pointSize: root.font.pointSize * 0.82 font.family: root.font.family } // Nyíl ikon Text { anchors.verticalCenter: parent.verticalCenter text: "⌄" color: Qt.rgba(1, 1, 1, 0.70) font.pointSize: root.font.pointSize * 0.85 font.family: root.font.family // Csak ha több user van, akkor látszik a nyíl visible: userModel.count > 1 rotation: popup.visible ? 180 : 0 Behavior on rotation { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } } } MouseArea { id: btnArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor // Csak akkor nyit popup-ot ha több felhasználó van onClicked: if (userModel.count > 1) { popup.visible ? popup.close() : popup.open() } } } // ── Felhasználó-lista popup ─────────────────────────────────────────────── Popup { id: popup parent: Overlay.overlay x: (parent.width - width) / 2 y: (parent.height - height) / 2 width: root.font.pointSize * 18 padding: root.font.pointSize * 0.6 modal: true focus: true closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside enter: Transition { NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 200; easing.type: Easing.OutCubic } NumberAnimation { property: "scale"; from: 0.92; to: 1; duration: 200; easing.type: Easing.OutCubic } } exit: Transition { NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 160; easing.type: Easing.InCubic } } background: Rectangle { radius: config.RoundCorners !== "" ? parseInt(config.RoundCorners) : root.font.pointSize * 1.2 color: config.BackgroundColor border.color: Qt.rgba(1, 1, 1, 0.18) border.width: root.font.pointSize * 0.08 } // Fejléc Column { width: parent.width spacing: root.font.pointSize * 0.4 Text { anchors.horizontalCenter: parent.horizontalCenter text: "Felhasználó váltás" color: Qt.rgba(1, 1, 1, 0.55) font.pointSize: root.font.pointSize * 0.75 font.family: root.font.family bottomPadding: root.font.pointSize * 0.2 } // Elválasztó vonal Rectangle { width: parent.width height: root.font.pointSize * 0.08 color: Qt.rgba(1, 1, 1, 0.12) } // Felhasználók listája ListView { id: userList width: parent.width height: Math.min(contentHeight, root.font.pointSize * 22) model: userModel clip: true spacing: root.font.pointSize * 0.15 boundsBehavior: Flickable.StopAtBounds ScrollIndicator.vertical: ScrollIndicator { } delegate: Rectangle { id: delegateRect width: userList.width height: root.font.pointSize * 4 radius: config.RoundCorners !== "" ? parseInt(config.RoundCorners) / 2 : root.font.pointSize * 0.6 // Kiemelés: aktív felhasználó property bool isActive: index === userSwitcher.selectedIndex color: isActive ? Qt.rgba(1, 1, 1, 0.12) : delegateMouse.containsMouse ? Qt.rgba(1, 1, 1, 0.07) : "transparent" Behavior on color { ColorAnimation { duration: 120 } } Row { anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: root.font.pointSize * 0.7 spacing: root.font.pointSize * 0.8 // Avatar kör Rectangle { id: delegateAvatarCircle width: root.font.pointSize * 3 height: root.font.pointSize * 3 radius: width / 2 clip: true color: Qt.rgba(0.12, 0.12, 0.20, 0.80) anchors.verticalCenter: parent.verticalCenter property var delegateCandidates: userSwitcher.resolveUserIcon(index) property int delegateAttempt: 0 Item { id: delegateMask anchors.fill: parent visible: false layer.enabled: true Rectangle { anchors.fill: parent radius: parent.width / 2 color: "black" } } Image { id: delegatePhoto anchors.fill: parent source: delegateAvatarCircle.delegateCandidates.length > 0 ? delegateAvatarCircle.delegateCandidates[0] : "" fillMode: Image.PreserveAspectCrop smooth: true visible: status === Image.Ready layer.enabled: true layer.effect: MultiEffect { maskEnabled: true maskSource: delegateMask } onStatusChanged: { if (status === Image.Error && delegateAvatarCircle.delegateAttempt < delegateAvatarCircle.delegateCandidates.length - 1) { delegateAvatarCircle.delegateAttempt++ delegatePhoto.source = delegateAvatarCircle.delegateCandidates[delegateAvatarCircle.delegateAttempt] } } } Image { anchors.centerIn: parent width: parent.width * 0.65 height: parent.height * 0.65 source: Qt.resolvedUrl("../Assets/User.svg") fillMode: Image.PreserveAspectFit visible: delegatePhoto.status !== Image.Ready opacity: 0.75 } } // Felhasználónév Text { anchors.verticalCenter: parent.verticalCenter text: userSwitcher.getUserName(index) color: delegateRect.isActive ? "#ffffff" : Qt.rgba(1,1,1,0.80) font.pointSize: root.font.pointSize * 0.88 font.family: root.font.family font.bold: delegateRect.isActive } } // Aktív jelző csík a bal oldalon Rectangle { visible: delegateRect.isActive width: root.font.pointSize * 0.23 height: parent.height * 0.55 radius: root.font.pointSize * 0.15 color: Qt.rgba(74/255, 222/255, 128/255, 0.90) anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: root.font.pointSize * 0.15 } MouseArea { id: delegateMouse anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { userSwitcher.selectedIndex = index userSwitcher.userSwitched(index, userSwitcher.getUserName(index)) popup.close() } } } } } } }