diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 53382c7d..9ffb08ec 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(color) add_subdirectory(frame) add_subdirectory(gauge) +add_subdirectory(input) add_subdirectory(menu) add_subdirectory(menu2) add_subdirectory(print_key_press) diff --git a/examples/input/CMakeLists.txt b/examples/input/CMakeLists.txt new file mode 100644 index 00000000..1e37595c --- /dev/null +++ b/examples/input/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(input_main + main.cpp +) +target_link_libraries(input_main PRIVATE ftxui) diff --git a/examples/input/main.cpp b/examples/input/main.cpp new file mode 100644 index 00000000..fa4e0be8 --- /dev/null +++ b/examples/input/main.cpp @@ -0,0 +1,47 @@ +#include +#include +#include + +#include "ftxui/screen_interactive.hpp" +#include "ftxui/component/input.hpp" +#include "ftxui/component/component_vertical.hpp" +#include "ftxui/util/string.hpp" + +using namespace ftxui::component; +using namespace ftxui::dom; + +class MyComponent : ComponentVertical { + public: + MyComponent(ftxui::component::Delegate* delegate) + : ComponentVertical(delegate), + input_1(delegate->NewChild()), + input_2(delegate->NewChild()), + input_3(delegate->NewChild()) { + Focus(&input_1); + } + + std::function on_enter = []() {}; + + private: + Input input_1; + Input input_2; + Input input_3; + + Element Render() override { + return + frame( + vbox( + hbox(text(L" input_1 : "), input_1.Render()), + hbox(text(L" input_2 : "), input_2.Render()), + hbox(text(L" input_3 : "), input_3.Render()) + ) + ); + } +}; + +int main(int argc, const char* argv[]) { + ftxui::ScreenInteractive screen(60, 17); + MyComponent component(screen.delegate()); + component.on_enter = screen.ExitLoopClosure(); + screen.Loop(); +} diff --git a/examples/print_key_press/main.cpp b/examples/print_key_press/main.cpp index b36d2815..eb5e927c 100644 --- a/examples/print_key_press/main.cpp +++ b/examples/print_key_press/main.cpp @@ -15,26 +15,30 @@ class DrawKey : public ftxui::component::Component { using namespace ftxui::dom; Children children; for (size_t i = std::max(0, (int)keys.size() - 10); i < keys.size(); ++i) { + std::string code = ""; + for(size_t j = 0; j<5; ++j) + code += " " + std::to_string(keys[i].values[j]); + try { - std::string line = std::to_string(i) + " -> " + std::to_string(keys[i]) + - " (" + char(keys[i]) + ")"; + std::string line = code + " -> " + std::to_string(keys[i].values[0]) + " (" + + char(keys[i].values[0]) + ")"; children.push_back(text(to_wstring(line))); } catch (...) { - std::string line = std::to_string(i) + " -> " + std::to_string(keys[i]) + - " (undefined)"; + std::string line = + code + " -> " + std::to_string(keys[i].values[0]) + " (undefined)"; children.push_back(text(to_wstring(line))); } } return vbox(std::move(children)); } - bool Event(int key) override { - keys.push_back(key); + bool OnEvent(ftxui::Event event) override { + keys.push_back(event); return true; } private: - std::vector keys; + std::vector keys; }; int main(int argc, const char* argv[]) { diff --git a/examples/toggle/main.cpp b/examples/toggle/main.cpp index 959cc705..e0323927 100644 --- a/examples/toggle/main.cpp +++ b/examples/toggle/main.cpp @@ -8,6 +8,7 @@ #include "ftxui/component/component_vertical.hpp" #include "ftxui/util/string.hpp" +using namespace ftxui; using namespace ftxui::component; using namespace ftxui::dom; @@ -48,11 +49,11 @@ class MyComponent : ComponentVertical { ); } - bool Event(int key) override { - if (ComponentVertical::Event(key)) + bool OnEvent(Event event) override { + if (ComponentVertical::OnEvent(event)) return true; - if (key == 10) { + if (event == Event::Return) { on_enter(); return true; } diff --git a/ftxui/CMakeLists.txt b/ftxui/CMakeLists.txt index 7128aac9..6991e408 100644 --- a/ftxui/CMakeLists.txt +++ b/ftxui/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(ftxui src/ftxui/component/component_direction.cpp src/ftxui/component/component_horizontal.cpp src/ftxui/component/component_vertical.cpp + src/ftxui/component/input.cpp src/ftxui/component/menu.cpp src/ftxui/component/toggle.cpp src/ftxui/dom/bold.cpp @@ -14,7 +15,6 @@ add_library(ftxui src/ftxui/dom/dim.cpp src/ftxui/dom/flex.cpp src/ftxui/dom/frame.cpp - src/ftxui/dom/frame.cpp src/ftxui/dom/gauge.cpp src/ftxui/dom/hbox.cpp src/ftxui/dom/inverted.cpp @@ -24,6 +24,7 @@ add_library(ftxui src/ftxui/dom/text.cpp src/ftxui/dom/underlined.cpp src/ftxui/dom/vbox.cpp + src/ftxui/event.cpp src/ftxui/screen.cpp src/ftxui/screen_interactive.cpp src/ftxui/terminal.cpp diff --git a/ftxui/include/ftxui/component/Event.hpp b/ftxui/include/ftxui/component/Event.hpp deleted file mode 100644 index 4ce8a77c..00000000 --- a/ftxui/include/ftxui/component/Event.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef FTXUI_COMPONENT_EVENT -#define FTXUI_COMPONENT_EVENT - -namespace ftxui { -namespace component { - -struct Event{ - // --- Character --- - static Event Character(char); - - // --- Arrow --- - static Event Arrow_Left; - static Event Arrow_Right; - static Event Arrow_Up; - static Event Arrow_Down; - - // --- Other --- - static Event Backspace; - static Event Delete; - static Event Escape; - static Event F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12; - - // Internal representation. - int values [3]; - Event(int values[3]) : values(values); - -}; - - -} // namespace component -} // namespace ftxui - - -#endif /* end of include guard: FTXUI_COMPONENT_EVENT */ diff --git a/ftxui/include/ftxui/component/component.hpp b/ftxui/include/ftxui/component/component.hpp index 80e77b7e..1c575ef8 100644 --- a/ftxui/include/ftxui/component/component.hpp +++ b/ftxui/include/ftxui/component/component.hpp @@ -1,8 +1,9 @@ #ifndef FTXUI_COMPONENT_COMPONENT_HPP #define FTXUI_COMPONENT_COMPONENT_HPP -#include "ftxui/dom/elements.hpp" #include "ftxui/component/delegate.hpp" +#include "ftxui/dom/elements.hpp" +#include "ftxui/event.hpp" namespace ftxui { namespace component { @@ -20,7 +21,7 @@ class Component { virtual dom::Element Render(); // Handle an event. By default, it calls this function on each children. - virtual bool Event(int key); + virtual bool OnEvent(Event even); // If this component contains children, this indicates which one is active. It // can be none of them. diff --git a/ftxui/include/ftxui/component/component_direction.hpp b/ftxui/include/ftxui/component/component_direction.hpp index d71f2ac0..55b99ef8 100644 --- a/ftxui/include/ftxui/component/component_direction.hpp +++ b/ftxui/include/ftxui/component/component_direction.hpp @@ -10,12 +10,12 @@ namespace component { class ComponentDirection : public Component { public: ComponentDirection(Delegate* delegate); - bool Event(int key) override; + bool OnEvent(Event) override; Component* GetActiveChild() override; protected: void Focus(Component* child); - virtual bool HandleDirection(int key) = 0; + virtual bool HandleDirection(Event) = 0; Component* active_child_; }; diff --git a/ftxui/include/ftxui/component/component_horizontal.hpp b/ftxui/include/ftxui/component/component_horizontal.hpp index f518c328..35dadf2b 100644 --- a/ftxui/include/ftxui/component/component_horizontal.hpp +++ b/ftxui/include/ftxui/component/component_horizontal.hpp @@ -10,8 +10,8 @@ namespace component { // It assumes its children are put in the horizontal direction. class ComponentHorizontal : public ComponentDirection { public: - ComponentHorizontal(Delegate* delegate); - bool HandleDirection(int key) override; + ComponentHorizontal(Delegate*); + bool HandleDirection(Event) override; }; } // namespace component diff --git a/ftxui/include/ftxui/component/component_vertical.hpp b/ftxui/include/ftxui/component/component_vertical.hpp index a8408589..22c91f25 100644 --- a/ftxui/include/ftxui/component/component_vertical.hpp +++ b/ftxui/include/ftxui/component/component_vertical.hpp @@ -10,8 +10,8 @@ namespace component { // It assumes its children are put in the vertical direction. class ComponentVertical : public ComponentDirection { public: - ComponentVertical(Delegate* delegate); - bool HandleDirection(int key) override; + ComponentVertical(Delegate*); + bool HandleDirection(Event) override; }; } // namespace component diff --git a/ftxui/include/ftxui/component/input.hpp b/ftxui/include/ftxui/component/input.hpp new file mode 100644 index 00000000..b45256d9 --- /dev/null +++ b/ftxui/include/ftxui/component/input.hpp @@ -0,0 +1,35 @@ +#ifndef FTXUI_COMPONENT_INPUT_H_ +#define FTXUI_COMPONENT_INPUT_H_ + +#include "ftxui/component/component.hpp" +#include + +namespace ftxui { +namespace component { + +class Input : public Component { + public: + // Constructor. + Input(Delegate*); + ~Input() override; + + // State. + std::wstring content = L"input"; + std::wstring placeholder = L"placeholder"; + + // State update callback. + std::function on_change = [](){}; + std::function on_enter = [](){}; + + // Component implementation. + dom::Element Render() override; + bool OnEvent(Event) override; + + private: + int cursor_position = 0; +}; + +} // namespace component +} // namespace ftxui + +#endif /* end of include guard: FTXUI_COMPONENT_INPUT_H_ */ diff --git a/ftxui/include/ftxui/component/menu.hpp b/ftxui/include/ftxui/component/menu.hpp index 79dd19f9..91aa484e 100644 --- a/ftxui/include/ftxui/component/menu.hpp +++ b/ftxui/include/ftxui/component/menu.hpp @@ -22,7 +22,7 @@ class Menu : public Component { // Component implementation. dom::Element Render() override; - bool Event(int key) override; + bool OnEvent(Event) override; }; } // namespace component diff --git a/ftxui/include/ftxui/component/toggle.hpp b/ftxui/include/ftxui/component/toggle.hpp index daa18e74..64eded36 100644 --- a/ftxui/include/ftxui/component/toggle.hpp +++ b/ftxui/include/ftxui/component/toggle.hpp @@ -22,7 +22,7 @@ class Toggle : public Component { // Component implementation. dom::Element Render() override; - bool Event(int key) override; + bool OnEvent(Event) override; }; } // namespace component diff --git a/ftxui/include/ftxui/event.hpp b/ftxui/include/ftxui/event.hpp new file mode 100644 index 00000000..15410d7c --- /dev/null +++ b/ftxui/include/ftxui/event.hpp @@ -0,0 +1,37 @@ +#ifndef FTXUI_EVENT_H_ +#define FTXUI_EVENT_H_ + +#include +#include + +namespace ftxui { + +struct Event{ + public: + // --- Character --- + static Event Character(char); + + // --- Arrow --- + static Event ArrowLeft; + static Event ArrowRight; + static Event ArrowUp; + static Event ArrowDown; + + // --- Other --- + static Event Backspace; + static Event Delete; + static Event Return; + static Event Escape; + static Event F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12; + + bool operator==(const Event& other) { return values == other.values; } + + // Internal representation. + std::array values = {0, 0, 0, 0, 0}; +}; + + +} // namespace ftxui + + +#endif /* end of include guard: FTXUI_EVENT_H_ */ diff --git a/ftxui/src/ftxui/component/component.cpp b/ftxui/src/ftxui/component/component.cpp index 369e9c34..9b40d594 100644 --- a/ftxui/src/ftxui/component/component.cpp +++ b/ftxui/src/ftxui/component/component.cpp @@ -17,7 +17,7 @@ dom::Element Component::Render() { return text(L"Not implemented component"); } -bool Component::Event(int key) { +bool Component::OnEvent(Event event) { return false; } diff --git a/ftxui/src/ftxui/component/component_direction.cpp b/ftxui/src/ftxui/component/component_direction.cpp index fa77b813..74dc8462 100644 --- a/ftxui/src/ftxui/component/component_direction.cpp +++ b/ftxui/src/ftxui/component/component_direction.cpp @@ -6,17 +6,17 @@ namespace component { ComponentDirection::ComponentDirection(Delegate* delegate) : Component(delegate), active_child_(nullptr) {} -bool ComponentDirection::Event(int key) { +bool ComponentDirection::OnEvent(Event event) { if (!Focused()) return false; if (!active_child_) return false; - if (active_child_->Event(key)) + if (active_child_->OnEvent(event)) return true; - return HandleDirection(key); + return HandleDirection(event); } Component* ComponentDirection::GetActiveChild() { diff --git a/ftxui/src/ftxui/component/component_horizontal.cpp b/ftxui/src/ftxui/component/component_horizontal.cpp index 012098ac..b9079cc8 100644 --- a/ftxui/src/ftxui/component/component_horizontal.cpp +++ b/ftxui/src/ftxui/component/component_horizontal.cpp @@ -6,9 +6,9 @@ namespace component { ComponentHorizontal::ComponentHorizontal(Delegate* delegate) : ComponentDirection(delegate) {} -bool ComponentHorizontal::HandleDirection(int key) { +bool ComponentHorizontal::HandleDirection(Event event) { // Left pressed ? - if (key == 68 || key == 'h') { + if (event == Event::ArrowLeft || event == Event::Character('h')) { Component* previous_sibling = active_child_->PreviousSibling(); if (previous_sibling) { active_child_ = previous_sibling; @@ -17,7 +17,7 @@ bool ComponentHorizontal::HandleDirection(int key) { } // Left pressed ? - if (key == 67 || key == 'l') { + if (event == Event::ArrowRight || event == Event::Character('l')) { Component* next_sibling = active_child_->NextSibling(); if (next_sibling) { active_child_ = next_sibling; diff --git a/ftxui/src/ftxui/component/component_vertical.cpp b/ftxui/src/ftxui/component/component_vertical.cpp index 70d516e7..ec0141fb 100644 --- a/ftxui/src/ftxui/component/component_vertical.cpp +++ b/ftxui/src/ftxui/component/component_vertical.cpp @@ -6,9 +6,9 @@ namespace component { ComponentVertical::ComponentVertical(Delegate* delegate) : ComponentDirection(delegate) {} -bool ComponentVertical::HandleDirection(int key) { +bool ComponentVertical::HandleDirection(Event event) { // Up pressed ? - if (key == 65 || key == 'k') { + if (event == Event::ArrowUp || event == Event::Character('k')) { Component* previous_sibling = active_child_->PreviousSibling(); if (previous_sibling) { active_child_ = previous_sibling; @@ -17,7 +17,7 @@ bool ComponentVertical::HandleDirection(int key) { } // Down pressed ? - if (key == 66 || key == 'j') { + if (event == Event::ArrowDown || event == Event::Character('j')) { Component* next_sibling = active_child_->NextSibling(); if (next_sibling) { active_child_ = next_sibling; diff --git a/ftxui/src/ftxui/component/input.cpp b/ftxui/src/ftxui/component/input.cpp new file mode 100644 index 00000000..3ed3addd --- /dev/null +++ b/ftxui/src/ftxui/component/input.cpp @@ -0,0 +1,46 @@ +#include "ftxui/component/input.hpp" +#include "ftxui/util/string.hpp" + +namespace ftxui { +namespace component { + +Input::Input(Delegate* delegate): Component(delegate) {} +Input::~Input() {} + +// Component implementation. +dom::Element Input::Render() { + bool is_place_ho + std::wstring& displayed_text = content.size() ? content : placeholder; + + using namespace dom; + if (Focused()) + return flex(inverted(text(displayed_text))); + else + return flex(text(displayed_text)); +} +bool Input::OnEvent(Event event) { + std::wstring c; + + // Backspace + if (event == Event::Backspace) { + if (content.size() != 0) + content = content.substr(0, content.size()-1); + return true; + } + + // Enter + if (event == Event::Return) { + return true; + } + + constexpr char ESC = char(27); + if (event.values[0] != ESC) { + content += event.values[0]; + return true; + } + + return false; +} + +} // namespace component +} // namespace ftxui diff --git a/ftxui/src/ftxui/component/menu.cpp b/ftxui/src/ftxui/component/menu.cpp index 3d5f259e..ebc5e460 100644 --- a/ftxui/src/ftxui/component/menu.cpp +++ b/ftxui/src/ftxui/component/menu.cpp @@ -1,5 +1,6 @@ #include "ftxui/component/menu.hpp" #include +#include namespace ftxui { namespace component { @@ -24,14 +25,14 @@ dom::Element Menu::Render() { return vbox(std::move(elements)); } -bool Menu::Event(int key) { +bool Menu::OnEvent(Event event) { if (!Focused()) return false; int new_selected = selected; - if (key == 65 || key == 'k') + if (event == Event::ArrowUp || event == Event::Character('k')) new_selected--; - if (key == 66 || key == 'j') + if (event == Event::ArrowDown || event == Event::Character('j')) new_selected++; new_selected = std::max(0, std::min(int(entries.size())-1, new_selected)); @@ -41,7 +42,7 @@ bool Menu::Event(int key) { return true; } - if (key == 10) { + if (event == Event::Return) { on_enter(); return true; } diff --git a/ftxui/src/ftxui/component/toggle.cpp b/ftxui/src/ftxui/component/toggle.cpp index 023ca0ee..2552affd 100644 --- a/ftxui/src/ftxui/component/toggle.cpp +++ b/ftxui/src/ftxui/component/toggle.cpp @@ -24,16 +24,15 @@ dom::Element Toggle::Render() { return hbox(std::move(children)); } -bool Toggle::Event(int key) { - +bool Toggle::OnEvent(Event event) { if (activated) { - if (key == 67 || key == 'l') { + if (event == Event::ArrowRight || event == Event::Character('l')) { activated = false; on_change(); return true; } } else { - if (key == 68 || key == 'h') { + if (event == Event::ArrowLeft || event == Event::Character('h')) { activated = true; on_change(); return true; diff --git a/ftxui/src/ftxui/event.cpp b/ftxui/src/ftxui/event.cpp new file mode 100644 index 00000000..f4fe1231 --- /dev/null +++ b/ftxui/src/ftxui/event.cpp @@ -0,0 +1,37 @@ +#include "ftxui/event.hpp" + +namespace ftxui { + +constexpr char ESC = char(27); + +// --- Character --- +Event Event::Character(char c) { + return Event{c}; +} + +// --- Arrow --- +Event Event::ArrowLeft{ESC, '[', 'D'}; +Event Event::ArrowRight{ESC, '[', 'C'}; +Event Event::ArrowUp{ESC, '[', 'A'}; +Event Event::ArrowDown{ESC, '[', 'B'}; + +// --- Other --- +Event Event::Backspace{char(127)}; +Event Event::Delete{ESC, '[', '3', '~'}; +Event Event::Escape{ESC}; +Event Event::Return{char(10)}; + +Event Event::F1{ESC, '[', 'O', 'P'}; +Event Event::F2{ESC, '[', 'O', 'Q'}; +Event Event::F3{ESC, '[', 'O', 'R'}; +Event Event::F4{ESC, '[', 'O', 'S'}; +Event Event::F5{ESC, '[', '1', '5', '~'}; +Event Event::F6{ESC, '[', '1', '7', '~'}; +Event Event::F7{ESC, '[', '1', '8', '~'}; +Event Event::F8{ESC, '[', '1', '9', '~'}; +Event Event::F9{ESC, '[', '2', '0', '~'}; +Event Event::F10{ESC, '[', '2', '1', '~'}; +Event Event::F11{ESC, '[', '2', '1', '~'}; // Same as F10 ? +Event Event::F12{ESC, '[', '2', '4', '~'}; + +} // namespace ftxui diff --git a/ftxui/src/ftxui/screen_interactive.cpp b/ftxui/src/ftxui/screen_interactive.cpp index 7d359431..3b2d49dd 100644 --- a/ftxui/src/ftxui/screen_interactive.cpp +++ b/ftxui/src/ftxui/screen_interactive.cpp @@ -8,6 +8,20 @@ namespace ftxui { +namespace { + constexpr char ESC = char(27); + + Event GetEvent() { + char v1 = char(getchar()); + if (v1 != ESC) + return Event{v1}; + + char v2 = char(getchar()); + char v3 = char(getchar()); + return Event{v1,v2,v3}; + }; +}; + class ScreenInteractive::Delegate : public component::Delegate { public: Delegate() : root_(this) {} @@ -29,7 +43,7 @@ class ScreenInteractive::Delegate : public component::Delegate { return child; } - void Event(int key) { component_->Event(key); } + void OnEvent(Event event) { component_->OnEvent(event); } std::vector children() override { @@ -77,8 +91,7 @@ void ScreenInteractive::Loop() { Draw(); while (!quit_) { - int key = getchar(); - delegate_->Event(key); + delegate_->OnEvent(GetEvent()); Clear(); Draw(); diff --git a/tutorial.md b/tutorial.md index ca41158a..ad19e5fc 100644 --- a/tutorial.md +++ b/tutorial.md @@ -23,6 +23,8 @@ Element bold(Element); Element dim(Element); Element inverted(Element); Element underlined(Element); +Element color(Color,Element); +Element bgcolor(Element); // --- Decorator --- Element hcenter(Element); @@ -139,3 +141,5 @@ Element bold(Element); Element dim(Element); Element inverted(Element); Element underlined(Element); +Element color(Element); +Element bgcolor(Element);