Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/user/keybinds.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ focused output, by index. They do not wrap around: `workspace-previous` on the
first workspace is a silent no-op. On a dynamic output, `workspace-next` reaches
the trailing empty workspace, which becomes active as usual.

`workspace-move-down` and `workspace-move-up` move the focused workspace up or down
on the focused output. They do not wrap around either.

The matching window actions can be bound independently:

```toml
Expand Down
2 changes: 2 additions & 0 deletions examples/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ follows_mouse = false
# "Mod+Period" = "workspace-next"
# "Mod+Shift+Comma" = "window-move-to-workspace-previous"
# "Mod+Shift+Period" = "window-move-to-workspace-next"
# "Mod+Ctrl+Comma" = "workspace-move-up"
# "Mod+Ctrl+Period" = "workspace-move-down"
# "Mod+Ctrl+H" = "output-focus-left"
# "Mod+Shift+O" = "dpms-off" # input activity powers displays back on
# "Mod+Ctrl+O" = "dpms-off:DP-1" # target one connector
Expand Down
2 changes: 2 additions & 0 deletions src/config/keybind_parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,12 @@ namespace umbriel {
{"window-toggle-maximize", "", KeybindAction::ToggleMaximize},
{"window-toggle-maximize-to-edges", "", KeybindAction::ToggleMaximizeToEdges},
{"window-toggle-pinned", "", KeybindAction::TogglePinned},
{"workspace-move-down", "", KeybindAction::WorkspaceMoveDown},
{"workspace-move-to-output-down", "", KeybindAction::WorkspaceMoveToOutputDown},
{"workspace-move-to-output-left", "", KeybindAction::WorkspaceMoveToOutputLeft},
{"workspace-move-to-output-right", "", KeybindAction::WorkspaceMoveToOutputRight},
{"workspace-move-to-output-up", "", KeybindAction::WorkspaceMoveToOutputUp},
{"workspace-move-up", "", KeybindAction::WorkspaceMoveUp},
{"workspace-next", "", KeybindAction::WorkspaceNext},
{"workspace-previous", "", KeybindAction::WorkspacePrevious},
{"workspace-set-layout", "<scrolling|dwindle|toggle>", KeybindAction::WorkspaceSetLayout,
Expand Down
2 changes: 2 additions & 0 deletions src/config/keybind_parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ namespace umbriel {
WorkspaceSetLayout,
DpmsOff,
DpmsOn,
WorkspaceMoveDown,
WorkspaceMoveUp,
Count,
};

Expand Down
2 changes: 2 additions & 0 deletions src/scene/cheatsheet_rows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@ namespace {
case A::WindowMoveToWorkspacePrevious:
case A::WorkspaceNext:
case A::WorkspacePrevious:
case A::WorkspaceMoveDown:
case A::WorkspaceMoveUp:
case A::WorkspaceSetLayout:
case A::WorkspaceMoveToOutputLeft:
case A::WorkspaceMoveToOutputRight:
Expand Down
11 changes: 11 additions & 0 deletions src/server/actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,15 @@ namespace umbriel {
return true;
}

template <int Direction> bool actionWorkspaceMove(Server& server, const Keybind& /*bind*/, std::string* /*error*/) {
Workspace* workspace = activeWorkspace(server);
if (workspace == nullptr || workspace->group() == nullptr) {
return true;
}
workspace->group()->moveActiveWorkspace(Direction);
return true;
}

template <int Sign> bool actionLayoutScroll(Server& server, const Keybind& bind, std::string* /*error*/) {
const int multiplier = bind.wheel != WheelDirection::None && tiledDragActive(server) ? 2 : 1;
scrollActiveLayout<Sign>(server, multiplier);
Expand Down Expand Up @@ -927,6 +936,8 @@ namespace umbriel {
&actionWorkspaceSetLayout,
&actionDpms<false>,
&actionDpms<true>,
&actionWorkspaceMove<1>,
&actionWorkspaceMove<-1>,
};

consteval bool everyActionHasHandler() {
Expand Down
37 changes: 37 additions & 0 deletions src/workspace/workspace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
#include <algorithm>
#include <charconv>
#include <cmath>
#include <cstddef>
#include <cstdio>
#include <utility>
#include "wlr.h"
// clang-format on

Expand Down Expand Up @@ -949,6 +951,41 @@ namespace umbriel {
return result;
}

bool WorkspaceGroup::moveActiveWorkspace(int direction) {
if (m_active == nullptr || m_workspaces.size() < 2 || direction == 0 || m_output == nullptr) {
return false;
}
const size_t index = m_active->index();
const auto target = static_cast<std::ptrdiff_t>(index) + direction;
if (target < 0 || target >= static_cast<std::ptrdiff_t>(m_workspaces.size())) {
return false;
}
if (m_dynamic && direction > 0) {
const bool targetIsTrailingEmpty = static_cast<size_t>(target) == m_workspaces.size() - 1
&& !m_workspaces[static_cast<size_t>(target)]->hasViews();
if (targetIsTrailingEmpty) {
return false;
}
}
slideFinish();
std::swap(m_workspaces[index], m_workspaces[static_cast<size_t>(target)]);
if (m_dynamic) {
refreshDynamicWorkspaceMetadata();
if (m_workspaces.back()->hasViews()) {
appendDynamicWorkspace();
}
} else {
for (const size_t slot : {index, static_cast<size_t>(target)}) {
Workspace* moved = m_workspaces[slot].get();
moved->rename(moved->name(), slot);
}
}
if (Overview* overview = m_server->overview(); overview != nullptr && overview->active()) {
overview->onWorkspaceInventoryChanged(this);
}
return true;
}

void WorkspaceGroup::reconcileInventory() {
slideFinish();
const char* outputName = m_output->wlr()->name != nullptr ? m_output->wlr()->name : "output";
Expand Down
1 change: 1 addition & 0 deletions src/workspace/workspace.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ namespace umbriel {
// Insert an empty numbered workspace into a dynamic group and renumber the following workspaces. Static configured
// groups cannot be extended this way and return null.
Workspace* insertDynamicWorkspace(size_t index);
bool moveActiveWorkspace(int direction);
void reconcileInventory();
void refreshLayouts();
void reconcileDynamic();
Expand Down