Mouse support. Fix & verify Webassembly support.

There was some undefined behavior to be fixed in the terminal input
parser.

The behavior of flush seems to have change. The fix was to invert '\0'
and std::flush.
This commit is contained in:
ArthurSonzogni 2021-04-25 16:58:16 +02:00
parent 0b9b6c692a
commit a27c878a3f
No known key found for this signature in database
GPG Key ID: 41D98248C074CD6C
6 changed files with 55 additions and 19 deletions

View File

@ -10,7 +10,7 @@
<div class="page"> <div class="page">
<h1>FTXUI WebAssembly Example </h1> <h1>FTXUI WebAssembly Example </h1>
<p> <p>
<a href="https://github.com/ArthurSonzogni/FTXUI">FTXUI</a> is a single <a href="https://github.com/ArthurSonzogni/FTXUI">FTXUI</a> is a simple
C++ library for terminal user interface. C++ library for terminal user interface.
</p> </p>
<p> <p>
@ -69,10 +69,8 @@
]; ];
const url_search_params = new URLSearchParams(window.location.search); const url_search_params = new URLSearchParams(window.location.search);
const example_index = url_search_params.get("id") || 16; const example = url_search_params.get("file") || "./dom/color_gallery.js"
const example = example_list[example_index]; const select = document.getElementById("selectExample");
var select = document.getElementById("selectExample");
for(var i = 0; i < example_list.length; i++) { for(var i = 0; i < example_list.length; i++) {
var opt = example_list[i]; var opt = example_list[i];
@ -81,9 +79,10 @@
el.value = opt; el.value = opt;
select.appendChild(el); select.appendChild(el);
} }
select.selectedIndex = example_index; select.selectedIndex = example_list.findIndex(path => path == example) || 0;
select.addEventListener("change", () => { select.addEventListener("change", () => {
location.href = (location.href).split('?')[0] + "?id=" + select.selectedIndex; location.href = (location.href).split('?')[0] + "?file=" +
example_list[select.selectedIndex];
}); });
let stdin_buffer = []; let stdin_buffer = [];
@ -94,6 +93,7 @@
stdout_buffer = []; stdout_buffer = [];
let stdout = code => { let stdout = code => {
if (code == 0) { if (code == 0) {
console.log(code);
term.write(new Uint8Array(stdout_buffer)); term.write(new Uint8Array(stdout_buffer));
stdout_buffer = []; stdout_buffer = [];
} else { } else {

View File

@ -27,6 +27,11 @@ class Input : public Component {
// Component implementation. // Component implementation.
Element Render() override; Element Render() override;
bool OnEvent(Event) override; bool OnEvent(Event) override;
private:
bool OnMouseEvent(Event);
Box input_box_;
Box cursor_box_;
}; };
} // namespace ftxui } // namespace ftxui

View File

@ -15,14 +15,15 @@ Element Input::Render() {
// Placeholder. // Placeholder.
if (content.size() == 0) { if (content.size() == 0) {
if (is_focused) if (is_focused)
return text(placeholder) | focus | dim | inverted | main_decorator; return text(placeholder) | focus | dim | inverted | main_decorator |
reflect(input_box_);
else else
return text(placeholder) | dim | main_decorator; return text(placeholder) | dim | main_decorator | reflect(input_box_);
} }
// Not focused. // Not focused.
if (!is_focused) if (!is_focused)
return text(content) | main_decorator; return text(content) | main_decorator | reflect(input_box_);
std::wstring part_before_cursor = content.substr(0, cursor_position); std::wstring part_before_cursor = content.substr(0, cursor_position);
std::wstring part_at_cursor = cursor_position < (int)content.size() std::wstring part_at_cursor = cursor_position < (int)content.size()
@ -37,13 +38,18 @@ Element Input::Render() {
return return
hbox( hbox(
text(part_before_cursor), text(part_before_cursor),
text(part_at_cursor) | underlined | focused, text(part_at_cursor) | underlined | focused | reflect(cursor_box_),
text(part_after_cursor) text(part_after_cursor)
) | flex | inverted | frame | main_decorator; ) | flex | inverted | frame | main_decorator | reflect(input_box_);
// clang-format off // clang-format on
} }
bool Input::OnEvent(Event event) { bool Input::OnEvent(Event event) {
cursor_position = std::max(0, std::min<int>(content.size(), cursor_position)); cursor_position = std::max(0, std::min<int>(content.size(), cursor_position));
if (event.is_mouse())
return OnMouseEvent(event);
std::wstring c; std::wstring c;
// Backspace. // Backspace.
@ -95,6 +101,26 @@ bool Input::OnEvent(Event event) {
return false; return false;
} }
bool Input::OnMouseEvent(Event event) {
if (!input_box_.Contain(event.mouse().x, event.mouse().y))
return false;
TakeFocus();
if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) {
int new_cursor_position =
cursor_position + event.mouse().x - cursor_box_.x_min;
new_cursor_position =
std::max(0, std::min<int>(content.size(), new_cursor_position));
if (cursor_position != new_cursor_position) {
cursor_position = new_cursor_position;
on_change();
}
}
return true;
}
} // namespace ftxui } // namespace ftxui
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -40,7 +40,7 @@ namespace {
void Flush() { void Flush() {
// Emscripten doesn't implement flush. We interpret zero as flush. // Emscripten doesn't implement flush. We interpret zero as flush.
std::cout << std::flush << (char)0; std::cout << '\0' << std::flush;
} }
constexpr int timeout_milliseconds = 20; constexpr int timeout_milliseconds = 20;
@ -353,6 +353,8 @@ void ScreenInteractive::Loop(Component* component) {
DECMode::kMouseSgrExtMode, DECMode::kMouseSgrExtMode,
}); });
flush();
auto event_listener = auto event_listener =
std::thread(&EventListener, &quit_, event_receiver_->MakeSender()); std::thread(&EventListener, &quit_, event_receiver_->MakeSender());

View File

@ -41,19 +41,23 @@ void TerminalInputParser::Send(TerminalInputParser::Output output) {
case CHARACTER: case CHARACTER:
out_->Send(Event::Character(std::move(pending_))); out_->Send(Event::Character(std::move(pending_)));
pending_.clear();
return; return;
case SPECIAL: case SPECIAL:
out_->Send(Event::Special(std::move(pending_))); out_->Send(Event::Special(std::move(pending_)));
pending_.clear();
return; return;
case MOUSE: case MOUSE:
out_->Send(Event::Mouse(std::move(pending_), output.mouse)); out_->Send(Event::Mouse(std::move(pending_), output.mouse));
pending_.clear();
return; return;
case CURSOR_REPORTING: case CURSOR_REPORTING:
out_->Send(Event::CursorReporting(std::move(pending_), output.cursor.x, out_->Send(Event::CursorReporting(std::move(pending_), output.cursor.x,
output.cursor.y)); output.cursor.y));
pending_.clear();
return; return;
} }
// NOT_REACHED(). // NOT_REACHED().
@ -133,7 +137,7 @@ TerminalInputParser::Output TerminalInputParser::ParseDCS() {
TerminalInputParser::Output TerminalInputParser::ParseCSI() { TerminalInputParser::Output TerminalInputParser::ParseCSI() {
bool altered = false; bool altered = false;
int argument; int argument = 0;
std::vector<int> arguments; std::vector<int> arguments;
while (true) { while (true) {
if (!Eat()) if (!Eat())
@ -205,9 +209,8 @@ TerminalInputParser::Output TerminalInputParser::ParseMouse(
output.mouse.button = Mouse::Button((arguments[0] & 3) + // output.mouse.button = Mouse::Button((arguments[0] & 3) + //
((arguments[0] & 64) >> 4)); ((arguments[0] & 64) >> 4));
output.mouse.motion = Mouse::Motion(pressed); output.mouse.motion = Mouse::Motion(pressed);
output.mouse.shift = arguments[0] & 4; output.mouse.shift = bool(arguments[0] & 4);
output.mouse.meta = arguments[0] & 8; output.mouse.meta = bool(arguments[0] & 8);
output.mouse.control = arguments[0] & 16;
output.mouse.x = arguments[1]; output.mouse.x = arguments[1];
output.mouse.y = arguments[2]; output.mouse.y = arguments[2];
return output; return output;

View File

@ -184,7 +184,7 @@ std::string Screen::ToString() {
} }
void Screen::Print() { void Screen::Print() {
std::cout << ToString() << std::flush << (char)0; std::cout << ToString() << '\0' << std::flush;
} }
/// @brief Access a character a given position. /// @brief Access a character a given position.