mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-27 06:52:50 -05:00
refactor: start trimming AI bullcrap patterns
This commit is contained in:
@@ -28,8 +28,6 @@ Singleton {
|
|||||||
function onReadyChanged() { _rebuildModels() }
|
function onReadyChanged() { _rebuildModels() }
|
||||||
function onDefaultAudioSinkChanged() { _rebuildModels() }
|
function onDefaultAudioSinkChanged() { _rebuildModels() }
|
||||||
function onDefaultAudioSourceChanged() { _rebuildModels() }
|
function onDefaultAudioSourceChanged() { _rebuildModels() }
|
||||||
function onNodeAdded() { _rebuildModels() }
|
|
||||||
function onNodeRemoved() { _rebuildModels() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import Quickshell.Services.UPower
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property var device: UPower.displayDevice
|
readonly property UPowerDevice device: UPower.displayDevice
|
||||||
readonly property bool batteryAvailable: device && device.ready && device.isLaptopBattery
|
readonly property bool batteryAvailable: device && device.ready && device.isLaptopBattery
|
||||||
readonly property int batteryLevel: batteryAvailable ? device.percentage * 100.0 : 0
|
readonly property int batteryLevel: batteryAvailable ? device.percentage * 100.0 : 0
|
||||||
readonly property bool isCharging: batteryAvailable && device.state === UPowerDeviceState.Charging
|
readonly property bool isCharging: batteryAvailable && device.state === UPowerDeviceState.Charging
|
||||||
|
|||||||
@@ -7,477 +7,170 @@ import Quickshell.Bluetooth
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool bluetoothEnabled: false
|
readonly property BluetoothAdapter adapter: Bluetooth.defaultAdapter
|
||||||
property bool bluetoothAvailable: false
|
readonly property bool available: adapter !== null
|
||||||
readonly property list<BluetoothDevice> bluetoothDevices: []
|
readonly property bool enabled: adapter?.enabled ?? false
|
||||||
readonly property list<BluetoothDevice> availableDevices: []
|
readonly property bool discovering: adapter?.discovering ?? false
|
||||||
property bool scanning: false
|
|
||||||
property bool discoverable: false
|
|
||||||
|
|
||||||
property var connectingDevices: ({})
|
readonly property var devices: {
|
||||||
|
var deviceList = []
|
||||||
|
if (!adapter) return deviceList
|
||||||
|
|
||||||
|
for (var i = 0; i < adapter.devices.count; i++) {
|
||||||
|
var dev = adapter.devices.get(i)
|
||||||
|
if (dev && dev.ready && _isValidDevice(dev)) {
|
||||||
|
deviceList.push({
|
||||||
|
address: dev.address,
|
||||||
|
name: dev.name || dev.deviceName,
|
||||||
|
paired: dev.paired,
|
||||||
|
connected: dev.connected,
|
||||||
|
iconName: _getDeviceIcon(dev),
|
||||||
|
type: _getDeviceType(dev),
|
||||||
|
batteryLevel: dev.batteryAvailable ? Math.round(dev.battery * 100) : -1,
|
||||||
|
batteryAvailable: dev.batteryAvailable,
|
||||||
|
native: dev
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deviceList
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var pairedDevices: {
|
||||||
|
return devices.filter(dev => dev.paired)
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var availableDevices: {
|
||||||
|
if (!discovering) return []
|
||||||
|
var availableList = []
|
||||||
|
|
||||||
|
if (Bluetooth.devices && Bluetooth.devices.values) {
|
||||||
|
for (var device of Bluetooth.devices.values) {
|
||||||
|
if (device && device.ready && !device.paired && _isValidDevice(device)) {
|
||||||
|
availableList.push({
|
||||||
|
address: device.address,
|
||||||
|
name: device.name || device.deviceName,
|
||||||
|
paired: false,
|
||||||
|
connected: false,
|
||||||
|
iconName: _getDeviceIcon(device),
|
||||||
|
type: _getDeviceType(device),
|
||||||
|
batteryLevel: -1,
|
||||||
|
batteryAvailable: false,
|
||||||
|
native: device
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return availableList
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var allDevicesWithBattery: {
|
||||||
|
return devices.filter(dev => dev.batteryAvailable && dev.batteryLevel >= 0)
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
refreshBluetoothState()
|
if (adapter && adapter.devices) {
|
||||||
updateDevices()
|
adapter.devices.itemAdded.connect(devicesChanged)
|
||||||
|
adapter.devices.itemRemoved.connect(devicesChanged)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Bluetooth.devices) {
|
||||||
|
Bluetooth.devices.itemAdded.connect(devicesChanged)
|
||||||
|
Bluetooth.devices.itemRemoved.connect(devicesChanged)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Bluetooth
|
target: Bluetooth
|
||||||
|
|
||||||
function onDefaultAdapterChanged() {
|
function onDefaultAdapterChanged() {
|
||||||
console.log("BluetoothService: Default adapter changed")
|
if (adapter && adapter.devices) {
|
||||||
refreshBluetoothState()
|
adapter.devices.itemAdded.connect(devicesChanged)
|
||||||
updateDevices()
|
adapter.devices.itemRemoved.connect(devicesChanged)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: Bluetooth.defaultAdapter
|
|
||||||
|
|
||||||
function onEnabledChanged() {
|
|
||||||
refreshBluetoothState()
|
|
||||||
updateDevices()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDiscoveringChanged() {
|
|
||||||
refreshBluetoothState()
|
|
||||||
updateDevices()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: Bluetooth.defaultAdapter ? Bluetooth.defaultAdapter.devices : null
|
|
||||||
|
|
||||||
function onModelReset() {
|
|
||||||
updateDevices()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onItemAdded() {
|
|
||||||
updateDevices()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onItemRemoved() {
|
|
||||||
updateDevices()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: Bluetooth.devices
|
|
||||||
|
|
||||||
function onModelReset() {
|
|
||||||
updateDevices()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onItemAdded() {
|
|
||||||
updateDevices()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onItemRemoved() {
|
|
||||||
updateDevices()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function refreshBluetoothState() {
|
|
||||||
root.bluetoothAvailable = Bluetooth.defaultAdapter !== null
|
|
||||||
root.bluetoothEnabled = Bluetooth.defaultAdapter ? Bluetooth.defaultAdapter.enabled : false
|
|
||||||
root.scanning = Bluetooth.defaultAdapter ? Bluetooth.defaultAdapter.discovering : false
|
|
||||||
root.discoverable = Bluetooth.defaultAdapter ? Bluetooth.defaultAdapter.discoverable : false
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateDevices() {
|
|
||||||
if (!Bluetooth.defaultAdapter) {
|
|
||||||
clearDeviceList(root.bluetoothDevices)
|
|
||||||
clearDeviceList(root.availableDevices)
|
|
||||||
root.bluetoothDevices = []
|
|
||||||
root.availableDevices = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let newPairedDevices = []
|
|
||||||
let newAvailableDevices = []
|
|
||||||
let allNativeDevices = []
|
|
||||||
|
|
||||||
let adapterDevices = Bluetooth.defaultAdapter.devices
|
|
||||||
if (adapterDevices.values) {
|
|
||||||
allNativeDevices = allNativeDevices.concat(adapterDevices.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Bluetooth.devices.values) {
|
|
||||||
for (let device of Bluetooth.devices.values) {
|
|
||||||
if (!allNativeDevices.some(d => d.address === device.address)) {
|
|
||||||
allNativeDevices.push(device)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let device of allNativeDevices) {
|
|
||||||
if (!device) continue
|
|
||||||
|
|
||||||
let deviceType = getDeviceType(device.name || device.deviceName, device.icon)
|
|
||||||
let displayName = device.name || device.deviceName
|
|
||||||
|
|
||||||
if (!displayName || displayName.startsWith('/org/bluez') || displayName.includes('hci0') || displayName.length < 2) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device.paired) {
|
|
||||||
let existingDevice = findDeviceInList(root.bluetoothDevices, device.address)
|
|
||||||
if (existingDevice) {
|
|
||||||
updateDeviceData(existingDevice, device, deviceType, displayName)
|
|
||||||
newPairedDevices.push(existingDevice)
|
|
||||||
} else {
|
|
||||||
let newDevice = createBluetoothDevice(device, deviceType, displayName)
|
|
||||||
newPairedDevices.push(newDevice)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (Bluetooth.defaultAdapter.discovering && isDeviceDiscoverable(device)) {
|
|
||||||
let existingDevice = findDeviceInList(root.availableDevices, device.address)
|
|
||||||
if (existingDevice) {
|
|
||||||
updateDeviceData(existingDevice, device, deviceType, displayName)
|
|
||||||
newAvailableDevices.push(existingDevice)
|
|
||||||
} else {
|
|
||||||
let newDevice = createBluetoothDevice(device, deviceType, displayName)
|
|
||||||
newAvailableDevices.push(newDevice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanupOldDevices(root.bluetoothDevices, newPairedDevices)
|
|
||||||
cleanupOldDevices(root.availableDevices, newAvailableDevices)
|
|
||||||
|
|
||||||
console.log("BluetoothService: Found", newPairedDevices.length, "paired devices and", newAvailableDevices.length, "available devices")
|
|
||||||
|
|
||||||
root.bluetoothDevices = newPairedDevices
|
|
||||||
root.availableDevices = newAvailableDevices
|
|
||||||
}
|
|
||||||
|
|
||||||
function createBluetoothDevice(nativeDevice, deviceType, displayName) {
|
|
||||||
return deviceComponent.createObject(root, {
|
|
||||||
mac: nativeDevice.address,
|
|
||||||
name: displayName,
|
|
||||||
type: deviceType,
|
|
||||||
paired: nativeDevice.paired,
|
|
||||||
connected: nativeDevice.connected,
|
|
||||||
battery: nativeDevice.batteryAvailable ? Math.round(nativeDevice.battery * 100) : -1,
|
|
||||||
signalStrength: nativeDevice.connected ? "excellent" : "unknown",
|
|
||||||
canPair: !nativeDevice.paired,
|
|
||||||
nativeDevice: nativeDevice,
|
|
||||||
connecting: false,
|
|
||||||
connectionFailed: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateDeviceData(deviceObj, nativeDevice, deviceType, displayName) {
|
|
||||||
deviceObj.name = displayName
|
|
||||||
deviceObj.type = deviceType
|
|
||||||
deviceObj.paired = nativeDevice.paired
|
|
||||||
|
|
||||||
// If device connected state changed, clear connecting/failed states and refresh audio
|
|
||||||
if (deviceObj.connected !== nativeDevice.connected) {
|
|
||||||
deviceObj.connecting = false
|
|
||||||
deviceObj.connectionFailed = false
|
|
||||||
|
|
||||||
// Refresh audio devices when bluetooth audio device connects/disconnects
|
|
||||||
if (deviceType === "headset" || deviceType === "speaker") {
|
|
||||||
Qt.callLater(() => {
|
|
||||||
if (typeof AudioService !== 'undefined') {
|
|
||||||
AudioService.updateDevices()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceObj.connected = nativeDevice.connected
|
|
||||||
deviceObj.battery = nativeDevice.batteryAvailable ? Math.round(nativeDevice.battery * 100) : -1
|
|
||||||
deviceObj.signalStrength = nativeDevice.connected ? "excellent" : "unknown"
|
|
||||||
deviceObj.canPair = !nativeDevice.paired
|
|
||||||
deviceObj.nativeDevice = nativeDevice
|
|
||||||
}
|
|
||||||
|
|
||||||
function findDeviceInList(deviceList, address) {
|
|
||||||
for (let device of deviceList) {
|
|
||||||
if (device.mac === address) {
|
|
||||||
return device
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanupOldDevices(oldList, newList) {
|
|
||||||
for (let oldDevice of oldList) {
|
|
||||||
if (!newList.includes(oldDevice)) {
|
|
||||||
oldDevice.destroy()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearDeviceList(deviceList) {
|
function _isValidDevice(device) {
|
||||||
for (let device of deviceList) {
|
var displayName = device.name || device.deviceName
|
||||||
device.destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDeviceDiscoverable(device) {
|
|
||||||
let displayName = device.name || device.deviceName
|
|
||||||
if (!displayName || displayName.length < 2) return false
|
if (!displayName || displayName.length < 2) return false
|
||||||
|
|
||||||
if (displayName.startsWith('/org/bluez') || displayName.includes('hci0')) return false
|
if (displayName.startsWith('/org/bluez') || displayName.includes('hci0')) return false
|
||||||
|
return displayName.length >= 3
|
||||||
let nameLower = displayName.toLowerCase()
|
|
||||||
|
|
||||||
if (nameLower.match(/^[0-9a-f]{2}[:-][0-9a-f]{2}[:-][0-9a-f]{2}/)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (displayName.length < 3) return false
|
|
||||||
|
|
||||||
if (nameLower.includes('iphone') || nameLower.includes('ipad') ||
|
|
||||||
nameLower.includes('airpods') || nameLower.includes('samsung') ||
|
|
||||||
nameLower.includes('galaxy') || nameLower.includes('pixel') ||
|
|
||||||
nameLower.includes('headphone') || nameLower.includes('speaker') ||
|
|
||||||
nameLower.includes('mouse') || nameLower.includes('keyboard') ||
|
|
||||||
nameLower.includes('watch') || nameLower.includes('buds') ||
|
|
||||||
nameLower.includes('android')) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return displayName.length >= 4 && !displayName.match(/^[A-Z0-9_-]+$/)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDeviceType(name, icon) {
|
function _getDeviceIcon(device) {
|
||||||
if (!name && !icon) return "bluetooth"
|
var name = (device.name || device.deviceName || "").toLowerCase()
|
||||||
|
var icon = (device.icon || "").toLowerCase()
|
||||||
let nameLower = (name || "").toLowerCase()
|
|
||||||
let iconLower = (icon || "").toLowerCase()
|
|
||||||
|
|
||||||
if (iconLower.includes("audio") || iconLower.includes("headset") || iconLower.includes("headphone") ||
|
|
||||||
nameLower.includes("headphone") || nameLower.includes("airpod") || nameLower.includes("headset") ||
|
|
||||||
nameLower.includes("arctis") || nameLower.includes("audio")) return "headset"
|
|
||||||
else if (iconLower.includes("input-mouse") || nameLower.includes("mouse")) return "mouse"
|
|
||||||
else if (iconLower.includes("input-keyboard") || nameLower.includes("keyboard")) return "keyboard"
|
|
||||||
else if (iconLower.includes("phone") || nameLower.includes("phone") || nameLower.includes("iphone") ||
|
|
||||||
nameLower.includes("samsung") || nameLower.includes("android")) return "phone"
|
|
||||||
else if (iconLower.includes("watch") || nameLower.includes("watch")) return "watch"
|
|
||||||
else if (iconLower.includes("audio-speakers") || nameLower.includes("speaker")) return "speaker"
|
|
||||||
else if (iconLower.includes("video-display") || nameLower.includes("tv") || nameLower.includes("display")) return "tv"
|
|
||||||
|
|
||||||
|
if (icon.includes("headset") || icon.includes("audio") || name.includes("headphone") ||
|
||||||
|
name.includes("airpod") || name.includes("headset") || name.includes("arctis")) return "headset"
|
||||||
|
if (icon.includes("mouse") || name.includes("mouse")) return "mouse"
|
||||||
|
if (icon.includes("keyboard") || name.includes("keyboard")) return "keyboard"
|
||||||
|
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") ||
|
||||||
|
name.includes("android") || name.includes("samsung")) return "smartphone"
|
||||||
|
if (icon.includes("watch") || name.includes("watch")) return "watch"
|
||||||
|
if (icon.includes("speaker") || name.includes("speaker")) return "speaker"
|
||||||
|
if (icon.includes("display") || name.includes("tv")) return "tv"
|
||||||
return "bluetooth"
|
return "bluetooth"
|
||||||
}
|
}
|
||||||
|
|
||||||
function startDiscovery() {
|
function _getDeviceType(device) {
|
||||||
if (Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.enabled) {
|
var name = (device.name || device.deviceName || "").toLowerCase()
|
||||||
Bluetooth.defaultAdapter.discovering = true
|
var icon = (device.icon || "").toLowerCase()
|
||||||
updateDevices()
|
|
||||||
}
|
if (icon.includes("headset") || icon.includes("audio") || name.includes("headphone") ||
|
||||||
|
name.includes("airpod") || name.includes("headset") || name.includes("arctis")) return "headset"
|
||||||
|
if (icon.includes("mouse") || name.includes("mouse")) return "mouse"
|
||||||
|
if (icon.includes("keyboard") || name.includes("keyboard")) return "keyboard"
|
||||||
|
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") ||
|
||||||
|
name.includes("android") || name.includes("samsung")) return "phone"
|
||||||
|
if (icon.includes("watch") || name.includes("watch")) return "watch"
|
||||||
|
if (icon.includes("speaker") || name.includes("speaker")) return "speaker"
|
||||||
|
if (icon.includes("display") || name.includes("tv")) return "tv"
|
||||||
|
return "bluetooth"
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopDiscovery() {
|
function toggleAdapter() {
|
||||||
if (Bluetooth.defaultAdapter) {
|
if (adapter) adapter.enabled = !adapter.enabled
|
||||||
Bluetooth.defaultAdapter.discovering = false
|
|
||||||
updateDevices()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pairDevice(mac) {
|
function startScan() {
|
||||||
console.log("Pairing device:", mac)
|
if (adapter) adapter.discovering = true
|
||||||
let device = findDeviceByMac(mac)
|
}
|
||||||
|
|
||||||
|
function stopScan() {
|
||||||
|
if (adapter) adapter.discovering = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function connect(address) {
|
||||||
|
var device = _findDevice(address)
|
||||||
|
if (device) device.connect()
|
||||||
|
}
|
||||||
|
|
||||||
|
function disconnect(address) {
|
||||||
|
var device = _findDevice(address)
|
||||||
|
if (device) device.disconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
function pair(address) {
|
||||||
|
var device = _findDevice(address)
|
||||||
|
if (device) device.pair()
|
||||||
|
}
|
||||||
|
|
||||||
|
function forget(address) {
|
||||||
|
var device = _findDevice(address)
|
||||||
|
if (device) device.forget()
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(address) {
|
||||||
|
var device = _findDevice(address)
|
||||||
if (device) {
|
if (device) {
|
||||||
device.pair()
|
if (device.connected) device.disconnect()
|
||||||
|
else device.connect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectDevice(mac) {
|
function _findDevice(address) {
|
||||||
console.log("Connecting to device:", mac)
|
if (!adapter) return null
|
||||||
let device = findDeviceByMac(mac)
|
return adapter.devices.values.find(d => d.address === address) ||
|
||||||
if (device) {
|
(Bluetooth.devices ? Bluetooth.devices.values.find(d => d.address === address) : null)
|
||||||
device.connect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeDevice(mac) {
|
|
||||||
console.log("Removing device:", mac)
|
|
||||||
let device = findDeviceByMac(mac)
|
|
||||||
if (device) {
|
|
||||||
device.forget()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleBluetoothDevice(mac) {
|
|
||||||
let typedDevice = findDeviceInList(root.bluetoothDevices, mac)
|
|
||||||
if (!typedDevice) {
|
|
||||||
typedDevice = findDeviceInList(root.availableDevices, mac)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typedDevice && typedDevice.nativeDevice) {
|
|
||||||
if (typedDevice.connected) {
|
|
||||||
console.log("Disconnecting device:", mac)
|
|
||||||
typedDevice.connecting = false
|
|
||||||
typedDevice.connectionFailed = false
|
|
||||||
typedDevice.nativeDevice.connected = false
|
|
||||||
} else {
|
|
||||||
console.log("Connecting to device:", mac)
|
|
||||||
typedDevice.connecting = true
|
|
||||||
typedDevice.connectionFailed = false
|
|
||||||
|
|
||||||
// Set a timeout to handle connection failure
|
|
||||||
Qt.callLater(() => {
|
|
||||||
connectionTimeout.deviceMac = mac
|
|
||||||
connectionTimeout.start()
|
|
||||||
})
|
|
||||||
|
|
||||||
typedDevice.nativeDevice.connected = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleBluetooth() {
|
|
||||||
if (Bluetooth.defaultAdapter) {
|
|
||||||
Bluetooth.defaultAdapter.enabled = !Bluetooth.defaultAdapter.enabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function findDeviceByMac(mac) {
|
|
||||||
let typedDevice = findDeviceInList(root.bluetoothDevices, mac)
|
|
||||||
if (typedDevice && typedDevice.nativeDevice) {
|
|
||||||
return typedDevice.nativeDevice
|
|
||||||
}
|
|
||||||
|
|
||||||
typedDevice = findDeviceInList(root.availableDevices, mac)
|
|
||||||
if (typedDevice && typedDevice.nativeDevice) {
|
|
||||||
return typedDevice.nativeDevice
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Bluetooth.defaultAdapter) {
|
|
||||||
let adapterDevices = Bluetooth.defaultAdapter.devices
|
|
||||||
if (adapterDevices.values) {
|
|
||||||
for (let device of adapterDevices.values) {
|
|
||||||
if (device && device.address === mac) {
|
|
||||||
return device
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Bluetooth.devices.values) {
|
|
||||||
for (let device of Bluetooth.devices.values) {
|
|
||||||
if (device && device.address === mac) {
|
|
||||||
return device
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: bluetoothMonitorTimer
|
|
||||||
interval: 2000
|
|
||||||
running: false
|
|
||||||
repeat: true
|
|
||||||
onTriggered: {
|
|
||||||
updateDevices()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function enableMonitoring(enabled) {
|
|
||||||
bluetoothMonitorTimer.running = enabled
|
|
||||||
if (enabled) {
|
|
||||||
refreshBluetoothState()
|
|
||||||
updateDevices()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: bluetoothStateRefreshTimer
|
|
||||||
interval: 5000
|
|
||||||
running: true
|
|
||||||
repeat: true
|
|
||||||
onTriggered: {
|
|
||||||
refreshBluetoothState()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: connectionTimeout
|
|
||||||
interval: 10000 // 10 second timeout
|
|
||||||
running: false
|
|
||||||
repeat: false
|
|
||||||
|
|
||||||
property string deviceMac: ""
|
|
||||||
|
|
||||||
onTriggered: {
|
|
||||||
if (deviceMac) {
|
|
||||||
let typedDevice = findDeviceInList(root.bluetoothDevices, deviceMac)
|
|
||||||
if (!typedDevice) {
|
|
||||||
typedDevice = findDeviceInList(root.availableDevices, deviceMac)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typedDevice && typedDevice.connecting && !typedDevice.connected) {
|
|
||||||
console.log("Connection timeout for device:", deviceMac)
|
|
||||||
typedDevice.connecting = false
|
|
||||||
typedDevice.connectionFailed = true
|
|
||||||
|
|
||||||
// Clear failure state after 3 seconds
|
|
||||||
Qt.callLater(() => {
|
|
||||||
clearFailureTimer.deviceMac = deviceMac
|
|
||||||
clearFailureTimer.start()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
deviceMac = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: clearFailureTimer
|
|
||||||
interval: 3000
|
|
||||||
running: false
|
|
||||||
repeat: false
|
|
||||||
|
|
||||||
property string deviceMac: ""
|
|
||||||
|
|
||||||
onTriggered: {
|
|
||||||
if (deviceMac) {
|
|
||||||
let typedDevice = findDeviceInList(root.bluetoothDevices, deviceMac)
|
|
||||||
if (!typedDevice) {
|
|
||||||
typedDevice = findDeviceInList(root.availableDevices, deviceMac)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typedDevice) {
|
|
||||||
typedDevice.connectionFailed = false
|
|
||||||
}
|
|
||||||
deviceMac = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component BluetoothDevice: QtObject {
|
|
||||||
required property string mac
|
|
||||||
required property string name
|
|
||||||
required property string type
|
|
||||||
required property bool paired
|
|
||||||
required property bool connected
|
|
||||||
required property int battery
|
|
||||||
required property string signalStrength
|
|
||||||
required property bool canPair
|
|
||||||
required property var nativeDevice // Reference to native Quickshell device
|
|
||||||
|
|
||||||
property bool connecting: false
|
|
||||||
property bool connectionFailed: false
|
|
||||||
|
|
||||||
readonly property string displayName: name
|
|
||||||
readonly property bool batteryAvailable: battery >= 0
|
|
||||||
readonly property string connectionStatus: {
|
|
||||||
if (connecting) return "Connecting..."
|
|
||||||
if (connectionFailed) return "Connection Failed"
|
|
||||||
if (connected) return "Connected"
|
|
||||||
return "Disconnected"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: deviceComponent
|
|
||||||
BluetoothDevice {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,159 +1,74 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQml.Models
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Services.Mpris
|
import Quickshell.Services.Mpris
|
||||||
|
import Quickshell.Widgets
|
||||||
pragma Singleton
|
pragma Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
/**
|
|
||||||
* A service that provides easy access to the active Mpris player.
|
|
||||||
*/
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
property MprisPlayer trackedPlayer: null
|
|
||||||
property MprisPlayer activePlayer: trackedPlayer ?? Mpris.players.values[0] ?? null
|
|
||||||
signal trackChanged(reverse: bool)
|
|
||||||
|
|
||||||
property bool __reverse: false
|
readonly property list<MprisPlayer> availablePlayers: Mpris.players.values
|
||||||
|
|
||||||
property var activeTrack
|
property MprisPlayer activePlayer: null
|
||||||
|
property MprisPlayer _candidatePlayer: availablePlayers.find(p => p.isPlaying)
|
||||||
|
?? availablePlayers.find(p => p.canControl && p.canPlay)
|
||||||
|
?? null
|
||||||
|
|
||||||
Instantiator {
|
Timer {
|
||||||
model: Mpris.players
|
id: playerSwitchTimer
|
||||||
|
interval: 300
|
||||||
Connections {
|
onTriggered: {
|
||||||
required property MprisPlayer modelData
|
if (_candidatePlayer !== activePlayer) {
|
||||||
target: modelData
|
activePlayer = _candidatePlayer
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
if (root.trackedPlayer == null || modelData.isPlaying) {
|
|
||||||
root.trackedPlayer = modelData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onDestruction: {
|
|
||||||
if (root.trackedPlayer == null || root.trackedPlayer.playbackState !== MprisPlaybackState.Playing) {
|
|
||||||
for (const player of Mpris.players.values) {
|
|
||||||
if (player.playbackState === MprisPlaybackState.Playing) {
|
|
||||||
root.trackedPlayer = player
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedPlayer == null && Mpris.players.values.length != 0) {
|
|
||||||
trackedPlayer = Mpris.players.values[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onPlaybackStateChanged() {
|
|
||||||
if (root.trackedPlayer !== modelData) root.trackedPlayer = modelData
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
on_CandidatePlayerChanged: {
|
||||||
target: activePlayer
|
if (_candidatePlayer === null && activePlayer !== null) {
|
||||||
|
playerSwitchTimer.restart()
|
||||||
function onPostTrackChanged() {
|
} else if (_candidatePlayer !== null) {
|
||||||
root.updateTrack()
|
playerSwitchTimer.stop()
|
||||||
}
|
activePlayer = _candidatePlayer
|
||||||
|
|
||||||
function onTrackArtUrlChanged() {
|
|
||||||
if (root.activePlayer.uniqueId == root.activeTrack.uniqueId && root.activePlayer.trackArtUrl != root.activeTrack.artUrl) {
|
|
||||||
const r = root.__reverse
|
|
||||||
root.updateTrack()
|
|
||||||
root.__reverse = r
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onActivePlayerChanged: this.updateTrack()
|
IpcHandler {
|
||||||
|
target: "mpris"
|
||||||
|
|
||||||
function updateTrack() {
|
function list(): string {
|
||||||
this.activeTrack = {
|
return root.availablePlayers.map(p => p.identity).join("");
|
||||||
uniqueId: this.activePlayer?.uniqueId ?? 0,
|
|
||||||
artUrl: this.activePlayer?.trackArtUrl ?? "",
|
|
||||||
title: this.activePlayer?.trackTitle || "Unknown Title",
|
|
||||||
artist: this.activePlayer?.trackArtist || "Unknown Artist",
|
|
||||||
album: this.activePlayer?.trackAlbum || "Unknown Album",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.trackChanged(__reverse)
|
function play(): void {
|
||||||
this.__reverse = false
|
if (root.activePlayer?.canPlay)
|
||||||
}
|
root.activePlayer.play();
|
||||||
|
|
||||||
property bool isPlaying: this.activePlayer && this.activePlayer.playbackState === MprisPlaybackState.Playing
|
|
||||||
property bool canTogglePlaying: this.activePlayer?.canTogglePlaying ?? false
|
|
||||||
function togglePlaying() {
|
|
||||||
if (this.canTogglePlaying) this.activePlayer.togglePlaying()
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool canGoPrevious: this.activePlayer?.canGoPrevious ?? false
|
|
||||||
function previous() {
|
|
||||||
if (this.canGoPrevious) {
|
|
||||||
this.__reverse = true
|
|
||||||
this.activePlayer.previous()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool canGoNext: this.activePlayer?.canGoNext ?? false
|
|
||||||
function next() {
|
|
||||||
if (this.canGoNext) {
|
|
||||||
this.__reverse = false
|
|
||||||
this.activePlayer.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool canChangeVolume: this.activePlayer && this.activePlayer.volumeSupported && this.activePlayer.canControl
|
|
||||||
|
|
||||||
property bool loopSupported: this.activePlayer && this.activePlayer.loopSupported && this.activePlayer.canControl
|
|
||||||
property var loopState: this.activePlayer?.loopState ?? MprisLoopState.None
|
|
||||||
function setLoopState(loopState) {
|
|
||||||
if (this.loopSupported) {
|
|
||||||
this.activePlayer.loopState = loopState
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool shuffleSupported: this.activePlayer && this.activePlayer.shuffleSupported && this.activePlayer.canControl
|
|
||||||
property bool hasShuffle: this.activePlayer?.shuffle ?? false
|
|
||||||
function setShuffle(shuffle) {
|
|
||||||
if (this.shuffleSupported) {
|
|
||||||
this.activePlayer.shuffle = shuffle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setActivePlayer(player) {
|
|
||||||
const targetPlayer = player ?? Mpris.players[0]
|
|
||||||
|
|
||||||
if (targetPlayer && this.activePlayer) {
|
|
||||||
this.__reverse = Mpris.players.indexOf(targetPlayer) < Mpris.players.indexOf(this.activePlayer)
|
|
||||||
} else {
|
|
||||||
this.__reverse = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.trackedPlayer = targetPlayer
|
function pause(): void {
|
||||||
}
|
if (root.activePlayer?.canPause)
|
||||||
|
root.activePlayer.pause();
|
||||||
|
}
|
||||||
|
|
||||||
// Seeking support
|
function playPause(): void {
|
||||||
property bool canSeek: this.activePlayer?.canSeek ?? false
|
if (root.activePlayer?.canTogglePlaying)
|
||||||
property real position: this.activePlayer?.position ?? 0
|
root.activePlayer.togglePlaying();
|
||||||
property real length: this.activePlayer?.length ?? 0
|
}
|
||||||
|
|
||||||
function seek(offsetUs) {
|
function previous(): void {
|
||||||
if (this.canSeek && this.activePlayer) {
|
if (root.activePlayer?.canGoPrevious)
|
||||||
this.activePlayer.seek(offsetUs)
|
root.activePlayer.previous();
|
||||||
|
}
|
||||||
|
|
||||||
|
function next(): void {
|
||||||
|
if (root.activePlayer?.canGoNext)
|
||||||
|
root.activePlayer.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop(): void {
|
||||||
|
root.activePlayer?.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPosition(trackId, positionUs) {
|
|
||||||
if (this.canSeek && this.activePlayer && typeof this.activePlayer.setPosition === "function") {
|
|
||||||
this.activePlayer.setPosition(trackId, positionUs)
|
|
||||||
} else if (this.canSeek && this.activePlayer) {
|
|
||||||
// Fallback to setting position property
|
|
||||||
this.activePlayer.position = positionUs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -12,25 +12,7 @@ PanelWindow {
|
|||||||
id: centerCommandCenter
|
id: centerCommandCenter
|
||||||
|
|
||||||
property var theme: Theme
|
property var theme: Theme
|
||||||
property bool hasActiveMedia: root.hasActiveMedia
|
readonly property bool hasActiveMedia: MprisController.activePlayer !== null
|
||||||
property var weather: root.weather
|
|
||||||
|
|
||||||
property bool showMediaPlayer: hasActiveMedia || hideMediaTimer.running
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: hideMediaTimer
|
|
||||||
interval: 3000
|
|
||||||
running: false
|
|
||||||
repeat: false
|
|
||||||
}
|
|
||||||
|
|
||||||
onHasActiveMediaChanged: {
|
|
||||||
if (hasActiveMedia) {
|
|
||||||
hideMediaTimer.stop()
|
|
||||||
} else {
|
|
||||||
hideMediaTimer.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visible: root.calendarVisible
|
visible: root.calendarVisible
|
||||||
|
|
||||||
@@ -70,7 +52,7 @@ PanelWindow {
|
|||||||
|
|
||||||
// Main row with widgets and calendar
|
// Main row with widgets and calendar
|
||||||
let widgetHeight = 160 // Media widget always present
|
let widgetHeight = 160 // Media widget always present
|
||||||
widgetHeight += (weather ? 140 : 80) + theme.spacingM // Weather widget always present
|
widgetHeight += 140 + theme.spacingM // Weather widget always present
|
||||||
let calendarHeight = 300
|
let calendarHeight = 300
|
||||||
let mainRowHeight = Math.max(widgetHeight, calendarHeight)
|
let mainRowHeight = Math.max(widgetHeight, calendarHeight)
|
||||||
|
|
||||||
@@ -177,7 +159,7 @@ PanelWindow {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: {
|
height: {
|
||||||
let widgetHeight = 160 // Media widget always present
|
let widgetHeight = 160 // Media widget always present
|
||||||
widgetHeight += (weather ? 140 : 80) + theme.spacingM // Weather widget always present
|
widgetHeight += 140 + theme.spacingM // Weather widget always present
|
||||||
let calendarHeight = 300
|
let calendarHeight = 300
|
||||||
return Math.max(widgetHeight, calendarHeight)
|
return Math.max(widgetHeight, calendarHeight)
|
||||||
}
|
}
|
||||||
@@ -204,9 +186,8 @@ PanelWindow {
|
|||||||
WeatherWidget {
|
WeatherWidget {
|
||||||
visible: true // Always visible - shows placeholder when no weather
|
visible: true // Always visible - shows placeholder when no weather
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: weather ? 140 : 80
|
height: 140
|
||||||
theme: centerCommandCenter.theme
|
theme: centerCommandCenter.theme
|
||||||
weather: centerCommandCenter.weather
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,32 @@ Rectangle {
|
|||||||
property MprisPlayer activePlayer: MprisController.activePlayer
|
property MprisPlayer activePlayer: MprisController.activePlayer
|
||||||
property var theme: Theme
|
property var theme: Theme
|
||||||
|
|
||||||
|
property string lastValidTitle: ""
|
||||||
|
property string lastValidArtist: ""
|
||||||
|
property string lastValidAlbum: ""
|
||||||
|
property string lastValidArtUrl: ""
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: clearCacheTimer
|
||||||
|
interval: 2000
|
||||||
|
onTriggered: {
|
||||||
|
if (!activePlayer) {
|
||||||
|
lastValidTitle = ""
|
||||||
|
lastValidArtist = ""
|
||||||
|
lastValidAlbum = ""
|
||||||
|
lastValidArtUrl = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onActivePlayerChanged: {
|
||||||
|
if (!activePlayer) {
|
||||||
|
clearCacheTimer.restart()
|
||||||
|
} else {
|
||||||
|
clearCacheTimer.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: theme.cornerRadiusLarge
|
radius: theme.cornerRadiusLarge
|
||||||
@@ -49,15 +75,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize when player changes
|
|
||||||
onActivePlayerChanged: {
|
|
||||||
if (activePlayer) {
|
|
||||||
currentPosition = activePlayer.position || 0
|
|
||||||
} else {
|
|
||||||
currentPosition = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backend events
|
// Backend events
|
||||||
Connections {
|
Connections {
|
||||||
target: activePlayer
|
target: activePlayer
|
||||||
@@ -85,7 +102,7 @@ Rectangle {
|
|||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: theme.spacingS
|
spacing: theme.spacingS
|
||||||
visible: !activePlayer || !activePlayer.trackTitle || activePlayer.trackTitle === ""
|
visible: (!activePlayer && !lastValidTitle) || (activePlayer && activePlayer.trackTitle === "" && lastValidTitle === "")
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "music_note"
|
text: "music_note"
|
||||||
@@ -107,7 +124,7 @@ Rectangle {
|
|||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: theme.spacingS
|
spacing: theme.spacingS
|
||||||
visible: activePlayer && activePlayer.trackTitle && activePlayer.trackTitle !== ""
|
visible: activePlayer && activePlayer.trackTitle !== "" || lastValidTitle !== ""
|
||||||
|
|
||||||
// Normal media info when playing
|
// Normal media info when playing
|
||||||
Row {
|
Row {
|
||||||
@@ -129,7 +146,12 @@ Rectangle {
|
|||||||
Image {
|
Image {
|
||||||
id: albumArt
|
id: albumArt
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: activePlayer?.trackArtUrl || ""
|
source: activePlayer?.trackArtUrl || lastValidArtUrl || ""
|
||||||
|
onSourceChanged: {
|
||||||
|
if (activePlayer?.trackArtUrl) {
|
||||||
|
lastValidArtUrl = activePlayer.trackArtUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
smooth: true
|
smooth: true
|
||||||
}
|
}
|
||||||
@@ -157,7 +179,12 @@ Rectangle {
|
|||||||
spacing: theme.spacingXS
|
spacing: theme.spacingXS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: activePlayer?.trackTitle || "Unknown Track"
|
text: activePlayer?.trackTitle || lastValidTitle || "Unknown Track"
|
||||||
|
onTextChanged: {
|
||||||
|
if (activePlayer?.trackTitle) {
|
||||||
|
lastValidTitle = activePlayer.trackTitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
font.pixelSize: theme.fontSizeMedium
|
font.pixelSize: theme.fontSizeMedium
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: theme.surfaceText
|
color: theme.surfaceText
|
||||||
@@ -166,7 +193,12 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: activePlayer?.trackArtist || "Unknown Artist"
|
text: activePlayer?.trackArtist || lastValidArtist || "Unknown Artist"
|
||||||
|
onTextChanged: {
|
||||||
|
if (activePlayer?.trackArtist) {
|
||||||
|
lastValidArtist = activePlayer.trackArtist;
|
||||||
|
}
|
||||||
|
}
|
||||||
font.pixelSize: theme.fontSizeSmall
|
font.pixelSize: theme.fontSizeSmall
|
||||||
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.8)
|
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.8)
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -174,7 +206,12 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: activePlayer?.trackAlbum || ""
|
text: activePlayer?.trackAlbum || lastValidAlbum || ""
|
||||||
|
onTextChanged: {
|
||||||
|
if (activePlayer?.trackAlbum) {
|
||||||
|
lastValidAlbum = activePlayer.trackAlbum;
|
||||||
|
}
|
||||||
|
}
|
||||||
font.pixelSize: theme.fontSizeSmall
|
font.pixelSize: theme.fontSizeSmall
|
||||||
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.6)
|
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.6)
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ Rectangle {
|
|||||||
id: weatherWidget
|
id: weatherWidget
|
||||||
|
|
||||||
property var theme: Theme
|
property var theme: Theme
|
||||||
property var weather
|
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
@@ -31,26 +30,18 @@ Rectangle {
|
|||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: theme.spacingS
|
spacing: theme.spacingS
|
||||||
visible: !weather || !weather.available || weather.temp === 0
|
visible: !WeatherService.weather.available || WeatherService.weather.temp === 0
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: weather && weather.loading ? "cloud_sync" : "cloud_off"
|
text: "cloud_off"
|
||||||
font.family: theme.iconFont
|
font.family: theme.iconFont
|
||||||
font.pixelSize: theme.iconSize + 8
|
font.pixelSize: theme.iconSize + 8
|
||||||
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.5)
|
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.5)
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
RotationAnimation on rotation {
|
|
||||||
from: 0
|
|
||||||
to: 360
|
|
||||||
duration: 2000
|
|
||||||
running: weather && weather.loading
|
|
||||||
loops: Animation.Infinite
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: weather && weather.loading ? "Loading Weather..." : "No Weather Data"
|
text: "No Weather Data"
|
||||||
font.pixelSize: theme.fontSizeMedium
|
font.pixelSize: theme.fontSizeMedium
|
||||||
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7)
|
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7)
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -62,7 +53,7 @@ Rectangle {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: theme.spacingL
|
anchors.margins: theme.spacingL
|
||||||
spacing: theme.spacingS
|
spacing: theme.spacingS
|
||||||
visible: weather && weather.available && weather.temp !== 0
|
visible: WeatherService.weather.available && WeatherService.weather.temp !== 0
|
||||||
|
|
||||||
// Weather header info
|
// Weather header info
|
||||||
Item {
|
Item {
|
||||||
@@ -70,12 +61,12 @@ Rectangle {
|
|||||||
height: 60
|
height: 60
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.fill: parent
|
anchors.centerIn: parent
|
||||||
spacing: theme.spacingL
|
spacing: theme.spacingL
|
||||||
|
|
||||||
// Weather icon
|
// Weather icon
|
||||||
Text {
|
Text {
|
||||||
text: weather ? WeatherService.getWeatherIcon(weather.wCode) : ""
|
text: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
|
||||||
font.family: theme.iconFont
|
font.family: theme.iconFont
|
||||||
font.pixelSize: theme.iconSize + 8
|
font.pixelSize: theme.iconSize + 8
|
||||||
color: theme.primary
|
color: theme.primary
|
||||||
@@ -87,7 +78,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: weather ? ((Prefs.useFahrenheit ? weather.tempF : weather.temp) + "°" + (Prefs.useFahrenheit ? "F" : "C")) : ""
|
text: (Prefs.useFahrenheit ? WeatherService.weather.tempF : WeatherService.weather.temp) + "°" + (Prefs.useFahrenheit ? "F" : "C")
|
||||||
font.pixelSize: theme.fontSizeXLarge
|
font.pixelSize: theme.fontSizeXLarge
|
||||||
color: theme.surfaceText
|
color: theme.surfaceText
|
||||||
font.weight: Font.Light
|
font.weight: Font.Light
|
||||||
@@ -96,13 +87,13 @@ Rectangle {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: if (weather) Prefs.setTemperatureUnit(!Prefs.useFahrenheit)
|
onClicked: if (WeatherService.weather.available) Prefs.setTemperatureUnit(!Prefs.useFahrenheit)
|
||||||
enabled: weather !== null
|
enabled: WeatherService.weather.available
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: weather ? weather.city : ""
|
text: WeatherService.weather.city || ""
|
||||||
font.pixelSize: theme.fontSizeMedium
|
font.pixelSize: theme.fontSizeMedium
|
||||||
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7)
|
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7)
|
||||||
visible: text.length > 0
|
visible: text.length > 0
|
||||||
@@ -127,7 +118,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
text: weather ? weather.humidity + "%" : "--"
|
text: WeatherService.weather.humidity ? WeatherService.weather.humidity + "%" : "--"
|
||||||
font.pixelSize: theme.fontSizeSmall
|
font.pixelSize: theme.fontSizeSmall
|
||||||
color: theme.surfaceText
|
color: theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -144,7 +135,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
text: weather ? weather.wind : "--"
|
text: WeatherService.weather.wind || "--"
|
||||||
font.pixelSize: theme.fontSizeSmall
|
font.pixelSize: theme.fontSizeSmall
|
||||||
color: theme.surfaceText
|
color: theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -161,7 +152,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
text: weather ? weather.sunrise : "--"
|
text: WeatherService.weather.sunrise || "--"
|
||||||
font.pixelSize: theme.fontSizeSmall
|
font.pixelSize: theme.fontSizeSmall
|
||||||
color: theme.surfaceText
|
color: theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -178,7 +169,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
text: weather ? weather.sunset : "--"
|
text: WeatherService.weather.sunset || "--"
|
||||||
font.pixelSize: theme.fontSizeSmall
|
font.pixelSize: theme.fontSizeSmall
|
||||||
color: theme.surfaceText
|
color: theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ import qs.Services
|
|||||||
Item {
|
Item {
|
||||||
id: bluetoothTab
|
id: bluetoothTab
|
||||||
|
|
||||||
property bool bluetoothEnabled: false
|
|
||||||
property var bluetoothDevices: []
|
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
clip: true
|
clip: true
|
||||||
@@ -28,8 +25,8 @@ Item {
|
|||||||
height: 60
|
height: 60
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: bluetoothToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
color: bluetoothToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
||||||
(bluetoothTab.bluetoothEnabled ? 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))
|
(BluetoothService.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: bluetoothTab.bluetoothEnabled ? Theme.primary : "transparent"
|
border.color: BluetoothService.enabled ? Theme.primary : "transparent"
|
||||||
border.width: 2
|
border.width: 2
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -42,7 +39,7 @@ Item {
|
|||||||
text: "bluetooth"
|
text: "bluetooth"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSizeLarge
|
font.pixelSize: Theme.iconSizeLarge
|
||||||
color: bluetoothTab.bluetoothEnabled ? Theme.primary : Theme.surfaceText
|
color: BluetoothService.enabled ? Theme.primary : Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,12 +50,12 @@ Item {
|
|||||||
Text {
|
Text {
|
||||||
text: "Bluetooth"
|
text: "Bluetooth"
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: bluetoothTab.bluetoothEnabled ? Theme.primary : Theme.surfaceText
|
color: BluetoothService.enabled ? Theme.primary : Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: bluetoothTab.bluetoothEnabled ? "Enabled" : "Disabled"
|
text: BluetoothService.enabled ? "Enabled" : "Disabled"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
}
|
}
|
||||||
@@ -72,7 +69,7 @@ Item {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
BluetoothService.toggleBluetooth()
|
BluetoothService.toggleAdapter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,7 +77,7 @@ Item {
|
|||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
visible: bluetoothTab.bluetoothEnabled
|
visible: BluetoothService.enabled
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "Paired Devices"
|
text: "Paired Devices"
|
||||||
@@ -90,7 +87,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: bluetoothTab.bluetoothDevices
|
model: BluetoothService.pairedDevices
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -108,33 +105,11 @@ Item {
|
|||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: modelData.iconName
|
||||||
switch (modelData.type) {
|
|
||||||
case "headset": return "headset"
|
|
||||||
case "mouse": return "mouse"
|
|
||||||
case "keyboard": return "keyboard"
|
|
||||||
case "phone": return "smartphone"
|
|
||||||
default: return "bluetooth"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize
|
font.pixelSize: Theme.iconSize
|
||||||
color: {
|
color: modelData.connected ? Theme.primary : Theme.surfaceText
|
||||||
if (modelData.connecting) return Theme.primary
|
|
||||||
if (modelData.connected) return Theme.primary
|
|
||||||
return Theme.surfaceText
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
opacity: modelData.connecting ? 0.6 : 1.0
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
SequentialAnimation {
|
|
||||||
running: modelData.connecting
|
|
||||||
loops: Animation.Infinite
|
|
||||||
NumberAnimation { from: 1.0; to: 0.3; duration: 800 }
|
|
||||||
NumberAnimation { from: 0.3; to: 1.0; duration: 800 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -152,20 +127,26 @@ Item {
|
|||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: modelData.connectionStatus
|
text: modelData.connected ? "Connected" : "Disconnected"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: {
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
if (modelData.connecting) return Theme.primary
|
|
||||||
if (modelData.connectionFailed) return Theme.error
|
|
||||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: modelData.battery >= 0 ? "• " + modelData.battery + "%" : ""
|
text: {
|
||||||
|
if (modelData.batteryAvailable && modelData.batteryLevel >= 0) {
|
||||||
|
return "• " + modelData.batteryLevel + "%"
|
||||||
|
}
|
||||||
|
var btBattery = BatteryService.bluetoothDevices.find(dev =>
|
||||||
|
dev.name === modelData.name ||
|
||||||
|
dev.name.toLowerCase().includes(modelData.name.toLowerCase()) ||
|
||||||
|
modelData.name.toLowerCase().includes(dev.name.toLowerCase())
|
||||||
|
)
|
||||||
|
return btBattery ? "• " + btBattery.percentage + "%" : ""
|
||||||
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
visible: modelData.battery >= 0
|
visible: text.length > 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,16 +177,13 @@ Item {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
id: btMenuButtonArea
|
id: btMenuButtonArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: !modelData.connecting
|
hoverEnabled: true
|
||||||
enabled: !modelData.connecting
|
cursorShape: Qt.PointingHandCursor
|
||||||
cursorShape: modelData.connecting ? Qt.ArrowCursor : Qt.PointingHandCursor
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!modelData.connecting) {
|
bluetoothContextMenuWindow.deviceData = modelData
|
||||||
bluetoothContextMenuWindow.deviceData = modelData
|
let localPos = btMenuButtonArea.mapToItem(bluetoothTab, btMenuButtonArea.width / 2, btMenuButtonArea.height)
|
||||||
let localPos = btMenuButtonArea.mapToItem(bluetoothTab, btMenuButtonArea.width / 2, btMenuButtonArea.height)
|
bluetoothContextMenuWindow.show(localPos.x, localPos.y)
|
||||||
bluetoothContextMenuWindow.show(localPos.x, localPos.y)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,14 +196,11 @@ Item {
|
|||||||
id: btDeviceArea
|
id: btDeviceArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.rightMargin: 40 // Don't overlap with menu button
|
anchors.rightMargin: 40 // Don't overlap with menu button
|
||||||
hoverEnabled: !modelData.connecting
|
hoverEnabled: true
|
||||||
enabled: !modelData.connecting
|
cursorShape: Qt.PointingHandCursor
|
||||||
cursorShape: modelData.connecting ? Qt.ArrowCursor : Qt.PointingHandCursor
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!modelData.connecting) {
|
BluetoothService.toggle(modelData.address)
|
||||||
BluetoothService.toggleBluetoothDevice(modelData.mac)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,7 +210,7 @@ Item {
|
|||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
visible: bluetoothTab.bluetoothEnabled
|
visible: BluetoothService.enabled
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -264,7 +239,7 @@ Item {
|
|||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: BluetoothService.scanning ? "stop" : "bluetooth_searching"
|
text: BluetoothService.discovering ? "stop" : "bluetooth_searching"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 4
|
font.pixelSize: Theme.iconSize - 4
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
@@ -273,7 +248,7 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: scanText
|
id: scanText
|
||||||
text: BluetoothService.scanning ? "Stop Scanning" : "Start Scanning"
|
text: BluetoothService.discovering ? "Stop Scanning" : "Start Scanning"
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -288,10 +263,10 @@ Item {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (BluetoothService.scanning) {
|
if (BluetoothService.discovering) {
|
||||||
BluetoothService.stopDiscovery()
|
BluetoothService.stopScan()
|
||||||
} else {
|
} else {
|
||||||
BluetoothService.startDiscovery()
|
BluetoothService.startScan()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,18 +292,7 @@ Item {
|
|||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: modelData.iconName
|
||||||
switch (modelData.type) {
|
|
||||||
case "headset": return "headset"
|
|
||||||
case "mouse": return "mouse"
|
|
||||||
case "keyboard": return "keyboard"
|
|
||||||
case "phone": return "smartphone"
|
|
||||||
case "watch": return "watch"
|
|
||||||
case "speaker": return "speaker"
|
|
||||||
case "tv": return "tv"
|
|
||||||
default: return "bluetooth"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize
|
font.pixelSize: Theme.iconSize
|
||||||
color: modelData.paired ? Theme.secondary : Theme.surfaceText
|
color: modelData.paired ? Theme.secondary : Theme.surfaceText
|
||||||
@@ -350,21 +314,10 @@ Item {
|
|||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: modelData.paired ? "Available" : "Not paired"
|
||||||
if (modelData.paired && modelData.connected) return "Connected"
|
|
||||||
if (modelData.paired) return "Paired"
|
|
||||||
return "Signal: " + modelData.signalStrength
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
|
||||||
text: 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 !== 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,11 +332,11 @@ Item {
|
|||||||
color: actionButtonArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: actionButtonArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
border.color: Theme.primary
|
border.color: Theme.primary
|
||||||
border.width: 1
|
border.width: 1
|
||||||
visible: modelData.canPair || modelData.paired
|
visible: true
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: modelData.paired ? (modelData.connected ? "Disconnect" : "Connect") : "Pair"
|
text: modelData.paired ? "Connect" : "Pair"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -397,13 +350,9 @@ Item {
|
|||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (modelData.paired) {
|
if (modelData.paired) {
|
||||||
if (modelData.connected) {
|
BluetoothService.connect(modelData.address)
|
||||||
BluetoothService.toggleBluetoothDevice(modelData.mac)
|
|
||||||
} else {
|
|
||||||
BluetoothService.connectDevice(modelData.mac)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
BluetoothService.pairDevice(modelData.mac)
|
BluetoothService.pair(modelData.address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -418,9 +367,9 @@ Item {
|
|||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (modelData.paired) {
|
if (modelData.paired) {
|
||||||
BluetoothService.toggleBluetoothDevice(modelData.mac)
|
BluetoothService.connect(modelData.address)
|
||||||
} else {
|
} else {
|
||||||
BluetoothService.pairDevice(modelData.mac)
|
BluetoothService.pair(modelData.address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -430,7 +379,7 @@ Item {
|
|||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
visible: BluetoothService.scanning && BluetoothService.availableDevices.length === 0
|
visible: BluetoothService.discovering && BluetoothService.availableDevices.length === 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -473,7 +422,7 @@ Item {
|
|||||||
text: "No devices found. Put your device in pairing mode and click Start Scanning."
|
text: "No devices found. Put your device in pairing mode and click Start Scanning."
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
visible: BluetoothService.availableDevices.length === 0 && !BluetoothService.scanning
|
visible: BluetoothService.availableDevices.length === 0 && !BluetoothService.discovering
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
width: parent.width
|
width: parent.width
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
@@ -568,7 +517,7 @@ Item {
|
|||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (bluetoothContextMenuWindow.deviceData) {
|
if (bluetoothContextMenuWindow.deviceData) {
|
||||||
BluetoothService.toggleBluetoothDevice(bluetoothContextMenuWindow.deviceData.mac)
|
BluetoothService.toggle(bluetoothContextMenuWindow.deviceData.address)
|
||||||
}
|
}
|
||||||
bluetoothContextMenuWindow.hide()
|
bluetoothContextMenuWindow.hide()
|
||||||
}
|
}
|
||||||
@@ -634,7 +583,7 @@ Item {
|
|||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (bluetoothContextMenuWindow.deviceData) {
|
if (bluetoothContextMenuWindow.deviceData) {
|
||||||
BluetoothService.removeDevice(bluetoothContextMenuWindow.deviceData.mac)
|
BluetoothService.forget(bluetoothContextMenuWindow.deviceData.address)
|
||||||
}
|
}
|
||||||
bluetoothContextMenuWindow.hide()
|
bluetoothContextMenuWindow.hide()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ PanelWindow {
|
|||||||
property bool powerOptionsExpanded: false
|
property bool powerOptionsExpanded: false
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: Math.min(600, parent.width - Theme.spacingL * 2)
|
width: Math.min(600, Screen.width - Theme.spacingL * 2)
|
||||||
height: controlCenterPopup.powerOptionsExpanded ? 570 : 500
|
height: controlCenterPopup.powerOptionsExpanded ? 570 : 500
|
||||||
x: Math.max(Theme.spacingL, parent.width - width - Theme.spacingL)
|
x: Math.max(Theme.spacingL, Screen.width - width - Theme.spacingL)
|
||||||
y: Theme.barHeight + Theme.spacingXS
|
y: Theme.barHeight + Theme.spacingXS
|
||||||
color: Theme.popupBackground()
|
color: Theme.popupBackground()
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
@@ -560,7 +560,7 @@ PanelWindow {
|
|||||||
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
|
// Show Bluetooth only if available
|
||||||
if (root.bluetoothAvailable) {
|
if (BluetoothService.available) {
|
||||||
tabs.push({name: "Bluetooth", icon: "bluetooth", id: "bluetooth", available: true})
|
tabs.push({name: "Bluetooth", icon: "bluetooth", id: "bluetooth", available: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,7 +573,7 @@ PanelWindow {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
property int tabCount: {
|
property int tabCount: {
|
||||||
let count = 3 // Network + Audio + Display (always visible)
|
let count = 3 // Network + Audio + Display (always visible)
|
||||||
if (root.bluetoothAvailable) count++
|
if (BluetoothService.available) count++
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
width: (parent.width - Theme.spacingXS * (tabCount - 1)) / tabCount
|
width: (parent.width - Theme.spacingXS * (tabCount - 1)) / tabCount
|
||||||
@@ -646,26 +646,10 @@ PanelWindow {
|
|||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
visible: controlCenterPopup.currentTab === "network"
|
visible: controlCenterPopup.currentTab === "network"
|
||||||
|
|
||||||
// Bind properties from root
|
|
||||||
networkStatus: root.networkStatus
|
|
||||||
wifiAvailable: root.wifiAvailable
|
|
||||||
wifiEnabled: root.wifiEnabled
|
|
||||||
wifiToggling: root.wifiToggling
|
|
||||||
ethernetIP: root.ethernetIP
|
|
||||||
ethernetInterface: root.ethernetInterface
|
|
||||||
ethernetConnected: root.ethernetConnected
|
|
||||||
currentWifiSSID: root.currentWifiSSID
|
|
||||||
wifiIP: root.wifiIP
|
|
||||||
wifiSignalStrength: root.wifiSignalStrength
|
|
||||||
wifiNetworks: root.wifiNetworks
|
|
||||||
wifiScanning: root.wifiScanning
|
|
||||||
wifiConnectionStatus: root.wifiConnectionStatus
|
|
||||||
wifiPasswordSSID: root.wifiPasswordSSID
|
wifiPasswordSSID: root.wifiPasswordSSID
|
||||||
wifiPasswordInput: root.wifiPasswordInput
|
wifiPasswordInput: root.wifiPasswordInput
|
||||||
wifiPasswordDialogVisible: root.wifiPasswordDialogVisible
|
wifiPasswordDialogVisible: root.wifiPasswordDialogVisible
|
||||||
changingNetworkPreference: root.changingNetworkPreference
|
|
||||||
|
|
||||||
// Bind the auto-refresh flag
|
|
||||||
onWifiAutoRefreshEnabledChanged: {
|
onWifiAutoRefreshEnabledChanged: {
|
||||||
root.wifiAutoRefreshEnabled = wifiAutoRefreshEnabled
|
root.wifiAutoRefreshEnabled = wifiAutoRefreshEnabled
|
||||||
}
|
}
|
||||||
@@ -682,11 +666,7 @@ PanelWindow {
|
|||||||
BluetoothTab {
|
BluetoothTab {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
visible: root.bluetoothAvailable && controlCenterPopup.currentTab === "bluetooth"
|
visible: BluetoothService.available && controlCenterPopup.currentTab === "bluetooth"
|
||||||
|
|
||||||
// Bind properties from root
|
|
||||||
bluetoothEnabled: root.bluetoothEnabled
|
|
||||||
bluetoothDevices: root.bluetoothDevices
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display Tab
|
// Display Tab
|
||||||
|
|||||||
@@ -11,32 +11,16 @@ Item {
|
|||||||
|
|
||||||
property int networkSubTab: {
|
property int networkSubTab: {
|
||||||
// Default to WiFi tab if WiFi is connected, otherwise Ethernet
|
// Default to WiFi tab if WiFi is connected, otherwise Ethernet
|
||||||
if (networkStatus === "wifi") return 1
|
if (NetworkService.networkStatus === "wifi") return 1
|
||||||
else if (networkStatus === "ethernet") return 0
|
else if (NetworkService.networkStatus === "ethernet") return 0
|
||||||
else return 1 // Default to WiFi when nothing is connected
|
else return 1 // Default to WiFi when nothing is connected
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose properties that the parent needs to bind to
|
// Expose properties that the parent needs to bind to
|
||||||
property bool wifiAutoRefreshEnabled: false
|
property bool wifiAutoRefreshEnabled: false
|
||||||
|
|
||||||
// These should be bound from parent
|
|
||||||
property string networkStatus: ""
|
|
||||||
property bool wifiAvailable: false
|
|
||||||
property bool wifiEnabled: false
|
|
||||||
property bool wifiToggling: false
|
|
||||||
property string ethernetIP: ""
|
|
||||||
property string ethernetInterface: ""
|
|
||||||
property bool ethernetConnected: false
|
|
||||||
property string currentWifiSSID: ""
|
|
||||||
property string wifiIP: ""
|
|
||||||
property string wifiSignalStrength: ""
|
|
||||||
property var wifiNetworks: []
|
|
||||||
property bool wifiScanning: false
|
|
||||||
property string wifiConnectionStatus: ""
|
|
||||||
property string wifiPasswordSSID: ""
|
property string wifiPasswordSSID: ""
|
||||||
property string wifiPasswordInput: ""
|
property string wifiPasswordInput: ""
|
||||||
property bool wifiPasswordDialogVisible: false
|
property bool wifiPasswordDialogVisible: false
|
||||||
property bool changingNetworkPreference: false
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -101,7 +85,7 @@ Item {
|
|||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: networkTab.wifiEnabled ? "wifi" : "wifi_off"
|
text: NetworkService.wifiEnabled ? "wifi" : "wifi_off"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 4
|
font.pixelSize: Theme.iconSize - 4
|
||||||
color: networkTab.networkSubTab === 1 ? Theme.primary : Theme.surfaceText
|
color: networkTab.networkSubTab === 1 ? Theme.primary : Theme.surfaceText
|
||||||
@@ -160,8 +144,8 @@ Item {
|
|||||||
height: 70
|
height: 70
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
|
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
|
||||||
border.color: networkTab.networkStatus === "ethernet" ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
border.color: NetworkService.networkStatus === "ethernet" ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
border.width: networkTab.networkStatus === "ethernet" ? 2 : 1
|
border.width: NetworkService.networkStatus === "ethernet" ? 2 : 1
|
||||||
visible: true
|
visible: true
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -190,7 +174,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: networkTab.ethernetConnected ? (networkTab.ethernetIP || "Connected") : "Disconnected"
|
text: NetworkService.ethernetConnected ? (NetworkService.ethernetIP || "Connected") : "Disconnected"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
}
|
}
|
||||||
@@ -342,8 +326,8 @@ Item {
|
|||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: wifiToggleArea.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: wifiToggleArea.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)
|
||||||
visible: networkTab.wifiAvailable
|
visible: NetworkService.wifiAvailable
|
||||||
opacity: networkTab.wifiToggling ? 0.6 : 1.0
|
opacity: NetworkService.wifiToggling ? 0.6 : 1.0
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
@@ -360,17 +344,17 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: wifiToggleIcon
|
id: wifiToggleIcon
|
||||||
text: networkTab.wifiToggling ? "sync" : "power_settings_new"
|
text: NetworkService.wifiToggling ? "sync" : "power_settings_new"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize
|
font.pixelSize: Theme.iconSize
|
||||||
color: networkTab.wifiEnabled ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
color: NetworkService.wifiEnabled ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
rotation: networkTab.wifiToggling ? wifiToggleIcon.rotation : 0
|
rotation: NetworkService.wifiToggling ? wifiToggleIcon.rotation : 0
|
||||||
|
|
||||||
RotationAnimation {
|
RotationAnimation {
|
||||||
target: wifiToggleIcon
|
target: wifiToggleIcon
|
||||||
property: "rotation"
|
property: "rotation"
|
||||||
running: networkTab.wifiToggling
|
running: NetworkService.wifiToggling
|
||||||
from: 0
|
from: 0
|
||||||
to: 360
|
to: 360
|
||||||
duration: 1000
|
duration: 1000
|
||||||
@@ -386,7 +370,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: networkTab.wifiToggling ? "Switching WiFi..." : (networkTab.wifiEnabled ? "Turn WiFi Off" : "Turn WiFi On")
|
text: NetworkService.wifiToggling ? "Switching WiFi..." : (NetworkService.wifiEnabled ? "Turn WiFi Off" : "Turn WiFi On")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -413,7 +397,7 @@ Item {
|
|||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
|
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
|
||||||
border.color: networkTab.networkStatus === "wifi" ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
border.color: networkTab.networkStatus === "wifi" ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
border.width: networkTab.networkStatus === "wifi" ? 2 : 1
|
border.width: networkTab.networkStatus === "wifi" ? 2 : 1
|
||||||
visible: networkTab.wifiAvailable && networkTab.wifiEnabled
|
visible: NetworkService.wifiAvailable && NetworkService.wifiEnabled
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -423,10 +407,10 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: networkTab.networkStatus === "wifi" ?
|
text: networkTab.networkStatus === "wifi" ?
|
||||||
(networkTab.wifiSignalStrength === "excellent" ? "wifi" :
|
(WifiService.wifiSignalStrength === "excellent" ? "wifi" :
|
||||||
networkTab.wifiSignalStrength === "good" ? "wifi_2_bar" :
|
WifiService.wifiSignalStrength === "good" ? "wifi_2_bar" :
|
||||||
networkTab.wifiSignalStrength === "fair" ? "wifi_1_bar" :
|
WifiService.wifiSignalStrength === "fair" ? "wifi_1_bar" :
|
||||||
networkTab.wifiSignalStrength === "poor" ? "wifi_calling_3" : "wifi") : "wifi"
|
WifiService.wifiSignalStrength === "poor" ? "wifi_calling_3" : "wifi") : "wifi"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSizeLarge
|
font.pixelSize: Theme.iconSizeLarge
|
||||||
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
|
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
|
||||||
@@ -438,14 +422,14 @@ Item {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: networkTab.networkStatus === "wifi" ? (networkTab.currentWifiSSID || "Connected") : "Not Connected"
|
text: NetworkService.networkStatus === "wifi" ? (WifiService.currentWifiSSID || "Connected") : "Not Connected"
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
|
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: networkTab.networkStatus === "wifi" ? (networkTab.wifiIP || "Connected") : "Select a network below"
|
text: NetworkService.networkStatus === "wifi" ? (NetworkService.wifiIP || "Connected") : "Select a network below"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
}
|
}
|
||||||
@@ -496,10 +480,10 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: networkTab.changingNetworkPreference ? "Switching..." :
|
text: NetworkService.changingNetworkPreference ? "Switching..." :
|
||||||
(networkTab.networkStatus === "wifi" ? "" : "Prefer over Ethernet")
|
(NetworkService.networkStatus === "wifi" ? "" : "Prefer over Ethernet")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: networkTab.networkStatus === "wifi" ? Theme.background : Theme.primary
|
color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
@@ -513,7 +497,7 @@ Item {
|
|||||||
enabled: !networkTab.changingNetworkPreference
|
enabled: !networkTab.changingNetworkPreference
|
||||||
onClicked: {
|
onClicked: {
|
||||||
console.log("Force WiFi preference clicked")
|
console.log("Force WiFi preference clicked")
|
||||||
if (networkTab.networkStatus !== "wifi") {
|
if (NetworkService.networkStatus !== "wifi") {
|
||||||
NetworkService.setNetworkPreference("wifi")
|
NetworkService.setNetworkPreference("wifi")
|
||||||
} else {
|
} else {
|
||||||
NetworkService.setNetworkPreference("auto")
|
NetworkService.setNetworkPreference("auto")
|
||||||
@@ -528,7 +512,7 @@ Item {
|
|||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
visible: networkTab.wifiEnabled
|
visible: NetworkService.wifiEnabled
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -547,21 +531,21 @@ Item {
|
|||||||
height: 32
|
height: 32
|
||||||
radius: 16
|
radius: 16
|
||||||
color: refreshArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
color: refreshArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
||||||
networkTab.wifiScanning ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) : "transparent"
|
WifiService.isScanning ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) : "transparent"
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: refreshIcon
|
id: refreshIcon
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: networkTab.wifiScanning ? "sync" : "refresh"
|
text: WifiService.isScanning ? "sync" : "refresh"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 4
|
font.pixelSize: Theme.iconSize - 4
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
rotation: networkTab.wifiScanning ? refreshIcon.rotation : 0
|
rotation: WifiService.isScanning ? refreshIcon.rotation : 0
|
||||||
|
|
||||||
RotationAnimation {
|
RotationAnimation {
|
||||||
target: refreshIcon
|
target: refreshIcon
|
||||||
property: "rotation"
|
property: "rotation"
|
||||||
running: networkTab.wifiScanning
|
running: WifiService.isScanning
|
||||||
from: 0
|
from: 0
|
||||||
to: 360
|
to: 360
|
||||||
duration: 1000
|
duration: 1000
|
||||||
@@ -581,7 +565,7 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
enabled: !networkTab.wifiScanning
|
enabled: !WifiService.isScanning
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (NetworkService.wifiEnabled) {
|
if (NetworkService.wifiEnabled) {
|
||||||
WifiService.scanWifi()
|
WifiService.scanWifi()
|
||||||
@@ -598,27 +582,27 @@ Item {
|
|||||||
height: 40
|
height: 40
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
if (networkTab.wifiConnectionStatus === "connecting") {
|
if (WifiService.connectionStatus === "connecting") {
|
||||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
|
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
|
||||||
} else if (networkTab.wifiConnectionStatus === "failed") {
|
} else if (WifiService.connectionStatus === "failed") {
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||||
} else if (networkTab.wifiConnectionStatus === "connected") {
|
} else if (WifiService.connectionStatus === "connected") {
|
||||||
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.12)
|
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.12)
|
||||||
}
|
}
|
||||||
return "transparent"
|
return "transparent"
|
||||||
}
|
}
|
||||||
border.color: {
|
border.color: {
|
||||||
if (networkTab.wifiConnectionStatus === "connecting") {
|
if (WifiService.connectionStatus === "connecting") {
|
||||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.3)
|
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.3)
|
||||||
} else if (networkTab.wifiConnectionStatus === "failed") {
|
} else if (WifiService.connectionStatus === "failed") {
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3)
|
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3)
|
||||||
} else if (networkTab.wifiConnectionStatus === "connected") {
|
} else if (WifiService.connectionStatus === "connected") {
|
||||||
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.3)
|
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.3)
|
||||||
}
|
}
|
||||||
return "transparent"
|
return "transparent"
|
||||||
}
|
}
|
||||||
border.width: networkTab.wifiConnectionStatus !== "" ? 1 : 0
|
border.width: WifiService.connectionStatus !== "" ? 1 : 0
|
||||||
visible: networkTab.wifiConnectionStatus !== ""
|
visible: WifiService.connectionStatus !== ""
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -627,26 +611,26 @@ Item {
|
|||||||
Text {
|
Text {
|
||||||
id: connectionIcon
|
id: connectionIcon
|
||||||
text: {
|
text: {
|
||||||
if (networkTab.wifiConnectionStatus === "connecting") return "sync"
|
if (WifiService.connectionStatus === "connecting") return "sync"
|
||||||
if (networkTab.wifiConnectionStatus === "failed") return "error"
|
if (WifiService.connectionStatus === "failed") return "error"
|
||||||
if (networkTab.wifiConnectionStatus === "connected") return "check_circle"
|
if (WifiService.connectionStatus === "connected") return "check_circle"
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 6
|
font.pixelSize: Theme.iconSize - 6
|
||||||
color: {
|
color: {
|
||||||
if (networkTab.wifiConnectionStatus === "connecting") return Theme.warning
|
if (WifiService.connectionStatus === "connecting") return Theme.warning
|
||||||
if (networkTab.wifiConnectionStatus === "failed") return Theme.error
|
if (WifiService.connectionStatus === "failed") return Theme.error
|
||||||
if (networkTab.wifiConnectionStatus === "connected") return Theme.success
|
if (WifiService.connectionStatus === "connected") return Theme.success
|
||||||
return Theme.surfaceText
|
return Theme.surfaceText
|
||||||
}
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
rotation: networkTab.wifiConnectionStatus === "connecting" ? connectionIcon.rotation : 0
|
rotation: WifiService.connectionStatus === "connecting" ? connectionIcon.rotation : 0
|
||||||
|
|
||||||
RotationAnimation {
|
RotationAnimation {
|
||||||
target: connectionIcon
|
target: connectionIcon
|
||||||
property: "rotation"
|
property: "rotation"
|
||||||
running: networkTab.wifiConnectionStatus === "connecting"
|
running: WifiService.connectionStatus === "connecting"
|
||||||
from: 0
|
from: 0
|
||||||
to: 360
|
to: 360
|
||||||
duration: 1000
|
duration: 1000
|
||||||
@@ -663,16 +647,16 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
if (networkTab.wifiConnectionStatus === "connecting") return "Connecting to " + WifiService.connectingSSID
|
if (WifiService.connectionStatus === "connecting") return "Connecting to " + WifiService.connectingSSID
|
||||||
if (networkTab.wifiConnectionStatus === "failed") return "Failed to connect to " + WifiService.connectingSSID
|
if (WifiService.connectionStatus === "failed") return "Failed to connect to " + WifiService.connectingSSID
|
||||||
if (networkTab.wifiConnectionStatus === "connected") return "Connected to " + WifiService.connectingSSID
|
if (WifiService.connectionStatus === "connected") return "Connected to " + WifiService.connectingSSID
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: {
|
color: {
|
||||||
if (networkTab.wifiConnectionStatus === "connecting") return Theme.warning
|
if (WifiService.connectionStatus === "connecting") return Theme.warning
|
||||||
if (networkTab.wifiConnectionStatus === "failed") return Theme.error
|
if (WifiService.connectionStatus === "failed") return Theme.error
|
||||||
if (networkTab.wifiConnectionStatus === "connected") return Theme.success
|
if (WifiService.connectionStatus === "connected") return Theme.success
|
||||||
return Theme.surfaceText
|
return Theme.surfaceText
|
||||||
}
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -689,7 +673,7 @@ Item {
|
|||||||
|
|
||||||
// WiFi networks list (only show if WiFi is available and enabled)
|
// WiFi networks list (only show if WiFi is available and enabled)
|
||||||
Repeater {
|
Repeater {
|
||||||
model: networkTab.wifiAvailable && networkTab.wifiEnabled ? networkTab.wifiNetworks : []
|
model: NetworkService.wifiAvailable && NetworkService.wifiEnabled ? WifiService.wifiNetworks : []
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -828,7 +812,7 @@ Item {
|
|||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
visible: !networkTab.wifiEnabled
|
visible: !NetworkService.wifiEnabled
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ Item {
|
|||||||
// Global mouse area for drag tracking
|
// Global mouse area for drag tracking
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: sliderGlobalMouseArea
|
id: sliderGlobalMouseArea
|
||||||
anchors.fill: slider.parent // Fill the entire settings popup
|
anchors.fill: sliderContainer
|
||||||
enabled: sliderMouseArea.isDragging
|
enabled: sliderMouseArea.isDragging
|
||||||
visible: false
|
visible: false
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ PanelWindow {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
width: 400
|
width: 400
|
||||||
height: 500
|
height: 500
|
||||||
x: parent.width - width - Theme.spacingL
|
x: Screen.width - width - Theme.spacingL
|
||||||
y: Theme.barHeight + Theme.spacingXS
|
y: Theme.barHeight + Theme.spacingXS
|
||||||
color: Theme.popupBackground()
|
color: Theme.popupBackground()
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ PanelWindow {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: dropdownContent
|
id: dropdownContent
|
||||||
width: Math.min(600, parent.width - Theme.spacingL * 2)
|
width: Math.min(600, Screen.width - Theme.spacingL * 2)
|
||||||
height: Math.min(600, parent.height - Theme.barHeight - Theme.spacingS * 2)
|
height: Math.min(600, Screen.height - Theme.barHeight - Theme.spacingS * 2)
|
||||||
x: Math.max(Theme.spacingL, parent.width - width - Theme.spacingL)
|
x: Math.max(Theme.spacingL, Screen.width - width - Theme.spacingL)
|
||||||
y: Theme.barHeight + Theme.spacingXS
|
y: Theme.barHeight + Theme.spacingXS
|
||||||
|
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ import Quickshell
|
|||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Services.Mpris
|
import Quickshell.Services.Mpris
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property list<real> audioLevels: [0, 0, 0, 0]
|
property list<real> audioLevels: [0, 0, 0, 0]
|
||||||
property bool hasActiveMedia: false
|
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
||||||
property var activePlayer: null
|
readonly property bool hasActiveMedia: activePlayer !== null
|
||||||
property bool cavaAvailable: false
|
property bool cavaAvailable: false
|
||||||
|
|
||||||
width: 20
|
width: 20
|
||||||
|
|||||||
@@ -5,12 +5,6 @@ import qs.Services
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string networkStatus: "disconnected"
|
|
||||||
property string wifiSignalStrength: "good"
|
|
||||||
property int volumeLevel: 50
|
|
||||||
property bool volumeMuted: false
|
|
||||||
property bool bluetoothAvailable: false
|
|
||||||
property bool bluetoothEnabled: false
|
|
||||||
property bool isActive: false
|
property bool isActive: false
|
||||||
|
|
||||||
signal clicked()
|
signal clicked()
|
||||||
@@ -30,9 +24,9 @@ Rectangle {
|
|||||||
// Network Status Icon
|
// Network Status Icon
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
if (root.networkStatus === "ethernet") return "lan"
|
if (NetworkService.networkStatus === "ethernet") return "lan"
|
||||||
else if (root.networkStatus === "wifi") {
|
else if (NetworkService.networkStatus === "wifi") {
|
||||||
switch (root.wifiSignalStrength) {
|
switch (WifiService.wifiSignalStrength) {
|
||||||
case "excellent": return "wifi"
|
case "excellent": return "wifi"
|
||||||
case "good": return "wifi_2_bar"
|
case "good": return "wifi_2_bar"
|
||||||
case "fair": return "wifi_1_bar"
|
case "fair": return "wifi_1_bar"
|
||||||
@@ -45,7 +39,7 @@ Rectangle {
|
|||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 8
|
font.pixelSize: Theme.iconSize - 8
|
||||||
font.weight: Theme.iconFontWeight
|
font.weight: Theme.iconFontWeight
|
||||||
color: root.networkStatus !== "disconnected" ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
color: NetworkService.networkStatus !== "disconnected" ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: true
|
visible: true
|
||||||
}
|
}
|
||||||
@@ -56,9 +50,9 @@ Rectangle {
|
|||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 8
|
font.pixelSize: Theme.iconSize - 8
|
||||||
font.weight: Theme.iconFontWeight
|
font.weight: Theme.iconFontWeight
|
||||||
color: root.bluetoothEnabled ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
color: BluetoothService.enabled ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: root.bluetoothAvailable && root.bluetoothEnabled
|
visible: BluetoothService.available && BluetoothService.enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// Audio Icon with scroll wheel support
|
// Audio Icon with scroll wheel support
|
||||||
@@ -70,8 +64,8 @@ Rectangle {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: audioIcon
|
id: audioIcon
|
||||||
text: root.volumeMuted ? "volume_off" :
|
text: AudioService.sinkMuted ? "volume_off" :
|
||||||
root.volumeLevel < 33 ? "volume_down" : "volume_up"
|
AudioService.volumeLevel < 33 ? "volume_down" : "volume_up"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 8
|
font.pixelSize: Theme.iconSize - 8
|
||||||
font.weight: Theme.iconFontWeight
|
font.weight: Theme.iconFontWeight
|
||||||
@@ -88,7 +82,7 @@ Rectangle {
|
|||||||
|
|
||||||
onWheel: function(wheelEvent) {
|
onWheel: function(wheelEvent) {
|
||||||
let delta = wheelEvent.angleDelta.y
|
let delta = wheelEvent.angleDelta.y
|
||||||
let currentVolume = root.volumeLevel
|
let currentVolume = AudioService.volumeLevel
|
||||||
let newVolume
|
let newVolume
|
||||||
|
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
|
|||||||
@@ -10,12 +10,10 @@ Rectangle {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: launcherArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
color: launcherArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
||||||
|
|
||||||
property string osLogo: ""
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: root.osLogo || "apps"
|
text: OSDetectorService.osLogo || "apps"
|
||||||
font.family: root.osLogo ? "NerdFont" : Theme.iconFont
|
font.family: OSDetectorService.osLogo ? "NerdFont" : Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 6
|
font.pixelSize: Theme.iconSize - 6
|
||||||
font.weight: Theme.iconFontWeight
|
font.weight: Theme.iconFontWeight
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
|
|||||||
@@ -1,111 +1,92 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Services.Mpris
|
import Quickshell.Services.Mpris
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var activePlayer: null
|
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
||||||
property bool hasActiveMedia: false
|
readonly property bool playerAvailable: activePlayer !== null
|
||||||
|
readonly property int contentWidth: Math.min(280, mediaRow.implicitWidth + Theme.spacingS * 2)
|
||||||
// Add a stable visibility property that doesn't flicker during track changes
|
|
||||||
property bool stableVisible: false
|
|
||||||
|
|
||||||
signal clicked()
|
signal clicked()
|
||||||
|
|
||||||
// Use a timer to stabilize visibility during track changes
|
|
||||||
Timer {
|
|
||||||
id: visibilityTimer
|
|
||||||
interval: 1000 // 1 second delay before hiding
|
|
||||||
onTriggered: root.stableVisible = root.hasActiveMedia
|
|
||||||
}
|
|
||||||
|
|
||||||
onHasActiveMediaChanged: {
|
|
||||||
if (hasActiveMedia) {
|
|
||||||
// Show immediately when media becomes available
|
|
||||||
stableVisible = true
|
|
||||||
visibilityTimer.stop()
|
|
||||||
} else {
|
|
||||||
// Delay hiding to avoid flicker during track changes
|
|
||||||
visibilityTimer.restart()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visible: stableVisible
|
|
||||||
width: stableVisible ? Math.min(280, mediaRow.implicitWidth + Theme.spacingS * 2) : 0
|
|
||||||
height: 30
|
height: 30
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "shown"
|
||||||
|
when: playerAvailable
|
||||||
|
|
||||||
Behavior on color {
|
PropertyChanges {
|
||||||
ColorAnimation {
|
target: root
|
||||||
duration: Theme.shortDuration
|
opacity: 1
|
||||||
easing.type: Theme.standardEasing
|
width: contentWidth
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hidden"
|
||||||
|
when: !playerAvailable
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: root
|
||||||
|
opacity: 0
|
||||||
|
width: 0
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: mediaRow
|
id: mediaRow
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
// Media info section (clickable to open full player)
|
// Media info section (clickable to open full player)
|
||||||
Row {
|
Row {
|
||||||
id: mediaInfo
|
id: mediaInfo
|
||||||
|
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
AudioVisualization {
|
AudioVisualization {
|
||||||
width: 20
|
width: 20
|
||||||
height: Theme.iconSize
|
height: Theme.iconSize
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
hasActiveMedia: root.hasActiveMedia
|
|
||||||
activePlayer: root.activePlayer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: mediaText
|
id: mediaText
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: 140
|
width: 140
|
||||||
|
|
||||||
text: {
|
text: {
|
||||||
// Handle the case when activePlayer is temporarily null during track changes
|
|
||||||
if (!activePlayer || !activePlayer.trackTitle) {
|
if (!activePlayer || !activePlayer.trackTitle) {
|
||||||
return "Loading..."
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it's web media by looking at player identity
|
let identity = activePlayer.identity || "";
|
||||||
let identity = activePlayer.identity || ""
|
let isWebMedia = identity.toLowerCase().includes("firefox") || identity.toLowerCase().includes("chrome") || identity.toLowerCase().includes("chromium") || identity.toLowerCase().includes("edge") || identity.toLowerCase().includes("safari");
|
||||||
let isWebMedia = identity.toLowerCase().includes("firefox") ||
|
let title = "";
|
||||||
identity.toLowerCase().includes("chrome") ||
|
let subtitle = "";
|
||||||
identity.toLowerCase().includes("chromium") ||
|
|
||||||
identity.toLowerCase().includes("edge") ||
|
|
||||||
identity.toLowerCase().includes("safari")
|
|
||||||
|
|
||||||
let title = ""
|
|
||||||
let subtitle = ""
|
|
||||||
|
|
||||||
if (isWebMedia && activePlayer.trackTitle) {
|
if (isWebMedia && activePlayer.trackTitle) {
|
||||||
title = activePlayer.trackTitle
|
title = activePlayer.trackTitle;
|
||||||
subtitle = activePlayer.trackArtist || identity
|
subtitle = activePlayer.trackArtist || identity;
|
||||||
} else {
|
} else {
|
||||||
title = activePlayer.trackTitle || "Unknown Track"
|
title = activePlayer.trackTitle || "Unknown Track";
|
||||||
subtitle = activePlayer.trackArtist || ""
|
subtitle = activePlayer.trackArtist || "";
|
||||||
}
|
}
|
||||||
|
if (title.length > 20)
|
||||||
|
title = title.substring(0, 20) + "...";
|
||||||
|
|
||||||
// Truncate title and subtitle to fit in available space - more generous limits
|
if (subtitle.length > 22)
|
||||||
if (title.length > 20) title = title.substring(0, 20) + "..."
|
subtitle = subtitle.substring(0, 22) + "...";
|
||||||
if (subtitle.length > 22) subtitle = subtitle.substring(0, 22) + "..."
|
|
||||||
|
|
||||||
return subtitle.length > 0 ? title + " • " + subtitle : title
|
return subtitle.length > 0 ? title + " • " + subtitle : title;
|
||||||
}
|
}
|
||||||
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -117,7 +98,9 @@ Rectangle {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: root.clicked()
|
onClicked: root.clicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Control buttons
|
// Control buttons
|
||||||
@@ -132,8 +115,8 @@ Rectangle {
|
|||||||
radius: 10
|
radius: 10
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
color: prevArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: prevArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
visible: stableVisible
|
visible: root.playerAvailable
|
||||||
opacity: (activePlayer && activePlayer.canGoPrevious) ? 1.0 : 0.3
|
opacity: (activePlayer && activePlayer.canGoPrevious) ? 1 : 0.3
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -145,13 +128,17 @@ Rectangle {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: prevArea
|
id: prevArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (activePlayer) activePlayer.previous()
|
if (activePlayer)
|
||||||
|
activePlayer.previous();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Play/Pause button
|
// Play/Pause button
|
||||||
@@ -160,16 +147,16 @@ Rectangle {
|
|||||||
height: 24
|
height: 24
|
||||||
radius: 12
|
radius: 12
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
color: activePlayer?.playbackState === 1 ? Theme.primary : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
color: activePlayer && activePlayer.playbackState === 1 ? Theme.primary : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||||||
visible: stableVisible
|
visible: root.playerAvailable
|
||||||
opacity: activePlayer ? 1.0 : 0.3
|
opacity: activePlayer ? 1 : 0.3
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: activePlayer?.playbackState === 1 ? "pause" : "play_arrow"
|
text: activePlayer && activePlayer.playbackState === 1 ? "pause" : "play_arrow"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: 14
|
font.pixelSize: 14
|
||||||
color: activePlayer?.playbackState === 1 ? Theme.background : Theme.primary
|
color: activePlayer && activePlayer.playbackState === 1 ? Theme.background : Theme.primary
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -177,9 +164,12 @@ Rectangle {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (activePlayer) activePlayer.togglePlaying()
|
if (activePlayer)
|
||||||
|
activePlayer.togglePlaying();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next button
|
// Next button
|
||||||
@@ -189,8 +179,8 @@ Rectangle {
|
|||||||
radius: 10
|
radius: 10
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
color: nextArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: nextArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
visible: stableVisible
|
visible: playerAvailable
|
||||||
opacity: (activePlayer && activePlayer.canGoNext) ? 1.0 : 0.3
|
opacity: (activePlayer && activePlayer.canGoNext) ? 1 : 0.3
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -202,14 +192,69 @@ Rectangle {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: nextArea
|
id: nextArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (activePlayer) activePlayer.next()
|
if (activePlayer)
|
||||||
|
activePlayer.next();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
from: "shown"
|
||||||
|
to: "hidden"
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
PauseAnimation {
|
||||||
|
duration: 500
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
properties: "opacity,width"
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
Transition {
|
||||||
|
from: "hidden"
|
||||||
|
to: "shown"
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
properties: "opacity,width"
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -31,19 +31,6 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Properties exposed to shell
|
// Properties exposed to shell
|
||||||
property bool hasActiveMedia: false
|
|
||||||
property var activePlayer: null
|
|
||||||
property bool weatherAvailable: false
|
|
||||||
property string weatherCode: ""
|
|
||||||
property int weatherTemp: 0
|
|
||||||
property int weatherTempF: 0
|
|
||||||
property string osLogo: ""
|
|
||||||
property string networkStatus: "disconnected"
|
|
||||||
property string wifiSignalStrength: "good"
|
|
||||||
property int volumeLevel: 50
|
|
||||||
property bool volumeMuted: false
|
|
||||||
property bool bluetoothAvailable: false
|
|
||||||
property bool bluetoothEnabled: false
|
|
||||||
|
|
||||||
// Shell reference to access root properties directly
|
// Shell reference to access root properties directly
|
||||||
property var shellRoot: null
|
property var shellRoot: null
|
||||||
@@ -155,7 +142,6 @@ PanelWindow {
|
|||||||
|
|
||||||
LauncherButton {
|
LauncherButton {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
osLogo: topBar.osLogo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkspaceSwitcher {
|
WorkspaceSwitcher {
|
||||||
@@ -184,9 +170,7 @@ PanelWindow {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.right: clockWidget.left
|
anchors.right: clockWidget.left
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
activePlayer: topBar.activePlayer
|
visible: Prefs.showMusic && MprisController.activePlayer
|
||||||
hasActiveMedia: topBar.hasActiveMedia
|
|
||||||
visible: Prefs.showMusic && topBar.hasActiveMedia
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (topBar.shellRoot) {
|
if (topBar.shellRoot) {
|
||||||
@@ -201,11 +185,7 @@ PanelWindow {
|
|||||||
anchors.left: clockWidget.right
|
anchors.left: clockWidget.right
|
||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
|
||||||
weatherAvailable: topBar.weatherAvailable
|
visible: Prefs.showWeather && WeatherService.weather.available && WeatherService.weather.temp > 0 && WeatherService.weather.tempF > 0
|
||||||
weatherCode: topBar.weatherCode
|
|
||||||
weatherTemp: topBar.weatherTemp
|
|
||||||
weatherTempF: topBar.weatherTempF
|
|
||||||
visible: Prefs.showWeather && topBar.weatherAvailable && topBar.weatherTemp > 0 && topBar.weatherTempF > 0
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (topBar.shellRoot) {
|
if (topBar.shellRoot) {
|
||||||
@@ -307,12 +287,6 @@ PanelWindow {
|
|||||||
|
|
||||||
ControlCenterButton {
|
ControlCenterButton {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
networkStatus: topBar.networkStatus
|
|
||||||
wifiSignalStrength: topBar.wifiSignalStrength
|
|
||||||
volumeLevel: topBar.volumeLevel
|
|
||||||
volumeMuted: topBar.volumeMuted
|
|
||||||
bluetoothAvailable: topBar.bluetoothAvailable
|
|
||||||
bluetoothEnabled: topBar.bluetoothEnabled
|
|
||||||
isActive: topBar.shellRoot ? topBar.shellRoot.controlCenterVisible : false
|
isActive: topBar.shellRoot ? topBar.shellRoot.controlCenterVisible : false
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|||||||
@@ -5,10 +5,6 @@ import qs.Services
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool weatherAvailable: false
|
|
||||||
property string weatherCode: ""
|
|
||||||
property int weatherTemp: 0
|
|
||||||
property int weatherTempF: 0
|
|
||||||
|
|
||||||
signal clicked()
|
signal clicked()
|
||||||
|
|
||||||
@@ -40,7 +36,7 @@ Rectangle {
|
|||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: WeatherService.getWeatherIcon(weatherCode)
|
text: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 4
|
font.pixelSize: Theme.iconSize - 4
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
@@ -48,7 +44,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: (Prefs.useFahrenheit ? weatherTempF : weatherTemp) + "°" + (Prefs.useFahrenheit ? "F" : "C")
|
text: (Prefs.useFahrenheit ? WeatherService.weather.tempF : WeatherService.weather.temp) + "°" + (Prefs.useFahrenheit ? "F" : "C")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ PanelWindow {
|
|||||||
|
|
||||||
QsMenuOpener {
|
QsMenuOpener {
|
||||||
id: menuOpener
|
id: menuOpener
|
||||||
menu: root.currentTrayItem?.menu
|
menu: root.currentTrayItem ? root.currentTrayItem.menu : null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom menu styling using ListView
|
// Custom menu styling using ListView
|
||||||
|
|||||||
66
shell.qml
66
shell.qml
@@ -37,19 +37,11 @@ ShellRoot {
|
|||||||
property real trayMenuY: 0
|
property real trayMenuY: 0
|
||||||
property var currentTrayMenu: null
|
property var currentTrayMenu: null
|
||||||
property var currentTrayItem: null
|
property var currentTrayItem: null
|
||||||
property string osLogo: OSDetectorService.osLogo
|
|
||||||
property string osName: OSDetectorService.osName
|
|
||||||
property bool notificationHistoryVisible: false
|
property bool notificationHistoryVisible: false
|
||||||
property bool mediaPlayerVisible: false
|
property bool mediaPlayerVisible: false
|
||||||
property MprisPlayer activePlayer: MprisController.activePlayer
|
property bool hasActiveMedia: MprisController.active && (MprisController.active.trackTitle || MprisController.active.trackArtist)
|
||||||
property bool hasActiveMedia: activePlayer && (activePlayer.trackTitle || activePlayer.trackArtist)
|
|
||||||
property bool controlCenterVisible: false
|
property bool controlCenterVisible: false
|
||||||
|
|
||||||
// Monitor control center visibility to enable/disable bluetooth scanning
|
|
||||||
onControlCenterVisibleChanged: {
|
|
||||||
console.log("Control center", controlCenterVisible ? "opened" : "closed")
|
|
||||||
BluetoothService.enableMonitoring(controlCenterVisible)
|
|
||||||
}
|
|
||||||
property bool batteryPopupVisible: false
|
property bool batteryPopupVisible: false
|
||||||
property bool powerMenuVisible: false
|
property bool powerMenuVisible: false
|
||||||
property bool powerConfirmVisible: false
|
property bool powerConfirmVisible: false
|
||||||
@@ -58,52 +50,11 @@ ShellRoot {
|
|||||||
property string powerConfirmMessage: ""
|
property string powerConfirmMessage: ""
|
||||||
property bool settingsVisible: false
|
property bool settingsVisible: false
|
||||||
|
|
||||||
// Network properties from NetworkService
|
|
||||||
property string networkStatus: NetworkService.networkStatus
|
|
||||||
property string ethernetIP: NetworkService.ethernetIP
|
|
||||||
property string ethernetInterface: NetworkService.ethernetInterface
|
|
||||||
property bool ethernetConnected: NetworkService.ethernetConnected
|
|
||||||
property string wifiIP: NetworkService.wifiIP
|
|
||||||
property bool bluetoothEnabled: BluetoothService.bluetoothEnabled
|
|
||||||
property bool bluetoothAvailable: BluetoothService.bluetoothAvailable
|
|
||||||
property bool wifiEnabled: NetworkService.wifiEnabled
|
|
||||||
property bool wifiAvailable: NetworkService.wifiAvailable
|
|
||||||
property bool wifiToggling: NetworkService.wifiToggling
|
|
||||||
property bool changingNetworkPreference: NetworkService.changingPreference
|
|
||||||
|
|
||||||
// WiFi properties from WifiService
|
|
||||||
property string wifiSignalStrength: WifiService.wifiSignalStrength
|
|
||||||
property string currentWifiSSID: WifiService.currentWifiSSID
|
|
||||||
property var wifiNetworks: WifiService.wifiNetworks
|
|
||||||
property var savedWifiNetworks: WifiService.savedWifiNetworks
|
|
||||||
property bool wifiScanning: WifiService.isScanning
|
|
||||||
|
|
||||||
// Audio properties from AudioService
|
|
||||||
property int volumeLevel: AudioService.volumeLevel
|
|
||||||
property bool volumeMuted: AudioService.sinkMuted
|
|
||||||
property var audioSinks: AudioService.audioSinks
|
|
||||||
property string currentAudioSink: AudioService.currentAudioSink
|
|
||||||
|
|
||||||
// Microphone properties from AudioService
|
|
||||||
property int micLevel: AudioService.micLevel
|
|
||||||
property var audioSources: AudioService.audioSources
|
|
||||||
property string currentAudioSource: AudioService.currentAudioSource
|
|
||||||
|
|
||||||
// Bluetooth properties from BluetoothService
|
|
||||||
property var bluetoothDevices: BluetoothService.bluetoothDevices
|
|
||||||
|
|
||||||
// Brightness properties from BrightnessService
|
|
||||||
property int brightnessLevel: BrightnessService.brightnessLevel
|
|
||||||
|
|
||||||
// Calendar properties from CalendarService
|
|
||||||
property bool calendarAvailable: CalendarService.khalAvailable
|
|
||||||
property var calendarEvents: CalendarService.eventsByDate
|
|
||||||
|
|
||||||
// WiFi password dialog
|
// WiFi password dialog
|
||||||
property bool wifiPasswordDialogVisible: false
|
property bool wifiPasswordDialogVisible: false
|
||||||
property string wifiPasswordSSID: ""
|
property string wifiPasswordSSID: ""
|
||||||
property string wifiPasswordInput: ""
|
property string wifiPasswordInput: ""
|
||||||
property string wifiConnectionStatus: WifiService.connectionStatus
|
|
||||||
property bool wifiAutoRefreshEnabled: false
|
property bool wifiAutoRefreshEnabled: false
|
||||||
|
|
||||||
// Wallpaper error status
|
// Wallpaper error status
|
||||||
@@ -116,8 +67,6 @@ ShellRoot {
|
|||||||
property bool isMediumScreen: screenWidth >= 1200 && screenWidth < 1600
|
property bool isMediumScreen: screenWidth >= 1200 && screenWidth < 1600
|
||||||
property bool isLargeScreen: screenWidth >= 1600
|
property bool isLargeScreen: screenWidth >= 1600
|
||||||
|
|
||||||
// Weather data from WeatherService
|
|
||||||
property var weather: WeatherService.weather
|
|
||||||
|
|
||||||
// Weather configuration
|
// Weather configuration
|
||||||
|
|
||||||
@@ -171,19 +120,6 @@ ShellRoot {
|
|||||||
modelData: item
|
modelData: item
|
||||||
|
|
||||||
// Connect shell properties
|
// Connect shell properties
|
||||||
hasActiveMedia: root.hasActiveMedia
|
|
||||||
activePlayer: root.activePlayer
|
|
||||||
weatherAvailable: root.weather.available
|
|
||||||
weatherCode: root.weather.wCode
|
|
||||||
weatherTemp: root.weather.temp
|
|
||||||
weatherTempF: root.weather.tempF
|
|
||||||
osLogo: root.osLogo
|
|
||||||
networkStatus: root.networkStatus
|
|
||||||
wifiSignalStrength: root.wifiSignalStrength
|
|
||||||
volumeLevel: root.volumeLevel
|
|
||||||
volumeMuted: root.volumeMuted
|
|
||||||
bluetoothAvailable: root.bluetoothAvailable
|
|
||||||
bluetoothEnabled: root.bluetoothEnabled
|
|
||||||
shellRoot: root
|
shellRoot: root
|
||||||
notificationCount: NotificationService.notifications.length
|
notificationCount: NotificationService.notifications.length
|
||||||
processDropdown: processListDropdown
|
processDropdown: processListDropdown
|
||||||
|
|||||||
Reference in New Issue
Block a user