Compare commits

...

23 Commits
main ... v4.1.1

Author SHA1 Message Date
ArthurSonzogni
513ec6c5e1
Increase version to 4.1.1 2023-05-29 14:07:29 +02:00
ArthurSonzogni
234b3c8a5d
Resolve compile issues in component_fuzzer 2023-05-27 20:54:12 +02:00
SpaceIm
6ce88c25e5
CMake: use compile_features to set min C++ standard (#647) 2023-05-27 20:50:35 +02:00
ArthurSonzogni
f74c5322bd
Revert "Update float style in animation.cpp (#607)"
This reverts commit 7b08dae6d0.
2023-05-25 19:52:51 +02:00
ArthurSonzogni
1e86587b68
Revert "Feature resizable spilt with custom separator (#583)"
This reverts commit eb313e0f2d.
2023-05-25 19:47:33 +02:00
SpaceIm
41c3d4dd52
add FTXUI_DEV_WARNINGS option in CMakeLists (#648)
This option allows to enable warnings as errors, and add more compiler warnings
2023-05-25 19:38:45 +02:00
Robin Lindén
2fb0b77f02
Fix MSVC possible loss of data warning (#645) 2023-05-25 19:37:58 +02:00
Henrik Gaßmann
e2aff66b98
build: Check package version in find_package (#643)
Modify the `ftxui-config.cmake.in` file so that it includes
`ftxui-targets.cmake`. This causes cmake to check the version is
compatible with what the user requested.

This patch also includes additional minor changes.

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2023-05-25 19:37:55 +02:00
ArthurSonzogni
22ebc823c9
Set CHANGELOG to v4.1.0 2023-05-19 11:18:01 +02:00
ArthurSonzogni
49f0277103
Fix errors due to missing commit in the support branch. 2023-05-19 11:18:01 +02:00
Henner Zeller
ac10603fdb
Fix types used in Hash() function used in tests. (#640)
Using `auto` seems to create a different output on different platforms.
Notably it was observed that aarch64 compiles return a different
value.

Fix the type of the characters iterated through the string.

Fixes #639

Signed-off-by: Henner Zeller <h.zeller@acm.org>
2023-05-18 11:23:22 +02:00
Henner Zeller
a3f51ecf22
Add pkg-config file. (#642)
This file describes the include and lib location and makes
it possible to use the library in build systems other than
CMake, e.g. in basic Makefiles.

Signed-off-by: Henner Zeller <h.zeller@acm.org>
2023-05-18 11:23:22 +02:00
Arthur Sonzogni
78897ef1f4
Support arrow keys in application mode. (#627)
Depending on the Cursor Key Mode (DECCKM), the terminal sends different
escape sequences:

Key     Normal    Application
-----   --------  -----------
Up      ESC [ A   ESC O A
Down    ESC [ B   ESC O B
Right   ESC [ C   ESC O C
Left    ESC [ D   ESC O D
Home    ESC [ H   ESC O H
End     ESC [ F   ESC O F

I decided not to update the current mode to fit what FTXUI parse, but
instead support parsing both. We convert the sequences from the
"application mode" into the "normal mode".

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/626
Fixed:https://github.com/ArthurSonzogni/FTXUI/issues/626
2023-05-18 11:23:22 +02:00
Arthur Sonzogni
debcbc668c
Check graph area is positive. (#625)
Bug:https://github.com/ArthurSonzogni/FTXUI/issues/624
2023-05-18 11:23:22 +02:00
Jason Turner
1a7a550bfe
Use globally set CMAKE_CXX_STANDARD if it is set (#620) 2023-05-18 11:23:22 +02:00
Arthur Sonzogni
4ce8f6d250
Fix useless new line when using the alternative screen. (#610)
Fix:https://github.com/ArthurSonzogni/FTXUI/issues/609
2023-05-18 11:23:22 +02:00
Arthur Sonzogni
7b08dae6d0
Update float style in animation.cpp (#607)
Partially taken out of:
https://github.com/ArthurSonzogni/FTXUI/pull/600

Co-authored-by: LostInCompilation <12819635+LostInCompilation@users.noreply.github.com>
2023-05-18 11:23:22 +02:00
dev-null-undefined
521d1125d9
Feature: Added nix build support (#603)
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2023-05-18 11:23:22 +02:00
Vinicius Moura Longaray
0903ad1608
Feature: LinearGradient color decorator. (#592)
Based on the existing color decorators, create new ones to apply a gradient effect on the DOM.

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2023-05-18 11:23:22 +02:00
Arthur Sonzogni
399549d180
Add colored border. (#595)
This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/564
2023-05-18 11:23:21 +02:00
Arthur Sonzogni
7bdca3ee68
Feature: Add the dashed style. (#594) 2023-05-18 11:23:21 +02:00
Arthur Sonzogni
eb313e0f2d
Feature resizable spilt with custom separator (#583)
* Feature: ResizableSplit with custom separator

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

Co-authored-by: Pin Loon Lee <pinloon_0428@hotmail.com>
2023-05-18 11:23:21 +02:00
Evgeny Gorodetskiy
d3ee655a90
Fix Apple Clang 14 build errors (issue #588) (#589) 2023-05-18 11:23:21 +02:00
42 changed files with 1198 additions and 204 deletions

View File

@ -66,7 +66,8 @@ jobs:
-DFTXUI_BUILD_EXAMPLES:BOOL=ON -DFTXUI_BUILD_EXAMPLES:BOOL=ON
-DFTXUI_BUILD_TESTS:BOOL=ON -DFTXUI_BUILD_TESTS:BOOL=ON
-DFTXUI_BUILD_TESTS_FUZZER:BOOL=OFF -DFTXUI_BUILD_TESTS_FUZZER:BOOL=OFF
-DFTXUI_ENABLE_INSTALL:BOOL=ON ; -DFTXUI_ENABLE_INSTALL:BOOL=ON
-DFTXUI_DEV_WARNINGS:BOOL=ON ;
- name: "Build" - name: "Build"
run: > run: >
@ -160,7 +161,8 @@ jobs:
-DFTXUI_BUILD_EXAMPLES=OFF -DFTXUI_BUILD_EXAMPLES=OFF
-DFTXUI_BUILD_TESTS=OFF -DFTXUI_BUILD_TESTS=OFF
-DFTXUI_BUILD_TESTS_FUZZER=OFF -DFTXUI_BUILD_TESTS_FUZZER=OFF
-DFTXUI_ENABLE_INSTALL=ON; -DFTXUI_ENABLE_INSTALL=ON
-DFTXUI_DEV_WARNINGS=ON ;
cmake --build . --target package; cmake --build . --target package;
- uses: shogo82148/actions-upload-release-asset@v1 - uses: shogo82148/actions-upload-release-asset@v1
with: with:
@ -196,7 +198,8 @@ jobs:
-DFTXUI_BUILD_EXAMPLES=ON -DFTXUI_BUILD_EXAMPLES=ON
-DFTXUI_BUILD_TESTS=OFF -DFTXUI_BUILD_TESTS=OFF
-DFTXUI_BUILD_TESTS_FUZZER=OFF -DFTXUI_BUILD_TESTS_FUZZER=OFF
-DFTXUI_ENABLE_INSTALL=OFF; -DFTXUI_ENABLE_INSTALL=OFF
-DFTXUI_DEV_WARNINGS=ON ;
cmake --build . --target doc; cmake --build . --target doc;
cmake --build . ; cmake --build . ;
rsync -amv rsync -amv

View File

@ -1,8 +1,33 @@
Changelog Changelog
========= =========
current (development) 4.1.1
--------------------- -----
### Component
- Revert Feature: Support `ResizableSplit` with customizable separator.
### Build
- Check version compatibility when using cmake find_package()
4.1.0 (abandonned)
-----
### Component
- Feature: Support `ResizableSplit` with customizable separator.
- Fix: Support arrow keys in application mode
- Fix: Remove useless new line when using an alternative screen.
### Dom
- Feature: Add the dashed style for border and separator.
- Feature: Add colored borders.
- Feature: Add `LinearGradient`!
- Improvement: Color::Interpolate() uses gamma correction.
- Fix: Check the `graph` area is positive.
### Build/Install
- Use globally set CMAKE_CXX_STANDARD if it is set.
- Expose the pkg-config file.
4.0.0 4.0.0
----- -----

View File

@ -2,7 +2,8 @@ cmake_minimum_required(VERSION 3.11)
project(ftxui project(ftxui
LANGUAGES CXX LANGUAGES CXX
VERSION 4.0.0 VERSION 4.1.1
DESCRIPTION "C++ Functional Terminal User Interface."
) )
option(FTXUI_BUILD_DOCS "Set to ON to build docs" ON) option(FTXUI_BUILD_DOCS "Set to ON to build docs" ON)
@ -12,6 +13,7 @@ option(FTXUI_BUILD_TESTS_FUZZER "Set to ON to enable fuzzing" OFF)
option(FTXUI_ENABLE_INSTALL "Generate the install target" ON) option(FTXUI_ENABLE_INSTALL "Generate the install target" ON)
option(FTXUI_CLANG_TIDY "Execute clang-tidy" OFF) option(FTXUI_CLANG_TIDY "Execute clang-tidy" OFF)
option(FTXUI_ENABLE_COVERAGE "Execute code coverage" OFF) option(FTXUI_ENABLE_COVERAGE "Execute code coverage" OFF)
option(FTXUI_DEV_WARNINGS "Enable more compiler warnings and warnings as errors" OFF)
set(FTXUI_MICROSOFT_TERMINAL_FALLBACK_HELP_TEXT "On windows, assume the \ set(FTXUI_MICROSOFT_TERMINAL_FALLBACK_HELP_TEXT "On windows, assume the \
terminal used will be one of Microsoft and use a set of reasonnable fallback \ terminal used will be one of Microsoft and use a set of reasonnable fallback \
@ -71,6 +73,7 @@ add_library(dom
src/ftxui/dom/gridbox.cpp src/ftxui/dom/gridbox.cpp
src/ftxui/dom/hbox.cpp src/ftxui/dom/hbox.cpp
src/ftxui/dom/inverted.cpp src/ftxui/dom/inverted.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

View File

@ -2,3 +2,5 @@
include(CMakeFindDependencyMacro) include(CMakeFindDependencyMacro)
find_dependency(Threads) find_dependency(Threads)
include("${CMAKE_CURRENT_LIST_DIR}/ftxui-targets.cmake")

View File

@ -4,23 +4,17 @@ endif()
set(CMAKE_C_COMPILER clang) set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++) set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_CXX_STANDARD 17)
function(fuzz name) function(fuzz name)
add_executable(${name} add_executable(${name}
src/ftxui/component/${name}.cpp src/ftxui/component/${name}.cpp
) )
target_include_directories(${name} target_include_directories(${name} PRIVATE src)
PRIVATE src target_link_libraries(${name} PRIVATE component)
) target_compile_options(${name} PRIVATE -fsanitize=fuzzer,address)
target_link_libraries(${name} target_link_libraries(${name} PRIVATE -fsanitize=fuzzer,address)
PRIVATE component target_compile_features(${name} PRIVATE cxx_std_17)
)
target_compile_options(${name}
PRIVATE -fsanitize=fuzzer,address
)
target_link_libraries(${name}
PRIVATE -fsanitize=fuzzer,address
)
endfunction(fuzz) endfunction(fuzz)
fuzz(terminal_input_parser_test_fuzzer) fuzz(terminal_input_parser_test_fuzzer)

View File

@ -3,31 +3,60 @@ if(NOT FTXUI_ENABLE_INSTALL)
endif() endif()
include(GNUInstallDirs) include(GNUInstallDirs)
install(TARGETS screen dom component
EXPORT ftxui-export
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(DIRECTORY include/ftxui DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
configure_package_config_file(ftxui-config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/ftxui-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/ftxui/cmake
PATH_VARS CMAKE_INSTALL_INCLUDEDIR
)
write_basic_package_version_file(
ftxui-config-version.cmake
VERSION ${PACKAGE_VERSION}
COMPATIBILITY AnyNewerVersion
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ftxui-config.cmake # ------------------------------------------------------------------------------
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ftxui # Install the library and its public headers into the standard subdirectories
) # ------------------------------------------------------------------------------
install(EXPORT ftxui-export install(
FILE ftxui-config-version.cmake TARGETS screen dom component
EXPORT ftxui-targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(
DIRECTORY include/ftxui
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
# ------------------------------------------------------------------------------
# Install the exported cmake targets for use in other CMake projects.
# ------------------------------------------------------------------------------
install(
EXPORT ftxui-targets
NAMESPACE ftxui:: NAMESPACE ftxui::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ftxui DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ftxui
) )
# ------------------------------------------------------------------------------
# Create and install the ftuxi-config.cmake and ftuxi-config-version.cmake files
# needed to support users of find_package()
# ------------------------------------------------------------------------------
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/ftxui-config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/cmake/ftxui-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/ftxui/cmake
PATH_VARS CMAKE_INSTALL_INCLUDEDIR
)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/cmake/ftxui-config-version.cmake
VERSION ${PACKAGE_VERSION}
COMPATIBILITY SameMajorVersion
)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/cmake/ftxui-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/cmake/ftxui-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ftxui
)
# ------------------------------------------------------------------------------
# Create and install pkg-config file for easy use of library in build systems
# other than CMake:
# ------------------------------------------------------------------------------
configure_file(ftxui.pc.in ${CMAKE_CURRENT_BINARY_DIR}/ftxui.pc @ONLY)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/ftxui.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

View File

@ -43,11 +43,7 @@ function(ftxui_set_options library)
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
) )
# C++17 is used. We require fold expression at least. target_compile_features(${library} PUBLIC cxx_std_17)
set_target_properties(${library} PROPERTIES
CXX_STANDARD 17
CXX_EXTENSIONS OFF
)
# Force Microsoft Visual Studio to decode sources files in UTF-8. This applies # 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.
@ -58,8 +54,10 @@ function(ftxui_set_options library)
# Add as many warning as possible: # Add as many warning as possible:
if (WIN32) if (WIN32)
if (MSVC) if (MSVC)
target_compile_options(${library} PRIVATE "/W3") if(FTXUI_DEV_WARNINGS)
target_compile_options(${library} PRIVATE "/WX") target_compile_options(${library} PRIVATE "/W3")
target_compile_options(${library} PRIVATE "/WX")
endif()
target_compile_options(${library} PRIVATE "/wd4244") target_compile_options(${library} PRIVATE "/wd4244")
target_compile_options(${library} PRIVATE "/wd4267") target_compile_options(${library} PRIVATE "/wd4267")
target_compile_options(${library} PRIVATE "/D_CRT_SECURE_NO_WARNINGS") target_compile_options(${library} PRIVATE "/D_CRT_SECURE_NO_WARNINGS")
@ -67,13 +65,21 @@ function(ftxui_set_options library)
# Force Win32 to UNICODE # Force Win32 to UNICODE
target_compile_definitions(${library} PRIVATE UNICODE _UNICODE) target_compile_definitions(${library} PRIVATE UNICODE _UNICODE)
else() else()
target_compile_options(${library} PRIVATE "-Wall") if(FTXUI_DEV_WARNINGS)
target_compile_options(${library} PRIVATE "-Wextra") target_compile_options(${library} PRIVATE "-Wall")
target_compile_options(${library} PRIVATE "-pedantic") target_compile_options(${library} PRIVATE "-Werror")
target_compile_options(${library} PRIVATE "-Werror") target_compile_options(${library} PRIVATE "-Wextra")
target_compile_options(${library} PRIVATE "-Wmissing-declarations")
target_compile_options(${library} PRIVATE "-Wdeprecated") target_compile_options(${library} PRIVATE "-Wcast-align")
target_compile_options(${library} PRIVATE "-Wshadow") target_compile_options(${library} PRIVATE "-Wdeprecated")
target_compile_options(${library} PRIVATE "-Wmissing-declarations")
target_compile_options(${library} PRIVATE "-Wnon-virtual-dtor")
target_compile_options(${library} PRIVATE "-Wnull-dereference")
target_compile_options(${library} PRIVATE "-Woverloaded-virtual")
target_compile_options(${library} PRIVATE "-Wpedantic")
target_compile_options(${library} PRIVATE "-Wshadow")
target_compile_options(${library} PRIVATE "-Wunused")
endif()
endif() endif()
if (FTXUI_MICROSOFT_TERMINAL_FALLBACK) if (FTXUI_MICROSOFT_TERMINAL_FALLBACK)

View File

@ -37,6 +37,7 @@ add_executable(ftxui-tests
src/ftxui/dom/gauge_test.cpp src/ftxui/dom/gauge_test.cpp
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/linear_gradient_test.cpp
src/ftxui/dom/scroll_indicator_test.cpp src/ftxui/dom/scroll_indicator_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
@ -56,8 +57,12 @@ target_link_libraries(ftxui-tests
target_include_directories(ftxui-tests target_include_directories(ftxui-tests
PRIVATE src PRIVATE src
) )
ftxui_set_options(ftxui-tests) target_compile_features(ftxui-tests PRIVATE cxx_std_20)
target_compile_features(ftxui-tests PUBLIC cxx_std_20)
if (FTXUI_MICROSOFT_TERMINAL_FALLBACK)
target_compile_definitions(ftxui-tests
PRIVATE "FTXUI_MICROSOFT_TERMINAL_FALLBACK")
endif()
include(GoogleTest) include(GoogleTest)
gtest_discover_tests(ftxui-tests gtest_discover_tests(ftxui-tests

View File

@ -444,6 +444,36 @@ ftxui::Color::HSV(uint8_t hue, uint8_t saturation, uint8_t value);
<script id="asciicast-xwzzghmqcqzIuyLwCpQFEqbEu" src="https://asciinema.org/a/xwzzghmqcqzIuyLwCpQFEqbEu.js" async></script> <script id="asciicast-xwzzghmqcqzIuyLwCpQFEqbEu" src="https://asciinema.org/a/xwzzghmqcqzIuyLwCpQFEqbEu.js" async></script>
@endhtmlonly @endhtmlonly
## LinearGradient #{#dom-linear-gradient}
FTXUI supports linear gradient. Either on the foreground or the background.
```cpp
Decorator color(const LinearGradient&);
Decorator bgcolor(const LinearGradient&);
```
A `ftxui::LinearGradient` is defined by an angle in degree, and a list of color
stops.
```cpp
auto gradient = LinearGradient()
.Angle(45)
.AddStop(0.0, Color::Red)
.AddStop(0.5, Color::Green)
.AddStop(1.0, Color::Blue);
```
You can also use simplified constructors:
```cpp
LinearGradient(Color::Red, Color::Blue);
```
```cpp
LinearGradient(45, Color::Red, Color::Blue);
```
See [demo](https://arthursonzogni.github.io/FTXUI/examples/?file=component/linear_gradient_gallery).
## 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`, `dim`, `underlined`, `inverted`, `blink`.
@ -458,6 +488,8 @@ Element strikethrough(Element);
Element blink(Element); Element blink(Element);
Decorator color(Color); Decorator color(Color);
Decorator bgcolor(Color); Decorator bgcolor(Color);
Decorator colorgrad(LinearGradient);
Decorator bgcolorgrad(LinearGradient);
``` ```
[Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2style_gallery_8cpp-example.html) [Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2style_gallery_8cpp-example.html)
@ -562,8 +594,7 @@ Simple [example](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/do
Complex [example](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/component/canvas_animated.cpp): Complex [example](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/component/canvas_animated.cpp):
![ezgif com-gif-maker (3)](https://user-images.githubusercontent.com/4759106/147250538-783a8246-98e0-4a25-b032-3bd3710549d1.gif) ![ezgif com-gif-maker (3)](https://user-images.githubusercontent.com/4759106/147250538-783a8246-98e0-4a25-b032-3bd3710549d1.gif)
# component {#module-component} # component {#module-component}
The `ftxui::component` module defines the logic that produces interactive The `ftxui::component` module defines the logic that produces interactive

View File

@ -8,9 +8,7 @@ function(example name)
target_link_libraries(ftxui_example_${name} PUBLIC ${DIRECTORY_LIB}) target_link_libraries(ftxui_example_${name} PUBLIC ${DIRECTORY_LIB})
file(RELATIVE_PATH dir ${EXAMPLES_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) file(RELATIVE_PATH dir ${EXAMPLES_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
set_property(GLOBAL APPEND PROPERTY FTXUI::EXAMPLES ${dir}/${name}) set_property(GLOBAL APPEND PROPERTY FTXUI::EXAMPLES ${dir}/${name})
set_target_properties(ftxui_example_${name} PROPERTIES target_compile_features(ftxui_example_${name} PRIVATE cxx_std_17)
CXX_STANDARD 20
)
endfunction(example) endfunction(example)
add_subdirectory(component) add_subdirectory(component)

View File

@ -17,6 +17,7 @@ example(focus_cursor)
example(gallery) example(gallery)
example(homescreen) example(homescreen)
example(input) example(input)
example(linear_gradient_gallery)
example(maybe) example(maybe)
example(menu) example(menu)
example(menu2) example(menu2)

View File

@ -0,0 +1,56 @@
#include <ftxui/component/component_base.hpp> // for ComponentBase, Component
#include <ftxui/dom/elements.hpp> // for operator|, Element, flex, bgcolor, text, vbox, center
#include <ftxui/dom/linear_gradient.hpp> // for LinearGradient
#include <ftxui/screen/color.hpp> // for Color, Color::Blue, Color::Red
#include <memory> // for __shared_ptr_access, shared_ptr
#include <string> // for allocator, operator+, char_traits, string, to_string
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Slider, Renderer, Vertical
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto screen = ScreenInteractive::Fullscreen();
int angle = 180.f;
float start = 0.f;
float end = 1.f;
std::string slider_angle_text;
std::string slider_start_text;
std::string slider_end_text;
auto slider_angle = Slider(&slider_angle_text, &angle, 0, 360);
auto slider_start = Slider(&slider_start_text, &start, 0.f, 1.f);
auto slider_end = Slider(&slider_end_text, &end, 0.f, 1.f);
auto layout = Container::Vertical({
slider_angle,
slider_start,
slider_end,
});
auto renderer = Renderer(layout, [&] {
slider_angle_text = "angle = " + std::to_string(angle) + "°";
slider_start_text = "start = " + std::to_string(int(start * 100)) + "%";
slider_end_text = "end = " + std::to_string(int(end * 100)) + "%";
auto background = text("Gradient") | center |
bgcolor(LinearGradient()
.Angle(angle)
.Stop(Color::Blue, start)
.Stop(Color::Red, end));
return vbox({
background | flex,
layout->Render(),
}) |
flex;
});
screen.Loop(renderer);
}
// 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.

View File

@ -1,19 +1,21 @@
set(DIRECTORY_LIB dom) set(DIRECTORY_LIB dom)
example(border) example(border)
example(border_colored)
example(border_style) example(border_style)
example(canvas)
example(color_gallery) example(color_gallery)
example(color_info_palette256) example(color_info_palette256)
example(color_truecolor_HSV) example(color_truecolor_HSV)
example(color_truecolor_RGB) example(color_truecolor_RGB)
example(dbox) example(dbox)
example(canvas)
example(gauge) example(gauge)
example(gauge_direction) example(gauge_direction)
example(graph) example(graph)
example(gridbox) example(gridbox)
example(hflow) example(hflow)
example(html_like) example(html_like)
example(linear_gradient)
example(package_manager) example(package_manager)
example(paragraph) example(paragraph)
example(separator) example(separator)

View File

@ -0,0 +1,40 @@
#include <ftxui/dom/elements.hpp> // for operator|, text, Element, Fit, borderDouble, borderHeavy, borderLight, borderRounded, vbox
#include <ftxui/screen/screen.hpp> // for Screen
#include <iostream> // for endl, cout, ostream
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto make_boxed = [] {
return vbox({
text("borderLight") | borderStyled(LIGHT, Color::Red),
text("borderDashed") | borderStyled(DASHED, Color::Green),
text("borderHeavy") | borderStyled(HEAVY, Color::Blue),
text("borderDouble") | borderStyled(DOUBLE, Color::Yellow),
text("borderRounded") | borderStyled(ROUNDED, Color::Cyan),
});
};
auto document = hbox({
make_boxed(),
separator() | color(Color::Red),
make_boxed(),
separator() | color(Color::Red),
make_boxed(),
}) |
borderStyled(ROUNDED, Color::Red);
auto screen =
Screen::Create(Dimension::Fit(document), Dimension::Fit(document));
Render(screen, document);
screen.Print();
std::cout << std::endl;
}
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

View File

@ -11,6 +11,7 @@ int main(int argc, const char* argv[]) {
auto document = vbox({ auto document = vbox({
text("borderLight") | borderLight, text("borderLight") | borderLight,
text("borderDashed") | borderDashed,
text("borderHeavy") | borderHeavy, text("borderHeavy") | borderHeavy,
text("borderDouble") | borderDouble, text("borderDouble") | borderDouble,
text("borderRounded") | borderRounded, text("borderRounded") | borderRounded,

View File

@ -0,0 +1,26 @@
#include <ftxui/dom/elements.hpp> // for bgcolor, operator|, operator|=, text, center, Element
#include <ftxui/dom/linear_gradient.hpp> // for LinearGradient::Stop, LinearGradient
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator, shared_ptr
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for Color, Color::DeepPink1, Color::DeepSkyBlue1, Color::Yellow, ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto document = text("gradient") | center;
document |= bgcolor(LinearGradient()
.Angle(45)
.Stop(Color::DeepPink1)
.Stop(Color::DeepSkyBlue1));
auto screen = Screen::Create(Dimension::Full(), Dimension::Full());
Render(screen, document);
screen.Print();
return 0;
}
// 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.

View File

@ -16,6 +16,12 @@ int main(int argc, const char* argv[]) {
hbox(text("left"), separatorLight(), text("right")), hbox(text("left"), separatorLight(), text("right")),
}) | borderLight, }) | borderLight,
vbox({
text("separatorDashed"),
separatorDashed(),
hbox(text("left"), separatorDashed(), text("right")),
}) | borderDashed,
vbox({ vbox({
text("separatorHeavy"), text("separatorHeavy"),
separatorHeavy(), separatorHeavy(),

View File

@ -1,59 +1,60 @@
#include <ftxui/screen/screen.hpp> // for Full, Screen #include <ftxui/dom/linear_gradient.hpp> // for LinearGradient
#include <memory> // for allocator #include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include "ftxui/dom/elements.hpp" // for text, bgcolor, color, vbox, Fit, filler, hbox #include "ftxui/dom/elements.hpp" // for text, bgcolor, color, vbox, filler, Fit, hbox
#include "ftxui/dom/node.hpp" // for Render #include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui #include "ftxui/screen/color.hpp" // for Color, operator""_rgb, Color::Black, Color::Blue, Color::BlueLight, Color::Cyan, Color::CyanLight, Color::DeepSkyBlue4, Color::Default, Color::GrayDark, Color::GrayLight, Color::Green, Color::GreenLight, Color::Magenta, Color::MagentaLight, Color::Red, Color::RedLight, Color::SkyBlue1, Color::White, Color::Yellow, Color::YellowLight, ftxui
#include "ftxui/screen/color.hpp" // for Color, Color::Black, Color::Blue, Color::BlueLight, Color::Cyan, Color::CyanLight, Color::Default, Color::GrayDark, Color::GrayLight, Color::Green, Color::GreenLight, Color::Magenta, Color::MagentaLight, Color::Red, Color::RedLight, Color::White, Color::Yellow, Color::YellowLight
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
using namespace ftxui; using namespace ftxui;
// clang-format off auto document = hbox({
auto document = vbox({
hbox( color(Color::Default, text("Default")),
vbox( color(Color::Black, text("Black")),
color(Color::Default, text("Default")), color(Color::GrayDark, text("GrayDark")),
color(Color::Black, text("Black")), color(Color::GrayLight, text("GrayLight")),
color(Color::GrayDark, text("GrayDark")), color(Color::White, text("White")),
color(Color::GrayLight, text("GrayLight")), color(Color::Blue, text("Blue")),
color(Color::White, text("White")), color(Color::BlueLight, text("BlueLight")),
color(Color::Blue, text("Blue")), color(Color::Cyan, text("Cyan")),
color(Color::BlueLight, text("BlueLight")), color(Color::CyanLight, text("CyanLight")),
color(Color::Cyan, text("Cyan")), color(Color::Green, text("Green")),
color(Color::CyanLight, text("CyanLight")), color(Color::GreenLight, text("GreenLight")),
color(Color::Green, text("Green")), color(Color::Magenta, text("Magenta")),
color(Color::GreenLight, text("GreenLight")), color(Color::MagentaLight, text("MagentaLight")),
color(Color::Magenta, text("Magenta")), color(Color::Red, text("Red")),
color(Color::MagentaLight, text("MagentaLight")), color(Color::RedLight, text("RedLight")),
color(Color::Red, text("Red")), color(Color::Yellow, text("Yellow")),
color(Color::RedLight, text("RedLight")), color(Color::YellowLight, text("YellowLight")),
color(Color::Yellow, text("Yellow")), color(0x66ff66_rgb, text("Phosphor")),
color(Color::YellowLight, text("YellowLight")), color(LinearGradient(Color::SkyBlue1, Color::DeepSkyBlue4),
color(0x66ff66_rgb, text("Phosphor")) text("Skyblue to DeepSkyBlue")),
), }),
vbox( vbox({
bgcolor(Color::Default, text("Default")), bgcolor(Color::Default, text("Default")),
bgcolor(Color::Black, text("Black")), bgcolor(Color::Black, text("Black")),
bgcolor(Color::GrayDark, text("GrayDark")), bgcolor(Color::GrayDark, text("GrayDark")),
bgcolor(Color::GrayLight, text("GrayLight")), bgcolor(Color::GrayLight, text("GrayLight")),
bgcolor(Color::White, text("White")), bgcolor(Color::White, text("White")),
bgcolor(Color::Blue, text("Blue")), bgcolor(Color::Blue, text("Blue")),
bgcolor(Color::BlueLight, text("BlueLight")), bgcolor(Color::BlueLight, text("BlueLight")),
bgcolor(Color::Cyan, text("Cyan")), bgcolor(Color::Cyan, text("Cyan")),
bgcolor(Color::CyanLight, text("CyanLight")), bgcolor(Color::CyanLight, text("CyanLight")),
bgcolor(Color::Green, text("Green")), bgcolor(Color::Green, text("Green")),
bgcolor(Color::GreenLight, text("GreenLight")), bgcolor(Color::GreenLight, text("GreenLight")),
bgcolor(Color::Magenta, text("Magenta")), bgcolor(Color::Magenta, text("Magenta")),
bgcolor(Color::MagentaLight, text("MagentaLight")), bgcolor(Color::MagentaLight, text("MagentaLight")),
bgcolor(Color::Red, text("Red")), bgcolor(Color::Red, text("Red")),
bgcolor(Color::RedLight, text("RedLight")), bgcolor(Color::RedLight, text("RedLight")),
bgcolor(Color::Yellow, text("Yellow")), bgcolor(Color::Yellow, text("Yellow")),
bgcolor(Color::YellowLight, text("YellowLight")), bgcolor(Color::YellowLight, text("YellowLight")),
bgcolor(0x66ff66_rgb, text("Phosphor")) bgcolor(0x66ff66_rgb, text("Phosphor")),
), bgcolor(LinearGradient(Color::SkyBlue1, Color::DeepSkyBlue4),
filler() text("Skyblue to DeepSkyBlue")),
); }),
// clang-format on filler(),
});
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document)); auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document); Render(screen, document);

43
flake.lock Normal file
View File

@ -0,0 +1,43 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1678901627,
"narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1679734080,
"narHash": "sha256-z846xfGLlon6t9lqUzlNtBOmsgQLQIZvR6Lt2dImk1M=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "dbf5322e93bcc6cfc52268367a8ad21c09d76fea",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

61
flake.nix Normal file
View File

@ -0,0 +1,61 @@
{
description = "C++ Functional Terminal User Interface library";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = {self, nixpkgs, flake-utils}:
flake-utils.lib.eachDefaultSystem (system:
let pkgs = import nixpkgs { inherit system; }; in
{
packages.ftxui = pkgs.stdenv.mkDerivation rec {
pname = "ftxui";
version = "v4.0.0";
src = pkgs.fetchFromGitHub {
owner = "ArthurSonzogni";
repo = "FTXUI";
rev = version;
sha256 = "sha256-3kAhHDUwzwdvHc8JZAcA14tGqa6w69qrN1JXhSxNBQY=";
};
nativeBuildInputs = [
pkgs.cmake
];
cmakeFlags = [
"-DFTXUI_ENABLE_INSTALL=ON"
"-DFTXUI_BUILD_EXAMPLES=OFF"
"-DFTXUI_BUILD_TESTS=OFF"
"-DFTXUI_BUILD_DOCS=OFF"
"-DCMAKE_BUILD_TYPE=Release"
];
meta = {
homepage = "https://arthursonzogni.github.io/FTXUI/";
description = "C++ Functional Terminal User Interface.";
longDescription = ''
Functional Terminal (X) User interface
A simple C++ library for terminal based user interfaces!
Feature
- Functional style. Inspired by [1] and React
- Simple and elegant syntax (in my opinion)
- Keyboard & mouse navigation.
- Support for UTF8 and fullwidth chars ( )
- Support for animations. Demo 1, Demo 2
- Support for drawing. Demo
- No dependencies
- Cross platform: Linux/MacOS (main target), WebAssembly, Windows (Thanks to contributors!).
- Learn by examples, and tutorials
- Multiple packages: CMake FetchContent (preferred), vcpkg, pkgbuild, conan, nix, etc...
- Good practises: documentation, tests, fuzzers, performance tests, automated CI, automated packaging, etc...
'';
license = pkgs.lib.licenses.mit;
platforms = pkgs.lib.platforms.all;
};
};
}
);
}

9
ftxui.pc.in Normal file
View File

@ -0,0 +1,9 @@
prefix="@CMAKE_INSTALL_PREFIX@"
libdir="@CMAKE_INSTALL_FULL_LIBDIR@"
includedir="@CMAKE_INSTALL_FULL_INCLUDEDIR@"
Name: @PROJECT_NAME@
Description: @CMAKE_PROJECT_DESCRIPTION@
Version: @PROJECT_VERSION@
Cflags: -I${includedir}
Libs: -L${libdir} -lftxui-component -lftxui-dom -lftxui-screen

View File

@ -6,6 +6,7 @@
#include "ftxui/dom/canvas.hpp" #include "ftxui/dom/canvas.hpp"
#include "ftxui/dom/flexbox_config.hpp" #include "ftxui/dom/flexbox_config.hpp"
#include "ftxui/dom/linear_gradient.hpp"
#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"
@ -20,7 +21,15 @@ using Elements = std::vector<Element>;
using Decorator = std::function<Element(Element)>; using Decorator = std::function<Element(Element)>;
using GraphFunction = std::function<std::vector<int>(int, int)>; using GraphFunction = std::function<std::vector<int>(int, int)>;
enum BorderStyle { LIGHT, HEAVY, DOUBLE, ROUNDED, EMPTY }; enum BorderStyle {
LIGHT,
DASHED,
HEAVY,
DOUBLE,
ROUNDED,
EMPTY,
};
enum class GaugeDirection { Left, Up, Right, Down }; enum class GaugeDirection { Left, Up, Right, Down };
// Pipe elements into decorator togethers. // Pipe elements into decorator togethers.
@ -37,6 +46,7 @@ Element text(std::string text);
Element vtext(std::string text); Element vtext(std::string text);
Element separator(); Element separator();
Element separatorLight(); Element separatorLight();
Element separatorDashed();
Element separatorHeavy(); Element separatorHeavy();
Element separatorDouble(); Element separatorDouble();
Element separatorEmpty(); Element separatorEmpty();
@ -59,11 +69,14 @@ Element gaugeDown(float progress);
Element gaugeDirection(float progress, GaugeDirection); Element gaugeDirection(float progress, GaugeDirection);
Element border(Element); Element border(Element);
Element borderLight(Element); Element borderLight(Element);
Element borderDashed(Element);
Element borderHeavy(Element); Element borderHeavy(Element);
Element borderDouble(Element); Element borderDouble(Element);
Element borderRounded(Element); Element borderRounded(Element);
Element borderEmpty(Element); Element borderEmpty(Element);
Decorator borderStyled(BorderStyle); Decorator borderStyled(BorderStyle);
Decorator borderStyled(BorderStyle, Color);
Decorator borderStyled(Color);
Decorator borderWith(const Pixel&); Decorator borderWith(const Pixel&);
Element window(Element title, Element content); Element window(Element title, Element content);
Element spinner(int charset_index, size_t image_index); Element spinner(int charset_index, size_t image_index);
@ -88,8 +101,12 @@ Element blink(Element);
Element strikethrough(Element); Element strikethrough(Element);
Decorator color(Color); Decorator color(Color);
Decorator bgcolor(Color); Decorator bgcolor(Color);
Decorator color(const LinearGradient&);
Decorator bgcolor(const LinearGradient&);
Element color(Color, Element); Element color(Color, Element);
Element bgcolor(Color, Element); Element bgcolor(Color, Element);
Element color(const LinearGradient&, Element);
Element bgcolor(const LinearGradient&, Element);
Decorator focusPosition(int x, int y); Decorator focusPosition(int x, int y);
Decorator focusPositionRelative(float x, float y); Decorator focusPositionRelative(float x, float y);
Element automerge(Element child); Element automerge(Element child);

View File

@ -0,0 +1,52 @@
#ifndef FTXUI_DOM_LINEAR_GRADIENT_HPP
#define FTXUI_DOM_LINEAR_GRADIENT_HPP
#include <optional>
#include <vector>
#include "ftxui/screen/color.hpp" // for Colors
namespace ftxui {
/// @brief A class representing the settings for linear-gradient color effect.
///
/// Example:
/// ```cpp
/// LinearGradient()
/// .Angle(45)
/// .Stop(Color::Red, 0.0)
/// .Stop(Color::Green, 0.5)
/// .Stop(Color::Blue, 1.0);
/// ```
///
/// There are also shorthand constructors:
/// ```cpp
/// LinearGradient(Color::Red, Color::Blue);
/// LinearGradient(45, Color::Red, Color::Blue);
/// ```
struct LinearGradient {
float angle = 0.f;
struct Stop {
Color color = Color::Default;
std::optional<float> position;
};
std::vector<Stop> stops;
// Simple constructor
LinearGradient();
LinearGradient(Color begin, Color end);
LinearGradient(float angle, Color begin, Color end);
// Modifier using the builder pattern.
LinearGradient& Angle(float angle);
LinearGradient& Stop(Color color, float position);
LinearGradient& Stop(Color color);
};
} // namespace ftxui
#endif // FTXUI_DOM_LINEAR_GRADIENT_HPP
// 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.

View File

@ -2,7 +2,8 @@
#define FTXUI_SCREEN_COLOR_HPP #define FTXUI_SCREEN_COLOR_HPP
#include <cstdint> // for uint8_t #include <cstdint> // for uint8_t
#include <string> // for wstring #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.

View File

@ -124,10 +124,10 @@ TEST(ButtonTest, Animation) {
Screen screen(12, 3); Screen screen(12, 3);
Render(screen, container->Render()); Render(screen, container->Render());
EXPECT_EQ(screen.ToString(), EXPECT_EQ(screen.ToString(),
"\x1B[1m\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m \x1B[22m " "\x1B[1m\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m \x1B[22m "
" \x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;192;192;192m\x1B[48;2;0;0;" " \x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;191;191;191m\x1B[48;2;0;0;"
"0m btn1 \x1B[22m btn2 " "0m btn1 \x1B[22m btn2 "
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;192;192;192m\x1B[48;2;0;0;" "\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;191;191;191m\x1B[48;2;0;0;"
"0m \x1B[22m \x1B[39m\x1B[49m"); "0m \x1B[22m \x1B[39m\x1B[49m");
} }
selected = 1; selected = 1;
@ -135,10 +135,10 @@ TEST(ButtonTest, Animation) {
Screen screen(12, 3); Screen screen(12, 3);
Render(screen, container->Render()); Render(screen, container->Render());
EXPECT_EQ(screen.ToString(), EXPECT_EQ(screen.ToString(),
"\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m \x1B[1m " "\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m \x1B[1m "
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;192;192;192m\x1B[48;2;0;0;" "\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;191;191;191m\x1B[48;2;0;0;"
"0m btn1 \x1B[1m btn2 " "0m btn1 \x1B[1m btn2 "
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;192;192;192m\x1B[48;2;0;0;" "\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;191;191;191m\x1B[48;2;0;0;"
"0m \x1B[1m \x1B[22m\x1B[39m\x1B[49m"); "0m \x1B[1m \x1B[22m\x1B[39m\x1B[49m");
} }
animation::Params params(2s); animation::Params params(2s);
@ -148,12 +148,12 @@ TEST(ButtonTest, Animation) {
Render(screen, container->Render()); Render(screen, container->Render());
EXPECT_EQ( EXPECT_EQ(
screen.ToString(), screen.ToString(),
"\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m " "\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m "
"\x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m " "\x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m "
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m " "\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m "
"btn1 \x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m btn2 " "btn1 \x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m btn2 "
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m " "\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m "
" \x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m " " \x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m "
"\x1B[22m\x1B[39m\x1B[49m"); "\x1B[22m\x1B[39m\x1B[49m");
} }
EXPECT_EQ(selected, 1); EXPECT_EQ(selected, 1);
@ -164,12 +164,12 @@ TEST(ButtonTest, Animation) {
Render(screen, container->Render()); Render(screen, container->Render());
EXPECT_EQ( EXPECT_EQ(
screen.ToString(), screen.ToString(),
"\x1B[1m\x1B[38;2;223;223;223m\x1B[48;2;64;64;64m " "\x1B[1m\x1B[38;2;226;226;226m\x1B[48;2;93;93;93m "
"\x1B[22m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m " "\x1B[22m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m "
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;223;223;223m\x1B[48;2;64;64;64m " "\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;226;226;226m\x1B[48;2;93;93;93m "
"btn1 \x1B[22m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m btn2 " "btn1 \x1B[22m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m btn2 "
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;223;223;223m\x1B[48;2;64;64;64m " "\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;226;226;226m\x1B[48;2;93;93;93m "
" \x1B[22m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m " " \x1B[22m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m "
"\x1B[39m\x1B[49m"); "\x1B[39m\x1B[49m");
} }
container->OnAnimation(params); container->OnAnimation(params);
@ -178,12 +178,12 @@ TEST(ButtonTest, Animation) {
Render(screen, container->Render()); Render(screen, container->Render());
EXPECT_EQ( EXPECT_EQ(
screen.ToString(), screen.ToString(),
"\x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m " "\x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m "
"\x1B[22m\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m " "\x1B[22m\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m "
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;" "\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;"
"128m btn1 \x1B[22m\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m btn2 " "127m btn1 \x1B[22m\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m btn2 "
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;" "\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;"
"128m \x1B[22m\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m " "127m \x1B[22m\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m "
"\x1B[39m\x1B[49m"); "\x1B[39m\x1B[49m");
} }
} }

View File

@ -96,8 +96,7 @@ MenuOption GeneratorMenuOption(const char* data, size_t size) {
MenuOption option; MenuOption option;
option.underline = GeneratorUnderlineOption(data, size); option.underline = GeneratorUnderlineOption(data, size);
option.entries = GeneratorMenuEntryOption(data, size); option.entries = GeneratorMenuEntryOption(data, size);
option.direction = option.direction = static_cast<MenuOption::Direction>(GeneratorInt(data, size) % 4);
static_cast<MenuOption::Direction>(GeneratorInt(data, size) % 4);
return option; return option;
} }

View File

@ -174,9 +174,9 @@ void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
} }
const size_t buffer_size = 100; const size_t buffer_size = 100;
std::array<char, buffer_size> buffer; // NOLINT; std::array<char, buffer_size> buffer; // NOLINT;
int l = read(fileno(stdin), buffer.data(), buffer_size); // NOLINT size_t l = read(fileno(stdin), buffer.data(), buffer_size); // NOLINT
for (int i = 0; i < l; ++i) { for (size_t i = 0; i < l; ++i) {
parser.Add(buffer[i]); // NOLINT parser.Add(buffer[i]); // NOLINT
} }
} }
@ -460,9 +460,13 @@ void ScreenInteractive::PostMain() {
g_active_screen->Install(); g_active_screen->Install();
} else { } else {
Uninstall(); Uninstall();
std::cout << '\r';
// On final exit, keep the current drawing and reset cursor position one // On final exit, keep the current drawing and reset cursor position one
// line after it. // line after it.
std::cout << std::endl; if (!use_alternative_screen_) {
std::cout << std::endl;
}
} }
} }

View File

@ -19,8 +19,31 @@ const std::map<std::string, std::string> g_uniformize = {
// See https://github.com/ArthurSonzogni/FTXUI/issues/337 // See https://github.com/ArthurSonzogni/FTXUI/issues/337
// Here, we uniformize the new line character to `\n`. // Here, we uniformize the new line character to `\n`.
{"\r", "\n"}, {"\r", "\n"},
// See: https://github.com/ArthurSonzogni/FTXUI/issues/508 // See: https://github.com/ArthurSonzogni/FTXUI/issues/508
{std::string({8}), std::string({127})}, {std::string({8}), std::string({127})},
// See: https://github.com/ArthurSonzogni/FTXUI/issues/626
//
// Depending on the Cursor Key Mode (DECCKM), the terminal sends different
// escape sequences:
//
// Key Normal Application
// ----- -------- -----------
// Up ESC [ A ESC O A
// Down ESC [ B ESC O B
// Right ESC [ C ESC O C
// Left ESC [ D ESC O D
// Home ESC [ H ESC O H
// End ESC [ F ESC O F
//
{"\x1BOA", "\x1B[A"}, // UP
{"\x1BOB", "\x1B[B"}, // DOWN
{"\x1BOC", "\x1B[C"}, // RIGHT
{"\x1BOD", "\x1B[D"}, // LEFT
{"\x1BOH", "\x1B[H"}, // HOME
{"\x1BOF", "\x1B[F"}, // END
}; };
TerminalInputParser::TerminalInputParser(Sender<Task> out) TerminalInputParser::TerminalInputParser(Sender<Task> out)

View File

@ -333,22 +333,44 @@ TEST(Event, Special) {
output.push_back(it); output.push_back(it);
return output; return output;
}; };
struct { struct {
std::vector<unsigned char> input; std::vector<unsigned char> input;
Event expected; Event expected;
} kTestCase[] = { } kTestCase[] = {
{str("\x1B[D"), Event::ArrowLeft}, // Arrow (defaut cursor mode)
{str("\x1B[C"), Event::ArrowRight},
{str("\x1B[A"), Event::ArrowUp}, {str("\x1B[A"), Event::ArrowUp},
{str("\x1B[B"), Event::ArrowDown}, {str("\x1B[B"), Event::ArrowDown},
{str("\x1B[C"), Event::ArrowRight},
{str("\x1B[D"), Event::ArrowLeft},
{str("\x1B[H"), Event::Home},
{str("\x1B[F"), Event::End},
// Arrow (application cursor mode)
{str("\x1BOA"), Event::ArrowUp},
{str("\x1BOB"), Event::ArrowDown},
{str("\x1BOC"), Event::ArrowRight},
{str("\x1BOD"), Event::ArrowLeft},
{str("\x1BOH"), Event::Home},
{str("\x1BOF"), Event::End},
// Backspace & Quirk for:
// https://github.com/ArthurSonzogni/FTXUI/issues/508
{{127}, Event::Backspace}, {{127}, Event::Backspace},
// Quirk for: https://github.com/ArthurSonzogni/FTXUI/issues/508
{{8}, Event::Backspace}, {{8}, Event::Backspace},
// Delete
{str("\x1B[3~"), Event::Delete}, {str("\x1B[3~"), Event::Delete},
//{str("\x1B"), Event::Escape},
// Return
{{13}, Event::Return},
{{10}, Event::Return}, {{10}, Event::Return},
// Tabs:
{{9}, Event::Tab}, {{9}, Event::Tab},
{{27, 91, 90}, Event::TabReverse}, {{27, 91, 90}, Event::TabReverse},
// Function keys
{str("\x1BOP"), Event::F1}, {str("\x1BOP"), Event::F1},
{str("\x1BOQ"), Event::F2}, {str("\x1BOQ"), Event::F2},
{str("\x1BOR"), Event::F3}, {str("\x1BOR"), Event::F3},
@ -361,10 +383,12 @@ TEST(Event, Special) {
{str("\x1B[21~"), Event::F10}, {str("\x1B[21~"), Event::F10},
{str("\x1B[23~"), Event::F11}, {str("\x1B[23~"), Event::F11},
{str("\x1B[24~"), Event::F12}, {str("\x1B[24~"), Event::F12},
{{27, 91, 72}, Event::Home},
{{27, 91, 70}, Event::End}, // Page up and down:
{{27, 91, 53, 126}, Event::PageUp}, {str("\x1B[5~"), Event::PageUp},
{{27, 91, 54, 126}, Event::PageDown}, {str("\x1B[6~"), Event::PageDown},
// Custom:
{{0}, Event::Custom}, {{0}, Event::Custom},
}; };

View File

@ -1,11 +1,13 @@
#include <algorithm> // for max #include <algorithm> // for max
#include <array> // for array #include <array> // for array
#include <memory> // for allocator, make_shared, __shared_ptr_access #include <ftxui/screen/color.hpp> // for Color
#include <string> // for basic_string, string #include <memory> // for allocator, make_shared, __shared_ptr_access
#include <utility> // for move #include <optional> // for optional, nullopt
#include <vector> // for __alloc_traits<>::value_type #include <string> // for basic_string, string
#include <utility> // for move
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for unpack, Element, Decorator, BorderStyle, ROUNDED, Elements, DOUBLE, EMPTY, HEAVY, LIGHT, border, borderDouble, borderEmpty, borderHeavy, borderLight, borderRounded, borderStyled, borderWith, window #include "ftxui/dom/elements.hpp" // for unpack, Element, Decorator, BorderStyle, ROUNDED, borderStyled, Elements, DASHED, DOUBLE, EMPTY, HEAVY, LIGHT, border, borderDashed, borderDouble, borderEmpty, borderHeavy, borderLight, borderRounded, borderWith, window
#include "ftxui/dom/node.hpp" // for Node, Elements #include "ftxui/dom/node.hpp" // for Node, Elements
#include "ftxui/dom/requirement.hpp" // for Requirement #include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/box.hpp" // for Box
@ -14,24 +16,29 @@
namespace ftxui { namespace ftxui {
using Charset = std::array<std::string, 6>; // NOLINT using Charset = std::array<std::string, 6>; // NOLINT
using Charsets = std::array<Charset, 5>; // NOLINT using Charsets = std::array<Charset, 6>; // NOLINT
// NOLINTNEXTLINE // NOLINTNEXTLINE
static Charsets simple_border_charset = { static Charsets simple_border_charset = {
Charset{"", "", "", "", "", ""}, Charset{"", "", "", "", "", ""}, // LIGHT
Charset{"", "", "", "", "", ""}, Charset{"", "", "", "", "", ""}, // DASHED
Charset{"", "", "", "", "", ""}, Charset{"", "", "", "", "", ""}, // HEAVY
Charset{"", "", "", "", "", ""}, Charset{"", "", "", "", "", ""}, // DOUBLE
Charset{" ", " ", " ", " ", " ", " "}, Charset{"", "", "", "", "", ""}, // ROUNDED
Charset{" ", " ", " ", " ", " ", " "}, // EMPTY
}; };
// For reference, here is the charset for normal border: // For reference, here is the charset for normal border:
class Border : public Node { class Border : public Node {
public: public:
Border(Elements children, BorderStyle style) Border(Elements children,
BorderStyle style,
std::optional<Color> foreground_color = std::nullopt)
: Node(std::move(children)), : Node(std::move(children)),
charset_(simple_border_charset[style]) {} // NOLINT charset_(simple_border_charset[style]),
foreground_color_(foreground_color) {} // NOLINT
const Charset& charset_; // NOLINT const Charset& charset_; // NOLINT
std::optional<Color> foreground_color_;
void ComputeRequirement() override { void ComputeRequirement() override {
Node::ComputeRequirement(); Node::ComputeRequirement();
@ -100,6 +107,18 @@ class Border : public Node {
if (children_.size() == 2) { if (children_.size() == 2) {
children_[1]->Render(screen); children_[1]->Render(screen);
} }
// Draw the border color.
if (foreground_color_) {
for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x, box_.y_min).foreground_color = *foreground_color_;
screen.PixelAt(x, box_.y_max).foreground_color = *foreground_color_;
}
for (int y = box_.y_min; y <= box_.y_max; ++y) {
screen.PixelAt(box_.x_min, y).foreground_color = *foreground_color_;
screen.PixelAt(box_.x_max, y).foreground_color = *foreground_color_;
}
}
} }
}; };
@ -173,10 +192,13 @@ class BorderPixel : public Node {
/// @ingroup dom /// @ingroup dom
/// @see border /// @see border
/// @see borderLight /// @see borderLight
/// @see borderDashed
/// @see borderDouble /// @see borderDouble
/// @see borderHeavy /// @see borderHeavy
/// @see borderEmpty /// @see borderEmpty
/// @see borderRounded /// @see borderRounded
/// @see borderStyled
/// @see borderWith
/// ///
/// Add a border around an element /// Add a border around an element
/// ///
@ -219,10 +241,66 @@ Decorator borderStyled(BorderStyle style) {
}; };
} }
/// @brief Same as border but with a foreground color.
/// @ingroup dom
/// @see border
Decorator borderStyled(Color foreground_color) {
return [foreground_color](Element child) {
return std::make_shared<Border>(unpack(std::move(child)), ROUNDED,
foreground_color);
};
}
/// @brief Same as border but with a foreground color and a different style
/// @ingroup dom
/// @see border
Decorator borderStyled(BorderStyle style, Color foreground_color) {
return [style, foreground_color](Element child) {
return std::make_shared<Border>(unpack(std::move(child)), style,
foreground_color);
};
}
/// @brief Draw a light border around the element. /// @brief Draw a light border around the element.
/// @ingroup dom /// @ingroup dom
/// @see border /// @see border
/// @see borderLight /// @see borderLight
/// @see borderDashed
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
/// Add a border around an element
///
/// ### Example
///
/// ```cpp
/// // Use 'borderDash' as a function...
/// Element document = borderDash(text("The element"));
///
/// // ...Or as a 'pipe'.
/// Element document = text("The element") | borderDAsh;
/// ```
///
/// ### Output
///
/// ```bash
/// ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓
/// ╏The element ╏
/// ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
/// ```
Element borderDashed(Element child) {
return std::make_shared<Border>(unpack(std::move(child)), DASHED);
}
/// @brief Draw a dashed border around the element.
/// @ingroup dom
/// @see border
/// @see borderLight
/// @see borderDashed
/// @see borderDouble /// @see borderDouble
/// @see borderHeavy /// @see borderHeavy
/// @see borderRounded /// @see borderRounded
@ -257,6 +335,7 @@ Element borderLight(Element child) {
/// @ingroup dom /// @ingroup dom
/// @see border /// @see border
/// @see borderLight /// @see borderLight
/// @see borderDashed
/// @see borderDouble /// @see borderDouble
/// @see borderHeavy /// @see borderHeavy
/// @see borderRounded /// @see borderRounded
@ -291,6 +370,7 @@ Element borderHeavy(Element child) {
/// @ingroup dom /// @ingroup dom
/// @see border /// @see border
/// @see borderLight /// @see borderLight
/// @see borderDashed
/// @see borderDouble /// @see borderDouble
/// @see borderHeavy /// @see borderHeavy
/// @see borderRounded /// @see borderRounded
@ -325,6 +405,7 @@ Element borderDouble(Element child) {
/// @ingroup dom /// @ingroup dom
/// @see border /// @see border
/// @see borderLight /// @see borderLight
/// @see borderDashed
/// @see borderDouble /// @see borderDouble
/// @see borderHeavy /// @see borderHeavy
/// @see borderRounded /// @see borderRounded
@ -359,6 +440,7 @@ Element borderRounded(Element child) {
/// @ingroup dom /// @ingroup dom
/// @see border /// @see border
/// @see borderLight /// @see borderLight
/// @see borderDashed
/// @see borderDouble /// @see borderDouble
/// @see borderHeavy /// @see borderHeavy
/// @see borderRounded /// @see borderRounded

View File

@ -14,7 +14,7 @@ namespace ftxui {
namespace { namespace {
uint32_t Hash(const std::string s) { uint32_t Hash(const std::string s) {
uint32_t hash = 0; uint32_t hash = 0;
for (auto c : s) { for (uint8_t c : s) {
hash += c; hash += c;
hash *= 7; hash *= 7;
} }
@ -37,7 +37,7 @@ TEST(CanvasTest, GoldPoint) {
}); });
Screen screen(30, 10); Screen screen(30, 10);
Render(screen, element); Render(screen, element);
EXPECT_EQ(Hash(screen.ToString()), 2143518726); EXPECT_EQ(Hash(screen.ToString()), 2085952774U) << screen.ToString();
} }
TEST(CanvasTest, GoldPointColor) { TEST(CanvasTest, GoldPointColor) {
@ -52,7 +52,7 @@ TEST(CanvasTest, GoldPointColor) {
}); });
Screen screen(30, 10); Screen screen(30, 10);
Render(screen, element); Render(screen, element);
EXPECT_EQ(Hash(screen.ToString()), 1264423298); EXPECT_EQ(Hash(screen.ToString()), 2295070594U) << screen.ToString();
} }
TEST(CanvasTest, GoldBlock) { TEST(CanvasTest, GoldBlock) {
@ -70,7 +70,7 @@ TEST(CanvasTest, GoldBlock) {
}); });
Screen screen(30, 10); Screen screen(30, 10);
Render(screen, element); Render(screen, element);
EXPECT_EQ(Hash(screen.ToString()), 3826174883); EXPECT_EQ(Hash(screen.ToString()), 2625314979U) << screen.ToString();
} }
TEST(CanvasTest, GoldBlockColor) { TEST(CanvasTest, GoldBlockColor) {
@ -85,7 +85,7 @@ TEST(CanvasTest, GoldBlockColor) {
}); });
Screen screen(30, 10); Screen screen(30, 10);
Render(screen, element); Render(screen, element);
EXPECT_EQ(Hash(screen.ToString()), 3048712696); EXPECT_EQ(Hash(screen.ToString()), 8392696U) << screen.ToString();
} }
TEST(CanvasTest, GoldText) { TEST(CanvasTest, GoldText) {

View File

@ -1,9 +1,9 @@
#include <gtest/gtest.h> #include <gtest/gtest.h> // for Test, EXPECT_EQ, Message, TestPartResult, TestInfo (ptr only), TEST
#include <string> // for allocator #include <string> // for allocator
#include "ftxui/dom/elements.hpp" // for operator|, text, bgcolor, color, Element #include "ftxui/dom/elements.hpp" // for operator|, text, bgcolor, color, Element
#include "ftxui/dom/node.hpp" // for Render #include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for Color, Color::Red #include "ftxui/screen/color.hpp" // for Color, Color::Red, Color::RedLight
#include "ftxui/screen/screen.hpp" // for Screen, Pixel #include "ftxui/screen/screen.hpp" // for Screen, Pixel
namespace ftxui { namespace ftxui {

View File

@ -161,7 +161,7 @@ void SetY(Global& g, std::vector<Line> lines) {
} }
case FlexboxConfig::AlignContent::Stretch: { case FlexboxConfig::AlignContent::Stretch: {
for (int i = ys.size() - 1; i >= 0; --i) { // NOLINT for (int i = static_cast<int>(ys.size()) - 1; i >= 0; --i) { // NOLINT
const int shifted = remaining_space * (i + 0) / (i + 1); const int shifted = remaining_space * (i + 0) / (i + 1);
ys[i] += shifted; ys[i] += shifted;
const int consumed = remaining_space - shifted; const int consumed = remaining_space - shifted;
@ -172,7 +172,7 @@ void SetY(Global& g, std::vector<Line> lines) {
} }
case FlexboxConfig::AlignContent::SpaceBetween: { case FlexboxConfig::AlignContent::SpaceBetween: {
for (int i = ys.size() - 1; i >= 1; --i) { // NOLINT for (int i = static_cast<int>(ys.size()) - 1; i >= 1; --i) { // NOLINT
ys[i] += remaining_space; ys[i] += remaining_space;
remaining_space = remaining_space * (i - 1) / i; remaining_space = remaining_space * (i - 1) / i;
} }
@ -180,7 +180,7 @@ void SetY(Global& g, std::vector<Line> lines) {
} }
case FlexboxConfig::AlignContent::SpaceAround: { case FlexboxConfig::AlignContent::SpaceAround: {
for (int i = ys.size() - 1; i >= 0; --i) { // NOLINT for (int i = static_cast<int>(ys.size()) - 1; i >= 0; --i) { // NOLINT
ys[i] += remaining_space * (2 * i + 1) / (2 * i + 2); ys[i] += remaining_space * (2 * i + 1) / (2 * i + 2);
remaining_space = remaining_space * (2 * i) / (2 * i + 2); remaining_space = remaining_space * (2 * i) / (2 * i + 2);
} }
@ -188,7 +188,7 @@ void SetY(Global& g, std::vector<Line> lines) {
} }
case FlexboxConfig::AlignContent::SpaceEvenly: { case FlexboxConfig::AlignContent::SpaceEvenly: {
for (int i = ys.size() - 1; i >= 0; --i) { // NOLINT for (int i = static_cast<int>(ys.size()) - 1; i >= 0; --i) { // NOLINT
ys[i] += remaining_space * (i + 1) / (i + 2); ys[i] += remaining_space * (i + 1) / (i + 2);
remaining_space = remaining_space * (i + 1) / (i + 2); remaining_space = remaining_space * (i + 1) / (i + 2);
} }

View File

@ -39,6 +39,9 @@ class Graph : public Node {
void Render(Screen& screen) override { void Render(Screen& screen) override {
const int width = (box_.x_max - box_.x_min + 1) * 2; const int width = (box_.x_max - box_.x_min + 1) * 2;
const int height = (box_.y_max - box_.y_min + 1) * 2; const int height = (box_.y_max - box_.y_min + 1) * 2;
if (width <= 0 || height <= 0) {
return;
}
auto data = graph_function_(width, height); auto data = graph_function_(width, height);
int i = 0; int i = 0;
for (int x = box_.x_min; x <= box_.x_max; ++x) { for (int x = box_.x_min; x <= box_.x_max; ++x) {

View File

@ -0,0 +1,293 @@
#include <stddef.h> // for size_t
#include <algorithm> // for max, min, sort, copy
#include <cmath> // for fmod, cos, sin
#include <ftxui/dom/linear_gradient.hpp> // for LinearGradient::Stop, LinearGradient
#include <memory> // for allocator_traits<>::value_type, make_shared
#include <optional> // for optional, operator!=, operator<
#include <utility> // for move
#include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for Element, Decorator, bgcolor, color
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/color.hpp" // for Color, Color::Default, Color::Blue
#include "ftxui/screen/screen.hpp" // for Pixel, Screen
namespace ftxui {
namespace {
struct LinearGradientNormalized {
float angle = 0.f;
std::vector<Color> colors;
std::vector<float> positions; // Sorted.
};
// Convert a LinearGradient to a normalized version.
LinearGradientNormalized Normalize(LinearGradient gradient) {
// Handle gradient of size 0.
if (gradient.stops.size() == 0) {
return LinearGradientNormalized{
0.f, {Color::Default, Color::Default}, {0.f, 1.f}};
}
// Fill in the two extent, if not provided.
if (!gradient.stops.front().position) {
gradient.stops.front().position = 0.f;
}
if (!gradient.stops.back().position) {
gradient.stops.back().position = 1.f;
}
// Fill in the blank, by interpolating positions.
size_t last_checkpoint = 0;
for (size_t i = 1; i < gradient.stops.size(); ++i) {
if (!gradient.stops[i].position) {
continue;
}
if (i - last_checkpoint >= 2) {
const float min = gradient.stops[i].position.value();
const float max = gradient.stops[last_checkpoint].position.value();
for (size_t j = last_checkpoint + 1; j < i; ++j) {
gradient.stops[j].position =
min + (max - min) * (j - last_checkpoint) / (i - last_checkpoint);
}
}
last_checkpoint = i;
}
// Sort the stops by position.
std::sort(
gradient.stops.begin(), gradient.stops.end(),
[](const auto& a, const auto& b) { return a.position < b.position; });
// If we don't being with zero, add a stop at zero.
if (gradient.stops.front().position != 0) {
gradient.stops.insert(gradient.stops.begin(),
{gradient.stops.front().color, 0.f});
}
// If we don't end with one, add a stop at one.
if (gradient.stops.back().position != 1) {
gradient.stops.push_back({gradient.stops.back().color, 1.f});
}
// Normalize the angle.
LinearGradientNormalized normalized;
normalized.angle = std::fmod(std::fmod(gradient.angle, 360.f) + 360.f, 360.f);
for (auto& stop : gradient.stops) {
normalized.colors.push_back(stop.color);
normalized.positions.push_back(stop.position.value());
}
return normalized;
}
Color Interpolate(const LinearGradientNormalized& gradient, float t) {
// Find the right color in the gradient's stops.
size_t i = 1;
while (true) {
if (i > gradient.positions.size()) {
return Color::Interpolate(0.5f, gradient.colors.back(),
gradient.colors.back());
}
if (t <= gradient.positions[i]) {
break;
}
++i;
}
const float t0 = gradient.positions[i - 1];
const float t1 = gradient.positions[i - 0];
const float tt = (t - t0) / (t1 - t0);
const Color& c0 = gradient.colors[i - 1];
const Color& c1 = gradient.colors[i - 0];
const Color& cc = Color::Interpolate(tt, c0, c1);
return cc;
}
class LinearGradientColor : public NodeDecorator {
public:
explicit LinearGradientColor(Element child,
const LinearGradient& gradient,
bool background_color)
: NodeDecorator(std::move(child)),
gradient_(Normalize(gradient)),
background_color_{background_color} {}
private:
void Render(Screen& screen) override {
const float degtorad = 0.01745329251f;
const float dx = std::cos(gradient_.angle * degtorad);
const float dy = std::sin(gradient_.angle * degtorad);
// Project every corner to get the extent of the gradient.
const float p1 = box_.x_min * dx + box_.y_min * dy;
const float p2 = box_.x_min * dx + box_.y_max * dy;
const float p3 = box_.x_max * dx + box_.y_min * dy;
const float p4 = box_.x_max * dx + box_.y_max * dy;
const float min = std::min({p1, p2, p3, p4});
const float max = std::max({p1, p2, p3, p4});
// Renormalize the projection to [0, 1] using the extent and projective
// geometry.
const float dX = dx / (max - min);
const float dY = dy / (max - min);
const float dZ = -min / (max - min);
// Project every pixel to get the color.
if (background_color_) {
for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) {
const float t = x * dX + y * dY + dZ;
screen.PixelAt(x, y).background_color = Interpolate(gradient_, t);
}
}
} else {
for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) {
const float t = x * dX + y * dY + dZ;
screen.PixelAt(x, y).foreground_color = Interpolate(gradient_, t);
}
}
}
NodeDecorator::Render(screen);
}
LinearGradientNormalized gradient_;
bool background_color_;
};
} // namespace
/// @brief Build the "empty" gradient. This is often followed by calls to
/// LinearGradient::Angle() and LinearGradient::Stop().
/// Example:
/// ```cpp
/// auto gradient =
/// LinearGradient()
/// .Angle(45)
/// .Stop(Color::Red, 0.0)
/// .Stop(Color::Green, 0.5)
/// .Stop(Color::Blue, 1.0);;
/// ```
/// @ingroup dom
LinearGradient::LinearGradient() = default;
/// @brief Build a gradient with two colors.
/// @param begin The color at the beginning of the gradient.
/// @param end The color at the end of the gradient.
/// @ingroup dom
LinearGradient::LinearGradient(Color begin, Color end) {
stops.push_back({begin, {}});
stops.push_back({end, {}});
}
/// @brief Build a gradient with two colors and an angle.
/// @param a The angle of the gradient.
/// @param begin The color at the beginning of the gradient.
/// @param end The color at the end of the gradient.
/// @ingroup dom
LinearGradient::LinearGradient(float a, Color begin, Color end) {
angle = a;
stops.push_back({begin, {}});
stops.push_back({end, {}});
}
/// @brief Set the angle of the gradient.
/// @param a The angle of the gradient.
/// @return The gradient.
/// @ingroup dom
LinearGradient& LinearGradient::Angle(float a) {
angle = a;
return *this;
}
/// @brief Add a color stop to the gradient.
/// @param c The color of the stop.
/// @param p The position of the stop.
/// @return The gradient.
LinearGradient& LinearGradient::Stop(Color c, float p) {
stops.push_back({c, p});
return *this;
}
/// @brief Add a color stop to the gradient.
/// @param c The color of the stop.
/// @return The gradient.
/// @ingroup dom
/// @note The position of the stop is interpolated from nearby stops.
LinearGradient& LinearGradient::Stop(Color c) {
stops.push_back({c, {}});
return *this;
}
/// @brief Set the foreground color of an element with linear-gradient effect.
/// @param gradient The gradient effect to be applied on the output element.
/// @param child The input element.
/// @return The output element colored.
/// @ingroup dom
///
/// ### Example
///
/// ```cpp
/// color(LinearGradient{0, {Color::Red, Color::Blue}}, text("Hello"))
/// ```
Element color(const LinearGradient& gradient, Element child) {
return std::make_shared<LinearGradientColor>(std::move(child), gradient,
/*background_color*/ false);
}
/// @brief Set the background color of an element with linear-gradient effect.
/// @param gradient The gradient effect to be applied on the output element.
/// @param child The input element.
/// @return The output element colored.
/// @ingroup dom
///
/// ### Example
///
/// ```cpp
/// bgcolor(LinearGradient{0, {Color::Red, Color::Blue}}, text("Hello"))
/// ```
Element bgcolor(const LinearGradient& gradient, Element child) {
return std::make_shared<LinearGradientColor>(std::move(child), gradient,
/*background_color*/ true);
}
/// @brief Decorate using a linear-gradient effect on the foreground color.
/// @param gradient The gradient effect to be applied on the output element.
/// @return The Decorator applying the color.
/// @ingroup dom
///
/// ### Example
///
/// ```cpp
/// text("Hello") | color(LinearGradient{0, {Color::Red, Color::Blue}})
/// ```
Decorator color(const LinearGradient& gradient) {
return
[gradient](Element child) { return color(gradient, std::move(child)); };
}
/// @brief Decorate using a linear-gradient effect on the background color.
/// @param gradient The gradient effect to be applied on the output element.
/// @return The Decorator applying the color.
/// @ingroup dom
///
/// ### Example
///
/// ```cpp
/// text("Hello") | color(LinearGradient{0, {Color::Red, Color::Blue}})
/// ```
Decorator bgcolor(const LinearGradient& gradient) {
return
[gradient](Element child) { return bgcolor(gradient, std::move(child)); };
}
} // namespace ftxui
// 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.

View File

@ -0,0 +1,90 @@
#include <gtest/gtest.h> // for Test, EXPECT_EQ, Message, TestPartResult, TestInfo (ptr only), TEST
#include <ftxui/dom/linear_gradient.hpp> // for LinearGradient::Stop, LinearGradient
#include <string> // for allocator
#include "ftxui/dom/elements.hpp" // for operator|, text, bgcolor, color, Element
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for Color, Color::Red, Color::RedLight
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
namespace ftxui {
TEST(ColorTest, API_default) {
LinearGradient gradient;
EXPECT_EQ(gradient.angle, 0);
EXPECT_EQ(gradient.stops.size(), 0);
}
TEST(ColorTest, API_builder) {
auto gradient = LinearGradient() //
.Angle(45)
.Stop(Color::Red)
.Stop(Color::RedLight, 0.5)
.Stop(Color::RedLight);
EXPECT_EQ(gradient.angle, 45);
EXPECT_EQ(gradient.stops.size(), 3);
EXPECT_EQ(gradient.stops[0].color, Color::Red);
EXPECT_EQ(gradient.stops[0].position, std::nullopt);
EXPECT_EQ(gradient.stops[1].color, Color::RedLight);
EXPECT_EQ(gradient.stops[1].position, 0.5);
EXPECT_EQ(gradient.stops[2].color, Color::RedLight);
EXPECT_EQ(gradient.stops[2].position, std::nullopt);
}
TEST(ColorTest, API_constructor_bicolor) {
auto gradient = LinearGradient(Color::Red, Color::RedLight);
EXPECT_EQ(gradient.angle, 0);
EXPECT_EQ(gradient.stops.size(), 2);
EXPECT_EQ(gradient.stops[0].color, Color::Red);
EXPECT_EQ(gradient.stops[0].position, std::nullopt);
EXPECT_EQ(gradient.stops[1].color, Color::RedLight);
EXPECT_EQ(gradient.stops[1].position, std::nullopt);
}
TEST(ColorTest, API_constructor_bicolor_angle) {
auto gradient = LinearGradient(45, Color::Red, Color::RedLight);
EXPECT_EQ(gradient.angle, 45);
EXPECT_EQ(gradient.stops.size(), 2);
EXPECT_EQ(gradient.stops[0].color, Color::Red);
EXPECT_EQ(gradient.stops[0].position, std::nullopt);
EXPECT_EQ(gradient.stops[1].color, Color::RedLight);
EXPECT_EQ(gradient.stops[1].position, std::nullopt);
}
TEST(ColorTest, GradientForeground) {
auto element =
text("text") | color(LinearGradient(Color::RedLight, Color::Red));
Screen screen(5, 1);
Render(screen, element);
Color gradient_begin = Color::Interpolate(0, Color::RedLight, Color::Red);
Color gradient_end = Color::Interpolate(1, Color::RedLight, Color::Red);
EXPECT_EQ(screen.PixelAt(0, 0).foreground_color, gradient_begin);
EXPECT_EQ(screen.PixelAt(0, 0).background_color, Color());
EXPECT_EQ(screen.PixelAt(4, 0).foreground_color, gradient_end);
EXPECT_EQ(screen.PixelAt(4, 0).background_color, Color());
}
TEST(ColorTest, GradientBackground) {
auto element =
text("text") | bgcolor(LinearGradient(Color::RedLight, Color::Red));
Screen screen(5, 1);
Render(screen, element);
Color gradient_begin = Color::Interpolate(0, Color::RedLight, Color::Red);
Color gradient_end = Color::Interpolate(1, Color::RedLight, Color::Red);
EXPECT_EQ(screen.PixelAt(0, 0).foreground_color, Color());
EXPECT_EQ(screen.PixelAt(0, 0).background_color, gradient_begin);
EXPECT_EQ(screen.PixelAt(4, 0).foreground_color, Color());
EXPECT_EQ(screen.PixelAt(4, 0).background_color, gradient_end);
}
} // namespace ftxui
// 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.

View File

@ -14,14 +14,15 @@ namespace ftxui {
namespace { namespace {
using Charset = std::array<std::string, 2>; // NOLINT using Charset = std::array<std::string, 2>; // NOLINT
using Charsets = std::array<Charset, 5>; // NOLINT using Charsets = std::array<Charset, 6>; // NOLINT
// NOLINTNEXTLINE // NOLINTNEXTLINE
const Charsets charsets = { const Charsets charsets = {
Charset{"", ""}, // Charset{"", ""}, // LIGHT
Charset{"", ""}, // Charset{"", ""}, // DASHED
Charset{"", ""}, // Charset{"", ""}, // HEAVY
Charset{"", ""}, // Charset{"", ""}, // DOUBLE
Charset{" ", " "}, // Charset{"", ""}, // ROUNDED
Charset{" ", " "}, // EMPTY
}; };
} // namespace } // namespace
@ -98,6 +99,7 @@ class SeparatorWithPixel : public SeparatorAuto {
/// @ingroup dom /// @ingroup dom
/// @see separator /// @see separator
/// @see separatorLight /// @see separatorLight
/// @see separatorDashed
/// @see separatorDouble /// @see separatorDouble
/// @see separatorHeavy /// @see separatorHeavy
/// @see separatorEmpty /// @see separatorEmpty
@ -135,6 +137,7 @@ Element separator() {
/// @ingroup dom /// @ingroup dom
/// @see separator /// @see separator
/// @see separatorLight /// @see separatorLight
/// @see separatorDashed
/// @see separatorDouble /// @see separatorDouble
/// @see separatorHeavy /// @see separatorHeavy
/// @see separatorEmpty /// @see separatorEmpty
@ -171,6 +174,7 @@ Element separatorStyled(BorderStyle style) {
/// @ingroup dom /// @ingroup dom
/// @see separator /// @see separator
/// @see separatorLight /// @see separatorLight
/// @see separatorDashed
/// @see separatorDouble /// @see separatorDouble
/// @see separatorHeavy /// @see separatorHeavy
/// @see separatorEmpty /// @see separatorEmpty
@ -202,11 +206,49 @@ Element separatorLight() {
return std::make_shared<SeparatorAuto>(LIGHT); return std::make_shared<SeparatorAuto>(LIGHT);
} }
/// @brief Draw a vertical or horizontal separation in between two other
/// elements, using the DASHED style.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorDashed
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("up"),
/// separatorLight(),
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// up
/// ╍╍╍╍
/// down
/// ```
Element separatorDashed() {
return std::make_shared<SeparatorAuto>(DASHED);
}
/// @brief Draw a vertical or horizontal separation in between two other /// @brief Draw a vertical or horizontal separation in between two other
/// elements, using the HEAVY style. /// elements, using the HEAVY style.
/// @ingroup dom /// @ingroup dom
/// @see separator /// @see separator
/// @see separatorLight /// @see separatorLight
/// @see separatorDashed
/// @see separatorDouble /// @see separatorDouble
/// @see separatorHeavy /// @see separatorHeavy
/// @see separatorEmpty /// @see separatorEmpty
@ -243,6 +285,7 @@ Element separatorHeavy() {
/// @ingroup dom /// @ingroup dom
/// @see separator /// @see separator
/// @see separatorLight /// @see separatorLight
/// @see separatorDashed
/// @see separatorDouble /// @see separatorDouble
/// @see separatorHeavy /// @see separatorHeavy
/// @see separatorEmpty /// @see separatorEmpty
@ -279,6 +322,7 @@ Element separatorDouble() {
/// @ingroup dom /// @ingroup dom
/// @see separator /// @see separator
/// @see separatorLight /// @see separatorLight
/// @see separatorDashed
/// @see separatorDouble /// @see separatorDouble
/// @see separatorHeavy /// @see separatorHeavy
/// @see separatorEmpty /// @see separatorEmpty
@ -316,6 +360,7 @@ Element separatorEmpty() {
/// @ingroup dom /// @ingroup dom
/// @see separator /// @see separator
/// @see separatorLight /// @see separatorLight
/// @see separatorDashed
/// @see separatorDouble /// @see separatorDouble
/// @see separatorHeavy /// @see separatorHeavy
/// @see separatorEmpty /// @see separatorEmpty
@ -351,6 +396,7 @@ Element separatorCharacter(std::string value) {
/// @ingroup dom /// @ingroup dom
/// @see separator /// @see separator
/// @see separatorLight /// @see separatorLight
/// @see separatorDashed
/// @see separatorHeavy /// @see separatorHeavy
/// @see separatorDouble /// @see separatorDouble
/// @see separatorStyled /// @see separatorStyled

View File

@ -35,6 +35,20 @@ TEST(SeparatorTest, Light) {
"down"); "down");
} }
TEST(SeparatorTest, Dashed) {
auto element = vbox({
text("top"),
separatorDashed(),
text("down"),
});
Screen screen(4, 3);
Render(screen, element);
EXPECT_EQ(screen.ToString(),
"top \r\n"
"╍╍╍╍\r\n"
"down");
}
TEST(SeparatorTest, Double) { TEST(SeparatorTest, Double) {
auto element = vbox({ auto element = vbox({
text("top"), text("top"),

View File

@ -14,12 +14,13 @@ bool IsCell(int x, int y) {
} }
// NOLINTNEXTLINE // NOLINTNEXTLINE
static std::string charset[5][6] = { static std::string charset[6][6] = {
{"", "", "", "", "", ""}, // {"", "", "", "", "", ""}, // LIGHT
{"", "", "", "", "", ""}, // {"", "", "", "", "", ""}, // DASHED
{"", "", "", "", "", ""}, // {"", "", "", "", "", ""}, // HEAVY
{"", "", "", "", "", ""}, // {"", "", "", "", "", ""}, // DOUBLE
{" ", " ", " ", " ", " ", " "}, // {"", "", "", "", "", ""}, // ROUNDED
{" ", " ", " ", " ", " ", " "}, // EMPTY
}; };
int Wrap(int input, int modulo) { int Wrap(int input, int modulo) {

View File

@ -1,6 +1,7 @@
#include "ftxui/screen/color.hpp" #include "ftxui/screen/color.hpp"
#include <array> // for array #include <array> // for array
#include <cmath>
#include <string_view> // for literals #include <string_view> // for literals
#include "ftxui/screen/color_info.hpp" // for GetColorInfo, ColorInfo #include "ftxui/screen/color_info.hpp" // for GetColorInfo, ColorInfo
@ -220,12 +221,12 @@ Color Color::Interpolate(float t, const Color& a, const Color& b) {
get_color(a, &red_a, &green_a, &blue_a); get_color(a, &red_a, &green_a, &blue_a);
get_color(b, &red_b, &green_b, &blue_b); get_color(b, &red_b, &green_b, &blue_b);
return Color::RGB(static_cast<uint8_t>(static_cast<float>(red_a) * (1 - t) + // Gamma correction:
static_cast<float>(red_b) * t), // https://en.wikipedia.org/wiki/Gamma_correction
static_cast<uint8_t>(static_cast<float>(green_a) * (1 - t) + return Color::RGB(
static_cast<float>(green_b) * t), pow(pow(red_a, 2.2f) * (1 - t) + pow(red_b, 2.2f) * t, 1 / 2.2f),
static_cast<uint8_t>(static_cast<float>(blue_a) * (1 - t) + pow(pow(green_a, 2.2f) * (1 - t) + pow(green_b, 2.2f) * t, 1 / 2.2f),
static_cast<float>(blue_b) * t)); pow(pow(blue_a, 2.2f) * (1 - t) + pow(blue_b, 2.2f) * t, 1 / 2.2f));
} }
inline namespace literals { inline namespace literals {

View File

@ -57,22 +57,22 @@ TEST(ColorTest, Interpolate) {
Color::RGB(1, 2, 3), // Color::RGB(1, 2, 3), //
Color::RGB(244, 244, 123)) // Color::RGB(244, 244, 123)) //
.Print(false), .Print(false),
"38;2;73;74;39"); "38;2;141;141;71");
EXPECT_EQ(Color::Interpolate(0.7f, // EXPECT_EQ(Color::Interpolate(0.7f, //
Color::RGB(1, 2, 3), // Color::RGB(1, 2, 3), //
Color::RGB(244, 244, 123)) // Color::RGB(244, 244, 123)) //
.Print(false), .Print(false),
"38;2;171;171;87"); "38;2;207;207;104");
EXPECT_EQ(Color::Interpolate(0.7f, // EXPECT_EQ(Color::Interpolate(0.7f, //
Color(Color::Red), // Color(Color::Red), //
Color::RGB(244, 244, 123)) // Color::RGB(244, 244, 123)) //
.Print(false), .Print(false),
"38;2;209;170;86"); "38;2;216;207;104");
EXPECT_EQ(Color::Interpolate(0.7f, // EXPECT_EQ(Color::Interpolate(0.7f, //
Color::RGB(244, 244, 123), // Color::RGB(244, 244, 123), //
Color(Color::Plum1)) // Color(Color::Plum1)) //
.Print(false), .Print(false),
"38;2;251;195;215"); "38;2;251;198;225");
} }
TEST(ColorTest, HSV) { TEST(ColorTest, HSV) {

View File

@ -154,9 +154,11 @@ struct TileEncoding {
const std::map<std::string, TileEncoding> tile_encoding = { // NOLINT const std::map<std::string, TileEncoding> tile_encoding = { // NOLINT
{"", {1, 0, 1, 0, 0}}, {"", {1, 0, 1, 0, 0}},
{"", {2, 0, 2, 0, 0}}, {"", {2, 0, 2, 0, 0}},
{"", {2, 0, 2, 0, 0}},
{"", {0, 1, 0, 1, 0}}, {"", {0, 1, 0, 1, 0}},
{"", {0, 2, 0, 2, 0}}, {"", {0, 2, 0, 2, 0}},
{"", {0, 2, 0, 2, 0}},
{"", {0, 0, 1, 1, 0}}, {"", {0, 0, 1, 1, 0}},
{"", {0, 0, 2, 1, 0}}, {"", {0, 0, 2, 1, 0}},