Skip to content

[hyprland/workspaces] Implement workspace taskbars - #3868

Merged
Alexays merged 27 commits into
Alexays:masterfrom
pol-rivero:master
Aug 7, 2025
Merged

[hyprland/workspaces] Implement workspace taskbars#3868
Alexays merged 27 commits into
Alexays:masterfrom
pol-rivero:master

Conversation

@pol-rivero

@pol-rivero pol-rivero commented Jan 4, 2025

Copy link
Copy Markdown
Contributor

Summary

This PR expands the functionality of hyprland/workspaces to make it behave more like the wlr/taskbar module. See #2656 for a high-level description.

A full fix of #2656 would require porting those changes to the sway/workspaces module as well. I don't use Sway and I'm not familiar on how it works, but if anyone is interested in giving it a go in a separate PR feel free to ping me and I'll do my best to help.

Config changes

Adds the following changes to the hyprland/workspaces config:

"hyprland/workspaces": {
    // EXISTING SETTINGS
    "format": "{icon}: {windows}", // Unchanged.
    "format-window-separator": "", // Unchanged.
    "window-rewrite-default": "",  // Will be IGNORED if "workspace-taskbar.enable" is true
    "window-rewrite": {},          // Will be IGNORED if "workspace-taskbar.enable" is true
    // NEW SETTINGS
    "workspace-taskbar": {
        // Enable the workspace taskbar. Default: false
        "enable": true,
        // If true, the active window will have an `active` class. Could cause higher CPU usage due to more frequent redraws. Default: false
        "update-active-window": true,
        // Format of the windows in the taskbar. Default: "{icon}". Allowed variables: {icon}, {title}
        "format": "{icon} {title:.20}",
        // Icon size in pixels. Default: 16
        "icon-size": 16,
        // Either the name of an installed icon theme or an array of themes (ordered by priority). If not set, the default icon theme is used.
        "icon-theme": "some_icon_theme",
        // Orientation of the taskbar (see screenshots below). Default: "horizontal".
        "orientation": "horizontal",
        // List of regexes. A window will NOT be shown if its window class or title match one or more items. Default: []
        "ignore-list": [ "code", "Firefox - .*" ],
        // Command to run when a window is clicked. Default: "" (switch to the workspace as usual). Allowed variables: {address}, {button}
        "on-click-window": "/some/arbitrary/script {address} {button}"
    }
}

Notice how workspace-taskbar.enable defaults to false, so existing configs shouldn't be affected by these changes without opting in.

What's missing

I'm mainly waiting for your feedback on the config json names and structure. Once that's done I will update the wiki with the finalized spec.

Screenshots

Old config file (defaults to old, text-based icons). Using default CSS.

image

"hyprland/workspaces": {
    "format": "{icon}: {windows}",
    "format-window-separator": ",",
    "window-rewrite-default": "?",
    "window-rewrite": {
        "terminator": "",
        "code": "󰨞"
    }
},

New config file (default format is just the icon). Using default CSS.

image

"hyprland/workspaces": {
    "format": "{icon}: {windows}",
    "workspace-taskbar": {
        "enable": true
    }
},

Example: windows with first 10 chars of title and bigger icon

image

"hyprland/workspaces": {
    "format": "{icon}: {windows}",
    "workspace-taskbar": {
        "enable": true,
        "format": "{icon} {title:.10}",
        "icon-size": 18
    }
},

Tooltip shows full window title.

image

  • Note: The screenshot doesn't show my cursor, which was hovering the "Comparing" window.

Allows an arbitrary format

image

"hyprland/workspaces": {
    "format": "[{icon}] (( {windows} ))",
    "workspace-taskbar": {
        "enable": true,
        "format": " [{title:.5} | {icon}] "
    }
},

My setup with custom CSS

image

  • Background indicates selected workspace

Same CSS as above, vertical orientation

image

"hyprland/workspaces": {
    "format": "{icon}: {windows}",
    "workspace-taskbar": {
        "enable": true,
        "format": "{icon} {title:.20}",
        "icon-size": 18,
        "orientation": "vertical"
    }
},

Action when clicking window

You can use the on-click-window config to set the command that will be executed when a specific window is clicked.

  • The pattern {address} will be replaced with the address of the clicked window.
  • The pattern {button} will be replaced with the pressed button number. See GdkEventButton.button.

For example:

"on-click-window": "hyprctl dispatch focuswindow address:{address}"

I personally require a more complex logic, so I use on-click-window to call an external script.

@pol-rivero

Copy link
Copy Markdown
Contributor Author

@Alexays could you take a look at this PR?

Add a list of window titles and icons to each workspace (like wlr/taskbar but grouped by workspace).

Only implemented on hyprland for now.
Use format from config instead of hardcoding
- orientation
- icon-size
- icon-theme
This seems to be an old bug that has been made visible with the new workspace taskbars feature.
Sometimes, when closing a window and re-opening a window of the same program, hyprland reuses the window address. Since m_orphanWindowMap was not being cleaned up on window close, the new window would not be updated properly.
Fix another older bug where the title of a window will not be updated after moving it to another monitor.
In onWindowMoved, when moving an orphan window to the display of the current bar, that window should no longer be an orphan.
Use a vector instead of a map for for storing the workspace windows.
This orders the windows by the time they were added to the workspace, instead of sorting by address (which is effectively a random order). The new ordering seems to match the wlr/taskbar module
Windows were not being shown or updated unless the window-rewrite config were present.
- Add missing CSS class to manpage
- Fix rare segfault when address is not found (seems to only happen when compiled for production)
@pol-rivero

Copy link
Copy Markdown
Contributor Author

@Alexays I've been using this feature daily for 2 months and haven't encountered any issues.
Could you review this PR? It seems there are other people interested in this functionality.

@dinhokusanagi

Copy link
Copy Markdown

@Alexays Please

@n-connect

Copy link
Copy Markdown

@pol-rivero

Thanks for this PR! Is it possible to have such functionality in the improved hyprland/workspaces like this wlr/taskbar config below?

        "wlr/taskbar" :   
        {
                // "sort-by-app-id": "true"             
                "icon-size" : 16,
                "icon-theme" : "Numix-Circle",
                "on-click" : "activate",
                "on-click-middle" : "maximize",
                "on-click-right" : "close",  
                "tooltip-format" : "{title}"
        }

@pol-rivero

pol-rivero commented Mar 8, 2025

Copy link
Copy Markdown
Contributor Author

@n-connect
Yes, this config should be what you are looking for:

"hyprland/workspaces": {
    "format": "{icon}: {windows}",   // Change to your preferred format
    "workspace-taskbar": {
        "enable": true,
        "format": "{icon} {title:.20}",   // Change to your preferred format
        "icon-size": 16,
        "icon-theme": "Numix-Circle",
        "on-click-window": "windowClick.sh {address} {button}"
    }
}

Where windowClick.sh points to a script like this:

#!/bin/bash

address=$1

# https://api.gtkd.org/gdk.c.types.GdkEventButton.button.html
button=$2

if [ $button -eq 1 ]; then
    # Left click: focus window. The cursor:no_warps lines are optional.
    hyprctl keyword cursor:no_warps true
    hyprctl dispatch focuswindow address:$address
    hyprctl keyword cursor:no_warps false
elif [ $button -eq 2 ]; then
    # Middle click: maximize window
    # TODO: Use the corresponding hyprctl dispatch command. I don't know what would 'maximize' mean in a tiling window manager like hyprland
elif [ $button -eq 3 ]; then
    # Right click: close window
    hyprctl dispatch closewindow address:$address
fi

@n-connect

Copy link
Copy Markdown

@pol-rivero,

Thanks for the full example, making local backup for test. Looking forward to this PR to come through. It really helps making a cleaner and smaller footprint - less occupied pixels on screen, by avoiding duplicated icons, and giving space for other possible stuff.

Another question, should this PR allow icon rewriting such as in the below pic? The 4 icons at right side of 1 ...
20250308_19h52m47s_grim

Yepp, this capability within wlr/taskbar is priceless - if one is looking maximizing possibility in an otherwise tiling WM - in an ultralight small screen laptop. I was not able to get work the fullscreenstate, 1 hyprland yet, but fullscreen binds only. Not the same as maximize :D

elif [ $button -eq 2 ]; then
    # Middle click: maximize window
    # TODO: Use the corresponding hyprctl dispatch command. I don't know what would 'maximize' mean in a tiling window manager like hyprland

@pol-rivero

Copy link
Copy Markdown
Contributor Author

@n-connect
The taskbar shows the app icon and therefore does not support rewriting (if workspace-taskbar.enable is set to true, the window-rewrite config will be ignored).

If you want to change the icon that is shown for a specific app, you can do the following:

  1. Find the .desktop file for that app. You can find it in one of these directories:
    • ~/.local/share/applications
    • /usr/share/applications
    • /usr/local/share/applications
  2. Open it with a text editor
  3. Find the line that starts with Icon= and replace it with another icon from your theme. You can use a tool like nwg-icon-picker to find the available icon names.

@n-connect

Copy link
Copy Markdown

@pol-rivero

Fair enough. Either possibility of getting the same icons as in war/taskbar, or one with your samples above is perfect.

Default CSS, new config file (default format is just the icon)

image

"hyprland/workspaces": {
    "format": "{icon}: {windows}",
    "workspace-taskbar": {
        "enable": true
    }
},

I just need to git magic the latest numbered release, plus your PR into one local copy to do compile it. I hope I can find find my previous notes about cmus and its https stream PR :)

@pol-rivero

Copy link
Copy Markdown
Contributor Author

I just need to git magic the latest numbered release, plus your PR into one local copy to do compile it.

This PR is already up to date, you can just git clone https://github.com/pol-rivero/waybar.

Also let me know if you are using arch, then it's much easier to install just by modifying the PKGBUILD.

@n-connect

n-connect commented Mar 9, 2025

Copy link
Copy Markdown

@pol-rivero,

Thx, not Arc, but Debian stable, only the nwg-menu has and Arc icon/char. Following your base git clone, and build gave the workspace taskbar. On the other hand its version is below the actual release number (fixme, it the build# still marks it as 0.12.0)

waybar -v
Waybar v0.11.0-153-ge541936d (branch 'master')

So I'll try the tag/version clone and PR checkout for rebuild later.

The look of the bar with your code, with the same cut as above:
20250309_11h04m25s_grim

Working parts of the script:

  • button -eq 3 -> hyprctl dispatch closewindow address:$address

For some reason the activation do not works:

  • button -eq 1 -> hyprctl dispatch focuswindow address:$address
  • after the normal mouse click the mouse jumps into the middle of the screen and seeable, that the focused window is a different one, but that one do not got on the top, tried to fix, no joy yet (see script below)
  • my guess is that the focuswindow ask for a window parameter instead of address

Edit: after a sleep/session lock, a new error emerged:

  • all my clients (as hyprctl clients got hidden), however only one workspace (1) exist
  • no workspace change has helped
  • finally the wlr/taskbar maximise function could bring up the windows of the running [hyprland/hyprctl] clients.

I'm not sure if there a connection to this PR, just a background info

How should I get the address/window value for cli tests? Checked hyprctl clients output...
Can you please check out the below configs, what could be the issue in your opinion?

        "hyprland/workspaces": {
            "format": "{icon}: {windows}",   // Change to your preferred format
            "workspace-taskbar": {
                "enable": true,    
                "format": "{icon}",   // Change to your preferred format            
                "icon-size": 16,                     
                "icon-theme": "Numix-Circle",
                "on-click-window": "$HOME/.config/waybar/windowClick.sh {address} {button}"
            }    
        },

Edit2: fixed typo below

#!/bin/bash
                
address=$1

# https://api.gtkd.org/gdk.c.types.GdkEventButton.button.html    
button=$2                      
         
if [ $button -eq 1 ]; then
    # Left click: focus window. The cursor:no_warps lines are optional.    
    #hyprctl keyword cursor:no_warps true
    hyprctl dispatch focuswindow address:$address
    hyprctl dispatch bringactivetotop        
    #hyprctl keyword cursor:no_warps false
elif [ $button -eq 2 ]; then
    # Middle click: maximize window    
    # TODO: Use the corresponding hyprctl dispatch command. I don't know what would 'maximize' mean in a tiling window manager like hyprland
    echo 'middle click'
elif [ $button -eq 3 ]; then       
    # Right click: close window                                                     
    hyprctl dispatch closewindow address:$address    
fi

@pol-rivero

Copy link
Copy Markdown
Contributor Author

Sorry, I don't understand what you mean. Is there a bug?

@n-connect

Copy link
Copy Markdown

Sorry, I don't understand what you mean. Is there a bug?

Sorry, short version:

  1. The normal mouse click is not working. This supposed to bring a window into the front and focus it (or in reversed order does not matter). The focus removed from the original window/client, but the icon(its windows) I've clicked on, has not brought on top. Eg. it looks like nothing changed - but I can see the focus actually changed, but only just that. The window did not bringed up compared to wlr/taskbar I'm using an ALT-Tab bind which quite similar, which serves the ALT-Tab purpose, but can't we used here unfortunally:
bind = ALT, Tab, cyclenext
bind = ALT, Tab, bringactivetotop 
  1. The windows closing right click works.

These two above are related to the shellscript you've sent earlier.

  1. New interesting possible bug: all my windows "got hidden somewhere" within the workspace. The are not seeable in workspace 1, after lid close/sleep. Hyprctl clients tells me they are on/in wp1. Not 100% sure waybar (and hpyrland workspace/taskbar) related, , although this never happened before, only since I've replaced the waybar with local build this moring. In the cli, where I've started waybar no separate errors.

This one is a completely generic one. I'll crosscheck with 0.12.0 packaged version if it happens again.

@pol-rivero

Copy link
Copy Markdown
Contributor Author

Ah I see.
Since closing the window works, that means the script is being called correctly with the address and button number.

You seem to have a typo on your script: hyprctl dispatch bringactivetotp is missing an O: bringactivetotOp. That's probably why it isn't working.

I don't know why the windows are being hidden, this change should not affect it at all. Let me know if it also happens with the 0.12.0 base.

@pol-rivero

Copy link
Copy Markdown
Contributor Author

@stojanov I don't know. Several people have pinged @Alexays about this, but he never responded.
I don't know if this repo has other maintainers we could try to contact...

@dinhokusanagi

Copy link
Copy Markdown

Compartilhando minha configuração pessoal aqui também, caso alguém ache útil:

imagem

    "hyprland/workspaces": {
        "format": "{icon}: {windows}",
        "format-window-separator": "",
        "workspace-taskbar": {
            "enable": true,
            "update-active-window": true,
            "format": "{icon} {title:.22}",
            "icon-size": 18,
            "on-click-window": "${SCRIPTS}/focus-window.sh {address} {button}"
        }
    },
#workspaces button {
    font-family: "Cantarell";
    font-weight: bold;
    background-color: transparent;
    color: #ffffff;
    box-shadow: none;
    text-shadow: none;
    padding: 0px;
    border-radius: 0;
    padding-left: 5px;
    padding-right: 2px;
}

#workspaces .workspace-label {
    padding-left: 3px;
    border-top: 1px solid transparent;
}

#workspaces .taskbar-window {
    border-top: 1px solid transparent;
    font-weight: normal;
    padding-left: 5px;
    padding-right: 5px;
}

#workspaces button.visible .taskbar-window,
#workspaces button.visible .workspace-label {
    border-color: white;
}

#workspaces .taskbar-window.active {
    background-color: rgba(255, 255, 255, 0.15);
}

#workspaces .taskbar-window image {
    margin-top: 1px;
}

#workspaces button.urgent {
    background-color: @urgent-color;
}

focus-window.sh

#!/bin/sh

address=$1

# https://api.gtkd.org/gdk.c.types.GdkEventButton.button.html
button=$2

if [ $button -eq 1 ]; then
    # Left click: focus window
    hyprctl keyword cursor:no_warps true
    hyprctl dispatch focuswindow address:$address
    hyprctl keyword cursor:no_warps false
elif [ $button -eq 2 ]; then
    # Middle click: close window
    hyprctl dispatch closewindow address:$address
fi

Which icon pack are you using?

@pol-rivero

Copy link
Copy Markdown
Contributor Author

@dinhokusanagi I'm using Tela
https://www.gnome-look.org/p/1279924/

@dinhokusanagi

Copy link
Copy Markdown

@dinhokusanagi I'm using Tela https://www.gnome-look.org/p/1279924/

thanks

@n-connect

Copy link
Copy Markdown

@pol-rivero

Gave a try to v0.13.0 as

git clone https://github.com/Alexays/Waybar.git ## not specifying 0.13.0, but HEAD
cd Waybar;git pull origin pull/3868/head
meson setup build
ninja -C build
ninja -C build install ## than tar.gz'd the built

Unfortunatelly it does no shows the workspace taskbar, its complettely empty:
20250728_00h05m59s_grim

Moving back to my last v0.12.x build it works.

Did I forgot something, or there are some adjusment needed for this PR code after 0.13.0 release?

Thx

@pol-rivero

Copy link
Copy Markdown
Contributor Author

@n-connect Thanks for the heads-up, I'll have a look later today

@pol-rivero

Copy link
Copy Markdown
Contributor Author

@n-connect I updated my branch to the latest upstream commit (0776e69) and reinstalled, and it's still working correctly for me.

Looking at the commands you provided, I don't see any git merge, maybe you are missing a merge from my PR into the master branch?

@n-connect

Copy link
Copy Markdown

@pol-rivero

Thank you for the merge, after 5bbace1 , with the same steps (without merge):

  • clone with version,
  • pull the PR3868
  • meson config/build/install/tar.gz -> and everything is fine again!
20250730_15h52m56s_grim

@n-connect

Copy link
Copy Markdown

@Alexays,

Please take a look on this PR, we are using it since beginning of this year - working well and let have a more tidy, clean smaller footprint Waybar with Hyprland.

What do you need to merge this into the mainline?

Thx!

@Alexays
Alexays merged commit b9a26f5 into Alexays:master Aug 7, 2025
9 checks passed
@Alexays

Alexays commented Aug 7, 2025

Copy link
Copy Markdown
Owner

Hi all!
Sorry for this delay on that PR, don't know why, But I didn't receive any notifications about this PR.

@pol-rivero

Copy link
Copy Markdown
Contributor Author

No way! We did it!! :)
🎉 🎉 🎉

@n-connect

Copy link
Copy Markdown

@Alexays, thanks for the merge!
@pol-rivero, thx for the nice code!

Aaaanda quick bulid it just works in master too :D
20250807_23h49m22s_grim

@dinhokusanagi

Copy link
Copy Markdown

Hello! First of all, I'd like to thank you for your excellent work developing Waybar and implementing taskbars for workspaces.

I have a suggestion that could further improve the user experience: adding the "active-first": true option to the hyprland/workspaces module.

This feature would allow the first window of each workspace to be highlighted in the taskbar, making it easier to identify and quickly access active windows.

I understand that every change requires analysis and testing, but I believe this addition could benefit many users.

🙏💛

@pol-rivero

Copy link
Copy Markdown
Contributor Author

@dinhokusanagi Let's see if I'm understanding correctly. If active-first is set to true, the active window should be displayed as the leftmost window of it's taskbar (or topmost if orientation is vertical). Is that right?

@dinhokusanagi

Copy link
Copy Markdown

@pol-rivero yes 👍🙂

@schitthub685

Copy link
Copy Markdown

Thanks for this feature.

I think it would be useful to have a reverse-direction option. I use active-only and my window layout has the master window on the right, and new windows spawning on the left, so it makes sense for new programs to also appear on the left in the taskbar.

Also if it is possible, when moving windows around it would be nice to have them reorder in the taskbar based on their position.

@pol-rivero

Copy link
Copy Markdown
Contributor Author

@dinhokusanagi @schitthub685 Both of your suggestions seem simple enough. I can create a PR when I have some free time. However, keep in mind that the maintainer is not very active and PRs can take a long time to be merged, this one took 7 months.
That means that, in the meantime, you will have to compile from my branch and ping me from time to time so that I merge in the latest upstream changes. If you are okay with that, let's go ahead.

@schitthub685

Also if it is possible, when moving windows around it would be nice to have them reorder in the taskbar based on their position.

Unfortunately that's not possible. We don't listen for the window move event, as that would cause tons of redraws.

@dinhokusanagi

Copy link
Copy Markdown

@dinhokusanagi @schitthub685 Both of your suggestions seem simple enough. I can create a PR when I have some free time. However, keep in mind that the maintainer is not very active and PRs can take a long time to be merged, this one took 7 months. That means that, in the meantime, you will have to compile from my branch and ping me from time to time so that I merge in the latest upstream changes. If you are okay with that, let's go ahead.

@schitthub685

Also if it is possible, when moving windows around it would be nice to have them reorder in the taskbar based on their position.

Unfortunately that's not possible. We don't listen for the window move event, as that would cause tons of redraws.

thanks

@QuarkVortexx

Copy link
Copy Markdown

This is a very nice addition to hyprland and thank you for that! As expansion of this feature, I started to wonder if it would be possible to add theme / css support for individual applications when they are hovered?

Also, I thought of quite large expansion idea: Would it be possible to display all applications across all windows and workspaces at once. Then have the ability to first group workspaces by monitor, this would create individual groups for each monitor. Then group applications by workspace, this would create individual workspaces inside each monitor group. This way you would have applications inside the workspace they reside in and also the workspaces inside the monitors they reside in.

To take this even further, adding drag and drop of applications would be nice. Applications could be dragged from one workspace to another. Additionally, when dragging of application is started, inside each monitor group (or only inside the hovered monitor group) an additional empty workspace could be displayed.

This feature would kind of mix the possible workflow of taskbar and workspaces. Interested to hear peoples thoughts.

@pol-rivero

Copy link
Copy Markdown
Contributor Author

@QuarkVortexx

I started to wonder if it would be possible to add theme / css support for individual applications when they are hovered?

I'm not sure I understand what you mean exactly. Do you mean the CSS :hover pseudo-class? I believe that is already supported.

Would it be possible to display all applications across all windows and workspaces at once. Then have the ability to first group workspaces by monitor

If you mean to have each monitor show only its workspaces, that is already de default behavior (see the all-outputs setting).

To take this even further, adding drag and drop of applications would be nice.

Unfortunately, that won't be possible without some massive changes in Waybar.

@pol-rivero

Copy link
Copy Markdown
Contributor Author

@dinhokusanagi @schitthub685 This PR implements the changes you requested: #4395
Feel free to build from source and tell me if it works as expected.

@dinhokusanagi Instead of the active-first boolean you proposed, I made it slightly more flexible by allowing the user to move the active window to the end as well as the start. You will need to set active-window-position to "first"

@dinhokusanagi

Copy link
Copy Markdown

@dinhokusanagi @schitthub685 This PR implements the changes you requested: #4395 Feel free to build from source and tell me if it works as expected.

@dinhokusanagi Instead of the active-first boolean you proposed, I made it slightly more flexible by allowing the user to move the active window to the end as well as the start. You will need to set active-window-position to "first"

thanks 🤝

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants