bar: switch to embedded style

This commit is contained in:
2 * r + 2 * t 2025-05-23 16:50:12 +08:00
parent b59cffa5ea
commit 36f28f7c42
12 changed files with 226 additions and 482 deletions

View file

@ -1,5 +1,4 @@
pragma Singleton
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
@ -7,26 +6,16 @@ import QtQuick
Singleton {
id: root
property bool vertical: true
property Preset preset: presets.pills
readonly property Sizes sizes: preset.sizes
readonly property Workspaces workspaces: preset.workspaces
readonly property Tray tray: preset.tray
readonly property Presets presets: Presets {}
readonly property Sizes sizes: Sizes {}
readonly property Workspaces workspaces: Workspaces {}
readonly property Tray tray: Tray {}
component Sizes: QtObject {
property int exclusiveZone: totalHeight
property int totalHeight: height
property int height: 40
property int innerHeight: 30
property int floatingGap: 15
property int maxLabelWidth: 600
property int maxLabelHeight: 400
}
component Workspaces: QtObject {
property int shown: root.vertical ? 5 : 10
property int shown: 5
property bool rounded: true
property bool activeIndicator: true
property bool occupiedBg: false
@ -40,37 +29,4 @@ Singleton {
component Tray: QtObject {
property bool recolourIcons: false
}
component Preset: QtObject {
required property string name
property Sizes sizes: Sizes {}
property Workspaces workspaces: Workspaces {}
property Tray tray: Tray {}
}
component Presets: QtObject {
readonly property Preset pills: Preset {
name: "pills"
sizes: Sizes {
totalHeight: height + floatingGap * 2
exclusiveZone: totalHeight - floatingGap
}
}
readonly property Preset panel: Preset {
name: "panel"
sizes: Sizes {
height: 30
}
workspaces: Workspaces {
rounded: false
showWindows: false
label: ""
occupiedLabel: ""
activeLabel: ""
}
tray: Tray {
recolourIcons: true
}
}
}
}

View file

@ -14,121 +14,16 @@ Variants {
screen: modelData
name: "bar"
exclusiveZone: BarConfig.sizes.exclusiveZone
implicitWidth: BarConfig.vertical ? BarConfig.sizes.totalHeight : -1
implicitHeight: BarConfig.vertical ? -1 : BarConfig.sizes.totalHeight
implicitWidth: content.implicitWidth
anchors.top: true
anchors.bottom: true
anchors.left: true
Component.onCompleted: {
if (BarConfig.vertical)
win.anchors.bottom = true;
else
win.anchors.right = true;
}
Connections {
target: BarConfig
function onVerticalChanged(): void {
win.visible = false;
if (BarConfig.vertical) {
win.anchors.right = false;
win.anchors.bottom = true;
} else {
win.anchors.bottom = false;
win.anchors.right = true;
}
win.visible = true;
}
}
Item {
Content {
id: content
anchors.fill: parent
Preset {
presetName: "pills"
sourceComponent: Pills {
screen: win.modelData
}
}
Preset {
presetName: "panel"
sourceComponent: Panel {
screen: win.modelData
}
}
}
LayerShadow {
source: content
}
}
component Preset: Loader {
id: loader
required property string presetName
anchors.fill: parent
asynchronous: true
active: false
opacity: 0
states: State {
name: "visible"
when: BarConfig.preset.name === loader.presetName
PropertyChanges {
loader.opacity: 1
loader.active: true
}
}
transitions: [
Transition {
from: ""
to: "visible"
SequentialAnimation {
PropertyAction {}
NumberAnimation {
property: "opacity"
duration: Appearance.anim.durations.large
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}
},
Transition {
from: "visible"
to: ""
SequentialAnimation {
NumberAnimation {
property: "opacity"
duration: Appearance.anim.durations.large
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
PropertyAction {}
}
}
]
Connections {
target: BarConfig
function onVerticalChanged(): void {
if (loader.state === "visible") {
loader.active = false;
loader.active = true;
}
}
screen: win.modelData
}
}
}

155
modules/bar/Content.qml Normal file
View file

@ -0,0 +1,155 @@
import "root:/widgets"
import "root:/services"
import "root:/config"
import "components"
import "components/workspaces"
import Quickshell
import Quickshell.Widgets
import QtQuick
StyledRect {
id: root
required property ShellScreen screen
anchors.top: parent.top
anchors.bottom: parent.bottom
implicitWidth: child.implicitWidth + BorderConfig.thickness
color: BorderConfig.colour
Component.onCompleted: Visibilities.bar = this
Item {
id: child
readonly property bool vertical: true
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
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
}
ClippingRectangle {
id: workspaces
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: osIcon.bottom
anchors.topMargin: Appearance.spacing.normal
radius: Appearance.rounding.full
color: Colours.palette.m3surfaceContainer
implicitWidth: workspacesInner.implicitWidth + Appearance.spacing.small
implicitHeight: workspacesInner.implicitHeight + Appearance.spacing.small * 2
Workspaces {
id: workspacesInner
anchors.centerIn: parent
vertical: true
}
Behavior on color {
ColorAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}
}
MouseArea {
anchors.top: workspaces.bottom
anchors.bottom: activeWindow.top
anchors.left: parent.left
anchors.right: parent.right
onWheel: event => {
if (event.angleDelta.y > 0)
Audio.setVolume(Audio.volume + 0.1);
else if (event.angleDelta.y < 0)
Audio.setVolume(Audio.volume - 0.1);
}
}
ActiveWindow {
id: activeWindow
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: workspaces.bottom
anchors.bottom: tray.top
anchors.margins: Appearance.spacing.large
}
MouseArea {
anchors.top: workspaces.bottom
anchors.bottom: activeWindow.top
anchors.left: parent.left
anchors.right: parent.right
onWheel: event => {
const monitor = Brightness.getMonitorForScreen(root.screen);
if (event.angleDelta.y > 0)
monitor.setBrightness(monitor.brightness + 0.1);
else if (event.angleDelta.y < 0)
monitor.setBrightness(monitor.brightness - 0.1);
}
}
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.palette.m3surfaceContainer
implicitHeight: statusIconsInner.implicitHeight + Appearance.padding.normal * 2
StatusIcons {
id: statusIconsInner
anchors.centerIn: parent
}
}
Power {
id: power
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: Appearance.padding.large
}
}
}

View file

@ -1,143 +0,0 @@
import "root:/widgets"
import "root:/services"
import "root:/config"
import "components"
import "components/workspaces"
import Quickshell
import QtQuick
import QtQuick.Layouts
StyledRect {
id: root
required property ShellScreen screen
function get(horiz, vert) {
return BarConfig.vertical ? vert : horiz;
}
vertical: BarConfig.vertical
color: Colours.alpha(Colours.palette.m3surface, false)
anchors.fill: parent
BoxLayout {
spacing: 0 //Appearance.padding.large
anchors.fill: parent
BoxLayout {
spacing: 0
Module {
color: Colours.palette.mauve
OsIcon {
color: Colours.on(Colours.palette.mauve)
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
}
}
Module {
color: Colours.palette.pink
ActiveWindow {
colour: Colours.on(Colours.palette.pink)
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
}
}
}
MouseArea {
Layout.fillWidth: true
Layout.fillHeight: true
onWheel: event => {
if (event.angleDelta.y > 0)
Audio.setVolume(Audio.volume + 0.1);
else if (event.angleDelta.y < 0)
Audio.setVolume(Audio.volume - 0.1);
}
}
MouseArea {
Layout.fillWidth: true
Layout.fillHeight: true
onWheel: event => {
const monitor = Brightness.getMonitorForScreen(root.screen);
if (event.angleDelta.y > 0)
monitor.setBrightness(monitor.brightness + 0.1);
else if (event.angleDelta.y < 0)
monitor.setBrightness(monitor.brightness - 0.1);
}
}
Module {
color: Colours.palette.green
Clock {
colour: Colours.on(Colours.palette.green)
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
}
}
Module {
color: Colours.palette.yellow
Tray {
colour: Colours.on(Colours.palette.yellow)
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
}
}
Module {
color: Colours.palette.peach
StatusIcons {
colour: Colours.on(Colours.palette.peach)
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
}
}
Module {
color: Colours.palette.m3error
Layout.maximumWidth: BarConfig.sizes.height
Layout.maximumHeight: BarConfig.sizes.height
Power {
x: (BarConfig.sizes.height - width) / 2
y: (BarConfig.sizes.height - height) / 2
color: Colours.palette.m3onError
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
}
}
}
Workspaces {
vertical: BarConfig.vertical
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
component Module: PaddedRect {
padding: BarConfig.vertical ? [Appearance.padding.large, 0] : [0, Appearance.padding.large]
Layout.minimumWidth: BarConfig.sizes.height
Layout.minimumHeight: BarConfig.sizes.height
}
}

View file

@ -1,135 +0,0 @@
import "root:/widgets"
import "root:/services"
import "root:/config"
import "components"
import "components/workspaces"
import Quickshell
import QtQuick
import QtQuick.Layouts
BoxLayout {
id: root
required property ShellScreen screen
function get(horiz, vert) {
return BarConfig.vertical ? vert : horiz;
}
vertical: BarConfig.vertical
spacing: Appearance.padding.large
anchors.fill: parent
anchors.margins: BarConfig.sizes.floatingGap
anchors.rightMargin: get(BarConfig.sizes.floatingGap, 0)
anchors.bottomMargin: get(0, BarConfig.sizes.floatingGap)
Pill {
OsIcon {
id: osIcon
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
}
Workspaces {
vertical: BarConfig.vertical
anchors.left: root.get(osIcon.right, undefined)
anchors.leftMargin: root.get(Appearance.padding.large, 0)
anchors.top: root.get(undefined, osIcon.bottom)
anchors.topMargin: root.get(0, Appearance.padding.large)
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
}
}
MouseArea {
Layout.fillWidth: true
Layout.fillHeight: true
onWheel: event => {
if (event.angleDelta.y > 0)
Audio.setVolume(Audio.volume + 0.1);
else if (event.angleDelta.y < 0)
Audio.setVolume(Audio.volume - 0.1);
}
}
Pill {
ActiveWindow {
vertical: BarConfig.vertical
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
}
}
MouseArea {
Layout.fillWidth: true
Layout.fillHeight: true
onWheel: event => {
const monitor = Brightness.getMonitorForScreen(root.screen);
if (event.angleDelta.y > 0)
monitor.setBrightness(monitor.brightness + 0.1);
else if (event.angleDelta.y < 0)
monitor.setBrightness(monitor.brightness - 0.1);
}
}
Pill {
Tray {
vertical: BarConfig.vertical
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
}
}
Pill {
Clock {
id: clock
vertical: BarConfig.vertical
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
}
StatusIcons {
anchors.left: root.get(clock.right, undefined)
anchors.leftMargin: root.get(Appearance.padding.large, 0)
anchors.top: root.get(undefined, clock.bottom)
anchors.topMargin: root.get(0, Appearance.padding.large)
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
}
}
Pill {
// Make circle
Layout.maximumWidth: BarConfig.sizes.height
Layout.maximumHeight: BarConfig.sizes.height
Power {
// Center in pill
x: (BarConfig.sizes.height - width) / 2
y: (BarConfig.sizes.height - height) / 2
anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter)
anchors.verticalCenter: root.get(parent.verticalCenter, undefined)
}
}
component Pill: PaddedRect {
color: Colours.alpha(Colours.palette.m3surface, false)
radius: Appearance.rounding.full
padding: BarConfig.vertical ? [Appearance.padding.large, 0] : [0, Appearance.padding.large]
Layout.minimumWidth: BarConfig.sizes.height
Layout.minimumHeight: BarConfig.sizes.height
}
}

View file

@ -6,66 +6,76 @@ import "root:/utils"
import "root:/config"
import QtQuick
StyledRect {
Item {
id: root
readonly property bool vertical: parent?.vertical ?? false
property color colour: Colours.palette.pink
clip: true
implicitWidth: child.implicitWidth
implicitHeight: child.implicitHeight
MaterialIcon {
id: icon
StyledRect {
id: child
animate: true
text: Icons.getAppCategoryIcon(Hyprland.activeClient?.wmClass, "desktop_windows")
color: root.colour
anchors.centerIn: parent
anchors.horizontalCenter: root.vertical ? parent.horizontalCenter : undefined
}
clip: true
AnchorText {
id: text
MaterialIcon {
id: icon
prevAnchor: icon
animate: true
text: Icons.getAppCategoryIcon(Hyprland.activeClient?.wmClass, "desktop_windows")
color: root.colour
text: metrics.elidedText
font.pointSize: metrics.font.pointSize
font.family: metrics.font.family
color: root.colour
transform: Rotation {
angle: vertical ? 90 : 0
origin.x: text.implicitHeight / 2
origin.y: text.implicitHeight / 2
anchors.horizontalCenter: root.vertical ? parent.horizontalCenter : undefined
}
width: vertical ? implicitHeight : implicitWidth
height: vertical ? implicitWidth : implicitHeight
}
AnchorText {
id: text
TextMetrics {
id: metrics
prevAnchor: icon
text: Hyprland.activeClient?.title ?? qsTr("Desktop")
font.pointSize: Appearance.font.size.smaller
font.family: Appearance.font.family.mono
elide: Qt.ElideRight
elideWidth: root.vertical ? BarConfig.sizes.maxLabelHeight : BarConfig.sizes.maxLabelWidth
}
text: metrics.elidedText
font.pointSize: metrics.font.pointSize
font.family: metrics.font.family
color: root.colour
Behavior on implicitWidth {
NumberAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.emphasized
transform: Rotation {
angle: vertical ? 90 : 0
origin.x: text.implicitHeight / 2
origin.y: text.implicitHeight / 2
}
width: vertical ? implicitHeight : implicitWidth
height: vertical ? implicitWidth : implicitHeight
}
}
Behavior on implicitHeight {
NumberAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.emphasized
TextMetrics {
id: metrics
text: Hyprland.activeClient?.title ?? qsTr("Desktop")
font.pointSize: Appearance.font.size.smaller
font.family: Appearance.font.family.mono
elide: Qt.ElideRight
elideWidth: root.vertical ? root.height - icon.height : root.width - icon.width
}
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
}
}
}
}

View file

@ -15,8 +15,8 @@ MouseArea {
required property color colour
acceptedButtons: Qt.LeftButton | Qt.RightButton
implicitWidth: Appearance.font.size.smaller * 2
implicitHeight: Appearance.font.size.smaller * 2
implicitWidth: Appearance.font.size.small * 2
implicitHeight: Appearance.font.size.small * 2
onClicked: event => {
if (event.button === Qt.LeftButton)

View file

@ -23,6 +23,8 @@ GridLayout {
Layout.columnSpan: 2
Layout.preferredWidth: 250
Layout.fillHeight: true
Item {}
}
Rect {

View file

@ -43,6 +43,8 @@ Variants {
anchors.left: true
anchors.right: true
margins.left: Visibilities.bar.implicitWidth
Variants {
id: regions

View file

@ -32,6 +32,6 @@ Singleton {
property DesktopEntry entry
command: ["app2unit", "--", `${entry.id}.desktop`]
command: ["app2unit", "--", `${entry?.id}.desktop`]
}
}

View file

@ -5,6 +5,8 @@ import Quickshell
Singleton {
property var screens: ({})
property var bar
function getForActive(): PersistentProperties {
return Object.entries(screens).find(s => s[0].slice(s[0].indexOf('"') + 1, s[0].lastIndexOf('"')) === Hyprland.focusedMonitor.name)[1];
}

View file

@ -4,7 +4,7 @@ import QtQuick
Rectangle {
id: root
property bool vertical: parent.vertical ?? false // Convenience property for propagation to children
property bool vertical: parent?.vertical ?? false // Convenience property for propagation to children
color: "transparent"
implicitWidth: childrenRect.width