quickshell-rice/quickshell/modules/topbar/TopRightBar.qml

2572 lines
106 KiB
QML

import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.SystemTray
import "../../components"
import "../../services"
import "../../theme"
GlassPanel {
id: root
implicitWidth: mainLayout.implicitWidth + 24
implicitHeight: 36
property var activeTrayItem: null
property var activeTrayMenuOpener: null
property real activeTrayX: 12
function getCleanAppName(item) {
if (!item) return "Application"
let title = item.title ? item.title.trim() : ""
let idStr = item.id ? item.id.trim() : ""
let raw = title
if (!raw || raw.includes("_status_icon") || raw.includes(".desktop") || raw.includes("org.")) {
raw = idStr
}
if (!raw) raw = idStr
if (!raw) return "Application"
raw = raw.replace(/(_status_icon_\d+|_tray_\d+|_\d+)$/i, "")
if (raw.includes(".")) {
let parts = raw.split(".")
raw = parts[parts.length - 1]
}
raw = raw.replace(/[-_]/g, " ")
raw = raw.replace(/([a-z])([A-Z])/g, "$1 $2")
let words = raw.split(" ").filter(w => w.length > 0).map(w => w.charAt(0).toUpperCase() + w.slice(1))
let result = words.join(" ")
if (result.toLowerCase() === "archupdate") return "Arch Update"
return result || "Application"
}
RowLayout {
id: mainLayout
anchors.fill: parent
anchors.leftMargin: 8
anchors.rightMargin: 8
spacing: 6
// System Tray Section (Active Background Apps)
RowLayout {
spacing: 4
Repeater {
model: SystemTray.items
Rectangle {
id: trayItemRect
width: 24
height: 24
radius: 5
color: trayMouse.containsMouse ? Theme.currentLine : "transparent"
// Continuous Background QsMenuOpener (Caches DBus Menu Items for Instant Display)
QsMenuOpener {
id: itemMenuOpener
menu: modelData.menu
}
Image {
anchors.centerIn: parent
width: 16
height: 16
source: modelData.icon
fillMode: Image.PreserveAspectFit
}
MouseArea {
id: trayMouse
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: (mouse) => {
if (mouse.button === Qt.LeftButton) {
modelData.activate()
} else if (mouse.button === Qt.RightButton) {
root.activeTrayItem = modelData
root.activeTrayMenuOpener = itemMenuOpener
var mapped = trayItemRect.mapToItem(root, 0, 0)
root.activeTrayX = mapped.x
PopupService.toggleTray()
}
}
}
}
}
}
// Vertical Separator between Running Apps (System Tray) and Notification Center
Rectangle {
Layout.preferredWidth: 1
Layout.preferredHeight: 16
color: Theme.separator
Layout.leftMargin: 4
Layout.rightMargin: 4
visible: true
}
// Notification Center Icon Snippet (Papirus Panel Icon & Dynamic Unread/DND SVG)
Rectangle {
id: notifBtn
Layout.preferredWidth: 26
Layout.preferredHeight: 26
radius: 6
color: notifMouse.containsMouse ? Theme.currentLine : "transparent"
Behavior on color { ColorAnimation { duration: 120 } }
Image {
anchors.centerIn: parent
width: 16
height: 16
sourceSize.width: 16
sourceSize.height: 16
fillMode: Image.PreserveAspectFit
source: {
let base = "file:///usr/share/icons/" + Theme.panelIconDir + "/24x24/panel/"
let hasUnread = NotificationService.notifications.length > 0
if (NotificationService.isDnd) {
return hasUnread ? base + "indicator-notification-unread-dnd.svg" : base + "indicator-notification-read-dnd.svg"
} else {
return hasUnread ? base + "indicator-notification-unread.svg" : base + "indicator-notification-read.svg"
}
}
}
MouseArea {
id: notifMouse
anchors.fill: parent
hoverEnabled: true
onClicked: PopupService.toggleNotification()
}
}
// Screenshot & Screen Recording Icon Snippet (Authentic Papirus Panel SVG)
Rectangle {
id: captureBtn
Layout.preferredWidth: 26
Layout.preferredHeight: 26
radius: 6
color: captureMouse.containsMouse ? Theme.currentLine : "transparent"
Behavior on color { ColorAnimation { duration: 120 } }
Image {
anchors.centerIn: parent
width: 16
height: 16
sourceSize.width: 16
sourceSize.height: 16
fillMode: Image.PreserveAspectFit
source: "file:///usr/share/icons/" + Theme.panelIconDir + "/24x24/panel/record-desktop-indicator.svg"
}
MouseArea {
id: captureMouse
anchors.fill: parent
hoverEnabled: true
onClicked: PopupService.toggleCapture()
}
}
// Clipboard Manager Icon Snippet
Rectangle {
id: clipBtn
Layout.preferredWidth: 26
Layout.preferredHeight: 26
radius: 6
color: clipMouse.containsMouse ? Theme.currentLine : "transparent"
Behavior on color { ColorAnimation { duration: 120 } }
Image {
anchors.centerIn: parent
width: 16
height: 16
sourceSize.width: 16
sourceSize.height: 16
fillMode: Image.PreserveAspectFit
source: "file:///usr/share/icons/" + Theme.panelIconDir + "/24x24/panel/clipboard.svg"
}
MouseArea {
id: clipMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
ClipboardService.scanClipboard()
PopupService.toggleClipboard()
}
}
}
// Audio Icon Snippet (Icon-only, Scroll Wheel + Click Popup)
Rectangle {
id: audioBtn
Layout.preferredWidth: 26
Layout.preferredHeight: 26
radius: 6
color: audioMouse.containsMouse ? Theme.currentLine : "transparent"
Behavior on color { ColorAnimation { duration: 120 } }
VolumeIcon {
anchors.centerIn: parent
volume: AudioService.volume
isMuted: AudioService.isMuted
width: 16
height: 16
}
MouseArea {
id: audioMouse
anchors.fill: parent
hoverEnabled: true
onClicked: PopupService.toggleAudio()
onWheel: (wheel) => {
if (wheel.angleDelta.y > 0) {
AudioService.volumeUp()
} else if (wheel.angleDelta.y < 0) {
AudioService.volumeDown()
}
}
}
}
// Bluetooth Icon Snippet (Placed to the Right of Audio Control)
Rectangle {
id: btBtn
Layout.preferredWidth: 26
Layout.preferredHeight: 26
radius: 6
color: btMouse.containsMouse ? Theme.currentLine : "transparent"
Behavior on color { ColorAnimation { duration: 120 } }
BluetoothIcon {
anchors.centerIn: parent
isPowered: BluetoothService.isPowered
isConnected: BluetoothService.connectedDevices.length > 0
width: 16
height: 16
}
MouseArea {
id: btMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
BluetoothService.scanBluetooth()
PopupService.toggleBluetooth()
}
}
}
// Screen Brightness Icon Snippet
Rectangle {
id: brightBtn
Layout.preferredWidth: 26
Layout.preferredHeight: 26
radius: 6
color: brightMouse.containsMouse ? Theme.currentLine : "transparent"
Behavior on color { ColorAnimation { duration: 120 } }
BrightnessIcon {
anchors.centerIn: parent
brightness: BrightnessService.masterBrightness
width: 16
height: 16
}
MouseArea {
id: brightMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
BrightnessService.scanDevices()
PopupService.toggleBrightness()
}
onWheel: (wheel) => {
if (wheel.angleDelta.y > 0) {
BrightnessService.brightnessUp()
} else if (wheel.angleDelta.y < 0) {
BrightnessService.brightnessDown()
}
}
}
}
// External Storage Mount Icon Snippet (Placed to the Right of Brightness)
Rectangle {
id: mountBtn
Layout.preferredWidth: 26
Layout.preferredHeight: 26
radius: 6
color: mountMouse.containsMouse ? Theme.currentLine : "transparent"
Behavior on color { ColorAnimation { duration: 120 } }
Image {
anchors.centerIn: parent
width: 16
height: 16
sourceSize.width: 16
sourceSize.height: 16
fillMode: Image.PreserveAspectFit
source: "file:///usr/share/icons/" + Theme.panelIconDir + "/24x24/panel/drive-removable-media-usb-panel.svg"
}
MouseArea {
id: mountMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
MountService.scanDevices()
PopupService.toggleMount()
}
}
}
// Network Icon Snippet (Ethernet & Wi-Fi Dynamic Badge)
Rectangle {
id: netBtn
Layout.preferredWidth: 26
Layout.preferredHeight: 26
radius: 6
color: netMouse.containsMouse ? Theme.currentLine : "transparent"
Behavior on color { ColorAnimation { duration: 120 } }
NetworkIcon {
anchors.centerIn: parent
isConnected: NetworkService.isConnected
signalPercent: NetworkService.signalPercent
isEthernet: NetworkService.ethernetConnected
isWifiPowered: NetworkService.isWifiPowered
width: 16
height: 16
}
MouseArea {
id: netMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
NetworkService.scanWifi()
PopupService.toggleNetwork()
}
}
}
// Battery Icon Snippet (Icon-only, Clickable)
Rectangle {
id: batBtn
Layout.preferredWidth: 26
Layout.preferredHeight: 26
radius: 6
color: batMouse.containsMouse ? Theme.currentLine : "transparent"
Behavior on color { ColorAnimation { duration: 120 } }
BatteryIcon {
anchors.centerIn: parent
percentage: BatteryService.percentage
isCharging: BatteryService.isCharging
width: 16
height: 16
}
MouseArea {
id: batMouse
anchors.fill: parent
hoverEnabled: true
onClicked: PopupService.toggleBattery()
}
}
}
// Screenshot & Screen Recording Control Detached Popup Window
PopupWindow {
id: captureMenu
anchor.window: window
anchor.rect.x: Math.round(root.x + root.width - implicitWidth)
anchor.rect.y: 40
anchor.edges: Edges.Bottom | Edges.Right
visible: PopupService.captureMenuOpen
color: "transparent"
implicitWidth: capGlass.implicitWidth
implicitHeight: capGlass.implicitHeight
GlassPanel {
id: capGlass
implicitWidth: 280
implicitHeight: Math.min(420, capCardLayout.implicitHeight + 24)
anchors.fill: parent
ColumnLayout {
id: capCardLayout
anchors.fill: parent
anchors.margins: 12
spacing: 10
// Header
Text {
text: "Screen Capture & Recording"
color: Theme.fg
font.pixelSize: 12
font.weight: Font.Bold
}
// Solid Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
}
// Section 1: Screenshots
Text {
text: "TAKE SCREENSHOT"
color: Theme.comment
font.pixelSize: 9
font.weight: Font.Bold
}
ColumnLayout {
Layout.fillWidth: true
spacing: 4
// 1. Region / Selection Screenshot
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 28
radius: 5
color: regShotMouse.containsMouse ? Theme.currentLine : (Theme.isDark ? Theme.currentLine : Qt.rgba(Theme.fg.r, Theme.fg.g, Theme.fg.b, 0.08))
border.color: Theme.currentLine
border.width: 1
RowLayout {
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
spacing: 8
Text { text: "✂️"; font.pixelSize: 11; Layout.alignment: Qt.AlignVCenter }
Text { text: "Region Screenshot"; color: Theme.fg; font.pixelSize: 10; font.weight: Font.Medium; Layout.fillWidth: true }
Text { text: "Super+Shift+S"; color: Theme.comment; font.pixelSize: 8 }
}
MouseArea {
id: regShotMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
PopupService.closeAll()
CaptureService.captureRegion()
}
}
}
// 2. Fullscreen Screenshot
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 28
radius: 5
color: fullShotMouse.containsMouse ? Theme.currentLine : (Theme.isDark ? Theme.currentLine : Qt.rgba(Theme.fg.r, Theme.fg.g, Theme.fg.b, 0.08))
border.color: Theme.currentLine
border.width: 1
RowLayout {
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
spacing: 8
Text { text: "🖥️"; font.pixelSize: 11; Layout.alignment: Qt.AlignVCenter }
Text { text: "Fullscreen Screenshot"; color: Theme.fg; font.pixelSize: 10; font.weight: Font.Medium; Layout.fillWidth: true }
Text { text: "PrintScreen"; color: Theme.comment; font.pixelSize: 8 }
}
MouseArea {
id: fullShotMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
PopupService.closeAll()
CaptureService.captureFullscreen()
}
}
}
// 3. Active Window Screenshot
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 28
radius: 5
color: winShotMouse.containsMouse ? Theme.currentLine : (Theme.isDark ? Theme.currentLine : Qt.rgba(Theme.fg.r, Theme.fg.g, Theme.fg.b, 0.08))
border.color: Theme.currentLine
border.width: 1
RowLayout {
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
spacing: 8
Text { text: "🪟"; font.pixelSize: 11; Layout.alignment: Qt.AlignVCenter }
Text { text: "Active Window"; color: Theme.fg; font.pixelSize: 10; font.weight: Font.Medium; Layout.fillWidth: true }
Text { text: "Super+Print"; color: Theme.comment; font.pixelSize: 8 }
}
MouseArea {
id: winShotMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
PopupService.closeAll()
CaptureService.captureWindow()
}
}
}
}
// Solid Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
}
// Section 2: Screen Recording
Text {
text: "RECORD VIDEO"
color: Theme.comment
font.pixelSize: 9
font.weight: Font.Bold
}
ColumnLayout {
Layout.fillWidth: true
spacing: 4
// 4. Region Screen Recording
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 28
radius: 5
color: regRecMouse.containsMouse ? Theme.currentLine : (Theme.isDark ? Theme.currentLine : Qt.rgba(Theme.fg.r, Theme.fg.g, Theme.fg.b, 0.08))
border.color: Theme.currentLine
border.width: 1
RowLayout {
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
spacing: 8
Text { text: "🎥"; font.pixelSize: 11; Layout.alignment: Qt.AlignVCenter }
Text { text: "Record Region"; color: Theme.fg; font.pixelSize: 10; font.weight: Font.Medium; Layout.fillWidth: true }
Text { text: "Super+Alt+R"; color: Theme.comment; font.pixelSize: 8 }
}
MouseArea {
id: regRecMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
PopupService.closeAll()
CaptureService.recordRegion()
}
}
}
// 5. Fullscreen Screen Recording
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 28
radius: 5
color: fullRecMouse.containsMouse ? Theme.currentLine : (Theme.isDark ? Theme.currentLine : Qt.rgba(Theme.fg.r, Theme.fg.g, Theme.fg.b, 0.08))
border.color: Theme.currentLine
border.width: 1
RowLayout {
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
spacing: 8
Text { text: "📽️"; font.pixelSize: 11; Layout.alignment: Qt.AlignVCenter }
Text { text: "Record Screen"; color: Theme.fg; font.pixelSize: 10; font.weight: Font.Medium; Layout.fillWidth: true }
Text { text: "Super+Alt+F"; color: Theme.comment; font.pixelSize: 8 }
}
MouseArea {
id: fullRecMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
PopupService.closeAll()
CaptureService.recordScreen()
}
}
}
}
// Solid Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
}
// Section 3: Open Spectacle Full GUI Utility (Text Only)
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 28
radius: 5
color: guiBtnMouse.containsMouse ? Theme.currentLine : "transparent"
border.color: Theme.accent
border.width: 1
Text {
anchors.centerIn: parent
text: "Open Spectacle Studio"
color: Theme.accent
font.pixelSize: 10
font.weight: Font.Bold
}
MouseArea {
id: guiBtnMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
PopupService.closeAll()
Quickshell.execDetached(["spectacle", "-g"])
}
}
}
}
}
}
// Detached Notification Center Popup Window (Controlled by PopupService)
PopupWindow {
id: notifMenu
anchor.window: window
anchor.rect.x: root.x + (root.width / 2) - (implicitWidth / 2)
anchor.rect.y: 42
anchor.edges: Edges.Bottom
visible: PopupService.notificationMenuOpen
color: "transparent"
implicitWidth: notifGlass.implicitWidth
implicitHeight: notifGlass.implicitHeight
GlassPanel {
id: notifGlass
implicitWidth: 320
implicitHeight: notifLayout.implicitHeight + 20
anchors.fill: parent
ColumnLayout {
id: notifLayout
anchors.fill: parent
anchors.margins: 10
spacing: 8
// Header with DND Toggle & Clear All
RowLayout {
Layout.fillWidth: true
Text {
text: "Notifications"
color: Theme.fg
font.pixelSize: 12
font.weight: Font.Bold
}
Item { Layout.fillWidth: true }
// DND Toggle Button
Rectangle {
width: 58; height: 20; radius: 4
color: NotificationService.isDnd ? Qt.rgba(Theme.subAccent.r, Theme.subAccent.g, Theme.subAccent.b, 0.2) : (dndBtnMouse.containsMouse ? Theme.currentLine : (Theme.isDark ? Theme.currentLine : Qt.rgba(Theme.fg.r, Theme.fg.g, Theme.fg.b, 0.08)))
border.color: NotificationService.isDnd ? Theme.subAccent : Theme.currentLine
border.width: 1
Text {
anchors.centerIn: parent
text: NotificationService.isDnd ? "DND: ON" : "DND: OFF"
color: NotificationService.isDnd ? Theme.subAccent : Theme.comment
font.pixelSize: 9
font.weight: Font.Bold
}
MouseArea {
id: dndBtnMouse
anchors.fill: parent
hoverEnabled: true
onClicked: NotificationService.toggleDnd()
}
}
Rectangle {
width: 50; height: 20; radius: 4
color: clearNotifMouse.containsMouse ? Qt.rgba(255/255, 85/255, 85/255, 0.2) : "transparent"
border.color: Theme.red
border.width: 1
Text {
anchors.centerIn: parent
text: "Clear"
color: Theme.red
font.pixelSize: 9
font.weight: Font.Bold
}
MouseArea {
id: clearNotifMouse
anchors.fill: parent
hoverEnabled: true
onClicked: NotificationService.clearAll()
}
}
}
// Solid Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
}
// Scrollable List of Notifications
Flickable {
Layout.fillWidth: true
Layout.preferredHeight: Math.min(380, notifListCol.implicitHeight)
contentHeight: notifListCol.implicitHeight
clip: true
ColumnLayout {
id: notifListCol
width: parent.width
spacing: 6
// Empty State
Text {
text: NotificationService.isDnd ? "Do Not Disturb is active" : "No new notifications"
color: Theme.comment
font.pixelSize: 10
font.italic: true
visible: NotificationService.notifications.length === 0
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 4
Layout.bottomMargin: 4
}
Repeater {
model: NotificationService.notifications
Rectangle {
Layout.fillWidth: true
implicitHeight: notifItemCol.implicitHeight + 12
radius: 6
color: Theme.surface
border.color: Theme.currentLine
border.width: 1
ColumnLayout {
id: notifItemCol
anchors.fill: parent
anchors.margins: 8
spacing: 4
RowLayout {
Layout.fillWidth: true
// Dynamically Sized App Source Badge Box
Rectangle {
implicitWidth: appText.implicitWidth + 12
implicitHeight: 18
radius: 4
color: Qt.rgba(Theme.accent.r, Theme.accent.g, Theme.accent.b, 0.18)
border.color: Theme.accent
border.width: 1
Text {
id: appText
anchors.centerIn: parent
text: modelData.app.toUpperCase()
color: Theme.accent
font.pixelSize: 8
font.weight: Font.Bold
}
}
Item { Layout.fillWidth: true }
Text {
text: modelData.time
color: Theme.comment
font.pixelSize: 9
}
Text {
text: "✕"
color: delNotifMouse.containsMouse ? Theme.red : Theme.comment
font.pixelSize: 10
MouseArea {
id: delNotifMouse
anchors.fill: parent
hoverEnabled: true
onClicked: NotificationService.dismissNotification(modelData.id)
}
}
}
Text {
text: modelData.summary
color: Theme.fg
font.pixelSize: 11
font.weight: Font.Bold
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: modelData.body
color: Theme.comment
font.pixelSize: 10
maximumLineCount: 3
wrapMode: Text.WrapAnywhere
elide: Text.ElideRight
visible: modelData.body !== ""
Layout.fillWidth: true
}
}
}
}
}
}
}
}
}
// Clipboard Manager Detached Popup Window
PopupWindow {
id: clipboardMenu
anchor.window: window
anchor.rect.x: Math.round(root.x + root.width - implicitWidth)
anchor.rect.y: 40
anchor.edges: Edges.Bottom | Edges.Right
visible: PopupService.clipboardMenuOpen
color: "transparent"
implicitWidth: clipGlass.implicitWidth
implicitHeight: clipGlass.implicitHeight
GlassPanel {
id: clipGlass
implicitWidth: 320
implicitHeight: Math.min(500, clipCardLayout.implicitHeight + 24)
anchors.fill: parent
ColumnLayout {
id: clipCardLayout
anchors.fill: parent
anchors.margins: 12
spacing: 10
// Header
RowLayout {
Layout.fillWidth: true
Text {
text: "Clipboard Manager"
color: Theme.fg
font.pixelSize: 12
font.weight: Font.Bold
}
Item { Layout.fillWidth: true }
Rectangle {
width: 54; height: 20; radius: 4
color: clearClipMouse.containsMouse ? Qt.rgba(255/255, 85/255, 85/255, 0.2) : "transparent"
border.color: Theme.red
border.width: 1
Text {
anchors.centerIn: parent
text: "Clear"
color: Theme.red
font.pixelSize: 9
font.weight: Font.Bold
}
MouseArea {
id: clearClipMouse
anchors.fill: parent
hoverEnabled: true
onClicked: ClipboardService.clearAll()
}
}
}
// Solid Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
}
// Scrollable List of Clipboard Items
Flickable {
Layout.fillWidth: true
Layout.preferredHeight: Math.min(380, clipListCol.implicitHeight)
contentHeight: clipListCol.implicitHeight
clip: true
ColumnLayout {
id: clipListCol
width: parent.width
spacing: 6
// Empty State
Text {
text: "Clipboard is empty"
color: Theme.comment
font.pixelSize: 10
font.italic: true
visible: ClipboardService.items.length === 0
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 4
Layout.bottomMargin: 4
}
Repeater {
model: ClipboardService.items
Rectangle {
Layout.fillWidth: true
implicitHeight: itemColLayout.implicitHeight + 12
radius: 6
color: clipItemMouse.containsMouse ? Theme.currentLine : Theme.surface
border.color: Theme.currentLine
border.width: 1
ColumnLayout {
id: itemColLayout
anchors.fill: parent
anchors.margins: 8
spacing: 6
RowLayout {
Layout.fillWidth: true
Rectangle {
width: 48; height: 16; radius: 3
color: modelData.type === "image" ? Qt.rgba(Theme.subAccent.r, Theme.subAccent.g, Theme.subAccent.b, 0.2) : Qt.rgba(Theme.accent.r, Theme.accent.g, Theme.accent.b, 0.2)
Text {
anchors.centerIn: parent
text: modelData.type === "image" ? "IMAGE" : "TEXT"
color: modelData.type === "image" ? Theme.subAccent : Theme.accent
font.pixelSize: 8
font.weight: Font.Bold
}
}
Text {
text: modelData.type === "image" ? modelData.size : (modelData.length + " chars")
color: Theme.comment
font.pixelSize: 9
}
Item { Layout.fillWidth: true }
Text {
text: modelData.time
color: Theme.comment
font.pixelSize: 9
}
Text {
text: "✕"
color: delMouse.containsMouse ? Theme.red : Theme.comment
font.pixelSize: 10
MouseArea {
id: delMouse
anchors.fill: parent
hoverEnabled: true
onClicked: ClipboardService.deleteEntry(modelData.id)
}
}
}
Image {
visible: modelData.type === "image"
source: modelData.type === "image" ? ("file://" + modelData.path) : ""
Layout.fillWidth: true
Layout.preferredHeight: 100
fillMode: Image.PreserveAspectFit
horizontalAlignment: Image.AlignLeft
}
Text {
visible: modelData.type === "text"
text: modelData.type === "text" ? modelData.content : ""
color: Theme.fg
font.pixelSize: 10
maximumLineCount: 4
wrapMode: Text.WrapAnywhere
elide: Text.ElideRight
Layout.fillWidth: true
}
}
MouseArea {
id: clipItemMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
ClipboardService.copyEntry(modelData.id)
PopupService.closeAll()
}
}
}
}
}
}
}
}
}
// Bluetooth Control Detached Popup Window
PopupWindow {
id: bluetoothMenu
anchor.window: window
anchor.rect.x: Math.round(root.x + root.width - implicitWidth)
anchor.rect.y: 40
anchor.edges: Edges.Bottom | Edges.Right
visible: PopupService.bluetoothMenuOpen
color: "transparent"
implicitWidth: btGlass.implicitWidth
implicitHeight: btGlass.implicitHeight
GlassPanel {
id: btGlass
implicitWidth: 300
implicitHeight: BluetoothService.isPowered ? 240 : 80
anchors.fill: parent
ColumnLayout {
id: btCardLayout
anchors.fill: parent
anchors.margins: 12
spacing: 10
// Header: Title + Power Toggle + Refresh Button
RowLayout {
Layout.fillWidth: true
Text {
text: "Bluetooth"
color: Theme.fg
font.pixelSize: 12
font.weight: Font.Bold
}
Item { Layout.fillWidth: true }
// Power ON/OFF Button
Rectangle {
width: 50; height: 20; radius: 4
color: BluetoothService.isPowered ? (Theme.isDark ? Qt.rgba(80/255, 250/255, 123/255, 0.2) : Qt.rgba(34/255, 197/255, 94/255, 0.25)) : (Theme.isDark ? Qt.rgba(255/255, 85/255, 85/255, 0.2) : Qt.rgba(239/255, 68/255, 68/255, 0.25))
border.color: BluetoothService.isPowered ? (Theme.isDark ? Theme.green : "#15803d") : (Theme.isDark ? Theme.red : "#b91c1c")
border.width: 1
Text {
anchors.centerIn: parent
text: BluetoothService.isPowered ? "ON" : "OFF"
color: BluetoothService.isPowered ? (Theme.isDark ? Theme.green : "#15803d") : (Theme.isDark ? Theme.red : "#b91c1c")
font.pixelSize: 9
font.weight: Font.Bold
}
MouseArea {
id: btPwrMouse
anchors.fill: parent
hoverEnabled: true
onClicked: BluetoothService.togglePower()
}
}
Rectangle {
width: 20; height: 20; radius: 4
color: refreshBtMouse.containsMouse ? Theme.currentLine : "transparent"
Text { text: "↻"; color: Theme.comment; font.pixelSize: 12; anchors.centerIn: parent }
MouseArea {
id: refreshBtMouse
anchors.fill: parent
hoverEnabled: true
onClicked: BluetoothService.scanBluetooth()
}
}
}
// Solid Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
}
// Off State Banner
Item {
Layout.fillWidth: true
Layout.fillHeight: true
visible: !BluetoothService.isPowered
Text {
anchors.centerIn: parent
text: "Bluetooth is turned off"
color: Theme.comment
font.pixelSize: 11
font.italic: true
}
}
Flickable {
Layout.fillWidth: true
Layout.fillHeight: true
contentHeight: btScrollCol.implicitHeight
clip: true
visible: BluetoothService.isPowered
ColumnLayout {
id: btScrollCol
width: parent.width
spacing: 8
// Connected Devices Section
Text {
text: "CONNECTED DEVICES"
color: Theme.comment
font.pixelSize: 9
font.weight: Font.Bold
}
ColumnLayout {
Layout.fillWidth: true
spacing: 3
Text {
text: "No connected devices"
color: Theme.comment
font.pixelSize: 10
font.italic: true
visible: BluetoothService.connectedDevices.length === 0
}
Repeater {
model: BluetoothService.connectedDevices
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 28
radius: 5
color: Qt.rgba(80/255, 250/255, 123/255, 0.15)
border.color: Theme.green
border.width: 1
RowLayout {
anchors.fill: parent
anchors.leftMargin: 8
anchors.rightMargin: 8
spacing: 6
Rectangle { width: 6; height: 6; radius: 3; color: Theme.green }
Text {
text: modelData.name
color: Theme.green
font.pixelSize: 10
font.weight: Font.Bold
elide: Text.ElideRight
Layout.fillWidth: true
}
Rectangle {
width: 58; height: 18; radius: 3
color: disconnMouse.containsMouse ? Theme.currentLine : "transparent"
border.color: Theme.red
border.width: 1
Text {
anchors.centerIn: parent
text: "Disconnect"
color: Theme.red
font.pixelSize: 8
font.weight: Font.Bold
}
MouseArea {
id: disconnMouse
anchors.fill: parent
hoverEnabled: true
onClicked: BluetoothService.disconnectDevice(modelData.mac)
}
}
}
}
}
}
// Available / Paired Devices Section
Text {
text: "AVAILABLE / PAIRED DEVICES"
color: Theme.comment
font.pixelSize: 9
font.weight: Font.Bold
}
ColumnLayout {
Layout.fillWidth: true
spacing: 3
Text {
text: "No available devices"
color: Theme.comment
font.pixelSize: 10
font.italic: true
visible: BluetoothService.availableDevices.length === 0
}
Repeater {
model: BluetoothService.availableDevices
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 28
radius: 5
color: btDevMouse.containsMouse ? Theme.currentLine : "transparent"
RowLayout {
anchors.fill: parent
anchors.leftMargin: 8
anchors.rightMargin: 8
spacing: 6
Rectangle { width: 6; height: 6; radius: 3; color: Theme.comment }
Text {
text: modelData.name
color: Theme.fg
font.pixelSize: 10
elide: Text.ElideRight
Layout.fillWidth: true
}
Rectangle {
width: 46; height: 18; radius: 3
color: connMouse.containsMouse ? Qt.rgba(Theme.accent.r, Theme.accent.g, Theme.accent.b, 0.2) : "transparent"
border.color: Theme.accent
border.width: 1
Text {
anchors.centerIn: parent
text: "Connect"
color: Theme.accent
font.pixelSize: 8
font.weight: Font.Bold
}
MouseArea {
id: connMouse
anchors.fill: parent
hoverEnabled: true
onClicked: BluetoothService.connectDevice(modelData.mac)
}
}
}
MouseArea {
id: btDevMouse
anchors.fill: parent
hoverEnabled: true
}
}
}
}
}
}
}
}
}
// Audio Control & Device Selector Detached Popup Window
PopupWindow {
id: audioMenu
anchor.window: window
anchor.rect.x: Math.round(root.x + root.width - implicitWidth)
anchor.rect.y: 40
anchor.edges: Edges.Bottom | Edges.Right
visible: PopupService.audioMenuOpen
color: "transparent"
implicitWidth: audioGlass.implicitWidth
implicitHeight: audioGlass.implicitHeight
GlassPanel {
id: audioGlass
implicitWidth: 300
implicitHeight: Math.min(480, audioCardLayout.implicitHeight + 24)
anchors.fill: parent
ColumnLayout {
id: audioCardLayout
anchors.fill: parent
anchors.margins: 12
spacing: 10
// Card Header: Volume Title + Percentage
RowLayout {
Layout.fillWidth: true
Text {
text: "Master Volume"
color: Theme.fg
font.pixelSize: 12
font.weight: Font.Bold
}
Item { Layout.fillWidth: true }
Text {
text: AudioService.volumeStr
color: Theme.accent
font.pixelSize: 11
font.weight: Font.Bold
}
}
// Volume Slider Track
Item {
id: volSliderTrack
Layout.fillWidth: true
Layout.preferredHeight: 16
property bool isDragging: volSliderMouse.pressed
Rectangle {
anchors.centerIn: parent
width: parent.width
height: 4
radius: 2
color: Theme.currentLine
Rectangle {
width: Math.max(4, parent.width * (AudioService.volume / 100.0))
height: parent.height
radius: 2
color: AudioService.isMuted ? Theme.red : Theme.accent
}
}
// Circle Slider Knob Handle
Rectangle {
width: volSliderMouse.containsMouse || volSliderTrack.isDragging ? 12 : 8
height: width
radius: width / 2
color: AudioService.isMuted ? Theme.red : Theme.accent
border.color: Theme.bg
border.width: 1
anchors.verticalCenter: parent.verticalCenter
x: Math.max(0, Math.min(volSliderTrack.width - width, (volSliderTrack.width * (AudioService.volume / 100.0)) - (width / 2)))
}
MouseArea {
id: volSliderMouse
anchors.fill: parent
hoverEnabled: true
onPositionChanged: (mouse) => {
if (pressed) {
var ratio = Math.max(0.0, Math.min(1.0, mouse.x / width))
AudioService.setVolume(ratio * 100)
}
}
onPressed: (mouse) => {
var ratio = Math.max(0.0, Math.min(1.0, mouse.x / width))
AudioService.setVolume(ratio * 100)
}
}
}
// Mute Toggle Button
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 26
radius: 5
color: muteBtnMouse.containsMouse ? Theme.currentLine : (Theme.isDark ? Theme.currentLine : Qt.rgba(Theme.fg.r, Theme.fg.g, Theme.fg.b, 0.08))
border.color: Theme.currentLine
border.width: 1
RowLayout {
anchors.centerIn: parent
spacing: 6
VolumeIcon {
volume: AudioService.volume
isMuted: AudioService.isMuted
width: 14
height: 14
}
Text {
text: AudioService.isMuted ? "Unmute Audio" : "Mute Audio"
color: AudioService.isMuted ? Theme.red : Theme.fg
font.pixelSize: 10
font.weight: Font.Medium
}
}
MouseArea {
id: muteBtnMouse
anchors.fill: parent
hoverEnabled: true
onClicked: AudioService.toggleMute()
}
}
// Solid Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
}
Flickable {
Layout.fillWidth: true
Layout.preferredHeight: Math.min(320, audioScrollCol.implicitHeight)
contentHeight: audioScrollCol.implicitHeight
clip: true
ColumnLayout {
id: audioScrollCol
width: parent.width
spacing: 8
// Output Devices Section
Text {
text: "OUTPUT DEVICES"
color: Theme.comment
font.pixelSize: 9
font.weight: Font.Bold
}
ColumnLayout {
Layout.fillWidth: true
spacing: 3
Repeater {
model: AudioService.sinks
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 24
radius: 4
color: modelData.isActive ? Qt.rgba(Theme.accent.r, Theme.accent.g, Theme.accent.b, 0.18) : (sinkItemMouse.containsMouse ? Theme.surface : "transparent")
border.color: modelData.isActive ? Theme.accent : "transparent"
border.width: 1
RowLayout {
anchors.fill: parent
anchors.leftMargin: 6
anchors.rightMargin: 6
spacing: 6
Rectangle {
width: 6; height: 6; radius: 3
color: modelData.isActive ? Theme.accent : Theme.comment
}
Text {
text: modelData.name
color: modelData.isActive ? (Theme.isDark ? Theme.accent : Theme.fg) : Theme.fg
font.pixelSize: 10
font.weight: modelData.isActive ? Font.Bold : Font.Normal
elide: Text.ElideRight
Layout.fillWidth: true
}
}
MouseArea {
id: sinkItemMouse
anchors.fill: parent
hoverEnabled: true
onClicked: AudioService.setDefaultDevice(modelData.id)
}
}
}
}
// Solid Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
}
// Input Devices Section
Text {
text: "INPUT DEVICES"
color: Theme.comment
font.pixelSize: 9
font.weight: Font.Bold
}
ColumnLayout {
Layout.fillWidth: true
spacing: 3
Repeater {
model: AudioService.sources
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 24
radius: 4
color: modelData.isActive ? (Theme.isDark ? Qt.rgba(80/255, 250/255, 123/255, 0.15) : Qt.rgba(80/255, 250/255, 123/255, 0.22)) : (srcItemMouse.containsMouse ? Theme.surface : "transparent")
border.color: modelData.isActive ? (Theme.isDark ? Theme.green : "#15803d") : "transparent"
border.width: 1
RowLayout {
anchors.fill: parent
anchors.leftMargin: 6
anchors.rightMargin: 6
spacing: 6
Rectangle {
width: 6; height: 6; radius: 3
color: modelData.isActive ? (Theme.isDark ? Theme.green : "#15803d") : Theme.comment
}
Text {
text: modelData.name
color: modelData.isActive ? (Theme.isDark ? Theme.green : "#15803d") : Theme.fg
font.pixelSize: 10
font.weight: modelData.isActive ? Font.Bold : Font.Normal
elide: Text.ElideRight
Layout.fillWidth: true
}
}
MouseArea {
id: srcItemMouse
anchors.fill: parent
hoverEnabled: true
onClicked: AudioService.setDefaultDevice(modelData.id)
}
}
}
}
}
}
}
}
}
// Screen & Backlight Brightness Control Detached Popup Window
PopupWindow {
id: brightMenu
anchor.window: window
anchor.rect.x: Math.round(root.x + root.width - implicitWidth)
anchor.rect.y: 40
anchor.edges: Edges.Bottom | Edges.Right
visible: PopupService.brightnessMenuOpen
color: "transparent"
implicitWidth: brightGlass.implicitWidth
implicitHeight: brightGlass.implicitHeight
GlassPanel {
id: brightGlass
implicitWidth: 300
implicitHeight: Math.min(400, brightCardLayout.implicitHeight + 24)
anchors.fill: parent
ColumnLayout {
id: brightCardLayout
anchors.fill: parent
anchors.margins: 12
spacing: 12
// Card Header: Title + Refresh Button
RowLayout {
Layout.fillWidth: true
Text {
text: "Brightness Controls"
color: Theme.fg
font.pixelSize: 12
font.weight: Font.Bold
}
Item { Layout.fillWidth: true }
Rectangle {
width: 20; height: 20; radius: 4
color: refreshBrightMouse.containsMouse ? Theme.currentLine : "transparent"
Text { text: "↻"; color: Theme.comment; font.pixelSize: 12; anchors.centerIn: parent }
MouseArea {
id: refreshBrightMouse
anchors.fill: parent
hoverEnabled: true
onClicked: BrightnessService.scanDevices()
}
}
}
// Solid Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
}
Flickable {
Layout.fillWidth: true
Layout.preferredHeight: Math.min(300, brightScrollCol.implicitHeight)
contentHeight: brightScrollCol.implicitHeight
clip: true
ColumnLayout {
id: brightScrollCol
width: parent.width
spacing: 12
Repeater {
model: BrightnessService.devices
ColumnLayout {
Layout.fillWidth: true
spacing: 4
// Device Label & Percentage Readout
RowLayout {
Layout.fillWidth: true
BrightnessIcon {
brightness: Math.round(devSliderTrack.currentPct)
width: 14
height: 14
}
Text {
text: modelData.name
color: Theme.fg
font.pixelSize: 10
font.weight: Font.Bold
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: Math.round(devSliderTrack.currentPct) + "%"
color: modelData.type === "kbd" ? Theme.subAccent : Theme.accent
font.pixelSize: 10
font.weight: Font.Bold
}
}
// Independent Device Brightness Slider Track
Item {
id: devSliderTrack
Layout.fillWidth: true
Layout.preferredHeight: 16
property real currentPct: modelData.brightness
Component.onCompleted: {
currentPct = modelData.brightness
}
Rectangle {
anchors.centerIn: parent
width: parent.width
height: 4
radius: 2
color: Theme.currentLine
Rectangle {
width: Math.max(4, parent.width * (devSliderTrack.currentPct / 100.0))
height: parent.height
radius: 2
color: modelData.type === "kbd" ? Theme.subAccent : Theme.accent
}
}
// Slider Knob Handle
Rectangle {
width: devSliderMouse.containsMouse || devSliderMouse.pressed ? 12 : 8
height: width
radius: width / 2
color: modelData.type === "kbd" ? Theme.subAccent : Theme.accent
border.color: Theme.bg
border.width: 1
anchors.verticalCenter: parent.verticalCenter
x: Math.max(0, Math.min(devSliderTrack.width - width, (devSliderTrack.width * (devSliderTrack.currentPct / 100.0)) - (width / 2)))
}
MouseArea {
id: devSliderMouse
anchors.fill: parent
hoverEnabled: true
function updatePos(mx) {
var ratio = Math.max(0.0, Math.min(1.0, mx / width))
var val = Math.round(ratio * 100)
devSliderTrack.currentPct = val
BrightnessService.setDeviceBrightness(modelData.id, val)
}
onPressed: (mouse) => updatePos(mouse.x)
onPositionChanged: (mouse) => {
if (pressed) updatePos(mouse.x)
}
}
}
}
}
}
}
}
}
}
// External Storage Mount Control Detached Popup Window
PopupWindow {
id: mountMenu
anchor.window: window
anchor.rect.x: Math.round(root.x + root.width - implicitWidth)
anchor.rect.y: 40
anchor.edges: Edges.Bottom | Edges.Right
visible: PopupService.mountMenuOpen
color: "transparent"
implicitWidth: mountGlass.implicitWidth
implicitHeight: mountGlass.implicitHeight
GlassPanel {
id: mountGlass
implicitWidth: 320
implicitHeight: Math.min(480, mountCardLayout.implicitHeight + 24)
anchors.fill: parent
ColumnLayout {
id: mountCardLayout
anchors.fill: parent
anchors.margins: 12
spacing: 10
// Header
RowLayout {
Layout.fillWidth: true
Text {
text: "External Storage"
color: Theme.fg
font.pixelSize: 12
font.weight: Font.Bold
}
Item { Layout.fillWidth: true }
Rectangle {
width: 20; height: 20; radius: 4
color: refreshMountMouse.containsMouse ? Theme.currentLine : "transparent"
Text { text: "↻"; color: Theme.comment; font.pixelSize: 12; anchors.centerIn: parent }
MouseArea {
id: refreshMountMouse
anchors.fill: parent
hoverEnabled: true
onClicked: MountService.scanDevices()
}
}
}
// Solid Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
}
Flickable {
Layout.fillWidth: true
Layout.preferredHeight: Math.min(360, mountScrollCol.implicitHeight)
contentHeight: mountScrollCol.implicitHeight
clip: true
ColumnLayout {
id: mountScrollCol
width: parent.width
spacing: 8
Repeater {
model: MountService.devices
Rectangle {
Layout.fillWidth: true
implicitHeight: devItemCol.implicitHeight + 14
radius: 6
color: modelData.isMounted ? (Theme.isDark ? Qt.rgba(80/255, 250/255, 123/255, 0.1) : Qt.rgba(80/255, 250/255, 123/255, 0.18)) : Theme.surface
border.color: modelData.isMounted ? (Theme.isDark ? Theme.green : "#15803d") : Theme.currentLine
border.width: 1
ColumnLayout {
id: devItemCol
anchors.fill: parent
anchors.margins: 7
spacing: 6
// Device Title & Mounted Status Dot
RowLayout {
Layout.fillWidth: true
Rectangle {
width: 6; height: 6; radius: 3
color: modelData.isMounted ? (Theme.isDark ? Theme.green : "#15803d") : Theme.comment
}
Text {
text: modelData.label + " (" + modelData.size + ")"
color: modelData.isMounted ? (Theme.isDark ? Theme.green : "#15803d") : Theme.fg
font.pixelSize: 10
font.weight: Font.Bold
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: modelData.isMounted ? "Mounted" : "Unmounted"
color: modelData.isMounted ? (Theme.isDark ? Theme.green : "#15803d") : Theme.comment
font.pixelSize: 9
}
}
Text {
text: modelData.vendor
color: Theme.comment
font.pixelSize: 9
elide: Text.ElideRight
visible: modelData.vendor !== ""
}
// 3 Action Buttons: [ Mount / Unmount ] | [ Mount & Open ] | [ Auto-Mount ]
RowLayout {
Layout.fillWidth: true
spacing: 4
// Button 1: Mount / Unmount
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 22
radius: 4
color: mntBtnMouse.containsMouse ? Theme.currentLine : (Theme.isDark ? Theme.currentLine : Qt.rgba(Theme.fg.r, Theme.fg.g, Theme.fg.b, 0.08))
border.color: Theme.currentLine
border.width: 1
Text {
anchors.centerIn: parent
text: modelData.isMounted ? "Unmount" : "Mount"
color: modelData.isMounted ? Theme.red : Theme.accent
font.pixelSize: 9
font.weight: Font.Bold
}
MouseArea {
id: mntBtnMouse
anchors.fill: parent
hoverEnabled: true
onClicked: MountService.toggleMount(modelData.dev, modelData.isMounted)
}
}
// Button 2: Mount & Open
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 22
radius: 4
color: openBtnMouse.containsMouse ? Theme.currentLine : (Theme.isDark ? Theme.currentLine : Qt.rgba(Theme.fg.r, Theme.fg.g, Theme.fg.b, 0.08))
border.color: Theme.currentLine
border.width: 1
Text {
anchors.centerIn: parent
text: "Mount & Open"
color: Theme.subAccent
font.pixelSize: 9
font.weight: Font.Bold
}
MouseArea {
id: openBtnMouse
anchors.fill: parent
hoverEnabled: true
onClicked: MountService.mountAndOpen(modelData.dev)
}
}
// Button 3: Auto-Mount Toggle
Rectangle {
id: autoToggleBtn
Layout.fillWidth: true
Layout.preferredHeight: 22
radius: 4
property bool autoOn: MountService.autoMountMap[modelData.dev] === true
color: autoOn ? Qt.rgba(80/255, 250/255, 123/255, 0.2) : (autoBtnMouse.containsMouse ? Theme.currentLine : (Theme.isDark ? Theme.currentLine : Qt.rgba(Theme.fg.r, Theme.fg.g, Theme.fg.b, 0.08)))
border.color: autoOn ? (Theme.isDark ? Theme.green : "#15803d") : Theme.currentLine
border.width: 1
Text {
anchors.centerIn: parent
text: autoToggleBtn.autoOn ? "Auto: ON" : "Auto: OFF"
color: autoToggleBtn.autoOn ? (Theme.isDark ? Theme.green : "#15803d") : Theme.comment
font.pixelSize: 9
font.weight: Font.Bold
}
MouseArea {
id: autoBtnMouse
anchors.fill: parent
hoverEnabled: true
onClicked: MountService.toggleAutoMount(modelData.dev)
}
}
}
}
}
}
}
}
}
}
}
// Network Details Popup Window
PopupWindow {
id: netMenu
anchor.window: window
anchor.rect.x: Math.round(root.x + root.width - implicitWidth)
anchor.rect.y: 40
anchor.edges: Edges.Bottom | Edges.Right
visible: PopupService.networkMenuOpen
color: "transparent"
implicitWidth: netGlass.implicitWidth
implicitHeight: netGlass.implicitHeight
GlassPanel {
id: netGlass
implicitWidth: 300
implicitHeight: NetworkService.isWifiPowered ? 240 : 80
anchors.fill: parent
ColumnLayout {
id: netCardLayout
anchors.fill: parent
anchors.margins: 12
spacing: 8
// Header
RowLayout {
Layout.fillWidth: true
Text {
text: "Wi-Fi Networks"
color: Theme.fg
font.pixelSize: 12
font.weight: Font.Bold
}
Item { Layout.fillWidth: true }
// Wi-Fi Power ON/OFF Toggle Button
Rectangle {
width: 50; height: 20; radius: 4
color: NetworkService.isWifiPowered ? (Theme.isDark ? Qt.rgba(80/255, 250/255, 123/255, 0.2) : Qt.rgba(34/255, 197/255, 94/255, 0.25)) : (Theme.isDark ? Qt.rgba(255/255, 85/255, 85/255, 0.2) : Qt.rgba(239/255, 68/255, 68/255, 0.25))
border.color: NetworkService.isWifiPowered ? (Theme.isDark ? Theme.green : "#15803d") : (Theme.isDark ? Theme.red : "#b91c1c")
border.width: 1
Text {
anchors.centerIn: parent
text: NetworkService.isWifiPowered ? "ON" : "OFF"
color: NetworkService.isWifiPowered ? (Theme.isDark ? Theme.green : "#15803d") : (Theme.isDark ? Theme.red : "#b91c1c")
font.pixelSize: 9
font.weight: Font.Bold
}
MouseArea {
id: wifiPwrMouse
anchors.fill: parent
hoverEnabled: true
onClicked: NetworkService.toggleWifiPower()
}
}
Rectangle {
width: 20; height: 20; radius: 4
color: refreshNetMouse.containsMouse ? Theme.currentLine : "transparent"
Text { text: "↻"; color: Theme.comment; font.pixelSize: 12; anchors.centerIn: parent }
MouseArea {
id: refreshNetMouse
anchors.fill: parent
hoverEnabled: true
onClicked: NetworkService.scanWifi()
}
}
}
// Solid Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
}
// 1. Ethernet Card
Rectangle {
Layout.fillWidth: true
implicitHeight: ethColLayout.implicitHeight + 12
radius: 6
color: NetworkService.ethernetConnected ? (Theme.isDark ? Qt.rgba(80/255, 250/255, 123/255, 0.15) : Qt.rgba(80/255, 250/255, 123/255, 0.22)) : Theme.surface
border.color: NetworkService.ethernetConnected ? (Theme.isDark ? Theme.green : "#15803d") : Theme.currentLine
border.width: 1
RowLayout {
id: ethColLayout
anchors.fill: parent
anchors.leftMargin: 8
anchors.rightMargin: 8
spacing: 6
Rectangle { width: 6; height: 6; radius: 3; color: NetworkService.ethernetConnected ? Theme.green : Theme.comment }
Text {
text: "Ethernet (" + (NetworkService.ethernetConnected ? "Connected" : "Disconnected") + ")"
color: NetworkService.ethernetConnected ? Theme.green : Theme.fg
font.pixelSize: 10
font.weight: NetworkService.ethernetConnected ? Font.Bold : Font.Normal
Layout.fillWidth: true
}
}
}
// Off State Banner
Item {
Layout.fillWidth: true
Layout.fillHeight: true
visible: !NetworkService.isWifiPowered
Text {
anchors.centerIn: parent
text: "Wi-Fi is turned off"
color: Theme.comment
font.pixelSize: 11
font.italic: true
}
}
Flickable {
Layout.fillWidth: true
Layout.fillHeight: true
contentHeight: netScrollCol.implicitHeight
clip: true
visible: NetworkService.isWifiPowered
ColumnLayout {
id: netScrollCol
width: parent.width
spacing: 3
Text {
text: "Wi-Fi is turned off"
color: Theme.comment
font.pixelSize: 10
font.italic: true
visible: !NetworkService.isWifiPowered
}
Repeater {
model: NetworkService.networks
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 26
radius: 5
color: modelData.inUse ? Qt.rgba(80/255, 250/255, 123/255, 0.15) : (netItemMouse.containsMouse ? Theme.currentLine : "transparent")
RowLayout {
anchors.fill: parent
anchors.leftMargin: 8
anchors.rightMargin: 8
spacing: 6
NetworkIcon {
isConnected: true
signalPercent: parseInt(modelData.signal)
width: 14
height: 14
}
Text {
text: modelData.ssid
color: modelData.inUse ? Theme.green : Theme.fg
font.pixelSize: 10
font.weight: modelData.inUse ? Font.Bold : Font.Normal
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: modelData.inUse ? "Connected" : modelData.signal + "%"
color: modelData.inUse ? Theme.green : Theme.comment
font.pixelSize: 9
}
}
MouseArea {
id: netItemMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
if (!modelData.inUse) {
NetworkService.connectNetwork(modelData.ssid, "")
}
}
}
}
}
}
}
}
}
}
// Battery & Power Control Detached Popup Window
PopupWindow {
id: batMenu
anchor.window: window
anchor.rect.x: Math.round(root.x + root.width - implicitWidth)
anchor.rect.y: 40
anchor.edges: Edges.Bottom | Edges.Right
visible: PopupService.batteryMenuOpen
color: "transparent"
implicitWidth: batGlass.implicitWidth
implicitHeight: batGlass.implicitHeight
GlassPanel {
id: batGlass
implicitWidth: 300
implicitHeight: Math.min(450, batCardLayout.implicitHeight + 24)
anchors.fill: parent
ColumnLayout {
id: batCardLayout
anchors.fill: parent
anchors.margins: 12
spacing: 10
// Header
Text {
text: "Battery & Power"
color: Theme.fg
font.pixelSize: 12
font.weight: Font.Bold
}
// Battery Status & Health Stats Card
Rectangle {
Layout.fillWidth: true
implicitHeight: 54
radius: 6
color: Theme.surface
border.color: Theme.currentLine
border.width: 1
RowLayout {
anchors.centerIn: parent
spacing: 10
BatteryIcon {
percentage: BatteryService.percentage
isCharging: BatteryService.isCharging
width: 22
height: 14
Layout.alignment: Qt.AlignVCenter
}
ColumnLayout {
spacing: 2
Layout.alignment: Qt.AlignVCenter
Text {
text: BatteryService.percentage + "% (" + (BatteryService.isCharging ? "Charging" : "Discharging") + ")"
color: Theme.fg
font.pixelSize: 11
font.weight: Font.Bold
}
Text {
text: "Battery Health: " + BatteryService.healthPercent + "%"
color: Theme.isDark ? Theme.green : "#15803d"
font.pixelSize: 10
}
}
}
}
// Solid Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
}
// Power Profile 3-Point Stepped Slider Section
ColumnLayout {
Layout.fillWidth: true
spacing: 6
RowLayout {
Layout.fillWidth: true
Text {
text: "POWER PROFILE"
color: Theme.comment
font.pixelSize: 9
font.weight: Font.Bold
}
Item { Layout.fillWidth: true }
Text {
text: BatteryService.activeProfile === "power-saver" ? "Power Saver" : (BatteryService.activeProfile === "performance" ? "Performance" : "Balanced")
color: BatteryService.activeProfile === "power-saver" ? (Theme.isDark ? Theme.green : "#15803d") : (BatteryService.activeProfile === "performance" ? Theme.subAccent : Theme.accent)
font.pixelSize: 10
font.weight: Font.Bold
}
}
// Stepped 3-Point Slider Container
Item {
id: profileSliderTrack
Layout.fillWidth: true
Layout.preferredHeight: 20
property int activeIndex: BatteryService.activeProfile === "power-saver" ? 0 : (BatteryService.activeProfile === "performance" ? 2 : 1)
// Track Line
Rectangle {
anchors.centerIn: parent
width: parent.width - 16
height: 4
radius: 2
color: Theme.currentLine
// Active Fill
Rectangle {
width: Math.max(0, (parent.width * (profileSliderTrack.activeIndex / 2.0)))
height: parent.height
radius: 2
color: profileSliderTrack.activeIndex === 0 ? Theme.green : (profileSliderTrack.activeIndex === 2 ? Theme.subAccent : Theme.accent)
Behavior on width { NumberAnimation { duration: 150; easing.type: Easing.OutCubic } }
}
}
// 3 Snap Point Dots
Repeater {
model: 3
Rectangle {
required property int index
width: 8
height: 8
radius: 4
color: index <= profileSliderTrack.activeIndex ? (profileSliderTrack.activeIndex === 0 ? Theme.green : (profileSliderTrack.activeIndex === 2 ? Theme.subAccent : Theme.accent)) : Theme.currentLine
anchors.verticalCenter: parent.verticalCenter
x: 8 + ((profileSliderTrack.width - 24) * (index / 2.0)) - 4
Behavior on color { ColorAnimation { duration: 120 } }
}
}
// Stepped Handle Knob
Rectangle {
id: profileKnob
width: 14
height: 14
radius: 7
color: profileSliderTrack.activeIndex === 0 ? Theme.green : (profileSliderTrack.activeIndex === 2 ? Theme.subAccent : Theme.accent)
border.color: Theme.bg
border.width: 2
anchors.verticalCenter: parent.verticalCenter
x: 8 + ((profileSliderTrack.width - 24) * (profileSliderTrack.activeIndex / 2.0)) - 7
Behavior on x { NumberAnimation { duration: 180; easing.type: Easing.OutCubic } }
Behavior on color { ColorAnimation { duration: 120 } }
}
// Mouse Drag / Snap Handler
MouseArea {
id: profileSliderMouse
anchors.fill: parent
hoverEnabled: true
function applySnap(mouseX) {
var ratio = Math.max(0.0, Math.min(1.0, (mouseX - 8) / (width - 24)))
var snapIdx = Math.round(ratio * 2.0)
var profiles = ["power-saver", "balanced", "performance"]
var targetProfile = profiles[snapIdx]
if (BatteryService.activeProfile !== targetProfile) {
BatteryService.setProfile(targetProfile)
}
}
onPositionChanged: (mouse) => {
if (pressed) applySnap(mouse.x)
}
onPressed: (mouse) => {
applySnap(mouse.x)
}
}
}
// 3 Point Labels Row (Power Saver, Balanced, Performance)
RowLayout {
Layout.fillWidth: true
Text {
text: "Power Saver"
color: profileSliderTrack.activeIndex === 0 ? Theme.green : Theme.comment
font.pixelSize: 9
font.weight: profileSliderTrack.activeIndex === 0 ? Font.Bold : Font.Normal
}
Item { Layout.fillWidth: true }
Text {
text: "Balanced"
color: profileSliderTrack.activeIndex === 1 ? Theme.accent : Theme.comment
font.pixelSize: 9
font.weight: profileSliderTrack.activeIndex === 1 ? Font.Bold : Font.Normal
}
Item { Layout.fillWidth: true }
Text {
text: "Performance"
color: profileSliderTrack.activeIndex === 2 ? Theme.subAccent : Theme.comment
font.pixelSize: 9
font.weight: profileSliderTrack.activeIndex === 2 ? Font.Bold : Font.Normal
}
}
}
// Solid Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
}
// Block PC Sleep Toggle Button (Caffeine Mode)
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 30
radius: 6
color: BatteryService.isSleepBlocked ? Qt.rgba(255/255, 184/255, 108/255, 0.2) : (sleepMouse.containsMouse ? Theme.currentLine : (Theme.isDark ? Theme.currentLine : Qt.rgba(Theme.fg.r, Theme.fg.g, Theme.fg.b, 0.08)))
border.color: BatteryService.isSleepBlocked ? Theme.orange : Theme.currentLine
border.width: 1
RowLayout {
anchors.centerIn: parent
spacing: 8
Rectangle {
width: 8; height: 8; radius: 4
color: BatteryService.isSleepBlocked ? Theme.orange : Theme.comment
}
Text {
text: BatteryService.isSleepBlocked ? "Block PC Sleep: ACTIVE" : "Block PC from Falling Asleep"
color: BatteryService.isSleepBlocked ? Theme.orange : Theme.fg
font.pixelSize: 10
font.weight: Font.Bold
}
}
MouseArea {
id: sleepMouse
anchors.fill: parent
hoverEnabled: true
onClicked: BatteryService.toggleSleepBlock()
}
}
}
}
}
// Running Applications System Tray Context Menu Detached Popup Window
PopupWindow {
id: trayContextMenu
anchor.window: window
anchor.rect.x: Math.round(Math.max(10, Math.min(window.width - 175, root.x + root.activeTrayX + 12 - 87)))
anchor.rect.y: 40
anchor.edges: Edges.Bottom | Edges.Left
visible: PopupService.trayMenuOpen
color: "transparent"
implicitWidth: trayGlass.implicitWidth
implicitHeight: trayGlass.implicitHeight
GlassPanel {
id: trayGlass
implicitWidth: 170
implicitHeight: trayCol.implicitHeight + 16
anchors.fill: parent
ColumnLayout {
id: trayCol
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 8
spacing: 4
// Sleek & Uniform App Header (Fixed 22px Height & Clean App Name)
RowLayout {
Layout.fillWidth: true
Layout.preferredHeight: 22
spacing: 6
Item {
width: 14
height: 14
Layout.alignment: Qt.AlignVCenter
Image {
anchors.fill: parent
sourceSize.width: 14
sourceSize.height: 14
source: root.activeTrayItem ? (root.activeTrayItem.icon || "") : ""
fillMode: Image.PreserveAspectFit
visible: root.activeTrayItem ? (root.activeTrayItem.icon ? true : false) : false
}
}
Text {
text: root.getCleanAppName(root.activeTrayItem)
color: Theme.accent
font.pixelSize: 10
font.weight: Font.Bold
elide: Text.ElideRight
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
}
}
// Solid Divider (Only visible if app has menu items underneath)
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
visible: (root.activeTrayItem && root.activeTrayItem.hasMenu && root.activeTrayMenuOpener && root.activeTrayMenuOpener.children && root.activeTrayMenuOpener.children.length > 0)
}
// Render Pure Native DBus App Menu Items (Pre-Cached Real Time Model)
Repeater {
model: (root.activeTrayItem && root.activeTrayItem.hasMenu && root.activeTrayMenuOpener && root.activeTrayMenuOpener.children) ? root.activeTrayMenuOpener.children : 0
ColumnLayout {
Layout.fillWidth: true
spacing: 0
// Separator Item
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.currentLine
visible: modelData.isSeparator === true
Layout.topMargin: 2
Layout.bottomMargin: 2
}
// Normal Native Menu Item Button
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 22
radius: 4
visible: modelData.isSeparator !== true && (modelData.text ? true : false)
color: (menuItemMouse.containsMouse && modelData.enabled) ? Theme.currentLine : "transparent"
RowLayout {
anchors.fill: parent
anchors.leftMargin: 6
anchors.rightMargin: 6
spacing: 6
Image {
width: 12; height: 12
sourceSize.width: 12; sourceSize.height: 12
source: modelData.icon || ""
visible: modelData.icon && modelData.icon !== ""
fillMode: Image.PreserveAspectFit
}
Text {
text: modelData.text ? modelData.text.replace(/&/g, "") : ""
color: modelData.enabled ? Theme.fg : Theme.comment
font.pixelSize: 10
font.weight: modelData.enabled ? Font.Medium : Font.Normal
elide: Text.ElideRight
Layout.fillWidth: true
}
}
MouseArea {
id: menuItemMouse
anchors.fill: parent
hoverEnabled: true
enabled: modelData.enabled
onClicked: {
modelData.triggered()
PopupService.closeAll()
}
}
}
}
}
}
}
}
}