From b3a333b4173b4e1fbb4a71a28e526e05c4aeac30 Mon Sep 17 00:00:00 2001 From: Arthur Sonzogni Date: Sat, 17 Jul 2021 10:36:50 +0200 Subject: [PATCH] Add support for password for input element. (#158) This fixes: https://github.com/ArthurSonzogni/FTXUI/issues/139 CC:@Creapermann --- examples/component/input.cpp | 26 +++++++++++------ include/ftxui/component/component_options.hpp | 3 ++ include/ftxui/screen/screen.hpp | 2 -- src/ftxui/component/input.cpp | 21 ++++++++------ src/ftxui/component/input_test.cpp | 28 +++++++++++++++++++ 5 files changed, 61 insertions(+), 19 deletions(-) diff --git a/examples/component/input.cpp b/examples/component/input.cpp index c5a31c2..4184a01 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -10,23 +10,31 @@ int main(int argc, const char* argv[]) { using namespace ftxui; - std::wstring first_name_; - std::wstring last_name_; + std::wstring first_name; + std::wstring last_name; + std::wstring password; - Component input_first_name_ = Input(&first_name_, "first name"); - Component input_last_name_ = Input(&last_name_, "last name"); + + Component input_first_name = Input(&first_name, "first name"); + Component input_last_name = Input(&last_name, "last name"); + + InputOption password_option; + password_option.password = true; + Component input_password = Input(&password, "password", password_option); auto component = Container::Vertical({ - input_first_name_, - input_last_name_, + input_first_name, + input_last_name, + input_password, }); auto renderer = Renderer(component, [&] { return vbox({ - text(L"Hello " + first_name_ + L" " + last_name_), + text(L"Hello " + first_name + L" " + last_name), separator(), - hbox({text(L" First name : "), input_first_name_->Render()}), - hbox({text(L" Last name : "), input_last_name_->Render()}), + hbox(text(L" First name : "), input_first_name->Render()), + hbox(text(L" Last name : "), input_last_name->Render()), + hbox(text(L" Password : "), input_password->Render()), }) | border; }); diff --git a/include/ftxui/component/component_options.hpp b/include/ftxui/component/component_options.hpp index 2ca8ac3..f24e044 100644 --- a/include/ftxui/component/component_options.hpp +++ b/include/ftxui/component/component_options.hpp @@ -50,6 +50,9 @@ struct InputOption { /// Called when the user presses enter. std::function on_enter = [] {}; + /// Obscure the input content using '*'. + bool password = false; + Ref cursor_position = 0; }; diff --git a/include/ftxui/screen/screen.hpp b/include/ftxui/screen/screen.hpp index 7d0a5dc..4a3d0ca 100644 --- a/include/ftxui/screen/screen.hpp +++ b/include/ftxui/screen/screen.hpp @@ -45,9 +45,7 @@ struct Pixel { /// @brief Define how the Screen's dimensions should look like. /// @ingroup screen struct Dimension { - /// coucou static Dimension Fixed(int); - /// @brief coucou static Dimension Fit(Element&); static Dimension Full(); diff --git a/src/ftxui/component/input.cpp b/src/ftxui/component/input.cpp index 1c3217e..527e31a 100644 --- a/src/ftxui/component/input.cpp +++ b/src/ftxui/component/input.cpp @@ -30,13 +30,18 @@ class InputBase : public ComponentBase { // Component implementation: Element Render() override { + std::wstring password_content; + if (option_->password) + password_content = std::wstring(content_->size(), U'•'); + std::wstring& content = option_->password ? password_content : *content_; + cursor_position() = - std::max(0, std::min(content_->size(), cursor_position())); + std::max(0, std::min(content.size(), cursor_position())); auto main_decorator = flex | size(HEIGHT, EQUAL, 1); bool is_focused = Focused(); // placeholder. - if (content_->size() == 0) { + if (content.size() == 0) { if (is_focused) return text(*placeholder_) | focus | dim | inverted | main_decorator | reflect(input_box_); @@ -46,15 +51,15 @@ class InputBase : public ComponentBase { // Not focused. if (!is_focused) - return text(*content_) | main_decorator | reflect(input_box_); + return text(content) | main_decorator | reflect(input_box_); - std::wstring part_before_cursor = content_->substr(0, cursor_position()); - std::wstring part_at_cursor = cursor_position() < (int)content_->size() - ? content_->substr(cursor_position(), 1) + std::wstring part_before_cursor = content.substr(0, cursor_position()); + std::wstring part_at_cursor = cursor_position() < (int)content.size() + ? content.substr(cursor_position(), 1) : L" "; std::wstring part_after_cursor = - cursor_position() < (int)content_->size() - 1 - ? content_->substr(cursor_position() + 1) + cursor_position() < (int)content.size() - 1 + ? content.substr(cursor_position() + 1) : L""; auto focused = is_focused ? focus : select; diff --git a/src/ftxui/component/input_test.cpp b/src/ftxui/component/input_test.cpp index f9d4037..1e61bcc 100644 --- a/src/ftxui/component/input_test.cpp +++ b/src/ftxui/component/input_test.cpp @@ -35,6 +35,34 @@ TEST(InputTest, Type) { input->OnEvent(Event::Character('b')); EXPECT_EQ(content, L"ab"); EXPECT_EQ(option.cursor_position(), 2u); + + auto document = input->Render(); + auto screen = Screen::Create(Dimension::Fit(document)); + Render(screen, document); + EXPECT_EQ(screen.PixelAt(0, 0).character, L"a"); + EXPECT_EQ(screen.PixelAt(1, 0).character, L"b"); +} + +TEST(InputTest, TypePassword) { + std::wstring content; + std::wstring placeholder; + auto option = InputOption(); + option.password = true; + Component input = Input(&content, &placeholder, &option); + + input->OnEvent(Event::Character('a')); + EXPECT_EQ(content, L"a"); + EXPECT_EQ(option.cursor_position(), 1u); + + input->OnEvent(Event::Character('b')); + EXPECT_EQ(content, L"ab"); + EXPECT_EQ(option.cursor_position(), 2u); + + auto document = input->Render(); + auto screen = Screen::Create(Dimension::Fit(document)); + Render(screen, document); + EXPECT_EQ(screen.PixelAt(0, 0).character, L"•"); + EXPECT_EQ(screen.PixelAt(1, 0).character, L"•"); } TEST(InputTest, Arrow) {