diff --git a/src/ftxui/component/terminal_input_parser.cpp b/src/ftxui/component/terminal_input_parser.cpp index 38860ad40e9e9bd562437f90c245261d85d1dc6a..7d9f74b24b619b092fa6fd02dae1d27092ef1716 100644 --- a/src/ftxui/component/terminal_input_parser.cpp +++ b/src/ftxui/component/terminal_input_parser.cpp @@ -136,12 +136,11 @@ TerminalInputParser::Output TerminalInputParser::ParseUTF8() { unsigned int first_zero = 8; // NOLINT for (unsigned int i = 0; i < 8; ++i) { // NOLINT mask |= selector; - if (head & selector) { - selector >>= 1U; - continue; + if (!(head & selector)) { + first_zero = i; + break; } - first_zero = i; - break; + selector >>= 1U; } // Accumulate the value of the first byte. diff --git a/src/ftxui/component/terminal_input_parser_test.cpp b/src/ftxui/component/terminal_input_parser_test.cpp index 863f9743f70521dc093a7694b47c4a924e1734a8..42d6d8b9d1c9948c54a95cf104c308511c392df0 100644 --- a/src/ftxui/component/terminal_input_parser_test.cpp +++ b/src/ftxui/component/terminal_input_parser_test.cpp @@ -72,7 +72,7 @@ TEST(Event, EscapeKeyEnoughWait) { EXPECT_FALSE(event_receiver->Receive(&received)); } -TEST(Event, MouseLeftClick) { +TEST(Event, MouseLeftClickPressed) { auto event_receiver = MakeReceiver<Task>(); { auto parser = TerminalInputParser(event_receiver->MakeSender()); @@ -95,6 +95,56 @@ TEST(Event, MouseLeftClick) { EXPECT_EQ(Mouse::Left, std::get<Event>(received).mouse().button); EXPECT_EQ(12, std::get<Event>(received).mouse().x); EXPECT_EQ(42, std::get<Event>(received).mouse().y); + EXPECT_EQ(std::get<Event>(received).mouse().motion, Mouse::Pressed); + EXPECT_FALSE(event_receiver->Receive(&received)); +} + +TEST(Event, MouseLeftClickReleased) { + auto event_receiver = MakeReceiver<Task>(); + { + auto parser = TerminalInputParser(event_receiver->MakeSender()); + parser.Add('\x1B'); + parser.Add('['); + parser.Add('3'); + parser.Add('2'); + parser.Add(';'); + parser.Add('1'); + parser.Add('2'); + parser.Add(';'); + parser.Add('4'); + parser.Add('2'); + parser.Add('m'); + } + + Task received; + EXPECT_TRUE(event_receiver->Receive(&received)); + EXPECT_TRUE(std::get<Event>(received).is_mouse()); + EXPECT_EQ(Mouse::Left, std::get<Event>(received).mouse().button); + EXPECT_EQ(12, std::get<Event>(received).mouse().x); + EXPECT_EQ(42, std::get<Event>(received).mouse().y); + EXPECT_EQ(std::get<Event>(received).mouse().motion, Mouse::Released); + EXPECT_FALSE(event_receiver->Receive(&received)); +} + +TEST(Event, MouseReporting) { + auto event_receiver = MakeReceiver<Task>(); + { + auto parser = TerminalInputParser(event_receiver->MakeSender()); + parser.Add('\x1B'); + parser.Add('['); + parser.Add('1'); + parser.Add('2'); + parser.Add(';'); + parser.Add('4'); + parser.Add('2'); + parser.Add('R'); + } + + Task received; + EXPECT_TRUE(event_receiver->Receive(&received)); + EXPECT_TRUE(std::get<Event>(received).is_cursor_reporting()); + EXPECT_EQ(42, std::get<Event>(received).cursor_x()); + EXPECT_EQ(12, std::get<Event>(received).cursor_y()); EXPECT_FALSE(event_receiver->Receive(&received)); } @@ -232,6 +282,105 @@ TEST(Event, UTF8) { } } +TEST(Event, NewLine) { + for (char newline : {'\r', '\n'}) { + auto event_receiver = MakeReceiver<Task>(); + { + auto parser = TerminalInputParser(event_receiver->MakeSender()); + parser.Add(newline); + } + Task received; + EXPECT_TRUE(event_receiver->Receive(&received)); + EXPECT_TRUE(std::get<Event>(received) == Event::Return); + } +} + +TEST(Event, Control) { + struct TestCase { + char input; + bool cancel; + }; + std::vector<TestCase> cases; + for (int i = 0; i < 32; ++i) { + if (i == 13 || i == 24 || i == 26 || i == 27) + continue; + cases.push_back({char(i), false}); + } + cases.push_back({char(24), true}); + cases.push_back({char(26), true}); + cases.push_back({char(127), false}); + + for(auto test : cases) { + auto event_receiver = MakeReceiver<Task>(); + { + auto parser = TerminalInputParser(event_receiver->MakeSender()); + parser.Add(test.input); + } + Task received; + if (test.cancel) { + EXPECT_FALSE(event_receiver->Receive(&received)); + } else { + EXPECT_TRUE(event_receiver->Receive(&received)); + EXPECT_EQ(std::get<Event>(received), Event::Special({test.input})); + } + } +} + +TEST(Event, Special) { + auto str = [](std::string input) { + std::vector<unsigned char> output; + for (auto it : input) + output.push_back(it); + return output; + }; + struct { + std::vector<unsigned char> input; + Event expected; + } kTestCase[] = { + {str("\x1B[D"), Event::ArrowLeft}, + {str("\x1B[C"), Event::ArrowRight}, + {str("\x1B[A"), Event::ArrowUp}, + {str("\x1B[B"), Event::ArrowDown}, + {{127}, Event::Backspace}, + {str("\x1B[3~"), Event::Delete}, + //{str("\x1B"), Event::Escape}, + {{10}, Event::Return}, + {{9}, Event::Tab}, + {{27, 91, 90}, Event::TabReverse}, + //{str("\x1B[OP"), Event::F1}, + //{str("\x1B[OQ"), Event::F2}, + //{str("\x1B[OR"), Event::F3}, + //{str("\x1B[OS"), Event::F4}, + {str("\x1B[15~"), Event::F5}, + {str("\x1B[17~"), Event::F6}, + {str("\x1B[18~"), Event::F7}, + {str("\x1B[19~"), Event::F8}, + {str("\x1B[20~"), Event::F9}, + {str("\x1B[21~"), Event::F10}, + {str("\x1B[21~"), Event::F11}, + {str("\x1B[24~"), Event::F12}, + {{27, 91, 72}, Event::Home}, + {{27, 91, 70}, Event::End}, + {{27, 91, 53, 126}, Event::PageUp}, + {{27, 91, 54, 126}, Event::PageDown}, + {{0}, Event::Custom}, + }; + + for (auto test : kTestCase) { + auto event_receiver = MakeReceiver<Task>(); + { + auto parser = TerminalInputParser(event_receiver->MakeSender()); + for (auto input : test.input) { + parser.Add(input); + } + } + Task received; + EXPECT_TRUE(event_receiver->Receive(&received)); + EXPECT_EQ(std::get<Event>(received), test.expected); + EXPECT_FALSE(event_receiver->Receive(&received)); + } +} + } // namespace ftxui // Copyright 2020 Arthur Sonzogni. All rights reserved. diff --git a/src/ftxui/dom/canvas_test.cpp b/src/ftxui/dom/canvas_test.cpp index c31c6d8500a475e1a1754a66dd370ae39e1357ee..274bdb9941b53d48c425d6dd84ae5c01be4e7d13 100644 --- a/src/ftxui/dom/canvas_test.cpp +++ b/src/ftxui/dom/canvas_test.cpp @@ -20,33 +20,49 @@ int Hash(const std::string s) { TEST(BorderTest, GoldPoint) { Terminal::SetColorSupport(Terminal::Color::TrueColor); auto element = canvas([](Canvas& c) { // - c.DrawPoint(3, 3, 1, Color::Red); + c.DrawPoint(3, 3, 1); c.DrawPointToggle(2, 8); - c.DrawPointLine(3, 7, 10, 19, Color::Blue); - c.DrawPointCircle(10, 5, 3, Color::Yellow); - c.DrawPointCircleFilled(20, 5, 3, Color::Green); - c.DrawPointEllipse(10, 10, 5, 2, Color::Blue); - c.DrawPointEllipseFilled(10, 20, 5, 2, Color::DarkGreen); + c.DrawPointToggle(2, 8); + c.DrawPointToggle(2, 8); + c.DrawPointLine(3, 7, 10, 19); + c.DrawPointCircle(10, 5, 3); + c.DrawPointCircleFilled(20, 5, 3); + c.DrawPointEllipse(10, 10, 5, 2); + c.DrawPointEllipseFilled(10, 20, 5, 2); }); Screen screen(30, 10); Render(screen, element); - EXPECT_EQ(Hash(screen.ToString()), 17651); + EXPECT_EQ(Hash(screen.ToString()), 1069); } TEST(BorderTest, GoldBlock) { Terminal::SetColorSupport(Terminal::Color::TrueColor); auto element = canvas([](Canvas& c) { // - c.DrawBlock(3, 3, 1, Color::Red); + c.DrawBlock(3, 3, 1); + c.DrawBlockToggle(2, 8); c.DrawBlockToggle(2, 8); - c.DrawBlockLine(3, 7, 10, 19, Color::Blue); - c.DrawBlockCircle(10, 5, 3, Color::Yellow); - c.DrawBlockCircleFilled(20, 5, 3, Color::Green); - c.DrawBlockEllipse(10, 10, 5, 2, Color::Blue); - c.DrawBlockEllipseFilled(10, 20, 5, 2, Color::DarkGreen); + c.DrawBlockToggle(2, 8); + c.DrawBlockLine(3, 7, 10, 19); + c.DrawBlockCircle(10, 5, 3); + c.DrawBlockCircleFilled(20, 5, 3); + c.DrawBlockEllipse(10, 10, 5, 2); + c.DrawBlockEllipseFilled(10, 20, 5, 2); }); Screen screen(30, 10); Render(screen, element); - EXPECT_EQ(Hash(screen.ToString()), 14383); + EXPECT_EQ(Hash(screen.ToString()), 472); +} + +TEST(BorderTest, GoldText) { + Terminal::SetColorSupport(Terminal::Color::TrueColor); + Canvas c(10, 10); + c.DrawText(0, 0, "test"); + c.DrawText(0, 5, "test"); + c.DrawText(0, 10, "test"); + auto element = canvas(c); + Screen screen(30, 10); + Render(screen, element); + EXPECT_EQ(Hash(screen.ToString()), 10447); } } // namespace ftxui