summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-07-23 23:04:28 +1000
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-07-23 23:04:28 +1000
commit982a5a3e8de4abb322b53ea0bf77b5e57860540f (patch)
tree59bff3a6eb8ea4c8de6d208465d0ea1361f15465
parentinternal: better styled switch (diff)
downloadcaelestia-shell-982a5a3e8de4abb322b53ea0bf77b5e57860540f.tar.gz
caelestia-shell-982a5a3e8de4abb322b53ea0bf77b5e57860540f.tar.bz2
caelestia-shell-982a5a3e8de4abb322b53ea0bf77b5e57860540f.zip
dcontent: create bluetooth panel
-rw-r--r--config/Config.qml2
-rw-r--r--config/DContentConfig.qml11
-rw-r--r--modules/bar/popouts/Background.qml2
-rw-r--r--modules/bar/popouts/Bluetooth.qml38
-rw-r--r--modules/bar/popouts/Content.qml4
-rw-r--r--modules/bar/popouts/Wrapper.qml22
-rw-r--r--modules/detachedcontent/DetachedContent.qml98
-rw-r--r--modules/detachedcontent/NavRail.qml193
-rw-r--r--modules/detachedcontent/Session.qml5
9 files changed, 372 insertions, 3 deletions
diff --git a/config/Config.qml b/config/Config.qml
index 1ab8cb4..8756a12 100644
--- a/config/Config.qml
+++ b/config/Config.qml
@@ -11,6 +11,7 @@ Singleton {
property alias bar: adapter.bar
property alias border: adapter.border
property alias dashboard: adapter.dashboard
+ property alias dcontent: adapter.dcontent
property alias launcher: adapter.launcher
property alias notifs: adapter.notifs
property alias osd: adapter.osd
@@ -33,6 +34,7 @@ Singleton {
property BarConfig bar: BarConfig {}
property BorderConfig border: BorderConfig {}
property DashboardConfig dashboard: DashboardConfig {}
+ property DContentConfig dcontent: DContentConfig {}
property LauncherConfig launcher: LauncherConfig {}
property NotifsConfig notifs: NotifsConfig {}
property OsdConfig osd: OsdConfig {}
diff --git a/config/DContentConfig.qml b/config/DContentConfig.qml
new file mode 100644
index 0000000..13afbd2
--- /dev/null
+++ b/config/DContentConfig.qml
@@ -0,0 +1,11 @@
+import Quickshell.Io
+
+JsonObject {
+ property Sizes sizes: Sizes {}
+
+ component Sizes: JsonObject {
+ property real heightMult: 0.7
+ property real ratio: 16 / 9
+ property real expandedNavWidth: 180
+ }
+}
diff --git a/modules/bar/popouts/Background.qml b/modules/bar/popouts/Background.qml
index f7d568f..14f5f20 100644
--- a/modules/bar/popouts/Background.qml
+++ b/modules/bar/popouts/Background.qml
@@ -8,7 +8,7 @@ ShapePath {
required property Wrapper wrapper
required property bool invertBottomRounding
- readonly property real rounding: Config.border.rounding
+ readonly property real rounding: wrapper.isDetached ? Appearance.rounding.normal : Config.border.rounding
readonly property bool flatten: wrapper.width < rounding * 2
readonly property real roundingX: flatten ? wrapper.width / 2 : rounding
property real ibr: invertBottomRounding ? -1 : 1
diff --git a/modules/bar/popouts/Bluetooth.qml b/modules/bar/popouts/Bluetooth.qml
index 5b1127f..56dd4a4 100644
--- a/modules/bar/popouts/Bluetooth.qml
+++ b/modules/bar/popouts/Bluetooth.qml
@@ -12,6 +12,8 @@ import QtQuick.Layouts
ColumnLayout {
id: root
+ required property Item wrapper
+
spacing: Appearance.spacing.small
StyledText {
@@ -170,6 +172,42 @@ ColumnLayout {
}
}
+ StyledRect {
+ Layout.topMargin: Appearance.spacing.small
+ implicitWidth: expandBtn.implicitWidth + Appearance.padding.normal * 2
+ implicitHeight: expandBtn.implicitHeight + Appearance.padding.small
+
+ radius: Appearance.rounding.normal
+ color: Colours.palette.m3primaryContainer
+
+ StateLayer {
+ color: Colours.palette.m3onPrimaryContainer
+
+ function onClicked(): void {
+ root.wrapper.detach("bluetooth");
+ }
+ }
+
+ RowLayout {
+ id: expandBtn
+
+ anchors.centerIn: parent
+ spacing: Appearance.spacing.small
+
+ StyledText {
+ Layout.leftMargin: Appearance.padding.smaller
+ text: qsTr("Open panel")
+ color: Colours.palette.m3onPrimaryContainer
+ }
+
+ MaterialIcon {
+ text: "chevron_right"
+ color: Colours.palette.m3onPrimaryContainer
+ font.pointSize: Appearance.font.size.large
+ }
+ }
+ }
+
component Toggle: RowLayout {
required property string label
property alias checked: toggle.checked
diff --git a/modules/bar/popouts/Content.qml b/modules/bar/popouts/Content.qml
index 684f702..42e138d 100644
--- a/modules/bar/popouts/Content.qml
+++ b/modules/bar/popouts/Content.qml
@@ -35,7 +35,9 @@ Item {
Popout {
name: "bluetooth"
- source: "Bluetooth.qml"
+ sourceComponent: Bluetooth {
+ wrapper: root.wrapper
+ }
}
Popout {
diff --git a/modules/bar/popouts/Wrapper.qml b/modules/bar/popouts/Wrapper.qml
index 293f9d5..9a7f7c1 100644
--- a/modules/bar/popouts/Wrapper.qml
+++ b/modules/bar/popouts/Wrapper.qml
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
import qs.services
import qs.config
import qs.modules.windowinfo
+import qs.modules.detachedcontent
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
@@ -21,6 +22,7 @@ Item {
property bool hasCurrent
property string detachedMode
+ property string queuedMode
readonly property bool isDetached: detachedMode.length > 0
property int animLength: Appearance.anim.durations.normal
@@ -28,7 +30,12 @@ Item {
function detach(mode: string): void {
animLength = Appearance.anim.durations.large;
- detachedMode = mode;
+ if (mode === "winfo") {
+ detachedMode = mode;
+ } else {
+ detachedMode = "any";
+ queuedMode = mode;
+ }
focus = true;
}
@@ -86,6 +93,19 @@ Item {
}
}
+ Comp {
+ id: detachedContent
+
+ shouldBeActive: root.detachedMode === "any"
+ asynchronous: true
+ anchors.centerIn: parent
+
+ sourceComponent: DetachedContent {
+ screen: root.screen
+ active: root.queuedMode
+ }
+ }
+
Behavior on x {
Anim {
duration: root.animLength
diff --git a/modules/detachedcontent/DetachedContent.qml b/modules/detachedcontent/DetachedContent.qml
new file mode 100644
index 0000000..4226917
--- /dev/null
+++ b/modules/detachedcontent/DetachedContent.qml
@@ -0,0 +1,98 @@
+pragma ComponentBehavior: Bound
+
+import qs.widgets
+import qs.services
+import qs.config
+import Quickshell
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Effects
+
+Item {
+ id: root
+
+ required property ShellScreen screen
+ property alias active: session.active
+ readonly property Session session: Session {
+ id: session
+ }
+
+ implicitWidth: implicitHeight * Config.dcontent.sizes.ratio
+ implicitHeight: screen.height * Config.dcontent.sizes.heightMult
+
+ GridLayout {
+ anchors.fill: parent
+
+ rows: 2
+ columns: 2
+ rowSpacing: 0
+ columnSpacing: 0
+
+ StyledRect {
+ Layout.fillHeight: true
+ Layout.rowSpan: 2
+
+ topLeftRadius: Appearance.rounding.normal
+ bottomLeftRadius: Appearance.rounding.normal
+ implicitWidth: navRail.implicitWidth
+ color: Colours.palette.m3surfaceContainer
+
+ NavRail {
+ id: navRail
+
+ session: root.session
+ }
+ }
+
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: 50
+ topRightRadius: Appearance.rounding.normal
+ color: Colours.palette.m3surfaceContainer
+ }
+
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ StyledText {
+ anchors.centerIn: parent
+ text: qsTr("Work in progress")
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.extraLarge
+ font.weight: 500
+ }
+
+ StyledRect {
+ anchors.fill: parent
+ color: Colours.palette.m3surfaceContainer
+ bottomRightRadius: Appearance.rounding.normal
+
+ layer.enabled: true
+ layer.effect: MultiEffect {
+ maskSource: mask
+ maskEnabled: true
+ maskInverted: true
+ maskThresholdMin: 0.5
+ maskSpreadAtMin: 1
+ }
+ }
+
+ Item {
+ id: mask
+
+ anchors.fill: parent
+ layer.enabled: true
+ visible: false
+
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: Appearance.padding.normal
+ anchors.topMargin: 0
+ anchors.leftMargin: 0
+ radius: Appearance.rounding.small
+ }
+ }
+ }
+ }
+}
diff --git a/modules/detachedcontent/NavRail.qml b/modules/detachedcontent/NavRail.qml
new file mode 100644
index 0000000..fb30c06
--- /dev/null
+++ b/modules/detachedcontent/NavRail.qml
@@ -0,0 +1,193 @@
+pragma ComponentBehavior: Bound
+
+import qs.widgets
+import qs.services
+import qs.config
+import QtQuick
+import QtQuick.Layouts
+
+Item {
+ id: root
+
+ required property Session session
+ property bool expanded
+
+ implicitWidth: layout.implicitWidth + Appearance.padding.large * 4
+ implicitHeight: layout.implicitHeight + Appearance.padding.large * 2
+
+ ColumnLayout {
+ id: layout
+
+ anchors.centerIn: parent
+ spacing: Appearance.spacing.normal
+
+ states: State {
+ name: "expanded"
+ when: root.expanded
+
+ PropertyChanges {
+ layout.spacing: Appearance.spacing.small / 2
+ menuIcon.opacity: 0
+ menuIconExpanded.opacity: 1
+ menuIcon.rotation: 180
+ menuIconExpanded.rotation: 0
+ }
+ AnchorChanges {
+ target: menuIcon
+ anchors.horizontalCenter: undefined
+ }
+ AnchorChanges {
+ target: menuIconExpanded
+ anchors.horizontalCenter: undefined
+ }
+ }
+
+ transitions: Transition {
+ Anim {
+ properties: "spacing,opacity,rotation"
+ }
+ }
+
+ Item {
+ Layout.fillWidth: true
+ Layout.bottomMargin: Appearance.spacing.small / 2
+ implicitHeight: Math.max(menuIcon.implicitHeight, menuIconExpanded.implicitHeight) + Appearance.padding.normal * 2
+
+ StateLayer {
+ radius: Appearance.rounding.small
+
+ function onClicked(): void {
+ root.expanded = !root.expanded;
+ }
+ }
+
+ MaterialIcon {
+ id: menuIcon
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ text: "menu"
+ font.pointSize: Appearance.font.size.large
+ }
+
+ MaterialIcon {
+ id: menuIconExpanded
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ text: "menu_open"
+ font.pointSize: Appearance.font.size.large
+ opacity: 0
+ rotation: -180
+ }
+ }
+
+ NavItem {
+ icon: "settings_bluetooth"
+ label: "bluetooth"
+ }
+ }
+
+ component NavItem: Item {
+ id: item
+
+ required property string icon
+ required property string label
+ readonly property bool active: root.session.active === label
+
+ implicitWidth: background.implicitWidth
+ implicitHeight: background.implicitHeight + smallLabel.implicitHeight + smallLabel.anchors.topMargin
+
+ states: State {
+ name: "expanded"
+ when: root.expanded
+
+ PropertyChanges {
+ expandedLabel.opacity: 1
+ smallLabel.opacity: 0
+ background.implicitWidth: Config.dcontent.sizes.expandedNavWidth
+ background.implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2
+ item.implicitHeight: background.implicitHeight
+ }
+ }
+
+ transitions: Transition {
+ Anim {
+ property: "opacity"
+ duration: Appearance.anim.durations.small
+ }
+
+ Anim {
+ properties: "implicitWidth,implicitHeight"
+ duration: Appearance.anim.durations.expressiveDefaultSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
+ }
+ }
+
+ StyledRect {
+ id: background
+
+ radius: Appearance.rounding.full
+ color: item.active ? Colours.palette.m3secondaryContainer : Colours.palette.m3surfaceContainer
+
+ implicitWidth: icon.implicitWidth + icon.anchors.leftMargin * 2
+ implicitHeight: icon.implicitHeight + Appearance.padding.small
+
+ StateLayer {
+ color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
+
+ function onClicked(): void {
+ root.session.active = item.label;
+ }
+ }
+
+ MaterialIcon {
+ id: icon
+
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: Appearance.padding.large
+
+ text: item.icon
+ color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
+ font.pointSize: Appearance.font.size.large
+ fill: item.active ? 1 : 0
+
+ Behavior on fill {
+ Anim {}
+ }
+ }
+
+ StyledText {
+ id: expandedLabel
+
+ anchors.left: icon.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: Appearance.spacing.small
+
+ opacity: 0
+ text: item.label
+ color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
+ font.capitalization: Font.Capitalize
+ }
+
+ StyledText {
+ id: smallLabel
+
+ anchors.horizontalCenter: icon.horizontalCenter
+ anchors.top: icon.bottom
+ anchors.topMargin: Appearance.spacing.small / 2
+
+ text: item.label
+ font.pointSize: Appearance.font.size.small
+ font.capitalization: Font.Capitalize
+ }
+ }
+ }
+
+ component Anim: NumberAnimation {
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standard
+ }
+}
diff --git a/modules/detachedcontent/Session.qml b/modules/detachedcontent/Session.qml
new file mode 100644
index 0000000..a3f2de5
--- /dev/null
+++ b/modules/detachedcontent/Session.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+QtObject {
+ property string active
+}