643 lines
28 KiB
QML
643 lines
28 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import ".."
|
|
import qs.components
|
|
import qs.components.controls
|
|
import qs.components.effects
|
|
import qs.components.containers
|
|
import qs.services
|
|
import qs.config
|
|
import qs.utils
|
|
import Quickshell
|
|
import Quickshell.Widgets
|
|
import QtQuick
|
|
import QtQuick.Layouts
|
|
|
|
Item {
|
|
id: root
|
|
|
|
required property Session session
|
|
|
|
// Clock
|
|
property bool clockShowIcon: Config.bar.clock.showIcon ?? true
|
|
|
|
// Bar Behavior
|
|
property bool persistent: Config.bar.persistent ?? true
|
|
property bool showOnHover: Config.bar.showOnHover ?? true
|
|
property int dragThreshold: Config.bar.dragThreshold ?? 20
|
|
|
|
// Status Icons
|
|
property bool showAudio: Config.bar.status.showAudio ?? true
|
|
property bool showMicrophone: Config.bar.status.showMicrophone ?? true
|
|
property bool showKbLayout: Config.bar.status.showKbLayout ?? false
|
|
property bool showNetwork: Config.bar.status.showNetwork ?? true
|
|
property bool showBluetooth: Config.bar.status.showBluetooth ?? true
|
|
property bool showBattery: Config.bar.status.showBattery ?? true
|
|
property bool showLockStatus: Config.bar.status.showLockStatus ?? true
|
|
|
|
// Tray Settings
|
|
property bool trayBackground: Config.bar.tray.background ?? false
|
|
property bool trayCompact: Config.bar.tray.compact ?? false
|
|
property bool trayRecolour: Config.bar.tray.recolour ?? false
|
|
|
|
// Workspaces
|
|
property int workspacesShown: Config.bar.workspaces.shown ?? 5
|
|
property bool workspacesActiveIndicator: Config.bar.workspaces.activeIndicator ?? true
|
|
property bool workspacesOccupiedBg: Config.bar.workspaces.occupiedBg ?? false
|
|
property bool workspacesShowWindows: Config.bar.workspaces.showWindows ?? false
|
|
property bool workspacesPerMonitor: Config.bar.workspaces.perMonitorWorkspaces ?? true
|
|
|
|
anchors.fill: parent
|
|
|
|
Component.onCompleted: {
|
|
// Update entries
|
|
if (Config.bar.entries) {
|
|
entriesModel.clear();
|
|
for (let i = 0; i < Config.bar.entries.length; i++) {
|
|
const entry = Config.bar.entries[i];
|
|
entriesModel.append({
|
|
id: entry.id,
|
|
enabled: entry.enabled !== false
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
function saveConfig(entryIndex, entryEnabled) {
|
|
// Update clock setting
|
|
Config.bar.clock.showIcon = root.clockShowIcon;
|
|
|
|
// Update bar behavior
|
|
Config.bar.persistent = root.persistent;
|
|
Config.bar.showOnHover = root.showOnHover;
|
|
Config.bar.dragThreshold = root.dragThreshold;
|
|
|
|
// Update status icons
|
|
Config.bar.status.showAudio = root.showAudio;
|
|
Config.bar.status.showMicrophone = root.showMicrophone;
|
|
Config.bar.status.showKbLayout = root.showKbLayout;
|
|
Config.bar.status.showNetwork = root.showNetwork;
|
|
Config.bar.status.showBluetooth = root.showBluetooth;
|
|
Config.bar.status.showBattery = root.showBattery;
|
|
Config.bar.status.showLockStatus = root.showLockStatus;
|
|
|
|
// Update tray settings
|
|
Config.bar.tray.background = root.trayBackground;
|
|
Config.bar.tray.compact = root.trayCompact;
|
|
Config.bar.tray.recolour = root.trayRecolour;
|
|
|
|
// Update workspaces
|
|
Config.bar.workspaces.shown = root.workspacesShown;
|
|
Config.bar.workspaces.activeIndicator = root.workspacesActiveIndicator;
|
|
Config.bar.workspaces.occupiedBg = root.workspacesOccupiedBg;
|
|
Config.bar.workspaces.showWindows = root.workspacesShowWindows;
|
|
Config.bar.workspaces.perMonitorWorkspaces = root.workspacesPerMonitor;
|
|
|
|
// Update entries from the model (same approach as clock - use provided value if available)
|
|
const entries = [];
|
|
for (let i = 0; i < entriesModel.count; i++) {
|
|
const entry = entriesModel.get(i);
|
|
// If this is the entry being updated, use the provided value (same as clock toggle reads from switch)
|
|
// Otherwise use the value from the model
|
|
let enabled = entry.enabled;
|
|
if (entryIndex !== undefined && i === entryIndex) {
|
|
enabled = entryEnabled;
|
|
}
|
|
entries.push({
|
|
id: entry.id,
|
|
enabled: enabled
|
|
});
|
|
}
|
|
Config.bar.entries = entries;
|
|
|
|
// Persist changes to disk
|
|
Config.save();
|
|
}
|
|
|
|
ListModel {
|
|
id: entriesModel
|
|
}
|
|
|
|
ClippingRectangle {
|
|
id: taskbarClippingRect
|
|
anchors.fill: parent
|
|
anchors.margins: Appearance.padding.normal
|
|
anchors.leftMargin: 0
|
|
anchors.rightMargin: Appearance.padding.normal / 2
|
|
|
|
radius: taskbarBorder.innerRadius
|
|
color: "transparent"
|
|
|
|
Loader {
|
|
id: taskbarLoader
|
|
|
|
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: taskbarContentComponent
|
|
}
|
|
}
|
|
|
|
InnerBorder {
|
|
id: taskbarBorder
|
|
}
|
|
|
|
Component {
|
|
id: taskbarContentComponent
|
|
|
|
StyledFlickable {
|
|
id: sidebarFlickable
|
|
flickableDirection: Flickable.VerticalFlick
|
|
contentHeight: sidebarLayout.height
|
|
|
|
StyledScrollBar.vertical: StyledScrollBar {
|
|
flickable: sidebarFlickable
|
|
}
|
|
|
|
ColumnLayout {
|
|
id: sidebarLayout
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.top: parent.top
|
|
|
|
spacing: Appearance.spacing.small
|
|
|
|
readonly property bool allSectionsExpanded: true
|
|
|
|
RowLayout {
|
|
spacing: Appearance.spacing.smaller
|
|
|
|
StyledText {
|
|
text: qsTr("Settings")
|
|
font.pointSize: Appearance.font.size.large
|
|
font.weight: 500
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
IconButton {
|
|
icon: sidebarLayout.allSectionsExpanded ? "unfold_less" : "unfold_more"
|
|
type: IconButton.Text
|
|
label.animate: true
|
|
onClicked: {
|
|
// No collapsible sections remaining
|
|
}
|
|
}
|
|
}
|
|
|
|
ConnectedButtonGroup {
|
|
rootItem: root
|
|
title: qsTr("Status Icons")
|
|
|
|
options: [
|
|
{
|
|
label: qsTr("Audio"),
|
|
propertyName: "showAudio",
|
|
onToggled: function(checked) {
|
|
root.showAudio = checked;
|
|
root.saveConfig();
|
|
}
|
|
},
|
|
{
|
|
label: qsTr("Microphone"),
|
|
propertyName: "showMicrophone",
|
|
onToggled: function(checked) {
|
|
root.showMicrophone = checked;
|
|
root.saveConfig();
|
|
}
|
|
},
|
|
{
|
|
label: qsTr("Keyboard"),
|
|
propertyName: "showKbLayout",
|
|
onToggled: function(checked) {
|
|
root.showKbLayout = checked;
|
|
root.saveConfig();
|
|
}
|
|
},
|
|
{
|
|
label: qsTr("Network"),
|
|
propertyName: "showNetwork",
|
|
onToggled: function(checked) {
|
|
root.showNetwork = checked;
|
|
root.saveConfig();
|
|
}
|
|
},
|
|
{
|
|
label: qsTr("Bluetooth"),
|
|
propertyName: "showBluetooth",
|
|
onToggled: function(checked) {
|
|
root.showBluetooth = checked;
|
|
root.saveConfig();
|
|
}
|
|
},
|
|
{
|
|
label: qsTr("Battery"),
|
|
propertyName: "showBattery",
|
|
onToggled: function(checked) {
|
|
root.showBattery = checked;
|
|
root.saveConfig();
|
|
}
|
|
},
|
|
{
|
|
label: qsTr("Capslock"),
|
|
propertyName: "showLockStatus",
|
|
onToggled: function(checked) {
|
|
root.showLockStatus = checked;
|
|
root.saveConfig();
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Appearance.spacing.small
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignTop
|
|
spacing: 0
|
|
|
|
SectionContainer {
|
|
Layout.fillWidth: true
|
|
alignTop: true
|
|
|
|
StyledText {
|
|
text: qsTr("Workspaces")
|
|
font.pointSize: Appearance.font.size.normal
|
|
}
|
|
|
|
StyledRect {
|
|
Layout.fillWidth: true
|
|
implicitHeight: workspacesShownRow.implicitHeight + Appearance.padding.large * 2
|
|
radius: Appearance.rounding.normal
|
|
color: Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
|
|
|
Behavior on implicitHeight {
|
|
Anim {}
|
|
}
|
|
|
|
RowLayout {
|
|
id: workspacesShownRow
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.margins: Appearance.padding.large
|
|
spacing: Appearance.spacing.normal
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
text: qsTr("Shown")
|
|
}
|
|
|
|
CustomSpinBox {
|
|
min: 1
|
|
max: 20
|
|
value: root.workspacesShown
|
|
onValueModified: value => {
|
|
root.workspacesShown = value;
|
|
root.saveConfig();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StyledRect {
|
|
Layout.fillWidth: true
|
|
implicitHeight: workspacesActiveIndicatorRow.implicitHeight + Appearance.padding.large * 2
|
|
radius: Appearance.rounding.normal
|
|
color: Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
|
|
|
Behavior on implicitHeight {
|
|
Anim {}
|
|
}
|
|
|
|
RowLayout {
|
|
id: workspacesActiveIndicatorRow
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.margins: Appearance.padding.large
|
|
spacing: Appearance.spacing.normal
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
text: qsTr("Active indicator")
|
|
}
|
|
|
|
StyledSwitch {
|
|
checked: root.workspacesActiveIndicator
|
|
onToggled: {
|
|
root.workspacesActiveIndicator = checked;
|
|
root.saveConfig();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StyledRect {
|
|
Layout.fillWidth: true
|
|
implicitHeight: workspacesOccupiedBgRow.implicitHeight + Appearance.padding.large * 2
|
|
radius: Appearance.rounding.normal
|
|
color: Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
|
|
|
Behavior on implicitHeight {
|
|
Anim {}
|
|
}
|
|
|
|
RowLayout {
|
|
id: workspacesOccupiedBgRow
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.margins: Appearance.padding.large
|
|
spacing: Appearance.spacing.normal
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
text: qsTr("Occupied background")
|
|
}
|
|
|
|
StyledSwitch {
|
|
checked: root.workspacesOccupiedBg
|
|
onToggled: {
|
|
root.workspacesOccupiedBg = checked;
|
|
root.saveConfig();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StyledRect {
|
|
Layout.fillWidth: true
|
|
implicitHeight: workspacesShowWindowsRow.implicitHeight + Appearance.padding.large * 2
|
|
radius: Appearance.rounding.normal
|
|
color: Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
|
|
|
Behavior on implicitHeight {
|
|
Anim {}
|
|
}
|
|
|
|
RowLayout {
|
|
id: workspacesShowWindowsRow
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.margins: Appearance.padding.large
|
|
spacing: Appearance.spacing.normal
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
text: qsTr("Show windows")
|
|
}
|
|
|
|
StyledSwitch {
|
|
checked: root.workspacesShowWindows
|
|
onToggled: {
|
|
root.workspacesShowWindows = checked;
|
|
root.saveConfig();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StyledRect {
|
|
Layout.fillWidth: true
|
|
implicitHeight: workspacesPerMonitorRow.implicitHeight + Appearance.padding.large * 2
|
|
radius: Appearance.rounding.normal
|
|
color: Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
|
|
|
Behavior on implicitHeight {
|
|
Anim {}
|
|
}
|
|
|
|
RowLayout {
|
|
id: workspacesPerMonitorRow
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.margins: Appearance.padding.large
|
|
spacing: Appearance.spacing.normal
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
text: qsTr("Per monitor workspaces")
|
|
}
|
|
|
|
StyledSwitch {
|
|
checked: root.workspacesPerMonitor
|
|
onToggled: {
|
|
root.workspacesPerMonitor = checked;
|
|
root.saveConfig();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignTop
|
|
spacing: Appearance.spacing.small
|
|
|
|
SectionContainer {
|
|
Layout.fillWidth: true
|
|
alignTop: true
|
|
|
|
StyledText {
|
|
text: qsTr("Clock")
|
|
font.pointSize: Appearance.font.size.normal
|
|
}
|
|
|
|
SwitchRow {
|
|
label: qsTr("Show clock icon")
|
|
checked: root.clockShowIcon
|
|
onToggled: checked => {
|
|
root.clockShowIcon = checked;
|
|
root.saveConfig();
|
|
}
|
|
}
|
|
}
|
|
|
|
SectionContainer {
|
|
Layout.fillWidth: true
|
|
alignTop: true
|
|
|
|
ConnectedButtonGroup {
|
|
rootItem: root
|
|
title: qsTr("Tray Settings")
|
|
|
|
options: [
|
|
{
|
|
label: qsTr("Background"),
|
|
propertyName: "trayBackground",
|
|
onToggled: function(checked) {
|
|
root.trayBackground = checked;
|
|
root.saveConfig();
|
|
}
|
|
},
|
|
{
|
|
label: qsTr("Compact"),
|
|
propertyName: "trayCompact",
|
|
onToggled: function(checked) {
|
|
root.trayCompact = checked;
|
|
root.saveConfig();
|
|
}
|
|
},
|
|
{
|
|
label: qsTr("Recolour"),
|
|
propertyName: "trayRecolour",
|
|
onToggled: function(checked) {
|
|
root.trayRecolour = checked;
|
|
root.saveConfig();
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignTop
|
|
spacing: 0
|
|
|
|
SectionContainer {
|
|
Layout.fillWidth: true
|
|
alignTop: true
|
|
|
|
StyledText {
|
|
text: qsTr("Bar Behavior")
|
|
font.pointSize: Appearance.font.size.normal
|
|
}
|
|
|
|
SwitchRow {
|
|
label: qsTr("Persistent")
|
|
checked: root.persistent
|
|
onToggled: checked => {
|
|
root.persistent = checked;
|
|
root.saveConfig();
|
|
}
|
|
}
|
|
|
|
SwitchRow {
|
|
label: qsTr("Show on hover")
|
|
checked: root.showOnHover
|
|
onToggled: checked => {
|
|
root.showOnHover = checked;
|
|
root.saveConfig();
|
|
}
|
|
}
|
|
|
|
SectionContainer {
|
|
contentSpacing: Appearance.spacing.normal
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Appearance.spacing.small
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Appearance.spacing.normal
|
|
|
|
StyledText {
|
|
text: qsTr("Drag threshold")
|
|
font.pointSize: Appearance.font.size.normal
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
StyledRect {
|
|
Layout.preferredWidth: 70
|
|
implicitHeight: dragThresholdInput.implicitHeight + Appearance.padding.small * 2
|
|
color: dragThresholdInputHover.containsMouse || dragThresholdInput.activeFocus
|
|
? Colours.layer(Colours.palette.m3surfaceContainer, 3)
|
|
: Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
|
radius: Appearance.rounding.small
|
|
border.width: 1
|
|
border.color: dragThresholdInput.activeFocus
|
|
? Colours.palette.m3primary
|
|
: Qt.alpha(Colours.palette.m3outline, 0.3)
|
|
|
|
Behavior on color { CAnim {} }
|
|
Behavior on border.color { CAnim {} }
|
|
|
|
MouseArea {
|
|
id: dragThresholdInputHover
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.IBeamCursor
|
|
acceptedButtons: Qt.NoButton
|
|
}
|
|
|
|
StyledTextField {
|
|
id: dragThresholdInput
|
|
anchors.centerIn: parent
|
|
width: parent.width - Appearance.padding.normal
|
|
horizontalAlignment: TextInput.AlignHCenter
|
|
validator: IntValidator { bottom: 0; top: 100 }
|
|
|
|
Component.onCompleted: {
|
|
text = root.dragThreshold.toString();
|
|
}
|
|
|
|
onTextChanged: {
|
|
if (activeFocus) {
|
|
const val = parseInt(text);
|
|
if (!isNaN(val) && val >= 0 && val <= 100) {
|
|
root.dragThreshold = val;
|
|
root.saveConfig();
|
|
}
|
|
}
|
|
}
|
|
onEditingFinished: {
|
|
const val = parseInt(text);
|
|
if (isNaN(val) || val < 0 || val > 100) {
|
|
text = root.dragThreshold.toString();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
text: "px"
|
|
color: Colours.palette.m3outline
|
|
font.pointSize: Appearance.font.size.normal
|
|
}
|
|
}
|
|
|
|
StyledSlider {
|
|
id: dragThresholdSlider
|
|
|
|
Layout.fillWidth: true
|
|
implicitHeight: Appearance.padding.normal * 3
|
|
|
|
from: 0
|
|
to: 100
|
|
value: root.dragThreshold
|
|
onMoved: {
|
|
root.dragThreshold = Math.round(dragThresholdSlider.value);
|
|
if (!dragThresholdInput.activeFocus) {
|
|
dragThresholdInput.text = Math.round(dragThresholdSlider.value).toString();
|
|
}
|
|
root.saveConfig();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|