77 Commits

Author SHA1 Message Date
ArthurSonzogni
d301fab1f4 Release v4.0.0 2023-02-12 18:02:40 +01:00
Arthur Sonzogni
3e35f45830 Execute clang-tidy and IWYU. (#576) 2023-02-12 14:07:28 +01:00
Arthur Sonzogni
a4e70dfb93 Fix vscroll hidding the last character. (#575)
This resolve:
https://github.com/ArthurSonzogni/FTXUI/issues/574
2023-02-12 13:51:51 +01:00
Arthur Sonzogni
b2853c8f14 Fix: cursor position offset. (#562)
This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/559
2023-01-22 11:51:37 +01:00
Arthur Sonzogni
6fe8310321 Feature: strikethrough and underlinedDouble decorator. (#561)
This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/560
2023-01-22 11:02:27 +01:00
Arthur Sonzogni
350dcac032 Fix build about fuzzer. (#557)
Previous patch introduced a regression. The condition needs to be
inverted.

Fixed:https://github.com/ArthurSonzogni/FTXUI/issues/556
2023-01-17 17:36:59 +01:00
Arthur Sonzogni
65848d1e5f cmake: support gtest from the package manager (#552)
Some developers would be happier with the gtest version provided from
their package manager. Use it if it is installed the package provide
cmake support.

Fixed: https://github.com/ArthurSonzogni/FTXUI/issues/551
2023-01-14 20:37:42 +01:00
Alex
1561293140 change vcpk.info to vcpkgx.com (#553)
The vcpkg.info site is now defunct.
[vcpkgx.com](https://vcpkgx.com/) provides a similar if not identical service to what vcpkg.info used to provide.
2023-01-12 10:16:00 +01:00
Arthur Sonzogni
90dfceefcb Fix slider focus. (#549)
This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/547

From discussion:
https://github.com/ArthurSonzogni/FTXUI/discussions/546
2023-01-07 18:13:59 +01:00
Arthur Sonzogni
5410329ac6 Adding heartbeat to spinner (#548)
Co-authored-by: cyrus <cyruswang2017@gmail.com>
2023-01-06 11:05:56 +01:00
Matthias Vallentin
9f9571190a Fix crash on empty tab container. (#540) 2022-12-30 16:31:47 +01:00
Arthur Sonzogni
b56afce48c Fix blinking cursor shape. (#539) 2022-12-28 13:17:56 +01:00
Arthur Sonzogni
abd5b2a503 Fix Windows UTF16 char. (#538)
Windows output UTF16 unicode char, but FTXUI works using UTF8. Do the
conversion.

This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/495
2022-12-28 11:47:11 +01:00
Arthur Sonzogni
60b9e491db Add all the Slider implementations. (#532)
This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/524
2022-12-22 21:15:54 +01:00
重装小杰
6cea410eaa Remove punctuation that affects compilation (#531) 2022-12-22 18:00:27 +01:00
Arthur Sonzogni
a52b959f66 Fix compile with gcc. (#529)
This resolves:
- https://github.com/ArthurSonzogni/FTXUI/issues/526
- https://github.com/ArthurSonzogni/FTXUI/issues/520
2022-12-19 20:00:03 +01:00
Arthur Sonzogni
0542227ba7 Execute clang tidy and IWYU (#528) 2022-12-19 19:44:43 +01:00
Arthur Sonzogni
4dc1a9fff9 Fix mouse on support over PuTTY (#525)
This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/523
2022-12-14 22:09:25 +01:00
ArthurSonzogni
b9f51844c3 Add documentation about the hoverable component. 2022-12-04 11:59:56 +01:00
Arthur Sonzogni (slow/sick)
0d54285e19 Add the Hoverable wrapper. (#522)
This will make it easier for developers. For instance:
https://github.com/ArthurSonzogni/FTXUI/issues/521
2022-12-04 11:54:49 +01:00
mr-mocap
f21ca3aa14 Improve UNIX signal handling (#518)
There was a dead lock caused by the reentrancy of the post method.

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2022-12-01 22:56:35 +01:00
Arthur Sonzogni (slow/sick)
05f29ff3b3 Remove codecvt dependency. (#516)
This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/514
2022-11-26 20:43:09 +01:00
Fredrik Hallenberg
55b9706cfd Fix automerge at border (#515) 2022-11-25 00:39:17 +01:00
wflohry
121bd0d046 Ignoring wstring_convert deprecation warnings (#498) 2022-11-19 14:22:44 +01:00
Arthur Sonzogni (slow/sick)
2c5681ee20 Interpret 8 as 127 (#510)
This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/508

Apparently, this is a common issue:
https://www.cs.colostate.edu/~mcrob/toolbox/unix/keyboard.html
2022-11-17 22:16:25 +01:00
Arthur Sonzogni (slow/sick)
1689802349 Support multiple kind of cursor shapes. (#505)
https://github.com/ArthurSonzogni/FTXUI/issues/424
2022-11-11 14:09:53 +01:00
Arthur Sonzogni (slow/sick)
9babfea36b Fix example demo. (#506) 2022-11-11 14:07:27 +01:00
Tim Ebbeke
dfdbe1eecf Fix std::ignore is in <tuple>. (#502) 2022-10-25 15:26:12 -04:00
Arthur Sonzogni
aeaf39b8ea Fix F1-F4 keymapping. (#501)
It was just wrong, even on Linux.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/492
2022-10-18 22:58:22 +02:00
Arthur Sonzogni
0acfd8f255 Introduce Loop. (#476)
It can be used to give developers a better control on the loop. Users
can use it not to take full control of the thread, and poll FTXUI from
time to time as part of an external loop.

This resolves: https://github.com/ArthurSonzogni/FTXUI/issues/474
2022-10-18 21:29:27 +02:00
Arthur Sonzogni
26d63bc56f Fix pedantic compile issue. (#500) 2022-10-16 10:45:11 +02:00
Aleksandar Brakmić
f692a50195 Added eCAL monitor to the list of projects that are using FTXUI (#497) 2022-10-10 00:10:38 +02:00
Rosen Penev
e04ea27dcd fix cross compiling with MinGW (#499)
MinGW on Linux is case sensitive. Windows is case insensitive.

Signed-off-by: Rosen Penev <rosenp@gmail.com>

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2022-10-10 00:08:28 +02:00
Arthur Sonzogni
f4b47333be Featre: Support ctrl+arrow in input. (#494)
CTRL+LEFT: Move the cursor to the beginning of the word.
CTRL+RIGHT: Move the cursor to the beginning of the word.

This was requested by:
https://github.com/ArthurSonzogni/FTXUI/issues/490
2022-10-06 21:16:55 +02:00
Arthur Sonzogni
ccfe22bc24 Test Page{Up,Down} for Radiobox. (#493)
See: https://github.com/ArthurSonzogni/FTXUI/pull/491#issuecomment-1264335576
2022-10-01 20:24:16 +02:00
jdfa
5ba29a9539 #487 Handled Space and Enter events for Radiobox (#491)
Return true when an event a RadioBox is state change due to pressing Return or space.

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2022-10-01 13:34:15 +02:00
Arthur Sonzogni
c61fadd8ec Update to unicode 13 standard. (#484)
It contains additional full width character and combining characters.

This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/483
2022-09-29 10:50:27 +02:00
Lobanova Valeriia
b3aad183da FIX: minor mistake (#482) 2022-09-25 22:00:12 +02:00
Arthur Sonzogni
fab74f745d Execute clang tidy. (#477) 2022-09-05 20:56:41 +02:00
Arthur Sonzogni
c8ec151154 Bring back C++17 minimal requirement. (#475) 2022-09-03 13:03:04 +02:00
Arthur Sonzogni
1d76a2321c Update codecov.yml (#473)
This avoids being spammed by codecov.
2022-08-30 21:19:32 +02:00
Arthur Sonzogni
1661a5e83d Implement ButtonOption::Border() (#472)
It was missing. See:
https://github.com/ArthurSonzogni/FTXUI/issues/471
2022-08-30 19:03:14 +02:00
Arthur Sonzogni
b3ba747d82 Feature: Slider in any directions. (#468)
Add the `SliderOption` option supporting:
```cpp
{
  Ref<T> value;
  ConstRef<T> min = T(0);
  ConstRef<T> max = T(100);
  ConstRef<T> increment = (max() - min()) / 20;
  GaugeDirection direction = GaugeDirection::Right;
  Color color_active = Color::White;
  Color color_inactive = Color::GrayDark;
};
```

In particular, this supports multiple direction. This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/467

This one do not support adding a label. The old constructors can still
be used to have a label.
2022-08-30 18:52:33 +02:00
Arthur Sonzogni
8226c5aea7 Fix clang-tidy. (#469) 2022-08-28 21:30:01 +02:00
Arthur Sonzogni
1e381fcad6 Add codecov.yml (#470) 2022-08-28 21:29:48 +02:00
Jan Sende
d04e04adc6 Fixed typo (#465) 2022-08-24 12:00:54 +02:00
Arthur Sonzogni
ec994a4e65 Add support for emscripten screen resize. (#463)
This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/432
2022-08-21 23:04:32 +02:00
Arthur Sonzogni
3ec765e1f0 Menu: keep the previously focused element with mouse. (#462)
This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/453
2022-08-21 17:23:13 +02:00
Ian J
251306a4bb Fixed divide by zero segault (#461)
Check if inner_size is less than 1 to prevent divide by 0 segfault.

Co-authored-by: Arthur Sonzogni <sonzogniarthur@gmail.com>
2022-08-19 19:43:55 +02:00
Jan Sende
7cc68cfbd0 Fixed dim and bold not mixing well (#460)
One single reset code controls both the dim and bold properties. Mixing both led to one of the properties being wrongly reset.

Co-authored-by: Arthur Sonzogni <sonzogniarthur@gmail.com>
2022-08-19 19:03:56 +02:00
Arthur Sonzogni
36460fea2a Switch to codecov v3 (#458) 2022-08-15 15:00:28 +02:00
Arthur Sonzogni
d755356481 Add ref for sliders. (#457)
This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/456
2022-08-13 16:26:53 +02:00
Arthur Sonzogni
f461050759 Run IWYU (#450) 2022-08-07 14:44:33 +02:00
Julien Marrec
e42ab7b2e6 Typo in iwyu.cmake (#447) 2022-08-07 12:15:52 +02:00
Arthur Sonzogni
d9241435ce Fix link to TermBreaker. 2022-07-15 17:59:13 +02:00
badlydrawnrod
d5044bdaaf Fix #441 Canvas::DrawText() (#442)
Fix for Canvas::DrawText() draws nothing if the start coordinate is out
of bounds.
2022-07-10 22:14:32 +02:00
ArthurSonzogni
f91677e79f Update links toward examples. 2022-07-09 20:47:18 +02:00
ArthurSonzogni
d6da30a518 Add COOP/COEP for worker.js file. 2022-07-09 19:58:11 +02:00
ArthurSonzogni
d5b1899290 Fix workflow for documentation. 2022-07-09 19:15:17 +02:00
Hunter Zolomon
d545fb6f19 Fix README link reference. (#440) 2022-07-09 18:57:30 +02:00
ArthurSonzogni
f80e20c4aa Build documents and examples on master. 2022-07-09 18:51:35 +02:00
Hunter Zolomon
d805eb0648 Improved Introductory Tutorial Page (#433)
This PR solves #434 by improving on the existing introductory tutorial through adjustment of the grammar and vocabulary of the sentences, rewriting of sentences, writing new descriptions for sub-headings, and adding new link references for the corresponding discussion elements.

I have not replaced the inline script ascii calls yet, and there might be a few other things that might require some adjustment. However, this version looks much cleaner, consistent, and descriptive that the previous iteration.

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2022-07-09 17:53:36 +02:00
倔强的贝吉塔
940f805b8f Add StartUp into the project list (#439) 2022-07-09 16:16:05 +02:00
Arthur Sonzogni
57da24dfdb Fix homescreen example thread safety. (#431)
This addresses and fix:
https://github.com/ArthurSonzogni/FTXUI/issues/430

This patchs makes |shift| to be read and written on the same thread. We
request the update to be made via a task posted on the main thread.

This patch has no real consequence, the previous behavior was fine.
I hope it will help users not to have thread safety issue and better
understand they can post tasks this way.
2022-07-04 23:34:37 +02:00
Jason Turner
0abaab6268 Update tag from v2.0.0 to v3.0.0 for cmake fetch (#429) 2022-07-01 09:15:58 +02:00
Levon
ef0f1da147 README: game_jam DisarmSelfDestruct link fixed (#425) 2022-06-26 22:52:32 +02:00
DanArmor
137f1fbf67 Add turing_cmd into the project list (#422) 2022-06-14 21:47:39 +02:00
AMS21
094d8d9d0a Fix border charset array being to large (#421)
This for some reason caused the clang compiler to crash, while also
being incorrect as the tables are actually only 5x6.

See the LLVM issue here:
https://github.com/llvm/llvm-project/issues/56016
2022-06-14 21:03:03 +02:00
Arthur Sonzogni
81e086788d Avoid making new allocation to clear the screen. (#420)
Previously, a new 2D vector was allocated for every new frame. This
caused a lot of temporary allocation to be made.

This patch modify "Screen::Clear" so that it do make a new allocation,
but clear the existing one instead.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/290#issuecomment-1153327251
2022-06-13 21:49:36 +02:00
Arthur Sonzogni
925a7578d4 Feature: the Modal component. (#418) 2022-06-12 17:08:22 +02:00
Vebjørn Johansen Rognli
bb3231695f Set includes as system interface to suppress warnings for other users (#415)
Set includes as system interface to suppress warnings for other users

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2022-06-11 18:39:07 +02:00
Jian De
0a522488a7 Add beagle-config into the project list (#412) 2022-06-05 18:07:20 +02:00
Jhon Adams
b63aa9e375 Swap incorrect width/height mapping (#409)
width and height were being set using the incorrect axes resulting in incorrect canvas dimensions

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2022-06-01 21:13:39 +02:00
Arthur Sonzogni
ed5b4cec49 Fix: Forward gridbox selected box. (#408)
This was discovered in:
https://github.com/ArthurSonzogni/FTXUI/issues/407
2022-05-28 22:35:52 +02:00
Amin Yahyaabadi
219daf46ff ci: use gcovr 5.0 temporarily (#406) 2022-05-23 17:34:07 +02:00
Arthur Sonzogni
11519ef1c6 Fix focus vs flexbox interaction. (#405)
- Fix focus in flexbox. This required resetting the focus state at the
  beginning of the ComputeRequirement(), because it can now run several
  times.

  This resolves:https://github.com/ArthurSonzogni/FTXUI/issues/399

- Add Box::Union.

- Add a preliminary implementation of forwarding selected_box from
  within the flexbox.
2022-05-22 21:41:29 +02:00
Conner
f9256fa132 Fix PostEvent() segfault (#403)
Fix segfault when PostEvent() called on inactive screen.

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2022-05-22 15:37:27 +02:00
142 changed files with 5744 additions and 1502 deletions

View File

@@ -8,14 +8,16 @@ Checks: "*,
-llvm*,
-modernize-use-trailing-return-type,
-zircon-*,
-readability-else-after-return,
-readability-static-accessed-through-instance,
-readability-avoid-const-params-in-decls,
-bugprone-easily-swappable-parameters,
-cppcoreguidelines-non-private-member-variables-in-classes,
-misc-no-recursion,
-misc-non-private-member-variables-in-classes,
-modernize-use-nodiscard,
-misc-no-recursion,
-readability-avoid-const-params-in-decls,
-readability-else-after-return,
-readability-identifier-length,
-readability-implicit-bool-conversion,
-readability-static-accessed-through-instance,
"
WarningsAsErrors: ''
HeaderFilterRegex: ''

View File

@@ -51,7 +51,7 @@ jobs:
ninja: true
clangtidy: true
cppcheck: false
gcovr: true
gcovr: "5.0"
opencppcoverage: true
# make sure coverage is only enabled for Debug builds, since it sets -O0
@@ -106,7 +106,7 @@ jobs:
ctest -C Debug --rerun-failed --output-on-failure;
- name: Publish to codecov
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v3
with:
flags: ${{ runner.os }}
name: ${{ runner.os }}-coverage
@@ -169,7 +169,7 @@ jobs:
overwrite: true
documentation:
needs: package
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
steps:
- name: "Checkout repository"
@@ -190,9 +190,23 @@ jobs:
run: >
mkdir build;
cd build;
emcmake cmake ..;
emcmake cmake ..
-DCMAKE_BUILD_TYPE=Release
-DFTXUI_BUILD_DOCS=ON
-DFTXUI_BUILD_EXAMPLES=ON
-DFTXUI_BUILD_TESTS=OFF
-DFTXUI_BUILD_TESTS_FUZZER=OFF
-DFTXUI_ENABLE_INSTALL=OFF;
cmake --build . --target doc;
rsync -amv --include='*/' --include='*.html' --include='*.js' --include='*.wasm' --exclude='*' examples doc/doxygen/html;
cmake --build . ;
rsync -amv
--include='*/'
--include='*.html'
--include='*.js'
--include='*.wasm'
--exclude='*'
examples
doc/doxygen/html;
- name: "Deploy"
uses: peaceiris/actions-gh-pages@v3

View File

@@ -4,6 +4,58 @@ Changelog
current (development)
---------------------
4.0.0
-----
### DOM
- Feature: more styles:
- `strikethrough`
- `underlinedDouble`
- Feature: Customize the cursor. Add the following decorators:
- `focusCursorBlock`
- `focusCursorBlockBlinking`
- `focusCursorBar`
- `focusCursorBarBlinking`
- `focusCursorUnderline`
- `focusCursorUnderlineBlinking`
- Bugfix: Fix `focus`/`select` when the `vbox`/`hbox`/`dbox` contains a
`flexbox`
- Bugfix: Fix the selected/focused area. It used to be 1 cell larger/longer than
requested
- Bugfix: Forward the selected/focused area from the child in gridbox.
- Bugfix: Fix incorrect Canvas computed dimensions.
- Bugfix: Support `vscroll_indicator` with a zero inner size.
- Bugfix: Fix `vscroll_indicator` hidding the last column.
### Component:
- Feature: Add the `Modal` component.
- Feature: `Slider` supports taking references for all its arguments.
- Feature: `Slider` supports `SliderOption`. It supports:
- multiple directions.
- multiple colors.
- various values (value, min, max, increment).
- Feature: Define `ScreenInteractive::Exit()`.
- Feature: Add `Loop` to give developers a better control on the main loop. This
can be used to integrate FTXUI into another main loop, without taking the full
control.
- Feature: `Input` supports CTRL+Left and CTRL+Right
- Feature: Use a blinking bar in the `Input` component.
- Improvement: The `Menu` keeps the focus when an entry is selected with the
mouse.
- Bugfix: Add implementation of `ButtonOption::Border()`. It was missing.
- Bugfix: Provide the correct key for F1-F4 and F11.
- Feature: Add the `Hoverable` component decorators.
### Screen
- Feature: add `Box::Union(a,b) -> Box`
- Bugfix: Fix resetting `dim` clashing with resetting of `bold`.
- Feature: Add emscripten screen resize support.
- Bugfix: Add unicode 13 support for full width characters.
- Bugfix: Fix MSVC treating codecvt C++17 deprecated function as an error.
### Build
- Support using the google test version provided by the package manager.
3.0.0
-----

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.11)
project(ftxui
LANGUAGES CXX
VERSION 3.0.0
VERSION 4.0.0
)
option(FTXUI_BUILD_DOCS "Set to ON to build docs" ON)
@@ -79,9 +79,11 @@ add_library(dom
src/ftxui/dom/separator.cpp
src/ftxui/dom/size.cpp
src/ftxui/dom/spinner.cpp
src/ftxui/dom/strikethrough.cpp
src/ftxui/dom/table.cpp
src/ftxui/dom/text.cpp
src/ftxui/dom/underlined.cpp
src/ftxui/dom/underlined_double.cpp
src/ftxui/dom/util.cpp
src/ftxui/dom/vbox.cpp
)
@@ -93,6 +95,7 @@ add_library(component
include/ftxui/component/component_base.hpp
include/ftxui/component/component_options.hpp
include/ftxui/component/event.hpp
include/ftxui/component/loop.hpp
include/ftxui/component/mouse.hpp
include/ftxui/component/receiver.hpp
include/ftxui/component/screen_interactive.hpp
@@ -107,9 +110,12 @@ add_library(component
src/ftxui/component/container.cpp
src/ftxui/component/dropdown.cpp
src/ftxui/component/event.cpp
src/ftxui/component/hoverable.cpp
src/ftxui/component/input.cpp
src/ftxui/component/loop.cpp
src/ftxui/component/maybe.cpp
src/ftxui/component/menu.cpp
src/ftxui/component/modal.cpp
src/ftxui/component/radiobox.cpp
src/ftxui/component/radiobox.cpp
src/ftxui/component/renderer.cpp
@@ -125,15 +131,16 @@ target_link_libraries(dom
PUBLIC screen
)
find_package(Threads)
target_link_libraries(component
PUBLIC dom
PUBLIC Threads::Threads
)
set_target_properties(screen PROPERTIES VERSION ${PROJECT_VERSION})
set_target_properties(dom PROPERTIES VERSION ${PROJECT_VERSION})
set_target_properties(component PROPERTIES VERSION ${PROJECT_VERSION})
if (NOT EMSCRIPTEN)
find_package(Threads)
target_link_libraries(component
PUBLIC Threads::Threads
)
endif()
include(cmake/ftxui_set_options.cmake)
ftxui_set_options(screen)
@@ -145,22 +152,13 @@ ftxui_check_coverage(screen)
ftxui_check_coverage(dom)
ftxui_check_coverage(component)
if (FTXUI_BUILD_TESTS AND ${CMAKE_VERSION} VERSION_GREATER "3.11.4")
include(cmake/ftxui_test.cmake)
endif()
if(FTXUI_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
if(FTXUI_BUILD_DOCS)
add_subdirectory(doc)
endif()
include(cmake/ftxui_test.cmake)
include(cmake/ftxui_benchmark.cmake)
include(cmake/ftxui_fuzzer.cmake)
include(cmake/iwyu.cmake)
include(cmake/ftxui_export.cmake)
include(cmake/ftxui_install.cmake)
include(cmake/ftxui_package.cmake)
if(FTXUI_ENABLE_INSTALL)
include(cmake/ftxui_install.cmake)
include(cmake/ftxui_package.cmake)
endif()
add_subdirectory(examples)
add_subdirectory(doc)

View File

@@ -35,8 +35,8 @@ A simple C++ library for terminal based user interfaces!
* Simple and elegant syntax (in my opinion)
* Keyboard & mouse navigation.
* Support for [UTF8](https://en.wikipedia.org/wiki/UTF-8) and [fullwidth chars](https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms) (→ 测试)
* Support for animations. [Demo 1](https://arthursonzogni.com/FTXUI/examples/?file=component/menu_underline_animated_gallery), [Demo 2](https://arthursonzogni.com/FTXUI/examples/?file=component/button_style)
* Support for drawing. [Demo](https://arthursonzogni.com/FTXUI/examples/?file=component/canvas_animated)
* Support for animations. [Demo 1](https://arthursonzogni.github.io/FTXUI/examples/?file=component/menu_underline_animated_gallery), [Demo 2](https://arthursonzogni.github.io/FTXUI/examples/?file=component/button_style)
* Support for drawing. [Demo](https://arthursonzogni.github.io/FTXUI/examples/?file=component/canvas_animated)
* No dependencies
* Cross platform: Linux/MacOS (main target), WebAssembly, Windows (Thanks to contributors!).
* Learn by [examples](#documentation), and [tutorials](#documentation)
@@ -47,8 +47,8 @@ A simple C++ library for terminal based user interfaces!
- [Starter example project](https://github.com/ArthurSonzogni/ftxui-starter)
- [Documentation](https://arthursonzogni.github.io/FTXUI/)
- [Examples (WebAssembly)](https://arthursonzogni.com/FTXUI/examples/)
- [Build using CMake](https://github.com/ArthurSonzogni/FTXUI/blob/master/doc/mainpage.md#using-cmake)
- [Examples (WebAssembly)](https://arthursonzogni.github.io/FTXUI/examples/)
- [Build using CMake](https://arthursonzogni.github.io/FTXUI/#build-cmake)
## Operating systems
@@ -111,7 +111,7 @@ Element can become flexible using the the `flex` decorator.
![image](https://user-images.githubusercontent.com/4759106/147243064-780ac7cc-605b-475f-94b8-cf7c4aed03a5.png)
[See](https://arthursonzogni.github.io/FTXUI/examples_2dom_2hflow_8cpp-example.html) also this [demo](https://arthursonzogni.com/FTXUI/examples/?file=component/flexbox).
[See](https://arthursonzogni.github.io/FTXUI/examples_2dom_2hflow_8cpp-example.html) also this [demo](https://arthursonzogni.github.io/FTXUI/examples/?file=component/flexbox).
</details>
@@ -122,7 +122,9 @@ An element can be decorated using the functions:
- `dim`
- `inverted`
- `underlined`
- `underlinedDouble`
- `blink`
- `strikethrough`
- `color`
- `bgcolor`
@@ -212,7 +214,7 @@ Prebuilt components are declared in [<ftxui/component/component.hpp>](https://ar
<details><summary>Gallery</summary>
[Gallery](https://arthursonzogni.github.io/FTXUI/examples_2component_2gallery_8cpp-example.html) of multiple components. ([demo](https://arthursonzogni.com/FTXUI/examples/?file=component/gallery))
[Gallery](https://arthursonzogni.github.io/FTXUI/examples_2component_2gallery_8cpp-example.html) of multiple components. ([demo](https://arthursonzogni.github.io/FTXUI/examples/?file=component/gallery))
![image](https://user-images.githubusercontent.com/4759106/147247330-b60beb9f-e665-48b4-81c0-4b01ee95bc66.png)
@@ -324,6 +326,10 @@ Feel free to add your projects here:
- [tabdeeli](https://github.com/typon/tabdeeli)
- [tiles](https://github.com/tusharpm/tiles)
- [cachyos-cli-installer](https://github.com/cachyos/new-cli-installer)
- [beagle-config](https://github.com/SAtacker/beagle-config)
- [turing_cmd](https://github.com/DanArmor/turing_cmd)
- [StartUp](https://github.com/StubbornVegeta/StartUp)
- [eCAL monitor](https://github.com/eclipse-ecal/ecal)
## [cpp-best-practices/game_jam](https://github.com/cpp-best-practices/game_jam)
@@ -335,7 +341,7 @@ Several games using the FTXUI have been made during the Game Jam:
- [DanteO](https://github.com/cpp-best-practices/game_jam/blob/main/Jam1_April_2022/danteo.md)
- [Sumo](https://github.com/cpp-best-practices/game_jam/blob/main/Jam1_April_2022/sumo.md)
- [Drag Me aROUND](https://github.com/cpp-best-practices/game_jam/blob/main/Jam1_April_2022/drag_me_around.md)
- [DisarmSelfDestruct](https://github.com/cpp-best-practices/game_jam/blob/main/Jam1_April_2022/LightsRound.v.0.1.0.md)
- [DisarmSelfDestruct](https://github.com/cpp-best-practices/game_jam/blob/main/Jam1_April_2022/DisarmSelfDestruct.md)
- [TheWorld](https://github.com/cpp-best-practices/game_jam/blob/main/Jam1_April_2022/TheWorld.md)
- [smoothlife](https://github.com/cpp-best-practices/game_jam/blob/main/Jam1_April_2022/smoothlife.md)
- [Consu](https://github.com/cpp-best-practices/game_jam/blob/main/Jam1_April_2022/consu.md)
@@ -349,7 +355,7 @@ include(FetchContent)
FetchContent_Declare(ftxui
GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui
GIT_TAG v2.0.0
GIT_TAG v3.0.0
)
FetchContent_GetProperties(ftxui)
@@ -360,7 +366,7 @@ endif()
```
If you don't, the following packages have been created:
- [vcpkg](https://vcpkg.info/port/ftxui)
- [vcpkg](https://vcpkgx.com/details.html?package=ftxui)
- [Arch Linux PKGBUILD](https://aur.archlinux.org/packages/ftxui-git/).
- [conan.io](https://conan.io/center/ftxui)

View File

@@ -1,31 +1,21 @@
if (NOT WIN32)
FetchContent_Declare(googlebenchmark
GIT_REPOSITORY "https://github.com/google/benchmark"
GIT_TAG 62937f91b5c763a8e119d0c20c67b87bde8eff1c
GIT_PROGRESS TRUE
if (NOT FTXUI_BUILD_TESTS OR
NOT ${CMAKE_VERSION} VERSION_GREATER "3.11.4" OR
WIN32
)
return()
endif()
FetchContent_GetProperties(googlebenchmark)
set (BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE INTERNAL "")
set (BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "")
if(NOT googlebenchmark_POPULATED)
FetchContent_Populate(googlebenchmark)
add_subdirectory(
${googlebenchmark_SOURCE_DIR}
${googlebenchmark_BINARY_DIR}
EXCLUDE_FROM_ALL
)
endif()
include(cmake/ftxui_find_google_benchmark.cmake)
add_executable(ftxui_benchmark
add_executable(ftxui-benchmark
src/ftxui/dom/benchmark_test.cpp
)
target_link_libraries(ftxui_benchmark
ftxui_set_options(ftxui-benchmark)
target_link_libraries(ftxui-benchmark
PRIVATE dom
PRIVATE benchmark::benchmark
PRIVATE benchmark::benchmark_main
)
target_include_directories(ftxui_benchmark
target_include_directories(ftxui-benchmark
PRIVATE src
)
endif()

View File

@@ -0,0 +1,32 @@
# Some developers would be happier with the google benchmark version provided
# from their package manager. Use it if it is installed the package provide
# cmake support.
# https://github.com/ArthurSonzogni/FTXUI/issues/551
find_package(benchmark QUIET)
if (benchmark_FOUND)
return()
endif()
option(FETCHCONTENT_UPDATES_DISCONNECTED TRUE)
option(FETCHCONTENT_QUIET FALSE)
include(FetchContent)
FetchContent_Declare(googlebenchmark
GIT_REPOSITORY "https://github.com/google/benchmark"
GIT_TAG 62937f91b5c763a8e119d0c20c67b87bde8eff1c
GIT_PROGRESS TRUE
)
FetchContent_GetProperties(googlebenchmark)
set (BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE INTERNAL "")
set (BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "")
if(googlebenchmark_POPULATED)
return()
endif()
FetchContent_Populate(googlebenchmark)
add_subdirectory(
${googlebenchmark_SOURCE_DIR}
${googlebenchmark_BINARY_DIR}
EXCLUDE_FROM_ALL
)

View File

@@ -0,0 +1,33 @@
# Some developers would be happier with the gtest version provided from their
# package manager. Use it if it is installed the package provide cmake support.
# https://github.com/ArthurSonzogni/FTXUI/issues/551
find_package(GTest QUIET)
if (GTest_FOUND)
return()
endif()
option(FETCHCONTENT_UPDATES_DISCONNECTED TRUE)
option(FETCHCONTENT_QUIET FALSE)
include(FetchContent)
FetchContent_Declare(googletest
GIT_REPOSITORY "https://github.com/google/googletest"
GIT_TAG 23ef29555ef4789f555f1ba8c51b4c52975f0907
GIT_PROGRESS TRUE
)
FetchContent_GetProperties(googletest)
if(googletest_POPULATED)
return()
endif()
FetchContent_Populate(googletest)
set(BUILD_GMOCK OFF CACHE INTERNAL "")
set(INSTALL_GTEST OFF CACHE INTERNAL "")
set(gtest_force_shared_crt ON CACHE INTERNAL "")
add_subdirectory(
${googletest_SOURCE_DIR}
${googletest_BINARY_DIR}
EXCLUDE_FROM_ALL
)

View File

@@ -1,3 +1,7 @@
if (NOT FTXUI_BUILD_TESTS_FUZZER)
return()
endif()
set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)

View File

@@ -1,3 +1,7 @@
if(NOT FTXUI_ENABLE_INSTALL)
return()
endif()
include(GNUInstallDirs)
install(TARGETS screen dom component
EXPORT ftxui-export

View File

@@ -1,3 +1,7 @@
if(NOT FTXUI_ENABLE_INSTALL)
return()
endif()
if (UNIX AND NOT APPLE)
set(CPACK_GENERATOR "DEB;External;RPM;STGZ;TBZ2;TGZ;TXZ;TZ;TZST;ZIP")
elseif (UNIX AND APPLE)

View File

@@ -1,30 +1,53 @@
find_program( CLANG_TIDY_EXE NAMES "clang-tidy" DOC "Path to clang-tidy executable" )
find_program(CLANG_TIDY_EXE NAMES "clang-tidy" DOC "Path to clang-tidy executable" )
if(NOT CLANG_TIDY_EXE)
message(STATUS "clang-tidy not found.")
else()
message(STATUS "clang-tidy found: ${CLANG_TIDY_EXE}")
endif()
function(ftxui_set_options library)
message(STATUS "ftxui_set_options " ${library})
set_target_properties(${library} PROPERTIES VERSION ${PROJECT_VERSION})
if (NOT ${library} MATCHES "ftxui-*")
set_target_properties(${library} PROPERTIES OUTPUT_NAME "ftxui-${library}")
endif()
if(CLANG_TIDY_EXE AND FTXUI_CLANG_TIDY)
set_target_properties(${library}
PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_EXE};-warnings-as-errors=*"
)
endif()
# By using "PUBLIC" as opposed to "SYSTEM INTERFACE", the compiler warning
# are enforced on the headers. This is behind "FTXUI_CLANG_TIDY", so that it
# applies only when developing FTXUI and on the CI. User's of the library
# get only the SYSTEM INTERFACE instead.
target_include_directories(${library}
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
else()
target_include_directories(${library} SYSTEM
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
endif()
target_include_directories(${library} SYSTEM
INTERFACE
$<INSTALL_INTERFACE:include>
)
target_include_directories(${library}
PRIVATE
src
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
# C++17 is used. We require fold expression at least.
target_compile_features(${library} PUBLIC cxx_std_17)
set_target_properties(${library} PROPERTIES
CXX_STANDARD 17
CXX_EXTENSIONS OFF
)
# Force Microsoft Visual Studio to decode sources files in UTF-8. This applies
# to the library and the library users.
@@ -60,9 +83,7 @@ function(ftxui_set_options library)
endfunction()
if (EMSCRIPTEN)
#string(APPEND CMAKE_CXX_FLAGS " -s ASSERTIONS=1")
string(APPEND CMAKE_CXX_FLAGS " -s USE_PTHREADS")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -s ASYNCIFY")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -s PROXY_TO_PTHREAD")
endif()

View File

@@ -1,40 +1,28 @@
enable_testing()
option(FETCHCONTENT_UPDATES_DISCONNECTED TRUE)
option(FETCHCONTENT_QUIET FALSE)
include(FetchContent)
FetchContent_Declare(googletest
GIT_REPOSITORY "https://github.com/google/googletest"
GIT_TAG 23ef29555ef4789f555f1ba8c51b4c52975f0907
GIT_PROGRESS TRUE
)
FetchContent_GetProperties(googletest)
if(NOT googletest_POPULATED)
FetchContent_Populate(googletest)
set(BUILD_GMOCK OFF CACHE INTERNAL "")
set(INSTALL_GTEST OFF CACHE INTERNAL "")
set(gtest_force_shared_crt ON CACHE INTERNAL "")
add_subdirectory(
${googletest_SOURCE_DIR}
${googletest_BINARY_DIR}
EXCLUDE_FROM_ALL
)
if (NOT FTXUI_BUILD_TESTS OR
NOT ${CMAKE_VERSION} VERSION_GREATER "3.11.4")
return()
endif()
add_executable(tests
enable_testing()
include(cmake/ftxui_find_google_test.cmake)
add_executable(ftxui-tests
src/ftxui/component/animation_test.cpp
src/ftxui/component/button_test.cpp
src/ftxui/component/collapsible_test.cpp
src/ftxui/component/component_test.cpp
src/ftxui/component/component_test.cpp
src/ftxui/component/container_test.cpp
src/ftxui/component/hoverable_test.cpp
src/ftxui/component/input_test.cpp
src/ftxui/component/menu_test.cpp
src/ftxui/component/modal_test.cpp
src/ftxui/component/radiobox_test.cpp
src/ftxui/component/receiver_test.cpp
src/ftxui/component/resizable_split_test.cpp
src/ftxui/component/screen_interactive_test.cpp
src/ftxui/component/slider_test.cpp
src/ftxui/component/terminal_input_parser_test.cpp
src/ftxui/component/toggle_test.cpp
src/ftxui/dom/blink_test.cpp
@@ -60,24 +48,18 @@ add_executable(tests
src/ftxui/screen/string_test.cpp
)
target_link_libraries(tests
target_link_libraries(ftxui-tests
PRIVATE component
PRIVATE gtest
PRIVATE gtest_main
PRIVATE GTest::gtest
PRIVATE GTest::gtest_main
)
target_include_directories(tests
target_include_directories(ftxui-tests
PRIVATE src
)
ftxui_set_options(tests)
ftxui_set_options(ftxui-tests)
target_compile_features(ftxui-tests PUBLIC cxx_std_20)
include(GoogleTest)
gtest_discover_tests(tests
gtest_discover_tests(ftxui-tests
DISCOVERY_TIMEOUT 600
)
include(cmake/ftxui_benchmark.cmake)
if (FTXUI_BUILD_TESTS_FUZZER)
include(cmake/ftxui_fuzzer.cmake)
endif()

View File

@@ -2,6 +2,6 @@ find_program(iwyu_path NAMES include-what-you-use iwyu)
if(iwyu_path)
set_property(TARGET ${lib}
PROPERTY ${iwyu_path} -Xiwyu
--mapping_file ${CMAKE_CURRENT_SOURCE_DIR}/iwyu.impl
--mapping_file ${CMAKE_CURRENT_SOURCE_DIR}/iwyu.imp
)
endif()

12
codecov.yml Normal file
View File

@@ -0,0 +1,12 @@
codecov:
require_ci_to_pass: no
notify:
after_n_builds: 4
wait_for_ci: yes
coverage:
precision: 2
round: down
range: "50...100"
comment: false

View File

@@ -1,22 +1,27 @@
if(NOT FTXUI_BUILD_DOCS)
return()
endif()
find_package(Doxygen)
if (DOXYGEN_FOUND)
# Generate example list for documentation
set(EXAMPLE_LIST "${CMAKE_CURRENT_BINARY_DIR}/example_list.md")
file(WRITE ${EXAMPLE_LIST} "# Examples")
get_property(EXAMPLES GLOBAL PROPERTY FTXUI::EXAMPLES)
foreach(EXAMPLE IN LISTS EXAMPLES)
if (NOT DOXYGEN_FOUND)
message("Doxygen need to be installed to generate the doxygen documentation")
return()
endif()
# Generate example list for documentation
set(EXAMPLE_LIST "${CMAKE_CURRENT_BINARY_DIR}/example_list.md")
file(WRITE ${EXAMPLE_LIST} "# Examples")
get_property(EXAMPLES GLOBAL PROPERTY FTXUI::EXAMPLES)
foreach(EXAMPLE IN LISTS EXAMPLES)
file(APPEND ${EXAMPLE_LIST} "\n@example examples/${EXAMPLE}.cpp")
endforeach(EXAMPLE IN LISTS EXAMPLES)
endforeach(EXAMPLE IN LISTS EXAMPLES)
configure_file(Doxyfile.in Doxyfile @ONLY)
configure_file(Doxyfile.in Doxyfile @ONLY)
# note the option ALL which allows to build the docs together with the application
add_custom_target(doc
# note the option ALL which allows to build the docs together with the application
add_custom_target(doc
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen"
VERBATIM
)
else (DOXYGEN_FOUND)
message("Doxygen need to be installed to generate the doxygen documentation")
endif (DOXYGEN_FOUND)

View File

@@ -178,11 +178,12 @@ div.headertitle {
}
div.fragment {
padding: 16px;
background-color: #f3f3f3;
border: 0 solid;
transition: all 0.5s ease-in-out;
border-radius:5px;
border: 0 solid;
border: none;
padding:16px;
transition: all 0.5s ease-in-out;
}
div.fragment:hover {
@@ -192,8 +193,9 @@ div.fragment:hover {
div.line {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 85%;
line-height: 1.45;
font-size: 90%;
font-variant-numeric: tabular-nums lining-nums;
font-kerning: none;
-webkit-transition-duration: 0;
-moz-transition-duration: 0;
-ms-transition-duration: 0;
@@ -206,15 +208,6 @@ div.line.glow {
box-shadow: none;
}
pre.fragment {
border: 0 solid #C4CFE5;
padding: 16px;
background-color: #f3f3f3;
font-size: 85%;
line-height: 1.45;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
}
/* @group Code Colorization */
span.keyword {
color: #808000

View File

@@ -4,8 +4,8 @@
Welcome to the FTXUI documentation!
This is a brief tutorial. You are also encouraged to learn, by reading the
[examples](./examples.html)
This is a brief tutorial. You are also encouraged to self-learn by reading the
[examples](./examples.html).
@tableofcontents
@@ -45,17 +45,19 @@ int main(void) {
**output**
```bash
┌────┐┌─────────────────────────────────────────────────────────────────┐┌─────┐
┌────┐┌────────────────────────────────────┐┌─────┐
│left││middle ││right│
└────┘└─────────────────────────────────────────────────────────────────┘└─────┘
└────┘└────────────────────────────────────┘└─────┘
```
# Build {#build}
## Using CMake {#build-cmake}
This is an example configuration for your **CMakeLists.txt**
CMakeLists.txt
~~~cmake
```cmake
cmake_minimum_required (VERSION 3.11)
# --- Fetch FTXUI --------------------------------------------------------------
@@ -89,38 +91,43 @@ target_link_libraries(ftxui-starter
PRIVATE ftxui::component # Not needed for this example.
)
~~~
```
Build
~~~sh
Subsequently, you build the project in the standard fashion as follows:
```bash
mkdir build && cd build
cmake ..
make
./main
~~~
```
# List of modules. {#modules}
The project is made from into 3 modules:
The project is comprised of 3 modules:
1. **ftxui/screen** defines a `ftxui::Screen`, this is a grid of `ftxui::Pixel`.
1. **ftxui/screen** defines a `ftxui::Screen`, a grid of `ftxui::Pixel`.
2. **ftxui/dom** is the main module. It defines a hierarchical set of
`ftxui::Element`. An element draws something on the `ftxui::Screen`. It is
responsive to the size of its container.
3. **ftxui/component** The part is only needed if you need to respond to the
User input. It defines a set of `ftxui::Component`. The use can navigates
3. **ftxui/component** The module is required if your program needs to respond
to user input. It defines a set of `ftxui::Component`. These components can
be utilized to navigate the program by using the arrow keys and interacting
with widgets such as the checkbox. You can also make you own components.
The use can navigates
using the arrow keys and interact with widgets like checkbox/inputbox/... You
can make you own components.
# screen {#module-screen}
It defines a `ftxui::Screen`. This is a grid of `ftxui::Pixel`. A Pixel
represent a Unicode character and its associated style (bold, colors, etc...).
The screen can be printed as a string using `ftxui::Screen::ToString()`.
This is the visual element of the program. It defines a `ftxui::Screen`, which
is a grid of `ftxui::Pixel`. A Pixel represents a Unicode character and its
associated style (bold, colors, etc.). The screen can be printed as a string
using `ftxui::Screen::ToString()`. The following example highlights this
process:
~~~cpp
```cpp
#include <ftxui/screen/screen.hpp>
#include <iostream>
@@ -136,12 +143,14 @@ The screen can be printed as a string using `ftxui::Screen::ToString()`.
std::cout << screen.ToString();
return EXIT_SUCCESS;
}
~~~
```
# dom {#module-dom}
This module defines a hierarchical set of `ftxui::Element`. An element manages
layout and can be responsive to the terminal dimensions.
the layout and can be responsive to the terminal dimension changes. Note the
following example where this module is used to create a simple layout with a
number of operators:
**Example:**
```cpp
@@ -165,7 +174,8 @@ document |= border
**List of elements**
They are all defined inside:
The list of all elements are included and can be accessed by including the
corresponding header file:
```cpp
#include <ftxui/dom/elements.hpp>
```
@@ -175,41 +185,44 @@ They are all defined inside:
## text ## {#dom-text}
The most simple widget. It displays a text.
~~~cpp
```cpp
text("I am a piece of text");
~~~
~~~bash
```
```bash
I am a piece of text.
~~~
```
## vtext {#dom-vtext}
Same as `ftxui::text`, but vertical.
~~~cpp
Identical to `ftxui::text`, but displayed vertically.
Code:
```cpp
vtext("HELLO");
~~~
~~~bash
```
Terminal output:
```bash
H
E
L
L
O
~~~
```
## paragraph {#dom-paragraph}
Similar to `ftxui::text`, but the individual word are wrapped along multiple
lines, depending on the width of its container.
Sample Code:
```cpp
paragraph("A very long text")
```
Similar to `ftxui::text`, but this support line wrapping and alignments. The
words are split by spaces
[Paragraph example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2paragraph_8cpp-example.html)
![ezgif com-gif-maker (4)](https://user-images.githubusercontent.com/4759106/147251370-983a06e7-6f41-4113-92b8-942f43d34d06.gif)
See:
For a more detailed example refer to [detailed example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2paragraph_8cpp-example.html). Paragraph also includes a number of other variants as shown below:
```cpp
Element paragraph(std::string text);
Element paragraphAlignLeft(std::string text);
@@ -221,25 +234,28 @@ Element paragraphAlignJustify(std::string text);
## border {#dom-border}
Add a border around an element
~~~cpp
border(text("The element"))
~~~
Adds a border around an element.
~~~bash
Code:
```cpp
border(text("The element"))
```
Terminal output:
```bash
┌───────────┐
│The element│
└───────────┘
~~~
```
Same, with the pipe operator:
You can achieve the same behavior by using the pipe operator.
Code:
```cpp
text("The element") | border
```
Border come with different styles.
See:
Border also comes in a variety of styles as shown below:
```cpp
Element border(Element);
Element borderLight(Element);
@@ -254,24 +270,27 @@ Decorator borderWith(Pixel);
## window ## {#dom-window}
A `ftxui::window` is a `ftxui::border`, but with some text on top of the border.
Add a border around an element
~~~cpp
A `ftxui::window` is a `ftxui::border`, but with an additional header. To add a
window around an element, wrap it and specify a string as the header.
Code:
```cpp
window("The window", text("The element"))
~~~
```
~~~bash
Terminal output:
```bash
┌The window─┐
│The element│
└───────────┘
~~~
```
## separator {#dom-separator}
Display a vertical or horizontal line to visually split the content of a
Displays a vertical/horizontal line to visually split the content of a
container in two.
~~~cpp
Code:
```cpp
border(
hbox({
text("Left"),
@@ -279,17 +298,17 @@ border(
text("Right")
})
)
~~~
```
~~~bash
Terminal output:
```bash
┌────┬─────┐
│left│right│
└────┴─────┘
~~~
```
Separators come with different styles:
See:
Separators come in a variety of flavors as shown below:
```cpp
Element separator(void);
Element separatorLight();
@@ -311,18 +330,21 @@ Element separatorVSelector(float up,
## gauge {#dom-gauge}
A gauge. It can be used to represent a progress bar.
~~~cpp
border(gauge(0.5))
~~~
It constitutes a gauge. It can be used to represent a progress bar.
~~~bash
Code:
```cpp
border(gauge(0.5))
```
Teminal output:
```bash
┌────────────────────────────────────────────────────────────────────────────┐
│██████████████████████████████████████ │
└────────────────────────────────────────────────────────────────────────────┘
~~~
```
A gauge can be displayed into several directions. See:
Gauges can be displayed in many orientations as shown below:
```cpp
Element gauge(float ratio);
Element gaugeLeft(float ratio);
@@ -344,21 +366,21 @@ Element graph(GraphFunction);
```
## Colors {#dom-colors}
A terminal console can usually display colored text and colored background.
~~~cpp
Most terminal consoles can display colored text and colored backgrounds. FTXUI
supports every color palette:
```cpp
Decorator color(Color);
Decorator bgcolor(Color);
~~~
```
FTXUI support every color palette:
Color [gallery](https://arthursonzogni.github.io/FTXUI/examples_2dom_2color_gallery_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147248595-04c7245a-5b85-4544-809d-a5984fc6f9e7.png)
### Palette16 #{#dom-colors-palette-16}
On most terminal the following colors are supported:
On most terminals the following colors are supported:
- Default
- Black
@@ -385,7 +407,7 @@ On most terminal the following colors are supported:
- Yellow
- YellowLight
Example:
Example use of the above colors using the pipe operator:
```cpp
text("Blue foreground") | color(Color::Blue);
text("Blue background") | bgcolor(Color::Blue);
@@ -405,7 +427,11 @@ text("HotPink") | color(Color::HotPink);
### TrueColor #{#dom-colors-true-color}
On terminal supporting trueColor, you can directly chose the 24bit RGB color:
On terminal supporting trueColor, you can directly use the 24bit RGB color
space:
Use the constructors below to specify the **RGB** or **HSV** values for your
color:
There are two constructors:
```cpp
@@ -419,97 +445,107 @@ ftxui::Color::HSV(uint8_t hue, uint8_t saturation, uint8_t value);
@endhtmlonly
## Style {#dom-style}
A terminal console can usually display colored text and colored background.
The text can also have different effects: bold, dim, underlined, inverted,
blink.
In addition to colored text and colored backgrounds. Many terminals support text
effects such as: `bold`, `dim`, `underlined`, `inverted`, `blink`.
~~~cpp
```cpp
Element bold(Element);
Element dim(Element);
Element inverted(Element);
Element underlined(Element);
Element underlinedDouble(Element);
Element strikethrough(Element);
Element blink(Element);
Decorator color(Color);
Decorator bgcolor(Color);
~~~
```
[Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2style_gallery_8cpp-example.html)
![image](https://user-images.githubusercontent.com/4759106/147244118-380bf834-9e33-40df-9ff0-07c10f2598ef.png)
Example:
~~~cpp
To use these effects, simply wrap your elements with your desired effect:
```cpp
underlined(bold(text("This text is bold and underlined")))
~~~
```
Tips: The pipe operator can be used to chain Decorator:
~~~cpp
text("This text is bold")) | bold | underlined
~~~
Alternatively, use the pipe operator to chain it on your element:
```cpp
text("This text is bold") | bold | underlined
```
## Layout {#dom-layout}
Element can be arranged together:
- horizontally with `ftxui::hbox`
- vertically with `ftxui::vbox`
- inside a grid with `ftxui::gridbox`
- wrap along one direction using the `ftxui::flexbox`.
Enables elements to be arranged in the following ways:
- **Horizontally** with `ftxui::hbox`
- **Vertically** with `ftxui::vbox`
- **Inside a grid** with `ftxui::gridbox`
- **Wrapped along one direction** using the `ftxui::flexbox`.
[Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2vbox_hbox_8cpp-example.html) using `ftxui::hbox`, `ftxui::vbox` and `ftxui::filler`.
![image](https://user-images.githubusercontent.com/4759106/147242524-7103b5d9-1a92-4e2d-ac70-b3d6740061e3.png)
[Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2gridbox_8cpp-example.htmlp) using `ftxui::gridbox`:
[Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2gridbox_8cpp-example.htmlp)
using `ftxui::gridbox`:
![image](https://user-images.githubusercontent.com/4759106/147242972-0db1f2e9-0790-496f-86e6-ed2c604f7a73.png)
[Example](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/dom/hflow.cpp) using flexbox:
[Example](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/dom/hflow.cpp)
using flexbox:
![image](https://user-images.githubusercontent.com/4759106/147243064-780ac7cc-605b-475f-94b8-cf7c4aed03a5.png)
[See](https://arthursonzogni.github.io/FTXUI/examples_2dom_2hflow_8cpp-example.html) also this [demo](https://arthursonzogni.com/FTXUI/examples/?file=component/flexbox).
Checkout this
[example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2hflow_8cpp-example.html)
and the associated
[demo](https://arthursonzogni.github.io/FTXUI/examples/?file=component/flexbox).
Element can become flexible using the the `ftxui::flex` decorator.
Element can also become flexible using the the `ftxui::flex` decorator.
**Examples**
~~~cpp
Code:
```cpp
hbox({
text("left") | border ,
text("middle") | border | flex,
text("right") | border,
});
~~~
~~~bash
┌────┐┌─────────────────────────────────────────────────────────────────┐┌─────┐
```
Terminal output:
```bash
┌────┐┌─────────────────────────────────────────────────────┐┌─────┐
│left││middle ││right│
└────┘└─────────────────────────────────────────────────────────────────┘└─────┘
~~~
└────┘└─────────────────────────────────────────────────────┘└─────┘
```
~~~cpp
Code:
```cpp
hbox({
text("left") | border ,
text("middle") | border | flex,
text("right") | border | flex,
});
~~~
~~~bash
┌────┐┌───────────────────────────────────┐┌───────────────────────────────────┐
```
Terminal output:
```bash
┌────┐┌───────────────────────────────┐┌───────────────────────────────┐
│left││middle ││right │
└────┘└───────────────────────────────────┘└───────────────────────────────────┘
~~~
└────┘└───────────────────────────────┘└───────────────────────────────┘
```
## Table {#dom-table}
A class to easily style a table of data.
Enables easy formatting of data into a neat table like visual form.
[Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2table_8cpp-example.html):
[Code example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2table_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147250766-77d8ec9e-cf2b-486d-9866-1fd9f1bd2e6b.png)
## Canvas {#dom-canvas}
See [<ftxui/dom/canvas.hpp>](./canvas_8hpp_source.html)
See the API [<ftxui/dom/canvas.hpp>](./canvas_8hpp_source.html)
```cpp
auto c = Canvas(100, 100);
@@ -517,41 +553,42 @@ See [<ftxui/dom/canvas.hpp>](./canvas_8hpp_source.html)
auto element = canvas(c);
```
Drawing can be made on a `ftxui::Canvas`, using braille, block, or simple
Drawing can be performed on a `ftxui::Canvas`, using braille, block, or simple
characters:
Simple [example](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/dom/canvas.cpp):
![image](https://user-images.githubusercontent.com/4759106/147245843-76cc62fb-ccb4-421b-aacf-939f9afb42fe.png)
Complex [examples](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/component/canvas_animated.cpp):
Complex [example](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/component/canvas_animated.cpp):
![ezgif com-gif-maker (3)](https://user-images.githubusercontent.com/4759106/147250538-783a8246-98e0-4a25-b032-3bd3710549d1.gif)
# component {#module-component}
The `ftxui::component`module defines the logic to produce interactive component
responding to user's events (keyboard, mouse, etc...)
The `ftxui::component` module defines the logic that produces interactive
components that respond to user events (keyboard, mouse, etc.).
A `ftxui::ScreenInteractive` defines a main loop to render a component.
A `ftxui::ScreenInteractive` defines a main loop that renders a component.
A `ftxui::Component` is a shared pointer to a `ftxui::ComponentBase`. The later
defines
A `ftxui::Component` is a shared pointer to a `ftxui::ComponentBase`. The latter defines:
- `ftxui::ComponentBase::Render()`: How to render the interface.
- `ftxui::ComponentBase::OnEvent()`: How to react to events.
- `ftxui::ComponentBase::Add()`: Give a parent/child relation ship in between
two component. This defines a tree a components, which help properly define
how keyboard navigation works.
- `ftxui::ComponentBase::Add()`: Construct a parent/child relationship
between two components. The tree of component is used to define how to
navigate using the keyboard.
`ftxui::Element` are used to render a single frame.
`ftxui::Element` are used to render a single frame. On the other side
`ftxui::Component` are used to render dynamic user interface, producing multiple
frame, and updating its state on events.
[Gallery](https://arthursonzogni.github.io/FTXUI/examples_2component_2gallery_8cpp-example.html) of multiple components. ([demo](https://arthursonzogni.com/FTXUI/examples/?file=component/gallery))
[Gallery](https://arthursonzogni.github.io/FTXUI/examples_2component_2gallery_8cpp-example.html) of multiple components. ([demo](https://arthursonzogni.github.io/FTXUI/examples/?file=component/gallery))
![image](https://user-images.githubusercontent.com/4759106/147247330-b60beb9f-e665-48b4-81c0-4b01ee95bc66.png)
Predefined components are available in ["ftxui/dom/component.hpp"](./component_8hpp.html)
All predefined components are available in
["ftxui/dom/component.hpp"](./component_8hpp.html)
\include ftxui/component/component.hpp
@@ -569,6 +606,8 @@ Produced by: `ftxui::Input()` from "ftxui/component/component.hpp"
## Menu {#component-menu}
Defines a menu object. It contains a list of entries, one of them is selected.
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2menu_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147247822-0035fd6f-bb13-4b3a-b057-77eb9291582f.png)
@@ -582,6 +621,8 @@ Produced by: `ftxui::Menu()` from "ftxui/component/component.hpp"
## Toggle {#component-toggle}
A special kind of menu. The entries are displayed horizontally.
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2toggle_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147249383-e2201cf1-b7b8-4a5a-916f-d761e3e7ae40.png)
@@ -594,6 +635,9 @@ Produced by: `ftxui::Toggle()` from "ftxui/component/component.hpp"
## CheckBox {#component-checkbox}
This component defines a checkbox. It is a single entry that can be turned
on/off.
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2checkbox_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147246646-b86926a9-1ef9-4efb-af98-48a9b62acd81.png)
@@ -606,6 +650,8 @@ Produced by: `ftxui::Checkbox()` from "ftxui/component/component.hpp"
## RadioBox {#component-radiobox}
A radiobutton component. This is a list of entries, where one can be turned on.
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2radiobox_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147246401-809d14a5-6621-4e36-8dd9-a2d75ef2a94e.png)
@@ -618,6 +664,9 @@ Produced by: `ftxui::Radiobox()` from "ftxui/component/component.hpp"
## Dropdown {#component-dropdown}
A drop down menu is a component that when checked display a list of element for
the user to select one.
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2dropdown_8cpp-example.html):
![youtube-video-gif (3)](https://user-images.githubusercontent.com/4759106/147246982-1e821751-531c-4e1f-bc37-2fa290e143cd.gif)
@@ -626,6 +675,9 @@ Produced by: `ftxui::Dropdown()` from "ftxui/component/component.hpp"
## Slider {#component-slider}
Represents a slider object that consists of a range with binned intermediate
intervals. It can be created by `ftxui::Slider()`.
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2slider_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147249265-7e2cad75-082c-436e-affe-44a550c480ab.png)
@@ -634,7 +686,7 @@ Produced by: `ftxui::Slider()` from "ftxui/component/component.hpp"
## Renderer {#component-renderer}
Produced by: `ftxui::Renderer()` from \ref 'ftxui/component/component.hpp'. This
Produced by: `ftxui::Renderer()` from \ref ftxui/component/component.hpp. This
component decorate another one by using a different function to render an
interface.
@@ -647,11 +699,11 @@ auto renderer = Renderer(inner, [&] {
});
```
`ftxui::Renderer` also support the component decorator pattern:
`ftxui::Renderer` also supports the component decorator pattern:
```cpp
auto component = [...]
component = component
| Renderer([] (Element e) { return e | border))
| Renderer([](Element e) { return e | border))
| Renderer(bold)
```
@@ -663,9 +715,8 @@ component = component | border | bold;
## CatchEvent {#component-catchevent}
Produced by: `ftxui::CatchEvent()` from \ref 'ftxui/component/component.hpp'.
This component decorate another one and catch the events before the underlying
component.
Produced by: `ftxui::CatchEvent()` from \ref ftxui/component/component.hpp.
This component decorate others, catching events before the underlying component.
Examples:
```cpp
@@ -694,9 +745,9 @@ component = component
## Collapsible {#component-collapsible}
Useful for section whose visibility can be toggle on/off by the user.
This is basically, a combinaison of a `ftxui::Checkbox` and a `ftxui::Maybe`
components.
Useful for visual elements whose visibility can be toggle on/off by the user.
Essentially, this the combination of the `ftxui::Checkbox()` and
`ftxui::Maybe()` components.
```cpp
auto collabsible = Collapsible("Show more", inner_element);
@@ -704,9 +755,9 @@ auto collabsible = Collapsible("Show more", inner_element);
## Maybe {#component-maybe}
Produced by: `ftxui::Maybe()` from \ref `ftxui/component/component.hpp`.
This component decorate another one, by showing/hiding it depending on a boolean
or a predicate.
Produced by: `ftxui::Maybe()` from \ref ftxui/component/component.hpp.
This component can be utilized to show/hide any other component via a boolean or
a predicate.
Example with a boolean:
```cpp
@@ -721,9 +772,8 @@ auto component = Renderer([]{ return "Hello World!"; });
auto maybe_component = Maybe(component, [&] { return time > 10; })
```
`ftxui::Maybe` can be used as a decorator.
```
As usual, `ftxui::Maybe` can also be used as a decorator:
```cpp
component = component
| Maybe(&a_boolean)
| Maybe([&] { return time > 10; })
@@ -759,22 +809,20 @@ one of them. This is useful for implementing a tab bar.
![ezgif com-gif-maker (2)](https://user-images.githubusercontent.com/4759106/147250217-fe447e0f-7a99-4e08-948a-995087d9b40e.gif)
## ResizableSplit::{Left, Right, Top, Bottom} {#component-resizable-split}
## ResizableSplit {#component-resizable-split}
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2resizable_split_8cpp-example.html):
![ezgif com-gif-maker](https://user-images.githubusercontent.com/4759106/147248372-c55512fe-9b96-4b08-a1df-d05cf2cae431.gif)
Produced by:
It defines a horizontal or vertical separation between two children components.
The position of the split is variable and controllable using the mouse.
There are four possible splits:
- `ftxui::ResizableSplitLeft()`
- `ftxui::ResizableSplitRight()`
- `ftxui::ResizableSplitTop()`
- `ftxui::ResizableSplitBottom()`
from "ftxui/component/component.hpp"
It defines an horizontal or vertical separation in between two children
component. The position of the split is variable and controllable using the
mouse.
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2resizable_split_8cpp-example.html):
![ezgif com-gif-maker](https://user-images.githubusercontent.com/4759106/147248372-c55512fe-9b96-4b08-a1df-d05cf2cae431.gif)
@htmlonly
<script id="asciicast-tprMH2EdkUoMb7D2YxgMGgpzx" src="https://asciinema.org/a/tprMH2EdkUoMb7D2YxgMGgpzx.js" async></script>
@@ -782,16 +830,14 @@ mouse.
## Force a frame redraw. {#component-force-redraw}
Whenever a new group of events have been processed: keyboard, mouse, window
resize, etc..., the `ftxui::ScreenInteractive::Loop()` is responsible for
drawing a new frame.
You might want to react to arbitrary events that are unknown to FTXUI. This can
be achieve by posting events via `ftxui::ScreenInteractive::PostEvent`, via a
thread. You can post the event`ftxui::Event::Custom`.
Typically, `ftxui::ScreenInteractive::Loop()` is responsible for drawing a new
frame whenever a new group of events (e.g keyboard, mouse, window resize, etc.)
has been processed. However, you might want to react to arbitrary events that
are unknown to FTXUI. To accomplish this, you must post events using
`ftxui::ScreenInteractive::PostEvent` (**this is thread safe**) via a thread.
You will have to post the event `ftxui::Event::Custom`.
Example:
```cpp
screen->PostEvent(Event::Custom);
```
`ftxui::ScreenInteractive::PostEvent` is thread safe.

View File

@@ -1,9 +1,16 @@
if(NOT FTXUI_BUILD_EXAMPLES)
return()
endif()
set(EXAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR})
function(example name)
add_executable(ftxui_example_${name} ${name}.cpp)
target_link_libraries(ftxui_example_${name} PUBLIC ${DIRECTORY_LIB})
file(RELATIVE_PATH dir ${EXAMPLES_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
set_property(GLOBAL APPEND PROPERTY FTXUI::EXAMPLES ${dir}/${name})
set_target_properties(ftxui_example_${name} PROPERTIES
CXX_STANDARD 20
)
endfunction(example)
add_subdirectory(component)
@@ -19,6 +26,7 @@ if (EMSCRIPTEN)
get_property(EXAMPLES GLOBAL PROPERTY FTXUI::EXAMPLES)
foreach(file
"index.html"
"sw.js"
"run_webassembly.py")
configure_file(${file} ${file})
endforeach(file)

View File

@@ -9,9 +9,11 @@ example(checkbox)
example(checkbox_in_frame)
example(collapsible)
example(composition)
example(custom_loop)
example(dropdown)
example(flexbox_gallery)
example(focus)
example(focus_cursor)
example(gallery)
example(homescreen)
example(input)
@@ -25,6 +27,7 @@ example(menu_multiple)
example(menu_style)
example(menu_underline_animated_gallery)
example(modal_dialog)
example(modal_dialog_custom)
example(nested_screen)
example(print_key_press)
example(radiobox)
@@ -32,6 +35,7 @@ example(radiobox_in_frame)
example(renderer)
example(resizable_split)
example(slider)
example(slider_direction)
example(slider_rgb)
example(tab_horizontal)
example(tab_vertical)

View File

@@ -1,4 +1,3 @@
#include <memory> // for shared_ptr
#include <string> // for operator+, to_string
#include "ftxui/component/captured_mouse.hpp" // for ftxui
@@ -6,7 +5,7 @@
#include "ftxui/component/component_base.hpp" // for Component
#include "ftxui/component/component_options.hpp" // for ButtonOption
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for separator, Element, text, border
#include "ftxui/dom/elements.hpp" // for Element, separator, text, border
#include "ftxui/screen/color.hpp" // for Color, Color::Blue, Color::Green, Color::Red
using namespace ftxui;

View File

@@ -0,0 +1,55 @@
#include <stdlib.h> // for EXIT_SUCCESS
#include <chrono> // for milliseconds
#include <ftxui/component/event.hpp> // for Event
#include <ftxui/component/mouse.hpp> // for ftxui
#include <ftxui/dom/elements.hpp> // for text, separator, Element, operator|, vbox, border
#include <memory> // for allocator, shared_ptr
#include <string> // for operator+, to_string
#include <thread> // for sleep_for
#include "ftxui/component/component.hpp" // for CatchEvent, Renderer, operator|=
#include "ftxui/component/loop.hpp" // for Loop
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto screen = ScreenInteractive::FitComponent();
// Create a component counting the number of frames drawn and event handled.
int custom_loop_count = 0;
int frame_count = 0;
int event_count = 0;
auto component = Renderer([&] {
frame_count++;
return vbox({
text("This demonstrates using a custom ftxui::Loop. It "),
text("runs at 100 iterations per seconds. The FTXUI events "),
text("are all processed once per iteration and a new frame "),
text("is rendered as needed"),
separator(),
text("ftxui event count: " + std::to_string(event_count)),
text("ftxui frame count: " + std::to_string(frame_count)),
text("Custom loop count: " + std::to_string(custom_loop_count)),
}) |
border;
});
component |= CatchEvent([&](Event) -> bool {
event_count++;
return false;
});
Loop loop(&screen, component);
while (!loop.HasQuitted()) {
custom_loop_count++;
loop.RunOnce();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return EXIT_SUCCESS;
}
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@@ -0,0 +1,38 @@
#include <ftxui/component/captured_mouse.hpp> // for ftxui
#include <string> // for allocator, operator+, char_traits, string
#include "ftxui/component/component.hpp" // for Renderer, Vertical
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive, Component
#include "ftxui/dom/elements.hpp" // for text, Decorator, focus, focusCursorBar, focusCursorBarBlinking, focusCursorBlock, focusCursorBlockBlinking, focusCursorUnderline, focusCursorUnderlineBlinking, hbox, Element
using namespace ftxui;
Component Instance(std::string label, Decorator focusCursor) {
return Renderer([=](bool focused) {
if (focused) {
return hbox({
text("> " + label + " "),
focusCursor(text(" ")),
});
}
return text(" " + label + " ");
});
};
int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::Fullscreen();
screen.Loop(Container::Vertical({
Instance("focus", focus),
Instance("focusCursorBlock", focusCursorBlock),
Instance("focusCursorBlockBlinking", focusCursorBlockBlinking),
Instance("focusCursorBar", focusCursorBar),
Instance("focusCursorBarBlinking", focusCursorBarBlinking),
Instance("focusCursorUnderline", focusCursorUnderline),
Instance("focusCursorUnderlineBlinking", focusCursorUnderlineBlinking),
}));
return 0;
}
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@@ -1,17 +1,18 @@
#include <stddef.h> // for size_t
#include <array> // for array
#include <atomic> // for atomic
#include <chrono> // for operator""s, chrono_literals
#include <cmath> // for sin
#include <functional> // for ref, reference_wrapper, function
#include <memory> // for allocator, shared_ptr, __shared_ptr_access
#include <string> // for string, basic_string, operator+, to_string, char_traits
#include <string> // for string, basic_string, char_traits, operator+, to_string
#include <thread> // for sleep_for, thread
#include <utility> // for move
#include <vector> // for vector
#include "../dom/color_info_sorted_2d.ipp" // for ColorInfoSorted2D
#include "ftxui/component/component.hpp" // for Checkbox, Renderer, Horizontal, Vertical, Input, Menu, Radiobox, ResizableSplitLeft, Tab
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "ftxui/component/component_options.hpp" // for MenuOption, InputOption
#include "ftxui/component/event.hpp" // for Event, Event::Custom
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
@@ -499,13 +500,18 @@ int main(int argc, const char* argv[]) {
});
});
bool refresh_ui_continue = true;
std::atomic<bool> refresh_ui_continue = true;
std::thread refresh_ui([&] {
while (refresh_ui_continue) {
using namespace std::chrono_literals;
std::this_thread::sleep_for(0.05s);
shift++;
screen.PostEvent(Event::Custom);
// The |shift| variable belong to the main thread. `screen.Post(task)`
// will execute the update on the thread where |screen| lives (e.g. the
// main thread). Using `screen.Post(task)` is threadsafe.
screen.Post([&] { shift++; });
// After updating the state, request a new frame to be drawn. This is done
// by simulating a new "custom" event to be handled.
screen.Post(Event::Custom);
}
});

View File

@@ -1,12 +1,11 @@
#include <memory> // for shared_ptr
#include <string> // for string, basic_string, allocator
#include <string> // for string, allocator, basic_string
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for operator|, Maybe, Checkbox, Radiobox, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for Component
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for border, color, operator|, text, Element
#include "ftxui/dom/elements.hpp" // for Element, border, color, operator|, text
#include "ftxui/screen/color.hpp" // for Color, Color::Red
using namespace ftxui;

View File

@@ -1,14 +1,14 @@
#include <array> // for array
#include <chrono> // for milliseconds
#include <functional> // for function
#include <memory> // for shared_ptr, __shared_ptr_access, allocator
#include <string> // for string, char_traits, basic_string, operator+
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string, char_traits, operator+, basic_string
#include <vector> // for vector
#include "ftxui/component/animation.hpp" // for ElasticOut, Linear
#include "ftxui/component/component.hpp" // for Menu, Horizontal, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for MenuOption, MenuEntryOption, AnimatedColorOption, AnimatedColorsOption, UnderlineOption
#include "ftxui/component/component_options.hpp" // for MenuOption, EntryState, MenuEntryOption, AnimatedColorOption, AnimatedColorsOption, UnderlineOption
#include "ftxui/component/mouse.hpp" // for ftxui
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for separator, operator|, Element, text, bgcolor, hbox, bold, color, filler, border, vbox, borderDouble, dim, flex, hcenter

View File

@@ -1,6 +1,6 @@
#include <chrono> // for operator""ms, literals
#include <memory> // for shared_ptr, __shared_ptr_access, allocator
#include <string> // for string, basic_string, operator+, to_string
#include <memory> // for allocator, shared_ptr, __shared_ptr_access
#include <string> // for string, operator+, to_string, basic_string
#include <vector> // for vector
#include "ftxui/component/animation.hpp" // for BackOut, Duration

View File

@@ -1,94 +1,83 @@
#include <memory> // for allocator, shared_ptr, __shared_ptr_access
#include <string> // for string, basic_string, char_traits, operator+
#include <vector> // for vector
#include <ftxui/component/component_options.hpp> // for ButtonOption
#include <ftxui/component/mouse.hpp> // for ftxui
#include <functional> // for function
#include <memory> // for allocator, shared_ptr
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Button, Renderer, Horizontal, Tab
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for operator|, Element, filler, text, hbox, separator, center, vbox, bold, border, clear_under, dbox, size, GREATER_THAN, HEIGHT
#include "ftxui/component/component.hpp" // for Button, operator|=, Renderer, Vertical, Modal
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive, Component
#include "ftxui/dom/elements.hpp" // for operator|, separator, text, size, Element, vbox, border, GREATER_THAN, WIDTH, center, HEIGHT
using namespace ftxui;
auto button_style = ButtonOption::Animated();
// Definition of the main component. The details are not important.
Component MainComponent(std::function<void()> show_modal,
std::function<void()> exit) {
auto component = Container::Vertical({
Button("Show modal", show_modal, button_style),
Button("Quit", exit, button_style),
});
// Polish how the two buttons are rendered:
component |= Renderer([&](Element inner) {
return vbox({
text("Main component"),
separator(),
inner,
}) //
| size(WIDTH, GREATER_THAN, 15) //
| size(HEIGHT, GREATER_THAN, 15) //
| border //
| center; //
});
return component;
}
// Definition of the modal component. The details are not important.
Component ModalComponent(std::function<void()> do_nothing,
std::function<void()> hide_modal) {
auto component = Container::Vertical({
Button("Do nothing", do_nothing, button_style),
Button("Quit modal", hide_modal, button_style),
});
// Polish how the two buttons are rendered:
component |= Renderer([&](Element inner) {
return vbox({
text("Modal component "),
separator(),
inner,
}) //
| size(WIDTH, GREATER_THAN, 30) //
| border; //
});
return component;
}
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto screen = ScreenInteractive::TerminalOutput();
// There are two layers. One at depth = 0 and the modal window at depth = 1;
int depth = 0;
// State of the application:
bool modal_shown = false;
// The current rating of FTXUI.
std::string rating = "3/5 stars";
// Some actions modifying the state:
auto show_modal = [&] { modal_shown = true; };
auto hide_modal = [&] { modal_shown = false; };
auto exit = screen.ExitLoopClosure();
auto do_nothing = [&] {};
// At depth=0, two buttons. One for rating FTXUI and one for quitting.
auto button_rate_ftxui = Button("Rate FTXUI", [&] { depth = 1; });
auto button_quit = Button("Quit", screen.ExitLoopClosure());
// Instanciate the main and modal components:
auto main_component = MainComponent(show_modal, exit);
auto modal_component = ModalComponent(do_nothing, hide_modal);
auto depth_0_container = Container::Horizontal({
button_rate_ftxui,
button_quit,
});
auto depth_0_renderer = Renderer(depth_0_container, [&] {
return vbox({
text("Modal dialog example"),
separator(),
text("☆☆☆ FTXUI:" + rating + " ☆☆☆") | bold,
filler(),
hbox({
button_rate_ftxui->Render(),
filler(),
button_quit->Render(),
}),
}) |
border | size(HEIGHT, GREATER_THAN, 18) | center;
});
// Use the `Modal` function to use together the main component and its modal
// window. The |modal_shown| boolean controls whether the modal is shown or
// not.
main_component |= Modal(modal_component, &modal_shown);
// At depth=1, The "modal" window.
std::vector<std::string> rating_labels = {
"1/5 stars", "2/5 stars", "3/5 stars", "4/5 stars", "5/5 stars",
};
auto on_rating = [&](std::string new_rating) {
rating = new_rating;
depth = 0;
};
auto depth_1_container = Container::Horizontal({
Button(&rating_labels[0], [&] { on_rating(rating_labels[0]); }),
Button(&rating_labels[1], [&] { on_rating(rating_labels[1]); }),
Button(&rating_labels[2], [&] { on_rating(rating_labels[2]); }),
Button(&rating_labels[3], [&] { on_rating(rating_labels[3]); }),
Button(&rating_labels[4], [&] { on_rating(rating_labels[4]); }),
});
auto depth_1_renderer = Renderer(depth_1_container, [&] {
return vbox({
text("Do you like FTXUI?"),
separator(),
hbox(depth_1_container->Render()),
}) |
border;
});
auto main_container = Container::Tab(
{
depth_0_renderer,
depth_1_renderer,
},
&depth);
auto main_renderer = Renderer(main_container, [&] {
Element document = depth_0_renderer->Render();
if (depth == 1) {
document = dbox({
document,
depth_1_renderer->Render() | clear_under | center,
});
}
return document;
});
screen.Loop(main_renderer);
screen.Loop(main_component);
return 0;
}
// Copyright 2020 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
// the LICENSE file.

View File

@@ -0,0 +1,94 @@
#include <memory> // for allocator, shared_ptr, __shared_ptr_access
#include <string> // for string, basic_string, char_traits, operator+
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Button, Renderer, Horizontal, Tab
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for operator|, Element, filler, text, hbox, separator, center, vbox, bold, border, clear_under, dbox, size, GREATER_THAN, HEIGHT
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto screen = ScreenInteractive::TerminalOutput();
// There are two layers. One at depth = 0 and the modal window at depth = 1;
int depth = 0;
// The current rating of FTXUI.
std::string rating = "3/5 stars";
// At depth=0, two buttons. One for rating FTXUI and one for quitting.
auto button_rate_ftxui = Button("Rate FTXUI", [&] { depth = 1; });
auto button_quit = Button("Quit", screen.ExitLoopClosure());
auto depth_0_container = Container::Horizontal({
button_rate_ftxui,
button_quit,
});
auto depth_0_renderer = Renderer(depth_0_container, [&] {
return vbox({
text("Modal dialog example"),
separator(),
text("☆☆☆ FTXUI:" + rating + " ☆☆☆") | bold,
filler(),
hbox({
button_rate_ftxui->Render(),
filler(),
button_quit->Render(),
}),
}) |
border | size(HEIGHT, GREATER_THAN, 18) | center;
});
// At depth=1, The "modal" window.
std::vector<std::string> rating_labels = {
"1/5 stars", "2/5 stars", "3/5 stars", "4/5 stars", "5/5 stars",
};
auto on_rating = [&](std::string new_rating) {
rating = new_rating;
depth = 0;
};
auto depth_1_container = Container::Horizontal({
Button(&rating_labels[0], [&] { on_rating(rating_labels[0]); }),
Button(&rating_labels[1], [&] { on_rating(rating_labels[1]); }),
Button(&rating_labels[2], [&] { on_rating(rating_labels[2]); }),
Button(&rating_labels[3], [&] { on_rating(rating_labels[3]); }),
Button(&rating_labels[4], [&] { on_rating(rating_labels[4]); }),
});
auto depth_1_renderer = Renderer(depth_1_container, [&] {
return vbox({
text("Do you like FTXUI?"),
separator(),
hbox(depth_1_container->Render()),
}) |
border;
});
auto main_container = Container::Tab(
{
depth_0_renderer,
depth_1_renderer,
},
&depth);
auto main_renderer = Renderer(main_container, [&] {
Element document = depth_0_renderer->Render();
if (depth == 1) {
document = dbox({
document,
depth_1_renderer->Render() | clear_under | center,
});
}
return document;
});
screen.Loop(main_renderer);
return 0;
}
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@@ -25,7 +25,7 @@ std::string Stringify(Event event) {
out = "(" + out + " ) -> ";
if (event.is_character()) {
out += "character(" + event.character() + ")";
out += "Event::Character(\"" + event.character() + "\")";
} else if (event.is_mouse()) {
out += "mouse";
switch (event.mouse().button) {
@@ -66,6 +66,68 @@ std::string Stringify(Event event) {
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)";
}

View File

@@ -0,0 +1,49 @@
#include <array> // for array
#include <cmath> // for sin
#include <ftxui/component/component_base.hpp> // for ComponentBase
#include <ftxui/component/component_options.hpp> // for SliderOption
#include <ftxui/dom/elements.hpp> // for size, GREATER_THAN, GaugeDirection, GaugeDirection::Up, HEIGHT
#include <ftxui/util/ref.hpp> // for ConstRef, Ref
#include <memory> // for shared_ptr, __shared_ptr_access
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Horizontal, Slider, operator|=
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
using namespace ftxui;
int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::TerminalOutput();
std::array<int, 30> values;
for (int i = 0; i < values.size(); ++i) {
values[i] = 50 + 20 * std::sin(i * 0.3);
}
auto layout_horizontal = Container::Horizontal({});
for (int i = 0; i < values.size(); ++i) {
// In C++17:
SliderOption<int> option;
option.value = &values[i];
option.max = 100;
option.increment = 5;
option.direction = GaugeDirection::Up;
layout_horizontal->Add(Slider<int>(option));
/* In C++20:
layout_horizontal->Add(Slider<int>({
.value = &values[i],
.max = 100,
.increment = 5,
.direction = GaugeDirection::Up,
}));
*/
}
layout_horizontal |= size(HEIGHT, GREATER_THAN, 20);
screen.Loop(layout_horizontal);
}
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@@ -26,7 +26,9 @@ example(style_color)
example(style_dim)
example(style_gallery)
example(style_inverted)
example(style_strikethrough)
example(style_underlined)
example(style_underlined_double)
example(table)
example(vbox_hbox)
example(vflow)

View File

@@ -2,7 +2,7 @@
#include <ftxui/dom/elements.hpp> // for text, gauge, operator|, flex, hbox, Element
#include <ftxui/screen/screen.hpp> // for Screen
#include <iostream> // for cout, endl, ostream
#include <string> // for allocator, operator+, char_traits, operator<<, string, to_string, basic_string
#include <string> // for allocator, char_traits, operator+, operator<<, string, to_string, basic_string
#include <thread> // for sleep_for
#include "ftxui/dom/node.hpp" // for Render

View File

@@ -1,8 +1,8 @@
#include <chrono> // for operator""s, chrono_literals
#include <ftxui/dom/elements.hpp> // for text, gauge, operator|, flex, hbox, Element
#include <ftxui/dom/elements.hpp> // for filler, operator|, separator, text, border, Element, vbox, vtext, hbox, center, gaugeDown, gaugeLeft, gaugeRight, gaugeUp
#include <ftxui/screen/screen.hpp> // for Screen
#include <iostream> // for cout, endl, ostream
#include <string> // for allocator, operator+, char_traits, operator<<, string, to_string, basic_string
#include <string> // for allocator, operator+, operator<<, string, to_string
#include <thread> // for sleep_for
#include "ftxui/dom/node.hpp" // for Render

View File

@@ -1,8 +1,7 @@
#include <stdio.h> // for getchar
#include <ftxui/dom/elements.hpp> // for operator|, size, Element, text, hcenter, Decorator, Fit, WIDTH, hflow, window, EQUAL, GREATER_THAN, HEIGHT, bold, border, dim, LESS_THAN
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for shared_ptr
#include <string> // for allocator, operator+, to_string, char_traits, string
#include <string> // for allocator, char_traits, operator+, to_string, string
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui

View File

@@ -1,7 +1,6 @@
#include <chrono> // for operator""s, chrono_literals
#include <ftxui/screen/screen.hpp> // for Screen
#include <iostream> // for cout, ostream
#include <memory> // for shared_ptr
#include <string> // for allocator, operator<<, string
#include <thread> // for sleep_for

View File

@@ -2,7 +2,7 @@
#include <ftxui/dom/elements.hpp> // for operator|, text, Element, hbox, bold, color, filler, separator, vbox, window, gauge, Fit, size, dim, EQUAL, WIDTH
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <iostream> // for cout, endl, ostream
#include <list> // for list, operator!=, _List_iterator, _List_iterator<>::_Self
#include <list> // for list, operator==, _List_iterator, _List_iterator<>::_Self
#include <memory> // for allocator, shared_ptr, allocator_traits<>::value_type
#include <string> // for string, operator<<, to_string
#include <thread> // for sleep_for

View File

@@ -17,7 +17,7 @@ int main(int argc, const char* argv[]) {
std::string reset_position;
for (int index = 0; index < 200; ++index) {
std::vector<Element> entries;
for (int i = 0; i < 22; ++i) {
for (int i = 0; i < 23; ++i) {
if (i != 0)
entries.push_back(separator());
entries.push_back( //

View File

@@ -14,10 +14,12 @@ int main(int argc, const char* argv[]) {
text("bold") | bold , text(" ") ,
text("dim") | dim , text(" ") ,
text("inverted") | inverted , text(" ") ,
text("underlined")| underlined , text(" ") ,
text("underlined") | underlined , text(" ") ,
text("underlinedDouble") | underlinedDouble , text(" ") ,
text("blink") | blink , text(" ") ,
text("strikethrough") | strikethrough , text(" ") ,
text("color") | color(Color::Blue) , text(" ") ,
text("bgcolor") | bgcolor(Color::Blue),
text("bgcolor") | bgcolor(Color::Blue) ,
});
// clang-format on
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));

View File

@@ -0,0 +1,25 @@
#include <ftxui/dom/elements.hpp> // for text, operator|, strikethrough, Fit, hbox, Element
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto document = //
hbox({
text("This text is "),
text("strikethrough") | strikethrough,
text(". Do you like it?"),
});
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document);
screen.Print();
return 0;
}
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@@ -0,0 +1,25 @@
#include <ftxui/dom/elements.hpp> // for text, operator|, underlinedDouble, Fit, hbox, Element
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto document = //
hbox({
text("This text is "),
text("underlinedDouble") | underlinedDouble,
text(". Do you like it?"),
});
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document);
screen.Print();
return 0;
}
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@@ -1,8 +1,7 @@
#include <stdio.h> // for getchar
#include <ftxui/dom/elements.hpp> // for operator|, Element, size, text, hcenter, Fit, vflow, window, EQUAL, bold, border, dim, HEIGHT, WIDTH
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for shared_ptr
#include <string> // for allocator, operator+, to_string, char_traits, string
#include <string> // for allocator, char_traits, operator+, to_string, string
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui

View File

@@ -4,7 +4,20 @@
<title>FTXUI examples WebAssembly</title>
<script src="https://cdn.jsdelivr.net/npm/xterm@4.18.0/lib/xterm.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-webgl@0.11.4/lib/xterm-addon-webgl.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.5.0/lib/xterm-addon-fit.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@4.11.0/css/xterm.css"></link>
<!--Add COOP/COEP via a ServiceWorker to use SharedArrayBuffer-->
<script>
if ("serviceWorker" in navigator && !window.crossOriginIsolated) {
navigator.serviceWorker.register(new URL("./sw.js", location.href)).then(
registration => {
if (registration.active && !navigator.serviceWorker.controller) {
window.location.reload();
}
},
);
}
</script>
</head>
<body>
<script id="example_script"></script>
@@ -67,21 +80,43 @@
}
}
const term = new Terminal();
term.open(document.querySelector('#terminal'));
term.resize(140,43);
term.loadAddon(new (WebglAddon.WebglAddon)());
const term_element = document.querySelector('#terminal');
term.open(term_element);
const webgl_addon = new (WebglAddon.WebglAddon)();
term.loadAddon(webgl_addon);
const onBinary = e => {
for(c of e)
stdin_buffer.push(c.charCodeAt(0));
}
term.onBinary(onBinary);
term.onData(onBinary)
term.resize(140,43);
window.Module = {
preRun: () => {
FS.init(stdin, stdout, stderr);
},
postRun: [],
onRuntimeInitialized: () => {},
onRuntimeInitialized: () => {
if (window.Module._ftxui_on_resize == undefined)
return;
const fit_addon = new (FitAddon.FitAddon)();
term.loadAddon(fit_addon);
fit_addon.fit();
const resize_handler = () => {
const {cols, rows} = fit_addon.proposeDimensions();
term.resize(cols, rows);
window.Module._ftxui_on_resize(cols, rows);
};
const resize_observer = new ResizeObserver(resize_handler);
resize_observer.observe(term_element);
resize_handler();
// Disable scrollbar
term.write('\x1b[?47h')
},
};
const words = example.split('/')
@@ -125,9 +160,11 @@
}
#terminal {
padding:10px;
width:100%;
height: 500px;
height: calc(clamp(200px, 100vh - 300px, 900px));
overflow: hidden;
border:none;
background-color:black;
padding:auto;
}

25
examples/sw.js Normal file
View File

@@ -0,0 +1,25 @@
// sw.js
self.addEventListener("install", () => self.skipWaiting());
self.addEventListener("activate", e => e.waitUntil(self.clients.claim()));
self.addEventListener("fetch", e => {
if (e.request.mode != 'navigate' &&
!e.request.url.includes(".worker.js")) {
return;
}
e.respondWith((async () => {
const response = await fetch(e.request);
const newHeaders = new Headers(response.headers);
newHeaders.set("Cross-Origin-Embedder-Policy", "require-corp");
newHeaders.set("Cross-Origin-Opener-Policy", "same-origin");
const moddedResponse = new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders,
});
return moddedResponse;
})());
});

View File

@@ -8,9 +8,9 @@
#include <vector> // for vector
#include "ftxui/component/component_base.hpp" // for Component, Components
#include "ftxui/component/component_options.hpp" // for ButtonOption, CheckboxOption, InputOption (ptr only), MenuEntryOption (ptr only), MenuOption, RadioboxOption (ptr only)
#include "ftxui/component/component_options.hpp" // for ButtonOption, CheckboxOption, MenuOption
#include "ftxui/dom/elements.hpp" // for Element
#include "ftxui/util/ref.hpp" // for Ref, ConstStringRef, ConstStringListRef, StringRef
#include "ftxui/util/ref.hpp" // for ConstRef, Ref, ConstStringRef, ConstStringListRef, StringRef
namespace ftxui {
struct ButtonOption;
@@ -67,8 +67,26 @@ Component Radiobox(ConstStringListRef entries,
Ref<RadioboxOption> option = {});
Component Toggle(ConstStringListRef entries, int* selected);
template <class T> // T = {int, float, long}
Component Slider(ConstStringRef label, T* value, T min, T max, T increment);
// General slider constructor:
template <typename T>
Component Slider(SliderOption<T> options = {});
// Shorthand without the `SliderOption` constructor:
Component Slider(ConstStringRef label,
Ref<int> value,
ConstRef<int> min = 0,
ConstRef<int> max = 100,
ConstRef<int> increment = 5);
Component Slider(ConstStringRef label,
Ref<float> value,
ConstRef<float> min = 0.f,
ConstRef<float> max = 100.f,
ConstRef<float> increment = 5.f);
Component Slider(ConstStringRef label,
Ref<long> value,
ConstRef<long> min = 0l,
ConstRef<long> max = 100l,
ConstRef<long> increment = 5l);
Component ResizableSplitLeft(Component main, Component back, int* main_size);
Component ResizableSplitRight(Component main, Component back, int* main_size);
@@ -88,9 +106,24 @@ Component Maybe(Component, std::function<bool()>);
ComponentDecorator Maybe(const bool* show);
ComponentDecorator Maybe(std::function<bool()>);
Component Modal(Component main, Component modal, const bool* show_modal);
ComponentDecorator Modal(Component modal, const bool* show_modal);
Component Collapsible(ConstStringRef label,
Component child,
Ref<bool> show = false);
Component Hoverable(Component component, bool* hover);
Component Hoverable(Component component,
std::function<void()> on_enter,
std::function<void()> on_leave);
Component Hoverable(Component component, //
std::function<void(bool)> on_change);
ComponentDecorator Hoverable(bool* hover);
ComponentDecorator Hoverable(std::function<void()> on_enter,
std::function<void()> on_leave);
ComponentDecorator Hoverable(std::function<void(bool)> on_change);
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_HPP */

View File

@@ -3,8 +3,8 @@
#include <chrono> // for milliseconds
#include <ftxui/component/animation.hpp> // for Duration, QuadraticInOut, Function
#include <ftxui/dom/elements.hpp> // for Element
#include <ftxui/util/ref.hpp> // for Ref
#include <ftxui/dom/elements.hpp> // for Element, GaugeDirection, GaugeDirection::Right
#include <ftxui/util/ref.hpp> // for Ref, ConstRef
#include <functional> // for function
#include <optional> // for optional
#include <string> // for string
@@ -94,7 +94,7 @@ struct MenuOption {
std::function<Element()> elements_postfix;
// Observers:
std::function<void()> on_change; ///> Called when the seelcted entry changes.
std::function<void()> on_change; ///> Called when the selected entry changes.
std::function<void()> on_enter; ///> Called when the user presses enter.
Ref<int> focused_entry = 0;
};
@@ -164,6 +164,19 @@ struct RadioboxOption {
Ref<int> focused_entry = 0;
};
// @brief Option for the `Slider` component.
// @ingroup component
template <typename T>
struct SliderOption {
Ref<T> value;
ConstRef<T> min = T(0);
ConstRef<T> max = T(100);
ConstRef<T> increment = (max() - min()) / 20;
GaugeDirection direction = GaugeDirection::Right;
Color color_active = Color::White;
Color color_inactive = Color::GrayDark;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP */

View File

@@ -38,6 +38,11 @@ struct Event {
static const Event ArrowUp;
static const Event ArrowDown;
static const Event ArrowLeftCtrl;
static const Event ArrowRightCtrl;
static const Event ArrowUpCtrl;
static const Event ArrowDownCtrl;
// --- Other ---
static const Event Backspace;
static const Event Delete;
@@ -61,9 +66,7 @@ struct Event {
std::string character() const { return input_; }
bool is_mouse() const { return type_ == Type::Mouse; }
struct Mouse& mouse() {
return mouse_;
}
struct Mouse& mouse() { return mouse_; }
bool is_cursor_reporting() const { return type_ == Type::CursorReporting; }
int cursor_x() const { return cursor_.x; }

View File

@@ -0,0 +1,39 @@
#ifndef FTXUI_COMPONENT_LOOP_HPP
#define FTXUI_COMPONENT_LOOP_HPP
#include <memory> // for shared_ptr
#include "ftxui/component/component_base.hpp" // for ComponentBase
namespace ftxui {
class ComponentBase;
using Component = std::shared_ptr<ComponentBase>;
class ScreenInteractive;
class Loop {
public:
Loop(ScreenInteractive* screen, Component component);
~Loop();
bool HasQuitted();
void RunOnce();
void RunOnceBlocking();
void Run();
private:
// This class is non copyable.
Loop(const ScreenInteractive&) = delete;
Loop& operator=(const Loop&) = delete;
ScreenInteractive* screen_;
Component component_;
};
} // namespace ftxui
#endif // FTXUI_COMPONENT_LOOP_HPP
// Copyright 2022 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@@ -86,11 +86,25 @@ class ReceiverImpl {
return false;
}
bool ReceiveNonBlocking(T* t) {
std::unique_lock<std::mutex> lock(mutex_);
if (queue_.empty())
return false;
*t = queue_.front();
queue_.pop();
return true;
}
bool HasPending() {
std::unique_lock<std::mutex> lock(mutex_);
return !queue_.empty();
}
bool HasQuitted() {
std::unique_lock<std::mutex> lock(mutex_);
return queue_.empty() && !senders_;
}
private:
friend class SenderImpl<T>;

View File

@@ -12,11 +12,12 @@
#include "ftxui/component/animation.hpp" // for TimePoint
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/task.hpp" // for Closure, Task
#include "ftxui/component/task.hpp" // for Task, Closure
#include "ftxui/screen/screen.hpp" // for Screen
namespace ftxui {
class ComponentBase;
class Loop;
struct Event;
using Component = std::shared_ptr<ComponentBase>;
@@ -33,9 +34,12 @@ class ScreenInteractive : public Screen {
// Return the currently active screen, nullptr if none.
static ScreenInteractive* Active();
// Start/Stop the main loop.
void Loop(Component);
void Exit();
Closure ExitLoopClosure();
// Post tasks to be executed by the loop.
void Post(Task task);
void PostEvent(Event event);
void RequestAnimationFrame();
@@ -48,13 +52,23 @@ class ScreenInteractive : public Screen {
Closure WithRestoredIO(Closure);
private:
void ExitNow();
void Install();
void Uninstall();
void Main(Component component);
void PreMain();
void PostMain();
bool HasQuitted();
void RunOnce(Component component);
void RunOnceBlocking(Component component);
void HandleTask(Component component, Task& task);
void Draw(Component component);
void SigStop();
void ResetCursorPosition();
void Signal(int signal);
ScreenInteractive* suspended_screen_ = nullptr;
enum class Dimension {
@@ -80,7 +94,7 @@ class ScreenInteractive : public Screen {
std::thread event_listener_;
std::thread animation_listener_;
bool animation_requested_ = false;
animation::TimePoint previous_animation_time;
animation::TimePoint previous_animation_time_;
int cursor_x_ = 1;
int cursor_y_ = 1;
@@ -88,10 +102,14 @@ class ScreenInteractive : public Screen {
bool mouse_captured = false;
bool previous_frame_resized_ = false;
bool frame_valid_ = false;
friend class Loop;
public:
class Private {
public:
static void SigStop(ScreenInteractive& s) { return s.SigStop(); }
static void Signal(ScreenInteractive& s, int signal) { s.Signal(signal); }
};
friend Private;
};

View File

@@ -83,7 +83,9 @@ Element bold(Element);
Element dim(Element);
Element inverted(Element);
Element underlined(Element);
Element underlinedDouble(Element);
Element blink(Element);
Element strikethrough(Element);
Decorator color(Color);
Decorator bgcolor(Color);
Element color(Color, Element);
@@ -126,9 +128,6 @@ enum Direction { WIDTH, HEIGHT };
enum Constraint { LESS_THAN, EQUAL, GREATER_THAN };
Decorator size(Direction, Constraint, int value);
// --
Decorator reflect(Box& box);
// --- Frame ---
// A frame is a scrollable area. The internal area is potentially larger than
// the external one. The internal area is scrolled in order to make visible the
@@ -139,7 +138,21 @@ Element yframe(Element);
Element focus(Element);
Element select(Element);
// --- Cursor ---
// Those are similar to `focus`, but also change the shape of the cursor.
Element focusCursorBlock(Element);
Element focusCursorBlockBlinking(Element);
Element focusCursorBar(Element);
Element focusCursorBarBlinking(Element);
Element focusCursorUnderline(Element);
Element focusCursorUnderlineBlinking(Element);
// --- Misc ---
Element vscroll_indicator(Element);
Decorator reflect(Box& box);
// Before drawing the |element| clear the pixel below. This is useful in
// combinaison with dbox.
Element clear_under(Element element);
// --- Util --------------------------------------------------------------------
Element hcenter(Element);
@@ -148,10 +161,6 @@ Element center(Element);
Element align_right(Element);
Element nothing(Element element);
// Before drawing the |element| clear the pixel below. This is useful in
// combinaison with dbox.
Element clear_under(Element element);
namespace Dimension {
Dimensions Fit(Element&);
} // namespace Dimension

View File

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

View File

@@ -30,6 +30,8 @@ struct Pixel {
bool dim : 1;
bool inverted : 1;
bool underlined : 1;
bool underlined_double : 1;
bool strikethrough : 1;
bool automerge : 1;
Pixel()
@@ -38,6 +40,8 @@ struct Pixel {
dim(false),
inverted(false),
underlined(false),
underlined_double(false),
strikethrough(false),
automerge(false) {}
};
@@ -80,6 +84,17 @@ class Screen {
struct Cursor {
int x = 0;
int y = 0;
enum Shape {
Hidden = 0,
BlockBlinking = 1,
Block = 2,
UnderlineBlinking = 3,
Underline = 4,
BarBlinking = 5,
Bar = 6,
};
Shape shape;
};
Cursor cursor() const { return cursor_; }
void SetCursor(Cursor cursor) { cursor_ = cursor; }
@@ -91,8 +106,6 @@ class Screen {
int dimy_;
std::vector<std::vector<Pixel>> pixels_;
Cursor cursor_;
private:
};
} // namespace ftxui

View File

@@ -26,6 +26,33 @@ int GlyphPosition(const std::string& input,
// Returns the number of glyphs in |input|.
int GlyphCount(const std::string& input);
// Properties from:
// https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/WordBreakProperty.txt
enum class WordBreakProperty {
ALetter,
CR,
Double_Quote,
Extend,
ExtendNumLet,
Format,
Hebrew_Letter,
Katakana,
LF,
MidLetter,
MidNum,
MidNumLet,
Newline,
Numeric,
Regional_Indicator,
Single_Quote,
WSegSpace,
ZWJ,
};
std::vector<WordBreakProperty> Utf8ToWordBreakProperty(
const std::string& input);
bool IsWordBreakingCharacter(const std::string& input, size_t glyph_index);
// Map every cells drawn by |input| to their corresponding Glyphs. Half-size
// Glyphs takes one cell, full-size Glyphs take two cells.
std::vector<int> CellToGlyphIndex(const std::string& input);

View File

@@ -66,6 +66,9 @@ class ConstStringRef {
ConstStringRef(const wchar_t* ref) : ConstStringRef(std::wstring(ref)) {}
ConstStringRef(const char* ref)
: ConstStringRef(to_wstring(std::string(ref))) {}
const std::string& operator()() const {
return address_ ? *address_ : owned_;
}
const std::string& operator*() const { return address_ ? *address_ : owned_; }
const std::string* operator->() const {
return address_ ? address_ : &owned_;

View File

@@ -1,17 +1,27 @@
[
{ include: ["<bits/chrono.h>", "private", "<chrono>", "public"]},
{ include: ["<bits/std_abs.h>", "private", "<cmath>", "public"]},
{ include: ["<bits/termios-c_cc.h>", "private", "<termios.h>", "public"]},
{ include: ["<bits/termios-c_lflag.h>", "private", "<termios.h>", "public"]},
{ include: ["<bits/termios-struct.h>", "private", "<termios.h>", "public"]},
{ include: ["<bits/termios-tcflow.h>", "private", "<termios.h>", "public"]},
{ include: ["<ext/alloc_traits.h>", "private", "<vector>", "public"] },
{ symbol: [ "ftxui", "private", "", "public" ] },
{ symbol: [ "char_traits", "private", "<string>", "public" ] },
{ symbol: [ "ECHO", "private", "<termios.h>", "public" ] },
{ symbol: [ "ICANON", "private", "<termios.h>", "public" ] },
{ symbol: [ "TCSANOW", "private", "<termios.h>", "public" ] },
{ symbol: [ "VMIN", "private", "<termios.h>", "public" ] },
{ symbol: [ "VTIME", "private", "<termios.h>", "public" ] },
{ symbol: [ "__shared_ptr_access", "private", "<memory>", "public" ] },
{ symbol: [ "termios", "private", "<termios.h>", "public" ] },
{ symbol: ["__alloc_traits<>:value_type", "private", "<vector>", "public" ] },
{ include: ["<gtest/internal/gtest-internal.h>", "private", "<gtest/gtest.h>", "public" ] },
{ include: ["<gtest/internal/gtest-string.h>", "private", "<gtest/gtest.h>", "public" ] },
{ include: ["<gtest/gtest-death-test.h>", "private", "<gtest/gtest.h>", "public" ] },
{ include: ["<gtest/gtest-message.h>", "private", "<gtest/gtest.h>", "public" ] },
{ include: ["<gtest/gtest-param-test.h>", "private", "<gtest/gtest.h>", "public" ] },
{ include: ["<gtest/gtest-printers.h>", "private", "<gtest/gtest.h>", "public" ] },
{ include: ["<gtest/gtest-test-part.h>", "private", "<gtest/gtest.h>", "public" ] },
{ include: ["<gtest/gtest-typed-test.h>", "private", "<gtest/gtest.h>", "public" ] },
{ symbol: ["ftxui", "private", "", "public" ] },
{ symbol: ["char_traits", "private", "<string>", "public" ] },
{ symbol: ["ECHO", "private", "<termios.h>", "public" ] },
{ symbol: ["ICANON", "private", "<termios.h>", "public" ] },
{ symbol: ["TCSANOW", "private", "<termios.h>", "public" ] },
{ symbol: ["VMIN", "private", "<termios.h>", "public" ] },
{ symbol: ["VTIME", "private", "<termios.h>", "public" ] },
{ symbol: ["__shared_ptr_access", "private", "<memory>", "public" ] },
{ symbol: ["termios", "private", "<termios.h>", "public" ] },
{ symbol: ["__alloc_traits<>:value_type", "private", "<vector>", "public" ] }
]

View File

@@ -1,4 +1,4 @@
#include <cmath>
#include <cmath> // for sin, pow, sqrt, cos
#include <ratio> // for ratio
#include <utility> // for move
@@ -58,7 +58,7 @@ float CubicIn(float p) {
// Modeled after the cubic y = (x - 1)^3 + 1
float CubicOut(float p) {
float f = (p - 1);
const float f = (p - 1);
return f * f * f + 1;
}
@@ -69,7 +69,7 @@ float CubicInOut(float p) {
if (p < 0.5F) { // NOLINT
return 4 * p * p * p;
} else {
float f = ((2 * p) - 2);
const float f = ((2 * p) - 2);
return 0.5F * f * f * f + 1; // NOLINT
}
}
@@ -81,7 +81,7 @@ float QuarticIn(float p) {
// Modeled after the quartic y = 1 - (x - 1)^4
float QuarticOut(float p) {
float f = (p - 1);
const float f = (p - 1);
return f * f * f * (1 - p) + 1;
}
@@ -92,7 +92,7 @@ float QuarticInOut(float p) {
if (p < 0.5F) { // NOLINT
return 8 * p * p * p * p; // NOLINT
} else {
float f = (p - 1);
const float f = (p - 1);
return -8 * f * f * f * f + 1; // NOLINT
}
}
@@ -104,7 +104,7 @@ float QuinticIn(float p) {
// Modeled after the quintic y = (x - 1)^5 + 1
float QuinticOut(float p) {
float f = (p - 1);
const float f = (p - 1);
return f * f * f * f * f + 1;
}
@@ -214,7 +214,7 @@ float BackIn(float p) {
// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi))
float BackOut(float p) {
float f = (1 - p);
const float f = (1 - p);
return 1 - (f * f * f - f * std::sin(f * kPi));
}
@@ -223,7 +223,7 @@ float BackOut(float p) {
// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1]
float BackInOut(float p) {
if (p < 0.5F) { // NOLINT
float f = 2 * p;
const float f = 2 * p;
return 0.5F * (f * f * f - f * std::sin(f * kPi)); // NOLINT
} else {
float f = (1 - (2 * p - 1)); // NOLINT

View File

@@ -1,10 +1,8 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestPartResult, TestFactoryImpl
#include <gtest/gtest.h>
#include <functional> // for function
#include <vector> // for allocator, vector
#include "ftxui/component/animation.hpp" // for Function, BackIn, BackInOut, BackOut, BounceIn, BounceInOut, BounceOut, CircularIn, CircularInOut, CircularOut, CubicIn, CubicInOut, CubicOut, ElasticIn, ElasticInOut, ElasticOut, ExponentialIn, ExponentialInOut, ExponentialOut, Linear, QuadraticIn, QuadraticInOut, QuadraticOut, QuarticIn, QuarticInOut, QuarticOut, QuinticIn, QuinticInOut, QuinticOut, SineIn, SineInOut, SineOut
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_NEAR, TEST
namespace ftxui {
@@ -28,8 +26,8 @@ TEST(AnimationTest, StartAndEnd) {
animation::easing::BounceInOut,
};
for (auto& it : functions) {
EXPECT_NEAR(0.f, it(0.f), 1.0e-4);
EXPECT_NEAR(1.f, it(1.f), 1.0e-4);
EXPECT_NEAR(0.F, it(0.F), 1.0e-4);
EXPECT_NEAR(1.F, it(1.F), 1.0e-4);
}
}

View File

@@ -80,7 +80,7 @@ Component Button(ConstStringRef label,
}
auto focus_management = focused ? focus : active ? select : nothing;
EntryState state = {
const EntryState state = {
*label_,
false,
active,

View File

@@ -1,5 +1,4 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <gtest/gtest.h> // for AssertionResult, Message, TestPartResult, EXPECT_EQ, Test, EXPECT_FALSE, EXPECT_TRUE, TestInfo (ptr only), TEST
#include <chrono> // for operator""s, chrono_literals
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string
@@ -13,7 +12,6 @@
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
#include "ftxui/screen/terminal.hpp" // for SetColorSupport, Color, TrueColor
#include "gtest/gtest_pred_impl.h" // for AssertionResult, EXPECT_EQ, Test, EXPECT_FALSE, EXPECT_TRUE, TEST
namespace ftxui {

View File

@@ -1,9 +1,10 @@
#include <functional> // for function
#include <memory> // for __shared_ptr_access, __shared_ptr_access<>::element_type, shared_ptr
#include <type_traits> // for remove_reference, remove_reference<>::type
#include <utility> // for move
#include "ftxui/component/component.hpp" // for Component, Make, CatchEvent
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component.hpp" // for Make, CatchEvent, ComponentDecorator
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
#include "ftxui/component/event.hpp" // for Event
namespace ftxui {

View File

@@ -1,14 +1,13 @@
#include <functional> // for function
#include <memory> // for shared_ptr
#include <utility> // for move
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Component, Checkbox
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for CheckboxOption
#include "ftxui/component/component.hpp" // for Make, Checkbox
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
#include "ftxui/component/component_options.hpp" // for CheckboxOption, EntryState
#include "ftxui/component/event.hpp" // for Event, Event::Return
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
#include "ftxui/dom/elements.hpp" // for operator|, text, Element, hbox, reflect, focus, nothing, select
#include "ftxui/dom/elements.hpp" // for operator|, Element, reflect, focus, nothing, select
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/util/ref.hpp" // for Ref, ConstStringRef
@@ -23,8 +22,8 @@ class CheckboxBase : public ComponentBase {
private:
// Component implementation.
Element Render() override {
bool is_focused = Focused();
bool is_active = Active();
const bool is_focused = Focused();
const bool is_active = Active();
auto focus_management = is_focused ? focus : is_active ? select : nothing;
auto state = EntryState{
*label_,

View File

@@ -26,7 +26,7 @@ namespace ftxui {
///
/// ▼ Show details
/// <details component>
/// ```
///  ```
Component Collapsible(ConstStringRef label, Component child, Ref<bool> show) {
class Impl : public ComponentBase {
public:

View File

@@ -1,5 +1,4 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <gtest/gtest.h>
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include "ftxui/component/component.hpp" // for Collapsible, Renderer
@@ -8,7 +7,6 @@
#include "ftxui/dom/elements.hpp" // for text, Element
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
#include "gtest/gtest_pred_impl.h" // for AssertionResult, Test, EXPECT_EQ, EXPECT_FALSE, EXPECT_TRUE, TEST
namespace ftxui {

View File

@@ -114,7 +114,7 @@ bool ComponentBase::OnEvent(Event event) { // NOLINT
/// The default implementation dispatch the event to every child.
/// @ingroup component
void ComponentBase::OnAnimation(animation::Params& params) {
for (Component& child : children_) {
for (const Component& child : children_) {
child->OnAnimation(params);
}
}

View File

@@ -1,5 +1,6 @@
#include "ftxui/component/component_options.hpp"
#include <ftxui/screen/color.hpp> // for Color, Color::Black, Color::White, Color::GrayDark, Color::GrayLight
#include <memory> // for shared_ptr
#include <utility> // for move
@@ -122,7 +123,7 @@ MenuOption MenuOption::Toggle() {
ButtonOption ButtonOption::Ascii() {
ButtonOption option;
option.transform = [](const EntryState& s) {
std::string label = s.focused ? "[" + s.label + "]" //
const std::string label = s.focused ? "[" + s.label + "]" //
: " " + s.label + " ";
return text(label);
};
@@ -143,6 +144,23 @@ ButtonOption ButtonOption::Simple() {
return option;
}
/// @brief Create a ButtonOption. The button is shown using a border, inverted
/// when focused. This is the current default.
ButtonOption ButtonOption::Border() {
ButtonOption option;
option.transform = [](const EntryState& s) {
auto element = text(s.label) | border;
if (s.active) {
element |= bold;
}
if (s.focused) {
element |= inverted;
}
return element;
};
return option;
}
/// @brief Create a ButtonOption, using animated colors.
// static
ButtonOption ButtonOption::Animated() {
@@ -163,7 +181,13 @@ ButtonOption ButtonOption::Animated(Color color) {
/// @brief Create a ButtonOption, using animated colors.
// static
ButtonOption ButtonOption::Animated(Color background, Color foreground) {
return ButtonOption::Animated(background, foreground, foreground, background);
// NOLINTBEGIN
return ButtonOption::Animated(
/*bakground=*/background,
/*foreground=*/foreground,
/*background_active=*/foreground,
/*foreground_active=*/background);
// NOLINTEND
}
/// @brief Create a ButtonOption, using animated colors.

View File

@@ -1,10 +1,8 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <gtest/gtest.h>
#include <memory> // for shared_ptr, __shared_ptr_access, allocator, __shared_ptr_access<>::element_type, make_shared
#include "ftxui/component/component.hpp" // for Make
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "gtest/gtest_pred_impl.h" // for EXPECT_EQ, Test, AssertionResult, TEST, EXPECT_FALSE
namespace ftxui {

View File

@@ -82,7 +82,7 @@ class ContainerBase : public ComponentBase {
return;
}
for (size_t offset = 1; offset < children_.size(); ++offset) {
size_t i = ((size_t(*selector_ + offset * dir + children_.size())) %
const size_t i = ((size_t(*selector_ + offset * dir + children_.size())) %
children_.size());
if (children_[i]->Focusable()) {
*selector_ = (int)i;
@@ -108,7 +108,7 @@ class VerticalContainer : public ContainerBase {
}
bool EventHandler(Event event) override {
int old_selected = *selector_;
const int old_selected = *selector_;
if (event == Event::ArrowUp || event == Event::Character('k')) {
MoveSelector(-1);
}
@@ -190,7 +190,7 @@ class HorizontalContainer : public ContainerBase {
}
bool EventHandler(Event event) override {
int old_selected = *selector_;
const int old_selected = *selector_;
if (event == Event::ArrowLeft || event == Event::Character('h')) {
MoveSelector(-1);
}
@@ -214,7 +214,7 @@ class TabContainer : public ContainerBase {
using ContainerBase::ContainerBase;
Element Render() override {
Component active_child = ActiveChild();
const Component active_child = ActiveChild();
if (active_child) {
return active_child->Render();
}
@@ -229,7 +229,7 @@ class TabContainer : public ContainerBase {
}
bool OnMouseEvent(Event event) override {
return ActiveChild()->OnEvent(event);
return ActiveChild() && ActiveChild()->OnEvent(event);
}
};

View File

@@ -1,11 +1,9 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <gtest/gtest.h>
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include "ftxui/component/component.hpp" // for Horizontal, Vertical, Button, Tab
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "ftxui/component/event.hpp" // for Event, Event::Tab, Event::TabReverse, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp
#include "gtest/gtest_pred_impl.h" // for AssertionResult, EXPECT_EQ, EXPECT_FALSE, EXPECT_TRUE, Test, TEST
namespace ftxui {

View File

@@ -55,24 +55,32 @@ const Event Event::ArrowLeft = Event::Special("\x1B[D"); // NOLINT
const Event Event::ArrowRight = Event::Special("\x1B[C"); // NOLINT
const Event Event::ArrowUp = Event::Special("\x1B[A"); // NOLINT
const Event Event::ArrowDown = Event::Special("\x1B[B"); // NOLINT
const Event Event::ArrowLeftCtrl = Event::Special("\x1B[1;5D"); // NOLINT
const Event Event::ArrowRightCtrl = Event::Special("\x1B[1;5C"); // NOLINT
const Event Event::ArrowUpCtrl = Event::Special("\x1B[1;5A"); // NOLINT
const Event Event::ArrowDownCtrl = Event::Special("\x1B[1;5B"); // NOLINT
const Event Event::Backspace = Event::Special({127}); // NOLINT
const Event Event::Delete = Event::Special("\x1B[3~"); // NOLINT
const Event Event::Escape = Event::Special("\x1B"); // NOLINT
const Event Event::Return = Event::Special({10}); // NOLINT
const Event Event::Tab = Event::Special({9}); // NOLINT
const Event Event::TabReverse = Event::Special({27, 91, 90}); // NOLINT
const Event Event::F1 = Event::Special("\x1B[OP"); // NOLINT
const Event Event::F2 = Event::Special("\x1B[OQ"); // NOLINT
const Event Event::F3 = Event::Special("\x1B[OR"); // NOLINT
const Event Event::F4 = Event::Special("\x1B[OS"); // NOLINT
// See https://invisible-island.net/xterm/xterm-function-keys.html
// We follow xterm-new / vterm-xf86-v4 / mgt / screen
const Event Event::F1 = Event::Special("\x1BOP"); // NOLINT
const Event Event::F2 = Event::Special("\x1BOQ"); // NOLINT
const Event Event::F3 = Event::Special("\x1BOR"); // NOLINT
const Event Event::F4 = Event::Special("\x1BOS"); // NOLINT
const Event Event::F5 = Event::Special("\x1B[15~"); // NOLINT
const Event Event::F6 = Event::Special("\x1B[17~"); // NOLINT
const Event Event::F7 = Event::Special("\x1B[18~"); // NOLINT
const Event Event::F8 = Event::Special("\x1B[19~"); // NOLINT
const Event Event::F9 = Event::Special("\x1B[20~"); // NOLINT
const Event Event::F10 = Event::Special("\x1B[21~"); // NOLINT
const Event Event::F11 = Event::Special("\x1B[21~"); // Doesn't exist // NOLINT
const Event Event::F11 = Event::Special("\x1B[23~"); // NOLINT
const Event Event::F12 = Event::Special("\x1B[24~"); // NOLINT
const Event Event::Home = Event::Special({27, 91, 72}); // NOLINT
const Event Event::End = Event::Special({27, 91, 70}); // NOLINT
const Event Event::PageUp = Event::Special({27, 91, 53, 126}); // NOLINT

View File

@@ -0,0 +1,217 @@
#include <ftxui/component/captured_mouse.hpp> // for CapturedMouse
#include <functional> // for function
#include <utility> // for move
#include "ftxui/component/component.hpp" // for ComponentDecorator, Hoverable, Make
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/mouse.hpp" // for Mouse
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for operator|, reflect, Element
#include "ftxui/screen/box.hpp" // for Box
namespace ftxui {
namespace {
void Post(std::function<void()> f) {
if (auto* screen = ScreenInteractive::Active()) {
screen->Post(std::move(f));
return;
}
f();
}
} // namespace
/// @brief Wrap a component. Gives the ability to know if it is hovered by the
/// mouse.
/// @param component: The wrapped component.
/// @param hover: The value to reflect whether the component is hovered or not.
/// @ingroup component
///
/// ### Example
///
/// ```cpp
/// auto button = Button("exit", screen.ExitLoopClosure());
/// bool hover = false;
/// auto button_hover = Hoverable(button, &hover);
/// ```
// NOLINTNEXTLINE
Component Hoverable(Component component, bool* hover) {
class Impl : public ComponentBase {
public:
Impl(Component component, bool* hover)
: component_(std::move(component)), hover_(hover) {
Add(component_);
}
private:
Element Render() override {
return ComponentBase::Render() | reflect(box_);
}
bool OnEvent(Event event) override {
if (event.is_mouse()) {
*hover_ = box_.Contain(event.mouse().x, event.mouse().y) &&
CaptureMouse(event);
}
return ComponentBase::OnEvent(event);
}
Component component_;
bool* hover_;
Box box_;
};
return Make<Impl>(component, hover);
}
/// @brief Wrap a component. Gives the ability to know if it is hovered by the
/// mouse.
/// @param component: The wrapped component.
/// @param hover: The value to reflect whether the component is hovered or not.
/// @ingroup component
///
/// ### Example
///
/// ```cpp
/// auto button = Button("exit", screen.ExitLoopClosure());
/// bool hover = false;
/// auto button_hover = Hoverable(button, &hover);
/// ```
Component Hoverable(Component component,
std::function<void()> on_enter,
std::function<void()> on_leave) {
class Impl : public ComponentBase {
public:
Impl(Component component,
std::function<void()> on_enter,
std::function<void()> on_leave)
: component_(std::move(component)),
on_enter_(std::move(on_enter)),
on_leave_(std::move(on_leave)) {
Add(component_);
}
private:
Element Render() override {
return ComponentBase::Render() | reflect(box_);
}
bool OnEvent(Event event) override {
if (event.is_mouse()) {
const bool hover = box_.Contain(event.mouse().x, event.mouse().y) &&
CaptureMouse(event);
if (hover != hover_) {
Post(hover ? on_enter_ : on_leave_);
}
hover_ = hover;
}
return ComponentBase::OnEvent(event);
}
Component component_;
Box box_;
bool hover_ = false;
std::function<void()> on_enter_;
std::function<void()> on_leave_;
};
return Make<Impl>(std::move(component), std::move(on_enter),
std::move(on_leave));
}
/// @brief Wrap a component. Gives the ability to know if it is hovered by the
/// mouse.
/// @param hover: The value to reflect whether the component is hovered or not.
/// @ingroup component
///
/// ### Example
///
/// ```cpp
/// bool hover = false;
/// auto button = Button("exit", screen.ExitLoopClosure());
/// button |= Hoverable(&hover);
/// ```
ComponentDecorator Hoverable(bool* hover) {
return [hover](Component component) {
return Hoverable(std::move(component), hover);
};
}
/// @brief Wrap a component. Gives the ability to know if it is hovered by the
/// mouse.
/// @param on_enter is called when the mouse hover the component.
/// @param on_leave is called when the mouse leave the component.
/// @ingroup component
///
/// ### Example
///
/// ```cpp
/// auto button = Button("exit", screen.ExitLoopClosure());
/// int on_enter_cnt = 0;
/// int on_leave_cnt = 0;
/// button |= Hoverable(
/// [&]{ on_enter_cnt++; },
/// [&]{ on_leave_cnt++; }
// );
/// ```
// NOLINTNEXTLINE
ComponentDecorator Hoverable(std::function<void()> on_enter,
// NOLINTNEXTLINE
std::function<void()> on_leave) {
return [on_enter, on_leave](Component component) {
return Hoverable(std::move(component), on_enter, on_leave);
};
}
/// @brief Wrap a component. Gives the ability to know if it is hovered by the
/// mouse.
/// @param component the wrapped component.
/// @param on_change is called when the mouse enter or leave the component.
/// @ingroup component
///
/// ### Example
///
/// ```cpp
/// auto button = Button("exit", screen.ExitLoopClosure());
/// bool hovered = false;
/// auto button_hoverable = Hoverable(button,
// [&](bool hover) { hovered = hover;});
/// ```
// NOLINTNEXTLINE
Component Hoverable(Component component, std::function<void(bool)> on_change) {
return Hoverable(
std::move(component), //
[on_change] { on_change(true); }, //
[on_change] { on_change(false); } //
);
}
/// @brief Wrap a component. Gives the ability to know if it is hovered by the
/// mouse.
/// @param on_change is called when the mouse enter or leave the component.
/// @ingroup component
///
/// ### Example
///
/// ```cpp
/// auto button = Button("exit", screen.ExitLoopClosure());
/// bool hovered = false;
/// button |= Hoverable([&](bool hover) { hovered = hover;});
/// ```
// NOLINTNEXTLINE
ComponentDecorator Hoverable(std::function<void(bool)> on_change) {
return [on_change](Component component) {
return Hoverable(std::move(component), on_change);
};
}
} // namespace ftxui
// Copyright 2022 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@@ -0,0 +1,192 @@
#include <gtest/gtest.h> // for AssertionResult, Message, TestPartResult, EXPECT_FALSE, EXPECT_EQ, Test, EXPECT_TRUE, TestInfo (ptr only), TEST
#include <ftxui/dom/elements.hpp> // for Element, text
#include <memory> // for shared_ptr, __shared_ptr_access, allocator
#include "ftxui/component/component.hpp" // for Hoverable, Horizontal, operator|=, Renderer
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Released
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
namespace ftxui {
namespace {
Event HoverEvent(int x, int y) {
Mouse mouse;
mouse.button = Mouse::Left;
mouse.motion = Mouse::Released;
mouse.shift = false;
mouse.meta = false;
mouse.control = false;
mouse.x = x;
mouse.y = y;
return Event::Mouse("jjj", mouse);
}
Component BasicComponent() {
return Renderer([] { return text("[ ]"); });
}
TEST(HoverableTest, BasicBool) {
bool hover_1 = false;
bool hover_2 = false;
auto c1 = Hoverable(BasicComponent(), &hover_1);
auto c2 = Hoverable(BasicComponent(), &hover_2);
auto layout = Container::Horizontal({c1, c2});
auto screen = Screen(8, 2);
Render(screen, layout->Render());
EXPECT_FALSE(hover_1);
EXPECT_FALSE(hover_2);
EXPECT_FALSE(layout->OnEvent(HoverEvent(0, 0)));
EXPECT_TRUE(hover_1);
EXPECT_FALSE(hover_2);
EXPECT_FALSE(layout->OnEvent(HoverEvent(1, 0)));
EXPECT_TRUE(hover_1);
EXPECT_FALSE(hover_2);
EXPECT_FALSE(layout->OnEvent(HoverEvent(2, 0)));
EXPECT_TRUE(hover_1);
EXPECT_FALSE(hover_2);
EXPECT_FALSE(layout->OnEvent(HoverEvent(3, 0)));
EXPECT_FALSE(hover_1);
EXPECT_TRUE(hover_2);
EXPECT_FALSE(layout->OnEvent(HoverEvent(0, 0)));
EXPECT_TRUE(hover_1);
EXPECT_FALSE(hover_2);
}
TEST(HoverableTest, BasicCallback) {
int on_enter_1 = 0;
int on_enter_2 = 0;
int on_leave_1 = 0;
int on_leave_2 = 0;
auto c1 = Hoverable(
BasicComponent(), [&] { on_enter_1++; }, [&] { on_leave_1++; });
auto c2 = Hoverable(
BasicComponent(), [&] { on_enter_2++; }, [&] { on_leave_2++; });
auto layout = Container::Horizontal({c1, c2});
auto screen = Screen(8, 2);
Render(screen, layout->Render());
EXPECT_EQ(on_enter_1, 0);
EXPECT_EQ(on_enter_2, 0);
EXPECT_EQ(on_leave_1, 0);
EXPECT_EQ(on_leave_2, 0);
EXPECT_FALSE(layout->OnEvent(HoverEvent(0, 0)));
EXPECT_EQ(on_enter_1, 1);
EXPECT_EQ(on_enter_2, 0);
EXPECT_EQ(on_leave_1, 0);
EXPECT_EQ(on_leave_2, 0);
EXPECT_FALSE(layout->OnEvent(HoverEvent(1, 0)));
EXPECT_EQ(on_enter_1, 1);
EXPECT_EQ(on_enter_2, 0);
EXPECT_EQ(on_leave_1, 0);
EXPECT_EQ(on_leave_2, 0);
EXPECT_FALSE(layout->OnEvent(HoverEvent(2, 0)));
EXPECT_EQ(on_enter_1, 1);
EXPECT_EQ(on_enter_2, 0);
EXPECT_EQ(on_leave_1, 0);
EXPECT_EQ(on_leave_2, 0);
EXPECT_FALSE(layout->OnEvent(HoverEvent(3, 0)));
EXPECT_EQ(on_enter_1, 1);
EXPECT_EQ(on_enter_2, 1);
EXPECT_EQ(on_leave_1, 1);
EXPECT_EQ(on_leave_2, 0);
EXPECT_FALSE(layout->OnEvent(HoverEvent(0, 0)));
EXPECT_EQ(on_enter_1, 2);
EXPECT_EQ(on_enter_2, 1);
EXPECT_EQ(on_leave_1, 1);
EXPECT_EQ(on_leave_2, 1);
}
TEST(HoverableTest, BasicBoolCallback) {
bool hover_1 = false;
bool hover_2 = false;
auto c1 = Hoverable(BasicComponent(), [&](bool hover) { hover_1 = hover; });
auto c2 = Hoverable(BasicComponent(), [&](bool hover) { hover_2 = hover; });
auto layout = Container::Horizontal({c1, c2});
auto screen = Screen(8, 2);
Render(screen, layout->Render());
EXPECT_FALSE(hover_1);
EXPECT_FALSE(hover_2);
EXPECT_FALSE(layout->OnEvent(HoverEvent(0, 0)));
EXPECT_TRUE(hover_1);
EXPECT_FALSE(hover_2);
EXPECT_FALSE(layout->OnEvent(HoverEvent(1, 0)));
EXPECT_TRUE(hover_1);
EXPECT_FALSE(hover_2);
EXPECT_FALSE(layout->OnEvent(HoverEvent(2, 0)));
EXPECT_TRUE(hover_1);
EXPECT_FALSE(hover_2);
EXPECT_FALSE(layout->OnEvent(HoverEvent(3, 0)));
EXPECT_FALSE(hover_1);
EXPECT_TRUE(hover_2);
EXPECT_FALSE(layout->OnEvent(HoverEvent(0, 0)));
EXPECT_TRUE(hover_1);
EXPECT_FALSE(hover_2);
}
TEST(HoverableTest, Coverage) {
bool hover_1 = false;
bool hover_2 = false;
int on_enter = 0;
int on_leave = 0;
auto c1 = BasicComponent();
c1 |= Hoverable(&hover_1);
c1 |= Hoverable([&](bool hover) { hover_2 = hover; });
c1 |= Hoverable([&] { on_enter++; }, [&] { on_leave++; });
auto c2 = BasicComponent();
auto layout = Container::Horizontal({c1, c2});
auto screen = Screen(8, 2);
Render(screen, layout->Render());
EXPECT_FALSE(hover_1);
EXPECT_FALSE(hover_2);
EXPECT_EQ(on_enter, 0);
EXPECT_EQ(on_leave, 0);
EXPECT_FALSE(layout->OnEvent(HoverEvent(0, 0)));
EXPECT_TRUE(hover_1);
EXPECT_TRUE(hover_2);
EXPECT_EQ(on_enter, 1);
EXPECT_EQ(on_leave, 0);
EXPECT_FALSE(layout->OnEvent(HoverEvent(1, 0)));
EXPECT_TRUE(hover_1);
EXPECT_TRUE(hover_2);
EXPECT_EQ(on_enter, 1);
EXPECT_EQ(on_leave, 0);
EXPECT_FALSE(layout->OnEvent(HoverEvent(3, 0)));
EXPECT_FALSE(hover_1);
EXPECT_FALSE(hover_2);
EXPECT_EQ(on_enter, 1);
EXPECT_EQ(on_leave, 1);
EXPECT_FALSE(layout->OnEvent(HoverEvent(0, 0)));
EXPECT_TRUE(hover_1);
EXPECT_TRUE(hover_2);
EXPECT_EQ(on_enter, 2);
EXPECT_EQ(on_leave, 1);
}
} // namespace
} // namespace ftxui
// Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@@ -1,8 +1,8 @@
#include <algorithm> // for max, min
#include <cstddef> // for size_t
#include <functional> // for function
#include <memory> // for shared_ptr, allocator
#include <string> // for string, wstring
#include <memory> // for shared_ptr
#include <string> // for string, allocator
#include <utility> // for move
#include <vector> // for vector
@@ -10,12 +10,12 @@
#include "ftxui/component/component.hpp" // for Make, Input
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for InputOption
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Backspace, Event::Custom, Event::Delete, Event::End, Event::Home, Event::Return
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowLeftCtrl, Event::ArrowRight, Event::ArrowRightCtrl, Event::Backspace, Event::Custom, Event::Delete, Event::End, Event::Home, Event::Return
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
#include "ftxui/component/screen_interactive.hpp" // for Component
#include "ftxui/dom/elements.hpp" // for operator|, text, Element, reflect, inverted, Decorator, flex, focus, hbox, size, bold, dim, frame, select, EQUAL, HEIGHT
#include "ftxui/dom/elements.hpp" // for operator|, text, Element, reflect, operator|=, flex, inverted, hbox, size, bold, dim, focus, focusCursorBarBlinking, frame, select, Decorator, EQUAL, HEIGHT
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/string.hpp" // for GlyphPosition, GlyphCount, to_string, CellToGlyphIndex, to_wstring
#include "ftxui/screen/string.hpp" // for GlyphPosition, WordBreakProperty, GlyphCount, Utf8ToWordBreakProperty, CellToGlyphIndex, WordBreakProperty::ALetter, WordBreakProperty::CR, WordBreakProperty::Double_Quote, WordBreakProperty::Extend, WordBreakProperty::ExtendNumLet, WordBreakProperty::Format, WordBreakProperty::Hebrew_Letter, WordBreakProperty::Katakana, WordBreakProperty::LF, WordBreakProperty::MidLetter, WordBreakProperty::MidNum, WordBreakProperty::MidNumLet, WordBreakProperty::Newline, WordBreakProperty::Numeric, WordBreakProperty::Regional_Indicator, WordBreakProperty::Single_Quote, WordBreakProperty::WSegSpace, WordBreakProperty::ZWJ
#include "ftxui/screen/util.hpp" // for clamp
#include "ftxui/util/ref.hpp" // for StringRef, Ref, ConstStringRef
@@ -23,6 +23,36 @@ namespace ftxui {
namespace {
// Group together several propertiej so they appear to form a similar group.
// For instance, letters are grouped with number and form a single word.
bool IsWordCharacter(WordBreakProperty property) {
switch (property) {
case WordBreakProperty::ALetter:
case WordBreakProperty::Hebrew_Letter:
case WordBreakProperty::Katakana:
case WordBreakProperty::Numeric:
return true;
case WordBreakProperty::CR:
case WordBreakProperty::Double_Quote:
case WordBreakProperty::LF:
case WordBreakProperty::MidLetter:
case WordBreakProperty::MidNum:
case WordBreakProperty::MidNumLet:
case WordBreakProperty::Newline:
case WordBreakProperty::Single_Quote:
case WordBreakProperty::WSegSpace:
// Unsure:
case WordBreakProperty::Extend:
case WordBreakProperty::ExtendNumLet:
case WordBreakProperty::Format:
case WordBreakProperty::Regional_Indicator:
case WordBreakProperty::ZWJ:
return false;
}
return true; // NOT_REACHED();
}
std::string PasswordField(size_t size) {
std::string out;
out.reserve(2 * size);
@@ -57,49 +87,51 @@ class InputBase : public ComponentBase {
if (option_->password()) {
password_content = PasswordField(content_->size());
}
std::string& content = option_->password() ? password_content : *content_;
const std::string& content =
option_->password() ? password_content : *content_;
int size = GlyphCount(content);
const int size = GlyphCount(content);
cursor_position() = std::max(0, std::min<int>(size, cursor_position()));
auto main_decorator = flex | ftxui::size(HEIGHT, EQUAL, 1);
bool is_focused = Focused();
const bool is_focused = Focused();
// placeholder.
if (size == 0) {
bool hovered = hovered_;
Decorator decorator = dim | main_decorator;
auto element = text(*placeholder_) | dim | main_decorator | reflect(box_);
if (is_focused) {
decorator = decorator | focus;
element |= focus;
}
if (hovered || is_focused) {
decorator = decorator | inverted;
if (hovered_ || is_focused) {
element |= inverted;
}
return text(*placeholder_) | decorator | reflect(box_);
return element;
}
// Not focused.
if (!is_focused) {
auto element = text(content) | main_decorator | reflect(box_);
if (hovered_) {
return text(content) | main_decorator | inverted | reflect(box_);
} else {
return text(content) | main_decorator | reflect(box_);
element |= inverted;
}
return element;
}
int index_before_cursor = GlyphPosition(content, cursor_position());
int index_after_cursor = GlyphPosition(content, 1, index_before_cursor);
std::string part_before_cursor = content.substr(0, index_before_cursor);
const int index_before_cursor = GlyphPosition(content, cursor_position());
const int index_after_cursor =
GlyphPosition(content, 1, index_before_cursor);
const std::string part_before_cursor =
content.substr(0, index_before_cursor);
std::string part_at_cursor = " ";
if (cursor_position() < size) {
part_at_cursor = content.substr(index_before_cursor,
index_after_cursor - index_before_cursor);
}
std::string part_after_cursor = content.substr(index_after_cursor);
auto focused = (is_focused || hovered_) ? focus : select;
const std::string part_after_cursor = content.substr(index_after_cursor);
auto focused = (is_focused || hovered_) ? focusCursorBarBlinking : select;
return hbox({
text(part_before_cursor),
text(part_at_cursor) | focused | inverted | reflect(cursor_box_),
text(part_at_cursor) | focused | reflect(cursor_box_),
text(part_after_cursor),
}) |
flex | frame | bold | main_decorator | reflect(box_);
@@ -113,15 +145,13 @@ class InputBase : public ComponentBase {
return OnMouseEvent(event);
}
std::string c;
// Backspace.
if (event == Event::Backspace) {
if (cursor_position() == 0) {
return false;
}
size_t start = GlyphPosition(*content_, cursor_position() - 1);
size_t end = GlyphPosition(*content_, cursor_position());
const size_t start = GlyphPosition(*content_, cursor_position() - 1);
const size_t end = GlyphPosition(*content_, cursor_position());
content_->erase(start, end - start);
cursor_position()--;
option_->on_change();
@@ -133,8 +163,8 @@ class InputBase : public ComponentBase {
if (cursor_position() == int(content_->size())) {
return false;
}
size_t start = GlyphPosition(*content_, cursor_position());
size_t end = GlyphPosition(*content_, cursor_position() + 1);
const size_t start = GlyphPosition(*content_, cursor_position());
const size_t end = GlyphPosition(*content_, cursor_position() + 1);
content_->erase(start, end - start);
option_->on_change();
return true;
@@ -150,6 +180,7 @@ class InputBase : public ComponentBase {
return false;
}
// Arrow
if (event == Event::ArrowLeft && cursor_position() > 0) {
cursor_position()--;
return true;
@@ -161,6 +192,16 @@ class InputBase : public ComponentBase {
return true;
}
// CTRL + Arrow:
if (event == Event::ArrowLeftCtrl) {
HandleLeftCtrl();
return true;
}
if (event == Event::ArrowRightCtrl) {
HandleRightCtrl();
return true;
}
if (event == Event::Home) {
cursor_position() = 0;
return true;
@@ -173,7 +214,7 @@ class InputBase : public ComponentBase {
// Content
if (event.is_character()) {
size_t start = GlyphPosition(*content_, cursor_position());
const size_t start = GlyphPosition(*content_, cursor_position());
content_->insert(start, event.character());
cursor_position()++;
option_->on_change();
@@ -183,6 +224,39 @@ class InputBase : public ComponentBase {
}
private:
void HandleLeftCtrl() {
auto properties = Utf8ToWordBreakProperty(*content_);
// Move left, as long as left is not a word character.
while (cursor_position() > 0 &&
!IsWordCharacter(properties[cursor_position() - 1])) {
cursor_position()--;
}
// Move left, as long as left is a word character:
while (cursor_position() > 0 &&
IsWordCharacter(properties[cursor_position() - 1])) {
cursor_position()--;
}
}
void HandleRightCtrl() {
auto properties = Utf8ToWordBreakProperty(*content_);
const int max = (int)properties.size();
// Move right, as long as right is not a word character.
while (cursor_position() < max &&
!IsWordCharacter(properties[cursor_position()])) {
cursor_position()++;
}
// Move right, as long as right is a word character:
while (cursor_position() < max &&
IsWordCharacter(properties[cursor_position()])) {
cursor_position()++;
}
}
bool OnMouseEvent(Event event) {
hovered_ =
box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event);
@@ -213,7 +287,8 @@ class InputBase : public ComponentBase {
if (mapping[original_cell] != original_glyph) {
original_cell = mapping.size();
}
int target_cell = int(original_cell) + event.mouse().x - cursor_box_.x_min;
const int target_cell =
int(original_cell) + event.mouse().x - cursor_box_.x_min;
int target_glyph = target_cell < (int)mapping.size() ? mapping[target_cell]
: (int)mapping.size();
target_glyph = util::clamp(target_glyph, 0, GlyphCount(*content_));

View File

@@ -1,5 +1,4 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <gtest/gtest.h>
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string
@@ -12,7 +11,6 @@
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Fixed, Screen, Pixel
#include "ftxui/util/ref.hpp" // for Ref
#include "gtest/gtest_pred_impl.h" // for EXPECT_EQ, Test, TEST
namespace ftxui {
@@ -371,6 +369,124 @@ TEST(InputTest, MouseClickComplex) {
EXPECT_EQ(option.cursor_position(), 4u);
}
TEST(InputTest, CtrlArrowLeft) {
std::string content = "word word 测ord wo测d word";
// 0 5 10 15 20
std::string placeholder;
auto option = InputOption();
option.cursor_position = 22;
auto input = Input(&content, &placeholder, &option);
// Use CTRL+Left several time
EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
EXPECT_EQ(option.cursor_position(), 20u);
EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
EXPECT_EQ(option.cursor_position(), 15u);
EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
EXPECT_EQ(option.cursor_position(), 10u);
EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
EXPECT_EQ(option.cursor_position(), 5u);
EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
EXPECT_EQ(option.cursor_position(), 0u);
EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
EXPECT_EQ(option.cursor_position(), 0u);
}
TEST(InputTest, CtrlArrowLeft2) {
std::string content = " word word 测ord wo测d word ";
// 0 3 6 9 12 15 18 21 24 27 30 33
std::string placeholder;
auto option = InputOption();
option.cursor_position = 33;
auto input = Input(&content, &placeholder, &option);
// Use CTRL+Left several time
EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
EXPECT_EQ(option.cursor_position(), 27u);
EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
EXPECT_EQ(option.cursor_position(), 21u);
EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
EXPECT_EQ(option.cursor_position(), 15u);
EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
EXPECT_EQ(option.cursor_position(), 9u);
EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
EXPECT_EQ(option.cursor_position(), 3u);
EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
EXPECT_EQ(option.cursor_position(), 0u);
EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
EXPECT_EQ(option.cursor_position(), 0u);
}
TEST(InputTest, CtrlArrowRight) {
std::string content = "word word 测ord wo测d word";
// 0 5 10 15 20
std::string placeholder;
auto option = InputOption();
option.cursor_position = 2;
auto input = Input(&content, &placeholder, &option);
// Use CTRL+Left several time
EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
EXPECT_EQ(option.cursor_position(), 4);
EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
EXPECT_EQ(option.cursor_position(), 9);
EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
EXPECT_EQ(option.cursor_position(), 14u);
EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
EXPECT_EQ(option.cursor_position(), 19u);
EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
EXPECT_EQ(option.cursor_position(), 24u);
EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
EXPECT_EQ(option.cursor_position(), 24u);
}
TEST(InputTest, CtrlArrowRight2) {
std::string content = " word word 测ord wo测d word ";
// 0 3 6 9 12 15 18 21 24 27 30 33
std::string placeholder;
auto option = InputOption();
option.cursor_position = 0;
auto input = Input(&content, &placeholder, &option);
// Use CTRL+Left several time
EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
EXPECT_EQ(option.cursor_position(), 7u);
EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
EXPECT_EQ(option.cursor_position(), 13u);
EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
EXPECT_EQ(option.cursor_position(), 19u);
EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
EXPECT_EQ(option.cursor_position(), 25u);
EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
EXPECT_EQ(option.cursor_position(), 31u);
EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
EXPECT_EQ(option.cursor_position(), 34u);
EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
EXPECT_EQ(option.cursor_position(), 34u);
}
} // namespace ftxui
// Copyright 2021 Arthur Sonzogni. All rights reserved.

View File

@@ -0,0 +1,48 @@
#include "ftxui/component/loop.hpp"
#include <utility> // for move
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive, Component
namespace ftxui {
// NOLINTNEXTLINE
Loop::Loop(ScreenInteractive* screen, Component component)
: screen_(screen), component_(std::move(component)) {
screen_->PreMain();
}
Loop::~Loop() {
screen_->PostMain();
}
bool Loop::HasQuitted() {
return screen_->HasQuitted();
}
/// @brief Execute the loop. Make the `component` to process every pending
/// tasks/events. A new frame might be drawn if the previous was invalidated.
/// Return true until the loop hasn't completed.
void Loop::RunOnce() {
screen_->RunOnce(component_);
}
/// @brief Wait for at least one event to be handled and execute
/// `Loop::RunOnce()`.
void Loop::RunOnceBlocking() {
screen_->RunOnceBlocking(component_);
}
/// Execute the loop, blocking the current thread, up until the loop has
/// quitted.
void Loop::Run() {
while (!HasQuitted()) {
RunOnceBlocking();
}
}
} // namespace ftxui
// Copyright 2022 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@@ -1,5 +1,6 @@
#include <functional> // for function
#include <memory> // for make_unique, __shared_ptr_access, __shared_ptr_access<>::element_type, shared_ptr
#include <type_traits> // for remove_reference, remove_reference<>::type
#include <utility> // for move
#include "ftxui/component/component.hpp" // for ComponentDecorator, Maybe, Make
@@ -43,7 +44,7 @@ Component Maybe(Component child, std::function<bool()> show) {
/// ### Example
///
/// ```cpp
/// auto component = Renderer([]{ return "Hello World!"; });
/// auto component = Renderer([]{ return text("Hello World!"); });
/// auto maybe_component = component | Maybe([&]{ return counter == 42; });
/// ```
ComponentDecorator Maybe(std::function<bool()> show) {
@@ -60,7 +61,7 @@ ComponentDecorator Maybe(std::function<bool()> show) {
/// ### Example
///
/// ```cpp
/// auto component = Renderer([]{ return "Hello World!"; });
/// auto component = Renderer([]{ return text("Hello World!"); });
/// auto maybe_component = Maybe(component, &show);
/// ```
Component Maybe(Component child, const bool* show) {
@@ -74,7 +75,7 @@ Component Maybe(Component child, const bool* show) {
/// ### Example
///
/// ```cpp
/// auto component = Renderer([]{ return "Hello World!"; });
/// auto component = Renderer([]{ return text("Hello World!"); });
/// auto maybe_component = component | Maybe(&show);
/// ```
ComponentDecorator Maybe(const bool* show) {

View File

@@ -1,16 +1,16 @@
#include <algorithm> // for max, reverse
#include <algorithm> // for max, fill_n, reverse
#include <chrono> // for milliseconds
#include <functional> // for function
#include <memory> // for allocator, shared_ptr, allocator_traits<>::value_type, swap
#include <string> // for char_traits, operator+, string, basic_string
#include <memory> // for allocator_traits<>::value_type, swap
#include <string> // for operator+, string
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include "ftxui/component/animation.hpp" // for Animator, Linear, Params (ptr only)
#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_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for MenuOption, MenuEntryOption, MenuOption::Direction, UnderlineOption, AnimatedColorOption, AnimatedColorsOption, MenuOption::Down, MenuOption::Left, MenuOption::Right, MenuOption::Up
#include "ftxui/component/component_options.hpp" // for MenuOption, MenuEntryOption, MenuOption::Direction, UnderlineOption, AnimatedColorOption, AnimatedColorsOption, EntryState, MenuOption::Down, MenuOption::Left, MenuOption::Right, MenuOption::Up
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp, Event::End, Event::Home, Event::PageDown, Event::PageUp, Event::Return, Event::Tab, Event::TabReverse
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Released, Mouse::WheelDown, Mouse::WheelUp, Mouse::None
#include "ftxui/component/screen_interactive.hpp" // for Component
@@ -83,8 +83,13 @@ class MenuBase : public ComponentBase {
}
void Clamp() {
if (*selected_ != selected_previous_) {
SelectedTakeFocus();
}
boxes_.resize(size());
*selected_ = util::clamp(*selected_, 0, size() - 1);
selected_previous_ = util::clamp(selected_previous_, 0, size() - 1);
selected_focus_ = util::clamp(selected_focus_, 0, size() - 1);
focused_entry() = util::clamp(focused_entry(), 0, size() - 1);
}
@@ -104,7 +109,7 @@ class MenuBase : public ComponentBase {
UpdateAnimationTarget();
Elements elements;
bool is_menu_focused = Focused();
const bool is_menu_focused = Focused();
if (option_->elements_prefix) {
elements.push_back(option_->elements_prefix());
}
@@ -112,20 +117,20 @@ class MenuBase : public ComponentBase {
if (i != 0 && option_->elements_infix) {
elements.push_back(option_->elements_infix());
}
bool is_focused = (focused_entry() == i) && is_menu_focused;
bool is_selected = (*selected_ == i);
const bool is_focused = (focused_entry() == i) && is_menu_focused;
const bool is_selected = (*selected_ == i);
auto focus_management = !is_selected ? nothing
: is_menu_focused ? focus
: nothing;
EntryState state = {
const EntryState state = {
entries_[i],
false,
is_selected,
is_focused,
};
Element element =
auto focus_management =
is_menu_focused && (selected_focus_ == i) ? focus : nothing;
const Element element =
(option_->entries.transform ? option_->entries.transform
: DefaultOptionTransform) //
(state);
@@ -140,7 +145,7 @@ class MenuBase : public ComponentBase {
std::reverse(elements.begin(), elements.end());
}
Element bar =
const Element bar =
IsHorizontal() ? hbox(std::move(elements)) : vbox(std::move(elements));
if (!option_->underline.enabled) {
@@ -166,6 +171,11 @@ class MenuBase : public ComponentBase {
}
}
void SelectedTakeFocus() {
selected_previous_ = *selected_;
selected_focus_ = *selected_;
}
void OnUp() {
switch (option_->direction) {
case MenuOption::Direction::Up:
@@ -234,7 +244,7 @@ class MenuBase : public ComponentBase {
}
if (Focused()) {
int old_selected = *selected_;
const int old_selected = *selected_;
if (event == Event::ArrowUp || event == Event::Character('k')) {
OnUp();
}
@@ -270,6 +280,7 @@ class MenuBase : public ComponentBase {
if (*selected_ != old_selected) {
focused_entry() = *selected_;
SelectedTakeFocus();
OnChange();
return true;
}
@@ -307,6 +318,7 @@ class MenuBase : public ComponentBase {
event.mouse().motion == Mouse::Released) {
if (*selected_ != i) {
*selected_ = i;
selected_previous_ = *selected_;
OnChange();
}
return true;
@@ -319,7 +331,7 @@ class MenuBase : public ComponentBase {
if (!box_.Contain(event.mouse().x, event.mouse().y)) {
return false;
}
int old_selected = *selected_;
const int old_selected = *selected_;
if (event.mouse().button == Mouse::WheelUp) {
(*selected_)--;
@@ -331,6 +343,7 @@ class MenuBase : public ComponentBase {
*selected_ = util::clamp(*selected_, 0, size() - 1);
if (*selected_ != old_selected) {
SelectedTakeFocus();
OnChange();
}
return true;
@@ -360,10 +373,10 @@ class MenuBase : public ComponentBase {
}
}
bool is_menu_focused = Focused();
const bool is_menu_focused = Focused();
for (int i = 0; i < size(); ++i) {
bool is_focused = (focused_entry() == i) && is_menu_focused;
bool is_selected = (*selected_ == i);
const bool is_focused = (focused_entry() == i) && is_menu_focused;
const bool is_selected = (*selected_ == i);
float target = is_selected ? 1.F : is_focused ? 0.5F : 0.F; // NOLINT
if (animator_background_[i].to() != target) {
animator_background_[i] = animation::Animator(
@@ -434,7 +447,7 @@ class MenuBase : public ComponentBase {
if (boxes_.empty()) {
return 0.F;
}
int value = IsHorizontal() ? boxes_[*selected_].x_min - box_.x_min
const int value = IsHorizontal() ? boxes_[*selected_].x_min - box_.x_min
: boxes_[*selected_].y_min - box_.y_min;
return float(value);
}
@@ -442,14 +455,16 @@ class MenuBase : public ComponentBase {
if (boxes_.empty()) {
return 0.F;
}
int value = IsHorizontal() ? boxes_[*selected_].x_max - box_.x_min
const int value = IsHorizontal() ? boxes_[*selected_].x_max - box_.x_min
: boxes_[*selected_].y_max - box_.y_min;
return float(value);
}
protected:
ConstStringListRef entries_;
int* selected_ = nullptr;
int* selected_;
int selected_previous_ = *selected_;
int selected_focus_ = *selected_;
Ref<MenuOption> option_;
std::vector<Box> boxes_;
@@ -542,17 +557,17 @@ Component MenuEntry(ConstStringRef label, Ref<MenuEntryOption> option) {
private:
Element Render() override {
bool focused = Focused();
const bool focused = Focused();
UpdateAnimationTarget();
EntryState state = {
const EntryState state = {
*label_,
false,
hovered_,
focused,
};
Element element =
const Element element =
(option_->transform ? option_->transform : DefaultOptionTransform) //
(state);
@@ -561,7 +576,7 @@ Component MenuEntry(ConstStringRef label, Ref<MenuEntryOption> option) {
}
void UpdateAnimationTarget() {
bool focused = Focused();
const bool focused = Focused();
float target = focused ? 1.F : hovered_ ? 0.5F : 0.F; // NOLINT
if (target == animator_background_.to()) {
return;

View File

@@ -1,8 +1,7 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <gtest/gtest.h> // for Test, EXPECT_EQ, Message, TestPartResult, TestInfo (ptr only), TEST
#include <chrono> // for operator""s, chrono_literals
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string
#include <string> // for string, basic_string
#include <vector> // for vector
#include "ftxui/component/animation.hpp" // for Duration, Params
@@ -13,7 +12,6 @@
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
#include "ftxui/util/ref.hpp" // for Ref
#include "gtest/gtest_pred_impl.h" // for EXPECT_EQ, Test, TEST
namespace ftxui {

View File

@@ -0,0 +1,65 @@
#include <ftxui/component/event.hpp> // for Event
#include <ftxui/dom/elements.hpp> // for operator|, Element, center, clear_under, dbox
#include <memory> // for __shared_ptr_access, shared_ptr
#include <utility> // for move
#include "ftxui/component/component.hpp" // for Make, Tab, ComponentDecorator, Modal
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
namespace ftxui {
// Add a |modal| window on top of the |main| component. It is shown one on the
// top of the other when |show_modal| is true.
/// @ingroup component
// NOLINTNEXTLINE
Component Modal(Component main, Component modal, const bool* show_modal) {
class Impl : public ComponentBase {
public:
explicit Impl(Component main, Component modal, const bool* show_modal)
: main_(std::move(main)),
modal_(std::move(modal)),
show_modal_(show_modal) {
Add(Container::Tab({main_, modal_}, &selector_));
}
private:
Element Render() override {
selector_ = *show_modal_;
auto document = main_->Render();
if (*show_modal_) {
document = dbox({
document,
modal_->Render() | clear_under | center,
});
}
return document;
}
bool OnEvent(Event event) override {
selector_ = *show_modal_;
return ComponentBase::OnEvent(event);
}
Component main_;
Component modal_;
const bool* show_modal_;
int selector_ = *show_modal_;
};
return Make<Impl>(main, modal, show_modal);
}
// Decorate a component. Add a |modal| window on top of it. It is shown one on
// the top of the other when |show_modal| is true.
/// @ingroup component
// NOLINTNEXTLINE
ComponentDecorator Modal(Component modal, const bool* show_modal) {
return [modal, show_modal](Component main) {
return Modal(std::move(main), modal, show_modal);
};
}
} // namespace ftxui
// Copyright 2022 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@@ -0,0 +1,45 @@
#include <gtest/gtest.h>
#include <ftxui/dom/elements.hpp> // for Element, operator|, text, border
#include <memory> // for shared_ptr, allocator, __shared_ptr_access
#include "ftxui/component/component.hpp" // for Renderer, Modal
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
namespace ftxui {
TEST(ModalTest, Basic) {
auto main = Renderer([] { return text("main") | border; });
auto modal = Renderer([] { return text("modal") | border; });
bool show_modal = false;
auto component = Modal(main, modal, &show_modal);
Screen screen(10, 7);
Render(screen, component->Render());
EXPECT_EQ(screen.ToString(),
"╭────────╮\r\n"
"│main │\r\n"
"│ │\r\n"
"│ │\r\n"
"│ │\r\n"
"│ │\r\n"
"╰────────╯");
show_modal = true;
Render(screen, component->Render());
EXPECT_EQ(screen.ToString(),
"╭────────╮\r\n"
"│main │\r\n"
"│╭─────╮ │\r\n"
"││modal│ │\r\n"
"│╰─────╯ │\r\n"
"│ │\r\n"
"╰────────╯");
}
} // namespace ftxui
// Copyright 2022 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@@ -1,6 +1,5 @@
#include <algorithm> // for max
#include <functional> // for function
#include <memory> // for shared_ptr, allocator_traits<>::value_type
#include <memory> // for allocator_traits<>::value_type
#include <utility> // for move
#include <vector> // for vector
@@ -27,18 +26,16 @@ class RadioboxBase : public ComponentBase {
RadioboxBase(ConstStringListRef entries,
int* selected,
Ref<RadioboxOption> option)
: entries_(entries), selected_(selected), option_(std::move(option)) {
hovered_ = *selected_;
}
: entries_(entries), selected_(selected), option_(std::move(option)) {}
private:
Element Render() override {
Clamp();
Elements elements;
bool is_menu_focused = Focused();
const bool is_menu_focused = Focused();
for (int i = 0; i < size(); ++i) {
bool is_focused = (focused_entry() == i) && is_menu_focused;
bool is_selected = (hovered_ == i);
const bool is_focused = (focused_entry() == i) && is_menu_focused;
const bool is_selected = (hovered_ == i);
auto focus_management = !is_selected ? nothing
: is_menu_focused ? focus
: select;
@@ -69,7 +66,7 @@ class RadioboxBase : public ComponentBase {
}
if (Focused()) {
int old_hovered = hovered_;
const int old_hovered = hovered_;
if (event == Event::ArrowUp || event == Event::Character('k')) {
(hovered_)--;
}
@@ -106,8 +103,8 @@ class RadioboxBase : public ComponentBase {
if (event == Event::Character(' ') || event == Event::Return) {
*selected_ = hovered_;
//*selected_ = focused_entry();
option_->on_change();
return true;
}
return false;
@@ -144,7 +141,7 @@ class RadioboxBase : public ComponentBase {
return false;
}
int old_hovered = hovered_;
const int old_hovered = hovered_;
if (event.mouse().button == Mouse::WheelUp) {
(hovered_)--;
@@ -175,7 +172,7 @@ class RadioboxBase : public ComponentBase {
ConstStringListRef entries_;
int* selected_;
int hovered_;
int hovered_ = *selected_;
std::vector<Box> boxes_;
Box box_;
Ref<RadioboxOption> option_;

View File

@@ -1,118 +1,276 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <gtest/gtest.h> // for AssertionResult, Message, TestPartResult, EXPECT_EQ, EXPECT_TRUE, Test, TestInfo (ptr only), EXPECT_FALSE, TEST
#include <ftxui/dom/elements.hpp> // for yframe
#include <ftxui/dom/node.hpp> // for Render
#include <ftxui/screen/screen.hpp> // for Screen
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string
#include <string> // for string, basic_string
#include <vector> // for vector
#include "ftxui/component/component.hpp" // for Radiobox
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component.hpp" // for Radiobox, operator|
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "ftxui/component/component_options.hpp" // for RadioboxOption
#include "ftxui/component/event.hpp" // for Event, Event::Return, Event::ArrowDown, Event::ArrowUp, Event::Tab, Event::TabReverse
#include "ftxui/component/event.hpp" // for Event, Event::Return, Event::ArrowDown, Event::End, Event::Home, Event::Tab, Event::TabReverse, Event::PageDown, Event::PageUp, Event::ArrowUp
#include "ftxui/util/ref.hpp" // for Ref
#include "gtest/gtest_pred_impl.h" // for EXPECT_EQ, Test, TEST
namespace ftxui {
TEST(RadioboxTest, Navigation) {
TEST(RadioboxTest, NavigationArrow) {
int selected = 0;
std::vector<std::string> entries = {"1", "2", "3"};
auto radiobox = Radiobox(&entries, &selected);
// With arrow key.
// Down + Return
EXPECT_EQ(selected, 0);
radiobox->OnEvent(Event::ArrowDown);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::ArrowDown);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 2);
radiobox->OnEvent(Event::ArrowDown);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 2);
radiobox->OnEvent(Event::ArrowUp);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::ArrowUp);
radiobox->OnEvent(Event::Return);
EXPECT_TRUE(radiobox->OnEvent(Event::ArrowDown));
EXPECT_EQ(selected, 0);
radiobox->OnEvent(Event::ArrowUp);
radiobox->OnEvent(Event::Return);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::ArrowDown));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
EXPECT_FALSE(radiobox->OnEvent(Event::ArrowDown));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
// Up + Return
EXPECT_TRUE(radiobox->OnEvent(Event::ArrowUp));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::ArrowUp));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 0);
EXPECT_FALSE(radiobox->OnEvent(Event::ArrowUp));
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 0);
}
TEST(RadioboxTest, NavigationArrowVim) {
int selected = 0;
std::vector<std::string> entries = {"1", "2", "3"};
auto radiobox = Radiobox(&entries, &selected);
// J + Return
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::Character('j')));
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Character('j')));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
EXPECT_FALSE(radiobox->OnEvent(Event::Character('j')));
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
// K + Return
EXPECT_TRUE(radiobox->OnEvent(Event::Character('k')));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Character('k')));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 0);
EXPECT_FALSE(radiobox->OnEvent(Event::Character('k')));
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 0);
}
TEST(RadioboxTest, NavigationTab) {
int selected = 0;
std::vector<std::string> entries = {"1", "2", "3"};
auto radiobox = Radiobox(&entries, &selected);
// Tab + Return
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::Tab));
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Tab));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::Tab));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::Tab));
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Tab));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
// TabReverse + Return
EXPECT_TRUE(radiobox->OnEvent(Event::TabReverse));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::TabReverse));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::TabReverse));
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::TabReverse));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::TabReverse));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
}
TEST(RadioboxTest, NavigationHome) {
int selected = 0;
std::vector<std::string> entries = {"1", "2", "3"};
auto radiobox = Radiobox(&entries, &selected);
selected = 0;
EXPECT_FALSE(radiobox->OnEvent(Event::Home));
EXPECT_EQ(selected, 0);
// With vim like characters.
EXPECT_EQ(selected, 0);
radiobox->OnEvent(Event::Character('j'));
radiobox->OnEvent(Event::Return);
selected = 1;
EXPECT_FALSE(radiobox->OnEvent(Event::Home));
EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::Character('j'));
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 2);
radiobox->OnEvent(Event::Character('j'));
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 2);
radiobox->OnEvent(Event::Character('k'));
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::Character('k'));
radiobox->OnEvent(Event::Return);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 0);
radiobox->OnEvent(Event::Character('k'));
radiobox->OnEvent(Event::Return);
EXPECT_FALSE(radiobox->OnEvent(Event::Home));
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 0);
// With more entries
entries = {"1", "2", "3"};
EXPECT_EQ(selected, 0);
radiobox->OnEvent(Event::ArrowDown);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::ArrowDown);
radiobox->OnEvent(Event::Return);
selected = 2;
EXPECT_FALSE(radiobox->OnEvent(Event::Home));
EXPECT_EQ(selected, 2);
radiobox->OnEvent(Event::ArrowDown);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 2);
radiobox->OnEvent(Event::ArrowUp);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::ArrowUp);
radiobox->OnEvent(Event::Return);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 0);
radiobox->OnEvent(Event::ArrowUp);
radiobox->OnEvent(Event::Return);
EXPECT_FALSE(radiobox->OnEvent(Event::Home));
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 0);
}
TEST(RadioboxTest, NavigationPageDown) {
int selected = 0;
std::vector<std::string> entries = {"1", "2", "3", "4", "5", "6"};
auto radiobox = Radiobox(&entries, &selected) | yframe;
Screen screen(1, 3);
Render(screen, radiobox->Render());
EXPECT_TRUE(radiobox->OnEvent(Event::PageDown));
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::PageDown));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 4);
EXPECT_TRUE(radiobox->OnEvent(Event::PageDown));
EXPECT_EQ(selected, 4);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 5);
EXPECT_FALSE(radiobox->OnEvent(Event::PageDown));
EXPECT_EQ(selected, 5);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 5);
}
TEST(RadioboxTest, NavigationPageUp) {
int selected = 5;
std::vector<std::string> entries = {"1", "2", "3", "4", "5", "6"};
auto radiobox = Radiobox(&entries, &selected) | yframe;
Screen screen(1, 3);
Render(screen, radiobox->Render());
EXPECT_TRUE(radiobox->OnEvent(Event::PageUp));
EXPECT_EQ(selected, 5);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 3);
EXPECT_TRUE(radiobox->OnEvent(Event::PageUp));
EXPECT_EQ(selected, 3);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::PageUp));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 0);
// With tab.
EXPECT_FALSE(radiobox->OnEvent(Event::PageUp));
EXPECT_EQ(selected, 0);
radiobox->OnEvent(Event::Tab);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::Tab);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 2);
radiobox->OnEvent(Event::Tab);
radiobox->OnEvent(Event::Return);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 0);
radiobox->OnEvent(Event::Tab);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::Tab);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 2);
radiobox->OnEvent(Event::TabReverse);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::TabReverse);
radiobox->OnEvent(Event::Return);
}
TEST(RadioboxTest, NavigationEnd) {
int selected = 0;
std::vector<std::string> entries = {"1", "2", "3"};
auto radiobox = Radiobox(&entries, &selected);
selected = 0;
EXPECT_TRUE(radiobox->OnEvent(Event::End));
EXPECT_EQ(selected, 0);
radiobox->OnEvent(Event::TabReverse);
radiobox->OnEvent(Event::Return);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
radiobox->OnEvent(Event::TabReverse);
radiobox->OnEvent(Event::Return);
EXPECT_FALSE(radiobox->OnEvent(Event::End));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
selected = 1;
EXPECT_FALSE(radiobox->OnEvent(Event::End));
EXPECT_EQ(selected, 1);
radiobox->OnEvent(Event::TabReverse);
radiobox->OnEvent(Event::Return);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
EXPECT_FALSE(radiobox->OnEvent(Event::End));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
selected = 2;
EXPECT_FALSE(radiobox->OnEvent(Event::End));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
}
TEST(RadioboxTest, EventSpace) {
int selected = 0;
std::vector<std::string> entries = {"1", "2", "3"};
auto radiobox = Radiobox(&entries, &selected);
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::ArrowDown));
EXPECT_EQ(selected, 0);
EXPECT_TRUE(radiobox->OnEvent(Event::Character(' ')));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::ArrowDown));
EXPECT_EQ(selected, 1);
EXPECT_TRUE(radiobox->OnEvent(Event::Character(' ')));
EXPECT_EQ(selected, 2);
EXPECT_FALSE(radiobox->OnEvent(Event::ArrowDown));
EXPECT_EQ(selected, 2);
EXPECT_TRUE(radiobox->OnEvent(Event::Character(' ')));
EXPECT_EQ(selected, 2);
}
TEST(RadioboxTest, RemoveEntries) {
@@ -126,9 +284,9 @@ TEST(RadioboxTest, RemoveEntries) {
EXPECT_EQ(selected, 0);
EXPECT_EQ(focused_entry, 0);
radiobox->OnEvent(Event::ArrowDown);
radiobox->OnEvent(Event::ArrowDown);
radiobox->OnEvent(Event::Return);
EXPECT_TRUE(radiobox->OnEvent(Event::ArrowDown));
EXPECT_TRUE(radiobox->OnEvent(Event::ArrowDown));
EXPECT_TRUE(radiobox->OnEvent(Event::Return));
EXPECT_EQ(selected, 2);
EXPECT_EQ(focused_entry, 2);

View File

@@ -1,10 +1,8 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult
#include <gtest/gtest.h>
#include <thread> // for thread
#include <utility> // for move
#include "ftxui/component/receiver.hpp"
#include "gtest/gtest_pred_impl.h" // for AssertionResult, Test, EXPECT_EQ
namespace ftxui {

View File

@@ -1,5 +1,4 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <gtest/gtest.h>
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include "ftxui/component/component.hpp" // for Renderer, ResizableSplitBottom, ResizableSplitLeft, ResizableSplitRight, ResizableSplitTop
@@ -9,7 +8,6 @@
#include "ftxui/dom/elements.hpp" // for text, Element
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
#include "gtest/gtest_pred_impl.h" // for AssertionResult, Test, EXPECT_EQ, EXPECT_TRUE, TEST
namespace ftxui {

View File

@@ -1,28 +1,32 @@
#include <algorithm> // for copy, max, min
#include <array> // for array
#include <chrono> // for operator-, milliseconds, duration, operator>=, time_point, common_type<>::type
#include <csignal> // for signal, raise, SIGTSTP, SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, SIGWINCH
#include <cstdio> // for fileno, size_t, stdin
#include <chrono> // for operator-, milliseconds, operator>=, duration, common_type<>::type, time_point
#include <csignal> // for signal, SIGTSTP, SIGABRT, SIGWINCH, raise, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, __sighandler_t, size_t
#include <cstdio> // for fileno, stdin
#include <ftxui/component/task.hpp> // for Task, Closure, AnimationTask
#include <ftxui/screen/screen.hpp> // for Pixel, Screen::Cursor, Screen, Screen::Cursor::Hidden
#include <functional> // for function
#include <initializer_list> // for initializer_list
#include <iostream> // for cout, ostream, basic_ostream, operator<<, endl, flush
#include <iostream> // for cout, ostream, operator<<, basic_ostream, endl, flush
#include <stack> // for stack
#include <thread> // for thread, sleep_for
#include <tuple> // for _Swallow_assign, ignore
#include <type_traits> // for decay_t
#include <utility> // for move, swap
#include <variant> // for visit
#include <variant> // for visit, variant
#include <vector> // for vector
#include "ftxui/component/animation.hpp" // for TimePoint, Clock, Duration, Params, RequestAnimationFrame
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/loop.hpp" // for Loop
#include "ftxui/component/receiver.hpp" // for ReceiverImpl, Sender, MakeReceiver, SenderImpl, Receiver
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/component/terminal_input_parser.hpp" // for TerminalInputParser
#include "ftxui/dom/node.hpp" // for Node, Render
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/terminal.hpp" // for Size, Dimensions
#include "ftxui/screen/terminal.hpp" // for Dimensions, Size
#if defined(_WIN32)
#define DEFINE_CONSOLEV2_PROPERTIES
@@ -30,12 +34,12 @@
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <Windows.h>
#include <windows.h>
#ifndef UNICODE
#error Must be compiled in UNICODE mode
#endif
#else
#include <sys/select.h> // for select, FD_ISSET, FD_SET, FD_ZERO, fd_set
#include <sys/select.h> // for select, FD_ISSET, FD_SET, FD_ZERO, fd_set, timeval
#include <termios.h> // for tcsetattr, termios, tcgetattr, TCSANOW, cc_t, ECHO, ICANON, VMIN, VTIME
#include <unistd.h> // for STDIN_FILENO, read
#endif
@@ -100,7 +104,11 @@ void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
// ignore UP key events
if (key_event.bKeyDown == FALSE)
continue;
parser.Add((char)key_event.uChar.UnicodeChar);
std::wstring wstring;
wstring += key_event.uChar.UnicodeChar;
for (auto it : to_string(wstring)) {
parser.Add(it);
}
} break;
case WINDOW_BUFFER_SIZE_EVENT:
out->Send(Event::Special({0}));
@@ -133,8 +141,18 @@ void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
}
}
#else
#include <sys/time.h> // for timeval
extern "C" {
EMSCRIPTEN_KEEPALIVE
void ftxui_on_resize(int columns, int rows) {
Terminal::SetFallbackSize({
columns,
rows,
});
std::raise(SIGWINCH);
}
}
#else // POSIX (Linux & Mac)
int CheckStdinReady(int usec_timeout) {
timeval tv = {0, usec_timeout};
@@ -163,18 +181,88 @@ void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
}
}
}
#endif
std::stack<Closure> on_exit_functions; // NOLINT
void OnExit() {
while (!on_exit_functions.empty()) {
on_exit_functions.top()();
on_exit_functions.pop();
}
}
std::atomic<int> g_signal_exit_count = 0; // NOLINT
#if !defined(_WIN32)
std::atomic<int> g_signal_stop_count = 0; // NOLINT
std::atomic<int> g_signal_resize_count = 0; // NOLINT
#endif
// Async signal safe function
void RecordSignal(int signal) {
switch (signal) {
case SIGABRT:
case SIGFPE:
case SIGILL:
case SIGINT:
case SIGSEGV:
case SIGTERM:
g_signal_exit_count++;
break;
#if !defined(_WIN32)
case SIGTSTP:
g_signal_stop_count++;
break;
case SIGWINCH:
g_signal_resize_count++;
break;
#endif
default:
break;
}
}
void ExecuteSignalHandlers() {
int signal_exit_count = g_signal_exit_count.exchange(0);
while (signal_exit_count--) {
ScreenInteractive::Private::Signal(*g_active_screen, SIGABRT);
}
#if !defined(_WIN32)
int signal_stop_count = g_signal_stop_count.exchange(0);
while (signal_stop_count--) {
ScreenInteractive::Private::Signal(*g_active_screen, SIGTSTP);
}
int signal_resize_count = g_signal_resize_count.exchange(0);
while (signal_resize_count--) {
ScreenInteractive::Private::Signal(*g_active_screen, SIGWINCH);
}
#endif
}
void InstallSignalHandler(int sig) {
auto old_signal_handler = std::signal(sig, RecordSignal);
on_exit_functions.push(
[=] { std::ignore = std::signal(sig, old_signal_handler); });
}
const std::string CSI = "\x1b["; // NOLINT
// DEC: Digital Equipment Corporation
enum class DECMode {
kLineWrap = 7,
kMouseX10 = 9,
kCursor = 25,
kMouseX10 = 9,
kMouseVt200 = 1000,
kMouseVt200Highlight = 1001,
kMouseBtnEventMouse = 1002,
kMouseAnyEvent = 1003,
kMouseUtf8 = 1005,
kMouseSgrExtMode = 1006,
kMouseUrxvtMode = 1015,
@@ -190,7 +278,7 @@ enum class DSRMode {
std::string Serialize(const std::vector<DECMode>& parameters) {
bool first = true;
std::string out;
for (DECMode parameter : parameters) {
for (const DECMode parameter : parameters) {
if (!first) {
out += ";";
}
@@ -215,30 +303,6 @@ std::string DeviceStatusReport(DSRMode ps) {
return CSI + std::to_string(int(ps)) + "n";
}
using SignalHandler = void(int);
std::stack<Closure> on_exit_functions; // NOLINT
void OnExit(int signal) {
(void)signal;
while (!on_exit_functions.empty()) {
on_exit_functions.top()();
on_exit_functions.pop();
}
}
const auto install_signal_handler = [](int sig, SignalHandler handler) {
auto old_signal_handler = std::signal(sig, handler);
on_exit_functions.push([=] { std::signal(sig, old_signal_handler); });
};
Closure g_on_resize = [] {}; // NOLINT
void OnResize(int /* signal */) {
g_on_resize();
}
void OnSigStop(int /*signal*/) {
ScreenInteractive::Private::SigStop(*g_active_screen);
}
class CapturedMouseImpl : public CapturedMouseInterface {
public:
explicit CapturedMouseImpl(std::function<void(void)> callback)
@@ -276,29 +340,54 @@ ScreenInteractive::ScreenInteractive(int dimx,
// static
ScreenInteractive ScreenInteractive::FixedSize(int dimx, int dimy) {
return ScreenInteractive(dimx, dimy, Dimension::Fixed, false);
return {
dimx,
dimy,
Dimension::Fixed,
false,
};
}
// static
ScreenInteractive ScreenInteractive::Fullscreen() {
return ScreenInteractive(0, 0, Dimension::Fullscreen, true);
return {
0,
0,
Dimension::Fullscreen,
true,
};
}
// static
ScreenInteractive ScreenInteractive::TerminalOutput() {
return ScreenInteractive(0, 0, Dimension::TerminalOutput, false);
return {
0,
0,
Dimension::TerminalOutput,
false,
};
}
// static
ScreenInteractive ScreenInteractive::FitComponent() {
return ScreenInteractive(0, 0, Dimension::FitComponent, false);
return {
0,
0,
Dimension::FitComponent,
false,
};
}
void ScreenInteractive::Post(Task task) {
if (!quit_) {
task_sender_->Send(std::move(task));
// Task/Events sent toward inactive screen or screen waiting to become
// inactive are dropped.
if (!task_sender_) {
return;
}
task_sender_->Send(std::move(task));
}
void ScreenInteractive::PostEvent(Event event) {
Post(event);
}
@@ -310,8 +399,8 @@ void ScreenInteractive::RequestAnimationFrame() {
animation_requested_ = true;
auto now = animation::Clock::now();
const auto time_histeresis = std::chrono::milliseconds(33);
if (now - previous_animation_time >= time_histeresis) {
previous_animation_time = now;
if (now - previous_animation_time_ >= time_histeresis) {
previous_animation_time_ = now;
}
}
@@ -325,34 +414,52 @@ CapturedMouse ScreenInteractive::CaptureMouse() {
}
void ScreenInteractive::Loop(Component component) { // NOLINT
class Loop loop(this, std::move(component));
loop.Run();
}
bool ScreenInteractive::HasQuitted() {
return task_receiver_->HasQuitted();
}
void ScreenInteractive::PreMain() {
// Suspend previously active screen:
if (g_active_screen) {
std::swap(suspended_screen_, g_active_screen);
std::cout << suspended_screen_->reset_cursor_position
<< suspended_screen_->ResetPosition(/*clear=*/true);
// Reset cursor position to the top of the screen and clear the screen.
suspended_screen_->ResetCursorPosition();
std::cout << suspended_screen_->ResetPosition(/*clear=*/true);
suspended_screen_->dimx_ = 0;
suspended_screen_->dimy_ = 0;
// Reset dimensions to force drawing the screen again next time:
suspended_screen_->Uninstall();
}
// This screen is now active:
g_active_screen = this;
g_active_screen->Install();
g_active_screen->Main(std::move(component));
g_active_screen->Uninstall();
g_active_screen = nullptr;
previous_animation_time_ = animation::Clock::now();
}
void ScreenInteractive::PostMain() {
// Put cursor position at the end of the drawing.
std::cout << reset_cursor_position;
ResetCursorPosition();
g_active_screen = nullptr;
// Restore suspended screen.
if (suspended_screen_) {
// Clear screen, and put the cursor at the beginning of the drawing.
std::cout << ResetPosition(/*clear=*/true);
dimx_ = 0;
dimy_ = 0;
Uninstall();
std::swap(g_active_screen, suspended_screen_);
g_active_screen->Install();
} else {
Uninstall();
// On final exit, keep the current drawing and reset cursor position one
// line after it.
std::cout << std::endl;
@@ -376,6 +483,8 @@ ScreenInteractive* ScreenInteractive::Active() {
}
void ScreenInteractive::Install() {
frame_valid_ = false;
// After uninstalling the new configuration, flush it to the terminal to
// ensure it is fully applied:
on_exit_functions.push([] { Flush(); });
@@ -384,11 +493,11 @@ void ScreenInteractive::Install() {
// Install signal handlers to restore the terminal state on exit. The default
// signal handlers are restored on exit.
for (int signal : {SIGTERM, SIGSEGV, SIGINT, SIGILL, SIGABRT, SIGFPE}) {
install_signal_handler(signal, OnExit);
for (const int signal : {SIGTERM, SIGSEGV, SIGINT, SIGILL, SIGABRT, SIGFPE}) {
InstallSignalHandler(signal);
}
// Save the old terminal configuration and restore it on exit.
// Save the old terminal configuration and restore it on exit.
#if defined(_WIN32)
// Enable VT processing on stdout and stdin
auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -420,6 +529,10 @@ void ScreenInteractive::Install() {
SetConsoleMode(stdin_handle, in_mode);
SetConsoleMode(stdout_handle, out_mode);
#else
for (const int signal : {SIGWINCH, SIGTSTP}) {
InstallSignalHandler(signal);
}
struct termios terminal; // NOLINT
tcgetattr(STDIN_FILENO, &terminal);
on_exit_functions.push([=] { tcsetattr(STDIN_FILENO, TCSANOW, &terminal); });
@@ -434,12 +547,6 @@ void ScreenInteractive::Install() {
tcsetattr(STDIN_FILENO, TCSANOW, &terminal);
// Handle resize.
g_on_resize = [&] { task_sender_->Send(Event::Special({0})); };
install_signal_handler(SIGWINCH, OnResize);
// Handle SIGTSTP/SIGCONT.
install_signal_handler(SIGTSTP, OnSigStop);
#endif
auto enable = [&](const std::vector<DECMode>& parameters) {
@@ -458,20 +565,23 @@ void ScreenInteractive::Install() {
});
}
on_exit_functions.push([=] {
std::cout << "\033[?25h"; // Enable cursor.
std::cout << "\033[?1 q"; // Cursor block blinking.
});
disable({
DECMode::kCursor,
// DECMode::kCursor,
DECMode::kLineWrap,
});
enable({
// DECMode::kMouseVt200,
DECMode::kMouseAnyEvent,
DECMode::kMouseUtf8,
DECMode::kMouseSgrExtMode,
});
enable({DECMode::kMouseVt200});
enable({DECMode::kMouseAnyEvent});
enable({DECMode::kMouseUrxvtMode});
enable({DECMode::kMouseSgrExtMode});
// After installing the new configuration, flush it to the terminal to ensure
// it is fully applied:
// After installing the new configuration, flush it to the terminal to
// ensure it is fully applied:
Flush();
quit_ = false;
@@ -483,36 +593,32 @@ void ScreenInteractive::Install() {
}
void ScreenInteractive::Uninstall() {
ExitLoopClosure()();
ExitNow();
event_listener_.join();
animation_listener_.join();
OnExit(0);
OnExit();
}
// NOLINTNEXTLINE
void ScreenInteractive::Main(Component component) {
previous_animation_time = animation::Clock::now();
auto draw = [&] {
Draw(component);
std::cout << ToString() << set_cursor_position;
Flush();
Clear();
};
bool attempt_draw = true;
while (!quit_) {
if (attempt_draw && !task_receiver_->HasPending()) {
draw();
attempt_draw = false;
}
void ScreenInteractive::RunOnceBlocking(Component component) {
ExecuteSignalHandlers();
Task task;
if (!task_receiver_->Receive(&task)) {
break;
if (task_receiver_->Receive(&task)) {
HandleTask(component, task);
}
RunOnce(component);
}
void ScreenInteractive::RunOnce(Component component) {
Task task;
while (task_receiver_->ReceiveNonBlocking(&task)) {
HandleTask(component, task);
ExecuteSignalHandlers();
}
Draw(std::move(component));
}
void ScreenInteractive::HandleTask(Component component, Task& task) {
// clang-format off
std::visit([&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
@@ -532,7 +638,7 @@ void ScreenInteractive::Main(Component component) {
arg.screen_ = this;
component->OnEvent(arg);
attempt_draw = true;
frame_valid_ = false;
return;
}
@@ -549,50 +655,52 @@ void ScreenInteractive::Main(Component component) {
}
animation_requested_ = false;
animation::TimePoint now = animation::Clock::now();
animation::Duration delta = now - previous_animation_time;
previous_animation_time = now;
const animation::TimePoint now = animation::Clock::now();
const animation::Duration delta = now - previous_animation_time_;
previous_animation_time_ = now;
animation::Params params(delta);
component->OnAnimation(params);
attempt_draw = true;
frame_valid_ = false;
return;
}
},
task);
// clang-format on
}
}
// NOLINTNEXTLINE
void ScreenInteractive::Draw(Component component) {
if (frame_valid_) {
return;
}
auto document = component->Render();
int dimx = 0;
int dimy = 0;
auto terminal = Terminal::Size();
document->ComputeRequirement();
switch (dimension_) {
case Dimension::Fixed:
dimx = dimx_;
dimy = dimy_;
break;
case Dimension::TerminalOutput:
document->ComputeRequirement();
dimx = Terminal::Size().dimx;
dimx = terminal.dimx;
dimy = document->requirement().min_y;
break;
case Dimension::Fullscreen:
dimx = Terminal::Size().dimx;
dimy = Terminal::Size().dimy;
dimx = terminal.dimx;
dimy = terminal.dimy;
break;
case Dimension::FitComponent:
auto terminal = Terminal::Size();
document->ComputeRequirement();
dimx = std::min(document->requirement().min_x, terminal.dimx);
dimy = std::min(document->requirement().min_y, terminal.dimy);
break;
}
bool resized = (dimx != dimx_) || (dimy != dimy_);
std::cout << reset_cursor_position << ResetPosition(/*clear=*/resized);
const bool resized = (dimx != dimx_) || (dimy != dimy_);
ResetCursorPosition();
std::cout << ResetPosition(/*clear=*/resized);
// Resize the screen if needed.
if (resized) {
@@ -631,44 +739,74 @@ void ScreenInteractive::Draw(Component component) {
Render(*this, document);
// Set cursor position for user using tools to insert CJK characters.
set_cursor_position = "";
{
const int dx = dimx_ - 1 - cursor_.x + int(dimx_ != terminal.dimx);
const int dy = dimy_ - 1 - cursor_.y;
set_cursor_position = "\x1B[" + std::to_string(dy) + "A" + //
"\x1B[" + std::to_string(dx) + "D";
reset_cursor_position = "\x1B[" + std::to_string(dy) + "B" + //
"\x1B[" + std::to_string(dx) + "C";
if (cursor_.shape == Cursor::Hidden) {
set_cursor_position += "\033[?25l";
} else {
set_cursor_position += "\033[?25h";
set_cursor_position +=
"\033[" + std::to_string(int(cursor_.shape)) + " q";
}
}
std::cout << ToString() << set_cursor_position;
Flush();
Clear();
frame_valid_ = true;
}
void ScreenInteractive::ResetCursorPosition() {
std::cout << reset_cursor_position;
reset_cursor_position = "";
int dx = dimx_ - 1 - cursor_.x;
int dy = dimy_ - 1 - cursor_.y;
if (dx != 0) {
set_cursor_position += "\x1B[" + std::to_string(dx) + "D";
reset_cursor_position += "\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";
}
}
Closure ScreenInteractive::ExitLoopClosure() {
return [this] {
quit_ = true;
task_sender_.reset();
};
return [this] { Exit(); };
}
void ScreenInteractive::SigStop() {
#if defined(_WIN32)
// Windows do no support SIGTSTP.
#else
void ScreenInteractive::Exit() {
Post([this] { ExitNow(); });
}
void ScreenInteractive::ExitNow() {
quit_ = true;
task_sender_.reset();
}
void ScreenInteractive::Signal(int signal) {
if (signal == SIGABRT) {
OnExit();
return;
}
// Windows do no support SIGTSTP / SIGWINCH
#if !defined(_WIN32)
if (signal == SIGTSTP) {
Post([&] {
ResetCursorPosition();
std::cout << ResetPosition(/*clear*/ true); // Cursor to the beginning
Uninstall();
std::cout << reset_cursor_position;
reset_cursor_position = "";
std::cout << ResetPosition(/*clear=*/true);
dimx_ = 0;
dimy_ = 0;
Flush();
std::raise(SIGTSTP);
std::ignore = std::raise(SIGTSTP);
Install();
});
return;
}
if (signal == SIGWINCH) {
Post(Event::Special({0}));
return;
}
#endif
}

View File

@@ -1,11 +1,10 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl, TestPartResult
#include <gtest/gtest.h>
#include <csignal> // for raise, SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM
#include <ftxui/component/event.hpp> // for Event, Event::Custom
#include "ftxui/component/component.hpp" // for Renderer
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/dom/elements.hpp" // for text, Element
#include "gtest/gtest_pred_impl.h" // for Test, TEST, EXPECT_EQ
namespace ftxui {
@@ -47,6 +46,20 @@ TEST(ScreenInteractive, Signal_SIGFPE) {
TestSignal(SIGFPE);
}
// Regression test for:
// https://github.com/ArthurSonzogni/FTXUI/issues/402
TEST(ScreenInteractive, PostEventToNonActive) {
auto screen = ScreenInteractive::FitComponent();
screen.Post(Event::Custom);
}
// Regression test for:
// https://github.com/ArthurSonzogni/FTXUI/issues/402
TEST(ScreenInteractive, PostTaskToNonActive) {
auto screen = ScreenInteractive::FitComponent();
screen.Post([] {});
}
} // namespace ftxui
// Copyright 2021 Arthur Sonzogni. All rights reserved.

View File

@@ -1,42 +1,109 @@
#include <algorithm> // for max, min
#include <ftxui/component/component_options.hpp> // for SliderOption
#include <string> // for allocator
#include <utility> // for move
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Slider
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
#include "ftxui/component/screen_interactive.hpp" // for Component
#include "ftxui/dom/elements.hpp" // for operator|, text, Element, reflect, xflex, gauge, hbox, underlined, color, dim, vcenter
#include "ftxui/dom/elements.hpp" // for operator|, text, GaugeDirection, Element, xflex, hbox, color, underlined, GaugeDirection::Down, GaugeDirection::Left, GaugeDirection::Right, GaugeDirection::Up, reflect, Decorator, dim, vcenter, yflex, gaugeDirection
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::GrayLight
#include "ftxui/util/ref.hpp" // for StringRef
#include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::White
#include "ftxui/screen/util.hpp" // for clamp
#include "ftxui/util/ref.hpp" // for ConstRef, Ref, ConstStringRef
namespace ftxui {
namespace {
Decorator flexDirection(GaugeDirection direction) {
switch (direction) {
case GaugeDirection::Up:
case GaugeDirection::Down:
return yflex;
case GaugeDirection::Left:
case GaugeDirection::Right:
return xflex;
}
return xflex; // NOT_REACHED()
}
} // namespace
template <class T>
class SliderBase : public ComponentBase {
public:
SliderBase(ConstStringRef label, T* value, T min, T max, T increment)
: label_(std::move(label)),
value_(value),
min_(min),
max_(max),
increment_(increment) {}
explicit SliderBase(Ref<SliderOption<T>> options)
: value_(options->value),
min_(options->min),
max_(options->max),
increment_(options->increment),
options_(options) {}
Element Render() override {
auto gauge_color =
Focused() ? color(Color::GrayLight) : color(Color::GrayDark);
float percent = float(*value_ - min_) / float(max_ - min_);
return hbox({
text(*label_) | dim | vcenter,
hbox({
text("["),
gauge(percent) | underlined | xflex | reflect(gauge_box_),
text("]"),
}) | xflex,
}) |
gauge_color | xflex | reflect(box_);
auto gauge_color = Focused() ? color(options_->color_active)
: color(options_->color_inactive);
const float percent = float(value_() - min_()) / float(max_() - min_());
return gaugeDirection(percent, options_->direction) |
flexDirection(options_->direction) | reflect(gauge_box_) |
gauge_color;
}
void OnLeft() {
switch (options_->direction) {
case GaugeDirection::Right:
value_() -= increment_();
break;
case GaugeDirection::Left:
value_() += increment_();
break;
case GaugeDirection::Up:
case GaugeDirection::Down:
break;
}
}
void OnRight() {
switch (options_->direction) {
case GaugeDirection::Right:
value_() += increment_();
break;
case GaugeDirection::Left:
value_() -= increment_();
break;
case GaugeDirection::Up:
case GaugeDirection::Down:
break;
}
}
void OnUp() {
switch (options_->direction) {
case GaugeDirection::Up:
value_() -= increment_();
break;
case GaugeDirection::Down:
value_() += increment_();
break;
case GaugeDirection::Left:
case GaugeDirection::Right:
break;
}
}
void OnDown() {
switch (options_->direction) {
case GaugeDirection::Down:
value_() -= increment_();
break;
case GaugeDirection::Up:
value_() += increment_();
break;
case GaugeDirection::Left:
case GaugeDirection::Right:
break;
}
}
bool OnEvent(Event event) final {
@@ -44,15 +111,22 @@ class SliderBase : public ComponentBase {
return OnMouseEvent(event);
}
T old_value = value_();
if (event == Event::ArrowLeft || event == Event::Character('h')) {
*value_ -= increment_;
*value_ = std::max(*value_, min_);
return true;
OnLeft();
}
if (event == Event::ArrowRight || event == Event::Character('l')) {
OnRight();
}
if (event == Event::ArrowUp || event == Event::Character('k')) {
OnDown();
}
if (event == Event::ArrowDown || event == Event::Character('j')) {
OnUp();
}
if (event == Event::ArrowRight || event == Event::Character('l')) {
*value_ += increment_;
*value_ = std::min(*value_, max_);
value_() = util::clamp(value_(), min_(), max_());
if (old_value != value_()) {
return true;
}
@@ -65,7 +139,8 @@ class SliderBase : public ComponentBase {
return true;
}
if (box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event)) {
if (gauge_box_.Contain(event.mouse().x, event.mouse().y) &&
CaptureMouse(event)) {
TakeFocus();
}
@@ -77,9 +152,33 @@ class SliderBase : public ComponentBase {
}
if (captured_mouse_) {
*value_ = min_ + (event.mouse().x - gauge_box_.x_min) * (max_ - min_) /
switch (options_->direction) {
case GaugeDirection::Right: {
value_() = min_() + (event.mouse().x - gauge_box_.x_min) *
(max_() - min_()) /
(gauge_box_.x_max - gauge_box_.x_min);
*value_ = std::max(min_, std::min(max_, *value_));
break;
}
case GaugeDirection::Left: {
value_() = max_() - (event.mouse().x - gauge_box_.x_min) *
(max_() - min_()) /
(gauge_box_.x_max - gauge_box_.x_min);
break;
}
case GaugeDirection::Down: {
value_() = min_() + (event.mouse().y - gauge_box_.y_min) *
(max_() - min_()) /
(gauge_box_.y_max - gauge_box_.y_min);
break;
}
case GaugeDirection::Up: {
value_() = max_() - (event.mouse().y - gauge_box_.y_min) *
(max_() - min_()) /
(gauge_box_.y_max - gauge_box_.y_min);
break;
}
}
value_() = std::max(min_(), std::min(max_(), value_()));
return true;
}
return false;
@@ -88,16 +187,63 @@ class SliderBase : public ComponentBase {
bool Focusable() const final { return true; }
private:
ConstStringRef label_;
T* value_;
T min_;
T max_;
T increment_ = 1;
Box box_;
Ref<T> value_;
ConstRef<T> min_;
ConstRef<T> max_;
ConstRef<T> increment_;
Ref<SliderOption<T>> options_;
Box gauge_box_;
CapturedMouse captured_mouse_;
};
class SliderWithLabel : public ComponentBase {
public:
SliderWithLabel(ConstStringRef label, Component inner)
: label_(std::move(label)) {
Add(std::move(inner));
SetActiveChild(ChildAt(0));
}
private:
bool OnEvent(Event event) final {
if (ComponentBase::OnEvent(event)) {
return true;
}
if (!event.is_mouse()) {
return false;
}
if (!box_.Contain(event.mouse().x, event.mouse().y)) {
return false;
}
if (!CaptureMouse(event)) {
return false;
}
TakeFocus();
return true;
}
Element Render() override {
auto focus_management = Focused() ? focus : Active() ? select : nothing;
auto gauge_color = Focused() ? color(Color::White) : color(Color::GrayDark);
return hbox({
text(label_()) | dim | vcenter,
hbox({
text("["),
ComponentBase::Render() | underlined,
text("]"),
}) | xflex,
}) |
gauge_color | xflex | reflect(box_) | focus_management;
}
ConstStringRef label_;
Box box_;
};
/// @brief An horizontal slider.
/// @param label The name of the slider.
/// @param value The current value of the slider.
@@ -120,28 +266,78 @@ class SliderBase : public ComponentBase {
/// ```bash
/// Value:[██████████████████████████ ]
/// ```
template <class T>
Component Slider(ConstStringRef label, T* value, T min, T max, T increment) {
return Make<SliderBase<T>>(std::move(label), value, min, max, increment);
Component Slider(ConstStringRef label,
Ref<int> value,
ConstRef<int> min,
ConstRef<int> max,
ConstRef<int> increment) {
SliderOption<int> option;
option.value = value;
option.min = min;
option.max = max;
option.increment = increment;
auto slider = Make<SliderBase<int>>(option);
return Make<SliderWithLabel>(std::move(label), slider);
}
template Component Slider(ConstStringRef label,
int* value,
int min,
int max,
int increment);
Component Slider(ConstStringRef label,
Ref<float> value,
ConstRef<float> min,
ConstRef<float> max,
ConstRef<float> increment) {
SliderOption<float> option;
option.value = value;
option.min = min;
option.max = max;
option.increment = increment;
auto slider = Make<SliderBase<float>>(option);
return Make<SliderWithLabel>(std::move(label), slider);
}
Component Slider(ConstStringRef label,
Ref<long> value,
ConstRef<long> min,
ConstRef<long> max,
ConstRef<long> increment) {
SliderOption<long> option;
option.value = value;
option.min = min;
option.max = max;
option.increment = increment;
auto slider = Make<SliderBase<long>>(option);
return Make<SliderWithLabel>(std::move(label), slider);
}
template Component Slider(ConstStringRef label,
float* value,
float min,
float max,
float increment);
/// @brief A slider in any direction.
/// @param option The options
/// ### Example
///
/// ```cpp
/// auto screen = ScreenInteractive::TerminalOutput();
/// int value = 50;
/// auto slider = Slider({
/// .value = &value,
/// .min = 0,
/// .max = 100,
/// .increment= 20,
/// });
/// screen.Loop(slider);
/// ```
template <typename T>
Component Slider(SliderOption<T> options) {
return Make<SliderBase<T>>(options);
}
template Component Slider(SliderOption<int8_t>);
template Component Slider(SliderOption<int16_t>);
template Component Slider(SliderOption<int32_t>);
template Component Slider(SliderOption<int64_t>);
template Component Slider(ConstStringRef label,
long* value,
long min,
long max,
long increment);
template Component Slider(SliderOption<uint8_t>);
template Component Slider(SliderOption<uint16_t>);
template Component Slider(SliderOption<uint32_t>);
template Component Slider(SliderOption<uint64_t>);
template Component Slider(SliderOption<float>);
template Component Slider(SliderOption<double>);
} // namespace ftxui

View File

@@ -0,0 +1,192 @@
#include <gtest/gtest.h> // for AssertionResult, Message, TestPartResult, Test, EXPECT_EQ, EXPECT_TRUE, TestInfo (ptr only), EXPECT_FALSE, TEST
#include <stddef.h> // for size_t
#include <array> // for array
#include <ftxui/component/mouse.hpp> // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
#include <ftxui/dom/elements.hpp> // for GaugeDirection, GaugeDirection::Down, GaugeDirection::Left, GaugeDirection::Right, GaugeDirection::Up, frame
#include <memory> // for shared_ptr, __shared_ptr_access, allocator
#include <string> // for to_string
#include "ftxui/component/component.hpp" // for Slider, Vertical, operator|=
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen
namespace ftxui {
namespace {
Event MousePressed(int x, int y) {
Mouse mouse;
mouse.button = Mouse::Left;
mouse.motion = Mouse::Pressed;
mouse.shift = false;
mouse.meta = false;
mouse.control = false;
mouse.x = x;
mouse.y = y;
return Event::Mouse("jjj", mouse);
}
Event MouseReleased(int x, int y) {
Mouse mouse;
mouse.button = Mouse::Left;
mouse.motion = Mouse::Released;
mouse.shift = false;
mouse.meta = false;
mouse.control = false;
mouse.x = x;
mouse.y = y;
return Event::Mouse("jjj", mouse);
}
} // namespace
TEST(SliderTest, Right) {
int value = 50;
auto slider = Slider<int>({
.value = &value,
.min = 0,
.max = 100,
.increment = 10,
.direction = GaugeDirection::Right,
});
Screen screen(11, 1);
Render(screen, slider->Render());
EXPECT_TRUE(slider->OnEvent(MousePressed(3, 0)));
EXPECT_EQ(value, 30);
EXPECT_TRUE(slider->OnEvent(MousePressed(9, 0)));
EXPECT_EQ(value, 90);
EXPECT_TRUE(slider->OnEvent(MousePressed(9, 2)));
EXPECT_EQ(value, 90);
EXPECT_TRUE(slider->OnEvent(MousePressed(5, 2)));
EXPECT_EQ(value, 50);
EXPECT_TRUE(slider->OnEvent(MouseReleased(5, 2)));
EXPECT_FALSE(slider->OnEvent(MousePressed(5, 2)));
}
TEST(SliderTest, Left) {
int value = 50;
auto slider = Slider<int>({
.value = &value,
.min = 0,
.max = 100,
.increment = 10,
.direction = GaugeDirection::Left,
});
Screen screen(11, 1);
Render(screen, slider->Render());
EXPECT_TRUE(slider->OnEvent(MousePressed(3, 0)));
EXPECT_EQ(value, 70);
EXPECT_TRUE(slider->OnEvent(MousePressed(9, 0)));
EXPECT_EQ(value, 10);
EXPECT_TRUE(slider->OnEvent(MousePressed(9, 2)));
EXPECT_EQ(value, 10);
EXPECT_TRUE(slider->OnEvent(MousePressed(5, 2)));
EXPECT_EQ(value, 50);
EXPECT_TRUE(slider->OnEvent(MouseReleased(5, 2)));
EXPECT_FALSE(slider->OnEvent(MousePressed(5, 2)));
}
TEST(SliderTest, Down) {
int value = 50;
auto slider = Slider<int>({
.value = &value,
.min = 0,
.max = 100,
.increment = 10,
.direction = GaugeDirection::Down,
});
Screen screen(1, 11);
Render(screen, slider->Render());
EXPECT_TRUE(slider->OnEvent(MousePressed(0, 3)));
EXPECT_EQ(value, 30);
EXPECT_TRUE(slider->OnEvent(MousePressed(0, 9)));
EXPECT_EQ(value, 90);
EXPECT_TRUE(slider->OnEvent(MousePressed(2, 9)));
EXPECT_EQ(value, 90);
EXPECT_TRUE(slider->OnEvent(MousePressed(2, 5)));
EXPECT_EQ(value, 50);
EXPECT_TRUE(slider->OnEvent(MouseReleased(2, 5)));
EXPECT_FALSE(slider->OnEvent(MousePressed(2, 5)));
}
TEST(SliderTest, Up) {
int value = 50;
auto slider = Slider<int>({
.value = &value,
.min = 0,
.max = 100,
.increment = 10,
.direction = GaugeDirection::Up,
});
Screen screen(1, 11);
Render(screen, slider->Render());
EXPECT_TRUE(slider->OnEvent(MousePressed(0, 3)));
EXPECT_EQ(value, 70);
EXPECT_TRUE(slider->OnEvent(MousePressed(0, 9)));
EXPECT_EQ(value, 10);
EXPECT_TRUE(slider->OnEvent(MousePressed(2, 9)));
EXPECT_EQ(value, 10);
EXPECT_TRUE(slider->OnEvent(MousePressed(2, 5)));
EXPECT_EQ(value, 50);
EXPECT_TRUE(slider->OnEvent(MouseReleased(2, 5)));
EXPECT_FALSE(slider->OnEvent(MousePressed(2, 5)));
}
TEST(SliderTest, Focus) {
static std::array<int, 10> values;
auto container = Container::Vertical({});
for (size_t i = 0; i < values.size(); ++i) {
container->Add(Slider(std::to_string(i), &values[i]));
}
container |= frame;
Screen screen(10, 3);
Render(screen, container->Render());
EXPECT_EQ(screen.at(0, 0), "0"); // Select 0
EXPECT_EQ(screen.at(0, 1), "1");
EXPECT_EQ(screen.at(0, 2), "2");
EXPECT_TRUE(container->OnEvent(Event::ArrowDown));
Render(screen, container->Render());
EXPECT_EQ(screen.at(0, 0), "0");
EXPECT_EQ(screen.at(0, 1), "1"); // Select 1
EXPECT_EQ(screen.at(0, 2), "2");
EXPECT_TRUE(container->OnEvent(Event::ArrowDown));
Render(screen, container->Render());
EXPECT_EQ(screen.at(0, 0), "1");
EXPECT_EQ(screen.at(0, 1), "2"); // Select 2
EXPECT_EQ(screen.at(0, 2), "3");
EXPECT_TRUE(container->OnEvent(Event::ArrowDown)); // Select 3
EXPECT_TRUE(container->OnEvent(Event::ArrowDown)); // Select 4
EXPECT_TRUE(container->OnEvent(Event::ArrowDown)); // Select 5
EXPECT_TRUE(container->OnEvent(Event::ArrowDown)); // Select 6
EXPECT_TRUE(container->OnEvent(Event::ArrowDown));
Render(screen, container->Render());
EXPECT_EQ(screen.at(0, 0), "6");
EXPECT_EQ(screen.at(0, 1), "7"); // Select 7
EXPECT_EQ(screen.at(0, 2), "8");
EXPECT_TRUE(container->OnEvent(Event::ArrowDown));
Render(screen, container->Render());
EXPECT_EQ(screen.at(0, 0), "7");
EXPECT_EQ(screen.at(0, 1), "8"); // Select 8
EXPECT_EQ(screen.at(0, 2), "9");
EXPECT_TRUE(container->OnEvent(Event::ArrowDown));
Render(screen, container->Render());
EXPECT_EQ(screen.at(0, 0), "7");
EXPECT_EQ(screen.at(0, 1), "8");
EXPECT_EQ(screen.at(0, 2), "9"); // Select 9
EXPECT_FALSE(container->OnEvent(Event::ArrowDown));
}
} // namespace ftxui
// Copyright 2022 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@@ -1,7 +1,10 @@
#include "ftxui/component/terminal_input_parser.hpp"
#include <cstdint> // for uint32_t
#include <memory> // for unique_ptr
#include <ftxui/component/mouse.hpp> // for Mouse, Mouse::Button, Mouse::Motion
#include <ftxui/component/receiver.hpp> // for SenderImpl, Sender
#include <map>
#include <memory> // for unique_ptr, allocator
#include <utility> // for move
#include "ftxui/component/event.hpp" // for Event
@@ -9,6 +12,17 @@
namespace ftxui {
// NOLINTNEXTLINE
const std::map<std::string, std::string> g_uniformize = {
// Microsoft's terminal uses a different new line character for the return
// key. This also happens with linux with the `bind` command:
// See https://github.com/ArthurSonzogni/FTXUI/issues/337
// Here, we uniformize the new line character to `\n`.
{"\r", "\n"},
// See: https://github.com/ArthurSonzogni/FTXUI/issues/508
{std::string({8}), std::string({127})},
};
TerminalInputParser::TerminalInputParser(Sender<Task> out)
: out_(std::move(out)) {}
@@ -54,17 +68,14 @@ void TerminalInputParser::Send(TerminalInputParser::Output output) {
pending_.clear();
return;
case SPECIAL:
// Microsoft's terminal uses a different new line character for the return
// key. This also happens with linux with the `bind` command:
// See https://github.com/ArthurSonzogni/FTXUI/issues/337
// Here, we uniformize the new line character to `\n`.
if (pending_ == "\r") {
out_->Send(Event::Special("\n"));
} else {
out_->Send(Event::Special(std::move(pending_)));
case SPECIAL: {
auto it = g_uniformize.find(pending_);
if (it != g_uniformize.end()) {
pending_ = it->second;
}
out_->Send(Event::Special(std::move(pending_)));
pending_.clear();
}
return;
case MOUSE:

View File

@@ -1,14 +1,13 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <algorithm> // for max
#include <gtest/gtest.h> // for AssertionResult, Test, Message, TestPartResult, SuiteApiResolver, TestInfo (ptr only), EXPECT_EQ, EXPECT_TRUE, TEST, TestFactoryImpl, EXPECT_FALSE
#include <ftxui/component/mouse.hpp> // for Mouse, Mouse::Left, Mouse::Middle, Mouse::Pressed, Mouse::Released, Mouse::Right
#include <ftxui/component/task.hpp> // for Task
#include <initializer_list> // for initializer_list
#include <memory> // for unique_ptr, allocator
#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::Custom, Event::Delete, Event::End, Event::Escape, Event::F10, Event::F11, Event::F12, Event::F5, Event::F6, Event::F7, Event::F8, Event::F9, Event::Home, Event::PageDown, Event::PageUp, Event::Tab, Event::TabReverse
#include "ftxui/component/event.hpp" // for Event, Event::Return, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp, Event::Backspace, Event::Custom, Event::Delete, Event::End, Event::F10, Event::F11, Event::F12, Event::F5, Event::F6, Event::F7, Event::F8, Event::F9, Event::Home, Event::PageDown, Event::PageUp, Event::Tab, Event::TabReverse, Event::Escape
#include "ftxui/component/receiver.hpp" // for MakeReceiver, ReceiverImpl
#include "ftxui/component/terminal_input_parser.hpp"
#include "gtest/gtest_pred_impl.h" // for AssertionResult, Test, EXPECT_EQ, EXPECT_TRUE, TEST, EXPECT_FALSE
namespace ftxui {
@@ -303,7 +302,7 @@ TEST(Event, Control) {
};
std::vector<TestCase> cases;
for (int i = 0; i < 32; ++i) {
if (i == 13 || i == 24 || i == 26 || i == 27)
if (i == 8 || i == 13 || i == 24 || i == 26 || i == 27)
continue;
cases.push_back({char(i), false});
}
@@ -343,22 +342,24 @@ TEST(Event, Special) {
{str("\x1B[A"), Event::ArrowUp},
{str("\x1B[B"), Event::ArrowDown},
{{127}, Event::Backspace},
// Quirk for: https://github.com/ArthurSonzogni/FTXUI/issues/508
{{8}, Event::Backspace},
{str("\x1B[3~"), Event::Delete},
//{str("\x1B"), Event::Escape},
{{10}, Event::Return},
{{9}, Event::Tab},
{{27, 91, 90}, Event::TabReverse},
//{str("\x1B[OP"), Event::F1},
//{str("\x1B[OQ"), Event::F2},
//{str("\x1B[OR"), Event::F3},
//{str("\x1B[OS"), Event::F4},
{str("\x1BOP"), Event::F1},
{str("\x1BOQ"), Event::F2},
{str("\x1BOR"), Event::F3},
{str("\x1BOS"), Event::F4},
{str("\x1B[15~"), Event::F5},
{str("\x1B[17~"), Event::F6},
{str("\x1B[18~"), Event::F7},
{str("\x1B[19~"), Event::F8},
{str("\x1B[20~"), Event::F9},
{str("\x1B[21~"), Event::F10},
{str("\x1B[21~"), Event::F11},
{str("\x1B[23~"), Event::F11},
{str("\x1B[24~"), Event::F12},
{{27, 91, 72}, Event::Home},
{{27, 91, 70}, Event::End},

View File

@@ -1,5 +1,3 @@
//#include "ftxui/component/event.hpp"
//#include "ftxui/component/receiver.hpp"
#include <vector>
#include "ftxui/component/terminal_input_parser.hpp"

View File

@@ -1,5 +1,4 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <gtest/gtest.h>
#include <functional> // for function
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string, basic_string
@@ -11,7 +10,6 @@
#include "ftxui/component/component_options.hpp" // for ToggleOption
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Return, Event::Tab, Event::TabReverse
#include "ftxui/util/ref.hpp" // for Ref
#include "gtest/gtest_pred_impl.h" // for AssertionResult, EXPECT_EQ, Test, EXPECT_TRUE, EXPECT_FALSE, TEST
using namespace ftxui;

View File

@@ -1,11 +1,9 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl, TestPartResult
#include <gtest/gtest.h>
#include <string> // for allocator
#include "ftxui/dom/elements.hpp" // for operator|, text, blink, Element
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
#include "gtest/gtest_pred_impl.h" // for Test, AssertionResult, EXPECT_TRUE, TEST
namespace ftxui {

View File

@@ -1,11 +1,9 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl, TestPartResult
#include <gtest/gtest.h>
#include <string> // for allocator
#include "ftxui/dom/elements.hpp" // for operator|, text, bold, Element
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
#include "gtest/gtest_pred_impl.h" // for Test, AssertionResult, EXPECT_TRUE, TEST
namespace ftxui {

View File

@@ -14,7 +14,7 @@
namespace ftxui {
using Charset = std::array<std::string, 6>; // NOLINT
using Charsets = std::array<Charset, 6>; // NOLINT
using Charsets = std::array<Charset, 5>; // NOLINT
// NOLINTNEXTLINE
static Charsets simple_border_charset = {
Charset{"", "", "", "", "", ""},

View File

@@ -1,11 +1,9 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl, TestPartResult
#include <gtest/gtest.h>
#include <string> // for allocator, string
#include "ftxui/dom/elements.hpp" // for text, operator|, Element, borderStyled, borderWith, window, border, borderDouble, borderEmpty, borderHeavy, borderLight, borderRounded, DOUBLE
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
namespace ftxui {

View File

@@ -12,7 +12,7 @@ void ComputeGrow(std::vector<Element>* elements,
int extra_space,
int flex_grow_sum) {
for (Element& element : *elements) {
int added_space =
const int added_space =
extra_space * element.flex_grow / std::max(flex_grow_sum, 1);
extra_space -= added_space;
flex_grow_sum -= element.flex_grow;
@@ -27,8 +27,8 @@ void ComputeShrinkEasy(std::vector<Element>* elements,
int extra_space,
int flex_shrink_sum) {
for (Element& element : *elements) {
int added_space = extra_space * element.min_size * element.flex_shrink /
std::max(flex_shrink_sum, 1);
const int added_space = extra_space * element.min_size *
element.flex_shrink / std::max(flex_shrink_sum, 1);
extra_space -= added_space;
flex_shrink_sum -= element.flex_shrink * element.min_size;
element.size = element.min_size + added_space;
@@ -48,7 +48,7 @@ void ComputeShrinkHard(std::vector<Element>* elements,
continue;
}
int added_space = extra_space * element.min_size / std::max(1, size);
const int added_space = extra_space * element.min_size / std::max(1, size);
extra_space -= added_space;
size -= element.min_size;
@@ -73,7 +73,7 @@ void Compute(std::vector<Element>* elements, int target_size) {
size += element.min_size;
}
int extra_space = target_size - size;
const int extra_space = target_size - size;
if (extra_space >= 0) {
ComputeGrow(elements, extra_space, flex_grow_sum);
} else if (flex_shrink_size + extra_space >= 0) {

View File

@@ -1,9 +1,11 @@
#include "ftxui/dom/canvas.hpp"
#include <algorithm> // for max, min
#include <cmath> // for abs
#include <cstdint> // for uint8_t
#include <cstdlib> // for abs
#include <map> // for allocator, map
#include <ftxui/screen/color.hpp> // for Color
#include <map> // for map
#include <memory> // for make_shared
#include <utility> // for move, pair
#include <vector> // for vector
@@ -468,7 +470,7 @@ void Canvas::DrawBlockOn(int x, int y) {
cell.type = CellType::kBlock;
}
uint8_t bit = (x % 2) * 2 + y % 2;
const uint8_t bit = (x % 2) * 2 + y % 2;
uint8_t value = g_map_block_inversed.at(cell.content.character);
value |= 1U << bit;
cell.content.character = g_map_block[value];
@@ -488,7 +490,7 @@ void Canvas::DrawBlockOff(int x, int y) {
}
y /= 2;
uint8_t bit = (y % 2) * 2 + x % 2;
const uint8_t bit = (y % 2) * 2 + x % 2;
uint8_t value = g_map_block_inversed.at(cell.content.character);
value &= ~(1U << bit);
cell.content.character = g_map_block[value];
@@ -509,7 +511,7 @@ void Canvas::DrawBlockToggle(int x, int y) {
}
y /= 2;
uint8_t bit = (y % 2) * 2 + x % 2;
const uint8_t bit = (y % 2) * 2 + x % 2;
uint8_t value = g_map_block_inversed.at(cell.content.character);
value ^= 1U << bit;
cell.content.character = g_map_block[value];
@@ -801,6 +803,7 @@ void Canvas::DrawText(int x,
const Stylizer& style) {
for (const auto& it : Utf8ToGlyphs(value)) {
if (!IsIn(x, y)) {
x += 2;
continue;
}
Cell& cell = storage_[XY{x / 2, y / 4}];
@@ -827,8 +830,8 @@ class CanvasNodeBase : public Node {
void Render(Screen& screen) override {
const Canvas& c = canvas();
int y_max = std::min(c.height() / 4, box_.y_max - box_.y_min + 1);
int x_max = std::min(c.width() / 2, box_.x_max - box_.x_min + 1);
const int y_max = std::min(c.height() / 4, box_.y_max - box_.y_min + 1);
const int x_max = std::min(c.width() / 2, box_.x_max - box_.x_min + 1);
for (int y = 0; y < y_max; ++y) {
for (int x = 0; x < x_max; ++x) {
screen.PixelAt(box_.x_min + x, box_.y_min + y) = c.GetPixel(x, y);
@@ -871,8 +874,8 @@ Element canvas(int width, int height, std::function<void(Canvas&)> fn) {
}
void Render(Screen& screen) final {
int width = (box_.y_max - box_.y_min + 1) * 2;
int height = (box_.x_max - box_.x_min + 1) * 4;
const int width = (box_.x_max - box_.x_min + 1) * 2;
const int height = (box_.y_max - box_.y_min + 1) * 4;
canvas_ = Canvas(width, height);
fn_(canvas_);
CanvasNodeBase::Render(screen);

View File

@@ -1,5 +1,4 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl, TestPartResult
#include <gtest/gtest.h>
#include <stdint.h> // for uint32_t
#include <string> // for allocator, string
@@ -9,7 +8,6 @@
#include "ftxui/screen/color.hpp" // for Color, Color::Black, Color::Blue, Color::Red, Color::White, Color::Yellow, Color::Cyan, Color::Green
#include "ftxui/screen/screen.hpp" // for Screen
#include "ftxui/screen/terminal.hpp" // for SetColorSupport, Color, TrueColor
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
namespace ftxui {
@@ -39,7 +37,7 @@ TEST(CanvasTest, GoldPoint) {
});
Screen screen(30, 10);
Render(screen, element);
EXPECT_EQ(Hash(screen.ToString()), -1195891837);
EXPECT_EQ(Hash(screen.ToString()), 2143518726);
}
TEST(CanvasTest, GoldPointColor) {
@@ -54,7 +52,7 @@ TEST(CanvasTest, GoldPointColor) {
});
Screen screen(30, 10);
Render(screen, element);
EXPECT_EQ(Hash(screen.ToString()), 1109533029);
EXPECT_EQ(Hash(screen.ToString()), 1264423298);
}
TEST(CanvasTest, GoldBlock) {
@@ -72,7 +70,7 @@ TEST(CanvasTest, GoldBlock) {
});
Screen screen(30, 10);
Render(screen, element);
EXPECT_EQ(Hash(screen.ToString()), 817159424);
EXPECT_EQ(Hash(screen.ToString()), 3826174883);
}
TEST(CanvasTest, GoldBlockColor) {
@@ -87,7 +85,7 @@ TEST(CanvasTest, GoldBlockColor) {
});
Screen screen(30, 10);
Render(screen, element);
EXPECT_EQ(Hash(screen.ToString()), 2869205941);
EXPECT_EQ(Hash(screen.ToString()), 3048712696);
}
TEST(CanvasTest, GoldText) {

View File

@@ -1,12 +1,10 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestPartResult, TestFactoryImpl
#include <gtest/gtest.h>
#include <string> // for allocator
#include "ftxui/dom/elements.hpp" // for operator|, text, bgcolor, color, Element
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for Color, Color::Red
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
namespace ftxui {

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