From 69e54a72c47189eaa07e27c020ff23c9caf06379 Mon Sep 17 00:00:00 2001 From: Nippy Date: Mon, 22 Jun 2026 11:15:06 +0200 Subject: gnome cosmic plasma sddm update --- .../Components/BatteryIndicator.qml | 257 +++++++ .../sddm/sddm-rave-theme/Components/Clock.qml | 81 +++ .../sddm/sddm-rave-theme/Components/Input.qml | 754 +++++++++++++++++++++ .../Components/KeyboardLayoutIndicator.qml | 237 +++++++ .../sddm/sddm-rave-theme/Components/LoginForm.qml | 89 +++ .../Components/NetworkIndicator.qml | 243 +++++++ .../sddm-rave-theme/Components/ParticleOverlay.qml | 90 +++ .../sddm-rave-theme/Components/SessionButton.qml | 158 +++++ .../Components/ToastNotification.qml | 83 +++ .../sddm-rave-theme/Components/UserSwitcher.qml | 332 +++++++++ .../sddm-rave-theme/Components/VirtualKeyboard.qml | 20 + .../Components/VirtualKeyboardButton.qml | 79 +++ 12 files changed, 2423 insertions(+) create mode 100644 raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/BatteryIndicator.qml create mode 100644 raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/Clock.qml create mode 100644 raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/Input.qml create mode 100644 raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/KeyboardLayoutIndicator.qml create mode 100644 raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/LoginForm.qml create mode 100644 raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/NetworkIndicator.qml create mode 100644 raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/ParticleOverlay.qml create mode 100644 raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/SessionButton.qml create mode 100644 raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/ToastNotification.qml create mode 100644 raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/UserSwitcher.qml create mode 100644 raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/VirtualKeyboard.qml create mode 100644 raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/VirtualKeyboardButton.qml (limited to 'raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components') diff --git a/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/BatteryIndicator.qml b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/BatteryIndicator.qml new file mode 100644 index 0000000..5497911 --- /dev/null +++ b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/BatteryIndicator.qml @@ -0,0 +1,257 @@ +import QtQuick 2.15 +import Qt.labs.folderlistmodel 2.15 + +Row { + id: batteryIndicator + + height: root.font.pointSize * 1.6 + spacing: root.font.pointSize * 0.5 + + // -1 = nincs akkumulátor / nem laptop + property int batteryLevel: -1 + property bool isCharging: false + property bool isFull: false + property string batteryPath: "" + + visible: batteryLevel >= 0 + + // Ismert akksi elérési utak + property var tryPaths: [ + "/sys/class/power_supply/BAT0/", + "/sys/class/power_supply/BAT1/", + "/sys/class/power_supply/BAT2/", + "/sys/class/power_supply/battery/", + "/sys/class/power_supply/BATT/" + ] + + // Dinamikus akkumulátor kereső (pl. kontroller, UPS, egyéb külső akkuk észleléséhez) + FolderListModel { + id: powerSupplyScanner + folder: "file:///sys/class/power_supply" + showDirs: true + showFiles: false + + onCountChanged: scan() + onFolderChanged: scan() + + function scan() { + var defaults = [ + "/sys/class/power_supply/BAT0/", + "/sys/class/power_supply/BAT1/", + "/sys/class/power_supply/BAT2/", + "/sys/class/power_supply/battery/", + "/sys/class/power_supply/BATT/" + ] + for (var i = 0; i < count; i++) { + var name = get(i, "fileName") + if (name && name !== "." && name !== "..") { + // Csak a tipikus laptop akkumulátor elnevezések (pl. BAT0, BAT1, BATT, battery) + var isLaptopBat = /^bat/i.test(name) || /^battery/i.test(name) + if (isLaptopBat) { + var fullPath = "/sys/class/power_supply/" + name + "/" + if (defaults.indexOf(fullPath) === -1) { + defaults.push(fullPath) + } + } + } + } + tryPaths = defaults + detectBattery(0) + } + } + + // ── Fájl olvasó ────────────────────────────────────────────────────────── + function readFile(path, cb) { + var xhr = new XMLHttpRequest() + xhr.open("GET", "file://" + path, true) + xhr.onreadystatechange = function() { + if (xhr.readyState === XMLHttpRequest.DONE) + cb(xhr.responseText && xhr.responseText.trim() !== "" ? xhr.responseText.trim() : null) + } + xhr.send() + } + + // ── Akkumulátor keresés és frissítés ───────────────────────────────────── + function detectBattery(idx) { + if (idx >= tryPaths.length) { batteryLevel = -1; return } + readFile(tryPaths[idx] + "capacity", function(cap) { + if (cap !== null) { + batteryPath = tryPaths[idx] + batteryLevel = Math.max(0, Math.min(100, parseInt(cap) || 0)) + readFile(batteryPath + "status", function(st) { + var s = st ? st.toLowerCase() : "" + isFull = (s === "full") + isCharging = (s === "charging") || isFull + batteryCanvas.requestPaint() + }) + } else { + detectBattery(idx + 1) + } + }) + } + + // ── Akkumulátor frissítés ──────────────────────────────────────────────── + function refresh() { + if (batteryPath !== "") { + readFile(batteryPath + "capacity", function(cap) { + if (cap !== null) { + batteryLevel = Math.max(0, Math.min(100, parseInt(cap) || 0)) + readFile(batteryPath + "status", function(st) { + var s = st ? st.toLowerCase() : "" + isFull = (s === "full") + isCharging = (s === "charging") || isFull + batteryCanvas.requestPaint() + }) + } + }) + } else { + powerSupplyScanner.scan() + } + } + + Component.onCompleted: powerSupplyScanner.scan() + + Timer { + interval: 30000 + repeat: true + running: true + onTriggered: batteryIndicator.refresh() + } + + // ── Szín a töltési szint alapján ───────────────────────────────────────── + function levelColor(lvl, charging) { + if (charging) return "#4ade80" // zöld – tölt + if (lvl > 60) return "#ffffff" // fehér – ok + if (lvl > 30) return "#facc15" // sárga – közepes + if (lvl > 15) return "#fb923c" // narancs – alacsony + return "#f87171" // piros – kritikus + } + + // ── UI ─────────────────────────────────────────────────────────────────── + Canvas { + id: batteryCanvas + width: root.font.pointSize * 3.2 + height: root.font.pointSize * 1.6 + anchors.verticalCenter: parent.verticalCenter + + onPaint: { + var ctx = getContext("2d") + ctx.clearRect(0, 0, width, height) + + // Qt Canvas nem támogatja ctx.roundRect() – saját helper + function rr(c, x, y, w, h, r) { + r = Math.min(Math.max(r, 0), Math.min(w, h) / 2) + c.beginPath() + c.moveTo(x + r, y) + c.arcTo(x + w, y, x + w, y + h, r) + c.arcTo(x + w, y + h, x, y + h, r) + c.arcTo(x, y + h, x, y, r) + c.arcTo(x, y, x + w, y, r) + c.closePath() + } + + var lvl = batteryIndicator.batteryLevel + var charging = batteryIndicator.isCharging + var full = batteryIndicator.isFull + var col = batteryIndicator.levelColor(lvl, charging) + + var bw = width * 0.84 // akksi test szélessége + var bh = height * 0.72 // akksi test magassága + var bx = 0 + var by = (height - bh) / 2 + var r = bh * 0.22 // sarokkerekítés + var tw = width * 0.07 // terminál szélessége + var th = bh * 0.44 // terminál magassága + var tx = bw // terminál x + var ty = (height - th) / 2 + + // Terminál (+ pólus) + rr(ctx, tx, ty, tw, th, 2) + ctx.fillStyle = col + ctx.globalAlpha = 0.7 + ctx.fill() + ctx.globalAlpha = 1.0 + + // Keret (akksi test) + rr(ctx, bx, by, bw, bh, r) + ctx.strokeStyle = col + ctx.lineWidth = 1.5 + ctx.stroke() + + // Töltési szint kitöltés + var padding = 3 + var fillW = Math.max(0, (bw - padding * 2) * lvl / 100) + if (fillW > 0) { + rr(ctx, bx + padding, by + padding, + fillW, bh - padding * 2, + Math.max(0, r - padding * 0.5)) + ctx.fillStyle = col + ctx.globalAlpha = 0.85 + ctx.fill() + ctx.globalAlpha = 1.0 + } + + // Villám jel (töltés közben) + if (charging && !full) { + var cx = bw / 2 + var cy = height / 2 + var s = bh * 0.52 + ctx.beginPath() + ctx.moveTo(cx + s * 0.18, cy - s * 0.5) + ctx.lineTo(cx - s * 0.08, cy + s * 0.05) + ctx.lineTo(cx + s * 0.10, cy + s * 0.05) + ctx.lineTo(cx - s * 0.18, cy + s * 0.5) + ctx.lineTo(cx + s * 0.08, cy - s * 0.05) + ctx.lineTo(cx - s * 0.10, cy - s * 0.05) + ctx.closePath() + ctx.fillStyle = "#ffffff" + ctx.globalAlpha = 0.95 + ctx.fill() + ctx.globalAlpha = 1.0 + } + + // Tele jel (checkbox-szerű pipával) + if (full) { + ctx.beginPath() + ctx.moveTo(bw * 0.32, by + bh / 2) + ctx.lineTo(bw * 0.46, by + bh * 0.68) + ctx.lineTo(bw * 0.68, by + bh * 0.28) + ctx.strokeStyle = "#ffffff" + ctx.lineWidth = 1.8 + ctx.lineCap = "round" + ctx.lineJoin = "round" + ctx.globalAlpha = 0.95 + ctx.stroke() + ctx.globalAlpha = 1.0 + } + } + } + + // Százalék szöveg + Text { + id: batteryText + anchors.verticalCenter: parent.verticalCenter + text: { + if (batteryIndicator.isFull) return "100%" + if (batteryIndicator.isCharging) return batteryIndicator.batteryLevel + "% ↑" + return batteryIndicator.batteryLevel + "%" + } + color: batteryIndicator.levelColor(batteryIndicator.batteryLevel, batteryIndicator.isCharging) + font.pointSize: root.font.pointSize * 0.82 + font.family: root.font.family + font.bold: batteryIndicator.batteryLevel <= 15 && !batteryIndicator.isCharging + + // Pulzáló animáció kritikus töltésnél + SequentialAnimation on opacity { + running: batteryIndicator.batteryLevel <= 15 && !batteryIndicator.isCharging + loops: Animation.Infinite + NumberAnimation { to: 0.3; duration: 700; easing.type: Easing.InOutSine } + NumberAnimation { to: 1.0; duration: 700; easing.type: Easing.InOutSine } + } + } + + // Szint változáskor újrafest + onBatteryLevelChanged: batteryCanvas.requestPaint() + onIsChargingChanged: batteryCanvas.requestPaint() + onIsFullChanged: batteryCanvas.requestPaint() +} diff --git a/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/Clock.qml b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/Clock.qml new file mode 100644 index 0000000..e9d1399 --- /dev/null +++ b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/Clock.qml @@ -0,0 +1,81 @@ +// Config created by Keyitdev https://git.rp1.hu/gabeszm/sddm-rave-theme +// Copyright (C) 2022-2025 Keyitdev +// Based on https://github.com/MarianArlt/sddm-sugar-dark +// Distributed under the GPLv3+ License https://www.gnu.org/licenses/gpl-3.0.html + +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +Column { + id: clock + + width: parent.width / 2 + spacing: 8 + + Image { + id: logoImage + + anchors.horizontalCenter: parent.horizontalCenter + source: (config.Logo && config.Logo !== "") ? Qt.resolvedUrl("../" + config.Logo) : "" + fillMode: Image.PreserveAspectFit + width: root.font.pointSize * 13 + height: root.font.pointSize * 13 + visible: config.Logo && config.Logo !== "" + } + + Label { + id:headerTextLabel + + anchors.horizontalCenter: parent.horizontalCenter + + font.pointSize: root.font.pointSize * 2 + color: config.HeaderTextColor + renderType: Text.QtRendering + text: config.HeaderText + } + + Label { + id: timeLabel + + anchors.horizontalCenter: parent.horizontalCenter + + font.pointSize: root.font.pointSize * 3.5 + font.bold: true + color: config.TimeTextColor + renderType: Text.QtRendering + + function updateTime() { + text = new Date().toLocaleTimeString(Qt.locale(config.Locale), config.HourFormat == "long" ? Locale.LongFormat : config.HourFormat !== "" ? config.HourFormat : Locale.ShortFormat) + } + } + + Label { + id: dateLabel + + anchors.horizontalCenter: parent.horizontalCenter + + color: config.DateTextColor + font.pointSize: root.font.pointSize * 0.95 + font.bold: true + renderType: Text.QtRendering + + function updateTime() { + text = new Date().toLocaleDateString(Qt.locale(config.Locale), config.DateFormat == "short" ? Locale.ShortFormat : config.DateFormat !== "" ? config.DateFormat : Locale.LongFormat) + } + } + + Timer { + interval: 1000 + repeat: true + running: true + onTriggered: { + dateLabel.updateTime() + timeLabel.updateTime() + } + } + + Component.onCompleted: { + dateLabel.updateTime() + timeLabel.updateTime() + } +} diff --git a/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/Input.qml b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/Input.qml new file mode 100644 index 0000000..47e6598 --- /dev/null +++ b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/Input.qml @@ -0,0 +1,754 @@ +// Config created by Keyitdev https://git.rp1.hu/gabeszm/sddm-rave-theme +// Copyright (C) 2022-2025 Keyitdev +// Based on https://github.com/MarianArlt/sddm-sugar-dark +// Distributed under the GPLv3+ License https://www.gnu.org/licenses/gpl-3.0.html + +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import QtQuick.Controls 2.15 +import QtQuick.Effects + +Column { + id: inputContainer + + Layout.fillWidth: true + + property ComboBox exposeSession: sessionSelect.exposeSession + property bool failed + + Item { + id: errorMessageField + + // change also in selectSession + height: root.font.pointSize * 2 + width: parent.width / 2 + anchors.horizontalCenter: parent.horizontalCenter + + Label { + id: errorMessage + + width: parent.width + horizontalAlignment: Text.AlignHCenter + + text: failed ? config.TranslateLoginFailedWarning || textConstants.loginFailed + "!" : keyboard.capsLock ? config.TranslateCapslockWarning || textConstants.capslockWarning : null + font.pointSize: root.font.pointSize * 0.8 + font.italic: true + color: config.WarningColor + opacity: 0 + + states: [ + State { + name: "fail" + when: failed + PropertyChanges { + target: errorMessage + opacity: 1 + } + }, + State { + name: "capslock" + when: keyboard.capsLock + PropertyChanges { + target: errorMessage + opacity: 1 + } + } + ] + transitions: [ + Transition { + PropertyAnimation { + properties: "opacity" + duration: 100 + } + } + ] + } + } + + // Profilkép + felhasználóváltó + Item { + id: avatarField + + height: root.font.pointSize * 12 + width: parent.width / 2 + anchors.horizontalCenter: parent.horizontalCenter + + property string userIconPath: { + var icon = userModel.data(userModel.index(selectUser.currentIndex, 0), Qt.UserRole + 4) + return (icon && icon !== "") ? icon : "" + } + + Column { + anchors.centerIn: parent + spacing: root.font.pointSize * 0.8 + + // Avatar kör + Rectangle { + id: avatarCircle + anchors.horizontalCenter: parent.horizontalCenter + width: root.font.pointSize * 7 + height: root.font.pointSize * 7 + radius: width / 2 + clip: true + color: Qt.rgba(0.10, 0.10, 0.15, 0.65) + + // The source image (invisible) + Image { + id: avatarPhoto + anchors.fill: parent + source: avatarField.userIconPath !== "" ? avatarField.userIconPath : "" + fillMode: Image.PreserveAspectCrop + smooth: true + mipmap: true + visible: false + } + + // The mask source + Item { + id: avatarMask + anchors.fill: parent + visible: false + layer.enabled: true + Rectangle { + anchors.fill: parent + radius: parent.width / 2 + color: "black" + } + } + + // Apply MultiEffect to mask the avatarPhoto + MultiEffect { + anchors.fill: parent + source: avatarPhoto + visible: avatarPhoto.status === Image.Ready && avatarField.userIconPath !== "" + maskEnabled: true + maskSource: avatarMask + maskThresholdMin: 0.5 + maskSpreadAtMin: 1.0 + } + + Image { + id: avatarFallback + anchors.centerIn: parent + width: parent.width * 0.55 + height: parent.height * 0.55 + source: Qt.resolvedUrl("../Assets/User.svg") + fillMode: Image.PreserveAspectFit + smooth: true + visible: avatarField.userIconPath === "" || avatarPhoto.status !== Image.Ready + opacity: 0.75 + } + + // Draw the border on top of everything! + Rectangle { + anchors.fill: parent + radius: parent.width / 2 + color: "transparent" + border.color: Qt.rgba(1, 1, 1, 0.30) + border.width: 2 + } + } + + // Felhasználóváltó gomb (csak ha több user van) + UserSwitcher { + id: userSwitcher + anchors.horizontalCenter: parent.horizontalCenter + selectedIndex: selectUser.currentIndex + + onUserSwitched: function(idx, name) { + selectUser.currentIndex = idx + username.text = name + } + } + } + } + + + Item { + id: usernameField + + height: root.font.pointSize * 4.5 + width: parent.width / 2 + anchors.horizontalCenter: parent.horizontalCenter + + Button { + id: staticUserIcon + + width: parent.height + height: parent.height + anchors.left: parent.left + z: 2 + visible: userModel.count <= 1 + enabled: false + + icon.height: parent.height * 0.25 + icon.width: parent.height * 0.25 + icon.color: config.UserIconColor + icon.source: Qt.resolvedUrl("../Assets/User.svg") + + background: Rectangle { + color: "transparent" + border.color: "transparent" + } + } + + ComboBox { + id: selectUser + + width: parent.height + height: parent.height + anchors.left: parent.left + z: 2 + visible: userModel.count > 1 + + model: userModel + currentIndex: model.lastIndex + textRole: "name" + hoverEnabled: true + onActivated: { + username.text = currentText; + } + + property var popkey: config.RightToLeftLayout == "true" ? Qt.Key_Right : Qt.Key_Left + Keys.onPressed: function (event) { + if (event.key == Qt.Key_Down && !popup.opened) + username.forceActiveFocus(); + if ((event.key == Qt.Key_Up || event.key == popkey) && !popup.opened) + popup.open(); + } + KeyNavigation.down: username + KeyNavigation.right: username + + delegate: ItemDelegate { + // minus padding + width: popupHandler.width - 20 + anchors.horizontalCenter: popupHandler.horizontalCenter + + contentItem: Text { + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + + text: model.name + font.pointSize: root.font.pointSize * 0.8 + font.capitalization: Font.AllLowercase + font.family: root.font.family + color: config.DropdownTextColor + } + + background: Rectangle { + color: selectUser.highlightedIndex === index ? Qt.rgba(1, 1, 1, 0.15) : "transparent" + radius: config.RoundCorners !== "" ? parseInt(config.RoundCorners) / 4 : 5 + } + } + + indicator: Button { + id: usernameIcon + + width: selectUser.height * 1 + height: parent.height + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: selectUser.height * 0 + + icon.height: parent.height * 0.25 + icon.width: parent.height * 0.25 + enabled: false + icon.color: config.UserIconColor + icon.source: Qt.resolvedUrl("../Assets/User.svg") + + background: Rectangle { + color: "transparent" + border.color: "transparent" + } + } + + background: Rectangle { + color: "transparent" + border.color: "transparent" + } + + popup: Popup { + id: popupHandler + + implicitHeight: contentItem.implicitHeight + width: usernameField.width + y: parent.height - username.height / 3 + x: config.RightToLeftLayout == "true" ? -loginButton.width + selectUser.width : 0 + rightMargin: config.RightToLeftLayout == "true" ? root.padding + usernameField.width / 2 : undefined + padding: 10 + + contentItem: ListView { + implicitHeight: contentHeight + 20 + + clip: true + model: selectUser.popup.visible ? selectUser.delegateModel : null + currentIndex: selectUser.highlightedIndex + ScrollIndicator.vertical: ScrollIndicator {} + } + + background: Rectangle { + radius: config.RoundCorners !== "" ? parseInt(config.RoundCorners) / 2 : 10 + color: Qt.rgba(1, 1, 1, 0.08) + border.color: Qt.rgba(1, 1, 1, 0.18) + border.width: 1 + layer.enabled: true + } + + enter: Transition { + NumberAnimation { + property: "opacity" + from: 0 + to: 1 + } + } + } + + states: [ + State { + name: "pressed" + when: selectUser.down + PropertyChanges { + target: usernameIcon + icon.color: Qt.lighter(config.HoverUserIconColor, 1.1) + } + }, + State { + name: "hovered" + when: selectUser.hovered + PropertyChanges { + target: usernameIcon + icon.color: Qt.lighter(config.HoverUserIconColor, 1.2) + } + }, + State { + name: "focused" + when: selectUser.activeFocus + PropertyChanges { + target: usernameIcon + icon.color: config.HoverUserIconColor + } + } + ] + transitions: [ + Transition { + PropertyAnimation { + properties: "color, border.color, icon.color" + duration: 150 + } + } + ] + } + + TextField { + id: username + + anchors.centerIn: parent + height: root.font.pointSize * 3 + width: parent.width + horizontalAlignment: TextInput.AlignHCenter + z: 1 + + text: config.ForceLastUser == "true" ? selectUser.currentText : null + color: config.LoginFieldTextColor + font.bold: true + font.capitalization: config.AllowUppercaseLettersInUsernames == "false" ? Font.AllLowercase : Font.MixedCase + placeholderText: config.TranslatePlaceholderUsername || textConstants.userName + placeholderTextColor: config.PlaceholderTextColor + selectByMouse: true + renderType: Text.QtRendering + + onFocusChanged: { + if (focus) + selectAll(); + } + + background: Rectangle { + color: username.activeFocus ? Qt.rgba(0, 0, 0, 0.35) : Qt.rgba(0, 0, 0, 0.2) + border.color: username.activeFocus ? Qt.rgba(255, 255, 255, 0.3) : Qt.rgba(255, 255, 255, 0.12) + border.width: username.activeFocus ? 1.5 : 1 + radius: config.RoundCorners !== "" ? parseInt(config.RoundCorners) : 10 + Behavior on color { + ColorAnimation { + duration: 150 + } + } + Behavior on border.color { + ColorAnimation { + duration: 150 + } + } + } + + onAccepted: config.AllowUppercaseLettersInUsernames == "false" ? sddm.login(username.text.toLowerCase(), password.text, sessionSelect.selectedSession) : sddm.login(username.text, password.text, sessionSelect.selectedSession) + KeyNavigation.down: passwordIcon + + states: [ + State { + name: "focused" + when: username.activeFocus + PropertyChanges { + target: username + color: Qt.lighter(config.LoginFieldTextColor, 1.15) + } + } + ] + } + } + + Item { + id: passwordField + + height: root.font.pointSize * 4.5 + width: parent.width / 2 + anchors.horizontalCenter: parent.horizontalCenter + + Button { + id: passwordIcon + + height: parent.height + width: selectUser.height * 1 + anchors.left: parent.left + anchors.leftMargin: selectUser.height * 0 + anchors.verticalCenter: parent.verticalCenter + z: 2 + + icon.height: parent.height * 0.25 + icon.width: parent.height * 0.25 + icon.color: config.PasswordIconColor + icon.source: Qt.resolvedUrl("../Assets/Password2.svg") + + background: Rectangle { + color: "transparent" + border.color: "transparent" + } + + states: [ + State { + name: "visiblePasswordFocused" + when: passwordIcon.checked && passwordIcon.activeFocus + PropertyChanges { + target: passwordIcon + icon.source: Qt.resolvedUrl("../Assets/Password.svg") + icon.color: config.HoverPasswordIconColor + } + }, + State { + name: "visiblePasswordHovered" + when: passwordIcon.checked && passwordIcon.hovered + PropertyChanges { + target: passwordIcon + icon.source: Qt.resolvedUrl("../Assets/Password.svg") + icon.color: config.HoverPasswordIconColor + } + }, + State { + name: "visiblePassword" + when: passwordIcon.checked + PropertyChanges { + target: passwordIcon + icon.source: Qt.resolvedUrl("../Assets/Password.svg") + } + }, + State { + name: "hiddenPasswordFocused" + when: passwordIcon.enabled && passwordIcon.activeFocus + PropertyChanges { + target: passwordIcon + icon.source: Qt.resolvedUrl("../Assets/Password2.svg") + icon.color: config.HoverPasswordIconColor + } + }, + State { + name: "hiddenPasswordHovered" + when: passwordIcon.hovered + PropertyChanges { + target: passwordIcon + icon.source: Qt.resolvedUrl("../Assets/Password2.svg") + icon.color: config.HoverPasswordIconColor + } + } + ] + + onClicked: toggle() + Keys.onReturnPressed: toggle() + Keys.onEnterPressed: toggle() + KeyNavigation.down: password + } + + TextField { + id: password + + height: root.font.pointSize * 3 + width: parent.width + anchors.centerIn: parent + horizontalAlignment: TextInput.AlignHCenter + + font.bold: true + color: config.PasswordFieldTextColor + focus: config.PasswordFocus == "true" ? true : false + echoMode: passwordIcon.checked ? TextInput.Normal : TextInput.Password + placeholderText: config.TranslatePlaceholderPassword || textConstants.password + placeholderTextColor: config.PlaceholderTextColor + passwordCharacter: "•" + passwordMaskDelay: config.HideCompletePassword == "true" ? undefined : 1000 + renderType: Text.QtRendering + selectByMouse: true + + background: Rectangle { + color: password.activeFocus ? Qt.rgba(0, 0, 0, 0.35) : Qt.rgba(0, 0, 0, 0.2) + border.color: password.activeFocus ? Qt.rgba(255, 255, 255, 0.3) : Qt.rgba(255, 255, 255, 0.12) + border.width: password.activeFocus ? 1.5 : 1 + radius: config.RoundCorners !== "" ? parseInt(config.RoundCorners) : 10 + Behavior on color { + ColorAnimation { + duration: 150 + } + } + Behavior on border.color { + ColorAnimation { + duration: 150 + } + } + } + onAccepted: config.AllowUppercaseLettersInUsernames == "false" ? sddm.login(username.text.toLowerCase(), password.text, sessionSelect.selectedSession) : sddm.login(username.text, password.text, sessionSelect.selectedSession) + KeyNavigation.down: loginButton + } + + states: [ + State { + name: "focused" + when: password.activeFocus + PropertyChanges { + target: password + color: Qt.lighter(config.LoginFieldTextColor, 1.15) + } + } + ] + transitions: [ + Transition { + PropertyAnimation { + properties: "color, border.color" + duration: 150 + } + } + ] + } + + Item { + id: login + + // important + // try 4 or 9 ... + height: root.font.pointSize * 9 + width: parent.width / 2 + anchors.horizontalCenter: parent.horizontalCenter + + visible: config.HideLoginButton == "true" ? false : true + + Item { + id: loginButton + + height: root.font.pointSize * 3 + width: parent.width + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + focus: true + activeFocusOnTab: true + + property bool hovered: hoverArea.containsMouse + property bool down: hoverArea.pressed + + signal clicked() + + onClicked: config.AllowUppercaseLettersInUsernames == "false" ? sddm.login(username.text.toLowerCase(), password.text, sessionSelect.selectedSession) : sddm.login(username.text, password.text, sessionSelect.selectedSession) + + Keys.onReturnPressed: clicked() + Keys.onEnterPressed: clicked() + + KeyNavigation.down: rebootBtn + + Rectangle { + id: buttonBackground + anchors.fill: parent + + radius: config.RoundCorners !== "" ? parseInt(config.RoundCorners) : 10 + border.width: loginButton.activeFocus ? 1.5 : 1 + + border.color: loginButton.down ? Qt.rgba(21/255, 128/255, 61/255, 0.95) : (loginButton.hovered ? Qt.rgba(34/255, 197/255, 94/255, 0.85) : Qt.rgba(74/255, 222/255, 128/255, 0.65)) + + color: loginButton.down ? Qt.rgba(21/255, 128/255, 61/255, 0.80) : (loginButton.hovered ? Qt.rgba(34/255, 197/255, 94/255, 0.65) : Qt.rgba(74/255, 222/255, 128/255, 0.40)) + + Behavior on border.color { + ColorAnimation { + duration: 150 + } + } + + Behavior on color { + ColorAnimation { + duration: 150 + } + } + } + + Text { + id: loginButtonText + anchors.centerIn: parent + + font.bold: true + font.pointSize: root.font.pointSize + font.family: root.font.family + color: "#ffffff" + text: config.TranslateLogin || textConstants.login + opacity: 1.0 + + Behavior on opacity { + NumberAnimation { + duration: 150 + } + } + } + + MouseArea { + id: hoverArea + anchors.fill: parent + hoverEnabled: true + onClicked: parent.clicked() + } + } + } + + Item { + id: powerButtonsRow + + height: root.font.pointSize * 10 + width: parent.width / 2 + anchors.horizontalCenter: parent.horizontalCenter + + Row { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + spacing: root.font.pointSize * 3 + + // Reboot gomb + Column { + spacing: root.font.pointSize * 0.6 + + RoundButton { + id: rebootBtn + + width: root.font.pointSize * 6 + height: root.font.pointSize * 6 + + activeFocusOnTab: true + Keys.onReturnPressed: sddm.reboot() + Keys.onEnterPressed: sddm.reboot() + KeyNavigation.right: shutdownBtn + hoverEnabled: true + + icon.source: Qt.resolvedUrl("../Assets/Reboot.svg") + icon.width: root.font.pointSize * 3 + icon.height: root.font.pointSize * 3 + icon.color: "#ffffff" + + opacity: rebootBtn.hovered || rebootBtn.activeFocus ? 1.0 : 0.75 + Behavior on opacity { NumberAnimation { duration: 150 } } + + scale: rebootBtn.pressed ? 0.88 : 1.0 + Behavior on scale { NumberAnimation { duration: 100 } } + + background: Rectangle { + anchors.fill: parent + radius: width / 2 + color: rebootBtn.pressed ? Qt.rgba(1, 0.45, 0.0, 0.35) : (rebootBtn.hovered ? Qt.rgba(1, 0.55, 0.0, 0.22) : "transparent") + border.color: "transparent" + border.width: 0 + Behavior on color { ColorAnimation { duration: 150 } } + } + + onClicked: sddm.reboot() + } + + Text { + anchors.horizontalCenter: parent.children[0].horizontalCenter + text: config.TranslateReboot || textConstants.reboot + color: "#ffffff" + font.pointSize: root.font.pointSize * 0.8 + font.family: root.font.family + horizontalAlignment: Text.AlignHCenter + opacity: rebootBtn.hovered || rebootBtn.activeFocus ? 1.0 : 0.75 + Behavior on opacity { NumberAnimation { duration: 150 } } + } + } + + // Shutdown gomb + Column { + spacing: root.font.pointSize * 0.6 + + RoundButton { + id: shutdownBtn + + width: root.font.pointSize * 6 + height: root.font.pointSize * 6 + + activeFocusOnTab: true + Keys.onReturnPressed: sddm.powerOff() + Keys.onEnterPressed: sddm.powerOff() + KeyNavigation.left: rebootBtn + hoverEnabled: true + + icon.source: Qt.resolvedUrl("../Assets/Shutdown.svg") + icon.width: root.font.pointSize * 3 + icon.height: root.font.pointSize * 3 + icon.color: "#ffffff" + + opacity: shutdownBtn.hovered || shutdownBtn.activeFocus ? 1.0 : 0.75 + Behavior on opacity { NumberAnimation { duration: 150 } } + + scale: shutdownBtn.pressed ? 0.88 : 1.0 + Behavior on scale { NumberAnimation { duration: 100 } } + + background: Rectangle { + anchors.fill: parent + radius: width / 2 + color: shutdownBtn.pressed ? Qt.rgba(0.8, 0.1, 0.1, 0.40) : (shutdownBtn.hovered ? Qt.rgba(0.8, 0.1, 0.1, 0.25) : "transparent") + border.color: "transparent" + border.width: 0 + Behavior on color { ColorAnimation { duration: 150 } } + } + + onClicked: sddm.powerOff() + } + + Text { + anchors.horizontalCenter: parent.children[0].horizontalCenter + text: config.TranslateShutdown || textConstants.shutdown + color: "#ffffff" + font.pointSize: root.font.pointSize * 0.8 + font.family: root.font.family + horizontalAlignment: Text.AlignHCenter + opacity: shutdownBtn.hovered || shutdownBtn.activeFocus ? 1.0 : 0.75 + Behavior on opacity { NumberAnimation { duration: 150 } } + } + } + } + } + + Connections { + target: sddm + function onLoginSucceeded() { + } + function onLoginFailed() { + failed = true; + resetError.running ? resetError.stop() && resetError.start() : resetError.start(); + } + } + + Timer { + id: resetError + interval: 2000 + onTriggered: failed = false + running: false + } +} diff --git a/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/KeyboardLayoutIndicator.qml b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/KeyboardLayoutIndicator.qml new file mode 100644 index 0000000..0fb03be --- /dev/null +++ b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/KeyboardLayoutIndicator.qml @@ -0,0 +1,237 @@ +// Keyboard Layout Indicator component +// Displays the current locale/layout and switches between them when clicked (if multiple are available) + +import QtQuick 2.15 +import QtQml 2.15 +import QtQuick.Controls 2.15 + +Item { + id: layoutIndicator + + implicitWidth: layoutRow.implicitWidth + implicitHeight: layoutRow.implicitHeight + width: implicitWidth + height: implicitHeight + + // Number of available layouts + property int layoutsCount: (typeof keyboard !== "undefined" && keyboard && keyboard.layouts) ? keyboard.layouts.length : 0 + + // Show if we have layout info or a valid locale fallback + visible: currentShortName !== "" + + // Get current layout shortname (e.g. "US", "HU") + property string currentShortName: "" + + function updateCurrentShortName() { + if (layoutsCount > 0) { + var idx = keyboard.currentLayout + if (idx >= 0 && idx < layoutsCount) { + var layoutObj = keyboard.layouts[idx] + if (layoutObj && layoutObj.shortName) { + currentShortName = layoutObj.shortName.toUpperCase() + return + } + } + } + + // Fallback to config locale or system locale + var loc = config.Locale || Qt.locale().name + if (loc) { + var parts = loc.split("_") + if (parts.length > 0 && parts[0]) { + currentShortName = parts[0].toUpperCase() + return + } + } + currentShortName = "" + } + + Component.onCompleted: updateCurrentShortName() + + // Colors matched to the theme config + property color textColor: config.UserIconColor || "#ffffff" + property color hoverColor: config.HoverUserIconColor || "#b7cef1" + + Row { + id: layoutRow + height: root.font.pointSize * 1.1 + spacing: root.font.pointSize * 0.4 + // Removed anchors.centerIn to prevent circular binding loop with parent width/height + + // Keyboard icon + Canvas { + id: kbdCanvas + width: root.font.pointSize * 1.5 + height: root.font.pointSize * 1.1 + anchors.verticalCenter: parent.verticalCenter + + onPaint: { + var ctx = getContext("2d") + ctx.clearRect(0, 0, width, height) + + var canHover = layoutsCount >= 1 + var col = (layoutMouseArea.containsMouse && canHover) ? layoutIndicator.hoverColor : layoutIndicator.textColor + + ctx.strokeStyle = col + ctx.fillStyle = col + ctx.lineWidth = height * 0.08 + ctx.lineCap = "round" + ctx.lineJoin = "round" + ctx.globalAlpha = 0.85 + + // Draw keyboard outline + var r = height * 0.15 + var bx = ctx.lineWidth / 2 + var by = ctx.lineWidth / 2 + var bw = width - ctx.lineWidth + var bh = height - ctx.lineWidth + + ctx.beginPath() + ctx.moveTo(bx + r, by) + ctx.arcTo(bx + bw, by, bx + bw, by + bh, r) + ctx.arcTo(bx + bw, by + bh, bx, by + bh, r) + ctx.arcTo(bx, by + bh, bx, by, r) + ctx.arcTo(bx, by, bx + bw, by, r) + ctx.closePath() + ctx.stroke() + + // Draw simple key grid + ctx.lineWidth = height * 0.06 + var kw = bw / 6 + var kh = bh / 4 + // Row 1 & 2 keys + for (var i = 0; i < 4; i++) { + ctx.fillRect(bx + kw * (i + 1), by + kh, kw * 0.6, kh * 0.6) + ctx.fillRect(bx + kw * (i + 0.5), by + kh * 2, kw * 0.6, kh * 0.6) + } + // Spacebar + ctx.fillRect(bx + kw * 1.5, by + kh * 3, kw * 3, kh * 0.6) + + ctx.globalAlpha = 1.0 + } + + Connections { + target: layoutMouseArea + function onContainsMouseChanged() { kbdCanvas.requestPaint() } + } + } + + // Layout Short Name (e.g. US, HU) + Text { + id: layoutText + anchors.verticalCenter: parent.verticalCenter + text: layoutIndicator.currentShortName + + color: { + var canHover = layoutsCount >= 1 + return (layoutMouseArea.containsMouse && canHover) ? layoutIndicator.hoverColor : layoutIndicator.textColor + } + + font.pointSize: root.font.pointSize * 0.82 + font.family: root.font.family + font.bold: true + } + } + + MouseArea { + id: layoutMouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: (layoutsCount >= 1) ? Qt.PointingHandCursor : Qt.ArrowCursor + + onClicked: { + if (layoutsCount >= 1) { + layoutPopup.open() + } + } + } + + Popup { + id: layoutPopup + y: parent.height + 5 + x: -width / 2 + parent.width / 2 + width: Math.max(160, layoutRow.width * 1.5) + padding: 5 + + background: Rectangle { + radius: config.RoundCorners !== "" ? parseInt(config.RoundCorners) / 2 : 10 + color: config.BackgroundColor || "#1a1a1a" + border.color: Qt.rgba(1, 1, 1, 0.20) + border.width: 1 + } + + contentItem: ListView { + id: layoutListView + implicitHeight: Math.min(200, contentHeight) + clip: true + model: layoutsCount + ScrollIndicator.vertical: ScrollIndicator { } + + delegate: Rectangle { + id: delegateItem + width: parent.width + height: root.font.pointSize * 2 + color: delegateMouse.containsMouse ? Qt.rgba(1, 1, 1, 0.15) : "transparent" + radius: config.RoundCorners !== "" ? parseInt(config.RoundCorners) / 4 : 5 + + // Get layout object safely + property var layoutObj: (typeof keyboard !== "undefined" && keyboard && keyboard.layouts) ? keyboard.layouts[index] : null + property string shortName: layoutObj && layoutObj.shortName ? layoutObj.shortName.toUpperCase() : "" + property string longName: layoutObj && layoutObj.longName ? layoutObj.longName : "" + + Row { + spacing: 8 + anchors.fill: parent + anchors.leftMargin: 10 + anchors.rightMargin: 10 + + Text { + text: delegateItem.shortName + color: index === keyboard.currentLayout ? (config.HoverUserIconColor || "#b7cef1") : (config.UserIconColor || "#ffffff") + font.pointSize: root.font.pointSize * 0.8 + font.family: root.font.family + font.bold: true + anchors.verticalCenter: parent.verticalCenter + } + + Text { + text: delegateItem.longName + color: index === keyboard.currentLayout ? (config.HoverUserIconColor || "#b7cef1") : (config.UserIconColor || "#ffffff") + font.pointSize: root.font.pointSize * 0.8 + font.family: root.font.family + elide: Text.ElideRight + width: parent.width - x - 10 + anchors.verticalCenter: parent.verticalCenter + } + } + + MouseArea { + id: delegateMouse + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + keyboard.currentLayout = index + layoutPopup.close() + } + } + } + } + + enter: Transition { + NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 150 } + } + exit: Transition { + NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 150 } + } + } + + Connections { + target: keyboard + ignoreUnknownSignals: true + function onCurrentLayoutChanged() { + kbdCanvas.requestPaint() + updateCurrentShortName() + } + } +} diff --git a/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/LoginForm.qml b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/LoginForm.qml new file mode 100644 index 0000000..515d6b3 --- /dev/null +++ b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/LoginForm.qml @@ -0,0 +1,89 @@ +// Config created by Keyitdev https://git.rp1.hu/gabeszm/sddm-rave-theme +// Copyright (C) 2022-2025 Keyitdev +// Based on https://github.com/MarianArlt/sddm-sugar-dark +// Distributed under the GPLv3+ License https://www.gnu.org/licenses/gpl-3.0.html + +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import SddmComponents 2.0 as SDDM + +ColumnLayout { + id: formContainer + SDDM.TextConstants { id: textConstants } + + property int p: config.ScreenPadding == "" ? 0 : config.ScreenPadding + property string a: config.FormPosition + + spacing: 15 + + Clock { + id: clock + + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: clock.implicitHeight + Layout.leftMargin: p != "0" ? a == "left" ? -p : a == "right" ? p : 0 : 0 + } + + // Akkumulátor + Hálózat jelzők + RowLayout { + id: statusRow + + Layout.alignment: Qt.AlignHCenter + Layout.leftMargin: p != "0" ? a == "left" ? -p : a == "right" ? p : 0 : 0 + Layout.bottomMargin: visible ? 4 : 0 + visible: (batteryIndicator.batteryLevel >= 0) || networkIndicator.isConnected + + spacing: root.font.pointSize * 1.2 + + BatteryIndicator { + id: batteryIndicator + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: batteryIndicator.implicitWidth + Layout.preferredHeight: batteryIndicator.implicitHeight + visible: batteryIndicator.batteryLevel >= 0 + } + + NetworkIndicator { + id: networkIndicator + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: networkIndicator.implicitWidth + Layout.preferredHeight: networkIndicator.implicitHeight + visible: networkIndicator.isConnected + } + } + + // Billentyűzet kiosztás jelző (külön sorban, hogy akksi/wifi hiányában is látszódjon) + KeyboardLayoutIndicator { + id: keyboardLayoutIndicator + + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: keyboardLayoutIndicator.implicitHeight + Layout.leftMargin: p != "0" ? a == "left" ? -p : a == "right" ? p : 0 : 0 + Layout.bottomMargin: visible ? 4 : 0 + } + + Input { + id: input + + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: input.implicitHeight + Layout.leftMargin: p != "0" ? a == "left" ? -p : a == "right" ? p : 0 : 0 + Layout.topMargin: 0 + } + + SessionButton { + id: sessionSelect + + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: sessionSelect.height + Layout.leftMargin: p != "0" ? a == "left" ? -p : a == "right" ? p : 0 : 0 + } + + VirtualKeyboardButton { + id: virtualKeyboardButton + + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: root.font.pointSize * 2 + Layout.leftMargin: p != "0" ? a == "left" ? -p : a == "right" ? p : 0 : 0 + } +} diff --git a/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/NetworkIndicator.qml b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/NetworkIndicator.qml new file mode 100644 index 0000000..bed97c8 --- /dev/null +++ b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/NetworkIndicator.qml @@ -0,0 +1,243 @@ +// 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 + +Row { + id: networkIndicator + + height: root.font.pointSize * 1.6 + spacing: root.font.pointSize * 0.5 + + // "none" | "wifi" | "ethernet" + property string netType: "none" + property int wifiQuality: 0 // 0-100 + property string activeIface: "" + property bool isConnected: netType !== "none" + + visible: isConnected + + property var wifiIfaces: [ + "wlan0","wlan1","wlp1s0","wlp2s0","wlp3s0", + "wlp4s0","wlp5s0","wlp6s0","wlp0s20f3" + ] + property var ethIfaces: [ + "eth0","eth1","eno1","eno2", + "enp1s0","enp2s0","enp3s0","enp4s0", + "enp0s3","enp0s31f6","ens33","ens3","ens18" + ] + + // ── Fájl olvasó ────────────────────────────────────────────────────────── + function readFile(path, cb) { + var xhr = new XMLHttpRequest() + xhr.open("GET", "file://" + path, true) + xhr.onreadystatechange = function() { + if (xhr.readyState === XMLHttpRequest.DONE) { + var res = xhr.responseText && xhr.responseText.trim() !== "" + ? xhr.responseText.trim() : null + cb(res) + } + } + xhr.send() + } + + // ── WiFi jelerősség /proc/net/wireless-ből ─────────────────────────────── + function readWifiSignal(iface) { + readFile("/proc/net/wireless", function(content) { + if (!content) { wifiQuality = 60; netCanvas.requestPaint(); return } + var lines = content.split("\n") + for (var i = 0; i < lines.length; i++) { + if (lines[i].indexOf(iface + ":") !== -1) { + // formátum: "wlan0: 0000 61. -49. -256 ..." + var parts = lines[i].trim().replace(/\./g,"").split(/\s+/) + // parts[2] = link quality (0–70 tartomány általában) + var q = parseFloat(parts[2]) || 0 + wifiQuality = Math.min(100, Math.round(q / 70 * 100)) + break + } + } + netCanvas.requestPaint() + }) + } + + // ── Interfész keresők ──────────────────────────────────────────────────── + function checkWifi(idx) { + if (idx >= wifiIfaces.length) { checkEth(0); return } + readFile("/sys/class/net/" + wifiIfaces[idx] + "/operstate", function(st) { + if (st === "up") { + activeIface = wifiIfaces[idx] + netType = "wifi" + readWifiSignal(wifiIfaces[idx]) + } else { + checkWifi(idx + 1) + } + }) + } + + // ── Ethernet keresés ───────────────────────────────────────────────────── + function checkEth(idx) { + if (idx >= ethIfaces.length) { netType = "none"; return } + readFile("/sys/class/net/" + ethIfaces[idx] + "/operstate", function(st) { + if (st === "up") { + activeIface = ethIfaces[idx] + netType = "ethernet" + netCanvas.requestPaint() + } else { + checkEth(idx + 1) + } + }) + } + + function refresh() { checkWifi(0) } + + Component.onCompleted: refresh() + + Timer { + interval: 15000 + repeat: true + running: true + onTriggered: networkIndicator.refresh() + } + + // ── Szín ───────────────────────────────────────────────────────────────── + function signalColor(q) { + if (q >= 65) return "#4ade80" // zöld – erős + if (q >= 40) return "#facc15" // sárga – közepes + return "#fb923c" // narancs – gyenge + } + + // ── UI ─────────────────────────────────────────────────────────────────── + Canvas { + id: netCanvas + width: root.font.pointSize * 2.0 + height: root.font.pointSize * 1.6 + anchors.verticalCenter: parent.verticalCenter + + // Qt Canvas nem támogatja ctx.roundRect() – saját helper + function rr(c, x, y, w, h, r) { + r = Math.min(Math.max(r, 0), Math.min(w, h) / 2) + c.beginPath() + c.moveTo(x + r, y) + c.arcTo(x + w, y, x + w, y + h, r) + c.arcTo(x + w, y + h, x, y + h, r) + c.arcTo(x, y + h, x, y, r) + c.arcTo(x, y, x + w, y, r) + c.closePath() + } + + onPaint: { + var ctx = getContext("2d") + ctx.clearRect(0, 0, width, height) + + if (networkIndicator.netType === "wifi") + drawWifi(ctx) + else if (networkIndicator.netType === "ethernet") + drawEthernet(ctx) + } + + // ── WiFi antenna ikon (4 ívelt sáv) ───────────────────────────── + function drawWifi(ctx) { + var q = networkIndicator.wifiQuality + var cx = width / 2 + var base = height * 0.96 + var dot = height * 0.08 + + // Sávok száma a minőség alapján + var bars = q >= 70 ? 4 : q >= 45 ? 3 : q >= 20 ? 2 : 1 + var col = networkIndicator.signalColor(q) + + var radii = [height*0.14, height*0.32, height*0.54, height*0.76] + var arcStart = Math.PI * 1.25 + var arcEnd = Math.PI * 1.75 + + for (var i = 0; i < 4; i++) { + ctx.beginPath() + ctx.arc(cx, base, radii[i], arcStart, arcEnd) + ctx.strokeStyle = col + ctx.globalAlpha = i < bars ? 0.90 : 0.22 + ctx.lineWidth = height * 0.10 + ctx.lineCap = "round" + ctx.stroke() + } + + // Középső pont (antenna) + ctx.beginPath() + ctx.arc(cx, base, dot, 0, Math.PI * 2) + ctx.fillStyle = col + ctx.globalAlpha = 0.95 + ctx.fill() + ctx.globalAlpha = 1.0 + } + + // ── Ethernet dugó ikon ─────────────────────────────────────────── + function drawEthernet(ctx) { + var col = "#4ade80" + var lw = height * 0.09 + ctx.strokeStyle = col + ctx.fillStyle = col + ctx.lineWidth = lw + ctx.lineCap = "round" + ctx.lineJoin = "round" + ctx.globalAlpha = 0.90 + + var cx = width / 2 + var cy = height / 2 + + // Kábel test (felső téglalap) + var bw = width * 0.70 + var bh = height * 0.38 + var bx = cx - bw / 2 + var by = cy - height * 0.30 + rr(ctx, bx, by, bw, bh, height * 0.06) + ctx.stroke() + + // 3 érintkező pin a téglalapban + var pinW = width * 0.08 + var pinH = height * 0.22 + var pinY = by - pinH + lw + var gap = bw / 4 + for (var p = 0; p < 3; p++) { + rr(ctx, bx + gap * (p + 0.5) - pinW/2, pinY, pinW, pinH, 2) + ctx.fill() + } + + // Kábel vonala lefelé + ctx.beginPath() + ctx.moveTo(cx, by + bh) + ctx.lineTo(cx, cy + height * 0.38) + ctx.stroke() + + // Dugó alap vonal + ctx.lineWidth = lw * 1.5 + ctx.beginPath() + ctx.moveTo(cx - width * 0.22, cy + height * 0.38) + ctx.lineTo(cx + width * 0.22, cy + height * 0.38) + ctx.stroke() + + ctx.globalAlpha = 1.0 + } + } + + // Felirat + Text { + id: netLabel + anchors.verticalCenter: parent.verticalCenter + text: { + if (networkIndicator.netType === "ethernet") return "Ethernet" + return networkIndicator.wifiQuality + "%" + } + color: networkIndicator.netType === "ethernet" + ? "#4ade80" + : networkIndicator.signalColor(networkIndicator.wifiQuality) + font.pointSize: root.font.pointSize * 0.82 + font.family: root.font.family + } + + onNetTypeChanged: { + netCanvas.requestPaint() + } + onWifiQualityChanged: { + netCanvas.requestPaint() + } +} diff --git a/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/ParticleOverlay.qml b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/ParticleOverlay.qml new file mode 100644 index 0000000..6fa02f9 --- /dev/null +++ b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/ParticleOverlay.qml @@ -0,0 +1,90 @@ +// 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 + +Canvas { + id: particleCanvas + + anchors.fill: parent + + property int particleCount: 55 + property var particles: [] + property real time: 0 + + function initParticles() { + var arr = [] + for (var i = 0; i < particleCount; i++) { + arr.push({ + x: Math.random() * width, + y: Math.random() * height, + vx: (Math.random() - 0.5) * 0.35, + vy: (Math.random() - 0.5) * 0.35, + baseR: Math.random() * 2.0 + 0.8, + hue: Math.random() * 360, + hueSpeed: (Math.random() - 0.5) * 0.4, + phase: Math.random() * Math.PI * 2, + opacity: Math.random() * 0.45 + 0.15, + glowR: Math.random() * 18 + 8 + }) + } + particles = arr + } + + onPaint: { + var ctx = getContext("2d") + ctx.clearRect(0, 0, width, height) + + for (var i = 0; i < particles.length; i++) { + var p = particles[i] + var r = p.baseR * (0.75 + 0.25 * Math.sin(p.phase + time * 2.2)) + + // Glow halo + var grd = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.glowR) + grd.addColorStop(0, "hsla(" + p.hue + ",100%,72%," + (p.opacity * 0.55) + ")") + grd.addColorStop(1, "hsla(" + p.hue + ",100%,72%,0)") + ctx.beginPath() + ctx.arc(p.x, p.y, p.glowR, 0, Math.PI * 2) + ctx.fillStyle = grd + ctx.fill() + + // Core dot + ctx.beginPath() + ctx.arc(p.x, p.y, r, 0, Math.PI * 2) + ctx.fillStyle = "hsla(" + p.hue + ",100%,88%," + p.opacity + ")" + ctx.fill() + } + } + + Timer { + interval: 16 + repeat: true + running: true + onTriggered: { + particleCanvas.time += 0.016 + var pts = particleCanvas.particles + var W = particleCanvas.width + var H = particleCanvas.height + var t = particleCanvas.time + for (var i = 0; i < pts.length; i++) { + var p = pts[i] + p.x += p.vx + Math.sin(t * 0.9 + p.phase) * 0.28 + p.y += p.vy + Math.cos(t * 0.7 + p.phase) * 0.28 + p.hue += p.hueSpeed + p.phase += 0.008 + var margin = p.glowR + if (p.x < -margin) p.x = W + margin + if (p.x > W + margin) p.x = -margin + if (p.y < -margin) p.y = H + margin + if (p.y > H + margin) p.y = -margin + } + particleCanvas.requestPaint() + } + } + + Component.onCompleted: { + // kis késleltetés hogy a méret biztosan kész legyen + Qt.callLater(initParticles) + } +} diff --git a/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/SessionButton.qml b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/SessionButton.qml new file mode 100644 index 0000000..301256b --- /dev/null +++ b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/SessionButton.qml @@ -0,0 +1,158 @@ +// Config created by Keyitdev https://git.rp1.hu/gabeszm/sddm-rave-theme +// Copyright (C) 2022-2025 Keyitdev +// Based on https://github.com/MarianArlt/sddm-sugar-dark +// Distributed under the GPLv3+ License https://www.gnu.org/licenses/gpl-3.0.html + +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +Item { + id: sessionButton + + height: root.font.pointSize * 2 + width: parent.width / 2 + + property var selectedSession: selectSession.currentIndex + property string textConstantSession + property int loginButtonWidth + property ComboBox exposeSession: selectSession + + ComboBox { + id: selectSession + + // important + // change also in errorMessage + height: root.font.pointSize * 2 + anchors.horizontalCenter: parent.horizontalCenter + + hoverEnabled: true + model: sessionModel + currentIndex: model.lastIndex + textRole: "name" + + Keys.onPressed: function(event) { + if ((event.key == Qt.Key_Left || event.key == Qt.Key_Right) && !popup.opened) { + popup.open(); + } + } + + delegate: ItemDelegate { + // minus padding + width: popupHandler.width - 20 + anchors.horizontalCenter: popupHandler.horizontalCenter + + contentItem: Text { + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + + text: model.name + font.pointSize: root.font.pointSize * 0.8 + font.family: root.font.family + color: config.DropdownTextColor + } + + background: Rectangle { + color: selectSession.highlightedIndex === index ? Qt.rgba(1, 1, 1, 0.15) : "transparent" + radius: config.RoundCorners !== "" ? parseInt(config.RoundCorners) / 4 : 5 + } + } + + indicator { + visible: false + } + + contentItem: Text { + id: displayedItem + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + leftPadding: root.font.pointSize * 0.8 + rightPadding: root.font.pointSize * 0.8 + + text: (config.TranslateSessionSelection || "Session") + ": " + selectSession.currentText + color: config.SessionButtonTextColor + font.pointSize: root.font.pointSize * 0.8 + font.family: root.font.family + + Keys.onReleased: parent.popup.open() + } + + background: Rectangle { + anchors.fill: parent + radius: config.RoundCorners !== "" ? parseInt(config.RoundCorners) : 10 + color: selectSession.down ? Qt.rgba(1, 1, 1, 0.12) : (selectSession.hovered ? Qt.rgba(1, 1, 1, 0.08) : Qt.rgba(1, 1, 1, 0.04)) + border.color: selectSession.visualFocus ? Qt.rgba(1, 1, 1, 0.60) : (selectSession.hovered ? Qt.rgba(1, 1, 1, 0.35) : Qt.rgba(1, 1, 1, 0.18)) + border.width: selectSession.visualFocus ? 1.5 : 1 + + Behavior on color { ColorAnimation { duration: 150 } } + Behavior on border.color { ColorAnimation { duration: 150 } } + } + + popup: Popup { + id: popupHandler + + implicitHeight: contentItem.implicitHeight + width: sessionButton.width + y: parent.height - 1 + x: -popupHandler.width/2 + displayedItem.width/2 + padding: 10 + + contentItem: ListView { + implicitHeight: contentHeight + 20 + + clip: true + model: selectSession.popup.visible ? selectSession.delegateModel : null + currentIndex: selectSession.highlightedIndex + ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + radius: config.RoundCorners !== "" ? parseInt(config.RoundCorners) / 2 : 10 + color: config.BackgroundColor + border.color: Qt.rgba(1, 1, 1, 0.20) + border.width: 1 + } + + enter: Transition { + NumberAnimation { property: "opacity"; from: 0; to: 1 } + } + } + + states: [ + State { + name: "pressed" + when: selectSession.down + PropertyChanges { + target: displayedItem + color: Qt.darker(config.HoverSessionButtonTextColor, 1.1) + } + }, + State { + name: "hovered" + when: selectSession.hovered + PropertyChanges { + target: displayedItem + color: Qt.lighter(config.HoverSessionButtonTextColor, 1.1) + } + }, + State { + name: "focused" + when: selectSession.visualFocus + PropertyChanges { + target: displayedItem + color: config.HoverSessionButtonTextColor + } + } + ] + transitions: [ + Transition { + PropertyAnimation { + properties: "color" + duration: 150 + } + } + ] + + } + +} diff --git a/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/ToastNotification.qml b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/ToastNotification.qml new file mode 100644 index 0000000..43817c4 --- /dev/null +++ b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/ToastNotification.qml @@ -0,0 +1,83 @@ +// 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 + +Item { + id: toastRoot + + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + height: toastRect.height + root.font.pointSize * 2 + z: 100 + + property string message: "" + property bool showing: false + + function show(msg) { + message = msg + showing = true + hideTimer.restart() + } + + function hide() { + showing = false + } + + Rectangle { + id: toastRect + + anchors.horizontalCenter: parent.horizontalCenter + y: toastRoot.showing ? root.font.pointSize * 2 : -height - 10 + + Behavior on y { + NumberAnimation { duration: 380; easing.type: Easing.OutBack } + } + + width: toastRow.implicitWidth + root.font.pointSize * 3 + height: root.font.pointSize * 3 + radius: height / 2 + + color: Qt.rgba(0.75, 0.08, 0.08, 0.88) + border.color: Qt.rgba(1, 0.35, 0.35, 0.65) + border.width: 1 + + // Pulse animation border + SequentialAnimation on border.color { + running: toastRoot.showing + loops: Animation.Infinite + ColorAnimation { to: Qt.rgba(1, 0.5, 0.5, 0.9); duration: 600; easing.type: Easing.InOutSine } + ColorAnimation { to: Qt.rgba(1, 0.25, 0.25, 0.5); duration: 600; easing.type: Easing.InOutSine } + } + + Row { + id: toastRow + anchors.centerIn: parent + spacing: root.font.pointSize * 0.6 + + Text { + anchors.verticalCenter: parent.verticalCenter + text: "⚠" + color: "#ff8888" + font.pointSize: root.font.pointSize * 0.9 + } + + Text { + anchors.verticalCenter: parent.verticalCenter + text: toastRoot.message + color: "#ffffff" + font.pointSize: root.font.pointSize * 0.85 + font.family: root.font.family + } + } + } + + Timer { + id: hideTimer + interval: 3000 + onTriggered: toastRoot.hide() + } +} diff --git a/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/UserSwitcher.qml b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/UserSwitcher.qml new file mode 100644 index 0000000..d16c496 --- /dev/null +++ b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/UserSwitcher.qml @@ -0,0 +1,332 @@ +// 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 + +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 isImagePath(p) { + return p && p !== "" + } + + function getUserIcon(idx) { + // Qt.UserRole+4 = IconRole (ebben az SDDM verziban a Home = +3) + var icon = userModel.data(userModel.index(idx, 0), Qt.UserRole + 4) + return isImagePath(icon) ? icon : "" + } + + 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: 1 + + 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(0.10, 0.10, 0.18, 0.70) + anchors.verticalCenter: parent.verticalCenter + + Image { + id: miniPhoto + anchors.fill: parent + source: userSwitcher.getUserIcon(userSwitcher.selectedIndex) + fillMode: Image.PreserveAspectCrop + smooth: true + visible: false + } + + Item { + id: miniMask + anchors.fill: parent + visible: false + layer.enabled: true + Rectangle { + anchors.fill: parent + radius: parent.width / 2 + color: "black" + } + } + + MultiEffect { + anchors.fill: parent + source: miniPhoto + visible: miniPhoto.status === Image.Ready && miniPhoto.source !== "" + maskEnabled: true + maskSource: miniMask + maskThresholdMin: 0.5 + maskSpreadAtMin: 1.0 + } + + 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 || miniPhoto.source === "" + opacity: 0.80 + } + } + + // Felhasználónév + Text { + anchors.verticalCenter: parent.verticalCenter + text: userSwitcher.selectedName + color: "#ffffff" + 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) : 16 + color: config.BackgroundColor + border.color: Qt.rgba(1, 1, 1, 0.18) + border.width: 1 + } + + // 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: 1 + 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: 2 + 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 : 8 + + // 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 string iconPath: userSwitcher.getUserIcon(index) + + Image { + id: delegatePhoto + anchors.fill: parent + source: parent.iconPath + fillMode: Image.PreserveAspectCrop + smooth: true + visible: false + } + + Item { + id: delegateMask + anchors.fill: parent + visible: false + layer.enabled: true + Rectangle { + anchors.fill: parent + radius: parent.width / 2 + color: "black" + } + } + + MultiEffect { + anchors.fill: parent + source: delegatePhoto + visible: delegatePhoto.status === Image.Ready && parent.iconPath !== "" + maskEnabled: true + maskSource: delegateMask + maskThresholdMin: 0.5 + maskSpreadAtMin: 1.0 + } + + 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 || parent.iconPath === "" + 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: 3 + height: parent.height * 0.55 + radius: 2 + color: Qt.rgba(74/255, 222/255, 128/255, 0.90) + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 2 + } + + MouseArea { + id: delegateMouse + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + userSwitcher.selectedIndex = index + userSwitcher.userSwitched(index, userSwitcher.getUserName(index)) + popup.close() + } + } + } + } + } + } +} diff --git a/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/VirtualKeyboard.qml b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/VirtualKeyboard.qml new file mode 100644 index 0000000..b7b7a0f --- /dev/null +++ b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/VirtualKeyboard.qml @@ -0,0 +1,20 @@ +import QtQuick 2.15 +import QtQuick.VirtualKeyboard +import QtQuick.VirtualKeyboard.Settings + +InputPanel { + id: virtualKeyboard + + property bool activated: false + visible: true + + Component.onCompleted: { + VirtualKeyboardSettings.activeLocales = ["en_US", "hu_HU"] + } + + MouseArea { + anchors.fill: parent + z: -1 + onClicked: {} // Consume clicks so they don't fall through to the background and steal focus + } +} diff --git a/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/VirtualKeyboardButton.qml b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/VirtualKeyboardButton.qml new file mode 100644 index 0000000..90a52b8 --- /dev/null +++ b/raveos-plasma-theme/theme-data/sddm/sddm-rave-theme/Components/VirtualKeyboardButton.qml @@ -0,0 +1,79 @@ +// 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 + +Item { + Button { + id: virtualKeyboardButton + + anchors.horizontalCenter: parent.horizontalCenter + z: 1 + focusPolicy: Qt.TabFocus + + visible: virtualKeyboard.status == Loader.Ready && config.HideVirtualKeyboard == "false" + checkable: true + onClicked: virtualKeyboard.switchState() + + Keys.onReturnPressed: { + toggle(); + virtualKeyboard.switchState(); + } + Keys.onEnterPressed: { + toggle(); + virtualKeyboard.switchState(); + } + + contentItem: Text { + id: virtualKeyboardButtonText + + text: config.TranslateVirtualKeyboardButtonOff || "Virtual Keyboard (off)" + font.pointSize: root.font.pointSize * 0.8 + font.family: root.font.family + color: parent.visualFocus ? config.HoverVirtualKeyboardButtonTextColor : config.VirtualKeyboardButtonTextColor + } + + background: Rectangle { + id: virtualKeyboardButtonBackground + + anchors.fill: parent + radius: config.RoundCorners !== "" ? parseInt(config.RoundCorners) : 10 + color: virtualKeyboardButton.down ? Qt.rgba(1, 1, 1, 0.12) : (virtualKeyboardButton.hovered ? Qt.rgba(1, 1, 1, 0.08) : Qt.rgba(1, 1, 1, 0.04)) + border.color: virtualKeyboardButton.visualFocus ? Qt.rgba(1, 1, 1, 0.60) : (virtualKeyboardButton.hovered ? Qt.rgba(1, 1, 1, 0.35) : Qt.rgba(1, 1, 1, 0.18)) + border.width: virtualKeyboardButton.visualFocus ? 1.5 : 1 + + Behavior on color { ColorAnimation { duration: 150 } } + Behavior on border.color { ColorAnimation { duration: 150 } } + } + states: [ + State { + name: "HoveredAndChecked" + when: virtualKeyboardButton.checked && virtualKeyboardButton.hovered + PropertyChanges { + target: virtualKeyboardButtonText + text: config.TranslateVirtualKeyboardButtonOn || "Virtual Keyboard (on)" + color: config.HoverVirtualKeyboardButtonTextColor + } + }, + State { + name: "checked" + when: virtualKeyboardButton.checked + PropertyChanges { + target: virtualKeyboardButtonText + text: config.TranslateVirtualKeyboardButtonOn || "Virtual Keyboard (on)" + } + }, + State { + name: "hovered" + when: virtualKeyboardButton.hovered + PropertyChanges { + target: virtualKeyboardButtonText + text: config.TranslateVirtualKeyboardButtonOff || "Virtual Keyboard (off)" + color: config.HoverVirtualKeyboardButtonTextColor + } + } + ] + } +} \ No newline at end of file -- cgit v1.3