mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-08 23:02:05 -04:00
Initial qmlformat
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user