Respect monitor-local workspaces in overview#111
Conversation
Build the tasking overview from Hyprland’s real workspace-to-monitor state instead of synthetic global workspace IDs. This prevents opening, rendering, closing, navigation, and drag-and-drop from moving or reassigning workspaces across monitors, while keeping move targets tied to the actual displayed workspace.
|
I tested it using |
|
Hi, @undef-user! I'm stuck on a first workspace with other workspaces being black. I can't drag a window to a different workspace and I can't move it there with a The workspace appears only after I move a window there with no_workspaces.mp4Note that I'm not using split-monitor-workspaces (and I don't think this should be a dependency of hyprtasking) |
|
Ah, I think I see the problem. My solution only looks at the currently existing workspaces; in my setup, I have 9 persistent per screen, so the grid is filled. Of course, you're right that neither this nor any other plugin should be a dependency. I'll see if I can expand the solution tomorrow. Thanks for the feedback! |
Keep the grid layout visually full by adding monitor-local placeholder tiles, and create real workspaces only when users navigate, select, or drop a window onto an empty tile. This preserves monitor-local workspace state without moving workspaces during overview rendering.
crashes all This reverts commit dc6a248.
|
Hi @yerlotic! |
|
Hi, @undef-user! I tested the grid layout hyprsomething.mp4Here are the notes:
|
|
Thanks for the feedback again @yerlotic ; I think (and hope) we’re getting closer to a solution. :D Regarding layers, I need to take a closer look; I don’t think I’ve interpreted the behavior correctly so far. Is it the intended behavior that a layer (in a 3x3 layout) creates/is 9 new/different workspaces? |
|
I tested it more and found more bugs: hyprmorebugs.mp4Notes:
|
Yes, it creates For more info see #60 |
|
Thanks for the information I am looking into it. :D |
|
Hello @yerlotic, sorry for bothering you again.
The behavior where switching back and forth between empty workspaces creates new workspace IDs is still present. As far as I understand, this is normal Hyprland behavior: empty non-persistent workspaces are cleaned up by Hyprland, so when hyprtasking enters that empty slot again, a new workspace has to be created and Hyprland assigns it a new ID. Workspaces that contain windows stay alive, which is why this does not happen there. I would really appreciate it if you could test this again when you have time. Also, please let me know whether you think hyprtasking should try to handle the empty-workspace ID behavior differently. |
|
While rereading the README, I also noticed that I accidentally added functionality allowing the setLayer dispatcher to accept relative positional arguments (+1/-1) as well as 1, 2, 3 for specific layers. |
|
Hi again, @undef-user!
I think it will be better if we assign lower id workspaces whenever possible. It's unlikely that anyone will ever run out of workspaces, but not letting this happen is a good thing
This was actually the intended behavior
Well, it doesn't
I was thinking about implementing something so each layer has its own focused workspace in a grid. Anyway, now the expected behavior is to run |
| return {.success = false, .error = "active workspace not in layout"}; | ||
|
|
||
| const int original_layer = cursor_view->layout->layer; | ||
| const std::string layer_arg = arg.empty() ? "+1" : arg; |
There was a problem hiding this comment.
I think the function should just return a dispatcher error in case there is no arg
|
You removed some useful comments (and I don't see why), so here is a patch to bring them back: return_comments.diff Also now |
…verview, fixing layers wont switch outside of overview
|
Hi again @yerlotic, The following now works as expected (hopefully 😅 ):
Regarding the empty workspace IDs continuing to increase: I have kept the current max + 1 allocation strategy for now, since the primary goal of this PR is to add multi-monitor support. Reusing the lowest available workspace ID globally could interfere with monitor-specific workspace ranges or rules. Because of that, I would prefer not to change this behavior within this PR unless we introduce a monitor-aware allocation strategy. If you’d like me to address this as well, I can look into it. |
@undef-user, well, now there are more ways to crash hyprland:
Could you please test your code without
If it is too much of a hustle to implement smart allocation, then ig we can stick to it. Also we could keep track of the last empty workspace and just not allocate a new one if not needed. |
grid refactor first try
| int HTLayoutGrid::get_max_occupied_layer() { | ||
| int max_occupied = 0; | ||
| for (int l = 0; l < (int)grid_cells.size(); l++) { | ||
| for (int y = 0; y < (int)grid_cells[l].size(); y++) { | ||
| for (int x = 0; x < (int)grid_cells[l][y].size(); x++) { | ||
| if (grid_cells[l][y][x].ws_id != WORKSPACE_INVALID) { | ||
| max_occupied = std::max(max_occupied, l); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return max_occupied; | ||
| } |
There was a problem hiding this comment.
This might introduce even more performance problems
There was a problem hiding this comment.
I'll look into it, as of now, I am not noticing any issues with that, but it's maybe over the top again.
Did you figure out what was causing them?
So you are trying to reimplement the grid, right? I think the benefits of this approach should be at least in a reduced amount of iteration to find if some workspace is occupied or for any similar reason |
|
The issue was, as far as I think the reinitializing of monitors after they went idle. I am trying to reimplement the grid as a 3 dimensional vector, right now I'm testing this approach as my driver so I can verify no problems occur. |
|
Let me know if f9f4ec1 works for you all |
Hi @raybbian, this looks cool. I noticed that the layers are broken after adding a new monitor and moving around a bit. Instead of moving to the same cell on a different layer, it moves rather strangely Then when I do After unplugging and plugging a monitor back in, some workspaces from that monitor appear on the main one and the layout now looks like this: As a result, jumping from ws The same buggy behavior happens with workspace rules: After loading hyprtasking, all the workspaces are on their monitors, but when I try to After adding Other than that, everything seems fine |
|
Try 5078467? |
|
Layer switching now works perfectly. But dynamic workspace rules don't really work. Then I save the config, but workspace 3 is still on monitor HDMI-1 ( Then I move all windows from that wokspace to somewhere else and switch back, it is still in the HDMI-1 monitor grid, but hyprland thinks that it's on VIRTUAL ( If I do a Edit: I've noticed that removing a monitor moves its workspaces to the first in the grid, not in the first available one. |
|
Hi, @raybbian! Edit:
Here is the desync Edit 2: hyprtasking/src/layout/grid.cpp Lines 49 to 51 in 3ca9c8f Unrelated, but wanted to ask anyway. Why is |
|
Try latest? |
|
@raybbian Latest works fine for me, tested it with and wirhout split-monitor-workspaces and im not facing any issues |
|
@raybbian Tried latest and found a single issue. If you fill up all the workspaces on main monitor with windows, make a window on a second monitor then unplug the second monitor, you will not have access to windows that were on that monitor. I think that the algorithm should just put all windows in the last workspace of the last available monitor when all workspaces on all monitors are occupied. So there were 3 workspaces on an unplugged monitor and 1 available workspace on monitor I didn't test the latest with 3 or more monitors so idk how it tries to fit windows if there is no place for them on one monitor and the slots are available on the second. |
|
@raybbian @yerlotic |
- Remove #define private public hack in layout_base.cpp - Replace concept-SFINAE config access with simple functions - Extract shared render_workspace/render_border helpers to layout_base - Dedup grid+linear render paths (~400 lines -> shared helpers) - Split change_layer() monster into calculate/find/apply helpers - Consolidate dispatch_move/dispatch_move_window into single impl - Cache dynamicPointerCast and get_view_from_id results - Extract create_workspace_at_grid_cell helper from input.cpp - Replace manual loops with std::ranges::find_if/any_of - Cache config lookups instead of repeated HTConfig::value() calls - Remove dead code, verbose comments, empty statement bodies - Add grid cell helpers: find_cell(), cell_at() to reduce nesting 11 files changed, 560 insertions(+), 634 deletions(-) Build passes, LSP clean (0 errors, 0 warnings)
… safety - Lazy-init par_view in get_monitor() instead of constructor where ht_manager doesn't yet have the view registered - Add ht_manager null guard before par_view lookup - Remove explicit monitor->m_activeWorkspace = start_workspace before CScopeGuard destructor fires (double restore caused focus issues) - Fix linear.cpp similarly for big_ws restore - Layout 0 LSP errors
…ect monitors - Add monitor_name to HTView (stable across DPMS cycles) - In register_monitors(), try to reconnect orphaned views by m_name before creating new ones, preventing workspace loss on reconnect - remove explicit monitor->m_activeWorkspace restore before CScopeGuard destructor (double-restore caused focus issues) - fix get_monitor() to lazy-init par_view with ht_manager null guard Build passes, LSP 0 errors
- Don't erase views just because get_monitor() returns null - Keep views with a known monitor_name so they can be reconnected when the same monitor wakes up with a new m_id
- In grid/linear render(), determine hovered workspace from cursor position - Render hovered workspace as 'active' (blue border, is_active=true) - Skip hovered in main loop, render it last as the active one - Remove 'big_ws' logic from linear layout; use same hover approach - render_workspace no longer sets m_activeWorkspace as side-effect; grid stores active_ws pointer, linear uses CScopeGuard - add pre_overview_workspace to HTView for exit_on_hovered=false fallback - do_exit_behavior: prefer hovered workspace if available Build passes, LSP 0 errors
- restore m_activeWorkspace setting in render_workspace() but with CScopeGuard so it is reset after each workspace render. This avoids the global 'active' state being set to the hovered workspace, which caused the mouse cursor to constantly change workspaces. - remove explicit m_activeWorkspace = pre_overview_ws from grid/linear after the loop (redundant with CScopeGuard). - pre_overview_workspace is now correctly restored from the CScopeGuard in both grid and linear, even when a different ws was hovered. - render_border is now called AFTER render_workspace, so the correct border color (active vs inactive) is used for each workspace. Build passes, LSP clean
- render_workspace() no longer sets m_activeWorkspace as side-effect. - The only thing that changes during hover is the border color (render_border uses flag explicitly). - This prevents the workspace from actually switching when hovering, while still showing a blue border on the hovered workspace. Build passes, LSP clean
- Revert grid.cpp render() to original behavior (active workspace rendering) - Restore linear.cpp to untouched original - render_workspace() uses for correct border color - Add variable for raw render_workspace_hook calls - Build passes, LSP clean
… logic - After clicking a workspace, the overview closes immediately - hyprtasking:move without move_window also closes overview now - Removed hover-active tracking; border color is purely based on again, like the original - Restored original render() behavior for grid and linear - Verified build passes and waybar shows correct active workspace
Build the tasking overview from Hyprland’s real workspace-to-monitor state instead of synthetic global workspace IDs. This prevents opening, rendering, closing, navigation, and drag-and-drop from moving or reassigning workspaces across monitors while keeping move targets tied to the actual displayed workspace.
Fixes #90
Fixes #96
Fixes #61