Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 61 additions & 37 deletions src/share/sleex/modules/bar/Workspaces.qml
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,34 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
import Quickshell.Io
import Quickshell.Widgets
import Qt5Compat.GraphicalEffects

Item {
required property var bar
property bool borderless: Config.options.bar.borderless
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen)
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
readonly property var activeWindow: Fhtc.focusedWindow
readonly property string screenName: bar.screen?.name ?? ""

// Get workspaces for this screen only, sorted by ID
readonly property var screenWorkspaces: {
return Object.values(Fhtc.workspaces)
.filter(ws => ws.output === screenName)
.sort((a, b) => a.id - b.id);
}

// Active workspace index within this screen (0-based)
readonly property int activeWorkspaceIndex: {
if (!Fhtc.activeWorkspace) return -1;
if (Fhtc.activeWorkspace.output !== screenName) return -1;
// Find the index of the active workspace in our sorted screen workspaces
const idx = screenWorkspaces.findIndex(ws => ws.id === Fhtc.activeWorkspace.id);
// Return -1 if the workspace is beyond the shown limit
if (idx >= Config.options.bar.workspaces.shown) return -1;
return idx;
}

readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / Config.options.bar.workspaces.shown)
property list<bool> workspaceOccupied: []
property int widgetPadding: 0
property int horizontalPadding: 5
Expand All @@ -28,23 +44,31 @@ Item {
property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55
property real workspaceIconOpacityShrinked: 1
property real workspaceIconMarginShrinked: -4
property int workspaceIndexInGroup: (monitor.activeWorkspace?.id - 1) % Config.options.bar.workspaces.shown


// Function to update workspaceOccupied
function updateWorkspaceOccupied() {
workspaceOccupied = Array.from({ length: Config.options.bar.workspaces.shown }, (_, i) => {
return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * Config.options.bar.workspaces.shown + i + 1);
// Get the workspace at this index for this screen
const ws = screenWorkspaces[i];
if (!ws) return false;
// Check if the workspace has any windows
return ws.windows && ws.windows.length > 0;
})
}

// Initialize workspaceOccupied when the component is created
Component.onCompleted: updateWorkspaceOccupied()

// Listen for changes in Hyprland.workspaces.values
// Listen for changes in Fhtc.workspaces and windows
Connections {
target: Hyprland.workspaces
function onValuesChanged() {
target: Fhtc
function onWorkspacesChanged() {
updateWorkspaceOccupied();
}
function onWindowsChanged() {
updateWorkspaceOccupied();
}
function onActiveWorkspaceChanged() {
updateWorkspaceOccupied();
}
}
Expand All @@ -57,23 +81,13 @@ Item {
WheelHandler {
onWheel: (event) => {
if (event.angleDelta.y < 0)
Hyprland.dispatch(`workspace +1`);
Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-next-workspace"]);
else if (event.angleDelta.y > 0)
Hyprland.dispatch(`workspace -1`);
Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-previous-workspace"]);
}
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
}

MouseArea {
anchors.fill: parent
acceptedButtons: Qt.BackButton
onPressed: (event) => {
if (event.button === Qt.BackButton) {
Hyprland.dispatch(`togglespecialworkspace`);
}
}
}

Item {
anchors.fill: parent
anchors.leftMargin: horizontalPadding
Expand All @@ -96,8 +110,8 @@ Item {
implicitWidth: workspaceButtonWidth
implicitHeight: workspaceButtonWidth
radius: Appearance.rounding.full
property var leftOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index))
property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+2))
property var leftOccupied: (workspaceOccupied[index-1])
property var rightOccupied: (workspaceOccupied[index+1])
property var radiusLeft: leftOccupied ? 0 : Appearance.rounding.full
property var radiusRight: rightOccupied ? 0 : Appearance.rounding.full

Expand All @@ -107,7 +121,7 @@ Item {
bottomRightRadius: radiusRight

color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4)
opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+1)) ? 1 : 0
opacity: (workspaceOccupied[index]) ? 1 : 0

Behavior on opacity {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
Expand All @@ -129,15 +143,16 @@ Item {
// Active workspace
Rectangle {
z: 2
visible: activeWorkspaceIndex >= 0
// Make active ws indicator, which has a brighter color, smaller to look like it is of the same size as ws occupied highlight
property real activeWorkspaceMargin: 2
implicitHeight: workspaceButtonWidth - activeWorkspaceMargin * 2
radius: Appearance.rounding.full
color: Appearance.colors.colPrimary
anchors.verticalCenter: parent.verticalCenter

property real idx1: workspaceIndexInGroup
property real idx2: workspaceIndexInGroup
property real idx1: activeWorkspaceIndex >= 0 ? activeWorkspaceIndex : 0
property real idx2: activeWorkspaceIndex >= 0 ? activeWorkspaceIndex : 0
x: Math.min(idx1, idx2) * workspaceButtonWidth + activeWorkspaceMargin
implicitWidth: Math.abs(idx1 - idx2) * workspaceButtonWidth + workspaceButtonWidth - activeWorkspaceMargin * 2

Expand Down Expand Up @@ -172,24 +187,33 @@ Item {

Button {
id: button
property int workspaceValue: workspaceGroup * Config.options.bar.workspaces.shown + index + 1
property var workspace: screenWorkspaces[index] ?? null
property int workspaceId: workspace?.id ?? -1
Layout.fillHeight: true
onPressed: Hyprland.dispatch(`workspace ${workspaceValue}`)
onPressed: {
if (button.workspaceId >= 0) {
Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-workspace", `${button.workspaceId}`]);
}
}
width: workspaceButtonWidth

background: Item {
id: workspaceButtonBackground
implicitWidth: workspaceButtonWidth
implicitHeight: workspaceButtonWidth

// Get the biggest window from the workspace's window list
property var biggestWindow: {
const windowsInThisWorkspace = HyprlandData.windowList.filter(w => w.workspace.id == button.workspaceValue)
if (!button.workspace || !button.workspace.windows || button.workspace.windows.length === 0) return null;
const windowIds = button.workspace.windows;
const windowsInThisWorkspace = windowIds.map(id => Fhtc.windows[id]).filter(w => w != null);
return windowsInThisWorkspace.reduce((maxWin, win) => {
const maxArea = (maxWin?.size?.[0] ?? 0) * (maxWin?.size?.[1] ?? 0)
const winArea = (win?.size?.[0] ?? 0) * (win?.size?.[1] ?? 0)
return winArea > maxArea ? win : maxWin
}, null)
}
property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing")
property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.["app-id"]), "image-missing")

StyledText { // Workspace number text
opacity: GlobalStates.workspaceShowNumbers
Expand All @@ -202,9 +226,9 @@ Item {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: Appearance.font.pixelSize.small - ((text.length - 1) * (text !== "10") * 2)
text: `${button.workspaceValue}`
text: `${index + 1}`
elide: Text.ElideRight
color: (monitor.activeWorkspace?.id == button.workspaceValue) ?
color: (activeWorkspaceIndex == index) ?
Appearance.m3colors.m3onPrimary :
(workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
Appearance.colors.colOnLayer1Inactive)
Expand All @@ -224,9 +248,9 @@ Item {
width: workspaceButtonWidth * 0.18
height: width
radius: width / 2
color: (monitor.activeWorkspace?.id == button.workspaceValue) ?
Appearance.m3colors.m3onPrimary :
(workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
color: (activeWorkspaceIndex == index) ?
Appearance.m3colors.m3onPrimary :
(workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
Appearance.colors.colOnLayer1Inactive)

Behavior on opacity {
Expand All @@ -240,7 +264,7 @@ Item {
opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 :
(workspaceButtonBackground.biggestWindow && !GlobalStates.workspaceShowNumbers && Config.options?.bar.workspaces.showAppIcons) ?
1 : workspaceButtonBackground.biggestWindow ? workspaceIconOpacityShrinked : 0
visible: opacity > 0
visible: opacity > 0
IconImage {
id: mainAppIcon
anchors.bottom: parent.bottom
Expand Down
3 changes: 1 addition & 2 deletions src/share/sleex/modules/dock/Dock.qml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import Quickshell.Io
import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
import Quickshell.Hyprland

Scope { // Scope
id: root
Expand All @@ -27,7 +26,7 @@ Scope { // Scope
property bool reveal: root.pinned
|| (Config.options?.dock.hoverToReveal && dockMouseArea.containsMouse)
|| dockApps.requestDockShow
|| (!ToplevelManager.activeToplevel?.activated)
|| (ToplevelManager.toplevels?.length === 0)

anchors {
bottom: true
Expand Down
16 changes: 16 additions & 0 deletions src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

qml_module(sleex-fhtc
URI Sleex.Fhtc
SOURCES
ipc.hpp ipc.cpp
workspaces.hpp workspaces.cpp
plugin.cpp
)

target_link_libraries(sleex-fhtc
PRIVATE
Qt6::Core
Qt6::Qml
Qt6::Network
Qt6::Json
)
Loading