From cc65dabe324fbc4760fa0d2005f4e5510e36d69e Mon Sep 17 00:00:00 2001 From: Pete Appleton Date: Wed, 31 Jan 2024 07:53:58 +0000 Subject: [PATCH 01/13] Add `hy3:resizenode` dispatcher --- README.md | 1 + src/Hy3Layout.cpp | 50 +++++++++++++++++++++++++++------------------ src/Hy3Layout.hpp | 2 +- src/Hy3Node.cpp | 2 ++ src/dispatchers.cpp | 13 ++++++++++++ 5 files changed, 47 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index a059ee6..a422339 100644 --- a/README.md +++ b/README.md @@ -341,6 +341,7 @@ plugin { - `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:debugnodes` - print the node tree into the hyprland log + - `hy3:resizenode, ` - like Hyprland `resizeactive`, but applied to the whole focused group instead of just a window - :warning: **ALPHA QUALITY** `hy3:setswallow, ` - set the containing node's window swallow state - :warning: **ALPHA QUALITY** `hy3:expand, ` - expand the current node to cover other nodes - `expand` - expand by one node diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index 14b3c9b..7cfd779 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -407,16 +407,9 @@ ShiftDirection reverse(ShiftDirection direction) { } } -void Hy3Layout::resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CWindow* pWindow) { - auto window = pWindow ? pWindow : g_pCompositor->m_pLastWindow; - if (!g_pCompositor->windowValidMapped(window)) return; - - auto* node = this->getNodeFromWindow(window); - - if (node != nullptr) { - node = &node->getExpandActor(); - - auto monitor = g_pCompositor->getMonitorFromID(window->m_iMonitorID); +void executeResizeOperation(const Vector2D& delta, eRectCorner corner, Hy3Node *node, CMonitor* monitor) { + if (node == nullptr) return; + if (monitor == nullptr) return; const bool display_left = STICKS(node->position.x, monitor->vecPosition.x + monitor->vecReservedTopLeft.x); @@ -465,12 +458,12 @@ void Hy3Layout::resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CW : ShiftDirection::Down; } - // Find the neighboring node in each axis, which will be either above or at the - // same level as the initiating node in the layout hierarchy. These are the nodes - // which must get resized (rather than the initiator) because they are the - // highest point in the hierarchy - auto horizontal_neighbor = node->findNeighbor(target_edge_x); - auto vertical_neighbor = node->findNeighbor(target_edge_y); + // Find the neighboring node in each axis, which will be either above or at the + // same level as the initiating node in the layout hierarchy. These are the nodes + // which must get resized (rather than the initiator) because they are the + // highest point in the hierarchy + auto horizontal_neighbor = node->findNeighbor(target_edge_x); + auto vertical_neighbor = node->findNeighbor(target_edge_y); static const auto animate = ConfigValue("misc:animate_manual_resizes"); @@ -480,11 +473,28 @@ void Hy3Layout::resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CW horizontal_neighbor->resize(reverse(target_edge_x), resize_delta.x, *animate == 0); } - if (vertical_neighbor) { - vertical_neighbor->resize(reverse(target_edge_y), resize_delta.y, *animate == 0); - } + if (vertical_neighbor) { + vertical_neighbor->resize(reverse(target_edge_y), resize_delta.y, *animate == 0); } - } else if (window->m_bIsFloating) { + } +} + +void Hy3Layout::resizeNode(const Vector2D& delta, eRectCorner corner, Hy3Node* node) { + if(node == nullptr) return; + + auto monitor = g_pCompositor->getMonitorFromID(g_pCompositor->getWorkspaceByID(node->workspace_id)->m_iMonitorID); + executeResizeOperation(delta, corner, node, monitor); +} + +void Hy3Layout::resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CWindow* pWindow) { + auto window = pWindow ? pWindow : g_pCompositor->m_pLastWindow; + if(window == nullptr || ! g_pCompositor->windowValidMapped(window)) return; + + auto* node = this->getNodeFromWindow(window); + + if (node != nullptr) { + executeResizeOperation(delta, corner, &node->getExpandActor(), g_pCompositor->getMonitorFromID(window->m_iMonitorID)); + } else if(window->m_bIsFloating) { // No parent node - is this a floating window? If so, use the same logic as the `main` layout const auto required_size = Vector2D( std::max((window->m_vRealSize.goalv() + delta).x, 20.0), diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp index 766d0bb..0eff9d4 100644 --- a/src/Hy3Layout.hpp +++ b/src/Hy3Layout.hpp @@ -117,6 +117,7 @@ public: void setNodeSwallow(int workspace, SetSwallowOption); void killFocusedNode(int workspace); void expand(int workspace, ExpandOption, ExpandFullscreenOption); + void resizeNode(const Vector2D& delta, eRectCorner corner, Hy3Node* node); bool shouldRenderSelected(CWindow*); @@ -146,7 +147,6 @@ private: void updateAutotileWorkspaces(); bool shouldAutotileWorkspace(int); - void resizeNode(Hy3Node*, Vector2D, ShiftDirection resize_edge_x, ShiftDirection resize_edge_y); struct { std::string raw_workspaces; diff --git a/src/Hy3Node.cpp b/src/Hy3Node.cpp index 5eed0fc..4c833a1 100644 --- a/src/Hy3Node.cpp +++ b/src/Hy3Node.cpp @@ -926,6 +926,8 @@ void Hy3Node::resize(ShiftDirection direction, double delta, bool no_animation) neighbor->size_ratio = requested_neighbor_size_ratio; parent_node->recalcSizePosRecursive(no_animation); + } else { + hy3_log(WARN, "Requested size ratio {} or {} out of bounds, ignoring", requested_size_ratio, requested_neighbor_size_ratio); } } } diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp index 667b1cf..5661a7a 100644 --- a/src/dispatchers.cpp +++ b/src/dispatchers.cpp @@ -8,6 +8,7 @@ int workspace_for_action(bool allow_fullscreen = false) { if (g_pLayoutManager->getCurrentLayout() != g_Hy3Layout.get()) return -1; + if(!g_pCompositor->m_pLastMonitor) return -1; int workspace_id = g_pCompositor->m_pLastMonitor->activeWorkspace; @@ -245,7 +246,19 @@ void dispatch_debug(std::string arg) { } } +void dispatch_resizenode(std::string value) { + int workspace = workspace_for_action(); + if (workspace == -1) return; + + auto* node = g_Hy3Layout->getWorkspaceFocusedNode(workspace, false, true); + const auto delta = g_pCompositor->parseWindowVectorArgsRelative(value, Vector2D(0, 0)); + + hy3_log(LOG, "resizeNode: node: {:x}, delta: {:X}", (uintptr_t)node, delta); + g_Hy3Layout->resizeNode(delta, CORNER_NONE, node); +} + void registerDispatchers() { + HyprlandAPI::addDispatcher(PHANDLE, "hy3:resizenode", dispatch_resizenode); HyprlandAPI::addDispatcher(PHANDLE, "hy3:makegroup", dispatch_makegroup); HyprlandAPI::addDispatcher(PHANDLE, "hy3:changegroup", dispatch_changegroup); HyprlandAPI::addDispatcher(PHANDLE, "hy3:setephemeral", dispatch_setephemeral); From 0e9077ec3d290ec6b6f9aa4e0035b0c0db678cff Mon Sep 17 00:00:00 2001 From: Pete Appleton Date: Wed, 7 Feb 2024 21:17:53 +0000 Subject: [PATCH 02/13] Integrate floating windows into `hy3:movefocus` Move floating window if focused, even if tiled windows on same workspace Navigate based on window middle Feels unintuitive in use when floating overlaid on tiled Fix: Set new monitor active when moving floating windows, remember previous workspace --- .gitignore | 2 + CHANGELOG.md | 5 +- CMakeLists.txt | 1 + src/Hy3Layout.cpp | 490 +++++++++++++++++++++++++++++++++++++------- src/Hy3Layout.hpp | 35 +++- src/Hy3Node.cpp | 65 +++--- src/Hy3Node.hpp | 37 +++- src/conversions.cpp | 51 +++++ src/conversions.hpp | 6 + src/dispatchers.cpp | 2 +- src/main.cpp | 1 + 11 files changed, 567 insertions(+), 128 deletions(-) create mode 100644 src/conversions.cpp create mode 100644 src/conversions.hpp diff --git a/.gitignore b/.gitignore index 36b6e89..f013aa6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ build/ compile_commands.json .vscode/ *.log +.env +.env.* \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c97a0bf..328c603 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,10 @@ - Implement `resizeactivewindow` for floating windows - Fully implement `resizeactivewindow` for tiled windows - +- Add `hy3:resizenode` dispatcher, drop-in replacement for `resizeactivewindow` applied at the Hy3 group level. +- Implement keyboard-based focusing for floating windows +- Implement keyboard-based movement for floating windows + - Add configuration `kbd_shift_delta` providing delta [in pixels] for shift ## hl0.35.0 and before - Fixed `hy3:killactive` and `hy3:movetoworkspace` not working in fullscreen. diff --git a/CMakeLists.txt b/CMakeLists.txt index e4e83ac..e639687 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ pkg_check_modules(DEPS REQUIRED hyprland pixman-1 libdrm pango pangocairo) add_library(hy3 SHARED src/main.cpp src/dispatchers.cpp + src/conversions.cpp src/Hy3Layout.cpp src/Hy3Node.cpp src/TabGroup.cpp diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index 7cfd779..356ca94 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -1,6 +1,7 @@ #include #include - +#include +#include #include #include #include @@ -8,6 +9,7 @@ #include "Hy3Layout.hpp" #include "SelectionHook.hpp" #include "globals.hpp" +#include "conversions.hpp" std::unique_ptr renderHookPtr = std::make_unique(Hy3Layout::renderHook); @@ -169,7 +171,7 @@ void Hy3Layout::insertNode(Hy3Node& node) { if (opening_after != nullptr && ((node.data.type == Hy3NodeType::Group - && (opening_after == &node || node.data.as_group.hasChild(opening_after))) + && (opening_after == &node || node.hasChild(opening_after))) || opening_after->reparenting)) { opening_after = nullptr; @@ -287,6 +289,7 @@ void Hy3Layout::insertNode(Hy3Node& node) { } void Hy3Layout::onWindowRemovedTiling(CWindow* window) { + this->m_focusIntercepts.erase(window); static const auto node_collapse_policy = ConfigValue("plugin:hy3:node_collapse_policy"); @@ -342,12 +345,16 @@ void Hy3Layout::onWindowRemovedTiling(CWindow* window) { } } +void Hy3Layout::onWindowRemovedFloating(CWindow *window) { + this->m_focusIntercepts.erase(window); +} + void Hy3Layout::onWindowFocusChange(CWindow* window) { auto* node = this->getNodeFromWindow(window); if (node == nullptr) return; hy3_log( - TRACE, + LOG, "changing window focus to window {:x} as node {:x}", (uintptr_t) window, (uintptr_t) node @@ -372,16 +379,10 @@ void Hy3Layout::recalculateMonitor(const int& monitor_id) { // todo: refactor this auto* top_node = this->getWorkspaceRootGroup(monitor->activeWorkspace); - if (top_node != nullptr) { - top_node->position = monitor->vecPosition + monitor->vecReservedTopLeft; - top_node->size = - monitor->vecSize - monitor->vecReservedTopLeft - monitor->vecReservedBottomRight; - - top_node->recalcSizePosRecursive(); + if(top_node == nullptr) { + top_node = this->getWorkspaceRootGroup(monitor->specialWorkspaceID); } - top_node = this->getWorkspaceRootGroup(monitor->specialWorkspaceID); - if (top_node != nullptr) { top_node->position = monitor->vecPosition + monitor->vecReservedTopLeft; top_node->size = @@ -480,28 +481,31 @@ void executeResizeOperation(const Vector2D& delta, eRectCorner corner, Hy3Node * } void Hy3Layout::resizeNode(const Vector2D& delta, eRectCorner corner, Hy3Node* node) { - if(node == nullptr) return; - - auto monitor = g_pCompositor->getMonitorFromID(g_pCompositor->getWorkspaceByID(node->workspace_id)->m_iMonitorID); - executeResizeOperation(delta, corner, node, monitor); + // Is the intended target really a node or a floating window? + auto window = g_pCompositor->m_pLastWindow; + if(window && window->m_bIsFloating) { + this->resizeActiveWindow(delta, corner, window); + } else if (node) { + auto monitor = g_pCompositor->getMonitorFromID(g_pCompositor->getWorkspaceByID(node->workspace_id)->m_iMonitorID); + executeResizeOperation(delta, corner, node, monitor); + } } void Hy3Layout::resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CWindow* pWindow) { auto window = pWindow ? pWindow : g_pCompositor->m_pLastWindow; if(window == nullptr || ! g_pCompositor->windowValidMapped(window)) return; - auto* node = this->getNodeFromWindow(window); - - if (node != nullptr) { - executeResizeOperation(delta, corner, &node->getExpandActor(), g_pCompositor->getMonitorFromID(window->m_iMonitorID)); - } else if(window->m_bIsFloating) { - // No parent node - is this a floating window? If so, use the same logic as the `main` layout + if (window->m_bIsFloating) { + // Use the same logic as the `main` layout for floating windows const auto required_size = Vector2D( std::max((window->m_vRealSize.goalv() + delta).x, 20.0), std::max((window->m_vRealSize.goalv() + delta).y, 20.0) ); window->m_vRealSize = required_size; - } + g_pXWaylandManager->setWindowSize(window, required_size); + } else if (auto* node = this->getNodeFromWindow(window);node != nullptr) { + executeResizeOperation(delta, corner, &node->getExpandActor(), g_pCompositor->getMonitorFromID(window->m_iMonitorID)); + }; } void Hy3Layout::fullscreenRequestForWindow( @@ -619,16 +623,26 @@ void Hy3Layout::switchWindows(CWindow* pWindowA, CWindow* pWindowB) { void Hy3Layout::moveWindowTo(CWindow* window, const std::string& direction) { auto* node = this->getNodeFromWindow(window); - if (node == nullptr) return; + if (node == nullptr) { + const auto neighbor = g_pCompositor->getWindowInDirection(window, direction[0]); - ShiftDirection shift; - if (direction == "l") shift = ShiftDirection::Left; - else if (direction == "r") shift = ShiftDirection::Right; - else if (direction == "u") shift = ShiftDirection::Up; - else if (direction == "d") shift = ShiftDirection::Down; - else return; + if (window->m_iWorkspaceID != neighbor->m_iWorkspaceID) { + // if different monitors, send to monitor + onWindowRemovedTiling(window); + window->moveToWorkspace(neighbor->m_iWorkspaceID); + window->m_iMonitorID = neighbor->m_iMonitorID; + onWindowCreatedTiling(window); + } + } else { + ShiftDirection shift; + if (direction == "l") shift = ShiftDirection::Left; + else if (direction == "r") shift = ShiftDirection::Right; + else if (direction == "u") shift = ShiftDirection::Up; + else if (direction == "d") shift = ShiftDirection::Down; + else return; - this->shiftNode(*node, shift, false, false); + this->shiftNode(*node, shift, false, false); + } } void Hy3Layout::alterSplitRatio(CWindow* pWindow, float delta, bool exact) { @@ -878,46 +892,369 @@ void Hy3Layout::shiftNode(Hy3Node& node, ShiftDirection direction, bool once, bo } } -void Hy3Layout::shiftWindow(int workspace, ShiftDirection direction, bool once, bool visible) { - auto* node = this->getWorkspaceFocusedNode(workspace); - if (node == nullptr) return; +void shiftFloatingWindow(CWindow* window, ShiftDirection direction) { + static const auto* kbd_shift_delta = + &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:kbd_shift_delta")->intValue; - this->shiftNode(*node, direction, once, visible); -} + if(!window) return; -void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible) { - auto* current_window = g_pCompositor->m_pLastWindow; - - if (current_window != nullptr) { - auto* p_workspace = g_pCompositor->getWorkspaceByID(current_window->m_iWorkspaceID); - if (p_workspace->m_bHasFullscreenWindow) return; - - if (current_window->m_bIsFloating) { - auto* next_window = g_pCompositor->getWindowInDirection( - current_window, - direction == ShiftDirection::Left ? 'l' - : direction == ShiftDirection::Up ? 'u' - : direction == ShiftDirection::Down ? 'd' - : 'r' - ); - - if (next_window != nullptr) g_pCompositor->focusWindow(next_window); - return; + Vector2D bounds {0, 0}; + // BUG: Assumes horizontal monitor layout + // BUG: Ignores monitor reserved space + for(auto m: g_pCompositor->m_vMonitors) { + bounds.x = std::max(bounds.x, m->vecPosition.x + m->vecSize.x); + if(m->ID == window->m_iMonitorID) { + bounds.y = m->vecPosition.y + m->vecSize.y; } } - auto* node = this->getWorkspaceFocusedNode(workspace); - if (node == nullptr) return; + const int delta = getSearchDirection(direction) == SearchDirection::Forwards + ? *kbd_shift_delta: -*kbd_shift_delta; - auto* target = this->shiftOrGetFocus(*node, direction, false, false, visible); + Vector2D movement_delta = (getAxis(direction) == Axis::Horizontal) + ? Vector2D{ delta, 0 } : Vector2D{ 0, delta }; - if (target != nullptr) { - target->focus(); - while (target->parent != nullptr) target = target->parent; - target->recalcSizePosRecursive(); + auto window_pos = window->m_vRealPosition.vec(); + auto window_size = window->m_vRealSize.vec(); + + // Keep at least `delta` pixels visible + if (window_pos.x + window_size.x + delta < 0 || window_pos.x + delta > bounds.x) movement_delta.x = 0; + if (window_pos.y + window_size.y + delta < 0 || window_pos.y + delta > bounds.y) movement_delta.y = 0; + if(movement_delta.x != 0 || movement_delta.y != 0) { + auto new_pos = window_pos + movement_delta; + // Do we need to change the workspace? + const auto new_monitor = g_pCompositor->getMonitorFromVector(new_pos); + if(new_monitor && new_monitor->ID != window->m_iMonitorID) { + // Ignore the movement request if the new workspace is special + if(! new_monitor->specialWorkspaceID) { + const auto old_workspace = g_pCompositor->getWorkspaceByID(window->m_iWorkspaceID); + const auto new_workspace = g_pCompositor->getWorkspaceByID(new_monitor->activeWorkspace); + const auto previous_monitor = g_pCompositor->getMonitorFromID(window->m_iMonitorID); + const auto original_new_pos = new_pos; + + if(new_workspace && previous_monitor) { + switch (direction) + { + case ShiftDirection::Left: new_pos.x += new_monitor->vecSize.x; break; + case ShiftDirection::Right: new_pos.x -= previous_monitor->vecSize.x; break; + case ShiftDirection::Up: new_pos.y += new_monitor->vecSize.y; break; + case ShiftDirection::Down: new_pos.y -= previous_monitor->vecSize.y; break; + default: UNREACHABLE(); + } + } + + window->m_vRealPosition = new_pos; + g_pCompositor->moveWindowToWorkspaceSafe(window, new_workspace); + g_pCompositor->setActiveMonitor(new_monitor); + + static auto* const allow_workspace_cycles = + &g_pConfigManager->getConfigValuePtr("binds:allow_workspace_cycles")->intValue; + if (*allow_workspace_cycles) new_workspace->rememberPrevWorkspace(old_workspace); + } + } else { + window->m_vRealPosition = new_pos; + } } } +void Hy3Layout::shiftWindow(int workspace_id, ShiftDirection direction, bool once, bool visible) { + auto focused_window = g_pCompositor->m_pLastWindow; + auto* node = getWorkspaceFocusedNode(workspace_id); + + if (focused_window && focused_window->m_bIsFloating) { + shiftFloatingWindow(focused_window, direction); + } else if (node) { + shiftNode(*node, direction, once, visible); + } +} + +void Hy3Layout::focusMonitor(CMonitor* monitor) { + if(monitor == nullptr) return; + + g_pCompositor->setActiveMonitor(monitor); + const auto focusedNode = this->getWorkspaceFocusedNode(monitor->activeWorkspace); + if(focusedNode != nullptr) { + focusedNode->focus(); + } else { + auto* workspace = g_pCompositor->getWorkspaceByID(monitor->activeWorkspace); + CWindow* next_window = nullptr; + if(workspace != nullptr) { + workspace->setActive(true); + if (workspace->m_bHasFullscreenWindow) { + next_window = g_pCompositor->getFullscreenWindowOnWorkspace(workspace->m_iID); + } else { + next_window = workspace->getLastFocusedWindow(); + } + } else { + for (auto& w: g_pCompositor->m_vWindows | std::views::reverse) { + if (w->m_bIsMapped && !w->isHidden() && w->m_bIsFloating && w->m_iX11Type != 2 + && w->m_iWorkspaceID == next_window->m_iWorkspaceID && !w->m_bX11ShouldntFocus + && !w->m_bNoFocus) + { + next_window = w.get(); + break; + } + } + } + g_pCompositor->focusWindow(next_window); + } +} + +CWindow *getFocusedWindow(const Hy3Node *node) { + auto search = node; + while(search != nullptr && search->data.type == Hy3NodeType::Group) { + search = search->data.as_group.focused_child; + } + + if(search == nullptr || search->data.type != Hy3NodeType::Window) { + return nullptr; + } + + return search->data.as_window; +} + +bool shiftIsForward(ShiftDirection direction) { + return direction == ShiftDirection::Right || direction == ShiftDirection::Down; +} + +bool shiftIsVertical(ShiftDirection direction) { + return direction == ShiftDirection::Up || direction == ShiftDirection::Down; +} + +bool shiftMatchesLayout(Hy3GroupLayout layout, ShiftDirection direction) { + return (layout == Hy3GroupLayout::SplitV && shiftIsVertical(direction)) + || (layout != Hy3GroupLayout::SplitV && !shiftIsVertical(direction)); +} + + +bool covers(const CBox &outer, const CBox &inner) { + return outer.x <= inner.x + && outer.y <= inner.y + && outer.x + outer.w >= inner.x + inner.w + && outer.y + outer.h >= inner.y + inner.h; +} + +bool isObscured (CWindow* window) { + if(!window) return false; + + const auto inner_box = window->getWindowMainSurfaceBox(); + + bool is_obscured = false; + for(auto& w :g_pCompositor->m_vWindows | std::views::reverse) { + if(w.get() == window) { + hy3_log(LOG, "{} doesn't obscure itself", window); + // Don't go any further if this is a floating window, because m_vWindows is sorted bottom->top per Compositor.cpp + if(window->m_bIsFloating) break; else continue; + } + + if(!w->m_bIsFloating) { + hy3_log(LOG, "Tiled window {} can't obscure anything", w.get()); + continue; + } + + const auto outer_box = w->getWindowMainSurfaceBox(); + is_obscured = covers(outer_box, inner_box); + + if(is_obscured) { + hy3_log(LOG, "{} obscures {}", w.get(), window); + break; + } + }; + + return is_obscured; +} + +bool isObscured(Hy3Node* node) { + return node && node->data.type == Hy3NodeType::Window && isObscured(node->data.as_window); +} + +bool isNotObscured(CWindow* window) { return !isObscured(window); } +bool isNotObscured(Hy3Node* node) { return !isObscured(node); } + +CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, bool considerFloating, bool considerTiled, bool considerOtherMonitors) { + if(!source) return nullptr; + + CWindow *target_window = nullptr; + const auto current_surface_box = source->getWindowMainSurfaceBox(); + auto target_distance = Distance { direction }; + + auto isCandidate = [=, mon = source->m_iMonitorID](CWindow* w) { + return (considerOtherMonitors || w->m_iMonitorID == mon) + && ((considerFloating && w->m_bIsFloating) || (considerTiled && !w->m_bIsFloating) || (w->m_iMonitorID != mon)) + && w->m_bIsMapped + && w->m_iX11Type != 2 + && !w->m_bNoFocus + && !w->isHidden() + && !w->m_bX11ShouldntFocus; + }; + + for(auto &pw: g_pCompositor->m_vWindows) { + auto w = pw.get(); + if(w != source && isCandidate(w)) { + auto dist = Distance { direction, current_surface_box, w->getWindowMainSurfaceBox() }; + if((dist < target_distance || (target_distance.isNotInitialised() && dist.isInDirection(direction))) && isNotObscured(w) ) { + target_window = w; + target_distance = dist; + } + } + } + + hy3_log(LOG, "getWindowInDirection: closest window to {} is {}", source, target_window); + return target_window; +} + +void Hy3Layout::shiftFocusToMonitor(ShiftDirection direction) { + auto target_monitor = g_pCompositor->getMonitorInDirection(directionToChar(direction)); + if(target_monitor) this->focusMonitor(target_monitor); +} + +void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible) { + Hy3Node *candidate_node = nullptr; + CWindow *closest_window = nullptr; + Hy3Node *source_node = nullptr; + CWindow *source_window = g_pCompositor->m_pLastWindow; + CWorkspace *source_workspace = g_pCompositor->getWorkspaceByID(workspace); + + if(source_window == nullptr) { + shiftFocusToMonitor(direction); + return; + } + + if(source_workspace == nullptr) return; + + hy3_log(LOG, + "shiftFocus: Source: {} ({}), workspace: {}, direction: {}, visible: {}", + source_window, + source_window->m_bIsFloating ? "floating" : "tiled", + workspace, + (int)direction, + visible + ); + + // Determine the starting point for looking for a tiled node - it's either the + // workspace's focused node or the floating window's focus entry point (which may be null) + source_node = source_window->m_bIsFloating ? getFocusOverride(source_window, direction) + : getWorkspaceFocusedNode(workspace); + + // Get the closest node to the starting point + if(source_node) { + candidate_node = this->shiftOrGetFocus(*source_node, direction, false, false, visible); + while(candidate_node && !isNotObscured(candidate_node)) { + candidate_node = this->shiftOrGetFocus(*candidate_node, direction, false, false, visible); + } + } + + bool select_tiled_windows = source_window->m_bIsFloating && !candidate_node; + + // Find the closest window in the right direction. Only consider tiled windows or other monitors + // if `shiftOrGetFocus` didn't provide a candidate. + closest_window = getWindowInDirection(source_window, direction, true, select_tiled_windows, !candidate_node); + + // If there's a window in the right direction then choose between that window and the tiled candidate. + bool focus_closest_window = false; + if(closest_window) { + if(candidate_node) { + // If the closest window is tiled then focus the tiled node which was obtained from `shiftOrGetFocus`, + // otherwise focus whichever is closer + if(closest_window->m_bIsFloating) { + const auto source_box = source_window->getWindowMainSurfaceBox(); + Distance distanceToClosestWindow(direction, source_box, closest_window->getWindowMainSurfaceBox()); + Distance distanceToTiledNode(direction, source_box, candidate_node->getMainSurfaceBox()); + if(distanceToClosestWindow < distanceToTiledNode) { + focus_closest_window = true; + } + } + } else { + focus_closest_window = true; + } + } + + auto new_monitor_id = source_window->m_iMonitorID; + + if(focus_closest_window) { + new_monitor_id = closest_window->m_iMonitorID; + setFocusOverride(closest_window, direction, source_node); + g_pCompositor->focusWindow(closest_window); + } else if(candidate_node) { + if(candidate_node->data.type == Hy3NodeType::Window) { + new_monitor_id = candidate_node->data.as_window->m_iMonitorID; + } + candidate_node->focusWindow(); + candidate_node->getRoot()->recalcSizePosRecursive(); + } else { + shiftFocusToMonitor(direction); + } + + if(new_monitor_id != source_window->m_iMonitorID) { + if(auto *monitor = g_pCompositor->getMonitorFromID(new_monitor_id)) { + g_pCompositor->setActiveMonitor(monitor); + } + } +} + +Hy3Node* Hy3Layout::getFocusOverride(CWindow* src, ShiftDirection direction) { + if(auto intercept = this->m_focusIntercepts.find(src); intercept != this->m_focusIntercepts.end()) { + hy3_log(LOG, "getFocusOverride: Found intercept for {}", src); + Hy3Node** accessor; + + switch (direction) + { + case ShiftDirection::Left: accessor = &intercept->second.left; break; + case ShiftDirection::Up: accessor = &intercept->second.up; break; + case ShiftDirection::Right: accessor = &intercept->second.right; break; + case ShiftDirection::Down: accessor = &intercept->second.down; break; + default: hy3_log(WARN, "Unknown ShiftDirection: {}", (int) direction); return nullptr; + } + + if(auto override = *accessor) { + // If the root isn't valid or is on a different workspsace then update the intercept data + if(override->workspace_id != src->m_iWorkspaceID || !std::ranges::contains(this->nodes, *override)) { + override = nullptr; + *accessor = nullptr; + // If there are no remaining overrides then discard the intercept + if(intercept->second.left == nullptr + && intercept->second.up == nullptr + && intercept->second.right == nullptr + && intercept->second.down == nullptr + ) { + this->m_focusIntercepts.erase(intercept); + } + } + + return override; + } + } + + hy3_log(LOG, "getFocusOverride: No intercept found for: {}", src); + return nullptr; +} + +void Hy3Layout::setFocusOverride(CWindow* src, ShiftDirection direction, Hy3Node* dest) { + hy3_log(LOG, "setFocusOverride: Storing intercept for: {} to: {:x}", src, (uintptr_t)dest); + if(auto intercept = this->m_focusIntercepts.find(src);intercept != this->m_focusIntercepts.end()) { + hy3_log(LOG, "setFocusOverride: Updating existing intercept"); + switch(direction) { + case ShiftDirection::Left: intercept->second.left = dest; break; + case ShiftDirection::Up: intercept->second.up = dest; break; + case ShiftDirection::Right: intercept->second.right = dest; break; + case ShiftDirection::Down: intercept->second.down = dest; break; + default: hy3_log(WARN, "Unknown ShiftDirection: {}", (int) direction); + } + } else { + hy3_log(LOG, "setFocusOverride: Adding new intercept"); + FocusOverride override; + switch(direction) { + case ShiftDirection::Left: override.left = dest; break; + case ShiftDirection::Up: override.up = dest; break; + case ShiftDirection::Right: override.right = dest; break; + case ShiftDirection::Down: override.down = dest; break; + default: hy3_log(WARN, "Unknown ShiftDirection: {}", (int) direction); + } + this->m_focusIntercepts.insert({ src, override }); + } +} + + void changeNodeWorkspaceRecursive(Hy3Node& node, CWorkspace* workspace) { node.workspace_id = workspace->m_iID; @@ -1346,7 +1683,7 @@ bool Hy3Layout::shouldRenderSelected(CWindow* window) { case Hy3NodeType::Group: { auto* node = this->getNodeFromWindow(window); if (node == nullptr) return false; - return focused->data.as_group.hasChild(node); + return focused->hasChild(node); } default: return false; } @@ -1568,19 +1905,6 @@ void Hy3Layout::applyNodeDataToWindow(Hy3Node* node, bool no_animation) { } } -bool shiftIsForward(ShiftDirection direction) { - return direction == ShiftDirection::Right || direction == ShiftDirection::Down; -} - -bool shiftIsVertical(ShiftDirection direction) { - return direction == ShiftDirection::Up || direction == ShiftDirection::Down; -} - -bool shiftMatchesLayout(Hy3GroupLayout layout, ShiftDirection direction) { - return (layout == Hy3GroupLayout::SplitV && shiftIsVertical(direction)) - || (layout != Hy3GroupLayout::SplitV && !shiftIsVertical(direction)); -} - Hy3Node* Hy3Layout::shiftOrGetFocus( Hy3Node& node, ShiftDirection direction, @@ -1663,10 +1987,14 @@ Hy3Node* Hy3Layout::shiftOrGetFocus( std::list::iterator insert; if (break_origin == parent_group.children.front() && !shiftIsForward(direction)) { - if (!shift) return nullptr; + if (!shift) { + return nullptr; + } insert = parent_group.children.begin(); } else if (break_origin == parent_group.children.back() && shiftIsForward(direction)) { - if (!shift) return nullptr; + if (!shift) { + return nullptr; + } insert = parent_group.children.end(); } else { auto& group_data = target_group->data.as_group; @@ -1688,14 +2016,18 @@ Hy3Node* Hy3Layout::shiftOrGetFocus( if (shiftIsForward(direction)) insert = iter; else insert = std::next(iter); } - } else return (*iter)->getFocusedNode(); + } else { + return (*iter)->getFocusedNode(); + } } else { // break into neighboring groups until we hit a window while (true) { target_group = *iter; auto& group_data = target_group->data.as_group; - if (group_data.children.empty()) return nullptr; // in theory this would never happen + if (group_data.children.empty()) { + return nullptr; + } // in theory this would never happen bool shift_after = false; @@ -1781,7 +2113,7 @@ Hy3Node* Hy3Layout::shiftOrGetFocus( if (old_parent != nullptr) { auto& group = old_parent->data.as_group; if (old_parent->parent != nullptr && group.ephemeral && group.children.size() == 1 - && !group.hasChild(&node)) + && !old_parent->hasChild(&node)) { Hy3Node::swallowGroups(old_parent); } diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp index 0eff9d4..19cfe21 100644 --- a/src/Hy3Layout.hpp +++ b/src/Hy3Layout.hpp @@ -1,4 +1,9 @@ #pragma once +#include +#include + +#include +#include class Hy3Layout; @@ -8,11 +13,6 @@ enum class GroupEphemeralityOption { ForceEphemeral, }; -#include -#include - -#include - enum class ShiftDirection { Left, Up, @@ -20,10 +20,17 @@ enum class ShiftDirection { Right, }; +enum class SearchDirection { + None, + Forwards, + Backwards +}; + enum class Axis { None, Horizontal, Vertical }; #include "Hy3Node.hpp" #include "TabGroup.hpp" +#include "conversions.hpp" enum class FocusShift { Top, @@ -67,17 +74,25 @@ enum class ExpandFullscreenOption { MaximizeAsFullscreen, }; +struct FocusOverride { + Hy3Node *left = nullptr; + Hy3Node *up = nullptr; + Hy3Node *right = nullptr; + Hy3Node *down = nullptr; +}; + + class Hy3Layout: public IHyprLayout { public: virtual void onWindowCreated(CWindow*, eDirection = DIRECTION_DEFAULT); virtual void onWindowCreatedTiling(CWindow*, eDirection = DIRECTION_DEFAULT); virtual void onWindowRemovedTiling(CWindow*); + virtual void onWindowRemovedFloating(CWindow*); virtual void onWindowFocusChange(CWindow*); virtual bool isWindowTiled(CWindow*); virtual void recalculateMonitor(const int& monitor_id); virtual void recalculateWindow(CWindow*); - virtual void - resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CWindow* pWindow = nullptr); + virtual void resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CWindow* pWindow = nullptr); virtual void fullscreenRequestForWindow(CWindow*, eFullscreenMode, bool enable_fullscreen); virtual std::any layoutMessage(SLayoutMessageHeader header, std::string content); virtual SWindowRenderLayoutHints requestRenderHints(CWindow*); @@ -139,6 +154,10 @@ public: private: Hy3Node* getNodeFromWindow(CWindow*); void applyNodeDataToWindow(Hy3Node*, bool no_animation = false); + void shiftFocusToMonitor(ShiftDirection direction); + std::unordered_map m_focusIntercepts; + Hy3Node* getFocusOverride(CWindow* src, ShiftDirection direction); + void setFocusOverride(CWindow* src, ShiftDirection direction, Hy3Node* dest); // if shift is true, shift the window in the given direction, returning // nullptr, if shift is false, return the window in the given direction or @@ -147,6 +166,8 @@ private: void updateAutotileWorkspaces(); bool shouldAutotileWorkspace(int); + void resizeNode(Hy3Node*, Vector2D, ShiftDirection resize_edge_x, ShiftDirection resize_edge_y); + void focusMonitor(CMonitor*); struct { std::string raw_workspaces; diff --git a/src/Hy3Node.cpp b/src/Hy3Node.cpp index 4c833a1..6712919 100644 --- a/src/Hy3Node.cpp +++ b/src/Hy3Node.cpp @@ -33,18 +33,6 @@ Hy3GroupData::~Hy3GroupData() { if (this->tab_bar != nullptr) this->tab_bar->bar.beginDestroy(); } -bool Hy3GroupData::hasChild(Hy3Node* 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; -} - void Hy3GroupData::collapseExpansions() { if (this->expand_focused == ExpandFocusType::NotExpanded) return; this->expand_focused = ExpandFocusType::NotExpanded; @@ -195,20 +183,17 @@ void markGroupFocusedRecursive(Hy3GroupData& 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; + // undo decos for root focus + auto* root = node; + while (root->parent != nullptr) { + root->parent->data.as_group.focused_child = root; + root->parent->data.as_group.group_focused = false; + root = root->parent; } root->updateDecos(); @@ -842,24 +827,6 @@ Hy3Node* Hy3Node::getImmediateSibling(ShiftDirection direction) { return *list_sibling; } -Axis getAxis(Hy3GroupLayout layout) { - switch (layout) { - case Hy3GroupLayout::SplitH: return Axis::Horizontal; - case Hy3GroupLayout::SplitV: return Axis::Vertical; - default: return Axis::None; - } -} - -Axis getAxis(ShiftDirection direction) { - switch (direction) { - case ShiftDirection::Left: - case ShiftDirection::Right: return Axis::Horizontal; - case ShiftDirection::Down: - case ShiftDirection::Up: return Axis::Vertical; - default: return Axis::None; - } -} - Hy3Node* Hy3Node::findNeighbor(ShiftDirection direction) { auto current_node = this; Hy3Node* sibling = nullptr; @@ -894,6 +861,10 @@ int directionToIteratorIncrement(ShiftDirection direction) { } } +CBox Hy3Node::getMainSurfaceBox() { + return { this->position, this->size }; +} + void Hy3Node::resize(ShiftDirection direction, double delta, bool no_animation) { auto& parent_node = this->parent; auto& containing_group = parent_node->data.as_group; @@ -951,3 +922,19 @@ void Hy3Node::swapData(Hy3Node& a, Hy3Node& b) { } } } + +bool Hy3Node::hasChild(Hy3Node* node) { + if(this->data.type == Hy3NodeType::Window) return false; + + auto n = node; + while(n != nullptr && n->parent != this) n = n->parent; + + return n != nullptr; +} + +Hy3Node* Hy3Node::getRoot() { + Hy3Node* maybeRoot = this; + while(maybeRoot->parent) maybeRoot = maybeRoot->parent; + + return maybeRoot; +} \ No newline at end of file diff --git a/src/Hy3Node.hpp b/src/Hy3Node.hpp index f892efb..88f4d7c 100644 --- a/src/Hy3Node.hpp +++ b/src/Hy3Node.hpp @@ -10,6 +10,7 @@ enum class Hy3GroupLayout; #include "Hy3Layout.hpp" #include "TabGroup.hpp" +#include "conversions.hpp" enum class Hy3GroupLayout { SplitH, @@ -42,7 +43,6 @@ struct Hy3GroupData { Hy3GroupData(Hy3GroupLayout layout); ~Hy3GroupData(); - bool hasChild(Hy3Node* child); void collapseExpansions(); void setLayout(Hy3GroupLayout layout); void setEphemeral(GroupEphemeralityOption ephemeral); @@ -101,9 +101,11 @@ struct Hy3Node { Hy3Node* getFocusedNode(bool ignore_group_focus = false, bool stop_at_expanded = false); Hy3Node* findNeighbor(ShiftDirection); Hy3Node* getImmediateSibling(ShiftDirection); + Hy3Node* getRoot(); void resize(ShiftDirection, double, bool no_animation = false); bool isIndirectlyFocused(); Hy3Node& getExpandActor(); + CBox getMainSurfaceBox(); void recalcSizePosRecursive(bool no_animation = false); void updateTabBar(bool no_animation = false); @@ -116,6 +118,7 @@ struct Hy3Node { Hy3Node* findNodeForTabGroup(Hy3TabGroup&); void appendAllWindows(std::vector&); + bool hasChild(Hy3Node* child); std::string debugNode(); // Remove this node from its parent, deleting the parent if it was @@ -131,3 +134,35 @@ struct Hy3Node { static bool swallowGroups(Hy3Node* into); static void swapData(Hy3Node&, Hy3Node&); }; + +struct Distance { + bool is_forward; + double primary_axis = -1; + double secondary_axis = -1; + bool operator< (Distance other) { + return isInitialised() + && other.isInitialised() + && is_forward == other.is_forward + && (primary_axis < other.primary_axis || (primary_axis == other.primary_axis && secondary_axis < other.secondary_axis)); + } + bool isInitialised() { return primary_axis != -1; } + bool isNotInitialised() { return primary_axis == -1; } + bool isSameDirection(Distance other) { + return other.primary_axis != 0 && other.is_forward == is_forward; + } + bool isInDirection(ShiftDirection direction) { + bool direction_is_forward = getSearchDirection(direction) == SearchDirection::Forwards; + return is_forward == direction_is_forward; + } + Distance(ShiftDirection direction) { + is_forward = getSearchDirection(direction) == SearchDirection::Forwards; + } + Distance(ShiftDirection direction, CBox from, CBox to) { + auto middle_from = from.middle(), middle_to = to.middle(); + auto primary_dist = getAxis(direction) == Axis::Horizontal ? middle_from.x - middle_to.x : middle_from.y - middle_to.y; + + is_forward = std::signbit(primary_dist); + primary_axis = abs(primary_dist); + secondary_axis = abs(getAxis(direction) == Axis::Horizontal ? middle_from.y - middle_to.y : middle_from.x - middle_to.x); + } +}; \ No newline at end of file diff --git a/src/conversions.cpp b/src/conversions.cpp new file mode 100644 index 0000000..07bdccc --- /dev/null +++ b/src/conversions.cpp @@ -0,0 +1,51 @@ +#include "Hy3Node.hpp" + +Axis getAxis(Hy3GroupLayout layout) { + switch (layout) + { + case Hy3GroupLayout::SplitH: + return Axis::Horizontal; + case Hy3GroupLayout::SplitV: + return Axis::Vertical; + default: + return Axis::None; + } +} + +Axis getAxis(ShiftDirection direction) { + switch (direction) + { + case ShiftDirection::Left: + case ShiftDirection::Right: + return Axis::Horizontal; + case ShiftDirection::Down: + case ShiftDirection::Up: + return Axis::Vertical; + default: + return Axis::None; + } +} + +SearchDirection getSearchDirection(ShiftDirection direction) { + switch(direction) { + case ShiftDirection::Left: + case ShiftDirection::Up: + return SearchDirection::Backwards; + case ShiftDirection::Right: + case ShiftDirection::Down: + return SearchDirection::Forwards; + default: + return SearchDirection::None; + } +} + +char directionToChar(ShiftDirection direction) { + switch (direction) + { + case ShiftDirection::Left: return 'l'; + case ShiftDirection::Up: return 'u'; + case ShiftDirection::Down: return 'd'; + case ShiftDirection::Right: return 'r'; + default: return 'r'; + } +} diff --git a/src/conversions.hpp b/src/conversions.hpp new file mode 100644 index 0000000..96f1c70 --- /dev/null +++ b/src/conversions.hpp @@ -0,0 +1,6 @@ +#include "Hy3Node.hpp" + +Axis getAxis(Hy3GroupLayout); +Axis getAxis(ShiftDirection); +SearchDirection getSearchDirection(ShiftDirection); +char directionToChar(ShiftDirection); diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp index 5661a7a..a0e0396 100644 --- a/src/dispatchers.cpp +++ b/src/dispatchers.cpp @@ -239,7 +239,7 @@ void dispatch_debug(std::string arg) { if (workspace == -1) return; auto* root = g_Hy3Layout->getWorkspaceRootGroup(workspace); - if (workspace == -1) { + if (root == nullptr) { hy3_log(LOG, "DEBUG NODES: no nodes on workspace"); } else { hy3_log(LOG, "DEBUG NODES\n{}", root->debugNode().c_str()); diff --git a/src/main.cpp b/src/main.cpp index a7f69a9..013776c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,6 +38,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { CONF("node_collapse_policy", INT, 2); CONF("group_inset", INT, 10); CONF("tab_first_window", INT, 0); + CONF("kbd_shift_delta", INT, 20); // tabs CONF("tabs:height", INT, 15); From bca5a9dbd277a4a66e26df02070179909926b841 Mon Sep 17 00:00:00 2001 From: Pete Appleton Date: Sat, 17 Feb 2024 23:11:45 +0000 Subject: [PATCH 03/13] Fix: Only look for nodes on neighbor monitor in correct direction Use last-focused-window when navigating to monitor by direction if it has same relative position as the logically closest window --- CHANGELOG.md | 2 +- src/Hy3Layout.cpp | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 328c603..1564940 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - Implement `resizeactivewindow` for floating windows - Fully implement `resizeactivewindow` for tiled windows -- Add `hy3:resizenode` dispatcher, drop-in replacement for `resizeactivewindow` applied at the Hy3 group level. +- `hy3:resizenode` added, drop-in replacement for `resizeactivewindow` applied to a whole node. - Implement keyboard-based focusing for floating windows - Implement keyboard-based movement for floating windows - Add configuration `kbd_shift_delta` providing delta [in pixels] for shift diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index 356ca94..f98721f 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -1078,8 +1078,14 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, bool co const auto current_surface_box = source->getWindowMainSurfaceBox(); auto target_distance = Distance { direction }; + // TODO: Don't assume that source window is on focused monitor + // BUG: This will only find windows on the immediately neighbouring monitor, it won't find any on + // the neighbour's neighbour if the immediate neighbour happens to be empty + CMonitor* other_monitor = considerOtherMonitors ? g_pCompositor->getMonitorInDirection(directionToChar(direction)) + : nullptr; + auto isCandidate = [=, mon = source->m_iMonitorID](CWindow* w) { - return (considerOtherMonitors || w->m_iMonitorID == mon) + return (w->m_iMonitorID == mon || (other_monitor && w->m_iMonitorID == other_monitor->ID)) && ((considerFloating && w->m_bIsFloating) || (considerTiled && !w->m_bIsFloating) || (w->m_iMonitorID != mon)) && w->m_bIsMapped && w->m_iX11Type != 2 @@ -1100,6 +1106,27 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, bool co } hy3_log(LOG, "getWindowInDirection: closest window to {} is {}", source, target_window); + + // If the closest window is on a different monitor and the nearest edge has the same position + // as the last focused window on that monitor's workspace then choose the last focused window instead + if(target_window && other_monitor && target_window->m_iMonitorID == other_monitor->ID) { + auto new_workspace = g_pCompositor->getWorkspaceByID(other_monitor->activeWorkspace); + if(new_workspace) { + auto last_focused = new_workspace->getLastFocusedWindow(); + if(last_focused) { + auto target_bounds = CBox(target_window->m_vRealPosition.vec(), target_window->m_vRealSize.vec()); + auto last_focused_bounds = CBox(last_focused->m_vRealPosition.vec(), last_focused->m_vRealSize.vec()); + if((direction == ShiftDirection::Left && target_bounds.x + target_bounds.w == last_focused_bounds.x + last_focused_bounds.w) + || (direction == ShiftDirection::Right && target_bounds.x == last_focused_bounds.x) + || (direction == ShiftDirection::Up && target_bounds.y + target_bounds.h == last_focused_bounds.y + last_focused_bounds.h) + || (direction == ShiftDirection::Down && target_bounds.y == last_focused_bounds.y)) { + target_window = last_focused; + } + } + } + } + + return target_window; } From 19f3cb0b11cb64ace6b41f2ff0668e8133924459 Mon Sep 17 00:00:00 2001 From: Pete Appleton Date: Sun, 18 Feb 2024 08:08:56 +0000 Subject: [PATCH 04/13] Add config field definitions --- src/Hy3Layout.cpp | 16 ++-------------- src/main.cpp | 2 ++ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index f98721f..c82152b 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -1042,23 +1042,16 @@ bool isObscured (CWindow* window) { bool is_obscured = false; for(auto& w :g_pCompositor->m_vWindows | std::views::reverse) { if(w.get() == window) { - hy3_log(LOG, "{} doesn't obscure itself", window); // Don't go any further if this is a floating window, because m_vWindows is sorted bottom->top per Compositor.cpp if(window->m_bIsFloating) break; else continue; } - if(!w->m_bIsFloating) { - hy3_log(LOG, "Tiled window {} can't obscure anything", w.get()); - continue; - } + if(!w->m_bIsFloating) continue; const auto outer_box = w->getWindowMainSurfaceBox(); is_obscured = covers(outer_box, inner_box); - if(is_obscured) { - hy3_log(LOG, "{} obscures {}", w.get(), window); - break; - } + if(is_obscured) break; }; return is_obscured; @@ -1221,7 +1214,6 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible Hy3Node* Hy3Layout::getFocusOverride(CWindow* src, ShiftDirection direction) { if(auto intercept = this->m_focusIntercepts.find(src); intercept != this->m_focusIntercepts.end()) { - hy3_log(LOG, "getFocusOverride: Found intercept for {}", src); Hy3Node** accessor; switch (direction) @@ -1252,14 +1244,11 @@ Hy3Node* Hy3Layout::getFocusOverride(CWindow* src, ShiftDirection direction) { } } - hy3_log(LOG, "getFocusOverride: No intercept found for: {}", src); return nullptr; } void Hy3Layout::setFocusOverride(CWindow* src, ShiftDirection direction, Hy3Node* dest) { - hy3_log(LOG, "setFocusOverride: Storing intercept for: {} to: {:x}", src, (uintptr_t)dest); if(auto intercept = this->m_focusIntercepts.find(src);intercept != this->m_focusIntercepts.end()) { - hy3_log(LOG, "setFocusOverride: Updating existing intercept"); switch(direction) { case ShiftDirection::Left: intercept->second.left = dest; break; case ShiftDirection::Up: intercept->second.up = dest; break; @@ -1268,7 +1257,6 @@ void Hy3Layout::setFocusOverride(CWindow* src, ShiftDirection direction, Hy3Node default: hy3_log(WARN, "Unknown ShiftDirection: {}", (int) direction); } } else { - hy3_log(LOG, "setFocusOverride: Adding new intercept"); FocusOverride override; switch(direction) { case ShiftDirection::Left: override.left = dest; break; diff --git a/src/main.cpp b/src/main.cpp index 013776c..a465f3d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,6 +39,8 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { CONF("group_inset", INT, 10); CONF("tab_first_window", INT, 0); CONF("kbd_shift_delta", INT, 20); + CONF("default_movefocus_layer", STRING, "samelayer"); + CONF("focus_obscured_windows", INT, 0); // tabs CONF("tabs:height", INT, 15); From f3dd53d76a0ad6be961c4b7fe90624874b028c91 Mon Sep 17 00:00:00 2001 From: Pete Appleton Date: Sun, 18 Feb 2024 09:08:37 +0000 Subject: [PATCH 05/13] Implement `focus_obscured_windows_policy` logic --- README.md | 14 ++++++++ src/BitFlag.hpp | 69 +++++++++++++++++++++++++++++++++++++++ src/Hy3Layout.cpp | 79 +++++++++++++++++++++++++++------------------ src/Hy3Layout.hpp | 17 +++++++++- src/dispatchers.cpp | 24 +++++++++++++- src/main.cpp | 2 +- 6 files changed, 171 insertions(+), 34 deletions(-) create mode 100644 src/BitFlag.hpp diff --git a/README.md b/README.md index a422339..0ec413e 100644 --- a/README.md +++ b/README.md @@ -221,6 +221,20 @@ plugin { # 2 = keep the nested group only if its parent is a tab group node_collapse_policy = # default: 2 + # policy controlling what windows will be focused using `hy3:movefocused` + # 0 = focus strictly by layout, don't attempt to skip windows that are obscured by another one + # 1 = do not focus windows which are entirely obscured by a floating window + # 2 = when `hy3:movefocus` layer is `samelayer` then use focus policy 0 (focus strictly by layout) + # when `hy3:movefocus` layer is `all` then use focus policy 1 (don't focus obscured windows) + focus_obscured_windows_policy = # default: 2 + + # which layers should be considered when changing focus with `hy3:movefocus`? + # samelayer = only focus windows on same layer as the source window (floating or tiled) + # all = choose the closest window irrespective of the layout + # tiled = only choose tiled windows, not especially useful but permitted by parser + # floating = only choose floating windows, not especially useful but permitted by parser + default_movefocus_layer = # default: `samelayer` + # offset from group split direction when only one window is in a group group_inset = # default: 10 diff --git a/src/BitFlag.hpp b/src/BitFlag.hpp new file mode 100644 index 0000000..8332a06 --- /dev/null +++ b/src/BitFlag.hpp @@ -0,0 +1,69 @@ +#ifndef BITFLAG +#define BITFLAG + +template +struct BitFlag +{ + int m_FlagValue = 0; + + BitFlag() = default; + + BitFlag(FlagType flag) { + m_FlagValue = (int)flag; + } + + operator FlagType() const { + return static_cast(m_FlagValue); + } + + void Set(FlagType flag) { + m_FlagValue |= (int)flag; + } + + void Unset(FlagType flag) { + m_FlagValue &= ~(int)flag; + } + + void Toggle(FlagType flag) { + m_FlagValue ^= (int)flag; + } + + void Mask(FlagType flag) { + m_FlagValue &= (int)flag; + } + + bool Has(FlagType flag) const { + return (m_FlagValue & (int)flag) == (int)flag; + } + + bool HasAny(FlagType flag) const { + return (m_FlagValue & (int)flag) != 0; + } + bool HasNot(FlagType flag) const { + return (m_FlagValue & (int)flag) != (int)flag; + } + + const BitFlag& operator |=(FlagType flag) { + Set(flag); + return *this; + } + + const BitFlag& operator &=(FlagType flag) { + Mask(flag); + return *this; + } + + const BitFlag& operator ^=(FlagType flag) { + Toggle(flag); + return *this; + } + + bool operator==(FlagType flag) const { + return m_FlagValue == (int)flag; + } + + bool operator!=(FlagType flag) const { + return m_FlagValue != (int)flag; + } +}; +#endif \ No newline at end of file diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index c82152b..14089c0 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -10,6 +10,7 @@ #include "SelectionHook.hpp" #include "globals.hpp" #include "conversions.hpp" +#include "BitFlag.hpp" std::unique_ptr renderHookPtr = std::make_unique(Hy3Layout::renderHook); @@ -948,7 +949,7 @@ void shiftFloatingWindow(CWindow* window, ShiftDirection direction) { g_pCompositor->setActiveMonitor(new_monitor); static auto* const allow_workspace_cycles = - &g_pConfigManager->getConfigValuePtr("binds:allow_workspace_cycles")->intValue; + &HyprlandAPI::getConfigValue(PHANDLE, "binds:allow_workspace_cycles")->intValue; if (*allow_workspace_cycles) new_workspace->rememberPrevWorkspace(old_workspace); } } else { @@ -1064,22 +1065,30 @@ bool isObscured(Hy3Node* node) { bool isNotObscured(CWindow* window) { return !isObscured(window); } bool isNotObscured(Hy3Node* node) { return !isObscured(node); } -CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, bool considerFloating, bool considerTiled, bool considerOtherMonitors) { +CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, BitFlag layers_same_monitor, BitFlag layers_other_monitors) { if(!source) return nullptr; + if(layers_other_monitors == Layer::None && layers_same_monitor == Layer::None) return nullptr; CWindow *target_window = nullptr; const auto current_surface_box = source->getWindowMainSurfaceBox(); auto target_distance = Distance { direction }; + int focus_policy = *&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:focus_obscured_windows_policy")->intValue; + bool permit_obscured_windows = focus_policy == 0 || (focus_policy == 2 && layers_same_monitor.HasNot(Layer::Floating | Layer::Tiled)); + // TODO: Don't assume that source window is on focused monitor // BUG: This will only find windows on the immediately neighbouring monitor, it won't find any on // the neighbour's neighbour if the immediate neighbour happens to be empty - CMonitor* other_monitor = considerOtherMonitors ? g_pCompositor->getMonitorInDirection(directionToChar(direction)) - : nullptr; + CMonitor* other_monitor = layers_other_monitors.HasAny(Layer::Floating | Layer::Tiled) + ? g_pCompositor->getMonitorInDirection(directionToChar(direction)) + : nullptr; auto isCandidate = [=, mon = source->m_iMonitorID](CWindow* w) { + const auto window_layer = w->m_bIsFloating ? Layer::Floating : Layer::Tiled; + const auto monitor_flags = w->m_iMonitorID == mon ? layers_same_monitor : layers_other_monitors; + return (w->m_iMonitorID == mon || (other_monitor && w->m_iMonitorID == other_monitor->ID)) - && ((considerFloating && w->m_bIsFloating) || (considerTiled && !w->m_bIsFloating) || (w->m_iMonitorID != mon)) + && (monitor_flags.Has(window_layer)) && w->m_bIsMapped && w->m_iX11Type != 2 && !w->m_bNoFocus @@ -1091,7 +1100,7 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, bool co auto w = pw.get(); if(w != source && isCandidate(w)) { auto dist = Distance { direction, current_surface_box, w->getWindowMainSurfaceBox() }; - if((dist < target_distance || (target_distance.isNotInitialised() && dist.isInDirection(direction))) && isNotObscured(w) ) { + if((dist < target_distance || (target_distance.isNotInitialised() && dist.isInDirection(direction))) && (permit_obscured_windows || isNotObscured(w)) ) { target_window = w; target_distance = dist; } @@ -1101,12 +1110,11 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, bool co hy3_log(LOG, "getWindowInDirection: closest window to {} is {}", source, target_window); // If the closest window is on a different monitor and the nearest edge has the same position - // as the last focused window on that monitor's workspace then choose the last focused window instead + // as the last focused window on that monitor's workspace then choose the last focused window instead; this + // allows seamless back-and-forth by direction keys if(target_window && other_monitor && target_window->m_iMonitorID == other_monitor->ID) { - auto new_workspace = g_pCompositor->getWorkspaceByID(other_monitor->activeWorkspace); - if(new_workspace) { - auto last_focused = new_workspace->getLastFocusedWindow(); - if(last_focused) { + if (auto new_workspace = g_pCompositor->getWorkspaceByID(other_monitor->activeWorkspace)) { + if (auto last_focused = new_workspace->getLastFocusedWindow()) { auto target_bounds = CBox(target_window->m_vRealPosition.vec(), target_window->m_vRealSize.vec()); auto last_focused_bounds = CBox(last_focused->m_vRealPosition.vec(), last_focused->m_vRealSize.vec()); if((direction == ShiftDirection::Left && target_bounds.x + target_bounds.w == last_focused_bounds.x + last_focused_bounds.w) @@ -1119,7 +1127,6 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, bool co } } - return target_window; } @@ -1128,20 +1135,18 @@ void Hy3Layout::shiftFocusToMonitor(ShiftDirection direction) { if(target_monitor) this->focusMonitor(target_monitor); } -void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible) { +void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible, BitFlag eligible_layers) { Hy3Node *candidate_node = nullptr; CWindow *closest_window = nullptr; Hy3Node *source_node = nullptr; CWindow *source_window = g_pCompositor->m_pLastWindow; CWorkspace *source_workspace = g_pCompositor->getWorkspaceByID(workspace); - if(source_window == nullptr) { + if(source_window == nullptr || (source_workspace && source_workspace->m_bHasFullscreenWindow)) { shiftFocusToMonitor(direction); return; } - if(source_workspace == nullptr) return; - hy3_log(LOG, "shiftFocus: Source: {} ({}), workspace: {}, direction: {}, visible: {}", source_window, @@ -1151,24 +1156,35 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible visible ); + // If no eligible_layers specified then choose the same layer as the source window + if(eligible_layers == Layer::None) eligible_layers = source_window->m_bIsFloating ? Layer::Floating : Layer::Tiled; + + int focus_policy = *&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:focus_obscured_windows_policy")->intValue; + bool skip_obscured = focus_policy == 1 || (focus_policy == 2 && eligible_layers.Has(Layer::Floating | Layer::Tiled)); + // Determine the starting point for looking for a tiled node - it's either the // workspace's focused node or the floating window's focus entry point (which may be null) - source_node = source_window->m_bIsFloating ? getFocusOverride(source_window, direction) - : getWorkspaceFocusedNode(workspace); + if (eligible_layers.Has(Layer::Tiled)) { + source_node = source_window->m_bIsFloating ? getFocusOverride(source_window, direction) + : getWorkspaceFocusedNode(workspace); - // Get the closest node to the starting point - if(source_node) { - candidate_node = this->shiftOrGetFocus(*source_node, direction, false, false, visible); - while(candidate_node && !isNotObscured(candidate_node)) { - candidate_node = this->shiftOrGetFocus(*candidate_node, direction, false, false, visible); + if(source_node) { + candidate_node = this->shiftOrGetFocus(*source_node, direction, false, false, visible); + while(candidate_node && skip_obscured && isObscured(candidate_node)) { + candidate_node = this->shiftOrGetFocus(*candidate_node, direction, false, false, visible); + } } } - bool select_tiled_windows = source_window->m_bIsFloating && !candidate_node; + BitFlag this_monitor = eligible_layers & Layer::Floating; + if(source_window->m_bIsFloating && !candidate_node) this_monitor |= (eligible_layers & Layer::Tiled); - // Find the closest window in the right direction. Only consider tiled windows or other monitors - // if `shiftOrGetFocus` didn't provide a candidate. - closest_window = getWindowInDirection(source_window, direction, true, select_tiled_windows, !candidate_node); + BitFlag other_monitors; + if(!candidate_node) other_monitors |= eligible_layers; + + // Find the closest window in the right direction. Consider other monitors + // if we don't have a tiled candidate + closest_window = getWindowInDirection(source_window, direction, this_monitor, other_monitors); // If there's a window in the right direction then choose between that window and the tiled candidate. bool focus_closest_window = false; @@ -1189,8 +1205,7 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible } } - auto new_monitor_id = source_window->m_iMonitorID; - + std::optional new_monitor_id; if(focus_closest_window) { new_monitor_id = closest_window->m_iMonitorID; setFocusOverride(closest_window, direction, source_node); @@ -1198,6 +1213,8 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible } else if(candidate_node) { if(candidate_node->data.type == Hy3NodeType::Window) { new_monitor_id = candidate_node->data.as_window->m_iMonitorID; + } else if(auto *workspace = g_pCompositor->getWorkspaceByID(candidate_node->getRoot()->workspace_id)) { + new_monitor_id = workspace->m_iMonitorID; } candidate_node->focusWindow(); candidate_node->getRoot()->recalcSizePosRecursive(); @@ -1205,8 +1222,8 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible shiftFocusToMonitor(direction); } - if(new_monitor_id != source_window->m_iMonitorID) { - if(auto *monitor = g_pCompositor->getMonitorFromID(new_monitor_id)) { + if(new_monitor_id.has_value()) { + if(auto *monitor = g_pCompositor->getMonitorFromID(new_monitor_id.value())) { g_pCompositor->setActiveMonitor(monitor); } } diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp index 19cfe21..359f6bb 100644 --- a/src/Hy3Layout.hpp +++ b/src/Hy3Layout.hpp @@ -4,6 +4,7 @@ #include #include +#include "BitFlag.hpp" class Hy3Layout; @@ -28,6 +29,20 @@ enum class SearchDirection { enum class Axis { None, Horizontal, Vertical }; +enum class Layer { + None = 0, + Tiled = 1 << 0, + Floating = 1 << 1 +}; + +inline Layer operator| (Layer a, Layer b) { + return static_cast((int)a | (int)b); +} + +inline Layer operator& (Layer a, Layer b) { + return static_cast((int)a & (int)b); +} + #include "Hy3Node.hpp" #include "TabGroup.hpp" #include "conversions.hpp" @@ -125,7 +140,7 @@ public: void changeGroupEphemeralityOn(Hy3Node&, bool ephemeral); void shiftNode(Hy3Node&, ShiftDirection, bool once, bool visible); void shiftWindow(int workspace, ShiftDirection, bool once, bool visible); - void shiftFocus(int workspace, ShiftDirection, bool visible); + void shiftFocus(int workspace, ShiftDirection, bool visible, BitFlag = Layer::None); void moveNodeToWorkspace(int origin, std::string wsname, bool follow); void changeFocus(int workspace, FocusShift); void focusTab(int workspace, TabFocus target, TabFocusMousePriority, bool wrap_scroll, int index); diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp index a0e0396..60f8fda 100644 --- a/src/dispatchers.cpp +++ b/src/dispatchers.cpp @@ -84,6 +84,14 @@ std::optional parseShiftArg(std::string arg) { else return {}; } +std::optional> parseLayerArg(std::string arg) { + if (arg == "same" || arg == "samelayer") return Layer::None; + else if (arg == "tiled") return Layer::Tiled; + else if (arg == "floating") return Layer::Floating; + else if (arg == "all" || arg == "any") return Layer::Tiled | Layer::Floating; + else return {}; +} + void dispatch_movewindow(std::string value) { int workspace = workspace_for_action(); if (workspace == -1) return; @@ -114,9 +122,23 @@ void dispatch_movefocus(std::string value) { if (workspace == -1) return; auto args = CVarList(value); + std::optional> layerArg; if (auto shift = parseShiftArg(args[0])) { - g_Hy3Layout->shiftFocus(workspace, shift.value(), args[1] == "visible"); + bool visible; + BitFlag layers; + + for(auto arg: args) { + if(arg == "visible") visible = true; + else if ((layerArg = parseLayerArg(arg))) layers = layerArg.value(); + } + + if(!layerArg) { + auto default_movefocus_layer = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:default_movefocus_layer")->strValue; + if((layerArg = parseLayerArg(*default_movefocus_layer))) layers = layerArg.value(); + } + + g_Hy3Layout->shiftFocus(workspace, shift.value(), visible, layers); } } diff --git a/src/main.cpp b/src/main.cpp index a465f3d..34d0eef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,7 +40,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { CONF("tab_first_window", INT, 0); CONF("kbd_shift_delta", INT, 20); CONF("default_movefocus_layer", STRING, "samelayer"); - CONF("focus_obscured_windows", INT, 0); + CONF("focus_obscured_windows_policy", INT, 2); // tabs CONF("tabs:height", INT, 15); From 1255b2f508f448d0b4fccd04e1365f3ad3a931c3 Mon Sep 17 00:00:00 2001 From: Pete Appleton Date: Sun, 18 Feb 2024 23:23:05 +0000 Subject: [PATCH 06/13] Update configuration to hyprlang --- src/Hy3Layout.cpp | 7 +++---- src/dispatchers.cpp | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index 14089c0..08eec3e 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -894,8 +894,7 @@ void Hy3Layout::shiftNode(Hy3Node& node, ShiftDirection direction, bool once, bo } void shiftFloatingWindow(CWindow* window, ShiftDirection direction) { - static const auto* kbd_shift_delta = - &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:kbd_shift_delta")->intValue; + static const auto kbd_shift_delta = ConfigValue("plugin:hy3:kbd_shift_delta"); if(!window) return; @@ -1073,7 +1072,7 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, BitFlag const auto current_surface_box = source->getWindowMainSurfaceBox(); auto target_distance = Distance { direction }; - int focus_policy = *&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:focus_obscured_windows_policy")->intValue; + int focus_policy = *ConfigValue("plugin:hy3:focus_obscured_windows_policy"); bool permit_obscured_windows = focus_policy == 0 || (focus_policy == 2 && layers_same_monitor.HasNot(Layer::Floating | Layer::Tiled)); // TODO: Don't assume that source window is on focused monitor @@ -1159,7 +1158,7 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible // If no eligible_layers specified then choose the same layer as the source window if(eligible_layers == Layer::None) eligible_layers = source_window->m_bIsFloating ? Layer::Floating : Layer::Tiled; - int focus_policy = *&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:focus_obscured_windows_policy")->intValue; + int focus_policy = *ConfigValue("plugin:hy3:focus_obscured_windows_policy"); bool skip_obscured = focus_policy == 1 || (focus_policy == 2 && eligible_layers.Has(Layer::Floating | Layer::Tiled)); // Determine the starting point for looking for a tiled node - it's either the diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp index 60f8fda..13939a9 100644 --- a/src/dispatchers.cpp +++ b/src/dispatchers.cpp @@ -134,7 +134,7 @@ void dispatch_movefocus(std::string value) { } if(!layerArg) { - auto default_movefocus_layer = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:default_movefocus_layer")->strValue; + auto default_movefocus_layer = ConfigValue("plugin:hy3:default_movefocus_layer"); if((layerArg = parseLayerArg(*default_movefocus_layer))) layers = layerArg.value(); } From 946076d380ad786746fe297ef1833b9bf6cfa5cf Mon Sep 17 00:00:00 2001 From: Pete Appleton Date: Mon, 19 Feb 2024 08:19:52 +0000 Subject: [PATCH 07/13] Fix compilation failure against hyprland trunk --- src/Hy3Layout.cpp | 15 +++++++-------- src/dispatchers.cpp | 6 +++--- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index 08eec3e..22382f7 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -947,8 +947,7 @@ void shiftFloatingWindow(CWindow* window, ShiftDirection direction) { g_pCompositor->moveWindowToWorkspaceSafe(window, new_workspace); g_pCompositor->setActiveMonitor(new_monitor); - static auto* const allow_workspace_cycles = - &HyprlandAPI::getConfigValue(PHANDLE, "binds:allow_workspace_cycles")->intValue; + const static auto allow_workspace_cycles = ConfigValue("binds:allow_workspace_cycles"); if (*allow_workspace_cycles) new_workspace->rememberPrevWorkspace(old_workspace); } } else { @@ -989,7 +988,7 @@ void Hy3Layout::focusMonitor(CMonitor* monitor) { for (auto& w: g_pCompositor->m_vWindows | std::views::reverse) { if (w->m_bIsMapped && !w->isHidden() && w->m_bIsFloating && w->m_iX11Type != 2 && w->m_iWorkspaceID == next_window->m_iWorkspaceID && !w->m_bX11ShouldntFocus - && !w->m_bNoFocus) + && !w->m_sAdditionalConfigData.noFocus) { next_window = w.get(); break; @@ -1072,8 +1071,8 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, BitFlag const auto current_surface_box = source->getWindowMainSurfaceBox(); auto target_distance = Distance { direction }; - int focus_policy = *ConfigValue("plugin:hy3:focus_obscured_windows_policy"); - bool permit_obscured_windows = focus_policy == 0 || (focus_policy == 2 && layers_same_monitor.HasNot(Layer::Floating | Layer::Tiled)); + const auto static focus_policy = ConfigValue("plugin:hy3:focus_obscured_windows_policy"); + bool permit_obscured_windows = *focus_policy == 0 || (*focus_policy == 2 && layers_same_monitor.HasNot(Layer::Floating | Layer::Tiled)); // TODO: Don't assume that source window is on focused monitor // BUG: This will only find windows on the immediately neighbouring monitor, it won't find any on @@ -1090,7 +1089,7 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, BitFlag && (monitor_flags.Has(window_layer)) && w->m_bIsMapped && w->m_iX11Type != 2 - && !w->m_bNoFocus + && !w->m_sAdditionalConfigData.noFocus && !w->isHidden() && !w->m_bX11ShouldntFocus; }; @@ -1158,8 +1157,8 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible // If no eligible_layers specified then choose the same layer as the source window if(eligible_layers == Layer::None) eligible_layers = source_window->m_bIsFloating ? Layer::Floating : Layer::Tiled; - int focus_policy = *ConfigValue("plugin:hy3:focus_obscured_windows_policy"); - bool skip_obscured = focus_policy == 1 || (focus_policy == 2 && eligible_layers.Has(Layer::Floating | Layer::Tiled)); + const auto static focus_policy = ConfigValue("plugin:hy3:focus_obscured_windows_policy"); + bool skip_obscured = *focus_policy == 1 || (*focus_policy == 2 && eligible_layers.Has(Layer::Floating | Layer::Tiled)); // Determine the starting point for looking for a tiled node - it's either the // workspace's focused node or the floating window's focus entry point (which may be null) diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp index 13939a9..74787f8 100644 --- a/src/dispatchers.cpp +++ b/src/dispatchers.cpp @@ -130,12 +130,12 @@ void dispatch_movefocus(std::string value) { for(auto arg: args) { if(arg == "visible") visible = true; - else if ((layerArg = parseLayerArg(arg))) layers = layerArg.value(); + else if ((layerArg = parseLayerArg(arg))) layers |= layerArg.value(); } if(!layerArg) { - auto default_movefocus_layer = ConfigValue("plugin:hy3:default_movefocus_layer"); - if((layerArg = parseLayerArg(*default_movefocus_layer))) layers = layerArg.value(); + const static auto default_movefocus_layer = ConfigValue("plugin:hy3:default_movefocus_layer"); + if((layerArg = parseLayerArg(*default_movefocus_layer))) layers |= layerArg.value(); } g_Hy3Layout->shiftFocus(workspace, shift.value(), visible, layers); From 82f6283a8fdeb704fefc04319e4136c439e1d9d5 Mon Sep 17 00:00:00 2001 From: Pete Appleton Date: Mon, 19 Feb 2024 21:09:55 +0000 Subject: [PATCH 08/13] Refactor distance calculation --- src/Hy3Layout.cpp | 16 +++++++------- src/Hy3Node.cpp | 5 +++-- src/Hy3Node.hpp | 55 +++++++++++++++++++++++------------------------ 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index 22382f7..3e9638a 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -1068,8 +1068,8 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, BitFlag if(layers_other_monitors == Layer::None && layers_same_monitor == Layer::None) return nullptr; CWindow *target_window = nullptr; - const auto current_surface_box = source->getWindowMainSurfaceBox(); - auto target_distance = Distance { direction }; + const auto source_middle = source->middle(); + std::optional target_distance; const auto static focus_policy = ConfigValue("plugin:hy3:focus_obscured_windows_policy"); bool permit_obscured_windows = *focus_policy == 0 || (*focus_policy == 2 && layers_same_monitor.HasNot(Layer::Floating | Layer::Tiled)); @@ -1097,8 +1097,8 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, BitFlag for(auto &pw: g_pCompositor->m_vWindows) { auto w = pw.get(); if(w != source && isCandidate(w)) { - auto dist = Distance { direction, current_surface_box, w->getWindowMainSurfaceBox() }; - if((dist < target_distance || (target_distance.isNotInitialised() && dist.isInDirection(direction))) && (permit_obscured_windows || isNotObscured(w)) ) { + auto dist = Distance { direction, source_middle, w->middle() }; + if((target_distance.has_value() ? dist < target_distance.value() : dist.isInDirection(direction)) && (permit_obscured_windows || isNotObscured(w)) ) { target_window = w; target_distance = dist; } @@ -1191,9 +1191,9 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible // If the closest window is tiled then focus the tiled node which was obtained from `shiftOrGetFocus`, // otherwise focus whichever is closer if(closest_window->m_bIsFloating) { - const auto source_box = source_window->getWindowMainSurfaceBox(); - Distance distanceToClosestWindow(direction, source_box, closest_window->getWindowMainSurfaceBox()); - Distance distanceToTiledNode(direction, source_box, candidate_node->getMainSurfaceBox()); + Distance distanceToClosestWindow(direction, source_window->middle(), closest_window->middle()); + Distance distanceToTiledNode(direction, source_window->middle(), candidate_node->middle()); + if(distanceToClosestWindow < distanceToTiledNode) { focus_closest_window = true; } @@ -1220,7 +1220,7 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible shiftFocusToMonitor(direction); } - if(new_monitor_id.has_value()) { + if(new_monitor_id && new_monitor_id.value() != source_window->m_iMonitorID) { if(auto *monitor = g_pCompositor->getMonitorFromID(new_monitor_id.value())) { g_pCompositor->setActiveMonitor(monitor); } diff --git a/src/Hy3Node.cpp b/src/Hy3Node.cpp index 6712919..f6c93cc 100644 --- a/src/Hy3Node.cpp +++ b/src/Hy3Node.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -861,8 +862,8 @@ int directionToIteratorIncrement(ShiftDirection direction) { } } -CBox Hy3Node::getMainSurfaceBox() { - return { this->position, this->size }; +Vector2D Hy3Node::middle() { + return this->position + this->size / 2.f; } void Hy3Node::resize(ShiftDirection direction, double delta, bool no_animation) { diff --git a/src/Hy3Node.hpp b/src/Hy3Node.hpp index 88f4d7c..42f3c9c 100644 --- a/src/Hy3Node.hpp +++ b/src/Hy3Node.hpp @@ -98,6 +98,7 @@ struct Hy3Node { CWindow* bringToTop(); void markFocused(); void raiseToTop(); + Vector2D middle(); Hy3Node* getFocusedNode(bool ignore_group_focus = false, bool stop_at_expanded = false); Hy3Node* findNeighbor(ShiftDirection); Hy3Node* getImmediateSibling(ShiftDirection); @@ -105,7 +106,6 @@ struct Hy3Node { void resize(ShiftDirection, double, bool no_animation = false); bool isIndirectlyFocused(); Hy3Node& getExpandActor(); - CBox getMainSurfaceBox(); void recalcSizePosRecursive(bool no_animation = false); void updateTabBar(bool no_animation = false); @@ -136,33 +136,32 @@ struct Hy3Node { }; struct Distance { - bool is_forward; - double primary_axis = -1; - double secondary_axis = -1; - bool operator< (Distance other) { - return isInitialised() - && other.isInitialised() - && is_forward == other.is_forward - && (primary_axis < other.primary_axis || (primary_axis == other.primary_axis && secondary_axis < other.secondary_axis)); - } - bool isInitialised() { return primary_axis != -1; } - bool isNotInitialised() { return primary_axis == -1; } - bool isSameDirection(Distance other) { - return other.primary_axis != 0 && other.is_forward == is_forward; - } - bool isInDirection(ShiftDirection direction) { - bool direction_is_forward = getSearchDirection(direction) == SearchDirection::Forwards; - return is_forward == direction_is_forward; - } - Distance(ShiftDirection direction) { - is_forward = getSearchDirection(direction) == SearchDirection::Forwards; - } - Distance(ShiftDirection direction, CBox from, CBox to) { - auto middle_from = from.middle(), middle_to = to.middle(); - auto primary_dist = getAxis(direction) == Axis::Horizontal ? middle_from.x - middle_to.x : middle_from.y - middle_to.y; + double primary_axis; + double secondary_axis; - is_forward = std::signbit(primary_dist); - primary_axis = abs(primary_dist); - secondary_axis = abs(getAxis(direction) == Axis::Horizontal ? middle_from.y - middle_to.y : middle_from.x - middle_to.x); + Distance() = default; + + Distance(ShiftDirection direction, Vector2D from, Vector2D to) { + auto dist = from - to; + primary_axis = getAxis(direction) == Axis::Horizontal ? dist.x : dist.y; + secondary_axis = getAxis(direction) == Axis::Horizontal ? dist.y : dist.x; + } + + bool operator< (Distance other) { + return signbit(primary_axis) == signbit(other.primary_axis) + && (abs(primary_axis) < abs(other.primary_axis) || (primary_axis == other.primary_axis && abs(secondary_axis) < abs(other.secondary_axis))); + } + + bool operator> (Distance other) { + return signbit(primary_axis) == signbit(other.primary_axis) + && (abs(primary_axis) > abs(other.primary_axis) || (primary_axis == other.primary_axis && abs(secondary_axis) > abs(other.secondary_axis))); + } + + bool isSameDirection(Distance other) { + return signbit(primary_axis) == signbit(other.primary_axis); + } + + bool isInDirection(ShiftDirection direction) { + return std::signbit(primary_axis) == (getSearchDirection(direction) == SearchDirection::Forwards); } }; \ No newline at end of file From 3841f2382f2a76dc69379abe7b792b4ee6397694 Mon Sep 17 00:00:00 2001 From: github-usr-name Date: Sat, 24 Feb 2024 12:32:02 +0000 Subject: [PATCH 09/13] Address PR issues --- .gitignore | 2 -- src/BitFlag.hpp | 79 ++++++++++++++++------------------------------- src/Hy3Layout.cpp | 5 +-- 3 files changed, 30 insertions(+), 56 deletions(-) diff --git a/.gitignore b/.gitignore index f013aa6..36b6e89 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,3 @@ build/ compile_commands.json .vscode/ *.log -.env -.env.* \ No newline at end of file diff --git a/src/BitFlag.hpp b/src/BitFlag.hpp index 8332a06..6253157 100644 --- a/src/BitFlag.hpp +++ b/src/BitFlag.hpp @@ -1,69 +1,44 @@ -#ifndef BITFLAG -#define BITFLAG +#pragma once template -struct BitFlag -{ - int m_FlagValue = 0; +struct BitFlag { + int m_FlagValue = 0; - BitFlag() = default; + BitFlag() = default; - BitFlag(FlagType flag) { - m_FlagValue = (int)flag; - } + BitFlag(FlagType flag) { m_FlagValue = (int) flag; } - operator FlagType() const { - return static_cast(m_FlagValue); - } + operator FlagType() const { return static_cast(m_FlagValue); } - void Set(FlagType flag) { - m_FlagValue |= (int)flag; - } + void Set(FlagType flag) { m_FlagValue |= (int) flag; } - void Unset(FlagType flag) { - m_FlagValue &= ~(int)flag; - } + void Unset(FlagType flag) { m_FlagValue &= ~(int) flag; } - void Toggle(FlagType flag) { - m_FlagValue ^= (int)flag; - } + void Toggle(FlagType flag) { m_FlagValue ^= (int) flag; } - void Mask(FlagType flag) { - m_FlagValue &= (int)flag; - } + void Mask(FlagType flag) { m_FlagValue &= (int) flag; } - bool Has(FlagType flag) const { - return (m_FlagValue & (int)flag) == (int)flag; - } + bool Has(FlagType flag) const { return (m_FlagValue & (int) flag) == (int) flag; } - bool HasAny(FlagType flag) const { - return (m_FlagValue & (int)flag) != 0; - } - bool HasNot(FlagType flag) const { - return (m_FlagValue & (int)flag) != (int)flag; - } + bool HasAny(FlagType flag) const { return (m_FlagValue & (int) flag) != 0; } + bool HasNot(FlagType flag) const { return (m_FlagValue & (int) flag) != (int) flag; } - const BitFlag& operator |=(FlagType flag) { - Set(flag); - return *this; - } + const BitFlag& operator|=(FlagType flag) { + Set(flag); + return *this; + } - const BitFlag& operator &=(FlagType flag) { - Mask(flag); - return *this; - } + const BitFlag& operator&=(FlagType flag) { + Mask(flag); + return *this; + } - const BitFlag& operator ^=(FlagType flag) { - Toggle(flag); - return *this; - } + const BitFlag& operator^=(FlagType flag) { + Toggle(flag); + return *this; + } - bool operator==(FlagType flag) const { - return m_FlagValue == (int)flag; - } + bool operator==(FlagType flag) const { return m_FlagValue == (int) flag; } - bool operator!=(FlagType flag) const { - return m_FlagValue != (int)flag; - } + bool operator!=(FlagType flag) const { return m_FlagValue != (int) flag; } }; -#endif \ No newline at end of file diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index 3e9638a..4b62d72 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -506,7 +506,7 @@ void Hy3Layout::resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CW g_pXWaylandManager->setWindowSize(window, required_size); } else if (auto* node = this->getNodeFromWindow(window);node != nullptr) { executeResizeOperation(delta, corner, &node->getExpandActor(), g_pCompositor->getMonitorFromID(window->m_iMonitorID)); - }; + } } void Hy3Layout::fullscreenRequestForWindow( @@ -2056,8 +2056,9 @@ Hy3Node* Hy3Layout::shiftOrGetFocus( auto& group_data = target_group->data.as_group; if (group_data.children.empty()) { + // in theory this would never happen return nullptr; - } // in theory this would never happen + } bool shift_after = false; From e9b22b07223a4b4d9ff866394741ca1ae0bc98a5 Mon Sep 17 00:00:00 2001 From: github-usr-name Date: Mon, 26 Feb 2024 07:29:35 +0000 Subject: [PATCH 10/13] Fix assumption [that source window is focused] in `getWindowInDirection` --- src/Hy3Layout.cpp | 59 +++++++++++++++-------------------------------- src/Hy3Layout.hpp | 14 +++++++++++ 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index 4b62d72..99b071c 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -1074,24 +1074,27 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, BitFlag const auto static focus_policy = ConfigValue("plugin:hy3:focus_obscured_windows_policy"); bool permit_obscured_windows = *focus_policy == 0 || (*focus_policy == 2 && layers_same_monitor.HasNot(Layer::Floating | Layer::Tiled)); - // TODO: Don't assume that source window is on focused monitor - // BUG: This will only find windows on the immediately neighbouring monitor, it won't find any on - // the neighbour's neighbour if the immediate neighbour happens to be empty - CMonitor* other_monitor = layers_other_monitors.HasAny(Layer::Floating | Layer::Tiled) - ? g_pCompositor->getMonitorInDirection(directionToChar(direction)) - : nullptr; + const auto source_monitor = g_pCompositor->getMonitorFromID(source->m_iMonitorID); + const auto next_monitor = layers_other_monitors.HasAny(Layer::Floating | Layer::Tiled) + ? g_pCompositor->getMonitorInDirection(source_monitor, directionToChar(direction)) + : nullptr; + + const auto next_workspace = next_monitor + ? next_monitor->specialWorkspaceID ? next_monitor->specialWorkspaceID + : next_monitor->activeWorkspace + : WORKSPACE_INVALID; auto isCandidate = [=, mon = source->m_iMonitorID](CWindow* w) { const auto window_layer = w->m_bIsFloating ? Layer::Floating : Layer::Tiled; const auto monitor_flags = w->m_iMonitorID == mon ? layers_same_monitor : layers_other_monitors; - return (w->m_iMonitorID == mon || (other_monitor && w->m_iMonitorID == other_monitor->ID)) - && (monitor_flags.Has(window_layer)) + return (monitor_flags.Has(window_layer)) && w->m_bIsMapped && w->m_iX11Type != 2 && !w->m_sAdditionalConfigData.noFocus && !w->isHidden() - && !w->m_bX11ShouldntFocus; + && !w->m_bX11ShouldntFocus + && (w->m_bPinned || w->m_iWorkspaceID == source->m_iWorkspaceID || w->m_iWorkspaceID == next_workspace); }; for(auto &pw: g_pCompositor->m_vWindows) { @@ -1110,8 +1113,8 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, BitFlag // If the closest window is on a different monitor and the nearest edge has the same position // as the last focused window on that monitor's workspace then choose the last focused window instead; this // allows seamless back-and-forth by direction keys - if(target_window && other_monitor && target_window->m_iMonitorID == other_monitor->ID) { - if (auto new_workspace = g_pCompositor->getWorkspaceByID(other_monitor->activeWorkspace)) { + if(target_window && target_window->m_iWorkspaceID == next_workspace) { + if (auto new_workspace = g_pCompositor->getWorkspaceByID(next_workspace)) { if (auto last_focused = new_workspace->getLastFocusedWindow()) { auto target_bounds = CBox(target_window->m_vRealPosition.vec(), target_window->m_vRealSize.vec()); auto last_focused_bounds = CBox(last_focused->m_vRealPosition.vec(), last_focused->m_vRealSize.vec()); @@ -1229,28 +1232,14 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible Hy3Node* Hy3Layout::getFocusOverride(CWindow* src, ShiftDirection direction) { if(auto intercept = this->m_focusIntercepts.find(src); intercept != this->m_focusIntercepts.end()) { - Hy3Node** accessor; - - switch (direction) - { - case ShiftDirection::Left: accessor = &intercept->second.left; break; - case ShiftDirection::Up: accessor = &intercept->second.up; break; - case ShiftDirection::Right: accessor = &intercept->second.right; break; - case ShiftDirection::Down: accessor = &intercept->second.down; break; - default: hy3_log(WARN, "Unknown ShiftDirection: {}", (int) direction); return nullptr; - } + Hy3Node** accessor = intercept->second.forDirection(direction); if(auto override = *accessor) { // If the root isn't valid or is on a different workspsace then update the intercept data if(override->workspace_id != src->m_iWorkspaceID || !std::ranges::contains(this->nodes, *override)) { - override = nullptr; *accessor = nullptr; // If there are no remaining overrides then discard the intercept - if(intercept->second.left == nullptr - && intercept->second.up == nullptr - && intercept->second.right == nullptr - && intercept->second.down == nullptr - ) { + if(intercept->second.isEmpty()) { this->m_focusIntercepts.erase(intercept); } } @@ -1264,22 +1253,10 @@ Hy3Node* Hy3Layout::getFocusOverride(CWindow* src, ShiftDirection direction) { void Hy3Layout::setFocusOverride(CWindow* src, ShiftDirection direction, Hy3Node* dest) { if(auto intercept = this->m_focusIntercepts.find(src);intercept != this->m_focusIntercepts.end()) { - switch(direction) { - case ShiftDirection::Left: intercept->second.left = dest; break; - case ShiftDirection::Up: intercept->second.up = dest; break; - case ShiftDirection::Right: intercept->second.right = dest; break; - case ShiftDirection::Down: intercept->second.down = dest; break; - default: hy3_log(WARN, "Unknown ShiftDirection: {}", (int) direction); - } + *intercept->second.forDirection(direction) = dest; } else { FocusOverride override; - switch(direction) { - case ShiftDirection::Left: override.left = dest; break; - case ShiftDirection::Up: override.up = dest; break; - case ShiftDirection::Right: override.right = dest; break; - case ShiftDirection::Down: override.down = dest; break; - default: hy3_log(WARN, "Unknown ShiftDirection: {}", (int) direction); - } + *override.forDirection(direction) = dest; this->m_focusIntercepts.insert({ src, override }); } } diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp index 359f6bb..9eeb7a5 100644 --- a/src/Hy3Layout.hpp +++ b/src/Hy3Layout.hpp @@ -94,6 +94,20 @@ struct FocusOverride { Hy3Node *up = nullptr; Hy3Node *right = nullptr; Hy3Node *down = nullptr; + + Hy3Node **forDirection(ShiftDirection direction) { + switch(direction) { + case ShiftDirection::Left: return &left; + case ShiftDirection::Up: return &up; + case ShiftDirection::Right: return &right; + case ShiftDirection::Down: return &down; + default: UNREACHABLE(); + } + } + + bool isEmpty() { + return !(left || right || up || down); + } }; From cae91835afd7e664731dd652c2aa339735c19318 Mon Sep 17 00:00:00 2001 From: github-usr-name Date: Tue, 27 Feb 2024 08:11:56 +0000 Subject: [PATCH 11/13] Fix detection of pinned windows on other monitors --- src/Hy3Layout.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index 99b071c..73acad4 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -1113,15 +1113,16 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, BitFlag // If the closest window is on a different monitor and the nearest edge has the same position // as the last focused window on that monitor's workspace then choose the last focused window instead; this // allows seamless back-and-forth by direction keys - if(target_window && target_window->m_iWorkspaceID == next_workspace) { + if(target_window && target_window->m_iMonitorID != source->m_iMonitorID) { if (auto new_workspace = g_pCompositor->getWorkspaceByID(next_workspace)) { if (auto last_focused = new_workspace->getLastFocusedWindow()) { auto target_bounds = CBox(target_window->m_vRealPosition.vec(), target_window->m_vRealSize.vec()); auto last_focused_bounds = CBox(last_focused->m_vRealPosition.vec(), last_focused->m_vRealSize.vec()); - if((direction == ShiftDirection::Left && target_bounds.x + target_bounds.w == last_focused_bounds.x + last_focused_bounds.w) - || (direction == ShiftDirection::Right && target_bounds.x == last_focused_bounds.x) - || (direction == ShiftDirection::Up && target_bounds.y + target_bounds.h == last_focused_bounds.y + last_focused_bounds.h) - || (direction == ShiftDirection::Down && target_bounds.y == last_focused_bounds.y)) { + + if((direction == ShiftDirection::Left && STICKS(target_bounds.x + target_bounds.w, last_focused_bounds.x + last_focused_bounds.w)) + || (direction == ShiftDirection::Right && STICKS(target_bounds.x, last_focused_bounds.x)) + || (direction == ShiftDirection::Up && STICKS(target_bounds.y + target_bounds.h, last_focused_bounds.y + last_focused_bounds.h)) + || (direction == ShiftDirection::Down && STICKS(target_bounds.y, last_focused_bounds.y))) { target_window = last_focused; } } From f306ecc6895d0060b3b56f48ce90bc47a1027387 Mon Sep 17 00:00:00 2001 From: github-usr-name Date: Sat, 2 Mar 2024 10:31:22 +0000 Subject: [PATCH 12/13] Fix node navigation breakage --- src/Hy3Layout.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index 73acad4..5088955 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -1144,7 +1144,13 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible CWindow *source_window = g_pCompositor->m_pLastWindow; CWorkspace *source_workspace = g_pCompositor->getWorkspaceByID(workspace); - if(source_window == nullptr || (source_workspace && source_workspace->m_bHasFullscreenWindow)) { + if (source_workspace) { + source_window = source_workspace->m_pLastFocusedWindow; + } else { + source_window = g_pCompositor->m_pLastWindow; + } + + if (source_window == nullptr || (source_workspace && source_workspace->m_bHasFullscreenWindow)) { shiftFocusToMonitor(direction); return; } From a726135c861229c8cde9dcf3b01e12a1689aeea6 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Sat, 2 Mar 2024 05:18:59 -0800 Subject: [PATCH 13/13] Run clang-format --- src/Hy3Layout.cpp | 424 +++++++++++++++++++++++++------------------- src/Hy3Layout.hpp | 53 +++--- src/Hy3Node.cpp | 17 +- src/Hy3Node.hpp | 15 +- src/conversions.cpp | 42 ++--- src/dispatchers.cpp | 15 +- 6 files changed, 302 insertions(+), 264 deletions(-) diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index 5088955..8da9759 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -1,16 +1,17 @@ -#include -#include #include #include +#include +#include + #include #include #include +#include "BitFlag.hpp" #include "Hy3Layout.hpp" #include "SelectionHook.hpp" -#include "globals.hpp" #include "conversions.hpp" -#include "BitFlag.hpp" +#include "globals.hpp" std::unique_ptr renderHookPtr = std::make_unique(Hy3Layout::renderHook); @@ -346,9 +347,7 @@ void Hy3Layout::onWindowRemovedTiling(CWindow* window) { } } -void Hy3Layout::onWindowRemovedFloating(CWindow *window) { - this->m_focusIntercepts.erase(window); -} +void Hy3Layout::onWindowRemovedFloating(CWindow* window) { this->m_focusIntercepts.erase(window); } void Hy3Layout::onWindowFocusChange(CWindow* window) { auto* node = this->getNodeFromWindow(window); @@ -380,7 +379,7 @@ void Hy3Layout::recalculateMonitor(const int& monitor_id) { // todo: refactor this auto* top_node = this->getWorkspaceRootGroup(monitor->activeWorkspace); - if(top_node == nullptr) { + if (top_node == nullptr) { top_node = this->getWorkspaceRootGroup(monitor->specialWorkspaceID); } @@ -409,57 +408,61 @@ ShiftDirection reverse(ShiftDirection direction) { } } -void executeResizeOperation(const Vector2D& delta, eRectCorner corner, Hy3Node *node, CMonitor* monitor) { +void executeResizeOperation( + const Vector2D& delta, + eRectCorner corner, + Hy3Node* node, + CMonitor* monitor +) { if (node == nullptr) return; if (monitor == nullptr) return; - const bool display_left = - STICKS(node->position.x, monitor->vecPosition.x + monitor->vecReservedTopLeft.x); - const bool display_right = STICKS( - node->position.x + node->size.x, - monitor->vecPosition.x + monitor->vecSize.x - monitor->vecReservedBottomRight.x - ); - const bool display_top = - STICKS(node->position.y, monitor->vecPosition.y + monitor->vecReservedTopLeft.y); - const bool display_bottom = STICKS( - node->position.y + node->size.y, - monitor->vecPosition.y + monitor->vecSize.y - monitor->vecReservedBottomRight.y - ); + const bool display_left = + STICKS(node->position.x, monitor->vecPosition.x + monitor->vecReservedTopLeft.x); + const bool display_right = STICKS( + node->position.x + node->size.x, + monitor->vecPosition.x + monitor->vecSize.x - monitor->vecReservedBottomRight.x + ); + const bool display_top = + STICKS(node->position.y, monitor->vecPosition.y + monitor->vecReservedTopLeft.y); + const bool display_bottom = STICKS( + node->position.y + node->size.y, + monitor->vecPosition.y + monitor->vecSize.y - monitor->vecReservedBottomRight.y + ); - Vector2D resize_delta = delta; - bool node_is_root = (node->data.type == Hy3NodeType::Group && node->parent == nullptr) - || (node->data.type == Hy3NodeType::Window - && (node->parent == nullptr || node->parent->parent == nullptr)); + Vector2D resize_delta = delta; + bool node_is_root = (node->data.type == Hy3NodeType::Group && node->parent == nullptr) + || (node->data.type == Hy3NodeType::Window + && (node->parent == nullptr || node->parent->parent == nullptr)); - if (node_is_root) { - if (display_left && display_right) resize_delta.x = 0; - if (display_top && display_bottom) resize_delta.y = 0; + if (node_is_root) { + if (display_left && display_right) resize_delta.x = 0; + if (display_top && display_bottom) resize_delta.y = 0; + } + + // Don't execute the logic unless there's something to do + if (resize_delta.x != 0 || resize_delta.y != 0) { + ShiftDirection target_edge_x; + ShiftDirection target_edge_y; + + // Determine the direction in which we're going to look for the neighbor node + // that will be resized + if (corner == CORNER_NONE) { // It's probably a keyboard event. + target_edge_x = display_right ? ShiftDirection::Left : ShiftDirection::Right; + target_edge_y = display_bottom ? ShiftDirection::Up : ShiftDirection::Down; + + // If the anchor is not at the top/left then reverse the delta + if (target_edge_x == ShiftDirection::Left) resize_delta.x = -resize_delta.x; + if (target_edge_y == ShiftDirection::Up) resize_delta.y = -resize_delta.y; + } else { // It's probably a mouse event + // Resize against the edges corresponding to the selected corner + target_edge_x = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT + ? ShiftDirection::Left + : ShiftDirection::Right; + target_edge_y = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT ? ShiftDirection::Up + : ShiftDirection::Down; } - // Don't execute the logic unless there's something to do - if (resize_delta.x != 0 || resize_delta.y != 0) { - ShiftDirection target_edge_x; - ShiftDirection target_edge_y; - - // Determine the direction in which we're going to look for the neighbor node - // that will be resized - if (corner == CORNER_NONE) { // It's probably a keyboard event. - target_edge_x = display_right ? ShiftDirection::Left : ShiftDirection::Right; - target_edge_y = display_bottom ? ShiftDirection::Up : ShiftDirection::Down; - - // If the anchor is not at the top/left then reverse the delta - if (target_edge_x == ShiftDirection::Left) resize_delta.x = -resize_delta.x; - if (target_edge_y == ShiftDirection::Up) resize_delta.y = -resize_delta.y; - } else { // It's probably a mouse event - // Resize against the edges corresponding to the selected corner - target_edge_x = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT - ? ShiftDirection::Left - : ShiftDirection::Right; - target_edge_y = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT - ? ShiftDirection::Up - : ShiftDirection::Down; - } - // Find the neighboring node in each axis, which will be either above or at the // same level as the initiating node in the layout hierarchy. These are the nodes // which must get resized (rather than the initiator) because they are the @@ -467,13 +470,13 @@ void executeResizeOperation(const Vector2D& delta, eRectCorner corner, Hy3Node * auto horizontal_neighbor = node->findNeighbor(target_edge_x); auto vertical_neighbor = node->findNeighbor(target_edge_y); - static const auto animate = ConfigValue("misc:animate_manual_resizes"); + static const auto animate = ConfigValue("misc:animate_manual_resizes"); - // Note that the resize direction is reversed, because from the neighbor's perspective - // the edge to be moved is the opposite way round. However, the delta is still the same. - if (horizontal_neighbor) { - horizontal_neighbor->resize(reverse(target_edge_x), resize_delta.x, *animate == 0); - } + // Note that the resize direction is reversed, because from the neighbor's perspective + // the edge to be moved is the opposite way round. However, the delta is still the same. + if (horizontal_neighbor) { + horizontal_neighbor->resize(reverse(target_edge_x), resize_delta.x, *animate == 0); + } if (vertical_neighbor) { vertical_neighbor->resize(reverse(target_edge_y), resize_delta.y, *animate == 0); @@ -484,17 +487,19 @@ void executeResizeOperation(const Vector2D& delta, eRectCorner corner, Hy3Node * void Hy3Layout::resizeNode(const Vector2D& delta, eRectCorner corner, Hy3Node* node) { // Is the intended target really a node or a floating window? auto window = g_pCompositor->m_pLastWindow; - if(window && window->m_bIsFloating) { + if (window && window->m_bIsFloating) { this->resizeActiveWindow(delta, corner, window); } else if (node) { - auto monitor = g_pCompositor->getMonitorFromID(g_pCompositor->getWorkspaceByID(node->workspace_id)->m_iMonitorID); + auto monitor = g_pCompositor->getMonitorFromID( + g_pCompositor->getWorkspaceByID(node->workspace_id)->m_iMonitorID + ); executeResizeOperation(delta, corner, node, monitor); } } void Hy3Layout::resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CWindow* pWindow) { auto window = pWindow ? pWindow : g_pCompositor->m_pLastWindow; - if(window == nullptr || ! g_pCompositor->windowValidMapped(window)) return; + if (window == nullptr || !g_pCompositor->windowValidMapped(window)) return; if (window->m_bIsFloating) { // Use the same logic as the `main` layout for floating windows @@ -504,8 +509,13 @@ void Hy3Layout::resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CW ); window->m_vRealSize = required_size; g_pXWaylandManager->setWindowSize(window, required_size); - } else if (auto* node = this->getNodeFromWindow(window);node != nullptr) { - executeResizeOperation(delta, corner, &node->getExpandActor(), g_pCompositor->getMonitorFromID(window->m_iMonitorID)); + } else if (auto* node = this->getNodeFromWindow(window); node != nullptr) { + executeResizeOperation( + delta, + corner, + &node->getExpandActor(), + g_pCompositor->getMonitorFromID(window->m_iMonitorID) + ); } } @@ -896,50 +906,51 @@ void Hy3Layout::shiftNode(Hy3Node& node, ShiftDirection direction, bool once, bo void shiftFloatingWindow(CWindow* window, ShiftDirection direction) { static const auto kbd_shift_delta = ConfigValue("plugin:hy3:kbd_shift_delta"); - if(!window) return; + if (!window) return; Vector2D bounds {0, 0}; // BUG: Assumes horizontal monitor layout // BUG: Ignores monitor reserved space - for(auto m: g_pCompositor->m_vMonitors) { + for (auto m: g_pCompositor->m_vMonitors) { bounds.x = std::max(bounds.x, m->vecPosition.x + m->vecSize.x); - if(m->ID == window->m_iMonitorID) { + if (m->ID == window->m_iMonitorID) { bounds.y = m->vecPosition.y + m->vecSize.y; } } - const int delta = getSearchDirection(direction) == SearchDirection::Forwards - ? *kbd_shift_delta: -*kbd_shift_delta; + const int delta = getSearchDirection(direction) == SearchDirection::Forwards ? *kbd_shift_delta + : -*kbd_shift_delta; - Vector2D movement_delta = (getAxis(direction) == Axis::Horizontal) - ? Vector2D{ delta, 0 } : Vector2D{ 0, delta }; + Vector2D movement_delta = + (getAxis(direction) == Axis::Horizontal) ? Vector2D {delta, 0} : Vector2D {0, delta}; auto window_pos = window->m_vRealPosition.vec(); auto window_size = window->m_vRealSize.vec(); // Keep at least `delta` pixels visible - if (window_pos.x + window_size.x + delta < 0 || window_pos.x + delta > bounds.x) movement_delta.x = 0; - if (window_pos.y + window_size.y + delta < 0 || window_pos.y + delta > bounds.y) movement_delta.y = 0; - if(movement_delta.x != 0 || movement_delta.y != 0) { + if (window_pos.x + window_size.x + delta < 0 || window_pos.x + delta > bounds.x) + movement_delta.x = 0; + if (window_pos.y + window_size.y + delta < 0 || window_pos.y + delta > bounds.y) + movement_delta.y = 0; + if (movement_delta.x != 0 || movement_delta.y != 0) { auto new_pos = window_pos + movement_delta; // Do we need to change the workspace? const auto new_monitor = g_pCompositor->getMonitorFromVector(new_pos); - if(new_monitor && new_monitor->ID != window->m_iMonitorID) { + if (new_monitor && new_monitor->ID != window->m_iMonitorID) { // Ignore the movement request if the new workspace is special - if(! new_monitor->specialWorkspaceID) { + if (!new_monitor->specialWorkspaceID) { const auto old_workspace = g_pCompositor->getWorkspaceByID(window->m_iWorkspaceID); const auto new_workspace = g_pCompositor->getWorkspaceByID(new_monitor->activeWorkspace); const auto previous_monitor = g_pCompositor->getMonitorFromID(window->m_iMonitorID); const auto original_new_pos = new_pos; - if(new_workspace && previous_monitor) { - switch (direction) - { - case ShiftDirection::Left: new_pos.x += new_monitor->vecSize.x; break; - case ShiftDirection::Right: new_pos.x -= previous_monitor->vecSize.x; break; - case ShiftDirection::Up: new_pos.y += new_monitor->vecSize.y; break; - case ShiftDirection::Down: new_pos.y -= previous_monitor->vecSize.y; break; - default: UNREACHABLE(); + if (new_workspace && previous_monitor) { + switch (direction) { + case ShiftDirection::Left: new_pos.x += new_monitor->vecSize.x; break; + case ShiftDirection::Right: new_pos.x -= previous_monitor->vecSize.x; break; + case ShiftDirection::Up: new_pos.y += new_monitor->vecSize.y; break; + case ShiftDirection::Down: new_pos.y -= previous_monitor->vecSize.y; break; + default: UNREACHABLE(); } } @@ -947,7 +958,8 @@ void shiftFloatingWindow(CWindow* window, ShiftDirection direction) { g_pCompositor->moveWindowToWorkspaceSafe(window, new_workspace); g_pCompositor->setActiveMonitor(new_monitor); - const static auto allow_workspace_cycles = ConfigValue("binds:allow_workspace_cycles"); + const static auto allow_workspace_cycles = + ConfigValue("binds:allow_workspace_cycles"); if (*allow_workspace_cycles) new_workspace->rememberPrevWorkspace(old_workspace); } } else { @@ -968,16 +980,16 @@ void Hy3Layout::shiftWindow(int workspace_id, ShiftDirection direction, bool onc } void Hy3Layout::focusMonitor(CMonitor* monitor) { - if(monitor == nullptr) return; + if (monitor == nullptr) return; g_pCompositor->setActiveMonitor(monitor); const auto focusedNode = this->getWorkspaceFocusedNode(monitor->activeWorkspace); - if(focusedNode != nullptr) { + if (focusedNode != nullptr) { focusedNode->focus(); } else { auto* workspace = g_pCompositor->getWorkspaceByID(monitor->activeWorkspace); CWindow* next_window = nullptr; - if(workspace != nullptr) { + if (workspace != nullptr) { workspace->setActive(true); if (workspace->m_bHasFullscreenWindow) { next_window = g_pCompositor->getFullscreenWindowOnWorkspace(workspace->m_iID); @@ -987,8 +999,8 @@ void Hy3Layout::focusMonitor(CMonitor* monitor) { } else { for (auto& w: g_pCompositor->m_vWindows | std::views::reverse) { if (w->m_bIsMapped && !w->isHidden() && w->m_bIsFloating && w->m_iX11Type != 2 - && w->m_iWorkspaceID == next_window->m_iWorkspaceID && !w->m_bX11ShouldntFocus - && !w->m_sAdditionalConfigData.noFocus) + && w->m_iWorkspaceID == next_window->m_iWorkspaceID && !w->m_bX11ShouldntFocus + && !w->m_sAdditionalConfigData.noFocus) { next_window = w.get(); break; @@ -999,13 +1011,13 @@ void Hy3Layout::focusMonitor(CMonitor* monitor) { } } -CWindow *getFocusedWindow(const Hy3Node *node) { +CWindow* getFocusedWindow(const Hy3Node* node) { auto search = node; - while(search != nullptr && search->data.type == Hy3NodeType::Group) { + while (search != nullptr && search->data.type == Hy3NodeType::Group) { search = search->data.as_group.focused_child; } - if(search == nullptr || search->data.type != Hy3NodeType::Window) { + if (search == nullptr || search->data.type != Hy3NodeType::Window) { return nullptr; } @@ -1025,32 +1037,31 @@ bool shiftMatchesLayout(Hy3GroupLayout layout, ShiftDirection direction) { || (layout != Hy3GroupLayout::SplitV && !shiftIsVertical(direction)); } - -bool covers(const CBox &outer, const CBox &inner) { - return outer.x <= inner.x - && outer.y <= inner.y - && outer.x + outer.w >= inner.x + inner.w - && outer.y + outer.h >= inner.y + inner.h; +bool covers(const CBox& outer, const CBox& inner) { + return outer.x <= inner.x && outer.y <= inner.y && outer.x + outer.w >= inner.x + inner.w + && outer.y + outer.h >= inner.y + inner.h; } -bool isObscured (CWindow* window) { - if(!window) return false; +bool isObscured(CWindow* window) { + if (!window) return false; const auto inner_box = window->getWindowMainSurfaceBox(); bool is_obscured = false; - for(auto& w :g_pCompositor->m_vWindows | std::views::reverse) { - if(w.get() == window) { - // Don't go any further if this is a floating window, because m_vWindows is sorted bottom->top per Compositor.cpp - if(window->m_bIsFloating) break; else continue; + for (auto& w: g_pCompositor->m_vWindows | std::views::reverse) { + if (w.get() == window) { + // Don't go any further if this is a floating window, because m_vWindows is sorted bottom->top + // per Compositor.cpp + if (window->m_bIsFloating) break; + else continue; } - if(!w->m_bIsFloating) continue; + if (!w->m_bIsFloating) continue; const auto outer_box = w->getWindowMainSurfaceBox(); is_obscured = covers(outer_box, inner_box); - if(is_obscured) break; + if (is_obscured) break; }; return is_obscured; @@ -1063,45 +1074,54 @@ bool isObscured(Hy3Node* node) { bool isNotObscured(CWindow* window) { return !isObscured(window); } bool isNotObscured(Hy3Node* node) { return !isObscured(node); } -CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, BitFlag layers_same_monitor, BitFlag layers_other_monitors) { - if(!source) return nullptr; - if(layers_other_monitors == Layer::None && layers_same_monitor == Layer::None) return nullptr; +CWindow* getWindowInDirection( + CWindow* source, + ShiftDirection direction, + BitFlag layers_same_monitor, + BitFlag layers_other_monitors +) { + if (!source) return nullptr; + if (layers_other_monitors == Layer::None && layers_same_monitor == Layer::None) return nullptr; - CWindow *target_window = nullptr; + CWindow* target_window = nullptr; const auto source_middle = source->middle(); std::optional target_distance; - const auto static focus_policy = ConfigValue("plugin:hy3:focus_obscured_windows_policy"); - bool permit_obscured_windows = *focus_policy == 0 || (*focus_policy == 2 && layers_same_monitor.HasNot(Layer::Floating | Layer::Tiled)); + const auto static focus_policy = + ConfigValue("plugin:hy3:focus_obscured_windows_policy"); + bool permit_obscured_windows = + *focus_policy == 0 + || (*focus_policy == 2 && layers_same_monitor.HasNot(Layer::Floating | Layer::Tiled)); const auto source_monitor = g_pCompositor->getMonitorFromID(source->m_iMonitorID); - const auto next_monitor = layers_other_monitors.HasAny(Layer::Floating | Layer::Tiled) - ? g_pCompositor->getMonitorInDirection(source_monitor, directionToChar(direction)) - : nullptr; + const auto next_monitor = + layers_other_monitors.HasAny(Layer::Floating | Layer::Tiled) + ? g_pCompositor->getMonitorInDirection(source_monitor, directionToChar(direction)) + : nullptr; - const auto next_workspace = next_monitor - ? next_monitor->specialWorkspaceID ? next_monitor->specialWorkspaceID - : next_monitor->activeWorkspace - : WORKSPACE_INVALID; + const auto next_workspace = next_monitor ? next_monitor->specialWorkspaceID + ? next_monitor->specialWorkspaceID + : next_monitor->activeWorkspace + : WORKSPACE_INVALID; auto isCandidate = [=, mon = source->m_iMonitorID](CWindow* w) { const auto window_layer = w->m_bIsFloating ? Layer::Floating : Layer::Tiled; const auto monitor_flags = w->m_iMonitorID == mon ? layers_same_monitor : layers_other_monitors; - return (monitor_flags.Has(window_layer)) - && w->m_bIsMapped - && w->m_iX11Type != 2 - && !w->m_sAdditionalConfigData.noFocus - && !w->isHidden() - && !w->m_bX11ShouldntFocus - && (w->m_bPinned || w->m_iWorkspaceID == source->m_iWorkspaceID || w->m_iWorkspaceID == next_workspace); + return (monitor_flags.Has(window_layer)) && w->m_bIsMapped && w->m_iX11Type != 2 + && !w->m_sAdditionalConfigData.noFocus && !w->isHidden() && !w->m_bX11ShouldntFocus + && (w->m_bPinned || w->m_iWorkspaceID == source->m_iWorkspaceID + || w->m_iWorkspaceID == next_workspace); }; - for(auto &pw: g_pCompositor->m_vWindows) { + for (auto& pw: g_pCompositor->m_vWindows) { auto w = pw.get(); - if(w != source && isCandidate(w)) { - auto dist = Distance { direction, source_middle, w->middle() }; - if((target_distance.has_value() ? dist < target_distance.value() : dist.isInDirection(direction)) && (permit_obscured_windows || isNotObscured(w)) ) { + if (w != source && isCandidate(w)) { + auto dist = Distance {direction, source_middle, w->middle()}; + if ((target_distance.has_value() ? dist < target_distance.value() + : dist.isInDirection(direction)) + && (permit_obscured_windows || isNotObscured(w))) + { target_window = w; target_distance = dist; } @@ -1111,19 +1131,32 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, BitFlag hy3_log(LOG, "getWindowInDirection: closest window to {} is {}", source, target_window); // If the closest window is on a different monitor and the nearest edge has the same position - // as the last focused window on that monitor's workspace then choose the last focused window instead; this - // allows seamless back-and-forth by direction keys - if(target_window && target_window->m_iMonitorID != source->m_iMonitorID) { + // as the last focused window on that monitor's workspace then choose the last focused window + // instead; this allows seamless back-and-forth by direction keys + if (target_window && target_window->m_iMonitorID != source->m_iMonitorID) { if (auto new_workspace = g_pCompositor->getWorkspaceByID(next_workspace)) { if (auto last_focused = new_workspace->getLastFocusedWindow()) { - auto target_bounds = CBox(target_window->m_vRealPosition.vec(), target_window->m_vRealSize.vec()); - auto last_focused_bounds = CBox(last_focused->m_vRealPosition.vec(), last_focused->m_vRealSize.vec()); + auto target_bounds = + CBox(target_window->m_vRealPosition.vec(), target_window->m_vRealSize.vec()); + auto last_focused_bounds = + CBox(last_focused->m_vRealPosition.vec(), last_focused->m_vRealSize.vec()); - if((direction == ShiftDirection::Left && STICKS(target_bounds.x + target_bounds.w, last_focused_bounds.x + last_focused_bounds.w)) - || (direction == ShiftDirection::Right && STICKS(target_bounds.x, last_focused_bounds.x)) - || (direction == ShiftDirection::Up && STICKS(target_bounds.y + target_bounds.h, last_focused_bounds.y + last_focused_bounds.h)) - || (direction == ShiftDirection::Down && STICKS(target_bounds.y, last_focused_bounds.y))) { - target_window = last_focused; + if ((direction == ShiftDirection::Left + && STICKS( + target_bounds.x + target_bounds.w, + last_focused_bounds.x + last_focused_bounds.w + )) + || (direction == ShiftDirection::Right && STICKS(target_bounds.x, last_focused_bounds.x) + ) + || (direction == ShiftDirection::Up + && STICKS( + target_bounds.y + target_bounds.h, + last_focused_bounds.y + last_focused_bounds.h + )) + || (direction == ShiftDirection::Down && STICKS(target_bounds.y, last_focused_bounds.y) + )) + { + target_window = last_focused; } } } @@ -1134,15 +1167,20 @@ CWindow* getWindowInDirection(CWindow* source, ShiftDirection direction, BitFlag void Hy3Layout::shiftFocusToMonitor(ShiftDirection direction) { auto target_monitor = g_pCompositor->getMonitorInDirection(directionToChar(direction)); - if(target_monitor) this->focusMonitor(target_monitor); + if (target_monitor) this->focusMonitor(target_monitor); } -void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible, BitFlag eligible_layers) { - Hy3Node *candidate_node = nullptr; - CWindow *closest_window = nullptr; - Hy3Node *source_node = nullptr; - CWindow *source_window = g_pCompositor->m_pLastWindow; - CWorkspace *source_workspace = g_pCompositor->getWorkspaceByID(workspace); +void Hy3Layout::shiftFocus( + int workspace, + ShiftDirection direction, + bool visible, + BitFlag eligible_layers +) { + Hy3Node* candidate_node = nullptr; + CWindow* closest_window = nullptr; + Hy3Node* source_node = nullptr; + CWindow* source_window = g_pCompositor->m_pLastWindow; + CWorkspace* source_workspace = g_pCompositor->getWorkspaceByID(workspace); if (source_workspace) { source_window = source_workspace->m_pLastFocusedWindow; @@ -1155,56 +1193,66 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible return; } - hy3_log(LOG, - "shiftFocus: Source: {} ({}), workspace: {}, direction: {}, visible: {}", - source_window, - source_window->m_bIsFloating ? "floating" : "tiled", - workspace, - (int)direction, - visible + hy3_log( + LOG, + "shiftFocus: Source: {} ({}), workspace: {}, direction: {}, visible: {}", + source_window, + source_window->m_bIsFloating ? "floating" : "tiled", + workspace, + (int) direction, + visible ); // If no eligible_layers specified then choose the same layer as the source window - if(eligible_layers == Layer::None) eligible_layers = source_window->m_bIsFloating ? Layer::Floating : Layer::Tiled; + if (eligible_layers == Layer::None) + eligible_layers = source_window->m_bIsFloating ? Layer::Floating : Layer::Tiled; - const auto static focus_policy = ConfigValue("plugin:hy3:focus_obscured_windows_policy"); - bool skip_obscured = *focus_policy == 1 || (*focus_policy == 2 && eligible_layers.Has(Layer::Floating | Layer::Tiled)); + const auto static focus_policy = + ConfigValue("plugin:hy3:focus_obscured_windows_policy"); + bool skip_obscured = *focus_policy == 1 + || (*focus_policy == 2 && eligible_layers.Has(Layer::Floating | Layer::Tiled)); // Determine the starting point for looking for a tiled node - it's either the // workspace's focused node or the floating window's focus entry point (which may be null) if (eligible_layers.Has(Layer::Tiled)) { source_node = source_window->m_bIsFloating ? getFocusOverride(source_window, direction) - : getWorkspaceFocusedNode(workspace); + : getWorkspaceFocusedNode(workspace); - if(source_node) { + if (source_node) { candidate_node = this->shiftOrGetFocus(*source_node, direction, false, false, visible); - while(candidate_node && skip_obscured && isObscured(candidate_node)) { + while (candidate_node && skip_obscured && isObscured(candidate_node)) { candidate_node = this->shiftOrGetFocus(*candidate_node, direction, false, false, visible); } } } BitFlag this_monitor = eligible_layers & Layer::Floating; - if(source_window->m_bIsFloating && !candidate_node) this_monitor |= (eligible_layers & Layer::Tiled); + if (source_window->m_bIsFloating && !candidate_node) + this_monitor |= (eligible_layers & Layer::Tiled); BitFlag other_monitors; - if(!candidate_node) other_monitors |= eligible_layers; + if (!candidate_node) other_monitors |= eligible_layers; // Find the closest window in the right direction. Consider other monitors // if we don't have a tiled candidate closest_window = getWindowInDirection(source_window, direction, this_monitor, other_monitors); - // If there's a window in the right direction then choose between that window and the tiled candidate. + // If there's a window in the right direction then choose between that window and the tiled + // candidate. bool focus_closest_window = false; - if(closest_window) { - if(candidate_node) { - // If the closest window is tiled then focus the tiled node which was obtained from `shiftOrGetFocus`, - // otherwise focus whichever is closer - if(closest_window->m_bIsFloating) { - Distance distanceToClosestWindow(direction, source_window->middle(), closest_window->middle()); + if (closest_window) { + if (candidate_node) { + // If the closest window is tiled then focus the tiled node which was obtained from + // `shiftOrGetFocus`, otherwise focus whichever is closer + if (closest_window->m_bIsFloating) { + Distance distanceToClosestWindow( + direction, + source_window->middle(), + closest_window->middle() + ); Distance distanceToTiledNode(direction, source_window->middle(), candidate_node->middle()); - if(distanceToClosestWindow < distanceToTiledNode) { + if (distanceToClosestWindow < distanceToTiledNode) { focus_closest_window = true; } } @@ -1214,14 +1262,15 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible } std::optional new_monitor_id; - if(focus_closest_window) { + if (focus_closest_window) { new_monitor_id = closest_window->m_iMonitorID; setFocusOverride(closest_window, direction, source_node); g_pCompositor->focusWindow(closest_window); - } else if(candidate_node) { - if(candidate_node->data.type == Hy3NodeType::Window) { + } else if (candidate_node) { + if (candidate_node->data.type == Hy3NodeType::Window) { new_monitor_id = candidate_node->data.as_window->m_iMonitorID; - } else if(auto *workspace = g_pCompositor->getWorkspaceByID(candidate_node->getRoot()->workspace_id)) { + } else if (auto* workspace = g_pCompositor->getWorkspaceByID(candidate_node->getRoot()->workspace_id)) + { new_monitor_id = workspace->m_iMonitorID; } candidate_node->focusWindow(); @@ -1230,23 +1279,27 @@ void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction, bool visible shiftFocusToMonitor(direction); } - if(new_monitor_id && new_monitor_id.value() != source_window->m_iMonitorID) { - if(auto *monitor = g_pCompositor->getMonitorFromID(new_monitor_id.value())) { + if (new_monitor_id && new_monitor_id.value() != source_window->m_iMonitorID) { + if (auto* monitor = g_pCompositor->getMonitorFromID(new_monitor_id.value())) { g_pCompositor->setActiveMonitor(monitor); } } } Hy3Node* Hy3Layout::getFocusOverride(CWindow* src, ShiftDirection direction) { - if(auto intercept = this->m_focusIntercepts.find(src); intercept != this->m_focusIntercepts.end()) { + if (auto intercept = this->m_focusIntercepts.find(src); + intercept != this->m_focusIntercepts.end()) + { Hy3Node** accessor = intercept->second.forDirection(direction); - if(auto override = *accessor) { + if (auto override = *accessor) { // If the root isn't valid or is on a different workspsace then update the intercept data - if(override->workspace_id != src->m_iWorkspaceID || !std::ranges::contains(this->nodes, *override)) { + if (override->workspace_id != src->m_iWorkspaceID + || !std::ranges::contains(this->nodes, *override)) + { *accessor = nullptr; // If there are no remaining overrides then discard the intercept - if(intercept->second.isEmpty()) { + if (intercept->second.isEmpty()) { this->m_focusIntercepts.erase(intercept); } } @@ -1259,16 +1312,17 @@ Hy3Node* Hy3Layout::getFocusOverride(CWindow* src, ShiftDirection direction) { } void Hy3Layout::setFocusOverride(CWindow* src, ShiftDirection direction, Hy3Node* dest) { - if(auto intercept = this->m_focusIntercepts.find(src);intercept != this->m_focusIntercepts.end()) { + if (auto intercept = this->m_focusIntercepts.find(src); + intercept != this->m_focusIntercepts.end()) + { *intercept->second.forDirection(direction) = dest; } else { FocusOverride override; *override.forDirection(direction) = dest; - this->m_focusIntercepts.insert({ src, override }); + this->m_focusIntercepts.insert({src, override}); } } - void changeNodeWorkspaceRecursive(Hy3Node& node, CWorkspace* workspace) { node.workspace_id = workspace->m_iID; @@ -2006,7 +2060,7 @@ Hy3Node* Hy3Layout::shiftOrGetFocus( } insert = parent_group.children.begin(); } else if (break_origin == parent_group.children.back() && shiftIsForward(direction)) { - if (!shift) { + if (!shift) { return nullptr; } insert = parent_group.children.end(); @@ -2030,7 +2084,7 @@ Hy3Node* Hy3Layout::shiftOrGetFocus( if (shiftIsForward(direction)) insert = iter; else insert = std::next(iter); } - } else { + } else { return (*iter)->getFocusedNode(); } } else { diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp index 9eeb7a5..7ba19c3 100644 --- a/src/Hy3Layout.hpp +++ b/src/Hy3Layout.hpp @@ -1,9 +1,10 @@ #pragma once #include +#include #include #include -#include + #include "BitFlag.hpp" class Hy3Layout; @@ -21,27 +22,15 @@ enum class ShiftDirection { Right, }; -enum class SearchDirection { - None, - Forwards, - Backwards -}; +enum class SearchDirection { None, Forwards, Backwards }; enum class Axis { None, Horizontal, Vertical }; -enum class Layer { - None = 0, - Tiled = 1 << 0, - Floating = 1 << 1 -}; +enum class Layer { None = 0, Tiled = 1 << 0, Floating = 1 << 1 }; -inline Layer operator| (Layer a, Layer b) { - return static_cast((int)a | (int)b); -} +inline Layer operator|(Layer a, Layer b) { return static_cast((int) a | (int) b); } -inline Layer operator& (Layer a, Layer b) { - return static_cast((int)a & (int)b); -} +inline Layer operator&(Layer a, Layer b) { return static_cast((int) a & (int) b); } #include "Hy3Node.hpp" #include "TabGroup.hpp" @@ -90,27 +79,24 @@ enum class ExpandFullscreenOption { }; struct FocusOverride { - Hy3Node *left = nullptr; - Hy3Node *up = nullptr; - Hy3Node *right = nullptr; - Hy3Node *down = nullptr; + Hy3Node* left = nullptr; + Hy3Node* up = nullptr; + Hy3Node* right = nullptr; + Hy3Node* down = nullptr; - Hy3Node **forDirection(ShiftDirection direction) { - switch(direction) { - case ShiftDirection::Left: return &left; - case ShiftDirection::Up: return &up; - case ShiftDirection::Right: return &right; - case ShiftDirection::Down: return &down; - default: UNREACHABLE(); + Hy3Node** forDirection(ShiftDirection direction) { + switch (direction) { + case ShiftDirection::Left: return &left; + case ShiftDirection::Up: return &up; + case ShiftDirection::Right: return &right; + case ShiftDirection::Down: return &down; + default: UNREACHABLE(); } } - bool isEmpty() { - return !(left || right || up || down); - } + bool isEmpty() { return !(left || right || up || down); } }; - class Hy3Layout: public IHyprLayout { public: virtual void onWindowCreated(CWindow*, eDirection = DIRECTION_DEFAULT); @@ -121,7 +107,8 @@ public: virtual bool isWindowTiled(CWindow*); virtual void recalculateMonitor(const int& monitor_id); virtual void recalculateWindow(CWindow*); - virtual void resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CWindow* pWindow = nullptr); + virtual void + resizeActiveWindow(const Vector2D& delta, eRectCorner corner, CWindow* pWindow = nullptr); virtual void fullscreenRequestForWindow(CWindow*, eFullscreenMode, bool enable_fullscreen); virtual std::any layoutMessage(SLayoutMessageHeader header, std::string content); virtual SWindowRenderLayoutHints requestRenderHints(CWindow*); diff --git a/src/Hy3Node.cpp b/src/Hy3Node.cpp index f6c93cc..d61fa72 100644 --- a/src/Hy3Node.cpp +++ b/src/Hy3Node.cpp @@ -862,9 +862,7 @@ int directionToIteratorIncrement(ShiftDirection direction) { } } -Vector2D Hy3Node::middle() { - return this->position + this->size / 2.f; -} +Vector2D Hy3Node::middle() { return this->position + this->size / 2.f; } void Hy3Node::resize(ShiftDirection direction, double delta, bool no_animation) { auto& parent_node = this->parent; @@ -899,7 +897,12 @@ void Hy3Node::resize(ShiftDirection direction, double delta, bool no_animation) parent_node->recalcSizePosRecursive(no_animation); } else { - hy3_log(WARN, "Requested size ratio {} or {} out of bounds, ignoring", requested_size_ratio, requested_neighbor_size_ratio); + hy3_log( + WARN, + "Requested size ratio {} or {} out of bounds, ignoring", + requested_size_ratio, + requested_neighbor_size_ratio + ); } } } @@ -925,17 +928,17 @@ void Hy3Node::swapData(Hy3Node& a, Hy3Node& b) { } bool Hy3Node::hasChild(Hy3Node* node) { - if(this->data.type == Hy3NodeType::Window) return false; + if (this->data.type == Hy3NodeType::Window) return false; auto n = node; - while(n != nullptr && n->parent != this) n = n->parent; + while (n != nullptr && n->parent != this) n = n->parent; return n != nullptr; } Hy3Node* Hy3Node::getRoot() { Hy3Node* maybeRoot = this; - while(maybeRoot->parent) maybeRoot = maybeRoot->parent; + while (maybeRoot->parent) maybeRoot = maybeRoot->parent; return maybeRoot; } \ No newline at end of file diff --git a/src/Hy3Node.hpp b/src/Hy3Node.hpp index 42f3c9c..651c763 100644 --- a/src/Hy3Node.hpp +++ b/src/Hy3Node.hpp @@ -147,14 +147,18 @@ struct Distance { secondary_axis = getAxis(direction) == Axis::Horizontal ? dist.y : dist.x; } - bool operator< (Distance other) { + bool operator<(Distance other) { return signbit(primary_axis) == signbit(other.primary_axis) - && (abs(primary_axis) < abs(other.primary_axis) || (primary_axis == other.primary_axis && abs(secondary_axis) < abs(other.secondary_axis))); + && (abs(primary_axis) < abs(other.primary_axis) + || (primary_axis == other.primary_axis + && abs(secondary_axis) < abs(other.secondary_axis))); } - bool operator> (Distance other) { + bool operator>(Distance other) { return signbit(primary_axis) == signbit(other.primary_axis) - && (abs(primary_axis) > abs(other.primary_axis) || (primary_axis == other.primary_axis && abs(secondary_axis) > abs(other.secondary_axis))); + && (abs(primary_axis) > abs(other.primary_axis) + || (primary_axis == other.primary_axis + && abs(secondary_axis) > abs(other.secondary_axis))); } bool isSameDirection(Distance other) { @@ -162,6 +166,7 @@ struct Distance { } bool isInDirection(ShiftDirection direction) { - return std::signbit(primary_axis) == (getSearchDirection(direction) == SearchDirection::Forwards); + return std::signbit(primary_axis) + == (getSearchDirection(direction) == SearchDirection::Forwards); } }; \ No newline at end of file diff --git a/src/conversions.cpp b/src/conversions.cpp index 07bdccc..46785b3 100644 --- a/src/conversions.cpp +++ b/src/conversions.cpp @@ -1,47 +1,35 @@ #include "Hy3Node.hpp" Axis getAxis(Hy3GroupLayout layout) { - switch (layout) - { - case Hy3GroupLayout::SplitH: - return Axis::Horizontal; - case Hy3GroupLayout::SplitV: - return Axis::Vertical; - default: - return Axis::None; + switch (layout) { + case Hy3GroupLayout::SplitH: return Axis::Horizontal; + case Hy3GroupLayout::SplitV: return Axis::Vertical; + default: return Axis::None; } } Axis getAxis(ShiftDirection direction) { - switch (direction) - { + switch (direction) { case ShiftDirection::Left: - case ShiftDirection::Right: - return Axis::Horizontal; + case ShiftDirection::Right: return Axis::Horizontal; case ShiftDirection::Down: - case ShiftDirection::Up: - return Axis::Vertical; - default: - return Axis::None; + case ShiftDirection::Up: return Axis::Vertical; + default: return Axis::None; } } SearchDirection getSearchDirection(ShiftDirection direction) { - switch(direction) { - case ShiftDirection::Left: - case ShiftDirection::Up: - return SearchDirection::Backwards; - case ShiftDirection::Right: - case ShiftDirection::Down: - return SearchDirection::Forwards; - default: - return SearchDirection::None; + switch (direction) { + case ShiftDirection::Left: + case ShiftDirection::Up: return SearchDirection::Backwards; + case ShiftDirection::Right: + case ShiftDirection::Down: return SearchDirection::Forwards; + default: return SearchDirection::None; } } char directionToChar(ShiftDirection direction) { - switch (direction) - { + switch (direction) { case ShiftDirection::Left: return 'l'; case ShiftDirection::Up: return 'u'; case ShiftDirection::Down: return 'd'; diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp index 74787f8..fc8e73b 100644 --- a/src/dispatchers.cpp +++ b/src/dispatchers.cpp @@ -8,7 +8,7 @@ int workspace_for_action(bool allow_fullscreen = false) { if (g_pLayoutManager->getCurrentLayout() != g_Hy3Layout.get()) return -1; - if(!g_pCompositor->m_pLastMonitor) return -1; + if (!g_pCompositor->m_pLastMonitor) return -1; int workspace_id = g_pCompositor->m_pLastMonitor->activeWorkspace; @@ -128,14 +128,15 @@ void dispatch_movefocus(std::string value) { bool visible; BitFlag layers; - for(auto arg: args) { - if(arg == "visible") visible = true; + for (auto arg: args) { + if (arg == "visible") visible = true; else if ((layerArg = parseLayerArg(arg))) layers |= layerArg.value(); } - if(!layerArg) { - const static auto default_movefocus_layer = ConfigValue("plugin:hy3:default_movefocus_layer"); - if((layerArg = parseLayerArg(*default_movefocus_layer))) layers |= layerArg.value(); + if (!layerArg) { + const static auto default_movefocus_layer = + ConfigValue("plugin:hy3:default_movefocus_layer"); + if ((layerArg = parseLayerArg(*default_movefocus_layer))) layers |= layerArg.value(); } g_Hy3Layout->shiftFocus(workspace, shift.value(), visible, layers); @@ -275,7 +276,7 @@ void dispatch_resizenode(std::string value) { auto* node = g_Hy3Layout->getWorkspaceFocusedNode(workspace, false, true); const auto delta = g_pCompositor->parseWindowVectorArgsRelative(value, Vector2D(0, 0)); - hy3_log(LOG, "resizeNode: node: {:x}, delta: {:X}", (uintptr_t)node, delta); + hy3_log(LOG, "resizeNode: node: {:x}, delta: {:X}", (uintptr_t) node, delta); g_Hy3Layout->resizeNode(delta, CORNER_NONE, node); }