Skip to content

Commit

Permalink
Merge pull request #671 from o-sdn-o/gui-bridge
Browse files Browse the repository at this point in the history
Allow exclusive keyboard mode
  • Loading branch information
o-sdn-o authored Nov 5, 2024
2 parents c0c02b0 + a8c2d4f commit 0f2b7fa
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 46 deletions.
39 changes: 34 additions & 5 deletions src/netxs/apps.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,13 +604,42 @@ namespace netxs::app::shared
};
auto body = data();
auto items = scroll->attach(ui::list::ctor());
auto title_grid = items->attach(ui::fork::ctor(axis::Y));
auto title_data = title_grid->attach(slot::_1, ui::item::ctor("Keyboard Test")->setpad({ 2, 0, 1, 0 }));
auto chord_grid = title_grid->attach(slot::_2, ui::grid::ctor())
->setpad({ 4, 5, 0, 2})
auto title_grid_state = items->attach(ui::list::ctor(axis::Y)->setpad({ 0, 0, 0, 2}));
auto title_block = title_grid_state->attach(ui::item::ctor("Keyboard Test")->setpad({ 2, 0, 1, 0 }));
auto chord_block = title_grid_state->attach(ui::grid::ctor())
->setpad({ 4, 5, 0, 1})
->active()
->template plugin<pro::focus>()
->template plugin<pro::grade>();
auto state_block = title_grid_state->attach(ui::fork::ctor());
auto state_label = state_block->attach(slot::_1, ui::item::ctor("Exclusive keyboard mode:")->setpad({ 2, 1, 0, 0 }));
auto state_state = state_block->attach(slot::_2, ui::item::ctor(ansi::bgc(reddk).fgx(0).add("█off ")))
->setpad({ 1, 1, 0, 0 })
->active()
->shader(cell::shaders::xlight, e2::form::state::hover)
->invoke([&](auto& boss)
{
auto state_ptr = ptr::shared(faux);
auto& state = *state_ptr;
auto& window_inst = *window;
window->LISTEN(tier::release, hids::events::keybd::focus::exclusive, seed, boss.tracker, (state_ptr))
{
state = !!seed.item;
boss.set(state ? ansi::bgc(greendk).fgc(whitelt).add(" on █")
: ansi::bgc(reddk).fgx(0) .add("█off "));
boss.base::reflow();
};
window->LISTEN(tier::release, e2::form::state::keybd::focus::off, gear_id, boss.tracker) // Call gear's subscription
{
if (state) window_inst.bell::signal(tier::preview, hids::events::keybd::focus::exclusive, {}); // to reset exclusive mode.
};
boss.LISTEN(tier::release, hids::events::mouse::button::click::left, gear)
{
state ? gear.set_exclusive()
: gear.set_exclusive(window_inst.This());
gear.dismiss_dblclick();
};
});
auto field = []
{
auto f = ui::item::ctor()
Expand Down Expand Up @@ -654,7 +683,7 @@ namespace netxs::app::shared
auto released = std::to_array({ field(), field(), field(), field() });
auto pressed_label = label( "pressed:")->alignment({ snap::tail, snap::both });
auto released_label = label("released:");
chord_grid->attach_cells({ 5, 3 }, { {}, label("Generic"), label("Literal"), label("Specific"), label("Scancodes"),
chord_block->attach_cells({ 5, 3 }, { {}, label("Generic"), label("Literal"), label("Specific"), label("Scancodes"),
pressed_label, pressed[0], pressed[1], pressed[2], pressed[3],
released_label, released[0], released[1], released[2], released[3] });
released[0]->set("<Press any keys>")->hidden = faux;;
Expand Down
2 changes: 1 addition & 1 deletion src/netxs/desktopio/application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace netxs::app

namespace netxs::app::shared
{
static const auto version = "v0.9.99.37";
static const auto version = "v0.9.99.38";
static const auto repository = "https://github.com/directvt/vtm";
static const auto usr_config = "~/.config/vtm/settings.xml"s;
static const auto sys_config = "/etc/vtm/settings.xml"s;
Expand Down
3 changes: 1 addition & 2 deletions src/netxs/desktopio/canvas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1100,7 +1100,6 @@ namespace netxs
}
void set_direct(view utf8, si32 w, si32 h)
{
static constexpr auto hasher = std::hash<view>{};
auto count = utf8.size();
token &= rtl_mask; // Keep rtl bit.
if (count < limit)
Expand All @@ -1114,7 +1113,7 @@ namespace netxs
}
else
{
token |= hasher(utf8) & ~rtl_mask; // Keep rtl bit.
token |= qiew::hash{}(utf8) & ~rtl_mask; // Keep rtl bit.
set_jumbo();
mtx(w, h);
jumbos().add(token & token_mask, utf8);
Expand Down
9 changes: 8 additions & 1 deletion src/netxs/desktopio/console.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1219,9 +1219,15 @@ namespace netxs::ui
};
if (direct) // Forward unhandled events outside.
{
LISTEN(tier::release, hids::events::keybd::focus::exclusive, seed) // Request exclusive mode.
{
auto [ext_gear_id, gear_ptr] = input.get_foreign_gear_id(seed.id);
if (!gear_ptr) return;
conio.focus_set.send(canal, ext_gear_id, seed.item ? -1 : -2); // -1: set, -2: reset.
};
LISTEN(tier::release, hids::events::keybd::key::any, gear) // Return back unhandled keybd events.
{
if (gear)
if (gear && !gear.handled && ptr::is_empty(gear.exclusive_wptr)) // Do not return events in exclusive mode.
{
auto [ext_gear_id, gear_ptr] = input.get_foreign_gear_id(gear.id);
if (gear_ptr)
Expand Down Expand Up @@ -1264,6 +1270,7 @@ namespace netxs::ui
LISTEN(tier::preview, hids::events::keybd::key::any, gear, tokens)
{
//todo unify
//todo key
if (gear.keybd::cluster == props.debug_toggle)
{
debug ? debug.stop()
Expand Down
75 changes: 45 additions & 30 deletions src/netxs/desktopio/controls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1834,21 +1834,18 @@ namespace netxs::ui
{
auto& handlers = Tier == tier::release ? handlers_release : handlers_preview;
auto chords = input::key::kmap::chord_list(chord_str);
//log("Chord: ", chord_str);
if (chords.size())
{
//auto handler_ptr = ptr::shared(std::move(handler));
for (auto& chord : chords)
{
//log("\t", input::key::kmap::to_string(chord, faux));
handlers[chord].push_back(handler_ptr);
}
return true;//handler_ptr;
return true;
}
else
{
log("%%Unknown key chord: '%chord%'", prompt::user, chord_str);
return faux;//sptr{};
return faux;
}
}
template<si32 Tier = tier::release>
Expand Down Expand Up @@ -1881,18 +1878,18 @@ namespace netxs::ui
{
if (gear.payload == input::keybd::type::keypress)
{
if (gear.keystat) _dispatch<tier::release>(gear, gear.vkchord);
if (gear.keystat) _dispatch<tier::release>(gear, gear.chchord);
if (gear.keystat) _dispatch<tier::release>(gear, gear.scchord);
if (!gear.handled) _dispatch<tier::release>(gear, gear.vkchord);
if (!gear.handled) _dispatch<tier::release>(gear, gear.chchord);
if (!gear.handled) _dispatch<tier::release>(gear, gear.scchord);
}
};
boss.LISTEN(tier::preview, hids::events::keybd::key::any, gear, memo)
{
if (gear.payload == input::keybd::type::keypress)
{
if (gear.keystat) _dispatch<tier::preview>(gear, gear.vkchord);
if (gear.keystat) _dispatch<tier::preview>(gear, gear.chchord);
if (gear.keystat) _dispatch<tier::preview>(gear, gear.scchord);
if (!gear.handled) _dispatch<tier::preview>(gear, gear.vkchord);
if (!gear.handled) _dispatch<tier::preview>(gear, gear.chchord);
if (!gear.handled) _dispatch<tier::preview>(gear, gear.scchord);
}
};
}
Expand Down Expand Up @@ -2912,22 +2909,29 @@ namespace netxs::ui
rect area; // elem: Object slot.
bool done; // elem: Object resized.
};
struct cell
{
twod size; // Cell size.
si32 span; // Cell span.
};
template<bool SetInner = faux>
auto cellsz(twod coor, twod span)
{
auto x = std::accumulate(widths.begin() + coor.x, widths.begin() + coor.x + span.x, 0);
auto y = std::accumulate(heights.begin() + coor.y, heights.begin() + coor.y + span.y, 0);
auto size = coor + span;
auto x = std::accumulate(widths.begin() + coor.x, widths.begin() + size.x, 0, [](auto x, auto& w){ return x += w.size.x; });
auto y = std::accumulate(widths.begin() + coor.y, widths.begin() + size.y, 0, [](auto y, auto& w){ return y += w.size.y; });
return twod{ x, y };
}
std::vector<si32> widths; // grid: Grid column widths.
std::vector<si32> heights; // grid: Grid row heights.
std::vector<cell> widths; // grid: Grid cell metrics.
std::vector<elem> blocks; // grid: Geometry of stored objects.

protected:
// grid: .
void deform(rect& new_area) override
{
widths.clear();
heights.clear();
auto m = dot_00;
auto first_run = true;
auto recalc = [&](auto object_iter, auto tail2, auto elem_iter)
{
auto changed = faux;
Expand All @@ -2937,9 +2941,18 @@ namespace netxs::ui
auto& elem = *elem_iter++;
if (elem.span.x < 1 || elem.span.y < 1) continue;
auto dimension = elem.coor + elem.span;
if (dimension.x > (si32)widths.size()) widths.resize(dimension.x);
if (dimension.y > (si32)heights.size()) heights.resize(dimension.y);
auto area_size = cellsz(elem.coor, elem.span);
auto max_len = std::max(dimension.x, dimension.y);
if (max_len > (si32)widths.size())
{
m = std::max(m, dimension);
widths.resize(max_len);
}
auto area_size = cellsz<true>(elem.coor, elem.span);
if (first_run)
{
widths[elem.coor.x].span = std::max(elem.span.x, widths[elem.coor.x].span);
widths[elem.coor.y].span = std::max(elem.span.y, widths[elem.coor.y].span);
}
if (elem.done && elem.area.size == area_size) continue;
elem.area.size = area_size;
elem.done = true;
Expand All @@ -2948,12 +2961,13 @@ namespace netxs::ui
if (delta.x > 0)
{
changed = true;
auto tail = widths.end();
auto head = widths.begin() + elem.coor.x + elem.span.x - 1;
*head++ += delta.x;
while (delta.x && head != tail)
auto head = widths.begin() + elem.coor.x;
auto tail = head + widths[elem.coor.x].span;
auto iter = head + elem.span.x - 1;
(*iter++).size.x += delta.x;
while (delta.x && iter != tail)
{
auto& w = *head++;
auto& w = (*iter++).size.x;
auto dx = std::min(w, delta.x);
w -= dx;
delta.x -= dx;
Expand All @@ -2962,12 +2976,13 @@ namespace netxs::ui
if (delta.y > 0)
{
changed = true;
auto tail = heights.end();
auto head = heights.begin() + elem.coor.y + elem.span.y - 1;
*head++ += delta.y;
while (delta.y && head != tail)
auto head = widths.begin() + elem.coor.y;
auto tail = head + widths[elem.coor.y].span;
auto iter = head + elem.span.y - 1;
(*iter++).size.y += delta.y;
while (delta.y && iter != tail)
{
auto& h = *head++;
auto& h = (*iter++).size.y;
auto dy = std::min(h, delta.y);
h -= dy;
delta.y -= dy;
Expand All @@ -2990,7 +3005,7 @@ namespace netxs::ui
while (recalc(subset.rbegin(), subset.rend(), blocks.rbegin()))
{ }
recoor(subset.begin(), subset.end(), blocks.begin());
new_area.size = std::max(new_area.size, cellsz(dot_00, { widths.size(), heights.size() }));
new_area.size = std::max(new_area.size, cellsz(dot_00, m));
}
// grid: .
void inform(rect /*new_area*/) override
Expand Down
1 change: 1 addition & 0 deletions src/netxs/desktopio/gui.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2945,6 +2945,7 @@ namespace netxs::gui
if (keybd_read_input(keystat, virtcod)) return;
if (keystat)
{
//todo key
if (keybd_test_pressed(vkey::capslock) && (keybd_test_pressed(vkey::up) || keybd_test_pressed(vkey::down))) // Change cell height by CapsLock+Up/DownArrow.
{
auto dir = keybd_test_pressed(vkey::up) ? 1.f : -1.f;
Expand Down
38 changes: 38 additions & 0 deletions src/netxs/desktopio/input.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ namespace netxs::events::userland
EVENT_XS( cut, input::foci ), // Cut mono focus branch.
EVENT_XS( dry, input::foci ), // Remove the reference to the specified applet.
EVENT_XS( hop, input::foci ), // Change next hop destination. args: pair<what, with>.
EVENT_XS( exclusive, input::foci ), // preview/release: Request exclusive keyboard mode.
GROUP_XS( bus, input::foci ),

SUBSET_XS( bus )
Expand Down Expand Up @@ -1239,6 +1240,9 @@ namespace netxs::input
si32 virtcod{};
si32 scancod{};
si32 keycode{};
ui64 vk_hash{};
ui64 sc_hash{};
ui64 ch_hash{};
text vkchord{};
text scchord{};
text chchord{};
Expand Down Expand Up @@ -1562,6 +1566,9 @@ namespace netxs::input
id_t user_index; // hids: User/Device image/icon index.
kmap other_key; // hids: Dynamic key-vt mapping.

wptr exclusive_wptr; // hids: Exclusive keyboard owner.
hook exclusive_memo; // hids: Exclusive keyboard owner reset.

template<class T>
hids(auth& indexer, T& props, base& owner, core const& idmap)
: bell{ indexer },
Expand Down Expand Up @@ -1594,6 +1601,27 @@ namespace netxs::input
return alive;
}

void set_exclusive(sptr kb_owner = {})
{
//todo make it crossprocess
auto prev = exclusive_wptr.lock();
if (kb_owner != prev)
{
exclusive_wptr = kb_owner;
exclusive_memo.reset();
if (prev) prev->bell::signal(tier::release, hids::events::keybd::focus::exclusive, {}); // Trigger to reset exclusive mode.
if (kb_owner)
{
kb_owner->bell::signal(tier::release, hids::events::keybd::focus::exclusive, { .id = bell::id, .item = kb_owner }); // Notify requestee.
kb_owner->LISTEN(tier::preview, hids::events::keybd::focus::exclusive, seed, exclusive_memo) // Reset exclusive mode on requestee focus change (requestee calls signal(tier::preview, hids::events::keybd::focus::exclusive)).
{
set_exclusive();
};
}
owner.bell::signal(tier::release, hids::events::keybd::focus::exclusive, { .id = bell::id, .item = kb_owner }); // Forward outside (crossprocess).
}
}

auto tooltip_enabled(time const& now)
{
return !mouse::m_sys.buttons
Expand Down Expand Up @@ -1720,6 +1748,10 @@ namespace netxs::input
alive = faux;
if (set_nodbl) nodbl = true;
}
void dismiss_dblclick()
{
nodbl = true;
}
void set_handled(bool b = true)
{
handled = b;
Expand Down Expand Up @@ -1923,6 +1955,12 @@ namespace netxs::input
void fire_keybd()
{
alive = true;
if (!ptr::is_empty(exclusive_wptr))
if (auto target = exclusive_wptr.lock())
{
target->bell::signal(tier::preview, hids::events::keybd::key::post, *this);
return;
}
owner.bell::signal(tier::preview, hids::events::keybd::key::post, *this);
}
void fire_focus()
Expand Down
5 changes: 5 additions & 0 deletions src/netxs/desktopio/ptr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ namespace netxs
// Due to the fact that alias templates are never deduced by template argument deduction (C++20).
namespace ptr
{
template<typename T>
bool is_empty(wptr<T> const& w)
{
return !w.owner_before(wptr<T>{}) && !wptr<T>{}.owner_before(w);
}
template<class T>
auto test(T a, T b)
{
Expand Down
9 changes: 8 additions & 1 deletion src/netxs/desktopio/terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8079,7 +8079,14 @@ namespace netxs::ui
if (auto gear_ptr = owner.bell::getref<hids>(k.gear_id))
if (auto parent_ptr = owner.base::parent())
{
auto seed = parent_ptr->base::riseup(tier::preview, hids::events::keybd::focus::set, { .id = k.gear_id, .solo = k.solo, .item = owner.This() });
if (k.solo < 0) // Exclusive keyboard mode: -1: set, -2: reset.
{
gear_ptr->set_exclusive(k.solo == -1 ? owner.This() : sptr{}); // Exclusive mode will be reset automatically when focus is changed.
}
else
{
auto seed = parent_ptr->base::riseup(tier::preview, hids::events::keybd::focus::set, { .id = k.gear_id, .solo = k.solo, .item = owner.This() });
}
}
}
}
Expand Down
11 changes: 6 additions & 5 deletions src/netxs/desktopio/utf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -596,14 +596,15 @@ namespace netxs::utf
struct qiew : public view
{
using view::view;
using equal = std::equal_to<>;

struct hash
{
auto operator()(qiew key) const { return std::hash<view>{}(key); }
};
struct equal
{
auto operator()(qiew lhs, qiew rhs) const { return lhs.compare(rhs) == 0; }
using is_transparent = void;
using hash_type = std::hash<view>;
auto operator()(text const& s) const { return hash_type{}(s); }
auto operator()(char const* s) const { return hash_type{}(s); }
auto operator()(view s) const { return hash_type{}(s); }
};

constexpr qiew(qiew const&) = default;
Expand Down
Loading

0 comments on commit 0f2b7fa

Please sign in to comment.