2023-04-27 11:14:11 -07:00
|
|
|
#include <hyprland/src/Compositor.hpp>
|
2023-05-01 00:05:45 -07:00
|
|
|
#include <hyprland/src/plugins/PluginAPI.hpp>
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
#include "globals.hpp"
|
|
|
|
#include "Hy3Layout.hpp"
|
|
|
|
#include "SelectionHook.hpp"
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
std::unique_ptr<HOOK_CALLBACK_FN> renderHookPtr
|
|
|
|
= std::make_unique<HOOK_CALLBACK_FN>(Hy3Layout::renderHook);
|
|
|
|
std::unique_ptr<HOOK_CALLBACK_FN> windowTitleHookPtr
|
|
|
|
= std::make_unique<HOOK_CALLBACK_FN>(Hy3Layout::windowGroupUpdateRecursiveHook);
|
|
|
|
std::unique_ptr<HOOK_CALLBACK_FN> urgentHookPtr
|
|
|
|
= std::make_unique<HOOK_CALLBACK_FN>(Hy3Layout::windowGroupUrgentHook);
|
|
|
|
std::unique_ptr<HOOK_CALLBACK_FN> tickHookPtr
|
|
|
|
= std::make_unique<HOOK_CALLBACK_FN>(Hy3Layout::tickHook);
|
2023-05-28 23:19:35 -07:00
|
|
|
|
2023-07-20 03:54:13 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::onWindowCreatedTiling(CWindow* window) {
|
|
|
|
if (window->m_bIsFloating) return;
|
2023-05-28 23:19:35 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* existing = this->getNodeFromWindow(window);
|
|
|
|
if (existing != nullptr) {
|
|
|
|
Debug::log(
|
|
|
|
WARN,
|
|
|
|
"Attempted to add a window(%p) that is already tiled(as %p) to the "
|
|
|
|
"layout",
|
|
|
|
window,
|
|
|
|
existing
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* monitor = g_pCompositor->getMonitorFromID(window->m_iMonitorID);
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
Hy3Node* opening_into;
|
|
|
|
Hy3Node* opening_after = nullptr;
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (monitor->activeWorkspace != -1) {
|
|
|
|
auto* root = this->getWorkspaceRootGroup(monitor->activeWorkspace);
|
2023-04-13 14:12:48 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (root != nullptr) {
|
|
|
|
opening_after = root->getFocusedNode();
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
// opening_after->parent cannot be nullptr
|
|
|
|
if (opening_after == root) {
|
2023-07-19 03:53:23 -07:00
|
|
|
opening_after
|
|
|
|
= opening_after->intoGroup(Hy3GroupLayout::SplitH, GroupEphemeralityOption::Standard);
|
2023-06-28 21:36:08 -07:00
|
|
|
}
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
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
|
|
|
|
&& 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())
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2023-06-07 03:22:17 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (opening_after != nullptr && opening_after->workspace_id != window->m_iWorkspaceID) {
|
|
|
|
opening_after = nullptr;
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (opening_after != nullptr) {
|
|
|
|
opening_into = opening_after->parent;
|
|
|
|
} else {
|
|
|
|
if ((opening_into = this->getWorkspaceRootGroup(window->m_iWorkspaceID)) == nullptr) {
|
|
|
|
this->nodes.push_back({
|
|
|
|
.data = Hy3GroupLayout::SplitH,
|
|
|
|
.position = monitor->vecPosition + monitor->vecReservedTopLeft,
|
|
|
|
.size = monitor->vecSize - monitor->vecReservedTopLeft - monitor->vecReservedBottomRight,
|
|
|
|
.workspace_id = window->m_iWorkspaceID,
|
|
|
|
.layout = this,
|
|
|
|
});
|
2023-06-07 03:22:17 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
opening_into = &this->nodes.back();
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (opening_into->data.type != Hy3NodeType::Group) {
|
|
|
|
Debug::log(ERR, "opening_into node %p was not of type Group", opening_into);
|
|
|
|
errorNotif();
|
|
|
|
return;
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (opening_into->workspace_id != window->m_iWorkspaceID) {
|
|
|
|
Debug::log(
|
|
|
|
WARN,
|
|
|
|
"opening_into node %p has workspace %d which does not match the "
|
|
|
|
"opening window (workspace %d)",
|
|
|
|
opening_into,
|
|
|
|
opening_into->workspace_id,
|
|
|
|
window->m_iWorkspaceID
|
|
|
|
);
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
|
|
|
|
2023-08-01 01:00:56 -07:00
|
|
|
{
|
|
|
|
// clang-format off
|
|
|
|
static const auto* at_enable = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:autotile:enable")->intValue;
|
|
|
|
static const auto* at_ephemeral = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:autotile:ephemeral_groups")->intValue;
|
|
|
|
static const auto* at_trigger_width = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:autotile:trigger_width")->intValue;
|
|
|
|
static const auto* at_trigger_height = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:autotile:trigger_height")->intValue;
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
auto& target_group = opening_into->data.as_group;
|
|
|
|
if (*at_enable && opening_after != nullptr && target_group.children.size() > 1
|
|
|
|
&& target_group.layout != Hy3GroupLayout::Tabbed)
|
|
|
|
{
|
|
|
|
auto is_horizontal = target_group.layout == Hy3GroupLayout::SplitH;
|
|
|
|
auto trigger = is_horizontal ? *at_trigger_width : *at_trigger_height;
|
|
|
|
auto target_size = is_horizontal ? opening_into->size.x : opening_into->size.y;
|
|
|
|
auto size_after_addition = target_size / (target_group.children.size() + 1);
|
|
|
|
|
|
|
|
if (trigger >= 0 && (trigger == 0 || size_after_addition < trigger)) {
|
|
|
|
auto opening_after1 = opening_after->intoGroup(
|
|
|
|
is_horizontal ? Hy3GroupLayout::SplitV : Hy3GroupLayout::SplitH,
|
|
|
|
*at_ephemeral ? GroupEphemeralityOption::Ephemeral : GroupEphemeralityOption::Standard
|
|
|
|
);
|
|
|
|
opening_into = opening_after;
|
|
|
|
opening_after = opening_after1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
this->nodes.push_back({
|
|
|
|
.parent = opening_into,
|
|
|
|
.data = window,
|
|
|
|
.workspace_id = window->m_iWorkspaceID,
|
|
|
|
.layout = this,
|
|
|
|
});
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto& node = this->nodes.back();
|
2023-04-13 14:12:48 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (opening_after == nullptr) {
|
|
|
|
opening_into->data.as_group.children.push_back(&node);
|
|
|
|
} else {
|
|
|
|
auto& children = opening_into->data.as_group.children;
|
|
|
|
auto iter = std::find(children.begin(), children.end(), opening_after);
|
|
|
|
auto iter2 = std::next(iter);
|
|
|
|
children.insert(iter2, &node);
|
|
|
|
}
|
2023-04-13 14:12:48 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
Debug::log(
|
|
|
|
LOG,
|
|
|
|
"opened new window %p(node: %p) on window %p in %p",
|
|
|
|
window,
|
|
|
|
&node,
|
|
|
|
opening_after,
|
|
|
|
opening_into
|
|
|
|
);
|
2023-04-13 14:12:48 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
node.markFocused();
|
|
|
|
opening_into->recalcSizePosRecursive();
|
|
|
|
Debug::log(
|
|
|
|
LOG,
|
|
|
|
"opening_into (%p) contains new child (%p)? %d",
|
|
|
|
opening_into,
|
|
|
|
&node,
|
|
|
|
opening_into->data.as_group.hasChild(&node)
|
|
|
|
);
|
2023-04-13 14:12:48 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::onWindowRemovedTiling(CWindow* window) {
|
2023-07-31 21:28:21 -07:00
|
|
|
static const auto* node_collapse_policy
|
|
|
|
= &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:node_collapse_policy")->intValue;
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* node = this->getNodeFromWindow(window);
|
|
|
|
Debug::log(LOG, "remove tiling %p (window %p)", node, window);
|
2023-06-04 21:32:30 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (node == nullptr) {
|
|
|
|
Debug::log(ERR, "onWindowRemovedTiling node null?");
|
|
|
|
return;
|
2023-06-02 00:12:33 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
window->m_sSpecialRenderData.rounding = true;
|
|
|
|
window->m_sSpecialRenderData.border = true;
|
|
|
|
window->m_sSpecialRenderData.decorate = true;
|
2023-05-25 02:38:11 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (window->m_bIsFullscreen) {
|
|
|
|
g_pCompositor->setWindowFullscreen(window, false, FULLSCREEN_FULL);
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* parent = node->removeFromParentRecursive();
|
|
|
|
this->nodes.remove(*node);
|
2023-04-12 03:33:45 -07:00
|
|
|
|
2023-07-19 03:53:23 -07:00
|
|
|
auto& group = parent->data.as_group;
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (parent != nullptr) {
|
|
|
|
parent->recalcSizePosRecursive();
|
2023-04-16 21:56:19 -07:00
|
|
|
|
2023-07-31 21:28:21 -07:00
|
|
|
// returns if a given node is a group that can be collapsed given the current config
|
|
|
|
auto node_is_collapsible = [](Hy3Node* node) {
|
|
|
|
if (node->data.type != Hy3NodeType::Group) return false;
|
|
|
|
if (*node_collapse_policy == 0) return true;
|
|
|
|
else if (*node_collapse_policy == 1) return false;
|
|
|
|
return node->parent->data.as_group.layout != Hy3GroupLayout::Tabbed;
|
|
|
|
};
|
|
|
|
|
2023-07-19 03:53:23 -07:00
|
|
|
if (group.children.size() == 1
|
2023-07-31 21:28:21 -07:00
|
|
|
&& (group.ephemeral || node_is_collapsible(group.children.front())))
|
2023-06-28 21:36:08 -07:00
|
|
|
{
|
|
|
|
auto* target_parent = parent;
|
|
|
|
while (target_parent != nullptr && Hy3Node::swallowGroups(target_parent)) {
|
|
|
|
target_parent = target_parent->parent;
|
|
|
|
}
|
2023-04-12 03:33:45 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (target_parent != parent && target_parent != nullptr)
|
|
|
|
target_parent->recalcSizePosRecursive();
|
2023-04-12 03:33:45 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
}
|
|
|
|
}
|
2023-04-12 03:33:45 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::onWindowFocusChange(CWindow* window) {
|
|
|
|
Debug::log(LOG, "Switched windows to %p", window);
|
|
|
|
auto* node = this->getNodeFromWindow(window);
|
|
|
|
if (node == nullptr) return;
|
2023-05-28 23:19:35 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
node->markFocused();
|
|
|
|
while (node->parent != nullptr) node = node->parent;
|
|
|
|
node->recalcSizePosRecursive();
|
|
|
|
}
|
2023-04-12 03:33:45 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
bool Hy3Layout::isWindowTiled(CWindow* window) {
|
|
|
|
return this->getNodeFromWindow(window) != nullptr;
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::recalculateMonitor(const int& monitor_id) {
|
|
|
|
Debug::log(LOG, "Recalculate monitor %d", monitor_id);
|
|
|
|
const auto monitor = g_pCompositor->getMonitorFromID(monitor_id);
|
|
|
|
if (monitor == nullptr) return;
|
2023-06-07 03:22:17 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
g_pHyprRenderer->damageMonitor(monitor);
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
const auto workspace = g_pCompositor->getWorkspaceByID(monitor->activeWorkspace);
|
|
|
|
if (workspace == nullptr) return;
|
2023-06-14 21:43:20 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (monitor->specialWorkspaceID) {
|
|
|
|
const auto top_node = this->getWorkspaceRootGroup(monitor->specialWorkspaceID);
|
2023-05-31 01:31:22 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (top_node != nullptr) {
|
|
|
|
top_node->position = monitor->vecPosition + monitor->vecReservedTopLeft;
|
|
|
|
top_node->size
|
|
|
|
= monitor->vecSize - monitor->vecReservedTopLeft - monitor->vecReservedBottomRight;
|
|
|
|
top_node->recalcSizePosRecursive();
|
2023-06-07 03:22:17 -07:00
|
|
|
}
|
|
|
|
}
|
2023-05-28 23:19:35 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (workspace->m_bHasFullscreenWindow) {
|
|
|
|
const auto window = g_pCompositor->getFullscreenWindowOnWorkspace(workspace->m_iID);
|
|
|
|
|
|
|
|
if (workspace->m_efFullscreenMode == FULLSCREEN_FULL) {
|
|
|
|
window->m_vRealPosition = monitor->vecPosition;
|
|
|
|
window->m_vRealSize = monitor->vecSize;
|
|
|
|
} else {
|
|
|
|
// Vaxry's hack from below, but again
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-29 12:58:41 -07:00
|
|
|
// clang-format off
|
|
|
|
static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue;
|
|
|
|
static const auto* gaps_out = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_out")->intValue;
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
int outer_gaps = -(*gaps_in - *gaps_out);
|
|
|
|
auto gap_pos_offset = Vector2D(outer_gaps, outer_gaps);
|
|
|
|
auto gap_size_offset = Vector2D(outer_gaps * 2, outer_gaps * 2);
|
|
|
|
Debug::log(LOG, "FS gaps: %d", outer_gaps);
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
Hy3Node fakeNode = {
|
|
|
|
.data = window,
|
|
|
|
.position = monitor->vecPosition + monitor->vecReservedTopLeft,
|
|
|
|
.size = monitor->vecSize - monitor->vecReservedTopLeft - monitor->vecReservedBottomRight,
|
2023-06-29 12:58:41 -07:00
|
|
|
.gap_pos_offset = gap_pos_offset,
|
|
|
|
.gap_size_offset = gap_size_offset,
|
2023-06-28 21:36:08 -07:00
|
|
|
.workspace_id = window->m_iWorkspaceID,
|
|
|
|
};
|
2023-06-01 21:58:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
this->applyNodeDataToWindow(&fakeNode);
|
2023-05-16 03:02:26 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
} else {
|
|
|
|
const auto top_node = this->getWorkspaceRootGroup(monitor->activeWorkspace);
|
2023-05-16 03:02:26 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (top_node != nullptr) {
|
|
|
|
top_node->position = monitor->vecPosition + monitor->vecReservedTopLeft;
|
|
|
|
top_node->size
|
|
|
|
= monitor->vecSize - monitor->vecReservedTopLeft - monitor->vecReservedBottomRight;
|
|
|
|
top_node->recalcSizePosRecursive();
|
2023-05-16 03:02:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::recalculateWindow(CWindow* window) {
|
|
|
|
auto* node = this->getNodeFromWindow(window);
|
|
|
|
if (node == nullptr) return;
|
|
|
|
node->recalcSizePosRecursive();
|
|
|
|
}
|
2023-05-28 23:19:35 -07:00
|
|
|
|
2023-07-13 12:54:47 -07:00
|
|
|
void Hy3Layout::resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CWindow* pWindow) {
|
2023-06-28 21:36:08 -07:00
|
|
|
auto window = pWindow ? pWindow : g_pCompositor->m_pLastWindow;
|
|
|
|
if (!g_pCompositor->windowValidMapped(window)) return;
|
2023-05-16 03:02:26 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* node = this->getNodeFromWindow(window);
|
|
|
|
if (node == nullptr) return;
|
2023-05-16 03:02:26 -07:00
|
|
|
|
2023-07-13 12:54:47 -07:00
|
|
|
bool drag_x;
|
|
|
|
bool drag_y;
|
2023-05-16 03:02:26 -07:00
|
|
|
|
2023-07-13 12:54:47 -07:00
|
|
|
if (corner == CORNER_NONE) {
|
|
|
|
drag_x = delta.x > 0;
|
|
|
|
drag_y = delta.y > 0;
|
|
|
|
} else {
|
|
|
|
drag_x = corner == CORNER_TOPRIGHT || corner == CORNER_BOTTOMRIGHT;
|
|
|
|
drag_y = corner == CORNER_BOTTOMLEFT || corner == CORNER_BOTTOMRIGHT;
|
2023-06-25 15:29:02 -07:00
|
|
|
}
|
2023-06-11 22:19:43 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
const auto animate
|
|
|
|
= &g_pConfigManager->getConfigValuePtr("misc:animate_manual_resizes")->intValue;
|
2023-04-26 00:57:24 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto monitor = g_pCompositor->getMonitorFromID(window->m_iMonitorID);
|
2023-05-28 23:19:35 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
const bool display_left
|
|
|
|
= 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
|
|
|
|
);
|
2023-04-26 00:57:24 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
Vector2D allowed_movement = delta;
|
|
|
|
if (display_left && display_right) allowed_movement.x = 0;
|
|
|
|
if (display_top && display_bottom) allowed_movement.y = 0;
|
2023-04-26 00:57:24 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* inner_node = node;
|
2023-04-26 00:57:24 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
// 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;
|
2023-05-31 01:31:22 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
switch (group.layout) {
|
|
|
|
case Hy3GroupLayout::Tabbed:
|
|
|
|
// treat tabbed layouts as if they dont exist during resizing
|
|
|
|
goto cont;
|
|
|
|
case Hy3GroupLayout::SplitH:
|
2023-07-13 12:54:47 -07:00
|
|
|
if ((drag_x && group.children.back() == inner_node)
|
|
|
|
|| (!drag_x && group.children.front() == inner_node))
|
2023-06-28 21:36:08 -07:00
|
|
|
{
|
|
|
|
goto cont;
|
2023-05-31 01:31:22 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
break;
|
|
|
|
case Hy3GroupLayout::SplitV:
|
2023-07-13 12:54:47 -07:00
|
|
|
if ((drag_y && group.children.back() == inner_node)
|
|
|
|
|| (!drag_y && group.children.front() == inner_node))
|
2023-06-28 21:36:08 -07:00
|
|
|
{
|
|
|
|
goto cont;
|
2023-05-31 01:31:22 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
break;
|
2023-05-31 01:31:22 -07:00
|
|
|
}
|
|
|
|
|
2023-04-26 00:57:24 -07:00
|
|
|
break;
|
2023-06-28 21:36:08 -07:00
|
|
|
cont:
|
|
|
|
inner_node = inner_node->parent;
|
2023-04-26 00:57:24 -07:00
|
|
|
}
|
2023-04-19 19:53:50 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* inner_parent = inner_node->parent;
|
|
|
|
if (inner_parent == nullptr) return;
|
2023-04-19 02:07:47 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* outer_node = inner_node;
|
2023-04-22 04:19:34 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
// break into parent groups when encountering a corner we're dragging in, a
|
|
|
|
// tab group, or a layout matching the inner_parent.
|
|
|
|
while (outer_node->parent != nullptr) {
|
|
|
|
auto& group = outer_node->parent->data.as_group;
|
2023-04-22 04:19:34 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
// break out of all layouts that match the orientation of the inner_parent
|
|
|
|
if (group.layout == inner_parent->data.as_group.layout) goto cont2;
|
2023-05-01 19:54:37 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
switch (group.layout) {
|
|
|
|
case Hy3GroupLayout::Tabbed:
|
|
|
|
// treat tabbed layouts as if they dont exist during resizing
|
|
|
|
goto cont2;
|
|
|
|
case Hy3GroupLayout::SplitH:
|
2023-07-13 12:54:47 -07:00
|
|
|
if ((drag_x && group.children.back() == outer_node)
|
|
|
|
|| (!drag_x && group.children.front() == outer_node))
|
2023-06-28 21:36:08 -07:00
|
|
|
{
|
|
|
|
goto cont2;
|
2023-05-01 19:54:37 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
break;
|
|
|
|
case Hy3GroupLayout::SplitV:
|
2023-07-13 12:54:47 -07:00
|
|
|
if ((drag_y && group.children.back() == outer_node)
|
|
|
|
|| (!drag_y && group.children.front() == outer_node))
|
2023-06-28 21:36:08 -07:00
|
|
|
{
|
|
|
|
goto cont2;
|
2023-04-22 04:19:34 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
break;
|
2023-04-22 04:19:34 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
break;
|
|
|
|
cont2:
|
|
|
|
outer_node = outer_node->parent;
|
|
|
|
}
|
2023-06-07 03:22:17 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
Debug::log(LOG, "resizeActive - inner_node: %p, outer_node: %p", inner_node, outer_node);
|
2023-04-22 04:19:34 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
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);
|
2023-04-22 04:19:34 -07:00
|
|
|
|
2023-07-13 12:54:47 -07:00
|
|
|
if (drag_x) {
|
2023-06-28 21:36:08 -07:00
|
|
|
if (inner_node == inner_group.children.back()) break;
|
|
|
|
iter = std::next(iter);
|
2023-05-01 19:54:37 -07:00
|
|
|
} else {
|
2023-06-28 21:36:08 -07:00
|
|
|
if (inner_node == inner_group.children.front()) break;
|
|
|
|
iter = std::prev(iter);
|
|
|
|
ratio_mod = -ratio_mod;
|
2023-04-22 04:19:34 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* neighbor = *iter;
|
2023-05-11 01:30:19 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
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;
|
2023-04-22 04:19:34 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto iter = std::find(inner_group.children.begin(), inner_group.children.end(), inner_node);
|
2023-04-22 04:19:34 -07:00
|
|
|
|
2023-07-13 12:54:47 -07:00
|
|
|
if (drag_y) {
|
2023-06-28 21:36:08 -07:00
|
|
|
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;
|
2023-04-22 04:19:34 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
|
|
|
|
auto* neighbor = *iter;
|
|
|
|
|
|
|
|
inner_node->size_ratio += ratio_mod;
|
|
|
|
neighbor->size_ratio -= ratio_mod;
|
|
|
|
} break;
|
|
|
|
case Hy3GroupLayout::Tabbed: break;
|
2023-04-22 04:19:34 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
inner_parent->recalcSizePosRecursive(*animate == 0);
|
2023-04-22 04:19:34 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
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;
|
2023-05-04 03:10:06 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto iter = std::find(outer_group.children.begin(), outer_group.children.end(), outer_node);
|
2023-05-04 03:10:06 -07:00
|
|
|
|
2023-07-13 12:54:47 -07:00
|
|
|
if (drag_x) {
|
2023-06-28 21:36:08 -07:00
|
|
|
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;
|
|
|
|
}
|
2023-05-04 03:10:06 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* neighbor = *iter;
|
2023-05-04 03:10:06 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
outer_node->size_ratio += ratio_mod;
|
|
|
|
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;
|
2023-04-13 14:12:48 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto iter = std::find(outer_group.children.begin(), outer_group.children.end(), outer_node);
|
2023-04-13 14:12:48 -07:00
|
|
|
|
2023-07-13 12:54:47 -07:00
|
|
|
if (drag_y) {
|
2023-06-28 21:36:08 -07:00
|
|
|
if (outer_node == outer_group.children.back()) break;
|
|
|
|
iter = std::next(iter);
|
|
|
|
} else {
|
|
|
|
if (outer_node == outer_group.children.front()) break;
|
|
|
|
iter = std::prev(iter);
|
|
|
|
ratio_mod = -ratio_mod;
|
|
|
|
}
|
2023-04-13 14:12:48 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* neighbor = *iter;
|
2023-04-13 14:12:48 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
outer_node->size_ratio += ratio_mod;
|
|
|
|
neighbor->size_ratio -= ratio_mod;
|
|
|
|
} break;
|
|
|
|
case Hy3GroupLayout::Tabbed: break;
|
2023-04-13 14:12:48 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
outer_parent->recalcSizePosRecursive(*animate == 0);
|
2023-04-13 14:12:48 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::fullscreenRequestForWindow(
|
|
|
|
CWindow* window,
|
|
|
|
eFullscreenMode fullscreen_mode,
|
|
|
|
bool on
|
|
|
|
) {
|
|
|
|
if (!g_pCompositor->windowValidMapped(window)) return;
|
|
|
|
if (on == window->m_bIsFullscreen || g_pCompositor->isWorkspaceSpecial(window->m_iWorkspaceID))
|
|
|
|
return;
|
2023-05-29 02:36:13 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
const auto monitor = g_pCompositor->getMonitorFromID(window->m_iMonitorID);
|
|
|
|
const auto workspace = g_pCompositor->getWorkspaceByID(window->m_iWorkspaceID);
|
|
|
|
if (workspace->m_bHasFullscreenWindow && on) return;
|
2023-05-29 02:36:13 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
window->m_bIsFullscreen = on;
|
|
|
|
workspace->m_bHasFullscreenWindow = !workspace->m_bHasFullscreenWindow;
|
2023-05-29 02:36:13 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (!window->m_bIsFullscreen) {
|
|
|
|
auto* node = this->getNodeFromWindow(window);
|
2023-05-29 02:36:13 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (node) {
|
|
|
|
// restore node positioning if tiled
|
|
|
|
this->applyNodeDataToWindow(node);
|
2023-05-29 02:36:13 -07:00
|
|
|
} else {
|
2023-06-28 21:36:08 -07:00
|
|
|
// restore floating position if not
|
|
|
|
window->m_vRealPosition = window->m_vLastFloatingPosition;
|
|
|
|
window->m_vRealSize = window->m_vLastFloatingSize;
|
2023-05-29 02:36:13 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
window->m_sSpecialRenderData.rounding = true;
|
|
|
|
window->m_sSpecialRenderData.border = true;
|
|
|
|
window->m_sSpecialRenderData.decorate = true;
|
2023-05-28 23:19:35 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
} else {
|
|
|
|
workspace->m_efFullscreenMode = fullscreen_mode;
|
2023-06-04 17:28:26 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
// save position and size if floating
|
|
|
|
if (window->m_bIsFloating) {
|
|
|
|
window->m_vLastFloatingPosition = window->m_vRealPosition.goalv();
|
|
|
|
window->m_vPosition = window->m_vRealPosition.goalv();
|
|
|
|
window->m_vLastFloatingSize = window->m_vRealSize.goalv();
|
|
|
|
window->m_vSize = window->m_vRealSize.goalv();
|
2023-04-26 00:57:24 -07:00
|
|
|
}
|
2023-06-11 22:19:43 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (fullscreen_mode == FULLSCREEN_FULL) {
|
|
|
|
Debug::log(LOG, "fullscreen");
|
|
|
|
window->m_vRealPosition = monitor->vecPosition;
|
|
|
|
window->m_vRealSize = monitor->vecSize;
|
|
|
|
} else {
|
|
|
|
Debug::log(LOG, "vaxry hack");
|
|
|
|
// Copy of vaxry's massive hack
|
2023-04-26 00:57:24 -07:00
|
|
|
|
2023-06-29 12:58:41 -07:00
|
|
|
// clang-format off
|
|
|
|
static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue;
|
|
|
|
static const auto* gaps_out = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_out")->intValue;
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
int outer_gaps = -(*gaps_in - *gaps_out);
|
|
|
|
auto gap_pos_offset = Vector2D(outer_gaps, outer_gaps);
|
|
|
|
auto gap_size_offset = Vector2D(outer_gaps * 2, outer_gaps * 2);
|
|
|
|
Debug::log(LOG, "FS gaps: %d", outer_gaps);
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
Hy3Node fakeNode = {
|
|
|
|
.data = window,
|
|
|
|
.position = monitor->vecPosition + monitor->vecReservedTopLeft,
|
|
|
|
.size = monitor->vecSize - monitor->vecReservedTopLeft - monitor->vecReservedBottomRight,
|
2023-06-29 12:58:41 -07:00
|
|
|
.gap_pos_offset = gap_pos_offset,
|
|
|
|
.gap_size_offset = gap_size_offset,
|
2023-06-28 21:36:08 -07:00
|
|
|
.workspace_id = window->m_iWorkspaceID,
|
|
|
|
};
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
this->applyNodeDataToWindow(&fakeNode);
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
g_pCompositor->updateWindowAnimatedDecorationValues(window);
|
|
|
|
g_pXWaylandManager->setWindowSize(window, window->m_vRealSize.goalv());
|
|
|
|
g_pCompositor->moveWindowToTop(window);
|
|
|
|
this->recalculateMonitor(monitor->ID);
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
std::any Hy3Layout::layoutMessage(SLayoutMessageHeader header, std::string content) {
|
|
|
|
if (content == "togglesplit") {
|
|
|
|
auto* node = this->getNodeFromWindow(header.pWindow);
|
|
|
|
if (node != nullptr && node->parent != nullptr) {
|
|
|
|
auto& layout = node->parent->data.as_group.layout;
|
|
|
|
|
|
|
|
switch (layout) {
|
|
|
|
case Hy3GroupLayout::SplitH:
|
|
|
|
layout = Hy3GroupLayout::SplitV;
|
|
|
|
node->parent->recalcSizePosRecursive();
|
|
|
|
break;
|
|
|
|
case Hy3GroupLayout::SplitV:
|
|
|
|
layout = Hy3GroupLayout::SplitH;
|
|
|
|
node->parent->recalcSizePosRecursive();
|
|
|
|
break;
|
|
|
|
case Hy3GroupLayout::Tabbed: break;
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
return "";
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
SWindowRenderLayoutHints Hy3Layout::requestRenderHints(CWindow* window) { return {}; }
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::switchWindows(CWindow* pWindowA, CWindow* pWindowB) {
|
|
|
|
// todo
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::alterSplitRatio(CWindow* pWindow, float delta, bool exact) {
|
|
|
|
// todo
|
2023-05-04 02:39:46 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
std::string Hy3Layout::getLayoutName() { return "hy3"; }
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
CWindow* Hy3Layout::getNextWindowCandidate(CWindow* window) {
|
2023-07-31 20:38:58 -07:00
|
|
|
auto* node = this->getWorkspaceFocusedNode(window->m_iWorkspaceID, true);
|
2023-06-28 21:36:08 -07:00
|
|
|
if (node == nullptr) return nullptr;
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
switch (node->data.type) {
|
|
|
|
case Hy3NodeType::Window: return node->data.as_window;
|
|
|
|
case Hy3NodeType::Group: return nullptr;
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::replaceWindowDataWith(CWindow* from, CWindow* to) {
|
|
|
|
auto* node = this->getNodeFromWindow(from);
|
|
|
|
if (node == nullptr) return;
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
node->data.as_window = to;
|
|
|
|
this->applyNodeDataToWindow(node);
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-07-19 04:05:23 -07:00
|
|
|
void Hy3Layout::requestFocusForWindow(CWindow* window) {
|
|
|
|
auto node = this->getNodeFromWindow(window);
|
|
|
|
if (node == nullptr) return;
|
|
|
|
node->focusWindow();
|
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::onEnable() {
|
|
|
|
for (auto& window: g_pCompositor->m_vWindows) {
|
|
|
|
if (window->isHidden() || !window->m_bIsMapped || window->m_bFadingOut || window->m_bIsFloating)
|
|
|
|
continue;
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
this->onWindowCreatedTiling(window.get());
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
HyprlandAPI::registerCallbackStatic(PHANDLE, "render", renderHookPtr.get());
|
|
|
|
HyprlandAPI::registerCallbackStatic(PHANDLE, "windowTitle", windowTitleHookPtr.get());
|
|
|
|
HyprlandAPI::registerCallbackStatic(PHANDLE, "urgent", urgentHookPtr.get());
|
|
|
|
HyprlandAPI::registerCallbackStatic(PHANDLE, "tick", tickHookPtr.get());
|
|
|
|
selection_hook::enable();
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::onDisable() {
|
|
|
|
HyprlandAPI::unregisterCallback(PHANDLE, renderHookPtr.get());
|
|
|
|
HyprlandAPI::unregisterCallback(PHANDLE, windowTitleHookPtr.get());
|
|
|
|
HyprlandAPI::unregisterCallback(PHANDLE, urgentHookPtr.get());
|
|
|
|
HyprlandAPI::unregisterCallback(PHANDLE, tickHookPtr.get());
|
|
|
|
selection_hook::disable();
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
for (auto& node: this->nodes) {
|
|
|
|
if (node.data.type == Hy3NodeType::Window) {
|
|
|
|
node.data.as_window->setHidden(false);
|
|
|
|
}
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
this->nodes.clear();
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-07-19 03:53:23 -07:00
|
|
|
void Hy3Layout::makeGroupOnWorkspace(
|
|
|
|
int workspace,
|
|
|
|
Hy3GroupLayout layout,
|
|
|
|
GroupEphemeralityOption ephemeral
|
|
|
|
) {
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* node = this->getWorkspaceFocusedNode(workspace);
|
2023-07-19 03:53:23 -07:00
|
|
|
this->makeGroupOn(node, layout, ephemeral);
|
2023-06-28 21:36:08 -07:00
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-07-19 03:53:23 -07:00
|
|
|
void Hy3Layout::makeOppositeGroupOnWorkspace(int workspace, GroupEphemeralityOption ephemeral) {
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* node = this->getWorkspaceFocusedNode(workspace);
|
2023-07-19 03:53:23 -07:00
|
|
|
this->makeOppositeGroupOn(node, ephemeral);
|
2023-06-28 21:36:08 -07:00
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-08-16 08:37:04 +01:00
|
|
|
void Hy3Layout::changeGroupOnWorkspace(
|
|
|
|
int workspace,
|
|
|
|
Hy3GroupLayout layout,
|
|
|
|
GroupEphemeralityOption ephemeral
|
|
|
|
) {
|
|
|
|
auto* node = this->getWorkspaceFocusedNode(workspace);
|
|
|
|
this->changeGroupOn(node, layout, ephemeral);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hy3Layout::untabGroupOnWorkspace(
|
|
|
|
int workspace,
|
|
|
|
GroupEphemeralityOption ephemeral
|
|
|
|
) {
|
|
|
|
auto* node = this->getWorkspaceFocusedNode(workspace);
|
|
|
|
this->untabGroupOn(node, ephemeral);
|
|
|
|
}
|
|
|
|
|
2023-07-19 03:53:23 -07:00
|
|
|
void Hy3Layout::makeGroupOn(
|
|
|
|
Hy3Node* node,
|
|
|
|
Hy3GroupLayout layout,
|
|
|
|
GroupEphemeralityOption ephemeral
|
|
|
|
) {
|
2023-06-28 21:36:08 -07:00
|
|
|
if (node == nullptr) return;
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (node->parent != nullptr) {
|
|
|
|
auto& group = node->parent->data.as_group;
|
|
|
|
if (group.children.size() == 1) {
|
2023-08-16 08:37:04 +01:00
|
|
|
group.updateLayout(layout, ephemeral);
|
2023-06-28 21:36:08 -07:00
|
|
|
node->parent->recalcSizePosRecursive();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-07-19 03:53:23 -07:00
|
|
|
node->intoGroup(layout, ephemeral);
|
2023-06-28 21:36:08 -07:00
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-08-16 08:37:04 +01:00
|
|
|
void Hy3Layout::makeOppositeGroupOn(
|
|
|
|
Hy3Node* node,
|
|
|
|
GroupEphemeralityOption ephemeral
|
|
|
|
) {
|
2023-06-28 21:36:08 -07:00
|
|
|
if (node == nullptr) return;
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (node->parent == nullptr) {
|
2023-07-19 03:53:23 -07:00
|
|
|
node->intoGroup(Hy3GroupLayout::SplitH, ephemeral);
|
2023-08-16 08:37:04 +01:00
|
|
|
return;
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-08-16 08:37:04 +01:00
|
|
|
auto& group = node->parent->data.as_group;
|
|
|
|
auto layout
|
|
|
|
= group.layout == Hy3GroupLayout::SplitH ? Hy3GroupLayout::SplitV : Hy3GroupLayout::SplitH;
|
|
|
|
|
|
|
|
if (group.children.size() == 1) {
|
|
|
|
group.updateLayout(layout, ephemeral);
|
|
|
|
node->parent->recalcSizePosRecursive();
|
|
|
|
return;
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
2023-08-16 08:37:04 +01:00
|
|
|
|
|
|
|
node->intoGroup(layout, ephemeral);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hy3Layout::changeGroupOn(
|
|
|
|
Hy3Node* node,
|
|
|
|
Hy3GroupLayout layout,
|
|
|
|
GroupEphemeralityOption ephemeral
|
|
|
|
) {
|
|
|
|
if (node == nullptr) return;
|
|
|
|
|
|
|
|
if (node->parent == nullptr) {
|
|
|
|
makeGroupOn(node, layout, ephemeral);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto& group = node->parent->data.as_group;
|
|
|
|
group.updateLayout(layout, ephemeral);
|
|
|
|
node->parent->recalcSizePosRecursive();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hy3Layout::untabGroupOn(Hy3Node* node, GroupEphemeralityOption ephemeral) {
|
|
|
|
if (node == nullptr) return;
|
|
|
|
|
|
|
|
if (node->parent == nullptr) return;
|
|
|
|
|
|
|
|
auto& group = node->parent->data.as_group;
|
|
|
|
|
|
|
|
if (group.layout != Hy3GroupLayout::Tabbed) return;
|
|
|
|
|
|
|
|
changeGroupOn(node, group.previous_nontab_layout, ephemeral);
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::shiftWindow(int workspace, ShiftDirection direction, bool once) {
|
|
|
|
auto* node = this->getWorkspaceFocusedNode(workspace);
|
|
|
|
Debug::log(LOG, "ShiftWindow %p %d", node, direction);
|
|
|
|
if (node == nullptr) return;
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
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->recalcSizePosRecursive();
|
|
|
|
} else {
|
|
|
|
auto* node2 = node->parent;
|
|
|
|
Hy3Node::swapData(*node, *node2);
|
|
|
|
node2->layout->nodes.remove(*node);
|
|
|
|
node2->recalcSizePosRecursive();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this->shiftOrGetFocus(*node, direction, true, once, false);
|
2023-04-18 00:15:32 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
}
|
2023-04-18 00:15:32 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible) {
|
|
|
|
auto* node = this->getWorkspaceFocusedNode(workspace);
|
|
|
|
Debug::log(LOG, "ShiftFocus %p %d", node, direction);
|
|
|
|
if (node == nullptr) return;
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
Hy3Node* target;
|
|
|
|
if ((target = this->shiftOrGetFocus(*node, direction, false, false, visible))) {
|
|
|
|
target->focus();
|
|
|
|
while (target->parent != nullptr) target = target->parent;
|
|
|
|
target->recalcSizePosRecursive();
|
|
|
|
}
|
|
|
|
}
|
2023-06-25 14:37:23 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::changeFocus(int workspace, FocusShift shift) {
|
|
|
|
auto* node = this->getWorkspaceFocusedNode(workspace);
|
|
|
|
if (node == nullptr) return;
|
2023-06-25 14:37:23 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
switch (shift) {
|
|
|
|
case FocusShift::Bottom: goto bottom;
|
|
|
|
case FocusShift::Top:
|
|
|
|
while (node->parent != nullptr) {
|
|
|
|
node = node->parent;
|
2023-06-25 14:37:23 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
node->focus();
|
|
|
|
return;
|
|
|
|
case FocusShift::Raise:
|
|
|
|
if (node->parent == nullptr) goto bottom;
|
|
|
|
else {
|
|
|
|
node->parent->focus();
|
2023-06-25 14:37:23 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
return;
|
|
|
|
case FocusShift::Lower:
|
|
|
|
if (node->data.type == Hy3NodeType::Group && node->data.as_group.focused_child != nullptr)
|
|
|
|
node->data.as_group.focused_child->focus();
|
|
|
|
return;
|
|
|
|
case FocusShift::Tab:
|
|
|
|
// make sure we go up at least one level
|
|
|
|
if (node->parent != nullptr) node = node->parent;
|
|
|
|
while (node->parent != nullptr) {
|
|
|
|
if (node->data.as_group.layout == Hy3GroupLayout::Tabbed) {
|
|
|
|
node->focus();
|
|
|
|
return;
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
node = node->parent;
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
return;
|
|
|
|
case FocusShift::TabNode:
|
|
|
|
// make sure we go up at least one level
|
|
|
|
if (node->parent != nullptr) node = node->parent;
|
|
|
|
while (node->parent != nullptr) {
|
|
|
|
if (node->parent->data.as_group.layout == Hy3GroupLayout::Tabbed) {
|
|
|
|
node->focus();
|
|
|
|
return;
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
node = node->parent;
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
bottom:
|
|
|
|
while (node->data.type == Hy3NodeType::Group && node->data.as_group.focused_child != nullptr) {
|
|
|
|
node = node->data.as_group.focused_child;
|
2023-04-18 00:15:32 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
node->focus();
|
|
|
|
return;
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
Hy3Node* findTabBarAt(Hy3Node& node, Vector2D pos, Hy3Node** focused_node) {
|
|
|
|
// clang-format off
|
|
|
|
static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue;
|
|
|
|
static const auto* gaps_out = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_out")->intValue;
|
|
|
|
static const auto* tab_bar_height = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:height")->intValue;
|
|
|
|
static const auto* tab_bar_padding = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:padding")->intValue;
|
|
|
|
// clang-format on
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto inset = *tab_bar_height + *tab_bar_padding;
|
|
|
|
|
|
|
|
if (node.parent == nullptr) {
|
|
|
|
inset += *gaps_out;
|
2023-04-12 01:33:00 -07:00
|
|
|
} else {
|
2023-06-28 21:36:08 -07:00
|
|
|
inset += *gaps_in;
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
2023-06-07 03:22:17 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (node.data.type == Hy3NodeType::Group) {
|
|
|
|
if (node.hidden) return nullptr;
|
|
|
|
// note: tab bar clicks ignore animations
|
|
|
|
if (node.position.x > pos.x || node.position.y > pos.y || node.position.x + node.size.x < pos.x
|
|
|
|
|| node.position.y + node.size.y < pos.y)
|
|
|
|
return nullptr;
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (node.data.as_group.layout == Hy3GroupLayout::Tabbed
|
|
|
|
&& node.data.as_group.tab_bar != nullptr)
|
|
|
|
{
|
2023-06-29 12:58:41 -07:00
|
|
|
if (pos.y < node.position.y + node.gap_pos_offset.y + inset) {
|
2023-06-28 21:36:08 -07:00
|
|
|
auto& children = node.data.as_group.children;
|
|
|
|
auto& tab_bar = *node.data.as_group.tab_bar;
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto size = tab_bar.size.vec();
|
|
|
|
auto x = pos.x - tab_bar.pos.vec().x;
|
|
|
|
auto child_iter = children.begin();
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
for (auto& tab: tab_bar.bar.entries) {
|
|
|
|
if (child_iter == children.end()) break;
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (x > tab.offset.fl() * size.x && x < (tab.offset.fl() + tab.width.fl()) * size.x) {
|
|
|
|
*focused_node = *child_iter;
|
|
|
|
return &node;
|
|
|
|
}
|
2023-04-19 18:22:11 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
child_iter = std::next(child_iter);
|
|
|
|
}
|
2023-04-19 21:11:36 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (node.data.as_group.focused_child != nullptr) {
|
|
|
|
return findTabBarAt(*node.data.as_group.focused_child, pos, focused_node);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (auto child: node.data.as_group.children) {
|
|
|
|
if (findTabBarAt(*child, pos, focused_node)) return child;
|
|
|
|
}
|
2023-04-19 21:11:36 -07:00
|
|
|
}
|
2023-04-19 18:22:11 -07:00
|
|
|
}
|
2023-04-26 00:57:24 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
return nullptr;
|
2023-04-16 23:53:22 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::focusTab(
|
|
|
|
int workspace,
|
|
|
|
TabFocus target,
|
|
|
|
TabFocusMousePriority mouse,
|
|
|
|
bool wrap_scroll,
|
|
|
|
int index
|
|
|
|
) {
|
|
|
|
auto* node = this->getWorkspaceRootGroup(workspace);
|
2023-04-19 21:17:48 -07:00
|
|
|
if (node == nullptr) return;
|
2023-04-12 03:33:45 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
Hy3Node* tab_node = nullptr;
|
|
|
|
Hy3Node* tab_focused_node;
|
2023-04-12 03:33:45 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (target == TabFocus::MouseLocation || mouse != TabFocusMousePriority::Ignore) {
|
|
|
|
if (g_pCompositor->windowFloatingFromCursor() == nullptr) {
|
|
|
|
auto mouse_pos = g_pInputManager->getMouseCoordsInternal();
|
|
|
|
tab_node = findTabBarAt(*node, mouse_pos, &tab_focused_node);
|
|
|
|
if (tab_node != nullptr) goto hastab;
|
|
|
|
}
|
2023-04-17 00:53:02 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (target == TabFocus::MouseLocation || mouse == TabFocusMousePriority::Require) return;
|
|
|
|
}
|
2023-04-19 20:59:06 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (tab_node == nullptr) {
|
|
|
|
tab_node = this->getWorkspaceFocusedNode(workspace);
|
|
|
|
if (tab_node == nullptr) return;
|
2023-04-17 00:53:02 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
while (tab_node != nullptr && tab_node->data.as_group.layout != Hy3GroupLayout::Tabbed
|
|
|
|
&& tab_node->parent != nullptr)
|
|
|
|
tab_node = tab_node->parent;
|
2023-04-17 00:53:02 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (tab_node == nullptr || tab_node->data.type != Hy3NodeType::Group
|
|
|
|
|| tab_node->data.as_group.layout != Hy3GroupLayout::Tabbed)
|
|
|
|
return;
|
2023-04-17 00:53:02 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
hastab:
|
|
|
|
if (target != TabFocus::MouseLocation) {
|
|
|
|
if (tab_node->data.as_group.focused_child == nullptr
|
|
|
|
|| tab_node->data.as_group.children.size() < 2)
|
|
|
|
return;
|
2023-04-17 00:53:02 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto& children = tab_node->data.as_group.children;
|
|
|
|
if (target == TabFocus::Index) {
|
|
|
|
int i = 1;
|
2023-04-17 00:53:02 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
for (auto* node: children) {
|
|
|
|
if (i == index) {
|
|
|
|
tab_focused_node = node;
|
|
|
|
goto cont;
|
|
|
|
}
|
2023-04-17 00:53:02 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
i++;
|
|
|
|
}
|
2023-04-17 00:53:02 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
return;
|
|
|
|
cont:;
|
|
|
|
} else {
|
|
|
|
auto node_iter
|
|
|
|
= std::find(children.begin(), children.end(), tab_node->data.as_group.focused_child);
|
|
|
|
if (node_iter == children.end()) return;
|
|
|
|
if (target == TabFocus::Left) {
|
|
|
|
if (node_iter == children.begin()) {
|
|
|
|
if (wrap_scroll) node_iter = std::prev(children.end());
|
|
|
|
else return;
|
|
|
|
} else node_iter = std::prev(node_iter);
|
|
|
|
|
|
|
|
tab_focused_node = *node_iter;
|
|
|
|
} else {
|
|
|
|
if (node_iter == std::prev(children.end())) {
|
|
|
|
if (wrap_scroll) node_iter = children.begin();
|
|
|
|
else return;
|
|
|
|
} else node_iter = std::next(node_iter);
|
|
|
|
|
|
|
|
tab_focused_node = *node_iter;
|
|
|
|
}
|
2023-04-17 00:53:02 -07:00
|
|
|
}
|
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* focus = tab_focused_node;
|
|
|
|
while (focus->data.type == Hy3NodeType::Group && !focus->data.as_group.group_focused
|
|
|
|
&& focus->data.as_group.focused_child != nullptr)
|
|
|
|
focus = focus->data.as_group.focused_child;
|
2023-04-12 01:33:00 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
focus->focus();
|
|
|
|
tab_node->recalcSizePosRecursive();
|
2023-04-18 23:41:18 -07:00
|
|
|
}
|
|
|
|
|
2023-07-20 03:54:13 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::killFocusedNode(int workspace) {
|
|
|
|
if (g_pCompositor->m_pLastWindow != nullptr && g_pCompositor->m_pLastWindow->m_bIsFloating) {
|
|
|
|
g_pCompositor->closeWindow(g_pCompositor->m_pLastWindow);
|
|
|
|
} else {
|
|
|
|
auto* node = this->getWorkspaceFocusedNode(workspace);
|
|
|
|
if (node == nullptr) return;
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
std::vector<CWindow*> windows;
|
|
|
|
node->appendAllWindows(windows);
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
for (auto* window: windows) {
|
|
|
|
window->setHidden(false);
|
|
|
|
g_pCompositor->closeWindow(window);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-14 16:14:58 -07:00
|
|
|
|
2023-08-09 02:21:18 -07:00
|
|
|
void Hy3Layout::expand(int workspace_id, ExpandOption option, ExpandFullscreenOption fs_option) {
|
|
|
|
auto* node = this->getWorkspaceFocusedNode(workspace_id, false, true);
|
|
|
|
if (node == nullptr) return;
|
|
|
|
|
|
|
|
const auto workspace = g_pCompositor->getWorkspaceByID(workspace_id);
|
|
|
|
const auto monitor = g_pCompositor->getMonitorFromID(workspace->m_iMonitorID);
|
|
|
|
|
|
|
|
switch (option) {
|
|
|
|
case ExpandOption::Expand: {
|
|
|
|
if (node->parent == nullptr) {
|
|
|
|
switch (fs_option) {
|
|
|
|
case ExpandFullscreenOption::MaximizeAsFullscreen:
|
|
|
|
case ExpandFullscreenOption::MaximizeIntermediate: goto fullscreen;
|
|
|
|
case ExpandFullscreenOption::MaximizeOnly: return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-16 01:33:56 -07:00
|
|
|
if (node->data.type == Hy3NodeType::Group && !node->data.as_group.group_focused)
|
2023-08-09 02:21:18 -07:00
|
|
|
node->data.as_group.expand_focused = ExpandFocusType::Stack;
|
|
|
|
|
|
|
|
auto& group = node->parent->data.as_group;
|
|
|
|
group.focused_child = node;
|
|
|
|
group.expand_focused = ExpandFocusType::Latch;
|
|
|
|
|
|
|
|
node->parent->recalcSizePosRecursive();
|
|
|
|
|
|
|
|
if (node->parent->parent == nullptr) {
|
|
|
|
switch (fs_option) {
|
|
|
|
case ExpandFullscreenOption::MaximizeAsFullscreen: goto fullscreen;
|
|
|
|
case ExpandFullscreenOption::MaximizeIntermediate:
|
|
|
|
case ExpandFullscreenOption::MaximizeOnly: return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ExpandOption::Shrink:
|
|
|
|
if (node->data.type == Hy3NodeType::Group) {
|
|
|
|
auto& group = node->data.as_group;
|
|
|
|
|
|
|
|
group.expand_focused = ExpandFocusType::NotExpanded;
|
|
|
|
if (group.focused_child->data.type == Hy3NodeType::Group)
|
|
|
|
group.focused_child->data.as_group.expand_focused = ExpandFocusType::Latch;
|
|
|
|
|
|
|
|
node->recalcSizePosRecursive();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ExpandOption::Base: {
|
|
|
|
if (node->data.type == Hy3NodeType::Group) {
|
|
|
|
node->data.as_group.collapseExpansions();
|
|
|
|
node->recalcSizePosRecursive();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ExpandOption::Maximize: break;
|
|
|
|
case ExpandOption::Fullscreen: break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
CWindow* window;
|
|
|
|
fullscreen:
|
|
|
|
if (node->data.type != Hy3NodeType::Window) return;
|
|
|
|
window = node->data.as_window;
|
|
|
|
if (!window->m_bIsFullscreen || g_pCompositor->isWorkspaceSpecial(window->m_iWorkspaceID)) return;
|
|
|
|
|
|
|
|
if (workspace->m_bHasFullscreenWindow) return;
|
|
|
|
|
|
|
|
window->m_bIsFullscreen = true;
|
|
|
|
workspace->m_bHasFullscreenWindow = true;
|
|
|
|
workspace->m_efFullscreenMode = FULLSCREEN_FULL;
|
|
|
|
window->m_vRealPosition = monitor->vecPosition;
|
|
|
|
window->m_vRealSize = monitor->vecSize;
|
|
|
|
goto fsupdate;
|
|
|
|
unfullscreen:
|
|
|
|
if (node->data.type != Hy3NodeType::Window) return;
|
|
|
|
window = node->data.as_window;
|
|
|
|
window->m_bIsFullscreen = false;
|
|
|
|
workspace->m_bHasFullscreenWindow = false;
|
|
|
|
goto fsupdate;
|
|
|
|
fsupdate:
|
|
|
|
g_pCompositor->updateWindowAnimatedDecorationValues(window);
|
|
|
|
g_pXWaylandManager->setWindowSize(window, window->m_vRealSize.goalv());
|
|
|
|
g_pCompositor->moveWindowToTop(window);
|
|
|
|
this->recalculateMonitor(monitor->ID);
|
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
bool Hy3Layout::shouldRenderSelected(CWindow* window) {
|
|
|
|
if (window == nullptr) return false;
|
|
|
|
auto* root = this->getWorkspaceRootGroup(window->m_iWorkspaceID);
|
|
|
|
if (root == nullptr || root->data.as_group.focused_child == nullptr) return false;
|
|
|
|
auto* focused = root->getFocusedNode();
|
|
|
|
if (focused == nullptr
|
|
|
|
|| (focused->data.type == Hy3NodeType::Window
|
|
|
|
&& focused->data.as_window != g_pCompositor->m_pLastWindow))
|
|
|
|
return false;
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
switch (focused->data.type) {
|
|
|
|
case Hy3NodeType::Window: return focused->data.as_window == window;
|
|
|
|
case Hy3NodeType::Group:
|
|
|
|
auto* node = this->getNodeFromWindow(window);
|
|
|
|
if (node == nullptr) return false;
|
|
|
|
return focused->data.as_group.hasChild(node);
|
2023-04-18 23:41:18 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
Hy3Node* Hy3Layout::getWorkspaceRootGroup(const int& workspace) {
|
|
|
|
for (auto& node: this->nodes) {
|
|
|
|
if (node.workspace_id == workspace && node.parent == nullptr
|
|
|
|
&& node.data.type == Hy3NodeType::Group)
|
|
|
|
{
|
|
|
|
return &node;
|
|
|
|
}
|
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
return nullptr;
|
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-08-09 02:21:18 -07:00
|
|
|
Hy3Node* Hy3Layout::getWorkspaceFocusedNode(
|
|
|
|
const int& workspace,
|
|
|
|
bool ignore_group_focus,
|
|
|
|
bool stop_at_expanded
|
|
|
|
) {
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* rootNode = this->getWorkspaceRootGroup(workspace);
|
|
|
|
if (rootNode == nullptr) return nullptr;
|
2023-08-09 02:21:18 -07:00
|
|
|
return rootNode->getFocusedNode(ignore_group_focus, stop_at_expanded);
|
2023-06-28 21:36:08 -07:00
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::renderHook(void*, std::any data) {
|
|
|
|
static bool rendering_normally = false;
|
|
|
|
static std::vector<Hy3TabGroup*> rendered_groups;
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto render_stage = std::any_cast<eRenderStage>(data);
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
switch (render_stage) {
|
|
|
|
case RENDER_PRE_WINDOWS:
|
|
|
|
rendering_normally = true;
|
|
|
|
rendered_groups.clear();
|
|
|
|
break;
|
|
|
|
case RENDER_POST_WINDOW:
|
|
|
|
if (!rendering_normally) break;
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
for (auto& entry: g_Hy3Layout->tab_groups) {
|
|
|
|
if (!entry.hidden && entry.target_window == g_pHyprOpenGL->m_pCurrentWindow
|
|
|
|
&& std::find(rendered_groups.begin(), rendered_groups.end(), &entry)
|
|
|
|
== rendered_groups.end())
|
2023-06-07 03:22:17 -07:00
|
|
|
{
|
2023-06-28 21:36:08 -07:00
|
|
|
entry.renderTabBar();
|
|
|
|
rendered_groups.push_back(&entry);
|
2023-04-18 23:41:18 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case RENDER_POST_WINDOWS:
|
|
|
|
rendering_normally = false;
|
|
|
|
|
|
|
|
for (auto& entry: g_Hy3Layout->tab_groups) {
|
|
|
|
if (!entry.hidden
|
|
|
|
&& entry.target_window->m_iMonitorID == g_pHyprOpenGL->m_RenderData.pMonitor->ID
|
|
|
|
&& std::find(rendered_groups.begin(), rendered_groups.end(), &entry)
|
|
|
|
== rendered_groups.end())
|
2023-06-07 03:22:17 -07:00
|
|
|
{
|
2023-06-28 21:36:08 -07:00
|
|
|
entry.renderTabBar();
|
2023-04-18 23:41:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2023-06-28 21:36:08 -07:00
|
|
|
default: break;
|
2023-04-18 23:41:18 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::windowGroupUrgentHook(void* p, std::any data) {
|
|
|
|
CWindow* window = std::any_cast<CWindow*>(data);
|
|
|
|
if (window == nullptr) return;
|
|
|
|
window->m_bIsUrgent = true;
|
|
|
|
Hy3Layout::windowGroupUpdateRecursiveHook(p, data);
|
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::windowGroupUpdateRecursiveHook(void*, std::any data) {
|
|
|
|
CWindow* window = std::any_cast<CWindow*>(data);
|
|
|
|
if (window == nullptr) return;
|
|
|
|
auto* node = g_Hy3Layout->getNodeFromWindow(window);
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
// it is UB for `this` to be null
|
|
|
|
if (node == nullptr) return;
|
|
|
|
node->updateTabBarRecursive();
|
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::tickHook(void*, std::any) {
|
|
|
|
auto& tab_groups = g_Hy3Layout->tab_groups;
|
|
|
|
auto entry = tab_groups.begin();
|
|
|
|
while (entry != tab_groups.end()) {
|
|
|
|
entry->tick();
|
|
|
|
if (entry->bar.destroy) tab_groups.erase(entry++);
|
|
|
|
else entry = std::next(entry);
|
2023-04-18 23:41:18 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
Hy3Node* Hy3Layout::getNodeFromWindow(CWindow* window) {
|
|
|
|
for (auto& node: this->nodes) {
|
|
|
|
if (node.data.type == Hy3NodeType::Window && node.data.as_window == window) {
|
|
|
|
return &node;
|
|
|
|
}
|
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
return nullptr;
|
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
void Hy3Layout::applyNodeDataToWindow(Hy3Node* node, bool no_animation) {
|
|
|
|
if (node->data.type != Hy3NodeType::Window) return;
|
2023-08-10 00:32:13 -07:00
|
|
|
auto* window = node->data.as_window;
|
|
|
|
auto root_node = this->getWorkspaceRootGroup(window->m_iWorkspaceID);
|
|
|
|
if (root_node == nullptr) return;
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
CMonitor* monitor = nullptr;
|
|
|
|
|
|
|
|
if (g_pCompositor->isWorkspaceSpecial(node->workspace_id)) {
|
|
|
|
for (auto& m: g_pCompositor->m_vMonitors) {
|
|
|
|
if (m->specialWorkspaceID == node->workspace_id) {
|
|
|
|
monitor = m.get();
|
|
|
|
break;
|
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
}
|
2023-06-28 21:36:08 -07:00
|
|
|
} else {
|
|
|
|
monitor = g_pCompositor->getMonitorFromID(
|
|
|
|
g_pCompositor->getWorkspaceByID(node->workspace_id)->m_iMonitorID
|
|
|
|
);
|
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (monitor == nullptr) {
|
|
|
|
Debug::log(ERR, "Orphaned Node %x (workspace ID: %i)!!", node, node->workspace_id);
|
|
|
|
errorNotif();
|
|
|
|
return;
|
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
// clang-format off
|
|
|
|
static const auto* border_size = &HyprlandAPI::getConfigValue(PHANDLE, "general:border_size")->intValue;
|
|
|
|
static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue;
|
|
|
|
static const auto* single_window_no_gaps = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:no_gaps_when_only")->intValue;
|
|
|
|
// clang-format on
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (!g_pCompositor->windowExists(window) || !window->m_bIsMapped) {
|
|
|
|
Debug::log(ERR, "Node %p holding invalid window %p!!", node, window);
|
|
|
|
errorNotif();
|
|
|
|
this->onWindowRemovedTiling(window);
|
|
|
|
return;
|
|
|
|
}
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
window->m_vSize = node->size;
|
|
|
|
window->m_vPosition = node->position;
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto calcPos = window->m_vPosition + Vector2D(*border_size, *border_size);
|
|
|
|
auto calcSize = window->m_vSize - Vector2D(2 * *border_size, 2 * *border_size);
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto only_node = root_node->data.as_group.children.size() == 1
|
|
|
|
&& root_node->data.as_group.children.front()->data.type == Hy3NodeType::Window;
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (!g_pCompositor->isWorkspaceSpecial(window->m_iWorkspaceID)
|
|
|
|
&& ((*single_window_no_gaps && only_node)
|
|
|
|
|| (window->m_bIsFullscreen
|
|
|
|
&& g_pCompositor->getWorkspaceByID(window->m_iWorkspaceID)->m_efFullscreenMode
|
|
|
|
== FULLSCREEN_FULL)))
|
|
|
|
{
|
|
|
|
window->m_vRealPosition = window->m_vPosition;
|
|
|
|
window->m_vRealSize = window->m_vSize;
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
window->updateWindowDecos();
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
window->m_sSpecialRenderData.rounding = false;
|
|
|
|
window->m_sSpecialRenderData.border = false;
|
|
|
|
window->m_sSpecialRenderData.decorate = false;
|
|
|
|
} else {
|
|
|
|
window->m_sSpecialRenderData.rounding = true;
|
|
|
|
window->m_sSpecialRenderData.border = true;
|
|
|
|
window->m_sSpecialRenderData.decorate = true;
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto gaps_offset_topleft = Vector2D(*gaps_in, *gaps_in) + node->gap_pos_offset;
|
|
|
|
auto gaps_offset_bottomright = Vector2D(*gaps_in * 2, *gaps_in * 2) + node->gap_size_offset;
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
calcPos = calcPos + gaps_offset_topleft;
|
|
|
|
calcSize = calcSize - gaps_offset_bottomright;
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
const auto reserved_area = window->getFullWindowReservedArea();
|
|
|
|
calcPos = calcPos + reserved_area.topLeft;
|
|
|
|
calcSize = calcSize - (reserved_area.topLeft - reserved_area.bottomRight);
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
window->m_vRealPosition = calcPos;
|
|
|
|
window->m_vRealSize = calcSize;
|
|
|
|
Debug::log(LOG, "Set size (%f %f)", calcSize.x, calcSize.y);
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
g_pXWaylandManager->setWindowSize(window, calcSize);
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
if (no_animation) {
|
|
|
|
g_pHyprRenderer->damageWindow(window);
|
2023-04-18 23:41:18 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
window->m_vRealPosition.warp();
|
|
|
|
window->m_vRealSize.warp();
|
|
|
|
|
|
|
|
g_pHyprRenderer->damageWindow(window);
|
2023-04-18 23:41:18 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
window->updateWindowDecos();
|
2023-04-18 23:41:18 -07:00
|
|
|
}
|
2023-04-12 01:33:00 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
bool shiftIsForward(ShiftDirection direction) {
|
|
|
|
return direction == ShiftDirection::Right || direction == ShiftDirection::Down;
|
|
|
|
}
|
2023-04-17 00:53:02 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
bool shiftIsVertical(ShiftDirection direction) {
|
|
|
|
return direction == ShiftDirection::Up || direction == ShiftDirection::Down;
|
|
|
|
}
|
2023-04-17 00:53:02 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
bool shiftMatchesLayout(Hy3GroupLayout layout, ShiftDirection direction) {
|
|
|
|
return (layout == Hy3GroupLayout::SplitV && shiftIsVertical(direction))
|
|
|
|
|| (layout != Hy3GroupLayout::SplitV && !shiftIsVertical(direction));
|
|
|
|
}
|
2023-04-17 00:53:02 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
Hy3Node* Hy3Layout::shiftOrGetFocus(
|
|
|
|
Hy3Node& node,
|
|
|
|
ShiftDirection direction,
|
|
|
|
bool shift,
|
|
|
|
bool once,
|
|
|
|
bool visible
|
|
|
|
) {
|
2023-08-09 02:21:18 -07:00
|
|
|
auto* break_origin = &node.getExpandActor();
|
2023-06-28 21:36:08 -07:00
|
|
|
auto* break_parent = break_origin->parent;
|
2023-04-17 00:53:02 -07:00
|
|
|
|
2023-06-28 21:36:08 -07:00
|
|
|
auto has_broken_once = false;
|
2023-05-10 00:42:53 -07:00
|
|
|
|
2023-06-07 03:22:17 -07:00
|
|
|
// break parents until we hit a container oriented the same way as the shift
|
|
|
|
// direction
|
2023-04-16 16:27:18 -07:00
|
|
|
while (true) {
|
|
|
|
if (break_parent == nullptr) return nullptr;
|
2023-04-13 14:12:48 -07:00
|
|
|
|
2023-04-16 16:27:18 -07:00
|
|
|
auto& group = break_parent->data.as_group; // must be a group in order to be a parent
|
2023-04-13 14:12:48 -07:00
|
|
|
|
2023-06-11 23:00:24 -07:00
|
|
|
if (shiftMatchesLayout(group.layout, direction)
|
|
|
|
&& (!visible || group.layout != Hy3GroupLayout::Tabbed))
|
|
|
|
{
|
2023-04-16 16:27:18 -07:00
|
|
|
// group has the correct orientation
|
2023-04-13 14:12:48 -07:00
|
|
|
|
2023-05-14 15:05:30 -07:00
|
|
|
if (once && shift && has_broken_once) break;
|
|
|
|
if (break_origin != &node) has_broken_once = true;
|
|
|
|
|
2023-06-07 03:22:17 -07:00
|
|
|
// if this movement would break out of the group, continue the break loop
|
|
|
|
// (do not enter this if) otherwise break.
|
2023-05-14 15:05:30 -07:00
|
|
|
if ((has_broken_once && once && shift)
|
2023-06-07 03:22:17 -07:00
|
|
|
|| !(
|
|
|
|
(!shiftIsForward(direction) && group.children.front() == break_origin)
|
|
|
|
|| (shiftIsForward(direction) && group.children.back() == break_origin)
|
|
|
|
))
|
2023-04-16 16:27:18 -07:00
|
|
|
break;
|
2023-04-13 14:12:48 -07:00
|
|
|
}
|
|
|
|
|
2023-04-16 16:27:18 -07:00
|
|
|
if (break_parent->parent == nullptr) {
|
2023-04-24 02:58:53 -07:00
|
|
|
if (!shift) return nullptr;
|
|
|
|
|
|
|
|
// if we haven't gone up any levels and the group is in the same direction
|
|
|
|
// there's no reason to wrap the root group.
|
2023-06-07 03:22:17 -07:00
|
|
|
if (group.layout != Hy3GroupLayout::Tabbed && shiftMatchesLayout(group.layout, direction))
|
|
|
|
break;
|
2023-04-24 02:58:53 -07:00
|
|
|
|
2023-06-07 03:22:17 -07:00
|
|
|
if (group.layout != Hy3GroupLayout::Tabbed && group.children.size() == 2
|
|
|
|
&& std::find(group.children.begin(), group.children.end(), &node) != group.children.end())
|
|
|
|
{
|
2023-04-27 10:37:35 -07:00
|
|
|
group.layout = shiftIsVertical(direction) ? Hy3GroupLayout::SplitV : Hy3GroupLayout::SplitH;
|
|
|
|
} else {
|
|
|
|
// wrap the root group in another group
|
|
|
|
this->nodes.push_back({
|
2023-06-07 03:22:17 -07:00
|
|
|
.parent = break_parent,
|
|
|
|
.data = shiftIsVertical(direction) ? Hy3GroupLayout::SplitV : Hy3GroupLayout::SplitH,
|
|
|
|
.position = break_parent->position,
|
|
|
|
.size = break_parent->size,
|
|
|
|
.workspace_id = break_parent->workspace_id,
|
|
|
|
.layout = this,
|
2023-04-27 10:37:35 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
auto* newChild = &this->nodes.back();
|
|
|
|
Hy3Node::swapData(*break_parent, *newChild);
|
|
|
|
break_parent->data.as_group.children.push_back(newChild);
|
2023-05-26 20:37:05 -07:00
|
|
|
break_parent->data.as_group.group_focused = false;
|
|
|
|
break_parent->data.as_group.focused_child = newChild;
|
2023-04-27 10:37:35 -07:00
|
|
|
break_origin = newChild;
|
|
|
|
}
|
2023-04-24 02:58:53 -07:00
|
|
|
|
|
|
|
break;
|
2023-04-16 16:27:18 -07:00
|
|
|
} else {
|
|
|
|
break_origin = break_parent;
|
|
|
|
break_parent = break_origin->parent;
|
|
|
|
}
|
2023-04-13 14:12:48 -07:00
|
|
|
}
|
|
|
|
|
2023-04-16 16:27:18 -07:00
|
|
|
auto& parent_group = break_parent->data.as_group;
|
|
|
|
Hy3Node* target_group = break_parent;
|
|
|
|
std::list<Hy3Node*>::iterator insert;
|
2023-04-13 14:12:48 -07:00
|
|
|
|
2023-04-16 16:27:18 -07:00
|
|
|
if (break_origin == parent_group.children.front() && !shiftIsForward(direction)) {
|
|
|
|
if (!shift) return nullptr;
|
|
|
|
insert = parent_group.children.begin();
|
|
|
|
} else if (break_origin == parent_group.children.back() && shiftIsForward(direction)) {
|
|
|
|
if (!shift) return nullptr;
|
|
|
|
insert = parent_group.children.end();
|
|
|
|
} else {
|
|
|
|
auto& group_data = target_group->data.as_group;
|
|
|
|
|
|
|
|
auto iter = std::find(group_data.children.begin(), group_data.children.end(), break_origin);
|
|
|
|
if (shiftIsForward(direction)) iter = std::next(iter);
|
|
|
|
else iter = std::prev(iter);
|
|
|
|
|
2023-08-09 02:21:18 -07:00
|
|
|
if ((*iter)->data.type == Hy3NodeType::Window
|
|
|
|
|| ((*iter)->data.type == Hy3NodeType::Group
|
|
|
|
&& (*iter)->data.as_group.expand_focused != ExpandFocusType::NotExpanded)
|
|
|
|
|| (shift && once && has_broken_once))
|
|
|
|
{
|
2023-04-16 16:27:18 -07:00
|
|
|
if (shift) {
|
|
|
|
if (target_group == node.parent) {
|
|
|
|
if (shiftIsForward(direction)) insert = std::next(iter);
|
|
|
|
else insert = iter;
|
|
|
|
} else {
|
|
|
|
if (shiftIsForward(direction)) insert = iter;
|
|
|
|
else insert = std::next(iter);
|
|
|
|
}
|
2023-08-09 02:21:18 -07:00
|
|
|
} else return (*iter)->getFocusedNode();
|
2023-04-16 16:27:18 -07:00
|
|
|
} else {
|
|
|
|
// break into neighboring groups until we hit a window
|
|
|
|
while (true) {
|
|
|
|
target_group = *iter;
|
|
|
|
auto& group_data = target_group->data.as_group;
|
|
|
|
|
|
|
|
if (group_data.children.empty()) return nullptr; // in theory this would never happen
|
|
|
|
|
2023-04-16 21:56:19 -07:00
|
|
|
bool shift_after = false;
|
|
|
|
|
2023-06-07 03:22:17 -07:00
|
|
|
if (!shift && group_data.layout == Hy3GroupLayout::Tabbed
|
|
|
|
&& group_data.focused_child != nullptr)
|
|
|
|
{
|
|
|
|
iter = std::find(
|
|
|
|
group_data.children.begin(),
|
|
|
|
group_data.children.end(),
|
|
|
|
group_data.focused_child
|
|
|
|
);
|
2023-05-31 22:54:27 -07:00
|
|
|
} else if (shiftMatchesLayout(group_data.layout, direction)) {
|
2023-06-07 03:22:17 -07:00
|
|
|
// if the group has the same orientation as movement pick the
|
|
|
|
// last/first child based on movement direction
|
2023-04-16 16:27:18 -07:00
|
|
|
if (shiftIsForward(direction)) iter = group_data.children.begin();
|
2023-04-16 21:56:19 -07:00
|
|
|
else {
|
|
|
|
iter = std::prev(group_data.children.end());
|
|
|
|
shift_after = true;
|
|
|
|
}
|
2023-04-16 16:27:18 -07:00
|
|
|
} else {
|
2023-05-26 20:37:05 -07:00
|
|
|
if (group_data.focused_child != nullptr) {
|
2023-06-07 03:22:17 -07:00
|
|
|
iter = std::find(
|
|
|
|
group_data.children.begin(),
|
|
|
|
group_data.children.end(),
|
|
|
|
group_data.focused_child
|
|
|
|
);
|
2023-04-16 21:56:19 -07:00
|
|
|
shift_after = true;
|
|
|
|
} else {
|
|
|
|
iter = group_data.children.begin();
|
|
|
|
}
|
2023-04-16 16:27:18 -07:00
|
|
|
}
|
|
|
|
|
2023-05-10 00:42:53 -07:00
|
|
|
if (shift && once) {
|
|
|
|
if (shift_after) insert = std::next(iter);
|
|
|
|
else insert = iter;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-08-09 02:21:18 -07:00
|
|
|
if ((*iter)->data.type == Hy3NodeType::Window
|
|
|
|
|| ((*iter)->data.type == Hy3NodeType::Group
|
|
|
|
&& (*iter)->data.as_group.expand_focused != ExpandFocusType::NotExpanded))
|
|
|
|
{
|
2023-04-16 16:27:18 -07:00
|
|
|
if (shift) {
|
2023-04-16 21:56:19 -07:00
|
|
|
if (shift_after) insert = std::next(iter);
|
2023-04-16 16:27:18 -07:00
|
|
|
else insert = iter;
|
|
|
|
break;
|
|
|
|
} else {
|
2023-08-09 02:21:18 -07:00
|
|
|
return (*iter)->getFocusedNode();
|
2023-04-16 16:27:18 -07:00
|
|
|
}
|
2023-04-13 14:12:48 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-04-16 16:27:18 -07:00
|
|
|
}
|
2023-04-13 14:12:48 -07:00
|
|
|
|
2023-04-16 16:27:18 -07:00
|
|
|
auto& group_data = target_group->data.as_group;
|
|
|
|
|
|
|
|
if (target_group == node.parent) {
|
2023-06-07 03:22:17 -07:00
|
|
|
// nullptr is used as a signal value instead of removing it first to avoid
|
|
|
|
// iterator invalidation.
|
2023-04-16 16:27:18 -07:00
|
|
|
auto iter = std::find(group_data.children.begin(), group_data.children.end(), &node);
|
|
|
|
*iter = nullptr;
|
|
|
|
target_group->data.as_group.children.insert(insert, &node);
|
|
|
|
target_group->data.as_group.children.remove(nullptr);
|
|
|
|
target_group->recalcSizePosRecursive();
|
|
|
|
} else {
|
|
|
|
target_group->data.as_group.children.insert(insert, &node);
|
2023-04-16 21:56:19 -07:00
|
|
|
|
|
|
|
// must happen AFTER `insert` is used
|
2023-04-23 21:02:51 -07:00
|
|
|
auto* old_parent = node.removeFromParentRecursive();
|
2023-04-22 04:19:34 -07:00
|
|
|
node.parent = target_group;
|
2023-04-18 23:41:18 -07:00
|
|
|
node.size_ratio = 1.0;
|
|
|
|
|
2023-07-19 03:53:23 -07:00
|
|
|
if (old_parent != nullptr) {
|
|
|
|
auto& group = old_parent->data.as_group;
|
2023-07-29 03:08:35 -07:00
|
|
|
if (old_parent->parent != nullptr && group.ephemeral && group.children.size() == 1
|
|
|
|
&& !group.hasChild(&node))
|
|
|
|
{
|
2023-07-19 03:53:23 -07:00
|
|
|
Hy3Node::swallowGroups(old_parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
old_parent->recalcSizePosRecursive();
|
|
|
|
}
|
|
|
|
|
2023-04-16 21:56:19 -07:00
|
|
|
target_group->recalcSizePosRecursive();
|
2023-04-19 02:07:47 -07:00
|
|
|
|
|
|
|
auto* target_parent = target_group->parent;
|
2023-04-23 21:02:51 -07:00
|
|
|
while (target_parent != nullptr && Hy3Node::swallowGroups(target_parent)) {
|
2023-04-19 02:07:47 -07:00
|
|
|
target_parent = target_parent->parent;
|
|
|
|
}
|
|
|
|
|
2023-05-31 01:31:22 -07:00
|
|
|
node.focus();
|
2023-04-22 04:14:36 -07:00
|
|
|
|
2023-04-19 02:07:47 -07:00
|
|
|
if (target_parent != target_group && target_parent != nullptr)
|
|
|
|
target_parent->recalcSizePosRecursive();
|
2023-04-13 14:12:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|