diff --git a/src/netxs/desktopio/ansivt.hpp b/src/netxs/desktopio/ansivt.hpp index f01943d17..53d05327c 100644 --- a/src/netxs/desktopio/ansivt.hpp +++ b/src/netxs/desktopio/ansivt.hpp @@ -1419,16 +1419,19 @@ namespace netxs::ansi { auto s = [&](auto const& traits, qiew utf8) { + client->defer = faux; intro.execute(traits.control, utf8, client); // Make one iteration using firstcmd and return. return utf8; }; auto y = [&](auto const& cluster) { client->post(cluster); + client->defer = true; }; auto a = [&](view plain) { client->ascii(plain); + client->defer = true; }; utf::decode(s, y, a, utf8, client->decsg); client->flush(); @@ -1694,6 +1697,7 @@ namespace netxs::ansi deco state{}; // parser: Parser style last state. mark brush{}; // parser: Parser brush. si32 decsg{}; // parser: DEC Special Graphics Mode. + bool defer{}; // parser: The last character was a cluster that could continue to grow. private: core::body proto_cells{}; // parser: Proto lyric. @@ -1741,6 +1745,7 @@ namespace netxs::ansi } proto_count = 0; proto_cells.clear(); + defer = faux; } auto empty() const { diff --git a/src/netxs/desktopio/application.hpp b/src/netxs/desktopio/application.hpp index c1eeab8b4..33540432f 100644 --- a/src/netxs/desktopio/application.hpp +++ b/src/netxs/desktopio/application.hpp @@ -24,7 +24,7 @@ namespace netxs::app namespace netxs::app::shared { - static const auto version = "v0.9.99.47"; + static const auto version = "v0.9.99.48"; 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; @@ -163,7 +163,6 @@ namespace netxs::app::shared keybd.bind("F12" , "ToggleFullscreen", mode); }; binding(0); - binding(1); }; using builder_t = std::function; @@ -663,7 +662,7 @@ namespace netxs::app::shared twod gridsize{}; si32 cellsize{}; std::list fontlist; - std::list> hotkeys; + std::list, si32>> hotkeys; }; auto get_gui_config(xmls& config) @@ -686,13 +685,16 @@ namespace netxs::app::shared for (auto keybind_ptr : keybinds) { auto& keybind = *keybind_ptr; - if (!keybind.fake) + auto chord = keybind.take_value(); + auto scheme = keybind.take("scheme", 0); //todo use text instead of si32 as a scheme identifier + auto action = keybind.take("action", ""s); + auto action_list = std::vector{}; + auto action_ptr_list = keybind.list("action"); + for (auto action_ptr : action_ptr_list) { - auto chord = keybind.take_value(); - auto action = keybind.take("action", ""s); - auto scheme = keybind.take("scheme", 0); - gui_config.hotkeys.push_back({ chord, action, scheme }); + action_list.push_back(action_ptr->take_value()); } + gui_config.hotkeys.push_back({ chord, action_list, scheme }); } return gui_config; } diff --git a/src/netxs/desktopio/controls.hpp b/src/netxs/desktopio/controls.hpp index fe0bae87c..19b797fe8 100644 --- a/src/netxs/desktopio/controls.hpp +++ b/src/netxs/desktopio/controls.hpp @@ -1950,35 +1950,25 @@ namespace netxs::ui } } template - auto _set(qiew chord_str, sptr handler_ptr, si32 scheme) + auto _set(auto& chords, sptr handler_ptr, si32 scheme) { //todo unify auto hscheme = scheme ? 1 : 0; auto& handlers = Tier == tier::release ? handlers_release[hscheme] : handlers_preview[hscheme]; - if (auto chords = _get_chords(chord_str)) + for (auto& chord : chords) { - for (auto& chord : chords.value()) - { - handlers[chord].push_back(handler_ptr); - } - return true; + handlers[chord].push_back(handler_ptr); } - else return faux; } template - auto _reset(qiew chord_str, si32 scheme) + auto _reset(auto& chords, si32 scheme) { auto hscheme = scheme ? 1 : 0; auto& handlers = Tier == tier::release ? handlers_release[hscheme] : handlers_preview[hscheme]; - if (auto chords = _get_chords(chord_str)) + for (auto& chord : chords) { - for (auto& chord : chords.value()) - { - handlers[chord].clear(); - } - return true; + handlers[chord].clear(); } - else return faux; } template void _dispatch(hids& gear, qiew chord) @@ -2054,20 +2044,38 @@ namespace netxs::ui api_map[name] = ptr::shared(std::move(proc)); } template - auto bind(qiew chord_str, qiew proc_name, si32 scheme = 0) + auto bind(qiew chord_str, auto&& proc_names, si32 scheme = 0) { if (!chord_str) return; - if (proc_name) + if (auto chord_list = _get_chords(chord_str)) { - if (auto iter = api_map.find(proc_name); iter != api_map.end()) + auto& chords = chord_list.value(); + auto set = [&](qiew proc_name) { - _set(chord_str, iter->second, scheme); + if (proc_name) + { + if (auto iter = api_map.find(proc_name); iter != api_map.end()) + { + _set(chords, iter->second, scheme); + } + else log("%%Action '%proc%' not found", prompt::user, proc_name); + } + else + { + _reset(chords, scheme); + } + }; + if constexpr (std::is_same_v>) // The case it is a string. + { + set(proc_names); + } + else + { + for (auto& proc_name : proc_names) + { + set(proc_name); + } } - else log("%%Action '%proc%' not found", prompt::user, proc_name); - } - else - { - _reset(chord_str, scheme); } } template @@ -2077,13 +2085,15 @@ namespace netxs::ui for (auto keybind_ptr : keybinds) { auto& keybind = *keybind_ptr; - if (!keybind.fake) + auto chord = keybind.take_value(); + auto scheme = keybind.take("scheme", 0); //todo use text instead of si32 as a scheme identifier + auto action_list = std::vector{}; + auto action_ptr_list = keybind.list("action"); + for (auto action_ptr : action_ptr_list) { - auto chord = keybind.take_value(); - auto action = keybind.take("action", ""s); - auto scheme = keybind.take("scheme", 0); - bind(chord, action, scheme); + action_list.push_back(action_ptr->take_value()); } + bind(chord, action_list, scheme); } } }; diff --git a/src/netxs/desktopio/gui.hpp b/src/netxs/desktopio/gui.hpp index 8ae037365..d394a1158 100644 --- a/src/netxs/desktopio/gui.hpp +++ b/src/netxs/desktopio/gui.hpp @@ -1970,7 +1970,7 @@ namespace netxs::gui kmap chords; // winbase: Pressed key table (key chord). si32 hotkey; // winbase: Alternate hotkey scheme. - winbase(auth& indexer, std::list& font_names, si32 cell_height, bool antialiasing, span blink_rate, twod grip_cell, std::list>& hotkeys) + winbase(auth& indexer, std::list& font_names, si32 cell_height, bool antialiasing, span blink_rate, twod grip_cell, std::list, si32>>& hotkeys) : base{ indexer }, titles{ *this, "", "", faux }, wfocus{ *this, ui::pro::focus::mode::relay }, @@ -2012,7 +2012,7 @@ namespace netxs::gui wkeybd.proc("_ResetWheelAccumulator", [&](hids& /*gear*/){ whlacc = {}; }); wkeybd.bind("-Ctrl", "_ResetWheelAccumulator", 0); wkeybd.bind("-Ctrl", "_ResetWheelAccumulator", 1); - for (auto& [chord, action, scheme] : hotkeys) wkeybd.bind(chord, action, scheme); + for (auto& [chord, action_list, scheme] : hotkeys) wkeybd.bind(chord, action_list, scheme); } virtual bool layer_create(layer& s, winbase* host_ptr = nullptr, twod win_coord = {}, twod grid_size = {}, dent border_dent = {}, twod cell_size = {}) = 0; diff --git a/src/netxs/desktopio/richtext.hpp b/src/netxs/desktopio/richtext.hpp index fee2a6427..f124868fb 100644 --- a/src/netxs/desktopio/richtext.hpp +++ b/src/netxs/desktopio/richtext.hpp @@ -1403,7 +1403,50 @@ namespace netxs::ui len.y * len.x); while (dst != end) *dst++ = blank; } - + //todo make it 2D + // rich: Pop glyph matrix. + auto pop_cluster() + { + auto cluster = netxs::text{}; + auto size = (si32)core::canvas.size(); + if (size) + { + auto& back = canvas.back(); + auto [w, h, x, y] = back.whxy(); + if constexpr (debugmode) log("\tw=%%, h=%%, x=%%, y=%%", w, h, x, y); + if (w && x == w && size >= w) + { + auto current_x = w - 1; + auto head = canvas.rbegin() + 1; + auto tail = head + current_x; + while (head != tail) + { + auto& c = *head; + if (!c.same_txt(back) || !c.like(back)) + { + break; + } + auto [cw, ch, cx, cy] = c.whxy(); + if constexpr (debugmode) log("\t\tcurrent_x=%%, cw=%%, ch=%%, cx=%%, cy=%%", current_x, cw, ch, cx, cy); + if (cw != w || ch != h || cy != y || cx != current_x) + { + break; + } + head++; + current_x--; + } + if (head == tail) + { + cluster = back.txt(); + if (cluster.size()) + { + core::crop(size - w); + } + } + } + } + return cluster; + } //todo unify auto& at(si32 p) const { @@ -1439,7 +1482,27 @@ namespace netxs::ui para(id_t id, auto utf8) { brush.link(id); ansi::parse(utf8, this); } para(auto utf8) { ansi::parse(utf8, this); } auto& operator = (auto utf8) { wipe(brush); ansi::parse(utf8, this); return *this; } - auto& operator += (auto utf8) { ansi::parse(utf8, this); return *this; } + auto& operator += (auto utf8) + { + if (parser::defer && caret && caret == length()) + { + if constexpr (debugmode) log("try to reassemble cluster=", lyric->back().txt()); + auto last_cluster = lyric->pop_cluster(); + if (caret != length()) + { + caret = length(); + auto reassembled_cluster = text{}; + reassembled_cluster.reserve(last_cluster.length() + utf8.length()); + reassembled_cluster += last_cluster; + reassembled_cluster += utf8; + ansi::parse(reassembled_cluster, this); + if constexpr (debugmode) log("\treassembled_cluster=", utf::buffer_to_hex(reassembled_cluster, true)); + return *this; + } + } + ansi::parse(utf8, this); + return *this; + } operator writ const& () const { return locus; } @@ -1452,7 +1515,7 @@ namespace netxs::ui return shadow().substr(start, width); } bool bare() const { return locus.bare(); } // para: Does the paragraph have no locator. - auto length() const { return lyric->size().x; } // para: Return printable length. + si32 length() const { return lyric->size().x; } // para: Return printable length. //todo Apple clang doesn't get auto return. auto empty() const { return !length(); } // para: Return true if empty. auto size() const { return lyric->size(); } // para: Return 2D volume size. auto& back() const { return brush; } // para: Return current brush. @@ -1460,7 +1523,7 @@ namespace netxs::ui void ease() { lyric->each([&](auto& c){ c.clr({}); }); } // para: Reset color for all text. void link(id_t id) { lyric->each([&](auto& c){ c.link(id); }); } // para: Set object ID for each cell. template - void wipe(cell c = cell{}) // para: Clear the text and locus, and reset SGR attributes. + void wipe(cell c = cell{}) // para: Clear the text and locus, and reset SGR attributes. { parser::reset(c); caret = 0; diff --git a/src/vtm.xml b/src/vtm.xml index 105145871..afb6b6389 100644 --- a/src/vtm.xml +++ b/src/vtm.xml @@ -389,16 +389,26 @@ R"==( - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + @@ -428,10 +438,14 @@ R"==( - - - - + + + + + + + +