1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-08 23:02:05 -04:00

Initial qmlformat

This commit is contained in:
bbedward
2025-07-17 17:58:56 -04:00
parent cbb42df3a9
commit 023b6bc0d1
62 changed files with 7805 additions and 6606 deletions

View File

@@ -1,16 +1,15 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Widgets
import Quickshell.Io
import Quickshell.Widgets
import qs.Common
import qs.Services
Item {
id: audioTab
property int audioSubTab: 0 // 0: Output, 1: Input
readonly property real volumeLevel: AudioService.volumeLevel
readonly property real micLevel: AudioService.micLevel
readonly property bool volumeMuted: AudioService.sinkMuted
@@ -19,23 +18,23 @@ Item {
readonly property string currentAudioSource: AudioService.currentAudioSource
readonly property var audioSinks: AudioService.audioSinks
readonly property var audioSources: AudioService.audioSources
Column {
anchors.fill: parent
spacing: Theme.spacingM
// Audio Sub-tabs
Row {
width: parent.width
height: 40
spacing: 2
Rectangle {
width: parent.width / 2 - 1
height: parent.height
radius: Theme.cornerRadius
color: audioTab.audioSubTab === 0 ? Theme.primary : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
Text {
anchors.centerIn: parent
text: "Output"
@@ -43,21 +42,22 @@ Item {
color: audioTab.audioSubTab === 0 ? Theme.primaryText : Theme.surfaceText
font.weight: Font.Medium
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: audioTab.audioSubTab = 0
}
}
Rectangle {
width: parent.width / 2 - 1
height: parent.height
radius: Theme.cornerRadius
color: audioTab.audioSubTab === 1 ? Theme.primary : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
Text {
anchors.centerIn: parent
text: "Input"
@@ -65,163 +65,174 @@ Item {
color: audioTab.audioSubTab === 1 ? Theme.primaryText : Theme.surfaceText
font.weight: Font.Medium
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: audioTab.audioSubTab = 1
}
}
}
// Output Tab Content
ScrollView {
width: parent.width
height: parent.height - 48
visible: audioTab.audioSubTab === 0
clip: true
Column {
width: parent.width
spacing: Theme.spacingL
// Volume Control
Column {
width: parent.width
spacing: Theme.spacingM
Text {
text: "Volume"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
Row {
width: parent.width
spacing: Theme.spacingM
Text {
text: audioTab.volumeMuted ? "volume_off" : "volume_down"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize
color: audioTab.volumeMuted ? Theme.error : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: AudioService.toggleMute()
}
}
Item {
id: volumeSliderContainer
width: parent.width - 80
height: 32
anchors.verticalCenter: parent.verticalCenter
Rectangle {
id: volumeSliderTrack
width: parent.width
height: 8
radius: 4
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
anchors.verticalCenter: parent.verticalCenter
Rectangle {
id: volumeSliderFill
width: parent.width * (audioTab.volumeLevel / 100)
height: parent.height
radius: parent.radius
color: Theme.primary
Behavior on width {
NumberAnimation { duration: 100 }
NumberAnimation {
duration: 100
}
}
}
// Draggable handle
Rectangle {
id: volumeHandle
width: 18
height: 18
radius: 9
color: Theme.primary
border.color: Qt.lighter(Theme.primary, 1.3)
border.width: 2
x: Math.max(0, Math.min(parent.width - width, volumeSliderFill.width - width/2))
x: Math.max(0, Math.min(parent.width - width, volumeSliderFill.width - width / 2))
anchors.verticalCenter: parent.verticalCenter
scale: volumeMouseArea.containsMouse || volumeMouseArea.pressed ? 1.2 : 1.0
scale: volumeMouseArea.containsMouse || volumeMouseArea.pressed ? 1.2 : 1
Behavior on scale {
NumberAnimation { duration: 150 }
NumberAnimation {
duration: 150
}
}
}
}
MouseArea {
id: volumeMouseArea
property bool isDragging: false
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
preventStealing: true
property bool isDragging: false
onPressed: (mouse) => {
isDragging = true
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width))
let newVolume = Math.round(ratio * 100)
AudioService.setVolume(newVolume)
isDragging = true;
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width));
let newVolume = Math.round(ratio * 100);
AudioService.setVolume(newVolume);
}
onReleased: {
isDragging = false
isDragging = false;
}
onPositionChanged: (mouse) => {
if (pressed && isDragging) {
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width))
let newVolume = Math.round(ratio * 100)
AudioService.setVolume(newVolume)
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width));
let newVolume = Math.round(ratio * 100);
AudioService.setVolume(newVolume);
}
}
onClicked: (mouse) => {
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width))
let newVolume = Math.round(ratio * 100)
AudioService.setVolume(newVolume)
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width));
let newVolume = Math.round(ratio * 100);
AudioService.setVolume(newVolume);
}
}
// Global mouse area for drag tracking
MouseArea {
id: volumeGlobalMouseArea
anchors.fill: parent.parent.parent.parent.parent // Fill the entire control center
enabled: volumeMouseArea.isDragging
visible: false
preventStealing: true
onPositionChanged: (mouse) => {
if (volumeMouseArea.isDragging) {
let globalPos = mapToItem(volumeSliderTrack, mouse.x, mouse.y)
let ratio = Math.max(0, Math.min(1, globalPos.x / volumeSliderTrack.width))
let newVolume = Math.round(ratio * 100)
AudioService.setVolume(newVolume)
let globalPos = mapToItem(volumeSliderTrack, mouse.x, mouse.y);
let ratio = Math.max(0, Math.min(1, globalPos.x / volumeSliderTrack.width));
let newVolume = Math.round(ratio * 100);
AudioService.setVolume(newVolume);
}
}
onReleased: {
volumeMouseArea.isDragging = false
volumeMouseArea.isDragging = false;
}
}
}
Text {
text: "volume_up"
font.family: Theme.iconFont
@@ -229,21 +240,23 @@ Item {
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
}
// Output Devices
Column {
width: parent.width
spacing: Theme.spacingM
Text {
text: "Output Device"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
// Current device indicator
Rectangle {
width: parent.width
@@ -253,250 +266,270 @@ Item {
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3)
border.width: 1
visible: audioTab.currentAudioSink !== ""
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
Text {
text: "check_circle"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize - 4
color: Theme.primary
}
Text {
text: "Current: " + (AudioService.currentSinkDisplayName || "None")
font.pixelSize: Theme.fontSizeMedium
color: Theme.primary
font.weight: Font.Medium
}
}
}
// Real audio devices
Repeater {
model: audioTab.audioSinks
Rectangle {
width: parent.width
height: 50
radius: Theme.cornerRadius
color: deviceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
(modelData.active ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
color: deviceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (modelData.active ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: modelData.active ? Theme.primary : "transparent"
border.width: 1
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
Text {
text: {
if (modelData.name.includes("bluez")) return "headset"
else if (modelData.name.includes("hdmi")) return "tv"
else if (modelData.name.includes("usb")) return "headset"
else return "speaker"
if (modelData.name.includes("bluez"))
return "headset";
else if (modelData.name.includes("hdmi"))
return "tv";
else if (modelData.name.includes("usb"))
return "headset";
else
return "speaker";
}
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize
color: modelData.active ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
Text {
text: modelData.displayName
font.pixelSize: Theme.fontSizeMedium
color: modelData.active ? Theme.primary : Theme.surfaceText
font.weight: modelData.active ? Font.Medium : Font.Normal
}
Text {
text: {
if (modelData.subtitle && modelData.subtitle !== "") {
return modelData.subtitle + (modelData.active ? " • Selected" : "")
} else {
return modelData.active ? "Selected" : ""
}
if (modelData.subtitle && modelData.subtitle !== "")
return modelData.subtitle + (modelData.active ? " • Selected" : "");
else
return modelData.active ? "Selected" : "";
}
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
visible: text !== ""
}
}
}
MouseArea {
id: deviceArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
AudioService.setAudioSink(modelData.name)
AudioService.setAudioSink(modelData.name);
}
}
}
}
}
}
}
// Input Tab Content
ScrollView {
width: parent.width
height: parent.height - 48
visible: audioTab.audioSubTab === 1
clip: true
Column {
width: parent.width
spacing: Theme.spacingL
// Microphone Level Control
Column {
width: parent.width
spacing: Theme.spacingM
Text {
text: "Microphone Level"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
Row {
width: parent.width
spacing: Theme.spacingM
Text {
text: audioTab.micMuted ? "mic_off" : "mic"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize
color: audioTab.micMuted ? Theme.error : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: AudioService.toggleMicMute()
}
}
Item {
id: micSliderContainer
width: parent.width - 80
height: 32
anchors.verticalCenter: parent.verticalCenter
Rectangle {
id: micSliderTrack
width: parent.width
height: 8
radius: 4
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
anchors.verticalCenter: parent.verticalCenter
Rectangle {
id: micSliderFill
width: parent.width * (audioTab.micLevel / 100)
height: parent.height
radius: parent.radius
color: Theme.primary
Behavior on width {
NumberAnimation { duration: 100 }
NumberAnimation {
duration: 100
}
}
}
// Draggable handle
Rectangle {
id: micHandle
width: 18
height: 18
radius: 9
color: Theme.primary
border.color: Qt.lighter(Theme.primary, 1.3)
border.width: 2
x: Math.max(0, Math.min(parent.width - width, micSliderFill.width - width/2))
x: Math.max(0, Math.min(parent.width - width, micSliderFill.width - width / 2))
anchors.verticalCenter: parent.verticalCenter
scale: micMouseArea.containsMouse || micMouseArea.pressed ? 1.2 : 1.0
scale: micMouseArea.containsMouse || micMouseArea.pressed ? 1.2 : 1
Behavior on scale {
NumberAnimation { duration: 150 }
NumberAnimation {
duration: 150
}
}
}
}
MouseArea {
id: micMouseArea
property bool isDragging: false
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
preventStealing: true
property bool isDragging: false
onPressed: (mouse) => {
isDragging = true
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width))
let newMicLevel = Math.round(ratio * 100)
AudioService.setMicLevel(newMicLevel)
isDragging = true;
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width));
let newMicLevel = Math.round(ratio * 100);
AudioService.setMicLevel(newMicLevel);
}
onReleased: {
isDragging = false
isDragging = false;
}
onPositionChanged: (mouse) => {
if (pressed && isDragging) {
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width))
let newMicLevel = Math.round(ratio * 100)
AudioService.setMicLevel(newMicLevel)
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width));
let newMicLevel = Math.round(ratio * 100);
AudioService.setMicLevel(newMicLevel);
}
}
onClicked: (mouse) => {
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width))
let newMicLevel = Math.round(ratio * 100)
AudioService.setMicLevel(newMicLevel)
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width));
let newMicLevel = Math.round(ratio * 100);
AudioService.setMicLevel(newMicLevel);
}
}
// Global mouse area for drag tracking
MouseArea {
id: micGlobalMouseArea
anchors.fill: parent.parent.parent.parent.parent // Fill the entire control center
enabled: micMouseArea.isDragging
visible: false
preventStealing: true
onPositionChanged: (mouse) => {
if (micMouseArea.isDragging) {
let globalPos = mapToItem(micSliderTrack, mouse.x, mouse.y)
let ratio = Math.max(0, Math.min(1, globalPos.x / micSliderTrack.width))
let newMicLevel = Math.round(ratio * 100)
AudioService.setMicLevel(newMicLevel)
let globalPos = mapToItem(micSliderTrack, mouse.x, mouse.y);
let ratio = Math.max(0, Math.min(1, globalPos.x / micSliderTrack.width));
let newMicLevel = Math.round(ratio * 100);
AudioService.setMicLevel(newMicLevel);
}
}
onReleased: {
micMouseArea.isDragging = false
micMouseArea.isDragging = false;
}
}
}
Text {
text: "mic"
font.family: Theme.iconFont
@@ -504,22 +537,23 @@ Item {
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
}
// Input Devices
Column {
width: parent.width
spacing: Theme.spacingM
Text {
text: "Input Device"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
// Current device indicator
Rectangle {
width: parent.width
@@ -529,100 +563,112 @@ Item {
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3)
border.width: 1
visible: audioTab.currentAudioSource !== ""
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
Text {
text: "check_circle"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize - 4
color: Theme.primary
}
Text {
text: "Current: " + (AudioService.currentSourceDisplayName || "None")
font.pixelSize: Theme.fontSizeMedium
color: Theme.primary
font.weight: Font.Medium
}
}
}
// Real audio input devices
Repeater {
model: audioTab.audioSources
Rectangle {
width: parent.width
height: 50
radius: Theme.cornerRadius
color: sourceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
(modelData.active ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
color: sourceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (modelData.active ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: modelData.active ? Theme.primary : "transparent"
border.width: 1
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
Text {
text: {
if (modelData.name.includes("bluez")) return "headset_mic"
else if (modelData.name.includes("usb")) return "headset_mic"
else return "mic"
if (modelData.name.includes("bluez"))
return "headset_mic";
else if (modelData.name.includes("usb"))
return "headset_mic";
else
return "mic";
}
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize
color: modelData.active ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
Text {
text: modelData.displayName
font.pixelSize: Theme.fontSizeMedium
color: modelData.active ? Theme.primary : Theme.surfaceText
font.weight: modelData.active ? Font.Medium : Font.Normal
}
Text {
text: {
if (modelData.subtitle && modelData.subtitle !== "") {
return modelData.subtitle + (modelData.active ? " • Selected" : "")
} else {
return modelData.active ? "Selected" : ""
}
if (modelData.subtitle && modelData.subtitle !== "")
return modelData.subtitle + (modelData.active ? " • Selected" : "");
else
return modelData.active ? "Selected" : "";
}
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
visible: text !== ""
}
}
}
MouseArea {
id: sourceArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
AudioService.setAudioSource(modelData.name)
AudioService.setAudioSource(modelData.name);
}
}
}
}
}
}
}
}
}
}

View File

@@ -1,41 +1,39 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Widgets
import Quickshell.Io
import Quickshell.Bluetooth
import Quickshell.Io
import Quickshell.Widgets
import qs.Common
import qs.Services
Item {
id: bluetoothTab
ScrollView {
anchors.fill: parent
clip: true
ScrollBar.vertical.policy: ScrollBar.AsNeeded
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
Column {
width: parent.width
spacing: Theme.spacingL
Rectangle {
width: parent.width
height: 60
radius: Theme.cornerRadius
color: bluetoothToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
(BluetoothService.adapter && BluetoothService.adapter.enabled ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12))
color: bluetoothToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : (BluetoothService.adapter && BluetoothService.adapter.enabled ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12))
border.color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : "transparent"
border.width: 2
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingL
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
Text {
text: "bluetooth"
font.family: Theme.iconFont
@@ -43,68 +41,72 @@ Item {
color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
Text {
text: "Bluetooth"
font.pixelSize: Theme.fontSizeLarge
color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Theme.surfaceText
font.weight: Font.Medium
}
Text {
text: BluetoothService.adapter && BluetoothService.adapter.enabled ? "Enabled" : "Disabled"
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
}
}
}
MouseArea {
id: bluetoothToggle
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
BluetoothService.toggleAdapter()
BluetoothService.toggleAdapter();
}
}
}
Column {
width: parent.width
spacing: Theme.spacingM
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
Text {
text: "Paired Devices"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
Repeater {
model: BluetoothService.adapter && BluetoothService.adapter.devices ? BluetoothService.adapter.devices.values.filter(dev => dev && dev.paired && BluetoothService.isValidDevice(dev)) : []
model: BluetoothService.adapter && BluetoothService.adapter.devices ? BluetoothService.adapter.devices.values.filter((dev) => {
return dev && dev.paired && BluetoothService.isValidDevice(dev);
}) : []
Rectangle {
width: parent.width
height: 60
radius: Theme.cornerRadius
color: btDeviceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
(modelData.connected ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
color: btDeviceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (modelData.connected ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: modelData.connected ? Theme.primary : "transparent"
border.width: 1
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
Text {
text: BluetoothService.getDeviceIcon(modelData)
font.family: Theme.iconFont
@@ -112,59 +114,59 @@ Item {
color: modelData.connected ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
Text {
text: modelData.name || modelData.deviceName
font.pixelSize: Theme.fontSizeMedium
color: modelData.connected ? Theme.primary : Theme.surfaceText
font.weight: modelData.connected ? Font.Medium : Font.Normal
}
Row {
spacing: Theme.spacingXS
Text {
text: modelData.connected ? "Connected" : "Disconnected"
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
}
Text {
text: {
if (modelData.batteryAvailable && modelData.battery > 0) {
return "• " + Math.round(modelData.battery * 100) + "%"
}
var btBattery = BatteryService.bluetoothDevices.find(dev =>
dev.name === (modelData.name || modelData.deviceName) ||
dev.name.toLowerCase().includes((modelData.name || modelData.deviceName).toLowerCase()) ||
(modelData.name || modelData.deviceName).toLowerCase().includes(dev.name.toLowerCase())
)
return btBattery ? "• " + btBattery.percentage + "%" : ""
if (modelData.batteryAvailable && modelData.battery > 0)
return "• " + Math.round(modelData.battery * 100) + "%";
var btBattery = BatteryService.bluetoothDevices.find((dev) => {
return dev.name === (modelData.name || modelData.deviceName) || dev.name.toLowerCase().includes((modelData.name || modelData.deviceName).toLowerCase()) || (modelData.name || modelData.deviceName).toLowerCase().includes(dev.name.toLowerCase());
});
return btBattery ? "• " + btBattery.percentage + "%" : "";
}
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
visible: text.length > 0
}
}
}
}
Rectangle {
id: btMenuButton
width: 32
height: 32
radius: Theme.cornerRadius
color: btMenuButtonArea.containsMouse ?
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) :
"transparent"
color: btMenuButtonArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
anchors.right: parent.right
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
Text {
text: "more_vert"
font.family: Theme.iconFont
@@ -174,50 +176,57 @@ Item {
opacity: 0.6
anchors.centerIn: parent
}
MouseArea {
id: btMenuButtonArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
bluetoothContextMenuWindow.deviceData = modelData
let localPos = btMenuButtonArea.mapToItem(bluetoothTab, btMenuButtonArea.width / 2, btMenuButtonArea.height)
bluetoothContextMenuWindow.show(localPos.x, localPos.y)
bluetoothContextMenuWindow.deviceData = modelData;
let localPos = btMenuButtonArea.mapToItem(bluetoothTab, btMenuButtonArea.width / 2, btMenuButtonArea.height);
bluetoothContextMenuWindow.show(localPos.x, localPos.y);
}
}
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
ColorAnimation {
duration: Theme.shortDuration
}
}
}
MouseArea {
id: btDeviceArea
anchors.fill: parent
anchors.rightMargin: 40
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
BluetoothService.debugDevice(modelData)
BluetoothService.toggle(modelData.address)
BluetoothService.debugDevice(modelData);
BluetoothService.toggle(modelData.address);
}
}
}
}
}
Column {
width: parent.width
spacing: Theme.spacingM
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
Row {
width: parent.width
spacing: Theme.spacingM
Text {
text: "Available Devices"
font.pixelSize: Theme.fontSizeLarge
@@ -225,9 +234,12 @@ Item {
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
Item { width: 1; height: 1 }
Item {
width: 1
height: 1
}
Rectangle {
width: Math.max(140, scanText.contentWidth + Theme.spacingL * 2)
height: 36
@@ -235,11 +247,11 @@ Item {
color: scanArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
border.color: Theme.primary
border.width: 1
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
Text {
text: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "bluetooth_searching"
font.family: Theme.iconFont
@@ -247,112 +259,140 @@ Item {
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Text {
id: scanText
text: BluetoothService.adapter && BluetoothService.adapter.discovering ? "Stop Scanning" : "Start Scanning"
font.pixelSize: Theme.fontSizeMedium
color: Theme.primary
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: scanArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (BluetoothService.adapter && BluetoothService.adapter.discovering) {
BluetoothService.stopScan()
} else {
BluetoothService.startScan()
}
if (BluetoothService.adapter && BluetoothService.adapter.discovering)
BluetoothService.stopScan();
else
BluetoothService.startScan();
}
}
}
}
Repeater {
model: BluetoothService.availableDevices
Rectangle {
property bool canPair: BluetoothService.canPair(modelData)
property string pairingStatus: BluetoothService.getPairingStatus(modelData)
width: parent.width
height: 70
radius: Theme.cornerRadius
color: {
if (availableDeviceArea.containsMouse) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
if (modelData.pairing) return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
if (modelData.blocked) return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.08)
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
if (availableDeviceArea.containsMouse)
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08);
if (modelData.pairing)
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12);
if (modelData.blocked)
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.08);
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08);
}
border.color: {
if (modelData.pairing) return Theme.warning
if (modelData.blocked) return Theme.error
return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
if (modelData.pairing)
return Theme.warning;
if (modelData.blocked)
return Theme.error;
return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2);
}
border.width: 1
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
Text {
text: BluetoothService.getDeviceIcon(modelData)
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize
color: {
if (modelData.pairing) return Theme.warning
if (modelData.blocked) return Theme.error
return Theme.surfaceText
if (modelData.pairing)
return Theme.warning;
if (modelData.blocked)
return Theme.error;
return Theme.surfaceText;
}
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
Text {
text: modelData.name || modelData.deviceName
font.pixelSize: Theme.fontSizeMedium
color: {
if (modelData.pairing) return Theme.warning
if (modelData.blocked) return Theme.error
return Theme.surfaceText
if (modelData.pairing)
return Theme.warning;
if (modelData.blocked)
return Theme.error;
return Theme.surfaceText;
}
font.weight: modelData.pairing ? Font.Medium : Font.Normal
}
Row {
spacing: Theme.spacingXS
Row {
spacing: Theme.spacingS
Text {
text: {
switch (pairingStatus) {
case "pairing": return "Pairing..."
case "blocked": return "Blocked"
default: return BluetoothService.getSignalStrength(modelData)
case "pairing":
return "Pairing...";
case "blocked":
return "Blocked";
default:
return BluetoothService.getSignalStrength(modelData);
}
}
font.pixelSize: Theme.fontSizeSmall
color: {
if (modelData.pairing) return Theme.warning
if (modelData.blocked) return Theme.error
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
if (modelData.pairing)
return Theme.warning;
if (modelData.blocked)
return Theme.error;
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7);
}
}
Text {
text: BluetoothService.getSignalIcon(modelData)
font.family: Theme.iconFont
@@ -360,18 +400,22 @@ Item {
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
visible: modelData.rssi !== undefined && modelData.rssi !== 0 && pairingStatus === "available"
}
Text {
text: (modelData.rssi !== undefined && modelData.rssi !== 0) ? modelData.rssi + "dBm" : ""
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
visible: modelData.rssi !== undefined && modelData.rssi !== 0 && pairingStatus === "available"
}
}
}
}
}
Rectangle {
width: 80
height: 28
@@ -380,74 +424,85 @@ Item {
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
color: {
if (!canPair && !modelData.pairing) return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
if (actionButtonArea.containsMouse) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
return "transparent"
if (!canPair && !modelData.pairing)
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3);
if (actionButtonArea.containsMouse)
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
return "transparent";
}
border.color: canPair || modelData.pairing ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 1
opacity: canPair || modelData.pairing ? 1.0 : 0.5
opacity: canPair || modelData.pairing ? 1 : 0.5
Text {
anchors.centerIn: parent
text: {
if (modelData.pairing) return "Pairing..."
if (modelData.blocked) return "Blocked"
return "Pair"
if (modelData.pairing)
return "Pairing...";
if (modelData.blocked)
return "Blocked";
return "Pair";
}
font.pixelSize: Theme.fontSizeSmall
color: canPair || modelData.pairing ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
font.weight: Font.Medium
}
MouseArea {
id: actionButtonArea
anchors.fill: parent
hoverEnabled: true
cursorShape: canPair ? Qt.PointingHandCursor : Qt.ArrowCursor
enabled: canPair
onClicked: {
if (canPair) {
BluetoothService.pair(modelData.address)
}
if (canPair)
BluetoothService.pair(modelData.address);
}
}
}
MouseArea {
id: availableDeviceArea
anchors.fill: parent
anchors.rightMargin: 90 // Don't overlap with action button
anchors.rightMargin: 90 // Don't overlap with action button
hoverEnabled: true
cursorShape: canPair ? Qt.PointingHandCursor : Qt.ArrowCursor
enabled: canPair
onClicked: {
if (canPair) {
BluetoothService.pair(modelData.address)
}
if (canPair)
BluetoothService.pair(modelData.address);
}
}
}
}
Column {
width: parent.width
spacing: Theme.spacingM
visible: BluetoothService.adapter && BluetoothService.adapter.discovering && BluetoothService.availableDevices.length === 0
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingM
Text {
text: "sync"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSizeLarge
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
RotationAnimation on rotation {
running: true
loops: Animation.Infinite
@@ -455,8 +510,9 @@ Item {
to: 360
duration: 2000
}
}
Text {
text: "Scanning for devices..."
font.pixelSize: Theme.fontSizeLarge
@@ -464,16 +520,18 @@ Item {
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
Text {
text: "Make sure your device is in pairing mode"
font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
anchors.horizontalCenter: parent.horizontalCenter
}
}
Text {
text: "No devices found. Put your device in pairing mode and click Start Scanning."
font.pixelSize: Theme.fontSizeMedium
@@ -483,15 +541,39 @@ Item {
width: parent.width
horizontalAlignment: Text.AlignHCenter
}
}
}
}
Rectangle {
id: bluetoothContextMenuWindow
property var deviceData: null
property bool menuVisible: false
function show(x, y) {
const menuWidth = 160;
const menuHeight = menuColumn.implicitHeight + Theme.spacingS * 2;
let finalX = x - menuWidth / 2;
let finalY = y;
finalX = Math.max(0, Math.min(finalX, bluetoothTab.width - menuWidth));
finalY = Math.max(0, Math.min(finalY, bluetoothTab.height - menuHeight));
bluetoothContextMenuWindow.x = finalX;
bluetoothContextMenuWindow.y = finalY;
bluetoothContextMenuWindow.visible = true;
bluetoothContextMenuWindow.menuVisible = true;
}
function hide() {
bluetoothContextMenuWindow.menuVisible = false;
Qt.callLater(() => {
bluetoothContextMenuWindow.visible = false;
});
}
visible: false
width: 160
height: menuColumn.implicitHeight + Theme.spacingS * 2
@@ -500,7 +582,9 @@ Item {
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
z: 1000
opacity: menuVisible ? 1 : 0
scale: menuVisible ? 1 : 0.85
Rectangle {
anchors.fill: parent
anchors.topMargin: 4
@@ -511,42 +595,26 @@ Item {
color: Qt.rgba(0, 0, 0, 0.15)
z: parent.z - 1
}
opacity: menuVisible ? 1.0 : 0.0
scale: menuVisible ? 1.0 : 0.85
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on scale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Column {
id: menuColumn
anchors.fill: parent
anchors.margins: Theme.spacingS
spacing: 1
Rectangle {
width: parent.width
height: 32
radius: Theme.cornerRadiusSmall
color: connectArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
Text {
text: bluetoothContextMenuWindow.deviceData && bluetoothContextMenuWindow.deviceData.connected ? "link_off" : "link"
font.family: Theme.iconFont
@@ -555,7 +623,7 @@ Item {
opacity: 0.7
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: bluetoothContextMenuWindow.deviceData && bluetoothContextMenuWindow.deviceData.connected ? "Disconnect" : "Connect"
font.pixelSize: Theme.fontSizeSmall
@@ -563,56 +631,60 @@ Item {
font.weight: Font.Normal
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: connectArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (bluetoothContextMenuWindow.deviceData) {
BluetoothService.toggle(bluetoothContextMenuWindow.deviceData.address)
}
bluetoothContextMenuWindow.hide()
if (bluetoothContextMenuWindow.deviceData)
BluetoothService.toggle(bluetoothContextMenuWindow.deviceData.address);
bluetoothContextMenuWindow.hide();
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
Rectangle {
width: parent.width - Theme.spacingS * 2
height: 5
anchors.horizontalCenter: parent.horizontalCenter
color: "transparent"
Rectangle {
anchors.centerIn: parent
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
}
}
Rectangle {
width: parent.width
height: 32
radius: Theme.cornerRadiusSmall
color: forgetArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
Text {
text: "delete"
font.family: Theme.iconFont
@@ -621,7 +693,7 @@ Item {
opacity: 0.7
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: "Forget Device"
font.pixelSize: Theme.fontSizeSmall
@@ -629,60 +701,60 @@ Item {
font.weight: Font.Normal
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: forgetArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (bluetoothContextMenuWindow.deviceData) {
BluetoothService.forget(bluetoothContextMenuWindow.deviceData.address)
}
bluetoothContextMenuWindow.hide()
if (bluetoothContextMenuWindow.deviceData)
BluetoothService.forget(bluetoothContextMenuWindow.deviceData.address);
bluetoothContextMenuWindow.hide();
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
function show(x, y) {
const menuWidth = 160
const menuHeight = menuColumn.implicitHeight + Theme.spacingS * 2
let finalX = x - menuWidth / 2
let finalY = y
finalX = Math.max(0, Math.min(finalX, bluetoothTab.width - menuWidth))
finalY = Math.max(0, Math.min(finalY, bluetoothTab.height - menuHeight))
bluetoothContextMenuWindow.x = finalX
bluetoothContextMenuWindow.y = finalY
bluetoothContextMenuWindow.visible = true
bluetoothContextMenuWindow.menuVisible = true
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
function hide() {
bluetoothContextMenuWindow.menuVisible = false
Qt.callLater(() => { bluetoothContextMenuWindow.visible = false })
Behavior on scale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
MouseArea {
anchors.fill: parent
visible: bluetoothContextMenuWindow.visible
onClicked: {
bluetoothContextMenuWindow.hide()
bluetoothContextMenuWindow.hide();
}
MouseArea {
x: bluetoothContextMenuWindow.x
y: bluetoothContextMenuWindow.y
@@ -691,5 +763,7 @@ Item {
onClicked: {
}
}
}
}
}

View File

@@ -2,43 +2,38 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Services
PanelWindow {
id: root
property bool controlCenterVisible: false
property string currentTab: "network" // "network", "audio", "bluetooth", "display"
property bool powerOptionsExpanded: false
visible: controlCenterVisible
onVisibleChanged: {
// Enable/disable WiFi auto-refresh based on control center visibility
WifiService.autoRefreshEnabled = visible && NetworkService.wifiEnabled
WifiService.autoRefreshEnabled = visible && NetworkService.wifiEnabled;
}
implicitWidth: 600
implicitHeight: 500
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
color: "transparent"
anchors {
top: true
left: true
right: true
bottom: true
}
property string currentTab: "network" // "network", "audio", "bluetooth", "display"
property bool powerOptionsExpanded: false
Rectangle {
width: Math.min(600, Screen.width - Theme.spacingL * 2)
height: root.powerOptionsExpanded ? 570 : 500
@@ -48,50 +43,66 @@ PanelWindow {
radius: Theme.cornerRadiusLarge
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
opacity: controlCenterVisible ? 1 : 0
// TopBar dropdown animation - optimized for performance
transform: [
Scale {
id: scaleTransform
origin.x: 600 // Use fixed width since popup is max 600px wide
origin.x: 600 // Use fixed width since popup is max 600px wide
origin.y: 0
xScale: controlCenterVisible ? 1.0 : 0.95
yScale: controlCenterVisible ? 1.0 : 0.8
xScale: controlCenterVisible ? 1 : 0.95
yScale: controlCenterVisible ? 1 : 0.8
},
Translate {
id: translateTransform
x: controlCenterVisible ? 0 : 15 // Slide slightly left when hidden
x: controlCenterVisible ? 0 : 15 // Slide slightly left when hidden
y: controlCenterVisible ? 0 : -30
}
]
// Single coordinated animation for better performance
states: [
State {
name: "visible"
when: controlCenterVisible
PropertyChanges { target: scaleTransform; xScale: 1.0; yScale: 1.0 }
PropertyChanges { target: translateTransform; x: 0; y: 0 }
PropertyChanges {
target: scaleTransform
xScale: 1
yScale: 1
}
PropertyChanges {
target: translateTransform
x: 0
y: 0
}
},
State {
name: "hidden"
when: !controlCenterVisible
PropertyChanges { target: scaleTransform; xScale: 0.95; yScale: 0.8 }
PropertyChanges { target: translateTransform; x: 15; y: -30 }
PropertyChanges {
target: scaleTransform
xScale: 0.95
yScale: 0.8
}
PropertyChanges {
target: translateTransform
x: 15
y: -30
}
}
]
// Power menu height animation
Behavior on height {
NumberAnimation {
duration: Theme.shortDuration // Faster for height changes
easing.type: Theme.standardEasing
}
}
transitions: [
Transition {
from: "*"; to: "*"
from: "*"
to: "*"
ParallelAnimation {
NumberAnimation {
targets: [scaleTransform, translateTransform]
@@ -99,29 +110,22 @@ PanelWindow {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
]
opacity: controlCenterVisible ? 1.0 : 0.0
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Column {
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
// Elegant User Header
Column {
width: parent.width
spacing: Theme.spacingL
Rectangle {
width: parent.width
height: 90
@@ -129,22 +133,23 @@ PanelWindow {
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.4)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Theme.spacingL
anchors.rightMargin: Theme.spacingL
spacing: Theme.spacingL
// Profile Picture Container
Item {
id: avatarContainer
width: 64
height: 64
property bool hasImage: profileImageLoader.status === Image.Ready
width: 64
height: 64
// This rectangle provides the themed ring via its border.
Rectangle {
anchors.fill: parent
@@ -158,12 +163,15 @@ PanelWindow {
// Hidden Image loader. Its only purpose is to load the texture.
Image {
id: profileImageLoader
source: {
if (Prefs.profileImage === "") return ""
if (Prefs.profileImage.startsWith("/")) {
return "file://" + Prefs.profileImage
}
return Prefs.profileImage
if (Prefs.profileImage === "")
return "";
if (Prefs.profileImage.startsWith("/"))
return "file://" + Prefs.profileImage;
return Prefs.profileImage;
}
smooth: true
asynchronous: true
@@ -180,11 +188,12 @@ PanelWindow {
maskSource: circularMask
visible: avatarContainer.hasImage
maskThresholdMin: 0.5
maskSpreadAtMin: 1.0
maskSpreadAtMin: 1
}
Item {
id: circularMask
width: 64 - 10
height: 64 - 10
layer.enabled: true
@@ -197,6 +206,7 @@ PanelWindow {
color: "black"
antialiasing: true
}
}
// Fallback for when there is no image.
@@ -213,6 +223,7 @@ PanelWindow {
font.pixelSize: Theme.iconSize + 8
color: Theme.primaryText
}
}
// Error icon for when the image fails to load.
@@ -224,46 +235,46 @@ PanelWindow {
color: Theme.primaryText
visible: Prefs.profileImage !== "" && profileImageLoader.status === Image.Error
}
}
// User Info Text
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
Text {
text: UserInfoService.fullName || UserInfoService.username || "User"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
Text {
text: "Uptime: " + (UserInfoService.uptime || "Unknown")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
font.weight: Font.Normal
}
}
}
// Action Buttons - Power and Settings
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: Theme.spacingL
spacing: Theme.spacingS
// Power Button
Rectangle {
width: 40
height: 40
radius: 20
color: powerButton.containsMouse || root.powerOptionsExpanded ?
Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) :
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
color: powerButton.containsMouse || root.powerOptionsExpanded ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
Rectangle {
anchors.centerIn: parent
width: parent.width
@@ -271,65 +282,74 @@ PanelWindow {
radius: parent.radius
color: "transparent"
clip: true
Text {
anchors.centerIn: parent
text: root.powerOptionsExpanded ? "expand_less" : "power_settings_new"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize - 2
color: powerButton.containsMouse || root.powerOptionsExpanded ? Theme.error : Theme.surfaceText
Behavior on text {
// Smooth icon transition
SequentialAnimation {
NumberAnimation {
target: parent
property: "opacity"
to: 0.0
to: 0
duration: Theme.shortDuration / 2
easing.type: Theme.standardEasing
}
PropertyAction { target: parent; property: "text" }
PropertyAction {
target: parent
property: "text"
}
NumberAnimation {
target: parent
property: "opacity"
to: 1.0
to: 1
duration: Theme.shortDuration / 2
easing.type: Theme.standardEasing
}
}
}
}
}
MouseArea {
id: powerButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.powerOptionsExpanded = !root.powerOptionsExpanded
root.powerOptionsExpanded = !root.powerOptionsExpanded;
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
// Settings Button
Rectangle {
width: 40
height: 40
radius: 20
color: settingsButton.containsMouse ?
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
color: settingsButton.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
Text {
anchors.centerIn: parent
text: "settings"
@@ -337,29 +357,33 @@ PanelWindow {
font.pixelSize: Theme.iconSize - 2
color: Theme.surfaceText
}
MouseArea {
id: settingsButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
controlCenterVisible = false
settingsPopup.settingsVisible = true
controlCenterVisible = false;
settingsPopup.settingsVisible = true;
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
}
// Animated Collapsible Power Options (optimized)
Rectangle {
width: parent.width
@@ -368,42 +392,25 @@ PanelWindow {
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.4)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: root.powerOptionsExpanded ? 1 : 0
opacity: root.powerOptionsExpanded ? 1.0 : 0.0
opacity: root.powerOptionsExpanded ? 1 : 0
clip: true
// Single coordinated animation for power options
Behavior on height {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Row {
anchors.centerIn: parent
spacing: Theme.spacingL
visible: root.powerOptionsExpanded
// Logout
Rectangle {
width: 100
height: 34
radius: Theme.cornerRadius
color: logoutButton.containsMouse ?
Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12) :
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
color: logoutButton.containsMouse ? Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
Text {
text: "logout"
font.family: Theme.iconFont
@@ -411,7 +418,7 @@ PanelWindow {
color: logoutButton.containsMouse ? Theme.warning : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: "Logout"
font.pixelSize: Theme.fontSizeSmall
@@ -419,46 +426,47 @@ PanelWindow {
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: logoutButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.powerOptionsExpanded = false
root.powerOptionsExpanded = false;
if (typeof root !== "undefined" && root.powerConfirmDialog) {
root.powerConfirmDialog.powerConfirmAction = "logout"
root.powerConfirmDialog.powerConfirmTitle = "Logout"
root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to logout?"
root.powerConfirmDialog.powerConfirmVisible = true
root.powerConfirmDialog.powerConfirmAction = "logout";
root.powerConfirmDialog.powerConfirmTitle = "Logout";
root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to logout?";
root.powerConfirmDialog.powerConfirmVisible = true;
}
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
// Reboot
Rectangle {
width: 100
height: 34
radius: Theme.cornerRadius
color: rebootButton.containsMouse ?
Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12) :
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
color: rebootButton.containsMouse ? Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
Text {
text: "restart_alt"
font.family: Theme.iconFont
@@ -466,7 +474,7 @@ PanelWindow {
color: rebootButton.containsMouse ? Theme.warning : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: "Restart"
font.pixelSize: Theme.fontSizeSmall
@@ -474,46 +482,47 @@ PanelWindow {
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: rebootButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.powerOptionsExpanded = false
root.powerOptionsExpanded = false;
if (typeof root !== "undefined" && root.powerConfirmDialog) {
root.powerConfirmDialog.powerConfirmAction = "reboot"
root.powerConfirmDialog.powerConfirmTitle = "Restart"
root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to restart?"
root.powerConfirmDialog.powerConfirmVisible = true
root.powerConfirmDialog.powerConfirmAction = "reboot";
root.powerConfirmDialog.powerConfirmTitle = "Restart";
root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to restart?";
root.powerConfirmDialog.powerConfirmVisible = true;
}
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
// Shutdown
Rectangle {
width: 100
height: 34
radius: Theme.cornerRadius
color: shutdownButton.containsMouse ?
Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) :
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
color: shutdownButton.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
Text {
text: "power_settings_new"
font.family: Theme.iconFont
@@ -521,7 +530,7 @@ PanelWindow {
color: shutdownButton.containsMouse ? Theme.error : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: "Shutdown"
font.pixelSize: Theme.fontSizeSmall
@@ -529,77 +538,114 @@ PanelWindow {
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: shutdownButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.powerOptionsExpanded = false
root.powerOptionsExpanded = false;
if (typeof root !== "undefined" && root.powerConfirmDialog) {
root.powerConfirmDialog.powerConfirmAction = "poweroff"
root.powerConfirmDialog.powerConfirmTitle = "Shutdown"
root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to shutdown?"
root.powerConfirmDialog.powerConfirmVisible = true
root.powerConfirmDialog.powerConfirmAction = "poweroff";
root.powerConfirmDialog.powerConfirmTitle = "Shutdown";
root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to shutdown?";
root.powerConfirmDialog.powerConfirmVisible = true;
}
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
// Single coordinated animation for power options
Behavior on height {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
// Tab buttons
Row {
width: parent.width
spacing: Theme.spacingXS
Repeater {
model: {
let tabs = [
{name: "Network", icon: "wifi", id: "network", available: true}
]
let tabs = [{
"name": "Network",
"icon": "wifi",
"id": "network",
"available": true
}];
// Always show audio
tabs.push({name: "Audio", icon: "volume_up", id: "audio", available: true})
tabs.push({
"name": "Audio",
"icon": "volume_up",
"id": "audio",
"available": true
});
// Show Bluetooth only if available
if (BluetoothService.available) {
tabs.push({name: "Bluetooth", icon: "bluetooth", id: "bluetooth", available: true})
}
if (BluetoothService.available)
tabs.push({
"name": "Bluetooth",
"icon": "bluetooth",
"id": "bluetooth",
"available": true
});
// Always show display
tabs.push({name: "Display", icon: "brightness_6", id: "display", available: true})
return tabs
tabs.push({
"name": "Display",
"icon": "brightness_6",
"id": "display",
"available": true
});
return tabs;
}
Rectangle {
property int tabCount: {
let count = 3 // Network + Audio + Display (always visible)
if (BluetoothService.available) count++
return count
let count = 3; // Network + Audio + Display (always visible)
if (BluetoothService.available)
count++;
return count;
}
width: (parent.width - Theme.spacingXS * (tabCount - 1)) / tabCount
height: 40
radius: Theme.cornerRadius
color: root.currentTab === modelData.id ?
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) :
tabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
color: root.currentTab === modelData.id ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : tabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
Text {
text: modelData.icon
font.family: Theme.iconFont
@@ -607,7 +653,7 @@ PanelWindow {
color: root.currentTab === modelData.id ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: modelData.name
font.pixelSize: Theme.fontSizeSmall
@@ -615,75 +661,100 @@ PanelWindow {
font.weight: root.currentTab === modelData.id ? Font.Medium : Font.Normal
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: tabArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.currentTab = modelData.id
root.currentTab = modelData.id;
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
}
}
// Tab content area
Rectangle {
width: parent.width
height: root.powerOptionsExpanded ? 240 : 300
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.1)
Behavior on height {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
// Network Tab
NetworkTab {
anchors.fill: parent
anchors.margins: Theme.spacingM
visible: root.currentTab === "network"
}
// Audio Tab
AudioTab {
anchors.fill: parent
anchors.margins: Theme.spacingM
visible: root.currentTab === "audio"
}
// Bluetooth Tab
BluetoothTab {
anchors.fill: parent
anchors.margins: Theme.spacingM
visible: BluetoothService.available && root.currentTab === "bluetooth"
}
// Display Tab
DisplayTab {
anchors.fill: parent
anchors.margins: Theme.spacingM
visible: root.currentTab === "display"
}
Behavior on height {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
// Power menu height animation
Behavior on height {
NumberAnimation {
duration: Theme.shortDuration // Faster for height changes
easing.type: Theme.standardEasing
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
// Click outside to close
@@ -691,7 +762,8 @@ PanelWindow {
anchors.fill: parent
z: -1
onClicked: {
controlCenterVisible = false
controlCenterVisible = false;
}
}
}
}

View File

@@ -1,79 +1,77 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Widgets
import Quickshell.Io
import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Widgets
ScrollView {
id: displayTab
clip: true
Column {
width: parent.width
spacing: Theme.spacingL
// Brightness Control
Column {
width: parent.width
spacing: Theme.spacingM
visible: BrightnessService.brightnessAvailable
Text {
text: "Brightness"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
CustomSlider {
width: parent.width
value: BrightnessService.brightnessLevel
leftIcon: "brightness_low"
rightIcon: "brightness_high"
enabled: BrightnessService.brightnessAvailable
onSliderValueChanged: function(newValue) {
BrightnessService.setBrightness(newValue)
BrightnessService.setBrightness(newValue);
}
}
}
// Display settings
Column {
width: parent.width
spacing: Theme.spacingM
Text {
text: "Display Settings"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
// Mode toggles row (Night Mode + Light/Dark Mode)
Row {
width: parent.width
spacing: Theme.spacingM
// Night mode toggle
Rectangle {
width: (parent.width - Theme.spacingM) / 2
height: 80
radius: Theme.cornerRadius
color: Prefs.nightModeEnabled ?
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
(nightModeToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
color: Prefs.nightModeEnabled ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : (nightModeToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: Prefs.nightModeEnabled ? Theme.primary : "transparent"
border.width: Prefs.nightModeEnabled ? 1 : 0
Column {
anchors.centerIn: parent
spacing: Theme.spacingS
Text {
text: Prefs.nightModeEnabled ? "nightlight" : "dark_mode"
font.family: Theme.iconFont
@@ -81,7 +79,7 @@ ScrollView {
color: Prefs.nightModeEnabled ? Theme.primary : Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
text: "Night Mode"
font.pixelSize: Theme.fontSizeMedium
@@ -89,43 +87,43 @@ ScrollView {
font.weight: Font.Medium
anchors.horizontalCenter: parent.horizontalCenter
}
}
MouseArea {
id: nightModeToggle
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (Prefs.nightModeEnabled) {
// Disable night mode - kill any running color temperature processes
nightModeDisableProcess.running = true
Prefs.setNightModeEnabled(false)
nightModeDisableProcess.running = true;
Prefs.setNightModeEnabled(false);
} else {
// Enable night mode using wlsunset or redshift
nightModeEnableProcess.running = true
Prefs.setNightModeEnabled(true)
nightModeEnableProcess.running = true;
Prefs.setNightModeEnabled(true);
}
}
}
}
// Light/Dark mode toggle
Rectangle {
width: (parent.width - Theme.spacingM) / 2
height: 80
radius: Theme.cornerRadius
color: Theme.isLightMode ?
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
(lightModeToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
color: Theme.isLightMode ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : (lightModeToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: Theme.isLightMode ? Theme.primary : "transparent"
border.width: Theme.isLightMode ? 1 : 0
Column {
anchors.centerIn: parent
spacing: Theme.spacingS
Text {
text: Theme.isLightMode ? "light_mode" : "palette"
font.family: Theme.iconFont
@@ -133,7 +131,7 @@ ScrollView {
color: Theme.isLightMode ? Theme.primary : Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
text: Theme.isLightMode ? "Light Mode" : "Dark Mode"
font.pixelSize: Theme.fontSizeMedium
@@ -141,53 +139,60 @@ ScrollView {
font.weight: Font.Medium
anchors.horizontalCenter: parent.horizontalCenter
}
}
MouseArea {
id: lightModeToggle
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
Theme.toggleLightMode()
Theme.toggleLightMode();
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
}
}
// Night mode processes
Process {
id: nightModeEnableProcess
command: ["bash", "-c", "if command -v wlsunset > /dev/null; then pkill wlsunset; wlsunset -t 3000 & elif command -v redshift > /dev/null; then pkill redshift; redshift -P -O 3000 & else echo 'No night mode tool available'; fi"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Failed to enable night mode")
Prefs.setNightModeEnabled(false)
console.warn("Failed to enable night mode");
Prefs.setNightModeEnabled(false);
}
}
}
Process {
id: nightModeDisableProcess
id: nightModeDisableProcess
command: ["bash", "-c", "pkill wlsunset; pkill redshift; if command -v wlsunset > /dev/null; then wlsunset -t 6500 -T 6500 & sleep 1; pkill wlsunset; elif command -v redshift > /dev/null; then redshift -P -O 6500; redshift -x; fi"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Failed to disable night mode")
}
if (exitCode !== 0)
console.warn("Failed to disable night mode");
}
}
}
}

File diff suppressed because it is too large Load Diff