mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2025-09-11 21:54:43 +08:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
22ebc823c9 | ||
![]() |
49f0277103 | ||
![]() |
ac10603fdb | ||
![]() |
a3f51ecf22 | ||
![]() |
78897ef1f4 | ||
![]() |
debcbc668c | ||
![]() |
1a7a550bfe | ||
![]() |
4ce8f6d250 | ||
![]() |
7b08dae6d0 | ||
![]() |
521d1125d9 | ||
![]() |
0903ad1608 | ||
![]() |
399549d180 | ||
![]() |
7bdca3ee68 | ||
![]() |
eb313e0f2d | ||
![]() |
d3ee655a90 |
20
CHANGELOG.md
20
CHANGELOG.md
@@ -1,8 +1,24 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
current (development)
|
||||
---------------------
|
||||
4.1.0
|
||||
-----
|
||||
|
||||
### 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
|
||||
-----
|
||||
|
@@ -2,7 +2,8 @@ cmake_minimum_required(VERSION 3.11)
|
||||
|
||||
project(ftxui
|
||||
LANGUAGES CXX
|
||||
VERSION 4.0.0
|
||||
VERSION 4.1.0
|
||||
DESCRIPTION "C++ Functional Terminal User Interface."
|
||||
)
|
||||
|
||||
option(FTXUI_BUILD_DOCS "Set to ON to build docs" ON)
|
||||
@@ -42,6 +43,7 @@ add_library(screen
|
||||
|
||||
add_library(dom
|
||||
include/ftxui/dom/canvas.hpp
|
||||
include/ftxui/dom/direction.hpp
|
||||
include/ftxui/dom/elements.hpp
|
||||
include/ftxui/dom/flexbox_config.hpp
|
||||
include/ftxui/dom/node.hpp
|
||||
@@ -71,6 +73,7 @@ add_library(dom
|
||||
src/ftxui/dom/gridbox.cpp
|
||||
src/ftxui/dom/hbox.cpp
|
||||
src/ftxui/dom/inverted.cpp
|
||||
src/ftxui/dom/linear_gradient.cpp
|
||||
src/ftxui/dom/node.cpp
|
||||
src/ftxui/dom/node_decorator.cpp
|
||||
src/ftxui/dom/paragraph.cpp
|
||||
|
@@ -4,6 +4,7 @@ endif()
|
||||
|
||||
set(CMAKE_C_COMPILER clang)
|
||||
set(CMAKE_CXX_COMPILER clang++)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
function(fuzz name)
|
||||
add_executable(${name}
|
||||
|
@@ -11,6 +11,7 @@ install(TARGETS screen dom component
|
||||
|
||||
install(DIRECTORY include/ftxui DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
# CMake package configuration for easy use of library in CMake
|
||||
include(CMakePackageConfigHelpers)
|
||||
configure_package_config_file(ftxui-config.cmake.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ftxui-config.cmake
|
||||
@@ -31,3 +32,12 @@ install(EXPORT ftxui-export
|
||||
NAMESPACE ftxui::
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ftxui
|
||||
)
|
||||
|
||||
# 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 lib/pkgconfig
|
||||
)
|
||||
|
@@ -43,9 +43,17 @@ function(ftxui_set_options library)
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
)
|
||||
|
||||
# Play nicely if we are being consumed by another project
|
||||
# and use their CMAKE_CXX_STANDARD. Otherwise, fall back to 17
|
||||
# C++17 is used. We require fold expression at least.
|
||||
if (DEFINED CMAKE_CXX_STANDARD)
|
||||
set(CXX_STANDARD ${CMAKE_CXX_STANDARD})
|
||||
else()
|
||||
set(CXX_STANDARD 17)
|
||||
endif()
|
||||
|
||||
set_target_properties(${library} PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD ${CXX_STANDARD}
|
||||
CXX_EXTENSIONS OFF
|
||||
)
|
||||
|
||||
|
@@ -37,6 +37,7 @@ add_executable(ftxui-tests
|
||||
src/ftxui/dom/gauge_test.cpp
|
||||
src/ftxui/dom/gridbox_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/separator_test.cpp
|
||||
src/ftxui/dom/spinner_test.cpp
|
||||
|
@@ -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>
|
||||
@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}
|
||||
In addition to colored text and colored backgrounds. Many terminals support text
|
||||
effects such as: `bold`, `dim`, `underlined`, `inverted`, `blink`.
|
||||
@@ -458,6 +488,8 @@ Element strikethrough(Element);
|
||||
Element blink(Element);
|
||||
Decorator color(Color);
|
||||
Decorator bgcolor(Color);
|
||||
Decorator colorgrad(LinearGradient);
|
||||
Decorator bgcolorgrad(LinearGradient);
|
||||
```
|
||||
|
||||
[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):
|
||||
|
||||

|
||||
|
||||

|
||||
# component {#module-component}
|
||||
|
||||
The `ftxui::component` module defines the logic that produces interactive
|
||||
|
@@ -17,6 +17,7 @@ example(focus_cursor)
|
||||
example(gallery)
|
||||
example(homescreen)
|
||||
example(input)
|
||||
example(linear_gradient_gallery)
|
||||
example(maybe)
|
||||
example(menu)
|
||||
example(menu2)
|
||||
|
56
examples/component/linear_gradient_gallery.cpp
Normal file
56
examples/component/linear_gradient_gallery.cpp
Normal 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.
|
@@ -2,9 +2,10 @@
|
||||
#include <cmath> // for sin
|
||||
#include <ftxui/component/component_base.hpp> // for ComponentBase
|
||||
#include <ftxui/component/component_options.hpp> // for SliderOption
|
||||
#include <ftxui/dom/elements.hpp> // for size, GREATER_THAN, GaugeDirection, GaugeDirection::Up, HEIGHT
|
||||
#include <ftxui/util/ref.hpp> // for ConstRef, Ref
|
||||
#include <memory> // for shared_ptr, __shared_ptr_access
|
||||
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Up
|
||||
#include <ftxui/dom/elements.hpp> // for size, GREATER_THAN, HEIGHT
|
||||
#include <ftxui/util/ref.hpp> // for ConstRef, Ref
|
||||
#include <memory> // for shared_ptr, __shared_ptr_access
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for ftxui
|
||||
#include "ftxui/component/component.hpp" // for Horizontal, Slider, operator|=
|
||||
@@ -26,7 +27,7 @@ int main(int argc, const char* argv[]) {
|
||||
option.value = &values[i];
|
||||
option.max = 100;
|
||||
option.increment = 5;
|
||||
option.direction = GaugeDirection::Up;
|
||||
option.direction = Direction::Up;
|
||||
layout_horizontal->Add(Slider<int>(option));
|
||||
|
||||
/* In C++20:
|
||||
@@ -34,7 +35,7 @@ int main(int argc, const char* argv[]) {
|
||||
.value = &values[i],
|
||||
.max = 100,
|
||||
.increment = 5,
|
||||
.direction = GaugeDirection::Up,
|
||||
.direction = Direction::Up,
|
||||
}));
|
||||
*/
|
||||
}
|
||||
|
@@ -1,19 +1,21 @@
|
||||
set(DIRECTORY_LIB dom)
|
||||
|
||||
example(border)
|
||||
example(border_colored)
|
||||
example(border_style)
|
||||
example(canvas)
|
||||
example(color_gallery)
|
||||
example(color_info_palette256)
|
||||
example(color_truecolor_HSV)
|
||||
example(color_truecolor_RGB)
|
||||
example(dbox)
|
||||
example(canvas)
|
||||
example(gauge)
|
||||
example(gauge_direction)
|
||||
example(graph)
|
||||
example(gridbox)
|
||||
example(hflow)
|
||||
example(html_like)
|
||||
example(linear_gradient)
|
||||
example(package_manager)
|
||||
example(paragraph)
|
||||
example(separator)
|
||||
|
40
examples/dom/border_colored.cpp
Normal file
40
examples/dom/border_colored.cpp
Normal 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.
|
@@ -11,6 +11,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
auto document = vbox({
|
||||
text("borderLight") | borderLight,
|
||||
text("borderDashed") | borderDashed,
|
||||
text("borderHeavy") | borderHeavy,
|
||||
text("borderDouble") | borderDouble,
|
||||
text("borderRounded") | borderRounded,
|
||||
|
26
examples/dom/linear_gradient.cpp
Normal file
26
examples/dom/linear_gradient.cpp
Normal 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.
|
@@ -16,6 +16,12 @@ int main(int argc, const char* argv[]) {
|
||||
hbox(text("left"), separatorLight(), text("right")),
|
||||
}) | borderLight,
|
||||
|
||||
vbox({
|
||||
text("separatorDashed"),
|
||||
separatorDashed(),
|
||||
hbox(text("left"), separatorDashed(), text("right")),
|
||||
}) | borderDashed,
|
||||
|
||||
vbox({
|
||||
text("separatorHeavy"),
|
||||
separatorHeavy(),
|
||||
|
@@ -1,59 +1,60 @@
|
||||
#include <ftxui/screen/screen.hpp> // for Full, Screen
|
||||
#include <memory> // for allocator
|
||||
#include <ftxui/dom/linear_gradient.hpp> // for LinearGradient
|
||||
#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/screen/box.hpp" // for 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
|
||||
#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
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
using namespace ftxui;
|
||||
// clang-format off
|
||||
auto document =
|
||||
hbox(
|
||||
vbox(
|
||||
color(Color::Default, text("Default")),
|
||||
color(Color::Black, text("Black")),
|
||||
color(Color::GrayDark, text("GrayDark")),
|
||||
color(Color::GrayLight, text("GrayLight")),
|
||||
color(Color::White, text("White")),
|
||||
color(Color::Blue, text("Blue")),
|
||||
color(Color::BlueLight, text("BlueLight")),
|
||||
color(Color::Cyan, text("Cyan")),
|
||||
color(Color::CyanLight, text("CyanLight")),
|
||||
color(Color::Green, text("Green")),
|
||||
color(Color::GreenLight, text("GreenLight")),
|
||||
color(Color::Magenta, text("Magenta")),
|
||||
color(Color::MagentaLight, text("MagentaLight")),
|
||||
color(Color::Red, text("Red")),
|
||||
color(Color::RedLight, text("RedLight")),
|
||||
color(Color::Yellow, text("Yellow")),
|
||||
color(Color::YellowLight, text("YellowLight")),
|
||||
color(0x66ff66_rgb, text("Phosphor"))
|
||||
),
|
||||
vbox(
|
||||
bgcolor(Color::Default, text("Default")),
|
||||
bgcolor(Color::Black, text("Black")),
|
||||
bgcolor(Color::GrayDark, text("GrayDark")),
|
||||
bgcolor(Color::GrayLight, text("GrayLight")),
|
||||
bgcolor(Color::White, text("White")),
|
||||
bgcolor(Color::Blue, text("Blue")),
|
||||
bgcolor(Color::BlueLight, text("BlueLight")),
|
||||
bgcolor(Color::Cyan, text("Cyan")),
|
||||
bgcolor(Color::CyanLight, text("CyanLight")),
|
||||
bgcolor(Color::Green, text("Green")),
|
||||
bgcolor(Color::GreenLight, text("GreenLight")),
|
||||
bgcolor(Color::Magenta, text("Magenta")),
|
||||
bgcolor(Color::MagentaLight, text("MagentaLight")),
|
||||
bgcolor(Color::Red, text("Red")),
|
||||
bgcolor(Color::RedLight, text("RedLight")),
|
||||
bgcolor(Color::Yellow, text("Yellow")),
|
||||
bgcolor(Color::YellowLight, text("YellowLight")),
|
||||
bgcolor(0x66ff66_rgb, text("Phosphor"))
|
||||
),
|
||||
filler()
|
||||
);
|
||||
// clang-format on
|
||||
auto document = hbox({
|
||||
vbox({
|
||||
color(Color::Default, text("Default")),
|
||||
color(Color::Black, text("Black")),
|
||||
color(Color::GrayDark, text("GrayDark")),
|
||||
color(Color::GrayLight, text("GrayLight")),
|
||||
color(Color::White, text("White")),
|
||||
color(Color::Blue, text("Blue")),
|
||||
color(Color::BlueLight, text("BlueLight")),
|
||||
color(Color::Cyan, text("Cyan")),
|
||||
color(Color::CyanLight, text("CyanLight")),
|
||||
color(Color::Green, text("Green")),
|
||||
color(Color::GreenLight, text("GreenLight")),
|
||||
color(Color::Magenta, text("Magenta")),
|
||||
color(Color::MagentaLight, text("MagentaLight")),
|
||||
color(Color::Red, text("Red")),
|
||||
color(Color::RedLight, text("RedLight")),
|
||||
color(Color::Yellow, text("Yellow")),
|
||||
color(Color::YellowLight, text("YellowLight")),
|
||||
color(0x66ff66_rgb, text("Phosphor")),
|
||||
color(LinearGradient(Color::SkyBlue1, Color::DeepSkyBlue4),
|
||||
text("Skyblue to DeepSkyBlue")),
|
||||
}),
|
||||
vbox({
|
||||
bgcolor(Color::Default, text("Default")),
|
||||
bgcolor(Color::Black, text("Black")),
|
||||
bgcolor(Color::GrayDark, text("GrayDark")),
|
||||
bgcolor(Color::GrayLight, text("GrayLight")),
|
||||
bgcolor(Color::White, text("White")),
|
||||
bgcolor(Color::Blue, text("Blue")),
|
||||
bgcolor(Color::BlueLight, text("BlueLight")),
|
||||
bgcolor(Color::Cyan, text("Cyan")),
|
||||
bgcolor(Color::CyanLight, text("CyanLight")),
|
||||
bgcolor(Color::Green, text("Green")),
|
||||
bgcolor(Color::GreenLight, text("GreenLight")),
|
||||
bgcolor(Color::Magenta, text("Magenta")),
|
||||
bgcolor(Color::MagentaLight, text("MagentaLight")),
|
||||
bgcolor(Color::Red, text("Red")),
|
||||
bgcolor(Color::RedLight, text("RedLight")),
|
||||
bgcolor(Color::Yellow, text("Yellow")),
|
||||
bgcolor(Color::YellowLight, text("YellowLight")),
|
||||
bgcolor(0x66ff66_rgb, text("Phosphor")),
|
||||
bgcolor(LinearGradient(Color::SkyBlue1, Color::DeepSkyBlue4),
|
||||
text("Skyblue to DeepSkyBlue")),
|
||||
}),
|
||||
filler(),
|
||||
});
|
||||
|
||||
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
|
||||
Render(screen, document);
|
||||
|
43
flake.lock
generated
Normal file
43
flake.lock
generated
Normal 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
61
flake.nix
Normal 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
9
ftxui.pc.in
Normal 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
|
@@ -18,7 +18,7 @@ void RequestAnimationFrame();
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using TimePoint = std::chrono::time_point<Clock>;
|
||||
using Duration = std::chrono::duration<double>;
|
||||
using Duration = std::chrono::duration<float>;
|
||||
|
||||
// Parameter of Component::OnAnimation(param).
|
||||
class Params {
|
||||
|
@@ -92,6 +92,7 @@ Component ResizableSplitLeft(Component main, Component back, int* main_size);
|
||||
Component ResizableSplitRight(Component main, Component back, int* main_size);
|
||||
Component ResizableSplitTop(Component main, Component back, int* main_size);
|
||||
Component ResizableSplitBottom(Component main, Component back, int* main_size);
|
||||
Component ResizableSplit(ResizableSplitOption options);
|
||||
|
||||
Component Renderer(Component child, std::function<Element()>);
|
||||
Component Renderer(std::function<Element()>);
|
||||
|
@@ -3,12 +3,14 @@
|
||||
|
||||
#include <chrono> // for milliseconds
|
||||
#include <ftxui/component/animation.hpp> // for Duration, QuadraticInOut, Function
|
||||
#include <ftxui/dom/elements.hpp> // for Element, GaugeDirection, GaugeDirection::Right
|
||||
#include <ftxui/util/ref.hpp> // for Ref, ConstRef
|
||||
#include <functional> // for function
|
||||
#include <optional> // for optional
|
||||
#include <string> // for string
|
||||
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Left, Direction::Right, Direction::Down
|
||||
#include <ftxui/dom/elements.hpp> // for Element, separator
|
||||
#include <ftxui/util/ref.hpp> // for Ref, ConstRef
|
||||
#include <functional> // for function
|
||||
#include <optional> // for optional
|
||||
#include <string> // for string
|
||||
|
||||
#include "ftxui/component/component_base.hpp" // for Component
|
||||
#include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::White
|
||||
|
||||
namespace ftxui {
|
||||
@@ -87,8 +89,7 @@ struct MenuOption {
|
||||
// Style:
|
||||
UnderlineOption underline;
|
||||
MenuEntryOption entries;
|
||||
enum Direction { Up, Down, Left, Right };
|
||||
Direction direction = Down;
|
||||
Direction direction = Direction::Down;
|
||||
std::function<Element()> elements_prefix;
|
||||
std::function<Element()> elements_infix;
|
||||
std::function<Element()> elements_postfix;
|
||||
@@ -164,6 +165,16 @@ struct RadioboxOption {
|
||||
Ref<int> focused_entry = 0;
|
||||
};
|
||||
|
||||
struct ResizableSplitOption {
|
||||
Component main;
|
||||
Component back;
|
||||
Ref<Direction> direction = Direction::Left;
|
||||
Ref<int> main_size =
|
||||
(direction() == Direction::Left || direction() == Direction::Right) ? 20
|
||||
: 10;
|
||||
std::function<Element()> separator_func = [] { return ::ftxui::separator(); };
|
||||
};
|
||||
|
||||
// @brief Option for the `Slider` component.
|
||||
// @ingroup component
|
||||
template <typename T>
|
||||
@@ -172,7 +183,7 @@ struct SliderOption {
|
||||
ConstRef<T> min = T(0);
|
||||
ConstRef<T> max = T(100);
|
||||
ConstRef<T> increment = (max() - min()) / 20;
|
||||
GaugeDirection direction = GaugeDirection::Right;
|
||||
Direction direction = Direction::Right;
|
||||
Color color_active = Color::White;
|
||||
Color color_inactive = Color::GrayDark;
|
||||
};
|
||||
|
17
include/ftxui/dom/direction.hpp
Normal file
17
include/ftxui/dom/direction.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef FTXUI_DOM_DIRECTION_HPP
|
||||
#define FTXUI_DOM_DIRECTION_HPP
|
||||
|
||||
namespace ftxui {
|
||||
enum class Direction {
|
||||
Up = 0,
|
||||
Down = 1,
|
||||
Left = 2,
|
||||
Right = 3,
|
||||
};
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
#endif // FTXUI_DOM_DIRECTION_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.
|
@@ -5,7 +5,9 @@
|
||||
#include <memory>
|
||||
|
||||
#include "ftxui/dom/canvas.hpp"
|
||||
#include "ftxui/dom/direction.hpp"
|
||||
#include "ftxui/dom/flexbox_config.hpp"
|
||||
#include "ftxui/dom/linear_gradient.hpp"
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/screen/box.hpp"
|
||||
#include "ftxui/screen/color.hpp"
|
||||
@@ -20,8 +22,14 @@ using Elements = std::vector<Element>;
|
||||
using Decorator = std::function<Element(Element)>;
|
||||
using GraphFunction = std::function<std::vector<int>(int, int)>;
|
||||
|
||||
enum BorderStyle { LIGHT, HEAVY, DOUBLE, ROUNDED, EMPTY };
|
||||
enum class GaugeDirection { Left, Up, Right, Down };
|
||||
enum BorderStyle {
|
||||
LIGHT,
|
||||
DASHED,
|
||||
HEAVY,
|
||||
DOUBLE,
|
||||
ROUNDED,
|
||||
EMPTY,
|
||||
};
|
||||
|
||||
// Pipe elements into decorator togethers.
|
||||
// For instance the next lines are equivalents:
|
||||
@@ -37,6 +45,7 @@ Element text(std::string text);
|
||||
Element vtext(std::string text);
|
||||
Element separator();
|
||||
Element separatorLight();
|
||||
Element separatorDashed();
|
||||
Element separatorHeavy();
|
||||
Element separatorDouble();
|
||||
Element separatorEmpty();
|
||||
@@ -56,14 +65,17 @@ Element gaugeLeft(float progress);
|
||||
Element gaugeRight(float progress);
|
||||
Element gaugeUp(float progress);
|
||||
Element gaugeDown(float progress);
|
||||
Element gaugeDirection(float progress, GaugeDirection);
|
||||
Element gaugeDirection(float progress, Direction direction);
|
||||
Element border(Element);
|
||||
Element borderLight(Element);
|
||||
Element borderDashed(Element);
|
||||
Element borderHeavy(Element);
|
||||
Element borderDouble(Element);
|
||||
Element borderRounded(Element);
|
||||
Element borderEmpty(Element);
|
||||
Decorator borderStyled(BorderStyle);
|
||||
Decorator borderStyled(BorderStyle, Color);
|
||||
Decorator borderStyled(Color);
|
||||
Decorator borderWith(const Pixel&);
|
||||
Element window(Element title, Element content);
|
||||
Element spinner(int charset_index, size_t image_index);
|
||||
@@ -88,8 +100,12 @@ Element blink(Element);
|
||||
Element strikethrough(Element);
|
||||
Decorator color(Color);
|
||||
Decorator bgcolor(Color);
|
||||
Decorator color(const LinearGradient&);
|
||||
Decorator bgcolor(const LinearGradient&);
|
||||
Element color(Color, Element);
|
||||
Element bgcolor(Color, Element);
|
||||
Element color(const LinearGradient&, Element);
|
||||
Element bgcolor(const LinearGradient&, Element);
|
||||
Decorator focusPosition(int x, int y);
|
||||
Decorator focusPositionRelative(float x, float y);
|
||||
Element automerge(Element child);
|
||||
@@ -124,9 +140,9 @@ Element notflex(Element); // Reset the flex attribute.
|
||||
Element filler(); // A blank expandable element.
|
||||
|
||||
// -- Size override;
|
||||
enum Direction { WIDTH, HEIGHT };
|
||||
enum WidthOrHeight { WIDTH, HEIGHT };
|
||||
enum Constraint { LESS_THAN, EQUAL, GREATER_THAN };
|
||||
Decorator size(Direction, Constraint, int value);
|
||||
Decorator size(WidthOrHeight, Constraint, int value);
|
||||
|
||||
// --- Frame ---
|
||||
// A frame is a scrollable area. The internal area is potentially larger than
|
||||
|
52
include/ftxui/dom/linear_gradient.hpp
Normal file
52
include/ftxui/dom/linear_gradient.hpp
Normal 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.
|
@@ -2,7 +2,8 @@
|
||||
#define FTXUI_SCREEN_COLOR_HPP
|
||||
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <string> // for wstring
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
#ifdef RGB
|
||||
// Workaround for wingdi.h (via Windows.h) defining macros that break things.
|
||||
|
@@ -9,8 +9,8 @@ namespace ftxui::animation {
|
||||
namespace easing {
|
||||
|
||||
namespace {
|
||||
constexpr float kPi = 3.14159265358979323846F;
|
||||
constexpr float kPi2 = kPi / 2.F;
|
||||
constexpr float kPi = 3.14159265358979323846f;
|
||||
constexpr float kPi2 = kPi / 2.f;
|
||||
} // namespace
|
||||
|
||||
// Easing function have been taken out of:
|
||||
@@ -37,18 +37,16 @@ float QuadraticIn(float p) {
|
||||
|
||||
// Modeled after the parabola y = -x^2 + 2x
|
||||
float QuadraticOut(float p) {
|
||||
return -(p * (p - 2));
|
||||
return -(p * (p - 2.f));
|
||||
}
|
||||
|
||||
// Modeled after the piecewise quadratic
|
||||
// y = (1/2)((2x)^2) ; [0, 0.5)
|
||||
// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1]
|
||||
float QuadraticInOut(float p) {
|
||||
if (p < 0.5F) { // NOLINT
|
||||
return 2 * p * p;
|
||||
} else {
|
||||
return (-2 * p * p) + (4 * p) - 1;
|
||||
}
|
||||
return p < 0.5f // NOLINT
|
||||
? 2.f * p * p // NOLINT
|
||||
: (-2.f * p * p) + (4.f * p) - 1.f; // NOLINT
|
||||
}
|
||||
|
||||
// Modeled after the cubic y = x^3
|
||||
@@ -58,20 +56,19 @@ float CubicIn(float p) {
|
||||
|
||||
// Modeled after the cubic y = (x - 1)^3 + 1
|
||||
float CubicOut(float p) {
|
||||
const float f = (p - 1);
|
||||
return f * f * f + 1;
|
||||
const float f = (p - 1.f);
|
||||
return f * f * f + 1.f;
|
||||
}
|
||||
|
||||
// Modeled after the piecewise cubic
|
||||
// y = (1/2)((2x)^3) ; [0, 0.5)
|
||||
// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1]
|
||||
float CubicInOut(float p) {
|
||||
if (p < 0.5F) { // NOLINT
|
||||
return 4 * p * p * p;
|
||||
} else {
|
||||
const float f = ((2 * p) - 2);
|
||||
return 0.5F * f * f * f + 1; // NOLINT
|
||||
if (p < 0.5f) { // NOLINT
|
||||
return 4.f * p * p * p;
|
||||
}
|
||||
const float f = ((2.f * p) - 2.f);
|
||||
return 0.5f * f * f * f + 1.f; // NOLINT
|
||||
}
|
||||
|
||||
// Modeled after the quartic x^4
|
||||
@@ -81,20 +78,19 @@ float QuarticIn(float p) {
|
||||
|
||||
// Modeled after the quartic y = 1 - (x - 1)^4
|
||||
float QuarticOut(float p) {
|
||||
const float f = (p - 1);
|
||||
return f * f * f * (1 - p) + 1;
|
||||
const float f = (p - 1.f);
|
||||
return f * f * f * (1.f - p) + 1.f;
|
||||
}
|
||||
|
||||
// Modeled after the piecewise quartic
|
||||
// y = (1/2)((2x)^4) ; [0, 0.5)
|
||||
// y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1]
|
||||
float QuarticInOut(float p) {
|
||||
if (p < 0.5F) { // NOLINT
|
||||
return 8 * p * p * p * p; // NOLINT
|
||||
} else {
|
||||
const float f = (p - 1);
|
||||
return -8 * f * f * f * f + 1; // NOLINT
|
||||
if (p < 0.5f) { // NOLINT
|
||||
return 8.f * p * p * p * p; // NOLINT
|
||||
}
|
||||
const float f = (p - 1.f);
|
||||
return -8.f * f * f * f * f + 1.f; // NOLINT
|
||||
}
|
||||
|
||||
// Modeled after the quintic y = x^5
|
||||
@@ -104,25 +100,24 @@ float QuinticIn(float p) {
|
||||
|
||||
// Modeled after the quintic y = (x - 1)^5 + 1
|
||||
float QuinticOut(float p) {
|
||||
const float f = (p - 1);
|
||||
return f * f * f * f * f + 1;
|
||||
const float f = (p - 1.f);
|
||||
return f * f * f * f * f + 1.f;
|
||||
}
|
||||
|
||||
// Modeled after the piecewise quintic
|
||||
// y = (1/2)((2x)^5) ; [0, 0.5)
|
||||
// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1]
|
||||
float QuinticInOut(float p) {
|
||||
if (p < 0.5F) { // NOLINT
|
||||
return 16 * p * p * p * p * p; // NOLINT
|
||||
} else { // NOLINT
|
||||
float f = ((2 * p) - 2); // NOLINT
|
||||
return 0.5 * f * f * f * f * f + 1; // NOLINT
|
||||
if (p < 0.5f) { // NOLINT
|
||||
return 16.f * p * p * p * p * p; // NOLINT
|
||||
}
|
||||
float f = ((2.f * p) - 2.f); // NOLINT
|
||||
return 0.5f * f * f * f * f * f + 1.f; // NOLINT
|
||||
}
|
||||
|
||||
// Modeled after quarter-cycle of sine wave
|
||||
float SineIn(float p) {
|
||||
return std::sin((p - 1) * kPi2) + 1;
|
||||
return std::sin((p - 1.f) * kPi2) + 1.f;
|
||||
}
|
||||
|
||||
// Modeled after quarter-cycle of sine wave (different phase)
|
||||
@@ -132,79 +127,77 @@ float SineOut(float p) {
|
||||
|
||||
// Modeled after half sine wave
|
||||
float SineInOut(float p) {
|
||||
return 0.5F * (1 - std::cos(p * kPi)); // NOLINT
|
||||
return 0.5f * (1.f - std::cos(p * kPi)); // NOLINT
|
||||
}
|
||||
|
||||
// Modeled after shifted quadrant IV of unit circle
|
||||
float CircularIn(float p) {
|
||||
return 1 - std::sqrt(1 - (p * p));
|
||||
return 1.f - std::sqrt(1.f - (p * p));
|
||||
}
|
||||
|
||||
// Modeled after shifted quadrant II of unit circle
|
||||
float CircularOut(float p) {
|
||||
return std::sqrt((2 - p) * p);
|
||||
return std::sqrt((2.f - p) * p);
|
||||
}
|
||||
|
||||
// Modeled after the piecewise circular function
|
||||
// y = (1/2)(1 - sqrt(1 - 4x^2)) ; [0, 0.5)
|
||||
// y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1]
|
||||
float CircularInOut(float p) {
|
||||
if (p < 0.5F) { // NOLINT
|
||||
return 0.5F * (1 - std::sqrt(1 - 4 * (p * p))); // NOLINT
|
||||
} else {
|
||||
return 0.5F * (std::sqrt(-((2 * p) - 3) * ((2 * p) - 1)) + 1); // NOLINT
|
||||
if (p < 0.5f) { // NOLINT
|
||||
return 0.5f * (1.f - std::sqrt(1.f - 4.f * (p * p))); // NOLINT
|
||||
}
|
||||
// NOLINTNEXTLINE
|
||||
return 0.5f * (std::sqrt(-((2.f * p) - 3.f) * ((2.f * p) - 1.f)) + 1.f);
|
||||
}
|
||||
|
||||
// Modeled after the exponential function y = 2^(10(x - 1))
|
||||
float ExponentialIn(float p) {
|
||||
return (p == 0.0) ? p : std::pow(2, 10 * (p - 1)); // NOLINT
|
||||
return (p == 0.f) ? p : std::pow(2.f, 10.f * (p - 1.f)); // NOLINT
|
||||
}
|
||||
|
||||
// Modeled after the exponential function y = -2^(-10x) + 1
|
||||
float ExponentialOut(float p) {
|
||||
return (p == 1.0) ? p : 1 - std::pow(2, -10 * p); // NOLINT
|
||||
return (p == 1.f) ? p : 1.f - std::pow(2.f, -10.f * p); // NOLINT
|
||||
}
|
||||
|
||||
// Modeled after the piecewise exponential
|
||||
// y = (1/2)2^(10(2x - 1)) ; [0,0.5)
|
||||
// y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1]
|
||||
float ExponentialInOut(float p) {
|
||||
if (p == 0.0 || p == 1.F) {
|
||||
if (p == 0.f || p == 1.f) {
|
||||
return p;
|
||||
}
|
||||
|
||||
if (p < 0.5F) { // NOLINT
|
||||
return 0.5 * std::pow(2, (20 * p) - 10); // NOLINT
|
||||
} else { // NOLINT
|
||||
return -0.5 * std::pow(2, (-20 * p) + 10) + 1; // NOLINT
|
||||
if (p < 0.5f) { // NOLINT
|
||||
return 0.5f * std::pow(2.f, (20.f * p) - 10.f); // NOLINT
|
||||
}
|
||||
return -0.5f * std::pow(2.f, (-20.f * p) + 10.f) + 1.f; // NOLINT
|
||||
}
|
||||
|
||||
// Modeled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1))
|
||||
float ElasticIn(float p) {
|
||||
return std::sin(13.F * kPi2 * p) * std::pow(2.F, 10.F * (p - 1)); // NOLINT
|
||||
return std::sin(13.f * kPi2 * p) * std::pow(2.f, 10.f * (p - 1.f)); // NOLINT
|
||||
}
|
||||
|
||||
// Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) +
|
||||
// 1
|
||||
float ElasticOut(float p) {
|
||||
// NOLINTNEXTLINE
|
||||
return std::sin(-13.F * kPi2 * (p + 1)) * std::pow(2.F, -10.F * p) + 1;
|
||||
return std::sin(-13.f * kPi2 * (p + 1.f)) * std::pow(2.f, -10.f * p) + 1.f;
|
||||
}
|
||||
|
||||
// Modeled after the piecewise exponentially-damped sine wave:
|
||||
// y = (1/2)*sin(13pi/2*(2*x))*pow(2, 10 * ((2*x) - 1)) ; [0,0.5)
|
||||
// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1]
|
||||
float ElasticInOut(float p) {
|
||||
if (p < 0.5F) { // NOLINT
|
||||
return 0.5 * std::sin(13.F * kPi2 * (2 * p)) * // NOLINT
|
||||
std::pow(2, 10 * ((2 * p) - 1)); // NOLINT
|
||||
} else { // NOLINT
|
||||
return 0.5 * (std::sin(-13.F * kPi2 * ((2 * p - 1) + 1)) * // NOLINT
|
||||
std::pow(2, -10 * (2 * p - 1)) + // NOLINT
|
||||
2); // NOLINT
|
||||
if (p < 0.5f) { // NOLINT
|
||||
return 0.5f * std::sin(13.f * kPi2 * (2.f * p)) * // NOLINT
|
||||
std::pow(2.f, 10.f * ((2.f * p) - 1.f)); // NOLINT
|
||||
}
|
||||
return 0.5f * (std::sin(-13.f * kPi2 * ((2.f * p - 1.f) + 1.f)) * // NOLINT
|
||||
std::pow(2.f, -10.f * (2.f * p - 1.f)) + // NOLINT
|
||||
2.f); // NOLINT
|
||||
}
|
||||
|
||||
// Modeled after the overshooting cubic y = x^3-x*sin(x*pi)
|
||||
@@ -214,46 +207,48 @@ float BackIn(float p) {
|
||||
|
||||
// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi))
|
||||
float BackOut(float p) {
|
||||
const float f = (1 - p);
|
||||
return 1 - (f * f * f - f * std::sin(f * kPi));
|
||||
const float f = (1.f - p);
|
||||
return 1.f - (f * f * f - f * std::sin(f * kPi));
|
||||
}
|
||||
|
||||
// Modeled after the piecewise overshooting cubic function:
|
||||
// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5)
|
||||
// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1]
|
||||
float BackInOut(float p) {
|
||||
if (p < 0.5F) { // NOLINT
|
||||
const float f = 2 * p;
|
||||
return 0.5F * (f * f * f - f * std::sin(f * kPi)); // NOLINT
|
||||
} else {
|
||||
float f = (1 - (2 * p - 1)); // NOLINT
|
||||
return 0.5F * (1 - (f * f * f - f * std::sin(f * kPi))) + 0.5; // NOLINT
|
||||
if (p < 0.5f) { // NOLINT
|
||||
const float f = 2.f * p;
|
||||
return 0.5f * (f * f * f - f * std::sin(f * kPi)); // NOLINT
|
||||
}
|
||||
const float f = (1.f - (2.f * p - 1.f)); // NOLINT
|
||||
return 0.5f * (1.f - (f * f * f - f * std::sin(f * kPi))) + 0.5f; // NOLINT
|
||||
}
|
||||
|
||||
float BounceIn(float p) {
|
||||
return 1 - BounceOut(1 - p);
|
||||
return 1.f - BounceOut(1.f - p);
|
||||
}
|
||||
|
||||
float BounceOut(float p) {
|
||||
if (p < 4 / 11.0) { // NOLINT
|
||||
return (121 * p * p) / 16.0; // NOLINT
|
||||
} else if (p < 8 / 11.0) { // NOLINT
|
||||
return (363 / 40.0 * p * p) - (99 / 10.0 * p) + 17 / 5.0; // NOLINT
|
||||
} else if (p < 9 / 10.0) { // NOLINT
|
||||
return (4356 / 361.0 * p * p) - (35442 / 1805.0 * p) + // NOLINT
|
||||
16061 / 1805.0; // NOLINT
|
||||
} else { // NOLINT
|
||||
return (54 / 5.0 * p * p) - (513 / 25.0 * p) + 268 / 25.0; // NOLINT
|
||||
if (p < 4.f / 11.f) { // NOLINT
|
||||
return (121.f * p * p) / 16.f; // NOLINT
|
||||
}
|
||||
|
||||
if (p < 8.f / 11.f) { // NOLINT
|
||||
return (363.f / 40.f * p * p) - (99.f / 10.f * p) + 17.f / 5.f; // NOLINT
|
||||
}
|
||||
|
||||
if (p < 9.f / 10.f) { // NOLINT
|
||||
return (4356.f / 361.f * p * p) - (35442.f / 1805.f * p) + // NOLINT
|
||||
16061.f / 1805.f; // NOLINT
|
||||
}
|
||||
|
||||
return (54.f / 5.f * p * p) - (513 / 25.f * p) + 268 / 25.f; // NOLINT
|
||||
}
|
||||
|
||||
float BounceInOut(float p) { // NOLINT
|
||||
if (p < 0.5F) { // NOLINT
|
||||
return 0.5F * BounceIn(p * 2); // NOLINT
|
||||
} else { // NOLINT
|
||||
return 0.5F * BounceOut(p * 2 - 1) + 0.5F; // NOLINT
|
||||
float BounceInOut(float p) { // NOLINT
|
||||
if (p < 0.5f) { // NOLINT
|
||||
return 0.5f * BounceIn(p * 2.f); // NOLINT
|
||||
}
|
||||
return 0.5f * BounceOut(p * 2.f - 1.f) + 0.5f; // NOLINT
|
||||
}
|
||||
|
||||
} // namespace easing
|
||||
|
@@ -124,10 +124,10 @@ TEST(ButtonTest, Animation) {
|
||||
Screen screen(12, 3);
|
||||
Render(screen, container->Render());
|
||||
EXPECT_EQ(screen.ToString(),
|
||||
"\x1B[1m\x1B[38;2;192;192;192m\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[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;191;191;191m\x1B[48;2;0;0;"
|
||||
"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");
|
||||
}
|
||||
selected = 1;
|
||||
@@ -135,10 +135,10 @@ TEST(ButtonTest, Animation) {
|
||||
Screen screen(12, 3);
|
||||
Render(screen, container->Render());
|
||||
EXPECT_EQ(screen.ToString(),
|
||||
"\x1B[38;2;192;192;192m\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[38;2;191;191;191m\x1B[48;2;0;0;0m \x1B[1m "
|
||||
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;191;191;191m\x1B[48;2;0;0;"
|
||||
"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");
|
||||
}
|
||||
animation::Params params(2s);
|
||||
@@ -148,12 +148,12 @@ TEST(ButtonTest, Animation) {
|
||||
Render(screen, container->Render());
|
||||
EXPECT_EQ(
|
||||
screen.ToString(),
|
||||
"\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m "
|
||||
"\x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m "
|
||||
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m "
|
||||
"btn1 \x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m btn2 "
|
||||
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m "
|
||||
" \x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m "
|
||||
"\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m "
|
||||
"\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;191;191;191m\x1B[48;2;0;0;0m "
|
||||
"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;191;191;191m\x1B[48;2;0;0;0m "
|
||||
" \x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m "
|
||||
"\x1B[22m\x1B[39m\x1B[49m");
|
||||
}
|
||||
EXPECT_EQ(selected, 1);
|
||||
@@ -164,12 +164,12 @@ TEST(ButtonTest, Animation) {
|
||||
Render(screen, container->Render());
|
||||
EXPECT_EQ(
|
||||
screen.ToString(),
|
||||
"\x1B[1m\x1B[38;2;223;223;223m\x1B[48;2;64;64;64m "
|
||||
"\x1B[22m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m "
|
||||
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;223;223;223m\x1B[48;2;64;64;64m "
|
||||
"btn1 \x1B[22m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m btn2 "
|
||||
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;223;223;223m\x1B[48;2;64;64;64m "
|
||||
" \x1B[22m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m "
|
||||
"\x1B[1m\x1B[38;2;226;226;226m\x1B[48;2;93;93;93m "
|
||||
"\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;226;226;226m\x1B[48;2;93;93;93m "
|
||||
"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;226;226;226m\x1B[48;2;93;93;93m "
|
||||
" \x1B[22m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m "
|
||||
"\x1B[39m\x1B[49m");
|
||||
}
|
||||
container->OnAnimation(params);
|
||||
@@ -178,12 +178,12 @@ TEST(ButtonTest, Animation) {
|
||||
Render(screen, container->Render());
|
||||
EXPECT_EQ(
|
||||
screen.ToString(),
|
||||
"\x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m "
|
||||
"\x1B[22m\x1B[38;2;192;192;192m\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;"
|
||||
"128m btn1 \x1B[22m\x1B[38;2;192;192;192m\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;"
|
||||
"128m \x1B[22m\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m "
|
||||
"\x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m "
|
||||
"\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;254;254;254m\x1B[48;2;127;127;"
|
||||
"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;254;254;254m\x1B[48;2;127;127;"
|
||||
"127m \x1B[22m\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m "
|
||||
"\x1B[39m\x1B[49m");
|
||||
}
|
||||
}
|
||||
|
@@ -96,8 +96,7 @@ MenuOption GeneratorMenuOption(const char* data, size_t size) {
|
||||
MenuOption option;
|
||||
option.underline = GeneratorUnderlineOption(data, size);
|
||||
option.entries = GeneratorMenuEntryOption(data, size);
|
||||
option.direction =
|
||||
static_cast<MenuOption::Direction>(GeneratorInt(data, size) % 4);
|
||||
option.direction = static_cast<Direction>(GeneratorInt(data, size) % 4);
|
||||
return option;
|
||||
}
|
||||
|
||||
|
@@ -1,16 +1,17 @@
|
||||
#include <algorithm> // for max, fill_n, reverse
|
||||
#include <chrono> // for milliseconds
|
||||
#include <functional> // for function
|
||||
#include <memory> // for allocator_traits<>::value_type, swap
|
||||
#include <string> // for operator+, string
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector, __alloc_traits<>::value_type
|
||||
#include <algorithm> // for max, fill_n, reverse
|
||||
#include <chrono> // for milliseconds
|
||||
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
|
||||
#include <functional> // for function
|
||||
#include <memory> // for allocator_traits<>::value_type, swap
|
||||
#include <string> // for operator+, string
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector, __alloc_traits<>::value_type
|
||||
|
||||
#include "ftxui/component/animation.hpp" // for Animator, Linear
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
#include "ftxui/component/component.hpp" // for Make, Menu, MenuEntry, Toggle
|
||||
#include "ftxui/component/component_base.hpp" // for ComponentBase
|
||||
#include "ftxui/component/component_options.hpp" // for MenuOption, MenuEntryOption, MenuOption::Direction, UnderlineOption, AnimatedColorOption, AnimatedColorsOption, EntryState, MenuOption::Down, MenuOption::Left, MenuOption::Right, MenuOption::Up
|
||||
#include "ftxui/component/component_options.hpp" // for MenuOption, MenuEntryOption, UnderlineOption, AnimatedColorOption, AnimatedColorsOption, EntryState
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp, Event::End, Event::Home, Event::PageDown, Event::PageUp, Event::Return, Event::Tab, Event::TabReverse
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Released, Mouse::WheelDown, Mouse::WheelUp, Mouse::None
|
||||
#include "ftxui/component/screen_interactive.hpp" // for Component
|
||||
@@ -36,25 +37,25 @@ Element DefaultOptionTransform(const EntryState& state) {
|
||||
return e;
|
||||
}
|
||||
|
||||
bool IsInverted(MenuOption::Direction direction) {
|
||||
bool IsInverted(Direction direction) {
|
||||
switch (direction) {
|
||||
case MenuOption::Direction::Up:
|
||||
case MenuOption::Direction::Left:
|
||||
case Direction::Up:
|
||||
case Direction::Left:
|
||||
return true;
|
||||
case MenuOption::Direction::Down:
|
||||
case MenuOption::Direction::Right:
|
||||
case Direction::Down:
|
||||
case Direction::Right:
|
||||
return false;
|
||||
}
|
||||
return false; // NOT_REACHED()
|
||||
}
|
||||
|
||||
bool IsHorizontal(MenuOption::Direction direction) {
|
||||
bool IsHorizontal(Direction direction) {
|
||||
switch (direction) {
|
||||
case MenuOption::Direction::Left:
|
||||
case MenuOption::Direction::Right:
|
||||
case Direction::Left:
|
||||
case Direction::Right:
|
||||
return true;
|
||||
case MenuOption::Direction::Down:
|
||||
case MenuOption::Direction::Up:
|
||||
case Direction::Down:
|
||||
case Direction::Up:
|
||||
return false;
|
||||
}
|
||||
return false; // NOT_REACHED()
|
||||
@@ -178,56 +179,56 @@ class MenuBase : public ComponentBase {
|
||||
|
||||
void OnUp() {
|
||||
switch (option_->direction) {
|
||||
case MenuOption::Direction::Up:
|
||||
case Direction::Up:
|
||||
(*selected_)++;
|
||||
break;
|
||||
case MenuOption::Direction::Down:
|
||||
case Direction::Down:
|
||||
(*selected_)--;
|
||||
break;
|
||||
case MenuOption::Direction::Left:
|
||||
case MenuOption::Direction::Right:
|
||||
case Direction::Left:
|
||||
case Direction::Right:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDown() {
|
||||
switch (option_->direction) {
|
||||
case MenuOption::Direction::Up:
|
||||
case Direction::Up:
|
||||
(*selected_)--;
|
||||
break;
|
||||
case MenuOption::Direction::Down:
|
||||
case Direction::Down:
|
||||
(*selected_)++;
|
||||
break;
|
||||
case MenuOption::Direction::Left:
|
||||
case MenuOption::Direction::Right:
|
||||
case Direction::Left:
|
||||
case Direction::Right:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnLeft() {
|
||||
switch (option_->direction) {
|
||||
case MenuOption::Direction::Left:
|
||||
case Direction::Left:
|
||||
(*selected_)++;
|
||||
break;
|
||||
case MenuOption::Direction::Right:
|
||||
case Direction::Right:
|
||||
(*selected_)--;
|
||||
break;
|
||||
case MenuOption::Direction::Down:
|
||||
case MenuOption::Direction::Up:
|
||||
case Direction::Down:
|
||||
case Direction::Up:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnRight() {
|
||||
switch (option_->direction) {
|
||||
case MenuOption::Direction::Left:
|
||||
case Direction::Left:
|
||||
(*selected_)--;
|
||||
break;
|
||||
case MenuOption::Direction::Right:
|
||||
case Direction::Right:
|
||||
(*selected_)++;
|
||||
break;
|
||||
case MenuOption::Direction::Down:
|
||||
case MenuOption::Direction::Up:
|
||||
case Direction::Down:
|
||||
case Direction::Up:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#include <gtest/gtest.h> // for Test, EXPECT_EQ, Message, TestPartResult, TestInfo (ptr only), TEST
|
||||
#include <chrono> // for operator""s, chrono_literals
|
||||
#include <chrono> // for operator""s, chrono_literals
|
||||
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
|
||||
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
|
||||
#include <string> // for string, basic_string
|
||||
#include <vector> // for vector
|
||||
@@ -7,7 +8,7 @@
|
||||
#include "ftxui/component/animation.hpp" // for Duration, Params
|
||||
#include "ftxui/component/component.hpp" // for Menu
|
||||
#include "ftxui/component/component_base.hpp" // for ComponentBase
|
||||
#include "ftxui/component/component_options.hpp" // for MenuOption, MenuOption::Down, MenuOption::Left, MenuOption::Right, MenuOption::Up
|
||||
#include "ftxui/component/component_options.hpp" // for MenuOption
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp, Event::Return
|
||||
#include "ftxui/dom/node.hpp" // for Render
|
||||
#include "ftxui/screen/screen.hpp" // for Screen
|
||||
@@ -53,7 +54,7 @@ TEST(MenuTest, DirectionDown) {
|
||||
auto menu = Menu(&entries, &selected, &option);
|
||||
|
||||
selected = 0;
|
||||
option.direction = MenuOption::Down;
|
||||
option.direction = Direction::Down;
|
||||
Screen screen(4, 3);
|
||||
Render(screen, menu->Render());
|
||||
EXPECT_EQ(screen.ToString(),
|
||||
@@ -80,7 +81,7 @@ TEST(MenuTest, DirectionsUp) {
|
||||
std::vector<std::string> entries = {"1", "2", "3"};
|
||||
MenuOption option;
|
||||
auto menu = Menu(&entries, &selected, &option);
|
||||
option.direction = MenuOption::Up;
|
||||
option.direction = Direction::Up;
|
||||
Screen screen(4, 3);
|
||||
Render(screen, menu->Render());
|
||||
EXPECT_EQ(screen.ToString(),
|
||||
@@ -106,7 +107,7 @@ TEST(MenuTest, DirectionsRight) {
|
||||
std::vector<std::string> entries = {"1", "2", "3"};
|
||||
MenuOption option;
|
||||
auto menu = Menu(&entries, &selected, &option);
|
||||
option.direction = MenuOption::Right;
|
||||
option.direction = Direction::Right;
|
||||
Screen screen(10, 1);
|
||||
Render(screen, menu->Render());
|
||||
EXPECT_EQ(screen.ToString(),
|
||||
@@ -132,7 +133,7 @@ TEST(MenuTest, DirectionsLeft) {
|
||||
std::vector<std::string> entries = {"1", "2", "3"};
|
||||
MenuOption option;
|
||||
auto menu = Menu(&entries, &selected, &option);
|
||||
option.direction = MenuOption::Left;
|
||||
option.direction = Direction::Left;
|
||||
Screen screen(10, 1);
|
||||
Render(screen, menu->Render());
|
||||
EXPECT_EQ(screen.ToString(),
|
||||
|
@@ -1,26 +1,28 @@
|
||||
#include <memory> // for __shared_ptr_access
|
||||
#include <ftxui/component/component_options.hpp> // for ResizableSplitOption
|
||||
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
|
||||
#include <ftxui/util/ref.hpp> // for Ref
|
||||
#include <functional> // for function
|
||||
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
#include "ftxui/component/component.hpp" // for Component, Make, Horizontal, Vertical, ResizableSplitBottom, ResizableSplitLeft, ResizableSplitRight, ResizableSplitTop
|
||||
#include "ftxui/component/component_base.hpp" // for ComponentBase
|
||||
#include "ftxui/component/component.hpp" // for Horizontal, Make, ResizableSplit, ResizableSplitBottom, ResizableSplitLeft, ResizableSplitRight, ResizableSplitTop
|
||||
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
|
||||
#include "ftxui/component/event.hpp" // for Event
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
|
||||
#include "ftxui/dom/elements.hpp" // for operator|, reflect, Element, separator, size, EQUAL, xflex, yflex, hbox, vbox, HEIGHT, WIDTH
|
||||
#include "ftxui/dom/elements.hpp" // for operator|, reflect, Element, size, EQUAL, xflex, yflex, hbox, vbox, HEIGHT, WIDTH, text
|
||||
#include "ftxui/screen/box.hpp" // for Box
|
||||
|
||||
namespace ftxui {
|
||||
namespace {
|
||||
|
||||
class ResizableSplitLeftBase : public ComponentBase {
|
||||
class ResizableSplitBase : public ComponentBase {
|
||||
public:
|
||||
ResizableSplitLeftBase(Component main, Component child, int* main_size)
|
||||
: main_(std::move(main)),
|
||||
child_(std::move(child)),
|
||||
main_size_(main_size) {
|
||||
ResizableSplitBase(ResizableSplitOption options)
|
||||
: options_(std::move(options)) {
|
||||
Add(Container::Horizontal({
|
||||
main_,
|
||||
child_,
|
||||
options_->main,
|
||||
options_->back,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -45,204 +47,86 @@ class ResizableSplitLeftBase : public ComponentBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (captured_mouse_) {
|
||||
*main_size_ = event.mouse().x - box_.x_min;
|
||||
return true;
|
||||
if (!captured_mouse_) {
|
||||
return ComponentBase::OnEvent(event);
|
||||
}
|
||||
|
||||
return ComponentBase::OnEvent(event);
|
||||
switch (options_->direction()) {
|
||||
case Direction::Left:
|
||||
options_->main_size() = event.mouse().x - box_.x_min;
|
||||
return true;
|
||||
case Direction::Right:
|
||||
options_->main_size() = box_.x_max - event.mouse().x;
|
||||
return true;
|
||||
case Direction::Up:
|
||||
options_->main_size() = event.mouse().y - box_.y_min;
|
||||
return true;
|
||||
case Direction::Down:
|
||||
options_->main_size() = box_.y_max - event.mouse().y;
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOTREACHED()
|
||||
return false;
|
||||
}
|
||||
|
||||
Element Render() final {
|
||||
switch (options_->direction()) {
|
||||
case Direction::Left:
|
||||
return RenderLeft();
|
||||
case Direction::Right:
|
||||
return RenderRight();
|
||||
case Direction::Up:
|
||||
return RenderTop();
|
||||
case Direction::Down:
|
||||
return RenderBottom();
|
||||
}
|
||||
// NOTREACHED()
|
||||
return text("unreacheable");
|
||||
}
|
||||
|
||||
Element RenderLeft() {
|
||||
return hbox({
|
||||
main_->Render() | size(WIDTH, EQUAL, *main_size_),
|
||||
separator() | reflect(separator_box_),
|
||||
child_->Render() | xflex,
|
||||
options_->main->Render() |
|
||||
size(WIDTH, EQUAL, options_->main_size()),
|
||||
options_->separator_func() | reflect(separator_box_),
|
||||
options_->back->Render() | xflex,
|
||||
}) |
|
||||
reflect(box_);
|
||||
};
|
||||
|
||||
private:
|
||||
Component main_;
|
||||
Component child_;
|
||||
int* const main_size_;
|
||||
CapturedMouse captured_mouse_;
|
||||
Box separator_box_;
|
||||
Box box_;
|
||||
};
|
||||
|
||||
class ResizableSplitRightBase : public ComponentBase {
|
||||
public:
|
||||
ResizableSplitRightBase(Component main, Component child, int* main_size)
|
||||
: main_(std::move(main)),
|
||||
child_(std::move(child)),
|
||||
main_size_(main_size) {
|
||||
Add(Container::Horizontal({
|
||||
child_,
|
||||
main_,
|
||||
}));
|
||||
}
|
||||
|
||||
bool OnEvent(Event event) final {
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(std::move(event));
|
||||
}
|
||||
return ComponentBase::OnEvent(std::move(event));
|
||||
}
|
||||
|
||||
bool OnMouseEvent(Event event) {
|
||||
if (captured_mouse_ && event.mouse().motion == Mouse::Released) {
|
||||
captured_mouse_.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.mouse().button == Mouse::Left &&
|
||||
event.mouse().motion == Mouse::Pressed &&
|
||||
separator_box_.Contain(event.mouse().x, event.mouse().y) &&
|
||||
!captured_mouse_) {
|
||||
captured_mouse_ = CaptureMouse(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (captured_mouse_) {
|
||||
*main_size_ = box_.x_max - event.mouse().x;
|
||||
return true;
|
||||
}
|
||||
|
||||
return ComponentBase::OnEvent(event);
|
||||
}
|
||||
|
||||
Element Render() final {
|
||||
Element RenderRight() {
|
||||
return hbox({
|
||||
child_->Render() | xflex,
|
||||
separator() | reflect(separator_box_),
|
||||
main_->Render() | size(WIDTH, EQUAL, *main_size_),
|
||||
options_->back->Render() | xflex,
|
||||
options_->separator_func() | reflect(separator_box_),
|
||||
options_->main->Render() |
|
||||
size(WIDTH, EQUAL, options_->main_size()),
|
||||
}) |
|
||||
reflect(box_);
|
||||
};
|
||||
|
||||
private:
|
||||
Component main_;
|
||||
Component child_;
|
||||
int* const main_size_;
|
||||
CapturedMouse captured_mouse_;
|
||||
Box separator_box_;
|
||||
Box box_;
|
||||
};
|
||||
|
||||
class ResizableSplitTopBase : public ComponentBase {
|
||||
public:
|
||||
ResizableSplitTopBase(Component main, Component child, int* main_size)
|
||||
: main_(std::move(main)),
|
||||
child_(std::move(child)),
|
||||
main_size_(main_size) {
|
||||
Add(Container::Vertical({
|
||||
main_,
|
||||
child_,
|
||||
}));
|
||||
}
|
||||
|
||||
bool OnEvent(Event event) final {
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(std::move(event));
|
||||
}
|
||||
return ComponentBase::OnEvent(std::move(event));
|
||||
}
|
||||
|
||||
bool OnMouseEvent(Event event) {
|
||||
if (captured_mouse_ && event.mouse().motion == Mouse::Released) {
|
||||
captured_mouse_.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.mouse().button == Mouse::Left &&
|
||||
event.mouse().motion == Mouse::Pressed &&
|
||||
separator_box_.Contain(event.mouse().x, event.mouse().y) &&
|
||||
!captured_mouse_) {
|
||||
captured_mouse_ = CaptureMouse(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (captured_mouse_) {
|
||||
*main_size_ = event.mouse().y - box_.y_min;
|
||||
return true;
|
||||
}
|
||||
|
||||
return ComponentBase::OnEvent(event);
|
||||
}
|
||||
|
||||
Element Render() final {
|
||||
Element RenderTop() {
|
||||
return vbox({
|
||||
main_->Render() | size(HEIGHT, EQUAL, *main_size_),
|
||||
separator() | reflect(separator_box_),
|
||||
child_->Render() | yflex,
|
||||
options_->main->Render() |
|
||||
size(HEIGHT, EQUAL, options_->main_size()),
|
||||
options_->separator_func() | reflect(separator_box_),
|
||||
options_->back->Render() | yflex,
|
||||
}) |
|
||||
reflect(box_);
|
||||
};
|
||||
|
||||
private:
|
||||
Component main_;
|
||||
Component child_;
|
||||
int* const main_size_;
|
||||
CapturedMouse captured_mouse_;
|
||||
Box separator_box_;
|
||||
Box box_;
|
||||
};
|
||||
|
||||
class ResizableSplitBottomBase : public ComponentBase {
|
||||
public:
|
||||
ResizableSplitBottomBase(Component main, Component child, int* main_size)
|
||||
: main_(std::move(main)),
|
||||
child_(std::move(child)),
|
||||
main_size_(main_size) {
|
||||
Add(Container::Vertical({
|
||||
child_,
|
||||
main_,
|
||||
}));
|
||||
}
|
||||
|
||||
bool OnEvent(Event event) final {
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(std::move(event));
|
||||
}
|
||||
return ComponentBase::OnEvent(std::move(event));
|
||||
}
|
||||
|
||||
bool OnMouseEvent(Event event) {
|
||||
if (captured_mouse_ && event.mouse().motion == Mouse::Released) {
|
||||
captured_mouse_.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.mouse().button == Mouse::Left &&
|
||||
event.mouse().motion == Mouse::Pressed &&
|
||||
separator_box_.Contain(event.mouse().x, event.mouse().y) &&
|
||||
!captured_mouse_) {
|
||||
captured_mouse_ = CaptureMouse(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (captured_mouse_) {
|
||||
*main_size_ = box_.y_max - event.mouse().y;
|
||||
return true;
|
||||
}
|
||||
|
||||
return ComponentBase::OnEvent(event);
|
||||
}
|
||||
|
||||
Element Render() final {
|
||||
Element RenderBottom() {
|
||||
return vbox({
|
||||
child_->Render() | yflex,
|
||||
separator() | reflect(separator_box_),
|
||||
main_->Render() | size(HEIGHT, EQUAL, *main_size_),
|
||||
options_->back->Render() | yflex,
|
||||
options_->separator_func() | reflect(separator_box_),
|
||||
options_->main->Render() |
|
||||
size(HEIGHT, EQUAL, options_->main_size()),
|
||||
}) |
|
||||
reflect(box_);
|
||||
};
|
||||
|
||||
private:
|
||||
Component main_;
|
||||
Component child_;
|
||||
int* const main_size_;
|
||||
Ref<ResizableSplitOption> options_;
|
||||
CapturedMouse captured_mouse_;
|
||||
Box separator_box_;
|
||||
Box box_;
|
||||
@@ -250,6 +134,35 @@ class ResizableSplitBottomBase : public ComponentBase {
|
||||
|
||||
} // namespace
|
||||
|
||||
/// @brief A split in between two components.
|
||||
/// @param options: all the parameters.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// auto left = Renderer([] { return text("Left") | center;});
|
||||
/// auto right = Renderer([] { return text("right") | center;});
|
||||
/// int left_size = 10;
|
||||
/// auto component = ResizableSplit({
|
||||
/// .main = left,
|
||||
/// .back = right,
|
||||
/// .direction = Direction::Left,
|
||||
/// .main_size = &left_size,
|
||||
/// .separator_func = [] { return separatorDouble(); },
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// ### Output
|
||||
///
|
||||
/// ```bash
|
||||
/// ║
|
||||
/// left ║ right
|
||||
/// ║
|
||||
/// ```
|
||||
Component ResizableSplit(ResizableSplitOption options) {
|
||||
return Make<ResizableSplitBase>(std::move(options));
|
||||
}
|
||||
|
||||
/// @brief An horizontal split in between two components, configurable using the
|
||||
/// mouse.
|
||||
/// @param main The main component of size |main_size|, on the left.
|
||||
@@ -276,8 +189,12 @@ class ResizableSplitBottomBase : public ComponentBase {
|
||||
/// │
|
||||
/// ```
|
||||
Component ResizableSplitLeft(Component main, Component back, int* main_size) {
|
||||
return Make<ResizableSplitLeftBase>(std::move(main), std::move(back),
|
||||
main_size);
|
||||
return ResizableSplit({
|
||||
std::move(main),
|
||||
std::move(back),
|
||||
Direction::Left,
|
||||
main_size,
|
||||
});
|
||||
}
|
||||
|
||||
/// @brief An horizontal split in between two components, configurable using the
|
||||
@@ -294,7 +211,7 @@ Component ResizableSplitLeft(Component main, Component back, int* main_size) {
|
||||
/// int right_size = 10;
|
||||
/// auto left = Renderer([] { return text("Left") | center;});
|
||||
/// auto right = Renderer([] { return text("right") | center;});
|
||||
/// auto split = ResizableSplitRight(right, left, &right_size);
|
||||
/// auto split = ResizableSplitRight(right, left, &right_size)
|
||||
/// screen.Loop(split);
|
||||
/// ```
|
||||
///
|
||||
@@ -306,8 +223,12 @@ Component ResizableSplitLeft(Component main, Component back, int* main_size) {
|
||||
/// │
|
||||
/// ```
|
||||
Component ResizableSplitRight(Component main, Component back, int* main_size) {
|
||||
return Make<ResizableSplitRightBase>(std::move(main), std::move(back),
|
||||
main_size);
|
||||
return ResizableSplit({
|
||||
std::move(main),
|
||||
std::move(back),
|
||||
Direction::Right,
|
||||
main_size,
|
||||
});
|
||||
}
|
||||
|
||||
/// @brief An vertical split in between two components, configurable using the
|
||||
@@ -324,7 +245,7 @@ Component ResizableSplitRight(Component main, Component back, int* main_size) {
|
||||
/// int top_size = 1;
|
||||
/// auto top = Renderer([] { return text("Top") | center;});
|
||||
/// auto bottom = Renderer([] { return text("Bottom") | center;});
|
||||
/// auto split = ResizableSplitTop(top, bottom, &top_size);
|
||||
/// auto split = ResizableSplitTop(top, bottom, &top_size)
|
||||
/// screen.Loop(split);
|
||||
/// ```
|
||||
///
|
||||
@@ -336,8 +257,12 @@ Component ResizableSplitRight(Component main, Component back, int* main_size) {
|
||||
/// bottom
|
||||
/// ```
|
||||
Component ResizableSplitTop(Component main, Component back, int* main_size) {
|
||||
return Make<ResizableSplitTopBase>(std::move(main), std::move(back),
|
||||
main_size);
|
||||
return ResizableSplit({
|
||||
std::move(main),
|
||||
std::move(back),
|
||||
Direction::Up,
|
||||
main_size,
|
||||
});
|
||||
}
|
||||
|
||||
/// @brief An vertical split in between two components, configurable using the
|
||||
@@ -354,7 +279,7 @@ Component ResizableSplitTop(Component main, Component back, int* main_size) {
|
||||
/// int bottom_size = 1;
|
||||
/// auto top = Renderer([] { return text("Top") | center;});
|
||||
/// auto bottom = Renderer([] { return text("Bottom") | center;});
|
||||
/// auto split = ResizableSplit::Bottom(bottom, top, &bottom_size);
|
||||
/// auto split = ResizableSplit::Bottom(bottom, top, &bottom_size)
|
||||
/// screen.Loop(split);
|
||||
/// ```
|
||||
///
|
||||
@@ -366,9 +291,14 @@ Component ResizableSplitTop(Component main, Component back, int* main_size) {
|
||||
/// bottom
|
||||
/// ```
|
||||
Component ResizableSplitBottom(Component main, Component back, int* main_size) {
|
||||
return Make<ResizableSplitBottomBase>(std::move(main), std::move(back),
|
||||
main_size);
|
||||
return ResizableSplit({
|
||||
std::move(main),
|
||||
std::move(back),
|
||||
Direction::Down,
|
||||
main_size,
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
// Copyright 2021 Arthur Sonzogni. All rights reserved.
|
||||
|
@@ -1,11 +1,12 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <gtest/gtest.h> // for AssertionResult, Message, TestPartResult, Test, EXPECT_EQ, EXPECT_TRUE, TestInfo (ptr only), TEST
|
||||
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
|
||||
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
|
||||
|
||||
#include "ftxui/component/component.hpp" // for Renderer, ResizableSplitBottom, ResizableSplitLeft, ResizableSplitRight, ResizableSplitTop
|
||||
#include "ftxui/component/component.hpp" // for ResizableSplit, Renderer, ResizableSplitBottom, ResizableSplitLeft, ResizableSplitRight, ResizableSplitTop
|
||||
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
|
||||
#include "ftxui/component/event.hpp" // for Event
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
|
||||
#include "ftxui/dom/elements.hpp" // for text, Element
|
||||
#include "ftxui/dom/elements.hpp" // for Element, separatorDouble, text
|
||||
#include "ftxui/dom/node.hpp" // for Render
|
||||
#include "ftxui/screen/screen.hpp" // for Screen
|
||||
|
||||
@@ -56,6 +57,31 @@ TEST(ResizableSplit, BasicLeft) {
|
||||
EXPECT_EQ(position, 10);
|
||||
}
|
||||
|
||||
TEST(ResizableSplit, BasicLeftWithCustomSeparator) {
|
||||
int position = 1;
|
||||
auto component = ResizableSplit({
|
||||
.main = BasicComponent(),
|
||||
.back = BasicComponent(),
|
||||
.direction = Direction::Left,
|
||||
.main_size = &position,
|
||||
.separator_func = [] { return separatorDouble(); },
|
||||
});
|
||||
auto screen = Screen(4, 4);
|
||||
Render(screen, component->Render());
|
||||
EXPECT_EQ(position, 1);
|
||||
EXPECT_EQ(screen.ToString(),
|
||||
" ║ \r\n"
|
||||
" ║ \r\n"
|
||||
" ║ \r\n"
|
||||
" ║ ");
|
||||
EXPECT_TRUE(component->OnEvent(MousePressed(1, 1)));
|
||||
EXPECT_EQ(position, 1);
|
||||
EXPECT_TRUE(component->OnEvent(MousePressed(2, 1)));
|
||||
EXPECT_EQ(position, 2);
|
||||
EXPECT_TRUE(component->OnEvent(MouseReleased(2, 1)));
|
||||
EXPECT_EQ(position, 2);
|
||||
}
|
||||
|
||||
TEST(ResizableSplit, BasicRight) {
|
||||
int position = 3;
|
||||
auto component =
|
||||
@@ -71,6 +97,31 @@ TEST(ResizableSplit, BasicRight) {
|
||||
EXPECT_EQ(position, 9);
|
||||
}
|
||||
|
||||
TEST(ResizableSplit, BasicRightWithCustomSeparator) {
|
||||
int position = 1;
|
||||
auto component = ResizableSplit({
|
||||
.main = BasicComponent(),
|
||||
.back = BasicComponent(),
|
||||
.direction = Direction::Right,
|
||||
.main_size = &position,
|
||||
.separator_func = [] { return separatorDouble(); },
|
||||
});
|
||||
auto screen = Screen(4, 4);
|
||||
Render(screen, component->Render());
|
||||
EXPECT_EQ(position, 1);
|
||||
EXPECT_EQ(screen.ToString(),
|
||||
" ║ \r\n"
|
||||
" ║ \r\n"
|
||||
" ║ \r\n"
|
||||
" ║ ");
|
||||
EXPECT_TRUE(component->OnEvent(MousePressed(2, 1)));
|
||||
EXPECT_EQ(position, 1);
|
||||
EXPECT_TRUE(component->OnEvent(MousePressed(1, 1)));
|
||||
EXPECT_EQ(position, 2);
|
||||
EXPECT_TRUE(component->OnEvent(MouseReleased(1, 1)));
|
||||
EXPECT_EQ(position, 2);
|
||||
}
|
||||
|
||||
TEST(ResizableSplit, BasicTop) {
|
||||
int position = 3;
|
||||
auto component =
|
||||
@@ -86,6 +137,31 @@ TEST(ResizableSplit, BasicTop) {
|
||||
EXPECT_EQ(position, 10);
|
||||
}
|
||||
|
||||
TEST(ResizableSplit, BasicTopWithCustomSeparator) {
|
||||
int position = 1;
|
||||
auto component = ResizableSplit({
|
||||
.main = BasicComponent(),
|
||||
.back = BasicComponent(),
|
||||
.direction = Direction::Up,
|
||||
.main_size = &position,
|
||||
.separator_func = [] { return separatorDouble(); },
|
||||
});
|
||||
auto screen = Screen(4, 4);
|
||||
Render(screen, component->Render());
|
||||
EXPECT_EQ(position, 1);
|
||||
EXPECT_EQ(screen.ToString(),
|
||||
" \r\n"
|
||||
"════\r\n"
|
||||
" \r\n"
|
||||
" ");
|
||||
EXPECT_TRUE(component->OnEvent(MousePressed(1, 1)));
|
||||
EXPECT_EQ(position, 1);
|
||||
EXPECT_TRUE(component->OnEvent(MousePressed(1, 2)));
|
||||
EXPECT_EQ(position, 2);
|
||||
EXPECT_TRUE(component->OnEvent(MouseReleased(1, 2)));
|
||||
EXPECT_EQ(position, 2);
|
||||
}
|
||||
|
||||
TEST(ResizableSplit, BasicBottom) {
|
||||
int position = 3;
|
||||
auto component =
|
||||
@@ -101,6 +177,31 @@ TEST(ResizableSplit, BasicBottom) {
|
||||
EXPECT_EQ(position, 9);
|
||||
}
|
||||
|
||||
TEST(ResizableSplit, BasicBottomWithCustomSeparator) {
|
||||
int position = 1;
|
||||
auto component = ResizableSplit({
|
||||
.main = BasicComponent(),
|
||||
.back = BasicComponent(),
|
||||
.direction = Direction::Down,
|
||||
.main_size = &position,
|
||||
.separator_func = [] { return separatorDouble(); },
|
||||
});
|
||||
auto screen = Screen(4, 4);
|
||||
Render(screen, component->Render());
|
||||
EXPECT_EQ(position, 1);
|
||||
EXPECT_EQ(screen.ToString(),
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
"════\r\n"
|
||||
" ");
|
||||
EXPECT_TRUE(component->OnEvent(MousePressed(1, 2)));
|
||||
EXPECT_EQ(position, 1);
|
||||
EXPECT_TRUE(component->OnEvent(MousePressed(1, 1)));
|
||||
EXPECT_EQ(position, 2);
|
||||
EXPECT_TRUE(component->OnEvent(MouseReleased(1, 1)));
|
||||
EXPECT_EQ(position, 2);
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
// Copyright 2022 Arthur Sonzogni. All rights reserved.
|
||||
|
@@ -174,9 +174,9 @@ void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
|
||||
}
|
||||
|
||||
const size_t buffer_size = 100;
|
||||
std::array<char, buffer_size> buffer; // NOLINT;
|
||||
int l = read(fileno(stdin), buffer.data(), buffer_size); // NOLINT
|
||||
for (int i = 0; i < l; ++i) {
|
||||
std::array<char, buffer_size> buffer; // NOLINT;
|
||||
size_t l = read(fileno(stdin), buffer.data(), buffer_size); // NOLINT
|
||||
for (size_t i = 0; i < l; ++i) {
|
||||
parser.Add(buffer[i]); // NOLINT
|
||||
}
|
||||
}
|
||||
@@ -460,9 +460,13 @@ void ScreenInteractive::PostMain() {
|
||||
g_active_screen->Install();
|
||||
} else {
|
||||
Uninstall();
|
||||
|
||||
std::cout << '\r';
|
||||
// On final exit, keep the current drawing and reset cursor position one
|
||||
// line after it.
|
||||
std::cout << std::endl;
|
||||
if (!use_alternative_screen_) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,8 @@
|
||||
#include <algorithm> // for max, min
|
||||
#include <ftxui/component/component_options.hpp> // for SliderOption
|
||||
#include <string> // for allocator
|
||||
#include <utility> // for move
|
||||
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
|
||||
#include <string> // for allocator
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
#include "ftxui/component/component.hpp" // for Make, Slider
|
||||
@@ -9,7 +10,7 @@
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
|
||||
#include "ftxui/component/screen_interactive.hpp" // for Component
|
||||
#include "ftxui/dom/elements.hpp" // for operator|, text, GaugeDirection, Element, xflex, hbox, color, underlined, GaugeDirection::Down, GaugeDirection::Left, GaugeDirection::Right, GaugeDirection::Up, reflect, Decorator, dim, vcenter, yflex, gaugeDirection
|
||||
#include "ftxui/dom/elements.hpp" // for operator|, text, Element, xflex, hbox, color, underlined, reflect, Decorator, dim, vcenter, focus, nothing, select, yflex, gaugeDirection
|
||||
#include "ftxui/screen/box.hpp" // for Box
|
||||
#include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::White
|
||||
#include "ftxui/screen/util.hpp" // for clamp
|
||||
@@ -18,13 +19,13 @@
|
||||
namespace ftxui {
|
||||
|
||||
namespace {
|
||||
Decorator flexDirection(GaugeDirection direction) {
|
||||
Decorator flexDirection(Direction direction) {
|
||||
switch (direction) {
|
||||
case GaugeDirection::Up:
|
||||
case GaugeDirection::Down:
|
||||
case Direction::Up:
|
||||
case Direction::Down:
|
||||
return yflex;
|
||||
case GaugeDirection::Left:
|
||||
case GaugeDirection::Right:
|
||||
case Direction::Left:
|
||||
case Direction::Right:
|
||||
return xflex;
|
||||
}
|
||||
return xflex; // NOT_REACHED()
|
||||
@@ -52,56 +53,56 @@ class SliderBase : public ComponentBase {
|
||||
|
||||
void OnLeft() {
|
||||
switch (options_->direction) {
|
||||
case GaugeDirection::Right:
|
||||
case Direction::Right:
|
||||
value_() -= increment_();
|
||||
break;
|
||||
case GaugeDirection::Left:
|
||||
case Direction::Left:
|
||||
value_() += increment_();
|
||||
break;
|
||||
case GaugeDirection::Up:
|
||||
case GaugeDirection::Down:
|
||||
case Direction::Up:
|
||||
case Direction::Down:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnRight() {
|
||||
switch (options_->direction) {
|
||||
case GaugeDirection::Right:
|
||||
case Direction::Right:
|
||||
value_() += increment_();
|
||||
break;
|
||||
case GaugeDirection::Left:
|
||||
case Direction::Left:
|
||||
value_() -= increment_();
|
||||
break;
|
||||
case GaugeDirection::Up:
|
||||
case GaugeDirection::Down:
|
||||
case Direction::Up:
|
||||
case Direction::Down:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnUp() {
|
||||
switch (options_->direction) {
|
||||
case GaugeDirection::Up:
|
||||
case Direction::Up:
|
||||
value_() -= increment_();
|
||||
break;
|
||||
case GaugeDirection::Down:
|
||||
case Direction::Down:
|
||||
value_() += increment_();
|
||||
break;
|
||||
case GaugeDirection::Left:
|
||||
case GaugeDirection::Right:
|
||||
case Direction::Left:
|
||||
case Direction::Right:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDown() {
|
||||
switch (options_->direction) {
|
||||
case GaugeDirection::Down:
|
||||
case Direction::Down:
|
||||
value_() -= increment_();
|
||||
break;
|
||||
case GaugeDirection::Up:
|
||||
case Direction::Up:
|
||||
value_() += increment_();
|
||||
break;
|
||||
case GaugeDirection::Left:
|
||||
case GaugeDirection::Right:
|
||||
case Direction::Left:
|
||||
case Direction::Right:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -153,25 +154,25 @@ class SliderBase : public ComponentBase {
|
||||
|
||||
if (captured_mouse_) {
|
||||
switch (options_->direction) {
|
||||
case GaugeDirection::Right: {
|
||||
case Direction::Right: {
|
||||
value_() = min_() + (event.mouse().x - gauge_box_.x_min) *
|
||||
(max_() - min_()) /
|
||||
(gauge_box_.x_max - gauge_box_.x_min);
|
||||
break;
|
||||
}
|
||||
case GaugeDirection::Left: {
|
||||
case Direction::Left: {
|
||||
value_() = max_() - (event.mouse().x - gauge_box_.x_min) *
|
||||
(max_() - min_()) /
|
||||
(gauge_box_.x_max - gauge_box_.x_min);
|
||||
break;
|
||||
}
|
||||
case GaugeDirection::Down: {
|
||||
case Direction::Down: {
|
||||
value_() = min_() + (event.mouse().y - gauge_box_.y_min) *
|
||||
(max_() - min_()) /
|
||||
(gauge_box_.y_max - gauge_box_.y_min);
|
||||
break;
|
||||
}
|
||||
case GaugeDirection::Up: {
|
||||
case Direction::Up: {
|
||||
value_() = max_() - (event.mouse().y - gauge_box_.y_min) *
|
||||
(max_() - min_()) /
|
||||
(gauge_box_.y_max - gauge_box_.y_min);
|
||||
|
@@ -2,7 +2,8 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <array> // for array
|
||||
#include <ftxui/component/mouse.hpp> // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
|
||||
#include <ftxui/dom/elements.hpp> // for GaugeDirection, GaugeDirection::Down, GaugeDirection::Left, GaugeDirection::Right, GaugeDirection::Up, frame
|
||||
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
|
||||
#include <ftxui/dom/elements.hpp> // for frame
|
||||
#include <memory> // for shared_ptr, __shared_ptr_access, allocator
|
||||
#include <string> // for to_string
|
||||
|
||||
@@ -47,7 +48,7 @@ TEST(SliderTest, Right) {
|
||||
.min = 0,
|
||||
.max = 100,
|
||||
.increment = 10,
|
||||
.direction = GaugeDirection::Right,
|
||||
.direction = Direction::Right,
|
||||
});
|
||||
Screen screen(11, 1);
|
||||
Render(screen, slider->Render());
|
||||
@@ -70,7 +71,7 @@ TEST(SliderTest, Left) {
|
||||
.min = 0,
|
||||
.max = 100,
|
||||
.increment = 10,
|
||||
.direction = GaugeDirection::Left,
|
||||
.direction = Direction::Left,
|
||||
});
|
||||
Screen screen(11, 1);
|
||||
Render(screen, slider->Render());
|
||||
@@ -93,7 +94,7 @@ TEST(SliderTest, Down) {
|
||||
.min = 0,
|
||||
.max = 100,
|
||||
.increment = 10,
|
||||
.direction = GaugeDirection::Down,
|
||||
.direction = Direction::Down,
|
||||
});
|
||||
Screen screen(1, 11);
|
||||
Render(screen, slider->Render());
|
||||
@@ -116,7 +117,7 @@ TEST(SliderTest, Up) {
|
||||
.min = 0,
|
||||
.max = 100,
|
||||
.increment = 10,
|
||||
.direction = GaugeDirection::Up,
|
||||
.direction = Direction::Up,
|
||||
});
|
||||
Screen screen(1, 11);
|
||||
Render(screen, slider->Render());
|
||||
|
@@ -19,8 +19,31 @@ const std::map<std::string, std::string> g_uniformize = {
|
||||
// See https://github.com/ArthurSonzogni/FTXUI/issues/337
|
||||
// Here, we uniformize the new line character to `\n`.
|
||||
{"\r", "\n"},
|
||||
|
||||
// See: https://github.com/ArthurSonzogni/FTXUI/issues/508
|
||||
{std::string({8}), std::string({127})},
|
||||
|
||||
// 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)
|
||||
|
@@ -333,22 +333,44 @@ TEST(Event, Special) {
|
||||
output.push_back(it);
|
||||
return output;
|
||||
};
|
||||
|
||||
struct {
|
||||
std::vector<unsigned char> input;
|
||||
Event expected;
|
||||
} kTestCase[] = {
|
||||
{str("\x1B[D"), Event::ArrowLeft},
|
||||
{str("\x1B[C"), Event::ArrowRight},
|
||||
// Arrow (defaut cursor mode)
|
||||
{str("\x1B[A"), Event::ArrowUp},
|
||||
{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},
|
||||
// Quirk for: https://github.com/ArthurSonzogni/FTXUI/issues/508
|
||||
{{8}, Event::Backspace},
|
||||
|
||||
// Delete
|
||||
{str("\x1B[3~"), Event::Delete},
|
||||
//{str("\x1B"), Event::Escape},
|
||||
|
||||
// Return
|
||||
{{13}, Event::Return},
|
||||
{{10}, Event::Return},
|
||||
|
||||
// Tabs:
|
||||
{{9}, Event::Tab},
|
||||
{{27, 91, 90}, Event::TabReverse},
|
||||
|
||||
// Function keys
|
||||
{str("\x1BOP"), Event::F1},
|
||||
{str("\x1BOQ"), Event::F2},
|
||||
{str("\x1BOR"), Event::F3},
|
||||
@@ -361,10 +383,12 @@ TEST(Event, Special) {
|
||||
{str("\x1B[21~"), Event::F10},
|
||||
{str("\x1B[23~"), Event::F11},
|
||||
{str("\x1B[24~"), Event::F12},
|
||||
{{27, 91, 72}, Event::Home},
|
||||
{{27, 91, 70}, Event::End},
|
||||
{{27, 91, 53, 126}, Event::PageUp},
|
||||
{{27, 91, 54, 126}, Event::PageDown},
|
||||
|
||||
// Page up and down:
|
||||
{str("\x1B[5~"), Event::PageUp},
|
||||
{str("\x1B[6~"), Event::PageDown},
|
||||
|
||||
// Custom:
|
||||
{{0}, Event::Custom},
|
||||
};
|
||||
|
||||
|
@@ -1,11 +1,13 @@
|
||||
#include <algorithm> // for max
|
||||
#include <array> // for array
|
||||
#include <memory> // for allocator, make_shared, __shared_ptr_access
|
||||
#include <string> // for basic_string, string
|
||||
#include <utility> // for move
|
||||
#include <vector> // for __alloc_traits<>::value_type
|
||||
#include <algorithm> // for max
|
||||
#include <array> // for array
|
||||
#include <ftxui/screen/color.hpp> // for Color
|
||||
#include <memory> // for allocator, make_shared, __shared_ptr_access
|
||||
#include <optional> // for optional, nullopt
|
||||
#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/requirement.hpp" // for Requirement
|
||||
#include "ftxui/screen/box.hpp" // for Box
|
||||
@@ -14,24 +16,29 @@
|
||||
namespace ftxui {
|
||||
|
||||
using Charset = std::array<std::string, 6>; // NOLINT
|
||||
using Charsets = std::array<Charset, 5>; // NOLINT
|
||||
using Charsets = std::array<Charset, 6>; // NOLINT
|
||||
// NOLINTNEXTLINE
|
||||
static Charsets simple_border_charset = {
|
||||
Charset{"┌", "┐", "└", "┘", "─", "│"},
|
||||
Charset{"┏", "┓", "┗", "┛", "━", "┃"},
|
||||
Charset{"╔", "╗", "╚", "╝", "═", "║"},
|
||||
Charset{"╭", "╮", "╰", "╯", "─", "│"},
|
||||
Charset{" ", " ", " ", " ", " ", " "},
|
||||
Charset{"┌", "┐", "└", "┘", "─", "│"}, // LIGHT
|
||||
Charset{"┏", "┓", "┗", "┛", "╍", "╏"}, // DASHED
|
||||
Charset{"┏", "┓", "┗", "┛", "━", "┃"}, // HEAVY
|
||||
Charset{"╔", "╗", "╚", "╝", "═", "║"}, // DOUBLE
|
||||
Charset{"╭", "╮", "╰", "╯", "─", "│"}, // ROUNDED
|
||||
Charset{" ", " ", " ", " ", " ", " "}, // EMPTY
|
||||
};
|
||||
|
||||
// For reference, here is the charset for normal border:
|
||||
class Border : public Node {
|
||||
public:
|
||||
Border(Elements children, BorderStyle style)
|
||||
Border(Elements children,
|
||||
BorderStyle style,
|
||||
std::optional<Color> foreground_color = std::nullopt)
|
||||
: Node(std::move(children)),
|
||||
charset_(simple_border_charset[style]) {} // NOLINT
|
||||
charset_(simple_border_charset[style]),
|
||||
foreground_color_(foreground_color) {} // NOLINT
|
||||
|
||||
const Charset& charset_; // NOLINT
|
||||
std::optional<Color> foreground_color_;
|
||||
|
||||
void ComputeRequirement() override {
|
||||
Node::ComputeRequirement();
|
||||
@@ -100,6 +107,18 @@ class Border : public Node {
|
||||
if (children_.size() == 2) {
|
||||
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
|
||||
/// @see border
|
||||
/// @see borderLight
|
||||
/// @see borderDashed
|
||||
/// @see borderDouble
|
||||
/// @see borderHeavy
|
||||
/// @see borderEmpty
|
||||
/// @see borderRounded
|
||||
/// @see borderStyled
|
||||
/// @see borderWith
|
||||
///
|
||||
/// 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.
|
||||
/// @ingroup dom
|
||||
/// @see border
|
||||
/// @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 borderHeavy
|
||||
/// @see borderRounded
|
||||
@@ -257,6 +335,7 @@ Element borderLight(Element child) {
|
||||
/// @ingroup dom
|
||||
/// @see border
|
||||
/// @see borderLight
|
||||
/// @see borderDashed
|
||||
/// @see borderDouble
|
||||
/// @see borderHeavy
|
||||
/// @see borderRounded
|
||||
@@ -291,6 +370,7 @@ Element borderHeavy(Element child) {
|
||||
/// @ingroup dom
|
||||
/// @see border
|
||||
/// @see borderLight
|
||||
/// @see borderDashed
|
||||
/// @see borderDouble
|
||||
/// @see borderHeavy
|
||||
/// @see borderRounded
|
||||
@@ -325,6 +405,7 @@ Element borderDouble(Element child) {
|
||||
/// @ingroup dom
|
||||
/// @see border
|
||||
/// @see borderLight
|
||||
/// @see borderDashed
|
||||
/// @see borderDouble
|
||||
/// @see borderHeavy
|
||||
/// @see borderRounded
|
||||
@@ -359,6 +440,7 @@ Element borderRounded(Element child) {
|
||||
/// @ingroup dom
|
||||
/// @see border
|
||||
/// @see borderLight
|
||||
/// @see borderDashed
|
||||
/// @see borderDouble
|
||||
/// @see borderHeavy
|
||||
/// @see borderRounded
|
||||
|
@@ -14,7 +14,7 @@ namespace ftxui {
|
||||
namespace {
|
||||
uint32_t Hash(const std::string s) {
|
||||
uint32_t hash = 0;
|
||||
for (auto c : s) {
|
||||
for (uint8_t c : s) {
|
||||
hash += c;
|
||||
hash *= 7;
|
||||
}
|
||||
@@ -37,7 +37,7 @@ TEST(CanvasTest, GoldPoint) {
|
||||
});
|
||||
Screen screen(30, 10);
|
||||
Render(screen, element);
|
||||
EXPECT_EQ(Hash(screen.ToString()), 2143518726);
|
||||
EXPECT_EQ(Hash(screen.ToString()), 2085952774U) << screen.ToString();
|
||||
}
|
||||
|
||||
TEST(CanvasTest, GoldPointColor) {
|
||||
@@ -52,7 +52,7 @@ TEST(CanvasTest, GoldPointColor) {
|
||||
});
|
||||
Screen screen(30, 10);
|
||||
Render(screen, element);
|
||||
EXPECT_EQ(Hash(screen.ToString()), 1264423298);
|
||||
EXPECT_EQ(Hash(screen.ToString()), 2295070594U) << screen.ToString();
|
||||
}
|
||||
|
||||
TEST(CanvasTest, GoldBlock) {
|
||||
@@ -70,7 +70,7 @@ TEST(CanvasTest, GoldBlock) {
|
||||
});
|
||||
Screen screen(30, 10);
|
||||
Render(screen, element);
|
||||
EXPECT_EQ(Hash(screen.ToString()), 3826174883);
|
||||
EXPECT_EQ(Hash(screen.ToString()), 2625314979U) << screen.ToString();
|
||||
}
|
||||
|
||||
TEST(CanvasTest, GoldBlockColor) {
|
||||
@@ -85,7 +85,7 @@ TEST(CanvasTest, GoldBlockColor) {
|
||||
});
|
||||
Screen screen(30, 10);
|
||||
Render(screen, element);
|
||||
EXPECT_EQ(Hash(screen.ToString()), 3048712696);
|
||||
EXPECT_EQ(Hash(screen.ToString()), 8392696U) << screen.ToString();
|
||||
}
|
||||
|
||||
TEST(CanvasTest, GoldText) {
|
||||
|
@@ -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 "ftxui/dom/elements.hpp" // for operator|, text, bgcolor, color, Element
|
||||
#include "ftxui/dom/node.hpp" // for Render
|
||||
#include "ftxui/screen/color.hpp" // for Color, Color::Red
|
||||
#include "ftxui/screen/color.hpp" // for Color, Color::Red, Color::RedLight
|
||||
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
|
||||
|
||||
namespace ftxui {
|
||||
|
@@ -161,7 +161,7 @@ void SetY(Global& g, std::vector<Line> lines) {
|
||||
}
|
||||
|
||||
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);
|
||||
ys[i] += shifted;
|
||||
const int consumed = remaining_space - shifted;
|
||||
@@ -172,7 +172,7 @@ void SetY(Global& g, std::vector<Line> lines) {
|
||||
}
|
||||
|
||||
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;
|
||||
remaining_space = remaining_space * (i - 1) / i;
|
||||
}
|
||||
@@ -180,7 +180,7 @@ void SetY(Global& g, std::vector<Line> lines) {
|
||||
}
|
||||
|
||||
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);
|
||||
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: {
|
||||
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);
|
||||
remaining_space = remaining_space * (i + 1) / (i + 2);
|
||||
}
|
||||
|
@@ -1,8 +1,9 @@
|
||||
#include <memory> // for allocator, make_shared
|
||||
#include <string> // for string
|
||||
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
|
||||
#include <memory> // for allocator, make_shared
|
||||
#include <string> // for string
|
||||
|
||||
#include "ftxui/dom/elements.hpp" // for GaugeDirection, Element, GaugeDirection::Down, GaugeDirection::Left, GaugeDirection::Right, GaugeDirection::Up, gauge, gaugeDirection, gaugeDown, gaugeLeft, gaugeRight, gaugeUp
|
||||
#include "ftxui/dom/node.hpp" // for Node
|
||||
#include "ftxui/dom/elements.hpp" // for Element, gauge, gaugeDirection, gaugeDown, gaugeLeft, gaugeRight, gaugeUp
|
||||
#include "ftxui/dom/node.hpp" // for Node
|
||||
#include "ftxui/dom/requirement.hpp" // for Requirement
|
||||
#include "ftxui/screen/box.hpp" // for Box
|
||||
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
|
||||
@@ -40,7 +41,7 @@ static const std::string charset_vertical[10] = {
|
||||
|
||||
class Gauge : public Node {
|
||||
public:
|
||||
Gauge(float progress, GaugeDirection direction)
|
||||
Gauge(float progress, Direction direction)
|
||||
: progress_(progress), direction_(direction) {
|
||||
// This handle NAN correctly:
|
||||
if (!(progress_ > 0.F)) {
|
||||
@@ -53,15 +54,15 @@ class Gauge : public Node {
|
||||
|
||||
void ComputeRequirement() override {
|
||||
switch (direction_) {
|
||||
case GaugeDirection::Right:
|
||||
case GaugeDirection::Left:
|
||||
case Direction::Right:
|
||||
case Direction::Left:
|
||||
requirement_.flex_grow_x = 1;
|
||||
requirement_.flex_grow_y = 0;
|
||||
requirement_.flex_shrink_x = 1;
|
||||
requirement_.flex_shrink_y = 0;
|
||||
break;
|
||||
case GaugeDirection::Up:
|
||||
case GaugeDirection::Down:
|
||||
case Direction::Up:
|
||||
case Direction::Down:
|
||||
requirement_.flex_grow_x = 0;
|
||||
requirement_.flex_grow_y = 1;
|
||||
requirement_.flex_shrink_x = 0;
|
||||
@@ -74,16 +75,16 @@ class Gauge : public Node {
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
switch (direction_) {
|
||||
case GaugeDirection::Right:
|
||||
case Direction::Right:
|
||||
RenderHorizontal(screen, /*invert=*/false);
|
||||
break;
|
||||
case GaugeDirection::Up:
|
||||
case Direction::Up:
|
||||
RenderVertical(screen, /*invert=*/false);
|
||||
break;
|
||||
case GaugeDirection::Left:
|
||||
case Direction::Left:
|
||||
RenderHorizontal(screen, /*invert=*/true);
|
||||
break;
|
||||
case GaugeDirection::Down:
|
||||
case Direction::Down:
|
||||
RenderVertical(screen, /*invert=*/true);
|
||||
break;
|
||||
}
|
||||
@@ -151,7 +152,7 @@ class Gauge : public Node {
|
||||
|
||||
private:
|
||||
float progress_;
|
||||
GaugeDirection direction_;
|
||||
Direction direction_;
|
||||
};
|
||||
|
||||
/// @brief Draw a high definition progress bar progressing in specified
|
||||
@@ -159,7 +160,7 @@ class Gauge : public Node {
|
||||
/// @param progress The proportion of the area to be filled. Belong to [0,1].
|
||||
// @param direction Direction of progress bars progression.
|
||||
/// @ingroup dom
|
||||
Element gaugeDirection(float progress, GaugeDirection direction) {
|
||||
Element gaugeDirection(float progress, Direction direction) {
|
||||
return std::make_shared<Gauge>(progress, direction);
|
||||
}
|
||||
|
||||
@@ -182,7 +183,7 @@ Element gaugeDirection(float progress, GaugeDirection direction) {
|
||||
/// └──────────────────────────────────────────────────────────────────────────┘
|
||||
/// ~~~
|
||||
Element gaugeRight(float progress) {
|
||||
return gaugeDirection(progress, GaugeDirection::Right);
|
||||
return gaugeDirection(progress, Direction::Right);
|
||||
}
|
||||
|
||||
/// @brief Draw a high definition progress bar progressing from right to left.
|
||||
@@ -204,7 +205,7 @@ Element gaugeRight(float progress) {
|
||||
/// └──────────────────────────────────────────────────────────────────────────┘
|
||||
/// ~~~
|
||||
Element gaugeLeft(float progress) {
|
||||
return gaugeDirection(progress, GaugeDirection::Left);
|
||||
return gaugeDirection(progress, Direction::Left);
|
||||
}
|
||||
|
||||
/// @brief Draw a high definition progress bar progressing from bottom to top.
|
||||
@@ -233,7 +234,7 @@ Element gaugeLeft(float progress) {
|
||||
/// └─┘
|
||||
/// ~~~
|
||||
Element gaugeUp(float progress) {
|
||||
return gaugeDirection(progress, GaugeDirection::Up);
|
||||
return gaugeDirection(progress, Direction::Up);
|
||||
}
|
||||
|
||||
/// @brief Draw a high definition progress bar progressing from top to bottom.
|
||||
@@ -262,7 +263,7 @@ Element gaugeUp(float progress) {
|
||||
/// └─┘
|
||||
/// ~~~
|
||||
Element gaugeDown(float progress) {
|
||||
return gaugeDirection(progress, GaugeDirection::Down);
|
||||
return gaugeDirection(progress, Direction::Down);
|
||||
}
|
||||
|
||||
/// @brief Draw a high definition progress bar.
|
||||
|
@@ -39,6 +39,9 @@ class Graph : public Node {
|
||||
void Render(Screen& screen) override {
|
||||
const int width = (box_.x_max - box_.x_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);
|
||||
int i = 0;
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
|
293
src/ftxui/dom/linear_gradient.cpp
Normal file
293
src/ftxui/dom/linear_gradient.cpp
Normal 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;
|
||||
}
|
||||
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.
|
90
src/ftxui/dom/linear_gradient_test.cpp
Normal file
90
src/ftxui/dom/linear_gradient_test.cpp
Normal 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.
|
@@ -14,14 +14,15 @@ namespace ftxui {
|
||||
|
||||
namespace {
|
||||
using Charset = std::array<std::string, 2>; // NOLINT
|
||||
using Charsets = std::array<Charset, 5>; // NOLINT
|
||||
using Charsets = std::array<Charset, 6>; // NOLINT
|
||||
// NOLINTNEXTLINE
|
||||
const Charsets charsets = {
|
||||
Charset{"│", "─"}, //
|
||||
Charset{"┃", "━"}, //
|
||||
Charset{"║", "═"}, //
|
||||
Charset{"│", "─"}, //
|
||||
Charset{" ", " "}, //
|
||||
Charset{"│", "─"}, // LIGHT
|
||||
Charset{"╏", "╍"}, // DASHED
|
||||
Charset{"┃", "━"}, // HEAVY
|
||||
Charset{"║", "═"}, // DOUBLE
|
||||
Charset{"│", "─"}, // ROUNDED
|
||||
Charset{" ", " "}, // EMPTY
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -98,6 +99,7 @@ class SeparatorWithPixel : public SeparatorAuto {
|
||||
/// @ingroup dom
|
||||
/// @see separator
|
||||
/// @see separatorLight
|
||||
/// @see separatorDashed
|
||||
/// @see separatorDouble
|
||||
/// @see separatorHeavy
|
||||
/// @see separatorEmpty
|
||||
@@ -135,6 +137,7 @@ Element separator() {
|
||||
/// @ingroup dom
|
||||
/// @see separator
|
||||
/// @see separatorLight
|
||||
/// @see separatorDashed
|
||||
/// @see separatorDouble
|
||||
/// @see separatorHeavy
|
||||
/// @see separatorEmpty
|
||||
@@ -171,6 +174,7 @@ Element separatorStyled(BorderStyle style) {
|
||||
/// @ingroup dom
|
||||
/// @see separator
|
||||
/// @see separatorLight
|
||||
/// @see separatorDashed
|
||||
/// @see separatorDouble
|
||||
/// @see separatorHeavy
|
||||
/// @see separatorEmpty
|
||||
@@ -202,11 +206,49 @@ Element separatorLight() {
|
||||
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
|
||||
/// elements, using the HEAVY style.
|
||||
/// @ingroup dom
|
||||
/// @see separator
|
||||
/// @see separatorLight
|
||||
/// @see separatorDashed
|
||||
/// @see separatorDouble
|
||||
/// @see separatorHeavy
|
||||
/// @see separatorEmpty
|
||||
@@ -243,6 +285,7 @@ Element separatorHeavy() {
|
||||
/// @ingroup dom
|
||||
/// @see separator
|
||||
/// @see separatorLight
|
||||
/// @see separatorDashed
|
||||
/// @see separatorDouble
|
||||
/// @see separatorHeavy
|
||||
/// @see separatorEmpty
|
||||
@@ -279,6 +322,7 @@ Element separatorDouble() {
|
||||
/// @ingroup dom
|
||||
/// @see separator
|
||||
/// @see separatorLight
|
||||
/// @see separatorDashed
|
||||
/// @see separatorDouble
|
||||
/// @see separatorHeavy
|
||||
/// @see separatorEmpty
|
||||
@@ -316,6 +360,7 @@ Element separatorEmpty() {
|
||||
/// @ingroup dom
|
||||
/// @see separator
|
||||
/// @see separatorLight
|
||||
/// @see separatorDashed
|
||||
/// @see separatorDouble
|
||||
/// @see separatorHeavy
|
||||
/// @see separatorEmpty
|
||||
@@ -351,6 +396,7 @@ Element separatorCharacter(std::string value) {
|
||||
/// @ingroup dom
|
||||
/// @see separator
|
||||
/// @see separatorLight
|
||||
/// @see separatorDashed
|
||||
/// @see separatorHeavy
|
||||
/// @see separatorDouble
|
||||
/// @see separatorStyled
|
||||
|
@@ -35,6 +35,20 @@ TEST(SeparatorTest, Light) {
|
||||
"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) {
|
||||
auto element = vbox({
|
||||
text("top"),
|
||||
|
@@ -3,7 +3,7 @@
|
||||
#include <utility> // for move
|
||||
#include <vector> // for __alloc_traits<>::value_type
|
||||
|
||||
#include "ftxui/dom/elements.hpp" // for Constraint, Direction, EQUAL, GREATER_THAN, LESS_THAN, WIDTH, unpack, Decorator, Element, size
|
||||
#include "ftxui/dom/elements.hpp" // for Constraint, WidthOrHeight, EQUAL, GREATER_THAN, LESS_THAN, WIDTH, unpack, Decorator, Element, size
|
||||
#include "ftxui/dom/node.hpp" // for Node, Elements
|
||||
#include "ftxui/dom/requirement.hpp" // for Requirement
|
||||
#include "ftxui/screen/box.hpp" // for Box
|
||||
@@ -12,7 +12,7 @@ namespace ftxui {
|
||||
|
||||
class Size : public Node {
|
||||
public:
|
||||
Size(Element child, Direction direction, Constraint constraint, int value)
|
||||
Size(Element child, WidthOrHeight direction, Constraint constraint, int value)
|
||||
: Node(unpack(std::move(child))),
|
||||
direction_(direction),
|
||||
constraint_(constraint),
|
||||
@@ -71,7 +71,7 @@ class Size : public Node {
|
||||
}
|
||||
|
||||
private:
|
||||
Direction direction_;
|
||||
WidthOrHeight direction_;
|
||||
Constraint constraint_;
|
||||
int value_;
|
||||
};
|
||||
@@ -82,7 +82,7 @@ class Size : public Node {
|
||||
/// @param constraint The type of constaint.
|
||||
/// @param value The value.
|
||||
/// @ingroup dom
|
||||
Decorator size(Direction direction, Constraint constraint, int value) {
|
||||
Decorator size(WidthOrHeight direction, Constraint constraint, int value) {
|
||||
return [=](Element e) {
|
||||
return std::make_shared<Size>(std::move(e), direction, constraint, value);
|
||||
};
|
||||
|
@@ -14,12 +14,13 @@ bool IsCell(int x, int y) {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#include "ftxui/screen/color.hpp"
|
||||
|
||||
#include <array> // for array
|
||||
#include <array> // for array
|
||||
#include <cmath>
|
||||
#include <string_view> // for literals
|
||||
|
||||
#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(b, &red_b, &green_b, &blue_b);
|
||||
|
||||
return Color::RGB(static_cast<uint8_t>(static_cast<float>(red_a) * (1 - t) +
|
||||
static_cast<float>(red_b) * t),
|
||||
static_cast<uint8_t>(static_cast<float>(green_a) * (1 - t) +
|
||||
static_cast<float>(green_b) * t),
|
||||
static_cast<uint8_t>(static_cast<float>(blue_a) * (1 - t) +
|
||||
static_cast<float>(blue_b) * t));
|
||||
// Gamma correction:
|
||||
// https://en.wikipedia.org/wiki/Gamma_correction
|
||||
return Color::RGB(
|
||||
pow(pow(red_a, 2.2f) * (1 - t) + pow(red_b, 2.2f) * t, 1 / 2.2f),
|
||||
pow(pow(green_a, 2.2f) * (1 - t) + pow(green_b, 2.2f) * t, 1 / 2.2f),
|
||||
pow(pow(blue_a, 2.2f) * (1 - t) + pow(blue_b, 2.2f) * t, 1 / 2.2f));
|
||||
}
|
||||
|
||||
inline namespace literals {
|
||||
|
@@ -57,22 +57,22 @@ TEST(ColorTest, Interpolate) {
|
||||
Color::RGB(1, 2, 3), //
|
||||
Color::RGB(244, 244, 123)) //
|
||||
.Print(false),
|
||||
"38;2;73;74;39");
|
||||
"38;2;141;141;71");
|
||||
EXPECT_EQ(Color::Interpolate(0.7f, //
|
||||
Color::RGB(1, 2, 3), //
|
||||
Color::RGB(244, 244, 123)) //
|
||||
.Print(false),
|
||||
"38;2;171;171;87");
|
||||
"38;2;207;207;104");
|
||||
EXPECT_EQ(Color::Interpolate(0.7f, //
|
||||
Color(Color::Red), //
|
||||
Color::RGB(244, 244, 123)) //
|
||||
.Print(false),
|
||||
"38;2;209;170;86");
|
||||
"38;2;216;207;104");
|
||||
EXPECT_EQ(Color::Interpolate(0.7f, //
|
||||
Color::RGB(244, 244, 123), //
|
||||
Color(Color::Plum1)) //
|
||||
.Print(false),
|
||||
"38;2;251;195;215");
|
||||
"38;2;251;198;225");
|
||||
}
|
||||
|
||||
TEST(ColorTest, HSV) {
|
||||
|
@@ -154,9 +154,11 @@ struct TileEncoding {
|
||||
const std::map<std::string, TileEncoding> tile_encoding = { // NOLINT
|
||||
{"─", {1, 0, 1, 0, 0}},
|
||||
{"━", {2, 0, 2, 0, 0}},
|
||||
{"╍", {2, 0, 2, 0, 0}},
|
||||
|
||||
{"│", {0, 1, 0, 1, 0}},
|
||||
{"┃", {0, 2, 0, 2, 0}},
|
||||
{"╏", {0, 2, 0, 2, 0}},
|
||||
|
||||
{"┌", {0, 0, 1, 1, 0}},
|
||||
{"┍", {0, 0, 2, 1, 0}},
|
||||
|
Reference in New Issue
Block a user