feature: opening a new horizontal lane works

This commit is contained in:
Matthew Kosarek 2023-11-28 17:23:50 -05:00
parent dff8dc83d5
commit 7804c8cf21
5 changed files with 187 additions and 32 deletions

View File

@ -49,11 +49,17 @@ bool MiracleWindowManagementPolicy::handle_keyboard_event(MirKeyboardEvent const
auto const scan_code = miral::toolkit::mir_keyboard_event_scan_code(event);
auto const modifiers = miral::toolkit::mir_keyboard_event_modifiers(event) & MODIFIER_MASK;
if (action == MirKeyboardAction::mir_keyboard_action_down && (modifiers && mir_input_event_modifier_alt)) {
if (scan_code == KEY_ENTER) {
if (action == MirKeyboardAction::mir_keyboard_action_down && (modifiers && mir_input_event_modifier_alt))
{
if (scan_code == KEY_ENTER)
{
external_client_launcher.launch({TERMINAL});
return true;
}
else if (scan_code == KEY_V)
{
tree.request_vertical();
}
}
return false;
@ -74,3 +80,13 @@ void MiracleWindowManagementPolicy::handle_window_ready(miral::WindowInfo &windo
// in the tree. This comes _after_ place_new_window has been called.
tree.confirm(window_info.window());
}
void MiracleWindowManagementPolicy::advise_focus_gained(const miral::WindowInfo &window_info)
{
tree.advise_focus_gained(window_info.window());
}
void MiracleWindowManagementPolicy::advise_focus_lost(const miral::WindowInfo &window_info)
{
tree.advise_focus_lost(window_info.window());
}

View File

@ -32,6 +32,8 @@ public:
miral::WindowSpecification const& requested_specification) -> miral::WindowSpecification override;
void handle_window_ready(
miral::WindowInfo& window_info) override;
void advise_focus_gained(miral::WindowInfo const& window_info) override;
void advise_focus_lost(miral::WindowInfo const& window_info) override;
private:
WindowTree tree; // TODO: Keep a list per output

View File

@ -721,7 +721,7 @@
<entry name="activated" value="4" summary="the surface is now activated">
<description summary="the surface is now activated">
Client window decorations should be painted as if the window is
active. Do not assume this means that the window actually has
active_lane. Do not assume this means that the window actually has
keyboard or pointer focus.
</description>
</entry>
@ -1013,7 +1013,7 @@
will follow the same dismissing order as required from the client.
The parent of a grabbing popup must either be another xdg_popup with an
active explicit grab, or an xdg_popup or xdg_toplevel, if there are no
active_lane explicit grab, or an xdg_popup or xdg_toplevel, if there are no
explicit grabs already taken.
If the topmost grabbing popup is destroyed, the grab will be returned to

View File

@ -15,35 +15,114 @@
*/
#include "window_tree.h"
#include <memory>
using namespace miracle;
namespace miracle
{
/// A window tree lane item _always_ begins its life as a single window.
/// When another window is added to it, it becomes a WindowTreeLane.
class WindowTreeLaneItem
/// Node Content _always_ begins its life as a single window.
/// When requested by the user, it turns itself into a new
/// node in the tree.
class NodeContent
{
public:
explicit WindowTreeLaneItem(miral::Window& window)
explicit NodeContent(miral::Window& window)
: window{window}
{
}
geom::Rectangle get_rectangle()
{
if (is_window())
{
return geom::Rectangle{window.top_left(), window.size()};
}
else
{
geom::Rectangle rectangle;
for (auto item : node->items)
{
auto item_rectangle = item->get_rectangle();
rectangle.top_left.x = std::min(rectangle.top_left.x, item_rectangle.top_left.x);
rectangle.top_left.y = std::min(rectangle.top_left.y, item_rectangle.top_left.y);
if (node->direction == Node::horizontal)
{
rectangle.size.width = geom::Width{rectangle.size.width.as_int() + item_rectangle.size.width.as_int()};
rectangle.size.height = std::max(
rectangle.size.height,
geom::Height{item_rectangle.top_left.y.as_int() + item_rectangle.size.height.as_int()});
}
else if (node->direction == Node::vertical)
{
rectangle.size.width = std::max(
rectangle.size.width,
geom::Width{item_rectangle.top_left.x.as_int() + item_rectangle.size.width.as_int()});
rectangle.size.height = geom::Height{rectangle.size.height.as_int() + item_rectangle.size.height.as_int()};
}
}
return rectangle;
}
}
void set_rectangle(geom::Rectangle rect)
{
if (is_window())
{
window.move_to(rect.top_left);
window.resize(rect.size);
}
else
{
// TODO: This needs to divide the space equally among windows
for (auto item : node->items)
{
item->set_rectangle(rect);
}
}
}
bool is_window()
{
return node == nullptr;
}
bool is_node()
{
return node != nullptr;
}
miral::Window& get_window() {
return window;
}
std::shared_ptr<Node> get_node() {
return node;
}
/// Transforms this lane item into a node.
std::shared_ptr<Node> to_node()
{
if (node != nullptr)
return node;
node = std::make_shared<Node>();
node->items.push_back(std::make_shared<NodeContent>(window));
return node;
}
private:
miral::Window window;
std::shared_ptr<Node> node = nullptr;
};
}
WindowTree::WindowTree(geom::Size default_size)
: root{std::make_shared<WindowTreeLane>()},
active{root},
: root_lane{std::make_shared<Node>()},
active_lane{root_lane},
size{default_size}
{
}
@ -51,8 +130,8 @@ WindowTree::WindowTree(geom::Size default_size)
miral::WindowSpecification WindowTree::allocate_position(const miral::WindowSpecification &requested_specification)
{
miral::WindowSpecification new_spec = requested_specification;
pending_lane = active;
if (root == active && root->items.empty())
pending_lane = active_lane;
if (root_lane == active_lane && root_lane->items.empty())
{
// Special case: take up the full size of the root node.
new_spec.top_left() = geom::Point{0, 0};
@ -74,8 +153,7 @@ miral::WindowSpecification WindowTree::allocate_position(const miral::WindowSpec
rectangle.top_left.x.as_int() + width_per_item * index,
rectangle.top_left.y
};
item->window.move_to(new_position);
item->window.resize(new_size);
item->set_rectangle(geom::Rectangle{new_position, new_size});
}
auto new_position = geom::Point{
@ -95,9 +173,10 @@ miral::Rectangle WindowTree::confirm(miral::Window &window)
return miral::Rectangle{};
}
auto item = std::make_shared<WindowTreeLaneItem>(window);
auto item = std::make_shared<NodeContent>(window);
pending_lane->items.push_back(item);
pending_lane = nullptr;
advise_focus_gained(window);
return item->get_rectangle();
}
@ -112,17 +191,26 @@ void WindowTree::resize(geom::Size new_size)
// TODO: Resize all windows
}
void WindowTreeLane::add(miral::Window &window)
void WindowTree::request_vertical()
{
active_lane = active_lane->to_lane(active_window);
active_lane->direction = Node::horizontal;
// This is when we add a new lane, which means that we need to know who is currently
// selected.
}
void WindowTreeLane::remove(miral::Window &window)
void WindowTree::advise_focus_gained(miral::Window& window)
{
active_lane = root_lane->find_lane(window);
active_window = window;
}
geom::Rectangle WindowTreeLane::get_rectangle()
void WindowTree::advise_focus_lost(miral::Window& window)
{
}
geom::Rectangle Node::get_rectangle()
{
geom::Rectangle rectangle{geom::Point{INT_MAX, INT_MAX}, geom::Size{0, 0}};
for (auto item : items)
@ -132,10 +220,48 @@ geom::Rectangle WindowTreeLane::get_rectangle()
rectangle.top_left.y = std::min(rectangle.top_left.y, item_rectangle.top_left.y);
rectangle.size.width = std::max(
rectangle.size.width,
geom::Width{item_rectangle.top_left.x.as_int() + item_rectangle.size.width.as_int()});
geom::Width{item_rectangle.size.width.as_int()});
rectangle.size.height = std::max(
rectangle.size.height,
geom::Height{item_rectangle.top_left.y.as_int() + item_rectangle.size.height.as_int()});
geom::Height{item_rectangle.size.height.as_int()});
}
return rectangle;
}
std::shared_ptr<Node> Node::find_lane(miral::Window &window)
{
for (auto item : items)
{
if (item->is_window())
{
if (item->get_window() == window)
return shared_from_this();
}
else
{
return item->get_node()->find_lane(window);
}
}
// TODO: Error
return nullptr;
}
std::shared_ptr<miracle::Node> Node::to_lane(miral::Window &window)
{
for (auto item : items)
{
if (item->is_window())
{
if (item->get_window() == window)
return item->to_node();
}
else
{
return item->get_node()->to_lane(window);
}
}
// TODO: Error
return nullptr;
}

View File

@ -29,27 +29,31 @@ namespace geom = mir::geometry;
namespace miracle
{
class WindowTreeLaneItem;
class NodeContent;
/// Terrible name. This describes an horizontal or vertical lane
/// This describes an horizontal or vertical lane
/// along which windows and other lanes may be laid out.
struct WindowTreeLane
struct Node : public std::enable_shared_from_this<Node>
{
std::vector<std::shared_ptr<WindowTreeLaneItem>> items;
std::vector<std::shared_ptr<NodeContent>> items;
enum {
horizontal,
vertical,
length
} direction;
} direction = horizontal;
/// The rectangle defined by the lane can be retrieved dynamically
/// by calculating the dimensions of all the windows involved in
/// this lane and its sub-lanes;
geom::Rectangle get_rectangle();
void add(miral::Window& window);
void remove(miral::Window& window);
/// Walk the tree to find the lane that contains this window.
std::shared_ptr<Node> find_lane(miral::Window& window);
/// Transform the window found in the list to a lane. Returns the
/// new window tree lane item if found, otherwise null.
std::shared_ptr<Node> to_lane(miral::Window& window);
};
/// Represents a tiling tree for an output.
@ -68,10 +72,17 @@ public:
void remove(miral::Window&);
void resize(geom::Size new_size);
// Request a change to vertical window placement
void request_vertical();
void advise_focus_gained(miral::Window&);
void advise_focus_lost(miral::Window&);
private:
std::shared_ptr<WindowTreeLane> root;
std::shared_ptr<WindowTreeLane> active;
std::shared_ptr<WindowTreeLane> pending_lane;
std::shared_ptr<Node> root_lane;
std::shared_ptr<Node> active_lane;
std::shared_ptr<Node> pending_lane;
miral::Window active_window;
geom::Size size;
};