action center: toggle pages

This commit is contained in:
end-4
2025-11-18 23:08:51 +01:00
parent b650120fd4
commit 4cbb0f23c6
27 changed files with 283 additions and 105 deletions
@@ -581,7 +581,7 @@ Singleton {
property bool leftAlignApps: false
}
property JsonObject actionCenter: JsonObject {
property list<string> toggles: [ "network", "bluetooth", "easyEffects", "powerProfile", "idleInhibitor", "antiFlashbang", "nightLight", "darkMode", "cloudflareWarp", "mic", "audio", "musicRecognition", "notifications", "onScreenKeyboard", "gameMode", "screenSnip", "colorPicker" ]
property list<string> toggles: [ "network", "bluetooth", "easyEffects", "powerProfile", "idleInhibitor", "nightLight", "darkMode", "antiFlashbang", "cloudflareWarp", "mic", "audio", "musicRecognition", "notifications", "onScreenKeyboard", "gameMode", "screenSnip", "colorPicker" ]
}
}
}
@@ -25,7 +25,6 @@ Rectangle {
ActionCenterBodyToggles {
id: togglesContainer
Layout.fillWidth: true
Layout.bottomMargin: -12
}
Rectangle {
@@ -7,39 +7,75 @@ import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
RowLayout {
spacing: 4
ColumnLayout {
id: root
property var screen: root.QsWindow.window?.screen
property var brightnessMonitor: Brightness.getMonitorForScreen(screen)
spacing: 12
WPanelIconButton {
iconName: WIcons.volumeIcon
onClicked: Audio.toggleMute();
}
WSlider {
Layout.fillWidth: true
value: Audio.sink.audio.volume
onMoved: {
Audio.sink.audio.volume = value;
RowLayout {
spacing: 4
WPanelIconButton {
color: colBackground
property real animationValue: root.brightnessMonitor.brightness
rotation: animationValue * 180
scale: 0.8 + animationValue * 0.2
iconName: "weather-sunny"
Behavior on animationValue {
animation: Looks.transition.longMovement.createObject(this)
}
}
WSlider {
Layout.fillWidth: true
value: root.brightnessMonitor.brightness
onMoved: {
root.brightnessMonitor.setBrightness(value)
}
}
WPanelIconButton {
opacity: 0
}
}
RowLayout {
spacing: 4
WPanelIconButton {
contentItem: Item {
anchors.centerIn: parent
Row {
WPanelIconButton {
iconName: WIcons.volumeIcon
onClicked: Audio.toggleMute();
}
WSlider {
Layout.fillWidth: true
value: Audio.sink.audio.volume
onMoved: {
Audio.sink.audio.volume = value;
}
}
WPanelIconButton {
contentItem: Item {
anchors.centerIn: parent
spacing: -1
FluentIcon {
anchors.verticalCenter: parent.verticalCenter
implicitSize: 18
icon: "options"
}
FluentIcon {
anchors.verticalCenter: parent.verticalCenter
implicitSize: 12
icon: "chevron-right"
Row {
anchors.centerIn: parent
spacing: -1
FluentIcon {
anchors.verticalCenter: parent.verticalCenter
implicitSize: 18
icon: "options"
}
FluentIcon {
anchors.verticalCenter: parent.verticalCenter
implicitSize: 12
icon: "chevron-right"
}
}
}
}
}
}
}
@@ -1,10 +1,13 @@
pragma ComponentBehavior: Bound
import Qt.labs.synchronizer
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.models.quickToggles
import qs.modules.common.functions
import qs.modules.waffle.looks
@@ -13,37 +16,124 @@ import qs.modules.waffle.actionCenter.toggles
Item {
id: root
property int columns: 3
property int rows: 2
property int currentPage: 0
property alias columns: grid.columns
property alias rows: grid.rows
readonly property int itemsPerPage: columns * rows
readonly property int pages: Math.ceil(toggles.length / itemsPerPage)
property list<string> toggles: Config.options.waffles.actionCenter.toggles
property list<string> togglesInCurrentPage: toggles.slice(currentPage * itemsPerPage, (currentPage + 1) * itemsPerPage)
property real padding: 22
implicitHeight: grid.implicitHeight + padding * 2
property real reducedBottomPadding: 12
implicitHeight: swipeView.implicitHeight + (padding - swipeView.padding) * 2 - reducedBottomPadding
GridLayout {
id: grid
anchors {
fill: parent
margins: parent.padding
}
function togglesInPage(index) {
var start = index * root.itemsPerPage;
var end = start + root.itemsPerPage;
return root.toggles.slice(start, end);
}
columns: 3
rows: 2
rowSpacing: 12
columnSpacing: 12
uniformCellHeights: true
uniformCellWidths: true
Repeater {
model: ScriptModel {
values: root.togglesInCurrentPage
}
delegate: ActionCenterTogglesDelegateChooser {}
function decreasePage() {
if (root.currentPage > 0) {
root.currentPage -= 1;
}
}
// TODO: pages indicator on the right
function increasePage() {
if (root.currentPage < (root.pages - 1)) {
root.currentPage += 1;
}
}
clip: true
SwipeView {
id: swipeView
anchors {
fill: parent
topMargin: root.padding - swipeView.padding
bottomMargin: root.padding - swipeView.padding - root.reducedBottomPadding
}
padding: 4
leftPadding: root.padding
rightPadding: root.padding
spacing: padding
orientation: Qt.Vertical
clip: true
Synchronizer on currentIndex {
property alias source: root.currentPage
}
Repeater {
model: root.pages
delegate: GridLayout {
id: grid
required property int index
// width: SwipeView.view.width - root.padding * 2
// height: SwipeView.view.height - root.padding * 2
columns: root.columns
rows: root.rows
rowSpacing: 12
columnSpacing: 12
Repeater {
model: ScriptModel {
values: root.togglesInPage(grid.index)
}
delegate: ActionCenterTogglesDelegateChooser {}
}
}
}
}
Column {
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 6
spacing: 6
FluentIcon {
anchors.horizontalCenter: parent.horizontalCenter
implicitSize: 12
icon: "caret-up"
color: Looks.colors.controlBg
filled: true
opacity: root.currentPage > 0 ? 1 : 0
}
Repeater {
model: root.pages
delegate: Item {
required property int index
anchors.horizontalCenter: parent.horizontalCenter
implicitWidth: 6
implicitHeight: 6
Circle {
anchors.centerIn: parent
diameter: index === root.currentPage ? 6 : 4
color: Looks.colors.controlBg
}
}
}
FluentIcon {
anchors.horizontalCenter: parent.horizontalCenter
implicitSize: 12
icon: "caret-down"
color: Looks.colors.controlBg
filled: true
opacity: root.currentPage < (root.pages - 1) ? 1 : 0
}
}
FocusedScrollMouseArea {
z: 999
anchors.fill: parent
acceptedButtons: Qt.NoButton
hoverEnabled: false
onScrollUp: decreasePage();
onScrollDown: increasePage();
}
}
@@ -14,10 +14,7 @@ WBarAttachedPanelContent {
anchors.centerIn: parent
spacing: 0
ActionCenterBody {
topLeftRadius: root.border.radius - root.border.border.width
topRightRadius: topLeftRadius
}
ActionCenterBody {}
Rectangle {
Layout.fillHeight: false
@@ -26,9 +23,6 @@ WBarAttachedPanelContent {
implicitHeight: 1
}
ActionCenterFooter {
bottomLeftRadius: root.border.radius - root.border.border.width
bottomRightRadius: bottomLeftRadius
}
ActionCenterFooter {}
}
}
@@ -42,6 +42,11 @@ Rectangle {
anchors.right: parent.right
anchors.rightMargin: 12
onClicked: {
GlobalStates.sidebarLeftOpen = false;
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath("settings.qml")]);
}
contentItem: FluentIcon {
icon: "settings"
}
@@ -56,6 +56,13 @@ Scope {
id: content
anchors.centerIn: parent
focus: true
Keys.onPressed: event => { // Esc to close
if (event.key === Qt.Key_Escape) {
content.close()
}
}
onClosed: {
barLoader.active = false;
GlobalStates.sidebarLeftOpen = false;
@@ -32,18 +32,18 @@ ColumnLayout {
property color colBorder: toggled ? Looks.colors.accentHover : Looks.colors.bg0Border
property color colForeground: toggled ? Looks.colors.accentFg : Looks.colors.fg1
spacing: 0
property real wholeToggleWidth: 96
Rectangle {
Layout.fillWidth: true
implicitWidth: 96
implicitWidth: root.wholeToggleWidth
implicitHeight: 48
color: root.colBackground
border.color: root.colBorder
border.width: 1
radius: Looks.radius.medium
RowLayout {
anchors.fill: parent
uniformCellSizes: true
spacing: 0
WButton {
@@ -102,8 +102,8 @@ ColumnLayout {
Item {
id: toggleNameWidget
implicitWidth: root.wholeToggleWidth
implicitHeight: 36
Layout.fillWidth: true
WText {
id: toggleNameText
anchors {
@@ -32,6 +32,7 @@ DelegateChooser {
roleValue: "bluetooth"
ActionCenterToggleButton {
toggleModel: BluetoothToggle {}
name: toggleModel.statusText
icon: WIcons.bluetoothIcon
}
}
@@ -39,7 +40,7 @@ DelegateChooser {
roleValue: "cloudflareWarp"
ActionCenterToggleButton {
toggleModel: CloudflareWarpToggle {}
icon: "globe-shield"
icon: "cloudflare"
}
}
DelegateChoice {
@@ -53,7 +54,7 @@ DelegateChooser {
roleValue: "darkMode"
ActionCenterToggleButton {
toggleModel: DarkModeToggle {}
icon: "dark-theme*"
icon: "dark-theme"
}
}
DelegateChoice {
@@ -95,6 +96,7 @@ DelegateChooser {
roleValue: "network"
ActionCenterToggleButton {
toggleModel: NetworkToggle {}
name: toggleModel.statusText
icon: WIcons.internetIcon
}
}
@@ -124,6 +126,7 @@ DelegateChooser {
ActionCenterToggleButton {
toggleModel: PowerProfilesToggle {}
icon: WIcons.powerProfileIcon
name: toggleModel.statusText
}
}
DelegateChoice {
@@ -18,18 +18,29 @@ BarButton {
contentItem: Item {
anchors.centerIn: root.background
implicitHeight: column.implicitHeight
implicitWidth: column.implicitWidth
Column {
id: column
implicitHeight: contentLayout.implicitHeight
implicitWidth: contentLayout.implicitWidth
Row {
id: contentLayout
anchors.centerIn: parent
WText {
anchors.right: parent.right
text: DateTime.time
spacing: 7
Column {
anchors.verticalCenter: parent.verticalCenter
WText {
anchors.right: parent.right
text: DateTime.time
}
WText {
anchors.right: parent.right
text: DateTime.date
}
}
WText {
anchors.right: parent.right
text: DateTime.date
FluentIcon {
anchors.verticalCenter: parent.verticalCenter
icon: "alert-snooze"
implicitSize: 18
filled: true
}
}
}
@@ -148,5 +148,13 @@ Singleton {
easing.bezierCurve: transition.easing.bezierCurve.easeIn
}
}
property Component longMovement: Component {
NumberAnimation {
duration: 1000
easing.type: Easing.BezierSpline
easing.bezierCurve: transition.easing.bezierCurve.easeIn
}
}
}
}
@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Quickshell
import qs
import qs.services
@@ -9,10 +10,10 @@ import qs.modules.waffle.looks
Item {
id: root
signal closed()
signal closed
property alias border: borderRect
required default property Item contentItem
default required property Item contentItem
property real visualMargin: 12
function close() {
@@ -26,6 +27,7 @@ Item {
Rectangle {
id: borderRect
z: 1
color: "transparent"
radius: Looks.radius.large
@@ -33,7 +35,6 @@ Item {
border.width: 1
implicitWidth: contentItem.implicitWidth + border.width * 2
implicitHeight: contentItem.implicitHeight + border.width * 2
children: [root.contentItem]
anchors {
left: parent.left
@@ -76,4 +77,20 @@ Item {
}
}
}
Item {
id: contentArea
z: 0
anchors.fill: borderRect
anchors.margins: borderRect.border.width
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
width: contentArea.width
height: contentArea.height
radius: borderRect.radius - borderRect.border.width
}
}
children: [root.contentItem]
}
}
@@ -16,6 +16,24 @@ Button {
property color colBackgroundToggledHover: Looks.colors.accentHover
property color colBackgroundToggledActive: Looks.colors.accentActive
property alias backgroundOpacity: backgroundRect.opacity
property color color: {
if (root.checked) {
if (root.down) {
return root.colBackgroundToggledActive;
} else if (root.hovered && !root.down) {
return root.colBackgroundToggledHover;
} else {
return root.colBackgroundToggled;
}
}
if (root.down) {
return root.colBackgroundActive;
} else if (root.hovered && !root.down) {
return root.colBackgroundHover;
} else {
return root.colBackground;
}
}
property alias monochromeIcon: buttonIcon.monochrome
property bool forceShowIcon: false
@@ -42,24 +60,7 @@ Button {
background: Rectangle {
id: backgroundRect
radius: Looks.radius.medium
color: {
if (root.checked) {
if (root.down) {
return root.colBackgroundToggledActive;
} else if (root.hovered && !root.down) {
return root.colBackgroundToggledHover;
} else {
return root.colBackgroundToggled;
}
}
if (root.down) {
return root.colBackgroundActive;
} else if (root.hovered && !root.down) {
return root.colBackgroundHover;
} else {
return root.colBackground;
}
}
color: root.color
Behavior on color {
animation: Looks.transition.color.createObject(this)
}
@@ -55,7 +55,7 @@ Singleton {
property string powerProfileIcon: {
switch(PowerProfiles.profile) {
case PowerProfile.PowerSaver: return "leaf-two";
case PowerProfile.Balanced: return "settings-cog-multiple";
case PowerProfile.Balanced: return "flash-on";
case PowerProfile.Performance: return "fire";
}
}
@@ -46,8 +46,8 @@ Slider {
right: parent.right
verticalCenter: parent.verticalCenter
}
topLeftRadius: root.trackWidth / 2
bottomLeftRadius: root.trackWidth / 2
topRightRadius: root.trackWidth / 2
bottomRightRadius: root.trackWidth / 2
color: Looks.colors.controlBg
implicitHeight: root.trackWidth
width: background.width * (1 - root.visualPosition)