From 783c7ae0cf1fc263446f7114f068df03466310e4 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Sun, 14 Jan 2024 02:31:06 -0800 Subject: [PATCH] Add hy3:movetoworkspace, which moves a node to a workspace --- README.md | 2 + src/Hy3Layout.cpp | 176 +++++++++++++++++++++++++++++++++++++------- src/Hy3Layout.hpp | 2 + src/Hy3Node.cpp | 1 + src/Hy3Node.hpp | 1 + src/dispatchers.cpp | 15 ++++ 6 files changed, 172 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 701c816..bd96816 100644 --- a/README.md +++ b/README.md @@ -317,6 +317,8 @@ plugin { - `hy3:movewindow, , [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 - `visible` - only move between visible nodes, not hidden tabs + - `hy3:movetoworkspace, , [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:changefocus, ` - `top` - focus all nodes in the workspace diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index d29d801..bca5224 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -94,12 +94,47 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) { 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_after = nullptr; - auto* root = this->getWorkspaceRootGroup(window->m_iWorkspaceID); + auto* root = this->getWorkspaceRootGroup(node.workspace_id); if (root != nullptr) { opening_after = root->getFocusedNode(); @@ -112,27 +147,36 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) { } if (opening_after == nullptr) { - if (g_pCompositor->m_pLastWindow != nullptr && !g_pCompositor->m_pLastWindow->m_bIsFloating - && g_pCompositor->m_pLastWindow != window - && g_pCompositor->m_pLastWindow->m_iWorkspaceID == window->m_iWorkspaceID + if (g_pCompositor->m_pLastWindow != nullptr + && g_pCompositor->m_pLastWindow->m_iWorkspaceID == node.workspace_id + && !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) { opening_after = this->getNodeFromWindow(g_pCompositor->m_pLastWindow); } else { - opening_after = this->getNodeFromWindow( - g_pCompositor->vectorToWindowTiled(g_pInputManager->getMouseCoordsInternal()) - ); + auto* mouse_window = + g_pCompositor->vectorToWindowTiled(g_pInputManager->getMouseCoordsInternal()); + + 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; } if (opening_after != nullptr) { opening_into = opening_after->parent; } 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 = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tab_first_window")->intValue; @@ -140,7 +184,7 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) { .data = Hy3GroupLayout::SplitH, .position = monitor->vecPosition + monitor->vecReservedTopLeft, .size = monitor->vecSize - monitor->vecReservedTopLeft - monitor->vecReservedBottomRight, - .workspace_id = window->m_iWorkspaceID, + .workspace_id = node.workspace_id, .layout = this, }); @@ -152,7 +196,7 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) { .data = Hy3GroupLayout::Tabbed, .position = parent.position, .size = parent.size, - .workspace_id = window->m_iWorkspaceID, + .workspace_id = node.workspace_id, .layout = this, }); @@ -169,14 +213,14 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) { return; } - if (opening_into->workspace_id != window->m_iWorkspaceID) { + if (opening_into->workspace_id != node.workspace_id) { hy3_log( WARN, "opening_into node ({:x}) is on workspace {} which does not match the new window " "(workspace {})", (uintptr_t) opening_into, opening_into->workspace_id, - window->m_iWorkspaceID + node.workspace_id ); } @@ -211,14 +255,8 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) { } } - this->nodes.push_back({ - .parent = opening_into, - .data = window, - .workspace_id = window->m_iWorkspaceID, - .layout = this, - }); - - auto& node = this->nodes.back(); + node.parent = opening_into; + node.reparenting = false; if (opening_after == nullptr) { opening_into->data.as_group.children.push_back(&node); @@ -231,8 +269,7 @@ void Hy3Layout::onWindowCreatedTiling(CWindow* window, eDirection) { hy3_log( LOG, - "tiled window ({:x} as node {:x}) after node {:x} in node {:x}", - (uintptr_t) window, + "tiled node {:x} inserted after node {:x} in node {:x}", (uintptr_t) &node, (uintptr_t) opening_after, (uintptr_t) opening_into @@ -970,6 +1007,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; + auto* parent = 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 auto* const allow_workspace_cycles = + &g_pConfigManager->getConfigValuePtr("binds:allow_workspace_cycles")->intValue; + if (*allow_workspace_cycles) workspace->rememberPrevWorkspace(origin_ws); + } +} + void Hy3Layout::changeFocus(int workspace, FocusShift shift) { auto* node = this->getWorkspaceFocusedNode(workspace); if (node == nullptr) return; @@ -1311,7 +1437,7 @@ bool Hy3Layout::shouldRenderSelected(CWindow* window) { Hy3Node* Hy3Layout::getWorkspaceRootGroup(const int& workspace) { for (auto& node: this->nodes) { if (node.workspace_id == workspace && node.parent == nullptr - && node.data.type == Hy3NodeType::Group) + && node.data.type == Hy3NodeType::Group && !node.reparenting) { return &node; } diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp index a165dce..9409be8 100644 --- a/src/Hy3Layout.hpp +++ b/src/Hy3Layout.hpp @@ -91,6 +91,7 @@ public: virtual void onEnable(); virtual void onDisable(); + void insertNode(Hy3Node& node); void makeGroupOnWorkspace(int workspace, Hy3GroupLayout, GroupEphemeralityOption); void makeOppositeGroupOnWorkspace(int workspace, GroupEphemeralityOption); void changeGroupOnWorkspace(int workspace, Hy3GroupLayout); @@ -108,6 +109,7 @@ public: void shiftNode(Hy3Node&, ShiftDirection, bool once, bool visible); void shiftWindow(int workspace, ShiftDirection, bool once, bool visible); void shiftFocus(int workspace, ShiftDirection, bool visible); + void moveNodeToWorkspace(int origin, std::string wsname, bool follow); void changeFocus(int workspace, FocusShift); void focusTab(int workspace, TabFocus target, TabFocusMousePriority, bool wrap_scroll, int index); void setNodeSwallow(int workspace, SetSwallowOption); diff --git a/src/Hy3Node.cpp b/src/Hy3Node.cpp index 99c2b47..35fd752 100644 --- a/src/Hy3Node.cpp +++ b/src/Hy3Node.cpp @@ -759,6 +759,7 @@ Hy3Node* Hy3Node::removeFromParentRecursive(Hy3Node** expand_actor) { } } + this->parent = nullptr; return parent; } diff --git a/src/Hy3Node.hpp b/src/Hy3Node.hpp index 4b17d82..bd25556 100644 --- a/src/Hy3Node.hpp +++ b/src/Hy3Node.hpp @@ -79,6 +79,7 @@ public: struct Hy3Node { Hy3Node* parent = nullptr; + bool reparenting = false; Hy3NodeData data; Vector2D position; Vector2D size; diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp index f4d663c..3d721bc 100644 --- a/src/dispatchers.cpp +++ b/src/dispatchers.cpp @@ -119,6 +119,20 @@ void dispatch_movefocus(std::string value) { } } +void dispatch_move_to_workspace(std::string value) { + int origin_workspace = workspace_for_action(); + 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) { int workspace = workspace_for_action(); if (workspace == -1) return; @@ -237,6 +251,7 @@ void registerDispatchers() { HyprlandAPI::addDispatcher(PHANDLE, "hy3:setephemeral", dispatch_setephemeral); HyprlandAPI::addDispatcher(PHANDLE, "hy3:movefocus", dispatch_movefocus); 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:focustab", dispatch_focustab); HyprlandAPI::addDispatcher(PHANDLE, "hy3:setswallow", dispatch_setswallow);