Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow exclusive keyboard mode #671

Merged
merged 8 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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