81 Commits

Author SHA1 Message Date
Arthur Sonzogni
751c8fab26 Force reload in examples. (#974)
After installing the service worker to use the COOP/COEP header, ensure
the document is reloaded.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/973
Fixed:https://github.com/ArthurSonzogni/FTXUI/issues/973
2024-12-25 13:32:35 +01:00
Sumit Patel
daa421fa6a add tuisic example project (#967) 2024-12-15 18:58:08 +01:00
Yongqi Zhu
e213cfda37 add Lazylist example project (#964) 2024-12-06 15:20:17 +01:00
Vemy
58ff448e76 Fix: Properly changing window title text color #940 (#961) 2024-12-01 09:38:09 +01:00
Dmitry Nefedov
dfa461b46b Clear terminal output of interactive screen on resize if alternate screen not in use (#952) 2024-11-27 21:52:20 +01:00
Dmitry Atamanov
0683285f01 Remove non-existent Just-Fast from README.md (#957) 2024-11-27 21:47:09 +01:00
glebundiy
7f74917887 Add yafth as an example project using FTXUI (#958) 2024-11-27 21:44:32 +01:00
Brian
ad0392ec39 Fixed typo on border (#956)
Fixed minor issue in function name
2024-11-20 22:37:02 +01:00
Boris Jaulmes
70bc44d28b Allow a Dimension::Fit to extend beyond the terminal maximum height (#950)
For long tables (and other DOM elements), one may want the screen to render on dimensions higher than the terminal.  
Hence, this PR proposes a way to do so, with an optional parameter in the `Dimension::Fit` util function.

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

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/572
Fixed:Bug:https://github.com/ArthurSonzogni/FTXUI/issues/572
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-11-07 21:07:09 +01:00
Petr Vyazovik
55af678fb9 Added pciex as an example project using FTXUI (#948) 2024-11-02 12:08:43 +01:00
Mikołaj Lubiak
edaa7a24e7 Add memory game and terminal animation to project list (#946) 2024-10-31 21:09:42 +01:00
Vemy
8922e6d55e Add 2048-cpp to projects using FTXUI (#944) 2024-10-29 09:15:07 +01:00
Mikołaj Lubiak
99df1ac8ba Add SliderWithCallback component (#938)
Add SliderOption::on_change.

Useful to observe a change to the value.

Signed-off-by: Mikołaj Lubiak <lubiak@proton.me>
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-10-29 08:03:59 +01:00
Herring
1d40687a40 Add index to EntryState (#933)
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-09-30 23:18:59 +02:00
ljrrjl
dfb9558eaf add ftxui-image-view (#924) 2024-08-27 14:35:15 +02:00
ArthurSonzogni
c5357acbaa Add scrollbar example. 2024-08-18 10:46:41 +02:00
ArthurSonzogni
fbd56cdf43 Fix CQ failures. 2024-08-17 12:01:43 +02:00
ArthurSonzogni
66d1c1f61f Quickfix 2024-08-16 11:47:01 +02:00
Arthur Sonzogni
f5d8c7deb5 Apply Clang-tidy (#918) 2024-08-16 11:19:51 +02:00
Sergey Latu
535290bb3b My project added to readme (#916) 2024-08-15 16:08:32 +02:00
Arthur Sonzogni
fcd050c017 Table: support initializer list constructor. (#915)
To avoid burdening the user with explicit type construction when using
the library, we can use a constructor that accepts an initializer list
(std::initializer_list). This allows users to pass initializer lists
directly without having to wrap them in
std::vector<std::vector<std::string>>. This resolves the ambiguous case
when the inner list contains only two elements.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/912
2024-08-13 15:55:09 +02:00
Paolo Bosetti
d7de24cd9e Added -fPIC compile option (#913)
Added -fPIC compile option.
2024-08-11 19:17:57 +02:00
Charney Kaye
547d9278d8 Add XJ music to FTXUI example projects (#909)
We use FTXUI as the frontend for the example C++ application for our adaptive music runtime engine.

https://github.com/xjmusic/xjmusic/tree/main/engine/example
2024-08-04 11:05:59 +02:00
Brian
5a9ef876a1 Update README.md (#905)
added step writer
2024-07-25 11:07:38 +02:00
LiAuTraver
307e4eb4b3 add missing include guard for screen/pixel.hpp (#890) 2024-06-27 13:07:10 +02:00
sAkuraOfficial
b28d57086a fix a small bug in button example (#868) 2024-06-13 18:43:49 +02:00
Arthur Sonzogni
ff305147ca Color alpha support. (#884) 2024-06-13 18:43:14 +02:00
Timon Ensel
d6a2049483 Add ostree-tui to README (#874) 2024-06-09 15:50:01 +02:00
Dr Power
b5e11ba1f6 Added Caravan to README.md (#871) 2024-06-09 15:44:12 +02:00
ArthurSonzogni
a715a767b5 Fix Color::HSV(h,0,v)
There was a problem when v==0
2024-06-02 12:03:41 +02:00
Felix
7b1f4d435b Solve issues with atomic copy (#867) 2024-05-26 15:28:05 +02:00
Arthur Sonzogni
ecacb22d37 Dropdown: Fix title not updated. (#851)
A bug was introduced by:
https://github.com/ArthurSonzogni/FTXUI/pull/826

The checkbox label wasn't updated.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/861
2024-05-15 18:23:59 +02:00
ArthurSonzogni
af49b57e60 Dropdown: Fix title not updated.
A bug was introduced by:
https://github.com/ArthurSonzogni/FTXUI/pull/826

The checkbox label wasn't updated.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/861
2024-05-13 10:53:11 +02:00
ccn
4913379625 Update index.html (#858)
correct spelling
2024-05-06 12:54:17 +02:00
ccn
d40cafde5c Update homescreen.cpp (#859)
fix typo
2024-05-06 12:53:56 +02:00
ccn
65296b9aa3 Update flex.cpp (#860)
fix typo
2024-05-06 12:53:37 +02:00
ccn
a58e6e6bcf Update README.md (#857)
minor typo
2024-05-05 18:11:07 +02:00
Arthur Sonzogni
8a2a9b0799 Generate compile commands for clangd. (#855)
Fix all the diagnostics reported.

Bug: https://github.com/ArthurSonzogni/FTXUI/issues/828
2024-05-01 14:32:22 +02:00
Arthur Sonzogni
6a755f3760 Fix Menu focus. (#850)
Bug:https://github.com/ArthurSonzogni/FTXUI/issues/841
2024-04-28 16:03:00 +02:00
Jørn Gustav Larsen
d386df6f94 Enable raw keyboard input (#832)
In order for applications to receive all keyboard inputs, including the
Ctrl-C and Ctrl-Z, the raw input mode has been enabled. As result the
SIGINT will no longer be used, instead the keyboard Ctrl-C event is used
for exiting the framework, but only if no components has made use of it.

Co-authored-by: Jørn Gustav Larsen <jgl@fasttracksoftware.com>
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-04-28 15:17:54 +02:00
Mark Antabi
d38b14ffb6 Allow user to specify window element border. (#849) 2024-04-28 14:48:02 +02:00
ArthurSonzogni
7e3e1d4bca Apply clang-tidy. 2024-04-28 10:40:57 +02:00
Clancy Walters
affa787244 Prefer Exit() over OnExit() (#847)
This is a no-op patch, but prefered, because this centralize the exit path below `Exit()`.
2024-04-27 11:32:46 +02:00
Arthur Sonzogni
014bdb4a05 Flush before applying a new configuration. (#848)
This avoids an ordering problem with whatever the user printed and
interacting with termios/WinAPI.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/846
2024-04-27 11:18:35 +02:00
Dimo Markov
293ff179f6 Separate a reusable Image class from Screen (#834)
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-04-27 11:03:44 +02:00
cole-io
1f6e1101e8 clarified README and added tip on linking (#845)
Clarified some sentences, changed "external package" section to "utilization", added a tip on linking
2024-04-22 08:48:32 +02:00
na-trium-144
0dfd59bd09 Fix ResizableSplit handling keyboard navigation incorrectly (#842)
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-04-18 17:28:28 +02:00
ArthurSonzogni
e03a0797be Fix minor compile error. 2024-04-07 18:10:52 +02:00
James
3c9fa60d28 Feature: Dropdown options with callback (#826)
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-04-06 17:45:10 +02:00
Jørn Gustav Larsen
2216f3a5da Problem with setting the cursor position on the right screen edge when drawing. (#831)
When moving the cursor back to its original location, a problem arises when cursor placed in the right edge column, where an off by one error occur. This pull request will resolve this problem.

Co-authored-by: Jørn Gustav Larsen <jgl@fasttracksoftware.com>
Co-authored-by: Jørn Gustav Larsen <jgl@adminbyrequest.com>
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-04-03 21:32:19 +02:00
Arthur Sonzogni
f609c12846 Revert change to button example. (#835)
It was introduced mistakenly by:
f495ce029c
2024-03-30 11:01:28 +01:00
faizan171997
ce5ac6b12f - Added exit button to homescreen example (#819) 2024-02-22 12:12:51 +01:00
ArthurSonzogni
f495ce029c Add example to use system ftxui
Fixed: https://github.com/ArthurSonzogni/FTXUI/issues/814
2024-01-26 18:32:44 +01:00
Arthur Sonzogni
810657dab8 Update mainpage.md
Fixed: https://github.com/ArthurSonzogni/FTXUI/issues/812
2024-01-25 11:56:25 +01:00
rio
65bbb4f0eb Make Checkbox take focus when clicked (#810) 2024-01-17 18:21:32 +01:00
Arthur Sonzogni
5112d9139d Button: invoke on_click at the end. (#807)
Some users might destroy `this`, which would result in UAF.

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

Fixed:https://github.com/ArthurSonzogni/FTXUI/issues/804
2024-01-10 22:08:57 +01:00
Mohammad Rahimi
91a162a30e Add FTowerX to README (#805)
FTowerX is Tower of Hanoi game developed using FTXUI
2024-01-08 16:48:42 +01:00
Nikola Dućak
4d5cc41c65 Add Captain's log to README (#803) 2024-01-08 07:25:38 +01:00
nyako
cc3bcbf069 ftxui_set_options: properly check the current compiler. (#802)
This solve the issue encountered when using clang under MSVC.
2024-01-08 07:05:41 +01:00
Particle_G
d0634e1ca0 Add missing Checkbox() implementation (#796)
Fix: #795
2023-12-23 08:35:20 +01:00
Arthur Sonzogni
a7b6785420 Restore cursor shape on exit. (#793) (#794)
Fixed: https://github.com/ArthurSonzogni/FTXUI/issues/792
2023-12-17 10:35:21 +01:00
Arthur Sonzogni
348c3853d4 Restore cursor shape on exit. (#793)
Fixed: https://github.com/ArthurSonzogni/FTXUI/issues/792
2023-12-17 10:24:33 +01:00
ArthurSonzogni
bfadcb7165 Fix default for ScreenInteractive::Fullscreen()
It was intended to open gthe alternate screen.
2023-11-19 14:09:42 +01:00
Arthur Sonzogni
6c2b43a2aa Improve the example page. (#780) 2023-11-19 12:09:52 +01:00
Arthur Sonzogni
b970cb6ea8 feature: allow fullscreen without alternative screen (#777)
This should solve #766

The original PR was:
#767

Co-authored-by: rbrugo <brugo.riccardo@gmail.com>
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2023-11-11 17:57:07 +01:00
Clément Roblot
c31aecf2ed Checkbox button debounce (#774)
This fixes: https://github.com/ArthurSonzogni/FTXUI/issues/773

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

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

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

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2023-11-11 17:33:50 +01:00
chrysante
e8589dd533 Fix Input onchange not called (#776) 2023-11-11 17:29:19 +01:00
Clément Roblot
0631c3ab3f Add example to filter characters inputted in an input field (#763)
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2023-11-05 10:26:12 +01:00
Ali Caglayan
d548a18658 update nix and add dev shell (#769)
We update the lock on the nix flake and also add a dev shell. This means
you can do `nix build` to build the project and `nix develop` to drop
into a development environment with cmake and clang.

Signed-off-by: Ali Caglayan <alizter@gmail.com>
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2023-10-23 08:48:50 +02:00
benoitdudu
f499d34f7e fix the doxygen documentation by moving comments at the right place (#768) 2023-10-19 16:58:02 -04:00
Ruebled
d4c9c5e226 Update README.md (#761)
Version FetchContent Update
2023-10-12 17:27:18 +02:00
Clément Roblot
62c0b43caf Scrollbar coloring (#755)
This a proposed MR to fix #754. While building the scroll bar the pixels were completely reseted thus canceling any style previously applied to said pixels. This MR removes this resetting of the pixels and leaves only the drawing of the shape of the scroll bar.
2023-10-02 10:29:23 +02:00
Arthur Sonzogni
c24a274292 Feature: hscroll_indicator (#753)
This is the symetrical of `vscroll_indicator`.

Requested by @ibrahimnasson.

Fixed:https://github.com/ArthurSonzogni/FTXUI/issues/752
2023-09-26 23:08:42 +02:00
Contexploit
20d4be286b Example: fix small flaw in example/button.cpp (#751) 2023-09-20 18:23:05 +02:00
Arthur Sonzogni
550a59f0a5 Add catalincd/resource-monitor (#745) 2023-09-18 20:10:48 +02:00
Arthur Sonzogni
5db2be0f4d List J0sephDavis/ftxuiFileReader (#746) 2023-09-18 20:09:45 +02:00
Arthur Sonzogni
8d1665022a List ftxui_CPUMeter (#744) 2023-09-03 16:21:13 +02:00
Arthur Sonzogni
e5978a8e76 Update README.md 2023-09-03 11:45:23 +02:00
MingSheng
0f1588e3d1 Add grid container library (#742) 2023-09-02 11:00:45 +02:00
Claus Klein
dd6a5d371f Fix CMAKE_UNITY_BUILD usage (#738)
Add missing include guard.
This fix https://github.com/cpp-best-practices/cmake_template/issues/64

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2023-08-28 21:38:51 +02:00
MingSheng
05c7bee6dd Feature: input can now use overwrite mode when toggled with insert key (#735)
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2023-08-28 21:07:26 +02:00
128 changed files with 3213 additions and 1122 deletions

View File

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

View File

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

14
.gitignore vendored
View File

@@ -3,6 +3,10 @@
* *
!*/ !*/
# Ignore build directories generated by default MSVC CMake integration
# (otherwise causes terribly slow indexing)
out/
# Allowed top-level files: # Allowed top-level files:
!.clang-format !.clang-format
!.clang-tidy !.clang-tidy
@@ -34,14 +38,16 @@
!doc/**/*.md !doc/**/*.md
# examples directory: # examples directory:
!examples/**/*.txt
!examples/**/*.cpp !examples/**/*.cpp
!examples/**/*.css
!examples/**/*.hpp !examples/**/*.hpp
!examples/**/*.ipp
!examples/**/*.html !examples/**/*.html
!examples/**/*.py
!examples/**/*.js
!examples/**/*.html.disabled !examples/**/*.html.disabled
!examples/**/*.ipp
!examples/**/*.js
!examples/**/*.mjs
!examples/**/*.py
!examples/**/*.txt
# include directory: # include directory:
!include/ftxui/**/*.hpp !include/ftxui/**/*.hpp

View File

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

View File

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

View File

@@ -32,7 +32,7 @@ A simple cross-platform C++ library for terminal based user interfaces!
## Feature ## Feature
* Functional style. Inspired by * Functional style. Inspired by
[[1]](https://hackernoon.com/building-reactive-terminal-interfaces-in-c-d392ce34e649?gi=d9fb9ce35901) [1](https://hackernoon.com/building-reactive-terminal-interfaces-in-c-d392ce34e649?gi=d9fb9ce35901)
and [React](https://reactjs.org/) and [React](https://reactjs.org/)
* Simple and elegant syntax (in my opinion) * Simple and elegant syntax (in my opinion)
* Keyboard & mouse navigation. * Keyboard & mouse navigation.
@@ -43,7 +43,7 @@ A simple cross-platform C++ library for terminal based user interfaces!
* **Cross platform**: Linux/MacOS (main target), WebAssembly, Windows (Thanks to contributors!). * **Cross platform**: Linux/MacOS (main target), WebAssembly, Windows (Thanks to contributors!).
* Learn by [examples](#documentation), and [tutorials](#documentation) * Learn by [examples](#documentation), and [tutorials](#documentation)
* Multiple packages: CMake [FetchContent]([https://bewagner.net/programming/2020/05/02/cmake-fetchcontent/](https://cmake.org/cmake/help/latest/module/FetchContent.html)) (preferred), vcpkg, pkgbuild, conan. * Multiple packages: CMake [FetchContent]([https://bewagner.net/programming/2020/05/02/cmake-fetchcontent/](https://cmake.org/cmake/help/latest/module/FetchContent.html)) (preferred), vcpkg, pkgbuild, conan.
* Good practises: documentation, tests, fuzzers, performance tests, automated CI, automated packaging, etc... * Good practices: documentation, tests, fuzzers, performance tests, automated CI, automated packaging, etc...
## Documentation ## Documentation
@@ -73,7 +73,7 @@ A simple cross-platform C++ library for terminal based user interfaces!
#### DOM #### DOM
This module defines a hierarchical set of Element. An element manages layout and can be responsive to the terminal dimensions. This module defines a hierarchical set of Element. An Element manages layout and can be responsive to the terminal dimensions.
They are declared in [<ftxui/dom/elements.hpp>](https://arthursonzogni.github.io/FTXUI/elements_8hpp_source.html They are declared in [<ftxui/dom/elements.hpp>](https://arthursonzogni.github.io/FTXUI/elements_8hpp_source.html
) )
@@ -199,7 +199,7 @@ Complex [examples](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/
#### Component #### Component
The ftxui/component is needed when you want to produce dynamic UI, reactive to the user's input. It defines a set of ftxui::Component. A component reacts to Events (keyboard, mouse, resize, ...) and Render Element (see previous section). ftxui/component produces dynamic UI, reactive to the user's input. It defines a set of ftxui::Component. A component reacts to Events (keyboard, mouse, resize, ...) and Renders as an Element (see previous section).
Prebuilt components are declared in [<ftxui/component/component.hpp>](https://arthursonzogni.github.io/FTXUI/component_8hpp_source.html) Prebuilt components are declared in [<ftxui/component/component.hpp>](https://arthursonzogni.github.io/FTXUI/component_8hpp_source.html)
@@ -293,7 +293,10 @@ Prebuilt components are declared in [<ftxui/component/component.hpp>](https://ar
</details> </details>
## Libraries for FTXUI ## Libraries for FTXUI
- *Want to share a useful component using FTXUI? Feel free adding yours here* - *Want to share a useful Component for FTXUI? Feel free to add yours here*
- [ftxui-grid-container](https://github.com/mingsheng13/grid-container-ftxui)
- [ftxui-ip-input](https://github.com/mingsheng13/ip-input-ftxui)
- [ftxui-image-view](https://github.com/ljrrjl/ftxui-image-view.git): For Image Display.
## Project using FTXUI ## Project using FTXUI
@@ -301,12 +304,12 @@ Prebuilt components are declared in [<ftxui/component/component.hpp>](https://ar
Feel free to add your projects here: Feel free to add your projects here:
- [json-tui](https://github.com/ArthurSonzogni/json-tui) - [json-tui](https://github.com/ArthurSonzogni/json-tui)
- [git-tui](https://github.com/ArthurSonzogni/git-tui) - [git-tui](https://github.com/ArthurSonzogni/git-tui)
- [ostree-tui](https://github.com/AP-Sensing/ostree-tui)
- [rgb-tui](https://github.com/ArthurSonzogni/rgb-tui) - [rgb-tui](https://github.com/ArthurSonzogni/rgb-tui)
- [chrome-log-beautifier](https://github.com/ArthurSonzogni/chrome-log-beautifier) - [chrome-log-beautifier](https://github.com/ArthurSonzogni/chrome-log-beautifier)
- [x86-64 CPU Architecture Simulation](https://github.com/AnisBdz/CPU) - [x86-64 CPU Architecture Simulation](https://github.com/AnisBdz/CPU)
- [ltuiny](https://github.com/adrianoviana87/ltuiny) - [ltuiny](https://github.com/adrianoviana87/ltuiny)
- [i3-termdialogs](https://github.com/mibli/i3-termdialogs) - [i3-termdialogs](https://github.com/mibli/i3-termdialogs)
- [Just-Fast](https://github.com/GiuseppeCesarano/just-fast)
- [simpPRU](https://github.com/VedantParanjape/simpPRU) - [simpPRU](https://github.com/VedantParanjape/simpPRU)
- [Pigeon ROS TUI](https://github.com/PigeonSensei/Pigeon_ros_tui) - [Pigeon ROS TUI](https://github.com/PigeonSensei/Pigeon_ros_tui)
- [hastur](https://github.com/robinlinden/hastur) - [hastur](https://github.com/robinlinden/hastur)
@@ -323,6 +326,22 @@ Feel free to add your projects here:
- [eCAL monitor](https://github.com/eclipse-ecal/ecal) - [eCAL monitor](https://github.com/eclipse-ecal/ecal)
- [Path Finder](https://github.com/Ruebled/Path_Finder) - [Path Finder](https://github.com/Ruebled/Path_Finder)
- [rw-tui](https://github.com/LeeKyuHyuk/rw-tui) - [rw-tui](https://github.com/LeeKyuHyuk/rw-tui)
- [resource-monitor](https://github.com/catalincd/resource-monitor)
- [ftxuiFileReader](https://github.com/J0sephDavis/ftxuiFileReader)
- [ftxui_CPUMeter](https://github.com/tzzzzzzzx/ftxui_CPUMeter)
- [Captain's log](https://github.com/nikoladucak/caps-log)
- [FTowerX](https://github.com/MhmRhm/FTowerX)
- [Caravan](https://github.com/r3w0p/caravan)
- [Step-Writer](https://github.com/BrianAnakPintar/step-writer)
- [XJ music](https://github.com/xjmusic/xjmusic)
- [UDP chat](https://github.com/Sergeydigl3/udp-chat-tui)
- [2048-cpp](https://github.com/Chessom/2048-cpp)
- [Memory game](https://github.com/mikolajlubiak/memory)
- [Terminal Animation](https://github.com/mikolajlubiak/terminal_animation)
- [pciex](https://github.com/s0nx/pciex)
- [Fallout terminal hacking](https://github.com/gshigin/yet-another-fallout-terminal-hacking-game)
- [Lazylist](https://github.com/zhuyongqi9/lazylist)
- [TUISIC](https://github.com/Dark-Kernel/tuisic)
### [cpp-best-practices/game_jam](https://github.com/cpp-best-practices/game_jam) ### [cpp-best-practices/game_jam](https://github.com/cpp-best-practices/game_jam)
@@ -339,16 +358,15 @@ Several games using the FTXUI have been made during the Game Jam:
- [smoothlife](https://github.com/cpp-best-practices/game_jam/blob/main/Jam1_April_2022/smoothlife.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) - [Consu](https://github.com/cpp-best-practices/game_jam/blob/main/Jam1_April_2022/consu.md)
## External package ## Utilization
It is **highly** recommended to use CMake FetchContent to depend on FTXUI. This It is **highly** recommended to use CMake FetchContent to depend on FTXUI so you may specify which commit you would like to depend on.
way you can specify which commit you would like to depend on.
```cmake ```cmake
include(FetchContent) include(FetchContent)
FetchContent_Declare(ftxui FetchContent_Declare(ftxui
GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui
GIT_TAG v3.0.0 GIT_TAG v5.0.0
) )
FetchContent_GetProperties(ftxui) FetchContent_GetProperties(ftxui)
@@ -358,14 +376,21 @@ if(NOT ftxui_POPULATED)
endif() endif()
``` ```
If you don't, the following packages have been created: If you don't, FTXUI may be used from the following packages:
- [vcpkg](https://vcpkgx.com/details.html?package=ftxui) - [vcpkg](https://vcpkgx.com/details.html?package=ftxui)
- [Arch Linux PKGBUILD](https://aur.archlinux.org/packages/ftxui-git/). - [Arch Linux PKGBUILD](https://aur.archlinux.org/packages/ftxui-git/).
- [conan.io](https://conan.io/center/ftxui) - [conan.io](https://conan.io/center/ftxui)
- [openSUSE](https://build.opensuse.org/package/show/devel:libraries:c_c++/ftxui) - [openSUSE](https://build.opensuse.org/package/show/devel:libraries:c_c++/ftxui)
-
[![Packaging status](https://repology.org/badge/vertical-allrepos/ftxui.svg)](https://repology.org/project/ftxui/versions) [![Packaging status](https://repology.org/badge/vertical-allrepos/ftxui.svg)](https://repology.org/project/ftxui/versions)
If you choose to build and link FTXUI yourself, `ftxui-component` must be first in the linking order relative to the other FTXUI libraries, i.e.
```bash
g++ . . . -lftxui-component -lftxui-dom -lftxui-screen . . .
```
## Contributors ## Contributors
<a href="https://github.com/ArthurSonzogni/FTXUI/graphs/contributors"> <a href="https://github.com/ArthurSonzogni/FTXUI/graphs/contributors">

View File

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

View File

@@ -18,6 +18,7 @@ add_executable(ftxui-tests
src/ftxui/component/menu_test.cpp src/ftxui/component/menu_test.cpp
src/ftxui/component/modal_test.cpp src/ftxui/component/modal_test.cpp
src/ftxui/component/radiobox_test.cpp src/ftxui/component/radiobox_test.cpp
src/ftxui/util/ref_test.cpp
src/ftxui/component/receiver_test.cpp src/ftxui/component/receiver_test.cpp
src/ftxui/component/resizable_split_test.cpp src/ftxui/component/resizable_split_test.cpp
src/ftxui/component/screen_interactive_test.cpp src/ftxui/component/screen_interactive_test.cpp
@@ -59,6 +60,11 @@ target_include_directories(ftxui-tests
) )
target_compile_features(ftxui-tests PRIVATE cxx_std_20) target_compile_features(ftxui-tests PRIVATE cxx_std_20)
# Disable unity build for tests. There are several files defining the same
# function in different anonymous namespaces. This is not allowed in unity
# builds, as it would result in multiple definitions of the same function.
set_target_properties(ftxui-tests PROPERTIES UNITY_BUILD OFF)
if (FTXUI_MICROSOFT_TERMINAL_FALLBACK) if (FTXUI_MICROSOFT_TERMINAL_FALLBACK)
target_compile_definitions(ftxui-tests target_compile_definitions(ftxui-tests
PRIVATE "FTXUI_MICROSOFT_TERMINAL_FALLBACK") PRIVATE "FTXUI_MICROSOFT_TERMINAL_FALLBACK")

View File

@@ -50,41 +50,17 @@ int main(void) {
└────┘└────────────────────────────────────┘└─────┘ └────┘└────────────────────────────────────┘└─────┘
``` ```
# Build {#build} ## Configure {#configure}
### Using CMake and find_package {#build-cmake-find-package}
## Using CMake {#build-cmake} Assuming FTXUI is available or installed on the system.
This is an example configuration for your **CMakeLists.txt** **CMakeLists.txt**
CMakeLists.txt
```cmake ```cmake
cmake_minimum_required (VERSION 3.11) cmake_minimum_required (VERSION 3.11)
find_package(ftxui 5 REQUIRED)
# --- Fetch FTXUI -------------------------------------------------------------- project(ftxui-starter LANGUAGES CXX VERSION 1.0.0)
include(FetchContent)
set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE)
FetchContent_Declare(ftxui
GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui
# Important: Specify a GIT_TAG XXXXX here.
)
FetchContent_GetProperties(ftxui)
if(NOT ftxui_POPULATED)
FetchContent_Populate(ftxui)
add_subdirectory(${ftxui_SOURCE_DIR} ${ftxui_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
# ------------------------------------------------------------------------------
project(ftxui-starter
LANGUAGES CXX
VERSION 1.0.0
)
add_executable(ftxui-starter src/main.cpp) add_executable(ftxui-starter src/main.cpp)
target_include_directories(ftxui-starter PRIVATE src)
target_link_libraries(ftxui-starter target_link_libraries(ftxui-starter
PRIVATE ftxui::screen PRIVATE ftxui::screen
PRIVATE ftxui::dom PRIVATE ftxui::dom
@@ -93,7 +69,33 @@ target_link_libraries(ftxui-starter
``` ```
Subsequently, you build the project in the standard fashion as follows: ### Using CMake and FetchContent {#build-cmake}
If you want to fetch FTXUI using cmake:
**CMakeLists.txt**
```cmake
cmake_minimum_required (VERSION 3.11)
include(FetchContent)
set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE)
FetchContent_Declare(ftxui
GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui
GIT_TAG main # Important: Specify a version or a commit hash here.
)
FetchContent_MakeAvailable(ftxui)
project(ftxui-starter LANGUAGES CXX VERSION 1.0.0)
add_executable(ftxui-starter src/main.cpp)
target_link_libraries(ftxui-starter
PRIVATE ftxui::screen
PRIVATE ftxui::dom
PRIVATE ftxui::component # Not needed for this example.
)
```
## Build
```bash ```bash
mkdir build && cd build mkdir build && cd build
cmake .. cmake ..
@@ -633,6 +635,26 @@ Produced by: `ftxui::Input()` from "ftxui/component/component.hpp"
<script id="asciicast-223719" src="https://asciinema.org/a/223719.js" async></script> <script id="asciicast-223719" src="https://asciinema.org/a/223719.js" async></script>
@endhtmlonly @endhtmlonly
### Filtered input
On can filter out the characters received by the input component, using
`ftxui::CatchEvent`.
```cpp
std::string phone_number;
Component input = Input(&phone_number, "phone number");
// Filter out non-digit characters.
input |= CatchEvent([&](Event event) {
return event.is_character() && !std::isdigit(event.character()[0]);
});
// Filter out characters past the 10th one.
input |= CatchEvent([&](Event event) {
return event.is_character() && phone_number.size() >= 10;
});
```
## Menu {#component-menu} ## Menu {#component-menu}
Defines a menu object. It contains a list of entries, one of them is selected. Defines a menu object. It contains a list of entries, one of them is selected.

View File

@@ -21,6 +21,8 @@ if (EMSCRIPTEN)
get_property(EXAMPLES GLOBAL PROPERTY FTXUI::EXAMPLES) get_property(EXAMPLES GLOBAL PROPERTY FTXUI::EXAMPLES)
foreach(file foreach(file
"index.html" "index.html"
"index.mjs"
"index.css"
"sw.js" "sw.js"
"run_webassembly.py") "run_webassembly.py")
configure_file(${file} ${file}) configure_file(${file} ${file})

View File

@@ -11,6 +11,7 @@ example(collapsible)
example(composition) example(composition)
example(custom_loop) example(custom_loop)
example(dropdown) example(dropdown)
example(dropdown_custom)
example(flexbox_gallery) example(flexbox_gallery)
example(focus) example(focus)
example(focus_cursor) example(focus_cursor)
@@ -25,6 +26,7 @@ example(menu2)
example(menu_entries) example(menu_entries)
example(menu_entries_animated) example(menu_entries_animated)
example(menu_in_frame) example(menu_in_frame)
example(menu_in_frame_horizontal)
example(menu_multiple) example(menu_multiple)
example(menu_style) example(menu_style)
example(menu_underline_animated_gallery) example(menu_underline_animated_gallery)
@@ -36,6 +38,7 @@ example(radiobox)
example(radiobox_in_frame) example(radiobox_in_frame)
example(renderer) example(renderer)
example(resizable_split) example(resizable_split)
example(scrollbar)
example(slider) example(slider)
example(slider_direction) example(slider_direction)
example(slider_rgb) example(slider_rgb)

View File

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

View File

@@ -0,0 +1,104 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <string> // for basic_string, string, allocator
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Dropdown, Horizontal, Vertical
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
int main() {
using namespace ftxui;
std::vector<std::string> entries = {
"tribute", "clearance", "ally", "bend", "electronics",
"module", "era", "cultural", "sniff", "nationalism",
"negotiation", "deliver", "figure", "east", "tribute",
"clearance", "ally", "bend", "electronics", "module",
"era", "cultural", "sniff", "nationalism", "negotiation",
"deliver", "figure", "east", "tribute", "clearance",
"ally", "bend", "electronics", "module", "era",
"cultural", "sniff", "nationalism", "negotiation", "deliver",
"figure", "east",
};
auto dropdown_1 = Dropdown({
.radiobox = {.entries = &entries},
.transform =
[](bool open, Element checkbox, Element radiobox) {
if (open) {
return vbox({
checkbox | inverted,
radiobox | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, 10),
filler(),
});
}
return vbox({
checkbox,
filler(),
});
},
});
auto dropdown_2 = Dropdown({
.radiobox = {.entries = &entries},
.transform =
[](bool open, Element checkbox, Element radiobox) {
if (open) {
return vbox({
checkbox | inverted,
radiobox | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, 10) | bgcolor(Color::Blue),
filler(),
});
}
return vbox({
checkbox | bgcolor(Color::Blue),
filler(),
});
},
});
auto dropdown_3 = Dropdown({
.radiobox =
{
.entries = &entries,
.transform =
[](const EntryState& s) {
auto t = text(s.label) | borderEmpty;
if (s.active) {
t |= bold;
}
if (s.focused) {
t |= inverted;
}
return t;
},
},
.transform =
[](bool open, Element checkbox, Element radiobox) {
checkbox |= borderEmpty;
if (open) {
return vbox({
checkbox | inverted,
radiobox | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, 20) | bgcolor(Color::Red),
filler(),
});
}
return vbox({
checkbox | bgcolor(Color::Red),
filler(),
});
},
});
auto screen = ScreenInteractive::FitComponent();
screen.Loop(Container::Horizontal({
dropdown_1,
dropdown_2,
dropdown_3,
}));
}

View File

@@ -264,7 +264,7 @@ int main() {
}); });
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Spiner // Spinner
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
auto spinner_tab_renderer = Renderer([&] { auto spinner_tab_renderer = Renderer([&] {
Elements entries; Elements entries;
@@ -490,15 +490,24 @@ int main() {
}, },
&tab_index); &tab_index);
auto exit_button =
Button("Exit", [&] { screen.Exit(); }, ButtonOption::Animated());
auto main_container = Container::Vertical({ auto main_container = Container::Vertical({
tab_selection, Container::Horizontal({
tab_selection,
exit_button,
}),
tab_content, tab_content,
}); });
auto main_renderer = Renderer(main_container, [&] { auto main_renderer = Renderer(main_container, [&] {
return vbox({ return vbox({
text("FTXUI Demo") | bold | hcenter, text("FTXUI Demo") | bold | hcenter,
tab_selection->Render(), hbox({
tab_selection->Render() | flex,
exit_button->Render(),
}),
tab_content->Render() | flex, tab_content->Render() | flex,
}); });
}); });

View File

@@ -15,30 +15,50 @@
int main() { int main() {
using namespace ftxui; using namespace ftxui;
// The data:
std::string first_name; std::string first_name;
std::string last_name; std::string last_name;
std::string password; std::string password;
std::string phoneNumber;
// The basic input components:
Component input_first_name = Input(&first_name, "first name"); Component input_first_name = Input(&first_name, "first name");
Component input_last_name = Input(&last_name, "last name"); Component input_last_name = Input(&last_name, "last name");
// The password input component:
InputOption password_option; InputOption password_option;
password_option.password = true; password_option.password = true;
Component input_password = Input(&password, "password", password_option); Component input_password = Input(&password, "password", password_option);
// The phone number input component:
// We are using `CatchEvent` to filter out non-digit characters.
Component input_phone_number = Input(&phoneNumber, "phone number");
input_phone_number |= CatchEvent([&](Event event) {
return event.is_character() && !std::isdigit(event.character()[0]);
});
input_phone_number |= CatchEvent([&](Event event) {
return event.is_character() && phoneNumber.size() > 10;
});
// The component tree:
auto component = Container::Vertical({ auto component = Container::Vertical({
input_first_name, input_first_name,
input_last_name, input_last_name,
input_password, input_password,
input_phone_number,
}); });
// Tweak how the component tree is rendered:
auto renderer = Renderer(component, [&] { auto renderer = Renderer(component, [&] {
return vbox({ return vbox({
text("Hello " + first_name + " " + last_name),
separator(),
hbox(text(" First name : "), input_first_name->Render()), hbox(text(" First name : "), input_first_name->Render()),
hbox(text(" Last name : "), input_last_name->Render()), hbox(text(" Last name : "), input_last_name->Render()),
hbox(text(" Password : "), input_password->Render()), hbox(text(" Password : "), input_password->Render()),
hbox(text(" Phone num : "), input_phone_number->Render()),
separator(),
text("Hello " + first_name + " " + last_name),
text("Your password is " + password),
text("Your phone number is " + phoneNumber),
}) | }) |
border; border;
}); });

View File

@@ -0,0 +1,30 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <memory> // for shared_ptr, __shared_ptr_access
#include <string> // for string, basic_string, operator+, to_string
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Radiobox, Renderer
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for operator|, Element, size, border, frame, HEIGHT, LESS_THAN
using namespace ftxui;
int main() {
std::vector<std::string> entries;
int selected = 0;
for (int i = 0; i < 100; ++i)
entries.push_back(std::to_string(i));
auto radiobox = Menu(&entries, &selected, MenuOption::Horizontal());
auto renderer = Renderer(
radiobox, [&] { return radiobox->Render() | hscroll_indicator | frame; });
auto screen = ScreenInteractive::FitComponent();
screen.Loop(renderer);
return 0;
}

View File

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

View File

@@ -0,0 +1,112 @@
// Copyright 2023 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <ftxui/component/component.hpp>
#include <ftxui/component/screen_interactive.hpp>
using namespace ftxui;
Component DummyWindowContent() {
class Impl : public ComponentBase {
private:
float scroll_x = 0.1;
float scroll_y = 0.1;
public:
Impl() {
auto content = Renderer([=] {
const std::string lorem =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed "
"do eiusmod tempor incididunt ut labore et dolore magna "
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
"ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis "
"aute irure dolor in reprehenderit in voluptate velit esse "
"cillum dolore eu fugiat nulla pariatur. Excepteur sint "
"occaecat cupidatat non proident, sunt in culpa qui officia "
"deserunt mollit anim id est laborum.";
return vbox({
text(lorem.substr(0, -1)), text(lorem.substr(5, -1)), text(""),
text(lorem.substr(10, -1)), text(lorem.substr(15, -1)), text(""),
text(lorem.substr(20, -1)), text(lorem.substr(25, -1)), text(""),
text(lorem.substr(30, -1)), text(lorem.substr(35, -1)), text(""),
text(lorem.substr(40, -1)), text(lorem.substr(45, -1)), text(""),
text(lorem.substr(50, -1)), text(lorem.substr(55, -1)), text(""),
text(lorem.substr(60, -1)), text(lorem.substr(65, -1)), text(""),
text(lorem.substr(70, -1)), text(lorem.substr(75, -1)), text(""),
text(lorem.substr(80, -1)), text(lorem.substr(85, -1)), text(""),
text(lorem.substr(90, -1)), text(lorem.substr(95, -1)), text(""),
text(lorem.substr(100, -1)), text(lorem.substr(105, -1)), text(""),
text(lorem.substr(110, -1)), text(lorem.substr(115, -1)), text(""),
text(lorem.substr(120, -1)), text(lorem.substr(125, -1)), text(""),
text(lorem.substr(130, -1)), text(lorem.substr(135, -1)), text(""),
text(lorem.substr(140, -1)),
});
});
auto scrollable_content = Renderer(content, [&, content] {
return content->Render() | focusPositionRelative(scroll_x, scroll_y) |
frame | flex;
});
SliderOption<float> option_x;
option_x.value = &scroll_x;
option_x.min = 0.f;
option_x.max = 1.f;
option_x.increment = 0.1f;
option_x.direction = Direction::Right;
option_x.color_active = Color::Blue;
option_x.color_inactive = Color::BlueLight;
auto scrollbar_x = Slider(option_x);
SliderOption<float> option_y;
option_y.value = &scroll_y;
option_y.min = 0.f;
option_y.max = 1.f;
option_y.increment = 0.1f;
option_y.direction = Direction::Down;
option_y.color_active = Color::Yellow;
option_y.color_inactive = Color::YellowLight;
auto scrollbar_y = Slider(option_y);
Add(Container::Vertical({
Container::Horizontal({
scrollable_content,
scrollbar_y,
}) | flex,
Container::Horizontal({
scrollbar_x,
Renderer([] { return text(L"x"); }),
}),
}));
}
};
return Make<Impl>();
}
int main() {
auto window_1 = Window({
.inner = DummyWindowContent(),
.title = "First window",
.width = 80,
.height = 30,
});
auto window_2 = Window({
.inner = DummyWindowContent(),
.title = "My window",
.left = 40,
.top = 20,
.width = 80,
.height = 30,
});
auto window_container = Container::Stacked({
window_1,
window_2,
});
auto screen = ScreenInteractive::Fullscreen();
screen.Loop(window_container);
return EXIT_SUCCESS;
}

View File

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

107
examples/index.css Normal file
View File

@@ -0,0 +1,107 @@
@import url(https://fonts.googleapis.com/css?family=Khula:700);
body {
background-color:#EEE;
padding:0px;
margin:0px;
font-family: Khula, Helvetica, sans-serif;
font-size: 130%;
}
.page {
max-width:1300px;
margin: auto;
padding: 10px;
}
a {
box-shadow: inset 0 0 0 0 #54b3d6;
color: #0087b9;
margin: 0 -.25rem;
padding: 0 .25rem;
transition: color .3s ease-in-out,
box-shadow .3s ease-in-out;
}
a:hover {
box-shadow: inset 120px 0 0 0 #54b3d6;
color: white;
}
h1 {
text-decoration: underline;
width:100%;
background-color: rgba(100,100,255,0.5);
padding: 10px;
margin: 0;
}
#selectExample {
flex:1;
}
#selectExample, #selectExample option {
font-size: 16px;
font-family: sans-serif;
font-weight: 700;
line-height: 1.3;
border:0px;
background-color: #bbb;
color:black;
}
#selectExample:focus {
outline:none;
}
#terminal {
width:100%;
height 500px;
height: calc(clamp(200px, 100vh - 300px, 900px));
overflow: hidden;
border:none;
background-color:black;
}
#terminalContainer {
overflow: hidden;
border-radius: 10px;
box-shadow: 0px 2px 10px 0px rgba(0,0,0,0.75),
0px 2px 80px 0px rgba(0,0,0,0.50);
}
.fakeButtons {
height: 10px;
width: 10px;
border-radius: 50%;
border: 1px solid #000;
margin:6px;
background-color: #ff3b47;
border-color: #9d252b;
display: inline-block;
}
.fakeMinimize {
left: 11px;
background-color: #ffc100;
border-color: #9d802c;
}
.fakeZoom {
left: 16px;
background-color: #00d742;
border-color: #049931;
}
.fakeMenu {
display:flex;
flex-direction: row;
width:100%;
box-sizing: border-box;
height: 25px;
background-color: #bbb;
color:black;
margin: 0 auto;
overflow: hidden;
}

View File

@@ -1,173 +1,32 @@
<!DOCTYPE html> <html lang="en"> <!DOCTYPE html>
<html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>FTXUI examples WebAssembly</title> <title>FTXUI examples WebAssembly</title>
<script src="https://cdn.jsdelivr.net/npm/xterm@4.18.0/lib/xterm.min.js"></script> <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>➡️</text></svg>">
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-webgl@0.11.4/lib/xterm-addon-webgl.min.js"></script> <link rel="stylesheet" href="index.css">
<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> <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 type="module" src="index.mjs"></script>
<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> </head>
<body> <body>
<script id="example_script"></script> <script id="example_script"></script>
<div class="page"> <div class="page">
<h1>FTXUI WebAssembly Example </h1>
<p> <p>
<a href="https://github.com/ArthurSonzogni/FTXUI">FTXUI</a> is a single <a href="https://github.com/ArthurSonzogni/FTXUI">FTXUI</a> is a simple
C++ library for terminal user interface. functional C++ library for terminal user interface. <br/>
This showcases the: <a href="https://github.com/ArthurSonzogni/FTXUI/tree/master/examples">./example/</a> folder. <br/>
</p> </p>
<p>
On this page, you can try all the examples contained in: <a <div id="terminalContainer">
href="https://github.com/ArthurSonzogni/FTXUI/tree/master/examples">./example/</a> <div class="fakeMenu">
Those are compiled using WebAssembly. <div class="fakeButtons fakeClose"></div>
</p> <div class="fakeButtons fakeMinimize"></div>
<select id="selectExample"></select> <div class="fakeButtons fakeZoom"></div>
<div id="terminal"></div> <select id="selectExample"></select>
</div>
<div id="terminal"></div>
</div>
</div> </div>
</body> </body>
<script>
const example_list = "@EXAMPLES@".split(";");
const url_search_params = new URLSearchParams(window.location.search);
const example = url_search_params.get("file") || "dom/color_gallery";
const select = document.getElementById("selectExample");
for(var i = 0; i < example_list.length; i++) {
var opt = example_list[i];
var el = document.createElement("option");
el.textContent = opt;
el.value = opt;
select.appendChild(el);
}
select.selectedIndex = example_list.findIndex(path => path == example) || 0;
select.addEventListener("change", () => {
location.href = (location.href).split('?')[0] + "?file=" +
example_list[select.selectedIndex];
});
let stdin_buffer = [];
const stdin = () => {
return stdin_buffer.shift() || 0;
}
let stdout_buffer = [];
const stdout = code => {
if (code == 0) {
term.write(new Uint8Array(stdout_buffer));
stdout_buffer = [];
} else {
stdout_buffer.push(code)
}
}
let stderrbuffer = [];
const stderr = code => {
if (code == 0 || code == 10) {
console.error(String.fromCodePoint(...stderrbuffer));
stderrbuffer = [];
} else {
stderrbuffer.push(code)
}
}
const term = new Terminal();
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: () => {
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('/')
words[1] = "ftxui_example_" + words[1] + ".js"
document.querySelector("#example_script").src = words.join('/');
</script>
<style>
body {
background-color:#EEE;
padding:20px;
font-family: Helvetica, sans-serif;
font-size: 130%;
}
.page {
max-width:1300px;
margin: auto;
}
h1 {
text-decoration: underline;
}
select {
display:block;
padding: .6em 1.4em .5em .8em;
border-radius: 20px 20px 0px 0px;
font-size: 16px;
font-family: sans-serif;
font-weight: 700;
color: #444;
line-height: 1.3;
background-color:black;
border:0px;
color:white;
transition: color 0.2s linear;
transition: background-color 0.2s linear;
}
#terminal {
width:100%;
height: 500px;
height: calc(clamp(200px, 100vh - 300px, 900px));
overflow: hidden;
border:none;
padding:auto;
}
</style>
</html> </html>

100
examples/index.mjs Normal file
View File

@@ -0,0 +1,100 @@
import xterm from 'https://cdn.jsdelivr.net/npm/xterm@4.18.0/+esm'
import xterm_addon_webgl from 'https://cdn.jsdelivr.net/npm/xterm-addon-webgl@0.11.4/+esm'
import xterm_addon_fit from 'https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.5.0/+esm'
// Add COOP/COEP via a ServiceWorker to use SharedArrayBuffer
if ("serviceWorker" in navigator && !window.crossOriginIsolated) {
const url_sw = new URL("./sw.js", location.href);
const registration = await navigator.serviceWorker.register(url_sw);
window.location.reload(); // Reload to ensure the COOP/COEP headers are set.
}
const example_list = "@EXAMPLES@".split(";");
const url_search_params = new URLSearchParams(window.location.search);
const select = document.getElementById("selectExample");
for(const example of example_list) {
const option = document.createElement("option");
option.textContent = example;
option.value = example;
select.appendChild(option);
}
const example = url_search_params.get("file") || "dom/color_gallery";
select.selectedIndex = example_list.findIndex(path => path == example) || 0;
select.addEventListener("change", () => {
history.pushState({}, "", "?file=" + example_list[select.selectedIndex]);
location.reload();
});
const term_element = document.querySelector('#terminal');
const term = new xterm.Terminal();
term.options.scrollback = 0;
term.open(term_element);
const fit_addon = new xterm_addon_fit.FitAddon();
const webgl_addon = new xterm_addon_webgl.WebglAddon();
term.loadAddon(webgl_addon);
term.loadAddon(fit_addon);
const stdin_buffer = [];
const stdout_buffer = [];
const stderr_buffer = [];
const stdin = () => {
return stdin_buffer.shift() || 0;
}
const stdout = code => {
if (code == 0) {
term.write(new Uint8Array(stdout_buffer));
stdout_buffer.length = 0;
} else {
stdout_buffer.push(code)
}
}
const stderr = code => {
if (code == 0 || code == 10) {
console.error(String.fromCodePoint(...stderr_buffer));
stderr_buffer = [];
} else {
stderr_buffer.push(code)
}
}
const onBinary = e => {
for(const 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: () => {
if (window.Module._ftxui_on_resize == undefined)
return;
fit_addon.fit();
const resize_handler = () => {
const {cols, rows} = fit_addon.proposeDimensions();
term.resize(cols, rows);
window.Module._ftxui_on_resize(cols, rows);
fit_addon.fit();
};
const resize_observer = new ResizeObserver(resize_handler);
resize_observer.observe(term_element);
resize_handler();
// Disable scrollbar
//term.write('\x1b[?47h')
},
};
const words = example.split('/')
words[1] = "ftxui_example_" + words[1] + ".js"
document.querySelector("#example_script").src = words.join('/');

30
flake.lock generated
View File

@@ -1,12 +1,15 @@
{ {
"nodes": { "nodes": {
"flake-utils": { "flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": { "locked": {
"lastModified": 1678901627, "lastModified": 1694529238,
"narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=", "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6", "rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -17,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1679734080, "lastModified": 1697915759,
"narHash": "sha256-z846xfGLlon6t9lqUzlNtBOmsgQLQIZvR6Lt2dImk1M=", "narHash": "sha256-WyMj5jGcecD+KC8gEs+wFth1J1wjisZf8kVZH13f1Zo=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "dbf5322e93bcc6cfc52268367a8ad21c09d76fea", "rev": "51d906d2341c9e866e48c2efcaac0f2d70bfd43e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -36,6 +39,21 @@
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
} }
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View File

@@ -8,9 +8,11 @@
outputs = {self, nixpkgs, flake-utils}: outputs = {self, nixpkgs, flake-utils}:
flake-utils.lib.eachDefaultSystem (system: flake-utils.lib.eachDefaultSystem (system:
let pkgs = import nixpkgs { inherit system; }; in let pkgs = import nixpkgs { inherit system; }; in
{ let llvm = pkgs.llvmPackages_latest; in
packages.ftxui = pkgs.stdenv.mkDerivation rec { {
packages = rec {
default = pkgs.stdenv.mkDerivation rec {
pname = "ftxui"; pname = "ftxui";
version = "v4.0.0"; version = "v4.0.0";
src = pkgs.fetchFromGitHub { src = pkgs.fetchFromGitHub {
@@ -56,6 +58,19 @@
platforms = pkgs.lib.platforms.all; platforms = pkgs.lib.platforms.all;
}; };
}; };
}
); ftxui = default;
};
devShells = {
default = pkgs.mkShell {
nativeBuildInputs = [
pkgs.cmake
pkgs.clang-tools
llvm.clang
];
};
};
}
);
} }

View File

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

View File

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

View File

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

View File

@@ -29,19 +29,22 @@ using Components = std::vector<Component>;
/// @ingroup component /// @ingroup component
class ComponentBase { class ComponentBase {
public: public:
// virtual Destructor. explicit ComponentBase(Components children)
: children_(std::move(children)) {}
virtual ~ComponentBase(); virtual ~ComponentBase();
ComponentBase() = default; ComponentBase() = default;
// A component is not copiable. // A component is not copyable/movable.
ComponentBase(const ComponentBase&) = delete; ComponentBase(const ComponentBase&) = delete;
void operator=(const ComponentBase&) = delete; ComponentBase(ComponentBase&&) = delete;
ComponentBase& operator=(const ComponentBase&) = delete;
ComponentBase& operator=(ComponentBase&&) = delete;
// Component hierarchy: // Component hierarchy:
ComponentBase* Parent() const; ComponentBase* Parent() const;
Component& ChildAt(size_t i); Component& ChildAt(size_t i);
size_t ChildCount() const; size_t ChildCount() const;
int Index() const;
void Add(Component children); void Add(Component children);
void Detach(); void Detach();
void DetachAllChildren(); void DetachAllChildren();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,7 +22,7 @@ using Elements = std::vector<Element>;
class Node { class Node {
public: public:
Node(); Node();
Node(Elements children); explicit Node(Elements children);
Node(const Node&) = delete; Node(const Node&) = delete;
Node(const Node&&) = delete; Node(const Node&&) = delete;
Node& operator=(const Node&) = delete; Node& operator=(const Node&) = delete;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,12 +3,10 @@
// the LICENSE file. // the LICENSE file.
#include <functional> // for function #include <functional> // for function
#include <memory> // for shared_ptr
#include <utility> // for move #include <utility> // for move
#include "ftxui/component/animation.hpp" // for Animator, Params (ptr only) #include "ftxui/component/animation.hpp" // for Animator, Params (ptr only)
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse #include "ftxui/component/component.hpp" // for Make, Button
#include "ftxui/component/component.hpp" // for Make, Button
#include "ftxui/component/component_base.hpp" // for ComponentBase #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for ButtonOption, AnimatedColorOption, AnimatedColorsOption, EntryState #include "ftxui/component/component_options.hpp" // for ButtonOption, AnimatedColorOption, AnimatedColorsOption, EntryState
#include "ftxui/component/event.hpp" // for Event, Event::Return #include "ftxui/component/event.hpp" // for Event, Event::Return
@@ -50,11 +48,8 @@ class ButtonBase : public ComponentBase, public ButtonOption {
} }
auto focus_management = focused ? focus : active ? select : nothing; auto focus_management = focused ? focus : active ? select : nothing;
const EntryState state = { const EntryState state{
*label, *label, false, active, focused_or_hover, Index(),
false,
active,
focused_or_hover,
}; };
auto element = (transform ? transform : DefaultTransform) // auto element = (transform ? transform : DefaultTransform) //
@@ -98,10 +93,13 @@ class ButtonBase : public ComponentBase, public ButtonOption {
} }
void OnClick() { void OnClick() {
on_click();
animation_background_ = 0.5F; // NOLINT animation_background_ = 0.5F; // NOLINT
animation_foreground_ = 0.5F; // NOLINT animation_foreground_ = 0.5F; // NOLINT
SetAnimationTarget(1.F); // NOLINT SetAnimationTarget(1.F); // NOLINT
// TODO(arthursonzogni): Consider posting the task to the main loop, instead
// of invoking it immediately.
on_click(); // May delete this.
} }
bool OnEvent(Event event) override { bool OnEvent(Event event) override {
@@ -110,7 +108,7 @@ class ButtonBase : public ComponentBase, public ButtonOption {
} }
if (event == Event::Return) { if (event == Event::Return) {
OnClick(); OnClick(); // May delete this.
return true; return true;
} }
return false; return false;
@@ -127,7 +125,7 @@ class ButtonBase : public ComponentBase, public ButtonOption {
if (event.mouse().button == Mouse::Left && if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) { event.mouse().motion == Mouse::Pressed) {
TakeFocus(); TakeFocus();
OnClick(); OnClick(); // May delete this.
return true; return true;
} }
@@ -204,7 +202,7 @@ Component Button(ButtonOption option) {
Component Button(ConstStringRef label, Component Button(ConstStringRef label,
std::function<void()> on_click, std::function<void()> on_click,
ButtonOption option) { ButtonOption option) {
option.label = label; option.label = std::move(label);
option.on_click = std::move(on_click); option.on_click = std::move(on_click);
return Make<ButtonBase>(std::move(option)); return Make<ButtonBase>(std::move(option));
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,7 @@
#include <cassert> // for assert #include <cassert> // for assert
#include <cstddef> // for size_t #include <cstddef> // for size_t
#include <iterator> // for begin, end #include <iterator> // for begin, end
#include <memory> // for unique_ptr, make_unique
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type #include <vector> // for vector, __alloc_traits<>::value_type
@@ -50,6 +51,22 @@ size_t ComponentBase::ChildCount() const {
return children_.size(); return children_.size();
} }
/// @brief Return index of the component in its parent. -1 if no parent.
/// @ingroup component
int ComponentBase::Index() const {
if (parent_ == nullptr) {
return -1;
}
int index = 0;
for (const Component& child : parent_->children_) {
if (child.get() == this) {
return index;
}
index++;
}
return -1; // Not reached.
}
/// @brief Add a child. /// @brief Add a child.
/// @@param child The child to be attached. /// @@param child The child to be attached.
/// @ingroup component /// @ingroup component

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,13 +5,11 @@
#include <cstddef> // for size_t #include <cstddef> // for size_t
#include <cstdint> // for uint32_t #include <cstdint> // for uint32_t
#include <functional> // for function #include <functional> // for function
#include <memory> // for allocator, shared_ptr, allocator_traits<>::value_type #include <sstream> // for basic_istream, stringstream
#include <sstream> // for basic_istream, stringstream #include <string> // for string, basic_string, operator==, getline
#include <string> // for string, basic_string, operator==, getline #include <utility> // for move
#include <utility> // for move #include <vector> // for vector
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Input #include "ftxui/component/component.hpp" // for Make, Input
#include "ftxui/component/component_base.hpp" // for ComponentBase #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for InputOption #include "ftxui/component/component_options.hpp" // for InputOption
@@ -100,8 +98,9 @@ class InputBase : public ComponentBase, public InputOption {
// Component implementation: // Component implementation:
Element Render() override { Element Render() override {
const bool is_focused = Focused(); const bool is_focused = Focused();
const auto focused = const auto focused = (!is_focused && !hovered_) ? select
(is_focused || hovered_) ? focusCursorBarBlinking : select; : insert() ? focusCursorBarBlinking
: focusCursorBlockBlinking;
auto transform_func = auto transform_func =
transform ? transform : InputOption::Default().transform; transform ? transform : InputOption::Default().transform;
@@ -133,7 +132,7 @@ class InputBase : public ComponentBase, public InputOption {
break; break;
} }
cursor_char_index -= line.size() + 1; cursor_char_index -= static_cast<int>(line.size() + 1);
cursor_line++; cursor_line++;
} }
@@ -163,7 +162,7 @@ class InputBase : public ComponentBase, public InputOption {
// The cursor is on this line. // The cursor is on this line.
const int glyph_start = cursor_char_index; const int glyph_start = cursor_char_index;
const int glyph_end = GlyphNext(line, glyph_start); const int glyph_end = static_cast<int>(GlyphNext(line, glyph_start));
const std::string part_before_cursor = line.substr(0, glyph_start); const std::string part_before_cursor = line.substr(0, glyph_start);
const std::string part_at_cursor = const std::string part_at_cursor =
line.substr(glyph_start, glyph_end - glyph_start); line.substr(glyph_start, glyph_end - glyph_start);
@@ -205,11 +204,12 @@ class InputBase : public ComponentBase, public InputOption {
const size_t start = GlyphPrevious(content(), cursor_position()); const size_t start = GlyphPrevious(content(), cursor_position());
const size_t end = cursor_position(); const size_t end = cursor_position();
content->erase(start, end - start); content->erase(start, end - start);
cursor_position() = start; cursor_position() = static_cast<int>(start);
on_change();
return true; return true;
} }
bool HandleDelete() { bool DeleteImpl() {
if (cursor_position() == (int)content->size()) { if (cursor_position() == (int)content->size()) {
return false; return false;
} }
@@ -219,12 +219,21 @@ class InputBase : public ComponentBase, public InputOption {
return true; return true;
} }
bool HandleDelete() {
if (DeleteImpl()) {
on_change();
return true;
}
return false;
}
bool HandleArrowLeft() { bool HandleArrowLeft() {
if (cursor_position() == 0) { if (cursor_position() == 0) {
return false; return false;
} }
cursor_position() = GlyphPrevious(content(), cursor_position()); cursor_position() =
static_cast<int>(GlyphPrevious(content(), cursor_position()));
return true; return true;
} }
@@ -233,7 +242,8 @@ class InputBase : public ComponentBase, public InputOption {
return false; return false;
} }
cursor_position() = GlyphNext(content(), cursor_position()); cursor_position() =
static_cast<int>(GlyphNext(content(), cursor_position()));
return true; return true;
} }
@@ -248,7 +258,7 @@ class InputBase : public ComponentBase, public InputOption {
if (content()[iter] == '\n') { if (content()[iter] == '\n') {
break; break;
} }
width += GlyphWidth(content(), iter); width += static_cast<int>(GlyphWidth(content(), iter));
} }
return width; return width;
} }
@@ -261,8 +271,9 @@ class InputBase : public ComponentBase, public InputOption {
return; return;
} }
columns -= GlyphWidth(content(), cursor_position()); columns -= static_cast<int>(GlyphWidth(content(), cursor_position()));
cursor_position() = GlyphNext(content(), cursor_position()); cursor_position() =
static_cast<int>(GlyphNext(content(), cursor_position()));
} }
} }
@@ -282,9 +293,10 @@ class InputBase : public ComponentBase, public InputOption {
if (content()[previous] == '\n') { if (content()[previous] == '\n') {
break; break;
} }
cursor_position() = previous; cursor_position() = static_cast<int>(previous);
} }
cursor_position() = GlyphPrevious(content(), cursor_position()); cursor_position() =
static_cast<int>(GlyphPrevious(content(), cursor_position()));
while (true) { while (true) {
if (cursor_position() == 0) { if (cursor_position() == 0) {
break; break;
@@ -293,10 +305,10 @@ class InputBase : public ComponentBase, public InputOption {
if (content()[previous] == '\n') { if (content()[previous] == '\n') {
break; break;
} }
cursor_position() = previous; cursor_position() = static_cast<int>(previous);
} }
MoveCursorColumn(columns); MoveCursorColumn(static_cast<int>(columns));
return true; return true;
} }
@@ -312,14 +324,16 @@ class InputBase : public ComponentBase, public InputOption {
if (content()[cursor_position()] == '\n') { if (content()[cursor_position()] == '\n') {
break; break;
} }
cursor_position() = GlyphNext(content(), cursor_position()); cursor_position() =
static_cast<int>(GlyphNext(content(), cursor_position()));
if (cursor_position() == (int)content().size()) { if (cursor_position() == (int)content().size()) {
return true; return true;
} }
} }
cursor_position() = GlyphNext(content(), cursor_position()); cursor_position() =
static_cast<int>(GlyphNext(content(), cursor_position()));
MoveCursorColumn(columns); MoveCursorColumn(static_cast<int>(columns));
return true; return true;
} }
@@ -329,7 +343,7 @@ class InputBase : public ComponentBase, public InputOption {
} }
bool HandleEnd() { bool HandleEnd() {
cursor_position() = content->size(); cursor_position() = static_cast<int>(content->size());
return true; return true;
} }
@@ -342,10 +356,13 @@ class InputBase : public ComponentBase, public InputOption {
} }
bool HandleCharacter(const std::string& character) { bool HandleCharacter(const std::string& character) {
if (!insert() && cursor_position() < (int)content->size() &&
content()[cursor_position()] != '\n') {
DeleteImpl();
}
content->insert(cursor_position(), character); content->insert(cursor_position(), character);
cursor_position() += character.size(); cursor_position() += static_cast<int>(character.size());
on_change(); on_change();
return true; return true;
} }
@@ -391,7 +408,9 @@ class InputBase : public ComponentBase, public InputOption {
if (event == Event::ArrowRightCtrl) { if (event == Event::ArrowRightCtrl) {
return HandleRightCtrl(); return HandleRightCtrl();
} }
if (event == Event::Insert) {
return HandleInsert();
}
return false; return false;
} }
@@ -406,7 +425,7 @@ class InputBase : public ComponentBase, public InputOption {
if (IsWordCharacter(content(), previous)) { if (IsWordCharacter(content(), previous)) {
break; break;
} }
cursor_position() = previous; cursor_position() = static_cast<int>(previous);
} }
// Move left, as long as left is a word character: // Move left, as long as left is a word character:
while (cursor_position()) { while (cursor_position()) {
@@ -414,7 +433,7 @@ class InputBase : public ComponentBase, public InputOption {
if (!IsWordCharacter(content(), previous)) { if (!IsWordCharacter(content(), previous)) {
break; break;
} }
cursor_position() = previous; cursor_position() = static_cast<int>(previous);
} }
return true; return true;
} }
@@ -426,7 +445,8 @@ class InputBase : public ComponentBase, public InputOption {
// Move right, until entering a word. // Move right, until entering a word.
while (cursor_position() < (int)content().size()) { while (cursor_position() < (int)content().size()) {
cursor_position() = GlyphNext(content(), cursor_position()); cursor_position() =
static_cast<int>(GlyphNext(content(), cursor_position()));
if (IsWordCharacter(content(), cursor_position())) { if (IsWordCharacter(content(), cursor_position())) {
break; break;
} }
@@ -437,7 +457,7 @@ class InputBase : public ComponentBase, public InputOption {
if (!IsWordCharacter(content(), cursor_position())) { if (!IsWordCharacter(content(), cursor_position())) {
break; break;
} }
cursor_position() = next; cursor_position() = static_cast<int>(next);
} }
return true; return true;
@@ -451,8 +471,10 @@ class InputBase : public ComponentBase, public InputOption {
return false; return false;
} }
if (event.mouse().button != Mouse::Left || if (event.mouse().button != Mouse::Left) {
event.mouse().motion != Mouse::Pressed) { return false;
}
if (event.mouse().motion != Mouse::Pressed) {
return false; return false;
} }
@@ -472,7 +494,7 @@ class InputBase : public ComponentBase, public InputOption {
break; break;
} }
cursor_char_index -= line.size() + 1; cursor_char_index -= static_cast<int>(line.size() + 1);
cursor_line++; cursor_line++;
} }
const int cursor_column = const int cursor_column =
@@ -498,17 +520,24 @@ class InputBase : public ComponentBase, public InputOption {
// Convert back the new_cursor_{line,column} toward cursor_position: // Convert back the new_cursor_{line,column} toward cursor_position:
cursor_position() = 0; cursor_position() = 0;
for (int i = 0; i < new_cursor_line; ++i) { for (int i = 0; i < new_cursor_line; ++i) {
cursor_position() += lines[i].size() + 1; cursor_position() += static_cast<int>(lines[i].size() + 1);
} }
while (new_cursor_column > 0) { while (new_cursor_column > 0) {
new_cursor_column -= GlyphWidth(content(), cursor_position()); new_cursor_column -=
cursor_position() = GlyphNext(content(), cursor_position()); static_cast<int>(GlyphWidth(content(), cursor_position()));
cursor_position() =
static_cast<int>(GlyphNext(content(), cursor_position()));
} }
on_change(); on_change();
return true; return true;
} }
bool HandleInsert() {
insert() = !insert();
return true;
}
bool Focusable() const final { return true; } bool Focusable() const final { return true; }
bool hovered_ = false; bool hovered_ = false;

View File

@@ -1,7 +1,6 @@
// Copyright 2023 Arthur Sonzogni. All rights reserved. // Copyright 2023 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string #include <string> // for string
#include "ftxui/component/component.hpp" // for Input #include "ftxui/component/component.hpp" // for Input
@@ -754,4 +753,32 @@ TEST(InputTest, OnEnter) {
EXPECT_TRUE(on_enter_called); EXPECT_TRUE(on_enter_called);
} }
TEST(InputTest, InsertMode) {
std::string content = "abc\nefg";
bool insert = true;
int cursor_position = 1;
Component input = Input({
.content = &content,
.insert = &insert,
.cursor_position = &cursor_position,
});
EXPECT_TRUE(insert);
EXPECT_TRUE(input->OnEvent(Event::Insert));
EXPECT_FALSE(insert);
EXPECT_EQ(content, "abc\nefg");
EXPECT_TRUE(input->OnEvent(Event::Character('x')));
EXPECT_EQ(content, "axc\nefg");
EXPECT_TRUE(input->OnEvent(Event::Character('y')));
EXPECT_EQ(content, "axy\nefg");
EXPECT_TRUE(input->OnEvent(Event::Character('z')));
EXPECT_EQ(content, "axyz\nefg");
EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
EXPECT_EQ(content, "axyz\nefg");
EXPECT_TRUE(input->OnEvent(Event::Character('X')));
EXPECT_EQ(content, "axyz\nefgX");
}
} // namespace ftxui } // namespace ftxui

View File

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

View File

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

View File

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

View File

@@ -2,11 +2,9 @@
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <gtest/gtest.h> // for Test, EXPECT_EQ, Message, TestPartResult, TestInfo (ptr only), TEST #include <gtest/gtest.h> // for Test, EXPECT_EQ, Message, TestPartResult, TestInfo (ptr only), TEST
#include <chrono> // for operator""s, chrono_literals
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up #include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
#include <memory> // for __shared_ptr_access, shared_ptr, allocator #include <string> // for string, basic_string
#include <string> // for string, basic_string #include <vector> // for vector
#include <vector> // for vector
#include "ftxui/component/animation.hpp" // for Duration, Params #include "ftxui/component/animation.hpp" // for Duration, Params
#include "ftxui/component/component.hpp" // for Menu #include "ftxui/component/component.hpp" // for Menu
@@ -228,5 +226,50 @@ TEST(MenuTest, AnimationsVertical) {
} }
} }
TEST(MenuTest, EntryIndex) {
int selected = 0;
std::vector<std::string> entries = {"0", "1", "2"};
auto option = MenuOption::Vertical();
option.entries = &entries;
option.selected = &selected;
option.entries_option.transform = [&](const EntryState& state) {
int curidx = std::stoi(state.label);
EXPECT_EQ(state.index, curidx);
return text(state.label);
};
auto menu = Menu(option);
menu->OnEvent(Event::ArrowDown);
menu->OnEvent(Event::ArrowDown);
menu->OnEvent(Event::Return);
entries.resize(2);
(void)menu->Render();
}
TEST(MenuTest, MenuEntryIndex) {
int selected = 0;
MenuEntryOption option;
option.transform = [&](const EntryState& state) {
int curidx = std::stoi(state.label);
EXPECT_EQ(state.index, curidx);
return text(state.label);
};
auto menu = Container::Vertical(
{
MenuEntry("0", option),
MenuEntry("1", option),
MenuEntry("2", option),
},
&selected);
menu->OnEvent(Event::ArrowDown);
menu->OnEvent(Event::ArrowDown);
menu->OnEvent(Event::Return);
for (int index = 0; index < menu->ChildCount(); index++) {
EXPECT_EQ(menu->ChildAt(index)->Index(), index);
}
}
} // namespace ftxui } // namespace ftxui
// NOLINTEND // NOLINTEND

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,6 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <string> // for string
#include <thread> // for thread #include <thread> // for thread
#include <utility> // for move #include <utility> // for move

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,6 @@
// Copyright 2021 Arthur Sonzogni. All rights reserved. // Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <vector>
#include "ftxui/component/terminal_input_parser.hpp" #include "ftxui/component/terminal_input_parser.hpp"
extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
@@ -9,12 +8,14 @@ extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
auto event_receiver = MakeReceiver<Task>(); auto event_receiver = MakeReceiver<Task>();
{ {
auto parser = TerminalInputParser(event_receiver->MakeSender()); auto parser = TerminalInputParser(event_receiver->MakeSender());
for (size_t i = 0; i < size; ++i) for (size_t i = 0; i < size; ++i) {
parser.Add(data[i]); parser.Add(data[i]);
}
} }
Task received; Task received;
while (event_receiver->Receive(&received)) while (event_receiver->Receive(&received)) {
; // Do nothing.
}
return 0; // Non-zero return values are reserved for future use. return 0; // Non-zero return values are reserved for future use.
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,6 @@
// the LICENSE file. // the LICENSE file.
#include <memory> // for make_shared, __shared_ptr_access #include <memory> // for make_shared, __shared_ptr_access
#include <utility> // for move #include <utility> // for move
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for Element, unpack, filler, flex, flex_grow, flex_shrink, notflex, xflex, xflex_grow, xflex_shrink, yflex, yflex_grow, yflex_shrink #include "ftxui/dom/elements.hpp" // for Element, unpack, filler, flex, flex_grow, flex_shrink, notflex, xflex, xflex_grow, xflex_shrink, yflex, yflex_grow, yflex_shrink
#include "ftxui/dom/node.hpp" // for Elements, Node #include "ftxui/dom/node.hpp" // for Elements, Node
@@ -92,14 +91,14 @@ class Flex : public Node {
} // namespace } // namespace
/// @brief An element that will take expand proportionnally to the space left in /// @brief An element that will take expand proportionally to the space left in
/// a container. /// a container.
/// @ingroup dom /// @ingroup dom
Element filler() { Element filler() {
return std::make_shared<Flex>(function_flex); return std::make_shared<Flex>(function_flex);
} }
/// @brief Make a child element to expand proportionnally to the space left in a /// @brief Make a child element to expand proportionally to the space left in a
/// container. /// container.
/// @ingroup dom /// @ingroup dom
/// ///

View File

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

View File

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

View File

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

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