Merge pull request #90 from ArthurSonzogni/functionnal_component

Functional component
This commit is contained in:
Arthur Sonzogni 2021-05-15 00:39:22 +02:00 committed by GitHub
commit 900ed256ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
82 changed files with 2632 additions and 2164 deletions

View File

@ -15,7 +15,7 @@ endif()
project(ftxui project(ftxui
LANGUAGES CXX LANGUAGES CXX
VERSION 0.4.${git_version} VERSION 0.5.${git_version}
) )
option(FTXUI_BUILD_EXAMPLES "Set to ON to build examples" ON) option(FTXUI_BUILD_EXAMPLES "Set to ON to build examples" ON)
@ -80,6 +80,7 @@ add_library(component
include/ftxui/component/captured_mouse.hpp include/ftxui/component/captured_mouse.hpp
include/ftxui/component/checkbox.hpp include/ftxui/component/checkbox.hpp
include/ftxui/component/component.hpp include/ftxui/component/component.hpp
include/ftxui/component/component_base.hpp
include/ftxui/component/container.hpp include/ftxui/component/container.hpp
include/ftxui/component/event.hpp include/ftxui/component/event.hpp
include/ftxui/component/input.hpp include/ftxui/component/input.hpp
@ -88,7 +89,6 @@ add_library(component
include/ftxui/component/radiobox.hpp include/ftxui/component/radiobox.hpp
include/ftxui/component/receiver.hpp include/ftxui/component/receiver.hpp
include/ftxui/component/screen_interactive.hpp include/ftxui/component/screen_interactive.hpp
include/ftxui/component/slider.hpp
include/ftxui/component/toggle.hpp include/ftxui/component/toggle.hpp
src/ftxui/component/button.cpp src/ftxui/component/button.cpp
src/ftxui/component/checkbox.cpp src/ftxui/component/checkbox.cpp
@ -99,6 +99,7 @@ add_library(component
src/ftxui/component/menu.cpp src/ftxui/component/menu.cpp
src/ftxui/component/radiobox.cpp src/ftxui/component/radiobox.cpp
src/ftxui/component/radiobox.cpp src/ftxui/component/radiobox.cpp
src/ftxui/component/renderer.cpp
src/ftxui/component/screen_interactive.cpp src/ftxui/component/screen_interactive.cpp
src/ftxui/component/slider.cpp src/ftxui/component/slider.cpp
src/ftxui/component/terminal_input_parser.cpp src/ftxui/component/terminal_input_parser.cpp

View File

@ -44,7 +44,7 @@ PROJECT_NUMBER = @CMAKE_PROJECT_VERSION@
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short. # quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF = "C++ functionnal terminal UI." PROJECT_BRIEF = "C++ functional terminal UI."
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included # With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55 # in the documentation. The maximum height of the logo should not exceed 55

View File

@ -34,6 +34,7 @@
@example ./examples/component/input.cpp @example ./examples/component/input.cpp
@example ./examples/component/homescreen.cpp @example ./examples/component/homescreen.cpp
@example ./examples/component/radiobox.cpp @example ./examples/component/radiobox.cpp
@example ./examples/component/slider_rgb.cpp
@example ./examples/component/menu.cpp @example ./examples/component/menu.cpp
@example ./examples/component/menu_style.cpp @example ./examples/component/menu_style.cpp
@example ./examples/component/radiobox_in_frame.cpp @example ./examples/component/radiobox_in_frame.cpp

View File

@ -31,7 +31,7 @@ int main(void) {
Dimension::Fit(document) // Height Dimension::Fit(document) // Height
); );
Render(screen, document); Render(screen, document);
std::cout << screen.ToString() << std::endl; screen.Print();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
@ -44,28 +44,71 @@ int main(void) {
└────┘└─────────────────────────────────────────────────────────────────┘└─────┘ └────┘└─────────────────────────────────────────────────────────────────┘└─────┘
``` ```
**cmake** # Build
```c
## Using CMake
CMakeLists.txt
~~~cmake
cmake_minimum_required (VERSION 3.11) cmake_minimum_required (VERSION 3.11)
# --- Fetch FTXUI --------------------------------------------------------------
include(FetchContent) include(FetchContent)
set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE)
FetchContent_Declare(ftxui FetchContent_Declare(ftxui
GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui
# Specify a GIT_TAG here.
) )
FetchContent_GetProperties(ftxui) FetchContent_GetProperties(ftxui)
if(NOT ftxui_POPULATED) if(NOT ftxui_POPULATED)
FetchContent_Populate(ftxui) FetchContent_Populate(ftxui)
add_subdirectory(${ftxui_SOURCE_DIR} ${ftxui_BINARY_DIR} EXCLUDE_FROM_ALL) add_subdirectory(${ftxui_SOURCE_DIR} ${ftxui_BINARY_DIR} EXCLUDE_FROM_ALL)
endif() endif()
add_executable(main src/main.cpp) # ------------------------------------------------------------------------------
target_link_libraries(main
project(ftxui-starter
LANGUAGES CXX
VERSION 1.0.0
)
add_executable(ftxui-starter src/main.cpp)
target_include_directories(ftxui-starter PRIVATE src)
target_link_libraries(ftxui-starter
PRIVATE ftxui::screen PRIVATE ftxui::screen
PRIVATE ftxui::dom PRIVATE ftxui::dom
PRIVATE ftxui::component # Not needed for this example. PRIVATE ftxui::component # Not needed for this example.
) )
set_target_properties(main PROPERTIES CXX_STANDARD 17)
``` # C++17 is used. We requires fold expressions at least.
set_target_properties(ftxui-starter PROPERTIES CXX_STANDARD 17)
~~~
Build
~~~
mkdir build && cd build
cmake ..
make
./main
~~~
## Using NXXM
**.nxxm/deps**
~~~json
{
"ArthurSonzogni/FTXUI": {}
}
~~~
Build:
~~~
nxxm . -t clang-cxx17
~~~
# List of modules. # List of modules.
@ -82,7 +125,7 @@ input. It defines a set of ftxui::Component. The use can navigates using the
arrow keys and interact with widgets like checkbox/inputbox/... You can make you arrow keys and interact with widgets like checkbox/inputbox/... You can make you
own components. own components.
## screen # screen
It defines a ftxui::Screen. This is a grid of ftxui::Pixel. A Pixel represent a It defines a ftxui::Screen. This is a grid of ftxui::Pixel. A Pixel represent a
unicode character and its associated style (bold, colors, etc...). unicode character and its associated style (bold, colors, etc...).
@ -106,7 +149,7 @@ The screen can be printed as a string using ftxui::Screen::ToString().
} }
~~~ ~~~
## dom # dom
This module defines a hierachical set of Element. An element manages layout and can be responsive to the terminal dimensions. This module defines a hierachical set of Element. An element manages layout and can be responsive to the terminal dimensions.
@ -130,23 +173,6 @@ You only need one header: ftxui/dom/elements.hpp
\include ftxui/dom/elements.hpp \include ftxui/dom/elements.hpp
## component
Finally, the ftxui/component directory defines the logic to get interactivity.
Please take a look at ./examples/component
This provides:
1. A main loop.
2. Get events and respond to them.
3. A predefined implementation of "keyboard navigation".
4. A set of predefined widget: CheckBox, RadioBox, Input, Menu, Toggle.
# ftxui/dom
Every elements of the dom are declared from:
\ref ftxui/dom/elements.hpp
## text ## text
The most simple widget. It displays a text. The most simple widget. It displays a text.
@ -219,6 +245,7 @@ border(gauge(0.5))
~~~ ~~~
## graph ## graph
@htmlonly @htmlonly
<script id="asciicast-223726" src="https://asciinema.org/a/223726.js" async></script> <script id="asciicast-223726" src="https://asciinema.org/a/223726.js" async></script>
@endhtmlonly @endhtmlonly
@ -362,7 +389,25 @@ An horizontal flow layout is implemented by:
└────┘└───────────────────────────────────┘└───────────────────────────────────┘ └────┘└───────────────────────────────────┘└───────────────────────────────────┘
~~~ ~~~
# ftxui/component
# component
The `ftxui/component` directory defines the logic to get produce
interactive component responding to user's events (keyboard, mouse, etc...)
A ftxui::ScreenInteractive defines a main loop to render a component.
A ftxui::Component is a shared pointer to a ftxui::ComponentBase. The later
defines
- ftxui::ComponentBase::Render(): How to render the interface.
- ftxui::ComponentBase::OnEvent(): How to react to events.
- ftxui::ComponentBase::Add(): Give a parent/child relation ship in between
two component. This defines a tree a components, which help properly define
how keyboard navigation works.
Predefined components are available in `ftxui/dom/component.hpp`:
\include ftxui/component/component.hpp
Element are stateless object. On the other side, components are used when an Element are stateless object. On the other side, components are used when an
internal state is needed. Components are used to interact with the user with internal state is needed. Components are used to interact with the user with
@ -370,7 +415,7 @@ its keyboard. They handle keyboard navigation, including component focus.
## Input ## Input
The component: \ref ftxui::Input Produced by: ftxui::Input() from "ftxui/component/component.hpp"
@htmlonly @htmlonly
<script id="asciicast-223719" src="https://asciinema.org/a/223719.js" async></script> <script id="asciicast-223719" src="https://asciinema.org/a/223719.js" async></script>
@ -378,7 +423,7 @@ The component: \ref ftxui::Input
## Menu ## Menu
The component: \ref ftxui::Menu Produced by: ftxui::Menu() from "ftxui/component/component.hpp"
@htmlonly @htmlonly
<script id="asciicast-223720" src="https://asciinema.org/a/223720.js" async></script> <script id="asciicast-223720" src="https://asciinema.org/a/223720.js" async></script>
@ -386,7 +431,7 @@ The component: \ref ftxui::Menu
## Toggle. ## Toggle.
The component: \ref ftxui::Toggle Produced by: ftxui::Toggle() from "ftxui/component/component.hpp"
@htmlonly @htmlonly
<script id="asciicast-223722" src="https://asciinema.org/a/223722.js" async></script> <script id="asciicast-223722" src="https://asciinema.org/a/223722.js" async></script>
@ -394,7 +439,7 @@ The component: \ref ftxui::Toggle
## CheckBox ## CheckBox
The component: \ref ftxui::CheckBox Produced by: ftxui::Checkbox() from "ftxui/component/component.hpp"
@htmlonly @htmlonly
<script id="asciicast-223724" src="https://asciinema.org/a/223724.js" async></script> <script id="asciicast-223724" src="https://asciinema.org/a/223724.js" async></script>
@ -402,99 +447,32 @@ The component: \ref ftxui::CheckBox
## RadioBox ## RadioBox
The component: \ref ftxui::RadioBox Produced by: ftxui::Radiobox() from "ftxui/component/component.hpp"
@htmlonly @htmlonly
<script id="asciicast-223725" src="https://asciinema.org/a/223725.js" async></script> <script id="asciicast-223725" src="https://asciinema.org/a/223725.js" async></script>
@endhtmlonly @endhtmlonly
# Build ## Renderer
Assuming this example example.cpp file. Produced by: ftxui::Renderer() from \ref "ftxui/component/component.hpp". This
component decorate another one by using a different function to render an
interface.
**main.cpp** ## Container::Horizontal
~~~cpp
#include "ftxui/screen/screen.c
#include "ftxui/dom/elements.c
#include <iostream>
int main(int argc, const char *argv[]) { Produced by: ftxui::Container::Horizontal() from
using namespace ftxui; "ftxui/component/component.hpp". It displays a list of components horizontally
auto document = and handle keyboard/mouse navigation.
hbox({
text(L"left") | bold | border,
text(L"middle") | flex | border,
text(L"right") | border,
});
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document);
std::cout << screen.ToString(); ## Container::Vertial
return 0; Produced by: ftxui::Container::Vertical() from
} "ftxui/component/component.hpp". It displays a list of components vertically
~~~ and handles keyboard/mouse navigation.
## Using CMake ## Container::Tab
CMakeLists.txt Produced by: ftxui::Container::Tab() from
~~~cmake "ftxui/component/component.hpp". It take a list of component and display only
cmake_minimum_required (VERSION 3.11) one of them. This is useful for implementing a tab bar.
# --- Fetch FTXUI --------------------------------------------------------------
include(FetchContent)
set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE)
FetchContent_Declare(ftxui
GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui
# Specify a GIT TAG here.
)
FetchContent_GetProperties(ftxui)
if(NOT ftxui_POPULATED)
FetchContent_Populate(ftxui)
add_subdirectory(${ftxui_SOURCE_DIR} ${ftxui_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
# ------------------------------------------------------------------------------
project(ftxui-starter
LANGUAGES CXX
VERSION 1.0.0
)
add_executable(ftxui-starter src/main.cpp)
target_include_directories(ftxui-starter PRIVATE src)
target_link_libraries(ftxui-starter
PRIVATE ftxui::screen
PRIVATE ftxui::dom
PRIVATE ftxui::component # Not needed for this example.
)
# C++17 is used. We requires fold expressions at least.
set_target_properties(ftxui-starter PROPERTIES CXX_STANDARD 17)
~~~
Build
~~~
mkdir build && cd build
cmake ..
make
./main
~~~
## Using NXXM
**.nxxm/deps**
~~~json
{
"ArthurSonzogni/FTXUI": {}
}
~~~
Build:
~~~
nxxm . -t clang-cxx17
~~~

View File

@ -17,6 +17,7 @@ example(modal_dialog)
example(radiobox) example(radiobox)
example(radiobox_in_frame) example(radiobox_in_frame)
example(slider) example(slider)
example(slider_rgb)
example(tab_horizontal) example(tab_horizontal)
example(tab_vertical) example(tab_vertical)
example(toggle) example(toggle)

View File

@ -1,51 +1,37 @@
#include <functional> // for function #include <memory> // for __shared_ptr_access, shared_ptr
#include <memory> // for unique_ptr, make_u... #include <string> // for operator+, to_wstring
#include <string> // for wstring
#include <utility> // for move
#include <vector> // for vector
#include "ftxui/component/button.hpp" // for Button #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Component #include "ftxui/component/component.hpp" // for Button, Horizontal, Renderer
#include "ftxui/component/container.hpp" // for Container #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/screen/box.hpp" // for ftxui #include "ftxui/dom/elements.hpp" // for separator, Element, gauge, text, operator|, vbox, border
using namespace ftxui; using namespace ftxui;
class MyComponent : public Component {
private:
std::vector<std::unique_ptr<Button>> buttons_;
Container container_ = Container::Horizontal();
public:
MyComponent() {
Add(&container_);
auto button_add = std::make_unique<Button>();
auto button_remove = std::make_unique<Button>();
container_.Add(button_add.get());
container_.Add(button_remove.get());
button_add->label = L"Add one button";
button_remove->label = L"Remove last button";
button_add->on_click = [&] {
auto extra_button = std::make_unique<Button>();
extra_button->label = L"extra button";
container_.Add(extra_button.get());
buttons_.push_back(std::move(extra_button));
};
button_remove->on_click = [&] { buttons_.resize(buttons_.size() - 1); };
buttons_.push_back(std::move(button_add));
buttons_.push_back(std::move(button_remove));
}
};
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::TerminalOutput(); int value = 50;
MyComponent component;
screen.Loop(&component); // The tree of components. This defines how to navigate using the keyboard.
auto buttons = Container::Horizontal({
Button("Decrease", [&] { value--; }),
Button("Increase", [&] { value++; }),
});
// Modify the way to render them on screen:
auto component = Renderer(buttons, [&] {
return vbox({
text(L"value = " + std::to_wstring(value)),
separator(),
gauge(value * 0.01f),
separator(),
buttons->Render(),
}) |
border;
});
auto screen = ScreenInteractive::FitComponent();
screen.Loop(component);
return 0; return 0;
} }

View File

@ -1,34 +1,23 @@
#include "ftxui/component/checkbox.hpp" #include "ftxui/component/checkbox.hpp"
#include "ftxui/component/component.hpp" // for Component #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/container.hpp" // for Container #include "ftxui/component/component.hpp" // for Checkbox, Vertical
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
using namespace ftxui; using namespace ftxui;
class MyComponent : public Component {
private:
CheckBox box_1_;
CheckBox box_2_;
CheckBox box_3_;
Container container_ = Container::Vertical();
public:
MyComponent() {
Add(&container_);
container_.Add(&box_1_);
container_.Add(&box_2_);
container_.Add(&box_3_);
box_1_.label = L"Build examples";
box_2_.label = L"Build tests";
box_3_.label = L"Use WebAssembly";
box_3_.state = true;
}
};
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
bool build_examples_state = false;
bool build_tests_state = false;
bool use_webassembly_state = true;
auto component = Container::Vertical({
Checkbox("Build examples", &build_examples_state),
Checkbox("Build tests", &build_tests_state),
Checkbox("Use WebAssembly", &use_webassembly_state),
});
auto screen = ScreenInteractive::TerminalOutput(); auto screen = ScreenInteractive::TerminalOutput();
MyComponent component; screen.Loop(component);
screen.Loop(&component);
return 0; return 0;
} }

View File

@ -1,50 +1,36 @@
#include <memory> // for allocator_traits<>... #include <memory> // for __shared_ptr_access, allocator_traits<>::value_type, shared_ptr
#include <string> // for operator+, wstring #include <string> // for operator+
#include <utility> // for move #include <vector> // for vector
#include <vector> // for vector
#include "ftxui/component/checkbox.hpp" // for CheckBox #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Component #include "ftxui/component/component.hpp" // for Checkbox, Renderer, Vertical
#include "ftxui/component/container.hpp" // for Container #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for Element, operator| #include "ftxui/dom/elements.hpp" // for Element, operator|, size, border, frame, HEIGHT, LESS_THAN
#include "ftxui/screen/box.hpp" // for ftxui #include "ftxui/screen/string.hpp" // for to_wstring
#include "ftxui/screen/string.hpp" // for to_wstring
using namespace ftxui; using namespace ftxui;
class MyComponent : public Component { struct CheckboxState {
public: bool checked;
MyComponent() {
Add(&container);
checkbox.resize(30);
for (int i = 0; i < checkbox.size(); ++i) {
checkbox[i].label = (L"CheckBox " + to_wstring(i));
container.Add(&checkbox[i]);
}
}
// clang-format off
Element Render() override {
Elements content;
for (auto& it : checkbox) {
content.push_back(it.Render());
}
return vbox(std::move(content))
| frame
| size(HEIGHT, LESS_THAN, 10)
| border;
}
private:
std::vector<CheckBox> checkbox;
Container container = Container::Vertical();
}; };
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
int size = 30;
std::vector<CheckboxState> states(size);
auto container = Container::Vertical({});
for (int i = 0; i < size; ++i) {
states[i].checked = false;
container->Add(Checkbox(L"Checkbox" + to_wstring(i), &states[i].checked));
}
auto component = Renderer(container, [&] {
return container->Render() | frame | ftxui::size(HEIGHT, LESS_THAN, 10) |
border;
});
auto screen = ScreenInteractive::FitComponent(); auto screen = ScreenInteractive::FitComponent();
MyComponent component; screen.Loop(component);
screen.Loop(&component);
return 0; return 0;
} }

View File

@ -1,130 +1,126 @@
#include <functional> // for function #include <functional> // for function
#include <memory> // for allocator, unique_ptr #include <memory> // for shared_ptr, allocator, __shared_ptr_access
#include <string> // for wstring #include <string> // for wstring, basic_string
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/button.hpp" // for Button #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/checkbox.hpp" // for CheckBox #include "ftxui/component/component.hpp" // for Slider, Checkbox, Vertical, Renderer, Button, Input, Menu, Radiobox, Toggle
#include "ftxui/component/component.hpp" // for Component, Compone... #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/container.hpp" // for Container #include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/component/input.hpp" // for Input #include "ftxui/dom/elements.hpp" // for separator, Element, operator|, size, xflex, text, WIDTH, hbox, vbox, EQUAL, border, GREATER_THAN
#include "ftxui/component/menu.hpp" // for Menu
#include "ftxui/component/radiobox.hpp" // for RadioBox
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/component/slider.hpp" // for Slider
#include "ftxui/component/toggle.hpp" // for Toggle
#include "ftxui/dom/elements.hpp" // for separator, operator|
#include "ftxui/screen/box.hpp" // for ftxui
using namespace ftxui; using namespace ftxui;
class MyComponent : public Component { // Display a component nicely with a title on the left.
Container container = Container::Vertical(); Component Wrap(std::wstring name, Component component) {
Menu menu; return Renderer(component, [name, component] {
Toggle toggle;
Container checkbox_container = Container::Vertical();
CheckBox checkbox1;
CheckBox checkbox2;
RadioBox radiobox;
Input input;
Button button;
int slider_value_1_ = 12;
int slider_value_2_ = 56;
int slider_value_3_ = 128;
ComponentPtr slider_1_ = Slider(L"R:", &slider_value_1_, 0, 256, 1);
ComponentPtr slider_2_ = Slider(L"G:", &slider_value_2_, 0, 256, 1);
ComponentPtr slider_3_ = Slider(L"B:", &slider_value_3_, 0, 256, 1);
public:
MyComponent() {
Add(&container);
menu.entries = {
L"Menu 1",
L"Menu 2",
L"Menu 3",
L"Menu 4",
};
container.Add(&menu);
toggle.entries = {
L"Toggle_1",
L"Toggle_2",
};
container.Add(&toggle);
container.Add(&checkbox_container);
checkbox1.label = L"checkbox1";
checkbox_container.Add(&checkbox1);
checkbox2.label = L"checkbox2";
checkbox_container.Add(&checkbox2);
radiobox.entries = {
L"Radiobox 1",
L"Radiobox 2",
L"Radiobox 3",
L"Radiobox 4",
};
container.Add(&radiobox);
input.placeholder = L"Input placeholder";
container.Add(&input);
container.Add(slider_1_.get());
container.Add(slider_2_.get());
container.Add(slider_3_.get());
button.label = L"Quit";
button.on_click = [&] { on_quit(); };
container.Add(&button);
}
Element Render(std::wstring name, Element element) {
return hbox({ return hbox({
text(name) | size(WIDTH, EQUAL, 8), text(name) | size(WIDTH, EQUAL, 8),
separator(), separator(),
element | xflex, component->Render() | xflex,
}) | }) |
xflex; xflex;
} });
}
Element Render(std::wstring name, Component& component) {
return Render(name, component.Render());
}
Element Render() override {
return //
vbox({
Render(L"menu", menu),
separator(),
Render(L"toggle", toggle),
separator(),
Render(L"checkbox", checkbox_container),
separator(),
Render(L"radiobox", radiobox),
separator(),
Render(L"input", input) | size(WIDTH, LESS_THAN, 50),
separator(),
Render(L"slider", //
vbox({
slider_1_->Render(),
slider_2_->Render(),
slider_3_->Render(),
})),
separator(),
Render(L"button", button),
}) |
xflex | size(WIDTH, GREATER_THAN, 40) | border;
}
std::function<void()> on_quit = [] {};
};
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::FitComponent(); auto screen = ScreenInteractive::FitComponent();
MyComponent component;
component.on_quit = screen.ExitLoopClosure(); // -- Menu
screen.Loop(&component); // ----------------------------------------------------------------------
const std::vector<std::wstring> menu_entries = {
L"Menu 1",
L"Menu 2",
L"Menu 3",
L"Menu 4",
};
int menu_selected = 0;
auto menu = Menu(&menu_entries, &menu_selected);
menu = Wrap(L"Menu", menu);
// -- Toggle------------------------------------------------------------------
int toggle_selected = 0;
std::vector<std::wstring> toggle_entries = {
L"Toggle_1",
L"Toggle_2",
};
auto toggle = Toggle(&toggle_entries, &toggle_selected);
toggle = Wrap(L"Toggle", toggle);
// -- Checkbox ---------------------------------------------------------------
bool checkbox_1_selected = false;
bool checkbox_2_selected = false;
auto checkboxes = Container::Vertical({
Checkbox("checkbox1", &checkbox_1_selected),
Checkbox("checkbox2", &checkbox_2_selected),
});
checkboxes = Wrap(L"Checkbox", checkboxes);
// -- Radiobox ---------------------------------------------------------------
int radiobox_selected = 0;
std::vector<std::wstring> radiobox_entries = {
L"Radiobox 1",
L"Radiobox 2",
L"Radiobox 3",
L"Radiobox 4",
};
auto radiobox = Radiobox(&radiobox_entries, &radiobox_selected);
radiobox = Wrap(L"Radiobox", radiobox);
// -- Input ------------------------------------------------------------------
std::wstring input_label;
auto input = Input(&input_label, L"placeholder");
input = Wrap(L"Input", input);
// -- Button -----------------------------------------------------------------
std::wstring button_label = L"Quit";
std::function<void()> on_button_clicked_;
auto button = Button(&button_label, screen.ExitLoopClosure());
button = Wrap(L"Button", button);
// -- Slider -----------------------------------------------------------------
int slider_value_1 = 12;
int slider_value_2 = 56;
int slider_value_3 = 128;
auto sliders = Container::Vertical({
Slider(L"R:", &slider_value_1, 0, 256, 1),
Slider(L"G:", &slider_value_2, 0, 256, 1),
Slider(L"B:", &slider_value_3, 0, 256, 1),
});
sliders = Wrap(L"Slider", sliders);
// -- Layout -----------------------------------------------------------------
auto layout = Container::Vertical({
menu,
toggle,
checkboxes,
radiobox,
input,
sliders,
button,
});
auto component = Renderer(layout, [&] {
return vbox({
menu->Render(),
separator(),
toggle->Render(),
separator(),
checkboxes->Render(),
separator(),
radiobox->Render(),
separator(),
input->Render(),
separator(),
sliders->Render(),
separator(),
button->Render(),
}) |
xflex | size(WIDTH, GREATER_THAN, 40) | border;
});
screen.Loop(component);
return 0; return 0;
} }

View File

@ -1,52 +1,50 @@
#include <chrono> // for operator""s, chron... #include <array> // for array
#include <chrono> // for operator""s, chrono_literals
#include <cmath> // for sin #include <cmath> // for sin
#include <functional> // for ref, reference_wra... #include <functional> // for ref, reference_wrapper, function
#include <string> // for allocator, wstring #include <memory> // for allocator, shared_ptr, __shared_ptr_access
#include <string> // for wstring, basic_string, operator+, to_wstring
#include <thread> // for sleep_for, thread #include <thread> // for sleep_for, thread
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/checkbox.hpp" // for CheckBox #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Component #include "ftxui/component/component.hpp" // for Checkbox, Renderer, Horizontal, Vertical, Input, Menu, Radiobox, Tab, Toggle
#include "ftxui/component/container.hpp" // for Container #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/event.hpp" // for Event, Event::Custom #include "ftxui/component/event.hpp" // for Event, Event::Custom
#include "ftxui/component/input.hpp" // for Input #include "ftxui/component/input.hpp" // for InputBase
#include "ftxui/component/menu.hpp" // for Menu #include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/component/radiobox.hpp" // for RadioBox #include "ftxui/dom/elements.hpp" // for text, operator|, color, bgcolor, Element, filler, size, vbox, flex, hbox, graph, separator, EQUAL, WIDTH, hcenter, bold, border, window, HEIGHT, Elements, hflow, flex_grow, frame, gauge, LESS_THAN, spinner, dim, GREATER_THAN
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/screen/color.hpp" // for Color, Color::BlueLight, Color::RedLight, Color::Black, Color::Blue, Color::Cyan, Color::CyanLight, Color::GrayDark, Color::GrayLight, Color::Green, Color::GreenLight, Color::Magenta, Color::MagentaLight, Color::Red, Color::White, Color::Yellow, Color::YellowLight, Color::Default
#include "ftxui/component/toggle.hpp" // for Toggle
#include "ftxui/dom/elements.hpp" // for text, operator|
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/color.hpp" // for Color, Color::Blue...
using namespace ftxui; using namespace ftxui;
int shift = 0; int main(int argc, const char* argv[]) {
class Graph { auto screen = ScreenInteractive::Fullscreen();
public:
std::vector<int> operator()(int width, int height) { int shift = 0;
std::vector<int> output(width);
for (int i = 0; i < width; ++i) { class Graph {
float v = 0; public:
v += 0.1f * sin((i + shift) * 0.1f); Graph(int* shift) : shift_(shift) {}
v += 0.2f * sin((i + shift + 10) * 0.15f); std::vector<int> operator()(int width, int height) {
v += 0.1f * sin((i + shift) * 0.03f); std::vector<int> output(width);
v *= height; for (int i = 0; i < width; ++i) {
v += 0.5f * height; float v = 0;
output[i] = (int)v; v += 0.1f * sin((i + *shift_) * 0.1f);
v += 0.2f * sin((i + *shift_ + 10) * 0.15f);
v += 0.1f * sin((i + *shift_) * 0.03f);
v *= height;
v += 0.5f * height;
output[i] = (int)v;
}
return output;
} }
return output; int* shift_;
} };
};
class HTopComponent : public Component { Graph my_graph(&shift);
Graph my_graph; auto htop = Renderer([&] {
public:
HTopComponent() {}
~HTopComponent() override {}
Element Render() override {
auto frequency = vbox({ auto frequency = vbox({
text(L"Frequency [Mhz]") | hcenter, text(L"Frequency [Mhz]") | hcenter,
hbox({ hbox({
@ -99,113 +97,140 @@ class HTopComponent : public Component {
ram | flex, ram | flex,
}) | }) |
flex | border; flex | border;
} });
};
class CompilerComponent : public Component { const std::vector<std::wstring> compiler_entries = {
Container container = Container::Horizontal(); L"gcc",
RadioBox compiler; L"clang",
Container flag = Container::Vertical(); L"emcc",
CheckBox flag_checkbox[4]; L"game_maker",
Container subcontainer = Container::Vertical(); L"Ada compilers",
Container input_container = Container::Horizontal(); L"ALGOL 60 compilers",
Input input_add; L"ALGOL 68 compilers",
Menu input; L"Assemblers (Intel *86)",
Input executable; L"Assemblers (Motorola 68*)",
L"Assemblers (Zilog Z80)",
L"Assemblers (other)",
L"BASIC Compilers",
L"BASIC interpreters",
L"Batch compilers",
L"C compilers",
L"Source-to-source compilers",
L"C++ compilers",
L"C# compilers",
L"COBOL compilers",
L"Common Lisp compilers",
L"D compilers",
L"DIBOL/DBL compilers",
L"ECMAScript interpreters",
L"Eiffel compilers",
L"Fortran compilers",
L"Go compilers",
L"Haskell compilers",
L"Java compilers",
L"Pascal compilers",
L"Perl Interpreters",
L"PHP compilers",
L"PL/I compilers",
L"Python compilers",
L"Scheme compilers and interpreters",
L"Smalltalk compilers",
L"Tcl Interpreters",
L"VMS Interpreters",
L"Rexx Interpreters",
L"CLI compilers",
};
public: int compiler_selected = 0;
~CompilerComponent() override {} Component compiler = Radiobox(&compiler_entries, &compiler_selected);
CompilerComponent() {
Add(&container);
// Compiler ---------------------------------------------------------------- std::array<std::wstring, 4> options_label = {
compiler.entries = { L"-Wall",
L"gcc", L"-Werror",
L"clang", L"-lpthread",
L"emcc", L"-O3",
L"game_maker", };
L"Ada compilers", std::array<bool, 4> options_state = {
L"ALGOL 60 compilers", false,
L"ALGOL 68 compilers", false,
L"Assemblers (Intel *86)", false,
L"Assemblers (Motorola 68*)", false,
L"Assemblers (Zilog Z80)", };
L"Assemblers (other)", std::wstring input_add_content;
L"BASIC Compilers", Component input_add = Input(&input_add_content, "input files");
L"BASIC interpreters",
L"Batch compilers",
L"C compilers",
L"Source-to-source compilers",
L"C++ compilers",
L"C# compilers",
L"COBOL compilers",
L"Common Lisp compilers",
L"D compilers",
L"DIBOL/DBL compilers",
L"ECMAScript interpreters",
L"Eiffel compilers",
L"Fortran compilers",
L"Go compilers",
L"Haskell compilers",
L"Java compilers",
L"Pascal compilers",
L"Perl Interpreters",
L"PHP compilers",
L"PL/I compilers",
L"Python compilers",
L"Scheme compilers and interpreters",
L"Smalltalk compilers",
L"Tcl Interpreters",
L"VMS Interpreters",
L"Rexx Interpreters",
L"CLI compilers",
};
container.Add(&compiler);
// Flags ---------------------------------------------------------------- std::vector<std::wstring> input_entries;
container.Add(&flag); int input_selected = 0;
flag_checkbox[0].label = L"-Wall"; Component input = Menu(&input_entries, &input_selected);
flag_checkbox[1].label = L"-Werror";
flag_checkbox[2].label = L"-lpthread";
flag_checkbox[3].label = L"-O3";
for (auto& c : flag_checkbox)
flag.Add(&c);
container.Add(&subcontainer); std::wstring executable_content_ = L"";
Component executable_ = Input(&executable_content_, "executable");
Component flags = Container::Vertical({
Checkbox(&options_label[0], &options_state[0]),
Checkbox(&options_label[1], &options_state[1]),
Checkbox(&options_label[2], &options_state[2]),
Checkbox(&options_label[3], &options_state[3]),
});
auto compiler_component = Container::Horizontal({
compiler,
flags,
Container::Vertical({
executable_,
Container::Horizontal({
input_add,
input,
}),
}),
});
InputBase::From(input_add)->on_enter = [&] {
input_entries.push_back(input_add_content);
input_add_content = L"";
};
auto render_command = [&] {
Elements line;
// Compiler
line.push_back(text(compiler_entries[compiler_selected]) | bold);
// flags
for (int i = 0; i < 4; ++i) {
if (options_state[i]) {
line.push_back(text(L" "));
line.push_back(text(options_label[i]) | dim);
}
}
// Executable // Executable
// ---------------------------------------------------------------- if (!executable_content_.empty()) {
executable.placeholder = L"executable"; line.push_back(text(L" -O ") | bold);
subcontainer.Add(&executable); line.push_back(text(executable_content_) | color(Color::BlueLight) |
bold);
}
// Input
for (auto& it : input_entries) {
line.push_back(text(L" " + it) | color(Color::RedLight));
}
return line;
};
// Input ---------------------------------------------------------------- auto compiler_renderer = Renderer(compiler_component, [&] {
subcontainer.Add(&input_container); auto compiler_win = window(text(L"Compiler"), compiler->Render() | frame);
auto flags_win = window(text(L"Flags"), flags->Render());
input_add.placeholder = L"input files"; auto executable_win = window(text(L"Executable:"), executable_->Render());
input_add.on_enter = [this] {
input.entries.push_back(input_add.content);
input_add.content = L"";
};
input_container.Add(&input_add);
input_container.Add(&input);
}
Element Render() override {
auto compiler_win = window(text(L"Compiler"), compiler.Render() | frame);
auto flags_win = window(text(L"Flags"), flag.Render());
auto executable_win = window(text(L"Executable:"), executable.Render());
auto input_win = auto input_win =
window(text(L"Input"), window(text(L"Input"),
hbox({ hbox({
vbox({ vbox({
hbox({ hbox({
text(L"Add: "), text(L"Add: "),
input_add.Render(), input_add->Render(),
}) | size(WIDTH, EQUAL, 20) | }) | size(WIDTH, EQUAL, 20) |
size(HEIGHT, EQUAL, 1), size(HEIGHT, EQUAL, 1),
filler(), filler(),
}), }),
separator(), separator(),
input.Render() | frame | size(HEIGHT, EQUAL, 3) | flex, input->Render() | frame | size(HEIGHT, EQUAL, 3) | flex,
})); }));
return vbox({ return vbox({
hbox({ hbox({
@ -217,49 +242,21 @@ class CompilerComponent : public Component {
}), }),
filler(), filler(),
}), }),
hflow(RenderCommandLine()) | flex_grow, hflow(render_command()) | flex_grow,
}) | }) |
flex_grow | border; flex_grow | border;
} });
Elements RenderCommandLine() { auto spinner_tab_renderer = Renderer([&] {
Elements line;
// Compiler
line.push_back(text(compiler.entries[compiler.selected]) | bold);
// flags
for (auto& it : flag_checkbox) {
if (it.state) {
line.push_back(text(L" "));
line.push_back(text(it.label) | dim);
}
}
// Executable
if (!executable.content.empty()) {
line.push_back(text(L" -O ") | bold);
line.push_back(text(executable.content) | color(Color::BlueLight) | bold);
}
// Input
for (auto& it : input.entries) {
line.push_back(text(L" " + it) | color(Color::RedLight));
}
return line;
}
};
class SpinnerComponent : public Component {
Element Render() override {
Elements entries; Elements entries;
for (int i = 0; i < 22; ++i) { for (int i = 0; i < 22; ++i) {
if (i != 0) entries.push_back(spinner(i, shift / 2) | bold |
entries.push_back(spinner(i, shift / 2) | bold | size(WIDTH, GREATER_THAN, 2) | border);
size(WIDTH, GREATER_THAN, 2) | border);
} }
return hflow(std::move(entries)) | border; return hflow(std::move(entries)) | border;
} });
};
class ColorComponent : public Component { auto color_tab_renderer = Renderer([] {
Element Render() override {
return hbox({ return hbox({
vbox({ vbox({
color(Color::Default, text(L"Default")), color(Color::Default, text(L"Default")),
@ -301,82 +298,67 @@ class ColorComponent : public Component {
}), }),
}) | }) |
hcenter | border; hcenter | border;
} });
};
class GaugeComponent : public Component { auto render_gauge = [&shift](int delta) {
Element RenderGauge(int delta) {
float progress = (shift + delta) % 1000 / 1000.f; float progress = (shift + delta) % 1000 / 1000.f;
return hbox({ return hbox({
text(std::to_wstring(int(progress * 100)) + L"% ") | text(std::to_wstring(int(progress * 100)) + L"% ") |
size(WIDTH, EQUAL, 5), size(WIDTH, EQUAL, 5),
gauge(progress), gauge(progress),
}); });
} };
Element Render() override {
auto gauge_component = Renderer([render_gauge] {
return vbox({ return vbox({
RenderGauge(0) | color(Color::Black), render_gauge(0) | color(Color::Black),
RenderGauge(100) | color(Color::GrayDark), render_gauge(100) | color(Color::GrayDark),
RenderGauge(50) | color(Color::GrayLight), render_gauge(50) | color(Color::GrayLight),
RenderGauge(6894) | color(Color::White), render_gauge(6894) | color(Color::White),
separator(), separator(),
RenderGauge(6841) | color(Color::Blue), render_gauge(6841) | color(Color::Blue),
RenderGauge(9813) | color(Color::BlueLight), render_gauge(9813) | color(Color::BlueLight),
RenderGauge(98765) | color(Color::Cyan), render_gauge(98765) | color(Color::Cyan),
RenderGauge(98) | color(Color::CyanLight), render_gauge(98) | color(Color::CyanLight),
RenderGauge(9846) | color(Color::Green), render_gauge(9846) | color(Color::Green),
RenderGauge(1122) | color(Color::GreenLight), render_gauge(1122) | color(Color::GreenLight),
RenderGauge(84) | color(Color::Magenta), render_gauge(84) | color(Color::Magenta),
RenderGauge(645) | color(Color::MagentaLight), render_gauge(645) | color(Color::MagentaLight),
RenderGauge(568) | color(Color::Red), render_gauge(568) | color(Color::Red),
RenderGauge(2222) | color(Color::RedLight), render_gauge(2222) | color(Color::RedLight),
RenderGauge(220) | color(Color::Yellow), render_gauge(220) | color(Color::Yellow),
RenderGauge(348) | color(Color::YellowLight), render_gauge(348) | color(Color::YellowLight),
}) | }) |
border; border;
});
int tab_index = 0;
std::vector<std::wstring> tab_entries = {
L"htop", L"color", L"spinner", L"gauge", L"compiler",
}; };
}; auto tab_selection = Toggle(&tab_entries, &tab_index);
auto tab_content = Container::Tab(&tab_index, {
htop,
color_tab_renderer,
spinner_tab_renderer,
gauge_component,
compiler_renderer,
});
class Tab : public Component { auto main_container = Container::Vertical({
public: tab_selection,
Container main_container = Container::Vertical(); tab_content,
});
Toggle tab_selection; auto main_renderer = Renderer(main_container, [&] {
Container container = Container::Tab(&tab_selection.selected);
HTopComponent htop_component;
ColorComponent color_component;
SpinnerComponent spinner_component;
GaugeComponent gauge_component;
CompilerComponent compiler_component;
Tab() {
Add(&main_container);
main_container.Add(&tab_selection);
tab_selection.entries = {
L"htop", L"color", L"spinner", L"gauge", L"compiler",
};
main_container.Add(&container);
container.Add(&htop_component);
container.Add(&color_component);
container.Add(&spinner_component);
container.Add(&gauge_component);
container.Add(&compiler_component);
}
Element Render() override {
return vbox({ return vbox({
text(L"FTXUI Demo") | bold | hcenter, text(L"FTXUI Demo") | bold | hcenter,
tab_selection.Render() | hcenter, tab_selection->Render() | hcenter,
container.Render() | flex, tab_content->Render() | flex,
}); });
} });
};
int main(int argc, const char* argv[]) { std::thread update([&screen, &shift]() {
auto screen = ScreenInteractive::Fullscreen();
std::thread update([&screen]() {
for (;;) { for (;;) {
using namespace std::chrono_literals; using namespace std::chrono_literals;
std::this_thread::sleep_for(0.05s); std::this_thread::sleep_for(0.05s);
@ -385,8 +367,7 @@ int main(int argc, const char* argv[]) {
} }
}); });
Tab tab; screen.Loop(main_renderer);
screen.Loop(&tab);
return 0; return 0;
} }

View File

@ -1,44 +1,38 @@
#include "ftxui/component/input.hpp" #include <memory> // for allocator, __shared_ptr_access
#include "ftxui/component/container.hpp" // for Container #include <string> // for operator+, char_traits, wstring
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
using namespace ftxui; #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Input, Renderer, Vertical
class MyComponent : public Component { #include "ftxui/component/component_base.hpp" // for ComponentBase
public: #include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
MyComponent() { #include "ftxui/dom/elements.hpp" // for text, hbox, Element, separator, operator|, vbox, border
Add(&container);
container.Add(&input_1);
container.Add(&input_2);
container.Add(&input_3);
input_1.placeholder = L"input1";
input_2.placeholder = L"input2";
input_3.placeholder = L"input3";
}
std::function<void()> on_enter = []() {};
private:
Container container = Container::Vertical();
Input input_1;
Input input_2;
Input input_3;
Element Render() override {
return border(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[]) { int main(int argc, const char* argv[]) {
using namespace ftxui;
std::wstring first_name_;
std::wstring last_name_;
Component input_first_name_ = Input(&first_name_, "first name");
Component input_last_name_ = Input(&last_name_, "last name");
auto component = Container::Vertical({
input_first_name_,
input_last_name_,
});
auto renderer = Renderer(component, [&] {
return vbox({
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()}),
}) |
border;
});
auto screen = ScreenInteractive::TerminalOutput(); auto screen = ScreenInteractive::TerminalOutput();
MyComponent component; screen.Loop(renderer);
component.on_enter = screen.ExitLoopClosure();
screen.Loop(&component);
} }
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -1,24 +1,30 @@
#include <functional> // for function #include <functional> // for function
#include <iostream> // for basic_ostream::ope... #include <iostream> // for basic_ostream::operator<<, operator<<, endl, basic_ostream, basic_ostream<>::__ostream_type, cout, ostream
#include <string> // for wstring, allocator #include <string> // for wstring, allocator, basic_string
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/menu.hpp" // for Menu #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Menu
#include "ftxui/component/menu.hpp" // for MenuBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/screen/box.hpp" // for ftxui
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
using namespace ftxui; using namespace ftxui;
auto screen = ScreenInteractive::TerminalOutput(); auto screen = ScreenInteractive::TerminalOutput();
Menu menu; std::vector<std::wstring> entries = {
menu.entries = {L"entry 1", L"entry 2", L"entry 3"}; L"entry 1",
menu.selected = 0; L"entry 2",
menu.on_enter = screen.ExitLoopClosure(); L"entry 3",
};
int selected = 0;
screen.Loop(&menu); auto menu = Menu(&entries, &selected);
MenuBase::From(menu)->on_enter = screen.ExitLoopClosure();
std::cout << "Selected element = " << menu.selected << std::endl; screen.Loop(menu);
std::cout << "Selected element = " << selected << std::endl;
} }
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -1,84 +1,77 @@
#include <functional> // for function #include <functional> // for function
#include <string> // for wstring, allocator #include <memory> // for allocator, __shared_ptr_access
#include <string> // for wstring, operator+, to_string, basic_string
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/component.hpp" // for Component #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/container.hpp" // for Container #include "ftxui/component/component.hpp" // for Menu, Horizontal, Renderer
#include "ftxui/component/menu.hpp" // for Menu #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/component/menu.hpp" // for MenuBase
#include "ftxui/dom/elements.hpp" // for text, separator, bold #include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/screen/box.hpp" // for ftxui #include "ftxui/dom/elements.hpp" // for text, separator, bold, hcenter, vbox, hbox, gauge, Element, operator|, border
#include "ftxui/screen/string.hpp" // for to_wstring #include "ftxui/screen/string.hpp" // for to_wstring
using namespace ftxui;
class MyComponent : public Component {
public:
MyComponent() {
Add(&container);
container.Add(&left_menu);
container.Add(&right_menu);
left_menu.entries = {
L"0%", L"10%", L"20%", L"30%", L"40%",
L"50%", L"60%", L"70%", L"80%", L"90%",
};
right_menu.entries = {
L"0%", L"1%", L"2%", L"3%", L"4%", L"5%",
L"6%", L"7%", L"8%", L"9%", L"10%",
};
left_menu.on_enter = [this]() { on_enter(); };
right_menu.on_enter = [this]() { on_enter(); };
}
std::function<void()> on_enter = []() {};
private:
Container container = Container::Horizontal();
Menu left_menu;
Menu right_menu;
Element Render() override {
int sum = left_menu.selected * 10 + right_menu.selected;
return border(vbox({
// -------- Top panel --------------
hbox({
// -------- Left Menu --------------
vbox({
hcenter(bold(text(L"Percentage by 10%"))),
separator(),
left_menu.Render(),
}) | flex,
// -------- Right Menu --------------
vbox({
hcenter(bold(text(L"Percentage by 1%"))),
separator(),
right_menu.Render(),
}) | flex,
filler(),
}),
separator(),
// -------- Bottom panel --------------
vbox({
hbox({
text(L" gauge : "),
gauge(sum / 100.0),
}),
hbox({
text(L" text : "),
text(to_wstring(std::to_string(sum) + " %")),
}),
}) | flex,
}));
}
};
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
using namespace ftxui;
std::vector<std::wstring> left_menu_entries = {
L"0%", L"10%", L"20%", L"30%", L"40%",
L"50%", L"60%", L"70%", L"80%", L"90%",
};
std::vector<std::wstring> right_menu_entries = {
L"0%", L"1%", L"2%", L"3%", L"4%", L"5%",
L"6%", L"7%", L"8%", L"9%", L"10%",
};
int left_menu_selected = 0;
int right_menu_selected = 0;
Component left_menu_ = Menu(&left_menu_entries, &left_menu_selected);
Component right_menu_ = Menu(&right_menu_entries, &right_menu_selected);
Component container = Container::Horizontal({
left_menu_,
right_menu_,
});
auto renderer = Renderer(container, [&] {
int sum = left_menu_selected * 10 + right_menu_selected;
return vbox({
// -------- Top panel --------------
hbox({
// -------- Left Menu --------------
vbox({
hcenter(bold(text(L"Percentage by 10%"))),
separator(),
left_menu_->Render(),
}),
separator(),
// -------- Right Menu --------------
vbox({
hcenter(bold(text(L"Percentage by 1%"))),
separator(),
right_menu_->Render(),
}),
separator(),
}),
separator(),
// -------- Bottom panel --------------
vbox({
hbox({
text(L" gauge : "),
gauge(sum / 100.0),
}),
hbox({
text(L" text : "),
text(to_wstring(std::to_string(sum) + " %")),
}),
}),
}) |
border;
});
auto screen = ScreenInteractive::TerminalOutput(); auto screen = ScreenInteractive::TerminalOutput();
MyComponent component; MenuBase::From(left_menu_)->on_enter = screen.ExitLoopClosure();
component.on_enter = screen.ExitLoopClosure(); MenuBase::From(right_menu_)->on_enter = screen.ExitLoopClosure();
screen.Loop(&component); screen.Loop(renderer);
} }
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -1,92 +1,83 @@
#include <functional> // for function #include <functional> // for function
#include <initializer_list> // for initializer_list #include <initializer_list> // for initializer_list
#include <string> // for wstring, allocator #include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for wstring, basic_string
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/component.hpp" // for Component #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/container.hpp" // for Container #include "ftxui/component/component.hpp" // for Menu, Horizontal, Renderer
#include "ftxui/component/menu.hpp" // for Menu #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/component/menu.hpp" // for MenuBase
#include "ftxui/dom/elements.hpp" // for operator|, Element #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive, Component
#include "ftxui/screen/box.hpp" // for ftxui #include "ftxui/dom/elements.hpp" // for operator|, Element, separator, bgcolor, color, flex, Decorator, bold, hbox, border, dim
#include "ftxui/screen/color.hpp" // for Color, Color::Blue #include "ftxui/screen/color.hpp" // for Color, Color::Blue, Color::BlueLight, Color::Red, Color::Yellow
using namespace ftxui;
class MyComponent : public Component {
public:
MyComponent() {
Add(&container);
for (Menu* menu : {
&menu_1,
&menu_2,
&menu_3,
&menu_4,
&menu_5,
&menu_6,
}) {
container.Add(menu);
menu->entries = {
L"Monkey", L"Dog", L"Cat", L"Bird", L"Elephant",
};
menu->on_enter = [this]() { on_enter(); };
}
menu_2.focused_style = bold | color(Color::Blue);
menu_2.selected_style = color(Color::Blue);
menu_2.selected_focused_style = bold | color(Color::Blue);
menu_3.selected_style = color(Color::Blue);
menu_3.focused_style = bgcolor(Color::Blue);
menu_3.selected_focused_style = bgcolor(Color::Blue);
menu_4.selected_style = bgcolor(Color::Blue);
menu_4.focused_style = bgcolor(Color::BlueLight);
menu_4.selected_focused_style = bgcolor(Color::BlueLight);
menu_5.normal_style = bgcolor(Color::Blue);
menu_5.selected_style = bgcolor(Color::Yellow);
menu_5.focused_style = bgcolor(Color::Red);
menu_5.selected_focused_style = bgcolor(Color::Red);
menu_6.normal_style = dim | color(Color::Blue);
menu_6.selected_style = color(Color::Blue);
menu_6.focused_style = bold | color(Color::Blue);
menu_6.selected_focused_style = bold | color(Color::Blue);
}
std::function<void()> on_enter = []() {};
private:
Container container = Container::Horizontal();
Menu menu_1;
Menu menu_2;
Menu menu_3;
Menu menu_4;
Menu menu_5;
Menu menu_6;
// clang-format off
Element Render() override {
return
hbox({
menu_1.Render() | flex, separator(),
menu_2.Render() | flex, separator(),
menu_3.Render() | flex, separator(),
menu_4.Render() | flex, separator(),
menu_5.Render() | flex, separator(),
menu_6.Render() | flex,
}) | border;
}
// clang-format on
};
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
using namespace ftxui;
std::vector<std::wstring> entries = {
L"Monkey", L"Dog", L"Cat", L"Bird", L"Elephant",
};
int menu_1_selected_ = 0;
int menu_2_selected_ = 0;
int menu_3_selected_ = 0;
int menu_4_selected_ = 0;
int menu_5_selected_ = 0;
int menu_6_selected_ = 0;
auto menu_1_ = Menu(&entries, &menu_1_selected_);
auto menu_2_ = Menu(&entries, &menu_2_selected_);
auto menu_3_ = Menu(&entries, &menu_3_selected_);
auto menu_4_ = Menu(&entries, &menu_4_selected_);
auto menu_5_ = Menu(&entries, &menu_5_selected_);
auto menu_6_ = Menu(&entries, &menu_6_selected_);
auto container = Container::Horizontal({
menu_1_,
menu_2_,
menu_3_,
menu_4_,
menu_5_,
menu_6_,
});
// clang-format off
auto renderer = Renderer(container, [&] {
return
hbox({
menu_1_->Render() | flex, separator(),
menu_2_->Render() | flex, separator(),
menu_3_->Render() | flex, separator(),
menu_4_->Render() | flex, separator(),
menu_5_->Render() | flex, separator(),
menu_6_->Render() | flex,
}) | border;
});
// clang-format on
auto screen = ScreenInteractive::TerminalOutput(); auto screen = ScreenInteractive::TerminalOutput();
MyComponent component; for (Component menu : {menu_1_, menu_2_, menu_3_, menu_4_, menu_5_, menu_6_})
component.on_enter = screen.ExitLoopClosure(); MenuBase::From(menu)->on_enter = screen.ExitLoopClosure();
screen.Loop(&component);
MenuBase::From(menu_2_)->focused_style = bold | color(Color::Blue);
MenuBase::From(menu_2_)->selected_style = color(Color::Blue);
MenuBase::From(menu_2_)->selected_focused_style = bold | color(Color::Blue);
MenuBase::From(menu_3_)->selected_style = color(Color::Blue);
MenuBase::From(menu_3_)->focused_style = bgcolor(Color::Blue);
MenuBase::From(menu_3_)->selected_focused_style = bgcolor(Color::Blue);
MenuBase::From(menu_4_)->selected_style = bgcolor(Color::Blue);
MenuBase::From(menu_4_)->focused_style = bgcolor(Color::BlueLight);
MenuBase::From(menu_4_)->selected_focused_style = bgcolor(Color::BlueLight);
MenuBase::From(menu_5_)->normal_style = bgcolor(Color::Blue);
MenuBase::From(menu_5_)->selected_style = bgcolor(Color::Yellow);
MenuBase::From(menu_5_)->focused_style = bgcolor(Color::Red);
MenuBase::From(menu_5_)->selected_focused_style = bgcolor(Color::Red);
MenuBase::From(menu_6_)->normal_style = dim | color(Color::Blue);
MenuBase::From(menu_6_)->selected_style = color(Color::Blue);
MenuBase::From(menu_6_)->focused_style = bold | color(Color::Blue);
MenuBase::From(menu_6_)->selected_focused_style = bold | color(Color::Blue);
screen.Loop(renderer);
} }
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -1,132 +1,89 @@
#include <functional> // for function #include <memory> // for allocator, __shared_ptr_access, shared_ptr
#include <memory> // for allocator_traits<>... #include <string> // for wstring, operator+, basic_string, char_traits
#include <string> // for operator+, wstring #include <vector> // for vector
#include <vector> // for vector
#include "ftxui/component/button.hpp" // for Button #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Component #include "ftxui/component/component.hpp" // for Button, Renderer, Horizontal, Tab
#include "ftxui/component/container.hpp" // for Container #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for Element, operator| #include "ftxui/dom/elements.hpp" // for Element, operator|, filler, text, hbox, separator, center, vbox, bold, border, clear_under, dbox, size, GREATER_THAN, HEIGHT
#include "ftxui/screen/box.hpp" // for ftxui
using namespace ftxui; int main(int argc, const char* argv[]) {
using namespace ftxui;
auto screen = ScreenInteractive::TerminalOutput();
// The main screen, at depth 0. It display the main content. // There are two layers. One at depth = 0 and the modal window at depth = 1;
class Content : public Component { int depth = 0;
public:
std::function<void()> on_rate_ftxui = [] {};
std::function<void()> on_quit = [] {};
std::wstring rating_ = L"3/5 stars";
Content() {
Add(&container_);
container_.Add(&button_rate_ftxui);
container_.Add(&button_quit_);
button_rate_ftxui.on_click = [&] { on_rate_ftxui(); };
button_quit_.on_click = [&] { on_quit(); };
}
Element Render() final { // The current rating of FTXUI.
auto button_elements = hbox({ std::wstring rating = L"3/5 stars";
button_rate_ftxui.Render(),
filler(),
button_quit_.Render(),
});
auto document = // // At depth=0, two buttons. One for rating FTXUI and one for quitting.
vbox({ auto button_rate_ftxui = Button("Rate FTXUI", [&] { depth = 1; });
text(L"Modal dialog example"), auto button_quit = Button("Quit", screen.ExitLoopClosure());
separator(),
text(L"☆☆☆ FTXUI:" + rating_ + L" ☆☆☆") | bold,
filler(),
button_elements,
}) |
border;
return document | size(HEIGHT, GREATER_THAN, 18) | center; auto depth_0_container = Container::Horizontal({
} button_rate_ftxui,
button_quit,
});
auto depth_0_renderer = Renderer(depth_0_container, [&] {
return vbox({
text(L"Modal dialog example"),
separator(),
text(L"☆☆☆ FTXUI:" + rating + L" ☆☆☆") | bold,
filler(),
hbox({
button_rate_ftxui->Render(),
filler(),
button_quit->Render(),
}),
}) |
border | size(HEIGHT, GREATER_THAN, 18) | center;
});
private: // At depth=1, The "modal" window.
Container container_ = Container::Horizontal(); std::vector<std::wstring> rating_labels = {
Button button_rate_ftxui = Button(L"Rate FTXUI"); L"1/5 stars", L"2/5 stars", L"3/5 stars", L"4/5 stars", L"5/5 stars",
Button button_quit_ = Button(L"Quit"); };
}; auto on_rating = [&](std::wstring new_rating) {
rating = new_rating;
depth = 0;
};
auto depth_1_container = Container::Horizontal({
Button(&rating_labels[0], [&] { on_rating(rating_labels[0]); }),
Button(&rating_labels[1], [&] { on_rating(rating_labels[1]); }),
Button(&rating_labels[2], [&] { on_rating(rating_labels[2]); }),
Button(&rating_labels[3], [&] { on_rating(rating_labels[3]); }),
Button(&rating_labels[4], [&] { on_rating(rating_labels[4]); }),
});
// The "modal" screen, at depth 1. It display the modal dialog. auto depth_1_renderer = Renderer(depth_1_container, [&] {
class Modal : public Component {
public:
std::function<void(std::wstring)> on_click;
Modal() {
Add(&container_);
buttons_.resize(5);
for (int i = 0; i < 5; ++i) {
std::wstring stars = std::to_wstring(i + 1) + L"/5 stars";
buttons_[i] = Button(stars);
buttons_[i].on_click = [&, stars] { on_click(stars); };
container_.Add(&buttons_[i]);
}
}
Element Render() final {
return vbox({ return vbox({
text(L"Do you like FTXUI?"), text(L"Do you like FTXUI?"),
separator(), separator(),
hbox({ hbox(depth_1_container->Render()),
buttons_[0].Render(),
buttons_[1].Render(),
buttons_[2].Render(),
buttons_[3].Render(),
buttons_[4].Render(),
}),
}) | }) |
border; border;
} });
private: auto main_container = Container::Tab(&depth, {
Container container_ = Container::Horizontal(); depth_0_renderer,
std::vector<Button> buttons_; depth_1_renderer,
}; });
class MyComponent : public Component { auto main_renderer = Renderer(main_container, [&] {
public: Element document = depth_0_renderer->Render();
std::function<void()> on_quit = [] {};
MyComponent() { if (depth == 1) {
Add(&container_);
container_.Add(&content_);
container_.Add(&modal_);
content_.on_quit = [&] { on_quit(); };
content_.on_rate_ftxui = [&] { modal_.TakeFocus(); };
modal_.on_click = [&](std::wstring rating) {
content_.rating_ = rating;
content_.TakeFocus();
};
}
Element Render() final {
Element document = content_.Render();
if (modal_.Focused()) {
document = dbox({ document = dbox({
document, document,
modal_.Render() | clear_under | center, depth_1_renderer->Render() | clear_under | center,
}); });
} }
return document; return document;
} });
private: screen.Loop(main_renderer);
Container container_ = Container::Tab(nullptr);
Content content_;
Modal modal_;
};
int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::TerminalOutput();
MyComponent my_component;
my_component.on_quit = screen.ExitLoopClosure();
screen.Loop(&my_component);
return 0; return 0;
} }

View File

@ -1,18 +1,23 @@
#include "ftxui/component/radiobox.hpp" #include <string> // for wstring, allocator, basic_string
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Radiobox
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
using namespace ftxui; using namespace ftxui;
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::TerminalOutput(); std::vector<std::wstring> radiobox_list = {
RadioBox radiobox;
radiobox.entries = {
L"Use gcc", L"Use gcc",
L"Use clang", L"Use clang",
L"Use emscripten", L"Use emscripten",
L"Use tcc", L"Use tcc",
}; };
screen.Loop(&radiobox); int selected = 0;
auto screen = ScreenInteractive::TerminalOutput();
screen.Loop(Radiobox(&radiobox_list, &selected));
return 0; return 0;
} }

View File

@ -1,35 +1,29 @@
#include <memory> // for __shared_ptr_access, shared_ptr
#include <string> // for wstring, operator+ #include <string> // for wstring, operator+
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/component.hpp" // for Component #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/radiobox.hpp" // for RadioBox #include "ftxui/component/component.hpp" // for Radiobox, Renderer
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for Element, operator| #include "ftxui/dom/elements.hpp" // for Element, operator|, size, border, frame, HEIGHT, LESS_THAN
#include "ftxui/screen/box.hpp" // for ftxui #include "ftxui/screen/string.hpp" // for to_wstring
#include "ftxui/screen/string.hpp" // for to_wstring
using namespace ftxui; using namespace ftxui;
class MyComponent : public Component {
RadioBox radiobox;
public:
MyComponent() {
for (int i = 0; i < 30; ++i) {
radiobox.entries.push_back(L"RadioBox " + to_wstring(i));
}
Add(&radiobox);
}
Element Render() override {
return radiobox.Render() | frame | size(HEIGHT, LESS_THAN, 10) | border;
}
};
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
std::vector<std::wstring> entries;
int selected = 0;
for (int i = 0; i < 30; ++i)
entries.push_back(L"RadioBox " + to_wstring(i));
auto radiobox = Radiobox(&entries, &selected);
auto renderer = Renderer(radiobox, [&] {
return radiobox->Render() | frame | size(HEIGHT, LESS_THAN, 10) | border;
});
auto screen = ScreenInteractive::FitComponent(); auto screen = ScreenInteractive::FitComponent();
MyComponent component; screen.Loop(renderer);
screen.Loop(&component);
return 0; return 0;
} }

View File

@ -1,82 +1,14 @@
#include <functional> // for function #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include <memory> // for allocator, unique_ptr #include "ftxui/component/component.hpp" // for Slider
#include <string> // for operator+, to_wstring
#include "ftxui/component/component.hpp" // for Component, Compone...
#include "ftxui/component/container.hpp" // for Container
#include "ftxui/component/event.hpp" // for Event, Event::Escape
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/component/slider.hpp" // for Slider
#include "ftxui/dom/elements.hpp" // for separator, operator|
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/color.hpp" // for Color
using namespace ftxui; using namespace ftxui;
Element ColorTile(int red, int green, int blue) {
return text(L"") | size(WIDTH, GREATER_THAN, 14) |
size(HEIGHT, GREATER_THAN, 7) | bgcolor(Color::RGB(red, green, blue));
}
Element ColorString(int red, int green, int blue) {
return text(L"RGB = (" + //
std::to_wstring(red) + L"," + //
std::to_wstring(green) + L"," + //
std::to_wstring(blue) + L")" //
);
}
class MyComponent : public Component {
public:
MyComponent(int* red, int* green, int* blue, std::function<void(void)> quit)
: red_(red), green_(green), blue_(blue), quit_(quit) {
Add(&container_);
container_.Add(slider_red_.get());
container_.Add(slider_green_.get());
container_.Add(slider_blue_.get());
}
Element Render() {
return hbox({
ColorTile(*red_, *green_, *blue_),
separator(),
vbox({
slider_red_->Render(),
separator(),
slider_green_->Render(),
separator(),
slider_blue_->Render(),
separator(),
ColorString(*red_, *green_, *blue_),
}) | xflex,
}) |
border | size(WIDTH, LESS_THAN, 80);
}
bool OnEvent(Event event) {
if (event == Event::Return || event == Event::Escape)
quit_();
return Component::OnEvent(event);
}
private:
int* red_;
int* green_;
int* blue_;
Container container_ = Container::Vertical();
ComponentPtr slider_red_ = Slider(L"Red :", red_, 0, 255, 1);
ComponentPtr slider_green_ = Slider(L"Green:", green_, 0, 255, 1);
ComponentPtr slider_blue_ = Slider(L"Blue :", blue_, 0, 255, 1);
std::function<void(void)> quit_;
};
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::TerminalOutput(); auto screen = ScreenInteractive::TerminalOutput();
int red = 128; int value = 50;
int green = 25; auto slider = Slider(L"Value:", &value, 0, 100, 1);
int blue = 100; screen.Loop(slider);
auto component = MyComponent(&red, &green, &blue, screen.ExitLoopClosure());
screen.Loop(&component);
} }
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -0,0 +1,62 @@
#include <memory> // for allocator, __shared_ptr_access, shared_ptr
#include <string> // for operator+, to_wstring, char_traits
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Slider, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for separator, Element, operator|, size, text, vbox, xflex, bgcolor, hbox, GREATER_THAN, WIDTH, border, HEIGHT, LESS_THAN
#include "ftxui/screen/color.hpp" // for Color
using namespace ftxui;
Element ColorTile(int red, int green, int blue) {
return text(L"") | size(WIDTH, GREATER_THAN, 14) |
size(HEIGHT, GREATER_THAN, 7) | bgcolor(Color::RGB(red, green, blue));
}
Element ColorString(int red, int green, int blue) {
return text(L"RGB = (" + //
std::to_wstring(red) + L"," + //
std::to_wstring(green) + L"," + //
std::to_wstring(blue) + L")" //
);
}
int main(int argc, const char* argv[]) {
int red = 128;
int green = 25;
int blue = 100;
auto slider_red = Slider(L"Red :", &red, 0, 255, 1);
auto slider_green = Slider(L"Green:", &green, 0, 255, 1);
auto slider_blue = Slider(L"Blue :", &blue, 0, 255, 1);
auto container = Container::Vertical({
slider_red,
slider_green,
slider_blue,
});
auto renderer = Renderer(container, [&] {
return hbox({
ColorTile(red, green, blue),
separator(),
vbox({
slider_red->Render(),
separator(),
slider_green->Render(),
separator(),
slider_blue->Render(),
separator(),
ColorString(red, green, blue),
}) | xflex,
}) |
border | size(WIDTH, LESS_THAN, 80);
});
auto screen = ScreenInteractive::TerminalOutput();
screen.Loop(renderer);
}
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@ -1,71 +1,68 @@
#include <functional> // for function #include <memory> // for allocator, __shared_ptr_access, shared_ptr
#include <string> // for wstring, allocator #include <string> // for wstring, basic_string
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/component.hpp" // for Component #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/container.hpp" // for Container #include "ftxui/component/component.hpp" // for Radiobox, Renderer, Tab, Toggle, Vertical
#include "ftxui/component/radiobox.hpp" // for RadioBox #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/component/toggle.hpp" // for Toggle #include "ftxui/dom/elements.hpp" // for Element, separator, operator|, vbox, border
#include "ftxui/dom/elements.hpp" // for Element, operator|
#include "ftxui/screen/box.hpp" // for ftxui
using namespace ftxui; using namespace ftxui;
class MyComponent : public Component {
public:
MyComponent() {
Add(&container_);
container_.Add(&toggle_);
toggle_.entries = {
L"tab_1",
L"tab_2",
L"tab_3",
};
container_.Add(&tab_container_);
radiobox_1_.entries = {L"Forest", L"Water", L"I don't know"};
tab_container_.Add(&radiobox_1_);
radiobox_2_.entries = {
L"Hello",
L"Hi",
L"Hay",
};
tab_container_.Add(&radiobox_2_);
radiobox_3_.entries = {
L"Table",
L"Nothing",
L"Is",
L"Empty",
};
tab_container_.Add(&radiobox_3_);
}
std::function<void()> on_enter = []() {};
Element Render() {
return vbox(toggle_.Render(), separator(), tab_container_.Render()) |
border;
}
private:
Toggle toggle_;
Container container_ = Container::Vertical();
Container tab_container_ = Container::Tab(&(toggle_.selected));
RadioBox radiobox_1_;
RadioBox radiobox_2_;
RadioBox radiobox_3_;
};
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
std::vector<std::wstring> tab_values{
L"tab_1",
L"tab_2",
L"tab_3",
};
int tab_selected = 0;
auto tab_toggle = Toggle(&tab_values, &tab_selected);
std::vector<std::wstring> tab_1_entries{
L"Forest",
L"Water",
L"I don't know",
};
int tab_1_selected = 0;
std::vector<std::wstring> tab_2_entries{
L"Hello",
L"Hi",
L"Hay",
};
int tab_2_selected = 0;
std::vector<std::wstring> tab_3_entries{
L"Table",
L"Nothing",
L"Is",
L"Empty",
};
int tab_3_selected = 0;
auto tab_container = Container::Tab(
&tab_selected, {
Radiobox(&tab_1_entries, &tab_1_selected),
Radiobox(&tab_2_entries, &tab_2_selected),
Radiobox(&tab_3_entries, &tab_3_selected),
});
auto container = Container::Vertical({
tab_toggle,
tab_container,
});
auto renderer = Renderer(container, [&] {
return vbox({
tab_toggle->Render(),
separator(),
tab_container->Render(),
}) |
border;
});
auto screen = ScreenInteractive::TerminalOutput(); auto screen = ScreenInteractive::TerminalOutput();
MyComponent component; screen.Loop(renderer);
component.on_enter = screen.ExitLoopClosure();
screen.Loop(&component);
} }
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -1,64 +1,68 @@
#include <functional> // for function #include <memory> // for allocator, __shared_ptr_access, shared_ptr
#include <string> // for wstring, allocator #include <string> // for wstring, basic_string
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/component.hpp" // for Component #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/container.hpp" // for Container #include "ftxui/component/component.hpp" // for Radiobox, Horizontal, Menu, Renderer, Tab
#include "ftxui/component/menu.hpp" // for Menu #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/screen/box.hpp" // for ftxui #include "ftxui/dom/elements.hpp" // for Element, separator, hbox, operator|, border
using namespace ftxui; using namespace ftxui;
class MyComponent : public Component {
public:
MyComponent() {
Add(&container_);
container_.Add(&menu_);
menu_.entries = {
L"menu_1",
L"menu_2",
L"menu_3",
};
container_.Add(&tab_container_);
menu_1_.entries = {L"Forest", L"Water", L"I don't know"};
tab_container_.Add(&menu_1_);
menu_2_.entries = {
L"Hello",
L"Hi",
L"Hay",
};
tab_container_.Add(&menu_2_);
menu_3_.entries = {
L"Table",
L"Nothing",
L"Is",
L"Empty",
};
tab_container_.Add(&menu_3_);
}
std::function<void()> on_enter = []() {};
private:
Menu menu_;
Container container_ = Container::Horizontal();
Container tab_container_ = Container::Tab(&(menu_.selected));
Menu menu_1_;
Menu menu_2_;
Menu menu_3_;
};
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
std::vector<std::wstring> tab_values{
L"tab_1",
L"tab_2",
L"tab_3",
};
int tab_selected = 0;
auto tab_menu = Menu(&tab_values, &tab_selected);
std::vector<std::wstring> tab_1_entries{
L"Forest",
L"Water",
L"I don't know",
};
int tab_1_selected = 0;
std::vector<std::wstring> tab_2_entries{
L"Hello",
L"Hi",
L"Hay",
};
int tab_2_selected = 0;
std::vector<std::wstring> tab_3_entries{
L"Table",
L"Nothing",
L"Is",
L"Empty",
};
int tab_3_selected = 0;
auto tab_container = Container::Tab(
&tab_selected, {
Radiobox(&tab_1_entries, &tab_1_selected),
Radiobox(&tab_2_entries, &tab_2_selected),
Radiobox(&tab_3_entries, &tab_3_selected),
});
auto container = Container::Horizontal({
tab_menu,
tab_container,
});
auto renderer = Renderer(container, [&] {
return hbox({
tab_menu->Render(),
separator(),
tab_container->Render(),
}) |
border;
});
auto screen = ScreenInteractive::TerminalOutput(); auto screen = ScreenInteractive::TerminalOutput();
MyComponent component; screen.Loop(renderer);
component.on_enter = screen.ExitLoopClosure();
screen.Loop(&component);
} }
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -1,59 +1,63 @@
#include "ftxui/component/toggle.hpp" #include <memory> // for allocator, __shared_ptr_access
#include "ftxui/component/container.hpp" // for Container #include <string> // for wstring, basic_string
#include "ftxui/component/event.hpp" // for Event, Event::Return #include <vector> // for vector
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Toggle, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for text, hbox, vbox, Element
using namespace ftxui; using namespace ftxui;
class MyComponent : public Component { int main(int argc, const char* argv[]) {
public: std::vector<std::wstring> toggle_1_entries = {
MyComponent() { L"On",
Add(&container_); L"Off",
container_.Add(&toggle_1_); };
container_.Add(&toggle_2_); std::vector<std::wstring> toggle_2_entries = {
container_.Add(&toggle_3_); L"Enabled",
container_.Add(&toggle_4_); L"Disabled",
};
std::vector<std::wstring> toggle_3_entries = {
L"10€",
L"0€",
};
std::vector<std::wstring> toggle_4_entries = {
L"Nothing",
L"One element",
L"Several elements",
};
toggle_1_.entries = {L"On", L"Off"}; int toggle_1_selected = 0;
toggle_2_.entries = {L"Enabled", L"Disabled"}; int toggle_2_selected = 0;
toggle_3_.entries = {L"10€", L"0€"}; int toggle_3_selected = 0;
toggle_4_.entries = {L"Nothing", L"One element", L"Several elements"}; int toggle_4_selected = 0;
} Component toggle_1 = Toggle(&toggle_1_entries, &toggle_1_selected);
Component toggle_2 = Toggle(&toggle_2_entries, &toggle_2_selected);
Component toggle_3 = Toggle(&toggle_3_entries, &toggle_3_selected);
Component toggle_4 = Toggle(&toggle_4_entries, &toggle_4_selected);
std::function<void()> on_enter = []() {}; auto container = Container::Vertical({
toggle_1,
toggle_2,
toggle_3,
toggle_4,
});
private: auto renderer = Renderer(container, [&] {
Container container_ = Container::Vertical();
Toggle toggle_1_;
Toggle toggle_2_;
Toggle toggle_3_;
Toggle toggle_4_;
Element Render() override {
return vbox({ return vbox({
text(L"Choose your options:"), text(L"Choose your options:"),
text(L""), text(L""),
hbox(text(L" * Poweroff on startup : "), toggle_1_.Render()), hbox(text(L" * Poweroff on startup : "), toggle_1->Render()),
hbox(text(L" * Out of process : "), toggle_2_.Render()), hbox(text(L" * Out of process : "), toggle_2->Render()),
hbox(text(L" * Price of the information : "), toggle_3_.Render()), hbox(text(L" * Price of the information : "), toggle_3->Render()),
hbox(text(L" * Number of elements : "), toggle_4_.Render()), hbox(text(L" * Number of elements : "), toggle_4->Render()),
}); });
} });
bool OnEvent(Event event) override {
if (event == Event::Return) {
on_enter();
return true;
}
return Component::OnEvent(event);
}
};
int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::TerminalOutput(); auto screen = ScreenInteractive::TerminalOutput();
MyComponent component; screen.Loop(renderer);
component.on_enter = screen.ExitLoopClosure();
screen.Loop(&component);
} }
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -1,11 +1,12 @@
#include <stddef.h> #include <stddef.h> // for size_t
#include <ftxui/dom/elements.hpp> #include <ftxui/dom/elements.hpp> // for operator|, size, Element, text, hcenter, Decorator, WIDTH, hflow, window, EQUAL, GREATER_THAN, HEIGHT, bold, border, dim, LESS_THAN
#include <ftxui/screen/screen.hpp> #include <ftxui/screen/screen.hpp> // for Dimension, Screen
#include <ftxui/screen/string.hpp> #include <ftxui/screen/string.hpp> // for to_wstring
#include <string> #include <memory> // for shared_ptr
#include <string> // for allocator, operator+, char_traits, wstring
#include "ftxui/dom/node.hpp" #include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" #include "ftxui/screen/box.hpp" // for ftxui
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
using namespace ftxui; using namespace ftxui;

View File

@ -1,13 +1,14 @@
#include <chrono> #include <chrono> // for operator""s, chrono_literals
#include <ftxui/dom/elements.hpp> #include <ftxui/dom/elements.hpp> // for paragraph, text, operator|, Element, border, color, hflow, spinner, vbox, bold, dim, underlined
#include <ftxui/screen/screen.hpp> #include <ftxui/screen/screen.hpp> // for Screen, Dimension
#include <iostream> #include <iostream> // for cout, ostream
#include <string> #include <memory> // for shared_ptr
#include <thread> #include <string> // for allocator, operator<<, string
#include <thread> // for sleep_for
#include "ftxui/dom/node.hpp" #include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" #include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/color.hpp" #include "ftxui/screen/color.hpp" // for Color, Color::Red
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
using namespace ftxui; using namespace ftxui;

View File

@ -33,15 +33,18 @@
"./component/menu.js", "./component/menu.js",
"./component/menu2.js", "./component/menu2.js",
"./component/menu_style.js", "./component/menu_style.js",
"./component/modal_dialog.js",
"./component/radiobox.js", "./component/radiobox.js",
"./component/radiobox_in_frame.js", "./component/radiobox_in_frame.js",
"./component/slider.js",
"./component/tab_horizontal.js", "./component/tab_horizontal.js",
"./component/tab_vertical.js", "./component/tab_vertical.js",
"./component/toggle.js", "./component/toggle.js",
"./component/modal_dialog.js",
"./dom/border.js", "./dom/border.js",
"./dom/color_gallery.js", "./dom/color_gallery.js",
"./dom/color_info_palette256.js",
"./dom/color_truecolor_HSV.js",
"./dom/color_truecolor_RGB.js",
"./dom/dbox.js", "./dom/dbox.js",
"./dom/gauge.js", "./dom/gauge.js",
"./dom/graph.js", "./dom/graph.js",
@ -55,16 +58,12 @@
"./dom/style_blink.js", "./dom/style_blink.js",
"./dom/style_bold.js", "./dom/style_bold.js",
"./dom/style_color.js", "./dom/style_color.js",
"./dom/color_truecolor_RGB.js",
"./dom/color_truecolor_HSV.js",
"./dom/color_info_palette256.js",
"./dom/style_dim.js", "./dom/style_dim.js",
"./dom/style_gallery.js", "./dom/style_gallery.js",
"./dom/style_inverted.js", "./dom/style_inverted.js",
"./dom/style_underlined.js", "./dom/style_underlined.js",
"./dom/vbox_hbox.js", "./dom/vbox_hbox.js",
"./dom/window.js", "./dom/window.js",
"./util/print_key_press.js", "./util/print_key_press.js",
]; ];

View File

@ -4,16 +4,17 @@
#include <stddef.h> // for size_t #include <stddef.h> // for size_t
#include <algorithm> // for max #include <algorithm> // for max
#include <ftxui/component/component.hpp> // for Component #include <ftxui/component/component.hpp> // for Make
#include <ftxui/component/screen_interactive.hpp> // for ScreenInteractive #include <ftxui/component/screen_interactive.hpp> // for ScreenInteractive
#include <string> // for allocator, operator+ #include <string> // for allocator, operator+, wstring, char_traits, to_wstring, string
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/event.hpp" // for Event #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/dom/elements.hpp" // for text, vbox, window #include "ftxui/component/event.hpp" // for Event
#include "ftxui/screen/box.hpp" // for ftxui #include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Middle, Mouse::None, Mouse::Pressed, Mouse::Released, Mouse::Right, Mouse::WheelDown, Mouse::WheelUp
#include "ftxui/dom/elements.hpp" // for text, vbox, window, Elements, Element
using namespace ftxui; using namespace ftxui;
@ -71,7 +72,7 @@ std::wstring Stringify(Event event) {
return out; return out;
} }
class DrawKey : public Component { class DrawKey : public ComponentBase {
public: public:
~DrawKey() override = default; ~DrawKey() override = default;
@ -94,6 +95,5 @@ class DrawKey : public Component {
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::TerminalOutput(); auto screen = ScreenInteractive::TerminalOutput();
DrawKey draw_key; screen.Loop(Make<DrawKey>());
screen.Loop(&draw_key);
} }

View File

@ -1,36 +1,36 @@
#ifndef FTXUI_COMPONENT_BUTTON_HPP #ifndef FTXUI_COMPONENT_BUTTON_HPP
#define FTXUI_COMPONENT_BUTTON_HPP #define FTXUI_COMPONENT_BUTTON_HPP
#include <functional> #include <functional> // for function
#include <string> #include <string> // for wstring
#include "ftxui/component/component.hpp" #include "ftxui/component/component.hpp" // for Component
#include "ftxui/dom/elements.hpp" #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/screen/box.hpp" #include "ftxui/dom/elements.hpp" // for Element
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/string.hpp" // for ConstStringRef
namespace ftxui { namespace ftxui {
struct Event; struct Event;
/// @brief A button. An action is associated to the click event. /// @brief A button. An action is associated to the click event.
/// @ingroup dom /// @ingroup dom
class Button : public Component { class ButtonBase : public ComponentBase {
public: public:
// Access this interface from a Component
static ButtonBase* From(Component);
// Constructor. // Constructor.
Button() = default; ButtonBase(ConstStringRef label, std::function<void()> on_click);
Button(std::wstring label) : label(label) {} ~ButtonBase() override = default;
~Button() override = default;
/// The Button label.
std::wstring label = L"button";
/// Called when the user press the "enter" button.
std::function<void()> on_click = [] {};
// Component implementation. // Component implementation.
Element Render() override; Element Render() override;
bool OnEvent(Event) override; bool OnEvent(Event) override;
private: private:
ConstStringRef label_;
std::function<void()> on_click_;
Box box_; Box box_;
}; };

View File

@ -1,10 +1,14 @@
#ifndef FTXUI_COMPONENT_CHECKBOX_HPP #ifndef FTXUI_COMPONENT_CHECKBOX_HPP
#define FTXUI_COMPONENT_CHECKBOX_HPP #define FTXUI_COMPONENT_CHECKBOX_HPP
#include <string> #include <functional> // for function
#include <string> // for allocator, wstring
#include "ftxui/component/component.hpp" #include "ftxui/component/component.hpp" // for Component
#include "ftxui/screen/box.hpp" #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/dom/elements.hpp" // for Element, Decorator, inverted, nothing
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/string.hpp" // for ConstStringRef
namespace ftxui { namespace ftxui {
struct Event; struct Event;
@ -12,14 +16,14 @@ struct Event;
/// @brief A Checkbox. It can be checked or unchecked.Display an element on a /// @brief A Checkbox. It can be checked or unchecked.Display an element on a
/// ftxui::Screen. /// ftxui::Screen.
/// @ingroup dom /// @ingroup dom
class CheckBox : public Component { class CheckboxBase : public ComponentBase {
public: public:
// Constructor. // Access this interface from a Component
CheckBox() = default; static CheckboxBase* From(Component component);
~CheckBox() override = default;
bool state = false; // The current state. true=checked, false:unchecked. // Constructor.
std::wstring label = L"label"; // The CheckBox label. CheckboxBase(ConstStringRef label, bool* state);
~CheckboxBase() override = default;
#if defined(_WIN32) #if defined(_WIN32)
std::wstring checked = L"[X] "; /// Prefix for a "checked" state. std::wstring checked = L"[X] "; /// Prefix for a "checked" state.
@ -32,7 +36,7 @@ class CheckBox : public Component {
Decorator focused_style = inverted; /// Decorator used when focused. Decorator focused_style = inverted; /// Decorator used when focused.
Decorator unfocused_style = nothing; /// Decorator used when unfocused. Decorator unfocused_style = nothing; /// Decorator used when unfocused.
/// Called when the user change the state of the CheckBox. /// Called when the user change the state of the CheckboxBase.
std::function<void()> on_change = []() {}; std::function<void()> on_change = []() {};
// Component implementation. // Component implementation.
@ -42,6 +46,8 @@ class CheckBox : public Component {
private: private:
bool OnMouseEvent(Event event); bool OnMouseEvent(Event event);
ConstStringRef label_;
bool* const state_;
int cursor_position = 0; int cursor_position = 0;
Box box_; Box box_;
}; };

View File

@ -1,77 +1,48 @@
#ifndef FTXUI_COMPONENT_COMPONENT_HPP #ifndef FTXUI_COMPONENT_HPP
#define FTXUI_COMPONENT_COMPONENT_HPP #define FTXUI_COMPONENT_HPP
#include <memory> // for unique_ptr #include <functional> // for function
#include <vector> // for vector #include <memory> // for shared_ptr, make_shared
#include <string> // for wstring
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CaptureMouse #include "ftxui/component/component_base.hpp"
#include "ftxui/dom/elements.hpp" // for Element #include "ftxui/dom/elements.hpp" // for Element
#include "ftxui/screen/string.hpp" // for ConstStringRef, StringRef
namespace ftxui { namespace ftxui {
class Delegate; class ComponentBase;
class Focus;
struct Event;
/// @brief It implement rendering itself as ftxui::Element. It implement using Component = std::shared_ptr<ComponentBase>;
/// keyboard navigation by responding to ftxui::Event. using Components = std::vector<Component>;
/// @ingroup component
class Component {
public:
// Constructor/Destructor.
Component() = default;
virtual ~Component();
// Component hierarchy. template <class T, class... Args>
Component* Parent(); std::shared_ptr<T> Make(Args&&... args) {
void Add(Component* children); return std::make_shared<T>(args...);
}
// Renders the component. Component Button(ConstStringRef label, std::function<void()> on_click);
virtual Element Render(); Component Checkbox(ConstStringRef label, bool* checked);
Component Input(StringRef content, ConstStringRef placeholder);
Component Menu(const std::vector<std::wstring>* entries, int* selected_);
Component Radiobox(const std::vector<std::wstring>* entries, int* selected_);
Component Toggle(const std::vector<std::wstring>* entries, int* selected);
Component Renderer(Component child, std::function<Element()>);
Component Renderer(std::function<Element()>);
template <class T> // T = {int, float}
Component Slider(StringRef label, T* value, T min, T max, T increment);
// Handles an event. namespace Container {
// By default, reduce on children with a lazy OR. Component Vertical(Components children);
// Component Horizontal(Components children);
// Returns whether the event was handled or not. Component Tab(int* selector, Components children);
virtual bool OnEvent(Event); } // namespace Container
// Focus management ----------------------------------------------------------
//
// If this component contains children, this indicates which one is active,
// nullptr if none is active.
//
// We say an element has the focus if the chain of ActiveChild() from the
// root component contains this object.
virtual Component* ActiveChild();
// Whether this is the active child of its parent.
bool Active();
// Whether all the ancestors are active.
bool Focused();
// Make the |child| to be the "active" one.
virtual void SetActiveChild(Component* child);
// Configure all the ancestors to give focus to this component.
void TakeFocus();
protected:
CapturedMouse CaptureMouse(const Event& event);
std::vector<Component*> children_;
private:
Component* parent_ = nullptr;
void Detach();
void Attach(Component* parent);
};
using ComponentPtr = std::unique_ptr<Component>;
} // namespace ftxui } // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_COMPONENT_HPP */ #endif /* end of include guard: FTXUI_COMPONENT_HPP */
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.

View File

@ -0,0 +1,81 @@
#ifndef FTXUI_COMPONENT_BASE_HPP
#define FTXUI_COMPONENT_BASE_HPP
#include <memory> // for unique_ptr
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CaptureMouse
#include "ftxui/dom/elements.hpp" // for Element
namespace ftxui {
class Delegate;
class Focus;
struct Event;
class ComponentBase;
using Component = std::shared_ptr<ComponentBase>;
using Components = std::vector<Component>;
/// @brief It implement rendering itself as ftxui::Element. It implement
/// keyboard navigation by responding to ftxui::Event.
/// @ingroup component
class ComponentBase {
public:
// Constructor/Destructor.
ComponentBase() = default;
virtual ~ComponentBase();
// ComponentBase hierarchy.
ComponentBase* Parent();
void Add(Component children);
// Renders the component.
virtual Element Render();
// Handles an event.
// By default, reduce on children with a lazy OR.
//
// Returns whether the event was handled or not.
virtual bool OnEvent(Event);
// Focus management ----------------------------------------------------------
//
// If this component contains children, this indicates which one is active,
// nullptr if none is active.
//
// We say an element has the focus if the chain of ActiveChild() from the
// root component contains this object.
virtual Component ActiveChild();
// Whether this is the active child of its parent.
bool Active();
// Whether all the ancestors are active.
bool Focused();
// Make the |child| to be the "active" one.
virtual void SetActiveChild(ComponentBase* child);
void SetActiveChild(Component child);
// Configure all the ancestors to give focus to this component.
void TakeFocus();
protected:
CapturedMouse CaptureMouse(const Event& event);
std::vector<Component> children_;
private:
ComponentBase* parent_ = nullptr;
void Detach();
};
using Component = std::shared_ptr<ComponentBase>;
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_BASE_HPP */
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@ -1,36 +1,42 @@
#ifndef FTXUI_COMPONENT_CONTAINER_HPP #ifndef FTXUI_COMPONENT_CONTAINER_HPP
#define FTXUI_COMPONENT_CONTAINER_HPP #define FTXUI_COMPONENT_CONTAINER_HPP
#include "ftxui/component/component.hpp" #include "ftxui/component/component.hpp" // for Component, Components
#include "ftxui/component/event.hpp" #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/dom/elements.hpp" #include "ftxui/component/event.hpp" // for Event
#include "ftxui/dom/elements.hpp" // for Element
namespace ftxui { namespace ftxui {
/// @brief A component where focus and events are automatically handled for you. /// @brief A component where focus and events are automatically handled for you.
class Container : public Component { class ContainerBase : public ComponentBase {
public: public:
static Container Vertical(); static Component Vertical();
static Container Horizontal(); static Component Vertical(Components children);
static Container Tab(int* selector);
~Container() override = default; static Component Horizontal();
static Component Horizontal(Components children);
static Component Tab(int* selector);
static Component Tab(int* selector, Components children);
~ContainerBase() override = default;
// Component override. // Component override.
bool OnEvent(Event event) override; bool OnEvent(Event event) override;
Element Render() override; Element Render() override;
Component* ActiveChild() override; Component ActiveChild() override;
virtual void SetActiveChild(Component*) override; virtual void SetActiveChild(ComponentBase*) override;
protected: protected:
// Handlers // Handlers
using EventHandler = bool (Container::*)(Event); using EventHandler = bool (ContainerBase::*)(Event);
bool VerticalEvent(Event event); bool VerticalEvent(Event event);
bool HorizontalEvent(Event event); bool HorizontalEvent(Event event);
bool TabEvent(Event) { return false; } bool TabEvent(Event) { return false; }
EventHandler event_handler_; EventHandler event_handler_;
using RenderHandler = Element (Container::*)(); using RenderHandler = Element (ContainerBase::*)();
Element VerticalRender(); Element VerticalRender();
Element HorizontalRender(); Element HorizontalRender();
Element TabRender(); Element TabRender();

View File

@ -8,7 +8,7 @@
namespace ftxui { namespace ftxui {
class ScreenInteractive; class ScreenInteractive;
class Component; class ComponentBase;
/// @brief Represent an event. It can be key press event, a terminal resize, or /// @brief Represent an event. It can be key press event, a terminal resize, or
/// more ... /// more ...
@ -76,7 +76,7 @@ struct Event {
//--- State section ---------------------------------------------------------- //--- State section ----------------------------------------------------------
private: private:
friend Component; friend ComponentBase;
friend ScreenInteractive; friend ScreenInteractive;
enum class Type { enum class Type {
Unknown, Unknown,

View File

@ -1,27 +1,30 @@
#ifndef FTXUI_COMPONENT_INPUT_H_ #ifndef FTXUI_COMPONENT_INPUT_H_
#define FTXUI_COMPONENT_INPUT_H_ #define FTXUI_COMPONENT_INPUT_H_
#include <functional> #include <functional> // for function
#include <string> #include <string> // for wstring
#include "ftxui/component/component.hpp" #include "ftxui/component/component.hpp" // for Component
#include "ftxui/dom/elements.hpp" #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/screen/box.hpp" #include "ftxui/dom/elements.hpp" // for Element
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/string.hpp" // for ConstStringRef, StringRef
namespace ftxui { namespace ftxui {
struct Event; struct Event;
/// @brief An input box. The user can type text into it. /// @brief An input box. The user can type text into it.
/// @ingroup component. /// @ingroup component.
class Input : public Component { class InputBase : public ComponentBase {
public: public:
// Access this interface from a Component
static InputBase* From(Component component);
// Constructor. // Constructor.
Input() = default; InputBase(StringRef content, ConstStringRef placeholder);
~Input() override = default; ~InputBase() override = default;
// State. // State.
std::wstring content;
std::wstring placeholder;
int cursor_position = 0; int cursor_position = 0;
// State update callback. // State update callback.
@ -33,6 +36,9 @@ class Input : public Component {
bool OnEvent(Event) override; bool OnEvent(Event) override;
private: private:
StringRef content_;
ConstStringRef placeholder_;
bool OnMouseEvent(Event); bool OnMouseEvent(Event);
Box input_box_; Box input_box_;
Box cursor_box_; Box cursor_box_;

View File

@ -1,28 +1,30 @@
#ifndef FTXUI_COMPONENT_MENU #ifndef FTXUI_COMPONENT_MENU
#define FTXUI_COMPONENT_MENU #define FTXUI_COMPONENT_MENU
#include <functional> #include <functional> // for function
#include <string> #include <string> // for wstring
#include <vector> #include <vector> // for vector
#include "ftxui/component/component.hpp" #include "ftxui/component/component.hpp" // for Component
#include "ftxui/dom/elements.hpp" #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/screen/box.hpp" #include "ftxui/dom/elements.hpp" // for Element, Decorator, operator|, bold, inverted, nothing
#include "ftxui/screen/box.hpp" // for Box
namespace ftxui { namespace ftxui {
struct Event; struct Event;
/// @brief A list of items. The user can navigate through them. /// @brief A list of items. The user can navigate through them.
/// @ingroup component /// @ingroup component
class Menu : public Component { class MenuBase : public ComponentBase {
public: public:
// Access this interface from a Component
static MenuBase* From(Component component);
// Constructor. // Constructor.
Menu() = default; MenuBase(const std::vector<std::wstring>* entries, int* selected_);
~Menu() override = default; ~MenuBase() override = default;
// State. // State.
std::vector<std::wstring> entries = {};
int selected = 0;
int focused = 0; int focused = 0;
Decorator normal_style = nothing; Decorator normal_style = nothing;
@ -38,7 +40,10 @@ class Menu : public Component {
Element Render() override; Element Render() override;
bool OnEvent(Event) override; bool OnEvent(Event) override;
private: protected:
const std::vector<std::wstring>* const entries_;
int* selected_ = 0;
bool OnMouseEvent(Event); bool OnMouseEvent(Event);
std::vector<Box> boxes_; std::vector<Box> boxes_;

View File

@ -1,12 +1,14 @@
#ifndef FTXUI_COMPONENT_RADIOBOX_HPP #ifndef FTXUI_COMPONENT_RADIOBOX_HPP
#define FTXUI_COMPONENT_RADIOBOX_HPP #define FTXUI_COMPONENT_RADIOBOX_HPP
#include <string> #include <functional> // for function
#include <vector> #include <string> // for wstring, allocator
#include <vector> // for vector
#include "ftxui/component/component.hpp" #include "ftxui/component/component.hpp" // for Component
#include "ftxui/dom/elements.hpp" #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/screen/box.hpp" #include "ftxui/dom/elements.hpp" // for Element, Decorator, inverted, nothing
#include "ftxui/screen/box.hpp" // for Box
namespace ftxui { namespace ftxui {
struct Event; struct Event;
@ -14,15 +16,16 @@ struct Event;
/// @brief A list of selectable element. One and only one can be selected at /// @brief A list of selectable element. One and only one can be selected at
/// the same time. /// the same time.
/// @ingroup component /// @ingroup component
class RadioBox : public Component { class RadioboxBase : public ComponentBase {
public: public:
// Constructor. // Access this interface from a Component
RadioBox() = default; static RadioboxBase* From(Component component);
~RadioBox() override = default;
// Constructor.
RadioboxBase(const std::vector<std::wstring>* entries, int* selected);
~RadioboxBase() override = default;
int selected = 0;
int focused = 0; int focused = 0;
std::vector<std::wstring> entries;
#if defined(_WIN32) #if defined(_WIN32)
std::wstring checked = L"(*) "; std::wstring checked = L"(*) ";
@ -43,6 +46,9 @@ class RadioBox : public Component {
bool OnEvent(Event) override; bool OnEvent(Event) override;
private: private:
const std::vector<std::wstring>* const entries_;
int* const selected_;
bool OnMouseEvent(Event event); bool OnMouseEvent(Event event);
int cursor_position = 0; int cursor_position = 0;
std::vector<Box> boxes_; std::vector<Box> boxes_;

View File

@ -1,13 +1,15 @@
#ifndef FTXUI_COMPONENT_RECEIVER_HPP_ #ifndef FTXUI_COMPONENT_RECEIVER_HPP_
#define FTXUI_COMPONENT_RECEIVER_HPP_ #define FTXUI_COMPONENT_RECEIVER_HPP_
#include <atomic> // for atomic #include <algorithm> // for copy, max
#include <atomic> // for atomic, __atomic_base
#include <condition_variable> // for condition_variable #include <condition_variable> // for condition_variable
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <memory> // for unique_ptr, make_unique #include <memory> // for unique_ptr, make_unique
#include <mutex> // for mutex, unique_lock #include <mutex> // for mutex, unique_lock
#include <queue> // for queue #include <queue> // for queue
#include <utility> // for move
namespace ftxui { namespace ftxui {

View File

@ -1,17 +1,20 @@
#ifndef FTXUI_COMPONENT_SCREEN_INTERACTIVE_HPP #ifndef FTXUI_COMPONENT_SCREEN_INTERACTIVE_HPP
#define FTXUI_COMPONENT_SCREEN_INTERACTIVE_HPP #define FTXUI_COMPONENT_SCREEN_INTERACTIVE_HPP
#include <atomic> // for atomic #include <atomic> // for atomic
#include <ftxui/component/receiver.hpp> #include <ftxui/component/receiver.hpp> // for Receiver, Sender
#include <memory> // for unique_ptr #include <functional> // for function
#include <string> // for string #include <memory> // for shared_ptr
#include <string> // for string
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/event.hpp" #include "ftxui/component/event.hpp"
#include "ftxui/screen/screen.hpp" // for Screen #include "ftxui/screen/screen.hpp" // for Screen
namespace ftxui { namespace ftxui {
class Component; class ComponentBase;
using Component = std::shared_ptr<ComponentBase>;
struct Event; struct Event;
class ScreenInteractive : public Screen { class ScreenInteractive : public Screen {
@ -22,15 +25,15 @@ class ScreenInteractive : public Screen {
static ScreenInteractive TerminalOutput(); static ScreenInteractive TerminalOutput();
~ScreenInteractive(); ~ScreenInteractive();
void Loop(Component*); void Loop(Component);
std::function<void()> ExitLoopClosure(); std::function<void()> ExitLoopClosure();
void PostEvent(Event event); void PostEvent(Event event);
CapturedMouse CaptureMouse(); CapturedMouse CaptureMouse();
private: private:
void Draw(Component* component); void Draw(Component component);
void EventLoop(Component* component); void EventLoop(Component component);
enum class Dimension { enum class Dimension {
FitComponent, FitComponent,

View File

@ -1,23 +0,0 @@
#ifndef FTXUI_COMPONENT_SLIDER_HPP
#define FTXUI_COMPONENT_SLIDER_HPP
#include <string>
#include "ftxui/component/component.hpp"
namespace ftxui {
// ComponentPtr Slider(std::string label,
// float* value,
// float min = 0.f,
// float max = 100.f,
// float increment = (max - min) * 0.05f);
template <class T> // T = {int, float}
ComponentPtr Slider(std::wstring label, T* value, T min, T max, T increment);
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_SLIDER_HPP */
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@ -1,26 +1,30 @@
#ifndef FTXUI_COMPONENT_TOGGLE_H_ #ifndef FTXUI_COMPONENT_TOGGLE_H_
#define FTXUI_COMPONENT_TOGGLE_H_ #define FTXUI_COMPONENT_TOGGLE_H_
#include <string> #include <functional> // for function
#include <vector> #include <string> // for wstring
#include <vector> // for vector
#include "ftxui/component/component.hpp" #include "ftxui/component/component.hpp" // for Component
#include "ftxui/dom/elements.hpp" #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/screen/box.hpp" #include "ftxui/dom/elements.hpp" // for Element, Decorator, operator|, bold, dim, inverted
#include "ftxui/screen/box.hpp" // for Box
namespace ftxui { namespace ftxui {
struct Event; struct Event;
/// @brief An horizontal list of elements. The user can navigate through them. /// @brief An horizontal list of elements. The user can navigate through them.
/// @ingroup component /// @ingroup component
class Toggle : public Component { class ToggleBase : public ComponentBase {
public: public:
// Access this interface from a Component
static ToggleBase* From(Component component);
// Constructor. // Constructor.
~Toggle() override = default; ToggleBase(const std::vector<std::wstring>* entries, int* selected);
~ToggleBase() override = default;
// State. // State.
std::vector<std::wstring> entries = {L"On", L"Off"};
int selected = 0;
int focused = 0; int focused = 0;
Decorator normal_style = dim; Decorator normal_style = dim;
@ -36,7 +40,10 @@ class Toggle : public Component {
Element Render() override; Element Render() override;
bool OnEvent(Event) override; bool OnEvent(Event) override;
private: protected:
const std::vector<std::wstring>* const entries_;
int* selected_ = 0;
bool OnMouseEvent(Event event); bool OnMouseEvent(Event event);
std::vector<Box> boxes_; std::vector<Box> boxes_;
}; };

View File

@ -16,6 +16,41 @@ int wchar_width(wchar_t);
int wchar_width_cjk(wchar_t); int wchar_width_cjk(wchar_t);
int wstring_width(const std::wstring&); int wstring_width(const std::wstring&);
int wstring_width_cjk(const std::wstring&); int wstring_width_cjk(const std::wstring&);
/// @brief For convenience, this class convert multiple mutable string
/// references toward a shared representation.
class StringRef {
public:
StringRef(std::wstring& ref);
StringRef(std::wstring* ref);
StringRef(const wchar_t* ref);
StringRef(const char* ref);
std::wstring& operator*();
std::wstring* operator->();
private:
std::wstring* const borrowed_ = nullptr;
std::wstring owned_;
};
/// @brief For convenience, this class convert multiple immutable string
/// references toward shared representation.
class ConstStringRef {
public:
ConstStringRef(const std::wstring& ref);
ConstStringRef(const std::wstring* ref);
ConstStringRef(const wchar_t* ref);
ConstStringRef(const char* ref);
const std::wstring& operator*();
const std::wstring* operator->();
private:
const std::wstring* const borrowed_ = nullptr;
const std::wstring owned_;
};
} // namespace ftxui } // namespace ftxui
#endif /* end of include guard: FTXUI_SCREEN_STRING_HPP */ #endif /* end of include guard: FTXUI_SCREEN_STRING_HPP */

View File

@ -1,8 +1,11 @@
[ [
{ symbol: [ "VMIN", private, "<termios.h>", public ] },
{ symbol: [ "VTIME", private, "<termios.h>", public ] },
{ symbol: [ "ECHO", private, "<termios.h>", public ] }, { symbol: [ "ECHO", private, "<termios.h>", public ] },
{ symbol: [ "ICANON", private, "<termios.h>", public ] }, { symbol: [ "ICANON", private, "<termios.h>", public ] },
{ symbol: [ "termios", private, "<termios.h>", public ] },
{ symbol: [ "TCSANOW", private, "<termios.h>", public ] }, { symbol: [ "TCSANOW", private, "<termios.h>", public ] },
{ symbol: [ "VMIN", private, "<termios.h>", public ] },
{ symbol: [ "VTIME", private, "<termios.h>", public ] },
{ symbol: [ "__shared_ptr_access", private, "<memory>", public ] },
{ symbol: [ "termios", private, "<termios.h>", public ] },
{ symbol: ["__alloc_traits<>:value_type", private, "<vector>", public ] },
{ include: ["<ext/alloc_traits.h>", private, "<vector>", public] },
] ]

View File

@ -9,12 +9,46 @@
namespace ftxui { namespace ftxui {
Element Button::Render() { /// @brief Draw a button. Execute a function when clicked.
auto style = Focused() ? inverted : nothing; /// @param label The label of the button.
return text(label) | border | style | reflect(box_); /// @param on_click The action to execute when clicked.
/// @ingroup component
/// @see ButtonBase
///
/// ### Example
///
/// ```cpp
/// auto screen = ScreenInteractive::FitComponent();
/// std::wstring label = L"Click to quit";
/// Component button = Button(&label, screen.ExitLoopClosure());
/// screen.Loop(button)
/// ```
///
/// ### Output
///
/// ```bash
/// ┌─────────────┐
/// │Click to quit│
/// └─────────────┘
/// ```
Component Button(ConstStringRef label, std::function<void()> on_click) {
return Make<ButtonBase>(label, on_click);
} }
bool Button::OnEvent(Event event) { // static
ButtonBase* ButtonBase::From(Component component) {
return static_cast<ButtonBase*>(component.get());
}
ButtonBase::ButtonBase(ConstStringRef label, std::function<void()> on_click)
: label_(label), on_click_(on_click) {}
Element ButtonBase::Render() {
auto style = Focused() ? inverted : nothing;
return text(*label_) | border | style | reflect(box_);
}
bool ButtonBase::OnEvent(Event event) {
if (event.is_mouse() && box_.Contain(event.mouse().x, event.mouse().y)) { if (event.is_mouse() && box_.Contain(event.mouse().x, event.mouse().y)) {
if (!CaptureMouse(event)) if (!CaptureMouse(event))
return false; return false;
@ -23,7 +57,7 @@ bool Button::OnEvent(Event event) {
if (event.mouse().button == Mouse::Left && if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) { event.mouse().motion == Mouse::Pressed) {
on_click(); on_click_();
return true; return true;
} }
@ -31,7 +65,7 @@ bool Button::OnEvent(Event event) {
} }
if (event == Event::Return) { if (event == Event::Return) {
on_click(); on_click_();
return true; return true;
} }
return false; return false;

View File

@ -9,29 +9,62 @@
namespace ftxui { namespace ftxui {
Element CheckBox::Render() { /// @brief Draw checkable element.
/// @param label The label of the checkbox.
/// @param checked Whether the checkbox is checked or not.
/// @ingroup component
/// @see CheckboxBase
///
/// ### Example
///
/// ```cpp
/// auto screen = ScreenInteractive::FitComponent();
/// std::wstring label = L"Make a sandwidth";
/// bool checked = false;
/// Component checkbox = Checkbox(&label, &checked);
/// screen.Loop(checkbox)
/// ```
///
/// ### Output
///
/// ```bash
/// ☐ Make a sandwitch
/// ```
Component Checkbox(ConstStringRef label, bool* checked) {
return Make<CheckboxBase>(label, checked);
}
// static
CheckboxBase* CheckboxBase::From(Component component) {
return static_cast<CheckboxBase*>(component.get());
}
CheckboxBase::CheckboxBase(ConstStringRef label, bool* state)
: label_(label), state_(state) {}
Element CheckboxBase::Render() {
bool is_focused = Focused(); bool is_focused = Focused();
auto style = is_focused ? focused_style : unfocused_style; auto style = is_focused ? focused_style : unfocused_style;
auto focus_management = is_focused ? focus : state ? select : nothing; auto focus_management = is_focused ? focus : *state_ ? select : nothing;
return hbox(text(state ? checked : unchecked), return hbox(text(*state_ ? checked : unchecked),
text(label) | style | focus_management) | text(*label_) | style | focus_management) |
reflect(box_); reflect(box_);
} }
bool CheckBox::OnEvent(Event event) { bool CheckboxBase::OnEvent(Event event) {
if (event.is_mouse()) if (event.is_mouse())
return OnMouseEvent(event); return OnMouseEvent(event);
if (event == Event::Character(' ') || event == Event::Return) { if (event == Event::Character(' ') || event == Event::Return) {
state = !state; *state_ = !*state_;
on_change(); on_change();
return true; return true;
} }
return false; return false;
} }
bool CheckBox::OnMouseEvent(Event event) { bool CheckboxBase::OnMouseEvent(Event event) {
if (!CaptureMouse(event)) if (!CaptureMouse(event))
return false; return false;
if (!box_.Contain(event.mouse().x, event.mouse().y)) if (!box_.Contain(event.mouse().x, event.mouse().y))
return false; return false;
@ -40,7 +73,7 @@ bool CheckBox::OnMouseEvent(Event event) {
if (event.mouse().button == Mouse::Left && if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) { event.mouse().motion == Mouse::Pressed) {
state = !state; *state_ = !*state_;
on_change(); on_change();
return true; return true;
} }

View File

@ -1,10 +1,13 @@
#include <algorithm> // for find_if, max
#include <iterator> // for begin, end
#include <utility> // for move
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
#include "ftxui/component/component.hpp" #include "ftxui/component/component.hpp"
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include <algorithm> #include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/component/captured_mouse.hpp" #include "ftxui/dom/elements.hpp" // for text, Element
#include "ftxui/component/event.hpp"
#include "ftxui/component/screen_interactive.hpp"
namespace ftxui { namespace ftxui {
@ -13,33 +16,35 @@ class CaptureMouseImpl : public CapturedMouseInterface {
public: public:
~CaptureMouseImpl() override {} ~CaptureMouseImpl() override {}
}; };
} } // namespace
Component::~Component() { ComponentBase::~ComponentBase() {
Detach(); Detach();
} }
/// @brief Return the parent Component, or nul if any. /// @brief Return the parent ComponentBase, or nul if any.
/// @see Attach /// @see Attach
/// @see Detach /// @see Detach
/// @see Parent /// @see Parent
/// @ingroup component /// @ingroup component
Component* Component::Parent() { ComponentBase* ComponentBase::Parent() {
return parent_; return parent_;
} }
/// @brief Add a children. /// @brief Add a children.
/// @@param child The child to be attached. /// @@param child The child to be attached.
/// @ingroup component /// @ingroup component
void Component::Add(Component* child) { void ComponentBase::Add(Component child) {
child->Attach(this); child->Detach();
child->parent_ = this;
children_.push_back(std::move(child));
} }
/// @brief Draw the component. /// @brief Draw the component.
/// Build a ftxui::Element to be drawn on the ftxi::Screen representing this /// Build a ftxui::Element to be drawn on the ftxi::Screen representing this
/// ftxui::Component. /// ftxui::ComponentBase.
/// @ingroup component /// @ingroup component
Element Component::Render() { Element ComponentBase::Render() {
if (children_.size() == 1) if (children_.size() == 1)
return children_.front()->Render(); return children_.front()->Render();
@ -52,8 +57,8 @@ Element Component::Render() {
/// The default implementation called OnEvent on every child until one return /// The default implementation called OnEvent on every child until one return
/// true. If none returns true, return false. /// true. If none returns true, return false.
/// @ingroup component /// @ingroup component
bool Component::OnEvent(Event event) { bool ComponentBase::OnEvent(Event event) {
for (Component* child : children_) { for (Component& child : children_) {
if (child->OnEvent(event)) if (child->OnEvent(event))
return true; return true;
} }
@ -63,27 +68,27 @@ bool Component::OnEvent(Event event) {
/// @brief Return the currently Active child. /// @brief Return the currently Active child.
/// @return the currently Active child. /// @return the currently Active child.
/// @ingroup component /// @ingroup component
Component* Component::ActiveChild() { Component ComponentBase::ActiveChild() {
return children_.empty() ? nullptr : children_.front(); return children_.empty() ? nullptr : children_.front();
} }
/// @brief Returns if the element if the currently active child of its parent. /// @brief Returns if the element if the currently active child of its parent.
/// @ingroup component /// @ingroup component
bool Component::Active() { bool ComponentBase::Active() {
return !parent_ || parent_->ActiveChild() == this; return !parent_ || parent_->ActiveChild().get() == this;
} }
/// @brief Returns if the elements if focused by the user. /// @brief Returns if the elements if focused by the user.
/// True when the Component is focused by the user. An element is Focused when /// True when the ComponentBase is focused by the user. An element is Focused
/// it is with all its ancestors the ActiveChild() of their parents. /// when it is with all its ancestors the ActiveChild() of their parents.
/// @ingroup component /// @ingroup component
bool Component::Focused() { bool ComponentBase::Focused() {
Component* current = this; ComponentBase* current = this;
for (;;) { for (;;) {
Component* parent = current->parent_; ComponentBase* parent = current->parent_;
if (!parent) if (!parent)
return true; return true;
if (parent->ActiveChild() != current) if (parent->ActiveChild().get() != current)
return false; return false;
current = parent; current = parent;
} }
@ -92,13 +97,20 @@ bool Component::Focused() {
/// @brief Make the |child| to be the "active" one. /// @brief Make the |child| to be the "active" one.
/// @argument child the child to become active. /// @argument child the child to become active.
/// @ingroup component /// @ingroup component
void Component::SetActiveChild(Component*) {} void ComponentBase::SetActiveChild(ComponentBase*) {}
/// @brief Make the |child| to be the "active" one.
/// @argument child the child to become active.
/// @ingroup component
void ComponentBase::SetActiveChild(Component child) {
SetActiveChild(child.get());
}
/// @brief Configure all the ancestors to give focus to this component. /// @brief Configure all the ancestors to give focus to this component.
/// @ingroup component /// @ingroup component
void Component::TakeFocus() { void ComponentBase::TakeFocus() {
Component* child = this; ComponentBase* child = this;
Component* parent = parent_; ComponentBase* parent = parent_;
while (parent) { while (parent) {
parent->SetActiveChild(child); parent->SetActiveChild(child);
child = parent; child = parent;
@ -110,7 +122,7 @@ void Component::TakeFocus() {
/// them. It represents a component taking priority over others. /// them. It represents a component taking priority over others.
/// @argument event /// @argument event
/// @ingroup component /// @ingroup component
CapturedMouse Component::CaptureMouse(const Event& event) { CapturedMouse ComponentBase::CaptureMouse(const Event& event) {
if (!event.screen_) if (!event.screen_)
return std::make_unique<CaptureMouseImpl>(); return std::make_unique<CaptureMouseImpl>();
return event.screen_->CaptureMouse(); return event.screen_->CaptureMouse();
@ -121,25 +133,17 @@ CapturedMouse Component::CaptureMouse(const Event& event) {
/// @see Detach /// @see Detach
/// @see Parent /// @see Parent
/// @ingroup component /// @ingroup component
void Component::Detach() { void ComponentBase::Detach() {
if (!parent_) if (!parent_)
return; return;
auto it = std::find(std::begin(parent_->children_), auto it = std::find_if(std::begin(parent_->children_), //
std::end(parent_->children_), this); std::end(parent_->children_), //
[this](const Component& that) { //
return this == that.get();
});
parent_->children_.erase(it); parent_->children_.erase(it);
} }
/// @brief Attach this element to its parent.
/// @see Attach
/// @see Detach
/// @see Parent
/// @ingroup component
void Component::Attach(Component* parent) {
Detach();
parent_ = parent;
parent_->children_.push_back(this);
}
} // namespace ftxui } // namespace ftxui
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -1,37 +1,127 @@
#include "ftxui/component/container.hpp" #include <stddef.h> // for size_t
#include <algorithm> // for max, min
#include <memory> // for __shared_ptr_access, shared_ptr, make_shared, allocator, __shared_ptr_access<>::element_type, allocator_traits<>::value_type
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include <stddef.h> #include "ftxui/component/container.hpp"
#include <algorithm>
#include <vector>
namespace ftxui { namespace ftxui {
namespace Container {
/// @brief A list of components, drawn one by one vertically and navigated
/// vertically using up/down arrow key or 'j'/'k' keys.
/// @param children the list of components.
/// @ingroup component
/// @see ContainerBase
///
/// ### Example
///
/// ```cpp
/// auto container = Container::Vertical({
/// children_1,
/// children_2,
/// children_3,
/// children_4,
/// });
/// ```
Component Vertical(Components children) {
return ContainerBase::Vertical(std::move(children));
}
/// @brief A list of components, drawn one by one horizontally and navigated
/// horizontally using left/right arrow key or 'h'/'l' keys.
/// @param children the list of components.
/// @ingroup component
/// @see ContainerBase
///
/// ### Example
///
/// ```cpp
/// auto container = Container::Horizontal({
/// children_1,
/// children_2,
/// children_3,
/// children_4,
/// });
/// ```
Component Horizontal(Components children) {
return ContainerBase::Horizontal(std::move(children));
}
/// @brief A list of components, where only one is drawn and interacted with at
/// a time. The |selector| gives the index of the selected component. This is
/// useful to implement tabs.
/// @param selector The index of the drawn children.
/// @param children the list of components.
/// @ingroup component
/// @see ContainerBase
///
/// ### Example
///
/// ```cpp
/// int tab_drawn = 0;
/// auto container = Container::Tab(&tab_drawn, {
/// children_1,
/// children_2,
/// children_3,
/// children_4,
/// });
/// ```
Component Tab(int* selector, Components children) {
return ContainerBase::Tab(selector, std::move(children));
}
} // namespace Container
// static // static
Container Container::Horizontal() { Component ContainerBase::Vertical() {
Container container; return Vertical({});
container.event_handler_ = &Container::HorizontalEvent; }
container.render_handler_ = &Container::HorizontalRender;
// static
Component ContainerBase::Vertical(Components children) {
auto container = std::make_shared<ContainerBase>();
container->event_handler_ = &ContainerBase::VerticalEvent;
container->render_handler_ = &ContainerBase::VerticalRender;
for (Component& child : children)
container->Add(std::move(child));
return container; return container;
} }
// static // static
Container Container::Vertical() { Component ContainerBase::Horizontal() {
Container container; return Horizontal({});
container.event_handler_ = &Container::VerticalEvent; }
container.render_handler_ = &Container::VerticalRender;
// static
Component ContainerBase::Horizontal(Components children) {
auto container = std::make_shared<ContainerBase>();
container->event_handler_ = &ContainerBase::HorizontalEvent;
container->render_handler_ = &ContainerBase::HorizontalRender;
for (Component& child : children)
container->Add(std::move(child));
return container; return container;
} }
// static // static
Container Container::Tab(int* selector) { Component ContainerBase::Tab(int* selector) {
Container container; return Tab(selector, {});
container.event_handler_ = &Container::TabEvent; }
container.render_handler_ = &Container::TabRender;
container.selector_ = selector; // static
Component ContainerBase::Tab(int* selector, Components children) {
auto container = std::make_shared<ContainerBase>();
container->selector_ = selector;
container->event_handler_ = &ContainerBase::TabEvent;
container->render_handler_ = &ContainerBase::TabRender;
for (Component& child : children)
container->Add(std::move(child));
return container; return container;
} }
bool Container::OnEvent(Event event) { bool ContainerBase::OnEvent(Event event) {
if (event.is_mouse()) if (event.is_mouse())
return OnMouseEvent(event); return OnMouseEvent(event);
@ -44,7 +134,7 @@ bool Container::OnEvent(Event event) {
return (this->*event_handler_)(event); return (this->*event_handler_)(event);
} }
Component* Container::ActiveChild() { Component ContainerBase::ActiveChild() {
if (children_.size() == 0) if (children_.size() == 0)
return nullptr; return nullptr;
@ -52,16 +142,16 @@ Component* Container::ActiveChild() {
return children_[selected % children_.size()]; return children_[selected % children_.size()];
} }
void Container::SetActiveChild(Component* child) { void ContainerBase::SetActiveChild(ComponentBase* child) {
for (size_t i = 0; i < children_.size(); ++i) { for (size_t i = 0; i < children_.size(); ++i) {
if (children_[i] == child) { if (children_[i].get() == child) {
(selector_ ? *selector_ : selected_) = i; (selector_ ? *selector_ : selected_) = i;
return; return;
} }
} }
} }
bool Container::VerticalEvent(Event event) { bool ContainerBase::VerticalEvent(Event event) {
int old_selected = selected_; int old_selected = selected_;
if (event == Event::ArrowUp || event == Event::Character('k')) if (event == Event::ArrowUp || event == Event::Character('k'))
selected_--; selected_--;
@ -76,7 +166,7 @@ bool Container::VerticalEvent(Event event) {
return old_selected != selected_; return old_selected != selected_;
} }
bool Container::HorizontalEvent(Event event) { bool ContainerBase::HorizontalEvent(Event event) {
int old_selected = selected_; int old_selected = selected_;
if (event == Event::ArrowLeft || event == Event::Character('h')) if (event == Event::ArrowLeft || event == Event::Character('h'))
selected_--; selected_--;
@ -91,11 +181,11 @@ bool Container::HorizontalEvent(Event event) {
return old_selected != selected_; return old_selected != selected_;
} }
Element Container::Render() { Element ContainerBase::Render() {
return (this->*render_handler_)(); return (this->*render_handler_)();
} }
Element Container::VerticalRender() { Element ContainerBase::VerticalRender() {
Elements elements; Elements elements;
for (auto& it : children_) for (auto& it : children_)
elements.push_back(it->Render()); elements.push_back(it->Render());
@ -104,7 +194,7 @@ Element Container::VerticalRender() {
return vbox(std::move(elements)); return vbox(std::move(elements));
} }
Element Container::HorizontalRender() { Element ContainerBase::HorizontalRender() {
Elements elements; Elements elements;
for (auto& it : children_) for (auto& it : children_)
elements.push_back(it->Render()); elements.push_back(it->Render());
@ -113,18 +203,18 @@ Element Container::HorizontalRender() {
return hbox(std::move(elements)); return hbox(std::move(elements));
} }
Element Container::TabRender() { Element ContainerBase::TabRender() {
Component* active_child = ActiveChild(); Component active_child = ActiveChild();
if (active_child) if (active_child)
return active_child->Render(); return active_child->Render();
return text(L"Empty container"); return text(L"Empty container");
} }
bool Container::OnMouseEvent(Event event) { bool ContainerBase::OnMouseEvent(Event event) {
if (selector_) if (selector_)
return ActiveChild()->OnEvent(event); return ActiveChild()->OnEvent(event);
for (Component* child : children_) { for (Component& child : children_) {
if (child->OnEvent(event)) if (child->OnEvent(event))
return true; return true;
} }

View File

@ -1,287 +1,293 @@
#include <gtest/gtest-message.h> // for Message #include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver #include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <memory> // for allocator #include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/container.hpp" #include "ftxui/component/container.hpp"
#include "ftxui/screen/box.hpp" // for ftxui #include "gtest/gtest_pred_impl.h" // for AssertionResult, EXPECT_EQ, EXPECT_FALSE, EXPECT_TRUE, Test, TEST
#include "gtest/gtest_pred_impl.h" // for AssertionResult, EXPECT_EQ, EXPEC...
using namespace ftxui; using namespace ftxui;
TEST(ContainerTest, HorizontalEvent) { TEST(ContainerTest, HorizontalEvent) {
auto container = Container::Horizontal(); auto container = ContainerBase::Horizontal();
Component c0, c1, c2; auto c0 = Container::Horizontal({});
container.Add(&c0); auto c1 = Container::Horizontal({});
container.Add(&c1); auto c2 = Container::Horizontal({});
container.Add(&c2); container->Add(c0);
container->Add(c1);
container->Add(c2);
// With arrow key. // With arrow key.
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::ArrowRight); container->OnEvent(Event::ArrowRight);
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::ArrowRight); container->OnEvent(Event::ArrowRight);
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::ArrowRight); container->OnEvent(Event::ArrowRight);
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::ArrowLeft); container->OnEvent(Event::ArrowLeft);
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::ArrowLeft); container->OnEvent(Event::ArrowLeft);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::ArrowLeft); container->OnEvent(Event::ArrowLeft);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
// With arrow key in the wrong dimension. // With arrow key in the wrong dimension.
container.OnEvent(Event::ArrowUp); container->OnEvent(Event::ArrowUp);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::ArrowDown); container->OnEvent(Event::ArrowDown);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
// With vim like characters. // With vim like characters.
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::Character('l')); container->OnEvent(Event::Character('l'));
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::Character('l')); container->OnEvent(Event::Character('l'));
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::Character('l')); container->OnEvent(Event::Character('l'));
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::Character('h')); container->OnEvent(Event::Character('h'));
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::Character('h')); container->OnEvent(Event::Character('h'));
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::Character('h')); container->OnEvent(Event::Character('h'));
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
// With vim like characters in the wrong direction. // With vim like characters in the wrong direction.
container.OnEvent(Event::Character('j')); container->OnEvent(Event::Character('j'));
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::Character('k')); container->OnEvent(Event::Character('k'));
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
// With tab characters. // With tab characters.
container.OnEvent(Event::Tab); container->OnEvent(Event::Tab);
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::Tab); container->OnEvent(Event::Tab);
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::Tab); container->OnEvent(Event::Tab);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::Tab); container->OnEvent(Event::Tab);
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::Tab); container->OnEvent(Event::Tab);
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::TabReverse); container->OnEvent(Event::TabReverse);
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::TabReverse); container->OnEvent(Event::TabReverse);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::TabReverse); container->OnEvent(Event::TabReverse);
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::TabReverse); container->OnEvent(Event::TabReverse);
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::TabReverse); container->OnEvent(Event::TabReverse);
} }
TEST(ContainerTest, VerticalEvent) { TEST(ContainerTest, VerticalEvent) {
auto container = Container::Vertical(); auto container = ContainerBase::Vertical();
Component c0, c1, c2; auto c0 = Container::Horizontal({});
container.Add(&c0); auto c1 = Container::Horizontal({});
container.Add(&c1); auto c2 = Container::Horizontal({});
container.Add(&c2); container->Add(c0);
container->Add(c1);
container->Add(c2);
// With arrow key. // With arrow key.
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::ArrowDown); container->OnEvent(Event::ArrowDown);
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::ArrowDown); container->OnEvent(Event::ArrowDown);
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::ArrowDown); container->OnEvent(Event::ArrowDown);
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::ArrowUp); container->OnEvent(Event::ArrowUp);
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::ArrowUp); container->OnEvent(Event::ArrowUp);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::ArrowUp); container->OnEvent(Event::ArrowUp);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
// With arrow key in the wrong dimension. // With arrow key in the wrong dimension.
container.OnEvent(Event::ArrowLeft); container->OnEvent(Event::ArrowLeft);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::ArrowRight); container->OnEvent(Event::ArrowRight);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
// With vim like characters. // With vim like characters.
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::Character('j')); container->OnEvent(Event::Character('j'));
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::Character('j')); container->OnEvent(Event::Character('j'));
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::Character('j')); container->OnEvent(Event::Character('j'));
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::Character('k')); container->OnEvent(Event::Character('k'));
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::Character('k')); container->OnEvent(Event::Character('k'));
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::Character('k')); container->OnEvent(Event::Character('k'));
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
// With vim like characters in the wrong direction. // With vim like characters in the wrong direction.
container.OnEvent(Event::Character('h')); container->OnEvent(Event::Character('h'));
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::Character('l')); container->OnEvent(Event::Character('l'));
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
// With tab characters. // With tab characters.
container.OnEvent(Event::Tab); container->OnEvent(Event::Tab);
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::Tab); container->OnEvent(Event::Tab);
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::Tab); container->OnEvent(Event::Tab);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::Tab); container->OnEvent(Event::Tab);
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::Tab); container->OnEvent(Event::Tab);
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::TabReverse); container->OnEvent(Event::TabReverse);
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::TabReverse); container->OnEvent(Event::TabReverse);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
container.OnEvent(Event::TabReverse); container->OnEvent(Event::TabReverse);
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
container.OnEvent(Event::TabReverse); container->OnEvent(Event::TabReverse);
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
container.OnEvent(Event::TabReverse); container->OnEvent(Event::TabReverse);
} }
TEST(ContainerTest, SetActiveChild) { TEST(ContainerTest, SetActiveChild) {
auto container = Container::Horizontal(); auto container = ContainerBase::Horizontal();
Component c0, c1, c2; auto c0 = Container::Horizontal({});
container.Add(&c0); auto c1 = Container::Horizontal({});
container.Add(&c1); auto c2 = Container::Horizontal({});
container.Add(&c2); container->Add(c0);
container->Add(c1);
container->Add(c2);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
EXPECT_TRUE(c0.Focused()); EXPECT_TRUE(c0->Focused());
EXPECT_TRUE(c0.Active()); EXPECT_TRUE(c0->Active());
EXPECT_FALSE(c1.Focused()); EXPECT_FALSE(c1->Focused());
EXPECT_FALSE(c1.Active()); EXPECT_FALSE(c1->Active());
EXPECT_FALSE(c2.Focused()); EXPECT_FALSE(c2->Focused());
EXPECT_FALSE(c2.Active()); EXPECT_FALSE(c2->Active());
container.SetActiveChild(&c0); container->SetActiveChild(c0);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
EXPECT_TRUE(c0.Focused()); EXPECT_TRUE(c0->Focused());
EXPECT_TRUE(c0.Active()); EXPECT_TRUE(c0->Active());
EXPECT_FALSE(c1.Focused()); EXPECT_FALSE(c1->Focused());
EXPECT_FALSE(c1.Active()); EXPECT_FALSE(c1->Active());
EXPECT_FALSE(c2.Focused()); EXPECT_FALSE(c2->Focused());
EXPECT_FALSE(c2.Active()); EXPECT_FALSE(c2->Active());
container.SetActiveChild(&c1); container->SetActiveChild(c1);
EXPECT_EQ(container.ActiveChild(), &c1); EXPECT_EQ(container->ActiveChild(), c1);
EXPECT_FALSE(c0.Focused()); EXPECT_FALSE(c0->Focused());
EXPECT_FALSE(c0.Active()); EXPECT_FALSE(c0->Active());
EXPECT_TRUE(c1.Focused()); EXPECT_TRUE(c1->Focused());
EXPECT_TRUE(c1.Active()); EXPECT_TRUE(c1->Active());
EXPECT_FALSE(c2.Focused()); EXPECT_FALSE(c2->Focused());
EXPECT_FALSE(c2.Active()); EXPECT_FALSE(c2->Active());
container.SetActiveChild(&c2); container->SetActiveChild(c2);
EXPECT_EQ(container.ActiveChild(), &c2); EXPECT_EQ(container->ActiveChild(), c2);
EXPECT_FALSE(c0.Focused()); EXPECT_FALSE(c0->Focused());
EXPECT_FALSE(c0.Active()); EXPECT_FALSE(c0->Active());
EXPECT_FALSE(c1.Focused()); EXPECT_FALSE(c1->Focused());
EXPECT_FALSE(c1.Active()); EXPECT_FALSE(c1->Active());
EXPECT_TRUE(c2.Focused()); EXPECT_TRUE(c2->Focused());
EXPECT_TRUE(c2.Active()); EXPECT_TRUE(c2->Active());
container.SetActiveChild(&c0); container->SetActiveChild(c0);
EXPECT_EQ(container.ActiveChild(), &c0); EXPECT_EQ(container->ActiveChild(), c0);
EXPECT_TRUE(c0.Focused()); EXPECT_TRUE(c0->Focused());
EXPECT_TRUE(c0.Active()); EXPECT_TRUE(c0->Active());
EXPECT_FALSE(c1.Focused()); EXPECT_FALSE(c1->Focused());
EXPECT_FALSE(c1.Active()); EXPECT_FALSE(c1->Active());
EXPECT_FALSE(c2.Focused()); EXPECT_FALSE(c2->Focused());
EXPECT_FALSE(c2.Active()); EXPECT_FALSE(c2->Active());
} }
TEST(ContainerTest, TakeFocus) { TEST(ContainerTest, TakeFocus) {
auto c = Container::Horizontal(); auto c = ContainerBase::Horizontal();
auto c1 = Container::Vertical(); auto c1 = ContainerBase::Vertical();
auto c2 = Container::Vertical(); auto c2 = ContainerBase::Vertical();
auto c3 = Container::Vertical(); auto c3 = ContainerBase::Vertical();
auto c11 = Container::Horizontal(); auto c11 = ContainerBase::Horizontal();
auto c12 = Container::Horizontal(); auto c12 = ContainerBase::Horizontal();
auto c13 = Container::Horizontal(); auto c13 = ContainerBase::Horizontal();
auto c21 = Container::Horizontal(); auto c21 = ContainerBase::Horizontal();
auto c22 = Container::Horizontal(); auto c22 = ContainerBase::Horizontal();
auto c23 = Container::Horizontal(); auto c23 = ContainerBase::Horizontal();
c.Add(&c1); c->Add(c1);
c.Add(&c2); c->Add(c2);
c.Add(&c3); c->Add(c3);
c1.Add(&c11); c1->Add(c11);
c1.Add(&c12); c1->Add(c12);
c1.Add(&c13); c1->Add(c13);
c2.Add(&c21); c2->Add(c21);
c2.Add(&c22); c2->Add(c22);
c2.Add(&c23); c2->Add(c23);
EXPECT_TRUE(c.Focused()); EXPECT_TRUE(c->Focused());
EXPECT_TRUE(c1.Focused()); EXPECT_TRUE(c1->Focused());
EXPECT_FALSE(c2.Focused()); EXPECT_FALSE(c2->Focused());
EXPECT_TRUE(c11.Focused()); EXPECT_TRUE(c11->Focused());
EXPECT_FALSE(c12.Focused()); EXPECT_FALSE(c12->Focused());
EXPECT_FALSE(c13.Focused()); EXPECT_FALSE(c13->Focused());
EXPECT_FALSE(c21.Focused()); EXPECT_FALSE(c21->Focused());
EXPECT_FALSE(c22.Focused()); EXPECT_FALSE(c22->Focused());
EXPECT_FALSE(c23.Focused()); EXPECT_FALSE(c23->Focused());
EXPECT_TRUE(c.Active()); EXPECT_TRUE(c->Active());
EXPECT_TRUE(c1.Active()); EXPECT_TRUE(c1->Active());
EXPECT_FALSE(c2.Active()); EXPECT_FALSE(c2->Active());
EXPECT_TRUE(c11.Active()); EXPECT_TRUE(c11->Active());
EXPECT_FALSE(c12.Active()); EXPECT_FALSE(c12->Active());
EXPECT_FALSE(c13.Active()); EXPECT_FALSE(c13->Active());
EXPECT_TRUE(c21.Active()); EXPECT_TRUE(c21->Active());
EXPECT_FALSE(c22.Active()); EXPECT_FALSE(c22->Active());
EXPECT_FALSE(c23.Active()); EXPECT_FALSE(c23->Active());
c22.TakeFocus(); c22->TakeFocus();
EXPECT_TRUE(c.Focused()); EXPECT_TRUE(c->Focused());
EXPECT_FALSE(c1.Focused()); EXPECT_FALSE(c1->Focused());
EXPECT_TRUE(c2.Focused()); EXPECT_TRUE(c2->Focused());
EXPECT_FALSE(c11.Focused()); EXPECT_FALSE(c11->Focused());
EXPECT_FALSE(c12.Focused()); EXPECT_FALSE(c12->Focused());
EXPECT_FALSE(c13.Focused()); EXPECT_FALSE(c13->Focused());
EXPECT_FALSE(c21.Focused()); EXPECT_FALSE(c21->Focused());
EXPECT_TRUE(c22.Focused()); EXPECT_TRUE(c22->Focused());
EXPECT_FALSE(c23.Focused()); EXPECT_FALSE(c23->Focused());
EXPECT_TRUE(c.Active()); EXPECT_TRUE(c->Active());
EXPECT_FALSE(c1.Active()); EXPECT_FALSE(c1->Active());
EXPECT_TRUE(c2.Active()); EXPECT_TRUE(c2->Active());
EXPECT_TRUE(c11.Active()); EXPECT_TRUE(c11->Active());
EXPECT_FALSE(c12.Active()); EXPECT_FALSE(c12->Active());
EXPECT_FALSE(c13.Active()); EXPECT_FALSE(c13->Active());
EXPECT_FALSE(c21.Active()); EXPECT_FALSE(c21->Active());
EXPECT_TRUE(c22.Active()); EXPECT_TRUE(c22->Active());
EXPECT_FALSE(c23.Active()); EXPECT_FALSE(c23->Active());
c1.TakeFocus(); c1->TakeFocus();
EXPECT_TRUE(c.Focused()); EXPECT_TRUE(c->Focused());
EXPECT_TRUE(c1.Focused()); EXPECT_TRUE(c1->Focused());
EXPECT_FALSE(c2.Focused()); EXPECT_FALSE(c2->Focused());
EXPECT_TRUE(c11.Focused()); EXPECT_TRUE(c11->Focused());
EXPECT_FALSE(c12.Focused()); EXPECT_FALSE(c12->Focused());
EXPECT_FALSE(c13.Focused()); EXPECT_FALSE(c13->Focused());
EXPECT_FALSE(c21.Focused()); EXPECT_FALSE(c21->Focused());
EXPECT_FALSE(c22.Focused()); EXPECT_FALSE(c22->Focused());
EXPECT_FALSE(c23.Focused()); EXPECT_FALSE(c23->Focused());
EXPECT_TRUE(c.Active()); EXPECT_TRUE(c->Active());
EXPECT_TRUE(c1.Active()); EXPECT_TRUE(c1->Active());
EXPECT_FALSE(c2.Active()); EXPECT_FALSE(c2->Active());
EXPECT_TRUE(c11.Active()); EXPECT_TRUE(c11->Active());
EXPECT_FALSE(c12.Active()); EXPECT_FALSE(c12->Active());
EXPECT_FALSE(c13.Active()); EXPECT_FALSE(c13->Active());
EXPECT_FALSE(c21.Active()); EXPECT_FALSE(c21->Active());
EXPECT_TRUE(c22.Active()); EXPECT_TRUE(c22->Active());
EXPECT_FALSE(c23.Active()); EXPECT_FALSE(c23->Active());
} }
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -1,7 +1,8 @@
#include "ftxui/component/event.hpp" #include <utility> // for move
#include "ftxui/component/mouse.hpp" #include "ftxui/component/event.hpp"
#include "ftxui/screen/string.hpp" #include "ftxui/component/mouse.hpp" // for Mouse
#include "ftxui/screen/string.hpp" // for to_wstring
namespace ftxui { namespace ftxui {

View File

@ -1,39 +1,74 @@
#include <algorithm> // for max, min
#include <memory> // for shared_ptr
#include <string> // for wstring, allocator, basic_string
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Backspace, Event::Custom, Event::Delete, Event::End, Event::Home, Event::Return
#include "ftxui/component/input.hpp" #include "ftxui/component/input.hpp"
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
#include <algorithm> #include "ftxui/component/screen_interactive.hpp" // for Component
#include <memory>
#include "ftxui/component/captured_mouse.hpp"
#include "ftxui/component/mouse.hpp"
#include "ftxui/component/screen_interactive.hpp"
namespace ftxui { namespace ftxui {
/// @brief An input box for editing text.
/// @param content The editable content.
/// @param placeholder The text displayed when content is still empty.
/// @ingroup component
/// @see InputBase
///
/// ### Example
///
/// ```cpp
/// auto screen = ScreenInteractive::FitComponent();
/// std::wstring content= L"";
/// std::wstring placeholder = L"placeholder";
/// Component input = Input(&content, &placeholder);
/// screen.Loop(input);
/// ```
///
/// ### Output
///
/// ```bash
/// placeholder
/// ```
Component Input(StringRef content, ConstStringRef placeholder) {
return Make<InputBase>(content, placeholder);
}
// static
InputBase* InputBase::From(Component component) {
return static_cast<InputBase*>(component.get());
}
InputBase::InputBase(StringRef content, ConstStringRef placeholder)
: content_(content), placeholder_(placeholder) {}
// Component implementation. // Component implementation.
Element Input::Render() { Element InputBase::Render() {
cursor_position = std::max(0, std::min<int>(content.size(), cursor_position)); cursor_position =
std::max(0, std::min<int>(content_->size(), cursor_position));
auto main_decorator = flex | size(HEIGHT, EQUAL, 1); auto main_decorator = flex | size(HEIGHT, EQUAL, 1);
bool is_focused = Focused(); bool is_focused = Focused();
// 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_); reflect(input_box_);
else else
return text(placeholder) | dim | main_decorator | reflect(input_box_); return text(*placeholder_) | dim | main_decorator | reflect(input_box_);
} }
// Not focused. // Not focused.
if (!is_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_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()
? content.substr(cursor_position, 1) ? content_->substr(cursor_position, 1)
: L" "; : L" ";
std::wstring part_after_cursor = cursor_position < (int)content.size() - 1 std::wstring part_after_cursor = cursor_position < (int)content_->size() - 1
? content.substr(cursor_position + 1) ? content_->substr(cursor_position + 1)
: L""; : L"";
auto focused = is_focused ? focus : select; auto focused = is_focused ? focus : select;
@ -47,8 +82,9 @@ Element Input::Render() {
// clang-format on // clang-format on
} }
bool Input::OnEvent(Event event) { bool InputBase::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()) if (event.is_mouse())
return OnMouseEvent(event); return OnMouseEvent(event);
@ -59,7 +95,7 @@ bool Input::OnEvent(Event event) {
if (event == Event::Backspace) { if (event == Event::Backspace) {
if (cursor_position == 0) if (cursor_position == 0)
return false; return false;
content.erase(cursor_position - 1, 1); content_->erase(cursor_position - 1, 1);
cursor_position--; cursor_position--;
on_change(); on_change();
return true; return true;
@ -67,9 +103,9 @@ bool Input::OnEvent(Event event) {
// Delete // Delete
if (event == Event::Delete) { if (event == Event::Delete) {
if (cursor_position == int(content.size())) if (cursor_position == int(content_->size()))
return false; return false;
content.erase(cursor_position, 1); content_->erase(cursor_position, 1);
on_change(); on_change();
return true; return true;
} }
@ -89,7 +125,7 @@ bool Input::OnEvent(Event event) {
return true; return true;
} }
if (event == Event::ArrowRight && cursor_position < (int)content.size()) { if (event == Event::ArrowRight && cursor_position < (int)content_->size()) {
cursor_position++; cursor_position++;
return true; return true;
} }
@ -100,13 +136,13 @@ bool Input::OnEvent(Event event) {
} }
if (event == Event::End) { if (event == Event::End) {
cursor_position = (int)content.size(); cursor_position = (int)content_->size();
return true; return true;
} }
// Content // Content
if (event.is_character()) { if (event.is_character()) {
content.insert(cursor_position, 1, event.character()); content_->insert(cursor_position, 1, event.character());
cursor_position++; cursor_position++;
on_change(); on_change();
return true; return true;
@ -114,7 +150,7 @@ bool Input::OnEvent(Event event) {
return false; return false;
} }
bool Input::OnMouseEvent(Event event) { bool InputBase::OnMouseEvent(Event event) {
if (!CaptureMouse(event)) if (!CaptureMouse(event))
return false; return false;
if (!input_box_.Contain(event.mouse().x, event.mouse().y)) if (!input_box_.Contain(event.mouse().x, event.mouse().y))
@ -127,7 +163,7 @@ bool Input::OnMouseEvent(Event event) {
int new_cursor_position = int new_cursor_position =
cursor_position + event.mouse().x - cursor_box_.x_min; cursor_position + event.mouse().x - cursor_box_.x_min;
new_cursor_position = new_cursor_position =
std::max(0, std::min<int>(content.size(), new_cursor_position)); std::max(0, std::min<int>(content_->size(), new_cursor_position));
if (cursor_position != new_cursor_position) { if (cursor_position != new_cursor_position) {
cursor_position = new_cursor_position; cursor_position = new_cursor_position;
on_change(); on_change();

View File

@ -1,159 +1,182 @@
#include "ftxui/component/input.hpp" #include <gtest/gtest-message.h> // for Message
#include "ftxui/component/event.hpp" #include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <memory> // for __shared_ptr_access
#include <string> // for wstring, allocator
#include "gtest/gtest.h" #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Backspace, Event::Delete, Event::End, Event::Home
#include "ftxui/component/input.hpp"
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
using namespace ftxui; using namespace ftxui;
TEST(InputTest, Init) { TEST(InputTest, Init) {
Input input; std::wstring content;
std::wstring placeholder;
Component input = Input(&content, &placeholder);
EXPECT_EQ(input.content, L""); EXPECT_EQ(InputBase::From(input)->cursor_position, 0);
EXPECT_EQ(input.placeholder, L"");
EXPECT_EQ(input.cursor_position, 0);
} }
TEST(InputTest, Type) { TEST(InputTest, Type) {
Input input; std::wstring content;
std::wstring placeholder;
Component input = Input(&content, &placeholder);
input.OnEvent(Event::Character('a')); input->OnEvent(Event::Character('a'));
EXPECT_EQ(input.content, L"a"); EXPECT_EQ(content, L"a");
EXPECT_EQ(input.cursor_position, 1u); EXPECT_EQ(InputBase::From(input)->cursor_position, 1u);
input.OnEvent(Event::Character('b')); input->OnEvent(Event::Character('b'));
EXPECT_EQ(input.content, L"ab"); EXPECT_EQ(content, L"ab");
EXPECT_EQ(input.cursor_position, 2u); EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
} }
TEST(InputTest, Arrow) { TEST(InputTest, Arrow) {
Input input; std::wstring content;
std::wstring placeholder;
Component input = Input(&content, &placeholder);
input.OnEvent(Event::Character('a')); input->OnEvent(Event::Character('a'));
input.OnEvent(Event::Character('b')); input->OnEvent(Event::Character('b'));
input.OnEvent(Event::Character('c')); input->OnEvent(Event::Character('c'));
EXPECT_EQ(input.cursor_position, 3u); EXPECT_EQ(InputBase::From(input)->cursor_position, 3u);
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
EXPECT_EQ(input.cursor_position, 2u); EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
EXPECT_EQ(input.cursor_position, 1u); EXPECT_EQ(InputBase::From(input)->cursor_position, 1u);
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
EXPECT_EQ(input.cursor_position, 0u); EXPECT_EQ(InputBase::From(input)->cursor_position, 0u);
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
EXPECT_EQ(input.cursor_position, 0u); EXPECT_EQ(InputBase::From(input)->cursor_position, 0u);
input.OnEvent(Event::ArrowRight); input->OnEvent(Event::ArrowRight);
EXPECT_EQ(input.cursor_position, 1u); EXPECT_EQ(InputBase::From(input)->cursor_position, 1u);
input.OnEvent(Event::ArrowRight); input->OnEvent(Event::ArrowRight);
EXPECT_EQ(input.cursor_position, 2u); EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
input.OnEvent(Event::ArrowRight); input->OnEvent(Event::ArrowRight);
EXPECT_EQ(input.cursor_position, 3u); EXPECT_EQ(InputBase::From(input)->cursor_position, 3u);
input.OnEvent(Event::ArrowRight); input->OnEvent(Event::ArrowRight);
EXPECT_EQ(input.cursor_position, 3u); EXPECT_EQ(InputBase::From(input)->cursor_position, 3u);
} }
TEST(InputTest, Insert) { TEST(InputTest, Insert) {
Input input; std::wstring content;
std::wstring placeholder;
Component input = Input(&content, &placeholder);
input.OnEvent(Event::Character('a')); input->OnEvent(Event::Character('a'));
input.OnEvent(Event::Character('b')); input->OnEvent(Event::Character('b'));
input.OnEvent(Event::Character('c')); input->OnEvent(Event::Character('c'));
EXPECT_EQ(input.content, L"abc"); EXPECT_EQ(content, L"abc");
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
input.OnEvent(Event::Character('-')); input->OnEvent(Event::Character('-'));
EXPECT_EQ(input.content, L"a-bc"); EXPECT_EQ(content, L"a-bc");
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
input.OnEvent(Event::Character('-')); input->OnEvent(Event::Character('-'));
EXPECT_EQ(input.content, L"a--bc"); EXPECT_EQ(content, L"a--bc");
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
input.OnEvent(Event::Character('-')); input->OnEvent(Event::Character('-'));
EXPECT_EQ(input.content, L"-a--bc"); EXPECT_EQ(content, L"-a--bc");
} }
TEST(InputTest, Home) { TEST(InputTest, Home) {
Input input; std::wstring content;
std::wstring placeholder;
Component input = Input(&content, &placeholder);
input.OnEvent(Event::Character('a')); input->OnEvent(Event::Character('a'));
input.OnEvent(Event::Character('b')); input->OnEvent(Event::Character('b'));
input.OnEvent(Event::Character('c')); input->OnEvent(Event::Character('c'));
EXPECT_EQ(input.content, L"abc"); EXPECT_EQ(content, L"abc");
EXPECT_EQ(input.cursor_position, 3u); EXPECT_EQ(InputBase::From(input)->cursor_position, 3u);
input.OnEvent(Event::Home); input->OnEvent(Event::Home);
EXPECT_EQ(input.cursor_position, 0u); EXPECT_EQ(InputBase::From(input)->cursor_position, 0u);
input.OnEvent(Event::Character('-')); input->OnEvent(Event::Character('-'));
EXPECT_EQ(input.content, L"-abc"); EXPECT_EQ(content, L"-abc");
} }
TEST(InputTest, End) { TEST(InputTest, End) {
Input input; std::wstring content;
std::wstring placeholder;
Component input = Input(&content, &placeholder);
input.OnEvent(Event::Character('a')); input->OnEvent(Event::Character('a'));
input.OnEvent(Event::Character('b')); input->OnEvent(Event::Character('b'));
input.OnEvent(Event::Character('c')); input->OnEvent(Event::Character('c'));
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
EXPECT_EQ(input.cursor_position, 1u); EXPECT_EQ(InputBase::From(input)->cursor_position, 1u);
input.OnEvent(Event::End); input->OnEvent(Event::End);
EXPECT_EQ(input.cursor_position, 3u); EXPECT_EQ(InputBase::From(input)->cursor_position, 3u);
} }
TEST(InputTest, Delete) { TEST(InputTest, Delete) {
Input input; std::wstring content;
std::wstring placeholder;
Component input = Input(&content, &placeholder);
input.OnEvent(Event::Character('a')); input->OnEvent(Event::Character('a'));
input.OnEvent(Event::Character('b')); input->OnEvent(Event::Character('b'));
input.OnEvent(Event::Character('c')); input->OnEvent(Event::Character('c'));
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
EXPECT_EQ(input.content, L"abc"); EXPECT_EQ(content, L"abc");
EXPECT_EQ(input.cursor_position, 2u); EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
input.OnEvent(Event::Delete); input->OnEvent(Event::Delete);
EXPECT_EQ(input.content, L"ab"); EXPECT_EQ(content, L"ab");
EXPECT_EQ(input.cursor_position, 2u); EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
input.OnEvent(Event::Delete); input->OnEvent(Event::Delete);
EXPECT_EQ(input.content, L"ab"); EXPECT_EQ(content, L"ab");
EXPECT_EQ(input.cursor_position, 2u); EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
} }
TEST(InputTest, Backspace) { TEST(InputTest, Backspace) {
Input input; std::wstring content;
std::wstring placeholder;
Component input = Input(&content, &placeholder);
input.OnEvent(Event::Character('a')); input->OnEvent(Event::Character('a'));
input.OnEvent(Event::Character('b')); input->OnEvent(Event::Character('b'));
input.OnEvent(Event::Character('c')); input->OnEvent(Event::Character('c'));
input.OnEvent(Event::ArrowLeft); input->OnEvent(Event::ArrowLeft);
EXPECT_EQ(input.content, L"abc"); EXPECT_EQ(content, L"abc");
EXPECT_EQ(input.cursor_position, 2u); EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
input.OnEvent(Event::Backspace); input->OnEvent(Event::Backspace);
EXPECT_EQ(input.content, L"ac"); EXPECT_EQ(content, L"ac");
EXPECT_EQ(input.cursor_position, 1u); EXPECT_EQ(InputBase::From(input)->cursor_position, 1u);
input.OnEvent(Event::Backspace); input->OnEvent(Event::Backspace);
EXPECT_EQ(input.content, L"c"); EXPECT_EQ(content, L"c");
EXPECT_EQ(input.cursor_position, 0u); EXPECT_EQ(InputBase::From(input)->cursor_position, 0u);
input.OnEvent(Event::Backspace); input->OnEvent(Event::Backspace);
EXPECT_EQ(input.content, L"c"); EXPECT_EQ(content, L"c");
EXPECT_EQ(input.cursor_position, 0u); EXPECT_EQ(InputBase::From(input)->cursor_position, 0u);
} }
// Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@ -1,23 +1,62 @@
#include <stddef.h> // for size_t
#include <algorithm> // for max, min
#include <memory> // for shared_ptr, allocator_traits<>::value_type
#include <utility> // for move
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowUp, Event::Return, Event::Tab, Event::TabReverse
#include "ftxui/component/menu.hpp" #include "ftxui/component/menu.hpp"
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Released
#include <stddef.h> #include "ftxui/component/screen_interactive.hpp" // for Component
#include <algorithm>
#include <memory>
#include <utility>
#include "ftxui/component/captured_mouse.hpp"
#include "ftxui/component/mouse.hpp"
#include "ftxui/component/screen_interactive.hpp"
namespace ftxui { namespace ftxui {
Element Menu::Render() { /// @brief A list of text. The focused element is selected.
/// @param entries The list of entries in the menu.
/// @param selected The index of the currently selected element.
/// @ingroup component
/// @see MenuBase
///
/// ### Example
///
/// ```cpp
/// auto screen = ScreenInteractive::TerminalOutput();
/// std::vector<std::wstring> entries = {
/// L"entry 1",
/// L"entry 2",
/// L"entry 3",
/// };
/// int selected = 0;
/// auto menu = Menu(&entries, &selected);
/// screen.Loop(menu);
/// ```
///
/// ### Output
///
/// ```bash
/// > entry 1
/// entry 2
/// entry 3
/// ```
Component Menu(const std::vector<std::wstring>* entries, int* selected) {
return Make<MenuBase>(entries, selected);
}
// static
MenuBase* MenuBase::From(Component component) {
return static_cast<MenuBase*>(component.get());
}
MenuBase::MenuBase(const std::vector<std::wstring>* entries, int* selected)
: entries_(entries), selected_(selected) {}
Element MenuBase::Render() {
Elements elements; Elements elements;
bool is_menu_focused = Focused(); bool is_menu_focused = Focused();
boxes_.resize(entries.size()); boxes_.resize(entries_->size());
for (size_t i = 0; i < entries.size(); ++i) { for (size_t i = 0; i < entries_->size(); ++i) {
bool is_focused = (focused == int(i)) && is_menu_focused; bool is_focused = (focused == int(i)) && is_menu_focused;
bool is_selected = (selected == int(i)); bool is_selected = (*selected_ == int(i));
auto style = is_selected auto style = is_selected
? (is_focused ? selected_focused_style : selected_style) ? (is_focused ? selected_focused_style : selected_style)
@ -26,13 +65,13 @@ Element Menu::Render() {
: is_menu_focused ? focus : is_menu_focused ? focus
: select; : select;
auto icon = is_selected ? L"> " : L" "; auto icon = is_selected ? L"> " : L" ";
elements.push_back(text(icon + entries[i]) | style | focus_management | elements.push_back(text(icon + entries_->at(i)) | style | focus_management |
reflect(boxes_[i])); reflect(boxes_[i]));
} }
return vbox(std::move(elements)); return vbox(std::move(elements));
} }
bool Menu::OnEvent(Event event) { bool MenuBase::OnEvent(Event event) {
if (!CaptureMouse(event)) if (!CaptureMouse(event))
return false; return false;
if (event.is_mouse()) if (event.is_mouse())
@ -41,20 +80,20 @@ bool Menu::OnEvent(Event event) {
if (!Focused()) if (!Focused())
return false; return false;
int old_selected = selected; int old_selected = *selected_;
if (event == Event::ArrowUp || event == Event::Character('k')) if (event == Event::ArrowUp || event == Event::Character('k'))
selected--; (*selected_)--;
if (event == Event::ArrowDown || event == Event::Character('j')) if (event == Event::ArrowDown || event == Event::Character('j'))
selected++; (*selected_)++;
if (event == Event::Tab && entries.size()) if (event == Event::Tab && entries_->size())
selected = (selected + 1) % entries.size(); *selected_ = (*selected_ + 1) % entries_->size();
if (event == Event::TabReverse && entries.size()) if (event == Event::TabReverse && entries_->size())
selected = (selected + entries.size() - 1) % entries.size(); *selected_ = (*selected_ + entries_->size() - 1) % entries_->size();
selected = std::max(0, std::min(int(entries.size()) - 1, selected)); *selected_ = std::max(0, std::min(int(entries_->size()) - 1, *selected_));
if (selected != old_selected) { if (*selected_ != old_selected) {
focused = selected; focused = *selected_;
on_change(); on_change();
return true; return true;
} }
@ -67,7 +106,7 @@ bool Menu::OnEvent(Event event) {
return false; return false;
} }
bool Menu::OnMouseEvent(Event event) { bool MenuBase::OnMouseEvent(Event event) {
if (!CaptureMouse(event)) if (!CaptureMouse(event))
return false; return false;
for (int i = 0; i < boxes_.size(); ++i) { for (int i = 0; i < boxes_.size(); ++i) {
@ -78,8 +117,8 @@ bool Menu::OnMouseEvent(Event event) {
focused = i; focused = i;
if (event.mouse().button == Mouse::Left && if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Released) { event.mouse().motion == Mouse::Released) {
if (selected != i) { if (*selected_ != i) {
selected = i; *selected_ = i;
on_change(); on_change();
} }
return true; return true;

View File

@ -1,36 +1,76 @@
#include <stddef.h> // for size_t
#include <algorithm> // for max, min
#include <functional> // for function
#include <memory> // for shared_ptr, allocator_traits<>::value_type
#include <utility> // for move
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowUp, Event::Return, Event::Tab, Event::TabReverse
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
#include "ftxui/component/radiobox.hpp" #include "ftxui/component/radiobox.hpp"
#include "ftxui/component/screen_interactive.hpp" // for Component
#include <stddef.h>
#include <algorithm>
#include <functional>
#include <memory>
#include <utility>
#include "ftxui/component/captured_mouse.hpp"
#include "ftxui/component/mouse.hpp"
#include "ftxui/component/screen_interactive.hpp"
namespace ftxui { namespace ftxui {
Element RadioBox::Render() { /// @brief A list of element, where only one can be selected.
/// @param entries The list of entries in the list.
/// @param selected The index of the currently selected element.
/// @ingroup component
/// @see RadioboxBase
///
/// ### Example
///
/// ```cpp
/// auto screen = ScreenInteractive::TerminalOutput();
/// std::vector<std::wstring> entries = {
/// L"entry 1",
/// L"entry 2",
/// L"entry 3",
/// };
/// int selected = 0;
/// auto menu = Radiobox(&entries, &selected);
/// screen.Loop(menu);
/// ```
///
/// ### Output
///
/// ```bash
/// ◉ entry 1
/// ○ entry 2
/// ○ entry 3
/// ```
Component Radiobox(const std::vector<std::wstring>* entries, int* selected) {
return Make<RadioboxBase>(entries, selected);
}
// static
RadioboxBase* RadioboxBase::From(Component component) {
return static_cast<RadioboxBase*>(component.get());
}
RadioboxBase::RadioboxBase(const std::vector<std::wstring>* entries,
int* selected_)
: entries_(entries), selected_(selected_) {}
Element RadioboxBase::Render() {
std::vector<Element> elements; std::vector<Element> elements;
bool is_focused = Focused(); bool is_focused = Focused();
boxes_.resize(entries.size()); boxes_.resize(entries_->size());
for (size_t i = 0; i < entries.size(); ++i) { for (size_t i = 0; i < entries_->size(); ++i) {
auto style = auto style =
(focused == int(i) && is_focused) ? focused_style : unfocused_style; (focused == int(i) && is_focused) ? focused_style : unfocused_style;
auto focus_management = (focused != int(i)) ? nothing auto focus_management = (focused != int(i)) ? nothing
: is_focused ? focus : is_focused ? focus
: select; : select;
const std::wstring& symbol = selected == int(i) ? checked : unchecked; const std::wstring& symbol = *selected_ == int(i) ? checked : unchecked;
elements.push_back(hbox(text(symbol), text(entries[i]) | style) | elements.push_back(hbox(text(symbol), text(entries_->at(i)) | style) |
focus_management | reflect(boxes_[i])); focus_management | reflect(boxes_[i]));
} }
return vbox(std::move(elements)); return vbox(std::move(elements));
} }
bool RadioBox::OnEvent(Event event) { bool RadioboxBase::OnEvent(Event event) {
if (!CaptureMouse(event)) if (!CaptureMouse(event))
return false; return false;
if (event.is_mouse()) if (event.is_mouse())
@ -44,12 +84,12 @@ bool RadioBox::OnEvent(Event event) {
new_focused--; new_focused--;
if (event == Event::ArrowDown || event == Event::Character('j')) if (event == Event::ArrowDown || event == Event::Character('j'))
new_focused++; new_focused++;
if (event == Event::Tab && entries.size()) if (event == Event::Tab && entries_->size())
new_focused = (new_focused + 1) % entries.size(); new_focused = (new_focused + 1) % entries_->size();
if (event == Event::TabReverse && entries.size()) if (event == Event::TabReverse && entries_->size())
new_focused = (new_focused + entries.size() - 1) % entries.size(); new_focused = (new_focused + entries_->size() - 1) % entries_->size();
new_focused = std::max(0, std::min(int(entries.size()) - 1, new_focused)); new_focused = std::max(0, std::min(int(entries_->size()) - 1, new_focused));
if (focused != new_focused) { if (focused != new_focused) {
focused = new_focused; focused = new_focused;
@ -57,14 +97,14 @@ bool RadioBox::OnEvent(Event event) {
} }
if (event == Event::Character(' ') || event == Event::Return) { if (event == Event::Character(' ') || event == Event::Return) {
selected = focused; *selected_ = focused;
on_change(); on_change();
} }
return false; return false;
} }
bool RadioBox::OnMouseEvent(Event event) { bool RadioboxBase::OnMouseEvent(Event event) {
if (!CaptureMouse(event)) if (!CaptureMouse(event))
return false; return false;
for (int i = 0; i < boxes_.size(); ++i) { for (int i = 0; i < boxes_.size(); ++i) {
@ -78,8 +118,8 @@ bool RadioBox::OnMouseEvent(Event event) {
event.mouse().motion == Mouse::Pressed) { event.mouse().motion == Mouse::Pressed) {
cursor_position = i; cursor_position = i;
TakeFocus(); TakeFocus();
if (selected != i) { if (*selected_ != i) {
selected = i; *selected_ = i;
on_change(); on_change();
} }
return true; return true;

View File

@ -1,7 +1,8 @@
#include <gtest/gtest-message.h> // for Message #include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl #include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <memory> // for __shared_ptr_access, shared_ptr
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowUp, Event::Tab, Event::TabReverse #include "ftxui/component/event.hpp" // for Event, Event::Return, Event::ArrowDown, Event::ArrowUp, Event::Tab, Event::TabReverse
#include "ftxui/component/mouse.hpp" // for ftxui #include "ftxui/component/mouse.hpp" // for ftxui
#include "ftxui/component/radiobox.hpp" #include "ftxui/component/radiobox.hpp"
#include "gtest/gtest_pred_impl.h" // for EXPECT_EQ, Test, TEST #include "gtest/gtest_pred_impl.h" // for EXPECT_EQ, Test, TEST
@ -9,76 +10,105 @@
using namespace ftxui; using namespace ftxui;
TEST(RadioboxTest, Navigation) { TEST(RadioboxTest, Navigation) {
RadioBox radiobox; int selected = 0;
radiobox.entries = {L"1", L"2", L"3"}; std::vector<std::wstring> entries = {L"1", L"2", L"3"};
auto radiobox = Radiobox(&entries, &selected);
// With arrow key. // With arrow key.
EXPECT_EQ(radiobox.focused, 0); EXPECT_EQ(selected, 0);
radiobox.OnEvent(Event::ArrowDown); radiobox->OnEvent(Event::ArrowDown);
EXPECT_EQ(radiobox.focused, 1); radiobox->OnEvent(Event::Return);
radiobox.OnEvent(Event::ArrowDown); EXPECT_EQ(selected, 1);
EXPECT_EQ(radiobox.focused, 2); radiobox->OnEvent(Event::ArrowDown);
radiobox.OnEvent(Event::ArrowDown); radiobox->OnEvent(Event::Return);
EXPECT_EQ(radiobox.focused, 2); EXPECT_EQ(selected, 2);
radiobox.OnEvent(Event::ArrowUp); radiobox->OnEvent(Event::ArrowDown);
EXPECT_EQ(radiobox.focused, 1); radiobox->OnEvent(Event::Return);
radiobox.OnEvent(Event::ArrowUp); EXPECT_EQ(selected, 2);
EXPECT_EQ(radiobox.focused, 0); radiobox->OnEvent(Event::ArrowUp);
radiobox.OnEvent(Event::ArrowUp); radiobox->OnEvent(Event::Return);
EXPECT_EQ(radiobox.focused, 0); EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::ArrowUp);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 0);
radiobox->OnEvent(Event::ArrowUp);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 0);
// With vim like characters. // With vim like characters.
EXPECT_EQ(radiobox.focused, 0); EXPECT_EQ(selected, 0);
radiobox.OnEvent(Event::Character('j')); radiobox->OnEvent(Event::Character('j'));
EXPECT_EQ(radiobox.focused, 1); radiobox->OnEvent(Event::Return);
radiobox.OnEvent(Event::Character('j')); EXPECT_EQ(selected, 1);
EXPECT_EQ(radiobox.focused, 2); radiobox->OnEvent(Event::Character('j'));
radiobox.OnEvent(Event::Character('j')); radiobox->OnEvent(Event::Return);
EXPECT_EQ(radiobox.focused, 2); EXPECT_EQ(selected, 2);
radiobox.OnEvent(Event::Character('k')); radiobox->OnEvent(Event::Character('j'));
EXPECT_EQ(radiobox.focused, 1); radiobox->OnEvent(Event::Return);
radiobox.OnEvent(Event::Character('k')); EXPECT_EQ(selected, 2);
EXPECT_EQ(radiobox.focused, 0); radiobox->OnEvent(Event::Character('k'));
radiobox.OnEvent(Event::Character('k')); radiobox->OnEvent(Event::Return);
EXPECT_EQ(radiobox.focused, 0); EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::Character('k'));
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 0);
radiobox->OnEvent(Event::Character('k'));
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 0);
// With more entries // With more entries
radiobox.entries = {L"1", L"2", L"3"}; entries = {L"1", L"2", L"3"};
EXPECT_EQ(radiobox.focused, 0); EXPECT_EQ(selected, 0);
radiobox.OnEvent(Event::ArrowDown); radiobox->OnEvent(Event::ArrowDown);
EXPECT_EQ(radiobox.focused, 1); radiobox->OnEvent(Event::Return);
radiobox.OnEvent(Event::ArrowDown); EXPECT_EQ(selected, 1);
EXPECT_EQ(radiobox.focused, 2); radiobox->OnEvent(Event::ArrowDown);
radiobox.OnEvent(Event::ArrowDown); radiobox->OnEvent(Event::Return);
EXPECT_EQ(radiobox.focused, 2); EXPECT_EQ(selected, 2);
radiobox.OnEvent(Event::ArrowUp); radiobox->OnEvent(Event::ArrowDown);
EXPECT_EQ(radiobox.focused, 1); radiobox->OnEvent(Event::Return);
radiobox.OnEvent(Event::ArrowUp); EXPECT_EQ(selected, 2);
EXPECT_EQ(radiobox.focused, 0); radiobox->OnEvent(Event::ArrowUp);
radiobox.OnEvent(Event::ArrowUp); radiobox->OnEvent(Event::Return);
EXPECT_EQ(radiobox.focused, 0); EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::ArrowUp);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 0);
radiobox->OnEvent(Event::ArrowUp);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 0);
// With tab. // With tab.
EXPECT_EQ(radiobox.focused, 0); EXPECT_EQ(selected, 0);
radiobox.OnEvent(Event::Tab); radiobox->OnEvent(Event::Tab);
EXPECT_EQ(radiobox.focused, 1); radiobox->OnEvent(Event::Return);
radiobox.OnEvent(Event::Tab); EXPECT_EQ(selected, 1);
EXPECT_EQ(radiobox.focused, 2); radiobox->OnEvent(Event::Tab);
radiobox.OnEvent(Event::Tab); radiobox->OnEvent(Event::Return);
EXPECT_EQ(radiobox.focused, 0); EXPECT_EQ(selected, 2);
radiobox.OnEvent(Event::Tab); radiobox->OnEvent(Event::Tab);
EXPECT_EQ(radiobox.focused, 1); radiobox->OnEvent(Event::Return);
radiobox.OnEvent(Event::Tab); EXPECT_EQ(selected, 0);
EXPECT_EQ(radiobox.focused, 2); radiobox->OnEvent(Event::Tab);
radiobox.OnEvent(Event::TabReverse); radiobox->OnEvent(Event::Return);
EXPECT_EQ(radiobox.focused, 1); EXPECT_EQ(selected, 1);
radiobox.OnEvent(Event::TabReverse); radiobox->OnEvent(Event::Tab);
EXPECT_EQ(radiobox.focused, 0); radiobox->OnEvent(Event::Return);
radiobox.OnEvent(Event::TabReverse); EXPECT_EQ(selected, 2);
EXPECT_EQ(radiobox.focused, 2); radiobox->OnEvent(Event::TabReverse);
radiobox.OnEvent(Event::TabReverse); radiobox->OnEvent(Event::Return);
EXPECT_EQ(radiobox.focused, 1); EXPECT_EQ(selected, 1);
radiobox.OnEvent(Event::TabReverse); radiobox->OnEvent(Event::TabReverse);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 0);
radiobox->OnEvent(Event::TabReverse);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 2);
radiobox->OnEvent(Event::TabReverse);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::TabReverse);
radiobox->OnEvent(Event::Return);
} }
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -0,0 +1,78 @@
#include <functional> // for function
#include <memory> // for __shared_ptr_access
#include <utility> // for move
#include "ftxui/component/component.hpp" // for Component, Make, Renderer
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/dom/elements.hpp" // for Element
namespace ftxui {
/// @brief A component rendering Element from a function.
/// @ingroup component.
class RendererBase : public ComponentBase {
public:
// Access this interface from a Component
static RendererBase* From(Component component) {
return static_cast<RendererBase*>(component.get());
}
// Constructor.
RendererBase(std::function<Element()> render) : render_(std::move(render)) {}
~RendererBase() override = default;
// Component implementation.
Element Render() override { return render_(); }
protected:
std::function<Element()> render_;
};
/// @brief Return a component, using |render| to render its interface.
/// @param render The function drawing the interface.
/// @ingroup component
///
/// ### Example
///
/// ```cpp
/// auto screen = ScreenInteractive::TerminalOutput();
/// auto renderer = Renderer([] {
/// return text(L"My interface");
/// });
/// screen.Loop(renderer);
/// ```
Component Renderer(std::function<Element()> render) {
return Make<RendererBase>(std::move(render));
}
/// @brief Return a new Component, similar to |child|, but using |render| as the
/// Component::Render() event.
/// @param child The component to forward events to.
/// @param render The function drawing the interface.
/// @ingroup component
///
/// ### Example
///
/// ```cpp
/// auto screen = ScreenInteractive::TerminalOutput();
/// std::wstring label = "Click to quit";
/// auto button = Button(&label, screen.ExitLoopClosure());
/// auto renderer = Renderer(button, [&] {
/// return hbox({
/// text("A button:"),
/// button->Render(),
/// });
/// });
/// screen.Loop(renderer);
/// ```
Component Renderer(Component child, std::function<Element()> render) {
Component renderer = Renderer(std::move(render));
renderer->Add(std::move(child));
return renderer;
}
} // namespace ftxui
// Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@ -1,24 +1,23 @@
#include "ftxui/component/screen_interactive.hpp"
#include <stdio.h> // for fileno, stdin #include <stdio.h> // for fileno, stdin
#include <algorithm> // for copy, max, min #include <algorithm> // for copy, max, min
#include <csignal> // for signal, SIGINT #include <csignal> // for signal, SIGINT, SIGWINCH
#include <cstdlib> // for exit, NULL #include <cstdlib> // for exit, NULL
#include <iostream> // for cout, ostream #include <iostream> // for cout, ostream, basic_ostream, operator<<, endl, flush
#include <stack> // for stack #include <stack> // for stack
#include <thread> // for thread #include <thread> // for thread
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
#include "ftxui/component/component.hpp" // for Component #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/event.hpp" // for Event #include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/mouse.hpp" // for Mouse #include "ftxui/component/mouse.hpp" // for Mouse
#include "ftxui/component/receiver.hpp" // for ReceiverImpl #include "ftxui/component/receiver.hpp" // for ReceiverImpl, SenderImpl, MakeReceiver
#include "ftxui/component/terminal_input_parser.hpp" // for TerminalInputPa... #include "ftxui/component/screen_interactive.hpp"
#include "ftxui/component/terminal_input_parser.hpp" // for TerminalInputParser
#include "ftxui/dom/node.hpp" // for Node, Render #include "ftxui/dom/node.hpp" // for Node, Render
#include "ftxui/dom/requirement.hpp" // for Requirement #include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/terminal.hpp" // for Terminal::Dimen... #include "ftxui/screen/terminal.hpp" // for Terminal::Dimensions, Terminal
#if defined(_WIN32) #if defined(_WIN32)
#define DEFINE_CONSOLEV2_PROPERTIES #define DEFINE_CONSOLEV2_PROPERTIES
@ -31,8 +30,8 @@
#error Must be compiled in UNICODE mode #error Must be compiled in UNICODE mode
#endif #endif
#else #else
#include <sys/select.h> // for select, FD_ISSET #include <sys/select.h> // for select, FD_ISSET, FD_SET, FD_ZERO, fd_set
#include <termios.h> // for tcsetattr, tcge... #include <termios.h> // for tcsetattr, tcgetattr, cc_t
#include <unistd.h> // for STDIN_FILENO, read #include <unistd.h> // for STDIN_FILENO, read
#endif #endif
@ -110,7 +109,7 @@ void EventListener(std::atomic<bool>* quit, Sender<Event> out) {
char c; char c;
while (!*quit) { while (!*quit) {
while(read(STDIN_FILENO, &c, 1), c) while (read(STDIN_FILENO, &c, 1), c)
parser.Add(c); parser.Add(c);
emscripten_sleep(1); emscripten_sleep(1);
@ -278,7 +277,7 @@ CapturedMouse ScreenInteractive::CaptureMouse() {
[this] { mouse_captured = false; }); [this] { mouse_captured = false; });
} }
void ScreenInteractive::Loop(Component* component) { void ScreenInteractive::Loop(Component component) {
// Install a SIGINT handler and restore the old handler on exit. // Install a SIGINT handler and restore the old handler on exit.
auto old_sigint_handler = std::signal(SIGINT, OnExit); auto old_sigint_handler = std::signal(SIGINT, OnExit);
on_exit_functions.push( on_exit_functions.push(
@ -417,7 +416,7 @@ void ScreenInteractive::Loop(Component* component) {
OnExit(0); OnExit(0);
} }
void ScreenInteractive::Draw(Component* component) { void ScreenInteractive::Draw(Component component) {
auto document = component->Render(); auto document = component->Render();
int dimx = 0; int dimx = 0;
int dimy = 0; int dimy = 0;

View File

@ -1,21 +1,23 @@
#include "ftxui/component/slider.hpp" #include <string> // for allocator
#include <utility> // for move
#include <memory> #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include <utility> #include "ftxui/component/component.hpp" // for Make, Slider
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/captured_mouse.hpp" #include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight
#include "ftxui/component/mouse.hpp" #include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
#include "ftxui/component/screen_interactive.hpp" #include "ftxui/component/screen_interactive.hpp" // for Component
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp" // for Element, text, color, operator|, xflex, gauge, dim, hbox, reflect, underlined, vcenter
#include "ftxui/screen/box.hpp" #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/color.hpp" #include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::GrayLight
#include "ftxui/screen/string.hpp" // for StringRef
namespace ftxui { namespace ftxui {
template <class T> template <class T>
class SliderImpl : public Component { class SliderBase : public ComponentBase {
public: public:
SliderImpl(std::wstring label, T* value, T min, T max, T increment) SliderBase(StringRef label, T* value, T min, T max, T increment)
: label_(label), : label_(label),
value_(value), value_(value),
min_(min), min_(min),
@ -27,7 +29,7 @@ class SliderImpl : public Component {
Focused() ? color(Color::GrayLight) : color(Color::GrayDark); Focused() ? color(Color::GrayLight) : color(Color::GrayDark);
float percent = float(*value_ - min_) / float(max_ - min_); float percent = float(*value_ - min_) / float(max_ - min_);
return hbox({ return hbox({
text(label_) | dim | vcenter, text(*label_) | dim | vcenter,
hbox({ hbox({
text(L"["), text(L"["),
gauge(percent) | underlined | xflex | reflect(gauge_box_), gauge(percent) | underlined | xflex | reflect(gauge_box_),
@ -53,7 +55,7 @@ class SliderImpl : public Component {
return true; return true;
} }
return Component::OnEvent(event); return ComponentBase::OnEvent(event);
} }
bool OnMouseEvent(Event event) { bool OnMouseEvent(Event event) {
@ -62,8 +64,7 @@ class SliderImpl : public Component {
return true; return true;
} }
if (box_.Contain(event.mouse().x, event.mouse().y) && if (box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event)) {
CaptureMouse(event)) {
TakeFocus(); TakeFocus();
} }
@ -84,7 +85,7 @@ class SliderImpl : public Component {
} }
private: private:
std::wstring label_; StringRef label_;
T* value_; T* value_;
T min_; T min_;
T max_; T max_;
@ -94,23 +95,44 @@ class SliderImpl : public Component {
CapturedMouse captured_mouse_; CapturedMouse captured_mouse_;
}; };
/// @brief An horizontal slider.
/// @param label The name of the slider.
/// @param value The current value of the slider.
/// @param min The minimum value.
/// @param max The maximum value.
/// @param increment The increment when used by the cursor.
/// @ingroup component
///
/// ### Example
///
/// ```cpp
/// auto screen = ScreenInteractive::TerminalOutput();
/// int value = 50;
/// auto slider = Slider(L"Value:", &value, 0, 100, 1);
/// screen.Loop(slider);
/// ```
///
/// ### Output
///
/// ```bash
/// Value:[██████████████████████████ ]
/// ```
template <class T> template <class T>
ComponentPtr Slider(std::wstring label, T* value, T min, T max, T increment) { Component Slider(StringRef label, T* value, T min, T max, T increment) {
return std::make_unique<SliderImpl<T>>(std::move(label), value, min, max, return Make<SliderBase<T>>(std::move(label), value, min, max, increment);
increment);
} }
template ComponentPtr Slider(std::wstring label, template Component Slider(StringRef label,
int* value, int* value,
int min, int min,
int max, int max,
int increment); int increment);
template ComponentPtr Slider(std::wstring label, template Component Slider(StringRef label,
float* value, float* value,
float min, float min,
float max, float max,
float increment); float increment);
} // namespace ftxui } // namespace ftxui

View File

@ -1,7 +1,10 @@
#include "ftxui/component/terminal_input_parser.hpp" #include "ftxui/component/terminal_input_parser.hpp"
#include <utility> #include <algorithm> // for max
#include "ftxui/component/event.hpp" #include <memory> // for unique_ptr
#include <utility> // for move
#include "ftxui/component/event.hpp" // for Event
namespace ftxui { namespace ftxui {

View File

@ -5,11 +5,12 @@
#include <string> // for string #include <string> // for string
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/event.hpp" // IWYU pragma: keep #include "ftxui/component/event.hpp" // for Event (ptr only)
#include "ftxui/component/mouse.hpp" // for Mouse #include "ftxui/component/mouse.hpp" // for Mouse
#include "ftxui/component/receiver.hpp" // for SenderImpl #include "ftxui/component/receiver.hpp" // for Sender
namespace ftxui { namespace ftxui {
struct Event;
// Parse a sequence of |char| accross |time|. Produces |Event|. // Parse a sequence of |char| accross |time|. Produces |Event|.
class TerminalInputParser { class TerminalInputParser {

View File

@ -1,9 +1,12 @@
#include <gtest/gtest-message.h> // for Message #include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult #include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <algorithm> // for max
#include <memory> // for unique_ptr, allocator
#include "ftxui/component/event.hpp" // for Event, Event::Escape
#include "ftxui/component/receiver.hpp" // for MakeReceiver, ReceiverImpl #include "ftxui/component/receiver.hpp" // for MakeReceiver, ReceiverImpl
#include "ftxui/component/terminal_input_parser.hpp" #include "ftxui/component/terminal_input_parser.hpp"
#include "gtest/gtest_pred_impl.h" // for AssertionResult, Test, Suite... #include "gtest/gtest_pred_impl.h" // for AssertionResult, Test, EXPECT_EQ, EXPECT_TRUE, EXPECT_FALSE, TEST
using namespace ftxui; using namespace ftxui;

View File

@ -1,26 +1,38 @@
#include <stddef.h> // for size_t #include <stddef.h> // for size_t
#include <algorithm> // for max, min #include <algorithm> // for max, min
#include <memory> // for shared_ptr, alloca... #include <memory> // for shared_ptr, allocator_traits<>::value_type
#include <utility> // for move #include <utility> // for move
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left #include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Return, Event::Tab, Event::TabReverse
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
#include "ftxui/component/toggle.hpp" #include "ftxui/component/toggle.hpp"
namespace ftxui { namespace ftxui {
Element Toggle::Render() { Component Toggle(const std::vector<std::wstring>* entries, int* selected) {
return Make<ToggleBase>(entries, selected);
}
// static
ToggleBase* ToggleBase::From(Component component) {
return static_cast<ToggleBase*>(component.get());
}
ToggleBase::ToggleBase(const std::vector<std::wstring>* entries, int* selected)
: entries_(entries), selected_(selected) {}
Element ToggleBase::Render() {
Elements children; Elements children;
bool is_toggle_focused = Focused(); bool is_toggle_focused = Focused();
boxes_.resize(entries.size()); boxes_.resize(entries_->size());
for (size_t i = 0; i < entries.size(); ++i) { for (size_t i = 0; i < entries_->size(); ++i) {
// Separator. // Separator.
if (i != 0) if (i != 0)
children.push_back(separator()); children.push_back(separator());
bool is_focused = (focused == int(i)) && is_toggle_focused; bool is_focused = (focused == int(i)) && is_toggle_focused;
bool is_selected = (selected == int(i)); bool is_selected = (*selected_ == int(i));
auto style = is_selected auto style = is_selected
? (is_focused ? selected_focused_style : selected_style) ? (is_focused ? selected_focused_style : selected_style)
@ -28,30 +40,30 @@ Element Toggle::Render() {
auto focus_management = !is_selected ? nothing auto focus_management = !is_selected ? nothing
: is_toggle_focused ? focus : is_toggle_focused ? focus
: select; : select;
children.push_back(text(entries[i]) | style | focus_management | children.push_back(text(entries_->at(i)) | style | focus_management |
reflect(boxes_[i])); reflect(boxes_[i]));
} }
return hbox(std::move(children)); return hbox(std::move(children));
} }
bool Toggle::OnEvent(Event event) { bool ToggleBase::OnEvent(Event event) {
if (event.is_mouse()) if (event.is_mouse())
return OnMouseEvent(event); return OnMouseEvent(event);
int old_selected = selected; int old_selected = *selected_;
if (event == Event::ArrowLeft || event == Event::Character('h')) if (event == Event::ArrowLeft || event == Event::Character('h'))
selected--; (*selected_)--;
if (event == Event::ArrowRight || event == Event::Character('l')) if (event == Event::ArrowRight || event == Event::Character('l'))
selected++; (*selected_)++;
if (event == Event::Tab && entries.size()) if (event == Event::Tab && entries_->size())
selected = (selected + 1) % entries.size(); *selected_ = (*selected_ + 1) % entries_->size();
if (event == Event::TabReverse && entries.size()) if (event == Event::TabReverse && entries_->size())
selected = (selected + entries.size() - 1) % entries.size(); *selected_ = (*selected_ + entries_->size() - 1) % entries_->size();
selected = std::max(0, std::min(int(entries.size()) - 1, selected)); *selected_ = std::max(0, std::min(int(entries_->size()) - 1, *selected_));
if (old_selected != selected) { if (old_selected != *selected_) {
focused = selected; focused = *selected_;
on_change(); on_change();
return true; return true;
} }
@ -64,7 +76,7 @@ bool Toggle::OnEvent(Event event) {
return false; return false;
} }
bool Toggle::OnMouseEvent(Event event) { bool ToggleBase::OnMouseEvent(Event event) {
if (!CaptureMouse(event)) if (!CaptureMouse(event))
return false; return false;
for (int i = 0; i < boxes_.size(); ++i) { for (int i = 0; i < boxes_.size(); ++i) {
@ -76,8 +88,8 @@ bool Toggle::OnMouseEvent(Event event) {
if (event.mouse().button == Mouse::Left && if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) { event.mouse().motion == Mouse::Pressed) {
TakeFocus(); TakeFocus();
if (selected != i) { if (*selected_ != i) {
selected = i; *selected_ = i;
on_change(); on_change();
} }
return true; return true;

View File

@ -1,5 +1,6 @@
#include <gtest/gtest-message.h> // for Message #include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl #include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <memory> // for __shared_ptr_access, shared_ptr
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Return, Event::Tab, Event::TabReverse #include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Return, Event::Tab, Event::TabReverse
#include "ftxui/component/mouse.hpp" // for ftxui #include "ftxui/component/mouse.hpp" // for ftxui
@ -9,131 +10,136 @@
using namespace ftxui; using namespace ftxui;
TEST(ToggleTest, leftRightArrow) { TEST(ToggleTest, leftRightArrow) {
Toggle toggle; std::vector<std::wstring> entries = {L"On", L"Off"};
int selected = 0;
auto toggle = Toggle(&entries, &selected);
// With arrow key. // With arrow key.
EXPECT_EQ(toggle.selected, 0); EXPECT_EQ(selected, 0);
toggle.OnEvent(Event::ArrowRight); toggle->OnEvent(Event::ArrowRight);
EXPECT_EQ(toggle.selected, 1); EXPECT_EQ(selected, 1);
toggle.OnEvent(Event::ArrowRight); toggle->OnEvent(Event::ArrowRight);
EXPECT_EQ(toggle.selected, 1); EXPECT_EQ(selected, 1);
toggle.OnEvent(Event::ArrowLeft); toggle->OnEvent(Event::ArrowLeft);
EXPECT_EQ(toggle.selected, 0); EXPECT_EQ(selected, 0);
toggle.OnEvent(Event::ArrowLeft); toggle->OnEvent(Event::ArrowLeft);
EXPECT_EQ(toggle.selected, 0); EXPECT_EQ(selected, 0);
// With vim like characters. // With vim like characters.
EXPECT_EQ(toggle.selected, 0); EXPECT_EQ(selected, 0);
toggle.OnEvent(Event::Character('l')); toggle->OnEvent(Event::Character('l'));
EXPECT_EQ(toggle.selected, 1); EXPECT_EQ(selected, 1);
toggle.OnEvent(Event::Character('l')); toggle->OnEvent(Event::Character('l'));
EXPECT_EQ(toggle.selected, 1); EXPECT_EQ(selected, 1);
toggle.OnEvent(Event::Character('h')); toggle->OnEvent(Event::Character('h'));
EXPECT_EQ(toggle.selected, 0); EXPECT_EQ(selected, 0);
toggle.OnEvent(Event::Character('h')); toggle->OnEvent(Event::Character('h'));
EXPECT_EQ(toggle.selected, 0); EXPECT_EQ(selected, 0);
// With more entries // With more entries
toggle.entries = {L"1", L"2", L"3"}; entries = {L"1", L"2", L"3"};
EXPECT_EQ(toggle.selected, 0); EXPECT_EQ(selected, 0);
toggle.OnEvent(Event::ArrowRight); toggle->OnEvent(Event::ArrowRight);
EXPECT_EQ(toggle.selected, 1); EXPECT_EQ(selected, 1);
toggle.OnEvent(Event::ArrowRight); toggle->OnEvent(Event::ArrowRight);
EXPECT_EQ(toggle.selected, 2); EXPECT_EQ(selected, 2);
toggle.OnEvent(Event::ArrowRight); toggle->OnEvent(Event::ArrowRight);
EXPECT_EQ(toggle.selected, 2); EXPECT_EQ(selected, 2);
toggle.OnEvent(Event::ArrowLeft); toggle->OnEvent(Event::ArrowLeft);
EXPECT_EQ(toggle.selected, 1); EXPECT_EQ(selected, 1);
toggle.OnEvent(Event::ArrowLeft); toggle->OnEvent(Event::ArrowLeft);
EXPECT_EQ(toggle.selected, 0); EXPECT_EQ(selected, 0);
toggle.OnEvent(Event::ArrowLeft); toggle->OnEvent(Event::ArrowLeft);
EXPECT_EQ(toggle.selected, 0); EXPECT_EQ(selected, 0);
} }
TEST(ToggleTest, Tab) { TEST(ToggleTest, Tab) {
Toggle toggle; std::vector<std::wstring> entries = {L"1", L"2", L"3"};
toggle.entries = {L"1", L"2", L"3"}; int selected = 0;
auto toggle = Toggle(&entries, &selected);
EXPECT_EQ(toggle.selected, 0); EXPECT_EQ(selected, 0);
toggle.OnEvent(Event::Tab); toggle->OnEvent(Event::Tab);
EXPECT_EQ(toggle.selected, 1); EXPECT_EQ(selected, 1);
toggle.OnEvent(Event::Tab); toggle->OnEvent(Event::Tab);
EXPECT_EQ(toggle.selected, 2); EXPECT_EQ(selected, 2);
toggle.OnEvent(Event::Tab); toggle->OnEvent(Event::Tab);
EXPECT_EQ(toggle.selected, 0); EXPECT_EQ(selected, 0);
toggle.OnEvent(Event::Tab); toggle->OnEvent(Event::Tab);
EXPECT_EQ(toggle.selected, 1); EXPECT_EQ(selected, 1);
toggle.OnEvent(Event::Tab); toggle->OnEvent(Event::Tab);
EXPECT_EQ(toggle.selected, 2); EXPECT_EQ(selected, 2);
toggle.OnEvent(Event::TabReverse); toggle->OnEvent(Event::TabReverse);
EXPECT_EQ(toggle.selected, 1); EXPECT_EQ(selected, 1);
toggle.OnEvent(Event::TabReverse); toggle->OnEvent(Event::TabReverse);
EXPECT_EQ(toggle.selected, 0); EXPECT_EQ(selected, 0);
toggle.OnEvent(Event::TabReverse); toggle->OnEvent(Event::TabReverse);
EXPECT_EQ(toggle.selected, 2); EXPECT_EQ(selected, 2);
toggle.OnEvent(Event::TabReverse); toggle->OnEvent(Event::TabReverse);
EXPECT_EQ(toggle.selected, 1); EXPECT_EQ(selected, 1);
toggle.OnEvent(Event::TabReverse); toggle->OnEvent(Event::TabReverse);
} }
TEST(ToggleTest, OnChange) { TEST(ToggleTest, OnChange) {
Toggle toggle; std::vector<std::wstring> entries = {L"1", L"2", L"3"};
toggle.entries = {L"1", L"2", L"3"}; int selected = 0;
auto toggle = Toggle(&entries, &selected);
int counter = 0; int counter = 0;
toggle.on_change = [&] { counter++; }; ToggleBase::From(toggle)->on_change = [&] { counter++; };
EXPECT_FALSE(toggle.OnEvent(Event::ArrowLeft)); // Reached far left. EXPECT_FALSE(toggle->OnEvent(Event::ArrowLeft)); // Reached far left.
EXPECT_EQ(counter, 0); EXPECT_EQ(counter, 0);
EXPECT_TRUE(toggle.OnEvent(Event::ArrowRight)); // [0] -> [1] EXPECT_TRUE(toggle->OnEvent(Event::ArrowRight)); // [0] -> [1]
EXPECT_EQ(counter, 1); EXPECT_EQ(counter, 1);
EXPECT_TRUE(toggle.OnEvent(Event::ArrowRight)); // [1] -> [2] EXPECT_TRUE(toggle->OnEvent(Event::ArrowRight)); // [1] -> [2]
EXPECT_EQ(counter, 2); EXPECT_EQ(counter, 2);
EXPECT_FALSE(toggle.OnEvent(Event::ArrowRight)); // Reached far right. EXPECT_FALSE(toggle->OnEvent(Event::ArrowRight)); // Reached far right.
EXPECT_EQ(counter, 2); EXPECT_EQ(counter, 2);
EXPECT_TRUE(toggle.OnEvent(Event::ArrowLeft)); // [2] -> [1] EXPECT_TRUE(toggle->OnEvent(Event::ArrowLeft)); // [2] -> [1]
EXPECT_EQ(counter, 3); EXPECT_EQ(counter, 3);
EXPECT_TRUE(toggle.OnEvent(Event::ArrowLeft)); // [1] -> [0] EXPECT_TRUE(toggle->OnEvent(Event::ArrowLeft)); // [1] -> [0]
EXPECT_EQ(counter, 4); EXPECT_EQ(counter, 4);
EXPECT_FALSE(toggle.OnEvent(Event::ArrowLeft)); // Reached far left. EXPECT_FALSE(toggle->OnEvent(Event::ArrowLeft)); // Reached far left.
EXPECT_EQ(counter, 4); EXPECT_EQ(counter, 4);
} }
TEST(ToggleTest, OnEnter) { TEST(ToggleTest, OnEnter) {
Toggle toggle; std::vector<std::wstring> entries = {L"1", L"2", L"3"};
toggle.entries = {L"1", L"2", L"3"}; int selected = 0;
auto toggle = Toggle(&entries, &selected);
int counter = 0; int counter = 0;
toggle.on_enter = [&] { counter++; }; ToggleBase::From(toggle)->on_enter = [&] { counter++; };
EXPECT_FALSE(toggle.OnEvent(Event::ArrowLeft)); // Reached far left. EXPECT_FALSE(toggle->OnEvent(Event::ArrowLeft)); // Reached far left.
EXPECT_TRUE(toggle.OnEvent(Event::Return)); EXPECT_TRUE(toggle->OnEvent(Event::Return));
EXPECT_EQ(counter, 1); EXPECT_EQ(counter, 1);
EXPECT_TRUE(toggle.OnEvent(Event::ArrowRight)); // [0] -> [1] EXPECT_TRUE(toggle->OnEvent(Event::ArrowRight)); // [0] -> [1]
EXPECT_TRUE(toggle.OnEvent(Event::Return)); EXPECT_TRUE(toggle->OnEvent(Event::Return));
EXPECT_EQ(counter, 2); EXPECT_EQ(counter, 2);
EXPECT_TRUE(toggle.OnEvent(Event::ArrowRight)); // [1] -> [2] EXPECT_TRUE(toggle->OnEvent(Event::ArrowRight)); // [1] -> [2]
EXPECT_TRUE(toggle.OnEvent(Event::Return)); EXPECT_TRUE(toggle->OnEvent(Event::Return));
EXPECT_EQ(counter, 3); EXPECT_EQ(counter, 3);
EXPECT_FALSE(toggle.OnEvent(Event::ArrowRight)); // Reached far right. EXPECT_FALSE(toggle->OnEvent(Event::ArrowRight)); // Reached far right.
EXPECT_TRUE(toggle.OnEvent(Event::Return)); EXPECT_TRUE(toggle->OnEvent(Event::Return));
EXPECT_EQ(counter, 4); EXPECT_EQ(counter, 4);
EXPECT_TRUE(toggle.OnEvent(Event::ArrowLeft)); // [2] -> [1] EXPECT_TRUE(toggle->OnEvent(Event::ArrowLeft)); // [2] -> [1]
EXPECT_TRUE(toggle.OnEvent(Event::Return)); EXPECT_TRUE(toggle->OnEvent(Event::Return));
EXPECT_EQ(counter, 5); EXPECT_EQ(counter, 5);
EXPECT_TRUE(toggle.OnEvent(Event::ArrowLeft)); // [1] -> [0] EXPECT_TRUE(toggle->OnEvent(Event::ArrowLeft)); // [1] -> [0]
EXPECT_TRUE(toggle.OnEvent(Event::Return)); EXPECT_TRUE(toggle->OnEvent(Event::Return));
EXPECT_EQ(counter, 6); EXPECT_EQ(counter, 6);
EXPECT_FALSE(toggle.OnEvent(Event::ArrowLeft)); // Reached far left. EXPECT_FALSE(toggle->OnEvent(Event::ArrowLeft)); // Reached far left.
EXPECT_TRUE(toggle.OnEvent(Event::Return)); EXPECT_TRUE(toggle->OnEvent(Event::Return));
EXPECT_EQ(counter, 7); EXPECT_EQ(counter, 7);
} }

View File

@ -1,10 +1,11 @@
#include <memory> #include <memory> // for make_shared
#include <utility> // for move
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, blink
#include "ftxui/dom/node.hpp" #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/node_decorator.hpp" #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp" #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" #include "ftxui/screen/screen.hpp" // for Pixel, Screen
namespace ftxui { namespace ftxui {

View File

@ -1,10 +1,11 @@
#include <memory> #include <memory> // for make_shared
#include <utility> // for move
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, bold
#include "ftxui/dom/node.hpp" #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/node_decorator.hpp" #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp" #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" #include "ftxui/screen/screen.hpp" // for Pixel, Screen
namespace ftxui { namespace ftxui {

View File

@ -2,7 +2,7 @@
#include <iterator> // for begin, end #include <iterator> // for begin, end
#include <memory> // for make_shared, __shared_ptr_access #include <memory> // for make_shared, __shared_ptr_access
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector #include <vector> // for vector, __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for unpack, Element, Decorator, Elements, border, borderWith, window #include "ftxui/dom/elements.hpp" // for unpack, Element, Decorator, Elements, border, borderWith, window
#include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/node.hpp" // for Node

View File

@ -1,10 +1,11 @@
#include <memory> #include <memory> // for make_shared
#include <utility> // for move
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, clear_under
#include "ftxui/dom/node.hpp" #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/node_decorator.hpp" #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp" #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" #include "ftxui/screen/screen.hpp" // for Pixel, Screen
namespace ftxui { namespace ftxui {

View File

@ -1,10 +1,11 @@
#include <memory> #include <memory> // for make_shared
#include <utility> // for move
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp" // for Element, unpack, Decorator, Elements, bgcolor, color
#include "ftxui/dom/node_decorator.hpp" #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp" #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/color.hpp" #include "ftxui/screen/color.hpp" // for Color
#include "ftxui/screen/screen.hpp" #include "ftxui/screen/screen.hpp" // for Pixel, Screen
namespace ftxui { namespace ftxui {

View File

@ -1,5 +1,7 @@
#include "ftxui/dom/elements.hpp" #include <utility> // for move
#include "ftxui/dom/elements.hpp" // for Element, filler, operator|, hbox, flex_grow, vbox, xflex_grow, yflex_grow, align_right, center, hcenter, vcenter
namespace ftxui { namespace ftxui {

View File

@ -1,10 +1,11 @@
#include <memory> #include <memory> // for make_shared
#include <utility> // for move
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, dim
#include "ftxui/dom/node.hpp" #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/node_decorator.hpp" #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp" #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" #include "ftxui/screen/screen.hpp" // for Pixel, Screen
namespace ftxui { namespace ftxui {

View File

@ -1,10 +1,11 @@
#include <memory> #include <memory> // for make_shared, __shared_ptr_access
#include <vector> #include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp" // for Element, unpack, filler, flex, flex_grow, flex_shrink, notflex, xflex, xflex_grow, xflex_shrink, yflex, yflex_grow, yflex_shrink
#include "ftxui/dom/node.hpp" #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/requirement.hpp" #include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" #include "ftxui/screen/box.hpp" // for Box
namespace ftxui { namespace ftxui {

View File

@ -1,7 +1,7 @@
#include <algorithm> // for max, min #include <algorithm> // for max, min
#include <memory> // for make_shared, shared_ptr, __shared_ptr_access #include <memory> // for make_shared, shared_ptr, __shared_ptr_access
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector #include <vector> // for vector, __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for Element, unpack, focus, frame, select, xframe, yframe #include "ftxui/dom/elements.hpp" // for Element, unpack, focus, frame, select, xframe, yframe
#include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/node.hpp" // for Node

View File

@ -1,9 +1,10 @@
#include <memory> #include <memory> // for make_shared
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp" // for Element, gauge
#include "ftxui/dom/requirement.hpp" #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/screen/box.hpp" #include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/screen.hpp" #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" // for Screen
namespace ftxui { namespace ftxui {

View File

@ -1,10 +1,11 @@
#include <memory> #include <memory> // for make_shared
#include <utility> #include <utility> // for move
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, inverted
#include "ftxui/dom/node_decorator.hpp" #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/screen/box.hpp" #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/screen.hpp" #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" // for Pixel, Screen
namespace ftxui { namespace ftxui {

View File

@ -1,5 +1,5 @@
#include <memory> // for __shared_ptr_access #include <memory> // for __shared_ptr_access
#include <vector> // for vector #include <vector> // for __alloc_traits<>::value_type, vector
#include "ftxui/dom/node_decorator.hpp" #include "ftxui/dom/node_decorator.hpp"
#include "ftxui/dom/requirement.hpp" // for Requirement #include "ftxui/dom/requirement.hpp" // for Requirement

View File

@ -1,11 +1,11 @@
#include <memory> #include <memory> // for make_shared, __shared_ptr_access
#include <utility> #include <utility> // for move
#include <vector> #include <vector> // for __alloc_traits<>::value_type, vector
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp" // for Element, unpack, Decorator, reflect
#include "ftxui/dom/node.hpp" #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/requirement.hpp" #include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" #include "ftxui/screen/box.hpp" // for Box
namespace ftxui { namespace ftxui {

View File

@ -1,9 +1,10 @@
#include <memory> #include <memory> // for make_shared
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp" // for Element, separator
#include "ftxui/dom/node.hpp" #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/screen/box.hpp" #include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/screen.hpp" #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" // for Pixel, Screen
namespace ftxui { namespace ftxui {

View File

@ -2,7 +2,7 @@
#include <algorithm> // for min, max #include <algorithm> // for min, max
#include <memory> // for make_shared, __shared_ptr_access #include <memory> // for make_shared, __shared_ptr_access
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector #include <vector> // for __alloc_traits<>::value_type, vector
#include "ftxui/dom/elements.hpp" // for Constraint, Direction, EQUAL, GREATER_THAN, LESS_THAN, WIDTH, unpack, Decorator, Element, size #include "ftxui/dom/elements.hpp" // for Constraint, Direction, EQUAL, GREATER_THAN, LESS_THAN, WIDTH, unpack, Decorator, Element, size
#include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/node.hpp" // for Node

View File

@ -1,9 +1,10 @@
#include <stddef.h> #include <stddef.h> // for size_t
#include <memory> #include <memory> // for allocator, allocator_traits<>::value_type
#include <string> #include <string> // for basic_string, wstring
#include <vector> #include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp" // for Element, gauge, text, vbox, spinner
namespace ftxui { namespace ftxui {

View File

@ -1,11 +1,12 @@
#include <gtest/gtest-message.h> // for Message #include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult #include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl, TestPartResult
#include <memory> // for allocator #include <memory> // for allocator
#include "ftxui/dom/elements.hpp" // for text, Element, operator|, border #include "ftxui/dom/elements.hpp" // for text, Element, operator|, border
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui #include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/screen.hpp" // for Screen #include "ftxui/screen/screen.hpp" // for Screen
#include "gtest/gtest_pred_impl.h" // for Test, SuiteApiResolver, EXPECT_EQ #include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
using namespace ftxui; using namespace ftxui;

View File

@ -1,10 +1,11 @@
#include <memory> #include <memory> // for make_shared
#include <utility> #include <utility> // for move
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, underlined
#include "ftxui/dom/node_decorator.hpp" #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/screen/box.hpp" #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/screen.hpp" #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" // for Pixel, Screen
namespace ftxui { namespace ftxui {

View File

@ -1,5 +1,6 @@
#include <functional> // for function #include <functional> // for function
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for Decorator, Element, Elements, operator|, nothing #include "ftxui/dom/elements.hpp" // for Decorator, Element, Elements, operator|, nothing

View File

@ -25,6 +25,30 @@ std::wstring to_wstring(const std::string& s) {
#pragma warning(pop) #pragma warning(pop)
#endif #endif
StringRef::StringRef(std::wstring& ref) : borrowed_(&ref) {}
StringRef::StringRef(std::wstring* ref) : borrowed_(ref) {}
StringRef::StringRef(const wchar_t* ref) : owned_(ref) {}
StringRef::StringRef(const char* ref) : owned_(to_wstring(std::string(ref))) {}
std::wstring& StringRef::operator*() {
return borrowed_ ? *borrowed_ : owned_;
}
std::wstring* StringRef::operator->() {
return borrowed_ ? borrowed_ : &owned_;
}
ConstStringRef::ConstStringRef(const std::wstring& ref) : borrowed_(&ref) {}
ConstStringRef::ConstStringRef(const std::wstring* ref) : borrowed_(ref) {}
ConstStringRef::ConstStringRef(const wchar_t* ref) : owned_(ref) {}
ConstStringRef::ConstStringRef(const char* ref)
: owned_(to_wstring(std::string(ref))) {}
const std::wstring& ConstStringRef::operator*() {
return borrowed_ ? *borrowed_ : owned_;
}
const std::wstring* ConstStringRef::operator->() {
return borrowed_ ? borrowed_ : &owned_;
}
} // namespace ftxui } // namespace ftxui
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -6,6 +6,8 @@ mkdir -p iwyu
cd iwyu cd iwyu
rm * -rf rm * -rf
echo $CMAKE_CXX_INCLUDE_WHAT_YOU_USE echo $CMAKE_CXX_INCLUDE_WHAT_YOU_USE
cmake .. -DFTXUI_BUILD_TESTS=ON -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="iwyu;-Xiwyu;--cxx17ns;-Xiwyu;--mapping_file=${mapping_dir}/iwyu.imp;-Xiwyu;--verbose=3" cmake .. -DFTXUI_BUILD_TESTS=ON -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--cxx17ns;-Xiwyu;--mapping_file=${mapping_dir}/iwyu.imp;-Xiwyu;--verbose=3"
make -j 2>out make -j 2>out
fix_include --comments < out fix_includes.py --comments < out
../tools/format.sh

View File

@ -1,4 +1,4 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.