From 7804c8cf21ae9381bbae837dd89aa6dcb4756ce4 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Tue, 28 Nov 2023 17:23:50 -0500 Subject: [PATCH] feature: opening a new horizontal lane works --- src/miracle_window_management_policy.cpp | 20 ++- src/miracle_window_management_policy.h | 2 + src/protocols/xdg-shell-unstable-v6.xml | 4 +- src/window_tree.cpp | 162 ++++++++++++++++++++--- src/window_tree.h | 31 +++-- 5 files changed, 187 insertions(+), 32 deletions(-) diff --git a/src/miracle_window_management_policy.cpp b/src/miracle_window_management_policy.cpp index ac331d1..ebbb4d7 100644 --- a/src/miracle_window_management_policy.cpp +++ b/src/miracle_window_management_policy.cpp @@ -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()); +} \ No newline at end of file diff --git a/src/miracle_window_management_policy.h b/src/miracle_window_management_policy.h index 6696e13..49aa5da 100644 --- a/src/miracle_window_management_policy.h +++ b/src/miracle_window_management_policy.h @@ -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 diff --git a/src/protocols/xdg-shell-unstable-v6.xml b/src/protocols/xdg-shell-unstable-v6.xml index 0853034..d154b84 100644 --- a/src/protocols/xdg-shell-unstable-v6.xml +++ b/src/protocols/xdg-shell-unstable-v6.xml @@ -721,7 +721,7 @@ 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. @@ -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 diff --git a/src/window_tree.cpp b/src/window_tree.cpp index ac98bbb..cbd645d 100644 --- a/src/window_tree.cpp +++ b/src/window_tree.cpp @@ -15,35 +15,114 @@ */ #include "window_tree.h" +#include 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() { - return geom::Rectangle{window.top_left(), window.size()}; + 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 get_node() { + return node; + } + + /// Transforms this lane item into a node. + std::shared_ptr to_node() + { + if (node != nullptr) + return node; + + node = std::make_shared(); + node->items.push_back(std::make_shared(window)); + return node; + } + +private: miral::Window window; + std::shared_ptr node = nullptr; }; } WindowTree::WindowTree(geom::Size default_size) - : root{std::make_shared()}, - active{root}, + : root_lane{std::make_shared()}, + 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(window); + auto item = std::make_shared(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::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 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; } \ No newline at end of file diff --git a/src/window_tree.h b/src/window_tree.h index 819ef8b..84a1959 100644 --- a/src/window_tree.h +++ b/src/window_tree.h @@ -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 { - std::vector> items; + std::vector> 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 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 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 root; - std::shared_ptr active; - std::shared_ptr pending_lane; + std::shared_ptr root_lane; + std::shared_ptr active_lane; + std::shared_ptr pending_lane; + miral::Window active_window; geom::Size size; };