Skip to content

Replace Waybar in Hyprland setup with QuickShell#3

Draft
Morxemplum wants to merge 57 commits intomainfrom
hyprland-quickshell
Draft

Replace Waybar in Hyprland setup with QuickShell#3
Morxemplum wants to merge 57 commits intomainfrom
hyprland-quickshell

Conversation

@Morxemplum
Copy link
Owner

@Morxemplum Morxemplum commented Dec 19, 2025

This pull request is dedicated to all of the changes that are dedicated to replacing the main bar utility that is used in my Hyprland setup, Waybar, with QuickShell.

Why ditch Waybar?

I'm not always the biggest fan of tiling window managers, so my relationship with Hyprland is on and off. Especially considering that for the longest time, Hyprland's NVIDIA support lagged behind other graphics implementations.

But also, I have some personal issues with Waybar itself. There quite a few reasons, where individually they kinda suck, but together, it honestly becomes a bit of a dealbreaker. So let's go over some of the issues that I have with Waybar.

No Hot Reloading when Changing Configuration

Hyprland configuration is amazing because of hot reloading. I can see changes that I make in (practically) real-time and quickly determine what I am doing wrong or if I am happy with my changes before I save the file. However, I don't tend to utilize it all that much because I ain't constantly changing hyprland.conf.

The bar that I use for Hyprland, on the other hand, I will be tweaking a lot, especially considering that I'm mostly writing a configuration or tweaking another config I found. Waybar was the goto recommendation for having a status bar on Hyprland.

However, when I started tweaking my configuration, none of my changes would update my bar, not even when I saved the file. The only to see my changes: Completely kill waybar and relaunch it.

This may sound like a nitpick, but when you are getting into the fine details of tuning your bar, this lack of hot reloading really starts to become a nuisance. To work around this issue, I decided to code a custom shell script that would perform the duties of hot reloading. It works... for the most part.

It wasn't too often where I would run into bugs related to the constant reloading the script was doing. Sometimes, my bar doesn't come back at all. Other times, I get multiple bars showing up on my screen. Upon initial startup, these bugs are rare enough to where they don't affect the overall user experience. When configuring, they showed up quite often. It just made configuring my bar start becoming as much of a headache as manually relaunching.

This also starts becoming a pain whenever an issue pops up in the bar and I have to fix it, which leads to my next point.

My Setup Was Kinda Buggy

Well, following the configuration guidelines of Waybar could only get me so far. Let's talk about the most prominent feature that I ran into bugs with: Icons.

When most people are configuring their bar, they like to use icons to help make it easier to read the different things spread out across the bar. However, making icons is a whole labor in of itself, so people will instead source their icons from somewhere.

The easiest way to get working icons: use a font that contains all your favorite icons. Nice, consistent, easy... right?

Well, a bug that I would often run into when returning to Hyprland is that the icons that I have assigned to all of the widgets in my bar would completely scramble, referencing completely different icons. This just breaks the appearance of my bar completely.

I have managed to fix it before, and the main way that you have to fix this is that you have to tamper with the system fonts and refresh the cache accordingly. Doing this is an absolute chore, and honestly, I don't see the worth in doing all of this just so my bar can have icons.

Waybar limits you if you want certain looks

Now, if you're the kinda guy who wants a flat, minimalist aesthetic for your bar, then Waybar has all of the configuration you need thanks to CSS.

But me, I like blur and frosted glass. I guess I'm kinda spoiled from seeing these effects in macOS and Windows 11. However, if I am going to be ricing my setup, I want my rice looking the way I want.

Part of why this would be impossible is because if I want the aesthetic to be aware of my windows, then I would need to dive into the window manager (Hyprland), and it seems like they don't offer such an option (and probably for good reason).

However, even just a basic frosted look is not possible to pull off in Waybar, because the wallpaper is also controlled by the compositor. No CSS blur effect is going to get me what I want.

Why Quickshell?

I came across a video on YouTube one day about this utility called Quickshell, which caught my eye. I noticed that the video was configuring a bar setup that was very similar to Waybar, but instead of using JSON and CSS, it was using Qt Quick and QML.

I actually had an interest in learning Qt for quite a bit. If you know me, I'm a bit of a freak when it comes to software optimization. A lot of the daily applications that I use will either use Electron or something that uses Chromium Embedded Framework (CEF). Why? Because these allow developers to make web apps that not only work inside a browser, but takes advantage of a browser's compatibility across the big operating systems.

Unfortunately, that ease of development and compatibility comes at a price. When developers bundle their software to make it look and behave like a native application, you have to run a web browser engine for those apps to function as intended. Browsers are well known to be resource intensive applications, consuming your CPU threads and your memory. I want those resources to be going to applications that really need them (e.g. games, creative software, my actual browser), not just to run some dumb chat app, music client, or text editor. I don't want them getting in the way.

There are solutions such as Tauri that ameliorate the issue, but you're still not running the application at a native system level. Plus, Tauri does have to make some small compromises to achieve their smaller footprint.

Of course, there was the Qt framework. Qt has been around for much longer than most web app frameworks, and it has cross-compatibility, but has the footprint of a native system application. That sounds perfect for me. The only reason why I haven't bothered learning it is because Qt apps are primarily written in C++. I won't go into detail here, but I have had a long historical struggle with learning and using C++.

QML and Qt Quick seem like a good middle ground though, as the format feels more like a glorified JSON or YAML, with the ability to execute code through JavaScript (yuck!)

QuickShell is a framework built to utilize QML, so it seems like making a bar using QuickShell would be a nice stepping stone to help introduce me to Qt.

What Still Needs To Be Done

That being said, all of the commits that I have done to get this pull request to the current state of minimum viable product are incredibly tedious. And I am still not yet done, which is why this pull request may remain open for a while. Making a Quickshell config takes a lot more time than Waybar, but I think writing a Quickshell config is high risk, high reward! I want to actually have the full tinkering that I have set up for Hyprland, but also find a way to be easy to use.

So, what needs to be done? Well, I made a quick checklist below that is split off into a couple categories.

Refactoring

With my commits I've written so far, I have made quite a bit of tech debt for this setup. There's some places that should be much more easier to configure, some snippets of code that look absolutely dirty and I want to improve, but also, I seem to have some gaps in my knowledge regarding some of the features provided by Quickshell. Quickshell's documentation is quite basic, which is a downside that contributes to making me take longer to write a good configuration.

I wanna focus on completing this first before I continue adding other features that are going to be important if I want this to be merged.

  • Fix dirty code snippets (some are highlighted with FIXME and TODO)
  • Fix warnings thrown by Quickshell regarding Singletons (the behavior seems weirdly inconsistent. Some Singleton implementations work perfectly fine as-is, and others I have to manually incorporate with a Loader)
  • Load modules the proper way using Loaders
  • Cull "magic numbers" present throughout the configuration and dedicate them to either built in constants, or configurable constants.
  • Figure out how to make some properties read only. I don't want those properties to be modified by me whatsoever.
  • With apps in the app tray, try to figure out how to get context menus to theme properly
  • Make a separate icon or animation to indicate a pending network connection
  • Applications launched through the bar should be decoupled from the bar. Otherwise, killing the bar will kill those applications, which we may not always want.
  • Improve the networking implementation to use the most active connection, not just the topmost option. For a rule of thumb, if we have both an active ethernet and Wifi connection, it would probably be best to prioritize the ethernet connection and only show Wifi if the ethernet connection is limited.

Tooltips

I've been ignoring tooltips because I know that is a lot more involved regarding the widgets and the actual UI setup. However, for a proper configuration, I want tooltips to be showing up for various widgets when I hover my mouse over them.

Here's some widgets that would benefit from tooltips:

  • System Information: For CPU, show usage for each core. For memory, show the actual RAM numbers in addition to VRAM usage. For Temp, show temps for each core. Optional: Fan statistics.
  • Workspace switcher: For each workspace, show the corresponding ID. Hyprland uses underlying IDs for their workspaces. If the ID is -1, then showcase the workspace name.
  • App Tray: Show the name of the app when hovering over them.
  • Network Connection: Show the name of the device and network connection. Optional: Showcase upload and download speeds.
  • Volume: When hovering over the volume icon, showcase a slider that can be quickly adjusted by scrolling the mouse. Consider this a mini-applet.
  • Time: Show the time on top of the day in the 12-hour format (configurable), along with a more English display of the date.
  • Everything that wasn't specified should have a tooltip saying what the widget is.

Applets

Some utilities have information that can't be encapsulated in the bar or even a tooltip, but I don't want to launch an application unless its the last resort and I want full customization. There are a couple that I have in mind.

  • Time: When clicking on the time, show an applet that has a layout of the current calendar month. It should be formatted as a calendar.
  • Volume: Clicking on the sound icon should open an applet that contains the currently in use audio device, a volume slider (that has finer control than the mini-applet), and can show what applications are using the sink, and volume sliders for a per-application quick adjustment. Optionally, it would also be nice to incorporate controls for the default source (microphone), if one is in use.
  • Networking: Getting a working applet here is a must. Quickshell has no official API for Networking, and there are no good standalone GUI frontends for network configuration. We will only be writing this applet to work for NetworkManager. This applet should have similar functionality to a networking applet in a desktop environment like GNOME or KDE, where I should be able to easily connect to a Wifi network, or quickly configure certain network settings.
  • Media: This would be a brand new applet that will have a corresponding widget that only appears when media is playing (i.e. music). Should include functionality such as play/pause, skipping and previous track, scrubbing, and display metadata for what is playing.

Other Additions

Here in this category are some other small additions that I would like incorporated into the configuration before I would consider this a good enough candidate to merge.

  • Package update notifier util: This one is particularly because I use arch (btw). But I would like a notification on how many packages I need to update and is only visible if a threshold is met. AUR and Flatpak are optional, but are nice-to-have.
  • Microphone, Camera, and Screensharing notification badges: If an application is using my microphone, camera, or sharing my screen, then show badges for them.
  • Terminate / Kill Fullscreen application: If I am currently in a fullscreen application, a button should appear in the bar as part of the program labeller to give me the option to terminate, or even kill an application. In really bad situations, it might be useful to make keybinds.
  • Power Menu: Straightforward. Give me a widget/applet that, when clicked, gives me power options (shut down, sleep, log off, etc.)
  • Bluetooth (Optional): Give me a utility widget and applet that handles Bluetooth connectivity and devices. I'm considering this optional right now because I don't use Bluetooth a lot on the PC, but this is a must if I plan on deploying this configuration on my laptop.
  • Basic Animations: I would also like to see basic animations present throughout the shell that isn't handled by the window manager.
  • Password Prompting: Some widgets, such as the clipboard manager, can contain sensitive information. Information that I may not want readily present if someone wants to snoop on my device. If possible, try and make a prompt that will ask for a password.

Finalize The Look / Final Optimizations

To finish everything off, we should do two things. First, finalize the look. What has been implemented so far has been a rough draft, and I haven't been focusing on what the bar looks like. I've only been looking at the functionality. This would be the time now to really nail down the look. After this, do final optimizations to try and lower memory usage and resources. I've been doing optimizations in my initial designs, but they were minor optimizations or "don't be stupid" optimizations. This last section should focus more on dedicated optimization to try and undo remaining technical debt and ensure a performant bar.

  • Finalize the "look"
  • Optimize the bar enough to where I am satisfied.
  • Update the README.md accordingly

Final Comments

This checklist is not a finalized list. There may be things I'll add or remove from the list as I go on, but this is an initial plan of what I want in this configuration before I consider it viable for daily driving.

This is not to be confused with a system tray, which I will be adding next.

If you can't tell by the amount of TODOs, this is an incomplete implementation. A lot of the bare functionality is present, but there is plenty of work that needs to be done here before I can consider this fully acceptable.
Otherwise commonly known as the "System Tray"
Quickshell does automatically check if a window is fullscreen before hiding it. However, that only works if Hyprland reports the client on the workspace as fullscreen.

In recent versions of Hyprland, they seem to have redone their XWayland rendering in a way where borderless fullscreen applications on XWayland (e.g. plenty of Steam games) will not be considered fullscreen by Hyprland, and hence the `fullscreen` attribute will be reported as `false` if you utilize hyprctl.

So I have updated the activeWindow process in Hyprclients that will check for this issue, and reports as fullscreen if all 3 conditions are met: The window uses xwayland, the window is floating, and the resolution reported matches the XOrg fullscreen resolution (as XOrg doesn't support fractional scaling and reports resolution at a truncated integer scale)
In the case where you may have a gap in the workspace id sequence (e.g 1, 2, 4), clicking on the third circle would look for a workspace with id 3, which didn't exist so it just ends up creating a new workspace.

This simple bug fix takes direct reference of the id attribute from the referenced workspace, solving the issue.
In addition, add separate thresholds for temperatures, as they are different from percentage thresholds

This reduces the width of the widget quite a bit, making it less bloated on the UI
When you disable scaling from the Wayland compositor, most compositors (except for KDE Plasma) will choose to simply not scale the XWayland applications and just render them at 1x scale.

Some XWayland applications will work just fine and scale properly, but some others need you to scale for them. This script tweaks some XOrg environment variables that will help instruct those applications to scale at the proper scale.

The scale values are hardcoded in this script, so if you want to change to your respective scale factor, you'll need to edit this script.
This is mainly because QML and Quickshell prefer the camelCase naming convention. I would use snake_case, but then it'll make the code look inconsistent with QML terminology, and I started adopting camelCase later on.
I double checked to see what properties would benefit from being readonly, but unless the attributes are based on quickshell attributes or attributes based on an API, there's really no point in doing readonly
Given that I'm going to be writing more modules before I can merge this pull request, it's best that we organize most of the top level modules into appropriate folders.
So it turns out that the theming issues I was running into regarding context menus on the App Tray were not a Quickshell issue. It was a Hyprland issue, because I also noticed that certain Qt apps (e.g. Dolphin) were completely botched in their theming.

Hyprland now has their own package that handles Qt6 theming, and so I replaced it and configured things accordingly. Now context menus on the App Tray respect my dark mode.
For properties, if an object needed for the attribute is null, a fallback value will be given until the object can be found
Seems like a prior refactor may have fixed the need to use loaders for the Singletons. This gets rid of the warnings from Quickshell regarding singleton registration
In the case of QML, using var is quite lazy and can make it harder to read and understand the code.

In the case of JS, var has been long outdated and modern JS discourages the use of var. Every variable has been replaced with const, unless the variable needs reassignment, in which let is used.
These are the only two widgets that I can come across being completely empty, and hence, having a conditional existence that can be lazily loaded.

Keep in mind there is going to be a lot of lazy loading being used when we get to Tooltips and Applets.
On CachyOS, it seems like the updates for the Legcord launcher are much slower, so I get nagged constantly to update until the repo pushes out an update.

In addition, the screensharing utilities on Legcord have been getting significantly less reliable, where screensharing w/ audio will just straight up not work anymore. Hoping that Vesktop will be more reliable in this area.
The app is dictated by a new category, "App Defaults", in the Config file.

In addition, we want to launch this application detached from Quickshell. In the case of a Quickshell crash, we don't want this application going down with it.
Standalone applications should not be bound to Quickshell. In addition, partially complete the TODO by making this action done by right clicking.
@Morxemplum
Copy link
Owner Author

Alright, that nearly finishes all the refactoring. The only thing left to do is to redo the implementation of network monitoring, but I wanna take a small break from it before I may do an overhaul on it (i.e. instead of updating most events on a timer, listen for events and changes).

Whoops! Seems like I didn't know what qmldir files are or what they did until now. This should hopefully get rid of the countless warnings presented by your linter in the IDE if you've been coming across them until now.
First implementation of tooltips. I might want to add on or modify this implementation in the event of the other widgets.

In addition, the old hover date functionality has been removed.
This mainly had to do with changes to window rules (which I removed the MPV rules temporarily, will readd later). I also had to rewrite the hyprpaper conf to comply to the new syntax.
…ocusedProgram

This probably indicates an error with Hyprland IPC and we should assume there is no program in focus
So uhhhhhh. I had to move my desk and my PC. I plugged my monitors into new ports. Looks like I put them in completely different ports. And tbh, I don't object to the more logical order here.
In addition, App tray is no longer lazy loaded. The main reason behind this is because I couldn't find a satisfactory implementation of the tooltip inside the loader, either drastically increasing load time or it just simply wouldn't work. I tried to use Quickshell's LazyLoader, but it also wouldn't work either.
Since I am changing accent color as now it has a distinct use for applets, I am using primaryColor instead.
@Morxemplum
Copy link
Owner Author

That finishes tooltips. Up to this point, I've been tackling one category at a time, but applets are going to take considerably longer to make than what has been worked on previously, so I will be splitting my workflow across multiple of the categories so that way I don't burn out from hyperfocusing on one category. Some of the stuff in the "Other Additions" category can be taken care of easier than others.

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.

1 participant