mirror of
https://github.com/Trensa-Organization/hy3.git
synced 2025-03-15 10:43:40 +01:00
Refactor
This commit is contained in:
parent
ae2409d037
commit
1435be18d4
12 changed files with 1447 additions and 1428 deletions
|
@ -15,7 +15,9 @@ pkg_check_modules(DEPS REQUIRED hyprland pixman-1 libdrm pango pangocairo)
|
|||
|
||||
add_library(hy3 SHARED
|
||||
src/main.cpp
|
||||
src/dispatchers.cpp
|
||||
src/Hy3Layout.cpp
|
||||
src/Hy3Node.cpp
|
||||
src/TabGroup.cpp
|
||||
src/SelectionHook.cpp
|
||||
)
|
||||
|
|
1633
src/Hy3Layout.cpp
1633
src/Hy3Layout.cpp
File diff suppressed because it is too large
Load diff
|
@ -1,25 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <hyprland/src/layout/IHyprLayout.hpp>
|
||||
class Hy3Layout;
|
||||
|
||||
#include <list>
|
||||
|
||||
struct Hy3Node;
|
||||
#include <hyprland/src/layout/IHyprLayout.hpp>
|
||||
|
||||
#include "Hy3Node.hpp"
|
||||
#include "TabGroup.hpp"
|
||||
|
||||
class Hy3Layout;
|
||||
struct Hy3Node;
|
||||
|
||||
enum class Hy3GroupLayout {
|
||||
SplitH,
|
||||
SplitV,
|
||||
Tabbed,
|
||||
};
|
||||
|
||||
enum class Hy3NodeType {
|
||||
Window,
|
||||
Group,
|
||||
};
|
||||
|
||||
enum class ShiftDirection {
|
||||
Left,
|
||||
Up,
|
||||
|
@ -49,93 +38,6 @@ enum class TabFocusMousePriority {
|
|||
Require,
|
||||
};
|
||||
|
||||
struct Hy3GroupData {
|
||||
Hy3GroupLayout layout = Hy3GroupLayout::SplitH;
|
||||
std::list<Hy3Node*> children;
|
||||
bool group_focused = true;
|
||||
Hy3Node* focused_child = nullptr;
|
||||
Hy3TabGroup* tab_bar = nullptr;
|
||||
|
||||
bool hasChild(Hy3Node* child);
|
||||
|
||||
Hy3GroupData(Hy3GroupLayout layout);
|
||||
~Hy3GroupData();
|
||||
|
||||
private:
|
||||
Hy3GroupData(Hy3GroupData&&);
|
||||
Hy3GroupData(const Hy3GroupData&) = delete;
|
||||
|
||||
friend class Hy3NodeData;
|
||||
};
|
||||
|
||||
class Hy3NodeData {
|
||||
public:
|
||||
Hy3NodeType type;
|
||||
union {
|
||||
Hy3GroupData as_group;
|
||||
CWindow* as_window;
|
||||
};
|
||||
|
||||
bool operator==(const Hy3NodeData&) const;
|
||||
|
||||
Hy3NodeData();
|
||||
~Hy3NodeData();
|
||||
Hy3NodeData(CWindow* window);
|
||||
Hy3NodeData(Hy3GroupLayout layout);
|
||||
Hy3NodeData& operator=(CWindow*);
|
||||
Hy3NodeData& operator=(Hy3GroupLayout);
|
||||
|
||||
// private: - I give up, C++ wins
|
||||
Hy3NodeData(Hy3GroupData);
|
||||
Hy3NodeData(Hy3NodeData&&);
|
||||
Hy3NodeData& operator=(Hy3NodeData&&);
|
||||
};
|
||||
|
||||
struct Hy3Node {
|
||||
Hy3Node* parent = nullptr;
|
||||
Hy3NodeData data;
|
||||
Vector2D position;
|
||||
Vector2D size;
|
||||
Vector2D gap_pos_offset;
|
||||
Vector2D gap_size_offset;
|
||||
float size_ratio = 1.0;
|
||||
int workspace_id = -1;
|
||||
bool hidden = false;
|
||||
bool valid = true;
|
||||
Hy3Layout* layout = nullptr;
|
||||
|
||||
void recalcSizePosRecursive(bool no_animation = false);
|
||||
std::string debugNode();
|
||||
void markFocused();
|
||||
void focus();
|
||||
bool focusWindow();
|
||||
void raiseToTop();
|
||||
Hy3Node* getFocusedNode();
|
||||
void updateDecos();
|
||||
void setHidden(bool);
|
||||
void updateTabBar(bool no_animation = false);
|
||||
void updateTabBarRecursive();
|
||||
bool isUrgent();
|
||||
bool isIndirectlyFocused();
|
||||
Hy3Node* findNodeForTabGroup(Hy3TabGroup&);
|
||||
std::string getTitle();
|
||||
void appendAllWindows(std::vector<CWindow*>&);
|
||||
|
||||
bool operator==(const Hy3Node&) const;
|
||||
|
||||
// Attempt to swallow a group. returns true if swallowed
|
||||
static bool swallowGroups(Hy3Node* into);
|
||||
// Remove this node from its parent, deleting the parent if it was
|
||||
// the only child and recursing if the parent was the only child of it's
|
||||
// parent.
|
||||
Hy3Node* removeFromParentRecursive();
|
||||
|
||||
// Replace this node with a group, returning this node's new address.
|
||||
Hy3Node* intoGroup(Hy3GroupLayout);
|
||||
|
||||
static void swapData(Hy3Node&, Hy3Node&);
|
||||
};
|
||||
|
||||
class Hy3Layout: public IHyprLayout {
|
||||
public:
|
||||
virtual void onWindowCreatedTiling(CWindow*);
|
||||
|
@ -188,7 +90,6 @@ private:
|
|||
bool yExtent = false;
|
||||
} drag_flags;
|
||||
|
||||
int getWorkspaceNodeCount(const int& workspace);
|
||||
Hy3Node* getNodeFromWindow(CWindow*);
|
||||
void applyNodeDataToWindow(Hy3Node*, bool no_animation = false);
|
||||
|
||||
|
|
680
src/Hy3Node.cpp
Normal file
680
src/Hy3Node.cpp
Normal file
|
@ -0,0 +1,680 @@
|
|||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/plugins/PluginAPI.hpp>
|
||||
|
||||
#include "globals.hpp"
|
||||
#include "Hy3Node.hpp"
|
||||
|
||||
// Hy3GroupData //
|
||||
|
||||
Hy3GroupData::Hy3GroupData(Hy3GroupLayout layout): layout(layout) {}
|
||||
|
||||
Hy3GroupData::Hy3GroupData(Hy3GroupData&& from) {
|
||||
this->layout = from.layout;
|
||||
this->children = std::move(from.children);
|
||||
this->group_focused = from.group_focused;
|
||||
this->focused_child = from.focused_child;
|
||||
from.focused_child = nullptr;
|
||||
this->tab_bar = from.tab_bar;
|
||||
from.tab_bar = nullptr;
|
||||
}
|
||||
|
||||
Hy3GroupData::~Hy3GroupData() {
|
||||
if (this->tab_bar != nullptr) this->tab_bar->bar.beginDestroy();
|
||||
}
|
||||
|
||||
bool Hy3GroupData::hasChild(Hy3Node* node) {
|
||||
Debug::log(LOG, "Searching for child %p of %p", this, node);
|
||||
for (auto child: this->children) {
|
||||
if (child == node) return true;
|
||||
|
||||
if (child->data.type == Hy3NodeType::Group) {
|
||||
if (child->data.as_group.hasChild(node)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hy3NodeData //
|
||||
|
||||
Hy3NodeData::Hy3NodeData(): Hy3NodeData((CWindow*) nullptr) {}
|
||||
|
||||
Hy3NodeData::Hy3NodeData(CWindow* window): type(Hy3NodeType::Window) { this->as_window = window; }
|
||||
|
||||
Hy3NodeData::Hy3NodeData(Hy3GroupLayout layout): Hy3NodeData(Hy3GroupData(layout)) {}
|
||||
|
||||
Hy3NodeData::Hy3NodeData(Hy3GroupData group): type(Hy3NodeType::Group) {
|
||||
new (&this->as_group) Hy3GroupData(std::move(group));
|
||||
}
|
||||
|
||||
Hy3NodeData::Hy3NodeData(Hy3NodeData&& from): type(from.type) {
|
||||
Debug::log(
|
||||
LOG,
|
||||
"Move CTor type matches? %d is group? %d",
|
||||
this->type == from.type,
|
||||
this->type == Hy3NodeType::Group
|
||||
);
|
||||
|
||||
switch (from.type) {
|
||||
case Hy3NodeType::Window: this->as_window = from.as_window; break;
|
||||
case Hy3NodeType::Group: new (&this->as_group) Hy3GroupData(std::move(from.as_group)); break;
|
||||
}
|
||||
}
|
||||
|
||||
Hy3NodeData::~Hy3NodeData() {
|
||||
switch (this->type) {
|
||||
case Hy3NodeType::Window: break;
|
||||
case Hy3NodeType::Group:
|
||||
this->as_group.~Hy3GroupData();
|
||||
|
||||
// who ever thought calling the dtor after a move was a good idea?
|
||||
this->type = Hy3NodeType::Window;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Hy3NodeData& Hy3NodeData::operator=(CWindow* window) {
|
||||
*this = Hy3NodeData(window);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Hy3NodeData& Hy3NodeData::operator=(Hy3GroupLayout layout) {
|
||||
*this = Hy3NodeData(layout);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Hy3NodeData& Hy3NodeData::operator=(Hy3NodeData&& from) {
|
||||
Debug::log(
|
||||
LOG,
|
||||
"operator= type matches? %d is group? %d",
|
||||
this->type == from.type,
|
||||
this->type == Hy3NodeType::Group
|
||||
);
|
||||
|
||||
if (this->type == Hy3NodeType::Group) {
|
||||
this->as_group.~Hy3GroupData();
|
||||
}
|
||||
|
||||
this->type = from.type;
|
||||
|
||||
switch (this->type) {
|
||||
case Hy3NodeType::Window: this->as_window = from.as_window; break;
|
||||
case Hy3NodeType::Group: new (&this->as_group) Hy3GroupData(std::move(from.as_group)); break;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Hy3NodeData::operator==(const Hy3NodeData& rhs) const { return this == &rhs; }
|
||||
|
||||
// Hy3Node //
|
||||
|
||||
bool Hy3Node::operator==(const Hy3Node& rhs) const { return this->data == rhs.data; }
|
||||
|
||||
void Hy3Node::focus() {
|
||||
this->markFocused();
|
||||
|
||||
switch (this->data.type) {
|
||||
case Hy3NodeType::Window:
|
||||
this->data.as_window->setHidden(false);
|
||||
g_pCompositor->focusWindow(this->data.as_window);
|
||||
break;
|
||||
case Hy3NodeType::Group:
|
||||
g_pCompositor->focusWindow(nullptr);
|
||||
this->raiseToTop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Hy3Node::focusWindow() {
|
||||
switch (this->data.type) {
|
||||
case Hy3NodeType::Window:
|
||||
this->markFocused();
|
||||
g_pCompositor->focusWindow(this->data.as_window);
|
||||
|
||||
return true;
|
||||
case Hy3NodeType::Group:
|
||||
if (this->data.as_group.layout == Hy3GroupLayout::Tabbed) {
|
||||
if (this->data.as_group.focused_child != nullptr) {
|
||||
return this->data.as_group.focused_child->focusWindow();
|
||||
}
|
||||
} else {
|
||||
for (auto* node: this->data.as_group.children) {
|
||||
if (node->focusWindow()) break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void markGroupFocusedRecursive(Hy3GroupData& group) {
|
||||
group.group_focused = true;
|
||||
for (auto& child: group.children) {
|
||||
if (child->data.type == Hy3NodeType::Group) markGroupFocusedRecursive(child->data.as_group);
|
||||
}
|
||||
}
|
||||
|
||||
void Hy3Node::markFocused() {
|
||||
Hy3Node* node = this;
|
||||
|
||||
// undo decos for root focus
|
||||
auto* root = node;
|
||||
while (root->parent != nullptr) root = root->parent;
|
||||
|
||||
// update focus
|
||||
if (this->data.type == Hy3NodeType::Group) {
|
||||
markGroupFocusedRecursive(this->data.as_group);
|
||||
}
|
||||
|
||||
auto* node2 = node;
|
||||
while (node2->parent != nullptr) {
|
||||
node2->parent->data.as_group.focused_child = node2;
|
||||
node2->parent->data.as_group.group_focused = false;
|
||||
node2 = node2->parent;
|
||||
}
|
||||
|
||||
root->updateDecos();
|
||||
}
|
||||
|
||||
void Hy3Node::raiseToTop() {
|
||||
switch (this->data.type) {
|
||||
case Hy3NodeType::Window: g_pCompositor->moveWindowToTop(this->data.as_window); break;
|
||||
case Hy3NodeType::Group:
|
||||
for (auto* child: this->data.as_group.children) {
|
||||
child->raiseToTop();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Hy3Node* Hy3Node::getFocusedNode() {
|
||||
switch (this->data.type) {
|
||||
case Hy3NodeType::Window: return this;
|
||||
case Hy3NodeType::Group:
|
||||
if (this->data.as_group.focused_child == nullptr || this->data.as_group.group_focused) {
|
||||
return this;
|
||||
} else {
|
||||
return this->data.as_group.focused_child->getFocusedNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Hy3Node::isIndirectlyFocused() {
|
||||
Hy3Node* node = this;
|
||||
|
||||
while (node->parent != nullptr) {
|
||||
if (!node->parent->data.as_group.group_focused
|
||||
&& node->parent->data.as_group.focused_child != node)
|
||||
return false;
|
||||
node = node->parent;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Hy3Node::recalcSizePosRecursive(bool no_animation) {
|
||||
// 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* group_inset = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:group_inset")->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
|
||||
|
||||
int outer_gaps = 0;
|
||||
Vector2D gap_pos_offset;
|
||||
Vector2D gap_size_offset;
|
||||
if (this->parent == nullptr) {
|
||||
outer_gaps = *gaps_out - *gaps_in;
|
||||
|
||||
gap_pos_offset = Vector2D(outer_gaps, outer_gaps);
|
||||
gap_size_offset = Vector2D(outer_gaps * 2, outer_gaps * 2);
|
||||
} else {
|
||||
gap_pos_offset = this->gap_pos_offset;
|
||||
gap_size_offset = this->gap_size_offset;
|
||||
}
|
||||
|
||||
auto tpos = this->position;
|
||||
auto tsize = this->size;
|
||||
|
||||
double tab_height_offset = *tab_bar_height + *tab_bar_padding;
|
||||
|
||||
if (this->data.type != Hy3NodeType::Group) {
|
||||
this->data.as_window->setHidden(this->hidden);
|
||||
this->layout->applyNodeDataToWindow(this, no_animation);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* group = &this->data.as_group;
|
||||
|
||||
if (group->children.size() == 1 && this->parent != nullptr) {
|
||||
auto child = group->children.front();
|
||||
|
||||
if (child == this) {
|
||||
Debug::log(ERR, "a group (%p) has become its own child", this);
|
||||
errorNotif();
|
||||
}
|
||||
|
||||
switch (group->layout) {
|
||||
case Hy3GroupLayout::SplitH:
|
||||
child->position.x = tpos.x;
|
||||
child->size.x = tsize.x - *group_inset;
|
||||
child->position.y = tpos.y;
|
||||
child->size.y = tsize.y;
|
||||
break;
|
||||
case Hy3GroupLayout::SplitV:
|
||||
child->position.y = tpos.y;
|
||||
child->size.y = tsize.y - *group_inset;
|
||||
child->position.x = tpos.x;
|
||||
child->size.x = tsize.x;
|
||||
break;
|
||||
case Hy3GroupLayout::Tabbed:
|
||||
child->position.y = tpos.y + tab_height_offset;
|
||||
child->size.y = tsize.y - tab_height_offset;
|
||||
child->position.x = tpos.x;
|
||||
child->size.x = tsize.x;
|
||||
break;
|
||||
}
|
||||
|
||||
child->gap_pos_offset = gap_pos_offset;
|
||||
child->gap_size_offset = gap_size_offset;
|
||||
|
||||
child->setHidden(this->hidden);
|
||||
|
||||
child->recalcSizePosRecursive(no_animation);
|
||||
this->updateTabBar(no_animation);
|
||||
return;
|
||||
}
|
||||
|
||||
int constraint;
|
||||
switch (group->layout) {
|
||||
case Hy3GroupLayout::SplitH: constraint = tsize.x; break;
|
||||
case Hy3GroupLayout::SplitV: constraint = tsize.y; break;
|
||||
case Hy3GroupLayout::Tabbed: break;
|
||||
}
|
||||
|
||||
double ratio_mul = group->layout != Hy3GroupLayout::Tabbed
|
||||
? group->children.empty() ? 0 : constraint / group->children.size()
|
||||
: 0;
|
||||
|
||||
double offset = 0;
|
||||
|
||||
if (group->layout == Hy3GroupLayout::Tabbed && group->focused_child != nullptr
|
||||
&& !group->focused_child->hidden)
|
||||
{
|
||||
group->focused_child->setHidden(false);
|
||||
|
||||
auto box = wlr_box {tpos.x, tpos.y, tsize.x, tsize.y};
|
||||
g_pHyprRenderer->damageBox(&box);
|
||||
}
|
||||
|
||||
for (auto* child: group->children) {
|
||||
switch (group->layout) {
|
||||
case Hy3GroupLayout::SplitH:
|
||||
child->position.x = tpos.x + offset;
|
||||
child->size.x = child->size_ratio * ratio_mul;
|
||||
offset += child->size.x;
|
||||
child->position.y = tpos.y;
|
||||
child->size.y = tsize.y;
|
||||
child->setHidden(this->hidden);
|
||||
child->recalcSizePosRecursive(no_animation);
|
||||
break;
|
||||
case Hy3GroupLayout::SplitV:
|
||||
child->position.y = tpos.y + offset;
|
||||
child->size.y = child->size_ratio * ratio_mul;
|
||||
offset += child->size.y;
|
||||
child->position.x = tpos.x;
|
||||
child->size.x = tsize.x;
|
||||
child->setHidden(this->hidden);
|
||||
child->recalcSizePosRecursive(no_animation);
|
||||
break;
|
||||
case Hy3GroupLayout::Tabbed:
|
||||
child->position.y = tpos.y + tab_height_offset;
|
||||
child->size.y = tsize.y - tab_height_offset;
|
||||
child->position.x = tpos.x;
|
||||
child->size.x = tsize.x;
|
||||
bool hidden = this->hidden || group->focused_child != child;
|
||||
child->setHidden(hidden);
|
||||
child->recalcSizePosRecursive(no_animation);
|
||||
break;
|
||||
}
|
||||
|
||||
child->gap_pos_offset = gap_pos_offset;
|
||||
child->gap_size_offset = gap_pos_offset;
|
||||
}
|
||||
|
||||
this->updateTabBar();
|
||||
}
|
||||
|
||||
struct FindTopWindowInNodeResult {
|
||||
CWindow* window = nullptr;
|
||||
size_t index = 0;
|
||||
};
|
||||
|
||||
void findTopWindowInNode(Hy3Node& node, FindTopWindowInNodeResult& result) {
|
||||
switch (node.data.type) {
|
||||
case Hy3NodeType::Window: {
|
||||
auto* window = node.data.as_window;
|
||||
auto& windows = g_pCompositor->m_vWindows;
|
||||
|
||||
for (; result.index < windows.size(); result.index++) {
|
||||
if (&*windows[result.index] == window) {
|
||||
result.window = window;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
case Hy3NodeType::Group: {
|
||||
auto& group = node.data.as_group;
|
||||
|
||||
if (group.layout == Hy3GroupLayout::Tabbed) {
|
||||
if (group.focused_child != nullptr) findTopWindowInNode(*group.focused_child, result);
|
||||
} else {
|
||||
for (auto* child: group.children) {
|
||||
findTopWindowInNode(*child, result);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void Hy3Node::updateTabBar(bool no_animation) {
|
||||
if (this->data.type == Hy3NodeType::Group) {
|
||||
auto& group = this->data.as_group;
|
||||
|
||||
if (group.layout == Hy3GroupLayout::Tabbed) {
|
||||
if (group.tab_bar == nullptr) group.tab_bar = &this->layout->tab_groups.emplace_back(*this);
|
||||
group.tab_bar->updateWithGroup(*this, no_animation);
|
||||
|
||||
FindTopWindowInNodeResult result;
|
||||
findTopWindowInNode(*this, result);
|
||||
group.tab_bar->target_window = result.window;
|
||||
if (result.window != nullptr) group.tab_bar->workspace_id = result.window->m_iWorkspaceID;
|
||||
} else if (group.tab_bar != nullptr) {
|
||||
group.tab_bar->bar.beginDestroy();
|
||||
group.tab_bar = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hy3Node::updateTabBarRecursive() {
|
||||
auto* node = this;
|
||||
|
||||
do {
|
||||
node->updateTabBar();
|
||||
node = node->parent;
|
||||
} while (node != nullptr);
|
||||
}
|
||||
|
||||
void Hy3Node::updateDecos() {
|
||||
switch (this->data.type) {
|
||||
case Hy3NodeType::Window:
|
||||
if (this->data.as_window->m_bIsMapped)
|
||||
g_pCompositor->updateWindowAnimatedDecorationValues(this->data.as_window);
|
||||
break;
|
||||
case Hy3NodeType::Group:
|
||||
for (auto* child: this->data.as_group.children) {
|
||||
child->updateDecos();
|
||||
}
|
||||
|
||||
this->updateTabBar();
|
||||
}
|
||||
}
|
||||
|
||||
std::string Hy3Node::getTitle() {
|
||||
switch (this->data.type) {
|
||||
case Hy3NodeType::Window: return this->data.as_window->m_szTitle;
|
||||
case Hy3NodeType::Group:
|
||||
std::string title;
|
||||
|
||||
switch (this->data.as_group.layout) {
|
||||
case Hy3GroupLayout::SplitH: title = "[H] "; break;
|
||||
case Hy3GroupLayout::SplitV: title = "[V] "; break;
|
||||
case Hy3GroupLayout::Tabbed: title = "[T] "; break;
|
||||
}
|
||||
|
||||
if (this->data.as_group.focused_child == nullptr) {
|
||||
title += "Group";
|
||||
} else {
|
||||
title += this->data.as_group.focused_child->getTitle();
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool Hy3Node::isUrgent() {
|
||||
switch (this->data.type) {
|
||||
case Hy3NodeType::Window: return this->data.as_window->m_bIsUrgent;
|
||||
case Hy3NodeType::Group:
|
||||
for (auto* child: this->data.as_group.children) {
|
||||
if (child->isUrgent()) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Hy3Node::setHidden(bool hidden) {
|
||||
this->hidden = hidden;
|
||||
|
||||
if (this->data.type == Hy3NodeType::Group) {
|
||||
for (auto* child: this->data.as_group.children) {
|
||||
child->setHidden(hidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Hy3Node* Hy3Node::findNodeForTabGroup(Hy3TabGroup& tab_group) {
|
||||
if (this->data.type == Hy3NodeType::Group) {
|
||||
if (this->hidden) return nullptr;
|
||||
|
||||
auto& group = this->data.as_group;
|
||||
|
||||
if (group.layout == Hy3GroupLayout::Tabbed && group.tab_bar == &tab_group) {
|
||||
return this;
|
||||
}
|
||||
|
||||
for (auto& node: group.children) {
|
||||
auto* r = node->findNodeForTabGroup(tab_group);
|
||||
if (r != nullptr) return r;
|
||||
}
|
||||
} else return nullptr;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Hy3Node::appendAllWindows(std::vector<CWindow*>& list) {
|
||||
switch (this->data.type) {
|
||||
case Hy3NodeType::Window: list.push_back(this->data.as_window); break;
|
||||
case Hy3NodeType::Group:
|
||||
for (auto* child: this->data.as_group.children) {
|
||||
child->appendAllWindows(list);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Hy3Node::debugNode() {
|
||||
std::stringstream buf;
|
||||
std::string addr = "0x" + std::to_string((size_t) this);
|
||||
switch (this->data.type) {
|
||||
case Hy3NodeType::Window:
|
||||
buf << "window(";
|
||||
buf << std::hex << this;
|
||||
buf << ") [hypr ";
|
||||
buf << this->data.as_window;
|
||||
buf << "] size ratio: ";
|
||||
buf << this->size_ratio;
|
||||
break;
|
||||
case Hy3NodeType::Group:
|
||||
buf << "group(";
|
||||
buf << std::hex << this;
|
||||
buf << ") [";
|
||||
|
||||
switch (this->data.as_group.layout) {
|
||||
case Hy3GroupLayout::SplitH: buf << "splith"; break;
|
||||
case Hy3GroupLayout::SplitV: buf << "splitv"; break;
|
||||
case Hy3GroupLayout::Tabbed: buf << "tabs"; break;
|
||||
}
|
||||
|
||||
buf << "] size ratio: ";
|
||||
buf << this->size_ratio;
|
||||
for (auto* child: this->data.as_group.children) {
|
||||
buf << "\n|-";
|
||||
if (child == nullptr) {
|
||||
buf << "nullptr";
|
||||
} else {
|
||||
// this is terrible
|
||||
for (char c: child->debugNode()) {
|
||||
buf << c;
|
||||
if (c == '\n') buf << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
Hy3Node* Hy3Node::removeFromParentRecursive() {
|
||||
Hy3Node* parent = this;
|
||||
|
||||
Debug::log(LOG, "Recursively removing parent nodes of %p", parent);
|
||||
|
||||
while (parent != nullptr) {
|
||||
if (parent->parent == nullptr) {
|
||||
Debug::log(ERR, "* UAF DEBUGGING - %p's parent is null, its the root group", parent);
|
||||
|
||||
if (parent == this) {
|
||||
Debug::log(ERR, "* UAF DEBUGGING - returning nullptr as this == root group");
|
||||
} else {
|
||||
Debug::log(ERR, "* UAF DEBUGGING - deallocing %p and returning nullptr", parent);
|
||||
parent->layout->nodes.remove(*parent);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* child = parent;
|
||||
parent = parent->parent;
|
||||
auto& group = parent->data.as_group;
|
||||
|
||||
if (group.children.size() > 2) {
|
||||
auto iter = std::find(group.children.begin(), group.children.end(), child);
|
||||
|
||||
group.group_focused = false;
|
||||
if (iter == group.children.begin()) {
|
||||
group.focused_child = *std::next(iter);
|
||||
} else {
|
||||
group.focused_child = *std::prev(iter);
|
||||
}
|
||||
}
|
||||
|
||||
if (!group.children.remove(child)) {
|
||||
Debug::log(
|
||||
ERR,
|
||||
"Was unable to remove child node %p from parent %p. Child likely has "
|
||||
"a false parent pointer.",
|
||||
child,
|
||||
parent
|
||||
);
|
||||
|
||||
errorNotif();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
group.group_focused = false;
|
||||
if (group.children.size() == 1) {
|
||||
group.focused_child = group.children.front();
|
||||
}
|
||||
|
||||
auto child_size_ratio = child->size_ratio;
|
||||
if (child != this) {
|
||||
parent->layout->nodes.remove(*child);
|
||||
} else {
|
||||
child->parent = nullptr;
|
||||
}
|
||||
|
||||
if (!group.children.empty()) {
|
||||
auto child_count = group.children.size();
|
||||
if (std::find(group.children.begin(), group.children.end(), this) != group.children.end()) {
|
||||
child_count -= 1;
|
||||
}
|
||||
|
||||
auto splitmod = -((1.0 - child_size_ratio) / child_count);
|
||||
|
||||
for (auto* child: group.children) {
|
||||
child->size_ratio += splitmod;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
Hy3Node* Hy3Node::intoGroup(Hy3GroupLayout layout) {
|
||||
this->layout->nodes.push_back({
|
||||
.parent = this,
|
||||
.data = layout,
|
||||
.workspace_id = this->workspace_id,
|
||||
.layout = this->layout,
|
||||
});
|
||||
|
||||
auto* node = &this->layout->nodes.back();
|
||||
swapData(*this, *node);
|
||||
|
||||
this->data = layout;
|
||||
this->data.as_group.children.push_back(node);
|
||||
this->data.as_group.group_focused = false;
|
||||
this->data.as_group.focused_child = node;
|
||||
this->recalcSizePosRecursive();
|
||||
this->updateTabBarRecursive();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
bool Hy3Node::swallowGroups(Hy3Node* into) {
|
||||
if (into == nullptr || into->data.type != Hy3NodeType::Group
|
||||
|| into->data.as_group.children.size() != 1)
|
||||
return false;
|
||||
|
||||
auto* child = into->data.as_group.children.front();
|
||||
|
||||
// a lot of segfaulting happens once the assumption that the root node is a
|
||||
// group is wrong.
|
||||
if (into->parent == nullptr && child->data.type != Hy3NodeType::Group) return false;
|
||||
|
||||
Debug::log(LOG, "Swallowing %p into %p", child, into);
|
||||
Hy3Node::swapData(*into, *child);
|
||||
into->layout->nodes.remove(*child);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Hy3Node::swapData(Hy3Node& a, Hy3Node& b) {
|
||||
Hy3NodeData aData = std::move(a.data);
|
||||
a.data = std::move(b.data);
|
||||
b.data = std::move(aData);
|
||||
|
||||
if (a.data.type == Hy3NodeType::Group) {
|
||||
for (auto child: a.data.as_group.children) {
|
||||
child->parent = &a;
|
||||
}
|
||||
}
|
||||
|
||||
if (b.data.type == Hy3NodeType::Group) {
|
||||
for (auto child: b.data.as_group.children) {
|
||||
child->parent = &b;
|
||||
}
|
||||
}
|
||||
}
|
112
src/Hy3Node.hpp
Normal file
112
src/Hy3Node.hpp
Normal file
|
@ -0,0 +1,112 @@
|
|||
#pragma once
|
||||
|
||||
struct Hy3Node;
|
||||
enum class Hy3GroupLayout;
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <hyprland/src/Window.hpp>
|
||||
|
||||
#include "Hy3Layout.hpp"
|
||||
#include "TabGroup.hpp"
|
||||
|
||||
enum class Hy3GroupLayout {
|
||||
SplitH,
|
||||
SplitV,
|
||||
Tabbed,
|
||||
};
|
||||
|
||||
enum class Hy3NodeType {
|
||||
Window,
|
||||
Group,
|
||||
};
|
||||
|
||||
struct Hy3GroupData {
|
||||
Hy3GroupLayout layout = Hy3GroupLayout::SplitH;
|
||||
std::list<Hy3Node*> children;
|
||||
bool group_focused = true;
|
||||
Hy3Node* focused_child = nullptr;
|
||||
Hy3TabGroup* tab_bar = nullptr;
|
||||
|
||||
Hy3GroupData(Hy3GroupLayout layout);
|
||||
~Hy3GroupData();
|
||||
|
||||
bool hasChild(Hy3Node* child);
|
||||
|
||||
private:
|
||||
Hy3GroupData(Hy3GroupData&&);
|
||||
Hy3GroupData(const Hy3GroupData&) = delete;
|
||||
|
||||
friend class Hy3NodeData;
|
||||
};
|
||||
|
||||
class Hy3NodeData {
|
||||
public:
|
||||
Hy3NodeType type;
|
||||
union {
|
||||
Hy3GroupData as_group;
|
||||
CWindow* as_window;
|
||||
};
|
||||
|
||||
Hy3NodeData();
|
||||
Hy3NodeData(CWindow* window);
|
||||
Hy3NodeData(Hy3GroupLayout layout);
|
||||
~Hy3NodeData();
|
||||
|
||||
Hy3NodeData& operator=(CWindow*);
|
||||
Hy3NodeData& operator=(Hy3GroupLayout);
|
||||
|
||||
bool operator==(const Hy3NodeData&) const;
|
||||
|
||||
// private: - I give up, C++ wins
|
||||
Hy3NodeData(Hy3GroupData);
|
||||
Hy3NodeData(Hy3NodeData&&);
|
||||
Hy3NodeData& operator=(Hy3NodeData&&);
|
||||
};
|
||||
|
||||
struct Hy3Node {
|
||||
Hy3Node* parent = nullptr;
|
||||
Hy3NodeData data;
|
||||
Vector2D position;
|
||||
Vector2D size;
|
||||
Vector2D gap_pos_offset;
|
||||
Vector2D gap_size_offset;
|
||||
float size_ratio = 1.0;
|
||||
int workspace_id = -1;
|
||||
bool hidden = false;
|
||||
Hy3Layout* layout = nullptr;
|
||||
|
||||
bool operator==(const Hy3Node&) const;
|
||||
|
||||
void focus();
|
||||
bool focusWindow();
|
||||
void markFocused();
|
||||
void raiseToTop();
|
||||
Hy3Node* getFocusedNode();
|
||||
bool isIndirectlyFocused();
|
||||
|
||||
void recalcSizePosRecursive(bool no_animation = false);
|
||||
void updateTabBar(bool no_animation = false);
|
||||
void updateTabBarRecursive();
|
||||
void updateDecos();
|
||||
|
||||
std::string getTitle();
|
||||
bool isUrgent();
|
||||
void setHidden(bool);
|
||||
|
||||
Hy3Node* findNodeForTabGroup(Hy3TabGroup&);
|
||||
void appendAllWindows(std::vector<CWindow*>&);
|
||||
std::string debugNode();
|
||||
|
||||
// Remove this node from its parent, deleting the parent if it was
|
||||
// the only child and recursing if the parent was the only child of it's
|
||||
// parent.
|
||||
Hy3Node* removeFromParentRecursive();
|
||||
|
||||
// Replace this node with a group, returning this node's new address.
|
||||
Hy3Node* intoGroup(Hy3GroupLayout);
|
||||
|
||||
// Attempt to swallow a group. returns true if swallowed
|
||||
static bool swallowGroups(Hy3Node* into);
|
||||
static void swapData(Hy3Node&, Hy3Node&);
|
||||
};
|
|
@ -1,7 +1,8 @@
|
|||
#include "globals.hpp"
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/plugins/PluginAPI.hpp>
|
||||
|
||||
#include "globals.hpp"
|
||||
|
||||
namespace selection_hook {
|
||||
inline CFunctionHook* g_LastSelectionHook = nullptr;
|
||||
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
#include "TabGroup.hpp"
|
||||
#include "globals.hpp"
|
||||
#include "Hy3Layout.hpp"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/helpers/Color.hpp>
|
||||
|
@ -10,6 +6,10 @@
|
|||
#include <pango/pangocairo.h>
|
||||
#include <pixman.h>
|
||||
|
||||
#include "globals.hpp"
|
||||
#include "Hy3Layout.hpp"
|
||||
#include "TabGroup.hpp"
|
||||
|
||||
Hy3TabBarEntry::Hy3TabBarEntry(Hy3TabBar& tab_bar, Hy3Node& node): tab_bar(tab_bar), node(node) {
|
||||
this->focused.create(
|
||||
AVARTYPE_FLOAT,
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class Hy3TabGroup;
|
||||
class Hy3TabBar;
|
||||
|
||||
#include "Hy3Layout.hpp"
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <hyprland/src/render/Texture.hpp>
|
||||
|
||||
#include "Hy3Node.hpp"
|
||||
|
||||
struct Hy3TabBarEntry {
|
||||
std::string window_title;
|
||||
|
|
147
src/dispatchers.cpp
Normal file
147
src/dispatchers.cpp
Normal file
|
@ -0,0 +1,147 @@
|
|||
#include <optional>
|
||||
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/plugins/PluginAPI.hpp>
|
||||
|
||||
#include "dispatchers.hpp"
|
||||
#include "globals.hpp"
|
||||
|
||||
int workspace_for_action() {
|
||||
if (g_pLayoutManager->getCurrentLayout() != g_Hy3Layout.get()) return -1;
|
||||
|
||||
int workspace_id = g_pCompositor->m_pLastMonitor->activeWorkspace;
|
||||
|
||||
if (workspace_id == -1) return -1;
|
||||
auto* workspace = g_pCompositor->getWorkspaceByID(workspace_id);
|
||||
if (workspace == nullptr) return -1;
|
||||
if (workspace->m_bHasFullscreenWindow) return -1;
|
||||
|
||||
return workspace_id;
|
||||
}
|
||||
|
||||
void dispatch_makegroup(std::string arg) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
if (arg == "h") {
|
||||
g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::SplitH);
|
||||
} else if (arg == "v") {
|
||||
g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::SplitV);
|
||||
} else if (arg == "tab") {
|
||||
g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::Tabbed);
|
||||
} else if (arg == "opposite") {
|
||||
g_Hy3Layout->makeOppositeGroupOnWorkspace(workspace);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<ShiftDirection> parseShiftArg(std::string arg) {
|
||||
if (arg == "l" || arg == "left") return ShiftDirection::Left;
|
||||
else if (arg == "r" || arg == "right") return ShiftDirection::Right;
|
||||
else if (arg == "u" || arg == "up") return ShiftDirection::Up;
|
||||
else if (arg == "d" || arg == "down") return ShiftDirection::Down;
|
||||
else return {};
|
||||
}
|
||||
|
||||
void dispatch_movewindow(std::string value) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
auto args = CVarList(value);
|
||||
|
||||
if (auto shift = parseShiftArg(args[0])) {
|
||||
auto once = args[1] == "once";
|
||||
g_Hy3Layout->shiftWindow(workspace, shift.value(), once);
|
||||
}
|
||||
}
|
||||
|
||||
void dispatch_movefocus(std::string value) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
auto args = CVarList(value);
|
||||
|
||||
if (auto shift = parseShiftArg(args[0])) {
|
||||
g_Hy3Layout->shiftFocus(workspace, shift.value(), args[1] == "visible");
|
||||
}
|
||||
}
|
||||
|
||||
void dispatch_changefocus(std::string arg) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
if (arg == "top") g_Hy3Layout->changeFocus(workspace, FocusShift::Top);
|
||||
else if (arg == "bottom") g_Hy3Layout->changeFocus(workspace, FocusShift::Bottom);
|
||||
else if (arg == "raise") g_Hy3Layout->changeFocus(workspace, FocusShift::Raise);
|
||||
else if (arg == "lower") g_Hy3Layout->changeFocus(workspace, FocusShift::Lower);
|
||||
else if (arg == "tab") g_Hy3Layout->changeFocus(workspace, FocusShift::Tab);
|
||||
else if (arg == "tabnode") g_Hy3Layout->changeFocus(workspace, FocusShift::TabNode);
|
||||
}
|
||||
|
||||
void dispatch_focustab(std::string value) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
auto i = 0;
|
||||
auto args = CVarList(value);
|
||||
|
||||
TabFocus focus;
|
||||
auto mouse = TabFocusMousePriority::Ignore;
|
||||
bool wrap_scroll = false;
|
||||
int index = 0;
|
||||
|
||||
if (args[i] == "l" || args[i] == "left") focus = TabFocus::Left;
|
||||
else if (args[i] == "r" || args[i] == "right") focus = TabFocus::Right;
|
||||
else if (args[i] == "index") {
|
||||
i++;
|
||||
focus = TabFocus::Index;
|
||||
if (!isNumber(args[i])) return;
|
||||
index = std::stoi(args[i]);
|
||||
Debug::log(LOG, "Focus index '%s' -> %d, errno: %d", args[i].c_str(), index, errno);
|
||||
} else if (args[i] == "mouse") {
|
||||
g_Hy3Layout->focusTab(workspace, TabFocus::MouseLocation, mouse, false, 0);
|
||||
return;
|
||||
} else return;
|
||||
|
||||
i++;
|
||||
|
||||
if (args[i] == "prioritize_hovered") {
|
||||
mouse = TabFocusMousePriority::Prioritize;
|
||||
i++;
|
||||
} else if (args[i] == "require_hovered") {
|
||||
mouse = TabFocusMousePriority::Require;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (args[i++] == "wrap") wrap_scroll = true;
|
||||
|
||||
g_Hy3Layout->focusTab(workspace, focus, mouse, wrap_scroll, index);
|
||||
}
|
||||
|
||||
void dispatch_killactive(std::string value) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
g_Hy3Layout->killFocusedNode(workspace);
|
||||
}
|
||||
|
||||
void dispatch_debug(std::string arg) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
auto* root = g_Hy3Layout->getWorkspaceRootGroup(workspace);
|
||||
if (workspace == -1) {
|
||||
Debug::log(LOG, "DEBUG NODES: no nodes on workspace");
|
||||
} else {
|
||||
Debug::log(LOG, "DEBUG NODES\n%s", root->debugNode().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void registerDispatchers() {
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:makegroup", dispatch_makegroup);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:movefocus", dispatch_movefocus);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:movewindow", dispatch_movewindow);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:changefocus", dispatch_changefocus);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:focustab", dispatch_focustab);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:killactive", dispatch_killactive);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:debugnodes", dispatch_debug);
|
||||
}
|
3
src/dispatchers.hpp
Normal file
3
src/dispatchers.hpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
void registerDispatchers();
|
|
@ -3,3 +3,15 @@
|
|||
|
||||
inline HANDLE PHANDLE = nullptr;
|
||||
inline std::unique_ptr<Hy3Layout> g_Hy3Layout;
|
||||
|
||||
inline void errorNotif() {
|
||||
HyprlandAPI::addNotificationV2(
|
||||
PHANDLE,
|
||||
{
|
||||
{"text", "Something has gone very wrong. Check the log for details."},
|
||||
{"time", (uint64_t) 10000},
|
||||
{"color", CColor(1.0, 0.0, 0.0, 1.0)},
|
||||
{"icon", ICON_ERROR},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
153
src/main.cpp
153
src/main.cpp
|
@ -3,155 +3,12 @@
|
|||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/plugins/PluginAPI.hpp>
|
||||
|
||||
#include "dispatchers.hpp"
|
||||
#include "globals.hpp"
|
||||
#include "SelectionHook.hpp"
|
||||
|
||||
APICALL EXPORT std::string PLUGIN_API_VERSION() { return HYPRLAND_API_VERSION; }
|
||||
|
||||
// return a window if a window action makes sense now
|
||||
CWindow* window_for_action() {
|
||||
if (g_pLayoutManager->getCurrentLayout() != g_Hy3Layout.get()) return nullptr;
|
||||
|
||||
auto* window = g_pCompositor->m_pLastWindow;
|
||||
|
||||
if (!window) return nullptr;
|
||||
|
||||
const auto workspace = g_pCompositor->getWorkspaceByID(window->m_iWorkspaceID);
|
||||
if (workspace->m_bHasFullscreenWindow) return nullptr;
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
int workspace_for_action() {
|
||||
if (g_pLayoutManager->getCurrentLayout() != g_Hy3Layout.get()) return -1;
|
||||
|
||||
int workspace_id = g_pCompositor->m_pLastMonitor->activeWorkspace;
|
||||
|
||||
if (workspace_id == -1) return -1;
|
||||
auto* workspace = g_pCompositor->getWorkspaceByID(workspace_id);
|
||||
if (workspace == nullptr) return -1;
|
||||
if (workspace->m_bHasFullscreenWindow) return -1;
|
||||
|
||||
return workspace_id;
|
||||
}
|
||||
|
||||
void dispatch_makegroup(std::string arg) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
if (arg == "h") {
|
||||
g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::SplitH);
|
||||
} else if (arg == "v") {
|
||||
g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::SplitV);
|
||||
} else if (arg == "tab") {
|
||||
g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::Tabbed);
|
||||
} else if (arg == "opposite") {
|
||||
g_Hy3Layout->makeOppositeGroupOnWorkspace(workspace);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<ShiftDirection> parseShiftArg(std::string arg) {
|
||||
if (arg == "l" || arg == "left") return ShiftDirection::Left;
|
||||
else if (arg == "r" || arg == "right") return ShiftDirection::Right;
|
||||
else if (arg == "u" || arg == "up") return ShiftDirection::Up;
|
||||
else if (arg == "d" || arg == "down") return ShiftDirection::Down;
|
||||
else return {};
|
||||
}
|
||||
|
||||
void dispatch_movewindow(std::string value) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
auto args = CVarList(value);
|
||||
|
||||
if (auto shift = parseShiftArg(args[0])) {
|
||||
auto once = args[1] == "once";
|
||||
g_Hy3Layout->shiftWindow(workspace, shift.value(), once);
|
||||
}
|
||||
}
|
||||
|
||||
void dispatch_movefocus(std::string value) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
auto args = CVarList(value);
|
||||
|
||||
if (auto shift = parseShiftArg(args[0])) {
|
||||
g_Hy3Layout->shiftFocus(workspace, shift.value(), args[1] == "visible");
|
||||
}
|
||||
}
|
||||
|
||||
void dispatch_changefocus(std::string arg) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
if (arg == "top") g_Hy3Layout->changeFocus(workspace, FocusShift::Top);
|
||||
else if (arg == "bottom") g_Hy3Layout->changeFocus(workspace, FocusShift::Bottom);
|
||||
else if (arg == "raise") g_Hy3Layout->changeFocus(workspace, FocusShift::Raise);
|
||||
else if (arg == "lower") g_Hy3Layout->changeFocus(workspace, FocusShift::Lower);
|
||||
else if (arg == "tab") g_Hy3Layout->changeFocus(workspace, FocusShift::Tab);
|
||||
else if (arg == "tabnode") g_Hy3Layout->changeFocus(workspace, FocusShift::TabNode);
|
||||
}
|
||||
|
||||
void dispatch_focustab(std::string value) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
auto i = 0;
|
||||
auto args = CVarList(value);
|
||||
|
||||
TabFocus focus;
|
||||
auto mouse = TabFocusMousePriority::Ignore;
|
||||
bool wrap_scroll = false;
|
||||
int index = 0;
|
||||
|
||||
if (args[i] == "l" || args[i] == "left") focus = TabFocus::Left;
|
||||
else if (args[i] == "r" || args[i] == "right") focus = TabFocus::Right;
|
||||
else if (args[i] == "index") {
|
||||
i++;
|
||||
focus = TabFocus::Index;
|
||||
if (!isNumber(args[i])) return;
|
||||
index = std::stoi(args[i]);
|
||||
Debug::log(LOG, "Focus index '%s' -> %d, errno: %d", args[i].c_str(), index, errno);
|
||||
} else if (args[i] == "mouse") {
|
||||
g_Hy3Layout->focusTab(workspace, TabFocus::MouseLocation, mouse, false, 0);
|
||||
return;
|
||||
} else return;
|
||||
|
||||
i++;
|
||||
|
||||
if (args[i] == "prioritize_hovered") {
|
||||
mouse = TabFocusMousePriority::Prioritize;
|
||||
i++;
|
||||
} else if (args[i] == "require_hovered") {
|
||||
mouse = TabFocusMousePriority::Require;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (args[i++] == "wrap") wrap_scroll = true;
|
||||
|
||||
g_Hy3Layout->focusTab(workspace, focus, mouse, wrap_scroll, index);
|
||||
}
|
||||
|
||||
void dispatch_killactive(std::string value) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
g_Hy3Layout->killFocusedNode(workspace);
|
||||
}
|
||||
|
||||
void dispatch_debug(std::string arg) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
||||
auto* root = g_Hy3Layout->getWorkspaceRootGroup(workspace);
|
||||
if (workspace == -1) {
|
||||
Debug::log(LOG, "DEBUG NODES: no nodes on workspace");
|
||||
} else {
|
||||
Debug::log(LOG, "DEBUG NODES\n%s", root->debugNode().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||
PHANDLE = handle;
|
||||
|
||||
|
@ -213,13 +70,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|||
g_Hy3Layout = std::make_unique<Hy3Layout>();
|
||||
HyprlandAPI::addLayout(PHANDLE, "hy3", g_Hy3Layout.get());
|
||||
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:makegroup", dispatch_makegroup);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:movefocus", dispatch_movefocus);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:movewindow", dispatch_movewindow);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:changefocus", dispatch_changefocus);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:focustab", dispatch_focustab);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:killactive", dispatch_killactive);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:debugnodes", dispatch_debug);
|
||||
registerDispatchers();
|
||||
|
||||
return {"hy3", "i3 like layout for hyprland", "outfoxxed", "0.1"};
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue