summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorATMDA <atdma2600@gmail.com>2025-11-19 21:04:32 -0500
committerATMDA <atdma2600@gmail.com>2025-11-19 21:04:32 -0500
commitef46a02b2f3561574c1ada5afefbbb806bcb6a3b (patch)
tree94a4c218e521fdd6b55f3154de4484593cd61829
parentrefactor: Updated Bluetooth, Ethernet, and Wireless lists to use DeviceList c... (diff)
downloadcaelestia-shell-ef46a02b2f3561574c1ada5afefbbb806bcb6a3b.tar.gz
caelestia-shell-ef46a02b2f3561574c1ada5afefbbb806bcb6a3b.tar.bz2
caelestia-shell-ef46a02b2f3561574c1ada5afefbbb806bcb6a3b.zip
refactor: ToggleButton Tooltips and DeviceList
Diffstat (limited to '')
-rw-r--r--components/controls/ToggleButton.qml51
-rw-r--r--components/controls/Tooltip.qml170
-rw-r--r--modules/controlcenter/bluetooth/BtPane.qml19
-rw-r--r--modules/controlcenter/bluetooth/Details.qml633
-rw-r--r--modules/controlcenter/bluetooth/DeviceList.qml84
-rw-r--r--modules/controlcenter/components/DeviceDetails.qml2
-rw-r--r--modules/controlcenter/components/DeviceList.qml14
-rw-r--r--modules/controlcenter/launcher/LauncherPane.qml4
-rw-r--r--modules/controlcenter/network/EthernetDetails.qml136
-rw-r--r--modules/controlcenter/network/EthernetList.qml6
-rw-r--r--modules/controlcenter/network/NetworkingPane.qml377
-rw-r--r--modules/controlcenter/network/WirelessDetails.qml176
-rw-r--r--modules/controlcenter/network/WirelessList.qml13
13 files changed, 836 insertions, 849 deletions
diff --git a/components/controls/ToggleButton.qml b/components/controls/ToggleButton.qml
index 9d8e094..b2c2afe 100644
--- a/components/controls/ToggleButton.qml
+++ b/components/controls/ToggleButton.qml
@@ -1,5 +1,6 @@
import ".."
import qs.components
+import qs.components.controls
import qs.components.effects
import qs.services
import qs.config
@@ -13,12 +14,31 @@ StyledRect {
property string icon
property string label
property string accent: "Secondary"
+ property real iconSize: Appearance.font.size.large
+ property real horizontalPadding: Appearance.padding.large
+ property real verticalPadding: Appearance.padding.normal
+ property string tooltip: ""
+ property bool hovered: false
signal clicked
+ Component.onCompleted: {
+ hovered = toggleStateLayer.containsMouse;
+ }
+
+ Connections {
+ target: toggleStateLayer
+ function onContainsMouseChanged() {
+ const newHovered = toggleStateLayer.containsMouse;
+ if (hovered !== newHovered) {
+ hovered = newHovered;
+ }
+ }
+ }
+
Layout.preferredWidth: implicitWidth + (toggleStateLayer.pressed ? Appearance.padding.normal * 2 : toggled ? Appearance.padding.small * 2 : 0)
- implicitWidth: toggleBtnInner.implicitWidth + Appearance.padding.large * 2
- implicitHeight: toggleBtnIcon.implicitHeight + Appearance.padding.normal * 2
+ implicitWidth: toggleBtnInner.implicitWidth + horizontalPadding * 2
+ implicitHeight: toggleBtnIcon.implicitHeight + verticalPadding * 2
radius: toggled || toggleStateLayer.pressed ? Appearance.rounding.small : Math.min(width, height) / 2 * Math.min(1, Appearance.rounding.scale)
color: toggled ? Colours.palette[`m3${accent.toLowerCase()}`] : Colours.palette[`m3${accent.toLowerCase()}Container`]
@@ -46,7 +66,7 @@ StyledRect {
fill: root.toggled ? 1 : 0
text: root.icon
color: root.toggled ? Colours.palette[`m3on${root.accent}`] : Colours.palette[`m3on${root.accent}Container`]
- font.pointSize: Appearance.font.size.large
+ font.pointSize: root.iconSize
Behavior on fill {
Anim {}
@@ -78,5 +98,30 @@ StyledRect {
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
}
}
+
+ // Tooltip - positioned absolutely, doesn't affect layout
+ Loader {
+ id: tooltipLoader
+ active: root.tooltip !== ""
+ asynchronous: true
+ z: 10000
+ width: 0
+ height: 0
+ sourceComponent: Component {
+ Tooltip {
+ target: root
+ text: root.tooltip
+ }
+ }
+ // Completely remove from layout
+ Layout.fillWidth: false
+ Layout.fillHeight: false
+ Layout.preferredWidth: 0
+ Layout.preferredHeight: 0
+ Layout.maximumWidth: 0
+ Layout.maximumHeight: 0
+ Layout.minimumWidth: 0
+ Layout.minimumHeight: 0
+ }
}
diff --git a/components/controls/Tooltip.qml b/components/controls/Tooltip.qml
new file mode 100644
index 0000000..bab8086
--- /dev/null
+++ b/components/controls/Tooltip.qml
@@ -0,0 +1,170 @@
+import ".."
+import qs.components.effects
+import qs.services
+import qs.config
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Popup {
+ id: root
+
+ required property Item target
+ required property string text
+ property int delay: 500
+ property int timeout: 0
+
+ property bool tooltipVisible: false
+ property Timer showTimer: Timer {
+ interval: root.delay
+ onTriggered: root.tooltipVisible = true
+ }
+ property Timer hideTimer: Timer {
+ interval: root.timeout
+ onTriggered: root.tooltipVisible = false
+ }
+
+ // Popup properties - doesn't affect layout
+ parent: {
+ let p = target;
+ // Walk up to find the root Item (usually has anchors.fill: parent)
+ while (p && p.parent) {
+ const parentItem = p.parent;
+ // Check if this looks like a root pane Item
+ if (parentItem && parentItem.anchors && parentItem.anchors.fill !== undefined) {
+ return parentItem;
+ }
+ p = parentItem;
+ }
+ // Fallback
+ return target.parent?.parent?.parent ?? target.parent?.parent ?? target.parent ?? target;
+ }
+
+ visible: tooltipVisible
+ modal: false
+ closePolicy: Popup.NoAutoClose
+ padding: 0
+ margins: 0
+
+ // Update position when target moves or tooltip becomes visible
+ onTooltipVisibleChanged: {
+ if (tooltipVisible) {
+ Qt.callLater(updatePosition);
+ }
+ }
+ Connections {
+ target: root.target
+ function onXChanged() { if (root.tooltipVisible) root.updatePosition(); }
+ function onYChanged() { if (root.tooltipVisible) root.updatePosition(); }
+ function onWidthChanged() { if (root.tooltipVisible) root.updatePosition(); }
+ function onHeightChanged() { if (root.tooltipVisible) root.updatePosition(); }
+ }
+
+ function updatePosition() {
+ if (!target || !parent) return;
+
+ // Wait for tooltipRect to have its size calculated
+ Qt.callLater(() => {
+ if (!target || !parent || !tooltipRect) return;
+
+ // Get target position in parent's coordinate system
+ const targetPos = target.mapToItem(parent, 0, 0);
+ const targetCenterX = targetPos.x + target.width / 2;
+
+ // Get tooltip size (use width/height if available, otherwise implicit)
+ const tooltipWidth = tooltipRect.width > 0 ? tooltipRect.width : tooltipRect.implicitWidth;
+ const tooltipHeight = tooltipRect.height > 0 ? tooltipRect.height : tooltipRect.implicitHeight;
+
+ // Center tooltip horizontally on target
+ let newX = targetCenterX - tooltipWidth / 2;
+
+ // Position tooltip above target
+ let newY = targetPos.y - tooltipHeight - Appearance.spacing.small;
+
+ // Keep within bounds
+ const padding = Appearance.padding.normal;
+ if (newX < padding) {
+ newX = padding;
+ } else if (newX + tooltipWidth > (parent.width - padding)) {
+ newX = parent.width - tooltipWidth - padding;
+ }
+
+ // Update popup position
+ x = newX;
+ y = newY;
+ });
+ }
+
+ enter: Transition {
+ Anim {
+ property: "opacity"
+ from: 0
+ to: 1
+ duration: Appearance.anim.durations.expressiveFastSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
+ }
+ }
+
+ exit: Transition {
+ Anim {
+ property: "opacity"
+ from: 1
+ to: 0
+ duration: Appearance.anim.durations.expressiveFastSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
+ }
+ }
+
+ // Monitor hover state
+ Connections {
+ target: root.target
+ function onHoveredChanged() {
+ if (target.hovered) {
+ showTimer.start();
+ if (timeout > 0) {
+ hideTimer.stop();
+ hideTimer.start();
+ }
+ } else {
+ showTimer.stop();
+ hideTimer.stop();
+ tooltipVisible = false;
+ }
+ }
+ }
+
+ contentItem: StyledRect {
+ id: tooltipRect
+
+ implicitWidth: tooltipText.implicitWidth + Appearance.padding.normal * 2
+ implicitHeight: tooltipText.implicitHeight + Appearance.padding.smaller * 2
+
+ color: Colours.palette.m3surfaceContainerHighest
+ radius: Appearance.rounding.small
+
+ // Add elevation for depth
+ Elevation {
+ anchors.fill: parent
+ radius: parent.radius
+ z: -1
+ level: 3
+ }
+
+ StyledText {
+ id: tooltipText
+
+ anchors.centerIn: parent
+
+ text: root.text
+ color: Colours.palette.m3onSurface
+ font.pointSize: Appearance.font.size.small
+ }
+ }
+
+ Component.onCompleted: {
+ if (tooltipVisible) {
+ updatePosition();
+ }
+ }
+}
+
diff --git a/modules/controlcenter/bluetooth/BtPane.qml b/modules/controlcenter/bluetooth/BtPane.qml
index 6877801..a987e75 100644
--- a/modules/controlcenter/bluetooth/BtPane.qml
+++ b/modules/controlcenter/bluetooth/BtPane.qml
@@ -24,8 +24,23 @@ SplitPaneWithDetails {
}
leftContent: Component {
- DeviceList {
- session: root.session
+ StyledFlickable {
+ id: leftFlickable
+
+ flickableDirection: Flickable.VerticalFlick
+ contentHeight: deviceList.height
+
+ StyledScrollBar.vertical: StyledScrollBar {
+ flickable: leftFlickable
+ }
+
+ DeviceList {
+ id: deviceList
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ session: root.session
+ }
}
}
diff --git a/modules/controlcenter/bluetooth/Details.qml b/modules/controlcenter/bluetooth/Details.qml
index 5496966..b260458 100644
--- a/modules/controlcenter/bluetooth/Details.qml
+++ b/modules/controlcenter/bluetooth/Details.qml
@@ -20,404 +20,427 @@ StyledFlickable {
readonly property BluetoothDevice device: session.bt.active
flickableDirection: Flickable.VerticalFlick
- contentHeight: layoutWrapper.height
+ contentHeight: detailsWrapper.height
StyledScrollBar.vertical: StyledScrollBar {
flickable: root
}
- Item {
- id: layoutWrapper
+ Item {
+ id: detailsWrapper
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ implicitHeight: details.implicitHeight
+
+ DeviceDetails {
+ id: details
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
- implicitHeight: layout.height
-
- ColumnLayout {
- id: layout
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- spacing: Appearance.spacing.normal
+ session: root.session
+ device: root.device
- SettingsHeader {
- icon: Icons.getBluetoothIcon(root.device?.icon ?? "")
- title: root.device?.name ?? ""
+ headerComponent: Component {
+ SettingsHeader {
+ icon: Icons.getBluetoothIcon(root.device?.icon ?? "")
+ title: root.device?.name ?? ""
+ }
}
- StyledText {
- Layout.topMargin: Appearance.spacing.large
- text: qsTr("Connection status")
- font.pointSize: Appearance.font.size.larger
- font.weight: 500
- }
+ sections: [
+ Component {
+ ColumnLayout {
+ spacing: Appearance.spacing.normal
- StyledText {
- text: qsTr("Connection settings for this device")
- color: Colours.palette.m3outline
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.large
+ text: qsTr("Connection status")
+ font.pointSize: Appearance.font.size.larger
+ font.weight: 500
+ }
- StyledRect {
- Layout.fillWidth: true
- implicitHeight: deviceStatus.implicitHeight + Appearance.padding.large * 2
+ StyledText {
+ text: qsTr("Connection settings for this device")
+ color: Colours.palette.m3outline
+ }
+
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: deviceStatus.implicitHeight + Appearance.padding.large * 2
- radius: Appearance.rounding.normal
- color: Colours.tPalette.m3surfaceContainer
+ radius: Appearance.rounding.normal
+ color: Colours.tPalette.m3surfaceContainer
- ColumnLayout {
- id: deviceStatus
+ ColumnLayout {
+ id: deviceStatus
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.verticalCenter: parent.verticalCenter
- anchors.margins: Appearance.padding.large
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: Appearance.padding.large
- spacing: Appearance.spacing.larger
+ spacing: Appearance.spacing.larger
- Toggle {
- label: qsTr("Connected")
- checked: root.device?.connected ?? false
- toggle.onToggled: root.device.connected = checked
- }
+ Toggle {
+ label: qsTr("Connected")
+ checked: root.device?.connected ?? false
+ toggle.onToggled: root.device.connected = checked
+ }
- Toggle {
- label: qsTr("Paired")
- checked: root.device?.paired ?? false
- toggle.onToggled: {
- if (root.device.paired)
- root.device.forget();
- else
- root.device.pair();
+ Toggle {
+ label: qsTr("Paired")
+ checked: root.device?.paired ?? false
+ toggle.onToggled: {
+ if (root.device.paired)
+ root.device.forget();
+ else
+ root.device.pair();
+ }
+ }
+
+ Toggle {
+ label: qsTr("Blocked")
+ checked: root.device?.blocked ?? false
+ toggle.onToggled: root.device.blocked = checked
+ }
+ }
}
}
+ },
+ Component {
+ ColumnLayout {
+ spacing: Appearance.spacing.normal
- Toggle {
- label: qsTr("Blocked")
- checked: root.device?.blocked ?? false
- toggle.onToggled: root.device.blocked = checked
- }
- }
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.large
+ text: qsTr("Device properties")
+ font.pointSize: Appearance.font.size.larger
+ font.weight: 500
+ }
- StyledText {
- Layout.topMargin: Appearance.spacing.large
- text: qsTr("Device properties")
- font.pointSize: Appearance.font.size.larger
- font.weight: 500
- }
+ StyledText {
+ text: qsTr("Additional settings")
+ color: Colours.palette.m3outline
+ }
- StyledText {
- text: qsTr("Additional settings")
- color: Colours.palette.m3outline
- }
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: deviceProps.implicitHeight + Appearance.padding.large * 2
- StyledRect {
- Layout.fillWidth: true
- implicitHeight: deviceProps.implicitHeight + Appearance.padding.large * 2
+ radius: Appearance.rounding.normal
+ color: Colours.tPalette.m3surfaceContainer
- radius: Appearance.rounding.normal
- color: Colours.tPalette.m3surfaceContainer
+ ColumnLayout {
+ id: deviceProps
- ColumnLayout {
- id: deviceProps
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: Appearance.padding.large
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.verticalCenter: parent.verticalCenter
- anchors.margins: Appearance.padding.large
+ spacing: Appearance.spacing.larger
- spacing: Appearance.spacing.larger
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.small
- RowLayout {
- Layout.fillWidth: true
- spacing: Appearance.spacing.small
+ Item {
+ id: renameDevice
- Item {
- id: renameDevice
+ Layout.fillWidth: true
+ Layout.rightMargin: Appearance.spacing.small
- Layout.fillWidth: true
- Layout.rightMargin: Appearance.spacing.small
+ implicitHeight: renameLabel.implicitHeight + deviceNameEdit.implicitHeight
- implicitHeight: renameLabel.implicitHeight + deviceNameEdit.implicitHeight
+ states: State {
+ name: "editingDeviceName"
+ when: root.session.bt.editingDeviceName
- states: State {
- name: "editingDeviceName"
- when: root.session.bt.editingDeviceName
+ AnchorChanges {
+ target: deviceNameEdit
+ anchors.top: renameDevice.top
+ }
+ PropertyChanges {
+ renameDevice.implicitHeight: deviceNameEdit.implicitHeight
+ renameLabel.opacity: 0
+ deviceNameEdit.padding: Appearance.padding.normal
+ }
+ }
- AnchorChanges {
- target: deviceNameEdit
- anchors.top: renameDevice.top
- }
- PropertyChanges {
- renameDevice.implicitHeight: deviceNameEdit.implicitHeight
- renameLabel.opacity: 0
- deviceNameEdit.padding: Appearance.padding.normal
- }
- }
+ transitions: Transition {
+ AnchorAnimation {
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standard
+ }
+ Anim {
+ properties: "implicitHeight,opacity,padding"
+ }
+ }
- transitions: Transition {
- AnchorAnimation {
- duration: Appearance.anim.durations.normal
- easing.type: Easing.BezierSpline
- easing.bezierCurve: Appearance.anim.curves.standard
- }
- Anim {
- properties: "implicitHeight,opacity,padding"
- }
- }
+ StyledText {
+ id: renameLabel
- StyledText {
- id: renameLabel
+ anchors.left: parent.left
- anchors.left: parent.left
+ text: qsTr("Device name")
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
- text: qsTr("Device name")
- color: Colours.palette.m3outline
- font.pointSize: Appearance.font.size.small
- }
+ StyledTextField {
+ id: deviceNameEdit
- StyledTextField {
- id: deviceNameEdit
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: renameLabel.bottom
+ anchors.leftMargin: root.session.bt.editingDeviceName ? 0 : -Appearance.padding.normal
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: renameLabel.bottom
- anchors.leftMargin: root.session.bt.editingDeviceName ? 0 : -Appearance.padding.normal
+ text: root.device?.name ?? ""
+ readOnly: !root.session.bt.editingDeviceName
+ onAccepted: {
+ root.session.bt.editingDeviceName = false;
+ root.device.name = text;
+ }
- text: root.device?.name ?? ""
- readOnly: !root.session.bt.editingDeviceName
- onAccepted: {
- root.session.bt.editingDeviceName = false;
- root.device.name = text;
- }
+ leftPadding: Appearance.padding.normal
+ rightPadding: Appearance.padding.normal
- leftPadding: Appearance.padding.normal
- rightPadding: Appearance.padding.normal
+ background: StyledRect {
+ radius: Appearance.rounding.small
+ border.width: 2
+ border.color: Colours.palette.m3primary
+ opacity: root.session.bt.editingDeviceName ? 1 : 0
- background: StyledRect {
- radius: Appearance.rounding.small
- border.width: 2
- border.color: Colours.palette.m3primary
- opacity: root.session.bt.editingDeviceName ? 1 : 0
+ Behavior on border.color {
+ CAnim {}
+ }
- Behavior on border.color {
- CAnim {}
- }
+ Behavior on opacity {
+ Anim {}
+ }
+ }
- Behavior on opacity {
- Anim {}
+ Behavior on anchors.leftMargin {
+ Anim {}
+ }
+ }
}
- }
- Behavior on anchors.leftMargin {
- Anim {}
- }
- }
- }
+ StyledRect {
+ implicitWidth: implicitHeight
+ implicitHeight: cancelEditIcon.implicitHeight + Appearance.padding.smaller * 2
- StyledRect {
- implicitWidth: implicitHeight
- implicitHeight: cancelEditIcon.implicitHeight + Appearance.padding.smaller * 2
+ radius: Appearance.rounding.small
+ color: Colours.palette.m3secondaryContainer
+ opacity: root.session.bt.editingDeviceName ? 1 : 0
+ scale: root.session.bt.editingDeviceName ? 1 : 0.5
- radius: Appearance.rounding.small
- color: Colours.palette.m3secondaryContainer
- opacity: root.session.bt.editingDeviceName ? 1 : 0
- scale: root.session.bt.editingDeviceName ? 1 : 0.5
+ StateLayer {
+ color: Colours.palette.m3onSecondaryContainer
+ disabled: !root.session.bt.editingDeviceName
- StateLayer {
- color: Colours.palette.m3onSecondaryContainer
- disabled: !root.session.bt.editingDeviceName
+ function onClicked(): void {
+ root.session.bt.editingDeviceName = false;
+ deviceNameEdit.text = Qt.binding(() => root.device?.name ?? "");
+ }
+ }
- function onClicked(): void {
- root.session.bt.editingDeviceName = false;
- deviceNameEdit.text = Qt.binding(() => root.device?.name ?? "");
- }
- }
+ MaterialIcon {
+ id: cancelEditIcon
- MaterialIcon {
- id: cancelEditIcon
+ anchors.centerIn: parent
+ animate: true
+ text: "cancel"
+ color: Colours.palette.m3onSecondaryContainer
+ }
- anchors.centerIn: parent
- animate: true
- text: "cancel"
- color: Colours.palette.m3onSecondaryContainer
- }
+ Behavior on opacity {
+ Anim {}
+ }
- Behavior on opacity {
- Anim {}
- }
+ Behavior on scale {
+ Anim {
+ duration: Appearance.anim.durations.expressiveFastSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
+ }
+ }
+ }
- Behavior on scale {
- Anim {
- duration: Appearance.anim.durations.expressiveFastSpatial
- easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
- }
- }
- }
+ StyledRect {
+ implicitWidth: implicitHeight
+ implicitHeight: editIcon.implicitHeight + Appearance.padding.smaller * 2
- StyledRect {
- implicitWidth: implicitHeight
- implicitHeight: editIcon.implicitHeight + Appearance.padding.smaller * 2
+ radius: root.session.bt.editingDeviceName ? Appearance.rounding.small : implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
+ color: Qt.alpha(Colours.palette.m3primary, root.session.bt.editingDeviceName ? 1 : 0)
- radius: root.session.bt.editingDeviceName ? Appearance.rounding.small : implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
- color: Qt.alpha(Colours.palette.m3primary, root.session.bt.editingDeviceName ? 1 : 0)
+ StateLayer {
+ color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
- StateLayer {
- color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
+ function onClicked(): void {
+ root.session.bt.editingDeviceName = !root.session.bt.editingDeviceName;
+ if (root.session.bt.editingDeviceName)
+ deviceNameEdit.forceActiveFocus();
+ else
+ deviceNameEdit.accepted();
+ }
+ }
- function onClicked(): void {
- root.session.bt.editingDeviceName = !root.session.bt.editingDeviceName;
- if (root.session.bt.editingDeviceName)
- deviceNameEdit.forceActiveFocus();
- else
- deviceNameEdit.accepted();
- }
- }
+ MaterialIcon {
+ id: editIcon
- MaterialIcon {
- id: editIcon
+ anchors.centerIn: parent
+ animate: true
+ text: root.session.bt.editingDeviceName ? "check_circle" : "edit"
+ color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
+ }
- anchors.centerIn: parent
- animate: true
- text: root.session.bt.editingDeviceName ? "check_circle" : "edit"
- color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
- }
+ Behavior on radius {
+ Anim {}
+ }
+ }
+ }
- Behavior on radius {
- Anim {}
+ Toggle {
+ label: qsTr("Trusted")
+ checked: root.device?.trusted ?? false
+ toggle.onToggled: root.device.trusted = checked
+ }
+
+ Toggle {
+ label: qsTr("Wake allowed")
+ checked: root.device?.wakeAllowed ?? false
+ toggle.onToggled: root.device.wakeAllowed = checked
+ }
}
}
}
+ },
+ Component {
+ ColumnLayout {
+ spacing: Appearance.spacing.normal
- Toggle {
- label: qsTr("Trusted")
- checked: root.device?.trusted ?? false
- toggle.onToggled: root.device.trusted = checked
- }
-
- Toggle {
- label: qsTr("Wake allowed")
- checked: root.device?.wakeAllowed ?? false
- toggle.onToggled: root.device.wakeAllowed = checked
- }
- }
- }
-
- StyledText {
- Layout.topMargin: Appearance.spacing.large
- text: qsTr("Device information")
- font.pointSize: Appearance.font.size.larger
- font.weight: 500
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.large
+ text: qsTr("Device information")
+ font.pointSize: Appearance.font.size.larger
+ font.weight: 500
+ }
- StyledText {
- text: qsTr("Information about this device")
- color: Colours.palette.m3outline
- }
+ StyledText {
+ text: qsTr("Information about this device")
+ color: Colours.palette.m3outline
+ }
- StyledRect {
- Layout.fillWidth: true
- implicitHeight: deviceInfo.implicitHeight + Appearance.padding.large * 2
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: deviceInfo.implicitHeight + Appearance.padding.large * 2
- radius: Appearance.rounding.normal
- color: Colours.tPalette.m3surfaceContainer
+ radius: Appearance.rounding.normal
+ color: Colours.tPalette.m3surfaceContainer
- ColumnLayout {
- id: deviceInfo
+ ColumnLayout {
+ id: deviceInfo
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.verticalCenter: parent.verticalCenter
- anchors.margins: Appearance.padding.large
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: Appearance.padding.large
- spacing: Appearance.spacing.small / 2
+ spacing: Appearance.spacing.small / 2
- StyledText {
- text: root.device?.batteryAvailable ? qsTr("Device battery (%1%)").arg(root.device.battery * 100) : qsTr("Battery unavailable")
- }
+ StyledText {
+ text: root.device?.batteryAvailable ? qsTr("Device battery (%1%)").arg(root.device.battery * 100) : qsTr("Battery unavailable")
+ }
- RowLayout {
- Layout.topMargin: Appearance.spacing.small / 2
- Layout.fillWidth: true
- Layout.preferredHeight: Appearance.padding.smaller
- spacing: Appearance.spacing.small / 2
+ RowLayout {
+ Layout.topMargin: Appearance.spacing.small / 2
+ Layout.fillWidth: true
+ Layout.preferredHeight: Appearance.padding.smaller
+ spacing: Appearance.spacing.small / 2
- StyledRect {
- Layout.fillHeight: true
- implicitWidth: root.device?.batteryAvailable ? parent.width * root.device.battery : 0
- radius: Appearance.rounding.full
- color: Colours.palette.m3primary
- }
+ StyledRect {
+ Layout.fillHeight: true
+ implicitWidth: root.device?.batteryAvailable ? parent.width * root.device.battery : 0
+ radius: Appearance.rounding.full
+ color: Colours.palette.m3primary
+ }
- StyledRect {
- Layout.fillWidth: true
- Layout.fillHeight: true
- radius: Appearance.rounding.full
- color: Colours.palette.m3secondaryContainer
+ StyledRect {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ radius: Appearance.rounding.full
+ color: Colours.palette.m3secondaryContainer
- StyledRect {
- anchors.right: parent.right
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- anchors.margins: parent.height * 0.25
+ StyledRect {
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.margins: parent.height * 0.25
- implicitWidth: height
- radius: Appearance.rounding.full
- color: Colours.palette.m3primary
- }
- }
- }
+ implicitWidth: height
+ radius: Appearance.rounding.full
+ color: Colours.palette.m3primary
+ }
+ }
+ }
- StyledText {
- Layout.topMargin: Appearance.spacing.normal
- text: qsTr("Dbus path")
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("Dbus path")
+ }
- StyledText {
- text: root.device?.dbusPath ?? ""
- color: Colours.palette.m3outline
- font.pointSize: Appearance.font.size.small
- }
+ StyledText {
+ text: root.device?.dbusPath ?? ""
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
- StyledText {
- Layout.topMargin: Appearance.spacing.normal
- text: qsTr("MAC address")
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("MAC address")
+ }
- StyledText {
- text: root.device?.address ?? ""
- color: Colours.palette.m3outline
- font.pointSize: Appearance.font.size.small
- }
+ StyledText {
+ text: root.device?.address ?? ""
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
- StyledText {
- Layout.topMargin: Appearance.spacing.normal
- text: qsTr("Bonded")
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("Bonded")
+ }
- StyledText {
- text: root.device?.bonded ? qsTr("Yes") : qsTr("No")
- color: Colours.palette.m3outline
- font.pointSize: Appearance.font.size.small
- }
+ StyledText {
+ text: root.device?.bonded ? qsTr("Yes") : qsTr("No")
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
- StyledText {
- Layout.topMargin: Appearance.spacing.normal
- text: qsTr("System name")
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("System name")
+ }
- StyledText {
- text: root.device?.deviceName ?? ""
- color: Colours.palette.m3outline
- font.pointSize: Appearance.font.size.small
+ StyledText {
+ text: root.device?.deviceName ?? ""
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
+ }
+ }
}
}
- }
- }
+ ]
}
+ }
+ // FAB Menu (positioned absolutely relative to flickable)
ColumnLayout {
anchors.right: fabRoot.right
anchors.bottom: fabRoot.top
diff --git a/modules/controlcenter/bluetooth/DeviceList.qml b/modules/controlcenter/bluetooth/DeviceList.qml
index b3db236..b978a2d 100644
--- a/modules/controlcenter/bluetooth/DeviceList.qml
+++ b/modules/controlcenter/bluetooth/DeviceList.qml
@@ -48,8 +48,12 @@ DeviceList {
toggled: Bluetooth.defaultAdapter?.enabled ?? false
icon: "power"
accent: "Tertiary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Toggle Bluetooth")
- function onClicked(): void {
+ onClicked: {
const adapter = Bluetooth.defaultAdapter;
if (adapter)
adapter.enabled = !adapter.enabled;
@@ -60,8 +64,12 @@ DeviceList {
toggled: Bluetooth.defaultAdapter?.discoverable ?? false
icon: root.smallDiscoverable ? "group_search" : ""
label: root.smallDiscoverable ? "" : qsTr("Discoverable")
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Make discoverable")
- function onClicked(): void {
+ onClicked: {
const adapter = Bluetooth.defaultAdapter;
if (adapter)
adapter.discoverable = !adapter.discoverable;
@@ -72,8 +80,12 @@ DeviceList {
toggled: Bluetooth.defaultAdapter?.pairable ?? false
icon: "missing_controller"
label: root.smallPairable ? "" : qsTr("Pairable")
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Make pairable")
- function onClicked(): void {
+ onClicked: {
const adapter = Bluetooth.defaultAdapter;
if (adapter)
adapter.pairable = !adapter.pairable;
@@ -81,11 +93,31 @@ DeviceList {
}
ToggleButton {
+ toggled: Bluetooth.defaultAdapter?.discovering ?? false
+ icon: "bluetooth_searching"
+ accent: "Secondary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Scan for devices")
+
+ onClicked: {
+ const adapter = Bluetooth.defaultAdapter;
+ if (adapter)
+ adapter.discovering = !adapter.discovering;
+ }
+ }
+
+ ToggleButton {
toggled: !root.session.bt.active
icon: "settings"
accent: "Primary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Bluetooth settings")
- function onClicked(): void {
+ onClicked: {
if (root.session.bt.active)
root.session.bt.active = null;
else {
@@ -96,47 +128,6 @@ DeviceList {
}
}
- titleSuffix: Component {
- RowLayout {
- spacing: Appearance.spacing.normal
-
- Item {
- Layout.fillWidth: true
- }
-
- StyledRect {
- implicitWidth: implicitHeight
- implicitHeight: scanIcon.implicitHeight + Appearance.padding.normal * 2
-
- radius: Bluetooth.defaultAdapter?.discovering ? Appearance.rounding.normal : implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
- color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer
-
- StateLayer {
- color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer
-
- function onClicked(): void {
- const adapter = Bluetooth.defaultAdapter;
- if (adapter)
- adapter.discovering = !adapter.discovering;
- }
- }
-
- MaterialIcon {
- id: scanIcon
-
- anchors.centerIn: parent
- animate: true
- text: "bluetooth_searching"
- color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer
- fill: Bluetooth.defaultAdapter?.discovering ? 1 : 0
- }
-
- Behavior on radius {
- Anim {}
- }
- }
- }
- }
delegate: Component {
StyledRect {
@@ -146,8 +137,7 @@ DeviceList {
readonly property bool loading: modelData && (modelData.state === BluetoothDeviceState.Connecting || modelData.state === BluetoothDeviceState.Disconnecting)
readonly property bool connected: modelData && modelData.state === BluetoothDeviceState.Connected
- anchors.left: parent.left
- anchors.right: parent.right
+ width: ListView.view ? ListView.view.width : undefined
implicitHeight: deviceInner.implicitHeight + Appearance.padding.normal * 2
color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.activeItem === modelData ? Colours.tPalette.m3surfaceContainer.a : 0)
diff --git a/modules/controlcenter/components/DeviceDetails.qml b/modules/controlcenter/components/DeviceDetails.qml
index 256e689..eef0aae 100644
--- a/modules/controlcenter/components/DeviceDetails.qml
+++ b/modules/controlcenter/components/DeviceDetails.qml
@@ -90,6 +90,8 @@ Item {
model: root.sections
Loader {
+ required property Component modelData
+
Layout.fillWidth: true
sourceComponent: modelData
}
diff --git a/modules/controlcenter/components/DeviceList.qml b/modules/controlcenter/components/DeviceList.qml
index f8473ff..bf7126f 100644
--- a/modules/controlcenter/components/DeviceList.qml
+++ b/modules/controlcenter/components/DeviceList.qml
@@ -6,6 +6,7 @@ import qs.components.controls
import qs.components.containers
import qs.services
import qs.config
+import Quickshell
import QtQuick
import QtQuick.Layouts
@@ -55,6 +56,7 @@ ColumnLayout {
property var activeItem: null
property Component headerComponent: null
property Component titleSuffix: null
+ property bool showHeader: true
signal itemSelected(var item)
@@ -66,7 +68,7 @@ ColumnLayout {
Layout.fillWidth: true
sourceComponent: root.headerComponent
- visible: root.headerComponent !== null
+ visible: root.headerComponent !== null && root.showHeader
}
// Title and description row
@@ -109,17 +111,15 @@ ColumnLayout {
id: view
Layout.fillWidth: true
- Layout.fillHeight: true
+ // Use contentHeight to show all items without estimation
+ implicitHeight: contentHeight
model: root.model
delegate: root.delegate
spacing: Appearance.spacing.small / 2
- clip: true
-
- StyledScrollBar.vertical: StyledScrollBar {
- flickable: view
- }
+ interactive: false // Disable individual scrolling - parent pane handles it
+ clip: false // Don't clip - let parent handle scrolling
}
}
diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml
index cf965e8..803d7e0 100644
--- a/modules/controlcenter/launcher/LauncherPane.qml
+++ b/modules/controlcenter/launcher/LauncherPane.qml
@@ -183,6 +183,10 @@ Item {
toggled: !root.session.launcher.active
icon: "settings"
accent: "Primary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Launcher settings")
onClicked: {
if (root.session.launcher.active) {
diff --git a/modules/controlcenter/network/EthernetDetails.qml b/modules/controlcenter/network/EthernetDetails.qml
index 7c2534a..ad078ec 100644
--- a/modules/controlcenter/network/EthernetDetails.qml
+++ b/modules/controlcenter/network/EthernetDetails.qml
@@ -1,6 +1,7 @@
pragma ComponentBehavior: Bound
import ".."
+import "../components"
import qs.components
import qs.components.controls
import qs.components.effects
@@ -10,99 +11,108 @@ import qs.config
import QtQuick
import QtQuick.Layouts
-Item {
+DeviceDetails {
id: root
required property Session session
- readonly property var device: session.ethernet.active
+ readonly property var ethernetDevice: session.ethernet.active
- implicitWidth: layout.implicitWidth
- implicitHeight: layout.implicitHeight
+ device: ethernetDevice
Component.onCompleted: {
- if (device && device.interface) {
- Nmcli.getEthernetDeviceDetails(device.interface, () => {});
+ if (ethernetDevice && ethernetDevice.interface) {
+ Nmcli.getEthernetDeviceDetails(ethernetDevice.interface, () => {});
}
}
- onDeviceChanged: {
- if (device && device.interface) {
- Nmcli.getEthernetDeviceDetails(device.interface, () => {});
+ onEthernetDeviceChanged: {
+ if (ethernetDevice && ethernetDevice.interface) {
+ Nmcli.getEthernetDeviceDetails(ethernetDevice.interface, () => {});
} else {
Nmcli.ethernetDeviceDetails = null;
}
}
- ColumnLayout {
- id: layout
+ headerComponent: Component {
+ ConnectionHeader {
+ icon: "cable"
+ title: root.ethernetDevice?.interface ?? qsTr("Unknown")
+ }
+ }
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- spacing: Appearance.spacing.normal
+ sections: [
+ Component {
+ ColumnLayout {
+ spacing: Appearance.spacing.normal
- ConnectionHeader {
- icon: "cable"
- title: root.device?.interface ?? qsTr("Unknown")
- }
-
- SectionHeader {
- title: qsTr("Connection status")
- description: qsTr("Connection settings for this device")
- }
+ SectionHeader {
+ title: qsTr("Connection status")
+ description: qsTr("Connection settings for this device")
+ }
- SectionContainer {
- ToggleRow {
- label: qsTr("Connected")
- checked: root.device?.connected ?? false
- toggle.onToggled: {
- if (checked) {
- Nmcli.connectEthernet(root.device?.connection || "", root.device?.interface || "", () => {});
- } else {
- if (root.device?.connection) {
- Nmcli.disconnectEthernet(root.device.connection, () => {});
+ SectionContainer {
+ ToggleRow {
+ label: qsTr("Connected")
+ checked: root.ethernetDevice?.connected ?? false
+ toggle.onToggled: {
+ if (checked) {
+ Nmcli.connectEthernet(root.ethernetDevice?.connection || "", root.ethernetDevice?.interface || "", () => {});
+ } else {
+ if (root.ethernetDevice?.connection) {
+ Nmcli.disconnectEthernet(root.ethernetDevice.connection, () => {});
+ }
}
}
}
}
}
+ },
+ Component {
+ ColumnLayout {
+ spacing: Appearance.spacing.normal
- SectionHeader {
- title: qsTr("Device properties")
- description: qsTr("Additional information")
- }
+ SectionHeader {
+ title: qsTr("Device properties")
+ description: qsTr("Additional information")
+ }
- SectionContainer {
- contentSpacing: Appearance.spacing.small / 2
+ SectionContainer {
+ contentSpacing: Appearance.spacing.small / 2
- PropertyRow {
- label: qsTr("Interface")
- value: root.device?.interface ?? qsTr("Unknown")
- }
+ PropertyRow {
+ label: qsTr("Interface")
+ value: root.ethernetDevice?.interface ?? qsTr("Unknown")
+ }
- PropertyRow {
- showTopMargin: true
- label: qsTr("Connection")
- value: root.device?.connection || qsTr("Not connected")
- }
+ PropertyRow {
+ showTopMargin: true
+ label: qsTr("Connection")
+ value: root.ethernetDevice?.connection || qsTr("Not connected")
+ }
- PropertyRow {
- showTopMargin: true
- label: qsTr("State")
- value: root.device?.state ?? qsTr("Unknown")
+ PropertyRow {
+ showTopMargin: true
+ label: qsTr("State")
+ value: root.ethernetDevice?.state ?? qsTr("Unknown")
+ }
}
}
+ },
+ Component {
+ ColumnLayout {
+ spacing: Appearance.spacing.normal
- SectionHeader {
- title: qsTr("Connection information")
- description: qsTr("Network connection details")
- }
+ SectionHeader {
+ title: qsTr("Connection information")
+ description: qsTr("Network connection details")
+ }
- SectionContainer {
- ConnectionInfoSection {
- deviceDetails: Nmcli.ethernetDeviceDetails
+ SectionContainer {
+ ConnectionInfoSection {
+ deviceDetails: Nmcli.ethernetDeviceDetails
+ }
}
}
- }
-
-} \ No newline at end of file
+ }
+ ]
+}
diff --git a/modules/controlcenter/network/EthernetList.qml b/modules/controlcenter/network/EthernetList.qml
index 03bd37e..ea3ece5 100644
--- a/modules/controlcenter/network/EthernetList.qml
+++ b/modules/controlcenter/network/EthernetList.qml
@@ -39,6 +39,9 @@ DeviceList {
toggled: !root.session.ethernet.active
icon: "settings"
accent: "Primary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
onClicked: {
if (root.session.ethernet.active)
@@ -55,8 +58,7 @@ DeviceList {
StyledRect {
required property var modelData
- anchors.left: parent.left
- anchors.right: parent.right
+ width: ListView.view ? ListView.view.width : undefined
color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.activeItem === modelData ? Colours.tPalette.m3surfaceContainer.a : 0)
radius: Appearance.rounding.normal
diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml
index d76e8f5..e28d35c 100644
--- a/modules/controlcenter/network/NetworkingPane.qml
+++ b/modules/controlcenter/network/NetworkingPane.qml
@@ -28,23 +28,22 @@ Item {
anchors.fill: parent
leftContent: Component {
+ StyledFlickable {
+ id: leftFlickable
- StyledFlickable {
- id: leftFlickable
+ flickableDirection: Flickable.VerticalFlick
+ contentHeight: leftContent.height
- flickableDirection: Flickable.VerticalFlick
- contentHeight: leftContent.height
-
- StyledScrollBar.vertical: StyledScrollBar {
- flickable: leftFlickable
- }
+ StyledScrollBar.vertical: StyledScrollBar {
+ flickable: leftFlickable
+ }
- ColumnLayout {
- id: leftContent
+ ColumnLayout {
+ id: leftContent
- anchors.left: parent.left
- anchors.right: parent.right
- spacing: Appearance.spacing.normal
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: Appearance.spacing.normal
// Network header above the collapsible sections
RowLayout {
@@ -65,6 +64,10 @@ Item {
toggled: Nmcli.wifiEnabled
icon: "wifi"
accent: "Tertiary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Toggle WiFi")
onClicked: {
Nmcli.toggleWifi(null);
@@ -75,6 +78,10 @@ Item {
toggled: Nmcli.scanning
icon: "wifi_find"
accent: "Secondary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Scan for networks")
onClicked: {
Nmcli.rescanWifi();
@@ -85,6 +92,10 @@ Item {
toggled: !root.session.ethernet.active && !root.session.network.active
icon: "settings"
accent: "Primary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Network settings")
onClicked: {
if (root.session.ethernet.active || root.session.network.active) {
@@ -109,127 +120,12 @@ Item {
title: qsTr("Ethernet")
expanded: true
- ColumnLayout {
+ Loader {
Layout.fillWidth: true
- spacing: Appearance.spacing.small
-
- RowLayout {
- Layout.fillWidth: true
- spacing: Appearance.spacing.small
-
- StyledText {
- text: qsTr("Devices (%1)").arg(Nmcli.ethernetDevices.length)
- font.pointSize: Appearance.font.size.normal
- font.weight: 500
- }
- }
-
- StyledText {
- Layout.fillWidth: true
- text: qsTr("All available ethernet devices")
- color: Colours.palette.m3outline
- }
-
- Repeater {
- id: ethernetRepeater
-
- Layout.fillWidth: true
- model: Nmcli.ethernetDevices
-
- delegate: StyledRect {
- required property var modelData
-
- Layout.fillWidth: true
-
- color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.ethernet.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0)
- radius: Appearance.rounding.normal
-
- StateLayer {
- function onClicked(): void {
- root.session.network.active = null;
- root.session.ethernet.active = modelData;
- }
- }
-
- RowLayout {
- id: rowLayout
-
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.verticalCenter: parent.verticalCenter
- anchors.margins: Appearance.padding.normal
-
- spacing: Appearance.spacing.normal
-
- StyledRect {
- implicitWidth: implicitHeight
- implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2
-
- radius: Appearance.rounding.normal
- color: modelData.connected ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh
-
- MaterialIcon {
- id: icon
-
- anchors.centerIn: parent
- text: "cable"
- font.pointSize: Appearance.font.size.large
- fill: modelData.connected ? 1 : 0
- color: modelData.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface
- }
- }
-
- ColumnLayout {
- Layout.fillWidth: true
-
- spacing: 0
-
- StyledText {
- Layout.fillWidth: true
- elide: Text.ElideRight
- maximumLineCount: 1
-
- text: modelData.interface || qsTr("Unknown")
- }
-
- StyledText {
- Layout.fillWidth: true
- text: modelData.connected ? qsTr("Connected") : qsTr("Disconnected")
- color: modelData.connected ? Colours.palette.m3primary : Colours.palette.m3outline
- font.pointSize: Appearance.font.size.small
- font.weight: modelData.connected ? 500 : 400
- elide: Text.ElideRight
- }
- }
-
- StyledRect {
- implicitWidth: implicitHeight
- implicitHeight: connectIcon.implicitHeight + Appearance.padding.smaller * 2
-
- radius: Appearance.rounding.full
- color: Qt.alpha(Colours.palette.m3primaryContainer, modelData.connected ? 1 : 0)
-
- StateLayer {
- function onClicked(): void {
- if (modelData.connected && modelData.connection) {
- Nmcli.disconnectEthernet(modelData.connection, () => {});
- } else {
- Nmcli.connectEthernet(modelData.connection || "", modelData.interface || "", () => {});
- }
- }
- }
-
- MaterialIcon {
- id: connectIcon
-
- anchors.centerIn: parent
- text: modelData.connected ? "link_off" : "link"
- color: modelData.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface
- }
- }
- }
-
- implicitHeight: rowLayout.implicitHeight + Appearance.padding.normal * 2
+ sourceComponent: Component {
+ EthernetList {
+ session: root.session
+ showHeader: false
}
}
}
@@ -242,195 +138,12 @@ Item {
title: qsTr("Wireless")
expanded: true
- ColumnLayout {
+ Loader {
Layout.fillWidth: true
- spacing: Appearance.spacing.small
-
- RowLayout {
- Layout.fillWidth: true
- spacing: Appearance.spacing.small
-
- StyledText {
- text: qsTr("Networks (%1)").arg(Nmcli.networks.length)
- font.pointSize: Appearance.font.size.normal
- font.weight: 500
- }
-
- StyledText {
- visible: Nmcli.scanning
- text: qsTr("Scanning...")
- color: Colours.palette.m3primary
- font.pointSize: Appearance.font.size.small
- }
- }
-
- StyledText {
- Layout.fillWidth: true
- text: qsTr("All available WiFi networks")
- color: Colours.palette.m3outline
- }
-
- Repeater {
- id: wirelessRepeater
-
- Layout.fillWidth: true
- model: ScriptModel {
- values: [...Nmcli.networks].sort((a, b) => {
- // Put active/connected network first
- if (a.active !== b.active)
- return b.active - a.active;
- // Then sort by signal strength
- return b.strength - a.strength;
- })
- }
-
- delegate: StyledRect {
- required property var modelData
-
- Layout.fillWidth: true
-
- color: Qt.alpha(Colours.tPalette.m3surfaceContainer, (modelData && root.session.network.active === modelData) ? Colours.tPalette.m3surfaceContainer.a : 0)
- radius: Appearance.rounding.normal
-
- StateLayer {
- function onClicked(): void {
- if (!modelData) {
- return;
- }
- root.session.ethernet.active = null;
- root.session.network.active = modelData;
- // Check if we need to refresh saved connections when selecting a network
- if (modelData.ssid) {
- checkSavedProfileForNetwork(modelData.ssid);
- }
- }
- }
-
- RowLayout {
- id: wirelessRowLayout
-
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.verticalCenter: parent.verticalCenter
- anchors.margins: Appearance.padding.normal
-
- spacing: Appearance.spacing.normal
-
- StyledRect {
- implicitWidth: implicitHeight
- implicitHeight: wirelessIcon.implicitHeight + Appearance.padding.normal * 2
-
- radius: Appearance.rounding.normal
- color: (modelData && modelData.active) ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh
-
- MaterialIcon {
- id: wirelessIcon
-
- anchors.centerIn: parent
- text: Icons.getNetworkIcon(modelData && modelData.strength !== undefined ? modelData.strength : 0)
- font.pointSize: Appearance.font.size.large
- fill: (modelData && modelData.active) ? 1 : 0
- color: (modelData && modelData.active) ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface
- }
-
- StyledRect {
- id: lockBadge
-
- visible: modelData && modelData.isSecure
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- anchors.margins: -Appearance.padding.smaller / 2
-
- implicitWidth: lockIconSize + Appearance.padding.smaller
- implicitHeight: lockIconSize + Appearance.padding.smaller
- radius: Appearance.rounding.full
- color: Colours.palette.m3secondaryContainer
-
- readonly property real lockIconSize: lockIcon.implicitWidth
-
- Elevation {
- anchors.fill: parent
- radius: parent.radius
- z: -1
- level: 2
- }
-
- MaterialIcon {
- id: lockIcon
-
- anchors.centerIn: parent
- text: "lock"
- font.pointSize: Appearance.font.size.small
- fill: 1
- color: Colours.palette.m3onSurface
- }
- }
- }
-
- ColumnLayout {
- Layout.fillWidth: true
-
- spacing: 0
-
- StyledText {
- Layout.fillWidth: true
- elide: Text.ElideRight
- maximumLineCount: 1
-
- text: (modelData && modelData.ssid) ? modelData.ssid : qsTr("Unknown")
- }
-
- RowLayout {
- Layout.fillWidth: true
- spacing: Appearance.spacing.smaller
-
- StyledText {
- Layout.fillWidth: true
- text: {
- if (!modelData) return qsTr("Open");
- if (modelData.active) return qsTr("Connected");
- if (modelData.isSecure && modelData.security && modelData.security.length > 0) {
- return modelData.security;
- }
- if (modelData.isSecure) return qsTr("Secured");
- return qsTr("Open");
- }
- color: (modelData && modelData.active) ? Colours.palette.m3primary : Colours.palette.m3outline
- font.pointSize: Appearance.font.size.small
- font.weight: (modelData && modelData.active) ? 500 : 400
- elide: Text.ElideRight
- }
- }
- }
-
- StyledRect {
- implicitWidth: implicitHeight
- implicitHeight: wirelessConnectIcon.implicitHeight + Appearance.padding.smaller * 2
-
- radius: Appearance.rounding.full
- color: Qt.alpha(Colours.palette.m3primaryContainer, (modelData && modelData.active) ? 1 : 0)
-
- StateLayer {
- function onClicked(): void {
- if (modelData && modelData.active) {
- Nmcli.disconnectFromNetwork();
- } else if (modelData) {
- NetworkConnection.handleConnect(modelData, root.session, null);
- }
- }
- }
-
- MaterialIcon {
- id: wirelessConnectIcon
-
- anchors.centerIn: parent
- text: (modelData && modelData.active) ? "link_off" : "link"
- color: (modelData && modelData.active) ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface
- }
- }
- }
-
- implicitHeight: wirelessRowLayout.implicitHeight + Appearance.padding.normal * 2
+ sourceComponent: Component {
+ WirelessList {
+ session: root.session
+ showHeader: false
}
}
}
@@ -443,16 +156,17 @@ Item {
Item {
id: rightPaneItem
- // Right pane - networking details/settings
property var ethernetPane: root.session.ethernet.active
property var wirelessPane: root.session.network.active
property var pane: ethernetPane || wirelessPane
property string paneId: ethernetPane ? ("eth:" + (ethernetPane.interface || "")) : (wirelessPane ? ("wifi:" + (wirelessPane.ssid || wirelessPane.bssid || "")) : "settings")
- property Component targetComponent: settings
- property Component nextComponent: settings
+ property Component targetComponent: settingsComponent
+ property Component nextComponent: settingsComponent
function getComponentForPane() {
- return pane ? (ethernetPane ? ethernetDetails : wirelessDetails) : settings;
+ if (ethernetPane) return ethernetDetailsComponent;
+ if (wirelessPane) return wirelessDetailsComponent;
+ return settingsComponent;
}
Component.onCompleted: {
@@ -507,7 +221,7 @@ Item {
}
Component {
- id: settings
+ id: settingsComponent
StyledFlickable {
id: settingsFlickable
@@ -530,7 +244,7 @@ Item {
}
Component {
- id: ethernetDetails
+ id: ethernetDetailsComponent
StyledFlickable {
id: ethernetFlickable
@@ -553,7 +267,7 @@ Item {
}
Component {
- id: wirelessDetails
+ id: wirelessDetailsComponent
StyledFlickable {
id: wirelessFlickable
@@ -580,11 +294,4 @@ Item {
session: root.session
z: 1000
}
-
- function checkSavedProfileForNetwork(ssid: string): void {
- if (ssid && ssid.length > 0) {
- Nmcli.loadSavedConnections(() => {});
- }
- }
}
-
diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml
index 57c06c8..7f6a4aa 100644
--- a/modules/controlcenter/network/WirelessDetails.qml
+++ b/modules/controlcenter/network/WirelessDetails.qml
@@ -1,6 +1,7 @@
pragma ComponentBehavior: Bound
import ".."
+import "../components"
import "."
import qs.components
import qs.components.controls
@@ -12,14 +13,13 @@ import qs.utils
import QtQuick
import QtQuick.Layouts
-Item {
+DeviceDetails {
id: root
required property Session session
readonly property var network: session.network.active
- implicitWidth: layout.implicitWidth
- implicitHeight: layout.implicitHeight
+ device: network
Component.onCompleted: {
updateDeviceDetails();
@@ -102,110 +102,120 @@ Item {
}
}
- ColumnLayout {
- id: layout
-
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- spacing: Appearance.spacing.normal
+ headerComponent: Component {
+ ConnectionHeader {
+ icon: root.network?.isSecure ? "lock" : "wifi"
+ title: root.network?.ssid ?? qsTr("Unknown")
+ }
+ }
- ConnectionHeader {
- icon: root.network?.isSecure ? "lock" : "wifi"
- title: root.network?.ssid ?? qsTr("Unknown")
- }
+ sections: [
+ Component {
+ ColumnLayout {
+ spacing: Appearance.spacing.normal
- SectionHeader {
- title: qsTr("Connection status")
- description: qsTr("Connection settings for this network")
- }
+ SectionHeader {
+ title: qsTr("Connection status")
+ description: qsTr("Connection settings for this network")
+ }
- SectionContainer {
- ToggleRow {
- label: qsTr("Connected")
- checked: root.network?.active ?? false
- toggle.onToggled: {
- if (checked) {
- NetworkConnection.handleConnect(root.network, root.session, null);
- } else {
- Nmcli.disconnectFromNetwork();
+ SectionContainer {
+ ToggleRow {
+ label: qsTr("Connected")
+ checked: root.network?.active ?? false
+ toggle.onToggled: {
+ if (checked) {
+ NetworkConnection.handleConnect(root.network, root.session, null);
+ } else {
+ Nmcli.disconnectFromNetwork();
+ }
}
}
- }
- TextButton {
- Layout.fillWidth: true
- Layout.topMargin: Appearance.spacing.normal
- Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2
- visible: {
- if (!root.network || !root.network.ssid) {
- return false;
+ TextButton {
+ Layout.fillWidth: true
+ Layout.topMargin: Appearance.spacing.normal
+ Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2
+ visible: {
+ if (!root.network || !root.network.ssid) {
+ return false;
+ }
+ return Nmcli.hasSavedProfile(root.network.ssid);
}
- return Nmcli.hasSavedProfile(root.network.ssid);
- }
- inactiveColour: Colours.palette.m3secondaryContainer
- inactiveOnColour: Colours.palette.m3onSecondaryContainer
- text: qsTr("Forget Network")
+ inactiveColour: Colours.palette.m3secondaryContainer
+ inactiveOnColour: Colours.palette.m3onSecondaryContainer
+ text: qsTr("Forget Network")
- onClicked: {
- if (root.network && root.network.ssid) {
- if (root.network.active) {
- Nmcli.disconnectFromNetwork();
+ onClicked: {
+ if (root.network && root.network.ssid) {
+ if (root.network.active) {
+ Nmcli.disconnectFromNetwork();
+ }
+ Nmcli.forgetNetwork(root.network.ssid);
}
- Nmcli.forgetNetwork(root.network.ssid);
}
}
}
}
+ },
+ Component {
+ ColumnLayout {
+ spacing: Appearance.spacing.normal
- SectionHeader {
- title: qsTr("Network properties")
- description: qsTr("Additional information")
- }
+ SectionHeader {
+ title: qsTr("Network properties")
+ description: qsTr("Additional information")
+ }
- SectionContainer {
- contentSpacing: Appearance.spacing.small / 2
+ SectionContainer {
+ contentSpacing: Appearance.spacing.small / 2
- PropertyRow {
- label: qsTr("SSID")
- value: root.network?.ssid ?? qsTr("Unknown")
- }
+ PropertyRow {
+ label: qsTr("SSID")
+ value: root.network?.ssid ?? qsTr("Unknown")
+ }
- PropertyRow {
- showTopMargin: true
- label: qsTr("BSSID")
- value: root.network?.bssid ?? qsTr("Unknown")
- }
+ PropertyRow {
+ showTopMargin: true
+ label: qsTr("BSSID")
+ value: root.network?.bssid ?? qsTr("Unknown")
+ }
- PropertyRow {
- showTopMargin: true
- label: qsTr("Signal strength")
- value: root.network ? qsTr("%1%").arg(root.network.strength) : qsTr("N/A")
- }
+ PropertyRow {
+ showTopMargin: true
+ label: qsTr("Signal strength")
+ value: root.network ? qsTr("%1%").arg(root.network.strength) : qsTr("N/A")
+ }
- PropertyRow {
- showTopMargin: true
- label: qsTr("Frequency")
- value: root.network ? qsTr("%1 MHz").arg(root.network.frequency) : qsTr("N/A")
- }
+ PropertyRow {
+ showTopMargin: true
+ label: qsTr("Frequency")
+ value: root.network ? qsTr("%1 MHz").arg(root.network.frequency) : qsTr("N/A")
+ }
- PropertyRow {
- showTopMargin: true
- label: qsTr("Security")
- value: root.network ? (root.network.isSecure ? root.network.security : qsTr("Open")) : qsTr("N/A")
+ PropertyRow {
+ showTopMargin: true
+ label: qsTr("Security")
+ value: root.network ? (root.network.isSecure ? root.network.security : qsTr("Open")) : qsTr("N/A")
+ }
}
}
+ },
+ Component {
+ ColumnLayout {
+ spacing: Appearance.spacing.normal
- SectionHeader {
- title: qsTr("Connection information")
- description: qsTr("Network connection details")
- }
+ SectionHeader {
+ title: qsTr("Connection information")
+ description: qsTr("Network connection details")
+ }
- SectionContainer {
- ConnectionInfoSection {
- deviceDetails: Nmcli.wirelessDeviceDetails
+ SectionContainer {
+ ConnectionInfoSection {
+ deviceDetails: Nmcli.wirelessDeviceDetails
+ }
}
}
- }
-
+ }
+ ]
}
diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml
index 2f0288f..4726712 100644
--- a/modules/controlcenter/network/WirelessList.qml
+++ b/modules/controlcenter/network/WirelessList.qml
@@ -9,6 +9,7 @@ import qs.components.containers
import qs.services
import qs.config
import qs.utils
+import Quickshell
import QtQuick
import QtQuick.Layouts
@@ -58,6 +59,9 @@ DeviceList {
toggled: Nmcli.wifiEnabled
icon: "wifi"
accent: "Tertiary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
onClicked: {
Nmcli.toggleWifi(null);
@@ -68,6 +72,9 @@ DeviceList {
toggled: Nmcli.scanning
icon: "wifi_find"
accent: "Secondary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
onClicked: {
Nmcli.rescanWifi();
@@ -78,6 +85,9 @@ DeviceList {
toggled: !root.session.network.active
icon: "settings"
accent: "Primary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
onClicked: {
if (root.session.network.active)
@@ -94,8 +104,7 @@ DeviceList {
StyledRect {
required property var modelData
- anchors.left: parent.left
- anchors.right: parent.right
+ width: ListView.view ? ListView.view.width : undefined
color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.activeItem === modelData ? Colours.tPalette.m3surfaceContainer.a : 0)
radius: Appearance.rounding.normal