From cd3fd91b3bf93cc5a4a3063f33967683f9578852 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Thu, 20 Jul 2023 03:54:13 -0700 Subject: [PATCH] Window containment/swallowing MVP --- README.md | 1 + src/Hy3Layout.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++++ src/Hy3Layout.hpp | 8 ++++++ src/Hy3Node.cpp | 9 +++++++ src/Hy3Node.hpp | 1 + src/dispatchers.cpp | 17 ++++++++++++ 6 files changed, 101 insertions(+) diff --git a/README.md b/README.md index 802f944..c5cfe54 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,7 @@ plugin { - `prioritize_hovered` - prioritize the tab group under the mouse when multiple are stacked. use the lowest group if none is under the mouse. - `require_hovered` - affect the tab group under the mouse. do nothing if none are hovered. - `wrap` - wrap to the opposite size of the tab bar if moving off the end + - `hy3:setswallow, ` - set the containing node's window swallow state - `hy3:debugnodes` - print the node tree into the hyprland log ## Installing diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index 525379e..59c85df 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -16,6 +16,59 @@ std::unique_ptr urgentHookPtr std::unique_ptr tickHookPtr = std::make_unique(Hy3Layout::tickHook); +bool performContainment(Hy3Node& node, bool contained, CWindow* window) { + if (node.data.type == Hy3NodeType::Group) { + auto& group = node.data.as_group; + contained |= group.containment; + + auto iter = node.data.as_group.children.begin(); + while (iter != node.data.as_group.children.end()) { + switch ((*iter)->data.type) { + case Hy3NodeType::Group: return performContainment(**iter, contained, window); + case Hy3NodeType::Window: + if (contained) { + auto wpid = (*iter)->data.as_window->getPID(); + auto ppid = getPPIDof(window->getPID()); + while (ppid > 10) { // `> 10` yoinked from HL swallow + if (ppid == wpid) { + node.layout->nodes.push_back({ + .parent = &node, + .data = window, + .workspace_id = node.workspace_id, + .layout = node.layout, + }); + + auto& child_node = node.layout->nodes.back(); + + group.children.insert(std::next(iter), &child_node); + child_node.markFocused(); + node.recalcSizePosRecursive(); + + return true; + } + + ppid = getPPIDof(ppid); + } + } + } + + iter = std::next(iter); + } + } + + return false; +} + +void Hy3Layout::onWindowCreated(CWindow* window) { + for (auto& node: this->nodes) { + if (node.parent == nullptr && performContainment(node, false, window)) { + return; + } + } + + IHyprLayout::onWindowCreated(window); +} + void Hy3Layout::onWindowCreatedTiling(CWindow* window) { if (window->m_bIsFloating) return; @@ -938,6 +991,18 @@ hastab: tab_node->recalcSizePosRecursive(); } +void Hy3Layout::setNodeSwallow(int workspace, SetSwallowOption option) { + auto* node = this->getWorkspaceFocusedNode(workspace); + if (node == nullptr || node->parent == nullptr) return; + + auto* containment = &node->parent->data.as_group.containment; + switch (option) { + case SetSwallowOption::NoSwallow: *containment = false; + case SetSwallowOption::Swallow: *containment = true; + case SetSwallowOption::Toggle: *containment = !*containment; + } +} + void Hy3Layout::killFocusedNode(int workspace) { if (g_pCompositor->m_pLastWindow != nullptr && g_pCompositor->m_pLastWindow->m_bIsFloating) { g_pCompositor->closeWindow(g_pCompositor->m_pLastWindow); diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp index 1a35249..b77ed7a 100644 --- a/src/Hy3Layout.hpp +++ b/src/Hy3Layout.hpp @@ -44,8 +44,15 @@ enum class TabFocusMousePriority { Require, }; +enum class SetSwallowOption { + NoSwallow, + Swallow, + Toggle, +}; + class Hy3Layout: public IHyprLayout { public: + virtual void onWindowCreated(CWindow*); virtual void onWindowCreatedTiling(CWindow*); virtual void onWindowRemovedTiling(CWindow*); virtual void onWindowFocusChange(CWindow*); @@ -75,6 +82,7 @@ public: void shiftFocus(int workspace, ShiftDirection, bool visible); void changeFocus(int workspace, FocusShift); void focusTab(int workspace, TabFocus target, TabFocusMousePriority, bool wrap_scroll, int index); + void setNodeSwallow(int workspace, SetSwallowOption); void killFocusedNode(int workspace); bool shouldRenderSelected(CWindow*); diff --git a/src/Hy3Node.cpp b/src/Hy3Node.cpp index 85b1055..97436e3 100644 --- a/src/Hy3Node.cpp +++ b/src/Hy3Node.cpp @@ -525,6 +525,15 @@ std::string Hy3Node::debugNode() { buf << "] size ratio: "; buf << this->size_ratio; + + if (this->data.as_group.ephemeral) { + buf << ", ephemeral"; + } + + if (this->data.as_group.containment) { + buf << ", containment"; + } + for (auto* child: this->data.as_group.children) { buf << "\n|-"; if (child == nullptr) { diff --git a/src/Hy3Node.hpp b/src/Hy3Node.hpp index 59107bb..e98f2cb 100644 --- a/src/Hy3Node.hpp +++ b/src/Hy3Node.hpp @@ -27,6 +27,7 @@ struct Hy3GroupData { bool group_focused = true; Hy3Node* focused_child = nullptr; bool ephemeral = false; + bool containment = false; Hy3TabGroup* tab_bar = nullptr; Hy3GroupData(Hy3GroupLayout layout); diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp index 1480a24..6abb0ff 100644 --- a/src/dispatchers.cpp +++ b/src/dispatchers.cpp @@ -126,6 +126,22 @@ void dispatch_focustab(std::string value) { g_Hy3Layout->focusTab(workspace, focus, mouse, wrap_scroll, index); } +void dispatch_setswallow(std::string arg) { + int workspace = workspace_for_action(); + if (workspace == -1) return; + + SetSwallowOption option; + if (arg == "true") { + option = SetSwallowOption::Swallow; + } else if (arg == "false") { + option = SetSwallowOption::NoSwallow; + } else if (arg == "toggle") { + option = SetSwallowOption::Toggle; + } else return; + + g_Hy3Layout->setNodeSwallow(workspace, option); +} + void dispatch_killactive(std::string value) { int workspace = workspace_for_action(); if (workspace == -1) return; @@ -151,6 +167,7 @@ void registerDispatchers() { HyprlandAPI::addDispatcher(PHANDLE, "hy3:movewindow", dispatch_movewindow); HyprlandAPI::addDispatcher(PHANDLE, "hy3:changefocus", dispatch_changefocus); HyprlandAPI::addDispatcher(PHANDLE, "hy3:focustab", dispatch_focustab); + HyprlandAPI::addDispatcher(PHANDLE, "hy3:setswallow", dispatch_setswallow); HyprlandAPI::addDispatcher(PHANDLE, "hy3:killactive", dispatch_killactive); HyprlandAPI::addDispatcher(PHANDLE, "hy3:debugnodes", dispatch_debug); }