kranewl

A wlroots-based dynamic Wayland compositor, written in C++, configurable with Lua
git clone git://git.deurzen.net/kranewl
Log | Files | Refs | LICENSE

commit 67fc7bc8bb2b264864d6b6c1ecfd8c870311b8d4
parent deb1f490ba502a6a56a59cace8f57c2d0c445412
Author: deurzen <max@deurzen.net>
Date:   Sun,  5 Jun 2022 17:48:12 +0200

implements track layer cycling

Diffstat:
Minclude/kranewl/cycle.hh | 7+++++--
Minclude/kranewl/cycle.t.hh | 13++++++++++---
Minclude/kranewl/input/key-bindings.hh | 22++++++++++++++--------
Minclude/kranewl/model.hh | 15++++++++-------
Minclude/kranewl/tree/view.hh | 5+++++
Minclude/kranewl/workspace.hh | 37+++++++++++++++++++++++--------------
Msrc/kranewl/input/cursor.cc | 4++--
Msrc/kranewl/model.cc | 139+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/kranewl/tree/output.cc | 5++++-
Msrc/kranewl/tree/view.cc | 18+++++++++++++++---
Msrc/kranewl/tree/xwayland-view.cc | 9+++------
Msrc/kranewl/workspace.cc | 294++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
12 files changed, 444 insertions(+), 124 deletions(-)

diff --git a/include/kranewl/cycle.hh b/include/kranewl/cycle.hh @@ -261,8 +261,8 @@ public: void reverse(); void rotate(Direction); void rotate_range(Direction, Index, Index); - std::optional<T> cycle_active(Direction); - std::optional<T> drag_active(Direction); + std::pair<std::optional<T>, std::optional<T>> cycle_active(Direction); + std::pair<std::optional<T>, std::optional<T>> drag_active(Direction); template<typename UnaryPredicate> std::optional<T> @@ -406,3 +406,6 @@ private: HistoryStack<T> m_stack; }; + +template<typename T> +using Cycle_ptr = Cycle<T>*; diff --git a/include/kranewl/cycle.t.hh b/include/kranewl/cycle.t.hh @@ -497,16 +497,23 @@ Cycle<T>::rotate_range(Direction direction, Index begin, Index end) } template <typename T> -std::optional<T> +std::pair<std::optional<T>, std::optional<T>> Cycle<T>::cycle_active(Direction direction) { push_active_to_stack(); + + std::optional<T> prev_active = active_element(); m_index = next_index(direction); - return active_element(); + std::optional<T> next_active = active_element(); + + return std::pair{ + prev_active, + next_active + }; } template <typename T> -std::optional<T> +std::pair<std::optional<T>, std::optional<T>> Cycle<T>::drag_active(Direction direction) { Index index = next_index(direction); diff --git a/include/kranewl/input/key-bindings.hh b/include/kranewl/input/key-bindings.hh @@ -97,7 +97,7 @@ static const KeyBindings key_bindings = { .action = [](Model& model) { View_ptr focus = model.focused_view(); - if (focus && model.is_free(focus)) + if (focus && focus->free()) model.nudge_focus(Edge::Left, 50); else model.shuffle_main(Direction::Backward); @@ -110,7 +110,7 @@ static const KeyBindings key_bindings = { .action = [](Model& model) { View_ptr focus = model.focused_view(); - if (focus && model.is_free(focus)) + if (focus && focus->free()) model.nudge_focus(Edge::Bottom, 50); else model.shuffle_stack(Direction::Forward); @@ -123,7 +123,7 @@ static const KeyBindings key_bindings = { .action = [](Model& model) { View_ptr focus = model.focused_view(); - if (focus && model.is_free(focus)) + if (focus && focus->free()) model.nudge_focus(Edge::Top, 50); else model.shuffle_stack(Direction::Backward); @@ -136,7 +136,7 @@ static const KeyBindings key_bindings = { .action = [](Model& model) { View_ptr focus = model.focused_view(); - if (focus && model.is_free(focus)) + if (focus && focus->free()) model.nudge_focus(Edge::Right, 50); else model.shuffle_main(Direction::Forward); @@ -218,28 +218,34 @@ static const KeyBindings key_bindings = { }, { { XKB_KEY_j, MODKEY }, { - .action = CALL(cycle_track(Direction::Forward)), + .action = CALL(cycle_focus_track(Direction::Forward)), .repeatable = true } }, { { XKB_KEY_k, MODKEY }, { - .action = CALL(cycle_track(Direction::Backward)), + .action = CALL(cycle_focus_track(Direction::Backward)), .repeatable = true } }, { { XKB_KEY_J, MODKEY | WLR_MODIFIER_SHIFT }, { - .action = CALL(drag_focus(Direction::Forward)), + .action = CALL(drag_focus_track(Direction::Forward)), .repeatable = true } }, { { XKB_KEY_K, MODKEY | WLR_MODIFIER_SHIFT }, { - .action = CALL(drag_focus(Direction::Backward)), + .action = CALL(drag_focus_track(Direction::Backward)), .repeatable = true } }, +{ { XKB_KEY_a, MODKEY }, + { + .action = CALL(cycle_track(Direction::Forward)), + .repeatable = false + } +}, { { XKB_KEY_r, MODKEY }, { .action = CALL(reverse_views()), diff --git a/include/kranewl/model.hh b/include/kranewl/model.hh @@ -90,6 +90,7 @@ public: void focus_view(View_ptr); void refocus(); void place_view(Placement&); + void relayer_view(View_ptr, SceneLayer); bool view_matches_search(View_ptr, SearchSelector const&) const; View_ptr search_view(SearchSelector const&); @@ -101,14 +102,19 @@ public: void abort_cursor_interactive(); void cycle_focus(Direction); - void cycle_track(Direction); + void cycle_focus_track(Direction); void drag_focus(Direction); - void drag_track(Direction); + void drag_focus_track(Direction); + + void toggle_track(); + void activate_track(SceneLayer); + void cycle_track(Direction); void sync_focus(); void relayer_views(Workspace_ptr); void relayer_views(Context_ptr); void relayer_views(Output_ptr); + void move_view_to_track(View_ptr, SceneLayer); void reverse_views(); void rotate_views(Direction); @@ -205,9 +211,6 @@ public: void set_focus_follows_cursor(Toggle, Workspace_ptr); void set_focus_follows_cursor(Toggle, Context_ptr); - bool is_free(View_ptr) const; - bool belongs_to_track(View_ptr) const; - void spawn_external(std::string&&) const; Output_ptr mp_output; @@ -238,8 +241,6 @@ private: View_ptr mp_focus; View_ptr mp_jumped_from; - SceneLayer m_current_track; - std::vector<std::tuple<SearchSelector_ptr, Rules>> m_default_rules; const KeyBindings m_key_bindings; diff --git a/include/kranewl/tree/view.hh b/include/kranewl/tree/view.hh @@ -102,6 +102,7 @@ typedef struct View : public Node { bool focused() const { return m_focused; } bool mapped() const { return m_mapped; } bool managed() const { return m_managed; } + bool free() const { return m_free; } bool urgent() const { return m_urgent; } bool floating() const { return m_floating; } bool fullscreen() const { return m_fullscreen; } @@ -116,6 +117,7 @@ typedef struct View : public Node { void set_focused(bool); void set_mapped(bool); void set_managed(bool); + void set_free(bool); void set_urgent(bool); void set_floating(bool); void set_fullscreen(bool); @@ -127,6 +129,8 @@ typedef struct View : public Node { void set_iconified(bool); void set_disowned(bool); + bool belongs_to_active_track() const; + std::chrono::time_point<std::chrono::steady_clock> last_focused() const; std::chrono::time_point<std::chrono::steady_clock> last_touched() const; std::chrono::time_point<std::chrono::steady_clock> managed_since() const; @@ -207,6 +211,7 @@ private: bool m_focused; bool m_mapped; bool m_managed; + bool m_free; bool m_urgent; bool m_floating; bool m_fullscreen; diff --git a/include/kranewl/workspace.hh b/include/kranewl/workspace.hh @@ -5,6 +5,7 @@ #include <kranewl/geometry.hh> #include <kranewl/layout.hh> #include <kranewl/placement.hh> +#include <kranewl/scene-layer.hh> #include <kranewl/util.hh> typedef struct View* View_ptr; @@ -73,18 +74,8 @@ public: }; - Workspace(Index index, std::string name, Context_ptr context) - : m_index(index), - m_name(name), - m_layout_handler({}), - mp_context(context), - mp_active(nullptr), - m_views({}, true), - m_free_views({}, true), - m_iconified_views({}, true), - m_disowned_views({}, true), - m_focus_follows_cursor(true) - {} + Workspace(Index, std::string, Context_ptr); + ~Workspace(); bool empty() const; bool contains(View_ptr) const; @@ -100,6 +91,7 @@ public: bool layout_wraps() const; Index size() const; + Index track_size() const; Index length() const; int main_count() const; @@ -111,6 +103,15 @@ public: std::string identifier() const; View_ptr active() const; + SceneLayer track_layer() const; + void activate_track(SceneLayer); + void toggle_track(); + void cycle_track(Direction); + + void add_view_to_track(View_ptr, SceneLayer); + void remove_view_from_track(View_ptr, SceneLayer); + void change_view_track(View_ptr, SceneLayer); + Cycle<View_ptr> const& views() const; std::vector<View_ptr> stack_after_focus() const; @@ -118,8 +119,10 @@ public: View_ptr prev_view() const; std::optional<View_ptr> find_view(ViewSelector const&) const; - void cycle(Direction); - void drag(Direction); + std::pair<std::optional<View_ptr>, std::optional<View_ptr>> cycle(Direction); + std::pair<std::optional<View_ptr>, std::optional<View_ptr>> cycle_focus_track(Direction); + std::pair<std::optional<View_ptr>, std::optional<View_ptr>> drag(Direction); + std::pair<std::optional<View_ptr>, std::optional<View_ptr>> drag_focus_track(Direction); template<typename UnaryPredicate> void @@ -243,6 +246,12 @@ private: View_ptr mp_active; Cycle<View_ptr> m_views; + + SceneLayer m_track_layer; + SceneLayer m_prev_track_layer; + const std::array<Cycle_ptr<View_ptr>, 8> m_tracks; + Cycle_ptr<View_ptr> mp_track; + Cycle<View_ptr> m_free_views; Cycle<View_ptr> m_iconified_views; Cycle<View_ptr> m_disowned_views; diff --git a/src/kranewl/input/cursor.cc b/src/kranewl/input/cursor.cc @@ -388,7 +388,7 @@ cursor_motion_to_client( cursor->mp_seat->mp_wlr_seat ); - if (view && view != prev_view && cursor->mp_model->belongs_to_track(view) + if (view && view != prev_view && view->belongs_to_active_track() && view->mp_workspace->focus_follows_cursor() && view->managed()) { cursor->mp_seat->mp_model->focus_view(view); @@ -558,7 +558,7 @@ Cursor::handle_cursor_button(struct wl_listener* listener, void* data) return; if (view && (!cursor->mp_model->mp_workspace->focus_follows_cursor() - || !cursor->mp_model->belongs_to_track(view))) + || !view->belongs_to_active_track())) { if (!view->focused() && view->managed()) cursor->mp_seat->mp_model->focus_view(view); diff --git a/src/kranewl/model.cc b/src/kranewl/model.cc @@ -56,7 +56,6 @@ Model::Model(Config const& config) m_sticky_views{}, mp_focus(nullptr), mp_jumped_from(nullptr), - m_current_track(SceneLayer::SCENE_LAYER_TILE), m_key_bindings(Bindings::key_bindings), m_cursor_bindings(Bindings::cursor_bindings) { @@ -310,10 +309,11 @@ Model::focus_view(View_ptr view) view->set_urgent(false); mp_focus = view; - m_current_track = view->scene_layer(); if (mp_workspace->layout_is_persistent() || mp_workspace->layout_is_single()) apply_layout(mp_workspace); + + view->mp_workspace->activate_track(view->scene_layer()); } void @@ -358,13 +358,17 @@ Model::place_view(Placement& placement) switch (placement.method) { case Placement::PlacementMethod::Free: { + view->set_free(true); view->set_free_decoration(placement.decoration); + move_view_to_track(view, SceneLayer::SCENE_LAYER_FREE); break; } case Placement::PlacementMethod::Tile: { + view->set_free(false); view->set_free_decoration(FREE_DECORATION); view->set_tile_decoration(placement.decoration); + move_view_to_track(view, SceneLayer::SCENE_LAYER_TILE); break; } } @@ -376,15 +380,19 @@ Model::place_view(Placement& placement) switch (placement.method) { case Placement::PlacementMethod::Free: { + view->set_free(true); view->set_free_decoration(placement.decoration); view->set_free_region(*placement.region); + move_view_to_track(view, SceneLayer::SCENE_LAYER_FREE); break; } case Placement::PlacementMethod::Tile: { + view->set_free(false); view->set_free_decoration(FREE_DECORATION); view->set_tile_decoration(placement.decoration); view->set_tile_region(*placement.region); + move_view_to_track(view, SceneLayer::SCENE_LAYER_TILE); break; } } @@ -508,7 +516,7 @@ Model::cursor_interactive(Cursor::Mode mode, View_ptr view) { TRACE(); - if (is_free(view)) + if (view->free()) mp_server->mp_seat->mp_cursor->initiate_cursor_interactive(mode, view); } @@ -545,20 +553,20 @@ void Model::relayer_views(Workspace_ptr workspace) { for (View_ptr view : *workspace) { - if (is_free(view)) { + if (view->free()) { if (view->scene_layer() != SceneLayer::SCENE_LAYER_FREE) - view->relayer(SceneLayer::SCENE_LAYER_FREE); + move_view_to_track(view, SceneLayer::SCENE_LAYER_FREE); view->lower(); } else { if (view->scene_layer() != SceneLayer::SCENE_LAYER_TILE) { - view->relayer(SceneLayer::SCENE_LAYER_TILE); + move_view_to_track(view, SceneLayer::SCENE_LAYER_TILE); } } } if (mp_focus) { mp_focus->raise(); - m_current_track = mp_focus->scene_layer(); + mp_focus->mp_workspace->activate_track(mp_focus->scene_layer()); } } @@ -575,6 +583,15 @@ Model::relayer_views(Output_ptr output) } void +Model::move_view_to_track(View_ptr view, SceneLayer layer) +{ + TRACE(); + + view->mp_workspace->change_view_track(view, layer); + view->relayer(layer); +} + +void Model::cycle_focus(Direction direction) { TRACE(); @@ -587,13 +604,14 @@ Model::cycle_focus(Direction direction) } void -Model::cycle_track(Direction direction) +Model::cycle_focus_track(Direction direction) { TRACE(); - mp_workspace->cycle_with_condition(direction, [this](View_ptr view) { - return belongs_to_track(view); - }); + if (mp_workspace->track_size() <= 1) + return; + + mp_workspace->cycle_focus_track(direction); sync_focus(); } @@ -611,21 +629,51 @@ Model::drag_focus(Direction direction) } void -Model::drag_track(Direction direction) +Model::drag_focus_track(Direction direction) { TRACE(); - if (mp_workspace->size() <= 1) + if (mp_workspace->track_size() <= 1) return; - mp_workspace->drag_with_condition(direction, [this](View_ptr view) { - return belongs_to_track(view); - }); + auto [prev,next] = mp_workspace->drag_focus_track(direction); + + if (prev && next) { + Region region_prev = (*prev)->free_region(); + Region region_next = (*next)->free_region(); + (*prev)->set_free_region(region_next); + (*next)->set_free_region(region_prev); + } + sync_focus(); apply_layout(mp_workspace); } void +Model::toggle_track() +{ + TRACE(); + mp_workspace->toggle_track(); + sync_focus(); +} + +void +Model::activate_track(SceneLayer layer) +{ + TRACE(); + mp_workspace->activate_track(layer); + sync_focus(); +} + +void +Model::cycle_track(Direction direction) +{ + TRACE(); + mp_workspace->cycle_track(direction); + sync_focus(); +} + +void Model::reverse_views() { TRACE(); @@ -1163,8 +1211,8 @@ Model::toggle_layout() TRACE(); mp_workspace->toggle_layout(); - relayer_views(mp_workspace); apply_layout(mp_workspace); + relayer_views(mp_workspace); } void @@ -1173,8 +1221,8 @@ Model::set_layout(LayoutHandler::LayoutKind layout) TRACE(); mp_workspace->set_layout(layout); - relayer_views(mp_workspace); apply_layout(mp_workspace); + relayer_views(mp_workspace); } void @@ -1195,7 +1243,7 @@ Model::set_layout_retain_region(LayoutHandler::LayoutKind layout) views.end(), std::back_inserter(regions), [=,this](View_ptr view) -> Region { - if (is_free(view)) + if (view->free()) return view->free_region(); else return view->tile_region(); @@ -1390,9 +1438,10 @@ Model::set_floating_view(Toggle toggle, View_ptr view) } apply_layout(view->mp_workspace); + move_view_to_track(view, view->scene_layer()); if (view == mp_focus) - m_current_track = view->scene_layer(); + view->mp_workspace->activate_track(view->scene_layer()); } void @@ -1453,7 +1502,8 @@ Model::set_fullscreen_view(Toggle toggle, View_ptr view) apply_layout(workspace); if (view == mp_focus) - m_current_track = view->scene_layer(); + // TODO: separate layer? + move_view_to_track(view, SceneLayer::SCENE_LAYER_OVERLAY); } void @@ -1706,7 +1756,7 @@ Model::center_view(View_ptr view) { TRACE(); - if (!is_free(view) || !view->mp_output) + if (!view->free() || !view->mp_output) return; view->center(); @@ -1735,7 +1785,7 @@ Model::nudge_view(Edge edge, Util::Change<std::size_t> change, View_ptr view) { TRACE(); - if (!is_free(view)) + if (!view->free()) return; Region region = view->free_region(); @@ -1786,7 +1836,7 @@ Model::stretch_view(Edge edge, Util::Change<int> change, View_ptr view) { TRACE(); - if (!is_free(view)) + if (!view->free()) return; Decoration decoration = view->free_decoration(); @@ -1882,7 +1932,7 @@ Model::inflate_view(Util::Change<int> change, View_ptr view) { TRACE(); - if (!is_free(view)) + if (!view->free()) return; Decoration decoration = view->free_decoration(); @@ -1955,7 +2005,7 @@ Model::snap_view(View_ptr view, uint32_t edges) { TRACE(); - if (!is_free(view)) + if (!view->free()) return; Region region = view->free_region(); @@ -2088,23 +2138,23 @@ Model::initialize_view(View_ptr view, Workspace_ptr workspace) if (rules.to_workspace && *rules.to_workspace < context->workspaces().size()) workspace = (*context)[*rules.to_workspace]; - move_view_to_workspace(view, workspace); + if (rules.do_float) { + view->set_floating(*rules.do_float); + view->relayer(*rules.do_float + ? SceneLayer::SCENE_LAYER_FREE + : SceneLayer::SCENE_LAYER_TILE + ); + } else + view->relayer(SceneLayer::SCENE_LAYER_TILE); - view->relayer( - is_free(view) - ? SCENE_LAYER_FREE - : SCENE_LAYER_TILE - ); + move_view_to_workspace(view, workspace); - if (rules.do_float) - set_floating_view(*rules.do_float ? Toggle::On : Toggle::Off, view); if (rules.snap_edges) snap_view(view, *rules.snap_edges); if (rules.do_fullscreen) set_fullscreen_view(*rules.do_fullscreen ? Toggle::On : Toggle::Off, view); - if (view == mp_focus) - m_current_track = view->scene_layer(); + move_view_to_track(view, view->scene_layer()); } XDGView_ptr @@ -2248,23 +2298,6 @@ Model::register_layer(Layer_ptr layer) spdlog::info("Registered layer {}", layer->uid_formatted()); } -bool -Model::is_free(View_ptr view) const -{ - return View::is_free(view) - || ((!view->fullscreen() || view->contained()) - && (view->sticky() - ? mp_workspace - : view->mp_workspace - )->layout_is_free()); -} - -bool -Model::belongs_to_track(View_ptr view) const -{ - return m_current_track == view->scene_layer(); -} - void Model::spawn_external(std::string&& command) const { diff --git a/src/kranewl/tree/output.cc b/src/kranewl/tree/output.cc @@ -105,8 +105,11 @@ Output::handle_present(struct wl_listener* listener, void*) View_ptr view_under_cursor = output->mp_seat->mp_cursor->view_under_cursor(); - if (view_under_cursor && view_under_cursor->managed()) + if (view_under_cursor && view_under_cursor->managed() + && view_under_cursor->belongs_to_active_track()) + { output->mp_model->focus_view(view_under_cursor); + } } output->m_cursor_focus_on_present = false; diff --git a/src/kranewl/tree/view.cc b/src/kranewl/tree/view.cc @@ -56,6 +56,7 @@ View::View( m_focused(false), m_mapped(false), m_managed(true), + m_free(false), m_urgent(false), m_floating(false), m_fullscreen(false), @@ -176,6 +177,12 @@ View::set_managed(bool managed) } void +View::set_free(bool free) +{ + m_free = free; +} + +void View::set_urgent(bool urgent) { m_urgent = urgent; @@ -265,6 +272,12 @@ View::set_disowned(bool disowned) } } +bool +View::belongs_to_active_track() const +{ + return m_scene_layer == mp_workspace->track_layer(); +} + std::chrono::time_point<std::chrono::steady_clock> View::last_focused() const { @@ -321,7 +334,7 @@ View::tile(Toggle toggle) return; set_floating(false); - if (!mp_model->is_free(this)) + if (!m_free) relayer(SCENE_LAYER_TILE); break; @@ -332,7 +345,7 @@ View::tile(Toggle toggle) return; set_floating(true); - if (mp_model->is_free(this)) + if (m_free) relayer(SCENE_LAYER_FREE); break; @@ -357,7 +370,6 @@ View::relayer(SceneLayer layer) return; m_scene_layer = layer; - wlr_scene_node_reparent( mp_scene, mp_server->m_scene_layers[layer] diff --git a/src/kranewl/tree/xwayland-view.cc b/src/kranewl/tree/xwayland-view.cc @@ -431,7 +431,7 @@ XWaylandView::handle_request_configure(struct wl_listener* listener, void* data) return; } - if (view->mp_model->is_free(view)) { + if (view->free()) { view->set_preferred_dim(Dim{ .w = event->width, .h = event->height, @@ -490,7 +490,7 @@ XWaylandView::handle_request_move(struct wl_listener* listener, void*) if (!xwayland_surface->mapped) return; - if (!view->mp_model->is_free(view)) + if (!view->free()) return; view->mp_model->cursor_interactive(Cursor::Mode::Move, view); @@ -504,10 +504,7 @@ XWaylandView::handle_request_resize(struct wl_listener* listener, void* data) XWaylandView_ptr view = wl_container_of(listener, view, ml_request_resize); struct wlr_xwayland_surface* xwayland_surface = view->mp_wlr_xwayland_surface; - if (!xwayland_surface->mapped) - return; - - if (!view->mp_model->is_free(view)) + if (!xwayland_surface->mapped || !view->free()) return; struct wlr_xwayland_resize_event* event diff --git a/src/kranewl/workspace.cc b/src/kranewl/workspace.cc @@ -10,6 +10,35 @@ #include <algorithm> #include <optional> +Workspace::Workspace(Index index, std::string name, Context_ptr context) + : m_index(index), + m_name(name), + m_layout_handler({}), + mp_context(context), + mp_active(nullptr), + m_views({}, true), + m_track_layer(SceneLayer::SCENE_LAYER_FREE), + m_prev_track_layer(SceneLayer::SCENE_LAYER_TILE), + m_tracks({ + new Cycle<View_ptr>{{}, false}, // SceneLayer::SCENE_LAYER_BACKGROUND + new Cycle<View_ptr>{{}, true}, // SceneLayer::SCENE_LAYER_BOTTOM + new Cycle<View_ptr>{{}, true}, // SceneLayer::SCENE_LAYER_TILE + new Cycle<View_ptr>{{}, true}, // SceneLayer::SCENE_LAYER_FREE + new Cycle<View_ptr>{{}, true}, // SceneLayer::SCENE_LAYER_TOP + new Cycle<View_ptr>{{}, true}, // SceneLayer::SCENE_LAYER_OVERLAY + new Cycle<View_ptr>{{}, false}, // SceneLayer::SCENE_LAYER_POPUP + new Cycle<View_ptr>{{}, false}, // SceneLayer::SCENE_LAYER_NOFOCUS + }), + mp_track(m_tracks[SceneLayer::SCENE_LAYER_FREE]), + m_free_views({}, true), + m_iconified_views({}, true), + m_disowned_views({}, true), + m_focus_follows_cursor(true) +{} + +Workspace::~Workspace() +{} + bool Workspace::empty() const { @@ -77,6 +106,12 @@ Workspace::size() const } std::size_t +Workspace::track_size() const +{ + return mp_track->size(); +} + +std::size_t Workspace::length() const { return m_views.length(); @@ -134,23 +169,112 @@ Workspace::active() const } -Cycle<View_ptr> const& -Workspace::views() const +SceneLayer +Workspace::track_layer() const { - return m_views; + return m_track_layer; +} + +void +Workspace::activate_track(SceneLayer layer) +{ + TRACE(); + + mp_track = m_tracks[layer]; + mp_active = mp_track->active_element().value_or(nullptr); + m_prev_track_layer = m_track_layer; + m_track_layer = layer; + + if (mp_active) + m_views.activate_element(mp_active); +} + +void +Workspace::toggle_track() +{ + TRACE(); + activate_track(m_prev_track_layer); +} + +void +Workspace::cycle_track(Direction direction) +{ + TRACE(); + + int last_index, cycles; + last_index = cycles = Util::last_index(m_tracks); + + do { + switch (direction) { + case Direction::Backward: + m_track_layer = static_cast<SceneLayer>( + m_track_layer == 0 + ? last_index + : m_track_layer - 1 + ); + break; + case Direction::Forward: + m_track_layer = static_cast<SceneLayer>( + m_track_layer == last_index + ? 0 + : m_track_layer + 1 + ); + break; + default: return; + } + } while (m_tracks[m_track_layer]->empty() && cycles--); + + activate_track(m_track_layer); +} + +void +Workspace::add_view_to_track(View_ptr view, SceneLayer layer) +{ + TRACE(); + + if (m_tracks[layer]->contains(view)) + return; + + m_tracks[layer]->insert_at_back(view); + + if (layer == m_track_layer) + mp_active = view; } -std::vector<View_ptr> -Workspace::stack_after_focus() const +void +Workspace::remove_view_from_track(View_ptr view, SceneLayer layer) { - std::vector<View_ptr> stack = m_views.stack(); + TRACE(); + + m_tracks[layer]->remove_element(view); - if (mp_active) { - Util::erase_remove(stack, mp_active); - stack.push_back(mp_active); + if (layer == m_track_layer) { + mp_active = m_tracks[layer]->active_element().value_or(nullptr); + + if (mp_active) + m_views.activate_element(mp_active); + else + cycle_track(Direction::Forward); } +} - return stack; +void +Workspace::change_view_track(View_ptr view, SceneLayer layer) +{ + TRACE(); + + if (view->scene_layer() == layer) + return; + + remove_view_from_track(view, view->scene_layer()); + add_view_to_track(view, layer); +} + + +Cycle<View_ptr> const& +Workspace::views() const +{ + return m_views; } View_ptr @@ -159,7 +283,7 @@ Workspace::next_view() const std::optional<View_ptr> view = m_views.next_element(Direction::Forward); - if (view != mp_active) + if (view && *view != mp_active) return *view; return nullptr; @@ -171,7 +295,7 @@ Workspace::prev_view() const std::optional<View_ptr> view = m_views.next_element(Direction::Backward); - if (view != mp_active) + if (view && *view != mp_active) return *view; return nullptr; @@ -180,6 +304,8 @@ Workspace::prev_view() const std::optional<View_ptr> Workspace::find_view(ViewSelector const& selector) const { + TRACE(); + if (m_views.empty()) return std::nullopt; @@ -215,57 +341,127 @@ Workspace::find_view(ViewSelector const& selector) const return std::nullopt; } -void +std::pair<std::optional<View_ptr>, std::optional<View_ptr>> Workspace::cycle(Direction direction) { + TRACE(); + switch (direction) { case Direction::Forward: { if (!layout_wraps() && m_views.active_index() == m_views.last_index()) - return; - + return std::pair{std::nullopt, std::nullopt}; break; } case Direction::Backward: { if (!layout_wraps() && m_views.active_index() == 0) - return; + return std::pair{std::nullopt, std::nullopt}; + break; + } + } + std::pair<std::optional<View_ptr>, std::optional<View_ptr>> views + = m_views.cycle_active(direction); + mp_active = views.second.value_or(nullptr); + + return views; +} + +std::pair<std::optional<View_ptr>, std::optional<View_ptr>> +Workspace::cycle_focus_track(Direction direction) +{ + TRACE(); + + switch (direction) { + case Direction::Forward: + { + if (!layout_wraps() && mp_track->active_index() == mp_track->last_index()) + return std::pair{std::nullopt, std::nullopt}; + break; + } + case Direction::Backward: + { + if (!layout_wraps() && mp_track->active_index() == 0) + return std::pair{std::nullopt, std::nullopt}; break; } } - m_views.cycle_active(direction); - mp_active = m_views.active_element().value_or(nullptr); + std::pair<std::optional<View_ptr>, std::optional<View_ptr>> views + = mp_track->cycle_active(direction); + + mp_active = views.second.value_or(nullptr); + if (mp_active) + m_views.activate_element(mp_active); + + return views; } -void +std::pair<std::optional<View_ptr>, std::optional<View_ptr>> Workspace::drag(Direction direction) { + TRACE(); + switch (direction) { case Direction::Forward: { if (!layout_wraps() && m_views.active_index() == m_views.last_index()) - return; - + return std::pair{std::nullopt, std::nullopt}; break; } case Direction::Backward: { if (!layout_wraps() && m_views.active_index() == 0) - return; + return std::pair{std::nullopt, std::nullopt}; + break; + } + } + + std::pair<std::optional<View_ptr>, std::optional<View_ptr>> views + = m_views.drag_active(direction); + mp_active = views.second.value_or(nullptr); + + return views; +} +std::pair<std::optional<View_ptr>, std::optional<View_ptr>> +Workspace::drag_focus_track(Direction direction) +{ + TRACE(); + + switch (direction) { + case Direction::Forward: + { + if (!layout_wraps() && mp_track->active_index() == mp_track->last_index()) + return std::pair{std::nullopt, std::nullopt}; + break; + } + case Direction::Backward: + { + if (!layout_wraps() && mp_track->active_index() == 0) + return std::pair{std::nullopt, std::nullopt}; break; } } - m_views.drag_active(direction); - mp_active = m_views.active_element().value_or(nullptr); + std::pair<std::optional<View_ptr>, std::optional<View_ptr>> views + = mp_track->drag_active(direction); + + if (views.first && views.second) + m_views.swap_elements(*views.first, *views.second); + + mp_active = views.second.value_or(nullptr); + if (mp_active) + m_views.activate_element(mp_active); + + return views; } void Workspace::reverse() { + TRACE(); m_views.reverse(); mp_active = m_views.active_element().value_or(nullptr); } @@ -273,6 +469,7 @@ Workspace::reverse() void Workspace::rotate(Direction direction) { + TRACE(); m_views.rotate(direction); mp_active = m_views.active_element().value_or(nullptr); } @@ -280,6 +477,8 @@ Workspace::rotate(Direction direction) void Workspace::shuffle_main(Direction direction) { + TRACE(); + m_views.rotate_range( direction, 0, @@ -292,6 +491,8 @@ Workspace::shuffle_main(Direction direction) void Workspace::shuffle_stack(Direction direction) { + TRACE(); + m_views.rotate_range( direction, static_cast<Index>(m_layout_handler.main_count()), @@ -304,8 +505,11 @@ Workspace::shuffle_stack(Direction direction) void Workspace::activate_view(View_ptr view) { + TRACE(); + if (m_views.contains(view)) { m_views.activate_element(view); + m_tracks[view->scene_layer()]->activate_element(view); mp_active = view; } } @@ -313,23 +517,32 @@ Workspace::activate_view(View_ptr view) void Workspace::add_view(View_ptr view) { + TRACE(); + if (m_views.contains(view)) return; m_views.insert_at_back(view); + m_tracks[view->scene_layer()]->insert_at_back(view); + + m_track_layer = view->scene_layer(); mp_active = view; } void Workspace::remove_view(View_ptr view) { + TRACE(); + m_views.remove_element(view); - mp_active = m_views.active_element().value_or(nullptr); + remove_view_from_track(view, view->scene_layer()); } void Workspace::replace_view(View_ptr view, View_ptr replacement) { + TRACE(); + bool was_active = m_views.active_element().value_or(nullptr) == view; @@ -344,6 +557,8 @@ Workspace::replace_view(View_ptr view, View_ptr replacement) void Workspace::view_to_icon(View_ptr view) { + TRACE(); + if (m_views.remove_element(view)) m_iconified_views.insert_at_back(view); @@ -353,6 +568,8 @@ Workspace::view_to_icon(View_ptr view) void Workspace::icon_to_view(View_ptr view) { + TRACE(); + if (m_iconified_views.remove_element(view)) m_views.insert_at_back(view); @@ -362,6 +579,8 @@ Workspace::icon_to_view(View_ptr view) void Workspace::add_icon(View_ptr view) { + TRACE(); + if (m_iconified_views.contains(view)) return; @@ -371,12 +590,15 @@ Workspace::add_icon(View_ptr view) void Workspace::remove_icon(View_ptr view) { + TRACE(); m_iconified_views.remove_element(view); } std::optional<View_ptr> Workspace::pop_icon() { + TRACE(); + return m_iconified_views.empty() ? std::nullopt : std::optional(m_iconified_views[m_iconified_views.size() - 1]); @@ -385,6 +607,8 @@ Workspace::pop_icon() void Workspace::view_to_disowned(View_ptr view) { + TRACE(); + if (m_views.remove_element(view)) m_disowned_views.insert_at_back(view); @@ -394,6 +618,8 @@ Workspace::view_to_disowned(View_ptr view) void Workspace::disowned_to_view(View_ptr view) { + TRACE(); + if (m_disowned_views.remove_element(view)) m_views.insert_at_back(view); @@ -403,6 +629,8 @@ Workspace::disowned_to_view(View_ptr view) void Workspace::add_disowned(View_ptr view) { + TRACE(); + if (m_disowned_views.contains(view)) return; @@ -412,36 +640,42 @@ Workspace::add_disowned(View_ptr view) void Workspace::remove_disowned(View_ptr view) { + TRACE(); m_disowned_views.remove_element(view); } void Workspace::save_layout(int number) const { + TRACE(); m_layout_handler.save_layout(number); } void Workspace::load_layout(int number) { + TRACE(); m_layout_handler.load_layout(number); } void Workspace::toggle_layout_data() { + TRACE(); m_layout_handler.set_prev_layout_data(); } void Workspace::cycle_layout_data(Direction direction) { + TRACE(); m_layout_handler.cycle_layout_data(direction); } void Workspace::copy_data_from_prev_layout() { + TRACE(); m_layout_handler.copy_data_from_prev_layout(); } @@ -449,48 +683,56 @@ Workspace::copy_data_from_prev_layout() void Workspace::change_gap_size(Util::Change<int> change) { + TRACE(); m_layout_handler.change_gap_size(change); } void Workspace::change_main_count(Util::Change<int> change) { + TRACE(); m_layout_handler.change_main_count(change); } void Workspace::change_main_factor(Util::Change<float> change) { + TRACE(); m_layout_handler.change_main_factor(change); } void Workspace::change_margin(Util::Change<int> change) { + TRACE(); m_layout_handler.change_margin(change); } void Workspace::change_margin(Edge edge, Util::Change<int> change) { + TRACE(); m_layout_handler.change_margin(edge, change); } void Workspace::reset_gap_size() { + TRACE(); m_layout_handler.reset_gap_size(); } void Workspace::reset_margin() { + TRACE(); m_layout_handler.reset_margin(); } void Workspace::reset_layout_data() { + TRACE(); m_layout_handler.reset_layout_data(); } @@ -498,12 +740,14 @@ Workspace::reset_layout_data() void Workspace::toggle_layout() { + TRACE(); m_layout_handler.set_prev_kind(); } void Workspace::set_layout(LayoutHandler::LayoutKind layout) { + TRACE(); m_layout_handler.set_kind(layout); }