Skip to content

Commit

Permalink
feature: add the ability to drag to an empty tiling window tree (#339)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattkae authored Jan 14, 2025
1 parent e51f509 commit 4f28c54
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 27 deletions.
15 changes: 0 additions & 15 deletions src/command_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1020,21 +1020,6 @@ void CommandController::set_mode(WindowManagerMode mode)
mode_observer_registrar.advise_changed(state.mode());
}

void CommandController::drag_to(
std::shared_ptr<Container> const& dragging,
std::shared_ptr<Container> const& to)
{
if (dragging == to)
return;

// TODO: Convert dragging to a leaf beforehand
if (!to->is_leaf() || !dragging->is_leaf())
return;

auto tree = to->tree();
tree->move_to(*dragging, *to);
}

nlohmann::json CommandController::to_json() const
{
std::lock_guard lock(mutex);
Expand Down
1 change: 0 additions & 1 deletion src/command_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ class CommandController
bool reload_config();
void set_mode(WindowManagerMode mode);
void select_container(std::shared_ptr<Container> const&);
void drag_to(std::shared_ptr<Container> const& dragging, std::shared_ptr<Container> const& to);
[[nodiscard]] nlohmann::json to_json() const;
[[nodiscard]] nlohmann::json outputs_json() const;
[[nodiscard]] nlohmann::json workspaces_json() const;
Expand Down
37 changes: 36 additions & 1 deletion src/drag_and_drop_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ bool DragAndDropService::handle_pointer_event(CompositorState& state, const MirP
container_start_x + diff_x,
container_start_y + diff_y);

if (state.focused_output()->active()->get_tree()->is_empty())
{
drag_to(state.focused_container(), state.focused_output()->active()->get_tree());
return true;
}

// Get the intersection and try to move ourselves there. We only care if we're intersecting
// a leaf container, as those would be the only one in the grid.
std::shared_ptr<Container> intersected = state.focused_output()->intersect(event);
Expand All @@ -78,7 +84,7 @@ bool DragAndDropService::handle_pointer_event(CompositorState& state, const MirP
return true;

last_intersected = intersected;
command_controller.drag_to(state.focused_container(), intersected);
drag_to(state.focused_container(), intersected);
return true;
}
else if (action == mir_pointer_action_button_down)
Expand Down Expand Up @@ -111,4 +117,33 @@ bool DragAndDropService::handle_pointer_event(CompositorState& state, const MirP
}

return false;
}

void DragAndDropService::drag_to(
std::shared_ptr<Container> const& dragging,
std::shared_ptr<Container> const& to)
{
if (dragging == to)
return;

// TODO: Convert dragging to a leaf beforehand
if (!to->is_leaf() || !dragging->is_leaf())
return;

auto tree = to->tree();
tree->move_to(*dragging, *to);
}

void DragAndDropService::drag_to(
std::shared_ptr<Container> const& dragging,
TilingWindowTree* tree)
{
if (dragging->tree() == tree)
return;

// TODO: Convert dragging to a leaf beforehand
if (!dragging->is_leaf())
return;

tree->move_to_tree(dragging);
}
9 changes: 9 additions & 0 deletions src/drag_and_drop_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ namespace miracle
class Container;
class CompositorState;
class CommandController;
class TilingWindowTree;

class DragAndDropService
{
Expand All @@ -44,6 +45,14 @@ class DragAndDropService
int current_x = 0;
int current_y = 0;
std::weak_ptr<Container> last_intersected;

void drag_to(
std::shared_ptr<Container> const& dragging,
std::shared_ptr<Container> const& to);

void drag_to(
std::shared_ptr<Container> const& dragging,
TilingWindowTree* tree);
};

} // miracle
Expand Down
18 changes: 15 additions & 3 deletions src/tiling_window_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,19 @@ bool TilingWindowTree::move_to(Container& to_move, Container& target)
return true;
}

bool TilingWindowTree::move_to_tree(std::shared_ptr<Container> const& container)
{
// When we remove [node] from its initial position, there's a chance
// that the target_lane was melted into another lane. Hence, we need to return it
auto to_update = handle_remove(container);

root_lane->graft_existing(container, root_lane->num_nodes());
container->tree(root_lane->tree());
to_update->commit_changes();
root_lane->commit_changes();
return true;
}

void TilingWindowTree::request_layout(Container& container, miracle::LayoutScheme scheme)
{
handle_layout_scheme(scheme, container);
Expand Down Expand Up @@ -596,10 +609,9 @@ std::shared_ptr<ParentContainer> TilingWindowTree::handle_remove(std::shared_ptr
std::tuple<std::shared_ptr<ParentContainer>, std::shared_ptr<ParentContainer>> TilingWindowTree::transfer_node(
std::shared_ptr<Container> const& node, std::shared_ptr<Container> const& to)
{
auto to_update = handle_remove(node);

// When we remove [node] from its initial position, there's a chance
// that the target_lane was melted into another lane. Hence, we need to return it
auto to_update = handle_remove(node);
auto target_parent = Container::as_parent(to->get_parent().lock());
auto index = target_parent->get_index_of_node(to);
target_parent->graft_existing(node, index + 1);
Expand Down Expand Up @@ -758,7 +770,7 @@ std::shared_ptr<LeafContainer> TilingWindowTree::show()
return Container::as_leaf(fullscreen_node);
}

bool TilingWindowTree::is_empty()
bool TilingWindowTree::is_empty() const
{
return root_lane->num_nodes() == 0;
}
Expand Down
7 changes: 6 additions & 1 deletion src/tiling_window_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class TilingWindowTree
miral::WindowInfo const&,
std::shared_ptr<ParentContainer> const& container);

[[deprecated("Use move_to_tree instead")]]
void graft(std::shared_ptr<Container> const&, std::shared_ptr<ParentContainer> const& parent, int index = -1);

/// Try to resize the current active window in the provided direction
Expand All @@ -82,6 +83,10 @@ class TilingWindowTree
/// not have to be in the tree.
bool move_to(Container& to_move, Container& target);

/// Moves [to_move] to the first available position of the tree and handles
/// the process of updating its old position in tree, if any.
bool move_to_tree(std::shared_ptr<Container> const& container);

/// Select the next window in the provided direction
bool select_next(Direction direction, Container&);

Expand Down Expand Up @@ -135,7 +140,7 @@ class TilingWindowTree
void hide();

void recalculate_root_node_area();
bool is_empty();
bool is_empty() const;

[[nodiscard]] Workspace* get_workspace() const;
[[nodiscard]] std::shared_ptr<ParentContainer> const& get_root() const { return root_lane; }
Expand Down
2 changes: 1 addition & 1 deletion src/workspace.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class Workspace
[[nodiscard]] uint32_t id() const { return id_; }
[[nodiscard]] std::optional<int> num() const { return num_; }
[[nodiscard]] nlohmann::json to_json() const;
[[nodiscard]] TilingWindowTree const* get_tree() const { return tree.get(); }
[[nodiscard]] TilingWindowTree* get_tree() const { return tree.get(); }
[[nodiscard]] std::optional<std::string> const& name() const { return name_; }
[[nodiscard]] std::string display_name() const;

Expand Down
36 changes: 31 additions & 5 deletions tests/test_tiling_window_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,21 @@ const geom::Rectangle TREE_BOUNDS {
geom::Point(0, 0),
geom::Size(OUTPUT_WIDTH, OUTPUT_HEIGHT)
};

const geom::Rectangle OTHER_TREE_BOUNDS {
geom::Point(OUTPUT_WIDTH, OUTPUT_HEIGHT),
geom::Size(OUTPUT_WIDTH, OUTPUT_HEIGHT)
};
}

class SimpleTilingWindowTreeInterface : public TilingWindowTreeInterface
{
public:
explicit SimpleTilingWindowTreeInterface(geom::Rectangle const& bounds)
{
zones = { bounds };
}

std::vector<miral::Zone> const& get_zones() override
{
return zones;
Expand All @@ -54,15 +64,15 @@ class SimpleTilingWindowTreeInterface : public TilingWindowTreeInterface
}

private:
std::vector<miral::Zone> zones = { TREE_BOUNDS };
std::vector<miral::Zone> zones;
};

class TilingWindowTreeTest : public testing::Test
{
public:
TilingWindowTreeTest() :
tree(
std::make_unique<SimpleTilingWindowTreeInterface>(),
std::make_unique<SimpleTilingWindowTreeInterface>(TREE_BOUNDS),
window_controller,
state,
std::make_shared<test::StubConfiguration>(),
Expand Down Expand Up @@ -207,14 +217,14 @@ TEST_F(TilingWindowTreeTest, can_move_container_to_different_parent)
ASSERT_EQ(tree.get_root()->num_nodes(), 3);
}

TEST_F(TilingWindowTreeTest, can_move_container_to_other_tree)
TEST_F(TilingWindowTreeTest, can_move_container_to_container_in_other_tree)
{
TilingWindowTree other_tree(
std::make_unique<SimpleTilingWindowTreeInterface>(),
std::make_unique<SimpleTilingWindowTreeInterface>(OTHER_TREE_BOUNDS),
window_controller,
state,
std::make_shared<test::StubConfiguration>(),
TREE_BOUNDS);
OTHER_TREE_BOUNDS);
auto leaf1 = create_leaf();
auto leaf2 = create_leaf(nullptr, &other_tree);

Expand All @@ -226,6 +236,22 @@ TEST_F(TilingWindowTreeTest, can_move_container_to_other_tree)
ASSERT_EQ(leaf2->tree(), &other_tree);
}

TEST_F(TilingWindowTreeTest, can_move_container_to_tree)
{
TilingWindowTree other_tree(
std::make_unique<SimpleTilingWindowTreeInterface>(OTHER_TREE_BOUNDS),
window_controller,
state,
std::make_shared<test::StubConfiguration>(),
OTHER_TREE_BOUNDS);
auto leaf1 = create_leaf();

ASSERT_EQ(leaf1->tree(), &tree);
ASSERT_TRUE(other_tree.move_to_tree(leaf1));
ASSERT_EQ(leaf1->tree(), &other_tree);
ASSERT_EQ(leaf1->get_logical_area(), OTHER_TREE_BOUNDS);
}

TEST_F(TilingWindowTreeTest, dragged_windows_do_not_change_their_position_when_a_new_window_is_added)
{
auto leaf1 = create_leaf();
Expand Down

0 comments on commit 4f28c54

Please sign in to comment.