mirror of
https://github.com/Trensa-Organization/hy3.git
synced 2025-03-15 10:43:40 +01:00
Initial work on expand/lens nodes
This commit is contained in:
parent
3cdb3d03d8
commit
2fa77b4dbc
6 changed files with 249 additions and 18 deletions
|
@ -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.
|
||||
- `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
|
||||
- `hy3:setswallow, <true | false | toggle>` - set the containing node's window swallow state
|
||||
- `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
|
||||
|
|
|
@ -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) {
|
||||
if (window == nullptr) return false;
|
||||
auto* root = this->getWorkspaceRootGroup(window->m_iWorkspaceID);
|
||||
|
@ -1090,10 +1175,14 @@ Hy3Node* Hy3Layout::getWorkspaceRootGroup(const int& workspace) {
|
|||
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);
|
||||
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) {
|
||||
|
@ -1292,7 +1381,7 @@ Hy3Node* Hy3Layout::shiftOrGetFocus(
|
|||
bool once,
|
||||
bool visible
|
||||
) {
|
||||
auto* break_origin = &node;
|
||||
auto* break_origin = &node.getExpandActor();
|
||||
auto* break_parent = break_origin->parent;
|
||||
|
||||
auto has_broken_once = false;
|
||||
|
@ -1377,7 +1466,11 @@ Hy3Node* Hy3Layout::shiftOrGetFocus(
|
|||
if (shiftIsForward(direction)) iter = std::next(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 (target_group == node.parent) {
|
||||
if (shiftIsForward(direction)) insert = std::next(iter);
|
||||
|
@ -1386,7 +1479,7 @@ Hy3Node* Hy3Layout::shiftOrGetFocus(
|
|||
if (shiftIsForward(direction)) insert = iter;
|
||||
else insert = std::next(iter);
|
||||
}
|
||||
} else return *iter;
|
||||
} else return (*iter)->getFocusedNode();
|
||||
} else {
|
||||
// break into neighboring groups until we hit a window
|
||||
while (true) {
|
||||
|
@ -1432,13 +1525,16 @@ Hy3Node* Hy3Layout::shiftOrGetFocus(
|
|||
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_after) insert = std::next(iter);
|
||||
else insert = iter;
|
||||
break;
|
||||
} else {
|
||||
return *iter;
|
||||
return (*iter)->getFocusedNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,20 @@ enum class SetSwallowOption {
|
|||
Toggle,
|
||||
};
|
||||
|
||||
enum class ExpandOption {
|
||||
Expand,
|
||||
Shrink,
|
||||
Base,
|
||||
Maximize,
|
||||
Fullscreen,
|
||||
};
|
||||
|
||||
enum class ExpandFullscreenOption {
|
||||
MaximizeOnly,
|
||||
MaximizeIntermediate,
|
||||
MaximizeAsFullscreen,
|
||||
};
|
||||
|
||||
class Hy3Layout: public IHyprLayout {
|
||||
public:
|
||||
virtual void onWindowCreated(CWindow*);
|
||||
|
@ -84,11 +98,16 @@ public:
|
|||
void focusTab(int workspace, TabFocus target, TabFocusMousePriority, bool wrap_scroll, int index);
|
||||
void setNodeSwallow(int workspace, SetSwallowOption);
|
||||
void killFocusedNode(int workspace);
|
||||
void expand(int workspace, ExpandOption, ExpandFullscreenOption);
|
||||
|
||||
bool shouldRenderSelected(CWindow*);
|
||||
|
||||
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 windowGroupUrgentHook(void*, std::any);
|
||||
|
|
|
@ -12,6 +12,7 @@ Hy3GroupData::Hy3GroupData(Hy3GroupData&& from) {
|
|||
this->layout = from.layout;
|
||||
this->children = std::move(from.children);
|
||||
this->group_focused = from.group_focused;
|
||||
this->expand_focused = from.expand_focused;
|
||||
this->focused_child = from.focused_child;
|
||||
from.focused_child = nullptr;
|
||||
this->tab_bar = from.tab_bar;
|
||||
|
@ -35,6 +36,20 @@ bool Hy3GroupData::hasChild(Hy3Node* node) {
|
|||
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((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) {
|
||||
case Hy3NodeType::Window: return this;
|
||||
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
|
||||
|| (!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;
|
||||
} 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;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// clang-format off
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
? group->children.empty() ? 0 : constraint / group->children.size()
|
||||
? child_count <= 0 ? 0 : constraint / child_count
|
||||
: 0;
|
||||
|
||||
double offset = 0;
|
||||
|
@ -275,7 +321,27 @@ void Hy3Node::recalcSizePosRecursive(bool no_animation) {
|
|||
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) {
|
||||
if (expand_focused && child == group->focused_child) continue;
|
||||
|
||||
switch (group->layout) {
|
||||
case Hy3GroupLayout::SplitH:
|
||||
child->position.x = tpos.x + offset;
|
||||
|
@ -283,7 +349,7 @@ void Hy3Node::recalcSizePosRecursive(bool no_animation) {
|
|||
offset += child->size.x;
|
||||
child->position.y = tpos.y;
|
||||
child->size.y = tsize.y;
|
||||
child->setHidden(this->hidden);
|
||||
child->setHidden(this->hidden || expand_focused);
|
||||
|
||||
if (group->children.size() == 1) {
|
||||
child->gap_pos_offset = gap_pos_offset;
|
||||
|
@ -311,7 +377,7 @@ void Hy3Node::recalcSizePosRecursive(bool no_animation) {
|
|||
offset += child->size.y;
|
||||
child->position.x = tpos.x;
|
||||
child->size.x = tsize.x;
|
||||
child->setHidden(this->hidden);
|
||||
child->setHidden(this->hidden || expand_focused);
|
||||
|
||||
if (group->children.size() == 1) {
|
||||
child->gap_pos_offset = gap_pos_offset;
|
||||
|
@ -336,8 +402,7 @@ void Hy3Node::recalcSizePosRecursive(bool no_animation) {
|
|||
case Hy3GroupLayout::Tabbed:
|
||||
child->position = tpos;
|
||||
child->size = tsize;
|
||||
bool hidden = this->hidden || group->focused_child != child;
|
||||
child->setHidden(hidden);
|
||||
child->setHidden(this->hidden || expand_focused || group->focused_child != child);
|
||||
|
||||
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);
|
||||
|
@ -528,6 +593,10 @@ std::string Hy3Node::debugNode() {
|
|||
buf << "] size ratio: ";
|
||||
buf << this->size_ratio;
|
||||
|
||||
if (this->data.as_group.expand_focused != ExpandFocusType::NotExpanded) {
|
||||
buf << ", has-expanded";
|
||||
}
|
||||
|
||||
if (this->data.as_group.ephemeral) {
|
||||
buf << ", ephemeral";
|
||||
}
|
||||
|
@ -560,6 +629,14 @@ Hy3Node* Hy3Node::removeFromParentRecursive() {
|
|||
|
||||
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) {
|
||||
if (parent->parent == nullptr) {
|
||||
Debug::log(ERR, "* UAF DEBUGGING - %p's parent is null, its the root group", parent);
|
||||
|
|
|
@ -21,11 +21,18 @@ enum class Hy3NodeType {
|
|||
Group,
|
||||
};
|
||||
|
||||
enum class ExpandFocusType {
|
||||
NotExpanded,
|
||||
Latch,
|
||||
Stack,
|
||||
};
|
||||
|
||||
struct Hy3GroupData {
|
||||
Hy3GroupLayout layout = Hy3GroupLayout::SplitH;
|
||||
std::list<Hy3Node*> children;
|
||||
bool group_focused = true;
|
||||
Hy3Node* focused_child = nullptr;
|
||||
ExpandFocusType expand_focused = ExpandFocusType::NotExpanded;
|
||||
bool ephemeral = false;
|
||||
bool containment = false;
|
||||
Hy3TabGroup* tab_bar = nullptr;
|
||||
|
@ -34,6 +41,7 @@ struct Hy3GroupData {
|
|||
~Hy3GroupData();
|
||||
|
||||
bool hasChild(Hy3Node* child);
|
||||
void collapseExpansions();
|
||||
|
||||
private:
|
||||
Hy3GroupData(Hy3GroupData&&);
|
||||
|
@ -84,8 +92,9 @@ struct Hy3Node {
|
|||
bool focusWindow();
|
||||
void markFocused();
|
||||
void raiseToTop();
|
||||
Hy3Node* getFocusedNode(bool ignore_group_focus = false);
|
||||
Hy3Node* getFocusedNode(bool ignore_group_focus = false, bool stop_at_expanded = false);
|
||||
bool isIndirectlyFocused();
|
||||
Hy3Node& getExpandActor();
|
||||
|
||||
void recalcSizePosRecursive(bool no_animation = false);
|
||||
void updateTabBar(bool no_animation = false);
|
||||
|
|
|
@ -149,6 +149,31 @@ void dispatch_killactive(std::string value) {
|
|||
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) {
|
||||
int workspace = workspace_for_action();
|
||||
if (workspace == -1) return;
|
||||
|
@ -169,5 +194,6 @@ void registerDispatchers() {
|
|||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:focustab", dispatch_focustab);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:setswallow", dispatch_setswallow);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:killactive", dispatch_killactive);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:expand", dispatch_expand);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hy3:debugnodes", dispatch_debug);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue