Merge branch 'main' into miniplayer-eyecandy
This commit is contained in:
commit
e51c5cf4bd
56 changed files with 3481 additions and 3542 deletions
|
|
@ -2,6 +2,7 @@ import QtQuick
|
|||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
|
@ -17,6 +18,8 @@ Variants {
|
|||
readonly property real scaling: ScalingService.scale(screen)
|
||||
screen: modelData
|
||||
|
||||
WlrLayershell.namespace: "noctalia-bar"
|
||||
|
||||
implicitHeight: Style.barHeight * scaling
|
||||
color: Color.transparent
|
||||
|
||||
|
|
@ -119,16 +122,6 @@ Variants {
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
// NIconButton {
|
||||
// id: demoPanelToggle
|
||||
// icon: "experiment"
|
||||
// tooltipText: "Open Demo Panel"
|
||||
// sizeMultiplier: 0.8
|
||||
// anchors.verticalCenter: parent.verticalCenter
|
||||
// onClicked: {
|
||||
// demoPanel.isLoaded = !demoPanel.isLoaded
|
||||
// }
|
||||
// }
|
||||
SidePanelToggle {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,25 +31,10 @@ NIconButton {
|
|||
}
|
||||
tooltipText: "Bluetooth Devices"
|
||||
onClicked: {
|
||||
if (!bluetoothMenuLoader.active) {
|
||||
bluetoothMenuLoader.isLoaded = true
|
||||
}
|
||||
if (bluetoothMenuLoader.item) {
|
||||
if (bluetoothMenuLoader.item.visible) {
|
||||
// Panel is visible, hide it with animation
|
||||
if (bluetoothMenuLoader.item.hide) {
|
||||
bluetoothMenuLoader.item.hide()
|
||||
} else {
|
||||
bluetoothMenuLoader.item.visible = false
|
||||
}
|
||||
} else {
|
||||
// Panel is hidden, show it
|
||||
bluetoothMenuLoader.item.visible = true
|
||||
}
|
||||
}
|
||||
bluetoothPanel.toggle(screen)
|
||||
}
|
||||
|
||||
BluetoothMenu {
|
||||
id: bluetoothMenuLoader
|
||||
BluetoothPanel {
|
||||
id: bluetoothPanel
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,496 +0,0 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Bluetooth
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
// Loader for Bluetooth menu
|
||||
NLoader {
|
||||
id: root
|
||||
|
||||
content: Component {
|
||||
NPanel {
|
||||
id: bluetoothPanel
|
||||
|
||||
function hide() {
|
||||
bluetoothMenuRect.scaleValue = 0.8
|
||||
bluetoothMenuRect.opacityValue = 0.0
|
||||
hideTimer.start()
|
||||
}
|
||||
|
||||
// Connect to NPanel's dismissed signal to handle external close events
|
||||
Connections {
|
||||
target: bluetoothPanel
|
||||
ignoreUnknownSignals: true
|
||||
function onDismissed() {
|
||||
// Start hide animation
|
||||
bluetoothMenuRect.scaleValue = 0.8
|
||||
bluetoothMenuRect.opacityValue = 0.0
|
||||
// Hide after animation completes
|
||||
hideTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
// Also handle visibility changes from external sources
|
||||
onVisibleChanged: {
|
||||
if (visible && Settings.data.network.bluetoothEnabled) {
|
||||
// Always refresh devices when menu opens to get fresh device objects
|
||||
BluetoothService.adapter.discovering = true
|
||||
} else if (bluetoothMenuRect.opacityValue > 0) {
|
||||
// Start hide animation
|
||||
bluetoothMenuRect.scaleValue = 0.8
|
||||
bluetoothMenuRect.opacityValue = 0.0
|
||||
// Hide after animation completes
|
||||
hideTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
// Timer to hide panel after animation
|
||||
Timer {
|
||||
id: hideTimer
|
||||
interval: Style.animationSlow
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
bluetoothPanel.visible = false
|
||||
bluetoothPanel.dismissed()
|
||||
}
|
||||
}
|
||||
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
|
||||
Rectangle {
|
||||
id: bluetoothMenuRect
|
||||
|
||||
property var deviceData: null
|
||||
|
||||
color: Color.mSurface
|
||||
radius: Style.radiusL * scaling
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
width: 380 * scaling
|
||||
height: 500 * scaling
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: Style.marginXS * scaling
|
||||
top: Settings.data.bar.position === "top" ? parent.top : undefined
|
||||
bottom: Settings.data.bar.position === "bottom" ? parent.bottom : undefined
|
||||
topMargin: Settings.data.bar.position === "top" ? Style.marginXS * scaling : undefined
|
||||
bottomMargin: Settings.data.bar.position === "bottom" ? Style.barHeight * scaling + Style.marginXS * scaling : undefined
|
||||
}
|
||||
|
||||
// Animation properties
|
||||
property real scaleValue: 0.8
|
||||
property real opacityValue: 0.0
|
||||
|
||||
scale: scaleValue
|
||||
opacity: opacityValue
|
||||
|
||||
// Prevent closing the window if clicking inside it
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
// Animate in when component is completed
|
||||
Component.onCompleted: {
|
||||
scaleValue = 1.0
|
||||
opacityValue = 1.0
|
||||
}
|
||||
|
||||
// Animation behaviors
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Style.animationSlow
|
||||
easing.type: Easing.OutExpo
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL * scaling
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
// HEADER
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "bluetooth"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Bluetooth"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop_circle" : "refresh"
|
||||
tooltipText: "Refresh Devices"
|
||||
sizeMultiplier: 0.8
|
||||
onClicked: {
|
||||
if (BluetoothService.adapter) {
|
||||
BluetoothService.adapter.discovering = !BluetoothService.adapter.discovering
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
tooltipText: "Close"
|
||||
sizeMultiplier: 0.8
|
||||
onClicked: {
|
||||
bluetoothPanel.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {}
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
|
||||
// Available devices
|
||||
Column {
|
||||
id: column
|
||||
|
||||
width: parent.width
|
||||
spacing: Style.marginM * scaling
|
||||
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NText {
|
||||
text: "Available Devices"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: Color.mOnSurface
|
||||
font.weight: Style.fontWeightMedium
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: {
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices)
|
||||
return []
|
||||
|
||||
var filtered = Bluetooth.devices.values.filter(dev => {
|
||||
return dev && !dev.paired && !dev.pairing
|
||||
&& !dev.blocked && (dev.signalStrength === undefined
|
||||
|| dev.signalStrength > 0)
|
||||
})
|
||||
return BluetoothService.sortDevices(filtered)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
property bool canConnect: BluetoothService.canConnect(modelData)
|
||||
property bool isBusy: BluetoothService.isDeviceBusy(modelData)
|
||||
|
||||
width: parent.width
|
||||
height: 70
|
||||
radius: Style.radiusM * scaling
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse && !isBusy)
|
||||
return Color.mTertiary
|
||||
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mPrimary
|
||||
|
||||
if (modelData.blocked)
|
||||
return Color.mError
|
||||
|
||||
return Color.mSurfaceVariant
|
||||
}
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.marginM * scaling
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
// One device BT icon
|
||||
NIcon {
|
||||
text: BluetoothService.getDeviceIcon(modelData)
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse)
|
||||
return Color.mOnTertiary
|
||||
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mOnPrimary
|
||||
|
||||
if (modelData.blocked)
|
||||
return Color.mOnError
|
||||
|
||||
return Color.mOnSurface
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: Style.marginXXS * scaling
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
// One device name
|
||||
NText {
|
||||
text: modelData.name || modelData.deviceName
|
||||
font.pointSize: Style.fonttSizeMedium * scaling
|
||||
elide: Text.ElideRight
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse)
|
||||
return Color.mOnTertiary
|
||||
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mOnPrimary
|
||||
|
||||
if (modelData.blocked)
|
||||
return Color.mOnError
|
||||
|
||||
return Color.mOnSurface
|
||||
}
|
||||
font.weight: Style.fontWeightMedium
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Style.marginXS * scaling
|
||||
|
||||
Row {
|
||||
spacing: Style.marginS * spacing
|
||||
|
||||
// One device signal strength - "Unknown" when not connected
|
||||
NText {
|
||||
text: {
|
||||
if (modelData.pairing)
|
||||
return "Pairing..."
|
||||
|
||||
if (modelData.blocked)
|
||||
return "Blocked"
|
||||
|
||||
return BluetoothService.getSignalStrength(modelData)
|
||||
}
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse)
|
||||
return Color.mOnTertiary
|
||||
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mOnPrimary
|
||||
|
||||
if (modelData.blocked)
|
||||
return Color.mOnError
|
||||
|
||||
return Color.mOnSurface
|
||||
}
|
||||
}
|
||||
|
||||
NIcon {
|
||||
text: BluetoothService.getSignalIcon(modelData)
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse)
|
||||
return Color.mOnTertiary
|
||||
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mOnPrimary
|
||||
|
||||
if (modelData.blocked)
|
||||
return Color.mOnError
|
||||
|
||||
return Color.mOnSurface
|
||||
}
|
||||
visible: modelData.signalStrength !== undefined && modelData.signalStrength > 0
|
||||
&& !modelData.pairing && !modelData.blocked
|
||||
}
|
||||
|
||||
NText {
|
||||
text: (modelData.signalStrength !== undefined
|
||||
&& modelData.signalStrength > 0) ? modelData.signalStrength + "%" : ""
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse)
|
||||
return Color.mOnTertiary
|
||||
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mOnPrimary
|
||||
|
||||
if (modelData.blocked)
|
||||
return Color.mOnError
|
||||
|
||||
return Color.mOnSurface
|
||||
}
|
||||
visible: modelData.signalStrength !== undefined && modelData.signalStrength > 0
|
||||
&& !modelData.pairing && !modelData.blocked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 80 * scaling
|
||||
height: 28 * scaling
|
||||
radius: Style.radiusM * scaling
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.marginM * scaling
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: modelData.state !== BluetoothDeviceState.Connecting
|
||||
color: Color.transparent
|
||||
|
||||
border.color: {
|
||||
if (availableDeviceArea.containsMouse) {
|
||||
return Color.mOnTertiary
|
||||
} else {
|
||||
return Color.mPrimary
|
||||
}
|
||||
}
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
opacity: canConnect || isBusy ? 1 : 0.5
|
||||
|
||||
// On device connect button
|
||||
NText {
|
||||
anchors.centerIn: parent
|
||||
text: {
|
||||
if (modelData.pairing)
|
||||
return "Pairing..."
|
||||
|
||||
if (modelData.blocked)
|
||||
return "Blocked"
|
||||
|
||||
return "Connect"
|
||||
}
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
font.weight: Style.fontWeightMedium
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse) {
|
||||
return Color.mOnTertiary
|
||||
} else {
|
||||
return Color.mPrimary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: availableDeviceArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: canConnect
|
||||
&& !isBusy ? Qt.PointingHandCursor : (isBusy ? Qt.BusyCursor : Qt.ArrowCursor)
|
||||
enabled: canConnect && !isBusy
|
||||
onClicked: {
|
||||
if (modelData)
|
||||
BluetoothService.connectDeviceWithTrust(modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback if nothing available
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Style.marginM * scaling
|
||||
visible: {
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices)
|
||||
return false
|
||||
|
||||
var availableCount = Bluetooth.devices.values.filter(dev => {
|
||||
return dev && !dev.paired && !dev.pairing
|
||||
&& !dev.blocked
|
||||
&& (dev.signalStrength === undefined
|
||||
|| dev.signalStrength > 0)
|
||||
}).length
|
||||
return availableCount === 0
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "sync"
|
||||
font.pointSize: Style.fontSizeXLL * 1.5 * scaling
|
||||
color: Color.mPrimary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
RotationAnimation on rotation {
|
||||
running: true
|
||||
loops: Animation.Infinite
|
||||
from: 0
|
||||
to: 360
|
||||
duration: 2000
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Scanning for devices..."
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: Color.mOnSurface
|
||||
font.weight: Style.fontWeightMedium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Make sure your device is in pairing mode"
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "No devices found. Put your device in pairing mode and click Start Scanning."
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
visible: {
|
||||
if (!BluetoothService.adapter || !Bluetooth.devices)
|
||||
return true
|
||||
|
||||
var availableCount = Bluetooth.devices.values.filter(dev => {
|
||||
return dev && !dev.paired && !dev.pairing
|
||||
&& !dev.blocked
|
||||
&& (dev.signalStrength === undefined
|
||||
|| dev.signalStrength > 0)
|
||||
}).length
|
||||
return availableCount === 0 && !BluetoothService.adapter.discovering
|
||||
}
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
// This item takes up all the remaining vertical space.
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
398
Modules/Bar/BluetoothPanel.qml
Normal file
398
Modules/Bar/BluetoothPanel.qml
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Bluetooth
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
NPanel {
|
||||
id: root
|
||||
|
||||
panelWidth: 380 * scaling
|
||||
panelHeight: 500 * scaling
|
||||
panelAnchorRight: true
|
||||
|
||||
panelContent: Rectangle {
|
||||
color: Color.transparent
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL * scaling
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
// HEADER
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "bluetooth"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Bluetooth"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop_circle" : "refresh"
|
||||
tooltipText: "Refresh Devices"
|
||||
sizeMultiplier: 0.8
|
||||
onClicked: {
|
||||
if (BluetoothService.adapter) {
|
||||
BluetoothService.adapter.discovering = !BluetoothService.adapter.discovering
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
tooltipText: "Close"
|
||||
sizeMultiplier: 0.8
|
||||
onClicked: {
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
|
||||
// Available devices
|
||||
Column {
|
||||
id: column
|
||||
|
||||
width: parent.width
|
||||
spacing: Style.marginM * scaling
|
||||
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NText {
|
||||
text: "Available Devices"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: Color.mOnSurface
|
||||
font.weight: Style.fontWeightMedium
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: {
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices)
|
||||
return []
|
||||
|
||||
var filtered = Bluetooth.devices.values.filter(dev => {
|
||||
return dev && !dev.paired && !dev.pairing && !dev.blocked
|
||||
&& (dev.signalStrength === undefined
|
||||
|| dev.signalStrength > 0)
|
||||
})
|
||||
return BluetoothService.sortDevices(filtered)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
property bool canConnect: BluetoothService.canConnect(modelData)
|
||||
property bool isBusy: BluetoothService.isDeviceBusy(modelData)
|
||||
|
||||
width: parent.width
|
||||
height: 70
|
||||
radius: Style.radiusM * scaling
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse && !isBusy)
|
||||
return Color.mTertiary
|
||||
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mPrimary
|
||||
|
||||
if (modelData.blocked)
|
||||
return Color.mError
|
||||
|
||||
return Color.mSurfaceVariant
|
||||
}
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.marginM * scaling
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
// One device BT icon
|
||||
NIcon {
|
||||
text: BluetoothService.getDeviceIcon(modelData)
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse)
|
||||
return Color.mOnTertiary
|
||||
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mOnPrimary
|
||||
|
||||
if (modelData.blocked)
|
||||
return Color.mOnError
|
||||
|
||||
return Color.mOnSurface
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: Style.marginXXS * scaling
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
// One device name
|
||||
NText {
|
||||
text: modelData.name || modelData.deviceName
|
||||
font.pointSize: Style.fonttSizeMedium * scaling
|
||||
elide: Text.ElideRight
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse)
|
||||
return Color.mOnTertiary
|
||||
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mOnPrimary
|
||||
|
||||
if (modelData.blocked)
|
||||
return Color.mOnError
|
||||
|
||||
return Color.mOnSurface
|
||||
}
|
||||
font.weight: Style.fontWeightMedium
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Style.marginXS * scaling
|
||||
|
||||
Row {
|
||||
spacing: Style.marginS * spacing
|
||||
|
||||
// One device signal strength - "Unknown" when not connected
|
||||
NText {
|
||||
text: {
|
||||
if (modelData.pairing)
|
||||
return "Pairing..."
|
||||
|
||||
if (modelData.blocked)
|
||||
return "Blocked"
|
||||
|
||||
return BluetoothService.getSignalStrength(modelData)
|
||||
}
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse)
|
||||
return Color.mOnTertiary
|
||||
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mOnPrimary
|
||||
|
||||
if (modelData.blocked)
|
||||
return Color.mOnError
|
||||
|
||||
return Color.mOnSurface
|
||||
}
|
||||
}
|
||||
|
||||
NIcon {
|
||||
text: BluetoothService.getSignalIcon(modelData)
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse)
|
||||
return Color.mOnTertiary
|
||||
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mOnPrimary
|
||||
|
||||
if (modelData.blocked)
|
||||
return Color.mOnError
|
||||
|
||||
return Color.mOnSurface
|
||||
}
|
||||
visible: modelData.signalStrength !== undefined && modelData.signalStrength > 0
|
||||
&& !modelData.pairing && !modelData.blocked
|
||||
}
|
||||
|
||||
NText {
|
||||
text: (modelData.signalStrength !== undefined
|
||||
&& modelData.signalStrength > 0) ? modelData.signalStrength + "%" : ""
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse)
|
||||
return Color.mOnTertiary
|
||||
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mOnPrimary
|
||||
|
||||
if (modelData.blocked)
|
||||
return Color.mOnError
|
||||
|
||||
return Color.mOnSurface
|
||||
}
|
||||
visible: modelData.signalStrength !== undefined && modelData.signalStrength > 0
|
||||
&& !modelData.pairing && !modelData.blocked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 80 * scaling
|
||||
height: 28 * scaling
|
||||
radius: Style.radiusM * scaling
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.marginM * scaling
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: modelData.state !== BluetoothDeviceState.Connecting
|
||||
color: Color.transparent
|
||||
|
||||
border.color: {
|
||||
if (availableDeviceArea.containsMouse) {
|
||||
return Color.mOnTertiary
|
||||
} else {
|
||||
return Color.mPrimary
|
||||
}
|
||||
}
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
opacity: canConnect || isBusy ? 1 : 0.5
|
||||
|
||||
// On device connect button
|
||||
NText {
|
||||
anchors.centerIn: parent
|
||||
text: {
|
||||
if (modelData.pairing)
|
||||
return "Pairing..."
|
||||
|
||||
if (modelData.blocked)
|
||||
return "Blocked"
|
||||
|
||||
return "Connect"
|
||||
}
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
font.weight: Style.fontWeightMedium
|
||||
color: {
|
||||
if (availableDeviceArea.containsMouse) {
|
||||
return Color.mOnTertiary
|
||||
} else {
|
||||
return Color.mPrimary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: availableDeviceArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: canConnect && !isBusy ? Qt.PointingHandCursor : (isBusy ? Qt.BusyCursor : Qt.ArrowCursor)
|
||||
enabled: canConnect && !isBusy
|
||||
onClicked: {
|
||||
if (modelData)
|
||||
BluetoothService.connectDeviceWithTrust(modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback if nothing available
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Style.marginM * scaling
|
||||
visible: {
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices)
|
||||
return false
|
||||
|
||||
var availableCount = Bluetooth.devices.values.filter(dev => {
|
||||
return dev && !dev.paired && !dev.pairing
|
||||
&& !dev.blocked
|
||||
&& (dev.signalStrength === undefined
|
||||
|| dev.signalStrength > 0)
|
||||
}).length
|
||||
return availableCount === 0
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "sync"
|
||||
font.pointSize: Style.fontSizeXLL * 1.5 * scaling
|
||||
color: Color.mPrimary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
RotationAnimation on rotation {
|
||||
running: true
|
||||
loops: Animation.Infinite
|
||||
from: 0
|
||||
to: 360
|
||||
duration: 2000
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Scanning for devices..."
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: Color.mOnSurface
|
||||
font.weight: Style.fontWeightMedium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Make sure your device is in pairing mode"
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "No devices found. Put your device in pairing mode and click Start Scanning."
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
visible: {
|
||||
if (!BluetoothService.adapter || !Bluetooth.devices)
|
||||
return true
|
||||
|
||||
var availableCount = Bluetooth.devices.values.filter(dev => {
|
||||
return dev && !dev.paired && !dev.pairing
|
||||
&& !dev.blocked
|
||||
&& (dev.signalStrength === undefined
|
||||
|| dev.signalStrength > 0)
|
||||
}).length
|
||||
return availableCount === 0 && !BluetoothService.adapter.discovering
|
||||
}
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
// This item takes up all the remaining vertical space.
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ Item {
|
|||
|
||||
onClicked: {
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.Brightness
|
||||
settingsPanel.isLoaded = true
|
||||
settingsPanel.open(screen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
onEntered: {
|
||||
if (!calendarPanel.isLoaded) {
|
||||
if (!calendarPanel.active) {
|
||||
tooltip.show()
|
||||
}
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ Rectangle {
|
|||
}
|
||||
onClicked: {
|
||||
tooltip.hide()
|
||||
calendarPanel.isLoaded = !calendarPanel.isLoaded
|
||||
calendarPanel.toggle(screen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@ Row {
|
|||
height: Math.round(Style.capsuleHeight * scaling)
|
||||
radius: Math.round(Style.radiusM * scaling)
|
||||
color: Color.mSurfaceVariant
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Math.round(Style.borderS * scaling))
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
|
|
|
|||
|
|
@ -20,21 +20,6 @@ NIconButton {
|
|||
colorBorderHover: Color.transparent
|
||||
|
||||
onClicked: {
|
||||
if (!notificationHistoryPanel.active) {
|
||||
notificationHistoryPanel.isLoaded = true
|
||||
}
|
||||
if (notificationHistoryPanel.item) {
|
||||
if (notificationHistoryPanel.item.visible) {
|
||||
// Panel is visible, hide it with animation
|
||||
if (notificationHistoryPanel.item.hide) {
|
||||
notificationHistoryPanel.item.hide()
|
||||
} else {
|
||||
notificationHistoryPanel.item.visible = false
|
||||
}
|
||||
} else {
|
||||
// Panel is hidden, show it
|
||||
notificationHistoryPanel.item.visible = true
|
||||
}
|
||||
}
|
||||
notificationHistoryPanel.toggle(screen)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,23 +14,5 @@ NIconButton {
|
|||
colorBorderHover: Color.transparent
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: {
|
||||
// Map this button's center to the screen and open the side panel below it
|
||||
const localCenterX = width / 2
|
||||
const localCenterY = height / 2
|
||||
const globalPoint = mapToItem(null, localCenterX, localCenterY)
|
||||
if (sidePanel.isLoaded) {
|
||||
// Call hide() instead of directly setting isLoaded to false
|
||||
if (sidePanel.item && sidePanel.item.hide) {
|
||||
sidePanel.item.hide()
|
||||
} else {
|
||||
sidePanel.isLoaded = false
|
||||
}
|
||||
} else if (sidePanel.openAt) {
|
||||
sidePanel.openAt(globalPoint.x, screen)
|
||||
} else {
|
||||
// Fallback: toggle if API unavailable
|
||||
sidePanel.isLoaded = true
|
||||
}
|
||||
}
|
||||
onClicked: sidePanel.toggle(screen)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,37 +76,34 @@ Rectangle {
|
|||
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
// Close any open menu first
|
||||
if (trayMenu && trayMenu.visible) {
|
||||
trayMenu.hideMenu()
|
||||
}
|
||||
trayPanel.close()
|
||||
|
||||
if (!modelData.onlyMenu) {
|
||||
modelData.activate()
|
||||
}
|
||||
} else if (mouse.button === Qt.MiddleButton) {
|
||||
// Close any open menu first
|
||||
if (trayMenu && trayMenu.visible) {
|
||||
trayMenu.hideMenu()
|
||||
}
|
||||
trayPanel.close()
|
||||
|
||||
modelData.secondaryActivate && modelData.secondaryActivate()
|
||||
} else if (mouse.button === Qt.RightButton) {
|
||||
trayTooltip.hide()
|
||||
// If menu is already visible, close it
|
||||
if (trayMenu && trayMenu.visible) {
|
||||
trayMenu.hideMenu()
|
||||
|
||||
// Close the menu if it was visible
|
||||
if (trayPanel && trayPanel.visible) {
|
||||
trayPanel.close()
|
||||
return
|
||||
}
|
||||
|
||||
if (modelData.hasMenu && modelData.menu && trayMenu) {
|
||||
trayPanel.open()
|
||||
|
||||
// Anchor the menu to the tray icon item (parent) and position it below the icon
|
||||
const menuX = (width / 2) - (trayMenu.width / 2)
|
||||
const menuY = (Style.barHeight * scaling)
|
||||
trayMenu.menu = modelData.menu
|
||||
trayMenu.showAt(parent, menuX, menuY)
|
||||
trayPanel.show()
|
||||
} else {
|
||||
|
||||
Logger.log("Tray", "No menu available for", modelData.id, "or trayMenu not set")
|
||||
}
|
||||
}
|
||||
|
|
@ -125,94 +122,37 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
// Attached TrayMenu drop down
|
||||
// Wrapped in NPanel so we can detect click outside of the menu to close the TrayMenu
|
||||
NPanel {
|
||||
PanelWindow {
|
||||
id: trayPanel
|
||||
showOverlay: false // no colors overlay even if activated in settings
|
||||
anchors.top: true
|
||||
anchors.left: true
|
||||
anchors.right: true
|
||||
anchors.bottom: true
|
||||
visible: false
|
||||
color: Color.transparent
|
||||
screen: screen
|
||||
|
||||
// Override hide function to animate first
|
||||
function hide() {
|
||||
// Start hide animation
|
||||
trayMenuRect.scaleValue = 0.8
|
||||
trayMenuRect.opacityValue = 0.0
|
||||
function open() {
|
||||
visible = true
|
||||
|
||||
// Hide after animation completes
|
||||
hideTimer.start()
|
||||
// Register into the panel service
|
||||
// so this will autoclose if we open another panel
|
||||
PanelService.registerOpen(trayPanel)
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: trayPanel
|
||||
ignoreUnknownSignals: true
|
||||
function onDismissed() {
|
||||
// Start hide animation
|
||||
trayMenuRect.scaleValue = 0.8
|
||||
trayMenuRect.opacityValue = 0.0
|
||||
|
||||
// Hide after animation completes
|
||||
hideTimer.start()
|
||||
}
|
||||
function close() {
|
||||
visible = false
|
||||
trayMenu.hideMenu()
|
||||
}
|
||||
|
||||
// Also handle visibility changes from external sources
|
||||
onVisibleChanged: {
|
||||
if (!visible && trayMenuRect.opacityValue > 0) {
|
||||
// Start hide animation
|
||||
trayMenuRect.scaleValue = 0.8
|
||||
trayMenuRect.opacityValue = 0.0
|
||||
|
||||
// Hide after animation completes
|
||||
hideTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
// Timer to hide panel after animation
|
||||
Timer {
|
||||
id: hideTimer
|
||||
interval: Style.animationSlow
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
trayPanel.visible = false
|
||||
trayMenu.hideMenu()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: trayMenuRect
|
||||
color: Color.transparent
|
||||
// Clicking outside of the rectangle to close
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: trayPanel.close()
|
||||
}
|
||||
|
||||
// Animation properties
|
||||
property real scaleValue: 0.8
|
||||
property real opacityValue: 0.0
|
||||
|
||||
scale: scaleValue
|
||||
opacity: opacityValue
|
||||
|
||||
// Animate in when component is completed
|
||||
Component.onCompleted: {
|
||||
scaleValue = 1.0
|
||||
opacityValue = 1.0
|
||||
}
|
||||
|
||||
// Animation behaviors
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Style.animationSlow
|
||||
easing.type: Easing.OutExpo
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
|
||||
TrayMenu {
|
||||
id: trayMenu
|
||||
}
|
||||
TrayMenu {
|
||||
id: trayMenu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ Item {
|
|||
}
|
||||
onClicked: {
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.AudioService
|
||||
settingsPanel.isLoaded = true
|
||||
settingsPanel.open(screen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,27 +34,10 @@ NIconButton {
|
|||
}
|
||||
tooltipText: "WiFi Networks"
|
||||
onClicked: {
|
||||
if (!wifiMenuLoader.active) {
|
||||
wifiMenuLoader.isLoaded = true
|
||||
}
|
||||
if (wifiMenuLoader.item) {
|
||||
if (wifiMenuLoader.item.visible) {
|
||||
// Panel is visible, hide it with animation
|
||||
if (wifiMenuLoader.item.hide) {
|
||||
wifiMenuLoader.item.hide()
|
||||
} else {
|
||||
wifiMenuLoader.item.visible = false
|
||||
NetworkService.onMenuClosed()
|
||||
}
|
||||
} else {
|
||||
// Panel is hidden, show it
|
||||
wifiMenuLoader.item.visible = true
|
||||
NetworkService.onMenuOpened()
|
||||
}
|
||||
}
|
||||
wifiPanel.toggle(screen)
|
||||
}
|
||||
|
||||
WiFiMenu {
|
||||
id: wifiMenuLoader
|
||||
WiFiPanel {
|
||||
id: wifiPanel
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,435 +0,0 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
// Loader for WiFi menu
|
||||
NLoader {
|
||||
id: root
|
||||
|
||||
content: Component {
|
||||
NPanel {
|
||||
id: wifiPanel
|
||||
|
||||
property string passwordPromptSsid: ""
|
||||
property string passwordInput: ""
|
||||
property bool showPasswordPrompt: false
|
||||
|
||||
function hide() {
|
||||
wifiMenuRect.scaleValue = 0.8
|
||||
wifiMenuRect.opacityValue = 0.0
|
||||
|
||||
hideTimer.start()
|
||||
}
|
||||
|
||||
// Connect to NPanel's dismissed signal to handle external close events
|
||||
Connections {
|
||||
target: wifiPanel
|
||||
ignoreUnknownSignals: true
|
||||
function onDismissed() {
|
||||
// Start hide animation
|
||||
wifiMenuRect.scaleValue = 0.8
|
||||
wifiMenuRect.opacityValue = 0.0
|
||||
|
||||
// Hide after animation completes
|
||||
hideTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
// Also handle visibility changes from external sources
|
||||
onVisibleChanged: {
|
||||
if (visible && Settings.data.network.wifiEnabled) {
|
||||
NetworkService.refreshNetworks()
|
||||
} else if (wifiMenuRect.opacityValue > 0) {
|
||||
// Start hide animation
|
||||
wifiMenuRect.scaleValue = 0.8
|
||||
wifiMenuRect.opacityValue = 0.0
|
||||
|
||||
// Hide after animation completes
|
||||
hideTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
// Timer to hide panel after animation
|
||||
Timer {
|
||||
id: hideTimer
|
||||
interval: Style.animationSlow
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
wifiPanel.visible = false
|
||||
wifiPanel.dismissed()
|
||||
// NetworkService.onMenuClosed()
|
||||
}
|
||||
}
|
||||
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
|
||||
// Timer to refresh networks when WiFi is enabled while menu is open
|
||||
Timer {
|
||||
id: wifiEnableRefreshTimer
|
||||
interval: 3000 // Wait 3 seconds for WiFi to be fully ready
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (Settings.data.network.wifiEnabled && wifiPanel.visible) {
|
||||
NetworkService.refreshNetworks()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: wifiMenuRect
|
||||
color: Color.mSurface
|
||||
radius: Style.radiusL * scaling
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
width: 340 * scaling
|
||||
height: 500 * scaling
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: Style.marginXS * scaling
|
||||
top: Settings.data.bar.position === "top" ? parent.top : undefined
|
||||
bottom: Settings.data.bar.position === "bottom" ? parent.bottom : undefined
|
||||
topMargin: Settings.data.bar.position === "top" ? Style.marginXS * scaling : undefined
|
||||
bottomMargin: Settings.data.bar.position === "bottom" ? Style.barHeight * scaling + Style.marginXS * scaling : undefined
|
||||
}
|
||||
|
||||
// Animation properties
|
||||
property real scaleValue: 0.8
|
||||
property real opacityValue: 0.0
|
||||
|
||||
scale: scaleValue
|
||||
opacity: opacityValue
|
||||
|
||||
// Animate in when component is completed
|
||||
Component.onCompleted: {
|
||||
scaleValue = 1.0
|
||||
opacityValue = 1.0
|
||||
}
|
||||
|
||||
// Animation behaviors
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Style.animationSlow
|
||||
easing.type: Easing.OutExpo
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL * scaling
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "wifi"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "WiFi"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
font.bold: true
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "refresh"
|
||||
tooltipText: "Refresh Networks"
|
||||
sizeMultiplier: 0.8
|
||||
enabled: Settings.data.network.wifiEnabled && !NetworkService.isLoading
|
||||
onClicked: {
|
||||
NetworkService.refreshNetworks()
|
||||
}
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
tooltipText: "Close"
|
||||
sizeMultiplier: 0.8
|
||||
onClicked: {
|
||||
wifiPanel.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
// Loading indicator
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
visible: Settings.data.network.wifiEnabled && NetworkService.isLoading
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NBusyIndicator {
|
||||
running: NetworkService.isLoading
|
||||
color: Color.mPrimary
|
||||
size: Style.baseWidgetSize * scaling
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Scanning for networks..."
|
||||
font.pointSize: Style.fontSizeNormal * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
// WiFi disabled message
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
visible: !Settings.data.network.wifiEnabled
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "wifi_off"
|
||||
font.pointSize: Style.fontSizeXXXL * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "WiFi is disabled"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Enable WiFi to see available networks"
|
||||
font.pointSize: Style.fontSizeNormal * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
// Network list
|
||||
ListView {
|
||||
id: networkList
|
||||
anchors.fill: parent
|
||||
visible: Settings.data.network.wifiEnabled && !NetworkService.isLoading
|
||||
model: Object.values(NetworkService.networks)
|
||||
spacing: Style.marginM * scaling
|
||||
clip: true
|
||||
|
||||
delegate: Item {
|
||||
width: parent ? parent.width : 0
|
||||
height: modelData.ssid === passwordPromptSsid
|
||||
&& showPasswordPrompt ? 108 * scaling : Style.baseWidgetSize * 1.5 * scaling
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.baseWidgetSize * 1.5 * scaling
|
||||
radius: Style.radiusM * scaling
|
||||
color: modelData.connected ? Color.mPrimary : (networkMouseArea.containsMouse ? Color.mTertiary : Color.transparent)
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS * scaling
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
NIcon {
|
||||
text: NetworkService.signalIcon(modelData.signal)
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: modelData.connected ? Color.mSurface : (networkMouseArea.containsMouse ? Color.mSurface : Color.mOnSurface)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginXS * scaling
|
||||
|
||||
// SSID
|
||||
NText {
|
||||
text: modelData.ssid || "Unknown Network"
|
||||
font.pointSize: Style.fontSizeNormal * scaling
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
color: modelData.connected ? Color.mSurface : (networkMouseArea.containsMouse ? Color.mSurface : Color.mOnSurface)
|
||||
}
|
||||
|
||||
// Security Protocol
|
||||
NText {
|
||||
text: modelData.security && modelData.security !== "--" ? modelData.security : "Open"
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
color: modelData.connected ? Color.mSurface : (networkMouseArea.containsMouse ? Color.mSurface : Color.mOnSurface)
|
||||
}
|
||||
|
||||
NText {
|
||||
visible: NetworkService.connectStatusSsid === modelData.ssid
|
||||
&& NetworkService.connectStatus === "error" && NetworkService.connectError.length > 0
|
||||
text: NetworkService.connectError
|
||||
color: Color.mError
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: Style.baseWidgetSize * 0.7 * scaling
|
||||
Layout.preferredHeight: Style.baseWidgetSize * 0.7 * scaling
|
||||
visible: NetworkService.connectStatusSsid === modelData.ssid
|
||||
&& (NetworkService.connectStatus !== ""
|
||||
|| NetworkService.connectingSsid === modelData.ssid)
|
||||
|
||||
NBusyIndicator {
|
||||
visible: NetworkService.connectingSsid === modelData.ssid
|
||||
running: NetworkService.connectingSsid === modelData.ssid
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
size: Style.baseWidgetSize * 0.7 * scaling
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
visible: modelData.connected
|
||||
text: "connected"
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
color: modelData.connected ? Color.mSurface : (networkMouseArea.containsMouse ? Color.mSurface : Color.mOnSurface)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: networkMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
if (modelData.connected) {
|
||||
NetworkService.disconnectNetwork(modelData.ssid)
|
||||
} else if (NetworkService.isSecured(modelData.security) && !modelData.existing) {
|
||||
passwordPromptSsid = modelData.ssid
|
||||
showPasswordPrompt = true
|
||||
passwordInput = "" // Clear previous input
|
||||
Qt.callLater(function () {
|
||||
passwordInputField.forceActiveFocus()
|
||||
})
|
||||
} else {
|
||||
NetworkService.connectNetwork(modelData.ssid, modelData.security)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Password prompt section
|
||||
Rectangle {
|
||||
id: passwordPromptSection
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: modelData.ssid === passwordPromptSsid && showPasswordPrompt ? 60 : 0
|
||||
Layout.margins: Style.marginS * scaling
|
||||
visible: modelData.ssid === passwordPromptSsid && showPasswordPrompt
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusS * scaling
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS * scaling
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.barHeight * scaling
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Style.radiusXS * scaling
|
||||
color: Color.transparent
|
||||
border.color: passwordInputField.activeFocus ? Color.mPrimary : Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
TextInput {
|
||||
id: passwordInputField
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginM * scaling
|
||||
text: passwordInput
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
color: Color.mOnSurface
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
clip: true
|
||||
focus: true
|
||||
selectByMouse: true
|
||||
activeFocusOnTab: true
|
||||
inputMethodHints: Qt.ImhNone
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: passwordInput = text
|
||||
onAccepted: {
|
||||
NetworkService.submitPassword(passwordPromptSsid, passwordInput)
|
||||
showPasswordPrompt = false
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: passwordInputMouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: passwordInputField.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: Style.baseWidgetSize * 2.5 * scaling
|
||||
Layout.preferredHeight: Style.barHeight * scaling
|
||||
radius: Style.radiusM * scaling
|
||||
color: Color.mPrimary
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
anchors.centerIn: parent
|
||||
text: "Connect"
|
||||
color: Color.mSurface
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
NetworkService.submitPassword(passwordPromptSsid, passwordInput)
|
||||
showPasswordPrompt = false
|
||||
}
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onEntered: parent.color = Qt.darker(Color.mPrimary, 1.1)
|
||||
onExited: parent.color = Color.mPrimary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
335
Modules/Bar/WiFiPanel.qml
Normal file
335
Modules/Bar/WiFiPanel.qml
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
NPanel {
|
||||
id: root
|
||||
|
||||
panelWidth: 380 * scaling
|
||||
panelHeight: 500 * scaling
|
||||
panelAnchorRight: true
|
||||
|
||||
property string passwordPromptSsid: ""
|
||||
property string passwordInput: ""
|
||||
property bool showPasswordPrompt: false
|
||||
|
||||
onOpened: {
|
||||
if (Settings.data.network.wifiEnabled && wifiPanel.visible) {
|
||||
NetworkService.refreshNetworks()
|
||||
}
|
||||
}
|
||||
|
||||
panelContent: Rectangle {
|
||||
color: Color.transparent
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL * scaling
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
// Header
|
||||
RowLayout {
|
||||
NIcon {
|
||||
text: "wifi"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "WiFi"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
font.bold: true
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "refresh"
|
||||
tooltipText: "Refresh Networks"
|
||||
sizeMultiplier: 0.8
|
||||
enabled: Settings.data.network.wifiEnabled && !NetworkService.isLoading
|
||||
onClicked: {
|
||||
NetworkService.refreshNetworks()
|
||||
}
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
tooltipText: "Close"
|
||||
sizeMultiplier: 0.8
|
||||
onClicked: {
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
// Loading indicator
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
visible: Settings.data.network.wifiEnabled && NetworkService.isLoading
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NBusyIndicator {
|
||||
running: NetworkService.isLoading
|
||||
color: Color.mPrimary
|
||||
size: Style.baseWidgetSize * scaling
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Scanning for networks..."
|
||||
font.pointSize: Style.fontSizeNormal * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
// WiFi disabled message
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
visible: !Settings.data.network.wifiEnabled
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
NIcon {
|
||||
text: "wifi_off"
|
||||
font.pointSize: Style.fontSizeXXXL * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "WiFi is disabled"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Enable WiFi to see available networks"
|
||||
font.pointSize: Style.fontSizeNormal * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
// Network list
|
||||
ListView {
|
||||
id: networkList
|
||||
anchors.fill: parent
|
||||
visible: Settings.data.network.wifiEnabled && !NetworkService.isLoading
|
||||
model: Object.values(NetworkService.networks)
|
||||
spacing: Style.marginM * scaling
|
||||
clip: true
|
||||
|
||||
delegate: Item {
|
||||
width: parent ? parent.width : 0
|
||||
height: modelData.ssid === passwordPromptSsid
|
||||
&& showPasswordPrompt ? 108 * scaling : Style.baseWidgetSize * 1.5 * scaling
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.baseWidgetSize * 1.5 * scaling
|
||||
radius: Style.radiusS * scaling
|
||||
color: modelData.connected ? Color.mPrimary : (networkMouseArea.containsMouse ? Color.mTertiary : Color.transparent)
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS * scaling
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
NIcon {
|
||||
text: NetworkService.signalIcon(modelData.signal)
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: modelData.connected ? Color.mSurface : (networkMouseArea.containsMouse ? Color.mSurface : Color.mOnSurface)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginXS * scaling
|
||||
|
||||
// SSID
|
||||
NText {
|
||||
text: modelData.ssid || "Unknown Network"
|
||||
font.pointSize: Style.fontSizeNormal * scaling
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
color: modelData.connected ? Color.mSurface : (networkMouseArea.containsMouse ? Color.mSurface : Color.mOnSurface)
|
||||
}
|
||||
|
||||
// Security Protocol
|
||||
NText {
|
||||
text: modelData.security && modelData.security !== "--" ? modelData.security : "Open"
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
color: modelData.connected ? Color.mSurface : (networkMouseArea.containsMouse ? Color.mSurface : Color.mOnSurface)
|
||||
}
|
||||
|
||||
NText {
|
||||
visible: NetworkService.connectStatusSsid === modelData.ssid
|
||||
&& NetworkService.connectStatus === "error" && NetworkService.connectError.length > 0
|
||||
text: NetworkService.connectError
|
||||
color: Color.mError
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: Style.baseWidgetSize * 0.7 * scaling
|
||||
Layout.preferredHeight: Style.baseWidgetSize * 0.7 * scaling
|
||||
visible: NetworkService.connectStatusSsid === modelData.ssid
|
||||
&& (NetworkService.connectStatus !== ""
|
||||
|| NetworkService.connectingSsid === modelData.ssid)
|
||||
|
||||
NBusyIndicator {
|
||||
visible: NetworkService.connectingSsid === modelData.ssid
|
||||
running: NetworkService.connectingSsid === modelData.ssid
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
size: Style.baseWidgetSize * 0.7 * scaling
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
visible: modelData.connected
|
||||
text: "connected"
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
color: modelData.connected ? Color.mSurface : (networkMouseArea.containsMouse ? Color.mSurface : Color.mOnSurface)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: networkMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
if (modelData.connected) {
|
||||
NetworkService.disconnectNetwork(modelData.ssid)
|
||||
} else if (NetworkService.isSecured(modelData.security) && !modelData.existing) {
|
||||
passwordPromptSsid = modelData.ssid
|
||||
showPasswordPrompt = true
|
||||
passwordInput = "" // Clear previous input
|
||||
Qt.callLater(function () {
|
||||
passwordInputField.forceActiveFocus()
|
||||
})
|
||||
} else {
|
||||
NetworkService.connectNetwork(modelData.ssid, modelData.security)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Password prompt section
|
||||
Rectangle {
|
||||
id: passwordPromptSection
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: modelData.ssid === passwordPromptSsid && showPasswordPrompt ? 60 : 0
|
||||
Layout.margins: Style.marginS * scaling
|
||||
visible: modelData.ssid === passwordPromptSsid && showPasswordPrompt
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusS * scaling
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS * scaling
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.barHeight * scaling
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Style.radiusXS * scaling
|
||||
color: Color.transparent
|
||||
border.color: passwordInputField.activeFocus ? Color.mPrimary : Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
TextInput {
|
||||
id: passwordInputField
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginM * scaling
|
||||
text: passwordInput
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
color: Color.mOnSurface
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
clip: true
|
||||
focus: true
|
||||
selectByMouse: true
|
||||
activeFocusOnTab: true
|
||||
inputMethodHints: Qt.ImhNone
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: passwordInput = text
|
||||
onAccepted: {
|
||||
NetworkService.submitPassword(passwordPromptSsid, passwordInput)
|
||||
showPasswordPrompt = false
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: passwordInputMouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: passwordInputField.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: Style.baseWidgetSize * 2.5 * scaling
|
||||
Layout.preferredHeight: Style.barHeight * scaling
|
||||
radius: Style.radiusM * scaling
|
||||
color: Color.mPrimary
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
anchors.centerIn: parent
|
||||
text: "Connect"
|
||||
color: Color.mSurface
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
NetworkService.submitPassword(passwordPromptSsid, passwordInput)
|
||||
showPasswordPrompt = false
|
||||
}
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onEntered: parent.color = Qt.darker(Color.mPrimary, 1.1)
|
||||
onExited: parent.color = Color.mPrimary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue