Compare commits

...

118 Commits
v5.0.0 ... main

Author SHA1 Message Date
Jacob Shing
4604adb502
Adds example project using FTXUI (#1044)
Some checks failed
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (cl, windows-latest) (push) Has been cancelled
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, macos-latest) (push) Has been cancelled
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, ubuntu-latest) (push) Has been cancelled
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, macos-latest) (push) Has been cancelled
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, ubuntu-latest) (push) Has been cancelled
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (cl, Windows MSVC, windows-latest) (push) Has been cancelled
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, Linux GCC, ubuntu-latest) (push) Has been cancelled
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, llvm-cov gcov, Linux Clang, ubuntu-latest) (push) Has been cancelled
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, llvm-cov gcov, MacOS clang, macos-latest) (push) Has been cancelled
Documentation / documentation (push) Has been cancelled
2025-05-05 17:44:13 +02:00
ArthurSonzogni
add5f40d31
Restore dbox behavior from ftxui5.0.0
Some checks failed
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (cl, windows-latest) (push) Has been cancelled
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, macos-latest) (push) Has been cancelled
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, ubuntu-latest) (push) Has been cancelled
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, macos-latest) (push) Has been cancelled
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, ubuntu-latest) (push) Has been cancelled
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (cl, Windows MSVC, windows-latest) (push) Has been cancelled
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, Linux GCC, ubuntu-latest) (push) Has been cancelled
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, llvm-cov gcov, Linux Clang, ubuntu-latest) (push) Has been cancelled
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, llvm-cov gcov, MacOS clang, macos-latest) (push) Has been cancelled
Documentation / documentation (push) Has been cancelled
Bug:https://github.com/eclipse-ecal/ecal/pull/2095
2025-05-02 16:48:00 +02:00
ArthurSonzogni
805db9bdea
Set Bazel compatibility level
Some checks failed
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (cl, windows-latest) (push) Has been cancelled
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, macos-latest) (push) Has been cancelled
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, ubuntu-latest) (push) Has been cancelled
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, macos-latest) (push) Has been cancelled
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, ubuntu-latest) (push) Has been cancelled
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (cl, Windows MSVC, windows-latest) (push) Has been cancelled
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, Linux GCC, ubuntu-latest) (push) Has been cancelled
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, llvm-cov gcov, Linux Clang, ubuntu-latest) (push) Has been cancelled
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, llvm-cov gcov, MacOS clang, macos-latest) (push) Has been cancelled
Documentation / documentation (push) Has been cancelled
2025-05-01 11:53:18 +02:00
ArthurSonzogni
784f53fd7e
Remove attestion for Bazel 2025-05-01 11:30:12 +02:00
ArthurSonzogni
799d8a267e
Fix release workflow 2025-05-01 10:52:54 +02:00
ArthurSonzogni
f4513702b0
Fix release workflow 2025-05-01 10:40:53 +02:00
ArthurSonzogni
ba6716c6e1
Fix publish workflow 2025-05-01 10:33:19 +02:00
ArthurSonzogni
694fa6bf5c
Fix publish workflow 2025-05-01 10:09:18 +02:00
Arthur Sonzogni
625915b52c
Fix publish workflow (#1041) 2025-05-01 10:06:49 +02:00
Arthur Sonzogni
2d4c114008
v6.1.2 (#1040) 2025-05-01 10:01:05 +02:00
Arthur Sonzogni
aa80d8bac9
Generate attestation + refactor workflows (#1039) 2025-05-01 09:59:08 +02:00
Arthur Sonzogni
bcdcf70348
Fix Bazel Central Repository Workflow (#1038)
Some checks are pending
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (cl, windows-latest) (push) Waiting to run
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, macos-latest) (push) Waiting to run
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, ubuntu-latest) (push) Waiting to run
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, macos-latest) (push) Waiting to run
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, ubuntu-latest) (push) Waiting to run
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (cl, Windows MSVC, windows-latest) (push) Waiting to run
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, Linux GCC, ubuntu-latest) (push) Waiting to run
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, llvm-cov gcov, Linux Clang, ubuntu-latest) (push) Waiting to run
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, llvm-cov gcov, MacOS clang, macos-latest) (push) Waiting to run
Build / Create release (push) Blocked by required conditions
Build / Build packages (build/ftxui*Darwin*, macos-latest) (push) Blocked by required conditions
Build / Build packages (build/ftxui*Linux*, ubuntu-latest) (push) Blocked by required conditions
Build / Build packages (build/ftxui*Win64*, windows-latest) (push) Blocked by required conditions
Build / Build source package (push) Blocked by required conditions
Build / documentation (push) Waiting to run
2025-04-30 12:04:32 +02:00
Arthur Sonzogni
4231c4903b
Release version 6.1.0. (#1037)
Some checks are pending
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (cl, windows-latest) (push) Waiting to run
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, macos-latest) (push) Waiting to run
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, ubuntu-latest) (push) Waiting to run
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, macos-latest) (push) Waiting to run
Build / Bazel, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, ubuntu-latest) (push) Waiting to run
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (cl, Windows MSVC, windows-latest) (push) Waiting to run
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, Linux GCC, ubuntu-latest) (push) Waiting to run
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, llvm-cov gcov, Linux Clang, ubuntu-latest) (push) Waiting to run
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, llvm-cov gcov, MacOS clang, macos-latest) (push) Waiting to run
Build / Create release (push) Blocked by required conditions
Build / Build packages (build/ftxui*Darwin*, macos-latest) (push) Blocked by required conditions
Build / Build packages (build/ftxui*Linux*, ubuntu-latest) (push) Blocked by required conditions
Build / Build packages (build/ftxui*Win64*, windows-latest) (push) Blocked by required conditions
Build / Build source package (push) Blocked by required conditions
Build / documentation (push) Waiting to run
This highlight support for the Bazel build system.
2025-04-29 16:05:11 +02:00
Arthur Sonzogni
10d73d365f
Support Bazel build system (#1033)
Bug:https://github.com/ArthurSonzogni/FTXUI/issues/1032
Fixed:https://github.com/ArthurSonzogni/FTXUI/issues/1032
2025-04-29 15:11:10 +02:00
Arthur Sonzogni
07fd3e685a
Bugfix: Avoid crash with ResizeableSplit. (#1025)
Component
---------
- Bugfix: Fix a crash with ResizeableSplit. See #1023.
  - Clamp screen size to terminal size.
  - Disallow `ResizeableSplit` with negative size.

Dom
---
- Bugfix: Disallow specifying a negative size constraint. See #1023.

Bug: https://github.com/ArthurSonzogni/FTXUI/issues/1023
2025-03-31 18:19:48 +02:00
ArthurSonzogni
09eb2f7fb0
v6.0.2 2025-03-30 01:27:57 +01:00
Arthur Sonzogni
1144e13125
Apply @forworldm code review. (#1022)
See: https://github.com/ArthurSonzogni/FTXUI/pull/1021?notification_referrer_id=NT_kwDOAEieQrMxNTU3OTg4MDA1MDo0NzU5MTA2#discussion_r2019827970
2025-03-30 01:22:17 +01:00
Arthur Sonzogni
4ba7dd2c5e
Window: Major crash fix. (#1021)
A patch handling focus was recently merged, but a special condition on
Windows was inverted, causing a segfault.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/1020
2025-03-29 12:51:08 +01:00
ArthurSonzogni
ee24bec3ba
v6.0.1
Same as v6.0.0.

Due to a problem tag v6.0.0 was replaced. This isn't a good practice and affect
developers that started using it in the short timeframe. Submitting a new
release with the same content is the best way to fix this.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/1017
Bug:https://github.com/ArthurSonzogni/FTXUI/issues/1019
2025-03-28 12:08:59 +01:00
ArthurSonzogni
327f43b175
v6.0.0 2025-03-27 19:19:44 +01:00
Arthur Sonzogni
5bf8ee819b
Update README.md 2025-03-23 23:55:31 +01:00
Arthur Sonzogni
d5b741b2be
Update README.md 2025-03-23 19:26:36 +01:00
ArthurSonzogni
b69e0f8b91
v6.0.0 2025-03-23 18:19:57 +01:00
Arthur Sonzogni
67163c2571
Fix errors. (#1010) 2025-03-23 15:29:01 +01:00
KenReneris
2c9a828402
Add support for italics (#1009)
Co-authored-by: Ken Reneris <ms/devops kreneris@microsoft.com>
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2025-03-22 18:03:43 +01:00
ArthurSonzogni
bc682d25a6
Fix compiler nits. 2025-03-22 17:31:27 +01:00
Arthur Sonzogni
96e8b8d92e
Implement Node::Select for flexbox. (#977) 2025-03-21 16:15:25 +01:00
ArthurSonzogni
f2fb434e31
Quickfix 2025-03-20 19:59:59 +01:00
Ayaan
b0e087ecef
Merge dom and component focus (#978)
Instead of two levels of focus with `focus` and `selected`, use a recursive
level. The components set the one "active" and hbox/vbox/dbox 

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2025-03-19 15:33:05 +01:00
Emmanuel Ogie
8519e9b0f3
Add terminal-rain and keywords to README.md (#1003)
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2025-03-13 12:24:57 +01:00
Frames
36c669c194
Add a new example in README.md (#1005)
Add project to README.

Add https://github.com/Cyxuan0311/FTB.git
2025-03-13 12:17:48 +01:00
Arthur Sonzogni
d75108e960
Fix linear_gradient float precision bug.
This was reported by:
https://github.com/ArthurSonzogni/FTXUI/issues/998

Indeed, the `t` interpolation factor, which is itself interpolated might become
slightly larger than 1.0. This is due to the float precision.
This was supposedly handled, but there was an off-by-one error in the check.

Along the way, fix a bug found by a fuzzer.

Bug: https://github.com/ArthurSonzogni/FTXUI/issues/998
Fixed: https://github.com/ArthurSonzogni/FTXUI/issues/998
2025-02-10 23:10:27 +01:00
Yazid
15587dad01
Adding BestEdrOfTheMarket in examples (#995) 2025-01-27 18:21:59 +01:00
s1dd
c58a234f05
[DOCS] Add inLimbo as example project (#988) 2025-01-20 15:29:54 +01:00
Jan Stranik
c89569f5a7
Update menu.cpp - remove unused variable (#982) 2025-01-03 18:20:11 +01:00
Arthur Sonzogni
f6a690a942
Fix dev warning. (#980) 2024-12-29 10:24:17 +01:00
Clément Roblot
6fafa2dfed
Feature: Selection
Add support for selection content in the dom.
2024-12-27 09:45:13 +01:00
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
167 changed files with 5513 additions and 1714 deletions

9
.bcr/README.md Normal file
View File

@ -0,0 +1,9 @@
# Bazel Central Registry
When the ruleset is released, we want it to be published to the
Bazel Central Registry automatically:
<https://registry.bazel.build>
This folder contains configuration files to automate the publish step.
See <https://github.com/bazel-contrib/publish-to-bcr/blob/main/templates/README.md>
for authoritative documentation about these files.

View File

@ -0,0 +1,16 @@
{
"homepage": "https://github.com/ArthurSonzogni/FTXUI",
"maintainers": [
{
"name": "Arthur Sonzogni",
"email": "sonzogniarthur@gmail.com",
"github": "ArthurSonzogni",
"github_user_id": 4759106
}
],
"repository": [
"github:ArthurSonzogni/FTXUI"
],
"versions": [],
"yanked_versions": {}
}

21
.bcr/presubmit.yml Normal file
View File

@ -0,0 +1,21 @@
matrix:
platform:
- centos7
- debian10
- ubuntu2004
- macos
- windows
bazel: [6.x, 7.x, 8.x]
tasks:
verify_targets:
name: Build and test.
platform: ${{ platform }}
bazel: ${{ bazel }}
build_targets:
- '@ftxui//:ftxui'
- '@ftxui//:screen'
- '@ftxui//:dom'
- '@ftxui//:component'
test_targets:
- '@ftxui//:tests'

View File

@ -0,0 +1,5 @@
{
"integrity": "",
"strip_prefix": "",
"url": "https://github.com/ArthurSonzogni/FTXUI/releases/download/{TAG}/source.tar.gz"
}

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

@ -1,19 +1,52 @@
name: Build name: Build
on: on:
create: # On new commits to main:
tags:
-v*
push: push:
branches: branches:
- main - main
# On pull requests:
pull_request: pull_request:
branches: branches:
- main - main
jobs: jobs:
test:
name: "Tests" test_bazel:
name: "Bazel, ${{ matrix.compiler }}, ${{ matrix.os }}"
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
compiler: gcc
- os: ubuntu-latest
compiler: llvm
- os: macos-latest
compiler: llvm
- os: macos-latest
compiler: gcc
- os: windows-latest
compiler: cl
runs-on: ${{ matrix.os }}
steps:
- name: "Checkout repository"
uses: actions/checkout@v3
- name: "Build with Bazel"
run: bazel build ...
- name: "Tests with Bazel"
run: bazel run tests
test_cmake:
name: "CMake, ${{ matrix.compiler }}, ${{ matrix.os }}"
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -21,7 +54,6 @@ jobs:
- name: Linux GCC - name: Linux GCC
os: ubuntu-latest os: ubuntu-latest
compiler: gcc compiler: gcc
gcov_executable: gcov
- name: Linux Clang - name: Linux Clang
os: ubuntu-latest os: ubuntu-latest
@ -44,7 +76,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 +111,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: matrix.gcov_executable != ''
working-directory: ./build
run: >
gcovr gcovr
-j ${{env.nproc}} -j ${{env.nproc}}
--delete --delete
@ -117,117 +154,3 @@ jobs:
flags: ${{ runner.os }} flags: ${{ runner.os }}
name: ${{ runner.os }}-coverage name: ${{ runner.os }}-coverage
files: ./build/coverage.xml files: ./build/coverage.xml
# Create a release on new v* tags
release:
needs: test
if: ${{ github.event_name == 'create' && startsWith(github.ref, 'refs/tags/v') }}
name: "Create release"
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: "Create release"
uses: softprops/action-gh-release@v1
id: create_release
with:
draft: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Build artifact for the release
package:
name: "Build packages"
needs: release
strategy:
matrix:
include:
- os: ubuntu-latest
asset_path: build/ftxui*Linux*
- os: macos-latest
asset_path: build/ftxui*Darwin*
- os: windows-latest
asset_path: build/ftxui*Win64*
runs-on: ${{ matrix.os }}
steps:
- name: Get number of CPU cores
uses: SimenB/github-actions-cpu-cores@v1
id: cpu-cores
- name: "Checkout repository"
uses: actions/checkout@v2
- name: "Install cmake"
uses: lukka/get-cmake@latest
- name: "Build packages"
run: >
mkdir build;
cd build;
cmake ..
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_BUILD_PARALLEL_LEVEL=${{ steps.cpu-cores.outputs.count }}
-DFTXUI_BUILD_DOCS=OFF
-DFTXUI_BUILD_EXAMPLES=OFF
-DFTXUI_BUILD_TESTS=OFF
-DFTXUI_BUILD_TESTS_FUZZER=OFF
-DFTXUI_ENABLE_INSTALL=ON
-DFTXUI_DEV_WARNINGS=ON ;
cmake --build . --target package;
- uses: shogo82148/actions-upload-release-asset@v1
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: ${{ matrix.asset_path }}
overwrite: true
documentation:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: "Checkout repository"
uses: actions/checkout@v2
- name: "Install cmake"
uses: lukka/get-cmake@latest
- name: "Install emsdk"
uses: mymindstorm/setup-emsdk@v7
- name: "Install Doxygen/Graphviz"
run: >
sudo apt-get update;
sudo apt-get install doxygen graphviz;
- name: "Build documentation"
run: >
mkdir build;
cd build;
emcmake cmake ..
-DCMAKE_BUILD_TYPE=Release
-DFTXUI_BUILD_DOCS=ON
-DFTXUI_BUILD_EXAMPLES=ON
-DFTXUI_BUILD_TESTS=OFF
-DFTXUI_BUILD_TESTS_FUZZER=OFF
-DFTXUI_ENABLE_INSTALL=OFF
-DFTXUI_DEV_WARNINGS=ON ;
cmake --build . --target doc;
cmake --build . ;
rsync -amv
--include='*/'
--include='*.html'
--include='*.js'
--include='*.wasm'
--exclude='*'
examples
doc/doxygen/html;
- name: "Deploy"
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: build/doc/doxygen/html/
enable_jekyll: false
allow_empty_commit: false
force_orphan: true
publish_branch: gh-pages

View File

@ -1,76 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '45 22 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

60
.github/workflows/documentation.yaml vendored Normal file
View File

@ -0,0 +1,60 @@
name: Documentation
on:
# On new commits to main:
push:
branches:
- main
jobs:
documentation:
runs-on: ubuntu-latest
steps:
- name: "Checkout repository"
uses: actions/checkout@v3
- name: "Install cmake"
uses: lukka/get-cmake@latest
- name: "Install emsdk"
uses: mymindstorm/setup-emsdk@v7
- name: "Install Doxygen/Graphviz"
run: >
sudo apt-get update;
sudo apt-get install doxygen graphviz;
- name: "Build documentation"
run: >
mkdir build;
cd build;
emcmake cmake ..
-DCMAKE_BUILD_TYPE=Release
-DFTXUI_BUILD_DOCS=ON
-DFTXUI_BUILD_EXAMPLES=ON
-DFTXUI_BUILD_TESTS=OFF
-DFTXUI_BUILD_TESTS_FUZZER=OFF
-DFTXUI_ENABLE_INSTALL=OFF
-DFTXUI_DEV_WARNINGS=ON ;
cmake --build . --target doc;
cmake --build . ;
rsync -amv
--include='*/'
--include='*.html'
--include='*.css'
--include='*.mjs'
--include='*.js'
--include='*.wasm'
--exclude='*'
examples
doc/doxygen/html;
- name: "Deploy"
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: build/doc/doxygen/html/
enable_jekyll: false
allow_empty_commit: false
force_orphan: true
publish_branch: gh-pages

24
.github/workflows/publish.yaml vendored Normal file
View File

@ -0,0 +1,24 @@
name: "Publish to Bazel Central Registry"
on:
# Manual kick-off (you type the tag)
workflow_dispatch:
inputs:
tag_name:
description: "Tag to publish"
required: true
type: string
permissions:
contents: write
jobs:
publish:
uses: bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml@v0.0.4
with:
tag_name: ${{ github.event.inputs.tag_name }}
registry_fork: ArthurSonzogni/bazel-central-registry
attest: false
secrets:
publish_token: ${{ secrets.PUBLISH_TOKEN }}

100
.github/workflows/release.yaml vendored Normal file
View File

@ -0,0 +1,100 @@
name: Release
on:
# On push to a tag:
push:
tags:
- 'v*'
# On manual trigger:
workflow_dispatch:
permissions:
# Needed to mint attestations
id-token: write
attestations: write
# Needed to upload release assets
contents: write
jobs:
release:
name: "Create release"
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: "Create release"
uses: softprops/action-gh-release@v1
id: create_release
with:
draft: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Build artifact for the release
package_compiled:
name: "Build packages"
needs: release
strategy:
matrix:
include:
- os: ubuntu-latest
asset_path: build/ftxui*Linux*
- os: macos-latest
asset_path: build/ftxui*Darwin*
- os: windows-latest
asset_path: build/ftxui*Win64*
runs-on: ${{ matrix.os }}
steps:
- name: Get number of CPU cores
uses: SimenB/github-actions-cpu-cores@v1
id: cpu-cores
- name: "Checkout repository"
uses: actions/checkout@v3
- name: "Install cmake"
uses: lukka/get-cmake@latest
- name: "Build packages"
run: >
mkdir build;
cd build;
cmake ..
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_BUILD_PARALLEL_LEVEL=${{ steps.cpu-cores.outputs.count }}
-DFTXUI_BUILD_DOCS=OFF
-DFTXUI_BUILD_EXAMPLES=OFF
-DFTXUI_BUILD_TESTS=OFF
-DFTXUI_BUILD_TESTS_FUZZER=OFF
-DFTXUI_ENABLE_INSTALL=ON
-DFTXUI_DEV_WARNINGS=ON ;
cmake --build . --target package;
- uses: shogo82148/actions-upload-release-asset@v1
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: ${{ matrix.asset_path }}
overwrite: true
# Build "source" artifact for the release. This is the same as the github
# "source" archive, but with a stable URL. This is useful for the Bazel
# Central Repository.
package_source:
name: "Build source package"
needs: release
runs-on: ubuntu-latest
steps:
- name: "Checkout repository"
uses: actions/checkout@v3
- name: "Create source package"
run: >
git archive --format=tar.gz -o source.tar.gz HEAD
- name: "Upload source package"
uses: shogo82148/actions-upload-release-asset@v1
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: source.tar.gz
overwrite: true

22
.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
@ -16,6 +20,10 @@
!flake.nix !flake.nix
!ftxui.pc.in !ftxui.pc.in
!iwyu.imp !iwyu.imp
!WORKSPACE.bazel
!BUILD.bazel
!MODULE.bazel
!.bazelrc
# .github directory: # .github directory:
!.github/**/*.yaml !.github/**/*.yaml
@ -25,6 +33,10 @@
!cmake/**/*.in !cmake/**/*.in
!cmake/**/*.cmake !cmake/**/*.cmake
# bazel directory:
!bazel/**/*.bzl
!.bcr/*
# doc directory: # doc directory:
!doc/**/Doxyfile.in !doc/**/Doxyfile.in
!doc/**/*.txt !doc/**/*.txt
@ -34,14 +46,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

251
BUILD.bazel Normal file
View File

@ -0,0 +1,251 @@
# Copyright 2025 Arthur Sonzogni. All rights reserved.
# Use of this source code is governed by the MIT license that can be found in
# the LICENSE file.
# TODO:
# - Build benchmark.
# - Build fuzzers.
# - Build documentation.
# - Enable the two tests timing out.
# - Support WebAssembly
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load(":bazel/ftxui.bzl", "ftxui_cc_library")
load(":bazel/ftxui.bzl", "generate_examples")
load(":bazel/ftxui.bzl", "cpp20")
load(":bazel/ftxui.bzl", "windows_copts")
load(":bazel/ftxui.bzl", "pthread_linkopts")
package(default_visibility = ["//visibility:public"])
# A meta target that depends on all the ftxui sub modules.
alias(
name = "ftxui",
# Note that :component depends on :dom, which depends on :screen. Bazel
# doesn't really support "public" and "private" dependencies. They are all
# public. This is equivalent to depending on all the submodules.
actual = ":component",
visibility = ["//visibility:public"],
)
# ftxui:screen is a module that provides a screen buffer and color management
# for terminal applications. A screen is a 2D array of cells, each cell can
# contain a glyph, a color, and other attributes. The library also provides
# functions to manipulate the screen.
ftxui_cc_library(
name = "screen",
srcs = [
"src/ftxui/screen/box.cpp",
"src/ftxui/screen/color.cpp",
"src/ftxui/screen/color_info.cpp",
"src/ftxui/screen/image.cpp",
"src/ftxui/screen/screen.cpp",
"src/ftxui/screen/string.cpp",
"src/ftxui/screen/string_internal.hpp",
"src/ftxui/screen/terminal.cpp",
"src/ftxui/screen/util.hpp",
],
hdrs = [
"include/ftxui/screen/box.hpp",
"include/ftxui/screen/color.hpp",
"include/ftxui/screen/color_info.hpp",
"include/ftxui/screen/deprecated.hpp",
"include/ftxui/screen/image.hpp",
"include/ftxui/screen/pixel.hpp",
"include/ftxui/screen/screen.hpp",
"include/ftxui/screen/string.hpp",
"include/ftxui/screen/terminal.hpp",
"include/ftxui/util/autoreset.hpp",
"include/ftxui/util/ref.hpp",
],
)
# ftxui:dom is a library that provides a way to create and manipulate a
# "document" that can be rendered to a screen. The document is a tree of nodes.
# Nodes can be text, layouts, or various decorators. Users needs to compose
# nodes to create a document. A document is responsive to the size of the
# screen.
ftxui_cc_library(
name = "dom",
srcs = [
"src/ftxui/dom/automerge.cpp",
"src/ftxui/dom/blink.cpp",
"src/ftxui/dom/bold.cpp",
"src/ftxui/dom/border.cpp",
"src/ftxui/dom/box_helper.cpp",
"src/ftxui/dom/box_helper.hpp",
"src/ftxui/dom/canvas.cpp",
"src/ftxui/dom/clear_under.cpp",
"src/ftxui/dom/color.cpp",
"src/ftxui/dom/composite_decorator.cpp",
"src/ftxui/dom/dbox.cpp",
"src/ftxui/dom/dim.cpp",
"src/ftxui/dom/flex.cpp",
"src/ftxui/dom/flexbox.cpp",
"src/ftxui/dom/flexbox_config.cpp",
"src/ftxui/dom/flexbox_helper.cpp",
"src/ftxui/dom/flexbox_helper.hpp",
"src/ftxui/dom/focus.cpp",
"src/ftxui/dom/frame.cpp",
"src/ftxui/dom/gauge.cpp",
"src/ftxui/dom/graph.cpp",
"src/ftxui/dom/gridbox.cpp",
"src/ftxui/dom/hbox.cpp",
"src/ftxui/dom/hyperlink.cpp",
"src/ftxui/dom/inverted.cpp",
"src/ftxui/dom/italic.cpp",
"src/ftxui/dom/linear_gradient.cpp",
"src/ftxui/dom/node.cpp",
"src/ftxui/dom/node_decorator.cpp",
"src/ftxui/dom/node_decorator.hpp",
"src/ftxui/dom/paragraph.cpp",
"src/ftxui/dom/reflect.cpp",
"src/ftxui/dom/scroll_indicator.cpp",
"src/ftxui/dom/selection.cpp",
"src/ftxui/dom/selection_style.cpp",
"src/ftxui/dom/separator.cpp",
"src/ftxui/dom/size.cpp",
"src/ftxui/dom/spinner.cpp",
"src/ftxui/dom/strikethrough.cpp",
"src/ftxui/dom/table.cpp",
"src/ftxui/dom/text.cpp",
"src/ftxui/dom/underlined.cpp",
"src/ftxui/dom/underlined_double.cpp",
"src/ftxui/dom/util.cpp",
"src/ftxui/dom/vbox.cpp",
],
hdrs = [
"include/ftxui/dom/canvas.hpp",
"include/ftxui/dom/deprecated.hpp",
"include/ftxui/dom/direction.hpp",
"include/ftxui/dom/elements.hpp",
"include/ftxui/dom/flexbox_config.hpp",
"include/ftxui/dom/linear_gradient.hpp",
"include/ftxui/dom/node.hpp",
"include/ftxui/dom/requirement.hpp",
"include/ftxui/dom/selection.hpp",
"include/ftxui/dom/table.hpp",
"include/ftxui/dom/take_any_args.hpp",
],
deps = [":screen"],
)
# ftxui:component is a library to create "dynamic" component renderering and
# updating a ftxui::dom document on the screen. It is a higher level API than
# ftxui:dom.
#
# The module is required if your program needs to respond to user input. It
# defines a set of ftxui::Component. These components can be utilized to
# navigate using the arrow keys and/or cursor. There are several builtin widgets
# like checkbox/inputbox/etc to interact with. You can combine them, or even
# define your own custom components.
ftxui_cc_library(
name = "component",
srcs = [
"src/ftxui/component/animation.cpp",
"src/ftxui/component/button.cpp",
"src/ftxui/component/catch_event.cpp",
"src/ftxui/component/checkbox.cpp",
"src/ftxui/component/collapsible.cpp",
"src/ftxui/component/component.cpp",
"src/ftxui/component/component_options.cpp",
"src/ftxui/component/container.cpp",
"src/ftxui/component/dropdown.cpp",
"src/ftxui/component/event.cpp",
"src/ftxui/component/hoverable.cpp",
"src/ftxui/component/input.cpp",
"src/ftxui/component/loop.cpp",
"src/ftxui/component/maybe.cpp",
"src/ftxui/component/menu.cpp",
"src/ftxui/component/modal.cpp",
"src/ftxui/component/radiobox.cpp",
"src/ftxui/component/renderer.cpp",
"src/ftxui/component/resizable_split.cpp",
"src/ftxui/component/screen_interactive.cpp",
"src/ftxui/component/slider.cpp",
"src/ftxui/component/terminal_input_parser.cpp",
"src/ftxui/component/terminal_input_parser.hpp",
"src/ftxui/component/util.cpp",
"src/ftxui/component/window.cpp",
],
hdrs = [
"include/ftxui/component/animation.hpp",
"include/ftxui/component/captured_mouse.hpp",
"include/ftxui/component/component.hpp",
"include/ftxui/component/component_base.hpp",
"include/ftxui/component/component_options.hpp",
"include/ftxui/component/event.hpp",
"include/ftxui/component/loop.hpp",
"include/ftxui/component/mouse.hpp",
"include/ftxui/component/receiver.hpp",
"include/ftxui/component/screen_interactive.hpp",
"include/ftxui/component/task.hpp",
],
linkopts = pthread_linkopts(),
deps = [":dom"],
)
# FTXUI's tests
cc_test(
name = "tests",
testonly = True,
srcs = [
"src/ftxui/component/animation_test.cpp",
"src/ftxui/component/button_test.cpp",
"src/ftxui/component/collapsible_test.cpp",
"src/ftxui/component/component_test.cpp",
"src/ftxui/component/container_test.cpp",
"src/ftxui/component/dropdown_test.cpp",
"src/ftxui/component/hoverable_test.cpp",
"src/ftxui/component/input_test.cpp",
"src/ftxui/component/menu_test.cpp",
"src/ftxui/component/modal_test.cpp",
"src/ftxui/component/radiobox_test.cpp",
"src/ftxui/component/receiver_test.cpp",
"src/ftxui/component/resizable_split_test.cpp",
"src/ftxui/component/slider_test.cpp",
"src/ftxui/component/terminal_input_parser_test.cpp",
"src/ftxui/component/terminal_input_parser_test_fuzzer.cpp",
"src/ftxui/component/toggle_test.cpp",
"src/ftxui/dom/blink_test.cpp",
"src/ftxui/dom/bold_test.cpp",
"src/ftxui/dom/border_test.cpp",
"src/ftxui/dom/canvas_test.cpp",
"src/ftxui/dom/color_test.cpp",
"src/ftxui/dom/dbox_test.cpp",
"src/ftxui/dom/dim_test.cpp",
"src/ftxui/dom/flexbox_helper_test.cpp",
"src/ftxui/dom/flexbox_test.cpp",
"src/ftxui/dom/gauge_test.cpp",
"src/ftxui/dom/gridbox_test.cpp",
"src/ftxui/dom/hbox_test.cpp",
"src/ftxui/dom/hyperlink_test.cpp",
"src/ftxui/dom/italic_test.cpp",
"src/ftxui/dom/linear_gradient_test.cpp",
"src/ftxui/dom/scroll_indicator_test.cpp",
"src/ftxui/dom/separator_test.cpp",
"src/ftxui/dom/spinner_test.cpp",
"src/ftxui/dom/table_test.cpp",
"src/ftxui/dom/text_test.cpp",
"src/ftxui/dom/underlined_test.cpp",
"src/ftxui/dom/vbox_test.cpp",
"src/ftxui/screen/color_test.cpp",
"src/ftxui/screen/string_test.cpp",
"src/ftxui/util/ref_test.cpp",
# TODO: Enable the two tests timing out with Bazel:
# - "src/ftxui/component/screen_interactive_test.cpp",
# - "src/ftxui/dom/selection_test.cpp",
],
includes = [
"include",
"src",
],
copts = cpp20() + windows_copts(),
deps = [
"//:ftxui",
"@googletest//:gtest_main",
],
)
generate_examples()

View File

@ -1,8 +1,155 @@
Changelog Changelog
========= =========
current (development) Future release
--------------------- ------------
### dom
- Bugfix: Restore the `dbox` behavior from ftxui 5.0.0. To apply bgcolor
blending between the two layers, a new `dboxBlend` will be added.
6.1.8 (2025-05-01)
------------------
### Build
- Feature: Support `bazel` build system. See #1032.
Proposed by Kostya Serebryany @kcc
If all goes well (pending), it should appear in the Bazel central repository.
It can be imported into your project using the following lines:
**MODULE.bazel**
```bazel
bazel_dep(name = "ftxui", version = "6.1.8")
```
**BUILD.bazel**
```bazel
deps = [
// Depend on the whole library:
"@ftxui//:ftxui",
// Choose a specific submodule:
"@ftxui//:component",
"@ftxui//:dom",
"@ftxui//:screen",
]
```
### Component
- Bugfix: Fix a crash with ResizeableSplit. See #1023.
- Clamp screen size to terminal size.
- Disallow `ResizeableSplit` with negative size.
### Dom
- Bugfix: Disallow specifying a negative size constraint. See #1023.
6.0.2 (2025-03-30)
-----
### Component
- BugFix: Fix major crash on Windows affecting all components. See #1020
- BugFix: Fix focusRelative.
6.0.1 (2025-03-28)
-----
Same as v6.0.0.
Due to a problem tag v6.0.0 was replaced. This isn't a good practice and affect
developers that started using it in the short timeframe. Submitting a new
release with the same content is the best way to fix this.
See #1017 and #1019.
6.0.0 (2025-03-23)
-----
### 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.
- Feature: Add support for Selection. Thanks @clement-roblot. See #926.
- See `ScreenInteractive::GetSelection()`.
- See `ScreenInteractive::SelectionChange(...)` listener.
- 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.
- Bugfix: Handle `Dropdown` with no entries.
- Bugfix: Fix crash in `LinearGradient` due to float precision and an off-by-one
mistake. See #998.
### Dom
- Feature: Add `italic` decorator. For instance:
```cpp
auto italic_text = text("Italic text") | italic;
```
```cpp
auto italic_text = italic(text("Italic text"));
```
Proposed by @kenReneris in #1009.
- 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.
- Feature: Add support for Selection. Thanks @clement-roblot. See #926.
- See `selectionColor` decorator.
- See `selectionBackgroundColor` decorator.
- See `selectionForegroundColor` decorator.
- See `selectionStyle(style)` decorator.
- See `selectionStyleReset` decorator.
- Breaking change: Change how "focus"/"select" are handled. This fixes the
behavior.
- Breaking change: `Component::OnRender()` becomes the method to override to
render a component. This replaces `Component::Render()` that is still in use
to call the rendering method on the children. This change allows to fix a
couple of issues around focus handling.
### 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

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.12)
project(ftxui project(ftxui
LANGUAGES CXX LANGUAGES CXX
VERSION 5.0.0 VERSION 6.1.8
DESCRIPTION "C++ Functional Terminal User Interface." DESCRIPTION "C++ Functional Terminal User Interface."
) )
@ -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
@ -51,11 +56,12 @@ add_library(dom
include/ftxui/dom/flexbox_config.hpp include/ftxui/dom/flexbox_config.hpp
include/ftxui/dom/node.hpp include/ftxui/dom/node.hpp
include/ftxui/dom/requirement.hpp include/ftxui/dom/requirement.hpp
include/ftxui/dom/selection.hpp
include/ftxui/dom/take_any_args.hpp include/ftxui/dom/take_any_args.hpp
src/ftxui/dom/automerge.cpp src/ftxui/dom/automerge.cpp
src/ftxui/dom/selection_style.cpp
src/ftxui/dom/blink.cpp src/ftxui/dom/blink.cpp
src/ftxui/dom/bold.cpp src/ftxui/dom/bold.cpp
src/ftxui/dom/hyperlink.cpp
src/ftxui/dom/border.cpp src/ftxui/dom/border.cpp
src/ftxui/dom/box_helper.cpp src/ftxui/dom/box_helper.cpp
src/ftxui/dom/box_helper.hpp src/ftxui/dom/box_helper.hpp
@ -76,13 +82,16 @@ add_library(dom
src/ftxui/dom/graph.cpp src/ftxui/dom/graph.cpp
src/ftxui/dom/gridbox.cpp src/ftxui/dom/gridbox.cpp
src/ftxui/dom/hbox.cpp src/ftxui/dom/hbox.cpp
src/ftxui/dom/hyperlink.cpp
src/ftxui/dom/inverted.cpp src/ftxui/dom/inverted.cpp
src/ftxui/dom/italic.cpp
src/ftxui/dom/linear_gradient.cpp src/ftxui/dom/linear_gradient.cpp
src/ftxui/dom/node.cpp src/ftxui/dom/node.cpp
src/ftxui/dom/node_decorator.cpp src/ftxui/dom/node_decorator.cpp
src/ftxui/dom/paragraph.cpp src/ftxui/dom/paragraph.cpp
src/ftxui/dom/reflect.cpp src/ftxui/dom/reflect.cpp
src/ftxui/dom/scroll_indicator.cpp src/ftxui/dom/scroll_indicator.cpp
src/ftxui/dom/selection.cpp
src/ftxui/dom/separator.cpp src/ftxui/dom/separator.cpp
src/ftxui/dom/size.cpp src/ftxui/dom/size.cpp
src/ftxui/dom/spinner.cpp src/ftxui/dom/spinner.cpp

9
MODULE.bazel Normal file
View File

@ -0,0 +1,9 @@
# FTXUI Module.
module(name = "ftxui", version = "6.1.8", compatibility_level = 6)
# Build deps.
bazel_dep(name = "rules_cc", version = "0.1.1")
bazel_dep(name = "platforms", version = "0.0.11")
# Test deps.
bazel_dep(name = "googletest", version = "1.16.0.bcr.1")

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.
@ -42,8 +42,8 @@ A simple cross-platform C++ library for terminal based user interfaces!
* No dependencies * No dependencies
* **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),Bazel, 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
) )
@ -109,6 +109,7 @@ Element can become flexible using the the `flex` decorator.
An element can be decorated using the functions: An element can be decorated using the functions:
- `bold` - `bold`
- `italic`
- `dim` - `dim`
- `inverted` - `inverted`
- `underlined` - `underlined`
@ -199,7 +200,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 +294,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 +305,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 +327,28 @@ 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)
- [inLimbo](https://github.com/nots1dd/inLimbo)
- [BestEdrOfTheMarket](https://github.com/Xacone/BestEdrOfTheMarket)
- [terminal-rain](https://github.com/Oakamoore/terminal-rain)
- [keywords](https://github.com/Oakamoore/keywords) ([Play web version :heart:](https://oakamoore.itch.io/keywords))
- [FTB - tertminal file browser](https://github.com/Cyxuan0311/FTB)
- [SHOOT!](https://github.com/ShingZhanho/ENGG1340-Project-25Spring)
### [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 +365,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 v6.1.8
) )
FetchContent_GetProperties(ftxui) FetchContent_GetProperties(ftxui)
@ -358,13 +383,19 @@ 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:
- [bazel](...)
- [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/libftxui.svg)](https://repology.org/project/libftxui/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

4
WORKSPACE.bazel Normal file
View File

@ -0,0 +1,4 @@
# Copyright 2025 Arthur Sonzogni. All rights reserved.
# Use of this source code is governed by the MIT license that can be found in
# the LICENSE file.
workspace(name = "ftxui")

115
bazel/ftxui.bzl Normal file
View File

@ -0,0 +1,115 @@
# ftxui_common.bzl
load("@rules_cc//cc:defs.bzl", "cc_library")
def cpp17():
return select({
"@rules_cc//cc/compiler:msvc-cl": ["/std:c++17"],
"@rules_cc//cc/compiler:clang-cl": ["/std:c++17"],
"@rules_cc//cc/compiler:clang": ["-std=c++17"],
"@rules_cc//cc/compiler:gcc": ["-std=c++17"],
"//conditions:default": ["-std=c++17"],
})
def cpp20():
return select({
"@rules_cc//cc/compiler:msvc-cl": ["/std:c++20"],
"@rules_cc//cc/compiler:clang-cl": ["/std:c++20"],
"@rules_cc//cc/compiler:clang": ["-std=c++20"],
"@rules_cc//cc/compiler:gcc": ["-std=c++20"],
"//conditions:default": ["-std=c++20"],
})
# Microsoft terminal is a bit buggy ¯\_(ツ)_/¯ and MSVC uses bad defaults.
def windows_copts():
MSVC_COPTS = [
# Microsoft Visual Studio must decode sources files as UTF-8.
"/utf-8",
# Microsoft Visual Studio must interpret the codepoint using unicode.
"/DUNICODE",
"/D_UNICODE",
# Fallback for Microsoft Terminal.
# This
# - Replace missing font symbols by others.
# - Reduce screen position pooling frequency to deals against a Microsoft
# race condition. This was fixed in 2020, but clients never not updated.
# - https://github.com/microsoft/terminal/pull/7583
# - https://github.com/ArthurSonzogni/FTXUI/issues/136
"/DFTXUI_MICROSOFT_TERMINAL_FALLBACK",
]
WINDOWS_COPTS = [
# Fallback for Microsoft Terminal.
# This
# - Replace missing font symbols by others.
# - Reduce screen position pooling frequency to deals against a Microsoft
# race condition. This was fixed in 2020, but clients never not updated.
# - https://github.com/microsoft/terminal/pull/7583
# - https://github.com/ArthurSonzogni/FTXUI/issues/136
"-DFTXUI_MICROSOFT_TERMINAL_FALLBACK",
];
return select({
# MSVC:
"@rules_cc//cc/compiler:msvc-cl": MSVC_COPTS,
"@rules_cc//cc/compiler:clang-cl": MSVC_COPTS,
"@platforms//os:windows": WINDOWS_COPTS,
"//conditions:default": [],
})
def pthread_linkopts():
return select({
# With MSVC, threading is already built-in (you don't need -pthread.
"@rules_cc//cc/compiler:msvc-cl": [],
"@rules_cc//cc/compiler:clang-cl": [],
"@rules_cc//cc/compiler:clang": ["-pthread"],
"@rules_cc//cc/compiler:gcc": ["-pthread"],
"//conditions:default": ["-pthread"],
})
def ftxui_cc_library(
name,
srcs,
hdrs,
linkopts = [],
deps = []):
cc_library(
name = name,
srcs = srcs,
hdrs = hdrs,
linkopts = linkopts,
deps = deps,
strip_include_prefix = "",
include_prefix = "",
includes = [
"include",
"src",
],
copts = cpp17() + windows_copts(),
visibility = ["//visibility:public"],
)
# Compile all the examples in the examples/ directory.
# This is useful to check the Bazel is synchronized with CMake definitions.
def generate_examples():
cpp_files = native.glob(["examples/**/*.cpp"])
for src in cpp_files:
# Skip failing examples due to the color_info_sorted_2d.ipp dependency.
if src == "examples/component/homescreen.cpp" or \
src == "examples/dom/color_info_palette256.cpp" or \
src == "examples/dom/color_gallery.cpp":
continue
# Turn "examples/component/button.cpp" → "example_component_button"
name = src.replace("/", "_").replace(".cpp", "")
native.cc_binary(
name = name,
srcs = [src],
deps = ["//:component"],
copts = cpp20() + windows_copts(),
)

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)
@ -77,10 +83,6 @@ function(ftxui_set_options library)
target_compile_options(${library} PRIVATE "-Wpedantic") target_compile_options(${library} PRIVATE "-Wpedantic")
target_compile_options(${library} PRIVATE "-Wshadow") target_compile_options(${library} PRIVATE "-Wshadow")
target_compile_options(${library} PRIVATE "-Wunused") target_compile_options(${library} PRIVATE "-Wunused")
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(${library} PRIVATE "-Wuseless-cast")
endif()
endif() endif()
endif() endif()

View File

@ -13,11 +13,13 @@ add_executable(ftxui-tests
src/ftxui/component/component_test.cpp src/ftxui/component/component_test.cpp
src/ftxui/component/component_test.cpp src/ftxui/component/component_test.cpp
src/ftxui/component/container_test.cpp src/ftxui/component/container_test.cpp
src/ftxui/component/dropdown_test.cpp
src/ftxui/component/hoverable_test.cpp src/ftxui/component/hoverable_test.cpp
src/ftxui/component/input_test.cpp src/ftxui/component/input_test.cpp
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
@ -37,8 +39,10 @@ add_executable(ftxui-tests
src/ftxui/dom/gridbox_test.cpp src/ftxui/dom/gridbox_test.cpp
src/ftxui/dom/hbox_test.cpp src/ftxui/dom/hbox_test.cpp
src/ftxui/dom/hyperlink_test.cpp src/ftxui/dom/hyperlink_test.cpp
src/ftxui/dom/italic_test.cpp
src/ftxui/dom/linear_gradient_test.cpp src/ftxui/dom/linear_gradient_test.cpp
src/ftxui/dom/scroll_indicator_test.cpp src/ftxui/dom/scroll_indicator_test.cpp
src/ftxui/dom/selection_test.cpp
src/ftxui/dom/separator_test.cpp src/ftxui/dom/separator_test.cpp
src/ftxui/dom/spinner_test.cpp src/ftxui/dom/spinner_test.cpp
src/ftxui/dom/table_test.cpp src/ftxui/dom/table_test.cpp
@ -59,6 +63,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 ..
@ -121,8 +123,8 @@ The project is comprised of 3 modules:
This is the visual element of the program. It defines a `ftxui::Screen`, which This is the visual element of the program. It defines a `ftxui::Screen`, which
is a grid of `ftxui::Pixel`. A Pixel represents a Unicode character and its is a grid of `ftxui::Pixel`. A Pixel represents a Unicode character and its
associated style (bold, colors, etc.). The screen can be printed as a string associated style (bold, italic, colors, etc.). The screen can be printed as a
using `ftxui::Screen::ToString()`. The following example highlights this string using `ftxui::Screen::ToString()`. The following example highlights this
process: process:
```cpp ```cpp
@ -474,10 +476,11 @@ See [demo](https://arthursonzogni.github.io/FTXUI/examples/?file=component/linea
## Style {#dom-style} ## Style {#dom-style}
In addition to colored text and colored backgrounds. Many terminals support text In addition to colored text and colored backgrounds. Many terminals support text
effects such as: `bold`, `dim`, `underlined`, `inverted`, `blink`. effects such as: `bold`, `italic`, `dim`, `underlined`, `inverted`, `blink`.
```cpp ```cpp
Element bold(Element); Element bold(Element);
Element italic(Element);
Element dim(Element); Element dim(Element);
Element inverted(Element); Element inverted(Element);
Element underlined(Element); Element underlined(Element);
@ -633,6 +636,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,12 +11,14 @@ 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)
example(gallery) example(gallery)
example(homescreen) example(homescreen)
example(input) example(input)
example(input_in_frame)
example(input_style) example(input_style)
example(linear_gradient_gallery) example(linear_gradient_gallery)
example(maybe) example(maybe)
@ -25,6 +27,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 +39,8 @@ example(radiobox)
example(radiobox_in_frame) example(radiobox_in_frame)
example(renderer) example(renderer)
example(resizable_split) example(resizable_split)
example(scrollbar)
example(selection)
example(slider) example(slider)
example(slider_direction) example(slider_direction)
example(slider_rgb) example(slider_rgb)

View File

@ -1,39 +1,9 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved. #include "ftxui/component/component.hpp"
// Use of this source code is governed by the MIT license that can be found in #include "ftxui/component/screen_interactive.hpp"
// the LICENSE file.
#include <memory> // for shared_ptr, __shared_ptr_access
#include <string> // for operator+, to_string
#include "ftxui/component/captured_mouse.hpp" // for ftxui int main(){
#include "ftxui/component/component.hpp" // for Button, Horizontal, Renderer auto screen = ftxui::ScreenInteractive::Fullscreen();
#include "ftxui/component/component_base.hpp" // for ComponentBase auto testComponent = ftxui::Renderer([](){return ftxui::text("test Component");});
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive screen.Loop(testComponent);
#include "ftxui/dom/elements.hpp" // for separator, gauge, text, Element, operator|, vbox, border return 0;
using namespace ftxui;
int main() {
int value = 50;
// The tree of components. This defines how to navigate using the keyboard.
auto buttons = Container::Horizontal({
Button("Decrease", [&] { value--; }),
Button("Increase", [&] { value++; }),
});
// Modify the way to render them on screen:
auto component = Renderer(buttons, [&] {
return vbox({
text("value = " + std::to_string(value)),
separator(),
gauge(value * 0.01f),
separator(),
buttons->Render(),
}) |
border;
});
auto screen = ScreenInteractive::FitComponent();
screen.Loop(component);
return 0;
} }

View File

@ -1,30 +1,38 @@
// 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 <memory> // for allocator, __shared_ptr_access #include <array> // for array
#include <string> // for string, basic_string, operator+, to_string #include <iostream>
#include <vector> // for vector #include <memory> // for shared_ptr, __shared_ptr_access
#include <string> // for operator+, to_string
#include "ftxui/component/captured_mouse.hpp" // for ftxui #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Input, Renderer, Vertical #include "ftxui/component/component.hpp" // for Checkbox, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for operator|, Element, size, border, frame, vscroll_indicator, HEIGHT, LESS_THAN #include "ftxui/dom/elements.hpp" // for operator|, Element, size, border, frame, vscroll_indicator, HEIGHT, LESS_THAN
using namespace ftxui;
int main() { int main() {
using namespace ftxui; bool download = false;
bool upload = false;
bool ping = false;
Component input_list = Container::Vertical({}); auto container = Container::Vertical({
std::vector<std::string> items(100, ""); Checkbox("Download", &download),
for (size_t i = 0; i < items.size(); ++i) { Checkbox("Upload", &upload),
input_list->Add(Input(&(items[i]), "placeholder " + std::to_string(i))); Checkbox("Ping", &ping),
}
auto renderer = Renderer(input_list, [&] {
return input_list->Render() | vscroll_indicator | frame | border |
size(HEIGHT, LESS_THAN, 10);
}); });
auto screen = ScreenInteractive::TerminalOutput(); auto screen = ScreenInteractive::FitComponent();
screen.Loop(renderer); screen.Loop(container);
std::cout << "---" << std::endl;
std::cout << "Download: " << download << std::endl;
std::cout << "Upload: " << upload << std::endl;
std::cout << "Ping: " << ping << std::endl;
std::cout << "---" << std::endl;
return 0;
} }

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

@ -97,7 +97,25 @@ int main() {
}); });
sliders = Wrap("Slider", sliders); sliders = Wrap("Slider", sliders);
// -- Layout ----------------------------------------------------------------- // A large text:
auto lorel_ipsum = Renderer([] {
return vbox({
text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. "),
text("Sed do eiusmod tempor incididunt ut labore et dolore magna "
"aliqua. "),
text("Ut enim ad minim veniam, quis nostrud exercitation ullamco "
"laboris nisi ut aliquip ex ea commodo consequat. "),
text("Duis aute irure dolor in reprehenderit in voluptate velit esse "
"cillum dolore eu fugiat nulla pariatur. "),
text("Excepteur sint occaecat cupidatat non proident, sunt in culpa "
"qui officia deserunt mollit anim id est laborum. "),
});
});
lorel_ipsum = Wrap("Lorel Ipsum", lorel_ipsum);
// -- Layout
// -----------------------------------------------------------------
auto layout = Container::Vertical({ auto layout = Container::Vertical({
menu, menu,
toggle, toggle,
@ -106,6 +124,7 @@ int main() {
input, input,
sliders, sliders,
button, button,
lorel_ipsum,
}); });
auto component = Renderer(layout, [&] { auto component = Renderer(layout, [&] {
@ -123,6 +142,8 @@ int main() {
sliders->Render(), sliders->Render(),
separator(), separator(),
button->Render(), button->Render(),
separator(),
lorel_ipsum->Render(),
}) | }) |
xflex | size(WIDTH, GREATER_THAN, 40) | border; xflex | size(WIDTH, GREATER_THAN, 40) | border;
}); });

View File

@ -264,7 +264,7 @@ int main() {
}); });
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Spiner // Spinner
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
auto spinner_tab_renderer = Renderer([&] { auto spinner_tab_renderer = Renderer([&] {
Elements entries; Elements entries;
@ -424,7 +424,7 @@ int main() {
auto paragraph_renderer_left = Renderer([&] { auto paragraph_renderer_left = Renderer([&] {
std::string str = std::string str =
"Lorem Ipsum is simply dummy text of the printing and typesetting " "Lorem Ipsum is simply dummy text of the printing and typesetting "
"industry. Lorem Ipsum has been the industry's standard dummy text " "industry.\nLorem Ipsum has been the industry's standard dummy text "
"ever since the 1500s, when an unknown printer took a galley of type " "ever since the 1500s, when an unknown printer took a galley of type "
"and scrambled it to make a type specimen book."; "and scrambled it to make a type specimen book.";
return vbox({ return vbox({
@ -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 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <memory> // for allocator, __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 Input, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for operator|, Element, size, border, frame, vscroll_indicator, HEIGHT, LESS_THAN
int main() {
using namespace ftxui;
Component input_list = Container::Vertical({});
std::vector<std::string> items(100, "");
for (size_t i = 0; i < items.size(); ++i) {
input_list->Add(Input(&(items[i]), "placeholder " + std::to_string(i)));
}
auto renderer = Renderer(input_list, [&] {
return input_list->Render() | vscroll_indicator | frame | border |
size(HEIGHT, LESS_THAN, 10);
});
auto screen = ScreenInteractive::TerminalOutput();
screen.Loop(renderer);
}

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

@ -0,0 +1,87 @@
// 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 char_traits, operator+, string, basic_string
#include "ftxui/component/component.hpp" // for Input, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for InputOption
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for text, hbox, separator, Element, operator|, vbox, border
#include "ftxui/util/ref.hpp" // for Ref
using namespace ftxui;
Element LoremIpsum() {
return vbox({
text("FTXUI: A powerful library for building user interfaces."),
text("Enjoy a rich set of components and a declarative style."),
text("Create beautiful and responsive UIs with minimal effort."),
text("Join the community and experience the power of FTXUI."),
});
}
int main() {
auto screen = ScreenInteractive::TerminalOutput();
auto quit =
Button("Quit", screen.ExitLoopClosure(), ButtonOption::Animated());
int selection_change_counter = 0;
std::string selection_content = "";
screen.SelectionChange([&] {
selection_change_counter++;
selection_content = screen.GetSelection();
});
// The components:
auto renderer = Renderer(quit, [&] {
return vbox({
text("Select changed: " + std::to_string(selection_change_counter) +
" times"),
text("Currently selected: "),
paragraph(selection_content) | vscroll_indicator | frame | border |
size(HEIGHT, EQUAL, 10),
window(text("Horizontal split"), hbox({
LoremIpsum(),
separator(),
LoremIpsum(),
separator(),
LoremIpsum(),
})),
window(text("Vertical split"), vbox({
LoremIpsum(),
separator(),
LoremIpsum(),
separator(),
LoremIpsum(),
})),
window(text("Grid split with different style"),
vbox({
hbox({
LoremIpsum(),
separator(),
LoremIpsum() //
| selectionBackgroundColor(Color::Yellow) //
| selectionColor(Color::Black) //
| selectionStyleReset,
separator(),
LoremIpsum() | selectionColor(Color::Blue),
}),
separator(),
hbox({
LoremIpsum() | selectionColor(Color::Red),
separator(),
LoremIpsum() | selectionStyle([](Pixel& pixel) {
pixel.underlined_double = true;
}),
separator(),
LoremIpsum(),
}),
})),
quit->Render(),
});
});
screen.Loop(renderer);
}

View File

@ -19,7 +19,7 @@ using namespace ftxui;
int main() { int main() {
auto screen = ScreenInteractive::TerminalOutput(); auto screen = ScreenInteractive::TerminalOutput();
std::array<int, 30> values; std::array<int, 30> values;
for (int i = 0; i < values.size(); ++i) { for (size_t i = 0; i < values.size(); ++i) {
values[i] = 50 + 20 * std::sin(i * 0.3); values[i] = 50 + 20 * std::sin(i * 0.3);
} }

View File

@ -29,6 +29,7 @@ example(style_dim)
example(style_gallery) example(style_gallery)
example(style_hyperlink) example(style_hyperlink)
example(style_inverted) example(style_inverted)
example(style_italic)
example(style_strikethrough) example(style_strikethrough)
example(style_underlined) example(style_underlined)
example(style_underlined_double) example(style_underlined_double)

View File

@ -12,7 +12,6 @@
int main() { int main() {
using namespace ftxui; using namespace ftxui;
int saturation = 255;
Elements red_line; Elements red_line;
Elements green_line; Elements green_line;
Elements blue_line; Elements blue_line;

View File

@ -10,6 +10,7 @@
#include <memory> // for shared_ptr #include <memory> // for shared_ptr
#include <string> // for operator<<, string #include <string> // for operator<<, string
#include <thread> // for sleep_for #include <thread> // for sleep_for
#include <utility> // for ignore
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/dom/node.hpp" // for Render #include "ftxui/dom/node.hpp" // for Render
@ -49,6 +50,7 @@ int main() {
std::string reset_position; std::string reset_position;
for (int i = 0;; ++i) { for (int i = 0;; ++i) {
std::ignore = i;
auto document = hbox({ auto document = hbox({
vbox({ vbox({
graph(std::ref(my_graph)), graph(std::ref(my_graph)),

View File

@ -15,6 +15,7 @@ int main() {
hbox({ hbox({
text("normal") , text(" ") , text("normal") , text(" ") ,
text("bold") | bold , text(" ") , text("bold") | bold , text(" ") ,
text("italic") | italic , text(" ") ,
text("dim") | dim , text(" ") , text("dim") | dim , text(" ") ,
text("inverted") | inverted , text(" ") , text("inverted") | inverted , text(" ") ,
text("underlined") | underlined , text(" ") , text("underlined") | underlined , text(" ") ,

View File

@ -0,0 +1,23 @@
// Copyright 2025 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/dom/elements.hpp> // for text, operator|, inverted, Fit, hbox, Element
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main() {
using namespace ftxui;
auto document = hbox({
text("This text is "),
text("italic") | italic,
text(". Do you like it?"),
});
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document);
screen.Print();
return 0;
}

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('/');

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,25 +29,31 @@ 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();
// Renders the component. // Renders the component.
virtual Element Render(); Element Render();
// Override this function modify how `Render` works.
virtual Element OnRender();
// Handles an event. // Handles an event.
// By default, reduce on children with a lazy OR. // By default, reduce on children with a lazy OR.
@ -91,6 +97,7 @@ class ComponentBase {
private: private:
ComponentBase* parent_ = nullptr; ComponentBase* parent_ = nullptr;
bool in_render = false;
}; };
} // namespace ftxui } // namespace ftxui

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

@ -16,6 +16,7 @@
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/event.hpp" // for Event #include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/task.hpp" // for Task, Closure #include "ftxui/component/task.hpp" // for Task, Closure
#include "ftxui/dom/selection.hpp" // for SelectionOption
#include "ftxui/screen/screen.hpp" // for Screen #include "ftxui/screen/screen.hpp" // for Screen
namespace ftxui { namespace ftxui {
@ -31,6 +32,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 +60,19 @@ 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);
// Selection API.
std::string GetSelection();
void SelectionChange(std::function<void()> callback);
private: private:
void ExitNow(); void ExitNow();
@ -71,6 +87,8 @@ class ScreenInteractive : public Screen {
void RunOnceBlocking(Component component); void RunOnceBlocking(Component component);
void HandleTask(Component component, Task& task); void HandleTask(Component component, Task& task);
bool HandleSelection(bool handled, Event event);
void RefreshSelection();
void Draw(Component component); void Draw(Component component);
void ResetCursorPosition(); void ResetCursorPosition();
@ -98,7 +116,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 +130,28 @@ 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;
// Selection API:
CapturedMouse selection_pending_;
struct SelectionData {
int start_x = -1;
int start_y = -1;
int end_x = -2;
int end_y = -2;
bool empty = true;
bool operator==(const SelectionData& other) const;
bool operator!=(const SelectionData& other) const;
};
SelectionData selection_data_;
SelectionData selection_data_previous_;
std::unique_ptr<Selection> selection_;
std::function<void()> selection_on_change_;
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);
@ -96,6 +95,7 @@ Element canvas(std::function<void(Canvas&)>);
// -- Decorator --- // -- Decorator ---
Element bold(Element); Element bold(Element);
Element dim(Element); Element dim(Element);
Element italic(Element);
Element inverted(Element); Element inverted(Element);
Element underlined(Element); Element underlined(Element);
Element underlinedDouble(Element); Element underlinedDouble(Element);
@ -114,6 +114,11 @@ Decorator focusPositionRelative(float x, float y);
Element automerge(Element child); Element automerge(Element child);
Decorator hyperlink(std::string link); Decorator hyperlink(std::string link);
Element hyperlink(std::string link, Element child); Element hyperlink(std::string link, Element child);
Element selectionStyleReset(Element);
Decorator selectionColor(Color foreground);
Decorator selectionBackgroundColor(Color foreground);
Decorator selectionForegroundColor(Color foreground);
Decorator selectionStyle(std::function<void(Pixel&)> style);
// --- Layout is // --- Layout is
// Horizontal, Vertical or stacked set of elements. // Horizontal, Vertical or stacked set of elements.
@ -157,7 +162,7 @@ Element frame(Element);
Element xframe(Element); Element xframe(Element);
Element yframe(Element); Element yframe(Element);
Element focus(Element); Element focus(Element);
Element select(Element); Element select(Element e); // Deprecated - Alias for focus.
// --- Cursor --- // --- Cursor ---
// Those are similar to `focus`, but also change the shape of the cursor. // Those are similar to `focus`, but also change the shape of the cursor.
@ -170,6 +175,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 +189,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

@ -8,6 +8,7 @@
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/dom/requirement.hpp" // for Requirement #include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/dom/selection.hpp" // for Selection
#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" #include "ftxui/screen/screen.hpp"
@ -22,7 +23,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;
@ -40,9 +41,15 @@ class Node {
// Propagated from Parents to Children. // Propagated from Parents to Children.
virtual void SetBox(Box box); virtual void SetBox(Box box);
// Step 3: Draw this element. // Step 3: (optional) Selection
// Propagated from Parents to Children.
virtual void Select(Selection& selection);
// Step 4: Draw this element.
virtual void Render(Screen& screen); virtual void Render(Screen& screen);
virtual std::string GetSelectedContent(Selection& selection);
// Layout may not resolve within a single iteration for some elements. This // Layout may not resolve within a single iteration for some elements. This
// allows them to request additionnal iterations. This signal must be // allows them to request additionnal iterations. This signal must be
// forwarded to children at least once. // forwarded to children at least once.
@ -52,6 +59,8 @@ class Node {
}; };
virtual void Check(Status* status); virtual void Check(Status* status);
friend void Render(Screen& screen, Node* node, Selection& selection);
protected: protected:
Elements children_; Elements children_;
Requirement requirement_; Requirement requirement_;
@ -60,6 +69,10 @@ class Node {
void Render(Screen& screen, const Element& element); void Render(Screen& screen, const Element& element);
void Render(Screen& screen, Node* node); void Render(Screen& screen, Node* node);
void Render(Screen& screen, Node* node, Selection& selection);
std::string GetNodeSelectedContent(Screen& screen,
Node* node,
Selection& selection);
} // namespace ftxui } // namespace ftxui

View File

@ -5,8 +5,10 @@
#define FTXUI_DOM_REQUIREMENT_HPP #define FTXUI_DOM_REQUIREMENT_HPP
#include "ftxui/screen/box.hpp" #include "ftxui/screen/box.hpp"
#include "ftxui/screen/screen.hpp"
namespace ftxui { namespace ftxui {
class Node;
struct Requirement { struct Requirement {
// The required size to fully draw the element. // The required size to fully draw the element.
@ -20,13 +22,28 @@ struct Requirement {
int flex_shrink_y = 0; int flex_shrink_y = 0;
// Focus management to support the frame/focus/select element. // Focus management to support the frame/focus/select element.
enum Selection { struct Focused {
NORMAL = 0, bool enabled = false;
SELECTED = 1, Box box;
FOCUSED = 2, Node* node = nullptr;
Screen::Cursor::Shape cursor_shape = Screen::Cursor::Shape::Hidden;
// Internal for interactions with components.
bool component_active = false;
// Return whether this requirement should be preferred over the other.
bool Prefer(const Focused& other) const {
if (!other.enabled) {
return false;
}
if (!enabled) {
return true;
}
return other.component_active && !component_active;
}
}; };
Selection selection = NORMAL; Focused focused;
Box selected_box;
}; };
} // namespace ftxui } // namespace ftxui

View File

@ -0,0 +1,50 @@
// 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_DOM_SELECTION_HPP
#define FTXUI_DOM_SELECTION_HPP
#include <functional>
#include <sstream>
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/pixel.hpp" // for Pixel
namespace ftxui {
/// @brief Represent a selection in the terminal.
class Selection {
public:
Selection(); // Empty selection.
Selection(int start_x, int start_y, int end_x, int end_y);
const Box& GetBox() const;
Selection SaturateHorizontal(Box box);
Selection SaturateVertical(Box box);
bool IsEmpty() const { return empty_; }
void AddPart(const std::string& part, int y, int left, int right);
std::string GetParts() { return parts_.str(); }
private:
Selection(int start_x, int start_y, int end_x, int end_y, Selection* parent);
const int start_x_ = 0;
const int start_y_ = 0;
const int end_x_ = 0;
const int end_y_ = 0;
const Box box_ = {};
Selection* const parent_ = this;
const bool empty_ = true;
std::stringstream parts_;
// The position of the last inserted part.
int x_ = 0;
int y_ = 0;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_DOM_SELECTION_HPP */

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

@ -14,7 +14,9 @@ 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;
void Shift(int x, int y);
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,54 @@
// 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),
italic(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 italic : 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

@ -4,53 +4,17 @@
#ifndef FTXUI_SCREEN_SCREEN_HPP #ifndef FTXUI_SCREEN_SCREEN_HPP
#define FTXUI_SCREEN_SCREEN_HPP #define FTXUI_SCREEN_SCREEN_HPP
#include <cstdint> // for uint8_t #include <cstdint> // for uint8_t
#include <memory> #include <functional> // for function
#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
#include "ftxui/util/autoreset.hpp" // for AutoReset
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 +24,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 +60,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,14 +69,18 @@ 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; using SelectionStyle = std::function<void(Pixel&)>;
const SelectionStyle& GetSelectionStyle() const;
void SetSelectionStyle(SelectionStyle decorator);
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_ = {""};
// The current selection style. This is overridden by various dom elements.
SelectionStyle selection_style_ = [](Pixel& pixel) {
pixel.inverted ^= true;
};
}; };
} // namespace ftxui } // namespace ftxui

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
@ -39,7 +37,7 @@ class ButtonBase : public ComponentBase, public ButtonOption {
explicit ButtonBase(ButtonOption option) : ButtonOption(std::move(option)) {} explicit ButtonBase(ButtonOption option) : ButtonOption(std::move(option)) {}
// Component implementation: // Component implementation:
Element Render() override { Element OnRender() override {
const bool active = Active(); const bool active = Active();
const bool focused = Focused(); const bool focused = Focused();
const bool focused_or_hover = focused || mouse_hover_; const bool focused_or_hover = focused || mouse_hover_;
@ -49,17 +47,16 @@ class ButtonBase : public ComponentBase, public ButtonOption {
SetAnimationTarget(target); SetAnimationTarget(target);
} }
auto focus_management = focused ? focus : active ? select : nothing; const EntryState state{
const EntryState state = { *label, false, active, focused_or_hover, Index(),
*label,
false,
active,
focused_or_hover,
}; };
auto element = (transform ? transform : DefaultTransform) // auto element = (transform ? transform : DefaultTransform) //
(state); (state);
return element | AnimatedColorStyle() | focus_management | reflect(box_); element |= AnimatedColorStyle();
element |= focus;
element |= reflect(box_);
return element;
} }
Decorator AnimatedColorStyle() { Decorator AnimatedColorStyle() {
@ -98,10 +95,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 +110,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 +127,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 +204,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
@ -24,19 +23,17 @@ class CheckboxBase : public ComponentBase, public CheckboxOption {
private: private:
// Component implementation. // Component implementation.
Element Render() override { Element OnRender() override {
const bool is_focused = Focused(); const bool is_focused = Focused();
const bool is_active = Active(); const bool is_active = Active();
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);
return element | focus_management | reflect(box_); element |= focus;
element |= reflect(box_);
return element;
} }
bool OnEvent(Event event) override { bool OnEvent(Event event) override {
@ -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
@ -14,6 +15,8 @@
#include "ftxui/component/event.hpp" // for Event #include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive #include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for text, Element #include "ftxui/dom/elements.hpp" // for text, Element
#include "ftxui/dom/node.hpp" // for Node, Elements
#include "ftxui/screen/box.hpp" // for Box
namespace ftxui::animation { namespace ftxui::animation {
class Params; class Params;
@ -50,6 +53,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
@ -86,10 +105,46 @@ void ComponentBase::DetachAllChildren() {
} }
/// @brief Draw the component. /// @brief Draw the component.
/// Build a ftxui::Element to be drawn on the ftxi::Screen representing this /// Build a ftxui::Element to be drawn on the ftxui::Screen representing this
/// ftxui::ComponentBase. /// ftxui::ComponentBase. Please override OnRender() to modify the rendering.
/// @ingroup component /// @ingroup component
Element ComponentBase::Render() { Element ComponentBase::Render() {
// Some users might call `ComponentBase::Render()` from
// `T::OnRender()`. To avoid infinite recursion, we use a flag.
if (in_render) {
return ComponentBase::OnRender();
}
in_render = true;
Element element = OnRender();
in_render = false;
class Wrapper : public Node {
public:
bool active_ = false;
Wrapper(Element child, bool active)
: Node({std::move(child)}), active_(active) {}
void SetBox(Box box) override {
Node::SetBox(box);
children_[0]->SetBox(box);
}
void ComputeRequirement() override {
Node::ComputeRequirement();
requirement_.focused.component_active = active_;
}
};
return std::make_shared<Wrapper>(std::move(element), Active());
}
/// @brief Draw the component.
/// Build a ftxui::Element to be drawn on the ftxi::Screen representing this
/// ftxui::ComponentBase. This function is means to be overridden.
/// @ingroup component
Element ComponentBase::OnRender() {
if (children_.size() == 1) { if (children_.size() == 1) {
return children_.front()->Render(); return children_.front()->Render();
} }

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
@ -99,7 +98,7 @@ class VerticalContainer : public ContainerBase {
public: public:
using ContainerBase::ContainerBase; using ContainerBase::ContainerBase;
Element Render() override { Element OnRender() override {
Elements elements; Elements elements;
elements.reserve(children_.size()); elements.reserve(children_.size());
for (auto& it : children_) { for (auto& it : children_) {
@ -164,6 +163,7 @@ class VerticalContainer : public ContainerBase {
return false; return false;
} }
const int old_selected = *selector_;
if (event.mouse().button == Mouse::WheelUp) { if (event.mouse().button == Mouse::WheelUp) {
MoveSelector(-1); MoveSelector(-1);
} }
@ -172,7 +172,7 @@ class VerticalContainer : public ContainerBase {
} }
*selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_)); *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
return true; return old_selected != *selector_;
} }
Box box_; Box box_;
@ -182,7 +182,7 @@ class HorizontalContainer : public ContainerBase {
public: public:
using ContainerBase::ContainerBase; using ContainerBase::ContainerBase;
Element Render() override { Element OnRender() override {
Elements elements; Elements elements;
elements.reserve(children_.size()); elements.reserve(children_.size());
for (auto& it : children_) { for (auto& it : children_) {
@ -218,7 +218,7 @@ class TabContainer : public ContainerBase {
public: public:
using ContainerBase::ContainerBase; using ContainerBase::ContainerBase;
Element Render() override { Element OnRender() override {
const Component active_child = ActiveChild(); const Component active_child = ActiveChild();
if (active_child) { if (active_child) {
return active_child->Render(); return active_child->Render();
@ -244,7 +244,7 @@ class StackedContainer : public ContainerBase {
: ContainerBase(std::move(children), nullptr) {} : ContainerBase(std::move(children), nullptr) {}
private: private:
Element Render() final { Element OnRender() final {
Elements elements; Elements elements;
for (auto& child : children_) { for (auto& child : children_) {
elements.push_back(child->Render()); elements.push_back(child->Render());
@ -334,7 +334,7 @@ Component Vertical(Components children) {
/// children_2, /// children_2,
/// children_3, /// children_3,
/// children_4, /// children_4,
/// }); /// }, &selected_children);
/// ``` /// ```
Component Vertical(Components children, int* selector) { Component Vertical(Components children, int* selector) {
return std::make_shared<VerticalContainer>(std::move(children), selector); return std::make_shared<VerticalContainer>(std::move(children), selector);
@ -355,7 +355,7 @@ Component Vertical(Components children, int* selector) {
/// children_2, /// children_2,
/// children_3, /// children_3,
/// children_4, /// children_4,
/// }, &selected_children); /// });
/// ``` /// ```
Component Horizontal(Components children) { Component Horizontal(Components children) {
return Horizontal(std::move(children), nullptr); return Horizontal(std::move(children), nullptr);

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,122 @@ 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 OnRender() override {
*selected_ = util::clamp(*selected_, 0, int(entries_.size()) - 1); selected_ =
title_ = entries_[static_cast<size_t>(*selected_)]; util::clamp(radiobox.selected(), 0, int(radiobox.entries.size()) - 1);
if (show_) { selected_ = util::clamp(selected_(), 0, int(radiobox.entries.size()) - 1);
const int max_height = 12;
return vbox({ if (selected_() >= 0 && selected_() < int(radiobox.entries.size())) {
checkbox_->Render(), title_ = radiobox.entries[selected_()];
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) || //
(event.is_mouse() && event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed);
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

@ -0,0 +1,34 @@
// Copyright 2025 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" // for Horizontal, Vertical, Button, Tab
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "ftxui/component/event.hpp" // for Event, Event::Tab, Event::TabReverse, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp
#include "gtest/gtest.h" // for AssertionResult, Message, TestPartResult, EXPECT_EQ, EXPECT_FALSE, Test, EXPECT_TRUE, TEST
namespace ftxui {
TEST(DropdownTest, Empty) {
std::vector<std::string> list = {};
int index = 0;
auto dropdown = Dropdown(list, &index);
dropdown->OnEvent(Event::Return);
auto screen = Screen(8, 8);
auto document = dropdown->Render();
Render(screen, document);
EXPECT_EQ(screen.ToString(),
"╭──────╮\r\n"
"│↓ │\r\n"
"├──────┤\r\n"
"│ │\r\n"
"│ │\r\n"
"│ │\r\n"
"│ │\r\n"
"╰──────╯");
}
} // 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
@ -50,8 +49,8 @@ Component Hoverable(Component component, bool* hover) {
} }
private: private:
Element Render() override { Element OnRender() override {
return ComponentBase::Render() | reflect(box_); return ComponentBase::OnRender() | reflect(box_);
} }
bool OnEvent(Event event) override { bool OnEvent(Event event) override {
@ -99,8 +98,8 @@ Component Hoverable(Component component,
} }
private: private:
Element Render() override { Element OnRender() override {
return ComponentBase::Render() | reflect(box_); return ComponentBase::OnRender() | reflect(box_);
} }
bool OnEvent(Event event) override { bool OnEvent(Event event) override {

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
@ -98,10 +96,11 @@ class InputBase : public ComponentBase, public InputOption {
private: private:
// Component implementation: // Component implementation:
Element Render() override { Element OnRender() override {
const bool is_focused = Focused(); const bool is_focused = Focused();
const auto focused = const auto focused = (!is_focused && !hovered_) ? nothing
(is_focused || hovered_) ? focusCursorBarBlinking : select; : insert() ? focusCursorBarBlinking
: focusCursorBlockBlinking;
auto transform_func = auto transform_func =
transform ? transform : InputOption::Default().transform; transform ? transform : InputOption::Default().transform;
@ -109,15 +108,12 @@ class InputBase : public ComponentBase, public InputOption {
// placeholder. // placeholder.
if (content->empty()) { if (content->empty()) {
auto element = text(placeholder()) | xflex | frame; auto element = text(placeholder()) | xflex | frame;
if (is_focused) {
element |= focus;
}
return transform_func({ return transform_func({
std::move(element), hovered_, is_focused, std::move(element), hovered_, is_focused,
true // placeholder true // placeholder
}) | }) |
reflect(box_); focus | reflect(box_);
} }
Elements elements; Elements elements;
@ -133,7 +129,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 +159,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);
@ -177,7 +173,7 @@ class InputBase : public ComponentBase, public InputOption {
elements.push_back(element); elements.push_back(element);
} }
auto element = vbox(std::move(elements)) | frame; auto element = vbox(std::move(elements), cursor_line) | frame;
return transform_func({ return transform_func({
std::move(element), hovered_, is_focused, std::move(element), hovered_, is_focused,
false // placeholder false // placeholder
@ -205,11 +201,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 +216,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 +239,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 +255,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 +268,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 +290,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 +302,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 +321,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 +340,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 +353,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 +405,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 +422,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 +430,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 +442,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 +454,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 +468,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 +491,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 +517,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
@ -25,8 +24,8 @@ Component Maybe(Component child, std::function<bool()> show) {
explicit Impl(std::function<bool()> show) : show_(std::move(show)) {} explicit Impl(std::function<bool()> show) : show_(std::move(show)) {}
private: private:
Element Render() override { Element OnRender() override {
return show_() ? ComponentBase::Render() : std::make_unique<Node>(); return show_() ? ComponentBase::OnRender() : std::make_unique<Node>();
} }
bool Focusable() const override { bool Focusable() const override {
return show_() && ComponentBase::Focusable(); return show_() && ComponentBase::Focusable();

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() {
@ -107,7 +105,7 @@ class MenuBase : public ComponentBase, public MenuOption {
} }
} }
Element Render() override { Element OnRender() override {
Clamp(); Clamp();
UpdateAnimationTarget(); UpdateAnimationTarget();
@ -125,21 +123,18 @@ 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 = Element element = (entries_option.transform ? entries_option.transform
is_menu_focused && (selected_focus_ == i) ? focus : nothing; : DefaultOptionTransform) //
const Element element =
(entries_option.transform ? entries_option.transform
: DefaultOptionTransform) //
(state); (state);
elements.push_back(element | AnimatedColorStyle(i) | reflect(boxes_[i]) | if (selected_focus_ == i) {
focus_management); element |= focus;
}
element |= AnimatedColorStyle(i);
element |= reflect(boxes_[i]);
elements.push_back(element);
} }
if (elements_postfix) { if (elements_postfix) {
elements.push_back(elements_postfix()); elements.push_back(elements_postfix());
@ -149,8 +144,9 @@ class MenuBase : public ComponentBase, public MenuOption {
std::reverse(elements.begin(), elements.end()); std::reverse(elements.begin(), elements.end());
} }
const Element bar = const Element bar = IsHorizontal()
IsHorizontal() ? hbox(std::move(elements)) : vbox(std::move(elements)); ? hbox(std::move(elements), selected_focus_)
: vbox(std::move(elements), selected_focus_);
if (!underline.enabled) { if (!underline.enabled) {
return bar | reflect(box_); return bar | reflect(box_);
@ -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));
} }
@ -620,23 +618,22 @@ Component MenuEntry(MenuEntryOption option) {
: MenuEntryOption(std::move(option)) {} : MenuEntryOption(std::move(option)) {}
private: private:
Element Render() override { Element OnRender() override {
const bool focused = Focused(); const bool is_focused = Focused();
UpdateAnimationTarget(); UpdateAnimationTarget();
const EntryState state = { const EntryState state{
label(), label(), false, hovered_, is_focused, Index(),
false,
hovered_,
focused,
}; };
const Element element = Element element = (transform ? transform : DefaultOptionTransform) //
(transform ? transform : DefaultOptionTransform) //
(state); (state);
auto focus_management = focused ? select : nothing; if (is_focused) {
return element | AnimatedColorStyle() | focus_management | reflect(box_); element |= focus;
}
return element | AnimatedColorStyle() | reflect(box_);
} }
void UpdateAnimationTarget() { void UpdateAnimationTarget() {
@ -684,7 +681,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;
} }
@ -697,7 +694,6 @@ Component MenuEntry(MenuEntryOption option) {
animator_foreground_.OnAnimation(params); animator_foreground_.OnAnimation(params);
} }
MenuEntryOption option_;
Box box_; Box box_;
bool hovered_ = false; bool hovered_ = false;

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 (size_t index = 0; index < menu->ChildCount(); index++) {
EXPECT_EQ(menu->ChildAt(index)->Index(), index);
}
}
} // namespace ftxui } // namespace ftxui
// NOLINTEND // NOLINTEND

View File

@ -26,7 +26,7 @@ Component Modal(Component main, Component modal, const bool* show_modal) {
} }
private: private:
Element Render() override { Element OnRender() override {
selector_ = *show_modal_; selector_ = *show_modal_;
auto document = main_->Render(); auto document = main_->Render();
if (*show_modal_) { if (*show_modal_) {

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,10 +24,11 @@ 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 OnRender() override {
Clamp(); Clamp();
Elements elements; Elements elements;
const bool is_menu_focused = Focused(); const bool is_menu_focused = Focused();
@ -37,21 +36,17 @@ class RadioboxBase : public ComponentBase, public RadioboxOption {
for (int i = 0; i < size(); ++i) { for (int i = 0; i < size(); ++i) {
const bool is_focused = (focused_entry() == i) && is_menu_focused; const bool is_focused = (focused_entry() == i) && is_menu_focused;
const bool is_selected = (hovered_ == i); const bool is_selected = (hovered_ == i);
auto focus_management = !is_selected ? nothing
: is_menu_focused ? focus
: 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);
if (is_selected) {
elements.push_back(element | focus_management | reflect(boxes_[i])); element |= focus;
}
elements.push_back(element | reflect(boxes_[i]));
} }
return vbox(std::move(elements)) | reflect(box_); return vbox(std::move(elements), hovered_) | reflect(box_);
} }
// NOLINTNEXTLINE(readability-function-cognitive-complexity) // NOLINTNEXTLINE(readability-function-cognitive-complexity)
@ -124,7 +119,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 +200,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 +236,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

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