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

View File

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

View File

@@ -5,8 +5,57 @@ current (development)
--------------------- ---------------------
### Component ### 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` - Feature: Add support for `Input`'s insert mode. Add `InputOption::insert`
option. Added by @mingsheng13. 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 ### Build
- Support for cmake's "unity/jumbo" builds. Fixed by @ClausKlein. - Support for cmake's "unity/jumbo" builds. Fixed by @ClausKlein.

View File

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

View File

@@ -32,7 +32,7 @@ A simple cross-platform C++ library for terminal based user interfaces!
## Feature ## Feature
* Functional style. Inspired by * 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/) and [React](https://reactjs.org/)
* Simple and elegant syntax (in my opinion) * Simple and elegant syntax (in my opinion)
* Keyboard & mouse navigation. * 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 # Force Microsoft Visual Studio to decode sources files in UTF-8. This applies
# to the library and the library users. # to the library and the library users.
if (MSVC) if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(${library} PUBLIC "/utf-8") target_compile_options(${library} PUBLIC "/utf-8")
endif() 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: # Add as many warning as possible:
if (WIN32) if (WIN32)
if (MSVC) if (MSVC)

View File

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

View File

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

View File

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

View File

@@ -12,13 +12,40 @@
using namespace ftxui; 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 main() {
int value = 50; 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. // The tree of components. This defines how to navigate using the keyboard.
auto buttons = Container::Horizontal({ // The selected `row` is shared to get a grid layout.
Button("Decrease", [&] { value--; }), int row = 0;
Button("Increase", [&] { value++; }), 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: // 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([&] { auto spinner_tab_renderer = Renderer([&] {
Elements entries; Elements entries;
@@ -490,8 +490,14 @@ int main() {
}, },
&tab_index); &tab_index);
auto exit_button =
Button("Exit", [&] { screen.Exit(); }, ButtonOption::Animated());
auto main_container = Container::Vertical({ auto main_container = Container::Vertical({
tab_selection, Container::Horizontal({
tab_selection,
exit_button,
}),
tab_content, 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; using namespace ftxui;
std::string Stringify(Event event) { std::string Code(Event event) {
std::string out; std::string codes;
for (auto& it : event.input()) for (auto& it : event.input()) {
out += " " + std::to_string((unsigned int)it); codes += " " + 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)";
} }
return out; return codes;
} }
int main() { int main() {
@@ -139,16 +31,35 @@ int main() {
std::vector<Event> keys; std::vector<Event> keys;
auto component = Renderer([&] { auto left_column = Renderer([&] {
Elements children; Elements children = {
for (size_t i = std::max(0, (int)keys.size() - 20); i < keys.size(); ++i) text("Codes"),
children.push_back(text(Stringify(keys[i]))); separator(),
return window(text("keys"), vbox(std::move(children))); };
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) { component |= CatchEvent([&](Event event) {
keys.push_back(event); keys.push_back(event);
return true; return false;
}); });
screen.Loop(component); screen.Loop(component);

View File

@@ -55,7 +55,8 @@ int main() {
content.DecorateCellsAlternateRow(color(Color::White), 3, 2); content.DecorateCellsAlternateRow(color(Color::White), 3, 2);
auto document = table.Render(); 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); Render(screen, document);
screen.Print(); screen.Print();
std::cout << std::endl; std::cout << std::endl;

View File

@@ -24,8 +24,9 @@
<div class="page"> <div class="page">
<h1>FTXUI WebAssembly Example </h1> <h1>FTXUI WebAssembly Example </h1>
<p> <p>
<a href="https://github.com/ArthurSonzogni/FTXUI">FTXUI</a> is a single <a href="https://github.com/ArthurSonzogni/FTXUI">FTXUI</a> is a simple
C++ library for terminal user interface. 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>
<p> <p>
On this page, you can try all the examples contained in: <a 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 <chrono> // for milliseconds, duration, steady_clock, time_point
#include <functional> // for function #include <functional> // for function
#include "ftxui/component/event.hpp" namespace ftxui::animation {
namespace ftxui {
namespace animation {
// Components who haven't completed their animation can call this function to // Components who haven't completed their animation can call this function to
// request a new frame to be drawn later. // request a new frame to be drawn later.
// //
@@ -26,7 +22,7 @@ using Duration = std::chrono::duration<float>;
// Parameter of Component::OnAnimation(param). // Parameter of Component::OnAnimation(param).
class Params { class Params {
public: public:
Params(Duration duration) : duration_(duration) {} explicit Params(Duration duration) : duration_(duration) {}
/// The duration this animation step represents. /// The duration this animation step represents.
Duration duration() const { return duration_; } Duration duration() const { return duration_; }
@@ -93,11 +89,11 @@ float BounceInOut(float p);
class Animator { class Animator {
public: public:
Animator(float* from, explicit Animator(float* from,
float to = 0.f, float to = 0.f,
Duration duration = std::chrono::milliseconds(250), Duration duration = std::chrono::milliseconds(250),
easing::Function easing_function = easing::Linear, easing::Function easing_function = easing::Linear,
Duration delay = std::chrono::milliseconds(0)); Duration delay = std::chrono::milliseconds(0));
void OnAnimation(Params&); void OnAnimation(Params&);
@@ -112,7 +108,6 @@ class Animator {
Duration current_; Duration current_;
}; };
} // namespace animation } // namespace ftxui::animation
} // namespace ftxui
#endif /* end of include guard: FTXUI_ANIMATION_HPP */ #endif /* end of include guard: FTXUI_ANIMATION_HPP */

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,9 +5,7 @@
#define FTXUI_COMPONENT_EVENT_HPP #define FTXUI_COMPONENT_EVENT_HPP
#include <ftxui/component/mouse.hpp> // for Mouse #include <ftxui/component/mouse.hpp> // for Mouse
#include <functional> #include <string> // for string, operator==
#include <string> // for string, operator==
#include <vector>
namespace ftxui { namespace ftxui {
@@ -33,7 +31,8 @@ struct Event {
static Event Character(wchar_t); static Event Character(wchar_t);
static Event Special(std::string); static Event Special(std::string);
static Event Mouse(std::string, Mouse mouse); 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 --- // --- Arrow ---
static const Event ArrowLeft; static const Event ArrowLeft;
@@ -53,33 +52,71 @@ struct Event {
static const Event Escape; static const Event Escape;
static const Event Tab; static const Event Tab;
static const Event TabReverse; 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 Insert;
static const Event Home; static const Event Home;
static const Event End; static const Event End;
static const Event PageUp; static const Event PageUp;
static const Event PageDown; 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 --- // --- Custom ---
static const Event Custom; static const Event Custom;
//--- Method section --------------------------------------------------------- //--- 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; } bool is_character() const { return type_ == Type::Character; }
std::string character() const { return input_; } std::string character() const { return input_; }
bool is_mouse() const { return type_ == Type::Mouse; } bool is_mouse() const { return type_ == Type::Mouse; }
struct Mouse& mouse() { return data_.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_x() const { return data_.cursor.x; }
int cursor_y() const { return data_.cursor.y; } 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_; } // Debug
bool operator!=(const Event& other) const { return !operator==(other); } std::string DebugString() const;
//--- State section ---------------------------------------------------------- //--- State section ----------------------------------------------------------
ScreenInteractive* screen_ = nullptr; ScreenInteractive* screen_ = nullptr;
@@ -91,7 +128,8 @@ struct Event {
Unknown, Unknown,
Character, Character,
Mouse, Mouse,
CursorReporting, CursorPosition,
CursorShape,
}; };
Type type_ = Type::Unknown; Type type_ = Type::Unknown;
@@ -103,6 +141,7 @@ struct Event {
union { union {
struct Mouse mouse; struct Mouse mouse;
struct Cursor cursor; struct Cursor cursor;
int cursor_shape;
} data_ = {}; } data_ = {};
std::string input_; std::string input_;

View File

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

View File

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

View File

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

View File

@@ -31,6 +31,8 @@ class ScreenInteractive : public Screen {
// Constructors: // Constructors:
static ScreenInteractive FixedSize(int dimx, int dimy); static ScreenInteractive FixedSize(int dimx, int dimy);
static ScreenInteractive Fullscreen(); static ScreenInteractive Fullscreen();
static ScreenInteractive FullscreenPrimaryScreen();
static ScreenInteractive FullscreenAlternateScreen();
static ScreenInteractive FitComponent(); static ScreenInteractive FitComponent();
static ScreenInteractive TerminalOutput(); static ScreenInteractive TerminalOutput();
@@ -57,6 +59,15 @@ class ScreenInteractive : public Screen {
// temporarily uninstalled. // temporarily uninstalled.
Closure WithRestoredIO(Closure); 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: private:
void ExitNow(); void ExitNow();
@@ -98,7 +109,7 @@ class ScreenInteractive : public Screen {
std::string set_cursor_position; std::string set_cursor_position;
std::string reset_cursor_position; std::string reset_cursor_position;
std::atomic<bool> quit_ = false; std::atomic<bool> quit_{false};
std::thread event_listener_; std::thread event_listener_;
std::thread animation_listener_; std::thread animation_listener_;
bool animation_requested_ = false; bool animation_requested_ = false;
@@ -112,6 +123,12 @@ class ScreenInteractive : public Screen {
bool frame_valid_ = false; 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; friend class Loop;
public: public:

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,7 +5,7 @@
#define FTXUI_DOM_TAKE_ANY_ARGS_HPP #define FTXUI_DOM_TAKE_ANY_ARGS_HPP
// IWYU pragma: private, include "ftxui/dom/elements.hpp" // IWYU pragma: private, include "ftxui/dom/elements.hpp"
#include <type_traits> #include <ftxui/dom/node.hpp>
namespace ftxui { namespace ftxui {
@@ -19,8 +19,9 @@ inline void Merge(Elements& container, Element element) {
template <> template <>
inline void Merge(Elements& container, Elements elements) { inline void Merge(Elements& container, Elements elements) {
for (auto& element : elements) for (auto& element : elements) {
container.push_back(std::move(element)); container.push_back(std::move(element));
}
} }
// Turn a set of arguments into a vector. // 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 Intersection(Box a, Box b) -> Box;
static auto Union(Box a, Box b) -> Box; static auto Union(Box a, Box b) -> Box;
bool Contain(int x, int y) const; bool Contain(int x, int y) const;
bool IsEmpty() const;
bool operator==(const Box& other) const; bool operator==(const Box& other) 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 <cstdint> // for uint8_t
#include <string> // for string #include <string> // for string
#include <vector> // for vector
#ifdef RGB #ifdef RGB
// Workaround for wingdi.h (via Windows.h) defining macros that break things. // Workaround for wingdi.h (via Windows.h) defining macros that break things.
@@ -24,14 +23,22 @@ class Color {
enum Palette16 : uint8_t; enum Palette16 : uint8_t;
enum Palette256 : uint8_t; enum Palette256 : uint8_t;
// NOLINTBEGIN
Color(); // Transparent. Color(); // Transparent.
Color(Palette1 index); // Transparent. Color(Palette1 index); // Transparent.
Color(Palette16 index); // Implicit conversion from index to Color. Color(Palette16 index); // Implicit conversion from index to Color.
Color(Palette256 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 RGB(uint8_t red, uint8_t green, uint8_t blue);
static Color HSV(uint8_t hue, uint8_t saturation, uint8_t value); 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 Interpolate(float t, const Color& a, const Color& b);
static Color Blend(const Color& lhs, const Color& rhs);
//--------------------------- //---------------------------
// List of colors: // List of colors:
@@ -309,6 +316,7 @@ class Color {
bool operator!=(const Color& rhs) const; bool operator!=(const Color& rhs) const;
std::string Print(bool is_background_color) const; std::string Print(bool is_background_color) const;
bool IsOpaque() const { return alpha_ == 255; }
private: private:
enum class ColorType : uint8_t { enum class ColorType : uint8_t {
@@ -321,6 +329,7 @@ class Color {
uint8_t red_ = 0; uint8_t red_ = 0;
uint8_t green_ = 0; uint8_t green_ = 0;
uint8_t blue_ = 0; uint8_t blue_ = 0;
uint8_t alpha_ = 0;
}; };
inline namespace literals { 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 #define FTXUI_SCREEN_SCREEN_HPP
#include <cstdint> // for uint8_t #include <cstdint> // for uint8_t
#include <memory> #include <string> // for string, basic_string, allocator
#include <string> // for string, basic_string, allocator #include <vector> // for vector
#include <vector> // for vector
#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/image.hpp" // for Pixel, Image
#include "ftxui/screen/color.hpp" // for Color, Color::Default
#include "ftxui/screen/terminal.hpp" // for Dimensions #include "ftxui/screen/terminal.hpp" // for Dimensions
namespace ftxui { 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. /// @brief Define how the Screen's dimensions should look like.
/// @ingroup screen /// @ingroup screen
namespace Dimension { namespace Dimension {
@@ -60,36 +22,25 @@ Dimensions Full();
/// @brief A rectangular grid of Pixel. /// @brief A rectangular grid of Pixel.
/// @ingroup screen /// @ingroup screen
class Screen { class Screen : public Image {
public: public:
// Constructors: // Constructors:
Screen(int dimx, int dimy); Screen(int dimx, int dimy);
static Screen Create(Dimensions dimension); static Screen Create(Dimensions dimension);
static Screen Create(Dimensions width, Dimensions height); 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; std::string ToString() const;
// Print the Screen on to the terminal. // Print the Screen on to the terminal.
void Print() const; void Print() const;
// Get screen dimensions. // Fill the screen with space and reset any screen state, like hyperlinks, and
int dimx() const { return dimx_; } // cursor
int dimy() const { return dimy_; } void Clear();
// Move the terminal cursor n-lines up with n = dimy(). // Move the terminal cursor n-lines up with n = dimy().
std::string ResetPosition(bool clear = false) const; std::string ResetPosition(bool clear = false) const;
// Fill the screen with space.
void Clear();
void ApplyShader(); void ApplyShader();
struct Cursor { struct Cursor {
@@ -107,6 +58,7 @@ class Screen {
}; };
Shape shape; Shape shape;
}; };
Cursor cursor() const { return cursor_; } Cursor cursor() const { return cursor_; }
void SetCursor(Cursor cursor) { cursor_ = cursor; } void SetCursor(Cursor cursor) { cursor_ = cursor; }
@@ -115,12 +67,7 @@ class Screen {
uint8_t RegisterHyperlink(const std::string& link); uint8_t RegisterHyperlink(const std::string& link);
const std::string& Hyperlink(uint8_t id) const; const std::string& Hyperlink(uint8_t id) const;
Box stencil;
protected: protected:
int dimx_;
int dimy_;
std::vector<std::vector<Pixel>> pixels_;
Cursor cursor_; Cursor cursor_;
std::vector<std::string> hyperlinks_ = {""}; std::vector<std::string> hyperlinks_ = {""};
}; };

View File

@@ -4,10 +4,8 @@
#ifndef FTXUI_SCREEN_STRING_HPP #ifndef FTXUI_SCREEN_STRING_HPP
#define FTXUI_SCREEN_STRING_HPP #define FTXUI_SCREEN_STRING_HPP
#include <stddef.h> // for size_t #include <string> // for string, wstring, to_string
#include <cstdint> // for uint8_t #include <vector> // for vector
#include <string> // for string, wstring, to_string
#include <vector> // for vector
namespace ftxui { namespace ftxui {
std::string to_string(const std::wstring& s); std::string to_string(const std::wstring& s);
@@ -30,6 +28,4 @@ std::vector<int> CellToGlyphIndex(const std::string& input);
} // namespace ftxui } // namespace ftxui
#include "ftxui/screen/deprecated.hpp"
#endif /* end of include guard: FTXUI_SCREEN_STRING_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_(variable), previous_value_(std::move(*variable)) {
*variable_ = std::move(new_value); *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_); } ~AutoReset() { *variable_ = std::move(previous_value_); }
private: private:

View File

@@ -5,8 +5,10 @@
#define FTXUI_UTIL_REF_HPP #define FTXUI_UTIL_REF_HPP
#include <ftxui/screen/string.hpp> #include <ftxui/screen/string.hpp>
#include <memory>
#include <string> #include <string>
#include <variant> #include <variant>
#include <vector>
namespace ftxui { namespace ftxui {
@@ -15,10 +17,12 @@ template <typename T>
class ConstRef { class ConstRef {
public: public:
ConstRef() = default; 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(const ConstRef<T>&) = default;
ConstRef(ConstRef<T>&&) = default; ConstRef(ConstRef<T>&&) noexcept = default;
ConstRef(T t) : variant_(std::move(t)) {} ~ConstRef() = default;
ConstRef(const T* t) : variant_(t) {}
// Make a "reseatable" reference // Make a "reseatable" reference
ConstRef<T>& operator=(const ConstRef<T>&) = default; ConstRef<T>& operator=(const ConstRef<T>&) = default;
@@ -42,10 +46,12 @@ template <typename T>
class Ref { class Ref {
public: public:
Ref() = default; 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(const Ref<T>&) = default;
Ref(Ref<T>&&) = default; Ref(Ref<T>&&) noexcept = default;
Ref(T t) : variant_(std::move(t)) {}
Ref(T* t) : variant_(t) {}
// Make a "reseatable" reference. // Make a "reseatable" reference.
Ref<T>& operator=(const Ref<T>&) = default; Ref<T>& operator=(const Ref<T>&) = default;
@@ -77,8 +83,10 @@ class StringRef : public Ref<std::string> {
public: public:
using Ref<std::string>::Ref; using Ref<std::string>::Ref;
StringRef(const wchar_t* ref) : StringRef(to_string(std::wstring(ref))) {} StringRef(const wchar_t* ref) // NOLINT
StringRef(const char* ref) : StringRef(std::string(ref)) {} : 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 /// @brief An adapter. Own or reference a constant string. For convenience, this
@@ -87,45 +95,120 @@ class ConstStringRef : public ConstRef<std::string> {
public: public:
using ConstRef<std::string>::ConstRef; using ConstRef<std::string>::ConstRef;
ConstStringRef(const std::wstring* ref) : ConstStringRef(to_string(*ref)) {} ConstStringRef(const std::wstring* ref) // NOLINT
ConstStringRef(const std::wstring ref) : ConstStringRef(to_string(ref)) {} : ConstStringRef(to_string(*ref)) {}
ConstStringRef(const wchar_t* ref) ConstStringRef(const std::wstring ref) // NOLINT
: ConstStringRef(to_string(ref)) {}
ConstStringRef(const wchar_t* ref) // NOLINT
: ConstStringRef(to_string(std::wstring(ref))) {} : 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. /// @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 { class ConstStringListRef {
public: 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() = default;
ConstStringListRef(const std::vector<std::string>* ref) : ref_(ref) {} ~ConstStringListRef() = default;
ConstStringListRef(const std::vector<std::wstring>* ref) : ref_wide_(ref) {} ConstStringListRef& operator=(const ConstStringListRef&) = default;
ConstStringListRef(const ConstStringListRef& other) = default; ConstStringListRef& operator=(ConstStringListRef&&) = default;
ConstStringListRef& operator=(const ConstStringListRef& other) = 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 { size_t size() const {
if (ref_) { return variant_ ? std::visit(SizeVisitor(), *variant_) : 0;
return ref_->size();
}
if (ref_wide_) {
return ref_wide_->size();
}
return 0;
} }
std::string operator[](size_t i) const { std::string operator[](size_t i) const {
if (ref_) { return variant_ ? std::visit(IndexedGetter(i), *variant_) : "";
return (*ref_)[i];
}
if (ref_wide_) {
return to_string((*ref_wide_)[i]);
}
return "";
} }
private: private:
const std::vector<std::string>* ref_ = nullptr; struct SizeVisitor {
const std::vector<std::wstring>* ref_wide_ = nullptr; 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 } // namespace ftxui

View File

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

View File

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

View File

@@ -1,8 +1,6 @@
// Copyright 2022 Arthur Sonzogni. All rights reserved. // Copyright 2022 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <chrono> // for operator""s, chrono_literals
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string #include <string> // for string
#include "ftxui/component/animation.hpp" // for Duration, Params #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 // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <functional> // for function #include <functional> // for function
#include <memory> // for __shared_ptr_access, __shared_ptr_access<>::element_type, shared_ptr #include <utility> // for move
#include <type_traits> // for remove_reference, remove_reference<>::type
#include <utility> // for move
#include "ftxui/component/component.hpp" // for Make, CatchEvent, ComponentDecorator #include "ftxui/component/component.hpp" // for Make, CatchEvent, ComponentDecorator
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase #include "ftxui/component/component_base.hpp" // for Component, ComponentBase

View File

@@ -4,7 +4,6 @@
#include <functional> // for function #include <functional> // for function
#include <utility> // for move #include <utility> // for move
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Checkbox #include "ftxui/component/component.hpp" // for Make, Checkbox
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase #include "ftxui/component/component_base.hpp" // for Component, ComponentBase
#include "ftxui/component/component_options.hpp" // for CheckboxOption, EntryState #include "ftxui/component/component_options.hpp" // for CheckboxOption, EntryState
@@ -73,6 +72,7 @@ class CheckboxBase : public ComponentBase, public CheckboxOption {
event.mouse().motion == Mouse::Pressed) { event.mouse().motion == Mouse::Pressed) {
*checked = !*checked; *checked = !*checked;
on_change(); on_change();
TakeFocus();
return true; return true;
} }
@@ -86,6 +86,32 @@ class CheckboxBase : public ComponentBase, public CheckboxOption {
}; };
} // namespace } // 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. /// @brief Draw checkable element.
/// @param label The label of the checkbox. /// @param label The label of the checkbox.
/// @param checked Whether the checkbox is checked or not. /// @param checked Whether the checkbox is checked or not.
@@ -110,7 +136,7 @@ class CheckboxBase : public ComponentBase, public CheckboxOption {
/// ``` /// ```
// NOLINTNEXTLINE // NOLINTNEXTLINE
Component Checkbox(ConstStringRef label, bool* checked, CheckboxOption option) { Component Checkbox(ConstStringRef label, bool* checked, CheckboxOption option) {
option.label = label; option.label = std::move(label);
option.checked = checked; option.checked = checked;
return Make<CheckboxBase>(std::move(option)); 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 // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <functional> // for function #include <functional> // for function
#include <memory> // for shared_ptr, allocator
#include <utility> // for move #include <utility> // for move
#include "ftxui/component/component.hpp" // for Checkbox, Maybe, Make, Vertical, Collapsible #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}); return hbox({prefix, t});
}; };
Add(Container::Vertical({ Add(Container::Vertical({
Checkbox(label, show_.operator->(), opt), Checkbox(std::move(label), show_.operator->(), opt),
Maybe(std::move(child), show_.operator->()), 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 <string> // for string
#include "ftxui/component/component.hpp" // for Collapsible, Renderer #include "ftxui/component/component.hpp" // for Collapsible, Renderer

View File

@@ -5,6 +5,7 @@
#include <cassert> // for assert #include <cassert> // for assert
#include <cstddef> // for size_t #include <cstddef> // for size_t
#include <iterator> // for begin, end #include <iterator> // for begin, end
#include <memory> // for unique_ptr, make_unique
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type #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 // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <cassert> #include <cassert>
#include <iostream>
#include <vector> #include <vector>
#include "ftxui/component/component.hpp" #include "ftxui/component/component.hpp"
#include "ftxui/component/terminal_input_parser.hpp" #include "ftxui/component/terminal_input_parser.hpp"
@@ -11,8 +10,9 @@ using namespace ftxui;
namespace { namespace {
bool GeneratorBool(const char*& data, size_t& size) { bool GeneratorBool(const char*& data, size_t& size) {
if (size == 0) if (size == 0) {
return false; return false;
}
auto out = bool(data[0] % 2); auto out = bool(data[0] % 2);
data++; data++;

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,6 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#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.hpp" // for Horizontal, Vertical, Button, Tab
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component #include "ftxui/component/component_base.hpp" // for ComponentBase, Component

View File

@@ -1,11 +1,11 @@
// Copyright 2021 Arthur Sonzogni. All rights reserved. // Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <cstddef> // for size_t #include <ftxui/component/event.hpp>
#include <functional> // for function #include <functional> // for function
#include <memory> // for __shared_ptr_access, allocator, shared_ptr
#include <string> // for string #include <string> // for string
#include <utility>
#include "ftxui/component/component.hpp" // for Maybe, Checkbox, Make, Radiobox, Vertical, Dropdown #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_base.hpp" // for Component, ComponentBase
#include "ftxui/component/component_options.hpp" // for CheckboxOption, EntryState #include "ftxui/component/component_options.hpp" // for CheckboxOption, EntryState
@@ -20,79 +20,115 @@ namespace ftxui {
/// @param entries The list of entries to display. /// @param entries The list of entries to display.
/// @param selected The index of the selected entry. /// @param selected The index of the selected entry.
Component Dropdown(ConstStringListRef entries, int* selected) { 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: public:
Impl(ConstStringListRef entries, int* selected) explicit Impl(DropdownOption option) : DropdownOption(std::move(option)) {
: entries_(entries), selected_(selected) { FillDefault();
CheckboxOption option; checkbox_ = Checkbox(checkbox);
option.transform = [](const EntryState& s) { radiobox_ = Radiobox(radiobox);
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_);
Add(Container::Vertical({ Add(Container::Vertical({
checkbox_, checkbox_,
Maybe(radiobox_, &show_), Maybe(radiobox_, checkbox.checked),
})); }));
} }
Element Render() override { Element Render() override {
*selected_ = util::clamp(*selected_, 0, int(entries_.size()) - 1); radiobox.selected =
title_ = entries_[static_cast<size_t>(*selected_)]; util::clamp(radiobox.selected(), 0, int(radiobox.entries.size()) - 1);
if (show_) { title_ = radiobox.entries[selected_()];
const int max_height = 12;
return vbox({
checkbox_->Render(),
separator(),
radiobox_->Render() | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, max_height),
}) |
border;
}
return vbox({ return transform(*open_, checkbox_->Render(), radiobox_->Render());
checkbox_->Render() | border,
filler(),
});
} }
// Switch focus in between the checkbox and the radiobox when selecting it. // Switch focus in between the checkbox and the radiobox when selecting it.
bool OnEvent(ftxui::Event event) override { bool OnEvent(ftxui::Event event) override {
const bool show_old = show_; const bool open_old = open_();
const int selected_old = *selected_; const int selected_old = selected_();
const bool handled = ComponentBase::OnEvent(event); 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(); radiobox_->TakeFocus();
} }
if (selected_old != *selected_) { // Auto-close the dropdown when the user selects an item, even if the item
checkbox_->TakeFocus(); // it the same as the previous one.
show_ = false; 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; 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: private:
ConstStringListRef entries_; Ref<bool> open_;
bool show_ = false; Ref<int> selected_;
int* selected_;
std::string title_;
Component checkbox_; Component checkbox_;
Component radiobox_; Component radiobox_;
std::string title_;
}; };
return Make<Impl>(entries, selected); return Make<Impl>(option);
} }
} // namespace ftxui } // namespace ftxui

View File

@@ -1,12 +1,25 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <map> // for map
#include <string>
#include <utility> // for move #include <utility> // for move
#include "ftxui/component/event.hpp" #include "ftxui/component/event.hpp"
#include "ftxui/component/mouse.hpp" // for Mouse #include "ftxui/component/mouse.hpp" // for Mouse
#include "ftxui/screen/string.hpp" // for to_wstring #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 { namespace ftxui {
/// @brief An event corresponding to a given typed character. /// @brief An event corresponding to a given typed character.
@@ -49,6 +62,16 @@ Event Event::Mouse(std::string input, struct Mouse mouse) {
return event; 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. /// @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. /// @param input An arbitrary sequence of character defined by the developer.
/// @ingroup component. /// @ingroup component.
@@ -61,50 +84,384 @@ Event Event::Special(std::string input) {
/// @internal /// @internal
// static // static
Event Event::CursorReporting(std::string input, int x, int y) { Event Event::CursorPosition(std::string input, int x, int y) {
Event event; Event event;
event.input_ = std::move(input); event.input_ = std::move(input);
event.type_ = Type::CursorReporting; event.type_ = Type::CursorPosition;
event.data_.cursor = {x, y}; // NOLINT event.data_.cursor = {x, y}; // NOLINT
return event; 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 --- // --- Arrow ---
const Event Event::ArrowLeft = Event::Special("\x1B[D"); // NOLINT const Event Event::ArrowLeft = Event::Special("\x1B[D");
const Event Event::ArrowRight = Event::Special("\x1B[C"); // NOLINT const Event Event::ArrowRight = Event::Special("\x1B[C");
const Event Event::ArrowUp = Event::Special("\x1B[A"); // NOLINT const Event Event::ArrowUp = Event::Special("\x1B[A");
const Event Event::ArrowDown = Event::Special("\x1B[B"); // NOLINT const Event Event::ArrowDown = Event::Special("\x1B[B");
const Event Event::ArrowLeftCtrl = Event::Special("\x1B[1;5D"); // NOLINT const Event Event::ArrowLeftCtrl = Event::Special("\x1B[1;5D");
const Event Event::ArrowRightCtrl = Event::Special("\x1B[1;5C"); // NOLINT const Event Event::ArrowRightCtrl = Event::Special("\x1B[1;5C");
const Event Event::ArrowUpCtrl = Event::Special("\x1B[1;5A"); // NOLINT const Event Event::ArrowUpCtrl = Event::Special("\x1B[1;5A");
const Event Event::ArrowDownCtrl = Event::Special("\x1B[1;5B"); // NOLINT const Event Event::ArrowDownCtrl = Event::Special("\x1B[1;5B");
const Event Event::Backspace = Event::Special({127}); // NOLINT const Event Event::Backspace = Event::Special({127});
const Event Event::Delete = Event::Special("\x1B[3~"); // NOLINT const Event Event::Delete = Event::Special("\x1B[3~");
const Event Event::Escape = Event::Special("\x1B"); // NOLINT const Event Event::Escape = Event::Special("\x1B");
const Event Event::Return = Event::Special({10}); // NOLINT const Event Event::Return = Event::Special({10});
const Event Event::Tab = Event::Special({9}); // NOLINT const Event Event::Tab = Event::Special({9});
const Event Event::TabReverse = Event::Special({27, 91, 90}); // NOLINT const Event Event::TabReverse = Event::Special({27, 91, 90});
// See https://invisible-island.net/xterm/xterm-function-keys.html // See https://invisible-island.net/xterm/xterm-function-keys.html
// We follow xterm-new / vterm-xf86-v4 / mgt / screen // We follow xterm-new / vterm-xf86-v4 / mgt / screen
const Event Event::F1 = Event::Special("\x1BOP"); // NOLINT const Event Event::F1 = Event::Special("\x1BOP");
const Event Event::F2 = Event::Special("\x1BOQ"); // NOLINT const Event Event::F2 = Event::Special("\x1BOQ");
const Event Event::F3 = Event::Special("\x1BOR"); // NOLINT const Event Event::F3 = Event::Special("\x1BOR");
const Event Event::F4 = Event::Special("\x1BOS"); // NOLINT const Event Event::F4 = Event::Special("\x1BOS");
const Event Event::F5 = Event::Special("\x1B[15~"); // NOLINT const Event Event::F5 = Event::Special("\x1B[15~");
const Event Event::F6 = Event::Special("\x1B[17~"); // NOLINT const Event Event::F6 = Event::Special("\x1B[17~");
const Event Event::F7 = Event::Special("\x1B[18~"); // NOLINT const Event Event::F7 = Event::Special("\x1B[18~");
const Event Event::F8 = Event::Special("\x1B[19~"); // NOLINT const Event Event::F8 = Event::Special("\x1B[19~");
const Event Event::F9 = Event::Special("\x1B[20~"); // NOLINT const Event Event::F9 = Event::Special("\x1B[20~");
const Event Event::F10 = Event::Special("\x1B[21~"); // NOLINT const Event Event::F10 = Event::Special("\x1B[21~");
const Event Event::F11 = Event::Special("\x1B[23~"); // NOLINT const Event Event::F11 = Event::Special("\x1B[23~");
const Event Event::F12 = Event::Special("\x1B[24~"); // NOLINT const Event Event::F12 = Event::Special("\x1B[24~");
const Event Event::Insert = Event::Special("\x1B[2~"); // NOLINT const Event Event::Insert = Event::Special("\x1B[2~");
const Event Event::Home = Event::Special({27, 91, 72}); // NOLINT const Event Event::Home = Event::Special({27, 91, 72});
const Event Event::End = Event::Special({27, 91, 70}); // NOLINT const Event Event::End = Event::Special({27, 91, 70});
const Event Event::PageUp = Event::Special({27, 91, 53, 126}); // NOLINT const Event Event::PageUp = Event::Special({27, 91, 53, 126});
const Event Event::PageDown = Event::Special({27, 91, 54, 126}); // NOLINT const Event Event::PageDown = Event::Special({27, 91, 54, 126});
const Event Event::Custom = Event::Special({0}); // NOLINT 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 } // namespace ftxui

View File

@@ -1,9 +1,8 @@
// Copyright 2022 Arthur Sonzogni. All rights reserved. // Copyright 2022 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <ftxui/component/captured_mouse.hpp> // for CapturedMouse #include <functional> // for function
#include <functional> // for function #include <utility> // for move
#include <utility> // for move
#include "ftxui/component/component.hpp" // for ComponentDecorator, Hoverable, Make #include "ftxui/component/component.hpp" // for ComponentDecorator, Hoverable, Make
#include "ftxui/component/component_base.hpp" // for ComponentBase #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 // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <ftxui/dom/elements.hpp> // for Element, text #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.hpp" // for Hoverable, Horizontal, operator|=, Renderer
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component #include "ftxui/component/component_base.hpp" // for ComponentBase, Component
@@ -70,10 +69,10 @@ TEST(HoverableTest, BasicCallback) {
int on_enter_2 = 0; int on_enter_2 = 0;
int on_leave_1 = 0; int on_leave_1 = 0;
int on_leave_2 = 0; int on_leave_2 = 0;
auto c1 = Hoverable( auto c1 =
BasicComponent(), [&] { on_enter_1++; }, [&] { on_leave_1++; }); Hoverable(BasicComponent(), [&] { on_enter_1++; }, [&] { on_leave_1++; });
auto c2 = Hoverable( auto c2 =
BasicComponent(), [&] { on_enter_2++; }, [&] { on_leave_2++; }); Hoverable(BasicComponent(), [&] { on_enter_2++; }, [&] { on_leave_2++; });
auto layout = Container::Horizontal({c1, c2}); auto layout = Container::Horizontal({c1, c2});
auto screen = Screen(8, 2); auto screen = Screen(8, 2);
Render(screen, layout->Render()); Render(screen, layout->Render());

View File

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

View File

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

View File

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

View File

@@ -3,8 +3,7 @@
// the LICENSE file. // the LICENSE file.
#include <functional> // for function #include <functional> // for function
#include <memory> // for make_unique, __shared_ptr_access, __shared_ptr_access<>::element_type, shared_ptr #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.hpp" // for ComponentDecorator, Maybe, Make
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase #include "ftxui/component/component_base.hpp" // for Component, ComponentBase

View File

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

View File

@@ -2,11 +2,9 @@
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <gtest/gtest.h> // for Test, EXPECT_EQ, Message, TestPartResult, TestInfo (ptr only), TEST #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 <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 <string> // for string, basic_string #include <vector> // for vector
#include <vector> // for vector
#include "ftxui/component/animation.hpp" // for Duration, Params #include "ftxui/component/animation.hpp" // for Duration, Params
#include "ftxui/component/component.hpp" // for Menu #include "ftxui/component/component.hpp" // for Menu

View File

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

View File

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

View File

@@ -1,7 +1,6 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <string> // for string
#include <thread> // for thread #include <thread> // for thread
#include <utility> // for move #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 // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <functional> // for function #include <functional> // for function
#include <memory> // for __shared_ptr_access, shared_ptr
#include <utility> // for move #include <utility> // for move
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Renderer #include "ftxui/component/component.hpp" // for Make, Renderer
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase #include "ftxui/component/component_base.hpp" // for Component, ComponentBase
#include "ftxui/component/event.hpp" // for Event #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/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
#include <ftxui/util/ref.hpp> // for Ref #include <ftxui/util/ref.hpp> // for Ref
#include <functional> // for function #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/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Horizontal, Make, ResizableSplit, ResizableSplitBottom, ResizableSplitLeft, ResizableSplitRight, ResizableSplitTop #include "ftxui/component/component.hpp" // for Horizontal, Make, ResizableSplit, ResizableSplitBottom, ResizableSplitLeft, ResizableSplitRight, ResizableSplitTop
@@ -23,10 +22,32 @@ class ResizableSplitBase : public ComponentBase {
public: public:
explicit ResizableSplitBase(ResizableSplitOption options) explicit ResizableSplitBase(ResizableSplitOption options)
: options_(std::move(options)) { : options_(std::move(options)) {
Add(Container::Horizontal({ switch (options_->direction()) {
options_->main, case Direction::Left:
options_->back, 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 { 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 // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up #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.hpp" // for ResizableSplit, Renderer, ResizableSplitBottom, ResizableSplitLeft, ResizableSplitRight, ResizableSplitTop
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component #include "ftxui/component/component_base.hpp" // for ComponentBase, Component
@@ -19,7 +18,7 @@ namespace ftxui {
namespace { namespace {
Component BasicComponent() { Component BasicComponent() {
return Renderer([] { return text(""); }); return Renderer([](bool focused) { return text(""); });
} }
Event MousePressed(int x, int y) { Event MousePressed(int x, int y) {
@@ -207,5 +206,32 @@ TEST(ResizableSplit, BasicBottomWithCustomSeparator) {
EXPECT_EQ(position, 2); 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 } // namespace ftxui
// NOLINTEND // NOLINTEND

View File

@@ -1,34 +1,38 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include "ftxui/component/screen_interactive.hpp"
#include <algorithm> // for copy, max, min #include <algorithm> // for copy, max, min
#include <array> // for array #include <array> // for array
#include <atomic>
#include <chrono> // for operator-, milliseconds, operator>=, duration, common_type<>::type, time_point #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 <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/component/task.hpp> // for Task, Closure, AnimationTask
#include <ftxui/screen/screen.hpp> // for Pixel, Screen::Cursor, Screen, Screen::Cursor::Hidden #include <ftxui/screen/screen.hpp> // for Pixel, Screen::Cursor, Screen, Screen::Cursor::Hidden
#include <functional> // for function #include <functional> // for function
#include <initializer_list> // for initializer_list #include <initializer_list> // for initializer_list
#include <iostream> // for cout, ostream, operator<<, basic_ostream, endl, flush #include <iostream> // for cout, ostream, operator<<, basic_ostream, endl, flush
#include <stack> // for stack #include <memory>
#include <thread> // for thread, sleep_for #include <stack> // for stack
#include <tuple> // for _Swallow_assign, ignore #include <string>
#include <thread> // for thread, sleep_for
#include <tuple> // for _Swallow_assign, ignore
#include <type_traits> // for decay_t #include <type_traits> // for decay_t
#include <utility> // for move, swap #include <utility> // for move, swap
#include <variant> // for visit, variant #include <variant> // for visit, variant
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/animation.hpp" // for TimePoint, Clock, Duration, Params, RequestAnimationFrame #include "ftxui/component/animation.hpp" // for TimePoint, Clock, Duration, Params, RequestAnimationFrame
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
#include "ftxui/component/component_base.hpp" // for ComponentBase #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/event.hpp" // for Event #include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/loop.hpp" // for Loop #include "ftxui/component/loop.hpp" // for Loop
#include "ftxui/component/receiver.hpp" // for ReceiverImpl, Sender, MakeReceiver, SenderImpl, Receiver #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/component/terminal_input_parser.hpp" // for TerminalInputParser
#include "ftxui/dom/node.hpp" // for Node, Render #include "ftxui/dom/node.hpp" // for Node, Render
#include "ftxui/dom/requirement.hpp" // for Requirement #include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/pixel.hpp" // for Pixel
#include "ftxui/screen/terminal.hpp" // for Dimensions, Size #include "ftxui/screen/terminal.hpp" // for Dimensions, Size
#if defined(_WIN32) #if defined(_WIN32)
@@ -132,7 +136,6 @@ void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
// Read char from the terminal. // Read char from the terminal.
void EventListener(std::atomic<bool>* quit, Sender<Task> out) { void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
(void)timeout_microseconds;
auto parser = TerminalInputParser(std::move(out)); auto parser = TerminalInputParser(std::move(out));
char c; char c;
@@ -159,7 +162,7 @@ void ftxui_on_resize(int columns, int rows) {
#else // POSIX (Linux & Mac) #else // POSIX (Linux & Mac)
int CheckStdinReady(int usec_timeout) { int CheckStdinReady(int usec_timeout) {
timeval tv = {0, usec_timeout}; timeval tv = {0, usec_timeout}; // NOLINT
fd_set fds; fd_set fds;
FD_ZERO(&fds); // NOLINT FD_ZERO(&fds); // NOLINT
FD_SET(STDIN_FILENO, &fds); // NOLINT FD_SET(STDIN_FILENO, &fds); // NOLINT
@@ -214,11 +217,11 @@ void RecordSignal(int signal) {
break; break;
#if !defined(_WIN32) #if !defined(_WIN32)
case SIGTSTP: case SIGTSTP: // NOLINT
g_signal_stop_count++; g_signal_stop_count++;
break; break;
case SIGWINCH: case SIGWINCH: // NOLINT
g_signal_resize_count++; g_signal_resize_count++;
break; break;
#endif #endif
@@ -249,14 +252,24 @@ void ExecuteSignalHandlers() {
void InstallSignalHandler(int sig) { void InstallSignalHandler(int sig) {
auto old_signal_handler = std::signal(sig, RecordSignal); auto old_signal_handler = std::signal(sig, RecordSignal);
on_exit_functions.push( on_exit_functions.emplace(
[=] { std::ignore = std::signal(sig, old_signal_handler); }); [=] { std::ignore = std::signal(sig, old_signal_handler); });
} }
// CSI: Control Sequence Introducer
const std::string CSI = "\x1b["; // NOLINT 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 // DEC: Digital Equipment Corporation
enum class DECMode { enum class DECMode : std::uint16_t {
kLineWrap = 7, kLineWrap = 7,
kCursor = 25, kCursor = 25,
@@ -275,7 +288,7 @@ enum class DECMode {
}; };
// Device Status Report (DSR) { // Device Status Report (DSR) {
enum class DSRMode { enum class DSRMode : std::uint8_t {
kCursor = 6, 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 // static
ScreenInteractive ScreenInteractive::Fullscreen() { 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 { return {
0, 0,
0, 0,
@@ -402,7 +441,7 @@ void ScreenInteractive::TrackMouse(bool enable) {
track_mouse_ = 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. /// It will be executed later, after every other scheduled tasks.
/// @ingroup component /// @ingroup component
void ScreenInteractive::Post(Task task) { void ScreenInteractive::Post(Task task) {
@@ -508,7 +547,8 @@ void ScreenInteractive::PostMain() {
// On final exit, keep the current drawing and reset cursor position one // On final exit, keep the current drawing and reset cursor position one
// line after it. // line after it.
if (!use_alternative_screen_) { 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. /// @brief Return the currently active screen, or null if none.
// static // static
ScreenInteractive* ScreenInteractive::Active() { ScreenInteractive* ScreenInteractive::Active() {
@@ -534,11 +586,26 @@ ScreenInteractive* ScreenInteractive::Active() {
void ScreenInteractive::Install() { void ScreenInteractive::Install() {
frame_valid_ = false; 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 // After uninstalling the new configuration, flush it to the terminal to
// ensure it is fully applied: // 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 // Install signal handlers to restore the terminal state on exit. The default
// signal handlers are restored on exit. // signal handlers are restored on exit.
@@ -584,15 +651,34 @@ void ScreenInteractive::Install() {
struct termios terminal; // NOLINT struct termios terminal; // NOLINT
tcgetattr(STDIN_FILENO, &terminal); 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. // Enabling raw terminal input mode
terminal.c_lflag &= ~ECHO; // NOLINT Do not print after a key press. terminal.c_iflag &= ~IGNBRK; // Disable ignoring break condition
terminal.c_cc[VMIN] = 0; terminal.c_iflag &= ~BRKINT; // Disable break causing input and output to be
terminal.c_cc[VTIME] = 0; // flushed
// auto oldf = fcntl(STDIN_FILENO, F_GETFL, 0); terminal.c_iflag &= ~PARMRK; // Disable marking parity errors.
// fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); terminal.c_iflag &= ~ISTRIP; // Disable striping 8th bit off characters.
// on_exit_functions.push([=] { fcntl(STDIN_FILENO, F_GETFL, oldf); }); 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); tcsetattr(STDIN_FILENO, TCSANOW, &terminal);
@@ -600,12 +686,12 @@ void ScreenInteractive::Install() {
auto enable = [&](const std::vector<DECMode>& parameters) { auto enable = [&](const std::vector<DECMode>& parameters) {
std::cout << Set(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) { auto disable = [&](const std::vector<DECMode>& parameters) {
std::cout << Reset(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_) { 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({ disable({
// DECMode::kCursor, // DECMode::kCursor,
DECMode::kLineWrap, DECMode::kLineWrap,
@@ -673,26 +754,45 @@ void ScreenInteractive::RunOnce(Component component) {
} }
// private // private
// NOLINTNEXTLINE
void ScreenInteractive::HandleTask(Component component, Task& task) { void ScreenInteractive::HandleTask(Component component, Task& task) {
// clang-format off std::visit(
std::visit([&](auto&& arg) { [&](auto&& arg) {
using T = std::decay_t<decltype(arg)>; using T = std::decay_t<decltype(arg)>;
// clang-format off
// Handle Event. // Handle Event.
if constexpr (std::is_same_v<T, 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_x_ = arg.cursor_x();
cursor_y_ = arg.cursor_y(); cursor_y_ = arg.cursor_y();
return; return;
} }
if (arg.is_cursor_shape()) {
cursor_reset_shape_= arg.cursor_shape();
return;
}
if (arg.is_mouse()) { if (arg.is_mouse()) {
arg.mouse().x -= cursor_x_; arg.mouse().x -= cursor_x_;
arg.mouse().y -= cursor_y_; arg.mouse().y -= cursor_y_;
} }
arg.screen_ = this; 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; frame_valid_ = false;
return; return;
} }
@@ -758,6 +858,13 @@ void ScreenInteractive::Draw(Component component) {
ResetCursorPosition(); ResetCursorPosition();
std::cout << ResetPosition(/*clear=*/resized); 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. // Resize the screen if needed.
if (resized) { if (resized) {
dimx_ = dimx; dimx_ = dimx;
@@ -799,10 +906,18 @@ void ScreenInteractive::Draw(Component component) {
const int dx = dimx_ - 1 - cursor_.x + int(dimx_ != terminal.dimx); const int dx = dimx_ - 1 - cursor_.x + int(dimx_ != terminal.dimx);
const int dy = dimy_ - 1 - cursor_.y; const int dy = dimy_ - 1 - cursor_.y;
set_cursor_position = "\x1B[" + std::to_string(dy) + "A" + // set_cursor_position.clear();
"\x1B[" + std::to_string(dx) + "D"; reset_cursor_position.clear();
reset_cursor_position = "\x1B[" + std::to_string(dy) + "B" + //
"\x1B[" + std::to_string(dx) + "C"; 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) { if (cursor_.shape == Cursor::Hidden) {
set_cursor_position += "\033[?25l"; set_cursor_position += "\033[?25l";
@@ -846,7 +961,7 @@ void ScreenInteractive::ExitNow() {
// private: // private:
void ScreenInteractive::Signal(int signal) { void ScreenInteractive::Signal(int signal) {
if (signal == SIGABRT) { if (signal == SIGABRT) {
OnExit(); Exit();
return; return;
} }

View File

@@ -64,4 +64,71 @@ TEST(ScreenInteractive, PostTaskToNonActive) {
screen.Post([] {}); 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 } // namespace ftxui

View File

@@ -1,8 +1,7 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <algorithm> // for max, min #include <algorithm> // for max, min
#include <cstdint> // for uint8_t, uint16_t, uint32_t, uint64_t
#include <ftxui/component/component_options.hpp> // for SliderOption #include <ftxui/component/component_options.hpp> // for SliderOption
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up #include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
#include <string> // for allocator #include <string> // for allocator
@@ -36,31 +35,26 @@ Decorator flexDirection(Direction direction) {
} }
template <class T> template <class T>
class SliderBase : public ComponentBase { class SliderBase : public SliderOption<T>, public ComponentBase {
public: public:
explicit SliderBase(SliderOption<T> options) explicit SliderBase(SliderOption<T> options) : SliderOption<T>(options) {}
: value_(options.value),
min_(options.min),
max_(options.max),
increment_(options.increment),
options_(options) {}
Element Render() override { Element Render() override {
auto gauge_color = Focused() ? color(options_.color_active) auto gauge_color =
: color(options_.color_inactive); Focused() ? color(this->color_active) : color(this->color_inactive);
const float percent = float(value_() - min_()) / float(max_() - min_()); const float percent =
return gaugeDirection(percent, options_.direction) | float(this->value() - this->min()) / float(this->max() - this->min());
flexDirection(options_.direction) | reflect(gauge_box_) | return gaugeDirection(percent, this->direction) |
gauge_color; flexDirection(this->direction) | reflect(gauge_box_) | gauge_color;
} }
void OnLeft() { void OnLeft() {
switch (options_.direction) { switch (this->direction) {
case Direction::Right: case Direction::Right:
value_() -= increment_(); this->value() -= this->increment();
break; break;
case Direction::Left: case Direction::Left:
value_() += increment_(); this->value() += this->increment();
break; break;
case Direction::Up: case Direction::Up:
case Direction::Down: case Direction::Down:
@@ -69,12 +63,12 @@ class SliderBase : public ComponentBase {
} }
void OnRight() { void OnRight() {
switch (options_.direction) { switch (this->direction) {
case Direction::Right: case Direction::Right:
value_() += increment_(); this->value() += this->increment();
break; break;
case Direction::Left: case Direction::Left:
value_() -= increment_(); this->value() -= this->increment();
break; break;
case Direction::Up: case Direction::Up:
case Direction::Down: case Direction::Down:
@@ -83,12 +77,12 @@ class SliderBase : public ComponentBase {
} }
void OnUp() { void OnUp() {
switch (options_.direction) { switch (this->direction) {
case Direction::Up: case Direction::Up:
value_() -= increment_(); this->value() -= this->increment();
break; break;
case Direction::Down: case Direction::Down:
value_() += increment_(); this->value() += this->increment();
break; break;
case Direction::Left: case Direction::Left:
case Direction::Right: case Direction::Right:
@@ -97,12 +91,12 @@ class SliderBase : public ComponentBase {
} }
void OnDown() { void OnDown() {
switch (options_.direction) { switch (this->direction) {
case Direction::Down: case Direction::Down:
value_() -= increment_(); this->value() += this->increment();
break; break;
case Direction::Up: case Direction::Up:
value_() += increment_(); this->value() -= this->increment();
break; break;
case Direction::Left: case Direction::Left:
case Direction::Right: case Direction::Right:
@@ -115,7 +109,7 @@ class SliderBase : public ComponentBase {
return OnMouseEvent(event); return OnMouseEvent(event);
} }
T old_value = value_(); T old_value = this->value();
if (event == Event::ArrowLeft || event == Event::Character('h')) { if (event == Event::ArrowLeft || event == Event::Character('h')) {
OnLeft(); OnLeft();
} }
@@ -129,8 +123,11 @@ class SliderBase : public ComponentBase {
OnUp(); OnUp();
} }
value_() = util::clamp(value_(), min_(), max_()); this->value() = std::max(this->min(), std::min(this->max(), this->value()));
if (old_value != value_()) { if (old_value != this->value()) {
if (this->on_change) {
this->on_change();
}
return true; return true;
} }
@@ -144,38 +141,52 @@ class SliderBase : public ComponentBase {
return true; return true;
} }
switch (options_.direction) { T old_value = this->value();
switch (this->direction) {
case Direction::Right: { case Direction::Right: {
value_() = min_() + (event.mouse().x - gauge_box_.x_min) * this->value() =
(max_() - min_()) / this->min() + (event.mouse().x - gauge_box_.x_min) *
(gauge_box_.x_max - gauge_box_.x_min); (this->max() - this->min()) /
(gauge_box_.x_max - gauge_box_.x_min);
break; break;
} }
case Direction::Left: { case Direction::Left: {
value_() = max_() - (event.mouse().x - gauge_box_.x_min) * this->value() =
(max_() - min_()) / this->max() - (event.mouse().x - gauge_box_.x_min) *
(gauge_box_.x_max - gauge_box_.x_min); (this->max() - this->min()) /
(gauge_box_.x_max - gauge_box_.x_min);
break; break;
} }
case Direction::Down: { case Direction::Down: {
value_() = min_() + (event.mouse().y - gauge_box_.y_min) * this->value() =
(max_() - min_()) / this->min() + (event.mouse().y - gauge_box_.y_min) *
(gauge_box_.y_max - gauge_box_.y_min); (this->max() - this->min()) /
(gauge_box_.y_max - gauge_box_.y_min);
break; break;
} }
case Direction::Up: { case Direction::Up: {
value_() = max_() - (event.mouse().y - gauge_box_.y_min) * this->value() =
(max_() - min_()) / this->max() - (event.mouse().y - gauge_box_.y_min) *
(gauge_box_.y_max - gauge_box_.y_min); (this->max() - this->min()) /
(gauge_box_.y_max - gauge_box_.y_min);
break; 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; return true;
} }
if (event.mouse().button != Mouse::Left || if (event.mouse().button != Mouse::Left) {
event.mouse().motion != Mouse::Pressed) { return false;
}
if (event.mouse().motion != Mouse::Pressed) {
return false; return false;
} }
@@ -196,11 +207,6 @@ class SliderBase : public ComponentBase {
bool Focusable() const final { return true; } bool Focusable() const final { return true; }
private: private:
Ref<T> value_;
ConstRef<T> min_;
ConstRef<T> max_;
ConstRef<T> increment_;
SliderOption<T> options_;
Box gauge_box_; Box gauge_box_;
CapturedMouse captured_mouse_; CapturedMouse captured_mouse_;
}; };
@@ -255,6 +261,7 @@ class SliderWithLabel : public ComponentBase {
Box box_; Box box_;
bool mouse_hover_ = false; bool mouse_hover_ = false;
}; };
} // namespace } // namespace
/// @brief An horizontal slider. /// @brief An horizontal slider.
@@ -339,6 +346,7 @@ template <typename T>
Component Slider(SliderOption<T> options) { Component Slider(SliderOption<T> options) {
return Make<SliderBase<T>>(options); return Make<SliderBase<T>>(options);
} }
template Component Slider(SliderOption<int8_t>); template Component Slider(SliderOption<int8_t>);
template Component Slider(SliderOption<int16_t>); template Component Slider(SliderOption<int16_t>);
template Component Slider(SliderOption<int32_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/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/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
#include <ftxui/dom/elements.hpp> // for frame #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.hpp" // for Slider, Vertical, operator|=
#include "ftxui/component/component_base.hpp" // for ComponentBase #include "ftxui/component/component_base.hpp" // for ComponentBase
@@ -46,6 +45,7 @@ Event MouseReleased(int x, int y) {
} // namespace } // namespace
TEST(SliderTest, Right) { TEST(SliderTest, Right) {
int updated = 0;
int value = 50; int value = 50;
auto slider = Slider<int>({ auto slider = Slider<int>({
.value = &value, .value = &value,
@@ -53,23 +53,31 @@ TEST(SliderTest, Right) {
.max = 100, .max = 100,
.increment = 10, .increment = 10,
.direction = Direction::Right, .direction = Direction::Right,
.on_change = [&]() { updated++; },
}); });
Screen screen(11, 1); Screen screen(11, 1);
Render(screen, slider->Render()); Render(screen, slider->Render());
EXPECT_EQ(value, 50); EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(3, 0))); EXPECT_TRUE(slider->OnEvent(MousePressed(3, 0)));
EXPECT_EQ(value, 50); EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(9, 0))); EXPECT_TRUE(slider->OnEvent(MousePressed(9, 0)));
EXPECT_EQ(value, 90); EXPECT_EQ(value, 90);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(9, 2))); EXPECT_TRUE(slider->OnEvent(MousePressed(9, 2)));
EXPECT_EQ(value, 90); EXPECT_EQ(value, 90);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(5, 2))); EXPECT_TRUE(slider->OnEvent(MousePressed(5, 2)));
EXPECT_EQ(value, 50); EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 2);
EXPECT_TRUE(slider->OnEvent(MouseReleased(5, 2))); EXPECT_TRUE(slider->OnEvent(MouseReleased(5, 2)));
EXPECT_FALSE(slider->OnEvent(MousePressed(5, 2))); EXPECT_FALSE(slider->OnEvent(MousePressed(5, 2)));
EXPECT_EQ(value, 50);
} }
TEST(SliderTest, Left) { TEST(SliderTest, Left) {
int updated = 0;
int value = 50; int value = 50;
auto slider = Slider<int>({ auto slider = Slider<int>({
.value = &value, .value = &value,
@@ -77,23 +85,31 @@ TEST(SliderTest, Left) {
.max = 100, .max = 100,
.increment = 10, .increment = 10,
.direction = Direction::Left, .direction = Direction::Left,
.on_change = [&]() { updated++; },
}); });
Screen screen(11, 1); Screen screen(11, 1);
Render(screen, slider->Render()); Render(screen, slider->Render());
EXPECT_EQ(value, 50); EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(3, 0))); EXPECT_TRUE(slider->OnEvent(MousePressed(3, 0)));
EXPECT_EQ(value, 50); EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(9, 0))); EXPECT_TRUE(slider->OnEvent(MousePressed(9, 0)));
EXPECT_EQ(value, 10); EXPECT_EQ(value, 10);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(9, 2))); EXPECT_TRUE(slider->OnEvent(MousePressed(9, 2)));
EXPECT_EQ(value, 10); EXPECT_EQ(value, 10);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(5, 2))); EXPECT_TRUE(slider->OnEvent(MousePressed(5, 2)));
EXPECT_EQ(value, 50); EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 2);
EXPECT_TRUE(slider->OnEvent(MouseReleased(5, 2))); EXPECT_TRUE(slider->OnEvent(MouseReleased(5, 2)));
EXPECT_FALSE(slider->OnEvent(MousePressed(5, 2))); EXPECT_FALSE(slider->OnEvent(MousePressed(5, 2)));
EXPECT_EQ(value, 50);
} }
TEST(SliderTest, Down) { TEST(SliderTest, Down) {
int updated = 0;
int value = 50; int value = 50;
auto slider = Slider<int>({ auto slider = Slider<int>({
.value = &value, .value = &value,
@@ -101,23 +117,32 @@ TEST(SliderTest, Down) {
.max = 100, .max = 100,
.increment = 10, .increment = 10,
.direction = Direction::Down, .direction = Direction::Down,
.on_change = [&]() { updated++; },
}); });
Screen screen(1, 11); Screen screen(1, 11);
Render(screen, slider->Render()); Render(screen, slider->Render());
EXPECT_EQ(value, 50); EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(0, 3))); EXPECT_TRUE(slider->OnEvent(MousePressed(0, 3)));
EXPECT_EQ(value, 50); EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(0, 9))); EXPECT_TRUE(slider->OnEvent(MousePressed(0, 9)));
EXPECT_EQ(value, 90); EXPECT_EQ(value, 90);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(2, 9))); EXPECT_TRUE(slider->OnEvent(MousePressed(2, 9)));
EXPECT_EQ(value, 90); EXPECT_EQ(value, 90);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(2, 5))); EXPECT_TRUE(slider->OnEvent(MousePressed(2, 5)));
EXPECT_EQ(value, 50); EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 2);
EXPECT_TRUE(slider->OnEvent(MouseReleased(2, 5))); EXPECT_TRUE(slider->OnEvent(MouseReleased(2, 5)));
EXPECT_FALSE(slider->OnEvent(MousePressed(2, 5))); EXPECT_FALSE(slider->OnEvent(MousePressed(2, 5)));
EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 2);
} }
TEST(SliderTest, Up) { TEST(SliderTest, Up) {
int updated = 0;
int value = 50; int value = 50;
auto slider = Slider<int>({ auto slider = Slider<int>({
.value = &value, .value = &value,
@@ -125,20 +150,27 @@ TEST(SliderTest, Up) {
.max = 100, .max = 100,
.increment = 10, .increment = 10,
.direction = Direction::Up, .direction = Direction::Up,
.on_change = [&]() { updated++; },
}); });
Screen screen(1, 11); Screen screen(1, 11);
Render(screen, slider->Render()); Render(screen, slider->Render());
EXPECT_EQ(value, 50); EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(0, 3))); EXPECT_TRUE(slider->OnEvent(MousePressed(0, 3)));
EXPECT_EQ(value, 50); EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 0);
EXPECT_TRUE(slider->OnEvent(MousePressed(0, 9))); EXPECT_TRUE(slider->OnEvent(MousePressed(0, 9)));
EXPECT_EQ(value, 10); EXPECT_EQ(value, 10);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(2, 9))); EXPECT_TRUE(slider->OnEvent(MousePressed(2, 9)));
EXPECT_EQ(value, 10); EXPECT_EQ(value, 10);
EXPECT_EQ(updated, 1);
EXPECT_TRUE(slider->OnEvent(MousePressed(2, 5))); EXPECT_TRUE(slider->OnEvent(MousePressed(2, 5)));
EXPECT_EQ(value, 50); EXPECT_EQ(value, 50);
EXPECT_EQ(updated, 2);
EXPECT_TRUE(slider->OnEvent(MouseReleased(2, 5))); EXPECT_TRUE(slider->OnEvent(MouseReleased(2, 5)));
EXPECT_FALSE(slider->OnEvent(MousePressed(2, 5))); EXPECT_FALSE(slider->OnEvent(MousePressed(2, 5)));
EXPECT_EQ(value, 50);
} }
TEST(SliderTest, Focus) { TEST(SliderTest, Focus) {

View File

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

View File

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

View File

@@ -5,7 +5,6 @@
#include <ftxui/component/task.hpp> // for Task #include <ftxui/component/task.hpp> // for Task
#include <initializer_list> // for initializer_list #include <initializer_list> // for initializer_list
#include <memory> // for allocator, unique_ptr #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/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 #include "ftxui/component/receiver.hpp" // for MakeReceiver, ReceiverImpl
@@ -76,7 +75,51 @@ TEST(Event, EscapeKeyEnoughWait) {
EXPECT_FALSE(event_receiver->Receive(&received)); 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) { 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 event_receiver = MakeReceiver<Task>();
{ {
auto parser = TerminalInputParser(event_receiver->MakeSender()); 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(Mouse::Left, std::get<Event>(received).mouse().button);
EXPECT_EQ(12, std::get<Event>(received).mouse().x); EXPECT_EQ(12, std::get<Event>(received).mouse().x);
EXPECT_EQ(42, std::get<Event>(received).mouse().y); 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)); EXPECT_FALSE(event_receiver->Receive(&received));
} }
@@ -109,8 +152,7 @@ TEST(Event, MouseLeftClickReleased) {
auto parser = TerminalInputParser(event_receiver->MakeSender()); auto parser = TerminalInputParser(event_receiver->MakeSender());
parser.Add('\x1B'); parser.Add('\x1B');
parser.Add('['); parser.Add('[');
parser.Add('3'); parser.Add('0');
parser.Add('2');
parser.Add(';'); parser.Add(';');
parser.Add('1'); parser.Add('1');
parser.Add('2'); parser.Add('2');
@@ -146,7 +188,7 @@ TEST(Event, MouseReporting) {
Task received; Task received;
EXPECT_TRUE(event_receiver->Receive(&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(42, std::get<Event>(received).cursor_x());
EXPECT_EQ(12, std::get<Event>(received).cursor_y()); EXPECT_EQ(12, std::get<Event>(received).cursor_y());
EXPECT_FALSE(event_receiver->Receive(&received)); EXPECT_FALSE(event_receiver->Receive(&received));
@@ -310,8 +352,8 @@ TEST(Event, Control) {
continue; continue;
cases.push_back({char(i), false}); cases.push_back({char(i), false});
} }
cases.push_back({char(24), true}); cases.push_back({char(24), false});
cases.push_back({char(26), true}); cases.push_back({char(26), false});
cases.push_back({char(127), false}); cases.push_back({char(127), false});
for (auto test : cases) { for (auto test : cases) {
@@ -342,13 +384,11 @@ TEST(Event, Special) {
std::vector<unsigned char> input; std::vector<unsigned char> input;
Event expected; Event expected;
} kTestCase[] = { } kTestCase[] = {
// Arrow (defaut cursor mode) // Arrow (default cursor mode)
{str("\x1B[A"), Event::ArrowUp}, {str("\x1B[A"), Event::ArrowUp}, {str("\x1B[B"), Event::ArrowDown},
{str("\x1B[B"), Event::ArrowDown}, {str("\x1B[C"), Event::ArrowRight}, {str("\x1B[D"), Event::ArrowLeft},
{str("\x1B[C"), Event::ArrowRight}, {str("\x1B[H"), Event::Home}, {str("\x1B[F"), Event::End},
{str("\x1B[D"), Event::ArrowLeft}, /*
{str("\x1B[H"), Event::Home},
{str("\x1B[F"), Event::End},
// Arrow (application cursor mode) // Arrow (application cursor mode)
{str("\x1BOA"), Event::ArrowUp}, {str("\x1BOA"), Event::ArrowUp},
@@ -429,6 +469,7 @@ TEST(Event, Special) {
// Custom: // Custom:
{{0}, Event::Custom}, {{0}, Event::Custom},
*/
}; };
for (auto test : kTestCase) { 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 } // namespace ftxui
// NOLINTEND // NOLINTEND

View File

@@ -1,7 +1,6 @@
// Copyright 2021 Arthur Sonzogni. All rights reserved. // Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <vector>
#include "ftxui/component/terminal_input_parser.hpp" #include "ftxui/component/terminal_input_parser.hpp"
extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) { 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 event_receiver = MakeReceiver<Task>();
{ {
auto parser = TerminalInputParser(event_receiver->MakeSender()); 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]); parser.Add(data[i]);
}
} }
Task received; 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. 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 // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <functional> // for function #include <functional> // for function
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string, basic_string #include <string> // for string, basic_string
#include <vector> // for vector #include <vector> // for vector

View File

@@ -5,8 +5,14 @@
#include <algorithm> #include <algorithm>
#include <ftxui/component/component.hpp> #include <ftxui/component/component.hpp>
#include <ftxui/component/component_base.hpp> #include <ftxui/component/component_base.hpp>
#include <ftxui/component/component_options.hpp>
#include <ftxui/component/screen_interactive.hpp> // for ScreenInteractive #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 { namespace ftxui {
@@ -206,7 +212,7 @@ class WindowImpl : public ComponentBase, public WindowOptions {
} }
// Clamp the window size. // 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); height() = std::max<int>(height(), 2);
return true; return true;
@@ -225,8 +231,10 @@ class WindowImpl : public ComponentBase, public WindowOptions {
return true; return true;
} }
if (event.mouse().button != Mouse::Left || if (event.mouse().button != Mouse::Left) {
event.mouse().motion != Mouse::Pressed) { return true;
}
if (event.mouse().motion != Mouse::Pressed) {
return true; return true;
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,7 @@
#include <cstdint> // for uint8_t #include <cstdint> // for uint8_t
#include <cstdlib> // for abs #include <cstdlib> // for abs
#include <ftxui/screen/color.hpp> // for Color #include <ftxui/screen/color.hpp> // for Color
#include <functional> // for function
#include <map> // for map #include <map> // for map
#include <memory> // for make_shared #include <memory> // for make_shared
#include <utility> // for move, pair #include <utility> // for move, pair
@@ -17,6 +18,8 @@
#include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/requirement.hpp" // for Requirement #include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box #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/screen.hpp" // for Pixel, Screen
#include "ftxui/screen/string.hpp" // for Utf8ToGlyphs #include "ftxui/screen/string.hpp" // for Utf8ToGlyphs
#include "ftxui/util/ref.hpp" // for ConstRef #include "ftxui/util/ref.hpp" // for ConstRef
@@ -28,7 +31,7 @@ namespace {
// Base UTF8 pattern: // Base UTF8 pattern:
// 11100010 10100000 10000000 // empty // 11100010 10100000 10000000 // empty
// Pattern for the individuel dots: // Pattern for the individual dots:
// ┌──────┬───────┐ // ┌──────┬───────┐
// │dot1 │ dot4 │ // │dot1 │ dot4 │
// ├──────┼───────┤ // ├──────┼───────┤
@@ -341,7 +344,7 @@ void Canvas::DrawPointEllipse(int x1,
int dy = x * x; int dy = x * x;
int err = dx + dy; 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); 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 dy = x * x;
int err = dx + dy; int err = dx + dy;
do { do { // NOLINT
for (int xx = x1 + x; xx <= x1 - x; ++xx) { for (int xx = x1 + x; xx <= x1 - x; ++xx) {
DrawPoint(xx, y1 + y, true, s); DrawPoint(xx, y1 + y, true, s);
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 dy = x * x;
int err = dx + dy; 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); 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 dy = x * x;
int err = dx + dy; int err = dx + dy;
do { do { // NOLINT
for (int xx = x1 + x; xx <= x1 - x; ++xx) { for (int xx = x1 + x; xx <= x1 - x; ++xx) {
DrawBlock(xx, 2 * (y1 + y), true, s); DrawBlock(xx, 2 * (y1 + y), true, s);
DrawBlock(xx, 2 * (y1 - y), true, s); DrawBlock(xx, 2 * (y1 - y), true, s);
@@ -810,13 +813,49 @@ void Canvas::DrawText(int x,
continue; continue;
} }
Cell& cell = storage_[XY{x / 2, y / 4}]; Cell& cell = storage_[XY{x / 2, y / 4}];
cell.type = CellType::kText; cell.type = CellType::kCell;
cell.content.character = it; cell.content.character = it;
style(cell.content); style(cell.content);
x += 2; 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. /// @brief Modify a pixel at a given location.
/// @param style a function that modifies the pixel. /// @param style a function that modifies the pixel.
void Canvas::Style(int x, int y, const Stylizer& style) { 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 y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) { for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x, y) = Pixel(); screen.PixelAt(x, y) = Pixel();
screen.PixelAt(x, y).character = " "; // Consider the pixel written.
} }
} }
Node::Render(screen); Node::Render(screen);

View File

@@ -12,16 +12,25 @@
namespace ftxui { namespace ftxui {
namespace { namespace {
class BgColor : public NodeDecorator { class BgColor : public NodeDecorator {
public: public:
BgColor(Element child, Color color) BgColor(Element child, Color color)
: NodeDecorator(std::move(child)), color_(color) {} : NodeDecorator(std::move(child)), color_(color) {}
void Render(Screen& screen) override { void Render(Screen& screen) override {
for (int y = box_.y_min; y <= box_.y_max; ++y) { if (color_.IsOpaque()) {
for (int x = box_.x_min; x <= box_.x_max; ++x) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
screen.PixelAt(x, y).background_color = color_; 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); NodeDecorator::Render(screen);
@@ -36,9 +45,18 @@ class FgColor : public NodeDecorator {
: NodeDecorator(std::move(child)), color_(color) {} : NodeDecorator(std::move(child)), color_(color) {}
void Render(Screen& screen) override { void Render(Screen& screen) override {
for (int y = box_.y_min; y <= box_.y_max; ++y) { if (color_.IsOpaque()) {
for (int x = box_.x_min; x <= box_.x_max; ++x) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
screen.PixelAt(x, y).foreground_color = color_; 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); NodeDecorator::Render(screen);
@@ -46,6 +64,7 @@ class FgColor : public NodeDecorator {
Color color_; Color color_;
}; };
} // namespace } // namespace
/// @brief Set the foreground color of an element. /// @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 // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <algorithm> // for max #include <algorithm> // for max
#include <cstddef> // for size_t
#include <memory> // for __shared_ptr_access, shared_ptr, make_shared #include <memory> // for __shared_ptr_access, shared_ptr, make_shared
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector #include <vector>
#include "ftxui/dom/elements.hpp" // for Element, Elements, dbox #include "ftxui/dom/elements.hpp" // for Element, Elements, dbox
#include "ftxui/dom/node.hpp" // for Node, Elements #include "ftxui/dom/node.hpp" // for Node, Elements
#include "ftxui/dom/requirement.hpp" // for Requirement #include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/pixel.hpp" // for Pixel
namespace ftxui { namespace ftxui {
namespace { namespace {
class DBox : public Node { class DBox : public Node {
public: public:
explicit DBox(Elements children) : Node(std::move(children)) {} explicit DBox(Elements children) : Node(std::move(children)) {}
@@ -47,6 +49,58 @@ class DBox : public Node {
child->SetBox(box); 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 } // namespace

View File

@@ -3,7 +3,6 @@
// the LICENSE file. // the LICENSE file.
#include <memory> // for make_shared, __shared_ptr_access #include <memory> // for make_shared, __shared_ptr_access
#include <utility> // for move #include <utility> // for move
#include <vector> // for __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/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 #include "ftxui/dom/node.hpp" // for Elements, Node
@@ -92,14 +91,14 @@ class Flex : public Node {
} // namespace } // 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. /// a container.
/// @ingroup dom /// @ingroup dom
Element filler() { Element filler() {
return std::make_shared<Flex>(function_flex); 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. /// container.
/// @ingroup dom /// @ingroup dom
/// ///

View File

@@ -6,8 +6,8 @@
#include <algorithm> // for max, min #include <algorithm> // for max, min
#include <cstddef> // for size_t #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 <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 <utility> // for swap, move
#include <vector>
#include "ftxui/dom/box_helper.hpp" // for Element, Compute #include "ftxui/dom/box_helper.hpp" // for Element, Compute
@@ -331,8 +331,8 @@ void Compute3(Global& global) {
line = Line(); line = Line();
} }
block.line = lines.size(); block.line = static_cast<int>(lines.size());
block.line_position = line.blocks.size(); block.line_position = static_cast<int>(line.blocks.size());
line.blocks.push_back(&block); line.blocks.push_back(&block);
x += block.min_size_x + global.config.gap_x; x += block.min_size_x + global.config.gap_x;
} }

View File

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

View File

@@ -3,7 +3,6 @@
// the LICENSE file. // the LICENSE file.
#include <gtest/gtest.h> #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 <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" #include "ftxui/dom/flexbox_helper.hpp"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,6 @@
// the LICENSE file. // the LICENSE file.
#include <gtest/gtest.h> // for Test, EXPECT_EQ, Message, TestPartResult, TestInfo (ptr only), TEST #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 <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/elements.hpp" // for operator|, text, bgcolor, color, Element
#include "ftxui/dom/node.hpp" // for Render #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 // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <ftxui/dom/node.hpp> // for Node, Elements #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/node_decorator.hpp"
#include "ftxui/dom/requirement.hpp" // for Requirement #include "ftxui/dom/requirement.hpp" // for Requirement

View File

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

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