feature: moving windows between workspaces

This commit is contained in:
Matthew Kosarek 2024-02-09 15:28:04 -05:00
parent 030e06428e
commit 0e9eea63e2
8 changed files with 154 additions and 254 deletions

View File

@ -21,6 +21,8 @@
- `meta + shift + q`: Quit the selected application
- `meta + shift + e`: Close the compositor
- `meta + f`: Toggle fullscreen on the window
- `meta + [0-9]`: Move to workspace *n*
- `meta + shift + [0-9]`: Move active window to workspace *n*
# Pointer Behavior
- Hovering over a window will select the window
@ -65,7 +67,14 @@ First, let's define some reoccurring data types in the configuration file:
struct DefaultActionOverride
{
// Name of the action to override
name: "terminal" | "request_vertical" | "request_horizontal" | "toggle_resize" | "move_up" | "move_down" | "move_left" | "move_right" | "select_up" | "select_down" | "select_left" | "select_right" | "quit_active_window" | "quit_compositor" | "fullscreen";
name: "terminal" | "request_vertical" | "request_horizontal" | "toggle_resize" | "move_up" | "move_down"
| "move_left" | "move_right" | "select_up" | "select_down" | "select_left" | "select_right"
| "quit_active_window" | "quit_compositor" | "fullscreen" | "select_workspace_1" | "select_workspace_2"
| "select_workspace_3" | "select_workspace_4" | "select_workspace_5" | "select_workspace_6"
| "select_workspace_7" | "select_workspace_8" | "select_workspace_9" | "select_workspace_0"
| "move_to_workspace_1" | "move_to_workspace_2" | "move_to_workspace_3" | "move_to_workspace_4"
| "move_to_workspace_5" | "move_to_workspace_6" | "move_to_workspace_7" | "move_to_workspace_8"
| "move_to_workspace_9" | "move_to_workspace_0"
// Action will fire based on this key event
action: "up" | "down" | "repeat" | "modifiers";

View File

@ -121,6 +121,26 @@ MiracleConfig::MiracleConfig()
key_command = DefaultKeyCommand::SelectWorkspace9;
else if (name == "select_workspace_0")
key_command = DefaultKeyCommand::SelectWorkspace0;
else if (name == "move_to_workspace_1")
key_command = DefaultKeyCommand::MoveToWorkspace1;
else if (name == "move_to_workspace_2")
key_command = DefaultKeyCommand::MoveToWorkspace2;
else if (name == "move_to_workspace_3")
key_command = DefaultKeyCommand::MoveToWorkspace3;
else if (name == "move_to_workspace_4")
key_command = DefaultKeyCommand::MoveToWorkspace4;
else if (name == "move_to_workspace_5")
key_command = DefaultKeyCommand::MoveToWorkspace5;
else if (name == "move_to_workspace_6")
key_command = DefaultKeyCommand::MoveToWorkspace6;
else if (name == "move_to_workspace_7")
key_command = DefaultKeyCommand::MoveToWorkspace7;
else if (name == "move_to_workspace_8")
key_command = DefaultKeyCommand::MoveToWorkspace8;
else if (name == "move_to_workspace_9")
key_command = DefaultKeyCommand::MoveToWorkspace9;
else if (name == "move_to_workspace_0")
key_command = DefaultKeyCommand::MoveToWorkspace0;
else {
mir::log_error("default_action_overrides: Unknown key command override: %s", name.c_str());
continue;
@ -299,6 +319,56 @@ MiracleConfig::MiracleConfig()
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default,
KEY_0
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default | mir_input_event_modifier_shift,
KEY_1
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default | mir_input_event_modifier_shift,
KEY_2
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default | mir_input_event_modifier_shift,
KEY_3
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default | mir_input_event_modifier_shift,
KEY_4
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default | mir_input_event_modifier_shift,
KEY_5
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default | mir_input_event_modifier_shift,
KEY_6
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default | mir_input_event_modifier_shift,
KEY_7
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default | mir_input_event_modifier_shift,
KEY_8
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default | mir_input_event_modifier_shift,
KEY_9
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default | mir_input_event_modifier_shift,
KEY_0
}
};
for (int i = 0; i < DefaultKeyCommand::MAX; i++)

View File

@ -36,6 +36,16 @@ enum DefaultKeyCommand
SelectWorkspace8,
SelectWorkspace9,
SelectWorkspace0,
MoveToWorkspace1,
MoveToWorkspace2,
MoveToWorkspace3,
MoveToWorkspace4,
MoveToWorkspace5,
MoveToWorkspace6,
MoveToWorkspace7,
MoveToWorkspace8,
MoveToWorkspace9,
MoveToWorkspace0,
MAX
};

View File

@ -189,6 +189,36 @@ bool MiracleWindowManagementPolicy::handle_keyboard_event(MirKeyboardEvent const
case SelectWorkspace0:
workspace_manager.request_workspace(active_output->screen, '0');
break;
case MoveToWorkspace1:
workspace_manager.move_active_to_workspace(active_output->screen, '1');
break;
case MoveToWorkspace2:
workspace_manager.move_active_to_workspace(active_output->screen, '2');
break;
case MoveToWorkspace3:
workspace_manager.move_active_to_workspace(active_output->screen, '3');
break;
case MoveToWorkspace4:
workspace_manager.move_active_to_workspace(active_output->screen, '4');
break;
case MoveToWorkspace5:
workspace_manager.move_active_to_workspace(active_output->screen, '5');
break;
case MoveToWorkspace6:
workspace_manager.move_active_to_workspace(active_output->screen, '6');
break;
case MoveToWorkspace7:
workspace_manager.move_active_to_workspace(active_output->screen, '7');
break;
case MoveToWorkspace8:
workspace_manager.move_active_to_workspace(active_output->screen, '8');
break;
case MoveToWorkspace9:
workspace_manager.move_active_to_workspace(active_output->screen, '9');
break;
case MoveToWorkspace0:
workspace_manager.move_active_to_workspace(active_output->screen, '0');
break;
default:
std::cerr << "Unknown key_command: " << key_command << std::endl;
break;

View File

@ -810,3 +810,9 @@ void WindowTree::show()
}
nodes_to_resurrect.clear();
}
std::shared_ptr<Node> WindowTree::get_root_node()
{
return root_lane;
}

View File

@ -108,6 +108,8 @@ public:
/// Shows the entire tree
void show();
std::shared_ptr<Node> get_root_node();
private:
struct MoveResult
{

View File

@ -15,14 +15,14 @@ WorkspaceManager::WorkspaceManager(WindowManagerTools const& tools) :
{
}
bool WorkspaceManager::request_workspace(std::shared_ptr<Screen> screen, char key)
std::shared_ptr<Screen> WorkspaceManager::request_workspace(std::shared_ptr<Screen> screen, char key)
{
for (auto workspace : workspaces)
{
if (workspace.key == key)
{
workspace.screen->make_workspace_active(key);
return true;
return workspace.screen;
}
}
@ -31,7 +31,7 @@ bool WorkspaceManager::request_workspace(std::shared_ptr<Screen> screen, char ke
screen
});
screen->advise_new_workspace(key);
return true;
return screen;
}
bool WorkspaceManager::request_first_available_workspace(std::shared_ptr<Screen> screen)
@ -48,228 +48,30 @@ bool WorkspaceManager::request_first_available_workspace(std::shared_ptr<Screen>
}
if (can_use)
return request_workspace(screen, DEFAULT_WORKSPACES[i]);
{
request_workspace(screen, DEFAULT_WORKSPACES[i]);
return true;
}
}
return false;
}
bool WorkspaceManager::move_active_to_workspace(std::shared_ptr<Screen> screen, char workspace)
{
auto window = tools_.active_window();
if (!window)
return false;
//
//void miracle::WorkspaceManager::jump_to_workspace(bool take_active, int index)
//{
// tools_.invoke_under_lock(
// [this, take_active, index]
// {
// if (active_workspace_index != index)
// {
// auto const old_workspace = active_workspace_index;
// auto const& window = take_active ? tools_.active_window() : Window{};
// auto const& old_active = workspaces[active_workspace_index];
//
// while (workspaces.size() - 1 < index)
// workspaces.push_back(tools_.create_workspace());
//
// auto const& new_active = workspaces[index];
// change_active_workspace(new_active, old_active, window);
// erase_if_empty(std::next(workspaces.begin(), old_workspace));
// active_workspace_index = index;
// }
// });
//}
//
//void miracle::WorkspaceManager::workspace_begin(bool take_active)
//{
// jump_to_workspace(take_active, 0);
//}
//
//void miracle::WorkspaceManager::workspace_end(bool take_active)
//{
// jump_to_workspace(take_active, workspaces.size() - 1);
//}
//
//void miracle::WorkspaceManager::workspace_up(bool take_active)
//{
// if (active_workspace_index != 0)
// jump_to_workspace(take_active, active_workspace_index - 1);
//}
//
//void miracle::WorkspaceManager::workspace_down(bool take_active)
//{
// if (active_workspace_index != workspaces.size() - 1)
// jump_to_workspace(active_workspace_index + 1, take_active);
//}
//
//void miracle::WorkspaceManager::erase_if_empty(workspace_list::iterator const& old_workspace)
//{
// bool empty = true;
// tools_.for_each_window_in_workspace(*old_workspace, [&](auto ww)
// {
// if (is_application(tools_.info_for(ww).depth_layer()))
// empty = false;
// });
// if (empty)
// {
// workspace_to_active.erase(*old_workspace);
// workspaces.erase(old_workspace);
// }
//}
//
//void miracle::WorkspaceManager::apply_workspace_hidden_to(Window const& window)
//{
// auto const& window_info = tools_.info_for(window);
// auto& workspace_info = workspace_info_for(window_info);
// if (!workspace_info.in_hidden_workspace)
// {
// workspace_info.in_hidden_workspace = true;
// workspace_info.old_state = window_info.state();
//
// WindowSpecification modifications;
// modifications.state() = mir_window_state_hidden;
// tools_.place_and_size_for_state(modifications, window_info);
// tools_.modify_window(window_info.window(), modifications);
// }
//}
//
//void miracle::WorkspaceManager::apply_workspace_visible_to(Window const& window)
//{
// auto const& window_info = tools_.info_for(window);
// auto& workspace_info = workspace_info_for(window_info);
// if (workspace_info.in_hidden_workspace)
// {
// workspace_info.in_hidden_workspace = false;
// WindowSpecification modifications;
// modifications.state() = workspace_info.old_state;
// tools_.place_and_size_for_state(modifications, window_info);
// tools_.modify_window(window_info.window(), modifications);
// }
//}
//
//void miracle::WorkspaceManager::change_active_workspace(
// std::shared_ptr<Workspace> const& new_active,
// std::shared_ptr<Workspace> const& old_active,
// Window const& window)
//{
// if (new_active == old_active) return;
//
// auto const old_active_window = tools_.active_window();
// auto const old_active_window_shell = old_active_window &&
// !is_application(tools_.info_for(old_active_window).depth_layer());
//
// if (!old_active_window || old_active_window_shell)
// {
// // If there's no active window, the first shown grabs focus: get the right one
// if (auto const ww = workspace_to_active[new_active])
// {
// tools_.for_each_workspace_containing(ww, [&](std::shared_ptr<Workspace> const& ws)
// {
// if (ws == new_active)
// {
// apply_workspace_visible_to(ww);
// }
// });
//
// // If focus was on a shell window, put it on an app
// if (old_active_window_shell)
// tools_.select_active_window(ww);
// }
// }
//
// tools_.remove_tree_from_workspace(window, old_active);
// tools_.add_tree_to_workspace(window, new_active);
//
// tools_.for_each_window_in_workspace(new_active, [&](Window const& ww)
// {
// if (is_application(tools_.info_for(ww).depth_layer()))
// {
// apply_workspace_visible_to(ww);
// }
// });
//
// bool hide_old_active = false;
// tools_.for_each_window_in_workspace(old_active, [&](Window const& ww)
// {
// if (is_application(tools_.info_for(ww).depth_layer()))
// {
// if (ww == old_active_window)
// {
// // If we hide the active window focus will shift: do that last
// hide_old_active = true;
// return;
// }
//
// apply_workspace_hidden_to(ww);
// return;
// }
// });
//
// if (hide_old_active)
// {
// apply_workspace_hidden_to(old_active_window);
//
// // Remember the old active_window when we switch away
// workspace_to_active[old_active] = old_active_window;
// }
//}
//
//void miracle::WorkspaceManager::advise_adding_to_workspace(std::shared_ptr<Workspace> const& workspace,
// std::vector<Window> const& windows)
//{
// if (windows.empty())
// return;
//
// for (auto const& window : windows)
// {
// if (workspace == workspaces[active_workspace_index])
// {
// apply_workspace_visible_to(window);
// }
// else
// {
// apply_workspace_hidden_to(window);
// }
// }
//}
//
//auto miracle::WorkspaceManager::active_workspace() const -> std::shared_ptr<Workspace>
//{
// return workspaces[active_workspace_index];
//}
//
//bool miracle::WorkspaceManager::is_application(MirDepthLayer layer)
//{
// switch (layer)
// {
// case mir_depth_layer_application:
// case mir_depth_layer_always_on_top:
// return true;
//
// default:;
// return false;
// }
//}
//
//bool miracle::WorkspaceManager::in_hidden_workspace(WindowInfo const& info) const
//{
// auto& workspace_info = workspace_info_for(info);
//
// return workspace_info.in_hidden_workspace;
//}
//
//void miracle::WorkspaceManager::advise_new_window(WindowInfo const& window_info)
//{
// if (auto const& parent = window_info.parent())
// {
// if (workspace_info_for(tools_.info_for(parent)).in_hidden_workspace)
// apply_workspace_hidden_to(window_info.window());
// }
// else
// {
// tools_.add_tree_to_workspace(window_info.window(), active_workspace());
// }
//}
//
//auto miracle::WorkspaceManager::make_workspace_info() -> std::shared_ptr<WorkspaceInfo>
//{
// return std::make_shared<WorkspaceInfo>();
//}
auto& original_tree = screen->get_active_tree();
auto window_node = original_tree.get_root_node()->find_node_for_window(window);
original_tree.advise_delete_window(window);
auto screen_to_move_to = request_workspace(screen, workspace);
miral::WindowSpecification spec;
spec = screen_to_move_to->get_active_tree().allocate_position(spec);
tools_.modify_window(window, spec);
screen_to_move_to->get_active_tree().advise_new_window(tools_.info_for(window));
screen_to_move_to->get_active_tree().handle_window_ready(tools_.info_for(window));
return true;
}

View File

@ -43,40 +43,11 @@ public:
/// is created on the current Screen. If it does exist, we navigate
/// to the screen containing that workspace and show it if it
/// isn't already shown.
bool request_workspace(std::shared_ptr<Screen> screen, char workspace);
std::shared_ptr<Screen> request_workspace(std::shared_ptr<Screen> screen, char workspace);
bool request_first_available_workspace(std::shared_ptr<Screen> screen);
// void workspace_begin(bool take_active);
//
// void workspace_end(bool take_active);
//
// void workspace_up(bool take_active);
//
// void workspace_down(bool take_active);
//
// void jump_to_workspace(bool take_active, int index);
//
// void apply_workspace_hidden_to(Window const& window);
//
// void apply_workspace_visible_to(Window const& window);
//
// void change_active_workspace(
// std::shared_ptr<Workspace> const& ww,
// std::shared_ptr<Workspace> const& old_active,
// miral::Window const& window);
//
// void advise_new_window(const WindowInfo &window_info);
//
// void advise_adding_to_workspace(
// std::shared_ptr<Workspace> const& workspace,
// std::vector<Window> const& windows);
//
// auto active_workspace() const -> std::shared_ptr<Workspace>;
//
// bool in_hidden_workspace(WindowInfo const& info) const;
//
// static bool is_application(MirDepthLayer layer);
bool move_active_to_workspace(std::shared_ptr<Screen> screen, char workspace);
private:
WindowManagerTools tools_;