From d2bd87936325fafe6065d1028cea78cb2ddcd639 Mon Sep 17 00:00:00 2001 From: Dmitry Sapozhnikov <11535558+o-sdn-o@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:33:11 +0500 Subject: [PATCH] #86 WIP: Implement TileFocusPrevGrip/TileFocusNextGrip --- doc/apps.md | 16 ++- doc/settings.md | 16 ++- doc/user-interface.md | 36 +++--- src/netxs/apps/tile.hpp | 235 +++++++++++++++++++++++++++++++++++----- src/vtm.xml | 16 ++- 5 files changed, 260 insertions(+), 59 deletions(-) diff --git a/doc/apps.md b/doc/apps.md index 4d3c6574f6..c2ef6e9246 100644 --- a/doc/apps.md +++ b/doc/apps.md @@ -455,8 +455,10 @@ Tiling Window Manager is a window container that organizes the workspace into mu - - + + + + @@ -468,8 +470,10 @@ Tiling Window Manager is a window container that organizes the workspace into mu - - + + + + @@ -485,7 +489,9 @@ Tiling Window Manager is a window container that organizes the workspace into mu - + + + diff --git a/doc/settings.md b/doc/settings.md index 198c560e0d..dfa75de79c 100644 --- a/doc/settings.md +++ b/doc/settings.md @@ -857,8 +857,10 @@ Notes - - + + + + @@ -903,8 +905,10 @@ Notes - - + + + + @@ -920,7 +924,9 @@ Notes - + + + diff --git a/doc/user-interface.md b/doc/user-interface.md index befac40b1b..5a66bca9b9 100644 --- a/doc/user-interface.md +++ b/doc/user-interface.md @@ -249,23 +249,25 @@ Hotkey ¹ Default action - Ctrl+PageUp Focus the previous pane or splitting grip. - Ctrl+PageDown Focus the next pane or splitting grip. - Alt+Shift+N Launch application instances in active empty slots. The app to run can be set by RightClick on the taskbar. - Alt+Shift+A Select all panes. - Alt+Shift+'|' Split active panes horizontally. - Alt+Shift+Minus Split active panes vertically. - Alt+Shift+R Change split orientation. - Alt+Shift+S Swap two or more panes. - Alt+Shift+E Equalize split ratio. - Alt+Shift+F2 Set tiling window manager title using clipboard data. - Alt+Shift+W Close active application. - LeftArrow Move the split grip to the left. - RightArrow Move the split grip to the right. - UpArrow Move the split grip up. - DownArrow Move the split grip down. - '-' Decrease the split grip width. - Shift+'+' Increase the split grip width. + Ctrl+PageUp Focus the previous pane or the split grip. + Ctrl+PageDown Focus the next pane or the split grip. + Alt+Shift+N Launch application instances in active empty slots. The app to run can be set by RightClick on the taskbar. + Alt+Shift+A Select all panes. + Alt+Shift+'|' Split active panes horizontally. + Alt+Shift+Minus Split active panes vertically. + Alt+Shift+R Change split orientation. + Alt+Shift+S Swap two or more panes. + Alt+Shift+E Equalize split ratio. + Alt+Shift+F2 Set tiling window manager title using clipboard data. + Alt+Shift+W Close active application. + LeftArrow Move the split grip to the left. + RightArrow Move the split grip to the right. + UpArrow Move the split grip up. + DownArrow Move the split grip down. + '-' Decrease the split grip width. + Shift+'+'
NumpadPlusIncrease the split grip width. + Shift+Tab Focus the previous split grip. + Tab Focus the next split grip. diff --git a/src/netxs/apps/tile.hpp b/src/netxs/apps/tile.hpp index 63a99b1b64..429e58f4f5 100644 --- a/src/netxs/apps/tile.hpp +++ b/src/netxs/apps/tile.hpp @@ -28,8 +28,12 @@ namespace netxs::events::userland SUBSET_XS( focus ) { - EVENT_XS( next, input::hids ), - EVENT_XS( prev, input::hids ), + EVENT_XS( next , input::hids ), + EVENT_XS( prev , input::hids ), + EVENT_XS( nextpane, input::hids ), + EVENT_XS( prevpane, input::hids ), + EVENT_XS( nextgrip, input::hids ), + EVENT_XS( prevgrip, input::hids ), }; SUBSET_XS( split ) { @@ -58,8 +62,12 @@ namespace netxs::app::tile using ui::wptr; #define proc_list \ + X(TileFocusPrev ) \ + X(TileFocusNext ) \ X(TileFocusPrevPane ) \ X(TileFocusNextPane ) \ + X(TileFocusPrevGrip ) \ + X(TileFocusNextGrip ) \ X(TileRunApplicatoin ) \ X(TileSelectAllPanes ) \ X(TileSplitHorizontally ) \ @@ -70,7 +78,7 @@ namespace netxs::app::tile X(TileSetManagerTitle ) \ X(TileClosePane ) \ X(TileMoveGrip ) \ - X(TileResizeGrip ) + X(TileResizeGrip ) \ struct action { @@ -344,8 +352,10 @@ namespace netxs::app::tile gear.dismiss(); }; auto& keybd = boss.template plugins(); - keybd.proc(action::TileMoveGrip , [&](hids& gear, txts& args){ gear.set_handled(); boss.base::riseup(tier::preview, app::tile::events::ui::grips::move, { args.size() ? xml::take_or(args.front(), dot_00) : dot_00 }); }); - keybd.proc(action::TileResizeGrip, [&](hids& gear, txts& args){ gear.set_handled(); boss.base::riseup(tier::preview, app::tile::events::ui::grips::resize, { args.size() ? xml::take_or(args.front(), 0) : 0 }); }); + keybd.proc(action::TileMoveGrip , [&](hids& gear, txts& args){ gear.set_handled(); boss.base::riseup(tier::preview, app::tile::events::ui::grips::move, { args.size() ? xml::take_or(args.front(), dot_00) : dot_00 }); }); + keybd.proc(action::TileResizeGrip , [&](hids& gear, txts& args){ gear.set_handled(); boss.base::riseup(tier::preview, app::tile::events::ui::grips::resize, { args.size() ? xml::take_or(args.front(), 0) : 0 }); }); + keybd.proc(action::TileFocusPrevGrip, [&](hids& gear, txts& /*args*/){ boss.base::riseup(tier::preview, app::tile::events::ui::focus::prevgrip, gear); }); + keybd.proc(action::TileFocusNextGrip, [&](hids& gear, txts& /*args*/){ boss.base::riseup(tier::preview, app::tile::events::ui::focus::nextgrip, gear); }); keybd.bind(*grip_bindings_ptr); }); return node; @@ -892,17 +902,21 @@ namespace netxs::app::tile boss.base::riseup(tier::release, e2::form::proceed::quit::one, true); }; auto& keybd = boss.template plugins(); - keybd.proc(action::TileFocusPrevPane , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::focus::prev, gear); }); - keybd.proc(action::TileFocusNextPane , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::focus::next, gear); }); - keybd.proc(action::TileRunApplicatoin , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::create, gear); }); - keybd.proc(action::TileSelectAllPanes , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::select, gear); }); - keybd.proc(action::TileSplitHorizontally , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::split::hz, gear); }); - keybd.proc(action::TileSplitVertically , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::split::vt, gear); }); - keybd.proc(action::TileSplitOrientation , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::rotate, gear); }); - keybd.proc(action::TileSwapPanes , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::swap, gear); }); - keybd.proc(action::TileEqualizeSplitRatio, [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::equalize, gear); }); - keybd.proc(action::TileSetManagerTitle , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::title , gear); }); - keybd.proc(action::TileClosePane , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::close, gear); }); + keybd.proc(action::TileFocusPrev , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::focus::prev, gear); }); + keybd.proc(action::TileFocusNext , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::focus::next, gear); }); + keybd.proc(action::TileFocusPrevPane , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::focus::prevpane, gear); }); + keybd.proc(action::TileFocusNextPane , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::focus::nextpane, gear); }); + keybd.proc(action::TileFocusPrevGrip , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::focus::prevgrip, gear); }); + keybd.proc(action::TileFocusNextGrip , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::focus::nextgrip, gear); }); + keybd.proc(action::TileRunApplicatoin , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::create, gear); }); + keybd.proc(action::TileSelectAllPanes , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::select, gear); }); + keybd.proc(action::TileSplitHorizontally , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::split::hz, gear); }); + keybd.proc(action::TileSplitVertically , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::split::vt, gear); }); + keybd.proc(action::TileSplitOrientation , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::rotate, gear); }); + keybd.proc(action::TileSwapPanes , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::swap, gear); }); + keybd.proc(action::TileEqualizeSplitRatio, [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::equalize, gear); }); + keybd.proc(action::TileSetManagerTitle , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::title , gear); }); + keybd.proc(action::TileClosePane , [&](hids& gear, txts& /*args*/){ boss.bell::signal(tier::preview, app::tile::events::ui::close, gear); }); auto bindings = pro::keybd::load(config, "tile"); keybd.bind(bindings); @@ -920,6 +934,7 @@ namespace netxs::app::tile { switch_counter[seed.gear_id] = {}; }; + //todo generalize refocusing boss.LISTEN(tier::preview, app::tile::events::ui::focus::prev, gear, -, (switch_counter_ptr)) { auto root_veer_ptr = boss.base::subset[1]; @@ -1004,6 +1019,168 @@ namespace netxs::app::tile } } }; + boss.LISTEN(tier::preview, app::tile::events::ui::focus::prevpane, gear, -, (switch_counter_ptr)) + { + auto root_veer_ptr = boss.base::subset[1]; + auto nothing_to_iterate = std::dynamic_pointer_cast(root_veer_ptr)->back()->root(); + if (nothing_to_iterate) return; + auto prev_item_ptr = sptr{}; + auto next_item_ptr = sptr{}; + foreach(root_veer_ptr, id_t{}, [&](auto& item_ptr, si32 item_type, auto) + { + if (item_type != item_type::grip) + { + if (is_focused(item_ptr, gear.id)) + { + prev_item_ptr = next_item_ptr; + } + next_item_ptr = item_ptr; + } + }); + auto skip = !prev_item_ptr && gear.shared_event && switch_counter[gear.id] == feed::rev; + if (skip) // Give another process a chance to handle this event. + { + switch_counter[gear.id] = {}; + } + else + { + if (!prev_item_ptr) // Focused item is at the boundary. + { + prev_item_ptr = next_item_ptr; + } + if (prev_item_ptr) + { + auto& prev_item = *prev_item_ptr; + boss.bell::enqueue(prev_item_ptr, [&, gear_id = gear.id](auto& /*boss*/) // Keep the focus tree intact while processing key events. + { + pro::focus::set(prev_item.This(), gear_id, solo::on); + }); + gear.set_handled(); + switch_counter[gear.id] = feed::rev; + } + } + }; + boss.LISTEN(tier::preview, app::tile::events::ui::focus::nextpane, gear) + { + auto root_veer_ptr = boss.base::subset[1]; + auto nothing_to_iterate = std::dynamic_pointer_cast(root_veer_ptr)->back()->root(); + if (nothing_to_iterate) return; + auto prev_item_ptr = sptr{}; + auto next_item_ptr = sptr{}; + auto temp_item_ptr = sptr{}; + foreach(root_veer_ptr, id_t{}, [&](auto& item_ptr, si32 item_type, auto) + { + if (item_type != item_type::grip) + { + if (!temp_item_ptr) + { + temp_item_ptr = item_ptr; // Fallback item. + } + if (prev_item_ptr) + { + std::swap(next_item_ptr, item_ptr); // Interrupt foreach (empty item_ptr). + } + else if (is_focused(item_ptr, gear.id)) + { + prev_item_ptr = item_ptr; + } + } + }); + auto skip = !next_item_ptr && gear.shared_event && switch_counter[gear.id] == feed::fwd; + if (skip) // Give another process a chance to handle this event. + { + switch_counter[gear.id] = {}; + } + else + { + if (!next_item_ptr) // Focused item is at the boundary. + { + next_item_ptr = temp_item_ptr; + } + if (next_item_ptr) + { + auto& next_item = *next_item_ptr; + boss.bell::enqueue(next_item_ptr, [&, gear_id = gear.id](auto& /*boss*/) // Keep the focus tree intact while processing key events. + { + pro::focus::set(next_item.This(), gear_id, solo::on); + }); + gear.set_handled(); + switch_counter[gear.id] = feed::fwd; + } + } + }; + boss.LISTEN(tier::preview, app::tile::events::ui::focus::prevgrip, gear) + { + auto root_veer_ptr = boss.base::subset[1]; + auto nothing_to_iterate = std::dynamic_pointer_cast(root_veer_ptr)->back()->root(); + if (nothing_to_iterate) return; + auto prev_grip_ptr = sptr{}; + auto next_grip_ptr = sptr{}; + foreach(root_veer_ptr, id_t{}, [&](auto& item_ptr, si32 item_type, auto) + { + if (item_type == item_type::grip) + { + if (is_focused(item_ptr, gear.id)) + { + prev_grip_ptr = next_grip_ptr; + } + next_grip_ptr = item_ptr; + } + }); + if (!prev_grip_ptr) // Focused grip is at the boundary. + { + prev_grip_ptr = next_grip_ptr; + } + if (prev_grip_ptr) + { + auto& prev_grip = *prev_grip_ptr; + boss.bell::enqueue(prev_grip_ptr, [&, gear_id = gear.id](auto& /*boss*/) // Keep the focus tree intact while processing key events. + { + pro::focus::set(prev_grip.This(), gear_id, solo::on); + }); + gear.set_handled(); + } + }; + boss.LISTEN(tier::preview, app::tile::events::ui::focus::nextgrip, gear) + { + auto root_veer_ptr = boss.base::subset[1]; + auto nothing_to_iterate = std::dynamic_pointer_cast(root_veer_ptr)->back()->root(); + if (nothing_to_iterate) return; + auto prev_grip_ptr = sptr{}; + auto next_grip_ptr = sptr{}; + auto temp_grip_ptr = sptr{}; + foreach(root_veer_ptr, id_t{}, [&](auto& item_ptr, si32 item_type, auto) + { + if (item_type == item_type::grip) + { + if (!temp_grip_ptr) + { + temp_grip_ptr = item_ptr; // Fallback item. + } + if (prev_grip_ptr) + { + std::swap(next_grip_ptr, item_ptr); // Interrupt foreach (empty item_ptr). + } + else if (is_focused(item_ptr, gear.id)) + { + prev_grip_ptr = item_ptr; + } + } + }); + if (!next_grip_ptr) // Focused item is at the boundary. + { + next_grip_ptr = temp_grip_ptr; + } + if (next_grip_ptr) + { + auto& next_grip = *next_grip_ptr; + boss.bell::enqueue(next_grip_ptr, [&, gear_id = gear.id](auto& /*boss*/) // Keep the focus tree intact while processing key events. + { + pro::focus::set(next_grip.This(), gear_id, solo::on); + }); + gear.set_handled(); + } + }; boss.LISTEN(tier::preview, app::tile::events::ui::swap, gear) { auto root_veer_ptr = boss.base::subset[1]; @@ -1176,17 +1353,21 @@ namespace netxs::app::tile using namespace app::shared; static const auto proc_map = menu::action_map_t { - { tile::action::TileFocusPrevPane , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::focus::prev); }}, - { tile::action::TileFocusNextPane , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::focus::next); }}, - { tile::action::TileRunApplicatoin , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::create ); }}, - { tile::action::TileSelectAllPanes , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::select ); }}, - { tile::action::TileSplitHorizontally , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::split::hz ); }}, - { tile::action::TileSplitVertically , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::split::vt ); }}, - { tile::action::TileSplitOrientation , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::rotate ); }}, - { tile::action::TileSwapPanes , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::swap ); }}, - { tile::action::TileEqualizeSplitRatio, [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::equalize ); }}, - { tile::action::TileSetManagerTitle , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::title ); }}, - { tile::action::TileClosePane , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::close ); }}, + { tile::action::TileFocusPrev , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::focus::prev ); }}, + { tile::action::TileFocusNext , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::focus::next ); }}, + { tile::action::TileFocusPrevPane , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::focus::prevpane); }}, + { tile::action::TileFocusNextPane , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::focus::nextpane); }}, + { tile::action::TileFocusPrevGrip , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::focus::prevgrip); }}, + { tile::action::TileFocusNextGrip , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::focus::nextgrip); }}, + { tile::action::TileRunApplicatoin , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::create ); }}, + { tile::action::TileSelectAllPanes , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::select ); }}, + { tile::action::TileSplitHorizontally , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::split::hz ); }}, + { tile::action::TileSplitVertically , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::split::vt ); }}, + { tile::action::TileSplitOrientation , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::rotate ); }}, + { tile::action::TileSwapPanes , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::swap ); }}, + { tile::action::TileEqualizeSplitRatio, [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::equalize ); }}, + { tile::action::TileSetManagerTitle , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::title ); }}, + { tile::action::TileClosePane , [](auto& boss, auto& /*item*/){ on_left_click(boss, app::tile::events::ui::close ); }}, }; config.cd("/config/tile", "/config/defapp"); auto grip_bindings_ptr = ptr::shared(pro::keybd::load(config, "tile/grips")); diff --git a/src/vtm.xml b/src/vtm.xml index 5afb1136d3..6f77f1cb22 100644 --- a/src/vtm.xml +++ b/src/vtm.xml @@ -408,8 +408,10 @@ R"==( - - + + + + @@ -454,8 +456,10 @@ R"==( - - + + + + @@ -471,7 +475,9 @@ R"==( - + + +