Merge branch 'main' into miniplayer-eyecandy

This commit is contained in:
Lemmy 2025-08-20 20:40:17 -04:00 committed by GitHub
commit e51c5cf4bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
56 changed files with 3481 additions and 3542 deletions

View file

@ -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 {}
}
}

View file

@ -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
}
}

View file

@ -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
}
}
}
}
}
}

View 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
}
}
}
}

View file

@ -70,7 +70,7 @@ Item {
onClicked: {
settingsPanel.requestedTab = SettingsPanel.Tab.Brightness
settingsPanel.isLoaded = true
settingsPanel.open(screen)
}
}
}

View file

@ -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)
}
}
}

View file

@ -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

View file

@ -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)
}
}

View file

@ -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)
}

View file

@ -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
}
}
}

View file

@ -65,7 +65,7 @@ Item {
}
onClicked: {
settingsPanel.requestedTab = SettingsPanel.Tab.AudioService
settingsPanel.isLoaded = true
settingsPanel.open(screen)
}
}
}

View file

@ -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
}
}

View file

@ -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
View 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
}
}
}
}
}
}
}
}
}
}
}