refactor: SplitPaneLayout now component
This commit is contained in:
parent
50e29f8be3
commit
ad4213d45c
6 changed files with 1317 additions and 1436 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,7 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import ".."
|
||||
import "../components"
|
||||
import qs.components
|
||||
import qs.components.controls
|
||||
import qs.components.effects
|
||||
|
|
@ -11,52 +12,17 @@ import Quickshell.Widgets
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
RowLayout {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Session session
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
spacing: 0
|
||||
SplitPaneLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Item {
|
||||
id: leftAudioItem
|
||||
Layout.preferredWidth: Math.floor(parent.width * 0.4)
|
||||
Layout.minimumWidth: 420
|
||||
Layout.fillHeight: true
|
||||
|
||||
ClippingRectangle {
|
||||
id: leftAudioClippingRect
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: Appearance.padding.normal / 2
|
||||
|
||||
radius: leftAudioBorder.innerRadius
|
||||
color: "transparent"
|
||||
|
||||
Loader {
|
||||
id: leftAudioLoader
|
||||
|
||||
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
|
||||
|
||||
asynchronous: true
|
||||
sourceComponent: audioLeftContentComponent
|
||||
}
|
||||
}
|
||||
|
||||
InnerBorder {
|
||||
id: leftAudioBorder
|
||||
leftThickness: 0
|
||||
rightThickness: Appearance.padding.normal / 2
|
||||
}
|
||||
|
||||
Component {
|
||||
id: audioLeftContentComponent
|
||||
leftContent: Component {
|
||||
|
||||
StyledFlickable {
|
||||
id: leftAudioFlickable
|
||||
|
|
@ -246,349 +212,321 @@ RowLayout {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: rightAudioItem
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
ClippingRectangle {
|
||||
id: rightAudioClippingRect
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: Appearance.padding.normal / 2
|
||||
|
||||
radius: rightAudioBorder.innerRadius
|
||||
color: "transparent"
|
||||
|
||||
Loader {
|
||||
id: rightAudioLoader
|
||||
}
|
||||
|
||||
rightContent: Component {
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: Appearance.padding.large * 2
|
||||
anchors.bottomMargin: Appearance.padding.large * 2
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: 0
|
||||
|
||||
asynchronous: true
|
||||
sourceComponent: audioRightContentComponent
|
||||
}
|
||||
}
|
||||
StyledFlickable {
|
||||
id: rightAudioFlickable
|
||||
anchors.fill: parent
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: contentLayout.height
|
||||
|
||||
InnerBorder {
|
||||
id: rightAudioBorder
|
||||
leftThickness: Appearance.padding.normal / 2
|
||||
}
|
||||
|
||||
Component {
|
||||
id: audioRightContentComponent
|
||||
|
||||
StyledFlickable {
|
||||
id: rightAudioFlickable
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: contentLayout.height
|
||||
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: rightAudioFlickable
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: contentLayout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: Appearance.padding.large * 2
|
||||
anchors.rightMargin: Appearance.padding.large * 2
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
ConnectionHeader {
|
||||
icon: "volume_up"
|
||||
title: qsTr("Audio Settings")
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
title: qsTr("Output volume")
|
||||
description: qsTr("Control the volume of your output device")
|
||||
}
|
||||
|
||||
SectionContainer {
|
||||
contentSpacing: Appearance.spacing.normal
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: rightAudioFlickable
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.small
|
||||
id: contentLayout
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: Appearance.padding.large * 2
|
||||
anchors.rightMargin: Appearance.padding.large * 2
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
StyledText {
|
||||
text: qsTr("Volume")
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.weight: 500
|
||||
}
|
||||
ConnectionHeader {
|
||||
icon: "volume_up"
|
||||
title: qsTr("Audio Settings")
|
||||
}
|
||||
|
||||
Item {
|
||||
SectionHeader {
|
||||
title: qsTr("Output volume")
|
||||
description: qsTr("Control the volume of your output device")
|
||||
}
|
||||
|
||||
SectionContainer {
|
||||
contentSpacing: Appearance.spacing.normal
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
StyledRect {
|
||||
Layout.preferredWidth: 70
|
||||
implicitHeight: outputVolumeInput.implicitHeight + Appearance.padding.small * 2
|
||||
color: outputVolumeInputHover.containsMouse || outputVolumeInput.activeFocus
|
||||
? Colours.layer(Colours.palette.m3surfaceContainer, 3)
|
||||
: Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
||||
radius: Appearance.rounding.small
|
||||
border.width: 1
|
||||
border.color: outputVolumeInput.activeFocus
|
||||
? Colours.palette.m3primary
|
||||
: Qt.alpha(Colours.palette.m3outline, 0.3)
|
||||
enabled: !Audio.muted
|
||||
opacity: enabled ? 1 : 0.5
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
Behavior on color { CAnim {} }
|
||||
Behavior on border.color { CAnim {} }
|
||||
StyledText {
|
||||
text: qsTr("Volume")
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.weight: 500
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: outputVolumeInputHover
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.IBeamCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
Layout.preferredWidth: 70
|
||||
implicitHeight: outputVolumeInput.implicitHeight + Appearance.padding.small * 2
|
||||
color: outputVolumeInputHover.containsMouse || outputVolumeInput.activeFocus
|
||||
? Colours.layer(Colours.palette.m3surfaceContainer, 3)
|
||||
: Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
||||
radius: Appearance.rounding.small
|
||||
border.width: 1
|
||||
border.color: outputVolumeInput.activeFocus
|
||||
? Colours.palette.m3primary
|
||||
: Qt.alpha(Colours.palette.m3outline, 0.3)
|
||||
enabled: !Audio.muted
|
||||
opacity: enabled ? 1 : 0.5
|
||||
|
||||
Behavior on color { CAnim {} }
|
||||
Behavior on border.color { CAnim {} }
|
||||
|
||||
MouseArea {
|
||||
id: outputVolumeInputHover
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.IBeamCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
|
||||
StyledTextField {
|
||||
id: outputVolumeInput
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Appearance.padding.normal
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
validator: IntValidator { bottom: 0; top: 100 }
|
||||
enabled: !Audio.muted
|
||||
|
||||
Component.onCompleted: {
|
||||
text = Math.round(Audio.volume * 100).toString();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Audio
|
||||
function onVolumeChanged() {
|
||||
if (!outputVolumeInput.activeFocus) {
|
||||
outputVolumeInput.text = Math.round(Audio.volume * 100).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
if (activeFocus) {
|
||||
const val = parseInt(text);
|
||||
if (!isNaN(val) && val >= 0 && val <= 100) {
|
||||
Audio.setVolume(val / 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
onEditingFinished: {
|
||||
const val = parseInt(text);
|
||||
if (isNaN(val) || val < 0 || val > 100) {
|
||||
text = Math.round(Audio.volume * 100).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "%"
|
||||
color: Colours.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
opacity: Audio.muted ? 0.5 : 1
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: muteIcon.implicitHeight + Appearance.padding.normal * 2
|
||||
|
||||
radius: Appearance.rounding.normal
|
||||
color: Audio.muted ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer
|
||||
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
if (Audio.sink?.audio) {
|
||||
Audio.sink.audio.muted = !Audio.sink.audio.muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: muteIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: Audio.muted ? "volume_off" : "volume_up"
|
||||
color: Audio.muted ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledTextField {
|
||||
id: outputVolumeInput
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Appearance.padding.normal
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
validator: IntValidator { bottom: 0; top: 100 }
|
||||
StyledSlider {
|
||||
id: outputVolumeSlider
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Appearance.padding.normal * 3
|
||||
|
||||
value: Audio.volume
|
||||
enabled: !Audio.muted
|
||||
|
||||
Component.onCompleted: {
|
||||
text = Math.round(Audio.volume * 100).toString();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Audio
|
||||
function onVolumeChanged() {
|
||||
if (!outputVolumeInput.activeFocus) {
|
||||
outputVolumeInput.text = Math.round(Audio.volume * 100).toString();
|
||||
}
|
||||
opacity: enabled ? 1 : 0.5
|
||||
onMoved: {
|
||||
Audio.setVolume(value);
|
||||
if (!outputVolumeInput.activeFocus) {
|
||||
outputVolumeInput.text = Math.round(value * 100).toString();
|
||||
}
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
if (activeFocus) {
|
||||
const val = parseInt(text);
|
||||
if (!isNaN(val) && val >= 0 && val <= 100) {
|
||||
Audio.setVolume(val / 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
onEditingFinished: {
|
||||
const val = parseInt(text);
|
||||
if (isNaN(val) || val < 0 || val > 100) {
|
||||
text = Math.round(Audio.volume * 100).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "%"
|
||||
color: Colours.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
opacity: Audio.muted ? 0.5 : 1
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: muteIcon.implicitHeight + Appearance.padding.normal * 2
|
||||
|
||||
radius: Appearance.rounding.normal
|
||||
color: Audio.muted ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer
|
||||
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
if (Audio.sink?.audio) {
|
||||
Audio.sink.audio.muted = !Audio.sink.audio.muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: muteIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: Audio.muted ? "volume_off" : "volume_up"
|
||||
color: Audio.muted ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledSlider {
|
||||
id: outputVolumeSlider
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Appearance.padding.normal * 3
|
||||
|
||||
value: Audio.volume
|
||||
enabled: !Audio.muted
|
||||
opacity: enabled ? 1 : 0.5
|
||||
onMoved: {
|
||||
Audio.setVolume(value);
|
||||
if (!outputVolumeInput.activeFocus) {
|
||||
outputVolumeInput.text = Math.round(value * 100).toString();
|
||||
}
|
||||
}
|
||||
SectionHeader {
|
||||
title: qsTr("Input volume")
|
||||
description: qsTr("Control the volume of your input device")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
title: qsTr("Input volume")
|
||||
description: qsTr("Control the volume of your input device")
|
||||
}
|
||||
SectionContainer {
|
||||
contentSpacing: Appearance.spacing.normal
|
||||
|
||||
SectionContainer {
|
||||
contentSpacing: Appearance.spacing.normal
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
StyledText {
|
||||
text: qsTr("Volume")
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.weight: 500
|
||||
}
|
||||
|
||||
Item {
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
StyledRect {
|
||||
Layout.preferredWidth: 70
|
||||
implicitHeight: inputVolumeInput.implicitHeight + Appearance.padding.small * 2
|
||||
color: inputVolumeInputHover.containsMouse || inputVolumeInput.activeFocus
|
||||
? Colours.layer(Colours.palette.m3surfaceContainer, 3)
|
||||
: Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
||||
radius: Appearance.rounding.small
|
||||
border.width: 1
|
||||
border.color: inputVolumeInput.activeFocus
|
||||
? Colours.palette.m3primary
|
||||
: Qt.alpha(Colours.palette.m3outline, 0.3)
|
||||
enabled: !Audio.sourceMuted
|
||||
opacity: enabled ? 1 : 0.5
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
Behavior on color { CAnim {} }
|
||||
Behavior on border.color { CAnim {} }
|
||||
StyledText {
|
||||
text: qsTr("Volume")
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.weight: 500
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: inputVolumeInputHover
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.IBeamCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
Layout.preferredWidth: 70
|
||||
implicitHeight: inputVolumeInput.implicitHeight + Appearance.padding.small * 2
|
||||
color: inputVolumeInputHover.containsMouse || inputVolumeInput.activeFocus
|
||||
? Colours.layer(Colours.palette.m3surfaceContainer, 3)
|
||||
: Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
||||
radius: Appearance.rounding.small
|
||||
border.width: 1
|
||||
border.color: inputVolumeInput.activeFocus
|
||||
? Colours.palette.m3primary
|
||||
: Qt.alpha(Colours.palette.m3outline, 0.3)
|
||||
enabled: !Audio.sourceMuted
|
||||
opacity: enabled ? 1 : 0.5
|
||||
|
||||
Behavior on color { CAnim {} }
|
||||
Behavior on border.color { CAnim {} }
|
||||
|
||||
MouseArea {
|
||||
id: inputVolumeInputHover
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.IBeamCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
|
||||
StyledTextField {
|
||||
id: inputVolumeInput
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Appearance.padding.normal
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
validator: IntValidator { bottom: 0; top: 100 }
|
||||
enabled: !Audio.sourceMuted
|
||||
|
||||
Component.onCompleted: {
|
||||
text = Math.round(Audio.sourceVolume * 100).toString();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Audio
|
||||
function onSourceVolumeChanged() {
|
||||
if (!inputVolumeInput.activeFocus) {
|
||||
inputVolumeInput.text = Math.round(Audio.sourceVolume * 100).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
if (activeFocus) {
|
||||
const val = parseInt(text);
|
||||
if (!isNaN(val) && val >= 0 && val <= 100) {
|
||||
Audio.setSourceVolume(val / 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
onEditingFinished: {
|
||||
const val = parseInt(text);
|
||||
if (isNaN(val) || val < 0 || val > 100) {
|
||||
text = Math.round(Audio.sourceVolume * 100).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "%"
|
||||
color: Colours.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
opacity: Audio.sourceMuted ? 0.5 : 1
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: muteInputIcon.implicitHeight + Appearance.padding.normal * 2
|
||||
|
||||
radius: Appearance.rounding.normal
|
||||
color: Audio.sourceMuted ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer
|
||||
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
if (Audio.source?.audio) {
|
||||
Audio.source.audio.muted = !Audio.source.audio.muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: muteInputIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "mic_off"
|
||||
color: Audio.sourceMuted ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledTextField {
|
||||
id: inputVolumeInput
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Appearance.padding.normal
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
validator: IntValidator { bottom: 0; top: 100 }
|
||||
StyledSlider {
|
||||
id: inputVolumeSlider
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Appearance.padding.normal * 3
|
||||
|
||||
value: Audio.sourceVolume
|
||||
enabled: !Audio.sourceMuted
|
||||
|
||||
Component.onCompleted: {
|
||||
text = Math.round(Audio.sourceVolume * 100).toString();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Audio
|
||||
function onSourceVolumeChanged() {
|
||||
if (!inputVolumeInput.activeFocus) {
|
||||
inputVolumeInput.text = Math.round(Audio.sourceVolume * 100).toString();
|
||||
}
|
||||
opacity: enabled ? 1 : 0.5
|
||||
onMoved: {
|
||||
Audio.setSourceVolume(value);
|
||||
if (!inputVolumeInput.activeFocus) {
|
||||
inputVolumeInput.text = Math.round(value * 100).toString();
|
||||
}
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
if (activeFocus) {
|
||||
const val = parseInt(text);
|
||||
if (!isNaN(val) && val >= 0 && val <= 100) {
|
||||
Audio.setSourceVolume(val / 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
onEditingFinished: {
|
||||
const val = parseInt(text);
|
||||
if (isNaN(val) || val < 0 || val > 100) {
|
||||
text = Math.round(Audio.sourceVolume * 100).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "%"
|
||||
color: Colours.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
opacity: Audio.sourceMuted ? 0.5 : 1
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: muteInputIcon.implicitHeight + Appearance.padding.normal * 2
|
||||
|
||||
radius: Appearance.rounding.normal
|
||||
color: Audio.sourceMuted ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer
|
||||
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
if (Audio.source?.audio) {
|
||||
Audio.source.audio.muted = !Audio.source.audio.muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: muteInputIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "mic_off"
|
||||
color: Audio.sourceMuted ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledSlider {
|
||||
id: inputVolumeSlider
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Appearance.padding.normal * 3
|
||||
|
||||
value: Audio.sourceVolume
|
||||
enabled: !Audio.sourceMuted
|
||||
opacity: enabled ? 1 : 0.5
|
||||
onMoved: {
|
||||
Audio.setSourceVolume(value);
|
||||
if (!inputVolumeInput.activeFocus) {
|
||||
inputVolumeInput.text = Math.round(value * 100).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import ".."
|
||||
import "../components"
|
||||
import qs.components.controls
|
||||
import qs.components.effects
|
||||
import qs.components.containers
|
||||
|
|
@ -10,95 +11,50 @@ import Quickshell.Bluetooth
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
RowLayout {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Session session
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
id: leftBtItem
|
||||
Layout.preferredWidth: Math.floor(parent.width * 0.4)
|
||||
Layout.minimumWidth: 420
|
||||
Layout.fillHeight: true
|
||||
|
||||
ClippingRectangle {
|
||||
id: leftBtClippingRect
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: Appearance.padding.normal / 2
|
||||
|
||||
radius: leftBtBorder.innerRadius
|
||||
color: "transparent"
|
||||
|
||||
Loader {
|
||||
id: leftBtLoader
|
||||
|
||||
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
|
||||
|
||||
asynchronous: true
|
||||
sourceComponent: btDeviceListComponent
|
||||
}
|
||||
}
|
||||
|
||||
InnerBorder {
|
||||
id: leftBtBorder
|
||||
leftThickness: 0
|
||||
rightThickness: Appearance.padding.normal / 2
|
||||
}
|
||||
|
||||
Component {
|
||||
id: btDeviceListComponent
|
||||
SplitPaneLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
leftContent: Component {
|
||||
DeviceList {
|
||||
anchors.fill: parent
|
||||
session: root.session
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: rightBtItem
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
ClippingRectangle {
|
||||
id: btClippingRect
|
||||
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
|
||||
rightContent: Component {
|
||||
Item {
|
||||
id: rightBtPane
|
||||
|
||||
property BluetoothDevice pane: root.session.bt.active
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large * 2
|
||||
Loader {
|
||||
id: rightLoader
|
||||
|
||||
asynchronous: true
|
||||
sourceComponent: pane ? details : settings
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large * 2
|
||||
|
||||
asynchronous: true
|
||||
sourceComponent: rightBtPane.pane ? details : settings
|
||||
}
|
||||
|
||||
Behavior on pane {
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: rightLoader
|
||||
property: "opacity"
|
||||
to: 0
|
||||
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||
}
|
||||
Anim {
|
||||
target: rightLoader
|
||||
property: "scale"
|
||||
to: 0.8
|
||||
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||
|
|
@ -107,11 +63,13 @@ RowLayout {
|
|||
PropertyAction {}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: rightLoader
|
||||
property: "opacity"
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
Anim {
|
||||
target: rightLoader
|
||||
property: "scale"
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
|
|
@ -119,49 +77,49 @@ RowLayout {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InnerBorder {
|
||||
id: rightBorder
|
||||
|
||||
leftThickness: Appearance.padding.normal / 2
|
||||
}
|
||||
|
||||
Component {
|
||||
id: settings
|
||||
|
||||
StyledFlickable {
|
||||
id: settingsFlickable
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: settingsInner.height
|
||||
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: settingsFlickable
|
||||
}
|
||||
|
||||
Settings {
|
||||
id: settingsInner
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
session: root.session
|
||||
Connections {
|
||||
target: root.session.bt
|
||||
function onActiveChanged() {
|
||||
rightBtPane.pane = root.session.bt.active;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: details
|
||||
Component {
|
||||
id: settings
|
||||
|
||||
Details {
|
||||
StyledFlickable {
|
||||
id: settingsFlickable
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: settingsInner.height
|
||||
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: settingsFlickable
|
||||
}
|
||||
|
||||
Settings {
|
||||
id: settingsInner
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
session: root.session
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: details
|
||||
|
||||
Details {
|
||||
session: root.session
|
||||
}
|
||||
}
|
||||
|
||||
component Anim: NumberAnimation {
|
||||
target: loader
|
||||
duration: Appearance.anim.durations.normal / 2
|
||||
easing.type: Easing.BezierSpline
|
||||
}
|
||||
|
|
|
|||
120
modules/controlcenter/components/SplitPaneLayout.qml
Normal file
120
modules/controlcenter/components/SplitPaneLayout.qml
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.components.effects
|
||||
import qs.config
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
spacing: 0
|
||||
|
||||
property Component leftContent: null
|
||||
property Component rightContent: null
|
||||
|
||||
// Left pane configuration
|
||||
property real leftWidthRatio: 0.4
|
||||
property int leftMinimumWidth: 420
|
||||
property var leftLoaderProperties: ({})
|
||||
|
||||
// Right pane configuration
|
||||
property var rightLoaderProperties: ({})
|
||||
|
||||
// Expose loaders for customization (access via splitLayout.leftLoader or splitLayout.rightLoader)
|
||||
property alias leftLoader: leftLoader
|
||||
property alias rightLoader: rightLoader
|
||||
|
||||
// Left pane
|
||||
Item {
|
||||
id: leftPane
|
||||
|
||||
Layout.preferredWidth: Math.floor(parent.width * root.leftWidthRatio)
|
||||
Layout.minimumWidth: root.leftMinimumWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
ClippingRectangle {
|
||||
id: leftClippingRect
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: Appearance.padding.normal / 2
|
||||
|
||||
radius: leftBorder.innerRadius
|
||||
color: "transparent"
|
||||
|
||||
Loader {
|
||||
id: leftLoader
|
||||
|
||||
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
|
||||
|
||||
asynchronous: true
|
||||
sourceComponent: root.leftContent
|
||||
|
||||
// Apply any additional properties from leftLoaderProperties
|
||||
Component.onCompleted: {
|
||||
for (const key in root.leftLoaderProperties) {
|
||||
leftLoader[key] = root.leftLoaderProperties[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InnerBorder {
|
||||
id: leftBorder
|
||||
|
||||
leftThickness: 0
|
||||
rightThickness: Appearance.padding.normal / 2
|
||||
}
|
||||
}
|
||||
|
||||
// Right pane
|
||||
Item {
|
||||
id: rightPane
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
ClippingRectangle {
|
||||
id: rightClippingRect
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: Appearance.padding.normal / 2
|
||||
|
||||
radius: rightBorder.innerRadius
|
||||
color: "transparent"
|
||||
|
||||
Loader {
|
||||
id: rightLoader
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large * 2
|
||||
|
||||
asynchronous: true
|
||||
sourceComponent: root.rightContent
|
||||
|
||||
// Apply any additional properties from rightLoaderProperties
|
||||
Component.onCompleted: {
|
||||
for (const key in root.rightLoaderProperties) {
|
||||
rightLoader[key] = root.rightLoaderProperties[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InnerBorder {
|
||||
id: rightBorder
|
||||
|
||||
leftThickness: Appearance.padding.normal / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import ".."
|
||||
import "../components"
|
||||
import "../../launcher/services"
|
||||
import qs.components
|
||||
import qs.components.controls
|
||||
|
|
@ -16,7 +17,7 @@ import QtQuick
|
|||
import QtQuick.Layouts
|
||||
import "../../../utils/scripts/fuzzysort.js" as Fuzzy
|
||||
|
||||
RowLayout {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Session session
|
||||
|
|
@ -26,8 +27,6 @@ RowLayout {
|
|||
|
||||
anchors.fill: parent
|
||||
|
||||
spacing: 0
|
||||
|
||||
onSelectedAppChanged: {
|
||||
root.session.launcher.active = root.selectedApp;
|
||||
updateToggleState();
|
||||
|
|
@ -156,43 +155,10 @@ RowLayout {
|
|||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: leftLauncherItem
|
||||
Layout.preferredWidth: Math.floor(parent.width * 0.4)
|
||||
Layout.minimumWidth: 420
|
||||
Layout.fillHeight: true
|
||||
SplitPaneLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
ClippingRectangle {
|
||||
id: leftLauncherClippingRect
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: Appearance.padding.normal / 2
|
||||
|
||||
radius: leftLauncherBorder.innerRadius
|
||||
color: "transparent"
|
||||
|
||||
Loader {
|
||||
id: leftLauncherLoader
|
||||
|
||||
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
|
||||
|
||||
asynchronous: true
|
||||
sourceComponent: leftContentComponent
|
||||
}
|
||||
}
|
||||
|
||||
InnerBorder {
|
||||
id: leftLauncherBorder
|
||||
leftThickness: 0
|
||||
rightThickness: Appearance.padding.normal / 2
|
||||
}
|
||||
|
||||
Component {
|
||||
id: leftContentComponent
|
||||
leftContent: Component {
|
||||
|
||||
ColumnLayout {
|
||||
id: leftLauncherLayout
|
||||
|
|
@ -336,7 +302,8 @@ RowLayout {
|
|||
// Lazy load: activate when left pane is loaded
|
||||
// The ListView will load asynchronously, and search will work because filteredApps
|
||||
// is updated regardless of whether the ListView is loaded
|
||||
return leftLauncherLoader.item !== null;
|
||||
// Access loader through parent - this will be set when component loads
|
||||
return true;
|
||||
}
|
||||
|
||||
sourceComponent: StyledListView {
|
||||
|
|
@ -412,25 +379,10 @@ RowLayout {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: rightLauncherItem
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
ClippingRectangle {
|
||||
id: rightLauncherClippingRect
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: Appearance.padding.normal / 2
|
||||
|
||||
radius: rightLauncherBorder.innerRadius
|
||||
color: "transparent"
|
||||
|
||||
Loader {
|
||||
id: rightLauncherLoader
|
||||
rightContent: Component {
|
||||
Item {
|
||||
id: rightLauncherPane
|
||||
|
||||
property var pane: root.session.launcher.active
|
||||
property string paneId: pane ? (pane.id || pane.entry?.id || "") : ""
|
||||
|
|
@ -442,28 +394,34 @@ RowLayout {
|
|||
return pane ? appDetails : settings;
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large * 2
|
||||
|
||||
opacity: 1
|
||||
scale: 1
|
||||
transformOrigin: Item.Center
|
||||
clip: false
|
||||
|
||||
asynchronous: true
|
||||
sourceComponent: rightLauncherLoader.targetComponent
|
||||
active: true
|
||||
|
||||
Component.onCompleted: {
|
||||
displayedApp = pane;
|
||||
targetComponent = getComponentForPane();
|
||||
nextComponent = targetComponent;
|
||||
}
|
||||
|
||||
onItemChanged: {
|
||||
// Ensure displayedApp is set when item is created (for async loading)
|
||||
if (item && pane && displayedApp !== pane) {
|
||||
displayedApp = pane;
|
||||
Loader {
|
||||
id: rightLauncherLoader
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: 1
|
||||
scale: 1
|
||||
transformOrigin: Item.Center
|
||||
clip: false
|
||||
|
||||
asynchronous: true
|
||||
sourceComponent: rightLauncherPane.targetComponent
|
||||
active: true
|
||||
|
||||
// Expose displayedApp to loaded components
|
||||
property var displayedApp: rightLauncherPane.displayedApp
|
||||
|
||||
onItemChanged: {
|
||||
// Ensure displayedApp is set when item is created (for async loading)
|
||||
if (item && rightLauncherPane.pane && rightLauncherPane.displayedApp !== rightLauncherPane.pane) {
|
||||
rightLauncherPane.displayedApp = rightLauncherPane.pane;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -484,9 +442,9 @@ RowLayout {
|
|||
}
|
||||
}
|
||||
PropertyAction {
|
||||
target: rightLauncherLoader
|
||||
target: rightLauncherPane
|
||||
property: "displayedApp"
|
||||
value: rightLauncherLoader.pane
|
||||
value: rightLauncherPane.pane
|
||||
}
|
||||
PropertyAction {
|
||||
target: rightLauncherLoader
|
||||
|
|
@ -494,9 +452,9 @@ RowLayout {
|
|||
value: false
|
||||
}
|
||||
PropertyAction {
|
||||
target: rightLauncherLoader
|
||||
target: rightLauncherPane
|
||||
property: "targetComponent"
|
||||
value: rightLauncherLoader.nextComponent
|
||||
value: rightLauncherPane.nextComponent
|
||||
}
|
||||
PropertyAction {
|
||||
target: rightLauncherLoader
|
||||
|
|
@ -539,90 +497,90 @@ RowLayout {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InnerBorder {
|
||||
id: rightLauncherBorder
|
||||
Component {
|
||||
id: settings
|
||||
|
||||
leftThickness: Appearance.padding.normal / 2
|
||||
}
|
||||
StyledFlickable {
|
||||
id: settingsFlickable
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: settingsInner.height
|
||||
|
||||
Component {
|
||||
id: settings
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: settingsFlickable
|
||||
}
|
||||
|
||||
StyledFlickable {
|
||||
id: settingsFlickable
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: settingsInner.height
|
||||
Settings {
|
||||
id: settingsInner
|
||||
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: settingsFlickable
|
||||
}
|
||||
|
||||
Settings {
|
||||
id: settingsInner
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
session: root.session
|
||||
}
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
session: root.session
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: appDetails
|
||||
Component {
|
||||
id: appDetails
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
ColumnLayout {
|
||||
id: appDetailsLayout
|
||||
anchors.fill: parent
|
||||
|
||||
spacing: Appearance.spacing.normal
|
||||
// Get displayedApp from parent Loader (the Loader has displayedApp property we set)
|
||||
readonly property var displayedApp: parent && parent.displayedApp !== undefined ? parent.displayedApp : null
|
||||
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.leftMargin: Appearance.padding.large * 2
|
||||
Layout.rightMargin: Appearance.padding.large * 2
|
||||
Layout.topMargin: Appearance.padding.large * 2
|
||||
implicitWidth: iconLoader.implicitWidth
|
||||
implicitHeight: iconLoader.implicitHeight
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
Loader {
|
||||
id: iconLoader
|
||||
sourceComponent: rightLauncherLoader.displayedApp ? appIconComponent : defaultIconComponent
|
||||
}
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.leftMargin: Appearance.padding.large * 2
|
||||
Layout.rightMargin: Appearance.padding.large * 2
|
||||
Layout.topMargin: Appearance.padding.large * 2
|
||||
implicitWidth: iconLoader.implicitWidth
|
||||
implicitHeight: iconLoader.implicitHeight
|
||||
|
||||
Component {
|
||||
id: appIconComponent
|
||||
IconImage {
|
||||
implicitSize: Appearance.font.size.extraLarge * 3 * 2
|
||||
source: {
|
||||
if (!rightLauncherLoader.displayedApp) return "image-missing";
|
||||
const entry = rightLauncherLoader.displayedApp.entry;
|
||||
if (entry && entry.icon) {
|
||||
return Quickshell.iconPath(entry.icon, "image-missing");
|
||||
}
|
||||
return "image-missing";
|
||||
Loader {
|
||||
id: iconLoader
|
||||
sourceComponent: parent.parent.displayedApp ? appIconComponent : defaultIconComponent
|
||||
}
|
||||
|
||||
Component {
|
||||
id: appIconComponent
|
||||
IconImage {
|
||||
implicitSize: Appearance.font.size.extraLarge * 3 * 2
|
||||
source: {
|
||||
const app = iconLoader.parent.parent.displayedApp;
|
||||
if (!app) return "image-missing";
|
||||
const entry = app.entry;
|
||||
if (entry && entry.icon) {
|
||||
return Quickshell.iconPath(entry.icon, "image-missing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: defaultIconComponent
|
||||
MaterialIcon {
|
||||
text: "apps"
|
||||
font.pointSize: Appearance.font.size.extraLarge * 3
|
||||
font.bold: true
|
||||
return "image-missing";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.leftMargin: Appearance.padding.large * 2
|
||||
Layout.rightMargin: Appearance.padding.large * 2
|
||||
text: rightLauncherLoader.displayedApp ? (rightLauncherLoader.displayedApp.name || rightLauncherLoader.displayedApp.entry?.name || qsTr("Application Details")) : qsTr("Launcher Applications")
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.bold: true
|
||||
Component {
|
||||
id: defaultIconComponent
|
||||
MaterialIcon {
|
||||
text: "apps"
|
||||
font.pointSize: Appearance.font.size.extraLarge * 3
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.leftMargin: Appearance.padding.large * 2
|
||||
Layout.rightMargin: Appearance.padding.large * 2
|
||||
text: displayedApp ? (displayedApp.name || displayedApp.entry?.name || qsTr("Application Details")) : qsTr("Launcher Applications")
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
|
|
@ -648,38 +606,38 @@ RowLayout {
|
|||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
SwitchRow {
|
||||
Layout.topMargin: Appearance.spacing.normal
|
||||
visible: rightLauncherLoader.displayedApp !== null
|
||||
label: qsTr("Hide from launcher")
|
||||
checked: root.hideFromLauncherChecked
|
||||
enabled: rightLauncherLoader.displayedApp !== null
|
||||
onToggled: checked => {
|
||||
root.hideFromLauncherChecked = checked;
|
||||
if (rightLauncherLoader.displayedApp) {
|
||||
const appId = rightLauncherLoader.displayedApp.id || rightLauncherLoader.displayedApp.entry?.id;
|
||||
const hiddenApps = Config.launcher.hiddenApps ? [...Config.launcher.hiddenApps] : [];
|
||||
if (checked) {
|
||||
if (!hiddenApps.includes(appId)) {
|
||||
hiddenApps.push(appId);
|
||||
}
|
||||
} else {
|
||||
const index = hiddenApps.indexOf(appId);
|
||||
if (index !== -1) {
|
||||
hiddenApps.splice(index, 1);
|
||||
}
|
||||
SwitchRow {
|
||||
Layout.topMargin: Appearance.spacing.normal
|
||||
visible: appDetailsLayout.displayedApp !== null
|
||||
label: qsTr("Hide from launcher")
|
||||
checked: root.hideFromLauncherChecked
|
||||
enabled: appDetailsLayout.displayedApp !== null
|
||||
onToggled: checked => {
|
||||
root.hideFromLauncherChecked = checked;
|
||||
const app = appDetailsLayout.displayedApp;
|
||||
if (app) {
|
||||
const appId = app.id || app.entry?.id;
|
||||
const hiddenApps = Config.launcher.hiddenApps ? [...Config.launcher.hiddenApps] : [];
|
||||
if (checked) {
|
||||
if (!hiddenApps.includes(appId)) {
|
||||
hiddenApps.push(appId);
|
||||
}
|
||||
} else {
|
||||
const index = hiddenApps.indexOf(appId);
|
||||
if (index !== -1) {
|
||||
hiddenApps.splice(index, 1);
|
||||
}
|
||||
Config.launcher.hiddenApps = hiddenApps;
|
||||
Config.save();
|
||||
}
|
||||
Config.launcher.hiddenApps = hiddenApps;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Anim: NumberAnimation {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import ".."
|
||||
import "../components"
|
||||
import "."
|
||||
import qs.components
|
||||
import qs.components.controls
|
||||
|
|
@ -21,49 +22,12 @@ Item {
|
|||
|
||||
anchors.fill: parent
|
||||
|
||||
RowLayout {
|
||||
id: contentLayout
|
||||
SplitPaneLayout {
|
||||
id: splitLayout
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
id: leftNetworkItem
|
||||
Layout.preferredWidth: Math.floor(parent.width * 0.4)
|
||||
Layout.minimumWidth: 420
|
||||
Layout.fillHeight: true
|
||||
|
||||
ClippingRectangle {
|
||||
id: leftNetworkClippingRect
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: Appearance.padding.normal / 2
|
||||
|
||||
radius: leftNetworkBorder.innerRadius
|
||||
color: "transparent"
|
||||
|
||||
Loader {
|
||||
id: leftNetworkLoader
|
||||
|
||||
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
|
||||
|
||||
asynchronous: true
|
||||
sourceComponent: networkListComponent
|
||||
}
|
||||
}
|
||||
|
||||
InnerBorder {
|
||||
id: leftNetworkBorder
|
||||
leftThickness: 0
|
||||
rightThickness: Appearance.padding.normal / 2
|
||||
}
|
||||
|
||||
Component {
|
||||
id: networkListComponent
|
||||
leftContent: Component {
|
||||
|
||||
StyledFlickable {
|
||||
id: leftFlickable
|
||||
|
|
@ -473,38 +437,47 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: rightNetworkItem
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
ClippingRectangle {
|
||||
id: networkClippingRect
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: Appearance.padding.normal / 2
|
||||
|
||||
radius: rightBorder.innerRadius
|
||||
color: "transparent"
|
||||
|
||||
rightContent: Component {
|
||||
Item {
|
||||
id: rightPaneItem
|
||||
|
||||
// Right pane - networking details/settings
|
||||
Loader {
|
||||
id: loader
|
||||
property var ethernetPane: root.session.ethernet.active
|
||||
property var wirelessPane: root.session.network.active
|
||||
property var pane: ethernetPane || wirelessPane
|
||||
property string paneId: ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : "")
|
||||
property Component targetComponent: settings
|
||||
property Component nextComponent: settings
|
||||
|
||||
property var ethernetPane: root.session.ethernet.active
|
||||
property var wirelessPane: root.session.network.active
|
||||
property var pane: ethernetPane || wirelessPane
|
||||
property string paneId: ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : "")
|
||||
property Component targetComponent: settings
|
||||
property Component nextComponent: settings
|
||||
function getComponentForPane() {
|
||||
return pane ? (ethernetPane ? ethernetDetails : wirelessDetails) : settings;
|
||||
}
|
||||
|
||||
function getComponentForPane() {
|
||||
return pane ? (ethernetPane ? ethernetDetails : wirelessDetails) : settings;
|
||||
Component.onCompleted: {
|
||||
targetComponent = getComponentForPane();
|
||||
nextComponent = targetComponent;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.session.ethernet
|
||||
function onActiveChanged() {
|
||||
nextComponent = getComponentForPane();
|
||||
paneId = ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : "");
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.session.network
|
||||
function onActiveChanged() {
|
||||
nextComponent = getComponentForPane();
|
||||
paneId = ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : "");
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: rightLoader
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large * 2
|
||||
|
|
@ -515,131 +488,129 @@ Item {
|
|||
clip: false
|
||||
|
||||
asynchronous: true
|
||||
sourceComponent: loader.targetComponent
|
||||
sourceComponent: rightPaneItem.targetComponent
|
||||
|
||||
Component.onCompleted: {
|
||||
targetComponent = getComponentForPane();
|
||||
nextComponent = targetComponent;
|
||||
Connections {
|
||||
target: rightPaneItem
|
||||
function onPaneIdChanged() {
|
||||
rightPaneItem.targetComponent = rightPaneItem.nextComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on paneId {
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: loader
|
||||
property: "opacity"
|
||||
to: 0
|
||||
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||
}
|
||||
Anim {
|
||||
target: loader
|
||||
property: "scale"
|
||||
to: 0.8
|
||||
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||
}
|
||||
Behavior on paneId {
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: rightLoader
|
||||
property: "opacity"
|
||||
to: 0
|
||||
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||
}
|
||||
PropertyAction {
|
||||
target: loader
|
||||
property: "targetComponent"
|
||||
value: loader.nextComponent
|
||||
Anim {
|
||||
target: rightLoader
|
||||
property: "scale"
|
||||
to: 0.8
|
||||
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: loader
|
||||
property: "opacity"
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
Anim {
|
||||
target: loader
|
||||
property: "scale"
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
}
|
||||
PropertyAction {
|
||||
target: rightPaneItem
|
||||
property: "targetComponent"
|
||||
value: rightPaneItem.nextComponent
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: rightLoader
|
||||
property: "opacity"
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
Anim {
|
||||
target: rightLoader
|
||||
property: "scale"
|
||||
to: 1
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPaneChanged: {
|
||||
nextComponent = getComponentForPane();
|
||||
paneId = ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : "");
|
||||
Connections {
|
||||
target: rightPaneItem
|
||||
function onPaneIdChanged() {
|
||||
rightPaneItem.targetComponent = rightPaneItem.nextComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InnerBorder {
|
||||
id: rightBorder
|
||||
Component {
|
||||
id: settings
|
||||
|
||||
leftThickness: Appearance.padding.normal / 2
|
||||
StyledFlickable {
|
||||
id: settingsFlickable
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: settingsInner.height
|
||||
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: settingsFlickable
|
||||
}
|
||||
|
||||
Component {
|
||||
id: settings
|
||||
NetworkSettings {
|
||||
id: settingsInner
|
||||
|
||||
StyledFlickable {
|
||||
id: settingsFlickable
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: settingsInner.height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
session: root.session
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: settingsFlickable
|
||||
}
|
||||
Component {
|
||||
id: ethernetDetails
|
||||
|
||||
NetworkSettings {
|
||||
id: settingsInner
|
||||
StyledFlickable {
|
||||
id: ethernetFlickable
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: ethernetDetailsInner.height
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
session: root.session
|
||||
}
|
||||
}
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: ethernetFlickable
|
||||
}
|
||||
|
||||
Component {
|
||||
id: ethernetDetails
|
||||
EthernetDetails {
|
||||
id: ethernetDetailsInner
|
||||
|
||||
StyledFlickable {
|
||||
id: ethernetFlickable
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: ethernetDetailsInner.height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
session: root.session
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: ethernetFlickable
|
||||
}
|
||||
Component {
|
||||
id: wirelessDetails
|
||||
|
||||
EthernetDetails {
|
||||
id: ethernetDetailsInner
|
||||
StyledFlickable {
|
||||
id: wirelessFlickable
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: wirelessDetailsInner.height
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
session: root.session
|
||||
}
|
||||
}
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: wirelessFlickable
|
||||
}
|
||||
|
||||
Component {
|
||||
id: wirelessDetails
|
||||
WirelessDetails {
|
||||
id: wirelessDetailsInner
|
||||
|
||||
StyledFlickable {
|
||||
id: wirelessFlickable
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: wirelessDetailsInner.height
|
||||
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: wirelessFlickable
|
||||
}
|
||||
|
||||
WirelessDetails {
|
||||
id: wirelessDetailsInner
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
session: root.session
|
||||
}
|
||||
}
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
session: root.session
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -651,7 +622,6 @@ Item {
|
|||
}
|
||||
|
||||
component Anim: NumberAnimation {
|
||||
target: loader
|
||||
duration: Appearance.anim.durations.normal / 2
|
||||
easing.type: Easing.BezierSpline
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue