Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1e06d76
FHT: added new cpp modules
LeVraiArdox Nov 14, 2025
a40b08f
Added new qml modules
LeVraiArdox Nov 17, 2025
c35a9e0
FHTC: bar workspace widget handle
LeVraiArdox Dec 9, 2025
a60ffd6
FHTC: bar workspace action handle
LeVraiArdox Dec 9, 2025
42bff99
FHTC: dock visibility handle
LeVraiArdox Dec 9, 2025
7b4f654
Merge branch 'main' into fht
LeVraiArdox Feb 4, 2026
0762706
Link fhtc c++ classes
LeVraiArdox Feb 4, 2026
e48511e
Impl ActiveWindow with FHTC
LeVraiArdox Feb 4, 2026
8759246
Update active ws id
LeVraiArdox Feb 4, 2026
52c85fe
Comment out every GlobalShortcut definitions
LeVraiArdox Feb 4, 2026
d49ccdd
Using C++ plugins for workspace and window dispatchers and vars
LeVraiArdox Feb 4, 2026
434c971
removed FR comments
LeVraiArdox Feb 4, 2026
8eaf952
Fix app title handling
LeVraiArdox Feb 4, 2026
8b2419c
Removed old FHTC services
LeVraiArdox Feb 5, 2026
1c561d3
Added monitors FHTC binding
LeVraiArdox Feb 5, 2026
5679c72
Renamed workspaces QML name
LeVraiArdox Feb 5, 2026
d57a147
Using new fhtc module name
LeVraiArdox Feb 5, 2026
dad80b8
Cleaned workspaces module
LeVraiArdox Feb 5, 2026
87ffb7d
[WIP OVERVIEW] fixed monitor selection logic
LeVraiArdox Feb 5, 2026
d64c5e3
Removed test file
LeVraiArdox Feb 5, 2026
4b263b4
Made the dispatch work
LeVraiArdox Feb 5, 2026
5afc5c9
ws: remove unnecessary output parameter
LeVraiArdox Feb 5, 2026
ba27407
Merge branch 'main' into fht
LeVraiArdox Mar 15, 2026
65e69a2
Uncomment shortcuts
LeVraiArdox Mar 15, 2026
bb1d90d
Added .vscode to gitignore
LeVraiArdox Mar 15, 2026
670df70
Fixed workspace icons
LeVraiArdox Mar 15, 2026
dcec763
Remove unused properties from Workspaces.qml
LeVraiArdox Mar 15, 2026
321661b
Update focusedScreen property to use FhtcMonitors.activeMonitorName
LeVraiArdox Mar 15, 2026
8bf1717
Overview: made it work for FHTC
LeVraiArdox Mar 15, 2026
df70993
Merge branch 'main' into fht
LeVraiArdox Mar 17, 2026
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.tar.zst
pkg/
pkg/
.vscode/
7 changes: 4 additions & 3 deletions src/share/sleex/modules/bar/ActiveWindow.qml
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import QtQuick.Layouts
import Quickshell.Wayland
import Quickshell.Hyprland
import Sleex.Fhtc

Item {
id: root
required property var bar
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen)
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel

implicitWidth: colLayout.implicitWidth

Expand All @@ -26,15 +27,15 @@ Item {
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colSubtext
elide: Text.ElideRight
text: root.activeWindow?.activated ? root.activeWindow?.appId : qsTr("Desktop")
text: FhtcWorkspaces.focusedWindow !== undefined ? FhtcWorkspaces.focusedWindow['app-id'] : qsTr("Desktop")
}

StyledText {
Layout.fillWidth: true
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colOnLayer0
elide: Text.ElideRight
text: root.activeWindow?.activated ? root.activeWindow?.title : `${qsTr("Workspace")} ${monitor.activeWorkspace?.id}`
text: FhtcWorkspaces.focusedWindow !== undefined ? FhtcWorkspaces.focusedWindow.title : `${qsTr("Workspace")} ${FhtcWorkspaces.activeWorkspaceId + 1}`
}

}
Expand Down
110 changes: 67 additions & 43 deletions src/share/sleex/modules/bar/Workspaces.qml
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,69 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
import Quickshell.Io
import Quickshell.Widgets
import Qt5Compat.GraphicalEffects
import Sleex.Fhtc

Item {
id: root
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 string screenName: bar.screen?.name ?? ""

// Get workspaces for this screen only, sorted by ID
readonly property var screenWorkspaces: {
return Object.values(FhtcWorkspaces.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 (!FhtcWorkspaces.activeWorkspace) return -1;
if (FhtcWorkspaces.activeWorkspace.output !== screenName) return -1;
// Find the index of the active workspace in our sorted screen workspaces
const idx = screenWorkspaces.findIndex(ws => ws.id === FhtcWorkspaces.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
property int workspaceButtonWidth: 30
property real workspaceIconSize: workspaceButtonWidth * 0.6
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

property bool useMaterialIcons: Config.options.bar.workspaces.useMaterialIcons


// 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: FhtcWorkspaces
function onWorkspacesChanged() {
updateWorkspaceOccupied();
}
function onWindowsChanged() {
updateWorkspaceOccupied();
}
function onActiveWorkspaceChanged() {
updateWorkspaceOccupied();
}
}
Expand All @@ -59,24 +82,15 @@ Item {
// Scroll to switch workspaces
WheelHandler {
onWheel: (event) => {
if (event.angleDelta.y < 0)
Hyprland.dispatch(`workspace +1`);
else if (event.angleDelta.y > 0)
Hyprland.dispatch(`workspace -1`);
if (event.angleDelta.y < 0) {
FhtcIpc.dispatch("focus-next-workspace", {});
} else if (event.angleDelta.y > 0) {
FhtcIpc.dispatch("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 @@ -99,8 +113,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 @@ -110,7 +124,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 @@ -132,15 +146,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 @@ -175,29 +190,38 @@ 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) {
FhtcIpc.dispatch("focus-workspace-by-index", { "workspace_idx": 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 => FhtcWorkspaces.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")

property string materialIconName: {
if (!biggestWindow) return ""

const winClass = biggestWindow.class.toLowerCase()
const winClass = biggestWindow?.["app-id"]
const map = {
"language": ["firefox", "chromium", "google-chrome", "brave", "edge", "vivaldi", "qutebrowser", "librewolf", "zen-browser"],
"terminal": ["foot", "kitty", "alacritty", "wezterm", "gnome-terminal", "konsole", "xfce4-terminal", "xterm"],
Expand Down Expand Up @@ -230,9 +254,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 @@ -252,9 +276,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 @@ -268,7 +292,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 Expand Up @@ -308,8 +332,8 @@ Item {
(workspaceButtonWidth - workspaceIconSize) / 2 - 2 : workspaceIconMarginShrinked
anchors.rightMargin: (!GlobalStates.workspaceShowNumbers) ?
(workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
color: (monitor.activeWorkspace?.id == button.workspaceValue) ?
Appearance.m3colors.m3onPrimary : Appearance.m3colors.m3onSecondaryContainer
color: (activeWorkspaceIndex == index) ?
Appearance.m3colors.m3onPrimary : Appearance.colors.colOnSecondaryContainer

Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
Expand Down
1 change: 0 additions & 1 deletion src/share/sleex/modules/cheatsheet/Cheatsheet.qml
Original file line number Diff line number Diff line change
Expand Up @@ -161,5 +161,4 @@ Scope { // Scope
cheatsheetLoader.active = false;
}
}

}
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
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,4 @@ Scope {
root.showOsdValues = false
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -200,5 +200,4 @@ Scope {
root.showOsdValues = false
}
}

}
12 changes: 6 additions & 6 deletions src/share/sleex/modules/overview/Overview.qml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland
import Sleex.Fhtc

Scope {
id: overviewScope
Expand All @@ -20,14 +21,14 @@ Scope {
id: root
required property var modelData
property string searchingText: ""
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id)
required property ShellScreen screen
property bool monitorIsFocused: (FhtcMonitors.activeMonitorName === screen.name)
screen: modelData
visible: GlobalStates.overviewOpen && monitorIsFocused

WlrLayershell.namespace: "quickshell:overview"
WlrLayershell.layer: WlrLayer.Overlay
// WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
color: "transparent"

mask: Region {
Expand Down Expand Up @@ -203,7 +204,7 @@ Scope {
}
for (let i = 0; i < overviewVariants.instances.length; i++) {
let panelWindow = overviewVariants.instances[i];
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
if (panelWindow.modelData.name == FhtcMonitors.activeMonitorName) {
overviewScope.dontAutoCancelSearch = true;
panelWindow.setSearchingText(
Config.options.search.prefix.clipboard
Expand All @@ -226,7 +227,7 @@ Scope {
}
for (let i = 0; i < overviewVariants.instances.length; i++) {
let panelWindow = overviewVariants.instances[i];
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
if (panelWindow.modelData.name == FhtcMonitors.activeMonitorName) {
overviewScope.dontAutoCancelSearch = true;
panelWindow.setSearchingText(
Config.options.search.prefix.emojis
Expand All @@ -237,5 +238,4 @@ Scope {
}
}
}

}
Loading