Initial work on expand/lens nodes

This commit is contained in:
outfoxxed 2023-08-09 02:21:18 -07:00
parent 3cdb3d03d8
commit 2fa77b4dbc
No known key found for this signature in database
GPG key ID: 4C88A185FB89301E
6 changed files with 249 additions and 18 deletions

View file

@ -238,5 +238,9 @@ plugin {
- `prioritize_hovered` - prioritize the tab group under the mouse when multiple are stacked. use the lowest group if none is under the mouse. - `prioritize_hovered` - prioritize the tab group under the mouse when multiple are stacked. use the lowest group if none is under the mouse.
- `require_hovered` - affect the tab group under the mouse. do nothing if none are hovered. - `require_hovered` - affect the tab group under the mouse. do nothing if none are hovered.
- `wrap` - wrap to the opposite size of the tab bar if moving off the end - `wrap` - wrap to the opposite size of the tab bar if moving off the end
- `hy3:setswallow, <true | false | toggle>` - set the containing node's window swallow state
- `hy3:debugnodes` - print the node tree into the hyprland log - `hy3:debugnodes` - print the node tree into the hyprland log
- :warning: **ALPHA QUALITY** `hy3:setswallow, <true | false | toggle>` - set the containing node's window swallow state
- :warning: **ALPHA QUALITY** `hy3:expand, <expand | shrink | base>` - expand the current node to cover other nodes
- `expand` - expand by one node
- `shrink` - shrink by one node
- `base` - undo all expansions

View file

@ -1059,6 +1059,91 @@ void Hy3Layout::killFocusedNode(int workspace) {
} }
} }
void Hy3Layout::expand(int workspace_id, ExpandOption option, ExpandFullscreenOption fs_option) {
auto* node = this->getWorkspaceFocusedNode(workspace_id, false, true);
if (node == nullptr) return;
const auto workspace = g_pCompositor->getWorkspaceByID(workspace_id);
const auto monitor = g_pCompositor->getMonitorFromID(workspace->m_iMonitorID);
switch (option) {
case ExpandOption::Expand: {
if (node->parent == nullptr) {
switch (fs_option) {
case ExpandFullscreenOption::MaximizeAsFullscreen:
case ExpandFullscreenOption::MaximizeIntermediate: goto fullscreen;
case ExpandFullscreenOption::MaximizeOnly: return;
}
}
if (node->data.type == Hy3NodeType::Group)
node->data.as_group.expand_focused = ExpandFocusType::Stack;
auto& group = node->parent->data.as_group;
group.focused_child = node;
group.expand_focused = ExpandFocusType::Latch;
node->parent->recalcSizePosRecursive();
if (node->parent->parent == nullptr) {
switch (fs_option) {
case ExpandFullscreenOption::MaximizeAsFullscreen: goto fullscreen;
case ExpandFullscreenOption::MaximizeIntermediate:
case ExpandFullscreenOption::MaximizeOnly: return;
}
}
} break;
case ExpandOption::Shrink:
if (node->data.type == Hy3NodeType::Group) {
auto& group = node->data.as_group;
group.expand_focused = ExpandFocusType::NotExpanded;
if (group.focused_child->data.type == Hy3NodeType::Group)
group.focused_child->data.as_group.expand_focused = ExpandFocusType::Latch;
node->recalcSizePosRecursive();
}
break;
case ExpandOption::Base: {
if (node->data.type == Hy3NodeType::Group) {
node->data.as_group.collapseExpansions();
node->recalcSizePosRecursive();
}
break;
}
case ExpandOption::Maximize: break;
case ExpandOption::Fullscreen: break;
}
return;
CWindow* window;
fullscreen:
if (node->data.type != Hy3NodeType::Window) return;
window = node->data.as_window;
if (!window->m_bIsFullscreen || g_pCompositor->isWorkspaceSpecial(window->m_iWorkspaceID)) return;
if (workspace->m_bHasFullscreenWindow) return;
window->m_bIsFullscreen = true;
workspace->m_bHasFullscreenWindow = true;
workspace->m_efFullscreenMode = FULLSCREEN_FULL;
window->m_vRealPosition = monitor->vecPosition;
window->m_vRealSize = monitor->vecSize;
goto fsupdate;
unfullscreen:
if (node->data.type != Hy3NodeType::Window) return;
window = node->data.as_window;
window->m_bIsFullscreen = false;
workspace->m_bHasFullscreenWindow = false;
goto fsupdate;
fsupdate:
g_pCompositor->updateWindowAnimatedDecorationValues(window);
g_pXWaylandManager->setWindowSize(window, window->m_vRealSize.goalv());
g_pCompositor->moveWindowToTop(window);
this->recalculateMonitor(monitor->ID);
}
bool Hy3Layout::shouldRenderSelected(CWindow* window) { bool Hy3Layout::shouldRenderSelected(CWindow* window) {
if (window == nullptr) return false; if (window == nullptr) return false;
auto* root = this->getWorkspaceRootGroup(window->m_iWorkspaceID); auto* root = this->getWorkspaceRootGroup(window->m_iWorkspaceID);
@ -1090,10 +1175,14 @@ Hy3Node* Hy3Layout::getWorkspaceRootGroup(const int& workspace) {
return nullptr; return nullptr;
} }
Hy3Node* Hy3Layout::getWorkspaceFocusedNode(const int& workspace, bool ignore_group_focus) { Hy3Node* Hy3Layout::getWorkspaceFocusedNode(
const int& workspace,
bool ignore_group_focus,
bool stop_at_expanded
) {
auto* rootNode = this->getWorkspaceRootGroup(workspace); auto* rootNode = this->getWorkspaceRootGroup(workspace);
if (rootNode == nullptr) return nullptr; if (rootNode == nullptr) return nullptr;
return rootNode->getFocusedNode(ignore_group_focus); return rootNode->getFocusedNode(ignore_group_focus, stop_at_expanded);
} }
void Hy3Layout::renderHook(void*, std::any data) { void Hy3Layout::renderHook(void*, std::any data) {
@ -1292,7 +1381,7 @@ Hy3Node* Hy3Layout::shiftOrGetFocus(
bool once, bool once,
bool visible bool visible
) { ) {
auto* break_origin = &node; auto* break_origin = &node.getExpandActor();
auto* break_parent = break_origin->parent; auto* break_parent = break_origin->parent;
auto has_broken_once = false; auto has_broken_once = false;
@ -1377,7 +1466,11 @@ Hy3Node* Hy3Layout::shiftOrGetFocus(
if (shiftIsForward(direction)) iter = std::next(iter); if (shiftIsForward(direction)) iter = std::next(iter);
else iter = std::prev(iter); else iter = std::prev(iter);
if ((*iter)->data.type == Hy3NodeType::Window || (shift && once && has_broken_once)) { if ((*iter)->data.type == Hy3NodeType::Window
|| ((*iter)->data.type == Hy3NodeType::Group
&& (*iter)->data.as_group.expand_focused != ExpandFocusType::NotExpanded)
|| (shift && once && has_broken_once))
{
if (shift) { if (shift) {
if (target_group == node.parent) { if (target_group == node.parent) {
if (shiftIsForward(direction)) insert = std::next(iter); if (shiftIsForward(direction)) insert = std::next(iter);
@ -1386,7 +1479,7 @@ Hy3Node* Hy3Layout::shiftOrGetFocus(
if (shiftIsForward(direction)) insert = iter; if (shiftIsForward(direction)) insert = iter;
else insert = std::next(iter); else insert = std::next(iter);
} }
} else return *iter; } else return (*iter)->getFocusedNode();
} else { } else {
// break into neighboring groups until we hit a window // break into neighboring groups until we hit a window
while (true) { while (true) {
@ -1432,13 +1525,16 @@ Hy3Node* Hy3Layout::shiftOrGetFocus(
break; break;
} }
if ((*iter)->data.type == Hy3NodeType::Window) { if ((*iter)->data.type == Hy3NodeType::Window
|| ((*iter)->data.type == Hy3NodeType::Group
&& (*iter)->data.as_group.expand_focused != ExpandFocusType::NotExpanded))
{
if (shift) { if (shift) {
if (shift_after) insert = std::next(iter); if (shift_after) insert = std::next(iter);
else insert = iter; else insert = iter;
break; break;
} else { } else {
return *iter; return (*iter)->getFocusedNode();
} }
} }
} }

View file

@ -50,6 +50,20 @@ enum class SetSwallowOption {
Toggle, Toggle,
}; };
enum class ExpandOption {
Expand,
Shrink,
Base,
Maximize,
Fullscreen,
};
enum class ExpandFullscreenOption {
MaximizeOnly,
MaximizeIntermediate,
MaximizeAsFullscreen,
};
class Hy3Layout: public IHyprLayout { class Hy3Layout: public IHyprLayout {
public: public:
virtual void onWindowCreated(CWindow*); virtual void onWindowCreated(CWindow*);
@ -84,11 +98,16 @@ public:
void focusTab(int workspace, TabFocus target, TabFocusMousePriority, bool wrap_scroll, int index); void focusTab(int workspace, TabFocus target, TabFocusMousePriority, bool wrap_scroll, int index);
void setNodeSwallow(int workspace, SetSwallowOption); void setNodeSwallow(int workspace, SetSwallowOption);
void killFocusedNode(int workspace); void killFocusedNode(int workspace);
void expand(int workspace, ExpandOption, ExpandFullscreenOption);
bool shouldRenderSelected(CWindow*); bool shouldRenderSelected(CWindow*);
Hy3Node* getWorkspaceRootGroup(const int& workspace); Hy3Node* getWorkspaceRootGroup(const int& workspace);
Hy3Node* getWorkspaceFocusedNode(const int& workspace, bool ignore_group_focus = false); Hy3Node* getWorkspaceFocusedNode(
const int& workspace,
bool ignore_group_focus = false,
bool stop_at_expanded = false
);
static void renderHook(void*, std::any); static void renderHook(void*, std::any);
static void windowGroupUrgentHook(void*, std::any); static void windowGroupUrgentHook(void*, std::any);

View file

@ -12,6 +12,7 @@ Hy3GroupData::Hy3GroupData(Hy3GroupData&& from) {
this->layout = from.layout; this->layout = from.layout;
this->children = std::move(from.children); this->children = std::move(from.children);
this->group_focused = from.group_focused; this->group_focused = from.group_focused;
this->expand_focused = from.expand_focused;
this->focused_child = from.focused_child; this->focused_child = from.focused_child;
from.focused_child = nullptr; from.focused_child = nullptr;
this->tab_bar = from.tab_bar; this->tab_bar = from.tab_bar;
@ -35,6 +36,20 @@ bool Hy3GroupData::hasChild(Hy3Node* node) {
return false; return false;
} }
void Hy3GroupData::collapseExpansions() {
if (this->expand_focused == ExpandFocusType::NotExpanded) return;
this->expand_focused = ExpandFocusType::NotExpanded;
Hy3Node* node = this->focused_child;
while (node->data.type == Hy3NodeType::Group
&& node->data.as_group.expand_focused == ExpandFocusType::Stack)
{
node->data.as_group.expand_focused = ExpandFocusType::NotExpanded;
node = node->data.as_group.focused_child;
}
}
// Hy3NodeData // // Hy3NodeData //
Hy3NodeData::Hy3NodeData(): Hy3NodeData((CWindow*) nullptr) {} Hy3NodeData::Hy3NodeData(): Hy3NodeData((CWindow*) nullptr) {}
@ -191,16 +206,29 @@ void Hy3Node::raiseToTop() {
} }
} }
Hy3Node* Hy3Node::getFocusedNode(bool ignore_group_focus) { Hy3Node* Hy3Node::getFocusedNode(bool ignore_group_focus, bool stop_at_expanded) {
switch (this->data.type) { switch (this->data.type) {
case Hy3NodeType::Window: return this; case Hy3NodeType::Window: return this;
case Hy3NodeType::Group: case Hy3NodeType::Group:
Debug::log(
LOG,
"focusing %p, gf: %d, ef: %d (stop: %d)",
this,
this->data.as_group.group_focused,
this->data.as_group.expand_focused,
stop_at_expanded
);
if (this->data.as_group.focused_child == nullptr if (this->data.as_group.focused_child == nullptr
|| (!ignore_group_focus && this->data.as_group.group_focused)) || (!ignore_group_focus && this->data.as_group.group_focused)
|| (stop_at_expanded && this->data.as_group.expand_focused != ExpandFocusType::NotExpanded))
{ {
return this; return this;
} else { } else {
return this->data.as_group.focused_child->getFocusedNode(ignore_group_focus); return this->data.as_group.focused_child->getFocusedNode(
ignore_group_focus,
stop_at_expanded
);
} }
} }
} }
@ -218,6 +246,17 @@ bool Hy3Node::isIndirectlyFocused() {
return true; return true;
} }
// note: assumes this node is the expanded one without checking
Hy3Node& Hy3Node::getExpandActor() {
Hy3Node* node = this;
while (node->parent != nullptr
&& node->parent->data.as_group.expand_focused != ExpandFocusType::NotExpanded)
node = node->parent;
return *node;
}
void Hy3Node::recalcSizePosRecursive(bool no_animation) { void Hy3Node::recalcSizePosRecursive(bool no_animation) {
// clang-format off // clang-format off
static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue; static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue;
@ -260,8 +299,15 @@ void Hy3Node::recalcSizePosRecursive(bool no_animation) {
case Hy3GroupLayout::Tabbed: break; case Hy3GroupLayout::Tabbed: break;
} }
auto expand_focused = group->expand_focused != ExpandFocusType::NotExpanded;
bool directly_contains_expanded
= expand_focused
&& (group->focused_child->data.type == Hy3NodeType::Window
|| group->focused_child->data.as_group.expand_focused == ExpandFocusType::NotExpanded);
auto child_count = group->children.size() - (directly_contains_expanded ? 1 : 0);
double ratio_mul = group->layout != Hy3GroupLayout::Tabbed double ratio_mul = group->layout != Hy3GroupLayout::Tabbed
? group->children.empty() ? 0 : constraint / group->children.size() ? child_count <= 0 ? 0 : constraint / child_count
: 0; : 0;
double offset = 0; double offset = 0;
@ -275,7 +321,27 @@ void Hy3Node::recalcSizePosRecursive(bool no_animation) {
g_pHyprRenderer->damageBox(&box); g_pHyprRenderer->damageBox(&box);
} }
// TODO: fix broken start positions when using hy3:expand, base
if (expand_focused) {
for (auto* child: group->children) {
if (child == group->focused_child) {
child->position = tpos;
child->size = tsize;
child->setHidden(hidden);
child->gap_pos_offset = gap_pos_offset;
child->gap_size_offset = gap_size_offset;
} else {
child->setHidden(true);
}
child->recalcSizePosRecursive(no_animation);
}
}
for (auto* child: group->children) { for (auto* child: group->children) {
if (expand_focused && child == group->focused_child) continue;
switch (group->layout) { switch (group->layout) {
case Hy3GroupLayout::SplitH: case Hy3GroupLayout::SplitH:
child->position.x = tpos.x + offset; child->position.x = tpos.x + offset;
@ -283,7 +349,7 @@ void Hy3Node::recalcSizePosRecursive(bool no_animation) {
offset += child->size.x; offset += child->size.x;
child->position.y = tpos.y; child->position.y = tpos.y;
child->size.y = tsize.y; child->size.y = tsize.y;
child->setHidden(this->hidden); child->setHidden(this->hidden || expand_focused);
if (group->children.size() == 1) { if (group->children.size() == 1) {
child->gap_pos_offset = gap_pos_offset; child->gap_pos_offset = gap_pos_offset;
@ -311,7 +377,7 @@ void Hy3Node::recalcSizePosRecursive(bool no_animation) {
offset += child->size.y; offset += child->size.y;
child->position.x = tpos.x; child->position.x = tpos.x;
child->size.x = tsize.x; child->size.x = tsize.x;
child->setHidden(this->hidden); child->setHidden(this->hidden || expand_focused);
if (group->children.size() == 1) { if (group->children.size() == 1) {
child->gap_pos_offset = gap_pos_offset; child->gap_pos_offset = gap_pos_offset;
@ -336,8 +402,7 @@ void Hy3Node::recalcSizePosRecursive(bool no_animation) {
case Hy3GroupLayout::Tabbed: case Hy3GroupLayout::Tabbed:
child->position = tpos; child->position = tpos;
child->size = tsize; child->size = tsize;
bool hidden = this->hidden || group->focused_child != child; child->setHidden(this->hidden || expand_focused || group->focused_child != child);
child->setHidden(hidden);
child->gap_pos_offset = Vector2D(gap_pos_offset.x, gap_pos_offset.y + tab_height_offset); child->gap_pos_offset = Vector2D(gap_pos_offset.x, gap_pos_offset.y + tab_height_offset);
child->gap_size_offset = Vector2D(gap_size_offset.x, gap_size_offset.y + tab_height_offset); child->gap_size_offset = Vector2D(gap_size_offset.x, gap_size_offset.y + tab_height_offset);
@ -528,6 +593,10 @@ std::string Hy3Node::debugNode() {
buf << "] size ratio: "; buf << "] size ratio: ";
buf << this->size_ratio; buf << this->size_ratio;
if (this->data.as_group.expand_focused != ExpandFocusType::NotExpanded) {
buf << ", has-expanded";
}
if (this->data.as_group.ephemeral) { if (this->data.as_group.ephemeral) {
buf << ", ephemeral"; buf << ", ephemeral";
} }
@ -560,6 +629,14 @@ Hy3Node* Hy3Node::removeFromParentRecursive() {
Debug::log(LOG, "Recursively removing parent nodes of %p", parent); Debug::log(LOG, "Recursively removing parent nodes of %p", parent);
if (this->parent != nullptr) {
// note: may have missing recalcs causing weird animations.
auto& actor = this->getExpandActor();
if (actor.data.type == Hy3NodeType::Group) {
actor.data.as_group.collapseExpansions();
}
}
while (parent != nullptr) { while (parent != nullptr) {
if (parent->parent == nullptr) { if (parent->parent == nullptr) {
Debug::log(ERR, "* UAF DEBUGGING - %p's parent is null, its the root group", parent); Debug::log(ERR, "* UAF DEBUGGING - %p's parent is null, its the root group", parent);

View file

@ -21,11 +21,18 @@ enum class Hy3NodeType {
Group, Group,
}; };
enum class ExpandFocusType {
NotExpanded,
Latch,
Stack,
};
struct Hy3GroupData { struct Hy3GroupData {
Hy3GroupLayout layout = Hy3GroupLayout::SplitH; Hy3GroupLayout layout = Hy3GroupLayout::SplitH;
std::list<Hy3Node*> children; std::list<Hy3Node*> children;
bool group_focused = true; bool group_focused = true;
Hy3Node* focused_child = nullptr; Hy3Node* focused_child = nullptr;
ExpandFocusType expand_focused = ExpandFocusType::NotExpanded;
bool ephemeral = false; bool ephemeral = false;
bool containment = false; bool containment = false;
Hy3TabGroup* tab_bar = nullptr; Hy3TabGroup* tab_bar = nullptr;
@ -34,6 +41,7 @@ struct Hy3GroupData {
~Hy3GroupData(); ~Hy3GroupData();
bool hasChild(Hy3Node* child); bool hasChild(Hy3Node* child);
void collapseExpansions();
private: private:
Hy3GroupData(Hy3GroupData&&); Hy3GroupData(Hy3GroupData&&);
@ -84,8 +92,9 @@ struct Hy3Node {
bool focusWindow(); bool focusWindow();
void markFocused(); void markFocused();
void raiseToTop(); void raiseToTop();
Hy3Node* getFocusedNode(bool ignore_group_focus = false); Hy3Node* getFocusedNode(bool ignore_group_focus = false, bool stop_at_expanded = false);
bool isIndirectlyFocused(); bool isIndirectlyFocused();
Hy3Node& getExpandActor();
void recalcSizePosRecursive(bool no_animation = false); void recalcSizePosRecursive(bool no_animation = false);
void updateTabBar(bool no_animation = false); void updateTabBar(bool no_animation = false);

View file

@ -149,6 +149,31 @@ void dispatch_killactive(std::string value) {
g_Hy3Layout->killFocusedNode(workspace); g_Hy3Layout->killFocusedNode(workspace);
} }
void dispatch_expand(std::string value) {
int workspace = workspace_for_action();
if (workspace == -1) return;
auto args = CVarList(value);
ExpandOption expand;
ExpandFullscreenOption fs_expand = ExpandFullscreenOption::MaximizeIntermediate;
if (args[0] == "expand") expand = ExpandOption::Expand;
else if (args[0] == "shrink") expand = ExpandOption::Shrink;
else if (args[0] == "base") expand = ExpandOption::Base;
else if (args[0] == "maximize") expand = ExpandOption::Maximize;
else if (args[0] == "fullscreen") expand = ExpandOption::Fullscreen;
else return;
if (args[1] == "intermediate_maximize") fs_expand = ExpandFullscreenOption::MaximizeIntermediate;
else if (args[1] == "fullscreen_maximize")
fs_expand = ExpandFullscreenOption::MaximizeAsFullscreen;
else if (args[1] == "maximize_only") fs_expand = ExpandFullscreenOption::MaximizeOnly;
else if (args[1] != "") return;
g_Hy3Layout->expand(workspace, expand, fs_expand);
}
void dispatch_debug(std::string arg) { void dispatch_debug(std::string arg) {
int workspace = workspace_for_action(); int workspace = workspace_for_action();
if (workspace == -1) return; if (workspace == -1) return;
@ -169,5 +194,6 @@ void registerDispatchers() {
HyprlandAPI::addDispatcher(PHANDLE, "hy3:focustab", dispatch_focustab); HyprlandAPI::addDispatcher(PHANDLE, "hy3:focustab", dispatch_focustab);
HyprlandAPI::addDispatcher(PHANDLE, "hy3:setswallow", dispatch_setswallow); HyprlandAPI::addDispatcher(PHANDLE, "hy3:setswallow", dispatch_setswallow);
HyprlandAPI::addDispatcher(PHANDLE, "hy3:killactive", dispatch_killactive); HyprlandAPI::addDispatcher(PHANDLE, "hy3:killactive", dispatch_killactive);
HyprlandAPI::addDispatcher(PHANDLE, "hy3:expand", dispatch_expand);
HyprlandAPI::addDispatcher(PHANDLE, "hy3:debugnodes", dispatch_debug); HyprlandAPI::addDispatcher(PHANDLE, "hy3:debugnodes", dispatch_debug);
} }