How to Set Up Hyprland Window Grouping (Tabbed Mode)
Hyprland window grouping creates tabbed containers by wrapping windows in a CGroup instance, managing them through CWindowGroupTarget, and rendering a configurable bar via CHyprGroupBarDecoration.
Hyprland window grouping (often called tabbed mode) allows you to stack multiple windows into a single container with a clickable tab bar. According to the hyprwm/Hyprland source code, this feature is implemented through three tightly-coupled C++ components that handle data modeling, layout targeting, and UI rendering.
Core Architecture of Hyprland Window Grouping
The implementation spans three primary classes that separate concerns between data storage, layout management, and visual presentation.
CGroup – The Data Model
The CGroup class, defined in src/desktop/View/CGroup.cpp, serves as the central data structure for window grouping. It stores the list of member windows, tracks the currently active window, and maintains group-wide state. All windows belonging to the same group share a single CGroup instance, enabling synchronized state management across the container.
Key methods include add() to insert windows, remove() to delete them, setCurrent() to change the active tab, and moveCurrent() to reorder tabs within the group.
CWindowGroupTarget – The Layout Engine Bridge
Located in src/layout/target/WindowGroupTarget.cpp, CWindowGroupTarget makes the compositor treat the entire group as a single logical window for positioning and resizing. When the layout engine calls setPositionGlobal() or assignToSpace(), this target propagates those changes to every member window through updatePos().
This design ensures that geometry changes apply uniformly—moving the group moves all windows simultaneously, and workspace assignments update the internal workspace reference via m_group->updateWorkspace().
CHyprGroupBarDecoration – The Tab Bar Renderer
The visual tab bar is implemented in src/render/decorations/CHyprGroupBarDecoration.cpp. This decoration renders on top of the group's master window and handles mouse interactions. Configuration values starting at line 28 (such as group:groupbar:enabled and group:groupbar:height) control its appearance.
Mouse click handling around line 420 intercepts tab selection events and translates them into calls to CGroup::setCurrent(), which triggers the layout target to update the active window and schedule a repaint.
Enabling and Configuring the Group Bar
The tab bar is controlled through configuration keys defined in CHyprGroupBarDecoration.cpp (lines 28–43). Add these settings to your hyprland.conf to enable the default tabbed appearance:
# Enable the group bar
group:groupbar:enabled = 1
# Height of the bar in pixels
group:groupbar:height = 24
# Display window titles inside tabs
group:groupbar:render_titles = 1
# Gap between the bar and window content
group:groupbar:gaps_out = 5
# Enable mouse scrolling to switch tabs
group:groupbar:scrolling = 1
# Enable gradient backgrounds for tabs
group:groupbar:gradients = 1
These configuration values are read as CConfigValue instances and determine how the decoration paints itself within the rendering pipeline, automatically respecting compositor settings like scaling, rounding, and blur.
Keybinds for Group Management
Window groups are manipulated through hyprctl dispatchers that map directly to CGroup methods. Define keybinds in your configuration to control grouping without mouse interaction:
# Create a new group with the focused window
bind = SUPER, G, exec, hyprctl dispatch groupadd
# Add the focused window to the group under the cursor
bind = SUPER SHIFT, G, exec, hyprctl dispatch groupaddfocused
# Switch to the next tab in the current group
bind = SUPER CTRL, Right, exec, hyprctl dispatch groupnext
# Switch to the previous tab
bind = SUPER CTRL, Left, exec, hyprctl dispatch groupprev
# Remove the focused window from its group
bind = SUPER SHIFT, R, exec, hyprctl dispatch groupremove
The groupadd and groupaddfocused commands call CGroup::add(), while groupnext and groupprev invoke CGroup::moveCurrent() to cycle through the window list. These actions propagate through CWindowGroupTarget and automatically refresh the group bar state.
Internal Geometry Propagation
When you move or resize a grouped window, the compositor interacts with CWindowGroupTarget rather than individual windows. The setPositionGlobal() method first applies the geometry change to the target itself, then iterates through all members to synchronize their positions via updatePos().
This propagation ensures that the entire group moves as a rigid unit. When the group switches workspaces through assignToSpace(), the target updates the internal workspace reference in CGroup and ensures all member windows follow the master window to the new space.
Programmatic Control via Plugins
If you are developing a Hyprland plugin in C++, you can manipulate groups directly using the internal API:
// pWindow is a pointer to the window you want to group
auto group = pWindow->m_group ? pWindow->m_group : makeShared<Desktop::View::CGroup>();
// Add the window to the group and replace its target
group->add(pWindow);
pWindow->m_target = WindowGroupTarget::create(group);
// Make this window the active tab
group->setCurrent(pWindow);
This snippet mirrors the logic found in CHyprGroupBarDecoration.cpp (around lines 425–429) where drag-and-drop operations add windows to existing groups. The WindowGroupTarget::create() factory function establishes the link between the window and the shared group state.
Summary
- Hyprland window grouping relies on three components:
CGroupfor data,CWindowGroupTargetfor layout, andCHyprGroupBarDecorationfor rendering. - The group bar is configured through
group:groupbar:*keys inCHyprGroupBarDecoration.cppand participates automatically in the compositor's rendering pipeline. - Dispatchers like
groupadd,groupnext, andgroupprevprovide keyboard-driven control over tab creation and navigation. - Geometry changes propagate through
CWindowGroupTargetmethods likesetPositionGlobal()andupdatePos(), ensuring groups move as unified entities.
Frequently Asked Questions
How do I enable the tab bar in Hyprland?
Add group:groupbar:enabled = 1 to your hyprland.conf file. Additional options like group:groupbar:height and group:groupbar:render_titles control the bar's appearance, as defined in src/render/decorations/CHyprGroupBarDecoration.cpp.
What is the difference between groupadd and groupaddfocused?
The groupadd dispatcher creates a new group containing only the currently focused window. The groupaddfocused dispatcher adds the focused window to the existing group that contains the window currently under the cursor, effectively merging windows into a shared CGroup instance.
Why does moving one window move the entire group?
Because CWindowGroupTarget (in src/layout/target/WindowGroupTarget.cpp) intercepts geometry changes and forwards them to every member window via updatePos(). This makes the compositor treat the group as a single entity for positioning and resizing operations.
Can I change the active tab with my mouse wheel?
Yes, set group:groupbar:scrolling = 1 in your configuration. The CHyprGroupBarDecoration class handles scroll events (around line 420 in its source file) and translates them into calls to CGroup::moveCurrent(), cycling through the tab list without keyboard interaction.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →