bar: allow hiding items and reordering (#379)
* feat: reorder bar, no popout yet * chore: cleanup * refactor: use DelegateChooser * feat: popouts * chore: cleanup * better popout check + fix async stuff + bar interaction + a bunch of other fixes * fix activewindow and bar vertical padding * readme: add config opt * bar: fix top/bottom padding * bar: better wheel behaviour --------- Co-authored-by: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>
This commit is contained in:
parent
e34953f595
commit
3710df29f5
11 changed files with 381 additions and 320 deletions
38
README.md
38
README.md
|
|
@ -198,6 +198,44 @@ All configuration options are in `~/.config/caelestia/shell.json`.
|
|||
},
|
||||
"bar": {
|
||||
"dragThreshold": 20,
|
||||
"entries": [
|
||||
{
|
||||
"id": "logo",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "workspaces",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "spacer",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "activeWindow",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "spacer",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "tray",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "clock",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "statusIcons",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "power",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"persistent": true,
|
||||
"showOnHover": true,
|
||||
"status": {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,45 @@ JsonObject {
|
|||
property Status status: Status {}
|
||||
property Sizes sizes: Sizes {}
|
||||
|
||||
property list<var> entries: [
|
||||
{
|
||||
id: "logo",
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: "workspaces",
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: "spacer",
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: "activeWindow",
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: "spacer",
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: "tray",
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: "clock",
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: "statusIcons",
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: "power",
|
||||
enabled: true
|
||||
},
|
||||
]
|
||||
|
||||
component Workspaces: JsonObject {
|
||||
property int shown: 5
|
||||
property bool rounded: true
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import qs.components
|
||||
import qs.components.controls
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.services
|
||||
import qs.config
|
||||
import "popouts" as BarPopouts
|
||||
|
|
@ -7,170 +7,176 @@ import "components"
|
|||
import "components/workspaces"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
required property PersistentProperties visibilities
|
||||
required property BarPopouts.Wrapper popouts
|
||||
readonly property int padding: Math.max(Appearance.padding.smaller, Config.border.thickness)
|
||||
readonly property int vPadding: Appearance.padding.large
|
||||
|
||||
function checkPopout(y: real): void {
|
||||
const spacing = Appearance.spacing.small;
|
||||
const aw = activeWindow.child;
|
||||
const awy = activeWindow.y + aw.y;
|
||||
|
||||
const ty = tray.y;
|
||||
const th = tray.implicitHeight;
|
||||
const trayItems = tray.items;
|
||||
|
||||
// Check status icons hover areas
|
||||
let statusIconFound = false;
|
||||
for (const area of statusIconsInner.hoverAreas) {
|
||||
if (!area.enabled)
|
||||
continue;
|
||||
|
||||
const item = area.item;
|
||||
const itemY = statusIcons.y + statusIconsInner.y + item.y - spacing / 2;
|
||||
const itemHeight = item.implicitHeight + spacing;
|
||||
|
||||
if (y >= itemY && y <= itemY + itemHeight) {
|
||||
popouts.currentName = area.name;
|
||||
popouts.currentCenter = Qt.binding(() => statusIcons.y + statusIconsInner.y + item.y + item.implicitHeight / 2);
|
||||
popouts.hasCurrent = true;
|
||||
statusIconFound = true;
|
||||
break;
|
||||
}
|
||||
const ch = childAt(width / 2, y) as WrappedLoader;
|
||||
if (!ch) {
|
||||
popouts.hasCurrent = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (y >= awy && y <= awy + aw.implicitHeight) {
|
||||
popouts.currentName = "activewindow";
|
||||
popouts.currentCenter = Qt.binding(() => activeWindow.y + aw.y + aw.implicitHeight / 2);
|
||||
popouts.hasCurrent = true;
|
||||
} else if (y > ty && y < ty + th) {
|
||||
const index = Math.floor(((y - ty) / th) * trayItems.count);
|
||||
const item = trayItems.itemAt(index);
|
||||
const id = ch.id;
|
||||
const top = ch.y;
|
||||
const item = ch.item;
|
||||
const itemHeight = item.implicitHeight;
|
||||
|
||||
popouts.currentName = `traymenu${index}`;
|
||||
popouts.currentCenter = Qt.binding(() => tray.y + item.y + item.implicitHeight / 2);
|
||||
if (id === "statusIcons") {
|
||||
const items = item.items;
|
||||
const icon = items.childAt(items.width / 2, mapToItem(items, 0, y).y);
|
||||
if (icon) {
|
||||
popouts.currentName = icon.name;
|
||||
popouts.currentCenter = Qt.binding(() => icon.mapToItem(root, 0, icon.implicitHeight / 2).y);
|
||||
popouts.hasCurrent = true;
|
||||
}
|
||||
} else if (id === "tray") {
|
||||
const index = Math.floor(((y - top) / itemHeight) * item.items.count);
|
||||
const trayItem = item.items.itemAt(index);
|
||||
if (trayItem) {
|
||||
popouts.currentName = `traymenu${index}`;
|
||||
popouts.currentCenter = Qt.binding(() => trayItem.mapToItem(root, 0, trayItem.implicitHeight / 2).y);
|
||||
popouts.hasCurrent = true;
|
||||
}
|
||||
} else if (id === "activeWindow") {
|
||||
popouts.currentName = id.toLowerCase();
|
||||
popouts.currentCenter = item.mapToItem(root, 0, itemHeight / 2).y;
|
||||
popouts.hasCurrent = true;
|
||||
} else if (!statusIconFound) {
|
||||
popouts.hasCurrent = false;
|
||||
}
|
||||
}
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
|
||||
implicitWidth: child.implicitWidth + Math.max(Appearance.padding.smaller, Config.border.thickness) * 2
|
||||
|
||||
Item {
|
||||
id: child
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
implicitWidth: Math.max(osIcon.implicitWidth, workspaces.implicitWidth, activeWindow.implicitWidth, tray.implicitWidth, clock.implicitWidth, statusIcons.implicitWidth, power.implicitWidth)
|
||||
|
||||
OsIcon {
|
||||
id: osIcon
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Appearance.padding.large
|
||||
function handleWheel(y: real, angleDelta: point): void {
|
||||
const ch = childAt(width / 2, y) as WrappedLoader;
|
||||
if (ch?.id === "workspaces") {
|
||||
// Workspace scroll
|
||||
const activeWs = Hyprland.activeToplevel?.workspace?.name;
|
||||
if (activeWs?.startsWith("special:"))
|
||||
Hyprland.dispatch(`togglespecialworkspace ${activeWs.slice(8)}`);
|
||||
else if (angleDelta.y < 0 || Hyprland.activeWsId > 1)
|
||||
Hyprland.dispatch(`workspace r${angleDelta.y > 0 ? "-" : "+"}1`);
|
||||
} else if (y < screen.height / 2) {
|
||||
// Volume scroll on top half
|
||||
if (angleDelta.y > 0)
|
||||
Audio.incrementVolume();
|
||||
else if (angleDelta.y < 0)
|
||||
Audio.decrementVolume();
|
||||
} else {
|
||||
// Brightness scroll on bottom half
|
||||
const monitor = Brightness.getMonitorForScreen(screen);
|
||||
if (angleDelta.y > 0)
|
||||
monitor.setBrightness(monitor.brightness + 0.1);
|
||||
else if (angleDelta.y < 0)
|
||||
monitor.setBrightness(monitor.brightness - 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
id: workspaces
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: osIcon.bottom
|
||||
anchors.topMargin: Appearance.spacing.normal
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
model: Config.bar.entries
|
||||
|
||||
implicitWidth: workspacesInner.implicitWidth + Appearance.padding.small * 2
|
||||
implicitHeight: workspacesInner.implicitHeight + Appearance.padding.small * 2
|
||||
DelegateChooser {
|
||||
role: "id"
|
||||
|
||||
CustomMouseArea {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: -Math.max(Appearance.padding.smaller, Config.border.thickness)
|
||||
anchors.rightMargin: -Math.max(Appearance.padding.smaller, Config.border.thickness)
|
||||
|
||||
function onWheel(event: WheelEvent): void {
|
||||
const activeWs = Hyprland.activeToplevel?.workspace?.name;
|
||||
if (activeWs?.startsWith("special:"))
|
||||
Hyprland.dispatch(`togglespecialworkspace ${activeWs.slice(8)}`);
|
||||
else if (event.angleDelta.y < 0 || Hyprland.activeWsId > 1)
|
||||
Hyprland.dispatch(`workspace r${event.angleDelta.y > 0 ? "-" : "+"}1`);
|
||||
DelegateChoice {
|
||||
roleValue: "spacer"
|
||||
delegate: WrappedLoader {
|
||||
Layout.fillHeight: enabled
|
||||
}
|
||||
}
|
||||
|
||||
Workspaces {
|
||||
id: workspacesInner
|
||||
|
||||
anchors.centerIn: parent
|
||||
DelegateChoice {
|
||||
roleValue: "logo"
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: OsIcon {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActiveWindow {
|
||||
id: activeWindow
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: workspaces.bottom
|
||||
anchors.bottom: tray.top
|
||||
anchors.margins: Appearance.spacing.large
|
||||
|
||||
monitor: Brightness.getMonitorForScreen(root.screen)
|
||||
}
|
||||
|
||||
Tray {
|
||||
id: tray
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: clock.top
|
||||
anchors.bottomMargin: Appearance.spacing.larger
|
||||
}
|
||||
|
||||
Clock {
|
||||
id: clock
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: statusIcons.top
|
||||
anchors.bottomMargin: Appearance.spacing.normal
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
id: statusIcons
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: power.top
|
||||
anchors.bottomMargin: Appearance.spacing.normal
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
|
||||
implicitHeight: statusIconsInner.implicitHeight + Appearance.padding.normal * 2
|
||||
|
||||
StatusIcons {
|
||||
id: statusIconsInner
|
||||
|
||||
anchors.centerIn: parent
|
||||
DelegateChoice {
|
||||
roleValue: "workspaces"
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: Workspaces {}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "activeWindow"
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: ActiveWindow {
|
||||
bar: root
|
||||
monitor: Brightness.getMonitorForScreen(root.screen)
|
||||
}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "tray"
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: Tray {}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "clock"
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: Clock {}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "statusIcons"
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: StatusIcons {}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "power"
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: Power {
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Power {
|
||||
id: power
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Appearance.padding.large
|
||||
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
component WrappedLoader: Loader {
|
||||
required property bool enabled
|
||||
required property string id
|
||||
required property int index
|
||||
|
||||
function findFirstEnabled(): Item {
|
||||
const count = repeater.count;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const item = repeater.itemAt(i);
|
||||
if (item?.enabled)
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findLastEnabled(): Item {
|
||||
for (let i = repeater.count - 1; i >= 0; i--) {
|
||||
const item = repeater.itemAt(i);
|
||||
if (item?.enabled)
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.leftMargin: root.padding
|
||||
Layout.rightMargin: root.padding
|
||||
|
||||
// Cursed ahh thing to add padding to first and last enabled components
|
||||
Layout.topMargin: findFirstEnabled() === this ? root.vPadding : 0
|
||||
Layout.bottomMargin: findLastEnabled() === this ? root.vPadding : 0
|
||||
|
||||
visible: enabled
|
||||
active: enabled
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,19 +14,24 @@ Item {
|
|||
required property BarPopouts.Wrapper popouts
|
||||
|
||||
readonly property int exclusiveZone: Config.bar.persistent || visibilities.bar ? content.implicitWidth : Config.border.thickness
|
||||
readonly property bool shouldBeVisible: Config.bar.persistent || visibilities.bar || isHovered
|
||||
property bool isHovered
|
||||
|
||||
function checkPopout(y: real): void {
|
||||
content.item?.checkPopout(y);
|
||||
}
|
||||
|
||||
function handleWheel(y: real, angleDelta: point): void {
|
||||
content.item?.handleWheel(y, angleDelta);
|
||||
}
|
||||
|
||||
visible: width > Config.border.thickness
|
||||
implicitWidth: Config.border.thickness
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: Config.bar.persistent || root.visibilities.bar || root.isHovered
|
||||
when: root.shouldBeVisible
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitWidth: content.implicitWidth
|
||||
|
|
@ -63,7 +68,7 @@ Item {
|
|||
Loader {
|
||||
id: content
|
||||
|
||||
Component.onCompleted: active = Qt.binding(() => Config.bar.persistent || root.visibilities.bar || root.isHovered || root.visible)
|
||||
Component.onCompleted: active = Qt.binding(() => root.shouldBeVisible || root.visible)
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
|
|
|||
|
|
@ -9,73 +9,62 @@ import QtQuick
|
|||
Item {
|
||||
id: root
|
||||
|
||||
required property var bar
|
||||
required property Brightness.Monitor monitor
|
||||
property color colour: Colours.palette.m3primary
|
||||
readonly property Item child: child
|
||||
|
||||
implicitWidth: child.implicitWidth
|
||||
implicitHeight: child.implicitHeight
|
||||
readonly property int maxHeight: {
|
||||
const otherModules = bar.children.filter(c => c.id && c.item !== this && c.id !== "spacer");
|
||||
const otherHeight = otherModules.reduce((acc, curr) => acc + curr.height, 0);
|
||||
// Length - 2 cause repeater counts as a child
|
||||
return bar.height - otherHeight - bar.spacing * (bar.children.length - 1) - bar.vPadding * 2;
|
||||
}
|
||||
property Title current: text1
|
||||
|
||||
Item {
|
||||
id: child
|
||||
clip: true
|
||||
implicitWidth: Math.max(icon.implicitWidth, current.implicitHeight)
|
||||
implicitHeight: icon.implicitHeight + current.implicitWidth + current.anchors.topMargin
|
||||
|
||||
property Item current: text1
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
clip: true
|
||||
implicitWidth: Math.max(icon.implicitWidth, current.implicitHeight)
|
||||
implicitHeight: icon.implicitHeight + current.implicitWidth + current.anchors.topMargin
|
||||
animate: true
|
||||
text: Icons.getAppCategoryIcon(Hyprland.activeToplevel?.lastIpcObject.class, "desktop_windows")
|
||||
color: root.colour
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
Title {
|
||||
id: text1
|
||||
}
|
||||
|
||||
animate: true
|
||||
text: Icons.getAppCategoryIcon(Hyprland.activeToplevel?.lastIpcObject.class, "desktop_windows")
|
||||
color: root.colour
|
||||
Title {
|
||||
id: text2
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
|
||||
text: Hyprland.activeToplevel?.title ?? qsTr("Desktop")
|
||||
font.pointSize: Appearance.font.size.smaller
|
||||
font.family: Appearance.font.family.mono
|
||||
elide: Qt.ElideRight
|
||||
elideWidth: root.maxHeight - icon.height
|
||||
|
||||
onTextChanged: {
|
||||
const next = root.current === text1 ? text2 : text1;
|
||||
next.text = elidedText;
|
||||
root.current = next;
|
||||
}
|
||||
onElideWidthChanged: root.current.text = elidedText
|
||||
}
|
||||
|
||||
Title {
|
||||
id: text1
|
||||
}
|
||||
|
||||
Title {
|
||||
id: text2
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
|
||||
text: Hyprland.activeToplevel?.title ?? qsTr("Desktop")
|
||||
font.pointSize: Appearance.font.size.smaller
|
||||
font.family: Appearance.font.family.mono
|
||||
elide: Qt.ElideRight
|
||||
elideWidth: root.height - icon.height
|
||||
|
||||
onTextChanged: {
|
||||
const next = child.current === text1 ? text2 : text1;
|
||||
next.text = elidedText;
|
||||
child.current = next;
|
||||
}
|
||||
onElideWidthChanged: child.current.text = elidedText
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,7 +78,7 @@ Item {
|
|||
font.pointSize: metrics.font.pointSize
|
||||
font.family: metrics.font.family
|
||||
color: root.colour
|
||||
opacity: child.current === this ? 1 : 0
|
||||
opacity: root.current === this ? 1 : 0
|
||||
|
||||
transform: Rotation {
|
||||
angle: 90
|
||||
|
|
|
|||
|
|
@ -2,24 +2,22 @@ import qs.components
|
|||
import qs.services
|
||||
import qs.config
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
MaterialIcon {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
text: "power_settings_new"
|
||||
color: Colours.palette.m3error
|
||||
font.bold: true
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
implicitWidth: icon.implicitHeight + Appearance.padding.small * 2
|
||||
implicitHeight: icon.implicitHeight
|
||||
|
||||
StateLayer {
|
||||
// Cursed workaround to make the height larger than the parent
|
||||
anchors.fill: undefined
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: 1
|
||||
|
||||
implicitWidth: parent.implicitHeight + Appearance.padding.small * 2
|
||||
implicitHeight: implicitWidth
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: icon.implicitHeight + Appearance.padding.small * 2
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
|
|
@ -27,4 +25,16 @@ MaterialIcon {
|
|||
root.visibilities.session = !root.visibilities.session;
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: -1
|
||||
|
||||
text: "power_settings_new"
|
||||
color: Colours.palette.m3error
|
||||
font.bold: true
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,51 +10,29 @@ import Quickshell.Services.UPower
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
property color colour: Colours.palette.m3secondary
|
||||
readonly property alias items: iconColumn
|
||||
|
||||
readonly property list<var> hoverAreas: [
|
||||
{
|
||||
name: "audio",
|
||||
item: audioIcon,
|
||||
enabled: Config.bar.status.showAudio
|
||||
},
|
||||
{
|
||||
name: "network",
|
||||
item: networkIcon,
|
||||
enabled: Config.bar.status.showNetwork
|
||||
},
|
||||
{
|
||||
name: "bluetooth",
|
||||
item: bluetoothGroup,
|
||||
enabled: Config.bar.status.showBluetooth
|
||||
},
|
||||
{
|
||||
name: "battery",
|
||||
item: batteryIcon,
|
||||
enabled: Config.bar.status.showBattery
|
||||
}
|
||||
]
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
clip: true
|
||||
implicitWidth: iconColumn.implicitWidth
|
||||
implicitHeight: iconColumn.implicitHeight
|
||||
implicitWidth: iconColumn.implicitWidth + Appearance.padding.normal * 2
|
||||
implicitHeight: iconColumn.implicitHeight + Appearance.padding.normal * 2
|
||||
|
||||
ColumnLayout {
|
||||
id: iconColumn
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.centerIn: parent
|
||||
spacing: Appearance.spacing.smaller / 2
|
||||
|
||||
// Audio icon
|
||||
Loader {
|
||||
id: audioIcon
|
||||
|
||||
asynchronous: true
|
||||
WrappedLoader {
|
||||
name: "audio"
|
||||
active: Config.bar.status.showAudio
|
||||
visible: active
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
animate: true
|
||||
|
|
@ -65,8 +43,6 @@ Item {
|
|||
|
||||
// Keyboard layout icon
|
||||
Loader {
|
||||
id: kbLayout
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
asynchronous: true
|
||||
active: Config.bar.status.showKbLayout
|
||||
|
|
@ -81,12 +57,9 @@ Item {
|
|||
}
|
||||
|
||||
// Network icon
|
||||
Loader {
|
||||
id: networkIcon
|
||||
|
||||
asynchronous: true
|
||||
WrappedLoader {
|
||||
name: "network"
|
||||
active: Config.bar.status.showNetwork
|
||||
visible: active
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
animate: true
|
||||
|
|
@ -95,13 +68,10 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
// Bluetooth section (grouped for hover area)
|
||||
Loader {
|
||||
id: bluetoothGroup
|
||||
|
||||
asynchronous: true
|
||||
// Bluetooth section
|
||||
WrappedLoader {
|
||||
name: "bluetooth"
|
||||
active: Config.bar.status.showBluetooth
|
||||
visible: active
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Appearance.spacing.smaller / 2
|
||||
|
|
@ -157,12 +127,9 @@ Item {
|
|||
}
|
||||
|
||||
// Battery icon
|
||||
Loader {
|
||||
id: batteryIcon
|
||||
|
||||
asynchronous: true
|
||||
WrappedLoader {
|
||||
name: "battery"
|
||||
active: Config.bar.status.showBattery
|
||||
visible: active
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
animate: true
|
||||
|
|
@ -194,6 +161,14 @@ Item {
|
|||
Anim {}
|
||||
}
|
||||
|
||||
component WrappedLoader: Loader {
|
||||
required property string name
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
asynchronous: true
|
||||
visible: active
|
||||
}
|
||||
|
||||
component Anim: NumberAnimation {
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.type: Easing.BezierSpline
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ pragma ComponentBehavior: Bound
|
|||
|
||||
import qs.services
|
||||
import qs.config
|
||||
import qs.components
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
readonly property list<Workspace> workspaces: layout.children.filter(c => c.isWorkspace).sort((w1, w2) => w1.ws - w2.ws)
|
||||
|
|
@ -15,60 +16,69 @@ Item {
|
|||
}, {})
|
||||
readonly property int groupOffset: Math.floor((Hyprland.activeWsId - 1) / Config.bar.workspaces.shown) * Config.bar.workspaces.shown
|
||||
|
||||
implicitWidth: layout.implicitWidth
|
||||
implicitHeight: layout.implicitHeight
|
||||
implicitWidth: layout.implicitWidth + Appearance.padding.small * 2
|
||||
implicitHeight: layout.implicitHeight + Appearance.padding.small * 2
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
Item {
|
||||
id: inner
|
||||
|
||||
spacing: 0
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.small
|
||||
|
||||
Repeater {
|
||||
model: Config.bar.workspaces.shown
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
Workspace {
|
||||
spacing: 0
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
|
||||
Repeater {
|
||||
model: Config.bar.workspaces.shown
|
||||
|
||||
Workspace {
|
||||
occupied: root.occupied
|
||||
groupOffset: root.groupOffset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: Config.bar.workspaces.occupiedBg
|
||||
asynchronous: true
|
||||
|
||||
z: -1
|
||||
anchors.fill: parent
|
||||
|
||||
sourceComponent: OccupiedBg {
|
||||
workspaces: root.workspaces
|
||||
occupied: root.occupied
|
||||
groupOffset: root.groupOffset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: Config.bar.workspaces.occupiedBg
|
||||
asynchronous: true
|
||||
Loader {
|
||||
active: Config.bar.workspaces.activeIndicator
|
||||
asynchronous: true
|
||||
|
||||
z: -1
|
||||
anchors.fill: parent
|
||||
|
||||
sourceComponent: OccupiedBg {
|
||||
workspaces: root.workspaces
|
||||
occupied: root.occupied
|
||||
groupOffset: root.groupOffset
|
||||
sourceComponent: ActiveIndicator {
|
||||
workspaces: root.workspaces
|
||||
mask: layout
|
||||
maskWidth: inner.width
|
||||
maskHeight: inner.height
|
||||
groupOffset: root.groupOffset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: Config.bar.workspaces.activeIndicator
|
||||
asynchronous: true
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
sourceComponent: ActiveIndicator {
|
||||
workspaces: root.workspaces
|
||||
mask: layout
|
||||
maskWidth: root.width
|
||||
maskHeight: root.height
|
||||
groupOffset: root.groupOffset
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onPressed: event => {
|
||||
const ws = layout.childAt(event.x, event.y).index + root.groupOffset + 1;
|
||||
if (Hyprland.activeWsId !== ws)
|
||||
Hyprland.dispatch(`workspace ${ws}`);
|
||||
onPressed: event => {
|
||||
const ws = layout.childAt(event.x, event.y).index + root.groupOffset + 1;
|
||||
if (Hyprland.activeWsId !== ws)
|
||||
Hyprland.dispatch(`workspace ${ws}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,17 +133,17 @@ Variants {
|
|||
visibilities: visibilities
|
||||
bar: bar
|
||||
}
|
||||
}
|
||||
|
||||
BarWrapper {
|
||||
id: bar
|
||||
BarWrapper {
|
||||
id: bar
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
screen: scope.modelData
|
||||
visibilities: visibilities
|
||||
popouts: panels.popouts
|
||||
screen: scope.modelData
|
||||
visibilities: visibilities
|
||||
popouts: panels.popouts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,18 +45,7 @@ CustomMouseArea {
|
|||
|
||||
function onWheel(event: WheelEvent): void {
|
||||
if (event.x < bar.implicitWidth) {
|
||||
if (event.y < screen.height / 2) {
|
||||
if (event.angleDelta.y > 0)
|
||||
Audio.incrementVolume();
|
||||
else if (event.angleDelta.y < 0)
|
||||
Audio.decrementVolume();
|
||||
} else {
|
||||
const monitor = Brightness.getMonitorForScreen(screen);
|
||||
if (event.angleDelta.y > 0)
|
||||
monitor.setBrightness(monitor.brightness + 0.1);
|
||||
else if (event.angleDelta.y < 0)
|
||||
monitor.setBrightness(monitor.brightness - 0.1);
|
||||
}
|
||||
bar.handleWheel(event.y, event.angleDelta);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ Item {
|
|||
const diff = root.height - Math.floor(off + nonAnimHeight);
|
||||
if (diff < 0)
|
||||
return off + diff;
|
||||
return off;
|
||||
return Math.max(off, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue