2572 lines
106 KiB
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()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|