mirror of
https://github.com/Trensa-Organization/hy3.git
synced 2025-03-15 18:53: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
|
add_library(hy3 SHARED
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
|
src/dispatchers.cpp
|
||||||
src/Hy3Layout.cpp
|
src/Hy3Layout.cpp
|
||||||
|
src/Hy3Node.cpp
|
||||||
src/TabGroup.cpp
|
src/TabGroup.cpp
|
||||||
src/SelectionHook.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
|
#pragma once
|
||||||
|
|
||||||
#include <hyprland/src/layout/IHyprLayout.hpp>
|
class Hy3Layout;
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
struct Hy3Node;
|
#include <hyprland/src/layout/IHyprLayout.hpp>
|
||||||
|
|
||||||
|
#include "Hy3Node.hpp"
|
||||||
#include "TabGroup.hpp"
|
#include "TabGroup.hpp"
|
||||||
|
|
||||||
class Hy3Layout;
|
|
||||||
struct Hy3Node;
|
|
||||||
|
|
||||||
enum class Hy3GroupLayout {
|
|
||||||
SplitH,
|
|
||||||
SplitV,
|
|
||||||
Tabbed,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Hy3NodeType {
|
|
||||||
Window,
|
|
||||||
Group,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ShiftDirection {
|
enum class ShiftDirection {
|
||||||
Left,
|
Left,
|
||||||
Up,
|
Up,
|
||||||
|
@ -49,93 +38,6 @@ enum class TabFocusMousePriority {
|
||||||
Require,
|
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 {
|
class Hy3Layout: public IHyprLayout {
|
||||||
public:
|
public:
|
||||||
virtual void onWindowCreatedTiling(CWindow*);
|
virtual void onWindowCreatedTiling(CWindow*);
|
||||||
|
@ -188,7 +90,6 @@ private:
|
||||||
bool yExtent = false;
|
bool yExtent = false;
|
||||||
} drag_flags;
|
} drag_flags;
|
||||||
|
|
||||||
int getWorkspaceNodeCount(const int& workspace);
|
|
||||||
Hy3Node* getNodeFromWindow(CWindow*);
|
Hy3Node* getNodeFromWindow(CWindow*);
|
||||||
void applyNodeDataToWindow(Hy3Node*, bool no_animation = false);
|
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/Compositor.hpp>
|
||||||
#include <hyprland/src/plugins/PluginAPI.hpp>
|
#include <hyprland/src/plugins/PluginAPI.hpp>
|
||||||
|
|
||||||
|
#include "globals.hpp"
|
||||||
|
|
||||||
namespace selection_hook {
|
namespace selection_hook {
|
||||||
inline CFunctionHook* g_LastSelectionHook = nullptr;
|
inline CFunctionHook* g_LastSelectionHook = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
#include "TabGroup.hpp"
|
|
||||||
#include "globals.hpp"
|
|
||||||
#include "Hy3Layout.hpp"
|
|
||||||
|
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
#include <hyprland/src/Compositor.hpp>
|
#include <hyprland/src/Compositor.hpp>
|
||||||
#include <hyprland/src/helpers/Color.hpp>
|
#include <hyprland/src/helpers/Color.hpp>
|
||||||
|
@ -10,6 +6,10 @@
|
||||||
#include <pango/pangocairo.h>
|
#include <pango/pangocairo.h>
|
||||||
#include <pixman.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) {
|
Hy3TabBarEntry::Hy3TabBarEntry(Hy3TabBar& tab_bar, Hy3Node& node): tab_bar(tab_bar), node(node) {
|
||||||
this->focused.create(
|
this->focused.create(
|
||||||
AVARTYPE_FLOAT,
|
AVARTYPE_FLOAT,
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <hyprland/src/Compositor.hpp>
|
|
||||||
#include <list>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class Hy3TabGroup;
|
class Hy3TabGroup;
|
||||||
class Hy3TabBar;
|
class Hy3TabBar;
|
||||||
|
|
||||||
#include "Hy3Layout.hpp"
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <hyprland/src/render/Texture.hpp>
|
||||||
|
|
||||||
|
#include "Hy3Node.hpp"
|
||||||
|
|
||||||
struct Hy3TabBarEntry {
|
struct Hy3TabBarEntry {
|
||||||
std::string window_title;
|
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 HANDLE PHANDLE = nullptr;
|
||||||
inline std::unique_ptr<Hy3Layout> g_Hy3Layout;
|
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/Compositor.hpp>
|
||||||
#include <hyprland/src/plugins/PluginAPI.hpp>
|
#include <hyprland/src/plugins/PluginAPI.hpp>
|
||||||
|
|
||||||
|
#include "dispatchers.hpp"
|
||||||
#include "globals.hpp"
|
#include "globals.hpp"
|
||||||
#include "SelectionHook.hpp"
|
#include "SelectionHook.hpp"
|
||||||
|
|
||||||
APICALL EXPORT std::string PLUGIN_API_VERSION() { return HYPRLAND_API_VERSION; }
|
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) {
|
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||||
PHANDLE = handle;
|
PHANDLE = handle;
|
||||||
|
|
||||||
|
@ -213,13 +70,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||||
g_Hy3Layout = std::make_unique<Hy3Layout>();
|
g_Hy3Layout = std::make_unique<Hy3Layout>();
|
||||||
HyprlandAPI::addLayout(PHANDLE, "hy3", g_Hy3Layout.get());
|
HyprlandAPI::addLayout(PHANDLE, "hy3", g_Hy3Layout.get());
|
||||||
|
|
||||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:makegroup", dispatch_makegroup);
|
registerDispatchers();
|
||||||
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);
|
|
||||||
|
|
||||||
return {"hy3", "i3 like layout for hyprland", "outfoxxed", "0.1"};
|
return {"hy3", "i3 like layout for hyprland", "outfoxxed", "0.1"};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue