47 Commits

Author SHA1 Message Date
ArthurSonzogni
634870f44d Avoid breaking changes. 2024-12-26 18:47:56 +01:00
Vemy
6900283afe Fix: Properly changing window title text color #940 (#961) 2024-12-26 18:47:56 +01:00
Dmitry Nefedov
a015d8b2d8 Clear terminal output of interactive screen on resize if alternate screen not in use (#952) 2024-12-26 18:47:56 +01:00
Brian
aae4e55e43 Fixed typo on border (#956)
Fixed minor issue in function name
2024-12-26 18:47:56 +01:00
Boris Jaulmes
8e25a75b73 Allow a Dimension::Fit to extend beyond the terminal maximum height (#950)
For long tables (and other DOM elements), one may want the screen to render on dimensions higher than the terminal.  
Hence, this PR proposes a way to do so, with an optional parameter in the `Dimension::Fit` util function.

Discussions / Issues :  
- https://github.com/ArthurSonzogni/FTXUI/issues/572
- https://github.com/ArthurSonzogni/FTXUI/discussions/949

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/572
Fixed:Bug:https://github.com/ArthurSonzogni/FTXUI/issues/572
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-12-26 18:47:56 +01:00
Mikołaj Lubiak
024ce3908e Add SliderWithCallback component (#938)
Add SliderOption::on_change.

Useful to observe a change to the value.

Signed-off-by: Mikołaj Lubiak <lubiak@proton.me>
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-12-26 18:47:54 +01:00
ArthurSonzogni
d5099afa18 Fix CQ failures. 2024-12-26 18:47:11 +01:00
ArthurSonzogni
6a790edb6b Quickfix 2024-12-26 18:47:11 +01:00
Arthur Sonzogni
0855d008df Apply Clang-tidy (#918) 2024-12-26 18:47:09 +01:00
Arthur Sonzogni
995a33ac89 Table: support initializer list constructor. (#915)
To avoid burdening the user with explicit type construction when using
the library, we can use a constructor that accepts an initializer list
(std::initializer_list). This allows users to pass initializer lists
directly without having to wrap them in
std::vector<std::vector<std::string>>. This resolves the ambiguous case
when the inner list contains only two elements.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/912
2024-12-26 18:46:49 +01:00
Paolo Bosetti
f35dc7b4c9 Added -fPIC compile option (#913)
Added -fPIC compile option.
2024-12-26 18:46:49 +01:00
LiAuTraver
b05ff6a518 add missing include guard for screen/pixel.hpp (#890) 2024-12-26 18:46:49 +01:00
Arthur Sonzogni
128e7215df Color alpha support. (#884) 2024-12-26 18:46:49 +01:00
ArthurSonzogni
67984b2afd Fix Color::HSV(h,0,v)
There was a problem when v==0
2024-12-26 18:46:48 +01:00
Felix
43cf8e7a94 Solve issues with atomic copy (#867) 2024-12-26 18:46:48 +01:00
Arthur Sonzogni
697671d9ed Dropdown: Fix title not updated. (#851)
A bug was introduced by:
https://github.com/ArthurSonzogni/FTXUI/pull/826

The checkbox label wasn't updated.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/861
2024-12-26 18:46:48 +01:00
ArthurSonzogni
3a51d782ef Dropdown: Fix title not updated.
A bug was introduced by:
https://github.com/ArthurSonzogni/FTXUI/pull/826

The checkbox label wasn't updated.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/861
2024-12-26 18:46:48 +01:00
ccn
1b2017e6f5 Update index.html (#858)
correct spelling
2024-12-26 18:46:48 +01:00
ccn
abddaa0c0a Update homescreen.cpp (#859)
fix typo
2024-12-26 18:46:48 +01:00
ccn
306d1b6d3b Update flex.cpp (#860)
fix typo
2024-12-26 18:46:48 +01:00
Arthur Sonzogni
343e3ab226 Generate compile commands for clangd. (#855)
Fix all the diagnostics reported.

Bug: https://github.com/ArthurSonzogni/FTXUI/issues/828
2024-12-26 18:46:48 +01:00
Arthur Sonzogni
e3eb8b1cb7 Fix Menu focus. (#850)
Bug:https://github.com/ArthurSonzogni/FTXUI/issues/841
2024-12-26 18:46:48 +01:00
Jørn Gustav Larsen
5daedf79ad Enable raw keyboard input (#832)
In order for applications to receive all keyboard inputs, including the
Ctrl-C and Ctrl-Z, the raw input mode has been enabled. As result the
SIGINT will no longer be used, instead the keyboard Ctrl-C event is used
for exiting the framework, but only if no components has made use of it.

Co-authored-by: Jørn Gustav Larsen <jgl@fasttracksoftware.com>
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-12-26 18:46:48 +01:00
Mark Antabi
9b6c4a7b4b Allow user to specify window element border. (#849) 2024-12-26 18:46:47 +01:00
ArthurSonzogni
9beb235c0e Apply clang-tidy. 2024-12-26 18:46:47 +01:00
Clancy Walters
2a69cd75d5 Prefer Exit() over OnExit() (#847)
This is a no-op patch, but prefered, because this centralize the exit path below `Exit()`.
2024-12-26 18:46:47 +01:00
Arthur Sonzogni
03e6685df5 Flush before applying a new configuration. (#848)
This avoids an ordering problem with whatever the user printed and
interacting with termios/WinAPI.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/846
2024-12-26 18:46:47 +01:00
Dimo Markov
a006bcafe1 Separate a reusable Image class from Screen (#834)
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-12-26 18:46:47 +01:00
na-trium-144
57ebf6c8c1 Fix ResizableSplit handling keyboard navigation incorrectly (#842)
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-12-26 18:46:47 +01:00
ArthurSonzogni
231c1dfd56 Fix minor compile error. 2024-12-26 18:46:47 +01:00
James
3b6e0d5a38 Feature: Dropdown options with callback (#826)
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-12-26 18:46:45 +01:00
Jørn Gustav Larsen
d8617ec2b6 Problem with setting the cursor position on the right screen edge when drawing. (#831)
When moving the cursor back to its original location, a problem arises when cursor placed in the right edge column, where an off by one error occur. This pull request will resolve this problem.

Co-authored-by: Jørn Gustav Larsen <jgl@fasttracksoftware.com>
Co-authored-by: Jørn Gustav Larsen <jgl@adminbyrequest.com>
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-12-26 18:46:18 +01:00
Arthur Sonzogni
f81c5d94a5 Revert change to button example. (#835)
It was introduced mistakenly by:
f495ce029c
2024-12-26 18:46:16 +01:00
Arthur Sonzogni
fed24da54e Update mainpage.md
Fixed: https://github.com/ArthurSonzogni/FTXUI/issues/812
2024-12-26 18:45:44 +01:00
rio
c8c3f8311e Make Checkbox take focus when clicked (#810) 2024-12-26 18:45:44 +01:00
Arthur Sonzogni
6039aedfcc Button: invoke on_click at the end. (#807)
Some users might destroy `this`, which would result in UAF.

In the future, we should consider alternatives like posting a task to
the main loop, or rely on users for this.

Fixed:https://github.com/ArthurSonzogni/FTXUI/issues/804
2024-12-26 18:45:44 +01:00
nyako
2759cfab8d ftxui_set_options: properly check the current compiler. (#802)
This solve the issue encountered when using clang under MSVC.
2024-12-26 18:45:44 +01:00
Particle_G
aceabdb4d4 Add missing Checkbox() implementation (#796)
Fix: #795
2024-12-26 18:45:44 +01:00
Arthur Sonzogni
1d797eeed4 Restore cursor shape on exit. (#793) (#794)
Fixed: https://github.com/ArthurSonzogni/FTXUI/issues/792
2024-12-26 18:45:44 +01:00
Arthur Sonzogni
6618d099f5 Restore cursor shape on exit. (#793)
Fixed: https://github.com/ArthurSonzogni/FTXUI/issues/792
2024-12-26 18:45:43 +01:00
ArthurSonzogni
4dd9d4b1d0 Fix default for ScreenInteractive::Fullscreen()
It was intended to open gthe alternate screen.
2024-12-26 18:45:43 +01:00
Arthur Sonzogni
d6918c6cb1 feature: allow fullscreen without alternative screen (#777)
This should solve #766

The original PR was:
#767

Co-authored-by: rbrugo <brugo.riccardo@gmail.com>
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-12-26 18:45:43 +01:00
Clément Roblot
64436fc52b Checkbox button debounce (#774)
This fixes: https://github.com/ArthurSonzogni/FTXUI/issues/773

Dragging the mouse with the left button pressed now avoids activating multiple
checkboxes.

Add support for detecting mouse press transition. Added:
```cpp
// The previous mouse event.
Mouse Mouse::previous;

// Return whether the mouse transitionned from:
// released to pressed => IsPressed()
// pressed to pressed => IsHeld()
// pressed to released => IsReleased()
bool Mouse::IsPressed(Button button) const;
bool Mouse::IsHeld(Button button) const;
bool Mouse::IsReleased(Button button) const;
```
A couple of components are now activated when the mouse is pressed,
as opposed to released.

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-12-26 18:45:43 +01:00
chrysante
19dd2afa16 Fix Input onchange not called (#776) 2024-12-26 18:45:43 +01:00
benoitdudu
37259aceaf fix the doxygen documentation by moving comments at the right place (#768) 2024-12-26 18:45:43 +01:00
Clément Roblot
cc998af4d8 Scrollbar coloring (#755)
This a proposed MR to fix #754. While building the scroll bar the pixels were completely reseted thus canceling any style previously applied to said pixels. This MR removes this resetting of the pixels and leaves only the drawing of the shape of the scroll bar.
2024-12-26 18:45:43 +01:00
Arthur Sonzogni
19ffc37696 Feature: hscroll_indicator (#753)
This is the symetrical of `vscroll_indicator`.

Requested by @ibrahimnasson.

Fixed:https://github.com/ArthurSonzogni/FTXUI/issues/752
2024-12-26 18:45:41 +01:00
120 changed files with 2569 additions and 873 deletions

View File

@@ -10,6 +10,7 @@ Checks: "*,
-android-*,
-bugprone-easily-swappable-parameters,
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-pro-type-union-access,
-fuchsia-*,
-google-*,
-hicpp-signed-bitwise,
@@ -26,6 +27,7 @@ Checks: "*,
-readability-simplify-boolean-expr,
-readability-static-accessed-through-instance,
-readability-use-anyofallof,
-readability-avoid-nested-conditional-operator,
-zircon-*,
"
WarningsAsErrors: ''

View File

@@ -2,8 +2,6 @@ name: Build
on:
create:
tags:
-v*
push:
branches:
- main
@@ -28,10 +26,11 @@ jobs:
compiler: llvm
gcov_executable: "llvm-cov gcov"
- name: MacOS clang
os: macos-latest
compiler: llvm
gcov_executable: "llvm-cov gcov"
# https://github.com/aminya/setup-cpp/issues/246
#- name: MacOS clang
#os: macos-latest
#compiler: llvm
#gcov_executable: "llvm-cov gcov"
- name: Windows MSVC
os: windows-latest
@@ -44,7 +43,7 @@ jobs:
id: cpu-cores
- name: "Checkout repository"
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: "Setup Cpp"
uses: aminya/setup-cpp@v1
@@ -79,11 +78,16 @@ jobs:
cmake
--build ./build
- name: Unix - Test and coverage
- name: Unix - Test
if: runner.os != 'Windows'
working-directory: ./build
run: >
ctest -C Debug --rerun-failed --output-on-failure;
- name: Unix - coverage
if: runner.os != 'Windows'
working-directory: ./build
run: >
gcovr
-j ${{env.nproc}}
--delete
@@ -155,7 +159,7 @@ jobs:
id: cpu-cores
- name: "Checkout repository"
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: "Install cmake"
uses: lukka/get-cmake@latest
@@ -186,7 +190,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: "Checkout repository"
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: "Install cmake"
uses: lukka/get-cmake@latest

4
.gitignore vendored
View File

@@ -3,6 +3,10 @@
*
!*/
# Ignore build directories generated by default MSVC CMake integration
# (otherwise causes terribly slow indexing)
out/
# Allowed top-level files:
!.clang-format
!.clang-tidy

View File

@@ -5,8 +5,57 @@ current (development)
---------------------
### Component
- Feature: Add support for raw input. Allowing more keys to be detected.
- Feature: Add `ScreenInteractive::ForceHandleCtrlC(false)` to allow component
to fully override the default `Ctrl+C` handler.
- Feature: Add `ScreenInteractive::ForceHandleCtrlZ(false)` to allow component
to fully override the default `Ctrl+Z` handler.
- Feature: Add `Mouse::WeelLeft` and `Mouse::WeelRight` events on supported
terminals.
- Feature: Add `Event::DebugString()`.
- Feature: Add support for `Input`'s insert mode. Add `InputOption::insert`
option. Added by @mingsheng13.
- Feature: Add `DropdownOption` to configure the dropdown. See #826.
- Bugfix/Breaking change: `Mouse transition`:
- Detect when the mouse move, as opposed to being pressed.
The Mouse::Moved motion was added.
- Dragging the mouse with the left button pressed now avoids activating
multiple checkboxes.
- A couple of components are now activated when the mouse is pressed,
as opposed to being released.
This fixes: https://github.com/ArthurSonzogni/FTXUI/issues/773
This fixes: https://github.com/ArthurSonzogni/FTXUI/issues/792
- Bugfix: mouse.control is now reported correctly.
- Feature: Add `ScreenInteractive::FullscreenPrimaryScreen()`. This allows
displaying a fullscreen component on the primary screen, as opposed to the
alternate screen.
- Bugfix: `Input` `onchange` was not called on backspace or delete key.
Fixed by @chrysante in chrysante in PR #776.
- Bugfix: Propertly restore cursor shape on exit. See #792.
- Bugfix: Fix cursor position in when in the last column. See #831.
- Bugfix: Fix `ResizeableSplit` keyboard navigation. Fixed by #842.
- Bugfix: Fix `Menu` focus. See #841
- Feature: Add `SliderOption::on_change`. This allows to set a callback when the
slider value changes. See #938.
### Dom
- Feature: Add `hscroll_indicator`. It display an horizontal indicator
reflecting the current scroll position. Proposed by @ibrahimnasson in
[issue 752](https://github.com/ArthurSonzogni/FTXUI/issues/752)
- Feature: Add `extend_beyond_screen` option to `Dimension::Fit(..)`, allowing
the element to be larger than the screen. Proposed by @LordWhiro. See #572 and
#949.
### Screen
- Feature: Add `Box::IsEmpty()`.
- Feature: Color transparency
- Add `Color::RGBA(r,g,b,a)`.
- Add `Color::HSVA(r,g,b,a)`.
- Add `Color::Blend(Color)`.
- Add `Color::IsOpaque()`
### Util
- Feature: Support arbitrary `Adapter` for `ConstStringListRef`. See #843.
### Build
- Support for cmake's "unity/jumbo" builds. Fixed by @ClausKlein.

View File

@@ -27,17 +27,22 @@ else()
${FTXUI_MICROSOFT_TERMINAL_FALLBACK_HELP_TEXT} OFF)
endif()
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
include(cmake/ftxui_message.cmake)
add_library(screen
include/ftxui/screen/box.hpp
include/ftxui/screen/color.hpp
include/ftxui/screen/color_info.hpp
include/ftxui/screen/image.hpp
include/ftxui/screen/pixel.hpp
include/ftxui/screen/screen.hpp
include/ftxui/screen/string.hpp
src/ftxui/screen/box.cpp
src/ftxui/screen/color.cpp
src/ftxui/screen/color_info.cpp
src/ftxui/screen/image.cpp
src/ftxui/screen/screen.cpp
src/ftxui/screen/string.cpp
src/ftxui/screen/terminal.cpp

View File

@@ -32,7 +32,7 @@ A simple cross-platform C++ library for terminal based user interfaces!
## Feature
* Functional style. Inspired by
[[1]](https://hackernoon.com/building-reactive-terminal-interfaces-in-c-d392ce34e649?gi=d9fb9ce35901)
[1](https://hackernoon.com/building-reactive-terminal-interfaces-in-c-d392ce34e649?gi=d9fb9ce35901)
and [React](https://reactjs.org/)
* Simple and elegant syntax (in my opinion)
* Keyboard & mouse navigation.

View File

@@ -45,10 +45,16 @@ function(ftxui_set_options library)
# Force Microsoft Visual Studio to decode sources files in UTF-8. This applies
# to the library and the library users.
if (MSVC)
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(${library} PUBLIC "/utf-8")
endif()
# CMake does automatically add -fPIC when linking a shared library, but it
# does not add it when linking a static library. This is a problem when the
# static library is later linked into a shared library.
# Doing it helps some users.
set_property(TARGET ${library} PROPERTY POSITION_INDEPENDENT_CODE ON)
# Add as many warning as possible:
if (WIN32)
if (MSVC)

View File

@@ -18,6 +18,7 @@ add_executable(ftxui-tests
src/ftxui/component/menu_test.cpp
src/ftxui/component/modal_test.cpp
src/ftxui/component/radiobox_test.cpp
src/ftxui/util/ref_test.cpp
src/ftxui/component/receiver_test.cpp
src/ftxui/component/resizable_split_test.cpp
src/ftxui/component/screen_interactive_test.cpp

View File

@@ -67,6 +67,7 @@ set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE)
FetchContent_Declare(ftxui
GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui
# Important: Specify a GIT_TAG XXXXX here.
GIT_TAG main
)
FetchContent_GetProperties(ftxui)

View File

@@ -11,6 +11,7 @@ example(collapsible)
example(composition)
example(custom_loop)
example(dropdown)
example(dropdown_custom)
example(flexbox_gallery)
example(focus)
example(focus_cursor)
@@ -25,6 +26,7 @@ example(menu2)
example(menu_entries)
example(menu_entries_animated)
example(menu_in_frame)
example(menu_in_frame_horizontal)
example(menu_multiple)
example(menu_style)
example(menu_underline_animated_gallery)

View File

@@ -12,13 +12,40 @@
using namespace ftxui;
// This is a helper function to create a button with a custom style.
// The style is defined by a lambda function that takes an EntryState and
// returns an Element.
// We are using `center` to center the text inside the button, then `border` to
// add a border around the button, and finally `flex` to make the button fill
// the available space.
ButtonOption Style() {
auto option = ButtonOption::Animated();
option.transform = [](const EntryState& s) {
auto element = text(s.label);
if (s.focused) {
element |= bold;
}
return element | center | borderEmpty | flex;
};
return option;
}
int main() {
int value = 50;
// clang-format off
auto btn_dec_01 = Button("-1", [&] { value += 1; }, Style());
auto btn_inc_01 = Button("+1", [&] { value -= 1; }, Style());
auto btn_dec_10 = Button("-10", [&] { value -= 10; }, Style());
auto btn_inc_10 = Button("+10", [&] { value += 10; }, Style());
// clang-format on
// The tree of components. This defines how to navigate using the keyboard.
auto buttons = Container::Horizontal({
Button("Decrease", [&] { value--; }),
Button("Increase", [&] { value++; }),
// The selected `row` is shared to get a grid layout.
int row = 0;
auto buttons = Container::Vertical({
Container::Horizontal({btn_dec_01, btn_inc_01}, &row) | flex,
Container::Horizontal({btn_dec_10, btn_inc_10}, &row) | flex,
});
// Modify the way to render them on screen:

View File

@@ -0,0 +1,104 @@
// 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.
#include <string> // for basic_string, string, allocator
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Dropdown, Horizontal, Vertical
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
int main() {
using namespace ftxui;
std::vector<std::string> entries = {
"tribute", "clearance", "ally", "bend", "electronics",
"module", "era", "cultural", "sniff", "nationalism",
"negotiation", "deliver", "figure", "east", "tribute",
"clearance", "ally", "bend", "electronics", "module",
"era", "cultural", "sniff", "nationalism", "negotiation",
"deliver", "figure", "east", "tribute", "clearance",
"ally", "bend", "electronics", "module", "era",
"cultural", "sniff", "nationalism", "negotiation", "deliver",
"figure", "east",
};
auto dropdown_1 = Dropdown({
.radiobox = {.entries = &entries},
.transform =
[](bool open, Element checkbox, Element radiobox) {
if (open) {
return vbox({
checkbox | inverted,
radiobox | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, 10),
filler(),
});
}
return vbox({
checkbox,
filler(),
});
},
});
auto dropdown_2 = Dropdown({
.radiobox = {.entries = &entries},
.transform =
[](bool open, Element checkbox, Element radiobox) {
if (open) {
return vbox({
checkbox | inverted,
radiobox | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, 10) | bgcolor(Color::Blue),
filler(),
});
}
return vbox({
checkbox | bgcolor(Color::Blue),
filler(),
});
},
});
auto dropdown_3 = Dropdown({
.radiobox =
{
.entries = &entries,
.transform =
[](const EntryState& s) {
auto t = text(s.label) | borderEmpty;
if (s.active) {
t |= bold;
}
if (s.focused) {
t |= inverted;
}
return t;
},
},
.transform =
[](bool open, Element checkbox, Element radiobox) {
checkbox |= borderEmpty;
if (open) {
return vbox({
checkbox | inverted,
radiobox | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, 20) | bgcolor(Color::Red),
filler(),
});
}
return vbox({
checkbox | bgcolor(Color::Red),
filler(),
});
},
});
auto screen = ScreenInteractive::FitComponent();
screen.Loop(Container::Horizontal({
dropdown_1,
dropdown_2,
dropdown_3,
}));
}

View File

@@ -264,7 +264,7 @@ int main() {
});
// ---------------------------------------------------------------------------
// Spiner
// Spinner
// ---------------------------------------------------------------------------
auto spinner_tab_renderer = Renderer([&] {
Elements entries;
@@ -490,8 +490,14 @@ int main() {
},
&tab_index);
auto exit_button =
Button("Exit", [&] { screen.Exit(); }, ButtonOption::Animated());
auto main_container = Container::Vertical({
tab_selection,
Container::Horizontal({
tab_selection,
exit_button,
}),
tab_content,
});

View File

@@ -0,0 +1,30 @@
// 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.
#include <memory> // for shared_ptr, __shared_ptr_access
#include <string> // for string, basic_string, operator+, to_string
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#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/dom/elements.hpp" // for operator|, Element, size, border, frame, HEIGHT, LESS_THAN
using namespace ftxui;
int main() {
std::vector<std::string> entries;
int selected = 0;
for (int i = 0; i < 100; ++i)
entries.push_back(std::to_string(i));
auto radiobox = Menu(&entries, &selected, MenuOption::Horizontal());
auto renderer = Renderer(
radiobox, [&] { return radiobox->Render() | hscroll_indicator | frame; });
auto screen = ScreenInteractive::FitComponent();
screen.Loop(renderer);
return 0;
}

View File

@@ -18,120 +18,12 @@
using namespace ftxui;
std::string Stringify(Event event) {
std::string out;
for (auto& it : event.input())
out += " " + std::to_string((unsigned int)it);
out = "(" + out + " ) -> ";
if (event.is_character()) {
out += "Event::Character(\"" + event.character() + "\")";
} else if (event.is_mouse()) {
out += "mouse";
switch (event.mouse().button) {
case Mouse::Left:
out += "_left";
break;
case Mouse::Middle:
out += "_middle";
break;
case Mouse::Right:
out += "_right";
break;
case Mouse::None:
out += "_none";
break;
case Mouse::WheelUp:
out += "_wheel_up";
break;
case Mouse::WheelDown:
out += "_wheel_down";
break;
}
switch (event.mouse().motion) {
case Mouse::Pressed:
out += "_pressed";
break;
case Mouse::Released:
out += "_released";
break;
}
if (event.mouse().control)
out += "_control";
if (event.mouse().shift)
out += "_shift";
if (event.mouse().meta)
out += "_meta";
out += "(" + //
std::to_string(event.mouse().x) + "," +
std::to_string(event.mouse().y) + ")";
} else if (event == Event::ArrowLeft) {
out += "Event::ArrowLeft";
} else if (event == Event::ArrowRight) {
out += "Event::ArrowRight";
} else if (event == Event::ArrowUp) {
out += "Event::ArrowUp";
} else if (event == Event::ArrowDown) {
out += "Event::ArrowDown";
} else if (event == Event::ArrowLeftCtrl) {
out += "Event::ArrowLeftCtrl";
} else if (event == Event ::ArrowRightCtrl) {
out += "Event::ArrowRightCtrl";
} else if (event == Event::ArrowUpCtrl) {
out += "Event::ArrowUpCtrl";
} else if (event == Event::ArrowDownCtrl) {
out += "Event::ArrowDownCtrl";
} else if (event == Event::Backspace) {
out += "Event::Backspace";
} else if (event == Event::Delete) {
out += "Event::Delete";
} else if (event == Event::Escape) {
out += "Event::Escape";
} else if (event == Event::Return) {
out += "Event::Return";
} else if (event == Event::Tab) {
out += "Event::Tab";
} else if (event == Event::TabReverse) {
out += "Event::TabReverse";
} else if (event == Event::F1) {
out += "Event::F1";
} else if (event == Event::F2) {
out += "Event::F2";
} else if (event == Event::F3) {
out += "Event::F3";
} else if (event == Event::F4) {
out += "Event::F4";
} else if (event == Event::F5) {
out += "Event::F5";
} else if (event == Event::F6) {
out += "Event::F6";
} else if (event == Event::F7) {
out += "Event::F7";
} else if (event == Event::F8) {
out += "Event::F8";
} else if (event == Event::F9) {
out += "Event::F9";
} else if (event == Event::F10) {
out += "Event::F10";
} else if (event == Event::F11) {
out += "Event::F11";
} else if (event == Event::F12) {
out += "Event::F12";
} else if (event == Event::Home) {
out += "Event::Home";
} else if (event == Event::End) {
out += "Event::End";
} else if (event == Event::PageUp) {
out += "Event::PageUp";
} else if (event == Event::PageDown) {
out += "Event::PageDown";
} else if (event == Event::Custom) {
out += "Custom";
} else {
out += "(special)";
std::string Code(Event event) {
std::string codes;
for (auto& it : event.input()) {
codes += " " + std::to_string((unsigned int)it);
}
return out;
return codes;
}
int main() {
@@ -139,16 +31,35 @@ int main() {
std::vector<Event> keys;
auto component = Renderer([&] {
Elements children;
for (size_t i = std::max(0, (int)keys.size() - 20); i < keys.size(); ++i)
children.push_back(text(Stringify(keys[i])));
return window(text("keys"), vbox(std::move(children)));
auto left_column = Renderer([&] {
Elements children = {
text("Codes"),
separator(),
};
for (size_t i = std::max(0, (int)keys.size() - 20); i < keys.size(); ++i) {
children.push_back(text(Code(keys[i])));
}
return vbox(children);
});
auto right_column = Renderer([&] {
Elements children = {
text("Event"),
separator(),
};
for (size_t i = std::max(0, (int)keys.size() - 20); i < keys.size(); ++i) {
children.push_back(text(keys[i].DebugString()));
}
return vbox(children);
});
int split_size = 40;
auto component = ResizableSplitLeft(left_column, right_column, &split_size);
component |= border;
component |= CatchEvent([&](Event event) {
keys.push_back(event);
return true;
return false;
});
screen.Loop(component);

View File

@@ -55,7 +55,8 @@ int main() {
content.DecorateCellsAlternateRow(color(Color::White), 3, 2);
auto document = table.Render();
auto screen = Screen::Create(Dimension::Fit(document));
auto screen =
Screen::Create(Dimension::Fit(document, /*extend_beyond_screen=*/true));
Render(screen, document);
screen.Print();
std::cout << std::endl;

View File

@@ -24,8 +24,9 @@
<div class="page">
<h1>FTXUI WebAssembly Example </h1>
<p>
<a href="https://github.com/ArthurSonzogni/FTXUI">FTXUI</a> is a single
C++ library for terminal user interface.
<a href="https://github.com/ArthurSonzogni/FTXUI">FTXUI</a> is a simple
functional C++ library for terminal user interface. <br/>
This showcases the: <a href="https://github.com/ArthurSonzogni/FTXUI/tree/master/examples">./example/</a> folder. <br/>
</p>
<p>
On this page, you can try all the examples contained in: <a

View File

@@ -7,11 +7,7 @@
#include <chrono> // for milliseconds, duration, steady_clock, time_point
#include <functional> // for function
#include "ftxui/component/event.hpp"
namespace ftxui {
namespace animation {
namespace ftxui::animation {
// Components who haven't completed their animation can call this function to
// request a new frame to be drawn later.
//
@@ -26,7 +22,7 @@ using Duration = std::chrono::duration<float>;
// Parameter of Component::OnAnimation(param).
class Params {
public:
Params(Duration duration) : duration_(duration) {}
explicit Params(Duration duration) : duration_(duration) {}
/// The duration this animation step represents.
Duration duration() const { return duration_; }
@@ -93,11 +89,11 @@ float BounceInOut(float p);
class Animator {
public:
Animator(float* from,
float to = 0.f,
Duration duration = std::chrono::milliseconds(250),
easing::Function easing_function = easing::Linear,
Duration delay = std::chrono::milliseconds(0));
explicit Animator(float* from,
float to = 0.f,
Duration duration = std::chrono::milliseconds(250),
easing::Function easing_function = easing::Linear,
Duration delay = std::chrono::milliseconds(0));
void OnAnimation(Params&);
@@ -112,7 +108,6 @@ class Animator {
Duration current_;
};
} // namespace animation
} // namespace ftxui
} // namespace ftxui::animation
#endif /* end of include guard: FTXUI_ANIMATION_HPP */

View File

@@ -9,6 +9,11 @@
namespace ftxui {
class CapturedMouseInterface {
public:
CapturedMouseInterface() = default;
CapturedMouseInterface(const CapturedMouseInterface&) = default;
CapturedMouseInterface(CapturedMouseInterface&&) = delete;
CapturedMouseInterface& operator=(const CapturedMouseInterface&) = default;
CapturedMouseInterface& operator=(CapturedMouseInterface&&) = delete;
virtual ~CapturedMouseInterface() = default;
};
using CapturedMouse = std::unique_ptr<CapturedMouseInterface>;

View File

@@ -6,9 +6,7 @@
#include <functional> // for function
#include <memory> // for make_shared, shared_ptr
#include <string> // for wstring
#include <utility> // for forward
#include <vector> // for vector
#include "ftxui/component/component_base.hpp" // for Component, Components
#include "ftxui/component/component_options.hpp" // for ButtonOption, CheckboxOption, MenuOption
@@ -75,6 +73,8 @@ Component Radiobox(ConstStringListRef entries,
RadioboxOption options = {});
Component Dropdown(ConstStringListRef entries, int* selected);
Component Dropdown(DropdownOption options);
Component Toggle(ConstStringListRef entries, int* selected);
// General slider constructor:
@@ -94,9 +94,9 @@ Component Slider(ConstStringRef label,
ConstRef<float> increment = 5.f);
Component Slider(ConstStringRef label,
Ref<long> value,
ConstRef<long> min = 0l,
ConstRef<long> max = 100l,
ConstRef<long> increment = 5l);
ConstRef<long> min = 0L,
ConstRef<long> max = 100L,
ConstRef<long> increment = 5L);
Component ResizableSplit(ResizableSplitOption options);
Component ResizableSplitLeft(Component main, Component back, int* main_size);

View File

@@ -29,14 +29,16 @@ using Components = std::vector<Component>;
/// @ingroup component
class ComponentBase {
public:
// virtual Destructor.
explicit ComponentBase(Components children)
: children_(std::move(children)) {}
virtual ~ComponentBase();
ComponentBase() = default;
// A component is not copiable.
// A component is not copyable/movable.
ComponentBase(const ComponentBase&) = delete;
void operator=(const ComponentBase&) = delete;
ComponentBase(ComponentBase&&) = delete;
ComponentBase& operator=(const ComponentBase&) = delete;
ComponentBase& operator=(ComponentBase&&) = delete;
// Component hierarchy:
ComponentBase* Parent() const;

View File

@@ -10,7 +10,6 @@
#include <ftxui/dom/elements.hpp> // for Element, separator
#include <ftxui/util/ref.hpp> // for Ref, ConstRef, StringRef
#include <functional> // for function
#include <optional> // for optional
#include <string> // for string
#include "ftxui/component/component_base.hpp" // for Component
@@ -22,10 +21,10 @@ namespace ftxui {
/// |Radiobox::transform|, |MenuEntryOption::transform|,
/// |MenuOption::transform|.
struct EntryState {
std::string label; /// < The label to display.
bool state; /// < The state of the button/checkbox/radiobox
bool active; /// < Whether the entry is the active one.
bool focused; /// < Whether the entry is one focused by the user.
std::string label; ///< The label to display.
bool state; ///< The state of the button/checkbox/radiobox
bool active; ///< Whether the entry is the active one.
bool focused; ///< Whether the entry is one focused by the user.
};
struct UnderlineOption {
@@ -151,10 +150,10 @@ struct CheckboxOption {
/// @brief Used to define style for the Input component.
struct InputState {
Element element;
bool hovered; /// < Whether the input is hovered by the mouse.
bool focused; /// < Whether the input is focused by the user.
bool is_placeholder; /// < Whether the input is empty and displaying the
/// < placeholder.
bool hovered; ///< Whether the input is hovered by the mouse.
bool focused; ///< Whether the input is focused by the user.
bool is_placeholder; ///< Whether the input is empty and displaying the
///< placeholder.
};
/// @brief Option for the Input component.
@@ -175,9 +174,9 @@ struct InputOption {
// Style:
std::function<Element(InputState)> transform;
Ref<bool> password = false; /// < Obscure the input content using '*'.
Ref<bool> multiline = true; /// < Whether the input can be multiline.
Ref<bool> insert = true; /// < Insert or overtype character mode.
Ref<bool> password = false; ///< Obscure the input content using '*'.
Ref<bool> multiline = true; ///< Whether the input can be multiline.
Ref<bool> insert = true; ///< Insert or overtype character mode.
/// Called when the content changes.
std::function<void()> on_change = [] {};
@@ -228,41 +227,57 @@ struct SliderOption {
Direction direction = Direction::Right;
Color color_active = Color::White;
Color color_inactive = Color::GrayDark;
std::function<void()> on_change; ///> Called when `value` is updated.
};
// Parameter pack used by `WindowOptions::render`.
struct WindowRenderState {
Element inner; /// < The element wrapped inside this window.
const std::string& title; /// < The title of the window.
bool active = false; /// < Whether the window is the active one.
bool drag = false; /// < Whether the window is being dragged.
bool resize = false; /// < Whether the window is being resized.
bool hover_left = false; /// < Whether the resizeable left side is hovered.
bool hover_right = false; /// < Whether the resizeable right side is hovered.
bool hover_top = false; /// < Whether the resizeable top side is hovered.
bool hover_down = false; /// < Whether the resizeable down side is hovered.
Element inner; ///< The element wrapped inside this window.
const std::string& title; ///< The title of the window.
bool active = false; ///< Whether the window is the active one.
bool drag = false; ///< Whether the window is being dragged.
bool resize = false; ///< Whether the window is being resized.
bool hover_left = false; ///< Whether the resizeable left side is hovered.
bool hover_right = false; ///< Whether the resizeable right side is hovered.
bool hover_top = false; ///< Whether the resizeable top side is hovered.
bool hover_down = false; ///< Whether the resizeable down side is hovered.
};
// @brief Option for the `Window` component.
// @ingroup component
struct WindowOptions {
Component inner; /// < The component wrapped by this window.
ConstStringRef title = ""; /// < The title displayed by this window.
Component inner; ///< The component wrapped by this window.
ConstStringRef title = ""; ///< The title displayed by this window.
Ref<int> left = 0; /// < The left side position of the window.
Ref<int> top = 0; /// < The top side position of the window.
Ref<int> width = 20; /// < The width of the window.
Ref<int> height = 10; /// < The height of the window.
Ref<int> left = 0; ///< The left side position of the window.
Ref<int> top = 0; ///< The top side position of the window.
Ref<int> width = 20; ///< The width of the window.
Ref<int> height = 10; ///< The height of the window.
Ref<bool> resize_left = true; /// < Can the left side be resized?
Ref<bool> resize_right = true; /// < Can the right side be resized?
Ref<bool> resize_top = true; /// < Can the top side be resized?
Ref<bool> resize_down = true; /// < Can the down side be resized?
Ref<bool> resize_left = true; ///< Can the left side be resized?
Ref<bool> resize_right = true; ///< Can the right side be resized?
Ref<bool> resize_top = true; ///< Can the top side be resized?
Ref<bool> resize_down = true; ///< Can the down side be resized?
/// An optional function to customize how the window looks like:
std::function<Element(const WindowRenderState&)> render;
};
/// @brief Option for the Dropdown component.
/// @ingroup component
/// A dropdown menu is a checkbox opening/closing a radiobox.
struct DropdownOption {
/// Whether the dropdown is open or closed:
Ref<bool> open = false;
// The options for the checkbox:
CheckboxOption checkbox;
// The options for the radiobox:
RadioboxOption radiobox;
// The transformation function:
std::function<Element(bool open, Element checkbox, Element radiobox)>
transform;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP */

View File

@@ -5,9 +5,7 @@
#define FTXUI_COMPONENT_EVENT_HPP
#include <ftxui/component/mouse.hpp> // for Mouse
#include <functional>
#include <string> // for string, operator==
#include <vector>
#include <string> // for string, operator==
namespace ftxui {
@@ -33,7 +31,8 @@ struct Event {
static Event Character(wchar_t);
static Event Special(std::string);
static Event Mouse(std::string, Mouse mouse);
static Event CursorReporting(std::string, int x, int y);
static Event CursorPosition(std::string, int x, int y); // Internal
static Event CursorShape(std::string, int shape); // Internal
// --- Arrow ---
static const Event ArrowLeft;
@@ -53,33 +52,71 @@ struct Event {
static const Event Escape;
static const Event Tab;
static const Event TabReverse;
static const Event F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12;
// --- Navigation keys ---
static const Event Insert;
static const Event Home;
static const Event End;
static const Event PageUp;
static const Event PageDown;
// --- Function keys ---
static const Event F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12;
// --- Control keys ---
static const Event a, A, CtrlA, AltA, CtrlAltA;
static const Event b, B, CtrlB, AltB, CtrlAltB;
static const Event c, C, CtrlC, AltC, CtrlAltC;
static const Event d, D, CtrlD, AltD, CtrlAltD;
static const Event e, E, CtrlE, AltE, CtrlAltE;
static const Event f, F, CtrlF, AltF, CtrlAltF;
static const Event g, G, CtrlG, AltG, CtrlAltG;
static const Event h, H, CtrlH, AltH, CtrlAltH;
static const Event i, I, CtrlI, AltI, CtrlAltI;
static const Event j, J, CtrlJ, AltJ, CtrlAltJ;
static const Event k, K, CtrlK, AltK, CtrlAltK;
static const Event l, L, CtrlL, AltL, CtrlAltL;
static const Event m, M, CtrlM, AltM, CtrlAltM;
static const Event n, N, CtrlN, AltN, CtrlAltN;
static const Event o, O, CtrlO, AltO, CtrlAltO;
static const Event p, P, CtrlP, AltP, CtrlAltP;
static const Event q, Q, CtrlQ, AltQ, CtrlAltQ;
static const Event r, R, CtrlR, AltR, CtrlAltR;
static const Event s, S, CtrlS, AltS, CtrlAltS;
static const Event t, T, CtrlT, AltT, CtrlAltT;
static const Event u, U, CtrlU, AltU, CtrlAltU;
static const Event v, V, CtrlV, AltV, CtrlAltV;
static const Event w, W, CtrlW, AltW, CtrlAltW;
static const Event x, X, CtrlX, AltX, CtrlAltX;
static const Event y, Y, CtrlY, AltY, CtrlAltY;
static const Event z, Z, CtrlZ, AltZ, CtrlAltZ;
// --- Custom ---
static const Event Custom;
//--- Method section ---------------------------------------------------------
bool operator==(const Event& other) const { return input_ == other.input_; }
bool operator!=(const Event& other) const { return !operator==(other); }
bool operator<(const Event& other) const { return input_ < other.input_; }
const std::string& input() const { return input_; }
bool is_character() const { return type_ == Type::Character; }
std::string character() const { return input_; }
bool is_mouse() const { return type_ == Type::Mouse; }
struct Mouse& mouse() { return data_.mouse; }
bool is_cursor_reporting() const { return type_ == Type::CursorReporting; }
// --- Internal Method section -----------------------------------------------
bool is_cursor_position() const { return type_ == Type::CursorPosition; }
int cursor_x() const { return data_.cursor.x; }
int cursor_y() const { return data_.cursor.y; }
const std::string& input() const { return input_; }
bool is_cursor_shape() const { return type_ == Type::CursorShape; }
int cursor_shape() const { return data_.cursor_shape; }
bool operator==(const Event& other) const { return input_ == other.input_; }
bool operator!=(const Event& other) const { return !operator==(other); }
// Debug
std::string DebugString() const;
//--- State section ----------------------------------------------------------
ScreenInteractive* screen_ = nullptr;
@@ -91,7 +128,8 @@ struct Event {
Unknown,
Character,
Mouse,
CursorReporting,
CursorPosition,
CursorShape,
};
Type type_ = Type::Unknown;
@@ -103,6 +141,7 @@ struct Event {
union {
struct Mouse mouse;
struct Cursor cursor;
int cursor_shape;
} data_ = {};
std::string input_;

View File

@@ -24,11 +24,14 @@ class Loop {
void RunOnceBlocking();
void Run();
private:
// This class is non copyable.
// This class is non copyable/movable.
Loop(const Loop&) = default;
Loop(Loop&&) = delete;
Loop& operator=(Loop&&) = delete;
Loop(const ScreenInteractive&) = delete;
Loop& operator=(const Loop&) = delete;
private:
ScreenInteractive* screen_;
Component component_;
};

View File

@@ -16,11 +16,14 @@ struct Mouse {
None = 3,
WheelUp = 4,
WheelDown = 5,
WheelLeft = 6, /// Supported terminal only.
WheelRight = 7, /// Supported terminal only.
};
enum Motion {
Released = 0,
Pressed = 1,
Moved = 2,
};
// Button

View File

@@ -7,12 +7,10 @@
#include <algorithm> // for copy, max
#include <atomic> // for atomic, __atomic_base
#include <condition_variable> // for condition_variable
#include <functional>
#include <iostream>
#include <memory> // for unique_ptr, make_unique
#include <mutex> // for mutex, unique_lock
#include <queue> // for queue
#include <utility> // for move
#include <memory> // for unique_ptr, make_unique
#include <mutex> // for mutex, unique_lock
#include <queue> // for queue
#include <utility> // for move
namespace ftxui {
@@ -54,6 +52,10 @@ template<class T> Receiver<T> MakeReceiver();
template <class T>
class SenderImpl {
public:
SenderImpl(const SenderImpl&) = delete;
SenderImpl(SenderImpl&&) = delete;
SenderImpl& operator=(const SenderImpl&) = delete;
SenderImpl& operator=(SenderImpl&&) = delete;
void Send(T t) { receiver_->Receive(std::move(t)); }
~SenderImpl() { receiver_->ReleaseSender(); }
@@ -61,7 +63,7 @@ class SenderImpl {
private:
friend class ReceiverImpl<T>;
SenderImpl(ReceiverImpl<T>* consumer) : receiver_(consumer) {}
explicit SenderImpl(ReceiverImpl<T>* consumer) : receiver_(consumer) {}
ReceiverImpl<T>* receiver_;
};
@@ -73,15 +75,17 @@ class ReceiverImpl {
senders_++;
return std::unique_ptr<SenderImpl<T>>(new SenderImpl<T>(this));
}
ReceiverImpl() { senders_ = 0; }
ReceiverImpl() = default;
bool Receive(T* t) {
while (senders_ || !queue_.empty()) {
std::unique_lock<std::mutex> lock(mutex_);
if (queue_.empty())
if (queue_.empty()) {
notifier_.wait(lock);
if (queue_.empty())
}
if (queue_.empty()) {
continue;
}
*t = std::move(queue_.front());
queue_.pop();
return true;
@@ -91,8 +95,9 @@ class ReceiverImpl {
bool ReceiveNonBlocking(T* t) {
std::unique_lock<std::mutex> lock(mutex_);
if (queue_.empty())
if (queue_.empty()) {
return false;
}
*t = queue_.front();
queue_.pop();
return true;
@@ -127,7 +132,7 @@ class ReceiverImpl {
std::mutex mutex_;
std::queue<T> queue_;
std::condition_variable notifier_;
std::atomic<int> senders_;
std::atomic<int> senders_{0};
};
template <class T>

View File

@@ -31,6 +31,8 @@ class ScreenInteractive : public Screen {
// Constructors:
static ScreenInteractive FixedSize(int dimx, int dimy);
static ScreenInteractive Fullscreen();
static ScreenInteractive FullscreenPrimaryScreen();
static ScreenInteractive FullscreenAlternateScreen();
static ScreenInteractive FitComponent();
static ScreenInteractive TerminalOutput();
@@ -57,6 +59,15 @@ class ScreenInteractive : public Screen {
// temporarily uninstalled.
Closure WithRestoredIO(Closure);
// FTXUI implements handlers for Ctrl-C and Ctrl-Z. By default, these handlers
// are executed, even if the component catches the event. This avoid users
// handling every event to be trapped in the application. However, in some
// cases, the application may want to handle these events itself. In this
// case, the application can force FTXUI to not handle these events by calling
// the following functions with force=true.
void ForceHandleCtrlC(bool force);
void ForceHandleCtrlZ(bool force);
private:
void ExitNow();
@@ -98,7 +109,7 @@ class ScreenInteractive : public Screen {
std::string set_cursor_position;
std::string reset_cursor_position;
std::atomic<bool> quit_ = false;
std::atomic<bool> quit_{false};
std::thread event_listener_;
std::thread animation_listener_;
bool animation_requested_ = false;
@@ -112,6 +123,12 @@ class ScreenInteractive : public Screen {
bool frame_valid_ = false;
bool force_handle_ctrl_c_ = true;
bool force_handle_ctrl_z_ = true;
// The style of the cursor to restore on exit.
int cursor_reset_shape_ = 1;
friend class Loop;
public:

View File

@@ -9,8 +9,8 @@
#include <string> // for string
#include <unordered_map> // for unordered_map
#include "ftxui/screen/color.hpp" // for Color
#include "ftxui/screen/screen.hpp" // for Pixel
#include "ftxui/screen/color.hpp" // for Color
#include "ftxui/screen/image.hpp" // for Pixel, Image
#ifdef DrawText
// Workaround for WinUsr.h (via Windows.h) defining macros that break things.
@@ -95,6 +95,12 @@ struct Canvas {
void DrawText(int x, int y, const std::string& value, const Color& color);
void DrawText(int x, int y, const std::string& value, const Stylizer& style);
// Draw using directly pixels or images --------------------------------------
// x is considered to be a multiple of 2.
// y is considered to be a multiple of 4.
void DrawPixel(int x, int y, const Pixel&);
void DrawImage(int x, int y, const Image&);
// Decorator:
// x is considered to be a multiple of 2.
// y is considered to be a multiple of 4.
@@ -104,15 +110,18 @@ struct Canvas {
bool IsIn(int x, int y) const {
return x >= 0 && x < width_ && y >= 0 && y < height_;
}
enum CellType {
kBraille,
kBlock,
kText,
kCell, // Units of size 2x4
kBlock, // Units of size 2x2
kBraille, // Units of size 1x1
};
struct Cell {
CellType type = kText;
CellType type = kCell;
Pixel content;
};
struct XY {
int x;
int y;

View File

@@ -4,7 +4,8 @@
#ifndef FTXUI_DOM_DEPRECATED_HPP
#define FTXUI_DOM_DEPRECATED_HPP
#include "ftxui/dom/elements.hpp"
#include <ftxui/dom/node.hpp>
#include <string>
namespace ftxui {
Element text(std::wstring text);

View File

@@ -14,7 +14,6 @@
#include "ftxui/dom/node.hpp"
#include "ftxui/screen/box.hpp"
#include "ftxui/screen/color.hpp"
#include "ftxui/screen/screen.hpp"
#include "ftxui/screen/terminal.hpp"
#include "ftxui/util/ref.hpp"
@@ -80,7 +79,7 @@ Decorator borderStyled(BorderStyle);
Decorator borderStyled(BorderStyle, Color);
Decorator borderStyled(Color);
Decorator borderWith(const Pixel&);
Element window(Element title, Element content);
Element window(Element title, Element content, BorderStyle border = ROUNDED);
Element spinner(int charset_index, size_t image_index);
Element paragraph(const std::string& text);
Element paragraphAlignLeft(const std::string& text);
@@ -170,6 +169,7 @@ Element focusCursorUnderlineBlinking(Element);
// --- Misc ---
Element vscroll_indicator(Element);
Element hscroll_indicator(Element);
Decorator reflect(Box& box);
// Before drawing the |element| clear the pixel below. This is useful in
// combinaison with dbox.
@@ -183,7 +183,7 @@ Element align_right(Element);
Element nothing(Element element);
namespace Dimension {
Dimensions Fit(Element&);
Dimensions Fit(Element&, bool extend_beyond_screen = false);
} // namespace Dimension
} // namespace ftxui

View File

@@ -4,7 +4,6 @@
#ifndef FTXUI_DOM_TABLE
#define FTXUI_DOM_TABLE
#include <memory>
#include <string> // for string
#include <vector> // for vector
@@ -39,6 +38,7 @@ class Table {
Table();
Table(std::vector<std::vector<std::string>>);
Table(std::vector<std::vector<Element>>);
Table(std::initializer_list<std::vector<std::string>> init);
TableSelection SelectAll();
TableSelection SelectCell(int column, int row);
TableSelection SelectRow(int row_index);

View File

@@ -5,7 +5,7 @@
#define FTXUI_DOM_TAKE_ANY_ARGS_HPP
// IWYU pragma: private, include "ftxui/dom/elements.hpp"
#include <type_traits>
#include <ftxui/dom/node.hpp>
namespace ftxui {
@@ -19,8 +19,9 @@ inline void Merge(Elements& container, Element element) {
template <>
inline void Merge(Elements& container, Elements elements) {
for (auto& element : elements)
for (auto& element : elements) {
container.push_back(std::move(element));
}
}
// Turn a set of arguments into a vector.

View File

@@ -15,6 +15,7 @@ struct Box {
static auto Intersection(Box a, Box b) -> Box;
static auto Union(Box a, Box b) -> Box;
bool Contain(int x, int y) const;
bool IsEmpty() const;
bool operator==(const Box& other) const;
bool operator!=(const Box& other) const;
};

View File

@@ -6,7 +6,6 @@
#include <cstdint> // for uint8_t
#include <string> // for string
#include <vector> // for vector
#ifdef RGB
// Workaround for wingdi.h (via Windows.h) defining macros that break things.
@@ -24,14 +23,22 @@ class Color {
enum Palette16 : uint8_t;
enum Palette256 : uint8_t;
// NOLINTBEGIN
Color(); // Transparent.
Color(Palette1 index); // Transparent.
Color(Palette16 index); // Implicit conversion from index to Color.
Color(Palette256 index); // Implicit conversion from index to Color.
Color(uint8_t red, uint8_t green, uint8_t blue);
// NOLINTEND
Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255);
static Color RGB(uint8_t red, uint8_t green, uint8_t blue);
static Color HSV(uint8_t hue, uint8_t saturation, uint8_t value);
static Color RGBA(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha);
static Color HSVA(uint8_t hue,
uint8_t saturation,
uint8_t value,
uint8_t alpha);
static Color Interpolate(float t, const Color& a, const Color& b);
static Color Blend(const Color& lhs, const Color& rhs);
//---------------------------
// List of colors:
@@ -309,6 +316,7 @@ class Color {
bool operator!=(const Color& rhs) const;
std::string Print(bool is_background_color) const;
bool IsOpaque() const { return alpha_ == 255; }
private:
enum class ColorType : uint8_t {
@@ -321,6 +329,7 @@ class Color {
uint8_t red_ = 0;
uint8_t green_ = 0;
uint8_t blue_ = 0;
uint8_t alpha_ = 0;
};
inline namespace literals {

View File

@@ -0,0 +1,48 @@
// Copyright 2024 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_SCREEN_IMAGE_HPP
#define FTXUI_SCREEN_IMAGE_HPP
#include <string> // for string, basic_string, allocator
#include <vector> // for vector
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/pixel.hpp" // for Pixel
namespace ftxui {
/// @brief A rectangular grid of Pixel.
/// @ingroup screen
class Image {
public:
// Constructors:
Image() = delete;
Image(int dimx, int dimy);
// Access a character in the grid at a given position.
std::string& at(int x, int y);
const std::string& at(int x, int y) const;
// Access a cell (Pixel) in the grid at a given position.
Pixel& PixelAt(int x, int y);
const Pixel& PixelAt(int x, int y) const;
// Get screen dimensions.
int dimx() const { return dimx_; }
int dimy() const { return dimy_; }
// Fill the image with space and default style
void Clear();
Box stencil;
protected:
int dimx_;
int dimy_;
std::vector<std::vector<Pixel>> pixels_;
};
} // namespace ftxui
#endif // FTXUI_SCREEN_IMAGE_HPP

View File

@@ -0,0 +1,52 @@
// Copyright 2024 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_SCREEN_PIXEL_HPP
#define FTXUI_SCREEN_PIXEL_HPP
#include <cstdint> // for uint8_t
#include <string> // for string, basic_string, allocator
#include "ftxui/screen/color.hpp" // for Color, Color::Default
namespace ftxui {
/// @brief A Unicode character and its associated style.
/// @ingroup screen
struct Pixel {
Pixel()
: blink(false),
bold(false),
dim(false),
inverted(false),
underlined(false),
underlined_double(false),
strikethrough(false),
automerge(false) {}
// A bit field representing the style:
bool blink : 1;
bool bold : 1;
bool dim : 1;
bool inverted : 1;
bool underlined : 1;
bool underlined_double : 1;
bool strikethrough : 1;
bool automerge : 1;
// The hyperlink associated with the pixel.
// 0 is the default value, meaning no hyperlink.
// It's an index for accessing Screen meta data
uint8_t hyperlink = 0;
// The graphemes stored into the pixel. To support combining characters,
// like: a?, this can potentially contain multiple codepoints.
std::string character = "";
// Colors:
Color background_color = Color::Default;
Color foreground_color = Color::Default;
};
} // namespace ftxui
#endif // FTXUI_SCREEN_PIXEL_HPP

View File

@@ -5,52 +5,14 @@
#define FTXUI_SCREEN_SCREEN_HPP
#include <cstdint> // for uint8_t
#include <memory>
#include <string> // for string, basic_string, allocator
#include <vector> // for vector
#include <string> // for string, basic_string, allocator
#include <vector> // for vector
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/color.hpp" // for Color, Color::Default
#include "ftxui/screen/image.hpp" // for Pixel, Image
#include "ftxui/screen/terminal.hpp" // for Dimensions
namespace ftxui {
/// @brief A unicode character and its associated style.
/// @ingroup screen
struct Pixel {
Pixel()
: blink(false),
bold(false),
dim(false),
inverted(false),
underlined(false),
underlined_double(false),
strikethrough(false),
automerge(false) {}
// A bit field representing the style:
bool blink : 1;
bool bold : 1;
bool dim : 1;
bool inverted : 1;
bool underlined : 1;
bool underlined_double : 1;
bool strikethrough : 1;
bool automerge : 1;
// The hyperlink associated with the pixel.
// 0 is the default value, meaning no hyperlink.
uint8_t hyperlink = 0;
// The graphemes stored into the pixel. To support combining characters,
// like: a⃦, this can potentially contain multiple codepoints.
std::string character = " ";
// Colors:
Color background_color = Color::Default;
Color foreground_color = Color::Default;
};
/// @brief Define how the Screen's dimensions should look like.
/// @ingroup screen
namespace Dimension {
@@ -60,36 +22,25 @@ Dimensions Full();
/// @brief A rectangular grid of Pixel.
/// @ingroup screen
class Screen {
class Screen : public Image {
public:
// Constructors:
Screen(int dimx, int dimy);
static Screen Create(Dimensions dimension);
static Screen Create(Dimensions width, Dimensions height);
// Access a character in the grid at a given position.
std::string& at(int x, int y);
const std::string& at(int x, int y) const;
// Access a cell (Pixel) in the grid at a given position.
Pixel& PixelAt(int x, int y);
const Pixel& PixelAt(int x, int y) const;
std::string ToString() const;
// Print the Screen on to the terminal.
void Print() const;
// Get screen dimensions.
int dimx() const { return dimx_; }
int dimy() const { return dimy_; }
// Fill the screen with space and reset any screen state, like hyperlinks, and
// cursor
void Clear();
// Move the terminal cursor n-lines up with n = dimy().
std::string ResetPosition(bool clear = false) const;
// Fill the screen with space.
void Clear();
void ApplyShader();
struct Cursor {
@@ -107,6 +58,7 @@ class Screen {
};
Shape shape;
};
Cursor cursor() const { return cursor_; }
void SetCursor(Cursor cursor) { cursor_ = cursor; }
@@ -115,12 +67,7 @@ class Screen {
uint8_t RegisterHyperlink(const std::string& link);
const std::string& Hyperlink(uint8_t id) const;
Box stencil;
protected:
int dimx_;
int dimy_;
std::vector<std::vector<Pixel>> pixels_;
Cursor cursor_;
std::vector<std::string> hyperlinks_ = {""};
};

View File

@@ -4,10 +4,8 @@
#ifndef FTXUI_SCREEN_STRING_HPP
#define FTXUI_SCREEN_STRING_HPP
#include <stddef.h> // for size_t
#include <cstdint> // for uint8_t
#include <string> // for string, wstring, to_string
#include <vector> // for vector
#include <string> // for string, wstring, to_string
#include <vector> // for vector
namespace ftxui {
std::string to_string(const std::wstring& s);
@@ -30,6 +28,4 @@ std::vector<int> CellToGlyphIndex(const std::string& input);
} // namespace ftxui
#include "ftxui/screen/deprecated.hpp"
#endif /* end of include guard: FTXUI_SCREEN_STRING_HPP */

View File

@@ -16,6 +16,10 @@ class AutoReset {
: variable_(variable), previous_value_(std::move(*variable)) {
*variable_ = std::move(new_value);
}
AutoReset(const AutoReset&) = delete;
AutoReset(AutoReset&&) = delete;
AutoReset& operator=(const AutoReset&) = delete;
AutoReset& operator=(AutoReset&&) = delete;
~AutoReset() { *variable_ = std::move(previous_value_); }
private:

View File

@@ -5,8 +5,10 @@
#define FTXUI_UTIL_REF_HPP
#include <ftxui/screen/string.hpp>
#include <memory>
#include <string>
#include <variant>
#include <vector>
namespace ftxui {
@@ -15,10 +17,12 @@ template <typename T>
class ConstRef {
public:
ConstRef() = default;
ConstRef(T t) : variant_(std::move(t)) {} // NOLINT
ConstRef(const T* t) : variant_(t) {} // NOLINT
ConstRef& operator=(ConstRef&&) noexcept = default;
ConstRef(const ConstRef<T>&) = default;
ConstRef(ConstRef<T>&&) = default;
ConstRef(T t) : variant_(std::move(t)) {}
ConstRef(const T* t) : variant_(t) {}
ConstRef(ConstRef<T>&&) noexcept = default;
~ConstRef() = default;
// Make a "reseatable" reference
ConstRef<T>& operator=(const ConstRef<T>&) = default;
@@ -42,10 +46,12 @@ template <typename T>
class Ref {
public:
Ref() = default;
Ref(T t) : variant_(std::move(t)) {} // NOLINT
Ref(T* t) : variant_(t) {} // NOLINT
~Ref() = default;
Ref& operator=(Ref&&) noexcept = default;
Ref(const Ref<T>&) = default;
Ref(Ref<T>&&) = default;
Ref(T t) : variant_(std::move(t)) {}
Ref(T* t) : variant_(t) {}
Ref(Ref<T>&&) noexcept = default;
// Make a "reseatable" reference.
Ref<T>& operator=(const Ref<T>&) = default;
@@ -77,8 +83,10 @@ class StringRef : public Ref<std::string> {
public:
using Ref<std::string>::Ref;
StringRef(const wchar_t* ref) : StringRef(to_string(std::wstring(ref))) {}
StringRef(const char* ref) : StringRef(std::string(ref)) {}
StringRef(const wchar_t* ref) // NOLINT
: StringRef(to_string(std::wstring(ref))) {}
StringRef(const char* ref) // NOLINT
: StringRef(std::string(ref)) {}
};
/// @brief An adapter. Own or reference a constant string. For convenience, this
@@ -87,45 +95,120 @@ class ConstStringRef : public ConstRef<std::string> {
public:
using ConstRef<std::string>::ConstRef;
ConstStringRef(const std::wstring* ref) : ConstStringRef(to_string(*ref)) {}
ConstStringRef(const std::wstring ref) : ConstStringRef(to_string(ref)) {}
ConstStringRef(const wchar_t* ref)
ConstStringRef(const std::wstring* ref) // NOLINT
: ConstStringRef(to_string(*ref)) {}
ConstStringRef(const std::wstring ref) // NOLINT
: ConstStringRef(to_string(ref)) {}
ConstStringRef(const wchar_t* ref) // NOLINT
: ConstStringRef(to_string(std::wstring(ref))) {}
ConstStringRef(const char* ref) : ConstStringRef(std::string(ref)) {}
ConstStringRef(const char* ref) // NOLINT
: ConstStringRef(std::string(ref)) {}
};
/// @brief An adapter. Reference a list of strings.
///
/// Supported input:
/// - `std::vector<std::string>`
/// - `std::vector<std::string>*`
/// - `std::vector<std::wstring>*`
/// - `Adapter*`
/// - `std::unique_ptr<Adapter>`
class ConstStringListRef {
public:
// Bring your own adapter:
class Adapter {
public:
Adapter() = default;
Adapter(const Adapter&) = default;
Adapter& operator=(const Adapter&) = default;
Adapter(Adapter&&) = default;
Adapter& operator=(Adapter&&) = default;
virtual ~Adapter() = default;
virtual size_t size() const = 0;
virtual std::string operator[](size_t i) const = 0;
};
using Variant = std::variant<const std::vector<std::string>, //
const std::vector<std::string>*, //
const std::vector<std::wstring>*, //
Adapter*, //
std::unique_ptr<Adapter> //
>;
ConstStringListRef() = default;
ConstStringListRef(const std::vector<std::string>* ref) : ref_(ref) {}
ConstStringListRef(const std::vector<std::wstring>* ref) : ref_wide_(ref) {}
ConstStringListRef(const ConstStringListRef& other) = default;
ConstStringListRef& operator=(const ConstStringListRef& other) = default;
~ConstStringListRef() = default;
ConstStringListRef& operator=(const ConstStringListRef&) = default;
ConstStringListRef& operator=(ConstStringListRef&&) = default;
ConstStringListRef(ConstStringListRef&&) = default;
ConstStringListRef(const ConstStringListRef&) = default;
ConstStringListRef(std::vector<std::string> value) // NOLINT
{
variant_ = std::make_shared<Variant>(value);
}
ConstStringListRef(const std::vector<std::string>* value) // NOLINT
{
variant_ = std::make_shared<Variant>(value);
}
ConstStringListRef(const std::vector<std::wstring>* value) // NOLINT
{
variant_ = std::make_shared<Variant>(value);
}
ConstStringListRef(Adapter* adapter) // NOLINT
{
variant_ = std::make_shared<Variant>(adapter);
}
template <typename AdapterType>
ConstStringListRef(std::unique_ptr<AdapterType> adapter) // NOLINT
{
variant_ = std::make_shared<Variant>(
static_cast<std::unique_ptr<Adapter>>(std::move(adapter)));
}
size_t size() const {
if (ref_) {
return ref_->size();
}
if (ref_wide_) {
return ref_wide_->size();
}
return 0;
return variant_ ? std::visit(SizeVisitor(), *variant_) : 0;
}
std::string operator[](size_t i) const {
if (ref_) {
return (*ref_)[i];
}
if (ref_wide_) {
return to_string((*ref_wide_)[i]);
}
return "";
return variant_ ? std::visit(IndexedGetter(i), *variant_) : "";
}
private:
const std::vector<std::string>* ref_ = nullptr;
const std::vector<std::wstring>* ref_wide_ = nullptr;
struct SizeVisitor {
size_t operator()(const std::vector<std::string>& v) const {
return v.size();
}
size_t operator()(const std::vector<std::string>* v) const {
return v->size();
}
size_t operator()(const std::vector<std::wstring>* v) const {
return v->size();
}
size_t operator()(const Adapter* v) const { return v->size(); }
size_t operator()(const std::unique_ptr<Adapter>& v) const {
return v->size();
}
};
struct IndexedGetter {
IndexedGetter(size_t index) // NOLINT
: index_(index) {}
size_t index_;
std::string operator()(const std::vector<std::string>& v) const {
return v[index_];
}
std::string operator()(const std::vector<std::string>* v) const {
return (*v)[index_];
}
std::string operator()(const std::vector<std::wstring>* v) const {
return to_string((*v)[index_]);
}
std::string operator()(const Adapter* v) const { return (*v)[index_]; }
std::string operator()(const std::unique_ptr<Adapter>& v) const {
return (*v)[index_];
}
};
std::shared_ptr<Variant> variant_;
};
} // namespace ftxui

View File

@@ -1,5 +1,4 @@
#include <cmath> // for sin, pow, sqrt, cos
#include <ratio> // for ratio
#include <utility> // for move
#include "ftxui/component/animation.hpp"

View File

@@ -3,12 +3,10 @@
// the LICENSE file.
#include <functional> // for function
#include <memory> // for shared_ptr
#include <utility> // for move
#include "ftxui/component/animation.hpp" // for Animator, Params (ptr only)
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Button
#include "ftxui/component/component.hpp" // for Make, Button
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for ButtonOption, AnimatedColorOption, AnimatedColorsOption, EntryState
#include "ftxui/component/event.hpp" // for Event, Event::Return
@@ -98,10 +96,13 @@ class ButtonBase : public ComponentBase, public ButtonOption {
}
void OnClick() {
on_click();
animation_background_ = 0.5F; // NOLINT
animation_foreground_ = 0.5F; // NOLINT
SetAnimationTarget(1.F); // NOLINT
// TODO(arthursonzogni): Consider posting the task to the main loop, instead
// of invoking it immediately.
on_click(); // May delete this.
}
bool OnEvent(Event event) override {
@@ -110,7 +111,7 @@ class ButtonBase : public ComponentBase, public ButtonOption {
}
if (event == Event::Return) {
OnClick();
OnClick(); // May delete this.
return true;
}
return false;
@@ -127,7 +128,7 @@ class ButtonBase : public ComponentBase, public ButtonOption {
if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) {
TakeFocus();
OnClick();
OnClick(); // May delete this.
return true;
}
@@ -204,7 +205,7 @@ Component Button(ButtonOption option) {
Component Button(ConstStringRef label,
std::function<void()> on_click,
ButtonOption option) {
option.label = label;
option.label = std::move(label);
option.on_click = std::move(on_click);
return Make<ButtonBase>(std::move(option));
}

View File

@@ -1,8 +1,6 @@
// Copyright 2022 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <chrono> // for operator""s, chrono_literals
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string
#include "ftxui/component/animation.hpp" // for Duration, Params

View File

@@ -2,9 +2,7 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <functional> // for function
#include <memory> // for __shared_ptr_access, __shared_ptr_access<>::element_type, shared_ptr
#include <type_traits> // for remove_reference, remove_reference<>::type
#include <utility> // for move
#include <utility> // for move
#include "ftxui/component/component.hpp" // for Make, CatchEvent, ComponentDecorator
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase

View File

@@ -4,7 +4,6 @@
#include <functional> // for function
#include <utility> // for move
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Checkbox
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
#include "ftxui/component/component_options.hpp" // for CheckboxOption, EntryState
@@ -73,6 +72,7 @@ class CheckboxBase : public ComponentBase, public CheckboxOption {
event.mouse().motion == Mouse::Pressed) {
*checked = !*checked;
on_change();
TakeFocus();
return true;
}
@@ -86,6 +86,32 @@ class CheckboxBase : public ComponentBase, public CheckboxOption {
};
} // namespace
/// @brief Draw checkable element.
/// @param option Additional optional parameters.
/// @ingroup component
/// @see CheckboxBase
///
/// ### Example
///
/// ```cpp
/// auto screen = ScreenInteractive::FitComponent();
/// CheckboxOption option;
/// option.label = "Make a sandwidth";
/// option.checked = false;
/// Component checkbox = Checkbox(option);
/// screen.Loop(checkbox)
/// ```
///
/// ### Output
///
/// ```bash
/// ☐ Make a sandwitch
/// ```
// NOLINTNEXTLINE
Component Checkbox(CheckboxOption option) {
return Make<CheckboxBase>(std::move(option));
}
/// @brief Draw checkable element.
/// @param label The label of the checkbox.
/// @param checked Whether the checkbox is checked or not.
@@ -110,7 +136,7 @@ class CheckboxBase : public ComponentBase, public CheckboxOption {
/// ```
// NOLINTNEXTLINE
Component Checkbox(ConstStringRef label, bool* checked, CheckboxOption option) {
option.label = label;
option.label = std::move(label);
option.checked = checked;
return Make<CheckboxBase>(std::move(option));
}

View File

@@ -2,7 +2,6 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <functional> // for function
#include <memory> // for shared_ptr, allocator
#include <utility> // for move
#include "ftxui/component/component.hpp" // for Checkbox, Maybe, Make, Vertical, Collapsible
@@ -48,7 +47,7 @@ Component Collapsible(ConstStringRef label, Component child, Ref<bool> show) {
return hbox({prefix, t});
};
Add(Container::Vertical({
Checkbox(label, show_.operator->(), opt),
Checkbox(std::move(label), show_.operator->(), opt),
Maybe(std::move(child), show_.operator->()),
}));
}

View File

@@ -1,4 +1,3 @@
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string
#include "ftxui/component/component.hpp" // for Collapsible, Renderer

View File

@@ -5,6 +5,7 @@
#include <cassert> // for assert
#include <cstddef> // for size_t
#include <iterator> // for begin, end
#include <memory> // for unique_ptr, make_unique
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type

View File

@@ -2,7 +2,6 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <cassert>
#include <iostream>
#include <vector>
#include "ftxui/component/component.hpp"
#include "ftxui/component/terminal_input_parser.hpp"
@@ -11,8 +10,9 @@ using namespace ftxui;
namespace {
bool GeneratorBool(const char*& data, size_t& size) {
if (size == 0)
if (size == 0) {
return false;
}
auto out = bool(data[0] % 2);
data++;

View File

@@ -3,12 +3,11 @@
// the LICENSE file.
#include "ftxui/component/component_options.hpp"
#include <ftxui/dom/linear_gradient.hpp> // for LinearGradient
#include <ftxui/screen/color.hpp> // for Color, Color::White, Color::Black, Color::GrayDark, Color::Blue, Color::GrayLight, Color::Red
#include <memory> // for shared_ptr
#include <utility> // for move
#include "ftxui/component/animation.hpp" // for Function, Duration
#include "ftxui/dom/direction.hpp"
#include "ftxui/dom/elements.hpp" // for operator|=, Element, text, bgcolor, inverted, bold, dim, operator|, color, borderEmpty, hbox, automerge, border, borderLight
namespace ftxui {

View File

@@ -2,7 +2,6 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <memory> // for shared_ptr, __shared_ptr_access, allocator, __shared_ptr_access<>::element_type, make_shared
#include <string> // for string
#include "ftxui/component/component.hpp" // for Make
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component

View File

@@ -5,7 +5,6 @@
#include <cstddef> // for size_t
#include <memory> // for make_shared, __shared_ptr_access, allocator, shared_ptr, allocator_traits<>::value_type
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include "ftxui/component/component.hpp" // for Horizontal, Vertical, Tab
#include "ftxui/component/component_base.hpp" // for Components, Component, ComponentBase

View File

@@ -1,8 +1,6 @@
// 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.
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string
#include "ftxui/component/component.hpp" // for Horizontal, Vertical, Button, Tab
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component

View File

@@ -1,11 +1,11 @@
// 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.
#include <cstddef> // for size_t
#include <ftxui/component/event.hpp>
#include <functional> // for function
#include <memory> // for __shared_ptr_access, allocator, shared_ptr
#include <string> // for string
#include <utility>
#include "ftxui/component/component.hpp" // for Maybe, Checkbox, Make, Radiobox, Vertical, Dropdown
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
#include "ftxui/component/component_options.hpp" // for CheckboxOption, EntryState
@@ -20,79 +20,115 @@ namespace ftxui {
/// @param entries The list of entries to display.
/// @param selected The index of the selected entry.
Component Dropdown(ConstStringListRef entries, int* selected) {
class Impl : public ComponentBase {
DropdownOption option;
option.radiobox.entries = std::move(entries);
option.radiobox.selected = selected;
return Dropdown(option);
}
/// @brief A dropdown menu.
/// @ingroup component
/// @param option The options for the dropdown.
// NOLINTNEXTLINE
Component Dropdown(DropdownOption option) {
class Impl : public ComponentBase, public DropdownOption {
public:
Impl(ConstStringListRef entries, int* selected)
: entries_(entries), selected_(selected) {
CheckboxOption option;
option.transform = [](const EntryState& s) {
auto prefix = text(s.state ? "" : ""); // NOLINT
auto t = text(s.label);
if (s.active) {
t |= bold;
}
if (s.focused) {
t |= inverted;
}
return hbox({prefix, t});
};
checkbox_ = Checkbox(&title_, &show_, option);
radiobox_ = Radiobox(entries_, selected_);
explicit Impl(DropdownOption option) : DropdownOption(std::move(option)) {
FillDefault();
checkbox_ = Checkbox(checkbox);
radiobox_ = Radiobox(radiobox);
Add(Container::Vertical({
checkbox_,
Maybe(radiobox_, &show_),
Maybe(radiobox_, checkbox.checked),
}));
}
Element Render() override {
*selected_ = util::clamp(*selected_, 0, int(entries_.size()) - 1);
title_ = entries_[static_cast<size_t>(*selected_)];
if (show_) {
const int max_height = 12;
return vbox({
checkbox_->Render(),
separator(),
radiobox_->Render() | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, max_height),
}) |
border;
}
radiobox.selected =
util::clamp(radiobox.selected(), 0, int(radiobox.entries.size()) - 1);
title_ = radiobox.entries[selected_()];
return vbox({
checkbox_->Render() | border,
filler(),
});
return transform(*open_, checkbox_->Render(), radiobox_->Render());
}
// Switch focus in between the checkbox and the radiobox when selecting it.
bool OnEvent(ftxui::Event event) override {
const bool show_old = show_;
const int selected_old = *selected_;
const bool handled = ComponentBase::OnEvent(event);
const bool open_old = open_();
const int selected_old = selected_();
bool handled = ComponentBase::OnEvent(event);
if (!show_old && show_) {
// Transfer focus to the radiobox when the dropdown is opened.
if (!open_old && open_()) {
radiobox_->TakeFocus();
}
if (selected_old != *selected_) {
checkbox_->TakeFocus();
show_ = false;
// Auto-close the dropdown when the user selects an item, even if the item
// it the same as the previous one.
if (open_old && open_()) {
const bool should_close = (selected_() != selected_old) || //
(event == Event::Return) || //
(event == Event::Character(' ')) || //
(event == Event::Escape); //
if (should_close) {
checkbox_->TakeFocus();
open_ = false;
handled = true;
}
}
return handled;
}
void FillDefault() {
open_ = checkbox.checked;
selected_ = radiobox.selected;
checkbox.checked = &*open_;
radiobox.selected = &*selected_;
checkbox.label = &title_;
if (!checkbox.transform) {
checkbox.transform = [](const EntryState& s) {
auto prefix = text(s.state ? "" : ""); // NOLINT
auto t = text(s.label);
if (s.active) {
t |= bold;
}
if (s.focused) {
t |= inverted;
}
return hbox({prefix, t});
};
}
if (!transform) {
transform = [](bool is_open, Element checkbox_element,
Element radiobox_element) {
if (is_open) {
const int max_height = 12;
return vbox({
std::move(checkbox_element),
separator(),
std::move(radiobox_element) | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, max_height),
}) |
border;
}
return vbox({std::move(checkbox_element), filler()}) | border;
};
}
}
private:
ConstStringListRef entries_;
bool show_ = false;
int* selected_;
std::string title_;
Ref<bool> open_;
Ref<int> selected_;
Component checkbox_;
Component radiobox_;
std::string title_;
};
return Make<Impl>(entries, selected);
return Make<Impl>(option);
}
} // namespace ftxui

View File

@@ -1,12 +1,25 @@
// 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.
#include <map> // for map
#include <string>
#include <utility> // for move
#include "ftxui/component/event.hpp"
#include "ftxui/component/mouse.hpp" // for Mouse
#include "ftxui/screen/string.hpp" // for to_wstring
// Disable warning for shadowing variable, for every compilers. Indeed, there is
// a static Event for every letter of the alphabet:
#ifdef __clang__
#pragma clang diagnostic ignored "-Wshadow"
#elif __GNUC__
#pragma GCC diagnostic ignored "-Wshadow"
#elif defined(_MSC_VER)
#pragma warning(disable : 6244)
#pragma warning(disable : 6246)
#endif
namespace ftxui {
/// @brief An event corresponding to a given typed character.
@@ -49,6 +62,16 @@ Event Event::Mouse(std::string input, struct Mouse mouse) {
return event;
}
/// @brief An event corresponding to a terminal DCS (Device Control String).
// static
Event Event::CursorShape(std::string input, int shape) {
Event event;
event.input_ = std::move(input);
event.type_ = Type::CursorShape;
event.data_.cursor_shape = shape; // NOLINT
return event;
}
/// @brief An custom event whose meaning is defined by the user of the library.
/// @param input An arbitrary sequence of character defined by the developer.
/// @ingroup component.
@@ -61,50 +84,384 @@ Event Event::Special(std::string input) {
/// @internal
// static
Event Event::CursorReporting(std::string input, int x, int y) {
Event Event::CursorPosition(std::string input, int x, int y) {
Event event;
event.input_ = std::move(input);
event.type_ = Type::CursorReporting;
event.type_ = Type::CursorPosition;
event.data_.cursor = {x, y}; // NOLINT
return event;
}
/// @brief Return a string representation of the event.
std::string Event::DebugString() const {
static std::map<Event, const char*> event_to_string = {
// --- Arrow ---
{Event::ArrowLeft, "Event::ArrowLeft"},
{Event::ArrowRight, "Event::ArrowRight"},
{Event::ArrowUp, "Event::ArrowUp"},
{Event::ArrowDown, "Event::ArrowDown"},
// --- ArrowCtrl ---
{Event::ArrowLeftCtrl, "Event::ArrowLeftCtrl"},
{Event::ArrowRightCtrl, "Event::ArrowRightCtrl"},
{Event::ArrowUpCtrl, "Event::ArrowUpCtrl"},
{Event::ArrowDownCtrl, "Event::ArrowDownCtrl"},
// --- Other ---
{Event::Backspace, "Event::Backspace"},
{Event::Delete, "Event::Delete"},
{Event::Escape, "Event::Escape"},
{Event::Return, "Event::Return"},
{Event::Tab, "Event::Tab"},
{Event::TabReverse, "Event::TabReverse"},
// --- Function keys ---
{Event::F1, "Event::F1"},
{Event::F2, "Event::F2"},
{Event::F3, "Event::F3"},
{Event::F4, "Event::F4"},
{Event::F5, "Event::F5"},
{Event::F6, "Event::F6"},
{Event::F7, "Event::F7"},
{Event::F8, "Event::F8"},
{Event::F9, "Event::F9"},
{Event::F10, "Event::F10"},
{Event::F11, "Event::F11"},
{Event::F12, "Event::F12"},
// --- Navigation keys ---
{Event::Insert, "Event::Insert"},
{Event::Home, "Event::Home"},
{Event::End, "Event::End"},
{Event::PageUp, "Event::PageUp"},
{Event::PageDown, "Event::PageDown"},
// --- Control keys ---
{Event::CtrlA, "Event::CtrlA"},
{Event::CtrlB, "Event::CtrlB"},
{Event::CtrlC, "Event::CtrlC"},
{Event::CtrlD, "Event::CtrlD"},
{Event::CtrlE, "Event::CtrlE"},
{Event::CtrlF, "Event::CtrlF"},
{Event::CtrlG, "Event::CtrlG"},
{Event::CtrlH, "Event::CtrlH"},
{Event::CtrlI, "Event::CtrlI"},
{Event::CtrlJ, "Event::CtrlJ"},
{Event::CtrlK, "Event::CtrlK"},
{Event::CtrlL, "Event::CtrlL"},
{Event::CtrlM, "Event::CtrlM"},
{Event::CtrlN, "Event::CtrlN"},
{Event::CtrlO, "Event::CtrlO"},
{Event::CtrlP, "Event::CtrlP"},
{Event::CtrlQ, "Event::CtrlQ"},
{Event::CtrlR, "Event::CtrlR"},
{Event::CtrlS, "Event::CtrlS"},
{Event::CtrlT, "Event::CtrlT"},
{Event::CtrlU, "Event::CtrlU"},
{Event::CtrlV, "Event::CtrlV"},
{Event::CtrlW, "Event::CtrlW"},
{Event::CtrlX, "Event::CtrlX"},
{Event::CtrlY, "Event::CtrlY"},
{Event::CtrlZ, "Event::CtrlZ"},
// --- Alt keys ---
{Event::AltA, "Event::AltA"},
{Event::AltB, "Event::AltB"},
{Event::AltC, "Event::AltC"},
{Event::AltD, "Event::AltD"},
{Event::AltE, "Event::AltE"},
{Event::AltF, "Event::AltF"},
{Event::AltG, "Event::AltG"},
{Event::AltH, "Event::AltH"},
{Event::AltI, "Event::AltI"},
{Event::AltJ, "Event::AltJ"},
{Event::AltK, "Event::AltK"},
{Event::AltL, "Event::AltL"},
{Event::AltM, "Event::AltM"},
{Event::AltN, "Event::AltN"},
{Event::AltO, "Event::AltO"},
{Event::AltP, "Event::AltP"},
{Event::AltQ, "Event::AltQ"},
{Event::AltR, "Event::AltR"},
{Event::AltS, "Event::AltS"},
{Event::AltT, "Event::AltT"},
{Event::AltU, "Event::AltU"},
{Event::AltV, "Event::AltV"},
{Event::AltW, "Event::AltW"},
{Event::AltX, "Event::AltX"},
{Event::AltY, "Event::AltY"},
{Event::AltZ, "Event::AltZ"},
// --- CtrlAlt keys ---
{Event::CtrlAltA, "Event::CtrlAltA"},
{Event::CtrlAltB, "Event::CtrlAltB"},
{Event::CtrlAltC, "Event::CtrlAltC"},
{Event::CtrlAltD, "Event::CtrlAltD"},
{Event::CtrlAltE, "Event::CtrlAltE"},
{Event::CtrlAltF, "Event::CtrlAltF"},
{Event::CtrlAltG, "Event::CtrlAltG"},
{Event::CtrlAltH, "Event::CtrlAltH"},
{Event::CtrlAltI, "Event::CtrlAltI"},
{Event::CtrlAltJ, "Event::CtrlAltJ"},
{Event::CtrlAltK, "Event::CtrlAltK"},
{Event::CtrlAltL, "Event::CtrlAltL"},
{Event::CtrlAltM, "Event::CtrlAltM"},
{Event::CtrlAltN, "Event::CtrlAltN"},
{Event::CtrlAltO, "Event::CtrlAltO"},
{Event::CtrlAltP, "Event::CtrlAltP"},
{Event::CtrlAltQ, "Event::CtrlAltQ"},
{Event::CtrlAltR, "Event::CtrlAltR"},
{Event::CtrlAltS, "Event::CtrlAltS"},
{Event::CtrlAltT, "Event::CtrlAltT"},
{Event::CtrlAltU, "Event::CtrlAltU"},
{Event::CtrlAltV, "Event::CtrlAltV"},
{Event::CtrlAltW, "Event::CtrlAltW"},
{Event::CtrlAltX, "Event::CtrlAltX"},
{Event::CtrlAltY, "Event::CtrlAltY"},
{Event::CtrlAltZ, "Event::CtrlAltZ"},
// --- Custom ---
{Event::Custom, "Event::Custom"},
};
static std::map<Mouse::Button, const char*> mouse_button_string = {
{Mouse::Button::Left, ".button = Mouse::Left"},
{Mouse::Button::Middle, ".button = Mouse::Middle"},
{Mouse::Button::Right, ".button = Mouse::Right"},
{Mouse::Button::WheelUp, ".button = Mouse::WheelUp"},
{Mouse::Button::WheelDown, ".button = Mouse::WheelDown"},
{Mouse::Button::None, ".button = Mouse::None"},
{Mouse::Button::WheelLeft, ".button = Mouse::WheelLeft"},
{Mouse::Button::WheelRight, ".button = Mouse::WheelRight"},
};
static std::map<Mouse::Motion, const char*> mouse_motion_string = {
{Mouse::Motion::Pressed, ".motion = Mouse::Pressed"},
{Mouse::Motion::Released, ".motion = Mouse::Released"},
{Mouse::Motion::Moved, ".motion = Mouse::Moved"},
};
switch (type_) {
case Type::Character: {
return "Event::Character(\"" + input_ + "\")";
}
case Type::Mouse: {
std::string out = "Event::Mouse(\"...\", Mouse{";
out += std::string(mouse_button_string[data_.mouse.button]);
out += ", ";
out += std::string(mouse_motion_string[data_.mouse.motion]);
out += ", ";
if (data_.mouse.shift) {
out += ".shift = true, ";
}
if (data_.mouse.meta) {
out += ".meta = true, ";
}
if (data_.mouse.control) {
out += ".control = true, ";
}
out += ".x = " + std::to_string(data_.mouse.x);
out += ", ";
out += ".y = " + std::to_string(data_.mouse.y);
out += "})";
return out;
}
case Type::CursorShape:
return "Event::CursorShape(" + input_ + ", " +
std::to_string(data_.cursor_shape) + ")";
case Type::CursorPosition:
return "Event::CursorPosition(" + input_ + ", " +
std::to_string(data_.cursor.x) + ", " +
std::to_string(data_.cursor.y) + ")";
default: {
auto event_it = event_to_string.find(*this);
if (event_it != event_to_string.end()) {
return event_it->second;
}
return "";
}
}
return "";
}
// clang-format off
// NOLINTBEGIN
// --- Arrow ---
const Event Event::ArrowLeft = Event::Special("\x1B[D"); // NOLINT
const Event Event::ArrowRight = Event::Special("\x1B[C"); // NOLINT
const Event Event::ArrowUp = Event::Special("\x1B[A"); // NOLINT
const Event Event::ArrowDown = Event::Special("\x1B[B"); // NOLINT
const Event Event::ArrowLeftCtrl = Event::Special("\x1B[1;5D"); // NOLINT
const Event Event::ArrowRightCtrl = Event::Special("\x1B[1;5C"); // NOLINT
const Event Event::ArrowUpCtrl = Event::Special("\x1B[1;5A"); // NOLINT
const Event Event::ArrowDownCtrl = Event::Special("\x1B[1;5B"); // NOLINT
const Event Event::Backspace = Event::Special({127}); // NOLINT
const Event Event::Delete = Event::Special("\x1B[3~"); // NOLINT
const Event Event::Escape = Event::Special("\x1B"); // NOLINT
const Event Event::Return = Event::Special({10}); // NOLINT
const Event Event::Tab = Event::Special({9}); // NOLINT
const Event Event::TabReverse = Event::Special({27, 91, 90}); // NOLINT
const Event Event::ArrowLeft = Event::Special("\x1B[D");
const Event Event::ArrowRight = Event::Special("\x1B[C");
const Event Event::ArrowUp = Event::Special("\x1B[A");
const Event Event::ArrowDown = Event::Special("\x1B[B");
const Event Event::ArrowLeftCtrl = Event::Special("\x1B[1;5D");
const Event Event::ArrowRightCtrl = Event::Special("\x1B[1;5C");
const Event Event::ArrowUpCtrl = Event::Special("\x1B[1;5A");
const Event Event::ArrowDownCtrl = Event::Special("\x1B[1;5B");
const Event Event::Backspace = Event::Special({127});
const Event Event::Delete = Event::Special("\x1B[3~");
const Event Event::Escape = Event::Special("\x1B");
const Event Event::Return = Event::Special({10});
const Event Event::Tab = Event::Special({9});
const Event Event::TabReverse = Event::Special({27, 91, 90});
// See https://invisible-island.net/xterm/xterm-function-keys.html
// We follow xterm-new / vterm-xf86-v4 / mgt / screen
const Event Event::F1 = Event::Special("\x1BOP"); // NOLINT
const Event Event::F2 = Event::Special("\x1BOQ"); // NOLINT
const Event Event::F3 = Event::Special("\x1BOR"); // NOLINT
const Event Event::F4 = Event::Special("\x1BOS"); // NOLINT
const Event Event::F5 = Event::Special("\x1B[15~"); // NOLINT
const Event Event::F6 = Event::Special("\x1B[17~"); // NOLINT
const Event Event::F7 = Event::Special("\x1B[18~"); // NOLINT
const Event Event::F8 = Event::Special("\x1B[19~"); // NOLINT
const Event Event::F9 = Event::Special("\x1B[20~"); // NOLINT
const Event Event::F10 = Event::Special("\x1B[21~"); // NOLINT
const Event Event::F11 = Event::Special("\x1B[23~"); // NOLINT
const Event Event::F12 = Event::Special("\x1B[24~"); // NOLINT
const Event Event::F1 = Event::Special("\x1BOP");
const Event Event::F2 = Event::Special("\x1BOQ");
const Event Event::F3 = Event::Special("\x1BOR");
const Event Event::F4 = Event::Special("\x1BOS");
const Event Event::F5 = Event::Special("\x1B[15~");
const Event Event::F6 = Event::Special("\x1B[17~");
const Event Event::F7 = Event::Special("\x1B[18~");
const Event Event::F8 = Event::Special("\x1B[19~");
const Event Event::F9 = Event::Special("\x1B[20~");
const Event Event::F10 = Event::Special("\x1B[21~");
const Event Event::F11 = Event::Special("\x1B[23~");
const Event Event::F12 = Event::Special("\x1B[24~");
const Event Event::Insert = Event::Special("\x1B[2~"); // NOLINT
const Event Event::Home = Event::Special({27, 91, 72}); // NOLINT
const Event Event::End = Event::Special({27, 91, 70}); // NOLINT
const Event Event::PageUp = Event::Special({27, 91, 53, 126}); // NOLINT
const Event Event::PageDown = Event::Special({27, 91, 54, 126}); // NOLINT
const Event Event::Custom = Event::Special({0}); // NOLINT
const Event Event::Insert = Event::Special("\x1B[2~");
const Event Event::Home = Event::Special({27, 91, 72});
const Event Event::End = Event::Special({27, 91, 70});
const Event Event::PageUp = Event::Special({27, 91, 53, 126});
const Event Event::PageDown = Event::Special({27, 91, 54, 126});
const Event Event::Custom = Event::Special({0});
const Event Event::a = Event::Character("a");
const Event Event::b = Event::Character("b");
const Event Event::c = Event::Character("c");
const Event Event::d = Event::Character("d");
const Event Event::e = Event::Character("e");
const Event Event::f = Event::Character("f");
const Event Event::g = Event::Character("g");
const Event Event::h = Event::Character("h");
const Event Event::i = Event::Character("i");
const Event Event::j = Event::Character("j");
const Event Event::k = Event::Character("k");
const Event Event::l = Event::Character("l");
const Event Event::m = Event::Character("m");
const Event Event::n = Event::Character("n");
const Event Event::o = Event::Character("o");
const Event Event::p = Event::Character("p");
const Event Event::q = Event::Character("q");
const Event Event::r = Event::Character("r");
const Event Event::s = Event::Character("s");
const Event Event::t = Event::Character("t");
const Event Event::u = Event::Character("u");
const Event Event::v = Event::Character("v");
const Event Event::w = Event::Character("w");
const Event Event::x = Event::Character("x");
const Event Event::y = Event::Character("y");
const Event Event::z = Event::Character("z");
const Event Event::A = Event::Character("A");
const Event Event::B = Event::Character("B");
const Event Event::C = Event::Character("C");
const Event Event::D = Event::Character("D");
const Event Event::E = Event::Character("E");
const Event Event::F = Event::Character("F");
const Event Event::G = Event::Character("G");
const Event Event::H = Event::Character("H");
const Event Event::I = Event::Character("I");
const Event Event::J = Event::Character("J");
const Event Event::K = Event::Character("K");
const Event Event::L = Event::Character("L");
const Event Event::M = Event::Character("M");
const Event Event::N = Event::Character("N");
const Event Event::O = Event::Character("O");
const Event Event::P = Event::Character("P");
const Event Event::Q = Event::Character("Q");
const Event Event::R = Event::Character("R");
const Event Event::S = Event::Character("S");
const Event Event::T = Event::Character("T");
const Event Event::U = Event::Character("U");
const Event Event::V = Event::Character("V");
const Event Event::W = Event::Character("W");
const Event Event::X = Event::Character("X");
const Event Event::Y = Event::Character("Y");
const Event Event::Z = Event::Character("Z");
const Event Event::CtrlA = Event::Special("\x01");
const Event Event::CtrlB = Event::Special("\x02");
const Event Event::CtrlC = Event::Special("\x03");
const Event Event::CtrlD = Event::Special("\x04");
const Event Event::CtrlE = Event::Special("\x05");
const Event Event::CtrlF = Event::Special("\x06");
const Event Event::CtrlG = Event::Special("\x07");
const Event Event::CtrlH = Event::Special("\x08");
const Event Event::CtrlI = Event::Special("\x09");
const Event Event::CtrlJ = Event::Special("\x0a");
const Event Event::CtrlK = Event::Special("\x0b");
const Event Event::CtrlL = Event::Special("\x0c");
const Event Event::CtrlM = Event::Special("\x0d");
const Event Event::CtrlN = Event::Special("\x0e");
const Event Event::CtrlO = Event::Special("\x0f");
const Event Event::CtrlP = Event::Special("\x10");
const Event Event::CtrlQ = Event::Special("\x11");
const Event Event::CtrlR = Event::Special("\x12");
const Event Event::CtrlS = Event::Special("\x13");
const Event Event::CtrlT = Event::Special("\x14");
const Event Event::CtrlU = Event::Special("\x15");
const Event Event::CtrlV = Event::Special("\x16");
const Event Event::CtrlW = Event::Special("\x17");
const Event Event::CtrlX = Event::Special("\x18");
const Event Event::CtrlY = Event::Special("\x19");
const Event Event::CtrlZ = Event::Special("\x1a");
const Event Event::AltA = Event::Special("\x1b""a");
const Event Event::AltB = Event::Special("\x1b""b");
const Event Event::AltC = Event::Special("\x1b""c");
const Event Event::AltD = Event::Special("\x1b""d");
const Event Event::AltE = Event::Special("\x1b""e");
const Event Event::AltF = Event::Special("\x1b""f");
const Event Event::AltG = Event::Special("\x1b""g");
const Event Event::AltH = Event::Special("\x1b""h");
const Event Event::AltI = Event::Special("\x1b""i");
const Event Event::AltJ = Event::Special("\x1b""j");
const Event Event::AltK = Event::Special("\x1b""k");
const Event Event::AltL = Event::Special("\x1b""l");
const Event Event::AltM = Event::Special("\x1b""m");
const Event Event::AltN = Event::Special("\x1b""n");
const Event Event::AltO = Event::Special("\x1b""o");
const Event Event::AltP = Event::Special("\x1b""p");
const Event Event::AltQ = Event::Special("\x1b""q");
const Event Event::AltR = Event::Special("\x1b""r");
const Event Event::AltS = Event::Special("\x1b""s");
const Event Event::AltT = Event::Special("\x1b""t");
const Event Event::AltU = Event::Special("\x1b""u");
const Event Event::AltV = Event::Special("\x1b""v");
const Event Event::AltW = Event::Special("\x1b""w");
const Event Event::AltX = Event::Special("\x1b""x");
const Event Event::AltY = Event::Special("\x1b""y");
const Event Event::AltZ = Event::Special("\x1b""z");
const Event Event::CtrlAltA = Event::Special("\x1b\x01");
const Event Event::CtrlAltB = Event::Special("\x1b\x02");
const Event Event::CtrlAltC = Event::Special("\x1b\x03");
const Event Event::CtrlAltD = Event::Special("\x1b\x04");
const Event Event::CtrlAltE = Event::Special("\x1b\x05");
const Event Event::CtrlAltF = Event::Special("\x1b\x06");
const Event Event::CtrlAltG = Event::Special("\x1b\x07");
const Event Event::CtrlAltH = Event::Special("\x1b\x08");
const Event Event::CtrlAltI = Event::Special("\x1b\x09");
const Event Event::CtrlAltJ = Event::Special("\x1b\x0a");
const Event Event::CtrlAltK = Event::Special("\x1b\x0b");
const Event Event::CtrlAltL = Event::Special("\x1b\x0c");
const Event Event::CtrlAltM = Event::Special("\x1b\x0d");
const Event Event::CtrlAltN = Event::Special("\x1b\x0e");
const Event Event::CtrlAltO = Event::Special("\x1b\x0f");
const Event Event::CtrlAltP = Event::Special("\x1b\x10");
const Event Event::CtrlAltQ = Event::Special("\x1b\x11");
const Event Event::CtrlAltR = Event::Special("\x1b\x12");
const Event Event::CtrlAltS = Event::Special("\x1b\x13");
const Event Event::CtrlAltT = Event::Special("\x1b\x14");
const Event Event::CtrlAltU = Event::Special("\x1b\x15");
const Event Event::CtrlAltV = Event::Special("\x1b\x16");
const Event Event::CtrlAltW = Event::Special("\x1b\x17");
const Event Event::CtrlAltX = Event::Special("\x1b\x18");
const Event Event::CtrlAltY = Event::Special("\x1b\x19");
const Event Event::CtrlAltZ = Event::Special("\x1b\x1a");
// NOLINTEND
// clang-format on
} // namespace ftxui

View File

@@ -1,9 +1,8 @@
// Copyright 2022 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <ftxui/component/captured_mouse.hpp> // for CapturedMouse
#include <functional> // for function
#include <utility> // for move
#include <functional> // for function
#include <utility> // for move
#include "ftxui/component/component.hpp" // for ComponentDecorator, Hoverable, Make
#include "ftxui/component/component_base.hpp" // for ComponentBase

View File

@@ -2,8 +2,7 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <ftxui/dom/elements.hpp> // for Element, text
#include <memory> // for shared_ptr, __shared_ptr_access, allocator
#include <string> // for string
#include <string> // for string
#include "ftxui/component/component.hpp" // for Hoverable, Horizontal, operator|=, Renderer
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
@@ -70,10 +69,10 @@ TEST(HoverableTest, BasicCallback) {
int on_enter_2 = 0;
int on_leave_1 = 0;
int on_leave_2 = 0;
auto c1 = Hoverable(
BasicComponent(), [&] { on_enter_1++; }, [&] { on_leave_1++; });
auto c2 = Hoverable(
BasicComponent(), [&] { on_enter_2++; }, [&] { on_leave_2++; });
auto c1 =
Hoverable(BasicComponent(), [&] { on_enter_1++; }, [&] { on_leave_1++; });
auto c2 =
Hoverable(BasicComponent(), [&] { on_enter_2++; }, [&] { on_leave_2++; });
auto layout = Container::Horizontal({c1, c2});
auto screen = Screen(8, 2);
Render(screen, layout->Render());

View File

@@ -5,13 +5,11 @@
#include <cstddef> // for size_t
#include <cstdint> // for uint32_t
#include <functional> // for function
#include <memory> // for allocator, shared_ptr, allocator_traits<>::value_type
#include <sstream> // for basic_istream, stringstream
#include <string> // for string, basic_string, operator==, getline
#include <utility> // for move
#include <vector> // for vector
#include <sstream> // for basic_istream, stringstream
#include <string> // for string, basic_string, operator==, getline
#include <utility> // for move
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Input
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for InputOption
@@ -134,7 +132,7 @@ class InputBase : public ComponentBase, public InputOption {
break;
}
cursor_char_index -= line.size() + 1;
cursor_char_index -= static_cast<int>(line.size() + 1);
cursor_line++;
}
@@ -164,7 +162,7 @@ class InputBase : public ComponentBase, public InputOption {
// The cursor is on this line.
const int glyph_start = cursor_char_index;
const int glyph_end = GlyphNext(line, glyph_start);
const int glyph_end = static_cast<int>(GlyphNext(line, glyph_start));
const std::string part_before_cursor = line.substr(0, glyph_start);
const std::string part_at_cursor =
line.substr(glyph_start, glyph_end - glyph_start);
@@ -206,11 +204,12 @@ class InputBase : public ComponentBase, public InputOption {
const size_t start = GlyphPrevious(content(), cursor_position());
const size_t end = cursor_position();
content->erase(start, end - start);
cursor_position() = start;
cursor_position() = static_cast<int>(start);
on_change();
return true;
}
bool HandleDelete() {
bool DeleteImpl() {
if (cursor_position() == (int)content->size()) {
return false;
}
@@ -220,12 +219,21 @@ class InputBase : public ComponentBase, public InputOption {
return true;
}
bool HandleDelete() {
if (DeleteImpl()) {
on_change();
return true;
}
return false;
}
bool HandleArrowLeft() {
if (cursor_position() == 0) {
return false;
}
cursor_position() = GlyphPrevious(content(), cursor_position());
cursor_position() =
static_cast<int>(GlyphPrevious(content(), cursor_position()));
return true;
}
@@ -234,7 +242,8 @@ class InputBase : public ComponentBase, public InputOption {
return false;
}
cursor_position() = GlyphNext(content(), cursor_position());
cursor_position() =
static_cast<int>(GlyphNext(content(), cursor_position()));
return true;
}
@@ -249,7 +258,7 @@ class InputBase : public ComponentBase, public InputOption {
if (content()[iter] == '\n') {
break;
}
width += GlyphWidth(content(), iter);
width += static_cast<int>(GlyphWidth(content(), iter));
}
return width;
}
@@ -262,8 +271,9 @@ class InputBase : public ComponentBase, public InputOption {
return;
}
columns -= GlyphWidth(content(), cursor_position());
cursor_position() = GlyphNext(content(), cursor_position());
columns -= static_cast<int>(GlyphWidth(content(), cursor_position()));
cursor_position() =
static_cast<int>(GlyphNext(content(), cursor_position()));
}
}
@@ -283,9 +293,10 @@ class InputBase : public ComponentBase, public InputOption {
if (content()[previous] == '\n') {
break;
}
cursor_position() = previous;
cursor_position() = static_cast<int>(previous);
}
cursor_position() = GlyphPrevious(content(), cursor_position());
cursor_position() =
static_cast<int>(GlyphPrevious(content(), cursor_position()));
while (true) {
if (cursor_position() == 0) {
break;
@@ -294,10 +305,10 @@ class InputBase : public ComponentBase, public InputOption {
if (content()[previous] == '\n') {
break;
}
cursor_position() = previous;
cursor_position() = static_cast<int>(previous);
}
MoveCursorColumn(columns);
MoveCursorColumn(static_cast<int>(columns));
return true;
}
@@ -313,14 +324,16 @@ class InputBase : public ComponentBase, public InputOption {
if (content()[cursor_position()] == '\n') {
break;
}
cursor_position() = GlyphNext(content(), cursor_position());
cursor_position() =
static_cast<int>(GlyphNext(content(), cursor_position()));
if (cursor_position() == (int)content().size()) {
return true;
}
}
cursor_position() = GlyphNext(content(), cursor_position());
cursor_position() =
static_cast<int>(GlyphNext(content(), cursor_position()));
MoveCursorColumn(columns);
MoveCursorColumn(static_cast<int>(columns));
return true;
}
@@ -330,7 +343,7 @@ class InputBase : public ComponentBase, public InputOption {
}
bool HandleEnd() {
cursor_position() = content->size();
cursor_position() = static_cast<int>(content->size());
return true;
}
@@ -345,10 +358,10 @@ class InputBase : public ComponentBase, public InputOption {
bool HandleCharacter(const std::string& character) {
if (!insert() && cursor_position() < (int)content->size() &&
content()[cursor_position()] != '\n') {
HandleDelete();
DeleteImpl();
}
content->insert(cursor_position(), character);
cursor_position() += character.size();
cursor_position() += static_cast<int>(character.size());
on_change();
return true;
}
@@ -412,7 +425,7 @@ class InputBase : public ComponentBase, public InputOption {
if (IsWordCharacter(content(), previous)) {
break;
}
cursor_position() = previous;
cursor_position() = static_cast<int>(previous);
}
// Move left, as long as left is a word character:
while (cursor_position()) {
@@ -420,7 +433,7 @@ class InputBase : public ComponentBase, public InputOption {
if (!IsWordCharacter(content(), previous)) {
break;
}
cursor_position() = previous;
cursor_position() = static_cast<int>(previous);
}
return true;
}
@@ -432,7 +445,8 @@ class InputBase : public ComponentBase, public InputOption {
// Move right, until entering a word.
while (cursor_position() < (int)content().size()) {
cursor_position() = GlyphNext(content(), cursor_position());
cursor_position() =
static_cast<int>(GlyphNext(content(), cursor_position()));
if (IsWordCharacter(content(), cursor_position())) {
break;
}
@@ -443,7 +457,7 @@ class InputBase : public ComponentBase, public InputOption {
if (!IsWordCharacter(content(), cursor_position())) {
break;
}
cursor_position() = next;
cursor_position() = static_cast<int>(next);
}
return true;
@@ -457,8 +471,10 @@ class InputBase : public ComponentBase, public InputOption {
return false;
}
if (event.mouse().button != Mouse::Left ||
event.mouse().motion != Mouse::Pressed) {
if (event.mouse().button != Mouse::Left) {
return false;
}
if (event.mouse().motion != Mouse::Pressed) {
return false;
}
@@ -478,7 +494,7 @@ class InputBase : public ComponentBase, public InputOption {
break;
}
cursor_char_index -= line.size() + 1;
cursor_char_index -= static_cast<int>(line.size() + 1);
cursor_line++;
}
const int cursor_column =
@@ -504,11 +520,13 @@ class InputBase : public ComponentBase, public InputOption {
// Convert back the new_cursor_{line,column} toward cursor_position:
cursor_position() = 0;
for (int i = 0; i < new_cursor_line; ++i) {
cursor_position() += lines[i].size() + 1;
cursor_position() += static_cast<int>(lines[i].size() + 1);
}
while (new_cursor_column > 0) {
new_cursor_column -= GlyphWidth(content(), cursor_position());
cursor_position() = GlyphNext(content(), cursor_position());
new_cursor_column -=
static_cast<int>(GlyphWidth(content(), cursor_position()));
cursor_position() =
static_cast<int>(GlyphNext(content(), cursor_position()));
}
on_change();

View File

@@ -1,7 +1,6 @@
// Copyright 2023 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string
#include "ftxui/component/component.hpp" // for Input

View File

@@ -15,8 +15,8 @@ namespace ftxui {
/// @see Component, ScreenInteractive.
/// @see ScreenInteractive::Loop().
/// @see ScreenInteractive::ExitLoop().
/// @param screen The screen to use.
/// @param component The component to run.
/// @param[in] screen The screen to use.
/// @param[in] component The component to run.
// NOLINTNEXTLINE
Loop::Loop(ScreenInteractive* screen, Component component)
: screen_(screen), component_(std::move(component)) {

View File

@@ -3,8 +3,7 @@
// the LICENSE file.
#include <functional> // for function
#include <memory> // for make_unique, __shared_ptr_access, __shared_ptr_access<>::element_type, shared_ptr
#include <type_traits> // for remove_reference, remove_reference<>::type
#include <utility> // for move
#include <utility> // for move
#include "ftxui/component/component.hpp" // for ComponentDecorator, Maybe, Make
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase

View File

@@ -5,13 +5,11 @@
#include <chrono> // for milliseconds
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
#include <functional> // for function
#include <memory> // for allocator_traits<>::value_type, swap
#include <string> // for operator+, string
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include "ftxui/component/animation.hpp" // for Animator, Linear
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/animation.hpp" // for Animator, Linear
#include "ftxui/component/component.hpp" // for Make, Menu, MenuEntry, Toggle
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for MenuOption, MenuEntryOption, UnderlineOption, AnimatedColorOption, AnimatedColorsOption, EntryState
@@ -70,7 +68,7 @@ bool IsHorizontal(Direction direction) {
/// @ingroup component
class MenuBase : public ComponentBase, public MenuOption {
public:
explicit MenuBase(MenuOption option) : MenuOption(std::move(option)) {}
explicit MenuBase(const MenuOption& option) : MenuOption(option) {}
bool IsHorizontal() { return ftxui::IsHorizontal(direction); }
void OnChange() {
@@ -131,8 +129,9 @@ class MenuBase : public ComponentBase, public MenuOption {
is_focused,
};
auto focus_management =
is_menu_focused && (selected_focus_ == i) ? focus : nothing;
auto focus_management = (selected_focus_ != i) ? nothing
: is_menu_focused ? focus
: select;
const Element element =
(entries_option.transform ? entries_option.transform
@@ -318,8 +317,9 @@ class MenuBase : public ComponentBase, public MenuOption {
TakeFocus();
focused_entry() = i;
if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Released) {
event.mouse().motion == Mouse::Pressed) {
if (selected() != i) {
selected() = i;
selected_previous_ = selected();
@@ -511,6 +511,7 @@ class MenuBase : public ComponentBase, public MenuOption {
/// entry 2
/// entry 3
/// ```
// NOLINTNEXTLINE
Component Menu(MenuOption option) {
return Make<MenuBase>(std::move(option));
}
@@ -543,9 +544,9 @@ Component Menu(MenuOption option) {
/// entry 3
/// ```
Component Menu(ConstStringListRef entries, int* selected, MenuOption option) {
option.entries = entries;
option.entries = std::move(entries);
option.selected = selected;
return Menu(std::move(option));
return Menu(option);
}
/// @brief An horizontal list of elements. The user can navigate through them.
@@ -554,7 +555,7 @@ Component Menu(ConstStringListRef entries, int* selected, MenuOption option) {
/// See also |Menu|.
/// @ingroup component
Component Toggle(ConstStringListRef entries, int* selected) {
return Menu(entries, selected, MenuOption::Toggle());
return Menu(std::move(entries), selected, MenuOption::Toggle());
}
/// @brief A specific menu entry. They can be put into a Container::Vertical to
@@ -584,7 +585,7 @@ Component Toggle(ConstStringListRef entries, int* selected) {
/// entry 3
/// ```
Component MenuEntry(ConstStringRef label, MenuEntryOption option) {
option.label = label;
option.label = std::move(label);
return MenuEntry(std::move(option));
}
@@ -684,7 +685,7 @@ Component MenuEntry(MenuEntryOption option) {
}
if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Released) {
event.mouse().motion == Mouse::Pressed) {
TakeFocus();
return true;
}

View File

@@ -2,11 +2,9 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <gtest/gtest.h> // for Test, EXPECT_EQ, Message, TestPartResult, TestInfo (ptr only), TEST
#include <chrono> // for operator""s, chrono_literals
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string, basic_string
#include <vector> // for vector
#include <string> // for string, basic_string
#include <vector> // for vector
#include "ftxui/component/animation.hpp" // for Duration, Params
#include "ftxui/component/component.hpp" // for Menu

View File

@@ -3,7 +3,6 @@
// the LICENSE file.
#include <gtest/gtest.h>
#include <ftxui/dom/elements.hpp> // for Element, operator|, text, border
#include <memory> // for shared_ptr, allocator, __shared_ptr_access
#include "ftxui/component/component.hpp" // for Renderer, Modal
#include "ftxui/component/component_base.hpp" // for ComponentBase

View File

@@ -2,11 +2,9 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <functional> // for function
#include <memory> // for allocator_traits<>::value_type
#include <utility> // for move
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Radiobox
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for RadioboxOption, EntryState
@@ -26,7 +24,8 @@ namespace {
/// @ingroup component
class RadioboxBase : public ComponentBase, public RadioboxOption {
public:
explicit RadioboxBase(RadioboxOption option) : RadioboxOption(option) {}
explicit RadioboxBase(const RadioboxOption& option)
: RadioboxOption(option) {}
private:
Element Render() override {
@@ -124,7 +123,7 @@ class RadioboxBase : public ComponentBase, public RadioboxOption {
TakeFocus();
focused_entry() = i;
if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Released) {
event.mouse().motion == Mouse::Pressed) {
if (selected() != i) {
selected() = i;
on_change();
@@ -205,6 +204,7 @@ class RadioboxBase : public ComponentBase, public RadioboxOption {
/// ○ entry 2
/// ○ entry 3
/// ```
/// NOLINTNEXTLINE
Component Radiobox(RadioboxOption option) {
return Make<RadioboxBase>(std::move(option));
}
@@ -240,7 +240,7 @@ Component Radiobox(RadioboxOption option) {
Component Radiobox(ConstStringListRef entries,
int* selected,
RadioboxOption option) {
option.entries = entries;
option.entries = std::move(entries);
option.selected = selected;
return Make<RadioboxBase>(std::move(option));
}

View File

@@ -4,9 +4,8 @@
#include <ftxui/dom/elements.hpp> // for yframe
#include <ftxui/dom/node.hpp> // for Render
#include <ftxui/screen/screen.hpp> // for Screen
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string, basic_string
#include <vector> // for vector
#include <string> // for string, basic_string
#include <vector> // for vector
#include "ftxui/component/component.hpp" // for Radiobox, operator|
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component

View File

@@ -1,7 +1,6 @@
// 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.
#include <string> // for string
#include <thread> // for thread
#include <utility> // for move

View File

@@ -2,10 +2,8 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <functional> // for function
#include <memory> // for __shared_ptr_access, shared_ptr
#include <utility> // for move
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Renderer
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
#include "ftxui/component/event.hpp" // for Event

View File

@@ -5,8 +5,7 @@
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
#include <ftxui/util/ref.hpp> // for Ref
#include <functional> // for function
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <utility> // for move
#include <utility> // for move
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Horizontal, Make, ResizableSplit, ResizableSplitBottom, ResizableSplitLeft, ResizableSplitRight, ResizableSplitTop
@@ -23,10 +22,32 @@ class ResizableSplitBase : public ComponentBase {
public:
explicit ResizableSplitBase(ResizableSplitOption options)
: options_(std::move(options)) {
Add(Container::Horizontal({
options_->main,
options_->back,
}));
switch (options_->direction()) {
case Direction::Left:
Add(Container::Horizontal({
options_->main,
options_->back,
}));
break;
case Direction::Right:
Add(Container::Horizontal({
options_->back,
options_->main,
}));
break;
case Direction::Up:
Add(Container::Vertical({
options_->main,
options_->back,
}));
break;
case Direction::Down:
Add(Container::Vertical({
options_->back,
options_->main,
}));
break;
}
}
bool OnEvent(Event event) final {

View File

@@ -2,8 +2,7 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string
#include <string> // for string
#include "ftxui/component/component.hpp" // for ResizableSplit, Renderer, ResizableSplitBottom, ResizableSplitLeft, ResizableSplitRight, ResizableSplitTop
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
@@ -19,7 +18,7 @@ namespace ftxui {
namespace {
Component BasicComponent() {
return Renderer([] { return text(""); });
return Renderer([](bool focused) { return text(""); });
}
Event MousePressed(int x, int y) {
@@ -207,5 +206,32 @@ TEST(ResizableSplit, BasicBottomWithCustomSeparator) {
EXPECT_EQ(position, 2);
}
TEST(ResizableSplit, NavigationVertical) {
int position = 0;
auto component_top = BasicComponent();
auto component_bottom = BasicComponent();
auto component =
ResizableSplitTop(component_top, component_bottom, &position);
EXPECT_TRUE(component_top->Active());
EXPECT_FALSE(component_bottom->Active());
EXPECT_FALSE(component->OnEvent(Event::ArrowRight));
EXPECT_TRUE(component_top->Active());
EXPECT_FALSE(component_bottom->Active());
EXPECT_TRUE(component->OnEvent(Event::ArrowDown));
EXPECT_FALSE(component_top->Active());
EXPECT_TRUE(component_bottom->Active());
EXPECT_FALSE(component->OnEvent(Event::ArrowDown));
EXPECT_FALSE(component_top->Active());
EXPECT_TRUE(component_bottom->Active());
EXPECT_TRUE(component->OnEvent(Event::ArrowUp));
EXPECT_TRUE(component_top->Active());
EXPECT_FALSE(component_bottom->Active());
}
} // namespace ftxui
// NOLINTEND

View File

@@ -1,34 +1,38 @@
// 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.
#include "ftxui/component/screen_interactive.hpp"
#include <algorithm> // for copy, max, min
#include <array> // for array
#include <atomic>
#include <chrono> // for operator-, milliseconds, operator>=, duration, common_type<>::type, time_point
#include <csignal> // for signal, SIGTSTP, SIGABRT, SIGWINCH, raise, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, __sighandler_t, size_t
#include <cstdio> // for fileno, stdin
#include <cstdint>
#include <cstdio> // for fileno, stdin
#include <ftxui/component/task.hpp> // for Task, Closure, AnimationTask
#include <ftxui/screen/screen.hpp> // for Pixel, Screen::Cursor, Screen, Screen::Cursor::Hidden
#include <functional> // for function
#include <initializer_list> // for initializer_list
#include <iostream> // for cout, ostream, operator<<, basic_ostream, endl, flush
#include <stack> // for stack
#include <thread> // for thread, sleep_for
#include <tuple> // for _Swallow_assign, ignore
#include <memory>
#include <stack> // for stack
#include <string>
#include <thread> // for thread, sleep_for
#include <tuple> // for _Swallow_assign, ignore
#include <type_traits> // for decay_t
#include <utility> // for move, swap
#include <variant> // for visit, variant
#include <vector> // for vector
#include "ftxui/component/animation.hpp" // for TimePoint, Clock, Duration, Params, RequestAnimationFrame
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/loop.hpp" // for Loop
#include "ftxui/component/receiver.hpp" // for ReceiverImpl, Sender, MakeReceiver, SenderImpl, Receiver
#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/requirement.hpp" // for Requirement
#include "ftxui/screen/pixel.hpp" // for Pixel
#include "ftxui/screen/terminal.hpp" // for Dimensions, Size
#if defined(_WIN32)
@@ -132,7 +136,6 @@ void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
// Read char from the terminal.
void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
(void)timeout_microseconds;
auto parser = TerminalInputParser(std::move(out));
char c;
@@ -159,7 +162,7 @@ void ftxui_on_resize(int columns, int rows) {
#else // POSIX (Linux & Mac)
int CheckStdinReady(int usec_timeout) {
timeval tv = {0, usec_timeout};
timeval tv = {0, usec_timeout}; // NOLINT
fd_set fds;
FD_ZERO(&fds); // NOLINT
FD_SET(STDIN_FILENO, &fds); // NOLINT
@@ -214,11 +217,11 @@ void RecordSignal(int signal) {
break;
#if !defined(_WIN32)
case SIGTSTP:
case SIGTSTP: // NOLINT
g_signal_stop_count++;
break;
case SIGWINCH:
case SIGWINCH: // NOLINT
g_signal_resize_count++;
break;
#endif
@@ -249,14 +252,24 @@ void ExecuteSignalHandlers() {
void InstallSignalHandler(int sig) {
auto old_signal_handler = std::signal(sig, RecordSignal);
on_exit_functions.push(
on_exit_functions.emplace(
[=] { std::ignore = std::signal(sig, old_signal_handler); });
}
// CSI: Control Sequence Introducer
const std::string CSI = "\x1b["; // NOLINT
//
// DCS: Device Control String
const std::string DCS = "\x1bP"; // NOLINT
// ST: String Terminator
const std::string ST = "\x1b\\"; // NOLINT
// DECRQSS: Request Status String
// DECSCUSR: Set Cursor Style
const std::string DECRQSS_DECSCUSR = DCS + "$q q" + ST; // NOLINT
// DEC: Digital Equipment Corporation
enum class DECMode {
enum class DECMode : std::uint16_t {
kLineWrap = 7,
kCursor = 25,
@@ -275,7 +288,7 @@ enum class DECMode {
};
// Device Status Report (DSR) {
enum class DSRMode {
enum class DSRMode : std::uint8_t {
kCursor = 6,
};
@@ -352,8 +365,34 @@ ScreenInteractive ScreenInteractive::FixedSize(int dimx, int dimy) {
};
}
/// @ingroup component
/// Create a ScreenInteractive taking the full terminal size. This is using the
/// alternate screen buffer to avoid messing with the terminal content.
/// @note This is the same as `ScreenInteractive::FullscreenAlternateScreen()`
// static
ScreenInteractive ScreenInteractive::Fullscreen() {
return FullscreenAlternateScreen();
}
/// @ingroup component
/// Create a ScreenInteractive taking the full terminal size. The primary screen
/// buffer is being used. It means if the terminal is resized, the previous
/// content might mess up with the terminal content.
// static
ScreenInteractive ScreenInteractive::FullscreenPrimaryScreen() {
return {
0,
0,
Dimension::Fullscreen,
false,
};
}
/// @ingroup component
/// Create a ScreenInteractive taking the full terminal size. This is using the
/// alternate screen buffer to avoid messing with the terminal content.
// static
ScreenInteractive ScreenInteractive::FullscreenAlternateScreen() {
return {
0,
0,
@@ -402,7 +441,7 @@ void ScreenInteractive::TrackMouse(bool enable) {
track_mouse_ = enable;
}
/// @brief Add a task to the main loop.
/// @brief Add a task to the main loop.
/// It will be executed later, after every other scheduled tasks.
/// @ingroup component
void ScreenInteractive::Post(Task task) {
@@ -508,7 +547,8 @@ void ScreenInteractive::PostMain() {
// On final exit, keep the current drawing and reset cursor position one
// line after it.
if (!use_alternative_screen_) {
std::cout << std::endl;
std::cout << '\n';
std::cout << std::flush;
}
}
}
@@ -524,6 +564,18 @@ Closure ScreenInteractive::WithRestoredIO(Closure fn) { // NOLINT
};
}
/// @brief Force FTXUI to handle or not handle Ctrl-C, even if the component
/// catches the Event::CtrlC.
void ScreenInteractive::ForceHandleCtrlC(bool force) {
force_handle_ctrl_c_ = force;
}
/// @brief Force FTXUI to handle or not handle Ctrl-Z, even if the component
/// catches the Event::CtrlZ.
void ScreenInteractive::ForceHandleCtrlZ(bool force) {
force_handle_ctrl_z_ = force;
}
/// @brief Return the currently active screen, or null if none.
// static
ScreenInteractive* ScreenInteractive::Active() {
@@ -534,11 +586,26 @@ ScreenInteractive* ScreenInteractive::Active() {
void ScreenInteractive::Install() {
frame_valid_ = false;
// Flush the buffer for stdout to ensure whatever the user has printed before
// is fully applied before we start modifying the terminal configuration. This
// is important, because we are using two different channels (stdout vs
// termios/WinAPI) to communicate with the terminal emulator below. See
// https://github.com/ArthurSonzogni/FTXUI/issues/846
Flush();
// After uninstalling the new configuration, flush it to the terminal to
// ensure it is fully applied:
on_exit_functions.push([] { Flush(); });
on_exit_functions.emplace([] { Flush(); });
on_exit_functions.push([this] { ExitLoopClosure()(); });
on_exit_functions.emplace([this] { ExitLoopClosure()(); });
// Request the terminal to report the current cursor shape. We will restore it
// on exit.
std::cout << DECRQSS_DECSCUSR;
on_exit_functions.emplace([this] {
std::cout << "\033[?25h"; // Enable cursor.
std::cout << "\033[" + std::to_string(cursor_reset_shape_) + " q";
});
// Install signal handlers to restore the terminal state on exit. The default
// signal handlers are restored on exit.
@@ -584,15 +651,34 @@ void ScreenInteractive::Install() {
struct termios terminal; // NOLINT
tcgetattr(STDIN_FILENO, &terminal);
on_exit_functions.push([=] { tcsetattr(STDIN_FILENO, TCSANOW, &terminal); });
on_exit_functions.emplace(
[=] { tcsetattr(STDIN_FILENO, TCSANOW, &terminal); });
terminal.c_lflag &= ~ICANON; // NOLINT Non canonique terminal.
terminal.c_lflag &= ~ECHO; // NOLINT Do not print after a key press.
terminal.c_cc[VMIN] = 0;
terminal.c_cc[VTIME] = 0;
// auto oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
// fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
// on_exit_functions.push([=] { fcntl(STDIN_FILENO, F_GETFL, oldf); });
// Enabling raw terminal input mode
terminal.c_iflag &= ~IGNBRK; // Disable ignoring break condition
terminal.c_iflag &= ~BRKINT; // Disable break causing input and output to be
// flushed
terminal.c_iflag &= ~PARMRK; // Disable marking parity errors.
terminal.c_iflag &= ~ISTRIP; // Disable striping 8th bit off characters.
terminal.c_iflag &= ~INLCR; // Disable mapping NL to CR.
terminal.c_iflag &= ~IGNCR; // Disable ignoring CR.
terminal.c_iflag &= ~ICRNL; // Disable mapping CR to NL.
terminal.c_iflag &= ~IXON; // Disable XON/XOFF flow control on output
terminal.c_lflag &= ~ECHO; // Disable echoing input characters.
terminal.c_lflag &= ~ECHONL; // Disable echoing new line characters.
terminal.c_lflag &= ~ICANON; // Disable Canonical mode.
terminal.c_lflag &= ~ISIG; // Disable sending signal when hitting:
// - => DSUSP
// - C-Z => SUSP
// - C-C => INTR
// - C-d => QUIT
terminal.c_lflag &= ~IEXTEN; // Disable extended input processing
terminal.c_cflag |= CS8; // 8 bits per byte
terminal.c_cc[VMIN] = 0; // Minimum number of characters for non-canonical
// read.
terminal.c_cc[VTIME] = 0; // Timeout in deciseconds for non-canonical read.
tcsetattr(STDIN_FILENO, TCSANOW, &terminal);
@@ -600,12 +686,12 @@ void ScreenInteractive::Install() {
auto enable = [&](const std::vector<DECMode>& parameters) {
std::cout << Set(parameters);
on_exit_functions.push([=] { std::cout << Reset(parameters); });
on_exit_functions.emplace([=] { std::cout << Reset(parameters); });
};
auto disable = [&](const std::vector<DECMode>& parameters) {
std::cout << Reset(parameters);
on_exit_functions.push([=] { std::cout << Set(parameters); });
on_exit_functions.emplace([=] { std::cout << Set(parameters); });
};
if (use_alternative_screen_) {
@@ -614,11 +700,6 @@ void ScreenInteractive::Install() {
});
}
on_exit_functions.push([=] {
std::cout << "\033[?25h"; // Enable cursor.
std::cout << "\033[?1 q"; // Cursor block blinking.
});
disable({
// DECMode::kCursor,
DECMode::kLineWrap,
@@ -673,26 +754,45 @@ void ScreenInteractive::RunOnce(Component component) {
}
// private
// NOLINTNEXTLINE
void ScreenInteractive::HandleTask(Component component, Task& task) {
// clang-format off
std::visit([&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
std::visit(
[&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
// clang-format off
// Handle Event.
if constexpr (std::is_same_v<T, Event>) {
if (arg.is_cursor_reporting()) {
if (arg.is_cursor_position()) {
cursor_x_ = arg.cursor_x();
cursor_y_ = arg.cursor_y();
return;
}
if (arg.is_cursor_shape()) {
cursor_reset_shape_= arg.cursor_shape();
return;
}
if (arg.is_mouse()) {
arg.mouse().x -= cursor_x_;
arg.mouse().y -= cursor_y_;
}
arg.screen_ = this;
component->OnEvent(arg);
const bool handled = component->OnEvent(arg);
if (arg == Event::CtrlC && (!handled || force_handle_ctrl_c_)) {
RecordSignal(SIGABRT);
}
#if !defined(_WIN32)
if (arg == Event::CtrlZ && (!handled || force_handle_ctrl_z_)) {
RecordSignal(SIGTSTP);
}
#endif
frame_valid_ = false;
return;
}
@@ -758,6 +858,13 @@ void ScreenInteractive::Draw(Component component) {
ResetCursorPosition();
std::cout << ResetPosition(/*clear=*/resized);
// If the terminal width decrease, the terminal emulator will start wrapping
// lines and make the display dirty. We should clear it completely.
if ((dimx < dimx_) && !use_alternative_screen_) {
std::cout << "\033[J"; // clear terminal output
std::cout << "\033[H"; // move cursor to home position
}
// Resize the screen if needed.
if (resized) {
dimx_ = dimx;
@@ -799,10 +906,18 @@ void ScreenInteractive::Draw(Component component) {
const int dx = dimx_ - 1 - cursor_.x + int(dimx_ != terminal.dimx);
const int dy = dimy_ - 1 - cursor_.y;
set_cursor_position = "\x1B[" + std::to_string(dy) + "A" + //
"\x1B[" + std::to_string(dx) + "D";
reset_cursor_position = "\x1B[" + std::to_string(dy) + "B" + //
"\x1B[" + std::to_string(dx) + "C";
set_cursor_position.clear();
reset_cursor_position.clear();
if (dy != 0) {
set_cursor_position += "\x1B[" + std::to_string(dy) + "A";
reset_cursor_position += "\x1B[" + std::to_string(dy) + "B";
}
if (dx != 0) {
set_cursor_position += "\x1B[" + std::to_string(dx) + "D";
reset_cursor_position += "\x1B[" + std::to_string(dx) + "C";
}
if (cursor_.shape == Cursor::Hidden) {
set_cursor_position += "\033[?25l";
@@ -846,7 +961,7 @@ void ScreenInteractive::ExitNow() {
// private:
void ScreenInteractive::Signal(int signal) {
if (signal == SIGABRT) {
OnExit();
Exit();
return;
}

View File

@@ -64,4 +64,71 @@ TEST(ScreenInteractive, PostTaskToNonActive) {
screen.Post([] {});
}
TEST(ScreenInteractive, CtrlC) {
auto screen = ScreenInteractive::FitComponent();
bool called = false;
auto component = Renderer([&] {
if (!called) {
called = true;
screen.PostEvent(Event::CtrlC);
}
return text("");
});
screen.Loop(component);
}
TEST(ScreenInteractive, CtrlC_Forced) {
auto screen = ScreenInteractive::FitComponent();
screen.ForceHandleCtrlC(true);
auto component = Renderer([&] {
screen.PostEvent(Event::CtrlC);
return text("");
});
int ctrl_c_count = 0;
component |= CatchEvent([&](Event event) {
if (event != Event::CtrlC) {
return false;
}
++ctrl_c_count;
if (ctrl_c_count == 100) {
return false;
}
return true;
});
screen.Loop(component);
ASSERT_LE(ctrl_c_count, 50);
}
TEST(ScreenInteractive, CtrlC_NotForced) {
auto screen = ScreenInteractive::FitComponent();
screen.ForceHandleCtrlC(false);
auto component = Renderer([&] {
screen.PostEvent(Event::CtrlC);
return text("");
});
int ctrl_c_count = 0;
component |= CatchEvent([&](Event event) {
if (event != Event::CtrlC) {
return false;
}
++ctrl_c_count;
if (ctrl_c_count == 100) {
return false;
}
return true;
});
screen.Loop(component);
ASSERT_GE(ctrl_c_count, 50);
}
} // namespace ftxui

View File

@@ -1,8 +1,7 @@
// 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.
#include <algorithm> // for max, min
#include <cstdint> // for uint8_t, uint16_t, uint32_t, uint64_t
#include <algorithm> // for max, min
#include <ftxui/component/component_options.hpp> // for SliderOption
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
#include <string> // for allocator
@@ -36,31 +35,26 @@ Decorator flexDirection(Direction direction) {
}
template <class T>
class SliderBase : public ComponentBase {
class SliderBase : public SliderOption<T>, public ComponentBase {
public:
explicit SliderBase(SliderOption<T> options)
: value_(options.value),
min_(options.min),
max_(options.max),
increment_(options.increment),
options_(options) {}
explicit SliderBase(SliderOption<T> options) : SliderOption<T>(options) {}
Element Render() override {
auto gauge_color = Focused() ? color(options_.color_active)
: color(options_.color_inactive);
const float percent = float(value_() - min_()) / float(max_() - min_());
return gaugeDirection(percent, options_.direction) |
flexDirection(options_.direction) | reflect(gauge_box_) |
gauge_color;
auto gauge_color =
Focused() ? color(this->color_active) : color(this->color_inactive);
const float percent =
float(this->value() - this->min()) / float(this->max() - this->min());
return gaugeDirection(percent, this->direction) |
flexDirection(this->direction) | reflect(gauge_box_) | gauge_color;
}
void OnLeft() {
switch (options_.direction) {
switch (this->direction) {
case Direction::Right:
value_() -= increment_();
this->value() -= this->increment();
break;
case Direction::Left:
value_() += increment_();
this->value() += this->increment();
break;
case Direction::Up:
case Direction::Down:
@@ -69,12 +63,12 @@ class SliderBase : public ComponentBase {
}
void OnRight() {
switch (options_.direction) {
switch (this->direction) {
case Direction::Right:
value_() += increment_();
this->value() += this->increment();
break;
case Direction::Left:
value_() -= increment_();
this->value() -= this->increment();
break;
case Direction::Up:
case Direction::Down:
@@ -83,12 +77,12 @@ class SliderBase : public ComponentBase {
}
void OnUp() {
switch (options_.direction) {
switch (this->direction) {
case Direction::Up:
value_() -= increment_();
this->value() -= this->increment();
break;
case Direction::Down:
value_() += increment_();
this->value() += this->increment();
break;
case Direction::Left:
case Direction::Right:
@@ -97,12 +91,12 @@ class SliderBase : public ComponentBase {
}
void OnDown() {
switch (options_.direction) {
switch (this->direction) {
case Direction::Down:
value_() -= increment_();
this->value() += this->increment();
break;
case Direction::Up:
value_() += increment_();
this->value() -= this->increment();
break;
case Direction::Left:
case Direction::Right:
@@ -115,7 +109,7 @@ class SliderBase : public ComponentBase {
return OnMouseEvent(event);
}
T old_value = value_();
T old_value = this->value();
if (event == Event::ArrowLeft || event == Event::Character('h')) {
OnLeft();
}
@@ -129,8 +123,11 @@ class SliderBase : public ComponentBase {
OnUp();
}
value_() = util::clamp(value_(), min_(), max_());
if (old_value != value_()) {
this->value() = std::max(this->min(), std::min(this->max(), this->value()));
if (old_value != this->value()) {
if (this->on_change) {
this->on_change();
}
return true;
}
@@ -144,38 +141,52 @@ class SliderBase : public ComponentBase {
return true;
}
switch (options_.direction) {
T old_value = this->value();
switch (this->direction) {
case Direction::Right: {
value_() = min_() + (event.mouse().x - gauge_box_.x_min) *
(max_() - min_()) /
(gauge_box_.x_max - gauge_box_.x_min);
this->value() =
this->min() + (event.mouse().x - gauge_box_.x_min) *
(this->max() - this->min()) /
(gauge_box_.x_max - gauge_box_.x_min);
break;
}
case Direction::Left: {
value_() = max_() - (event.mouse().x - gauge_box_.x_min) *
(max_() - min_()) /
(gauge_box_.x_max - gauge_box_.x_min);
this->value() =
this->max() - (event.mouse().x - gauge_box_.x_min) *
(this->max() - this->min()) /
(gauge_box_.x_max - gauge_box_.x_min);
break;
}
case Direction::Down: {
value_() = min_() + (event.mouse().y - gauge_box_.y_min) *
(max_() - min_()) /
(gauge_box_.y_max - gauge_box_.y_min);
this->value() =
this->min() + (event.mouse().y - gauge_box_.y_min) *
(this->max() - this->min()) /
(gauge_box_.y_max - gauge_box_.y_min);
break;
}
case Direction::Up: {
value_() = max_() - (event.mouse().y - gauge_box_.y_min) *
(max_() - min_()) /
(gauge_box_.y_max - gauge_box_.y_min);
this->value() =
this->max() - (event.mouse().y - gauge_box_.y_min) *
(this->max() - this->min()) /
(gauge_box_.y_max - gauge_box_.y_min);
break;
}
}
value_() = std::max(min_(), std::min(max_(), value_()));
this->value() =
std::max(this->min(), std::min(this->max(), this->value()));
if (old_value != this->value() && this->on_change) {
this->on_change();
}
return true;
}
if (event.mouse().button != Mouse::Left ||
event.mouse().motion != Mouse::Pressed) {
if (event.mouse().button != Mouse::Left) {
return false;
}
if (event.mouse().motion != Mouse::Pressed) {
return false;
}
@@ -196,11 +207,6 @@ class SliderBase : public ComponentBase {
bool Focusable() const final { return true; }
private:
Ref<T> value_;
ConstRef<T> min_;
ConstRef<T> max_;
ConstRef<T> increment_;
SliderOption<T> options_;
Box gauge_box_;
CapturedMouse captured_mouse_;
};
@@ -255,6 +261,7 @@ class SliderWithLabel : public ComponentBase {
Box box_;
bool mouse_hover_ = false;
};
} // namespace
/// @brief An horizontal slider.
@@ -339,6 +346,7 @@ template <typename T>
Component Slider(SliderOption<T> options) {
return Make<SliderBase<T>>(options);
}
template Component Slider(SliderOption<int8_t>);
template Component Slider(SliderOption<int16_t>);
template Component Slider(SliderOption<int32_t>);

View File

@@ -6,8 +6,7 @@
#include <ftxui/component/mouse.hpp> // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
#include <ftxui/dom/elements.hpp> // for frame
#include <memory> // for shared_ptr, __shared_ptr_access, allocator
#include <string> // for string, to_string
#include <string> // for string, to_string
#include "ftxui/component/component.hpp" // for Slider, Vertical, operator|=
#include "ftxui/component/component_base.hpp" // for ComponentBase
@@ -46,6 +45,7 @@ Event MouseReleased(int x, int y) {
} // namespace
TEST(SliderTest, Right) {
int updated = 0;
int value = 50;
auto slider = Slider<int>({
.value = &value,
@@ -53,23 +53,31 @@ TEST(SliderTest, Right) {
.max = 100,
.increment = 10,
.direction = Direction::Right,
.on_change = [&]() { updated++; },
});
Screen screen(11, 1);
Render(screen, slider->Render());
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(3, 0)));
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(9, 0)));
EXPECT_EQ(value, 90);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(9, 2)));
EXPECT_EQ(value, 90);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(5, 2)));
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 2);
EXPECT_TRUE(slider->OnEvent(MouseReleased(5, 2)));
EXPECT_FALSE(slider->OnEvent(MousePressed(5, 2)));
EXPECT_EQ(value, 50);
}
TEST(SliderTest, Left) {
int updated = 0;
int value = 50;
auto slider = Slider<int>({
.value = &value,
@@ -77,23 +85,31 @@ TEST(SliderTest, Left) {
.max = 100,
.increment = 10,
.direction = Direction::Left,
.on_change = [&]() { updated++; },
});
Screen screen(11, 1);
Render(screen, slider->Render());
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(3, 0)));
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(9, 0)));
EXPECT_EQ(value, 10);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(9, 2)));
EXPECT_EQ(value, 10);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(5, 2)));
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 2);
EXPECT_TRUE(slider->OnEvent(MouseReleased(5, 2)));
EXPECT_FALSE(slider->OnEvent(MousePressed(5, 2)));
EXPECT_EQ(value, 50);
}
TEST(SliderTest, Down) {
int updated = 0;
int value = 50;
auto slider = Slider<int>({
.value = &value,
@@ -101,23 +117,32 @@ TEST(SliderTest, Down) {
.max = 100,
.increment = 10,
.direction = Direction::Down,
.on_change = [&]() { updated++; },
});
Screen screen(1, 11);
Render(screen, slider->Render());
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(0, 3)));
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(0, 9)));
EXPECT_EQ(value, 90);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(2, 9)));
EXPECT_EQ(value, 90);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(2, 5)));
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 2);
EXPECT_TRUE(slider->OnEvent(MouseReleased(2, 5)));
EXPECT_FALSE(slider->OnEvent(MousePressed(2, 5)));
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 2);
}
TEST(SliderTest, Up) {
int updated = 0;
int value = 50;
auto slider = Slider<int>({
.value = &value,
@@ -125,20 +150,27 @@ TEST(SliderTest, Up) {
.max = 100,
.increment = 10,
.direction = Direction::Up,
.on_change = [&]() { updated++; },
});
Screen screen(1, 11);
Render(screen, slider->Render());
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(0, 3)));
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(0, 9)));
EXPECT_EQ(value, 10);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(2, 9)));
EXPECT_EQ(value, 10);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(2, 5)));
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 2);
EXPECT_TRUE(slider->OnEvent(MouseReleased(2, 5)));
EXPECT_FALSE(slider->OnEvent(MousePressed(2, 5)));
EXPECT_EQ(value, 50);
}
TEST(SliderTest, Focus) {

View File

@@ -9,7 +9,7 @@
#include <map>
#include <memory> // for unique_ptr, allocator
#include <utility> // for move
#include <vector>
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/task.hpp" // for Task
@@ -150,10 +150,15 @@ void TerminalInputParser::Send(TerminalInputParser::Output output) {
pending_.clear();
return;
case CURSOR_REPORTING:
out_->Send(Event::CursorReporting(std::move(pending_), // NOLINT
output.cursor.x, // NOLINT
output.cursor.y)); // NOLINT
case CURSOR_POSITION:
out_->Send(Event::CursorPosition(std::move(pending_), // NOLINT
output.cursor.x, // NOLINT
output.cursor.y)); // NOLINT
pending_.clear();
return;
case CURSOR_SHAPE:
out_->Send(Event::CursorShape(std::move(pending_), output.cursor_shape));
pending_.clear();
return;
}
@@ -165,15 +170,8 @@ TerminalInputParser::Output TerminalInputParser::Parse() {
return UNCOMPLETED;
}
switch (Current()) {
case 24: // CAN NOLINT
case 26: // SUB NOLINT
return DROP;
case '\x1B':
return ParseESC();
default:
break;
if (Current() == '\x1B') {
return ParseESC();
}
if (Current() < 32) { // C0 NOLINT
@@ -277,15 +275,29 @@ TerminalInputParser::Output TerminalInputParser::ParseESC() {
return ParseCSI();
case ']':
return ParseOSC();
default:
// Expecting 2 characters.
case ' ':
case '#':
case '%':
case '(':
case ')':
case '*':
case '+':
case 'O':
case 'N': {
if (!Eat()) {
return UNCOMPLETED;
} else {
return SPECIAL;
}
return SPECIAL;
}
// Expecting 1 character:
default:
return SPECIAL;
}
}
// ESC P ... ESC BACKSLASH
TerminalInputParser::Output TerminalInputParser::ParseDCS() {
// Parse until the string terminator ST.
while (true) {
@@ -305,6 +317,16 @@ TerminalInputParser::Output TerminalInputParser::ParseDCS() {
continue;
}
if (pending_.size() == 10 && //
pending_[2] == '1' && //
pending_[3] == '$' && //
pending_[4] == 'r' && //
true) {
Output output(CURSOR_SHAPE);
output.cursor_shape = pending_[5] - '0';
return output;
}
return SPECIAL;
}
}
@@ -351,7 +373,7 @@ TerminalInputParser::Output TerminalInputParser::ParseCSI() {
case 'm':
return ParseMouse(altered, false, std::move(arguments));
case 'R':
return ParseCursorReporting(std::move(arguments));
return ParseCursorPosition(std::move(arguments));
default:
return SPECIAL;
}
@@ -394,23 +416,46 @@ TerminalInputParser::Output TerminalInputParser::ParseMouse( // NOLINT
(void)altered;
Output output(MOUSE);
output.mouse.button = Mouse::Button((arguments[0] & 3) + // NOLINT
((arguments[0] & 64) >> 4)); // NOLINT
output.mouse.motion = Mouse::Motion(pressed); // NOLINT
output.mouse.shift = bool(arguments[0] & 4); // NOLINT
output.mouse.meta = bool(arguments[0] & 8); // NOLINT
output.mouse.x = arguments[1]; // NOLINT
output.mouse.y = arguments[2]; // NOLINT
output.mouse.motion = Mouse::Motion(pressed); // NOLINT
// Bits value Modifer Comment
// ---- ----- ------- ---------
// 0 1 1 2 button 0 = Left, 1 = Middle, 2 = Right, 3 = Release
// 2 4 Shift
// 3 8 Meta
// 4 16 Control
// 5 32 Move
// 6 64 Wheel
// clang-format off
const int button = arguments[0] & (1 + 2); // NOLINT
const bool is_shift = arguments[0] & 4; // NOLINT
const bool is_meta = arguments[0] & 8; // NOLINT
const bool is_control = arguments[0] & 16; // NOLINT
const bool is_move = arguments[0] & 32; // NOLINT
const bool is_wheel = arguments[0] & 64; // NOLINT
// clang-format on
output.mouse.motion = is_move ? Mouse::Moved : Mouse::Motion(pressed);
output.mouse.button = is_wheel ? Mouse::Button(Mouse::WheelUp + button) //
: Mouse::Button(button);
output.mouse.shift = is_shift;
output.mouse.meta = is_meta;
output.mouse.control = is_control;
output.mouse.x = arguments[1]; // NOLINT
output.mouse.y = arguments[2]; // NOLINT
// Motion event.
return output;
}
// NOLINTNEXTLINE
TerminalInputParser::Output TerminalInputParser::ParseCursorReporting(
TerminalInputParser::Output TerminalInputParser::ParseCursorPosition(
std::vector<int> arguments) {
if (arguments.size() != 2) {
return SPECIAL;
}
Output output(CURSOR_REPORTING);
Output output(CURSOR_POSITION);
output.cursor.y = arguments[0]; // NOLINT
output.cursor.x = arguments[1]; // NOLINT
return output;

View File

@@ -4,11 +4,9 @@
#ifndef FTXUI_COMPONENT_TERMINAL_INPUT_PARSER
#define FTXUI_COMPONENT_TERMINAL_INPUT_PARSER
#include <memory> // for unique_ptr
#include <string> // for string
#include <vector> // for vector
#include "ftxui/component/event.hpp" // for Event (ptr only)
#include "ftxui/component/mouse.hpp" // for Mouse
#include "ftxui/component/receiver.hpp" // for Sender
#include "ftxui/component/task.hpp" // for Task
@@ -19,7 +17,7 @@ struct Event;
// Parse a sequence of |char| accross |time|. Produces |Event|.
class TerminalInputParser {
public:
TerminalInputParser(Sender<Task> out);
explicit TerminalInputParser(Sender<Task> out);
void Timeout(int time);
void Add(char c);
@@ -31,12 +29,13 @@ class TerminalInputParser {
UNCOMPLETED,
DROP,
CHARACTER,
SPECIAL,
MOUSE,
CURSOR_REPORTING,
CURSOR_POSITION,
CURSOR_SHAPE,
SPECIAL,
};
struct CursorReporting {
struct CursorPosition {
int x;
int y;
};
@@ -45,10 +44,12 @@ class TerminalInputParser {
Type type;
union {
Mouse mouse;
CursorReporting cursor;
CursorPosition cursor{};
int cursor_shape;
};
Output(Type t) : type(t) {}
Output(Type t) // NOLINT
: type(t) {}
};
void Send(Output output);
@@ -59,7 +60,7 @@ class TerminalInputParser {
Output ParseCSI();
Output ParseOSC();
Output ParseMouse(bool altered, bool pressed, std::vector<int> arguments);
Output ParseCursorReporting(std::vector<int> arguments);
Output ParseCursorPosition(std::vector<int> arguments);
Sender<Task> out_;
int position_ = -1;

View File

@@ -5,7 +5,6 @@
#include <ftxui/component/task.hpp> // for Task
#include <initializer_list> // for initializer_list
#include <memory> // for allocator, unique_ptr
#include <variant> // for get
#include "ftxui/component/event.hpp" // for Event, Event::Return, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp, Event::Backspace, Event::End, Event::Home, Event::Custom, Event::Delete, Event::F1, Event::F10, Event::F11, Event::F12, Event::F2, Event::F3, Event::F4, Event::F5, Event::F6, Event::F7, Event::F8, Event::F9, Event::PageDown, Event::PageUp, Event::Tab, Event::TabReverse, Event::Escape
#include "ftxui/component/receiver.hpp" // for MakeReceiver, ReceiverImpl
@@ -76,7 +75,51 @@ TEST(Event, EscapeKeyEnoughWait) {
EXPECT_FALSE(event_receiver->Receive(&received));
}
TEST(Event, EscapeFast) {
auto event_receiver = MakeReceiver<Task>();
{
auto parser = TerminalInputParser(event_receiver->MakeSender());
parser.Add('\x1B');
parser.Add('a');
parser.Add('\x1B');
parser.Add('b');
parser.Timeout(49);
}
Task received;
EXPECT_TRUE(event_receiver->Receive(&received));
EXPECT_EQ(std::get<Event>(received), Event::AltA);
EXPECT_TRUE(event_receiver->Receive(&received));
EXPECT_EQ(std::get<Event>(received), Event::AltB);
EXPECT_FALSE(event_receiver->Receive(&received));
}
TEST(Event, MouseLeftClickPressed) {
auto event_receiver = MakeReceiver<Task>();
{
auto parser = TerminalInputParser(event_receiver->MakeSender());
parser.Add('\x1B');
parser.Add('[');
parser.Add('0');
parser.Add(';');
parser.Add('1');
parser.Add('2');
parser.Add(';');
parser.Add('4');
parser.Add('2');
parser.Add('M');
}
Task received;
EXPECT_TRUE(event_receiver->Receive(&received));
EXPECT_TRUE(std::get<Event>(received).is_mouse());
EXPECT_EQ(Mouse::Left, std::get<Event>(received).mouse().button);
EXPECT_EQ(12, std::get<Event>(received).mouse().x);
EXPECT_EQ(42, std::get<Event>(received).mouse().y);
EXPECT_EQ(std::get<Event>(received).mouse().motion, Mouse::Pressed);
EXPECT_FALSE(event_receiver->Receive(&received));
}
TEST(Event, MouseLeftMoved) {
auto event_receiver = MakeReceiver<Task>();
{
auto parser = TerminalInputParser(event_receiver->MakeSender());
@@ -99,7 +142,7 @@ TEST(Event, MouseLeftClickPressed) {
EXPECT_EQ(Mouse::Left, std::get<Event>(received).mouse().button);
EXPECT_EQ(12, std::get<Event>(received).mouse().x);
EXPECT_EQ(42, std::get<Event>(received).mouse().y);
EXPECT_EQ(std::get<Event>(received).mouse().motion, Mouse::Pressed);
EXPECT_EQ(std::get<Event>(received).mouse().motion, Mouse::Moved);
EXPECT_FALSE(event_receiver->Receive(&received));
}
@@ -109,8 +152,7 @@ TEST(Event, MouseLeftClickReleased) {
auto parser = TerminalInputParser(event_receiver->MakeSender());
parser.Add('\x1B');
parser.Add('[');
parser.Add('3');
parser.Add('2');
parser.Add('0');
parser.Add(';');
parser.Add('1');
parser.Add('2');
@@ -146,7 +188,7 @@ TEST(Event, MouseReporting) {
Task received;
EXPECT_TRUE(event_receiver->Receive(&received));
EXPECT_TRUE(std::get<Event>(received).is_cursor_reporting());
EXPECT_TRUE(std::get<Event>(received).is_cursor_position());
EXPECT_EQ(42, std::get<Event>(received).cursor_x());
EXPECT_EQ(12, std::get<Event>(received).cursor_y());
EXPECT_FALSE(event_receiver->Receive(&received));
@@ -310,8 +352,8 @@ TEST(Event, Control) {
continue;
cases.push_back({char(i), false});
}
cases.push_back({char(24), true});
cases.push_back({char(26), true});
cases.push_back({char(24), false});
cases.push_back({char(26), false});
cases.push_back({char(127), false});
for (auto test : cases) {
@@ -342,13 +384,11 @@ TEST(Event, Special) {
std::vector<unsigned char> input;
Event expected;
} kTestCase[] = {
// Arrow (defaut cursor mode)
{str("\x1B[A"), Event::ArrowUp},
{str("\x1B[B"), Event::ArrowDown},
{str("\x1B[C"), Event::ArrowRight},
{str("\x1B[D"), Event::ArrowLeft},
{str("\x1B[H"), Event::Home},
{str("\x1B[F"), Event::End},
// Arrow (default cursor mode)
{str("\x1B[A"), Event::ArrowUp}, {str("\x1B[B"), Event::ArrowDown},
{str("\x1B[C"), Event::ArrowRight}, {str("\x1B[D"), Event::ArrowLeft},
{str("\x1B[H"), Event::Home}, {str("\x1B[F"), Event::End},
/*
// Arrow (application cursor mode)
{str("\x1BOA"), Event::ArrowUp},
@@ -429,6 +469,7 @@ TEST(Event, Special) {
// Custom:
{{0}, Event::Custom},
*/
};
for (auto test : kTestCase) {
@@ -446,5 +487,28 @@ TEST(Event, Special) {
}
}
TEST(Event, DeviceControlString) {
auto event_receiver = MakeReceiver<Task>();
{
auto parser = TerminalInputParser(event_receiver->MakeSender());
parser.Add(27); // ESC
parser.Add(80); // P
parser.Add(49); // 1
parser.Add(36); // $
parser.Add(114); // r
parser.Add(49); // 1
parser.Add(32); // SP
parser.Add(113); // q
parser.Add(27); // ESC
parser.Add(92); // (backslash)
}
Task received;
EXPECT_TRUE(event_receiver->Receive(&received));
EXPECT_TRUE(std::get<Event>(received).is_cursor_shape());
EXPECT_EQ(1, std::get<Event>(received).cursor_shape());
EXPECT_FALSE(event_receiver->Receive(&received));
}
} // namespace ftxui
// NOLINTEND
// NOLINTEND

View File

@@ -1,7 +1,6 @@
// 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.
#include <vector>
#include "ftxui/component/terminal_input_parser.hpp"
extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
@@ -9,12 +8,14 @@ extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
auto event_receiver = MakeReceiver<Task>();
{
auto parser = TerminalInputParser(event_receiver->MakeSender());
for (size_t i = 0; i < size; ++i)
for (size_t i = 0; i < size; ++i) {
parser.Add(data[i]);
}
}
Task received;
while (event_receiver->Receive(&received))
;
while (event_receiver->Receive(&received)) {
// Do nothing.
}
return 0; // Non-zero return values are reserved for future use.
}

View File

@@ -2,7 +2,6 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <functional> // for function
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string, basic_string
#include <vector> // for vector

View File

@@ -5,8 +5,14 @@
#include <algorithm>
#include <ftxui/component/component.hpp>
#include <ftxui/component/component_base.hpp>
#include <ftxui/component/component_options.hpp>
#include <ftxui/component/screen_interactive.hpp> // for ScreenInteractive
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include <memory>
#include <utility>
#include "ftxui/dom/elements.hpp" // for text, window, hbox, vbox, size, clear_under, reflect, emptyElement
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/color.hpp" // for Color
#include "ftxui/screen/screen.hpp" // for Screen
namespace ftxui {
@@ -206,7 +212,7 @@ class WindowImpl : public ComponentBase, public WindowOptions {
}
// Clamp the window size.
width() = std::max<int>(width(), title().size() + 2);
width() = std::max<int>(width(), static_cast<int>(title().size() + 2));
height() = std::max<int>(height(), 2);
return true;
@@ -225,8 +231,10 @@ class WindowImpl : public ComponentBase, public WindowOptions {
return true;
}
if (event.mouse().button != Mouse::Left ||
event.mouse().motion != Mouse::Pressed) {
if (event.mouse().button != Mouse::Left) {
return true;
}
if (event.mouse().motion != Mouse::Pressed) {
return true;
}

View File

@@ -2,7 +2,6 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <benchmark/benchmark.h>
#include <iostream>
#include "ftxui/dom/elements.hpp" // for gauge, separator, operator|, text, Element, hbox, vbox, blink, border, inverted
#include "ftxui/dom/node.hpp" // for Render

View File

@@ -8,12 +8,12 @@
#include <optional> // for optional, nullopt
#include <string> // for basic_string, string
#include <utility> // for move
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for unpack, Element, Decorator, BorderStyle, ROUNDED, borderStyled, Elements, DASHED, DOUBLE, EMPTY, HEAVY, LIGHT, border, borderDashed, borderDouble, borderEmpty, borderHeavy, borderLight, borderRounded, borderWith, window
#include "ftxui/dom/node.hpp" // for Node, Elements
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/pixel.hpp" // for Pixel
#include "ftxui/screen/screen.hpp" // for Pixel, Screen
namespace ftxui {
@@ -38,7 +38,8 @@ class Border : public Node {
BorderStyle style,
std::optional<Color> foreground_color = std::nullopt)
: Node(std::move(children)),
charset_(simple_border_charset[style]),
charset_(simple_border_charset[style]) // NOLINT
,
foreground_color_(foreground_color) {} // NOLINT
const Charset& charset_; // NOLINT
@@ -64,7 +65,7 @@ class Border : public Node {
if (children_.size() == 2) {
Box title_box;
title_box.x_min = box.x_min + 1;
title_box.x_max = box.x_max - 1;
title_box.x_max = std::min(box.x_max - 1, box.x_min + children_[1]->requirement().min_x);
title_box.y_min = box.y_min;
title_box.y_max = box.y_min;
children_[1]->SetBox(title_box);
@@ -266,7 +267,7 @@ Decorator borderStyled(BorderStyle style, Color foreground_color) {
};
}
/// @brief Draw a light border around the element.
/// @brief Draw a dashed border around the element.
/// @ingroup dom
/// @see border
/// @see borderLight
@@ -301,7 +302,7 @@ Element borderDashed(Element child) {
return std::make_shared<Border>(unpack(std::move(child)), DASHED);
}
/// @brief Draw a dashed border around the element.
/// @brief Draw a light border around the element.
/// @ingroup dom
/// @see border
/// @see borderLight
@@ -479,6 +480,7 @@ Element borderEmpty(Element child) {
/// @brief Draw window with a title and a border around the element.
/// @param title The title of the window.
/// @param content The element to be wrapped.
/// @param border The style of the border. Default is ROUNDED.
/// @ingroup dom
/// @see border
///
@@ -488,6 +490,12 @@ Element borderEmpty(Element child) {
/// Element document = window(text("Title"),
/// text("content")
/// );
///
/// // With specifying border
/// Element document = window(text("Title"),
/// text("content"),
/// ROUNDED
/// );
/// ```
///
/// ### Output
@@ -497,8 +505,8 @@ Element borderEmpty(Element child) {
/// │content│
/// └───────┘
/// ```
Element window(Element title, Element content) {
Element window(Element title, Element content, BorderStyle border) {
return std::make_shared<Border>(unpack(std::move(content), std::move(title)),
ROUNDED);
border);
}
} // namespace ftxui

View File

@@ -4,6 +4,7 @@
#include "ftxui/dom/box_helper.hpp"
#include <algorithm> // for max
#include <vector> // for vector
namespace ftxui::box_helper {

View File

@@ -6,8 +6,7 @@
#include <vector>
namespace ftxui {
namespace box_helper {
namespace ftxui::box_helper {
struct Element {
// Input:
@@ -21,7 +20,6 @@ struct Element {
void Compute(std::vector<Element>* elements, int target_size);
} // namespace box_helper
} // namespace ftxui
} // namespace ftxui::box_helper
#endif /* end of include guard: FTXUI_DOM_BOX_HELPER_HPP */

View File

@@ -8,6 +8,7 @@
#include <cstdint> // for uint8_t
#include <cstdlib> // for abs
#include <ftxui/screen/color.hpp> // for Color
#include <functional> // for function
#include <map> // for map
#include <memory> // for make_shared
#include <utility> // for move, pair
@@ -17,6 +18,8 @@
#include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/image.hpp" // for Image
#include "ftxui/screen/pixel.hpp" // for Pixel
#include "ftxui/screen/screen.hpp" // for Pixel, Screen
#include "ftxui/screen/string.hpp" // for Utf8ToGlyphs
#include "ftxui/util/ref.hpp" // for ConstRef
@@ -28,7 +31,7 @@ namespace {
// Base UTF8 pattern:
// 11100010 10100000 10000000 // empty
// Pattern for the individuel dots:
// Pattern for the individual dots:
// ┌──────┬───────┐
// │dot1 │ dot4 │
// ├──────┼───────┤
@@ -341,7 +344,7 @@ void Canvas::DrawPointEllipse(int x1,
int dy = x * x;
int err = dx + dy;
do {
do { // NOLINT
DrawPoint(x1 - x, y1 + y, true, s);
DrawPoint(x1 + x, y1 + y, true, s);
DrawPoint(x1 + x, y1 - y, true, s);
@@ -405,7 +408,7 @@ void Canvas::DrawPointEllipseFilled(int x1,
int dy = x * x;
int err = dx + dy;
do {
do { // NOLINT
for (int xx = x1 + x; xx <= x1 - x; ++xx) {
DrawPoint(xx, y1 + y, true, s);
DrawPoint(xx, y1 - y, true, s);
@@ -686,7 +689,7 @@ void Canvas::DrawBlockEllipse(int x1,
int dy = x * x;
int err = dx + dy;
do {
do { // NOLINT
DrawBlock(x1 - x, 2 * (y1 + y), true, s);
DrawBlock(x1 + x, 2 * (y1 + y), true, s);
DrawBlock(x1 + x, 2 * (y1 - y), true, s);
@@ -752,7 +755,7 @@ void Canvas::DrawBlockEllipseFilled(int x1,
int dy = x * x;
int err = dx + dy;
do {
do { // NOLINT
for (int xx = x1 + x; xx <= x1 - x; ++xx) {
DrawBlock(xx, 2 * (y1 + y), true, s);
DrawBlock(xx, 2 * (y1 - y), true, s);
@@ -810,13 +813,49 @@ void Canvas::DrawText(int x,
continue;
}
Cell& cell = storage_[XY{x / 2, y / 4}];
cell.type = CellType::kText;
cell.type = CellType::kCell;
cell.content.character = it;
style(cell.content);
x += 2;
}
}
/// @brief Directly draw a predefined pixel at the given coordinate
/// @param x the x coordinate of the pixel.
/// @param y the y coordinate of the pixel.
/// @param p the pixel to draw.
void Canvas::DrawPixel(int x, int y, const Pixel& p) {
Cell& cell = storage_[XY{x / 2, y / 4}];
cell.type = CellType::kCell;
cell.content = p;
}
/// @brief Draw a predefined image, with top-left corner at the given coordinate
/// You can supply negative coordinates to align the image however you like -
/// only the 'visible' portion will be drawn
/// @param x the x coordinate corresponding to the top-left corner of the image.
/// @param y the y coordinate corresponding to the top-left corner of the image.
/// @param image the image to draw.
void Canvas::DrawImage(int x, int y, const Image& image) {
x /= 2;
y /= 4;
const int dx_begin = std::max(0, -x);
const int dy_begin = std::max(0, -y);
const int dx_end = std::min(image.dimx(), width_ - x);
const int dy_end = std::min(image.dimy(), height_ - y);
for (int dy = dy_begin; dy < dy_end; ++dy) {
for (int dx = dx_begin; dx < dx_end; ++dx) {
Cell& cell = storage_[XY{
x + dx,
y + dy,
}];
cell.type = CellType::kCell;
cell.content = image.PixelAt(dx, dy);
}
}
}
/// @brief Modify a pixel at a given location.
/// @param style a function that modifies the pixel.
void Canvas::Style(int x, int y, const Stylizer& style) {

View File

@@ -23,6 +23,7 @@ class ClearUnder : public NodeDecorator {
for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x, y) = Pixel();
screen.PixelAt(x, y).character = " "; // Consider the pixel written.
}
}
Node::Render(screen);

View File

@@ -12,16 +12,25 @@
namespace ftxui {
namespace {
namespace {
class BgColor : public NodeDecorator {
public:
BgColor(Element child, Color color)
: NodeDecorator(std::move(child)), color_(color) {}
void Render(Screen& screen) override {
for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x, y).background_color = color_;
if (color_.IsOpaque()) {
for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x, y).background_color = color_;
}
}
} else {
for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) {
Color& color = screen.PixelAt(x, y).background_color;
color = Color::Blend(color, color_);
}
}
}
NodeDecorator::Render(screen);
@@ -36,9 +45,18 @@ class FgColor : public NodeDecorator {
: NodeDecorator(std::move(child)), color_(color) {}
void Render(Screen& screen) override {
for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x, y).foreground_color = color_;
if (color_.IsOpaque()) {
for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x, y).foreground_color = color_;
}
}
} else {
for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) {
Color& color = screen.PixelAt(x, y).foreground_color;
color = Color::Blend(color, color_);
}
}
}
NodeDecorator::Render(screen);
@@ -46,6 +64,7 @@ class FgColor : public NodeDecorator {
Color color_;
};
} // namespace
/// @brief Set the foreground color of an element.

View File

@@ -2,18 +2,20 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <algorithm> // for max
#include <cstddef> // for size_t
#include <memory> // for __shared_ptr_access, shared_ptr, make_shared
#include <utility> // for move
#include <vector> // for vector
#include <vector>
#include "ftxui/dom/elements.hpp" // for Element, Elements, dbox
#include "ftxui/dom/node.hpp" // for Node, Elements
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/pixel.hpp" // for Pixel
namespace ftxui {
namespace {
namespace {
class DBox : public Node {
public:
explicit DBox(Elements children) : Node(std::move(children)) {}
@@ -47,6 +49,58 @@ class DBox : public Node {
child->SetBox(box);
}
}
void Render(Screen& screen) override {
if (children_.size() <= 1) {
Node::Render(screen);
return;
}
const int width = box_.x_max - box_.x_min + 1;
const int height = box_.y_max - box_.y_min + 1;
std::vector<Pixel> pixels(std::size_t(width * height));
for (auto& child : children_) {
child->Render(screen);
// Accumulate the pixels
Pixel* acc = pixels.data();
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
auto& pixel = screen.PixelAt(x + box_.x_min, y + box_.y_min);
acc->background_color =
Color::Blend(acc->background_color, pixel.background_color);
acc->automerge = pixel.automerge || acc->automerge;
if (pixel.character.empty()) {
acc->foreground_color =
Color::Blend(acc->foreground_color, pixel.background_color);
} else {
acc->blink = pixel.blink;
acc->bold = pixel.bold;
acc->dim = pixel.dim;
acc->inverted = pixel.inverted;
acc->underlined = pixel.underlined;
acc->underlined_double = pixel.underlined_double;
acc->strikethrough = pixel.strikethrough;
acc->hyperlink = pixel.hyperlink;
acc->character = pixel.character;
acc->foreground_color = pixel.foreground_color;
}
++acc; // NOLINT
pixel = Pixel();
}
}
}
// Render the accumulated pixels:
Pixel* acc = pixels.data();
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
screen.PixelAt(x + box_.x_min, y + box_.y_min) = *acc++; // NOLINT
}
}
}
};
} // namespace

View File

@@ -3,7 +3,6 @@
// the LICENSE file.
#include <memory> // for make_shared, __shared_ptr_access
#include <utility> // for move
#include <vector> // for __alloc_traits<>::value_type
#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" // for Elements, Node
@@ -92,14 +91,14 @@ class Flex : public Node {
} // namespace
/// @brief An element that will take expand proportionnally to the space left in
/// @brief An element that will take expand proportionally to the space left in
/// a container.
/// @ingroup dom
Element filler() {
return std::make_shared<Flex>(function_flex);
}
/// @brief Make a child element to expand proportionnally to the space left in a
/// @brief Make a child element to expand proportionally to the space left in a
/// container.
/// @ingroup dom
///

View File

@@ -6,8 +6,8 @@
#include <algorithm> // for max, min
#include <cstddef> // for size_t
#include <ftxui/dom/flexbox_config.hpp> // for FlexboxConfig, FlexboxConfig::Direction, FlexboxConfig::AlignContent, FlexboxConfig::JustifyContent, FlexboxConfig::Wrap, FlexboxConfig::Direction::RowInversed, FlexboxConfig::AlignItems, FlexboxConfig::Direction::Row, FlexboxConfig::Direction::Column, FlexboxConfig::Direction::ColumnInversed, FlexboxConfig::Wrap::WrapInversed, FlexboxConfig::AlignContent::Stretch, FlexboxConfig::JustifyContent::Stretch, FlexboxConfig::Wrap::Wrap, FlexboxConfig::AlignContent::Center, FlexboxConfig::AlignContent::FlexEnd, FlexboxConfig::AlignContent::FlexStart, FlexboxConfig::AlignContent::SpaceAround, FlexboxConfig::AlignContent::SpaceBetween, FlexboxConfig::AlignContent::SpaceEvenly, FlexboxConfig::AlignItems::Center, FlexboxConfig::AlignItems::FlexEnd, FlexboxConfig::AlignItems::FlexStart, FlexboxConfig::AlignItems::Stretch, FlexboxConfig::JustifyContent::Center, FlexboxConfig::JustifyContent::FlexEnd, FlexboxConfig::JustifyContent::FlexStart, FlexboxConfig::JustifyContent::SpaceAround, FlexboxConfig::JustifyContent::SpaceBetween, FlexboxConfig::JustifyContent::SpaceEvenly, FlexboxConfig::Wrap::NoWrap
#include <memory> // for allocator_traits<>::value_type
#include <utility> // for swap, move
#include <vector>
#include "ftxui/dom/box_helper.hpp" // for Element, Compute
@@ -331,8 +331,8 @@ void Compute3(Global& global) {
line = Line();
}
block.line = lines.size();
block.line_position = line.blocks.size();
block.line = static_cast<int>(lines.size());
block.line_position = static_cast<int>(line.blocks.size());
line.blocks.push_back(&block);
x += block.min_size_x + global.config.gap_x;
}

View File

@@ -7,8 +7,7 @@
#include <vector>
#include "ftxui/dom/flexbox_config.hpp"
namespace ftxui {
namespace flexbox_helper {
namespace ftxui::flexbox_helper {
struct Block {
// Input:
@@ -20,8 +19,8 @@ struct Block {
int flex_shrink_y = 0;
// Output:
int line;
int line_position;
int line{};
int line_position{};
int x = 0;
int y = 0;
int dim_x = 0;
@@ -38,7 +37,6 @@ struct Global {
void Compute(Global& global);
} // namespace flexbox_helper
} // namespace ftxui
} // namespace ftxui::flexbox_helper
#endif /* end of include guard: FTXUI_DOM_FLEXBOX_HELPER_HPP*/

View File

@@ -3,7 +3,6 @@
// the LICENSE file.
#include <gtest/gtest.h>
#include <ftxui/dom/flexbox_config.hpp> // for FlexboxConfig, FlexboxConfig::Direction, FlexboxConfig::Direction::Column, FlexboxConfig::Direction::ColumnInversed, FlexboxConfig::Direction::Row, FlexboxConfig::Direction::RowInversed
#include <memory> // for allocator_traits<>::value_type
#include "ftxui/dom/flexbox_helper.hpp"

View File

@@ -4,7 +4,6 @@
#include <algorithm> // for max, min
#include <memory> // for make_shared, __shared_ptr_access
#include <utility> // for move
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, focus, frame, select, xframe, yframe
#include "ftxui/dom/node.hpp" // for Node, Elements
@@ -37,7 +36,6 @@ class Select : public Node {
}
};
class Focus : public Select {
public:
using Select::Select;
@@ -143,7 +141,6 @@ class FocusCursor : public Focus {
Screen::Cursor::Shape shape_;
};
} // namespace
/// @brief Set the `child` to be the one selected among its siblings.

View File

@@ -159,7 +159,7 @@ class Gauge : public Node {
Direction direction_;
};
} // namespace ftxui
} // namespace
/// @brief Draw a high definition progress bar progressing in specified
/// direction.

View File

@@ -2,7 +2,6 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <gtest/gtest.h>
#include <memory> // for allocator
#include "ftxui/dom/elements.hpp" // for gauge, gaugeUp
#include "ftxui/dom/node.hpp" // for Render

View File

@@ -35,7 +35,7 @@ int Integrate(std::vector<int>& elements) {
class GridBox : public Node {
public:
explicit GridBox(std::vector<Elements> lines) : lines_(std::move(lines)) {
y_size = lines_.size();
y_size = static_cast<int>(lines_.size());
for (const auto& line : lines_) {
x_size = std::max(x_size, int(line.size()));
}

View File

@@ -4,7 +4,6 @@
#include <gtest/gtest.h>
#include <algorithm> // for remove
#include <cstddef> // for size_t
#include <memory> // for shared_ptr
#include <string> // for allocator, basic_string, string
#include <vector> // for vector

View File

@@ -3,7 +3,6 @@
// the LICENSE file.
#include <gtest/gtest.h> // for Test, EXPECT_EQ, Message, TestPartResult, TestInfo (ptr only), TEST
#include <ftxui/dom/linear_gradient.hpp> // for LinearGradient::Stop, LinearGradient
#include <memory> // for allocator_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for operator|, text, bgcolor, color, Element
#include "ftxui/dom/node.hpp" // for Render

View File

@@ -2,8 +2,6 @@
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <ftxui/dom/node.hpp> // for Node, Elements
#include <memory> // for __shared_ptr_access
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/node_decorator.hpp"
#include "ftxui/dom/requirement.hpp" // for Requirement

View File

@@ -15,7 +15,7 @@ struct Box;
// Helper class.
class NodeDecorator : public Node {
public:
NodeDecorator(Element child) : Node(unpack(std::move(child))) {}
explicit NodeDecorator(Element child) : Node(unpack(std::move(child))) {}
void ComputeRequirement() override;
void SetBox(Box box) override;
};

Some files were not shown because too many files have changed in this diff Show More