5 Commits

Author SHA1 Message Date
Arthur Sonzogni
d4bd64fbf6 Merge b7b6a48755 into 994915dbb9 2025-08-03 16:21:19 +00:00
ArthurSonzogni
b7b6a48755 Update 2025-08-03 18:21:14 +02:00
ArthurSonzogni
0cf6f93f77 Refactor FetchTerminalEvent 2025-07-10 15:45:42 +02:00
ArthurSonzogni
4ac28b5675 Add unique_ptr<TaskRunner> 2025-07-10 15:45:42 +02:00
ArthurSonzogni
1b479ee12d Add a task system. 2025-07-10 15:45:41 +02:00
53 changed files with 244 additions and 580 deletions

View File

@@ -2,6 +2,3 @@
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html # http://clang.llvm.org/docs/ClangFormatStyleOptions.html
BasedOnStyle: Chromium BasedOnStyle: Chromium
Standard: Cpp11 Standard: Cpp11
InsertBraces: true
InsertNewlineAtEOF: true

View File

@@ -158,26 +158,26 @@ ftxui_cc_library(
"src/ftxui/component/resizable_split.cpp", "src/ftxui/component/resizable_split.cpp",
"src/ftxui/component/screen_interactive.cpp", "src/ftxui/component/screen_interactive.cpp",
"src/ftxui/component/slider.cpp", "src/ftxui/component/slider.cpp",
"src/ftxui/component/task.cpp",
"src/ftxui/component/task_internal.hpp",
"src/ftxui/component/task_queue.cpp",
"src/ftxui/component/task_queue.hpp",
"src/ftxui/component/task_runner.cpp",
"src/ftxui/component/task_runner.hpp",
"src/ftxui/component/terminal_input_parser.cpp", "src/ftxui/component/terminal_input_parser.cpp",
"src/ftxui/component/terminal_input_parser.hpp", "src/ftxui/component/terminal_input_parser.hpp",
"src/ftxui/component/util.cpp", "src/ftxui/component/util.cpp",
"src/ftxui/component/window.cpp", "src/ftxui/component/window.cpp",
# Core
"src/ftxui/core/task.cpp",
"src/ftxui/core/task.hpp",
"src/ftxui/core/task_queue.cpp",
"src/ftxui/core/task_queue.hpp",
"src/ftxui/core/task_runner.cpp",
"src/ftxui/core/task_runner.hpp",
# Private header from ftxui:dom. # Private header from ftxui:dom.
"src/ftxui/dom/node_decorator.hpp", "src/ftxui/dom/node_decorator.hpp",
# Private header from ftxui:screen. # Private header from ftxui:screen.
"src/ftxui/screen/string_internal.hpp", "src/ftxui/screen/string_internal.hpp",
"src/ftxui/screen/util.hpp", "src/ftxui/screen/util.hpp",
# Private header.
"include/ftxui/util/warn_windows_macro.hpp",
], ],
hdrs = [ hdrs = [
"include/ftxui/component/animation.hpp", "include/ftxui/component/animation.hpp",

View File

@@ -24,13 +24,10 @@ Next
import ftxui.util; import ftxui.util;
``` ```
Thanks @mikomikotaishi for PR #1015. Thanks @mikomikotaishi for PR #1015.
- Remove dependency on 'pthread'.
### Component ### Component
- Fix ScreenInteractive::FixedSize screen stomps on the preceding terminal - Fix ScreenInteractive::FixedSize screen stomps on the preceding terminal
output. Thanks @zozowell in #1064. output. Thanks @zozowell in #1064.
- Fix vertical `ftxui::Slider`. The "up" key was previously decreasing the
value. Thanks @its-pablo in #1093 for reporting the issue.
6.1.9 (2025-05-07) 6.1.9 (2025-05-07)

View File

@@ -144,16 +144,16 @@ add_library(component
src/ftxui/component/resizable_split.cpp src/ftxui/component/resizable_split.cpp
src/ftxui/component/screen_interactive.cpp src/ftxui/component/screen_interactive.cpp
src/ftxui/component/slider.cpp src/ftxui/component/slider.cpp
src/ftxui/component/task.cpp
src/ftxui/component/task_internal.hpp
src/ftxui/component/task_queue.cpp
src/ftxui/component/task_queue.hpp
src/ftxui/component/task_runner.cpp
src/ftxui/component/task_runner.hpp
src/ftxui/component/terminal_input_parser.cpp src/ftxui/component/terminal_input_parser.cpp
src/ftxui/component/terminal_input_parser.hpp src/ftxui/component/terminal_input_parser.hpp
src/ftxui/component/util.cpp src/ftxui/component/util.cpp
src/ftxui/component/window.cpp src/ftxui/component/window.cpp
src/ftxui/core/task.cpp
src/ftxui/core/task.hpp
src/ftxui/core/task_queue.cpp
src/ftxui/core/task_queue.hpp
src/ftxui/core/task_runner.cpp
src/ftxui/core/task_runner.hpp
) )
target_link_libraries(dom PUBLIC screen) target_link_libraries(dom PUBLIC screen)
@@ -178,8 +178,8 @@ include(cmake/ftxui_install.cmake)
include(cmake/ftxui_package.cmake) include(cmake/ftxui_package.cmake)
include(cmake/ftxui_modules.cmake) include(cmake/ftxui_modules.cmake)
add_subdirectory(examples)
add_subdirectory(doc) add_subdirectory(doc)
add_subdirectory(examples)
# You can generate ./examples_modules/ by running # You can generate ./examples_modules/ by running
# ./tools/generate_examples_modules.sh # ./tools/generate_examples_modules.sh

View File

@@ -1,4 +1,3 @@
<p align="center"> <p align="center">
<img src="https://github.com/ArthurSonzogni/FTXUI/assets/4759106/6925b6da-0a7e-49d9-883c-c890e1f36007" alt="Demo image"></img> <img src="https://github.com/ArthurSonzogni/FTXUI/assets/4759106/6925b6da-0a7e-49d9-883c-c890e1f36007" alt="Demo image"></img>
<br/> <br/>
@@ -363,8 +362,6 @@ Feel free to add your projects here:
- [FTB - tertminal file browser](https://github.com/Cyxuan0311/FTB) - [FTB - tertminal file browser](https://github.com/Cyxuan0311/FTB)
- [openJuice](https://github.com/mikomikotaishi/openJuice) - [openJuice](https://github.com/mikomikotaishi/openJuice)
- [SHOOT!](https://github.com/ShingZhanho/ENGG1340-Project-25Spring) - [SHOOT!](https://github.com/ShingZhanho/ENGG1340-Project-25Spring)
- [VerifySN (Fast Hash Tool)](https://github.com/d06i/verifySN)
- [tic-tac-toe](https://github.com/birland/tic-tac-toe)
### [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)

Submodule build/_deps/googlebenchmark-src deleted from 015d1a091a

View File

@@ -1,42 +0,0 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
cmake_minimum_required(VERSION 3.31.6)
# Reject any attempt to use a toolchain file. We must not use one because
# we could be downloading it here. If the CMAKE_TOOLCHAIN_FILE environment
# variable is set, the cache variable will have been initialized from it.
unset(CMAKE_TOOLCHAIN_FILE CACHE)
unset(ENV{CMAKE_TOOLCHAIN_FILE})
# We name the project and the target for the ExternalProject_Add() call
# to something that will highlight to the user what we are working on if
# something goes wrong and an error message is produced.
project(googlebenchmark-populate NONE)
# Pass through things we've already detected in the main project to avoid
# paying the cost of redetecting them again in ExternalProject_Add()
set(GIT_EXECUTABLE [==[/usr/bin/git]==])
set(GIT_VERSION_STRING [==[2.51.0]==])
set_property(GLOBAL PROPERTY _CMAKE_FindGit_GIT_EXECUTABLE_VERSION
[==[/usr/bin/git;2.51.0]==]
)
include(ExternalProject)
ExternalProject_Add(googlebenchmark-populate
"UPDATE_DISCONNECTED" "False" "GIT_REPOSITORY" "https://github.com/google/benchmark" "EXTERNALPROJECT_INTERNAL_ARGUMENT_SEPARATOR" "GIT_TAG" "015d1a091af6937488242b70121858bce8fd40e9" "GIT_PROGRESS" "TRUE"
SOURCE_DIR "/home/runner/work/FTXUI/FTXUI/build/_deps/googlebenchmark-src"
BINARY_DIR "/home/runner/work/FTXUI/FTXUI/build/_deps/googlebenchmark-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
USES_TERMINAL_DOWNLOAD YES
USES_TERMINAL_UPDATE YES
USES_TERMINAL_PATCH YES
)

Submodule build/_deps/googletest-src deleted from 23ef29555e

View File

@@ -1,42 +0,0 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
cmake_minimum_required(VERSION 3.31.6)
# Reject any attempt to use a toolchain file. We must not use one because
# we could be downloading it here. If the CMAKE_TOOLCHAIN_FILE environment
# variable is set, the cache variable will have been initialized from it.
unset(CMAKE_TOOLCHAIN_FILE CACHE)
unset(ENV{CMAKE_TOOLCHAIN_FILE})
# We name the project and the target for the ExternalProject_Add() call
# to something that will highlight to the user what we are working on if
# something goes wrong and an error message is produced.
project(googletest-populate NONE)
# Pass through things we've already detected in the main project to avoid
# paying the cost of redetecting them again in ExternalProject_Add()
set(GIT_EXECUTABLE [==[/usr/bin/git]==])
set(GIT_VERSION_STRING [==[2.51.0]==])
set_property(GLOBAL PROPERTY _CMAKE_FindGit_GIT_EXECUTABLE_VERSION
[==[/usr/bin/git;2.51.0]==]
)
include(ExternalProject)
ExternalProject_Add(googletest-populate
"UPDATE_DISCONNECTED" "False" "GIT_REPOSITORY" "https://github.com/google/googletest" "EXTERNALPROJECT_INTERNAL_ARGUMENT_SEPARATOR" "GIT_TAG" "23ef29555ef4789f555f1ba8c51b4c52975f0907" "GIT_PROGRESS" "TRUE"
SOURCE_DIR "/home/runner/work/FTXUI/FTXUI/build/_deps/googletest-src"
BINARY_DIR "/home/runner/work/FTXUI/FTXUI/build/_deps/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
USES_TERMINAL_DOWNLOAD YES
USES_TERMINAL_UPDATE YES
USES_TERMINAL_PATCH YES
)

View File

@@ -11,9 +11,6 @@ include(CMakePackageConfigHelpers)
install( install(
TARGETS screen dom component TARGETS screen dom component
EXPORT ftxui-targets EXPORT ftxui-targets
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
) )
install( install(

View File

@@ -22,9 +22,9 @@ add_executable(ftxui-tests
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
src/ftxui/component/slider_test.cpp src/ftxui/component/slider_test.cpp
src/ftxui/component/task_test.cpp
src/ftxui/component/terminal_input_parser_test.cpp src/ftxui/component/terminal_input_parser_test.cpp
src/ftxui/component/toggle_test.cpp src/ftxui/component/toggle_test.cpp
src/ftxui/core/task_test.cpp
src/ftxui/dom/blink_test.cpp src/ftxui/dom/blink_test.cpp
src/ftxui/dom/bold_test.cpp src/ftxui/dom/bold_test.cpp
src/ftxui/dom/border_test.cpp src/ftxui/dom/border_test.cpp

View File

@@ -1,99 +1,19 @@
@page installation_conan Conan @page installation_conan Conan
@tableofcontents
FTXUI can be easily obtained and integrated into your project using the Conan package manager. Unofficial recipe for FTXUI exists on Conan Center:
<https://conan.io/center/recipes/ftxui>
## Prerequisites
First, ensure that Conan is installed on your system. If not, you can install it via pip:
```powershell
pip install conan
```
Conan often works in tandem with CMake, so you will need to have CMake installed as well. Once you have confirmed both Conan and CMake are installed, create a project directory, for example, `ftxui-demo`:
```powershell
mkdir C:\ftxui-demo
cd C:\ftxui-demo
```
## Configuration
After ensuring your environment is set up correctly, create a Conan configuration file `conanfile.txt`. This file is used to declare your project's dependencies. The community-maintained package for FTXUI can be found on [Conan Center](https://conan.io/center/recipes/ftxui).
> [!note] > [!note]
> This is an unofficial build script. This means it is not maintained by the FTXUI > This is an unofficial recipe. That means it is not maintained by the FTXUI
> team but by the community. The package maintainer appears to actively update it > team, but by the community. The package maintainers seems to actively update
> to the latest releases. Many thanks to the maintainer for their work! > the package to the latest version. Thanks to the maintainers for their work!
@todo If you are familiar with the process, please consider adding an "official" build script to Conan Center.
This could be a GitHub Action that automatically updates Conan Center upon new releases.
```ini @todo Add instructions on how to use the conan recipe.
[requires]
ftxui/6.0.2
[generators] @todo Please consider adding an "official" recipe to Conan Center if know how.
CMakeDeps It could be a github action that will automatically update the conan center
CMakeToolchain when a new release is made.
[layout]
cmake_layout
```
## Install Dependencies and Build
Once configured, run the following command to install FTXUI and its dependencies:
```powershell
conan install . --output-folder=build --build=missing
```
This will download and install `ftxui/6.0.2` along with all its dependencies from Conan's remote repositories.
After the installation completes, you can test it by creating a `demo.cpp` file in your project directory:
```cpp
#include <ftxui/screen/screen.hpp>
#include <ftxui/dom/elements.hpp>
#include <iostream>
int main() {
using namespace ftxui;
auto document = hbox({
text(" Hello "),
text("FTXUI ") | bold | color(Color::Red),
text(" world! ")
});
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document);
std::cout << screen.ToString() << std::endl;
return 0;
}
```
If the test is successful, you can then create a `CMakeLists.txt` file in the project directory:
```cmake
cmake_minimum_required(VERSION 3.20)
project(ftxui-demo)
# Set the C++ standard
set(CMAKE_CXX_STANDARD 20)
# Find the FTXUI package installed via Conan
find_package(ftxui CONFIG REQUIRED)
# Create the executable
add_executable(demo demo.cpp)
# Link the executable to the FTXUI library
target_link_libraries(demo PRIVATE ftxui::component)
```
@todo 考虑到中国多数地区使用Conan很有可能遇到各种网络问题我想做一个定制的版本说明但是我对conan的了解有限再加上没有找到合适的资料因此这个计划短暂的被搁置了如果您知道方法欢迎在[中文版本](xiaoditx.girhub.io/public/docs/ftxui%E4%B8%AD%E6%96%87%E7%BF%BB%E8%AF%91/installation/conan/)的下方留下评论以提醒我
---
<div class="section_buttons"> <div class="section_buttons">

View File

@@ -17,12 +17,10 @@ add_subdirectory(dom)
if (EMSCRIPTEN) if (EMSCRIPTEN)
get_property(EXAMPLES GLOBAL PROPERTY FTXUI::EXAMPLES) get_property(EXAMPLES GLOBAL PROPERTY FTXUI::EXAMPLES)
foreach(file foreach(file
"index.css"
"index.html" "index.html"
"index.mjs" "index.mjs"
"run_webassembly.py" "index.css"
"sw.js" "run_webassembly.py")
)
configure_file(${file} ${file}) configure_file(${file} ${file})
endforeach(file) endforeach(file)
endif() endif()

View File

@@ -34,8 +34,8 @@ int main() {
int value = 50; int value = 50;
// clang-format off // clang-format off
auto btn_dec_01 = Button("-1", [&] { value -= 1; }, Style()); auto btn_dec_01 = Button("-1", [&] { value += 1; }, Style());
auto btn_inc_01 = Button("+1", [&] { value += 1; }, Style()); auto btn_inc_01 = Button("+1", [&] { value -= 1; }, Style());
auto btn_dec_10 = Button("-10", [&] { value -= 10; }, Style()); auto btn_dec_10 = Button("-10", [&] { value -= 10; }, Style());
auto btn_inc_10 = Button("+10", [&] { value += 10; }, Style()); auto btn_inc_10 = Button("+10", [&] { value += 10; }, Style());
// clang-format on // clang-format on

View File

@@ -133,9 +133,8 @@ int main() {
float dy = 50.f; float dy = 50.f;
ys[x] = int(dy + 20 * cos(dx * 0.14) + 10 * sin(dx * 0.42)); ys[x] = int(dy + 20 * cos(dx * 0.14) + 10 * sin(dx * 0.42));
} }
for (int x = 1; x < 99; x++) { for (int x = 1; x < 99; x++)
c.DrawPointLine(x, ys[x], x + 1, ys[x + 1]); c.DrawPointLine(x, ys[x], x + 1, ys[x + 1]);
}
return canvas(std::move(c)); return canvas(std::move(c));
}); });

View File

@@ -82,12 +82,10 @@ int main() {
size(WIDTH, EQUAL, dimx) | size(HEIGHT, EQUAL, dimy) | size(WIDTH, EQUAL, dimx) | size(HEIGHT, EQUAL, dimy) |
bgcolor(Color::HSV(index * 25, 255, 255)) | bgcolor(Color::HSV(index * 25, 255, 255)) |
color(Color::Black); color(Color::Black);
if (element_xflex_grow) { if (element_xflex_grow)
element = element | xflex_grow; element = element | xflex_grow;
} if (element_yflex_grow)
if (element_yflex_grow) {
element = element | yflex_grow; element = element | yflex_grow;
}
return element; return element;
}; };
@@ -121,12 +119,10 @@ int main() {
group = group | notflex; group = group | notflex;
if (!group_xflex_grow) { if (!group_xflex_grow)
group = hbox(group, filler()); group = hbox(group, filler());
} if (!group_yflex_grow)
if (!group_yflex_grow) {
group = vbox(group, filler()); group = vbox(group, filler());
}
group = group | flex; group = group | flex;
return group; return group;

View File

@@ -514,7 +514,7 @@ int main() {
}); });
Loop loop(&screen, main_renderer); Loop loop(&screen, main_renderer);
while (!loop.HasQuitted()) { while(!loop.HasQuitted()) {
// Update the state of the application. // Update the state of the application.
shift++; shift++;
@@ -525,7 +525,7 @@ int main() {
loop.RunOnce(); loop.RunOnce();
// Sleep for a short duration to control the frame rate (60 FPS). // Sleep for a short duration to control the frame rate (60 FPS).
std::this_thread::sleep_for(std::chrono::milliseconds(1000 / 60)); std::this_thread::sleep_for(std::chrono::milliseconds(1000/60));
} }
return 0; return 0;

View File

@@ -22,12 +22,10 @@ MenuEntryOption Colored(ftxui::Color c) {
option.transform = [c](EntryState state) { option.transform = [c](EntryState state) {
state.label = (state.active ? "> " : " ") + state.label; state.label = (state.active ? "> " : " ") + state.label;
Element e = text(state.label) | color(c); Element e = text(state.label) | color(c);
if (state.focused) { if (state.focused)
e = e | inverted; e = e | inverted;
} if (state.active)
if (state.active) {
e = e | bold; e = e | bold;
}
return e; return e;
}; };
return option; return option;

View File

@@ -17,9 +17,8 @@ int main() {
std::vector<std::string> entries; std::vector<std::string> entries;
int selected = 0; int selected = 0;
for (int i = 0; i < 30; ++i) { for (int i = 0; i < 30; ++i)
entries.push_back("Entry " + std::to_string(i)); entries.push_back("Entry " + std::to_string(i));
}
auto radiobox = Menu(&entries, &selected); auto radiobox = Menu(&entries, &selected);
auto renderer = Renderer(radiobox, [&] { auto renderer = Renderer(radiobox, [&] {
return radiobox->Render() | vscroll_indicator | frame | return radiobox->Render() | vscroll_indicator | frame |

View File

@@ -17,9 +17,8 @@ int main() {
std::vector<std::string> entries; std::vector<std::string> entries;
int selected = 0; int selected = 0;
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i)
entries.push_back(std::to_string(i)); entries.push_back(std::to_string(i));
}
auto radiobox = Menu(&entries, &selected, MenuOption::Horizontal()); auto radiobox = Menu(&entries, &selected, MenuOption::Horizontal());
auto renderer = Renderer( auto renderer = Renderer(
radiobox, [&] { return radiobox->Render() | hscroll_indicator | frame; }); radiobox, [&] { return radiobox->Render() | hscroll_indicator | frame; });

View File

@@ -116,12 +116,10 @@ Component VMenu1(std::vector<std::string>* entries, int* selected) {
option.entries_option.transform = [](EntryState state) { option.entries_option.transform = [](EntryState state) {
state.label = (state.active ? "> " : " ") + state.label; state.label = (state.active ? "> " : " ") + state.label;
Element e = text(state.label); Element e = text(state.label);
if (state.focused) { if (state.focused)
e = e | bgcolor(Color::Blue); e = e | bgcolor(Color::Blue);
} if (state.active)
if (state.active) {
e = e | bold; e = e | bold;
}
return e; return e;
}; };
return Menu(entries, selected, option); return Menu(entries, selected, option);
@@ -132,12 +130,10 @@ Component VMenu2(std::vector<std::string>* entries, int* selected) {
option.entries_option.transform = [](EntryState state) { option.entries_option.transform = [](EntryState state) {
state.label += (state.active ? " <" : " "); state.label += (state.active ? " <" : " ");
Element e = hbox(filler(), text(state.label)); Element e = hbox(filler(), text(state.label));
if (state.focused) { if (state.focused)
e = e | bgcolor(Color::Red); e = e | bgcolor(Color::Red);
} if (state.active)
if (state.active) {
e = e | bold; e = e | bold;
}
return e; return e;
}; };
return Menu(entries, selected, option); return Menu(entries, selected, option);
@@ -148,16 +144,13 @@ Component VMenu3(std::vector<std::string>* entries, int* selected) {
option.entries_option.transform = [](EntryState state) { option.entries_option.transform = [](EntryState state) {
Element e = state.active ? text("[" + state.label + "]") Element e = state.active ? text("[" + state.label + "]")
: text(" " + state.label + " "); : text(" " + state.label + " ");
if (state.focused) { if (state.focused)
e = e | bold; e = e | bold;
}
if (state.focused) { if (state.focused)
e = e | color(Color::Blue); e = e | color(Color::Blue);
} if (state.active)
if (state.active) {
e = e | bold; e = e | bold;
}
return e; return e;
}; };
return Menu(entries, selected, option); return Menu(entries, selected, option);
@@ -252,12 +245,10 @@ Component HMenu5(std::vector<std::string>* entries, int* selected) {
animation::easing::ElasticOut); animation::easing::ElasticOut);
option.entries_option.transform = [](EntryState state) { option.entries_option.transform = [](EntryState state) {
Element e = text(state.label) | hcenter | flex; Element e = text(state.label) | hcenter | flex;
if (state.active && state.focused) { if (state.active && state.focused)
e = e | bold; e = e | bold;
} if (!state.focused && !state.active)
if (!state.focused && !state.active) {
e = e | dim; e = e | dim;
}
return e; return e;
}; };
option.underline.color_inactive = Color::Default; option.underline.color_inactive = Color::Default;

View File

@@ -20,9 +20,8 @@ using namespace ftxui;
Component DummyComponent(int id) { Component DummyComponent(int id) {
return Renderer([id](bool focused) { return Renderer([id](bool focused) {
auto t = text("component " + std::to_string(id)); auto t = text("component " + std::to_string(id));
if (focused) { if (focused)
t = t | inverted; t = t | inverted;
}
return t; return t;
}); });
} }

View File

@@ -17,9 +17,8 @@ int main() {
std::vector<std::string> entries; std::vector<std::string> entries;
int selected = 0; int selected = 0;
for (int i = 0; i < 30; ++i) { for (int i = 0; i < 30; ++i)
entries.push_back("RadioBox " + std::to_string(i)); entries.push_back("RadioBox " + std::to_string(i));
}
auto radiobox = Radiobox(&entries, &selected); auto radiobox = Radiobox(&entries, &selected);
auto renderer = Renderer(radiobox, [&] { auto renderer = Renderer(radiobox, [&] {
return radiobox->Render() | vscroll_indicator | frame | return radiobox->Render() | vscroll_indicator | frame |

View File

@@ -19,11 +19,10 @@ int main() {
// 1. Example of focusable renderer: // 1. Example of focusable renderer:
auto renderer_focusable = Renderer([](bool focused) { auto renderer_focusable = Renderer([](bool focused) {
if (focused) { if (focused)
return text("FOCUSABLE RENDERER()") | center | bold | border; return text("FOCUSABLE RENDERER()") | center | bold | border;
} else { else
return text(" Focusable renderer() ") | center | border; return text(" Focusable renderer() ") | center | border;
}
}); });
// 2. Examples of a non focusable renderer. // 2. Examples of a non focusable renderer.
@@ -34,11 +33,10 @@ int main() {
// 3. Renderer can wrap other components to redefine their Render() function. // 3. Renderer can wrap other components to redefine their Render() function.
auto button = Button("Wrapped quit button", screen.ExitLoopClosure()); auto button = Button("Wrapped quit button", screen.ExitLoopClosure());
auto renderer_wrap = Renderer(button, [&] { auto renderer_wrap = Renderer(button, [&] {
if (button->Focused()) { if (button->Focused())
return button->Render() | bold | color(Color::Red); return button->Render() | bold | color(Color::Red);
} else { else
return button->Render(); return button->Render();
}
}); });
// Let's renderer everyone: // Let's renderer everyone:

View File

@@ -32,12 +32,10 @@ int main() {
// Plot a function: // Plot a function:
std::vector<int> ys(100); std::vector<int> ys(100);
for (int x = 0; x < 100; x++) { for (int x = 0; x < 100; x++)
ys[x] = int(80 + 20 * cos(x * 0.2)); ys[x] = int(80 + 20 * cos(x * 0.2));
} for (int x = 0; x < 99; x++)
for (int x = 0; x < 99; x++) {
c.DrawPointLine(x, ys[x], x + 1, ys[x + 1], Color::Red); c.DrawPointLine(x, ys[x], x + 1, ys[x + 1], Color::Red);
}
auto document = canvas(&c) | border; auto document = canvas(&c) | border;

View File

@@ -86,9 +86,8 @@ int main() {
auto render = [&]() { auto render = [&]() {
std::vector<Element> entries; std::vector<Element> entries;
for (auto& task : displayed_task) { for (auto& task : displayed_task)
entries.push_back(renderTask(task)); entries.push_back(renderTask(task));
}
return vbox({ return vbox({
// List of tasks. // List of tasks.
@@ -139,9 +138,8 @@ int main() {
std::this_thread::sleep_for(0.01s); std::this_thread::sleep_for(0.01s);
// Exit // Exit
if (nb_active + nb_queued == 0) { if (nb_active + nb_queued == 0)
break; break;
}
// Update the model for the next frame. // Update the model for the next frame.
updateModel(); updateModel();

View File

@@ -21,9 +21,8 @@ int main() {
for (int index = 0; index < 200; ++index) { for (int index = 0; index < 200; ++index) {
std::vector<Element> entries; std::vector<Element> entries;
for (int i = 0; i < 23; ++i) { for (int i = 0; i < 23; ++i) {
if (i != 0) { if (i != 0)
entries.push_back(separator()); entries.push_back(separator());
}
entries.push_back( // entries.push_back( //
hbox({ hbox({
text(std::to_string(i)) | size(WIDTH, EQUAL, 2), text(std::to_string(i)) | size(WIDTH, EQUAL, 2),

View File

@@ -1,19 +1,15 @@
@import url(https://fonts.googleapis.com/css?family=Khula:700); @import url(https://fonts.googleapis.com/css?family=Khula:700);
html {
--toc-width: 250px;
}
body { body {
background-color: #EEE; background-color:#EEE;
padding: 0px; padding:0px;
margin: 0px; margin:0px;
font-family: Khula, Helvetica, sans-serif; font-family: Khula, Helvetica, sans-serif;
font-size: 130%; font-size: 130%;
} }
.page { .page {
max-width: 1300px; max-width:1300px;
margin: auto; margin: auto;
padding: 10px; padding: 10px;
} }
@@ -34,48 +30,45 @@ a:hover {
h1 { h1 {
text-decoration: underline; text-decoration: underline;
width: 100%; width:100%;
background-color: rgba(100, 100, 255, 0.5); background-color: rgba(100,100,255,0.5);
padding: 10px; padding: 10px;
margin: 0; margin: 0;
} }
#selectExample { #selectExample {
flex: 1; flex:1;
} }
#selectExample, #selectExample, #selectExample option {
#selectExample option {
font-size: 16px; font-size: 16px;
font-family: sans-serif; font-family: sans-serif;
font-weight: 700; font-weight: 700;
line-height: 1.3; line-height: 1.3;
border: 0px; border:0px;
background-color: #bbb; background-color: #bbb;
color: black; color:black;
} }
#selectExample:focus { #selectExample:focus {
outline: none; outline:none;
} }
#terminal { #terminal {
width: 100%; width:100%;
height 500px; height 500px;
height: calc(clamp(200px, 100vh - 300px, 900px)); height: calc(clamp(200px, 100vh - 300px, 900px));
overflow: hidden; overflow: hidden;
border: none; border:none;
padding: 10px; background-color:black;
margin: 10px;
} }
#terminalContainer { #terminalContainer {
overflow: hidden; overflow: hidden;
border-radius: 10px; border-radius: 10px;
box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.75), box-shadow: 0px 2px 10px 0px rgba(0,0,0,0.75),
0px 2px 80px 0px rgba(0, 0, 0, 0.50); 0px 2px 80px 0px rgba(0,0,0,0.50);
background-color: black;
} }
.fakeButtons { .fakeButtons {
@@ -83,7 +76,7 @@ h1 {
width: 10px; width: 10px;
border-radius: 50%; border-radius: 50%;
border: 1px solid #000; border: 1px solid #000;
margin: 6px; margin:6px;
background-color: #ff3b47; background-color: #ff3b47;
border-color: #9d252b; border-color: #9d252b;
display: inline-block; display: inline-block;
@@ -102,79 +95,13 @@ h1 {
} }
.fakeMenu { .fakeMenu {
display: flex; display:flex;
flex-direction: row; flex-direction: row;
width: 100%; width:100%;
box-sizing: border-box; box-sizing: border-box;
height: 25px; height: 25px;
background-color: #bbb; background-color: #bbb;
color: black; color:black;
margin: 0 auto; margin: 0 auto;
overflow: hidden; overflow: hidden;
} }
.toc-container {
position: fixed;
left: 0;
top: 0;
bottom: 0;
width: var(--toc-width);
background: white;
padding: 0;
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin;
}
.toc-title {
font-weight: bold;
margin-bottom: 5px;
font-size: 0.9em;
color: #555;
position: sticky;
transition: position 1.0s ease-in-out;
top: 0;
z-index: 1;
padding: 20px;
margin: 0;
border-bottom: 1px solid #ddd;
/* Gradient background for the title */
background-color: #f0f0f0;
}
.toc-item {
padding: 3px 8px;
margin: 0;
cursor: pointer;
font-size: 0.85em;
border-radius: 3px;
transition: background 0.2s;
}
.toc-item:hover {
background: #f0f0f0;
}
.toc-item.selected {
background: #e0e0e0;
font-weight: bold;
}
@media (max-width: 1024px) {
.toc-container {
display: none;
}
.page {
margin-left: 0;
}
}
@media (min-width: 1025px) {
.page {
margin-left: calc(var(--toc-width) + 20px);
}
}

View File

@@ -9,18 +9,13 @@
<script type="module" src="index.mjs"></script> <script type="module" src="index.mjs"></script>
</head> </head>
<body> <body>
<div class="toc-container">
<div class="toc-list"></div>
</div>
<script id="example_script"></script> <script id="example_script"></script>
<div class="page"> <div class="page">
<p> <p>
<a href="https://github.com/ArthurSonzogni/FTXUI">FTXUI</a> is a simple <a href="https://github.com/ArthurSonzogni/FTXUI">FTXUI</a> is a simple
functional C++ library for terminal user interface. <br/> functional C++ library for terminal user interface. <br/>
This showcases the: <a This showcases the: <a href="https://github.com/ArthurSonzogni/FTXUI/tree/master/examples">./example/</a> folder. <br/>
href="https://github.com/ArthurSonzogni/FTXUI/tree/master/examples">./example/</a>
folder. See <a id="source">source</a>.
</p> </p>
<div id="terminalContainer"> <div id="terminalContainer">

View File

@@ -92,69 +92,6 @@ window.Module = {
}, },
}; };
const source = document.querySelector("#source");
source.href = "https://github.com/ArthurSonzogni/FTXUI/blob/main/examples/" + example + ".cpp";
const words = example.split('/') const words = example.split('/')
words[1] = "ftxui_example_" + words[1] + ".js" words[1] = "ftxui_example_" + words[1] + ".js"
document.querySelector("#example_script").src = words.join('/'); document.querySelector("#example_script").src = words.join('/');
// Table of Contents (TOC) for quick navigation.
// Get select element
const selectEl = document.querySelector('select#selectExample');
if (!selectEl) {
console.error('select#selectExample not found');
} else {
// Get TOC container
const tocContainer = document.querySelector('.toc-container');
const tocList = tocContainer.querySelector('.toc-list');
// Group options by directory
const groupedOptions = Array.from(selectEl.options).reduce((acc, option) => {
const [dir, file] = option.text.split('/');
if (!acc[dir]) {
acc[dir] = [];
}
acc[dir].push({ option, file });
return acc;
}, {});
// Generate TOC items
for (const dir in groupedOptions) {
const dirContainer = document.createElement('div');
const dirHeader = document.createElement('div');
dirHeader.textContent = dir;
dirHeader.className = 'toc-title';
dirContainer.appendChild(dirHeader);
groupedOptions[dir].forEach(({ option, file }) => {
const tocItem = document.createElement('div');
tocItem.textContent = file;
tocItem.className = 'toc-item';
if (selectEl.options[selectEl.selectedIndex].value === option.value) {
tocItem.classList.add('selected');
}
// Click handler
tocItem.addEventListener('click', () => {
for(let i=0; i<selectEl.options.length; ++i) {
if (selectEl.options[i].value == option.value) {
selectEl.selectedIndex = i;
break;
}
}
history.pushState({}, "", "?file=" + option.value);
location.reload();
});
dirContainer.appendChild(tocItem);
});
tocList.appendChild(dirContainer);
}
}''

View File

@@ -8,7 +8,6 @@
#include <memory> // for make_shared, shared_ptr #include <memory> // for make_shared, shared_ptr
#include <utility> // for forward #include <utility> // for forward
#include <ftxui/util/warn_windows_macro.hpp>
#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
#include "ftxui/dom/elements.hpp" // for Element #include "ftxui/dom/elements.hpp" // for Element

View File

@@ -9,7 +9,6 @@
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Left, Direction::Right, Direction::Down #include <ftxui/dom/direction.hpp> // for Direction, Direction::Left, Direction::Right, Direction::Down
#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 <ftxui/util/warn_windows_macro.hpp>
#include <functional> // for function #include <functional> // for function
#include <string> // for string #include <string> // for string

View File

@@ -4,7 +4,6 @@
#ifndef FTXUI_COMPONENT_RECEIVER_HPP_ #ifndef FTXUI_COMPONENT_RECEIVER_HPP_
#define FTXUI_COMPONENT_RECEIVER_HPP_ #define FTXUI_COMPONENT_RECEIVER_HPP_
#include <ftxui/util/warn_windows_macro.hpp>
#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

View File

@@ -25,7 +25,7 @@ using Component = std::shared_ptr<ComponentBase>;
class ScreenInteractivePrivate; class ScreenInteractivePrivate;
namespace task { namespace task {
class TaskRunner; class TaskRunner;
} }
/// @brief ScreenInteractive is a `Screen` that can handle events, run a main /// @brief ScreenInteractive is a `Screen` that can handle events, run a main
@@ -168,6 +168,7 @@ class ScreenInteractive : public Screen {
Component component_; Component component_;
public: public:
class Private { class Private {
public: public:

View File

@@ -60,7 +60,7 @@ class Screen : public Image {
BarBlinking = 5, BarBlinking = 5,
Bar = 6, Bar = 6,
}; };
Shape shape = Hidden; Shape shape;
}; };
Cursor cursor() const { return cursor_; } Cursor cursor() const { return cursor_; }

View File

@@ -1,18 +0,0 @@
// 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.
#ifndef FTXUI_UTIL_WARN_WINDOWS_MACRO_H_
#define FTXUI_UTIL_WARN_WINDOWS_MACRO_H_
#ifdef min
#error \
"The macro 'min' is defined, which conflicts with the standard C++ library and FTXUI. This is often caused by including <windows.h>. To fix this, add '#define NOMINMAX' before including <windows.h>, or pass '/DNOMINMAX' as a compiler flag."
#endif
#ifdef max
#error \
"The macro 'max' is defined, which conflicts with the standard C++ library and FTXUI. This is often caused by including <windows.h>. To fix this, add '#define NOMINMAX' before including <windows.h>, or pass '/DNOMINMAX' as a compiler flag."
#endif
#endif // FTXUI_UTIL_WARN_WINDOWS_MACRO_H_

View File

@@ -23,9 +23,8 @@ bool GeneratorBool(const char*& data, size_t& size) {
std::string GeneratorString(const char*& data, size_t& size) { std::string GeneratorString(const char*& data, size_t& size) {
int index = 0; int index = 0;
while (index < size && data[index]) { while (index < size && data[index])
++index; ++index;
}
auto out = std::string(data, data + index); auto out = std::string(data, data + index);
data += index; data += index;
@@ -41,9 +40,8 @@ std::string GeneratorString(const char*& data, size_t& size) {
} }
int GeneratorInt(const char* data, size_t size) { int GeneratorInt(const char* data, size_t size) {
if (size == 0) { if (size == 0)
return 0; return 0;
}
auto out = int(data[0]); auto out = int(data[0]);
data++; data++;
size--; size--;
@@ -115,9 +113,8 @@ Components GeneratorComponents(const char*& data, size_t& size, int depth);
Component GeneratorComponent(const char*& data, size_t& size, int depth) { Component GeneratorComponent(const char*& data, size_t& size, int depth) {
depth--; depth--;
int value = GeneratorInt(data, size); int value = GeneratorInt(data, size);
if (depth <= 0) { if (depth <= 0)
return Button(GeneratorString(data, size), [] {}); return Button(GeneratorString(data, size), [] {});
}
constexpr int value_max = 19; constexpr int value_max = 19;
value = (value % value_max + value_max) % value_max; value = (value % value_max + value_max) % value_max;

View File

@@ -4,6 +4,7 @@
#include "ftxui/component/screen_interactive.hpp" #include "ftxui/component/screen_interactive.hpp"
#include <algorithm> // for copy, max, min #include <algorithm> // for copy, max, min
#include <array> // for array #include <array> // for array
#include <array>
#include <atomic> #include <atomic>
#include <chrono> // for operator-, milliseconds, operator>=, duration, common_type<>::type, time_point #include <chrono> // for operator-, milliseconds, operator>=, duration, common_type<>::type, time_point
#include <csignal> // for signal, SIGTSTP, SIGABRT, SIGWINCH, raise, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, __sighandler_t, size_t #include <csignal> // for signal, SIGTSTP, SIGABRT, SIGWINCH, raise, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, __sighandler_t, size_t
@@ -19,6 +20,7 @@
#include <string> #include <string>
#include <thread> // for thread, sleep_for #include <thread> // for thread, sleep_for
#include <tuple> // for _Swallow_assign, ignore #include <tuple> // for _Swallow_assign, ignore
#include <type_traits> // for decay_t
#include <utility> // for move, swap #include <utility> // for move, swap
#include <variant> // for visit, variant #include <variant> // for visit, variant
#include <vector> // for vector #include <vector> // for vector
@@ -27,9 +29,12 @@
#include "ftxui/component/component_base.hpp" // for ComponentBase #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/event.hpp" // for Event #include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/loop.hpp" // for Loop #include "ftxui/component/loop.hpp" // for Loop
#include "ftxui/component/task_runner.hpp" #include "ftxui/component/receiver.hpp" // for ReceiverImpl, Sender, MakeReceiver, SenderImpl, Receiver
#include "ftxui/component/terminal_input_parser.hpp" // for TerminalInputParser #include "ftxui/component/terminal_input_parser.hpp" // for TerminalInputParser
#include "ftxui/core/task_runner.hpp"
#include "ftxui/dom/node.hpp" // for Node, Render #include "ftxui/dom/node.hpp" // for Node, Render
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/pixel.hpp" // for Pixel
#include "ftxui/screen/terminal.hpp" // for Dimensions, Size #include "ftxui/screen/terminal.hpp" // for Dimensions, Size
#include "ftxui/screen/util.hpp" // for util::clamp #include "ftxui/screen/util.hpp" // for util::clamp
#include "ftxui/util/autoreset.hpp" // for AutoReset #include "ftxui/util/autoreset.hpp" // for AutoReset
@@ -579,14 +584,14 @@ void ScreenInteractive::Install() {
SetConsoleMode(stdin_handle, in_mode); SetConsoleMode(stdin_handle, in_mode);
SetConsoleMode(stdout_handle, out_mode); SetConsoleMode(stdout_handle, out_mode);
#else // POSIX (Linux & Mac) #else // POSIX (Linux & Mac)
// #if defined(__EMSCRIPTEN__) //#if defined(__EMSCRIPTEN__)
//// Reading stdin isn't blocking. //// Reading stdin isn't blocking.
// int flags = fcntl(0, F_GETFL, 0); //int flags = fcntl(0, F_GETFL, 0);
// fcntl(0, F_SETFL, flags | O_NONBLOCK); //fcntl(0, F_SETFL, flags | O_NONBLOCK);
//// Restore the terminal configuration on exit. //// Restore the terminal configuration on exit.
// on_exit_functions.emplace([flags] { fcntl(0, F_SETFL, flags); }); //on_exit_functions.emplace([flags] { fcntl(0, F_SETFL, flags); });
// #endif //#endif
for (const int signal : {SIGWINCH, SIGTSTP}) { for (const int signal : {SIGWINCH, SIGTSTP}) {
InstallSignalHandler(signal); InstallSignalHandler(signal);
} }
@@ -679,7 +684,7 @@ void ScreenInteractive::RunOnceBlocking(Component component) {
size_t executed_task = internal_->task_runner.ExecutedTasks(); size_t executed_task = internal_->task_runner.ExecutedTasks();
// Wait for at least one task to execute. // Wait for at least one task to execute.
while (executed_task == internal_->task_runner.ExecutedTasks() && while(executed_task == internal_->task_runner.ExecutedTasks() &&
!HasQuitted()) { !HasQuitted()) {
RunOnce(component); RunOnce(component);
@@ -1016,23 +1021,23 @@ void ScreenInteractive::Signal(int signal) {
void ScreenInteractive::FetchTerminalEvents() { void ScreenInteractive::FetchTerminalEvents() {
#if defined(_WIN32) #if defined(_WIN32)
auto get_input_records = [&]() -> std::vector<INPUT_RECORD> { auto get_input_records = [&] {
// Check if there is input in the console. // Check if there is input in the console.
auto console = GetStdHandle(STD_INPUT_HANDLE); auto console = GetStdHandle(STD_INPUT_HANDLE);
DWORD number_of_events = 0; DWORD number_of_events = 0;
if (!GetNumberOfConsoleInputEvents(console, &number_of_events)) { if (!GetNumberOfConsoleInputEvents(console, &number_of_events)) {
return std::vector<INPUT_RECORD>(); return;
} }
if (number_of_events <= 0) { if (number_of_events <= 0) {
// No input, return. // No input, return.
return std::vector<INPUT_RECORD>(); return;
} }
// Read the input events. // Read the input events.
std::vector<INPUT_RECORD> records(number_of_events); std::vector<INPUT_RECORD> records(number_of_events);
DWORD number_of_events_read = 0; DWORD number_of_events_read = 0;
if (!ReadConsoleInput(console, records.data(), (DWORD)records.size(), if (!ReadConsoleInput(console, records.data(), (DWORD)records.size(),
&number_of_events_read)) { &number_of_events_read)) {
return std::vector<INPUT_RECORD>(); return;
} }
records.resize(number_of_events_read); records.resize(number_of_events_read);
return records; return records;
@@ -1057,9 +1062,8 @@ void ScreenInteractive::FetchTerminalEvents() {
case KEY_EVENT: { case KEY_EVENT: {
auto key_event = r.Event.KeyEvent; auto key_event = r.Event.KeyEvent;
// ignore UP key events // ignore UP key events
if (key_event.bKeyDown == FALSE) { if (key_event.bKeyDown == FALSE)
continue; continue;
}
std::wstring wstring; std::wstring wstring;
wstring += key_event.uChar.UnicodeChar; wstring += key_event.uChar.UnicodeChar;
for (auto it : to_string(wstring)) { for (auto it : to_string(wstring)) {
@@ -1092,7 +1096,7 @@ void ScreenInteractive::FetchTerminalEvents() {
internal_->last_char_time = std::chrono::steady_clock::now(); internal_->last_char_time = std::chrono::steady_clock::now();
// Convert the chars to events. // Convert the chars to events.
for (size_t i = 0; i < l; ++i) { for(size_t i = 0; i < l; ++i) {
internal_->terminal_input_parser.Add(out[i]); internal_->terminal_input_parser.Add(out[i]);
} }
#else // POSIX (Linux & Mac) #else // POSIX (Linux & Mac)
@@ -1111,7 +1115,7 @@ void ScreenInteractive::FetchTerminalEvents() {
size_t l = read(fileno(stdin), out.data(), out.size()); size_t l = read(fileno(stdin), out.data(), out.size());
// Convert the chars to events. // Convert the chars to events.
for (size_t i = 0; i < l; ++i) { for(size_t i = 0; i < l; ++i) {
internal_->terminal_input_parser.Add(out[i]); internal_->terminal_input_parser.Add(out[i]);
} }
#endif #endif
@@ -1122,8 +1126,8 @@ void ScreenInteractive::PostAnimationTask() {
// Repeat the animation task every 15ms. This correspond to a frame rate // Repeat the animation task every 15ms. This correspond to a frame rate
// of around 66fps. // of around 66fps.
internal_->task_runner.PostDelayedTask([this] { PostAnimationTask(); }, internal_->task_runner.PostDelayedTask(
std::chrono::milliseconds(15)); [this] { PostAnimationTask(); }, std::chrono::milliseconds(15));
} }
bool ScreenInteractive::SelectionData::operator==( bool ScreenInteractive::SelectionData::operator==(

View File

@@ -28,9 +28,8 @@ namespace {
class StdCapture { class StdCapture {
public: public:
explicit StdCapture(std::string* captured) : captured_(captured) { explicit StdCapture(std::string* captured) : captured_(captured) {
if (pipe(pipefd_) != 0) { if (pipe(pipefd_) != 0)
return; return;
}
old_stdout_ = dup(fileno(stdout)); old_stdout_ = dup(fileno(stdout));
fflush(stdout); fflush(stdout);
dup2(pipefd_[1], fileno(stdout)); dup2(pipefd_[1], fileno(stdout));

View File

@@ -33,20 +33,6 @@ Decorator flexDirection(Direction direction) {
return xflex; // NOT_REACHED() return xflex; // NOT_REACHED()
} }
Direction Opposite(Direction d) {
switch (d) {
case Direction::Up:
return Direction::Down;
case Direction::Down:
return Direction::Up;
case Direction::Left:
return Direction::Right;
case Direction::Right:
return Direction::Left;
}
return d; // NOT_REACHED()
}
template <class T> template <class T>
class SliderBase : public SliderOption<T>, public ComponentBase { class SliderBase : public SliderOption<T>, public ComponentBase {
public: public:
@@ -61,15 +47,59 @@ class SliderBase : public SliderOption<T>, public ComponentBase {
flexDirection(this->direction) | reflect(gauge_box_) | gauge_color; flexDirection(this->direction) | reflect(gauge_box_) | gauge_color;
} }
void OnDirection(Direction pressed) { void OnLeft() {
if (pressed == this->direction) { switch (this->direction) {
case Direction::Right:
this->value() -= this->increment();
break;
case Direction::Left:
this->value() += this->increment(); this->value() += this->increment();
return; break;
case Direction::Up:
case Direction::Down:
break;
}
} }
if (pressed == Opposite(this->direction)) { void OnRight() {
switch (this->direction) {
case Direction::Right:
this->value() += this->increment();
break;
case Direction::Left:
this->value() -= this->increment(); this->value() -= this->increment();
return; break;
case Direction::Up:
case Direction::Down:
break;
}
}
void OnUp() {
switch (this->direction) {
case Direction::Up:
this->value() -= this->increment();
break;
case Direction::Down:
this->value() += this->increment();
break;
case Direction::Left:
case Direction::Right:
break;
}
}
void OnDown() {
switch (this->direction) {
case Direction::Down:
this->value() += this->increment();
break;
case Direction::Up:
this->value() -= this->increment();
break;
case Direction::Left:
case Direction::Right:
break;
} }
} }
@@ -80,16 +110,16 @@ class SliderBase : public SliderOption<T>, public ComponentBase {
T old_value = this->value(); T old_value = this->value();
if (event == Event::ArrowLeft || event == Event::Character('h')) { if (event == Event::ArrowLeft || event == Event::Character('h')) {
OnDirection(Direction::Left); OnLeft();
} }
if (event == Event::ArrowRight || event == Event::Character('l')) { if (event == Event::ArrowRight || event == Event::Character('l')) {
OnDirection(Direction::Right); OnRight();
} }
if (event == Event::ArrowUp || event == Event::Character('k')) { if (event == Event::ArrowUp || event == Event::Character('k')) {
OnDirection(Direction::Up); OnDown();
} }
if (event == Event::ArrowDown || event == Event::Character('j')) { if (event == Event::ArrowDown || event == Event::Character('j')) {
OnDirection(Direction::Down); OnUp();
} }
this->value() = std::max(this->min(), std::min(this->max(), this->value())); this->value() = std::max(this->min(), std::min(this->max(), this->value()));

View File

@@ -17,19 +17,16 @@ namespace ftxui {
// Test char |c| to are trivially converted into |Event::Character(c)|. // Test char |c| to are trivially converted into |Event::Character(c)|.
TEST(Event, Character) { TEST(Event, Character) {
std::vector<char> basic_char; std::vector<char> basic_char;
for (char c = 'a'; c <= 'z'; ++c) { for (char c = 'a'; c <= 'z'; ++c)
basic_char.push_back(c); basic_char.push_back(c);
} for (char c = 'A'; c <= 'Z'; ++c)
for (char c = 'A'; c <= 'Z'; ++c) {
basic_char.push_back(c); basic_char.push_back(c);
}
std::vector<Event> received_events; std::vector<Event> received_events;
auto parser = TerminalInputParser( auto parser = TerminalInputParser(
[&](Event event) { received_events.push_back(std::move(event)); }); [&](Event event) { received_events.push_back(std::move(event)); });
for (char c : basic_char) { for (char c : basic_char)
parser.Add(c); parser.Add(c);
}
for (size_t i = 0; i < basic_char.size(); ++i) { for (size_t i = 0; i < basic_char.size(); ++i) {
EXPECT_TRUE(received_events[i].is_character()); EXPECT_TRUE(received_events[i].is_character());
@@ -288,9 +285,8 @@ TEST(Event, UTF8) {
std::vector<Event> received_events; std::vector<Event> received_events;
auto parser = TerminalInputParser( auto parser = TerminalInputParser(
[&](Event event) { received_events.push_back(std::move(event)); }); [&](Event event) { received_events.push_back(std::move(event)); });
for (auto input : test.input) { for (auto input : test.input)
parser.Add(input); parser.Add(input);
}
if (test.valid) { if (test.valid) {
EXPECT_EQ(1, received_events.size()); EXPECT_EQ(1, received_events.size());
@@ -319,9 +315,8 @@ TEST(Event, Control) {
}; };
std::vector<TestCase> cases; std::vector<TestCase> cases;
for (int i = 0; i < 32; ++i) { for (int i = 0; i < 32; ++i) {
if (i == 8 || i == 13 || i == 24 || i == 26 || i == 27) { if (i == 8 || i == 13 || i == 24 || i == 26 || i == 27)
continue; continue;
}
cases.push_back({char(i), false}); cases.push_back({char(i), false});
} }
cases.push_back({char(24), false}); cases.push_back({char(24), false});
@@ -346,9 +341,8 @@ TEST(Event, Control) {
TEST(Event, Special) { TEST(Event, Special) {
auto str = [](std::string input) { auto str = [](std::string input) {
std::vector<unsigned char> output; std::vector<unsigned char> output;
for (auto it : input) { for (auto it : input)
output.push_back(it); output.push_back(it);
}
return output; return output;
}; };
@@ -357,12 +351,9 @@ TEST(Event, Special) {
Event expected; Event expected;
} kTestCase[] = { } kTestCase[] = {
// Arrow (default cursor mode) // Arrow (default cursor mode)
{str(""), Event::ArrowUp}, {str(""), Event::ArrowUp}, {str(""), Event::ArrowDown},
{str(""), Event::ArrowDown}, {str(""), Event::ArrowRight}, {str(""), Event::ArrowLeft},
{str(""), Event::ArrowRight}, {str(""), Event::Home}, {str(""), Event::End},
{str(""), Event::ArrowLeft},
{str(""), Event::Home},
{str(""), Event::End},
// Arrow (application cursor mode) // Arrow (application cursor mode)
{str("\x1BOA"), Event::ArrowUp}, {str("\x1BOA"), Event::ArrowUp},

View File

@@ -1,7 +1,7 @@
// Copyright 2024 Arthur Sonzogni. All rights reserved. // Copyright 2024 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/task_internal.hpp" #include "task.hpp"
namespace ftxui::task { namespace ftxui::task {
bool PendingTask::operator<(const PendingTask& other) const { bool PendingTask::operator<(const PendingTask& other) const {
@@ -17,3 +17,4 @@ bool PendingTask::operator<(const PendingTask& other) const {
return time.value() > other.time.value(); return time.value() > other.time.value();
} }
} // namespace ftxui::task } // namespace ftxui::task

View File

@@ -21,7 +21,8 @@ struct PendingTask {
// Delayed task with a duration // Delayed task with a duration
PendingTask(Task t, std::chrono::steady_clock::duration duration) PendingTask(Task t, std::chrono::steady_clock::duration duration)
: task(std::move(t)), time(std::chrono::steady_clock::now() + duration) {} : task(std::move(t)),
time(std::chrono::steady_clock::now() + duration) {}
/// The task to be executed. /// The task to be executed.
Task task; Task task;
@@ -37,4 +38,5 @@ struct PendingTask {
} // namespace ftxui::task } // namespace ftxui::task
#endif // TASK_HPP_ #endif // TASK_HPP_

View File

@@ -1,7 +1,7 @@
// Copyright 2024 Arthur Sonzogni. All rights reserved. // Copyright 2024 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/task_queue.hpp" #include "task_queue.hpp"
namespace ftxui::task { namespace ftxui::task {
@@ -51,3 +51,4 @@ auto TaskQueue::Get() -> MaybeTask {
} }
} // namespace ftxui::task } // namespace ftxui::task

View File

@@ -7,7 +7,7 @@
#include <queue> #include <queue>
#include <variant> #include <variant>
#include "ftxui/component/task_internal.hpp" // for PendingTask, Task #include "task.hpp"
namespace ftxui::task { namespace ftxui::task {
@@ -25,7 +25,9 @@ struct TaskQueue {
std::variant<Task, std::chrono::steady_clock::duration, std::monostate>; std::variant<Task, std::chrono::steady_clock::duration, std::monostate>;
auto Get() -> MaybeTask; auto Get() -> MaybeTask;
bool HasImmediateTasks() const { return !immediate_tasks_.empty(); } bool HasImmediateTasks() const {
return !immediate_tasks_.empty();
}
private: private:
std::queue<PendingTask> immediate_tasks_; std::queue<PendingTask> immediate_tasks_;
@@ -34,4 +36,5 @@ struct TaskQueue {
} // namespace ftxui::task } // namespace ftxui::task
#endif #endif

View File

@@ -1,7 +1,7 @@
// Copyright 2024 Arthur Sonzogni. All rights reserved. // Copyright 2024 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/task_runner.hpp" #include "task_runner.hpp"
#include <cassert> #include <cassert>
#include <thread> #include <thread>
@@ -20,6 +20,7 @@ TaskRunner::~TaskRunner() {
current_task_runner = previous_task_runner_; current_task_runner = previous_task_runner_;
} }
// static // static
auto TaskRunner::Current() -> TaskRunner* { auto TaskRunner::Current() -> TaskRunner* {
assert(current_task_runner); assert(current_task_runner);
@@ -73,3 +74,4 @@ auto TaskRunner::Run() -> void {
} }
} // namespace ftxui::task } // namespace ftxui::task

View File

@@ -4,8 +4,8 @@
#ifndef TASK_RUNNER_HPP #ifndef TASK_RUNNER_HPP
#define TASK_RUNNER_HPP #define TASK_RUNNER_HPP
#include "ftxui/component/task_internal.hpp" #include "task.hpp"
#include "ftxui/component/task_queue.hpp" #include "task_queue.hpp"
namespace ftxui::task { namespace ftxui::task {
@@ -21,8 +21,8 @@ class TaskRunner {
auto PostTask(Task task) -> void; auto PostTask(Task task) -> void;
/// Schedules a task to be executed after a certain duration. /// Schedules a task to be executed after a certain duration.
auto PostDelayedTask(Task task, std::chrono::steady_clock::duration duration) auto PostDelayedTask(Task task,
-> void; std::chrono::steady_clock::duration duration) -> void;
/// Runs the tasks in the queue, return the delay until the next delayed task /// Runs the tasks in the queue, return the delay until the next delayed task
/// can be executed. /// can be executed.
@@ -31,7 +31,9 @@ class TaskRunner {
// Runs the tasks in the queue, blocking until all tasks are executed. // Runs the tasks in the queue, blocking until all tasks are executed.
auto Run() -> void; auto Run() -> void;
bool HasImmediateTasks() const { return queue_.HasImmediateTasks(); } bool HasImmediateTasks() const {
return queue_.HasImmediateTasks();
}
size_t ExecutedTasks() const { return executed_tasks_; } size_t ExecutedTasks() const { return executed_tasks_; }
@@ -43,4 +45,5 @@ class TaskRunner {
} // namespace ftxui::task } // namespace ftxui::task
#endif // TASK_RUNNER_HPP #endif // TASK_RUNNER_HPP

View File

@@ -4,12 +4,11 @@
// Copyright 2024 Arthur Sonzogni. All rights reserved. // Copyright 2024 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/task_internal.hpp" #include "task.hpp"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <thread> // for sleep_for #include "task_runner.hpp"
#include "ftxui/component/task_runner.hpp"
namespace ftxui::task { namespace ftxui::task {
@@ -92,3 +91,5 @@ TEST(TaskTest, RunDelayedTask) {
} }
} // namespace ftxui::task } // namespace ftxui::task

View File

@@ -81,7 +81,7 @@ class Size : public Node {
} // namespace } // namespace
/// @brief Apply a constraint on the size of an element. /// @brief Apply a constraint on the size of an element.
/// @param direction Whether the WIDTH or the HEIGHT of the element must be /// @param direction Whether the WIDTH of the HEIGHT of the element must be
/// constrained. /// constrained.
/// @param constraint The type of constaint. /// @param constraint The type of constaint.
/// @param value The value. /// @param value The value.

View File

@@ -47,9 +47,8 @@ namespace {
#if defined(_WIN32) #if defined(_WIN32)
void WindowsEmulateVT100Terminal() { void WindowsEmulateVT100Terminal() {
static bool done = false; static bool done = false;
if (done) { if (done)
return; return;
}
done = true; done = true;
// Enable VT processing on stdout and stdin // Enable VT processing on stdout and stdin

View File

@@ -1284,9 +1284,8 @@ bool IsCombining(uint32_t ucs) {
} }
bool IsFullWidth(uint32_t ucs) { bool IsFullWidth(uint32_t ucs) {
if (ucs < 0x0300) { // Quick path: // NOLINT if (ucs < 0x0300) // Quick path: // NOLINT
return false; return false;
}
return Bisearch(ucs, g_full_width_characters); return Bisearch(ucs, g_full_width_characters);
} }

View File

@@ -1,4 +1,4 @@
// Copyright 2025 Arthur Sonzogni. All rights reserved. // Copyright 2024 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.