diff --git a/README.md b/README.md index 757bba2..8ceec53 100644 --- a/README.md +++ b/README.md @@ -215,6 +215,10 @@ plugin { - `hy3:makegroup, , [ephemeral | force_ephemeral]` - make a vertical / horizontal split or tab group - `ephemeral` - the group will be removed once it contains only one node. does not affect existing groups. - `force_ephemeral` - same as ephemeral, but converts existing single windows groups. + - `hy3:changegroup, ` - change the group the node belongs to, to a different layout + - `untab` will untab the group if it was previously tabbed + - `opposite` will toggle between horizontal and vertical layouts if the group is not tabbed. + - `hy3:setephemeral, ` - change the ephemerality of the group the node belongs to - `hy3:movefocus, , [visible]` - move the focus left, up, down, or right - `visible` - only move between visible nodes, not hidden tabs - `hy3:movewindow, , [once]` - move a window left, up, down, or right diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index d627af4..76231c5 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -748,6 +748,34 @@ void Hy3Layout::makeOppositeGroupOnWorkspace(int workspace, GroupEphemeralityOpt this->makeOppositeGroupOn(node, ephemeral); } +void Hy3Layout::changeGroupOnWorkspace(int workspace, Hy3GroupLayout layout) { + auto* node = this->getWorkspaceFocusedNode(workspace); + if (node == nullptr) return; + + this->changeGroupOn(*node, layout); +} + +void Hy3Layout::untabGroupOnWorkspace(int workspace) { + auto* node = this->getWorkspaceFocusedNode(workspace); + if (node == nullptr) return; + + this->untabGroupOn(*node); +} + +void Hy3Layout::changeGroupToOppositeOnWorkspace(int workspace) { + auto* node = this->getWorkspaceFocusedNode(workspace); + if (node == nullptr) return; + + this->changeGroupToOppositeOn(*node); +} + +void Hy3Layout::changeGroupEphemeralityOnWorkspace(int workspace, bool ephemeral) { + auto* node = this->getWorkspaceFocusedNode(workspace); + if (node == nullptr) return; + + this->changeGroupEphemeralityOn(*node, ephemeral); +} + void Hy3Layout::makeGroupOn( Hy3Node* node, Hy3GroupLayout layout, @@ -758,10 +786,8 @@ void Hy3Layout::makeGroupOn( if (node->parent != nullptr) { auto& group = node->parent->data.as_group; if (group.children.size() == 1) { - group.layout = layout; - group.ephemeral = ephemeral == GroupEphemeralityOption::ForceEphemeral ? true - : ephemeral == GroupEphemeralityOption::Ephemeral ? group.ephemeral - : false; + group.setLayout(layout); + group.setEphemeral(ephemeral); node->parent->recalcSizePosRecursive(); return; } @@ -775,21 +801,62 @@ void Hy3Layout::makeOppositeGroupOn(Hy3Node* node, GroupEphemeralityOption ephem if (node->parent == nullptr) { node->intoGroup(Hy3GroupLayout::SplitH, ephemeral); - } else { - auto& group = node->parent->data.as_group; - auto layout - = group.layout == Hy3GroupLayout::SplitH ? Hy3GroupLayout::SplitV : Hy3GroupLayout::SplitH; - - if (group.children.size() == 1) { - group.layout = layout; - group.ephemeral = ephemeral == GroupEphemeralityOption::ForceEphemeral ? true - : ephemeral == GroupEphemeralityOption::Ephemeral ? group.ephemeral - : false; - node->parent->recalcSizePosRecursive(); - } else { - node->intoGroup(layout, ephemeral); - } + return; } + + auto& group = node->parent->data.as_group; + auto layout + = group.layout == Hy3GroupLayout::SplitH ? Hy3GroupLayout::SplitV : Hy3GroupLayout::SplitH; + + if (group.children.size() == 1) { + group.setLayout(layout); + group.setEphemeral(ephemeral); + node->parent->recalcSizePosRecursive(); + return; + } + + node->intoGroup(layout, ephemeral); +} + +void Hy3Layout::changeGroupOn(Hy3Node& node, Hy3GroupLayout layout) { + if (node.parent == nullptr) { + makeGroupOn(&node, layout, GroupEphemeralityOption::Ephemeral); + return; + } + + auto& group = node.parent->data.as_group; + group.setLayout(layout); + node.parent->recalcSizePosRecursive(); +} + +void Hy3Layout::untabGroupOn(Hy3Node& node) { + if (node.parent == nullptr) return; + + auto& group = node.parent->data.as_group; + if (group.layout != Hy3GroupLayout::Tabbed) return; + + changeGroupOn(node, group.previous_nontab_layout); +} + +void Hy3Layout::changeGroupToOppositeOn(Hy3Node& node) { + if (node.parent == nullptr) return; + + auto& group = node.parent->data.as_group; + + if (group.layout == Hy3GroupLayout::Tabbed) return; + group.setLayout( + group.layout == Hy3GroupLayout::SplitH ? Hy3GroupLayout::SplitV : Hy3GroupLayout::SplitH + ); + node.parent->recalcSizePosRecursive(); +} + +void Hy3Layout::changeGroupEphemeralityOn(Hy3Node& node, bool ephemeral) { + if (node.parent == nullptr) return; + + auto& group = node.parent->data.as_group; + group.setEphemeral( + ephemeral ? GroupEphemeralityOption::ForceEphemeral : GroupEphemeralityOption::Standard + ); } void Hy3Layout::shiftWindow(int workspace, ShiftDirection direction, bool once) { @@ -799,7 +866,7 @@ void Hy3Layout::shiftWindow(int workspace, ShiftDirection direction, bool once) if (once && node->parent != nullptr && node->parent->data.as_group.children.size() == 1) { if (node->parent->parent == nullptr) { - node->parent->data.as_group.layout = Hy3GroupLayout::SplitH; + node->parent->data.as_group.setLayout(Hy3GroupLayout::SplitH); node->parent->recalcSizePosRecursive(); } else { auto* node2 = node->parent; @@ -1424,7 +1491,9 @@ Hy3Node* Hy3Layout::shiftOrGetFocus( if (group.layout != Hy3GroupLayout::Tabbed && group.children.size() == 2 && std::find(group.children.begin(), group.children.end(), &node) != group.children.end()) { - group.layout = shiftIsVertical(direction) ? Hy3GroupLayout::SplitV : Hy3GroupLayout::SplitH; + group.setLayout( + shiftIsVertical(direction) ? Hy3GroupLayout::SplitV : Hy3GroupLayout::SplitH + ); } else { // wrap the root group in another group this->nodes.push_back({ diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp index ee91b23..e5148cc 100644 --- a/src/Hy3Layout.hpp +++ b/src/Hy3Layout.hpp @@ -90,8 +90,16 @@ public: void makeGroupOnWorkspace(int workspace, Hy3GroupLayout, GroupEphemeralityOption); void makeOppositeGroupOnWorkspace(int workspace, GroupEphemeralityOption); + void changeGroupOnWorkspace(int workspace, Hy3GroupLayout); + void untabGroupOnWorkspace(int workspace); + void changeGroupToOppositeOnWorkspace(int workspace); + void changeGroupEphemeralityOnWorkspace(int workspace, bool ephemeral); void makeGroupOn(Hy3Node*, Hy3GroupLayout, GroupEphemeralityOption); void makeOppositeGroupOn(Hy3Node*, GroupEphemeralityOption); + void changeGroupOn(Hy3Node&, Hy3GroupLayout); + void untabGroupOn(Hy3Node&); + void changeGroupToOppositeOn(Hy3Node&); + void changeGroupEphemeralityOn(Hy3Node&, bool ephemeral); void shiftWindow(int workspace, ShiftDirection, bool once); void shiftFocus(int workspace, ShiftDirection, bool visible); void changeFocus(int workspace, FocusShift); diff --git a/src/Hy3Node.cpp b/src/Hy3Node.cpp index 3c8f99a..4c44880 100644 --- a/src/Hy3Node.cpp +++ b/src/Hy3Node.cpp @@ -8,10 +8,15 @@ // Hy3GroupData // -Hy3GroupData::Hy3GroupData(Hy3GroupLayout layout): layout(layout) {} +Hy3GroupData::Hy3GroupData(Hy3GroupLayout layout): layout(layout) { + if (layout != Hy3GroupLayout::Tabbed) { + this->previous_nontab_layout = layout; + } +} Hy3GroupData::Hy3GroupData(Hy3GroupData&& from) { this->layout = from.layout; + this->previous_nontab_layout = from.previous_nontab_layout; this->children = std::move(from.children); this->group_focused = from.group_focused; this->expand_focused = from.expand_focused; @@ -52,6 +57,24 @@ void Hy3GroupData::collapseExpansions() { } } +void Hy3GroupData::setLayout(Hy3GroupLayout layout) { + this->layout = layout; + + if (layout != Hy3GroupLayout::Tabbed) { + this->previous_nontab_layout = layout; + } +} + +void Hy3GroupData::setEphemeral(GroupEphemeralityOption ephemeral) { + switch (ephemeral) { + case GroupEphemeralityOption::Standard: this->ephemeral = false; break; + case GroupEphemeralityOption::ForceEphemeral: this->ephemeral = true; break; + case GroupEphemeralityOption::Ephemeral: + // no change + break; + } +} + // Hy3NodeData // Hy3NodeData::Hy3NodeData(): Hy3NodeData((CWindow*) nullptr) {} diff --git a/src/Hy3Node.hpp b/src/Hy3Node.hpp index 111d8cd..0f81b44 100644 --- a/src/Hy3Node.hpp +++ b/src/Hy3Node.hpp @@ -29,6 +29,7 @@ enum class ExpandFocusType { struct Hy3GroupData { Hy3GroupLayout layout = Hy3GroupLayout::SplitH; + Hy3GroupLayout previous_nontab_layout = Hy3GroupLayout::SplitH; std::list children; bool group_focused = true; Hy3Node* focused_child = nullptr; @@ -42,6 +43,8 @@ struct Hy3GroupData { bool hasChild(Hy3Node* child); void collapseExpansions(); + void setLayout(Hy3GroupLayout layout); + void setEphemeral(GroupEphemeralityOption ephemeral); private: Hy3GroupData(Hy3GroupData&&); diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp index d6568e4..f2ae3d4 100644 --- a/src/dispatchers.cpp +++ b/src/dispatchers.cpp @@ -43,6 +43,36 @@ void dispatch_makegroup(std::string value) { } } +void dispatch_changegroup(std::string value) { + int workspace = workspace_for_action(); + if (workspace == -1) return; + + auto args = CVarList(value); + + if (args[0] == "h") { + g_Hy3Layout->changeGroupOnWorkspace(workspace, Hy3GroupLayout::SplitH); + } else if (args[0] == "v") { + g_Hy3Layout->changeGroupOnWorkspace(workspace, Hy3GroupLayout::SplitV); + } else if (args[0] == "tab") { + g_Hy3Layout->changeGroupOnWorkspace(workspace, Hy3GroupLayout::Tabbed); + } else if (args[0] == "untab") { + g_Hy3Layout->untabGroupOnWorkspace(workspace); + } else if (args[0] == "opposite") { + g_Hy3Layout->changeGroupToOppositeOnWorkspace(workspace); + } +} + +void dispatch_setephemeral(std::string value) { + int workspace = workspace_for_action(); + if (workspace == -1) return; + + auto args = CVarList(value); + + bool ephemeral = args[0] == "true"; + + g_Hy3Layout->changeGroupEphemeralityOnWorkspace(workspace, ephemeral); +} + std::optional parseShiftArg(std::string arg) { if (arg == "l" || arg == "left") return ShiftDirection::Left; else if (arg == "r" || arg == "right") return ShiftDirection::Right; @@ -188,6 +218,8 @@ void dispatch_debug(std::string arg) { void registerDispatchers() { HyprlandAPI::addDispatcher(PHANDLE, "hy3:makegroup", dispatch_makegroup); + HyprlandAPI::addDispatcher(PHANDLE, "hy3:changegroup", dispatch_changegroup); + 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:changefocus", dispatch_changefocus);