refactor: SplitPaneWithDetails integrated

This commit is contained in:
ATMDA 2025-11-19 19:25:26 -05:00
parent 70ec8cea65
commit 2d26626643
4 changed files with 192 additions and 278 deletions

View file

@ -2,87 +2,39 @@ pragma ComponentBehavior: Bound
import ".." import ".."
import "../components" import "../components"
import qs.components
import qs.components.controls import qs.components.controls
import qs.components.effects
import qs.components.containers import qs.components.containers
import qs.config import qs.config
import Quickshell.Widgets import Quickshell.Widgets
import Quickshell.Bluetooth import Quickshell.Bluetooth
import QtQuick import QtQuick
import QtQuick.Layouts
Item { SplitPaneWithDetails {
id: root id: root
required property Session session required property Session session
anchors.fill: parent anchors.fill: parent
SplitPaneLayout { activeItem: session.bt.active
anchors.fill: parent paneIdGenerator: function(item) {
return item ? (item.address || "") : "";
}
leftContent: Component { leftContent: Component {
DeviceList { DeviceList {
anchors.fill: parent session: root.session
session: root.session
}
}
rightContent: Component {
Item {
id: rightBtPane
property BluetoothDevice pane: root.session.bt.active
property string paneId: pane ? (pane.address || "") : ""
property Component targetComponent: settings
property Component nextComponent: settings
function getComponentForPane() {
return pane ? details : settings;
}
Component.onCompleted: {
targetComponent = getComponentForPane();
nextComponent = targetComponent;
}
Loader {
id: rightLoader
anchors.fill: parent
asynchronous: true
sourceComponent: rightBtPane.targetComponent
}
Behavior on paneId {
PaneTransition {
target: rightLoader
propertyActions: [
PropertyAction {
target: rightBtPane
property: "targetComponent"
value: rightBtPane.nextComponent
}
]
}
}
Connections {
target: root.session.bt
function onActiveChanged() {
rightBtPane.pane = root.session.bt.active;
rightBtPane.nextComponent = rightBtPane.getComponentForPane();
rightBtPane.paneId = pane ? (pane.address || "") : "";
}
}
}
} }
} }
Component { rightDetailsComponent: Component {
id: settings Details {
session: root.session
}
}
rightSettingsComponent: Component {
StyledFlickable { StyledFlickable {
id: settingsFlickable id: settingsFlickable
flickableDirection: Flickable.VerticalFlick flickableDirection: Flickable.VerticalFlick
@ -102,12 +54,4 @@ Item {
} }
} }
} }
Component {
id: details
Details {
session: root.session
}
}
} }

View file

@ -0,0 +1,130 @@
pragma ComponentBehavior: Bound
import ".."
import qs.components
import qs.components.effects
import qs.components.containers
import qs.config
import Quickshell.Widgets
import QtQuick
import QtQuick.Layouts
/**
* SplitPaneWithDetails
*
* A reusable component that provides a split-pane layout with a list on the left
* and a details/settings view on the right. The right pane automatically switches
* between details and settings views based on whether an item is selected.
*
* This component eliminates duplication across WirelessPane, EthernetPane, and BtPane
* by providing a standardized pattern for split-pane layouts with transition animations.
*
* Usage:
* ```qml
* SplitPaneWithDetails {
* activeItem: root.session.network.active
* leftContent: Component {
* WirelessList {
* session: root.session
* }
* }
* rightDetailsComponent: Component {
* WirelessDetails {
* session: root.session
* }
* }
* rightSettingsComponent: Component {
* StyledFlickable {
* WirelessSettings {
* session: root.session
* }
* }
* }
* paneIdGenerator: (item) => item ? (item.ssid || item.bssid || "") : ""
* }
* ```
*/
Item {
id: root
required property Component leftContent
required property Component rightDetailsComponent
required property Component rightSettingsComponent
property var activeItem: null
property var paneIdGenerator: function(item) { return item ? String(item) : ""; }
// Optional: Additional component to overlay on top (e.g., password dialogs)
property Component overlayComponent: null
SplitPaneLayout {
id: splitLayout
anchors.fill: parent
leftContent: root.leftContent
rightContent: Component {
Item {
id: rightPaneItem
property var pane: root.activeItem
property string paneId: root.paneIdGenerator(pane)
property Component targetComponent: root.rightSettingsComponent
property Component nextComponent: root.rightSettingsComponent
function getComponentForPane() {
return pane ? root.rightDetailsComponent : root.rightSettingsComponent;
}
Component.onCompleted: {
targetComponent = getComponentForPane();
nextComponent = targetComponent;
}
Loader {
id: rightLoader
anchors.fill: parent
opacity: 1
scale: 1
transformOrigin: Item.Center
clip: false
asynchronous: true
sourceComponent: rightPaneItem.targetComponent
}
Behavior on paneId {
PaneTransition {
target: rightLoader
propertyActions: [
PropertyAction {
target: rightPaneItem
property: "targetComponent"
value: rightPaneItem.nextComponent
}
]
}
}
onPaneChanged: {
nextComponent = getComponentForPane();
paneId = root.paneIdGenerator(pane);
}
}
}
}
// Overlay component (e.g., password dialogs)
Loader {
id: overlayLoader
anchors.fill: parent
z: 1000
sourceComponent: root.overlayComponent
active: root.overlayComponent !== null
}
}

View file

@ -1,131 +1,50 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import ".." import ".."
import "../components"
import qs.components import qs.components
import qs.components.effects
import qs.components.containers import qs.components.containers
import qs.config import qs.config
import Quickshell.Widgets import Quickshell.Widgets
import QtQuick import QtQuick
import QtQuick.Layouts
RowLayout { SplitPaneWithDetails {
id: root id: root
required property Session session required property Session session
anchors.fill: parent anchors.fill: parent
spacing: 0 activeItem: session.ethernet.active
paneIdGenerator: function(item) {
Item { return item ? (item.interface || "") : "";
Layout.preferredWidth: Math.floor(parent.width * 0.4) }
Layout.minimumWidth: 420
Layout.fillHeight: true
leftContent: Component {
EthernetList { EthernetList {
anchors.fill: parent
anchors.margins: Appearance.padding.large + Appearance.padding.normal
anchors.leftMargin: Appearance.padding.large
anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2
session: root.session session: root.session
} }
InnerBorder {
leftThickness: 0
rightThickness: Appearance.padding.normal / 2
}
} }
Item { rightDetailsComponent: Component {
Layout.fillWidth: true EthernetDetails {
Layout.fillHeight: true session: root.session
}
}
ClippingRectangle { rightSettingsComponent: Component {
anchors.fill: parent StyledFlickable {
anchors.margins: Appearance.padding.normal flickableDirection: Flickable.VerticalFlick
anchors.leftMargin: 0 contentHeight: settingsInner.height
anchors.rightMargin: Appearance.padding.normal / 2
radius: rightBorder.innerRadius
color: "transparent"
clip: true clip: true
Loader { EthernetSettings {
id: loader id: settingsInner
property var pane: root.session.ethernet.active anchors.left: parent.left
property string paneId: pane ? (pane.interface || "") : "" anchors.right: parent.right
property Component targetComponent: settings
property Component nextComponent: settings
anchors.fill: parent
anchors.margins: Appearance.padding.large * 2
opacity: 1
scale: 1
transformOrigin: Item.Center
clip: true
asynchronous: true
sourceComponent: loader.targetComponent
Component.onCompleted: {
targetComponent = pane ? details : settings;
nextComponent = targetComponent;
}
Behavior on paneId {
PaneTransition {
target: loader
propertyActions: [
PropertyAction {
target: loader
property: "targetComponent"
value: loader.nextComponent
}
]
}
}
onPaneChanged: {
nextComponent = pane ? details : settings;
paneId = pane ? (pane.interface || "") : "";
}
}
}
InnerBorder {
id: rightBorder
leftThickness: Appearance.padding.normal / 2
}
Component {
id: settings
StyledFlickable {
flickableDirection: Flickable.VerticalFlick
contentHeight: settingsInner.height
clip: true
EthernetSettings {
id: settingsInner
anchors.left: parent.left
anchors.right: parent.right
session: root.session
}
}
}
Component {
id: details
EthernetDetails {
session: root.session session: root.session
} }
} }
} }
} }

View file

@ -1,136 +1,57 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import ".." import ".."
import "../components"
import qs.components import qs.components
import qs.components.effects
import qs.components.containers import qs.components.containers
import qs.config import qs.config
import Quickshell.Widgets import Quickshell.Widgets
import QtQuick import QtQuick
import QtQuick.Layouts
RowLayout { SplitPaneWithDetails {
id: root id: root
required property Session session required property Session session
anchors.fill: parent anchors.fill: parent
spacing: 0 activeItem: session.network.active
paneIdGenerator: function(item) {
Item { return item ? (item.ssid || item.bssid || "") : "";
Layout.preferredWidth: Math.floor(parent.width * 0.4) }
Layout.minimumWidth: 420
Layout.fillHeight: true
leftContent: Component {
WirelessList { WirelessList {
anchors.fill: parent
anchors.margins: Appearance.padding.large + Appearance.padding.normal
anchors.leftMargin: Appearance.padding.large
anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2
session: root.session session: root.session
} }
InnerBorder {
leftThickness: 0
rightThickness: Appearance.padding.normal / 2
}
} }
Item { rightDetailsComponent: Component {
Layout.fillWidth: true WirelessDetails {
Layout.fillHeight: true session: root.session
ClippingRectangle {
anchors.fill: parent
anchors.margins: Appearance.padding.normal
anchors.leftMargin: 0
anchors.rightMargin: Appearance.padding.normal / 2
radius: rightBorder.innerRadius
color: "transparent"
Loader {
id: loader
property var pane: root.session.network.active
property string paneId: pane ? (pane.ssid || pane.bssid || "") : ""
property Component targetComponent: settings
property Component nextComponent: settings
anchors.fill: parent
anchors.margins: Appearance.padding.large * 2
opacity: 1
scale: 1
transformOrigin: Item.Center
clip: false
asynchronous: true
sourceComponent: loader.targetComponent
Component.onCompleted: {
targetComponent = pane ? details : settings;
nextComponent = targetComponent;
}
Behavior on paneId {
PaneTransition {
target: loader
propertyActions: [
PropertyAction {
target: loader
property: "targetComponent"
value: loader.nextComponent
}
]
}
}
onPaneChanged: {
nextComponent = pane ? details : settings;
paneId = pane ? (pane.ssid || pane.bssid || "") : "";
}
}
} }
}
InnerBorder { rightSettingsComponent: Component {
id: rightBorder StyledFlickable {
flickableDirection: Flickable.VerticalFlick
contentHeight: settingsInner.height
clip: true
leftThickness: Appearance.padding.normal / 2 WirelessSettings {
} id: settingsInner
Component { anchors.left: parent.left
id: settings anchors.right: parent.right
StyledFlickable {
flickableDirection: Flickable.VerticalFlick
contentHeight: settingsInner.height
clip: true
WirelessSettings {
id: settingsInner
anchors.left: parent.left
anchors.right: parent.right
session: root.session
}
}
}
Component {
id: details
WirelessDetails {
session: root.session session: root.session
} }
} }
} }
WirelessPasswordDialog { overlayComponent: Component {
anchors.fill: parent WirelessPasswordDialog {
session: root.session anchors.fill: parent
z: 1000 session: root.session
}
} }
} }