This commit is contained in:
Kaley, Fischer 2024-03-02 13:11:47 +01:00
commit a19662cb4b
13 changed files with 663 additions and 366 deletions

4
.gitignore vendored
View file

@ -1,2 +1,4 @@
build/ build/
compile_commands.json compile_commands.json
.vscode/
*.log

16
CHANGELOG.md Normal file
View file

@ -0,0 +1,16 @@
# Changelog
## Upcoming
- Implement `resizeactivewindow` for floating windows
- Fully implement `resizeactivewindow` for tiled windows
## hl0.35.0 and before
- Fixed `hy3:killactive` and `hy3:movetoworkspace` not working in fullscreen.
- `hy3:movetoworkspace` added to move a whole node to a workspace.
- Newly tiled windows (usually from moving a window to a new workspace) are now
placed relative to the last selected node.
## hl0.34.0 and before
*check commit history*

View file

@ -7,6 +7,8 @@ i3 / sway like layout for [hyprland](https://github.com/hyprwm/hyprland).
[Installation](#installation), [Configuration](#configuration) [Installation](#installation), [Configuration](#configuration)
*Check the [changelog](./CHANGELOG.md) for a list of new features and improvements*
### Features ### Features
- [x] i3 like tiling - [x] i3 like tiling
- [x] Node based window manipulation (you can interact with multiple windows at once) - [x] Node based window manipulation (you can interact with multiple windows at once)
@ -27,7 +29,7 @@ Commits are tested before pushing and will build against the hyprland release **
There may be a mismatch with hyprland's main branch. If hy3 fails to build against hyprland's main branch There may be a mismatch with hyprland's main branch. If hy3 fails to build against hyprland's main branch
please make an issue or ping me in the [hy3 matrix room](https://matrix.to/#/#hy3-support:outfoxxed.me). please make an issue or ping me in the [hy3 matrix room](https://matrix.to/#/#hy3-support:outfoxxed.me).
Tagged hy3 versions are always checked against the corrosponding hyprland tag. Tagged hy3 versions are always checked against the corresponding hyprland tag.
If you encounter any bugs, please report them in the issue tracker. If you encounter any bugs, please report them in the issue tracker.
@ -120,7 +122,7 @@ isn't capable of locking hy3 builds to the correct hyprland version.
> exec-once = hyprpm reload -n > exec-once = hyprpm reload -n
> ``` > ```
> >
> in your hyprland.conf. (See [the wiki](https://wiki.hyprland.org/Plugins/Using-Plugins/) for details.) > in your hyprland.conf. (See [the wiki](https://wiki.hyprland.org/Plugins/Using-Plugins/) for details.)
To install hy3 via hyprpm run To install hy3 via hyprpm run
@ -295,7 +297,7 @@ plugin {
# 0 = always automatically split horizontally # 0 = always automatically split horizontally
# <number> = pixel height to split at # <number> = pixel height to split at
trigger_height = <int> # default: 0 trigger_height = <int> # default: 0
# a space or comma separated list of workspace ids where autotile should be enabled # a space or comma separated list of workspace ids where autotile should be enabled
# it's possible to create an exception rule by prefixing the definition with "not:" # it's possible to create an exception rule by prefixing the definition with "not:"
# workspaces = 1,2 # autotiling will only be enabled on workspaces 1 and 2 # workspaces = 1,2 # autotiling will only be enabled on workspaces 1 and 2
@ -320,6 +322,8 @@ plugin {
- `hy3:movewindow, <l | u | d | r | left | down | up | right>, [once], [visible]` - move a window left, up, down, or right - `hy3:movewindow, <l | u | d | r | left | down | up | right>, [once], [visible]` - move a window left, up, down, or right
- `once` - only move directly to the neighboring group, without moving into any of its subgroups - `once` - only move directly to the neighboring group, without moving into any of its subgroups
- `visible` - only move between visible nodes, not hidden tabs - `visible` - only move between visible nodes, not hidden tabs
- `hy3:movetoworkspace, <workspace>, [follow]` - move the active node to the given workspace
- `follow` - change focus to the given workspace when moving the selected node
- `hy3:killactive` - close all windows in the focused node - `hy3:killactive` - close all windows in the focused node
- `hy3:changefocus, <top | bottom | raise | lower | tab | tabnode>` - `hy3:changefocus, <top | bottom | raise | lower | tab | tabnode>`
- `top` - focus all nodes in the workspace - `top` - focus all nodes in the workspace

71
flake.lock generated
View file

@ -3,17 +3,18 @@
"hyprland": { "hyprland": {
"inputs": { "inputs": {
"hyprland-protocols": "hyprland-protocols", "hyprland-protocols": "hyprland-protocols",
"hyprlang": "hyprlang",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"systems": "systems", "systems": "systems",
"wlroots": "wlroots", "wlroots": "wlroots",
"xdph": "xdph" "xdph": "xdph"
}, },
"locked": { "locked": {
"lastModified": 1702236723, "lastModified": 1708650152,
"narHash": "sha256-zIEnimM1vhsFkz+Kubb8kJ6YgHuLe56pALOSJc6CMVY=", "narHash": "sha256-OZUS5FED7KKAPpNaJYQr4BPGXQzGrDFgkKVg9U2aZh8=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "Hyprland", "repo": "Hyprland",
"rev": "167f2ed3b2bb18ceeabb831ac80b655ef8e16867", "rev": "8c3613632a6ccebf9fb797ec756ecfce99514eec",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -47,13 +48,56 @@
"type": "github" "type": "github"
} }
}, },
"hyprlang": {
"inputs": {
"nixpkgs": [
"hyprland",
"nixpkgs"
]
},
"locked": {
"lastModified": 1708005943,
"narHash": "sha256-9TT3xk++LI5/SPYgjYX34xZ4ebR93c1uerIq+SE/ues=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "aeb3e012adc7b3235335c540b214b82267c2b983",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprlang",
"type": "github"
}
},
"hyprlang_2": {
"inputs": {
"nixpkgs": [
"hyprland",
"xdph",
"nixpkgs"
]
},
"locked": {
"lastModified": 1704287638,
"narHash": "sha256-TuRXJGwtK440AXQNl5eiqmQqY4LZ/9+z/R7xC0ie3iA=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "6624f2bb66d4d27975766e81f77174adbe58ec97",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprlang",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1700612854, "lastModified": 1707546158,
"narHash": "sha256-yrQ8osMD+vDLGFX7pcwsY/Qr5PUd6OmDMYJZzZi0+zc=", "narHash": "sha256-nYYJTpzfPMDxI8mzhQsYjIUX+grorqjKEU9Np6Xwy/0=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "19cbff58383a4ae384dea4d1d0c823d72b49d614", "rev": "d934204a0f8d9198e1e4515dd6fec76a139c87f0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -87,18 +131,18 @@
"flake": false, "flake": false,
"locked": { "locked": {
"host": "gitlab.freedesktop.org", "host": "gitlab.freedesktop.org",
"lastModified": 1701368958, "lastModified": 1708558866,
"narHash": "sha256-7kvyoA91etzVEl9mkA/EJfB6z/PltxX7Xc4gcr7/xlo=", "narHash": "sha256-Mz6hCtommq7RQfcPnxLINigO4RYSNt23HeJHC6mVmWI=",
"owner": "wlroots", "owner": "wlroots",
"repo": "wlroots", "repo": "wlroots",
"rev": "5d639394f3e83b01596dcd166a44a9a1a2583350", "rev": "0cb091f1a2d345f37d2ee445f4ffd04f7f4ec9e5",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {
"host": "gitlab.freedesktop.org", "host": "gitlab.freedesktop.org",
"owner": "wlroots", "owner": "wlroots",
"repo": "wlroots", "repo": "wlroots",
"rev": "5d639394f3e83b01596dcd166a44a9a1a2583350", "rev": "0cb091f1a2d345f37d2ee445f4ffd04f7f4ec9e5",
"type": "gitlab" "type": "gitlab"
} }
}, },
@ -108,6 +152,7 @@
"hyprland", "hyprland",
"hyprland-protocols" "hyprland-protocols"
], ],
"hyprlang": "hyprlang_2",
"nixpkgs": [ "nixpkgs": [
"hyprland", "hyprland",
"nixpkgs" "nixpkgs"
@ -118,11 +163,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1700508250, "lastModified": 1706521509,
"narHash": "sha256-X4o/mifI7Nhu0UKYlxx53wIC+gYDo3pVM9L2u3PE2bE=", "narHash": "sha256-AInZ50acOJ3wzUwGzNr1TmxGTMx+8j6oSTzz4E7Vbp8=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland", "repo": "xdg-desktop-portal-hyprland",
"rev": "eb120ff25265ecacd0fc13d7dab12131b60d0f47", "rev": "c06fd88b3da492b8f9067be021b9184f7012b5a8",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -10,7 +10,7 @@
packages = hyprlandSystems (system: pkgs: let packages = hyprlandSystems (system: pkgs: let
hyprlandPackage = hyprland.packages.${system}.hyprland; hyprlandPackage = hyprland.packages.${system}.hyprland;
in rec { in rec {
hy3 = hyprlandPackage.stdenv.mkDerivation { hy3 = (pkgs.keepDebugInfo hyprlandPackage.stdenv).mkDerivation {
pname = "hy3"; pname = "hy3";
version = "0.1"; version = "0.1";
src = ./.; src = ./.;
@ -45,7 +45,7 @@
name = "hy3"; name = "hy3";
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
clang-tools_16 clang-tools_17
bear bear
]; ];

View file

@ -73,11 +73,14 @@ void Hy3Layout::onWindowCreated(CWindow* window, eDirection direction) {
void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) { void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) {
hy3_log( hy3_log(
TRACE, LOG,
"onWindowCreatedTiling called with window {:x} (floating: {})", "onWindowCreatedTiling called with window {:x} (floating: {}, monitor: {}, workspace: {})",
(uintptr_t) window, (uintptr_t) window,
window->m_bIsFloating window->m_bIsFloating,
window->m_iMonitorID,
window->m_iWorkspaceID
); );
if (window->m_bIsFloating) return; if (window->m_bIsFloating) return;
auto* existing = this->getNodeFromWindow(window); auto* existing = this->getNodeFromWindow(window);
@ -91,55 +94,104 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) {
return; return;
} }
auto* monitor = g_pCompositor->getMonitorFromID(window->m_iMonitorID); this->nodes.push_back({
.parent = nullptr,
.data = window,
.workspace_id = window->m_iWorkspaceID,
.layout = this,
});
this->insertNode(this->nodes.back());
}
void Hy3Layout::insertNode(Hy3Node& node) {
if (node.parent != nullptr) {
hy3_log(
ERR,
"insertNode called for node {:x} which already has a parent ({:x})",
(uintptr_t) &node,
(uintptr_t) node.parent
);
return;
}
auto* workspace = g_pCompositor->getWorkspaceByID(node.workspace_id);
if (workspace == nullptr) {
hy3_log(
ERR,
"insertNode called for node {:x} with invalid workspace id {}",
(uintptr_t) &node,
node.workspace_id
);
return;
}
node.reparenting = true;
auto* monitor = g_pCompositor->getMonitorFromID(workspace->m_iMonitorID);
Hy3Node* opening_into; Hy3Node* opening_into;
Hy3Node* opening_after = nullptr; Hy3Node* opening_after = nullptr;
if (monitor->activeWorkspace != -1) { auto* root = this->getWorkspaceRootGroup(node.workspace_id);
auto* root = this->getWorkspaceRootGroup(monitor->activeWorkspace);
if (root != nullptr) { if (root != nullptr) {
opening_after = root->getFocusedNode(); opening_after = root->getFocusedNode();
// opening_after->parent cannot be nullptr // opening_after->parent cannot be nullptr
if (opening_after == root) { if (opening_after == root) {
opening_after = opening_after =
opening_after->intoGroup(Hy3GroupLayout::SplitH, GroupEphemeralityOption::Standard); opening_after->intoGroup(Hy3GroupLayout::SplitH, GroupEphemeralityOption::Standard);
}
} }
} }
if (opening_after == nullptr) { if (opening_after == nullptr) {
if (g_pCompositor->m_pLastWindow != nullptr && !g_pCompositor->m_pLastWindow->m_bIsFloating if (g_pCompositor->m_pLastWindow != nullptr
&& g_pCompositor->m_pLastWindow != window && g_pCompositor->m_pLastWindow->m_iWorkspaceID == node.workspace_id
&& g_pCompositor->m_pLastWindow->m_iWorkspaceID == window->m_iWorkspaceID && !g_pCompositor->m_pLastWindow->m_bIsFloating
&& (node.data.type == Hy3NodeType::Window
|| g_pCompositor->m_pLastWindow != node.data.as_window)
&& g_pCompositor->m_pLastWindow->m_bIsMapped) && g_pCompositor->m_pLastWindow->m_bIsMapped)
{ {
opening_after = this->getNodeFromWindow(g_pCompositor->m_pLastWindow); opening_after = this->getNodeFromWindow(g_pCompositor->m_pLastWindow);
} else { } else {
opening_after = this->getNodeFromWindow( auto* mouse_window = g_pCompositor->vectorToWindowUnified(
g_pCompositor->vectorToWindowTiled(g_pInputManager->getMouseCoordsInternal()) g_pInputManager->getMouseCoordsInternal(),
RESERVED_EXTENTS | INPUT_EXTENTS
); );
if (mouse_window != nullptr && mouse_window->m_iWorkspaceID == node.workspace_id) {
opening_after = this->getNodeFromWindow(mouse_window);
}
} }
} }
if (opening_after != nullptr && opening_after->workspace_id != window->m_iWorkspaceID) { if (opening_after != nullptr
&& ((node.data.type == Hy3NodeType::Group
&& (opening_after == &node || node.data.as_group.hasChild(opening_after)))
|| opening_after->reparenting))
{
opening_after = nullptr; opening_after = nullptr;
} }
if (opening_after != nullptr) { if (opening_after != nullptr) {
opening_into = opening_after->parent; opening_into = opening_after->parent;
} else { } else {
if ((opening_into = this->getWorkspaceRootGroup(window->m_iWorkspaceID)) == nullptr) { if ((opening_into = this->getWorkspaceRootGroup(node.workspace_id)) == nullptr) {
static const auto* tab_first_window = static const auto tab_first_window =
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tab_first_window")->intValue; ConfigValue<Hyprlang::INT>("plugin:hy3:tab_first_window");
auto width =
monitor->vecSize.x - monitor->vecReservedBottomRight.x - monitor->vecReservedTopLeft.x;
auto height =
monitor->vecSize.y - monitor->vecReservedBottomRight.y - monitor->vecReservedTopLeft.y;
this->nodes.push_back({ this->nodes.push_back({
.data = Hy3GroupLayout::SplitH, .data = height > width ? Hy3GroupLayout::SplitV : Hy3GroupLayout::SplitH,
.position = monitor->vecPosition + monitor->vecReservedTopLeft, .position = monitor->vecPosition + monitor->vecReservedTopLeft,
.size = monitor->vecSize - monitor->vecReservedTopLeft - monitor->vecReservedBottomRight, .size = monitor->vecSize - monitor->vecReservedTopLeft - monitor->vecReservedBottomRight,
.workspace_id = window->m_iWorkspaceID, .workspace_id = node.workspace_id,
.layout = this, .layout = this,
}); });
@ -151,7 +203,7 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) {
.data = Hy3GroupLayout::Tabbed, .data = Hy3GroupLayout::Tabbed,
.position = parent.position, .position = parent.position,
.size = parent.size, .size = parent.size,
.workspace_id = window->m_iWorkspaceID, .workspace_id = node.workspace_id,
.layout = this, .layout = this,
}); });
@ -168,23 +220,23 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) {
return; return;
} }
if (opening_into->workspace_id != window->m_iWorkspaceID) { if (opening_into->workspace_id != node.workspace_id) {
hy3_log( hy3_log(
WARN, WARN,
"opening_into node ({:x}) is on workspace {} which does not match the new window " "opening_into node ({:x}) is on workspace {} which does not match the new window "
"(workspace {})", "(workspace {})",
(uintptr_t) opening_into, (uintptr_t) opening_into,
opening_into->workspace_id, opening_into->workspace_id,
window->m_iWorkspaceID node.workspace_id
); );
} }
{ {
// clang-format off // clang-format off
static const auto* at_enable = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:autotile:enable")->intValue; static const auto at_enable = ConfigValue<Hyprlang::INT>("plugin:hy3:autotile:enable");
static const auto* at_ephemeral = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:autotile:ephemeral_groups")->intValue; static const auto at_ephemeral = ConfigValue<Hyprlang::INT>("plugin:hy3:autotile:ephemeral_groups");
static const auto* at_trigger_width = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:autotile:trigger_width")->intValue; static const auto at_trigger_width = ConfigValue<Hyprlang::INT>("plugin:hy3:autotile:trigger_width");
static const auto* at_trigger_height = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:autotile:trigger_height")->intValue; static const auto at_trigger_height = ConfigValue<Hyprlang::INT>("plugin:hy3:autotile:trigger_height");
// clang-format on // clang-format on
this->updateAutotileWorkspaces(); this->updateAutotileWorkspaces();
@ -210,14 +262,8 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) {
} }
} }
this->nodes.push_back({ node.parent = opening_into;
.parent = opening_into, node.reparenting = false;
.data = window,
.workspace_id = window->m_iWorkspaceID,
.layout = this,
});
auto& node = this->nodes.back();
if (opening_after == nullptr) { if (opening_after == nullptr) {
opening_into->data.as_group.children.push_back(&node); opening_into->data.as_group.children.push_back(&node);
@ -230,8 +276,7 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) {
hy3_log( hy3_log(
LOG, LOG,
"tiled window ({:x} as node {:x}) after node {:x} in node {:x}", "tiled node {:x} inserted after node {:x} in node {:x}",
(uintptr_t) window,
(uintptr_t) &node, (uintptr_t) &node,
(uintptr_t) opening_after, (uintptr_t) opening_after,
(uintptr_t) opening_into (uintptr_t) opening_into
@ -242,8 +287,8 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) {
} }
void Hy3Layout::onWindowRemovedTiling(CWindow* window) { void Hy3Layout::onWindowRemovedTiling(CWindow* window) {
static const auto* node_collapse_policy = static const auto node_collapse_policy =
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:node_collapse_policy")->intValue; ConfigValue<Hyprlang::INT>("plugin:hy3:node_collapse_policy");
auto* node = this->getNodeFromWindow(window); auto* node = this->getNodeFromWindow(window);
@ -352,215 +397,100 @@ void Hy3Layout::recalculateWindow(CWindow* window) {
node->recalcSizePosRecursive(); node->recalcSizePosRecursive();
} }
ShiftDirection reverse(ShiftDirection direction) {
switch (direction) {
case ShiftDirection::Left: return ShiftDirection::Right;
case ShiftDirection::Right: return ShiftDirection::Left;
case ShiftDirection::Up: return ShiftDirection::Down;
case ShiftDirection::Down: return ShiftDirection::Up;
default: return direction;
}
}
void Hy3Layout::resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CWindow* pWindow) { void Hy3Layout::resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CWindow* pWindow) {
auto window = pWindow ? pWindow : g_pCompositor->m_pLastWindow; auto window = pWindow ? pWindow : g_pCompositor->m_pLastWindow;
if (!g_pCompositor->windowValidMapped(window)) return; if (!g_pCompositor->windowValidMapped(window)) return;
auto* node = this->getNodeFromWindow(window); auto* node = this->getNodeFromWindow(window);
if (node == nullptr) return;
if (node->parent != nullptr && node->parent->data.as_group.focused_child == node) if (node != nullptr) {
node = &node->getExpandActor(); node = &node->getExpandActor();
bool drag_x; auto monitor = g_pCompositor->getMonitorFromID(window->m_iMonitorID);
bool drag_y;
if (corner == CORNER_NONE) { const bool display_left =
drag_x = delta.x > 0; STICKS(node->position.x, monitor->vecPosition.x + monitor->vecReservedTopLeft.x);
drag_y = delta.y > 0; const bool display_right = STICKS(
} else { node->position.x + node->size.x,
drag_x = corner == CORNER_TOPRIGHT || corner == CORNER_BOTTOMRIGHT; monitor->vecPosition.x + monitor->vecSize.x - monitor->vecReservedBottomRight.x
drag_y = corner == CORNER_BOTTOMLEFT || corner == CORNER_BOTTOMRIGHT; );
} const bool display_top =
STICKS(node->position.y, monitor->vecPosition.y + monitor->vecReservedTopLeft.y);
const bool display_bottom = STICKS(
node->position.y + node->size.y,
monitor->vecPosition.y + monitor->vecSize.y - monitor->vecReservedBottomRight.y
);
const auto animate = Vector2D resize_delta = delta;
&g_pConfigManager->getConfigValuePtr("misc:animate_manual_resizes")->intValue; bool node_is_root = (node->data.type == Hy3NodeType::Group && node->parent == nullptr)
|| (node->data.type == Hy3NodeType::Window
&& (node->parent == nullptr || node->parent->parent == nullptr));
auto monitor = g_pCompositor->getMonitorFromID(window->m_iMonitorID); if (node_is_root) {
if (display_left && display_right) resize_delta.x = 0;
const bool display_left = if (display_top && display_bottom) resize_delta.y = 0;
STICKS(node->position.x, monitor->vecPosition.x + monitor->vecReservedTopLeft.x);
const bool display_right = STICKS(
node->position.x + node->size.x,
monitor->vecPosition.x + monitor->vecSize.x - monitor->vecReservedBottomRight.x
);
const bool display_top =
STICKS(node->position.y, monitor->vecPosition.y + monitor->vecReservedTopLeft.y);
const bool display_bottom = STICKS(
node->position.y + node->size.y,
monitor->vecPosition.y + monitor->vecSize.y - monitor->vecReservedBottomRight.y
);
Vector2D allowed_movement = delta;
if (display_left && display_right) allowed_movement.x = 0;
if (display_top && display_bottom) allowed_movement.y = 0;
auto* inner_node = node;
// break into parent groups when encountering a corner we're dragging in or a
// tab group
while (inner_node->parent != nullptr) {
auto& group = inner_node->parent->data.as_group;
switch (group.layout) {
case Hy3GroupLayout::Tabbed:
// treat tabbed layouts as if they dont exist during resizing
goto cont;
case Hy3GroupLayout::SplitH:
if ((drag_x && group.children.back() == inner_node)
|| (!drag_x && group.children.front() == inner_node))
{
goto cont;
}
break;
case Hy3GroupLayout::SplitV:
if ((drag_y && group.children.back() == inner_node)
|| (!drag_y && group.children.front() == inner_node))
{
goto cont;
}
break;
} }
break; // Don't execute the logic unless there's something to do
cont: if (resize_delta.x != 0 || resize_delta.y != 0) {
inner_node = inner_node->parent; ShiftDirection target_edge_x;
} ShiftDirection target_edge_y;
auto* inner_parent = inner_node->parent; // Determine the direction in which we're going to look for the neighbor node
if (inner_parent == nullptr) return; // that will be resized
if (corner == CORNER_NONE) { // It's probably a keyboard event.
target_edge_x = display_right ? ShiftDirection::Left : ShiftDirection::Right;
target_edge_y = display_bottom ? ShiftDirection::Up : ShiftDirection::Down;
auto* outer_node = inner_node; // If the anchor is not at the top/left then reverse the delta
if (target_edge_x == ShiftDirection::Left) resize_delta.x = -resize_delta.x;
// break into parent groups when encountering a corner we're dragging in, a if (target_edge_y == ShiftDirection::Up) resize_delta.y = -resize_delta.y;
// tab group, or a layout matching the inner_parent. } else { // It's probably a mouse event
while (outer_node->parent != nullptr) { // Resize against the edges corresponding to the selected corner
auto& group = outer_node->parent->data.as_group; target_edge_x = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT
? ShiftDirection::Left
// break out of all layouts that match the orientation of the inner_parent : ShiftDirection::Right;
if (group.layout == inner_parent->data.as_group.layout) goto cont2; target_edge_y = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT
? ShiftDirection::Up
switch (group.layout) { : ShiftDirection::Down;
case Hy3GroupLayout::Tabbed:
// treat tabbed layouts as if they dont exist during resizing
goto cont2;
case Hy3GroupLayout::SplitH:
if ((drag_x && group.children.back() == outer_node)
|| (!drag_x && group.children.front() == outer_node))
{
goto cont2;
}
break;
case Hy3GroupLayout::SplitV:
if ((drag_y && group.children.back() == outer_node)
|| (!drag_y && group.children.front() == outer_node))
{
goto cont2;
}
break;
}
break;
cont2:
outer_node = outer_node->parent;
}
auto& inner_group = inner_parent->data.as_group;
// adjust the inner node
switch (inner_group.layout) {
case Hy3GroupLayout::SplitH: {
auto ratio_mod =
allowed_movement.x * (float) inner_group.children.size() / inner_parent->size.x;
auto iter = std::find(inner_group.children.begin(), inner_group.children.end(), inner_node);
if (drag_x) {
if (inner_node == inner_group.children.back()) break;
iter = std::next(iter);
} else {
if (inner_node == inner_group.children.front()) break;
iter = std::prev(iter);
ratio_mod = -ratio_mod;
}
auto* neighbor = *iter;
inner_node->size_ratio += ratio_mod;
neighbor->size_ratio -= ratio_mod;
} break;
case Hy3GroupLayout::SplitV: {
auto ratio_mod = allowed_movement.y * (float) inner_parent->data.as_group.children.size()
/ inner_parent->size.y;
auto iter = std::find(inner_group.children.begin(), inner_group.children.end(), inner_node);
if (drag_y) {
if (inner_node == inner_group.children.back()) break;
iter = std::next(iter);
} else {
if (inner_node == inner_group.children.front()) break;
iter = std::prev(iter);
ratio_mod = -ratio_mod;
}
auto* neighbor = *iter;
inner_node->size_ratio += ratio_mod;
neighbor->size_ratio -= ratio_mod;
} break;
case Hy3GroupLayout::Tabbed: break;
}
inner_parent->recalcSizePosRecursive(*animate == 0);
if (outer_node != nullptr && outer_node->parent != nullptr) {
auto* outer_parent = outer_node->parent;
auto& outer_group = outer_parent->data.as_group;
// adjust the outer node
switch (outer_group.layout) {
case Hy3GroupLayout::SplitH: {
auto ratio_mod =
allowed_movement.x * (float) outer_group.children.size() / outer_parent->size.x;
auto iter = std::find(outer_group.children.begin(), outer_group.children.end(), outer_node);
if (drag_x) {
if (outer_node == inner_group.children.back()) break;
iter = std::next(iter);
} else {
if (outer_node == inner_group.children.front()) break;
iter = std::prev(iter);
ratio_mod = -ratio_mod;
} }
auto* neighbor = *iter; // Find the neighboring node in each axis, which will be either above or at the
// same level as the initiating node in the layout hierarchy. These are the nodes
// which must get resized (rather than the initiator) because they are the
// highest point in the hierarchy
auto horizontal_neighbor = node->findNeighbor(target_edge_x);
auto vertical_neighbor = node->findNeighbor(target_edge_y);
outer_node->size_ratio += ratio_mod; static const auto animate = ConfigValue<Hyprlang::INT>("misc:animate_manual_resizes");
neighbor->size_ratio -= ratio_mod;
} break;
case Hy3GroupLayout::SplitV: {
auto ratio_mod = allowed_movement.y * (float) outer_parent->data.as_group.children.size()
/ outer_parent->size.y;
auto iter = std::find(outer_group.children.begin(), outer_group.children.end(), outer_node); // Note that the resize direction is reversed, because from the neighbor's perspective
// the edge to be moved is the opposite way round. However, the delta is still the same.
if (drag_y) { if (horizontal_neighbor) {
if (outer_node == outer_group.children.back()) break; horizontal_neighbor->resize(reverse(target_edge_x), resize_delta.x, *animate == 0);
iter = std::next(iter);
} else {
if (outer_node == outer_group.children.front()) break;
iter = std::prev(iter);
ratio_mod = -ratio_mod;
} }
auto* neighbor = *iter; if (vertical_neighbor) {
vertical_neighbor->resize(reverse(target_edge_y), resize_delta.y, *animate == 0);
outer_node->size_ratio += ratio_mod; }
neighbor->size_ratio -= ratio_mod;
} break;
case Hy3GroupLayout::Tabbed: break;
} }
} else if (window->m_bIsFloating) {
outer_parent->recalcSizePosRecursive(*animate == 0); // No parent node - is this a floating window? If so, use the same logic as the `main` layout
const auto required_size = Vector2D(
std::max((window->m_vRealSize.goalv() + delta).x, 20.0),
std::max((window->m_vRealSize.goalv() + delta).y, 20.0)
);
window->m_vRealSize = required_size;
} }
} }
@ -613,13 +543,21 @@ void Hy3Layout::fullscreenRequestForWindow(
// Copy of vaxry's massive hack // Copy of vaxry's massive hack
// clang-format off // clang-format off
static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue; static const auto gaps_in = ConfigValue<Hyprlang::CUSTOMTYPE, CCssGapData>("general:gaps_in");
static const auto* gaps_out = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_out")->intValue; static const auto gaps_out = ConfigValue<Hyprlang::CUSTOMTYPE, CCssGapData>("general:gaps_out");
// clang-format on // clang-format on
int outer_gaps = -(*gaps_in - *gaps_out); // clang-format off
auto gap_pos_offset = Vector2D(outer_gaps, outer_gaps); auto gap_pos_offset = Vector2D(
auto gap_size_offset = Vector2D(outer_gaps * 2, outer_gaps * 2); -(gaps_in->left - gaps_out->left),
-(gaps_in->top - gaps_out->top)
);
// clang-format on
auto gap_size_offset = Vector2D(
-(gaps_in->left - gaps_out->left) + -(gaps_in->right - gaps_out->right),
-(gaps_in->top - gaps_out->top) + -(gaps_in->bottom - gaps_out->bottom)
);
Hy3Node fakeNode = { Hy3Node fakeNode = {
.data = window, .data = window,
@ -701,7 +639,7 @@ CWindow* Hy3Layout::getNextWindowCandidate(CWindow* window) {
for (auto& w: g_pCompositor->m_vWindows | std::views::reverse) { for (auto& w: g_pCompositor->m_vWindows | std::views::reverse) {
if (w->m_bIsMapped && !w->isHidden() && w->m_bIsFloating && w->m_iX11Type != 2 if (w->m_bIsMapped && !w->isHidden() && w->m_bIsFloating && w->m_iX11Type != 2
&& w->m_iWorkspaceID == window->m_iWorkspaceID && !w->m_bX11ShouldntFocus && w->m_iWorkspaceID == window->m_iWorkspaceID && !w->m_bX11ShouldntFocus
&& !w->m_bNoFocus && w.get() != window) && !w->m_sAdditionalConfigData.noFocus && w.get() != window)
{ {
return w.get(); return w.get();
} }
@ -714,6 +652,7 @@ CWindow* Hy3Layout::getNextWindowCandidate(CWindow* window) {
switch (node->data.type) { switch (node->data.type) {
case Hy3NodeType::Window: return node->data.as_window; case Hy3NodeType::Window: return node->data.as_window;
case Hy3NodeType::Group: return nullptr; case Hy3NodeType::Group: return nullptr;
default: return nullptr;
} }
} }
@ -969,6 +908,95 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible
} }
} }
void changeNodeWorkspaceRecursive(Hy3Node& node, CWorkspace* workspace) {
node.workspace_id = workspace->m_iID;
if (node.data.type == Hy3NodeType::Window) {
auto* window = node.data.as_window;
window->moveToWorkspace(workspace->m_iID);
window->updateToplevel();
window->updateDynamicRules();
} else {
for (auto* child: node.data.as_group.children) {
changeNodeWorkspaceRecursive(*child, workspace);
}
}
}
void Hy3Layout::moveNodeToWorkspace(int origin, std::string wsname, bool follow) {
std::string target_name;
auto target = getWorkspaceIDFromString(wsname, target_name);
if (target == WORKSPACE_INVALID) {
hy3_log(ERR, "moveNodeToWorkspace called with invalid workspace {}", wsname);
return;
}
if (origin == target) return;
auto* node = this->getWorkspaceFocusedNode(origin);
auto* focused_window = g_pCompositor->m_pLastWindow;
auto* focused_window_node = this->getNodeFromWindow(focused_window);
auto* workspace = g_pCompositor->getWorkspaceByID(target);
auto wsid = node != nullptr ? node->workspace_id
: focused_window != nullptr ? focused_window->m_iWorkspaceID
: WORKSPACE_INVALID;
if (wsid == WORKSPACE_INVALID) return;
auto* origin_ws = g_pCompositor->getWorkspaceByID(wsid);
if (workspace == nullptr) {
hy3_log(LOG, "creating target workspace {} for node move", target);
workspace = g_pCompositor->createNewWorkspace(target, origin_ws->m_iMonitorID, target_name);
}
// floating or fullscreen
if (focused_window != nullptr
&& (focused_window_node == nullptr || focused_window->m_bIsFullscreen))
{
hy3_log(LOG, "{:x}, {:x}", (uintptr_t) focused_window, (uintptr_t) workspace);
g_pCompositor->moveWindowToWorkspaceSafe(focused_window, workspace);
} else {
if (node == nullptr) return;
hy3_log(
LOG,
"moving node {:x} from workspace {} to workspace {} (follow: {})",
(uintptr_t) node,
origin,
target,
follow
);
Hy3Node* expand_actor = nullptr;
node->removeFromParentRecursive(&expand_actor);
if (expand_actor != nullptr) expand_actor->recalcSizePosRecursive();
changeNodeWorkspaceRecursive(*node, workspace);
this->insertNode(*node);
}
if (follow) {
auto* monitor = g_pCompositor->getMonitorFromID(workspace->m_iMonitorID);
if (workspace->m_bIsSpecialWorkspace) {
monitor->setSpecialWorkspace(workspace);
} else if (origin_ws->m_bIsSpecialWorkspace) {
g_pCompositor->getMonitorFromID(origin_ws->m_iMonitorID)->setSpecialWorkspace(nullptr);
}
monitor->changeWorkspace(workspace);
static const auto allow_workspace_cycles =
ConfigValue<Hyprlang::INT>("binds:allow_workspace_cycles");
if (*allow_workspace_cycles) workspace->rememberPrevWorkspace(origin_ws);
}
}
void Hy3Layout::changeFocus(int workspace, FocusShift shift) { void Hy3Layout::changeFocus(int workspace, FocusShift shift) {
auto* node = this->getWorkspaceFocusedNode(workspace); auto* node = this->getWorkspaceFocusedNode(workspace);
if (node == nullptr) return; if (node == nullptr) return;
@ -1029,18 +1057,18 @@ bottom:
Hy3Node* findTabBarAt(Hy3Node& node, Vector2D pos, Hy3Node** focused_node) { Hy3Node* findTabBarAt(Hy3Node& node, Vector2D pos, Hy3Node** focused_node) {
// clang-format off // clang-format off
static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue; static const auto gaps_in = ConfigValue<Hyprlang::CUSTOMTYPE, CCssGapData>("general:gaps_in");
static const auto* gaps_out = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_out")->intValue; static const auto gaps_out = ConfigValue<Hyprlang::CUSTOMTYPE, CCssGapData>("general:gaps_out");
static const auto* tab_bar_height = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:height")->intValue; static const auto tab_bar_height = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:height");
static const auto* tab_bar_padding = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:padding")->intValue; static const auto tab_bar_padding = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:padding");
// clang-format on // clang-format on
auto inset = *tab_bar_height + *tab_bar_padding; auto inset = *tab_bar_height + *tab_bar_padding;
if (node.parent == nullptr) { if (node.parent == nullptr) {
inset += *gaps_out; inset += gaps_out->left;
} else { } else {
inset += *gaps_in; inset += gaps_in->left;
} }
if (node.data.type == Hy3NodeType::Group) { if (node.data.type == Hy3NodeType::Group) {
@ -1100,8 +1128,13 @@ void Hy3Layout::focusTab(
Hy3Node* tab_focused_node; Hy3Node* tab_focused_node;
if (target == TabFocus::MouseLocation || mouse != TabFocusMousePriority::Ignore) { if (target == TabFocus::MouseLocation || mouse != TabFocusMousePriority::Ignore) {
if (g_pCompositor->windowFloatingFromCursor() == nullptr) { auto mouse_pos = g_pInputManager->getMouseCoordsInternal();
auto mouse_pos = g_pInputManager->getMouseCoordsInternal(); if (g_pCompositor->vectorToWindowUnified(
mouse_pos,
RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING | FLOATING_ONLY
)
== nullptr)
{
tab_node = findTabBarAt(*node, mouse_pos, &tab_focused_node); tab_node = findTabBarAt(*node, mouse_pos, &tab_focused_node);
if (tab_node != nullptr) goto hastab; if (tab_node != nullptr) goto hastab;
} }
@ -1275,12 +1308,12 @@ fullscreen:
window->m_vRealPosition = monitor->vecPosition; window->m_vRealPosition = monitor->vecPosition;
window->m_vRealSize = monitor->vecSize; window->m_vRealSize = monitor->vecSize;
goto fsupdate; goto fsupdate;
unfullscreen: // unfullscreen:
if (node->data.type != Hy3NodeType::Window) return; // if (node->data.type != Hy3NodeType::Window) return;
window = node->data.as_window; // window = node->data.as_window;
window->m_bIsFullscreen = false; // window->m_bIsFullscreen = false;
workspace->m_bHasFullscreenWindow = false; // workspace->m_bHasFullscreenWindow = false;
goto fsupdate; // goto fsupdate;
fsupdate: fsupdate:
g_pCompositor->updateWindowAnimatedDecorationValues(window); g_pCompositor->updateWindowAnimatedDecorationValues(window);
g_pXWaylandManager->setWindowSize(window, window->m_vRealSize.goalv()); g_pXWaylandManager->setWindowSize(window, window->m_vRealSize.goalv());
@ -1300,17 +1333,19 @@ bool Hy3Layout::shouldRenderSelected(CWindow* window) {
switch (focused->data.type) { switch (focused->data.type) {
case Hy3NodeType::Window: return focused->data.as_window == window; case Hy3NodeType::Window: return focused->data.as_window == window;
case Hy3NodeType::Group: case Hy3NodeType::Group: {
auto* node = this->getNodeFromWindow(window); auto* node = this->getNodeFromWindow(window);
if (node == nullptr) return false; if (node == nullptr) return false;
return focused->data.as_group.hasChild(node); return focused->data.as_group.hasChild(node);
} }
default: return false;
}
} }
Hy3Node* Hy3Layout::getWorkspaceRootGroup(const int& workspace) { Hy3Node* Hy3Layout::getWorkspaceRootGroup(const int& workspace) {
for (auto& node: this->nodes) { for (auto& node: this->nodes) {
if (node.workspace_id == workspace && node.parent == nullptr if (node.workspace_id == workspace && node.parent == nullptr
&& node.data.type == Hy3NodeType::Group) && node.data.type == Hy3NodeType::Group && !node.reparenting)
{ {
return &node; return &node;
} }
@ -1441,8 +1476,8 @@ void Hy3Layout::applyNodeDataToWindow(Hy3Node* node, bool no_animation) {
} }
// clang-format off // clang-format off
static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue; static const auto gaps_in = ConfigValue<Hyprlang::CUSTOMTYPE, CCssGapData>("general:gaps_in");
static const auto* single_window_no_gaps = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:no_gaps_when_only")->intValue; static const auto single_window_no_gaps = ConfigValue<Hyprlang::INT>("plugin:hy3:no_gaps_when_only");
// clang-format on // clang-format on
if (!g_pCompositor->windowExists(window) || !window->m_bIsMapped) { if (!g_pCompositor->windowExists(window) || !window->m_bIsMapped) {
@ -1490,9 +1525,10 @@ void Hy3Layout::applyNodeDataToWindow(Hy3Node* node, bool no_animation) {
auto calcPos = window->m_vPosition; auto calcPos = window->m_vPosition;
auto calcSize = window->m_vSize; auto calcSize = window->m_vSize;
auto gaps_offset_topleft = Vector2D(*gaps_in, *gaps_in) + node->gap_topleft_offset; auto gaps_offset_topleft = Vector2D(gaps_in->left, gaps_in->top) + node->gap_topleft_offset;
auto gaps_offset_bottomright = Vector2D(*gaps_in * 2, *gaps_in * 2) auto gaps_offset_bottomright =
+ node->gap_bottomright_offset + node->gap_topleft_offset; Vector2D(gaps_in->left + gaps_in->right, gaps_in->top + gaps_in->bottom)
+ node->gap_bottomright_offset + node->gap_topleft_offset;
calcPos = calcPos + gaps_offset_topleft; calcPos = calcPos + gaps_offset_topleft;
calcSize = calcSize - gaps_offset_bottomright; calcSize = calcSize - gaps_offset_bottomright;
@ -1770,8 +1806,8 @@ Hy3Node* Hy3Layout::shiftOrGetFocus(
} }
void Hy3Layout::updateAutotileWorkspaces() { void Hy3Layout::updateAutotileWorkspaces() {
static const auto* autotile_raw_workspaces = static const auto autotile_raw_workspaces =
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:autotile:workspaces")->strValue; ConfigValue<Hyprlang::STRING>("plugin:hy3:autotile:workspaces");
if (*autotile_raw_workspaces == this->autotile.raw_workspaces) { if (*autotile_raw_workspaces == this->autotile.raw_workspaces) {
return; return;

View file

@ -13,9 +13,6 @@ enum class GroupEphemeralityOption {
#include <hyprland/src/layout/IHyprLayout.hpp> #include <hyprland/src/layout/IHyprLayout.hpp>
#include "Hy3Node.hpp"
#include "TabGroup.hpp"
enum class ShiftDirection { enum class ShiftDirection {
Left, Left,
Up, Up,
@ -23,6 +20,11 @@ enum class ShiftDirection {
Right, Right,
}; };
enum class Axis { None, Horizontal, Vertical };
#include "Hy3Node.hpp"
#include "TabGroup.hpp"
enum class FocusShift { enum class FocusShift {
Top, Top,
Bottom, Bottom,
@ -91,6 +93,7 @@ public:
virtual void onEnable(); virtual void onEnable();
virtual void onDisable(); virtual void onDisable();
void insertNode(Hy3Node& node);
void makeGroupOnWorkspace(int workspace, Hy3GroupLayout, GroupEphemeralityOption); void makeGroupOnWorkspace(int workspace, Hy3GroupLayout, GroupEphemeralityOption);
void makeOppositeGroupOnWorkspace(int workspace, GroupEphemeralityOption); void makeOppositeGroupOnWorkspace(int workspace, GroupEphemeralityOption);
void changeGroupOnWorkspace(int workspace, Hy3GroupLayout); void changeGroupOnWorkspace(int workspace, Hy3GroupLayout);
@ -108,6 +111,7 @@ public:
void shiftNode(Hy3Node&, ShiftDirection, bool once, bool visible); void shiftNode(Hy3Node&, ShiftDirection, bool once, bool visible);
void shiftWindow(int workspace, ShiftDirection, bool once, bool visible); void shiftWindow(int workspace, ShiftDirection, bool once, bool visible);
void shiftFocus(int workspace, ShiftDirection, bool visible); void shiftFocus(int workspace, ShiftDirection, bool visible);
void moveNodeToWorkspace(int origin, std::string wsname, bool follow);
void changeFocus(int workspace, FocusShift); void changeFocus(int workspace, FocusShift);
void focusTab(int workspace, TabFocus target, TabFocusMousePriority, bool wrap_scroll, int index); void focusTab(int workspace, TabFocus target, TabFocusMousePriority, bool wrap_scroll, int index);
void setNodeSwallow(int workspace, SetSwallowOption); void setNodeSwallow(int workspace, SetSwallowOption);
@ -142,6 +146,7 @@ private:
void updateAutotileWorkspaces(); void updateAutotileWorkspaces();
bool shouldAutotileWorkspace(int); bool shouldAutotileWorkspace(int);
void resizeNode(Hy3Node*, Vector2D, ShiftDirection resize_edge_x, ShiftDirection resize_edge_y);
struct { struct {
std::string raw_workspaces; std::string raw_workspaces;

View file

@ -7,6 +7,8 @@
#include "Hy3Node.hpp" #include "Hy3Node.hpp"
#include "globals.hpp" #include "globals.hpp"
const float MIN_RATIO = 0.0f;
// Hy3GroupData // // Hy3GroupData //
Hy3GroupData::Hy3GroupData(Hy3GroupLayout layout): layout(layout) { Hy3GroupData::Hy3GroupData(Hy3GroupLayout layout): layout(layout) {
@ -174,6 +176,7 @@ CWindow* Hy3Node::bringToTop() {
} }
return nullptr; return nullptr;
default: return nullptr;
} }
} }
@ -237,6 +240,7 @@ Hy3Node* Hy3Node::getFocusedNode(bool ignore_group_focus, bool stop_at_expanded)
stop_at_expanded stop_at_expanded
); );
} }
default: return nullptr;
} }
} }
@ -266,11 +270,23 @@ Hy3Node& Hy3Node::getExpandActor() {
void Hy3Node::recalcSizePosRecursive(bool no_animation) { void Hy3Node::recalcSizePosRecursive(bool no_animation) {
// clang-format off // clang-format off
static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue; static const auto gaps_in = ConfigValue<Hyprlang::CUSTOMTYPE, CCssGapData>("general:gaps_in");
static const auto* gaps_out = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_out")->intValue; static const auto gaps_out = ConfigValue<Hyprlang::CUSTOMTYPE, CCssGapData>("general:gaps_out");
static const auto* group_inset = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:group_inset")->intValue; static const auto group_inset = ConfigValue<Hyprlang::INT>("plugin:hy3:group_inset");
static const auto* tab_bar_height = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:height")->intValue; static const auto tab_bar_height = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:height");
static const auto* tab_bar_padding = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:padding")->intValue; static const auto tab_bar_padding = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:padding");
// clang-format on
// clang-format off
auto gap_topleft_offset = Vector2D(
-(gaps_in->left - gaps_out->left),
-(gaps_in->top - gaps_out->top)
);
auto gap_bottomright_offset = Vector2D(
-(gaps_in->right - gaps_out->right),
-(gaps_in->bottom - gaps_out->bottom)
);
// clang-format on // clang-format on
if (this->data.type == Hy3NodeType::Window && this->data.as_window->m_bIsFullscreen) { if (this->data.type == Hy3NodeType::Window && this->data.as_window->m_bIsFullscreen) {
@ -283,11 +299,6 @@ void Hy3Node::recalcSizePosRecursive(bool no_animation) {
return; return;
} }
int outer_gaps = -(*gaps_in - *gaps_out);
auto gap_topleft_offset = Vector2D(outer_gaps, outer_gaps);
auto gap_bottomright_offset = Vector2D(outer_gaps, outer_gaps);
Hy3Node fake_node = { Hy3Node fake_node = {
.data = this->data.as_window, .data = this->data.as_window,
.position = monitor->vecPosition + monitor->vecReservedTopLeft, .position = monitor->vecPosition + monitor->vecReservedTopLeft,
@ -301,15 +312,7 @@ void Hy3Node::recalcSizePosRecursive(bool no_animation) {
return; return;
} }
int outer_gaps = 0; if (this->parent != nullptr) {
Vector2D gap_topleft_offset;
Vector2D gap_bottomright_offset;
if (this->parent == nullptr) {
outer_gaps = -(*gaps_in - *gaps_out);
gap_topleft_offset = Vector2D(outer_gaps, outer_gaps);
gap_bottomright_offset = Vector2D(outer_gaps, outer_gaps);
} else {
gap_topleft_offset = this->gap_topleft_offset; gap_topleft_offset = this->gap_topleft_offset;
gap_bottomright_offset = this->gap_bottomright_offset; gap_bottomright_offset = this->gap_bottomright_offset;
} }
@ -584,6 +587,7 @@ bool Hy3Node::isUrgent() {
} }
return false; return false;
default: return false;
} }
} }
@ -759,6 +763,7 @@ Hy3Node* Hy3Node::removeFromParentRecursive(Hy3Node** expand_actor) {
} }
} }
this->parent = nullptr;
return parent; return parent;
} }
@ -804,6 +809,129 @@ bool Hy3Node::swallowGroups(Hy3Node* into) {
return true; return true;
} }
Hy3Node* getOuterChild(Hy3GroupData& group, ShiftDirection direction) {
switch (direction) {
case ShiftDirection::Left:
case ShiftDirection::Up: return group.children.front(); break;
case ShiftDirection::Right:
case ShiftDirection::Down: return group.children.back(); break;
default: return nullptr;
}
}
Hy3Node* Hy3Node::getImmediateSibling(ShiftDirection direction) {
const auto& group = this->parent->data.as_group;
auto iter = std::find(group.children.begin(), group.children.end(), this);
std::__cxx11::list<Hy3Node*>::const_iterator list_sibling;
switch (direction) {
case ShiftDirection::Left:
case ShiftDirection::Up: list_sibling = std::prev(iter); break;
case ShiftDirection::Right:
case ShiftDirection::Down: list_sibling = std::next(iter); break;
default: list_sibling = iter;
}
if (list_sibling == group.children.end()) {
hy3_log(WARN, "getImmediateSibling: sibling not found");
list_sibling = iter;
}
return *list_sibling;
}
Axis getAxis(Hy3GroupLayout layout) {
switch (layout) {
case Hy3GroupLayout::SplitH: return Axis::Horizontal;
case Hy3GroupLayout::SplitV: return Axis::Vertical;
default: return Axis::None;
}
}
Axis getAxis(ShiftDirection direction) {
switch (direction) {
case ShiftDirection::Left:
case ShiftDirection::Right: return Axis::Horizontal;
case ShiftDirection::Down:
case ShiftDirection::Up: return Axis::Vertical;
default: return Axis::None;
}
}
Hy3Node* Hy3Node::findNeighbor(ShiftDirection direction) {
auto current_node = this;
Hy3Node* sibling = nullptr;
while (sibling == nullptr && current_node->parent != nullptr) {
auto& parent_group = current_node->parent->data.as_group;
if (parent_group.layout != Hy3GroupLayout::Tabbed
&& getAxis(parent_group.layout) == getAxis(direction))
{
// If the current node is the outermost child of its parent group then proceed
// then we need to look at the parent - otherwise, the sibling is simply the immediate
// sibling in the child collection
if (getOuterChild(parent_group, direction) != current_node) {
sibling = current_node->getImmediateSibling(direction);
}
}
current_node = current_node->parent;
}
return sibling;
}
int directionToIteratorIncrement(ShiftDirection direction) {
switch (direction) {
case ShiftDirection::Left:
case ShiftDirection::Up: return -1;
case ShiftDirection::Right:
case ShiftDirection::Down: return 1;
default: hy3_log(WARN, "Unknown ShiftDirection enum value: {}", (int) direction); return 1;
}
}
void Hy3Node::resize(ShiftDirection direction, double delta, bool no_animation) {
auto& parent_node = this->parent;
auto& containing_group = parent_node->data.as_group;
if (containing_group.layout != Hy3GroupLayout::Tabbed
&& getAxis(direction) == getAxis(containing_group.layout))
{
double parent_size =
getAxis(direction) == Axis::Horizontal ? parent_node->size.x : parent_node->size.y;
auto ratio_mod = delta * (float) containing_group.children.size() / parent_size;
const auto end_of_children = containing_group.children.end();
auto iter = std::find(containing_group.children.begin(), end_of_children, this);
if (iter != end_of_children) {
const auto outermost_node_in_group = getOuterChild(containing_group, direction);
if (this != outermost_node_in_group) {
auto inc = directionToIteratorIncrement(direction);
iter = std::next(iter, inc);
ratio_mod *= inc;
}
if (iter != end_of_children) {
auto* neighbor = *iter;
auto requested_size_ratio = this->size_ratio + ratio_mod;
auto requested_neighbor_size_ratio = neighbor->size_ratio - ratio_mod;
if (requested_size_ratio >= MIN_RATIO && requested_neighbor_size_ratio >= MIN_RATIO) {
this->size_ratio = requested_size_ratio;
neighbor->size_ratio = requested_neighbor_size_ratio;
parent_node->recalcSizePosRecursive(no_animation);
}
}
}
}
}
void Hy3Node::swapData(Hy3Node& a, Hy3Node& b) { void Hy3Node::swapData(Hy3Node& a, Hy3Node& b) {
Hy3NodeData aData = std::move(a.data); Hy3NodeData aData = std::move(a.data);
a.data = std::move(b.data); a.data = std::move(b.data);

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
struct Hy3Node; struct Hy3Node;
struct Hy3GroupData;
enum class Hy3GroupLayout; enum class Hy3GroupLayout;
#include <list> #include <list>
@ -79,6 +80,7 @@ public:
struct Hy3Node { struct Hy3Node {
Hy3Node* parent = nullptr; Hy3Node* parent = nullptr;
bool reparenting = false;
Hy3NodeData data; Hy3NodeData data;
Vector2D position; Vector2D position;
Vector2D size; Vector2D size;
@ -97,6 +99,9 @@ struct Hy3Node {
void markFocused(); void markFocused();
void raiseToTop(); void raiseToTop();
Hy3Node* getFocusedNode(bool ignore_group_focus = false, bool stop_at_expanded = false); Hy3Node* getFocusedNode(bool ignore_group_focus = false, bool stop_at_expanded = false);
Hy3Node* findNeighbor(ShiftDirection);
Hy3Node* getImmediateSibling(ShiftDirection);
void resize(ShiftDirection, double, bool no_animation = false);
bool isIndirectlyFocused(); bool isIndirectlyFocused();
Hy3Node& getExpandActor(); Hy3Node& getExpandActor();

View file

@ -126,18 +126,18 @@ bool Hy3TabBarEntry::shouldRemove() {
void Hy3TabBarEntry::prepareTexture(float scale, CBox& box) { void Hy3TabBarEntry::prepareTexture(float scale, CBox& box) {
// clang-format off // clang-format off
static const auto* s_rounding = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:rounding")->intValue; static const auto s_rounding = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:rounding");
static const auto* render_text = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:render_text")->intValue; static const auto render_text = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:render_text");
static const auto* text_center = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:text_center")->intValue; static const auto text_center = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:text_center");
static const auto* text_font = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:text_font")->strValue; static const auto text_font = ConfigValue<Hyprlang::STRING>("plugin:hy3:tabs:text_font");
static const auto* text_height = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:text_height")->intValue; static const auto text_height = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:text_height");
static const auto* text_padding = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:text_padding")->intValue; static const auto text_padding = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:text_padding");
static const auto* col_active = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.active")->intValue; static const auto col_active = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.active");
static const auto* col_urgent = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.urgent")->intValue; static const auto col_urgent = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.urgent");
static const auto* col_inactive = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.inactive")->intValue; static const auto col_inactive = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.inactive");
static const auto* col_text_active = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.text.active")->intValue; static const auto col_text_active = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.text.active");
static const auto* col_text_urgent = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.text.urgent")->intValue; static const auto col_text_urgent = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.text.urgent");
static const auto* col_text_inactive = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.text.inactive")->intValue; static const auto col_text_inactive = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.text.inactive");
// clang-format on // clang-format on
auto width = box.width; auto width = box.width;
@ -225,10 +225,9 @@ void Hy3TabBarEntry::prepareTexture(float scale, CBox& box) {
PangoLayout* layout = pango_cairo_create_layout(cairo); PangoLayout* layout = pango_cairo_create_layout(cairo);
pango_layout_set_text(layout, this->window_title.c_str(), -1); pango_layout_set_text(layout, this->window_title.c_str(), -1);
if (*text_center) if (*text_center) pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
PangoFontDescription* font_desc = pango_font_description_from_string(text_font->c_str()); PangoFontDescription* font_desc = pango_font_description_from_string(*text_font);
pango_font_description_set_size(font_desc, *text_height * scale * PANGO_SCALE); pango_font_description_set_size(font_desc, *text_height * scale * PANGO_SCALE);
pango_layout_set_font_description(layout, font_desc); pango_layout_set_font_description(layout, font_desc);
pango_font_description_free(font_desc); pango_font_description_free(font_desc);
@ -453,17 +452,19 @@ Hy3TabGroup::Hy3TabGroup(Hy3Node& node) {
} }
void Hy3TabGroup::updateWithGroup(Hy3Node& node, bool warp) { void Hy3TabGroup::updateWithGroup(Hy3Node& node, bool warp) {
static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue; static const auto gaps_in = ConfigValue<Hyprlang::CUSTOMTYPE, CCssGapData>("general:gaps_in");
static const auto* gaps_out = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_out")->intValue; static const auto gaps_out = ConfigValue<Hyprlang::CUSTOMTYPE, CCssGapData>("general:gaps_out");
static const auto* bar_height = static const auto bar_height = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:height");
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:height")->intValue;
auto gaps = node.parent == nullptr ? *gaps_out : *gaps_in; auto& gaps = node.parent == nullptr ? gaps_out : gaps_in;
auto tpos = node.position + Vector2D(gaps, gaps) + node.gap_topleft_offset; auto tpos = node.position + Vector2D(gaps->left, gaps->top) + node.gap_topleft_offset;
// clang-format off
auto tsize = Vector2D( auto tsize = Vector2D(
node.size.x - node.gap_bottomright_offset.x - node.gap_topleft_offset.x - gaps * 2, node.size.x - node.gap_bottomright_offset.x - node.gap_topleft_offset.x - (gaps->left + gaps->right),
*bar_height *bar_height
); );
// clang-format on
this->hidden = node.hidden; this->hidden = node.hidden;
if (this->pos.goalv() != tpos) { if (this->pos.goalv() != tpos) {
@ -485,10 +486,8 @@ void Hy3TabGroup::updateWithGroup(Hy3Node& node, bool warp) {
} }
void Hy3TabGroup::tick() { void Hy3TabGroup::tick() {
static const auto* enter_from_top = static const auto enter_from_top = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:from_top");
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:from_top")->intValue; static const auto padding = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:padding");
static const auto* padding =
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:padding")->intValue;
auto* workspace = g_pCompositor->getWorkspaceByID(this->workspace_id); auto* workspace = g_pCompositor->getWorkspaceByID(this->workspace_id);
this->bar.tick(); this->bar.tick();
@ -529,12 +528,9 @@ void Hy3TabGroup::tick() {
} }
void Hy3TabGroup::renderTabBar() { void Hy3TabGroup::renderTabBar() {
static const auto* window_rounding = static const auto window_rounding = ConfigValue<Hyprlang::INT>("decoration:rounding");
&HyprlandAPI::getConfigValue(PHANDLE, "decoration:rounding")->intValue; static const auto enter_from_top = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:from_top");
static const auto* enter_from_top = static const auto padding = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:padding");
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:from_top")->intValue;
static const auto* padding =
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:padding")->intValue;
auto* monitor = g_pHyprOpenGL->m_RenderData.pMonitor; auto* monitor = g_pHyprOpenGL->m_RenderData.pMonitor;
auto* workspace = g_pCompositor->getWorkspaceByID(this->workspace_id); auto* workspace = g_pCompositor->getWorkspaceByID(this->workspace_id);

View file

@ -6,7 +6,7 @@
#include "dispatchers.hpp" #include "dispatchers.hpp"
#include "globals.hpp" #include "globals.hpp"
int workspace_for_action() { int workspace_for_action(bool allow_fullscreen = false) {
if (g_pLayoutManager->getCurrentLayout() != g_Hy3Layout.get()) return -1; if (g_pLayoutManager->getCurrentLayout() != g_Hy3Layout.get()) return -1;
int workspace_id = g_pCompositor->m_pLastMonitor->activeWorkspace; int workspace_id = g_pCompositor->m_pLastMonitor->activeWorkspace;
@ -14,7 +14,7 @@ int workspace_for_action() {
if (workspace_id == -1) return -1; if (workspace_id == -1) return -1;
auto* workspace = g_pCompositor->getWorkspaceByID(workspace_id); auto* workspace = g_pCompositor->getWorkspaceByID(workspace_id);
if (workspace == nullptr) return -1; if (workspace == nullptr) return -1;
if (workspace->m_bHasFullscreenWindow) return -1; if (!allow_fullscreen && workspace->m_bHasFullscreenWindow) return -1;
return workspace_id; return workspace_id;
} }
@ -119,6 +119,20 @@ void dispatch_movefocus(std::string value) {
} }
} }
void dispatch_move_to_workspace(std::string value) {
int origin_workspace = workspace_for_action(true);
if (origin_workspace == -1) return;
auto args = CVarList(value);
auto workspace = args[0];
if (workspace == "") return;
bool follow = args[1] == "follow";
g_Hy3Layout->moveNodeToWorkspace(origin_workspace, workspace, follow);
}
void dispatch_changefocus(std::string arg) { void dispatch_changefocus(std::string arg) {
int workspace = workspace_for_action(); int workspace = workspace_for_action();
if (workspace == -1) return; if (workspace == -1) return;
@ -188,7 +202,7 @@ void dispatch_setswallow(std::string arg) {
} }
void dispatch_killactive(std::string value) { void dispatch_killactive(std::string value) {
int workspace = workspace_for_action(); int workspace = workspace_for_action(true);
if (workspace == -1) return; if (workspace == -1) return;
g_Hy3Layout->killFocusedNode(workspace); g_Hy3Layout->killFocusedNode(workspace);
@ -237,6 +251,7 @@ void registerDispatchers() {
HyprlandAPI::addDispatcher(PHANDLE, "hy3:setephemeral", dispatch_setephemeral); HyprlandAPI::addDispatcher(PHANDLE, "hy3:setephemeral", dispatch_setephemeral);
HyprlandAPI::addDispatcher(PHANDLE, "hy3:movefocus", dispatch_movefocus); HyprlandAPI::addDispatcher(PHANDLE, "hy3:movefocus", dispatch_movefocus);
HyprlandAPI::addDispatcher(PHANDLE, "hy3:movewindow", dispatch_movewindow); HyprlandAPI::addDispatcher(PHANDLE, "hy3:movewindow", dispatch_movewindow);
HyprlandAPI::addDispatcher(PHANDLE, "hy3:movetoworkspace", dispatch_move_to_workspace);
HyprlandAPI::addDispatcher(PHANDLE, "hy3:changefocus", dispatch_changefocus); HyprlandAPI::addDispatcher(PHANDLE, "hy3:changefocus", dispatch_changefocus);
HyprlandAPI::addDispatcher(PHANDLE, "hy3:focustab", dispatch_focustab); HyprlandAPI::addDispatcher(PHANDLE, "hy3:focustab", dispatch_focustab);
HyprlandAPI::addDispatcher(PHANDLE, "hy3:setswallow", dispatch_setswallow); HyprlandAPI::addDispatcher(PHANDLE, "hy3:setswallow", dispatch_setswallow);

View file

@ -1,6 +1,9 @@
#pragma once #pragma once
#include <type_traits>
#include <hyprland/src/plugins/PluginAPI.hpp> #include <hyprland/src/plugins/PluginAPI.hpp>
#include <hyprlang.hpp>
#include "Hy3Layout.hpp" #include "Hy3Layout.hpp"
#include "log.hpp" #include "log.hpp"
@ -19,3 +22,44 @@ inline void errorNotif() {
} }
); );
} }
class HyprlangUnspecifiedCustomType {};
// abandon hope all ye who enter here
template <typename T, typename V = HyprlangUnspecifiedCustomType>
class ConfigValue {
public:
ConfigValue(const std::string& option) {
this->static_data_ptr = HyprlandAPI::getConfigValue(PHANDLE, option)->getDataStaticPtr();
}
template <typename U = T>
typename std::enable_if<std::is_same<U, Hyprlang::CUSTOMTYPE>::value, const V&>::type
operator*() const {
return *(V*) ((Hyprlang::CUSTOMTYPE*) *this->static_data_ptr)->getData();
}
template <typename U = T>
typename std::enable_if<std::is_same<U, Hyprlang::CUSTOMTYPE>::value, const V*>::type
operator->() const {
return &**this;
}
// Bullshit microptimization case for strings
template <typename U = T>
typename std::enable_if<std::is_same<U, Hyprlang::STRING>::value, const char*>::type
operator*() const {
return *(const char**) this->static_data_ptr;
}
template <typename U = T>
typename std::enable_if<
!std::is_same<U, Hyprlang::CUSTOMTYPE>::value && !std::is_same<U, Hyprlang::STRING>::value,
const T&>::type
operator*() const {
return *(T*) *this->static_data_ptr;
}
private:
void* const* static_data_ptr;
};

View file

@ -1,6 +1,3 @@
#include <optional>
#include <stdexcept>
#include <hyprland/src/Compositor.hpp> #include <hyprland/src/Compositor.hpp>
#include <hyprland/src/plugins/PluginAPI.hpp> #include <hyprland/src/plugins/PluginAPI.hpp>
#include <hyprland/src/version.h> #include <hyprland/src/version.h>
@ -30,38 +27,42 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
selection_hook::init(); selection_hook::init();
#define CONF(NAME, TYPE, VALUE) \ #define CONF(NAME, TYPE, VALUE) \
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:" NAME, SConfigValue {.TYPE##Value = VALUE}) HyprlandAPI::addConfigValue( \
PHANDLE, \
"plugin:hy3:" NAME, \
Hyprlang::CConfigValue((Hyprlang::TYPE) VALUE) \
)
// general // general
CONF("no_gaps_when_only", int, 0); CONF("no_gaps_when_only", INT, 0);
CONF("node_collapse_policy", int, 2); CONF("node_collapse_policy", INT, 2);
CONF("group_inset", int, 10); CONF("group_inset", INT, 10);
CONF("special_scale_factor", float, 0.8); CONF("special_scale_factor", INT, 0.8);
CONF("tab_first_window", int, 0); CONF("tab_first_window", INT, 0);
// tabs // tabs
CONF("tabs:height", int, 15); CONF("tabs:height", INT, 15);
CONF("tabs:padding", int, 5); CONF("tabs:padding", INT, 5);
CONF("tabs:from_top", int, 0); CONF("tabs:from_top", INT, 0);
CONF("tabs:rounding", int, 3); CONF("tabs:rounding", INT, 3);
CONF("tabs:render_text", int, 1); CONF("tabs:render_text", INT, 1);
CONF("tabs:text_center", int, 0); CONF("tabs:text_center", INT, 0);
CONF("tabs:text_font", str, "Sans"); CONF("tabs:text_font", STRING, "Sans");
CONF("tabs:text_height", int, 8); CONF("tabs:text_height", INT, 8);
CONF("tabs:text_padding", int, 3); CONF("tabs:text_padding", INT, 3);
CONF("tabs:col.active", int, 0xff32b4ff); CONF("tabs:col.active", INT, 0xff32b4ff);
CONF("tabs:col.urgent", int, 0xffff4f4f); CONF("tabs:col.urgent", INT, 0xffff4f4f);
CONF("tabs:col.inactive", int, 0x80808080); CONF("tabs:col.inactive", INT, 0x80808080);
CONF("tabs:col.text.active", int, 0xff000000); CONF("tabs:col.text.active", INT, 0xff000000);
CONF("tabs:col.text.urgent", int, 0xff000000); CONF("tabs:col.text.urgent", INT, 0xff000000);
CONF("tabs:col.text.inactive", int, 0xff000000); CONF("tabs:col.text.inactive", INT, 0xff000000);
// autotiling // autotiling
CONF("autotile:enable", int, 0); CONF("autotile:enable", INT, 0);
CONF("autotile:ephemeral_groups", int, 1); CONF("autotile:ephemeral_groups", INT, 1);
CONF("autotile:trigger_height", int, 0); CONF("autotile:trigger_height", INT, 0);
CONF("autotile:trigger_width", int, 0); CONF("autotile:trigger_width", INT, 0);
CONF("autotile:workspaces", str, "all"); CONF("autotile:workspaces", STRING, "all");
#undef CONF #undef CONF