mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 14:59:27 -05:00
hefty: bar: workspace widget
This commit is contained in:
@@ -109,7 +109,8 @@ Singleton {
|
||||
*/
|
||||
function transparentize(color, percentage = 1) {
|
||||
var c = Qt.color(color);
|
||||
return Qt.rgba(c.r, c.g, c.b, c.a * (1 - percentage));
|
||||
var a = c.a * (1 - clamp01(percentage));
|
||||
return Qt.rgba(c.r, c.g, c.b, a);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,7 +122,7 @@ Singleton {
|
||||
*/
|
||||
function applyAlpha(color, alpha) {
|
||||
var c = Qt.color(color);
|
||||
var a = Math.max(0, Math.min(1, alpha));
|
||||
var a = clamp01(alpha);
|
||||
return Qt.rgba(c.r, c.g, c.b, a);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
pragma Singleton
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* Rounds the given number to the nearest even integer.
|
||||
*
|
||||
* @param {number} num - The number to round.
|
||||
* @returns {number} The nearest even integer.
|
||||
*/
|
||||
function roundToEven(num) {
|
||||
return Math.round(num / 2) * 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import QtQuick
|
||||
|
||||
// QtObject that allows stuff to be freely declared inside
|
||||
QtObject {
|
||||
default property list<QtObject> data
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import QtQuick
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import qs.services
|
||||
import qs.modules.common as C
|
||||
|
||||
NestableObject {
|
||||
id: root
|
||||
|
||||
required property HyprlandMonitor monitor
|
||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||
readonly property int activeWorkspace: monitor?.activeWorkspace?.id
|
||||
readonly property bool currentWorkspaceNotFake: activeWindow?.activated ?? false // Active empty workspace = fake. At least, that's how I like to call it.
|
||||
readonly property int fakeWorkspace: currentWorkspaceNotFake ? -9999 : activeWorkspace
|
||||
readonly property int shownCount: C.Config.options.bar.workspaces.shown
|
||||
readonly property int group: Math.floor((activeWorkspace - 1) / shownCount)
|
||||
|
||||
property list<bool> occupied: []
|
||||
property list<var> biggestWindow: occupied.map((_, index) => {
|
||||
const wsId = getWorkspaceIdAt(index);
|
||||
var biggestWindow = HyprlandData.biggestWindowForWorkspace(wsId);
|
||||
return biggestWindow;
|
||||
})
|
||||
|
||||
function getWorkspaceId(group, index) {
|
||||
return group * root.shownCount + index + 1;
|
||||
}
|
||||
function getWorkspaceIdAt(index) {
|
||||
return root.getWorkspaceId(root.group, index);
|
||||
}
|
||||
|
||||
// Function to update workspaceOccupied
|
||||
function updateWorkspaceOccupied() {
|
||||
root.occupied = Array.from({
|
||||
length: root.shownCount
|
||||
}, (_, i) => {
|
||||
const thisWorkspaceId = getWorkspaceId(root.group, i);
|
||||
return Hyprland.workspaces.values.some(ws => ws.id === thisWorkspaceId);
|
||||
});
|
||||
}
|
||||
|
||||
// Occupied workspace updates
|
||||
Component.onCompleted: updateWorkspaceOccupied()
|
||||
Connections {
|
||||
target: Hyprland.workspaces
|
||||
function onValuesChanged() {
|
||||
root.updateWorkspaceOccupied();
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: Hyprland
|
||||
function onFocusedWorkspaceChanged() {
|
||||
root.updateWorkspaceOccupied();
|
||||
}
|
||||
}
|
||||
onGroupChanged: {
|
||||
updateWorkspaceOccupied();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import QtQuick
|
||||
import org.kde.kirigami as Kirigami
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
|
||||
Kirigami.Icon {
|
||||
id: root
|
||||
|
||||
property real implicitSize: 26
|
||||
implicitWidth: implicitSize
|
||||
implicitHeight: implicitSize
|
||||
|
||||
roundToIconSize: false
|
||||
animated: true // It's just fading from one icon to another
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import QtQuick
|
||||
|
||||
Rectangle {
|
||||
property double diameter
|
||||
property real diameter
|
||||
|
||||
implicitWidth: diameter
|
||||
implicitHeight: diameter
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
|
||||
// Note: You still have to set sizes yourself
|
||||
MultiEffect {
|
||||
maskEnabled: true
|
||||
maskThresholdMin: 0.5
|
||||
maskSpreadAtMin: 1
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import QtQuick
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
// https://m3.material.io/foundations/interaction/states/state-layers
|
||||
enum State {
|
||||
Hover, Focus, Press, Drag
|
||||
}
|
||||
|
||||
property var state: StateLayer.State.Hover
|
||||
opacity: switch(state) {
|
||||
case StateLayer.State.Hover: return 0.08;
|
||||
case StateLayer.State.Focus: return 0.1;
|
||||
case StateLayer.State.Press: return 0.1;
|
||||
case StateLayer.State.Drag: return 0.16;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool hover: false
|
||||
property bool press: false
|
||||
property bool drag: false
|
||||
property color contentColor: Appearance.m3colors.m3onBackground
|
||||
color: "transparent"
|
||||
|
||||
FadeLoader {
|
||||
id: hoverLoader
|
||||
anchors.fill: parent
|
||||
shown: root.hover
|
||||
sourceComponent: StateLayer {
|
||||
radius: root.radius
|
||||
state: StateLayer.State.Hover
|
||||
color: root.contentColor
|
||||
}
|
||||
}
|
||||
FadeLoader {
|
||||
id: focusLoader
|
||||
anchors.fill: parent
|
||||
shown: root.focus
|
||||
sourceComponent: StateLayer {
|
||||
radius: root.radius
|
||||
state: StateLayer.State.Focus
|
||||
color: root.contentColor
|
||||
}
|
||||
}
|
||||
FadeLoader {
|
||||
id: pressLoader
|
||||
anchors.fill: parent
|
||||
shown: root.press
|
||||
sourceComponent: StateLayer {
|
||||
radius: root.radius
|
||||
state: StateLayer.State.Press
|
||||
color: root.contentColor
|
||||
}
|
||||
}
|
||||
FadeLoader {
|
||||
id: dragLoader
|
||||
anchors.fill: parent
|
||||
shown: root.drag
|
||||
sourceComponent: StateLayer {
|
||||
radius: root.radius
|
||||
state: StateLayer.State.Drag
|
||||
color: root.contentColor
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ PanelWindow {
|
||||
borderWidth: (root.currentPanel === bar && Config.options.bar.cornerStyle !== 1) ? 0 : 1
|
||||
borderColor: Appearance.colors.colLayer0Border
|
||||
visible: false // cuz there's already the shadow
|
||||
debug: true
|
||||
// debug: true
|
||||
}
|
||||
DropShadow {
|
||||
id: shadow
|
||||
|
||||
@@ -50,7 +50,7 @@ Item {
|
||||
id: layout
|
||||
columns: C.Config.options.bar.vertical ? 1 : -1
|
||||
anchors.centerIn: parent
|
||||
property real spacing: 0
|
||||
property real spacing: 4
|
||||
columnSpacing: spacing
|
||||
rowSpacing: spacing
|
||||
}
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs
|
||||
import qs.modules.common
|
||||
import qs.modules.common.models
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen)
|
||||
WorkspaceModel {
|
||||
id: wsModel
|
||||
monitor: root.monitor
|
||||
}
|
||||
|
||||
property bool vertical: Config.options.bar.vertical
|
||||
property bool superPressAndHeld: false // Relevant modifications at bottom of file
|
||||
|
||||
property real workspaceButtonWidth: 26
|
||||
property real activeWorkspaceMargin: 2
|
||||
property real activeWorkspaceSize: workspaceButtonWidth - activeWorkspaceMargin * 2
|
||||
property real workspaceIconSize: workspaceButtonWidth * 0.69
|
||||
property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55
|
||||
property real workspaceIconOpacityShrinked: 1
|
||||
property real workspaceIconMarginShrinked: -4
|
||||
property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % wsModel.shownCount
|
||||
|
||||
Layout.alignment: vertical ? Qt.AlignHCenter : Qt.AlignVCenter
|
||||
Layout.fillWidth: vertical
|
||||
Layout.fillHeight: !vertical
|
||||
implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : occupiedIndicators.implicitWidth
|
||||
implicitHeight: vertical ? occupiedIndicators.implicitHeight : Appearance.sizes.barHeight
|
||||
|
||||
/////////////////// Occupied indicators ///////////////////
|
||||
StyledRectangle {
|
||||
id: occupiedIndicatorsBg
|
||||
anchors.fill: parent
|
||||
contentLayer: StyledRectangle.ContentLayer.Group
|
||||
color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4)
|
||||
visible: false
|
||||
}
|
||||
|
||||
WorkspaceLayout {
|
||||
id: occupiedIndicators
|
||||
anchors.centerIn: parent
|
||||
|
||||
// rowSpacing: 0
|
||||
// columnSpacing: 0
|
||||
// columns: root.vertical ? 1 : -1
|
||||
// rows: root.vertical ? -1 : 1
|
||||
|
||||
layer.enabled: true
|
||||
visible: false
|
||||
|
||||
Repeater {
|
||||
model: wsModel.shownCount
|
||||
delegate: Item {
|
||||
id: wsBg
|
||||
required property int index
|
||||
readonly property int wsId: wsModel.getWorkspaceIdAt(index)
|
||||
property bool currentOccupied: wsModel.occupied[index] && wsId != wsModel.fakeWorkspace
|
||||
property bool previousOccupied: index > 0 && wsModel.occupied[index - 1] && (wsId - 1) != wsModel.fakeWorkspace
|
||||
property bool nextOccupied: index < wsModel.shownCount - 1 && wsModel.occupied[index + 1] && (wsId + 1) != wsModel.fakeWorkspace
|
||||
implicitWidth: root.workspaceButtonWidth
|
||||
implicitHeight: root.workspaceButtonWidth
|
||||
|
||||
// The idea: over-stretch to occupied sides, animate this for a smooth transition.
|
||||
// masking already prevents weird overlaps
|
||||
Circle {
|
||||
property real undirectionalWidth: root.workspaceButtonWidth * wsBg.currentOccupied
|
||||
property real undirectionalLength: root.workspaceButtonWidth * (1 + 0.5 * wsBg.previousOccupied + 0.5 * wsBg.nextOccupied) * currentOccupied
|
||||
property real undirectionalOffset: (!wsBg.currentOccupied ? 0.5 : -0.5 * wsBg.previousOccupied) * root.workspaceButtonWidth
|
||||
radius: undirectionalWidth / 2
|
||||
anchors.verticalCenter: root.vertical ? undefined : parent.verticalCenter
|
||||
anchors.horizontalCenter: root.vertical ? parent.horizontalCenter : undefined
|
||||
x: root.vertical ? 0 : undirectionalOffset
|
||||
y: root.vertical ? undirectionalOffset : 0
|
||||
implicitWidth: root.vertical ? undirectionalWidth : undirectionalLength
|
||||
implicitHeight: root.vertical ? undirectionalLength : undirectionalWidth
|
||||
|
||||
Behavior on undirectionalWidth {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on undirectionalLength {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on undirectionalOffset {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MaskMultiEffect {
|
||||
id: occupiedIndicatorsMultiEffect
|
||||
z: 1
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: occupiedIndicators.implicitWidth
|
||||
implicitHeight: occupiedIndicators.implicitHeight
|
||||
source: occupiedIndicatorsBg
|
||||
maskSource: occupiedIndicators
|
||||
}
|
||||
|
||||
/////////////////// Active indicator ///////////////////
|
||||
TrailingIndicator {
|
||||
id: activeIndicator
|
||||
anchors.fill: parent
|
||||
z: 2
|
||||
|
||||
index: root.workspaceIndexInGroup
|
||||
layer.enabled: true // For the masking
|
||||
}
|
||||
|
||||
/////////////////// Hover ///////////////////
|
||||
MouseArea {
|
||||
id: interactionMouseArea
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
property int hoverIndex: {
|
||||
const position = root.vertical ? mouseY : mouseX;
|
||||
return Math.floor(position / root.workspaceButtonWidth);
|
||||
}
|
||||
|
||||
onPressed: Hyprland.dispatch(`workspace ${wsModel.getWorkspaceIdAt(hoverIndex)}`)
|
||||
|
||||
TrailingIndicator {
|
||||
id: interactionIndicator
|
||||
index: interactionMouseArea.containsMouse ? interactionMouseArea.hoverIndex : root.workspaceIndexInGroup
|
||||
color: "transparent"
|
||||
StateOverlay {
|
||||
id: hoverOverlay
|
||||
anchors.fill: interactionIndicator.indicatorRectangle
|
||||
radius: root.activeWorkspaceSize / 2
|
||||
hover: interactionMouseArea.containsMouse
|
||||
press: interactionMouseArea.containsPress
|
||||
contentColor: Appearance.colors.colPrimary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////// Numbers ///////////////////
|
||||
WorkspaceLayout {
|
||||
id: numbersGrid
|
||||
z: 4
|
||||
layer.enabled: true // For the masking
|
||||
|
||||
Repeater {
|
||||
model: wsModel.shownCount
|
||||
delegate: WorkspaceItem {
|
||||
id: wsNum
|
||||
property bool hasBiggestWindow: !!wsModel.biggestWindow[index]
|
||||
property color contentColor: wsModel.occupied[wsNum.index] ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1Inactive
|
||||
|
||||
FadeLoader {
|
||||
shown: !(Config.options?.bar.workspaces.alwaysShowNumbers
|
||||
|| root.superPressAndHeld
|
||||
|| (Config.options?.bar.workspaces.showAppIcons && wsNum.hasBiggestWindow)
|
||||
)
|
||||
anchors.centerIn: parent
|
||||
Circle {
|
||||
anchors.centerIn: parent
|
||||
diameter: root.workspaceButtonWidth * 0.18
|
||||
color: wsNum.contentColor
|
||||
}
|
||||
}
|
||||
FadeLoader {
|
||||
shown: root.superPressAndHeld
|
||||
|| ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !wsNum.hasBiggestWindow || root.showNumbers))
|
||||
|| (root.superPressAndHeld && !Config.options?.bar.workspaces.showAppIcons)
|
||||
)
|
||||
anchors.centerIn: parent
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
font {
|
||||
pixelSize: Appearance.font.pixelSize.small - ((text.length - 1) * (text !== "10") * 2)
|
||||
family: Config.options?.bar.workspaces.useNerdFont ? Appearance.font.family.iconNerd : defaultFont
|
||||
}
|
||||
color: wsNum.contentColor
|
||||
text: wsNum.wsId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Colorizer {
|
||||
z: 5
|
||||
anchors.fill: numbersGrid
|
||||
colorizationColor: Appearance.colors.colOnPrimary
|
||||
sourceColor: Appearance.colors.colOnSecondaryContainer
|
||||
|
||||
source: activeIndicator
|
||||
maskEnabled: true
|
||||
maskSource: numbersGrid
|
||||
|
||||
maskThresholdMin: 0.5
|
||||
maskSpreadAtMin: 1
|
||||
}
|
||||
|
||||
/////////////////// App icons ///////////////////
|
||||
WorkspaceLayout {
|
||||
id: appsGrid
|
||||
z: 6
|
||||
|
||||
Repeater {
|
||||
model: wsModel.shownCount
|
||||
delegate: WorkspaceItem {
|
||||
id: wsApp
|
||||
property var biggestWindow: wsModel.biggestWindow[index]
|
||||
property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing")
|
||||
|
||||
AppIcon {
|
||||
id: appIcon
|
||||
property real cornerMargin: (!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
(root.workspaceButtonWidth - root.workspaceIconSize) / 2 : root.workspaceIconMarginShrinked
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
bottomMargin: (parent.implicitHeight - root.workspaceButtonWidth) / 2 + cornerMargin
|
||||
rightMargin: (parent.implicitWidth - root.workspaceButtonWidth) / 2 + cornerMargin
|
||||
}
|
||||
|
||||
animated: !wsApp.biggestWindow // Prevent the "image-missing" icon
|
||||
visible: false // Prevent dupe: the colorizer already copies the icon
|
||||
|
||||
source: wsApp.mainAppIconSource
|
||||
implicitSize: NumberUtils.roundToEven((!root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ? root.workspaceIconSize : root.workspaceIconSizeShrinked)
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on cornerMargin {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on implicitSize {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
Circle {
|
||||
id: iconMask
|
||||
visible: false
|
||||
layer.enabled: true
|
||||
diameter: appIcon.implicitSize
|
||||
}
|
||||
|
||||
Colorizer {
|
||||
anchors.fill: appIcon
|
||||
implicitWidth: appIcon.implicitWidth
|
||||
implicitHeight: appIcon.implicitHeight
|
||||
colorizationColor: Appearance.colors.colOnSecondaryContainer
|
||||
colorization: Config.options.bar.workspaces.monochromeIcons * 0.7
|
||||
brightness: 0
|
||||
source: appIcon
|
||||
|
||||
opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 :
|
||||
(wsApp.biggestWindow && !root.superPressAndHeld && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
1 : wsApp.biggestWindow ? root.workspaceIconOpacityShrinked : 0
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
maskEnabled: true
|
||||
maskSource: iconMask
|
||||
maskThresholdMin: 0.5
|
||||
maskSpreadAtMin: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////// Components ///////////////////
|
||||
component WorkspaceLayout: Grid {
|
||||
anchors {
|
||||
top: !vertical ? parent.top : undefined
|
||||
bottom: !vertical ? parent.bottom : undefined
|
||||
left: vertical ? parent.left : undefined
|
||||
right: vertical ? parent.right : undefined
|
||||
}
|
||||
|
||||
rowSpacing: 0
|
||||
columnSpacing: 0
|
||||
columns: root.vertical ? 1 : -1
|
||||
rows: root.vertical ? -1 : 1
|
||||
}
|
||||
|
||||
component WorkspaceItem: Item {
|
||||
required property int index
|
||||
readonly property int wsId: wsModel.getWorkspaceIdAt(index)
|
||||
implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : root.workspaceButtonWidth
|
||||
implicitHeight: root.vertical ? root.workspaceButtonWidth : Appearance.sizes.barHeight
|
||||
}
|
||||
|
||||
component TrailingIndicator: Item {
|
||||
id: trailingIndicator
|
||||
anchors.fill: parent
|
||||
required property int index
|
||||
property alias indicatorRectangle: indicatorRect
|
||||
property alias color: indicatorRect.color
|
||||
|
||||
StyledRectangle {
|
||||
id: indicatorRect
|
||||
anchors {
|
||||
verticalCenter: vertical ? undefined : parent.verticalCenter
|
||||
horizontalCenter: vertical ? parent.horizontalCenter : undefined
|
||||
}
|
||||
|
||||
AnimatedTabIndexPair {
|
||||
id: idxPair
|
||||
index: trailingIndicator.index
|
||||
}
|
||||
|
||||
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin
|
||||
property real indicatorLength: Math.abs(idxPair.idx1 - idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceSize
|
||||
property real indicatorThickness: root.activeWorkspaceSize
|
||||
|
||||
contentLayer: StyledRectangle.ContentLayer.Group
|
||||
radius: indicatorThickness / 2
|
||||
color: Appearance.colors.colPrimary
|
||||
|
||||
x: root.vertical ? null : indicatorPosition
|
||||
y: root.vertical ? indicatorPosition : null
|
||||
implicitWidth: root.vertical ? indicatorThickness : indicatorLength
|
||||
implicitHeight: root.vertical ? indicatorLength : indicatorThickness
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////// Super key press handling ///////////////////
|
||||
Timer {
|
||||
id: superPressAndHeldTimer
|
||||
interval: (Config?.options.bar.autoHide.showWhenPressingSuper.delay ?? 100)
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
root.superPressAndHeld = true;
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onSuperDownChanged() {
|
||||
if (!Config?.options.bar.autoHide.showWhenPressingSuper.enable)
|
||||
return;
|
||||
if (GlobalStates.superDown)
|
||||
superPressAndHeldTimer.restart();
|
||||
else {
|
||||
superPressAndHeldTimer.stop();
|
||||
root.superPressAndHeld = false;
|
||||
}
|
||||
}
|
||||
function onSuperReleaseMightTriggerChanged() {
|
||||
superPressAndHeldTimer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
@@ -15,21 +16,22 @@ import Qt5Compat.GraphicalEffects
|
||||
Item {
|
||||
id: root
|
||||
property bool vertical: false
|
||||
property bool borderless: Config.options.bar.borderless
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen)
|
||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||
|
||||
readonly property int workspacesShown: Config.options.bar.workspaces.shown
|
||||
readonly property int workspaceGroup: Math.floor((monitor?.activeWorkspace?.id - 1) / root.workspacesShown)
|
||||
property list<bool> workspaceOccupied: []
|
||||
property int widgetPadding: 4
|
||||
readonly property bool activeActuallyFocused: activeWindow?.activated ?? false
|
||||
|
||||
WorkspaceModel {
|
||||
id: wsModel
|
||||
monitor: root.monitor
|
||||
}
|
||||
|
||||
property int workspaceButtonWidth: 26
|
||||
property real activeWorkspaceMargin: 2
|
||||
property real workspaceIconSize: workspaceButtonWidth * 0.69
|
||||
property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55
|
||||
property real workspaceIconOpacityShrinked: 1
|
||||
property real workspaceIconMarginShrinked: -4
|
||||
property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % root.workspacesShown
|
||||
property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % wsModel.shownCount
|
||||
|
||||
property bool showNumbers: false
|
||||
Timer {
|
||||
@@ -55,33 +57,8 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// Function to update workspaceOccupied
|
||||
function updateWorkspaceOccupied() {
|
||||
workspaceOccupied = Array.from({ length: root.workspacesShown }, (_, i) => {
|
||||
return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * root.workspacesShown + i + 1);
|
||||
})
|
||||
}
|
||||
|
||||
// Occupied workspace updates
|
||||
Component.onCompleted: updateWorkspaceOccupied()
|
||||
Connections {
|
||||
target: Hyprland.workspaces
|
||||
function onValuesChanged() {
|
||||
updateWorkspaceOccupied();
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: Hyprland
|
||||
function onFocusedWorkspaceChanged() {
|
||||
updateWorkspaceOccupied();
|
||||
}
|
||||
}
|
||||
onWorkspaceGroupChanged: {
|
||||
updateWorkspaceOccupied();
|
||||
}
|
||||
|
||||
implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : (root.workspaceButtonWidth * root.workspacesShown)
|
||||
implicitHeight: root.vertical ? (root.workspaceButtonWidth * root.workspacesShown) : Appearance.sizes.barHeight
|
||||
implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : (root.workspaceButtonWidth * wsModel.shownCount)
|
||||
implicitHeight: root.vertical ? (root.workspaceButtonWidth * wsModel.shownCount) : Appearance.sizes.barHeight
|
||||
|
||||
// Scroll to switch workspaces
|
||||
WheelHandler {
|
||||
@@ -115,15 +92,18 @@ Item {
|
||||
rows: root.vertical ? -1 : 1
|
||||
|
||||
Repeater {
|
||||
model: root.workspacesShown
|
||||
model: wsModel.shownCount
|
||||
|
||||
delegate: Rectangle {
|
||||
required property int index
|
||||
|
||||
Rectangle {
|
||||
z: 1
|
||||
implicitWidth: workspaceButtonWidth
|
||||
implicitHeight: workspaceButtonWidth
|
||||
implicitWidth: root.workspaceButtonWidth
|
||||
implicitHeight: root.workspaceButtonWidth
|
||||
radius: (width / 2)
|
||||
property var previousOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index))
|
||||
property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+2))
|
||||
property bool thisOccupied: (wsModel.occupied[index] && !(!wsModel.currentWorkspaceNotFake && monitor?.activeWorkspace?.id === index+1))
|
||||
property var previousOccupied: (wsModel.occupied[index-1] && !(!wsModel.currentWorkspaceNotFake && monitor?.activeWorkspace?.id === index))
|
||||
property var rightOccupied: (wsModel.occupied[index+1] && !(!wsModel.currentWorkspaceNotFake && monitor?.activeWorkspace?.id === index+2))
|
||||
property var radiusPrev: previousOccupied ? 0 : (width / 2)
|
||||
property var radiusNext: rightOccupied ? 0 : (width / 2)
|
||||
|
||||
@@ -133,7 +113,7 @@ Item {
|
||||
bottomRightRadius: radiusNext
|
||||
|
||||
color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4)
|
||||
opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+1)) ? 1 : 0
|
||||
opacity: thisOccupied ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
@@ -168,9 +148,9 @@ Item {
|
||||
id: idxPair
|
||||
index: root.workspaceIndexInGroup
|
||||
}
|
||||
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * workspaceButtonWidth + root.activeWorkspaceMargin
|
||||
property real indicatorLength: Math.abs(idxPair.idx1 - idxPair.idx2) * workspaceButtonWidth + workspaceButtonWidth - root.activeWorkspaceMargin * 2
|
||||
property real indicatorThickness: workspaceButtonWidth - root.activeWorkspaceMargin * 2
|
||||
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin
|
||||
property real indicatorLength: Math.abs(idxPair.idx1 - idxPair.idx2) * root.workspaceButtonWidth + root.workspaceButtonWidth - root.activeWorkspaceMargin * 2
|
||||
property real indicatorThickness: root.workspaceButtonWidth - root.activeWorkspaceMargin * 2
|
||||
|
||||
x: root.vertical ? null : indicatorPosition
|
||||
implicitWidth: root.vertical ? indicatorThickness : indicatorLength
|
||||
@@ -183,36 +163,35 @@ Item {
|
||||
Grid {
|
||||
id: wsNumbers
|
||||
z: 3
|
||||
anchors.fill: parent
|
||||
|
||||
columns: root.vertical ? 1 : -1
|
||||
rows: root.vertical ? -1 : 1
|
||||
columnSpacing: 0
|
||||
rowSpacing: 0
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
Repeater {
|
||||
model: root.workspacesShown
|
||||
|
||||
Button {
|
||||
model: wsModel.shownCount
|
||||
delegate: Button {
|
||||
id: button
|
||||
property int workspaceValue: workspaceGroup * root.workspacesShown + index + 1
|
||||
required property int index
|
||||
property int workspaceValue: wsModel.getWorkspaceIdAt(index)
|
||||
implicitHeight: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight
|
||||
implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.verticalBarWidth
|
||||
onPressed: Hyprland.dispatch(`workspace ${workspaceValue}`)
|
||||
width: vertical ? undefined : workspaceButtonWidth
|
||||
height: vertical ? workspaceButtonWidth : undefined
|
||||
width: vertical ? undefined : root.workspaceButtonWidth
|
||||
height: vertical ? root.workspaceButtonWidth : undefined
|
||||
|
||||
background: Item {
|
||||
id: workspaceButtonBackground
|
||||
implicitWidth: workspaceButtonWidth
|
||||
implicitHeight: workspaceButtonWidth
|
||||
implicitWidth: root.workspaceButtonWidth
|
||||
implicitHeight: root.workspaceButtonWidth
|
||||
property var biggestWindow: HyprlandData.biggestWindowForWorkspace(button.workspaceValue)
|
||||
property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing")
|
||||
|
||||
property color numberColor: (monitor?.activeWorkspace?.id == button.workspaceValue) ?
|
||||
Appearance.m3colors.m3onPrimary :
|
||||
(workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
|
||||
(wsModel.occupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
|
||||
Appearance.colors.colOnLayer1Inactive)
|
||||
|
||||
StyledText { // Workspace number text
|
||||
@@ -245,7 +224,7 @@ Item {
|
||||
) ? 0 : 1
|
||||
visible: opacity > 0
|
||||
anchors.centerIn: parent
|
||||
width: workspaceButtonWidth * 0.18
|
||||
width: root.workspaceButtonWidth * 0.18
|
||||
height: width
|
||||
radius: width / 2
|
||||
color: workspaceButtonBackground.numberColor
|
||||
@@ -256,8 +235,8 @@ Item {
|
||||
}
|
||||
Item { // Main app icon
|
||||
anchors.centerIn: parent
|
||||
width: workspaceButtonWidth
|
||||
height: workspaceButtonWidth
|
||||
width: root.workspaceButtonWidth
|
||||
height: root.workspaceButtonWidth
|
||||
opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 :
|
||||
(workspaceButtonBackground.biggestWindow && !root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
1 : workspaceButtonBackground.biggestWindow ? workspaceIconOpacityShrinked : 0
|
||||
@@ -267,9 +246,9 @@ Item {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.bottomMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
(workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
|
||||
(root.workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
|
||||
anchors.rightMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
(workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
|
||||
(root.workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
|
||||
|
||||
source: workspaceButtonBackground.mainAppIconSource
|
||||
implicitSize: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ? workspaceIconSize : workspaceIconSizeShrinked
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
import QtQuick
|
||||
import org.kde.kirigami as Kirigami
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets as W
|
||||
|
||||
Kirigami.Icon {
|
||||
W.AppIcon {
|
||||
id: root
|
||||
required property string iconName
|
||||
property bool separateLightDark: false
|
||||
property bool tryCustomIcon: true
|
||||
|
||||
property real implicitSize: 26
|
||||
implicitWidth: implicitSize
|
||||
implicitHeight: implicitSize
|
||||
|
||||
animated: true
|
||||
roundToIconSize: false
|
||||
fallback: root.iconName
|
||||
source: tryCustomIcon ? `${Looks.iconsPath}/${root.iconName}${!root.separateLightDark ? "" : Looks.dark ? "-dark" : "-light"}.svg` : fallback
|
||||
|
||||
Reference in New Issue
Block a user