mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-25 12:11:33 +08:00
Feature: LinearGradient
color decorator. (#592)
Based on the existing color decorators, create new ones to apply a gradient effect on the DOM. Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
This commit is contained in:
parent
399549d180
commit
0903ad1608
@ -11,6 +11,10 @@ current (development)
|
|||||||
### Dom
|
### Dom
|
||||||
- Feature: Add the dashed style for border and separator.
|
- Feature: Add the dashed style for border and separator.
|
||||||
- Feature: Add colored borders.
|
- Feature: Add colored borders.
|
||||||
|
- Feature: Customize with gradient color effect. Add the following decorators:
|
||||||
|
- `colorgrad`
|
||||||
|
- `bgcolorgrad`
|
||||||
|
- Improvement: Color::Interpolate() uses gamma correction.
|
||||||
|
|
||||||
###
|
###
|
||||||
- Breaking: Direction enum is renamed WidthOrHeight
|
- Breaking: Direction enum is renamed WidthOrHeight
|
||||||
|
@ -72,6 +72,7 @@ add_library(dom
|
|||||||
src/ftxui/dom/gridbox.cpp
|
src/ftxui/dom/gridbox.cpp
|
||||||
src/ftxui/dom/hbox.cpp
|
src/ftxui/dom/hbox.cpp
|
||||||
src/ftxui/dom/inverted.cpp
|
src/ftxui/dom/inverted.cpp
|
||||||
|
src/ftxui/dom/linear_gradient.cpp
|
||||||
src/ftxui/dom/node.cpp
|
src/ftxui/dom/node.cpp
|
||||||
src/ftxui/dom/node_decorator.cpp
|
src/ftxui/dom/node_decorator.cpp
|
||||||
src/ftxui/dom/paragraph.cpp
|
src/ftxui/dom/paragraph.cpp
|
||||||
|
@ -37,6 +37,7 @@ add_executable(ftxui-tests
|
|||||||
src/ftxui/dom/gauge_test.cpp
|
src/ftxui/dom/gauge_test.cpp
|
||||||
src/ftxui/dom/gridbox_test.cpp
|
src/ftxui/dom/gridbox_test.cpp
|
||||||
src/ftxui/dom/hbox_test.cpp
|
src/ftxui/dom/hbox_test.cpp
|
||||||
|
src/ftxui/dom/linear_gradient_test.cpp
|
||||||
src/ftxui/dom/scroll_indicator_test.cpp
|
src/ftxui/dom/scroll_indicator_test.cpp
|
||||||
src/ftxui/dom/separator_test.cpp
|
src/ftxui/dom/separator_test.cpp
|
||||||
src/ftxui/dom/spinner_test.cpp
|
src/ftxui/dom/spinner_test.cpp
|
||||||
|
@ -444,6 +444,36 @@ ftxui::Color::HSV(uint8_t hue, uint8_t saturation, uint8_t value);
|
|||||||
<script id="asciicast-xwzzghmqcqzIuyLwCpQFEqbEu" src="https://asciinema.org/a/xwzzghmqcqzIuyLwCpQFEqbEu.js" async></script>
|
<script id="asciicast-xwzzghmqcqzIuyLwCpQFEqbEu" src="https://asciinema.org/a/xwzzghmqcqzIuyLwCpQFEqbEu.js" async></script>
|
||||||
@endhtmlonly
|
@endhtmlonly
|
||||||
|
|
||||||
|
## LinearGradient #{#dom-linear-gradient}
|
||||||
|
|
||||||
|
FTXUI supports linear gradient. Either on the foreground or the background.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
Decorator color(const LinearGradient&);
|
||||||
|
Decorator bgcolor(const LinearGradient&);
|
||||||
|
```
|
||||||
|
|
||||||
|
A `ftxui::LinearGradient` is defined by an angle in degree, and a list of color
|
||||||
|
stops.
|
||||||
|
```cpp
|
||||||
|
auto gradient = LinearGradient()
|
||||||
|
.Angle(45)
|
||||||
|
.AddStop(0.0, Color::Red)
|
||||||
|
.AddStop(0.5, Color::Green)
|
||||||
|
.AddStop(1.0, Color::Blue);
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use simplified constructors:
|
||||||
|
```cpp
|
||||||
|
LinearGradient(Color::Red, Color::Blue);
|
||||||
|
```
|
||||||
|
```cpp
|
||||||
|
LinearGradient(45, Color::Red, Color::Blue);
|
||||||
|
```
|
||||||
|
|
||||||
|
See [demo](https://arthursonzogni.github.io/FTXUI/examples/?file=component/linear_gradient_gallery).
|
||||||
|
|
||||||
|
|
||||||
## Style {#dom-style}
|
## Style {#dom-style}
|
||||||
In addition to colored text and colored backgrounds. Many terminals support text
|
In addition to colored text and colored backgrounds. Many terminals support text
|
||||||
effects such as: `bold`, `dim`, `underlined`, `inverted`, `blink`.
|
effects such as: `bold`, `dim`, `underlined`, `inverted`, `blink`.
|
||||||
@ -458,6 +488,8 @@ Element strikethrough(Element);
|
|||||||
Element blink(Element);
|
Element blink(Element);
|
||||||
Decorator color(Color);
|
Decorator color(Color);
|
||||||
Decorator bgcolor(Color);
|
Decorator bgcolor(Color);
|
||||||
|
Decorator colorgrad(LinearGradient);
|
||||||
|
Decorator bgcolorgrad(LinearGradient);
|
||||||
```
|
```
|
||||||
|
|
||||||
[Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2style_gallery_8cpp-example.html)
|
[Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2style_gallery_8cpp-example.html)
|
||||||
@ -562,8 +594,7 @@ Simple [example](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/do
|
|||||||
|
|
||||||
Complex [example](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/component/canvas_animated.cpp):
|
Complex [example](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/component/canvas_animated.cpp):
|
||||||
|
|
||||||
![ezgif com-gif-maker (3)](https://user-images.githubusercontent.com/4759106/147250538-783a8246-98e0-4a25-b032-3bd3710549d1.gif)
|
![ezgif com-gif-maker (3)](https://user-images.githubusercontent.com/4759106/147250538-783a8246-98e0-4a25-b032-3bd3710549d1.gif)
|
||||||
|
|
||||||
# component {#module-component}
|
# component {#module-component}
|
||||||
|
|
||||||
The `ftxui::component` module defines the logic that produces interactive
|
The `ftxui::component` module defines the logic that produces interactive
|
||||||
|
@ -17,6 +17,7 @@ example(focus_cursor)
|
|||||||
example(gallery)
|
example(gallery)
|
||||||
example(homescreen)
|
example(homescreen)
|
||||||
example(input)
|
example(input)
|
||||||
|
example(linear_gradient_gallery)
|
||||||
example(maybe)
|
example(maybe)
|
||||||
example(menu)
|
example(menu)
|
||||||
example(menu2)
|
example(menu2)
|
||||||
|
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.
|
@ -3,18 +3,19 @@ set(DIRECTORY_LIB dom)
|
|||||||
example(border)
|
example(border)
|
||||||
example(border_colored)
|
example(border_colored)
|
||||||
example(border_style)
|
example(border_style)
|
||||||
|
example(canvas)
|
||||||
example(color_gallery)
|
example(color_gallery)
|
||||||
example(color_info_palette256)
|
example(color_info_palette256)
|
||||||
example(color_truecolor_HSV)
|
example(color_truecolor_HSV)
|
||||||
example(color_truecolor_RGB)
|
example(color_truecolor_RGB)
|
||||||
example(dbox)
|
example(dbox)
|
||||||
example(canvas)
|
|
||||||
example(gauge)
|
example(gauge)
|
||||||
example(gauge_direction)
|
example(gauge_direction)
|
||||||
example(graph)
|
example(graph)
|
||||||
example(gridbox)
|
example(gridbox)
|
||||||
example(hflow)
|
example(hflow)
|
||||||
example(html_like)
|
example(html_like)
|
||||||
|
example(linear_gradient)
|
||||||
example(package_manager)
|
example(package_manager)
|
||||||
example(paragraph)
|
example(paragraph)
|
||||||
example(separator)
|
example(separator)
|
||||||
|
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.
|
@ -1,59 +1,60 @@
|
|||||||
#include <ftxui/screen/screen.hpp> // for Full, Screen
|
#include <ftxui/dom/linear_gradient.hpp> // for LinearGradient
|
||||||
#include <memory> // for allocator
|
#include <ftxui/screen/screen.hpp> // for Full, Screen
|
||||||
|
#include <memory> // for allocator
|
||||||
|
|
||||||
#include "ftxui/dom/elements.hpp" // for text, bgcolor, color, vbox, Fit, filler, hbox
|
#include "ftxui/dom/elements.hpp" // for text, bgcolor, color, vbox, filler, Fit, hbox
|
||||||
#include "ftxui/dom/node.hpp" // for Render
|
#include "ftxui/dom/node.hpp" // for Render
|
||||||
#include "ftxui/screen/box.hpp" // for ftxui
|
#include "ftxui/screen/color.hpp" // for Color, operator""_rgb, Color::Black, Color::Blue, Color::BlueLight, Color::Cyan, Color::CyanLight, Color::DeepSkyBlue4, Color::Default, Color::GrayDark, Color::GrayLight, Color::Green, Color::GreenLight, Color::Magenta, Color::MagentaLight, Color::Red, Color::RedLight, Color::SkyBlue1, Color::White, Color::Yellow, Color::YellowLight, ftxui
|
||||||
#include "ftxui/screen/color.hpp" // for Color, Color::Black, Color::Blue, Color::BlueLight, Color::Cyan, Color::CyanLight, Color::Default, Color::GrayDark, Color::GrayLight, Color::Green, Color::GreenLight, Color::Magenta, Color::MagentaLight, Color::Red, Color::RedLight, Color::White, Color::Yellow, Color::YellowLight
|
|
||||||
|
|
||||||
int main(int argc, const char* argv[]) {
|
int main(int argc, const char* argv[]) {
|
||||||
using namespace ftxui;
|
using namespace ftxui;
|
||||||
// clang-format off
|
auto document = hbox({
|
||||||
auto document =
|
vbox({
|
||||||
hbox(
|
color(Color::Default, text("Default")),
|
||||||
vbox(
|
color(Color::Black, text("Black")),
|
||||||
color(Color::Default, text("Default")),
|
color(Color::GrayDark, text("GrayDark")),
|
||||||
color(Color::Black, text("Black")),
|
color(Color::GrayLight, text("GrayLight")),
|
||||||
color(Color::GrayDark, text("GrayDark")),
|
color(Color::White, text("White")),
|
||||||
color(Color::GrayLight, text("GrayLight")),
|
color(Color::Blue, text("Blue")),
|
||||||
color(Color::White, text("White")),
|
color(Color::BlueLight, text("BlueLight")),
|
||||||
color(Color::Blue, text("Blue")),
|
color(Color::Cyan, text("Cyan")),
|
||||||
color(Color::BlueLight, text("BlueLight")),
|
color(Color::CyanLight, text("CyanLight")),
|
||||||
color(Color::Cyan, text("Cyan")),
|
color(Color::Green, text("Green")),
|
||||||
color(Color::CyanLight, text("CyanLight")),
|
color(Color::GreenLight, text("GreenLight")),
|
||||||
color(Color::Green, text("Green")),
|
color(Color::Magenta, text("Magenta")),
|
||||||
color(Color::GreenLight, text("GreenLight")),
|
color(Color::MagentaLight, text("MagentaLight")),
|
||||||
color(Color::Magenta, text("Magenta")),
|
color(Color::Red, text("Red")),
|
||||||
color(Color::MagentaLight, text("MagentaLight")),
|
color(Color::RedLight, text("RedLight")),
|
||||||
color(Color::Red, text("Red")),
|
color(Color::Yellow, text("Yellow")),
|
||||||
color(Color::RedLight, text("RedLight")),
|
color(Color::YellowLight, text("YellowLight")),
|
||||||
color(Color::Yellow, text("Yellow")),
|
color(0x66ff66_rgb, text("Phosphor")),
|
||||||
color(Color::YellowLight, text("YellowLight")),
|
color(LinearGradient(Color::SkyBlue1, Color::DeepSkyBlue4),
|
||||||
color(0x66ff66_rgb, text("Phosphor"))
|
text("Skyblue to DeepSkyBlue")),
|
||||||
),
|
}),
|
||||||
vbox(
|
vbox({
|
||||||
bgcolor(Color::Default, text("Default")),
|
bgcolor(Color::Default, text("Default")),
|
||||||
bgcolor(Color::Black, text("Black")),
|
bgcolor(Color::Black, text("Black")),
|
||||||
bgcolor(Color::GrayDark, text("GrayDark")),
|
bgcolor(Color::GrayDark, text("GrayDark")),
|
||||||
bgcolor(Color::GrayLight, text("GrayLight")),
|
bgcolor(Color::GrayLight, text("GrayLight")),
|
||||||
bgcolor(Color::White, text("White")),
|
bgcolor(Color::White, text("White")),
|
||||||
bgcolor(Color::Blue, text("Blue")),
|
bgcolor(Color::Blue, text("Blue")),
|
||||||
bgcolor(Color::BlueLight, text("BlueLight")),
|
bgcolor(Color::BlueLight, text("BlueLight")),
|
||||||
bgcolor(Color::Cyan, text("Cyan")),
|
bgcolor(Color::Cyan, text("Cyan")),
|
||||||
bgcolor(Color::CyanLight, text("CyanLight")),
|
bgcolor(Color::CyanLight, text("CyanLight")),
|
||||||
bgcolor(Color::Green, text("Green")),
|
bgcolor(Color::Green, text("Green")),
|
||||||
bgcolor(Color::GreenLight, text("GreenLight")),
|
bgcolor(Color::GreenLight, text("GreenLight")),
|
||||||
bgcolor(Color::Magenta, text("Magenta")),
|
bgcolor(Color::Magenta, text("Magenta")),
|
||||||
bgcolor(Color::MagentaLight, text("MagentaLight")),
|
bgcolor(Color::MagentaLight, text("MagentaLight")),
|
||||||
bgcolor(Color::Red, text("Red")),
|
bgcolor(Color::Red, text("Red")),
|
||||||
bgcolor(Color::RedLight, text("RedLight")),
|
bgcolor(Color::RedLight, text("RedLight")),
|
||||||
bgcolor(Color::Yellow, text("Yellow")),
|
bgcolor(Color::Yellow, text("Yellow")),
|
||||||
bgcolor(Color::YellowLight, text("YellowLight")),
|
bgcolor(Color::YellowLight, text("YellowLight")),
|
||||||
bgcolor(0x66ff66_rgb, text("Phosphor"))
|
bgcolor(0x66ff66_rgb, text("Phosphor")),
|
||||||
),
|
bgcolor(LinearGradient(Color::SkyBlue1, Color::DeepSkyBlue4),
|
||||||
filler()
|
text("Skyblue to DeepSkyBlue")),
|
||||||
);
|
}),
|
||||||
// clang-format on
|
filler(),
|
||||||
|
});
|
||||||
|
|
||||||
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
|
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
|
||||||
Render(screen, document);
|
Render(screen, document);
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "ftxui/dom/canvas.hpp"
|
#include "ftxui/dom/canvas.hpp"
|
||||||
#include "ftxui/dom/direction.hpp"
|
#include "ftxui/dom/direction.hpp"
|
||||||
#include "ftxui/dom/flexbox_config.hpp"
|
#include "ftxui/dom/flexbox_config.hpp"
|
||||||
|
#include "ftxui/dom/linear_gradient.hpp"
|
||||||
#include "ftxui/dom/node.hpp"
|
#include "ftxui/dom/node.hpp"
|
||||||
#include "ftxui/screen/box.hpp"
|
#include "ftxui/screen/box.hpp"
|
||||||
#include "ftxui/screen/color.hpp"
|
#include "ftxui/screen/color.hpp"
|
||||||
@ -99,8 +100,12 @@ Element blink(Element);
|
|||||||
Element strikethrough(Element);
|
Element strikethrough(Element);
|
||||||
Decorator color(Color);
|
Decorator color(Color);
|
||||||
Decorator bgcolor(Color);
|
Decorator bgcolor(Color);
|
||||||
|
Decorator color(const LinearGradient&);
|
||||||
|
Decorator bgcolor(const LinearGradient&);
|
||||||
Element color(Color, Element);
|
Element color(Color, Element);
|
||||||
Element bgcolor(Color, Element);
|
Element bgcolor(Color, Element);
|
||||||
|
Element color(const LinearGradient&, Element);
|
||||||
|
Element bgcolor(const LinearGradient&, Element);
|
||||||
Decorator focusPosition(int x, int y);
|
Decorator focusPosition(int x, int y);
|
||||||
Decorator focusPositionRelative(float x, float y);
|
Decorator focusPositionRelative(float x, float y);
|
||||||
Element automerge(Element child);
|
Element automerge(Element child);
|
||||||
|
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
|
#define FTXUI_SCREEN_COLOR_HPP
|
||||||
|
|
||||||
#include <cstdint> // for uint8_t
|
#include <cstdint> // for uint8_t
|
||||||
#include <string> // for wstring
|
#include <string> // for string
|
||||||
|
#include <vector> // for vector
|
||||||
|
|
||||||
#ifdef RGB
|
#ifdef RGB
|
||||||
// Workaround for wingdi.h (via Windows.h) defining macros that break things.
|
// Workaround for wingdi.h (via Windows.h) defining macros that break things.
|
||||||
|
@ -124,10 +124,10 @@ TEST(ButtonTest, Animation) {
|
|||||||
Screen screen(12, 3);
|
Screen screen(12, 3);
|
||||||
Render(screen, container->Render());
|
Render(screen, container->Render());
|
||||||
EXPECT_EQ(screen.ToString(),
|
EXPECT_EQ(screen.ToString(),
|
||||||
"\x1B[1m\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m \x1B[22m "
|
"\x1B[1m\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m \x1B[22m "
|
||||||
" \x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;192;192;192m\x1B[48;2;0;0;"
|
" \x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;191;191;191m\x1B[48;2;0;0;"
|
||||||
"0m btn1 \x1B[22m btn2 "
|
"0m btn1 \x1B[22m btn2 "
|
||||||
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;192;192;192m\x1B[48;2;0;0;"
|
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;191;191;191m\x1B[48;2;0;0;"
|
||||||
"0m \x1B[22m \x1B[39m\x1B[49m");
|
"0m \x1B[22m \x1B[39m\x1B[49m");
|
||||||
}
|
}
|
||||||
selected = 1;
|
selected = 1;
|
||||||
@ -135,10 +135,10 @@ TEST(ButtonTest, Animation) {
|
|||||||
Screen screen(12, 3);
|
Screen screen(12, 3);
|
||||||
Render(screen, container->Render());
|
Render(screen, container->Render());
|
||||||
EXPECT_EQ(screen.ToString(),
|
EXPECT_EQ(screen.ToString(),
|
||||||
"\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m \x1B[1m "
|
"\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m \x1B[1m "
|
||||||
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;192;192;192m\x1B[48;2;0;0;"
|
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;191;191;191m\x1B[48;2;0;0;"
|
||||||
"0m btn1 \x1B[1m btn2 "
|
"0m btn1 \x1B[1m btn2 "
|
||||||
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;192;192;192m\x1B[48;2;0;0;"
|
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;191;191;191m\x1B[48;2;0;0;"
|
||||||
"0m \x1B[1m \x1B[22m\x1B[39m\x1B[49m");
|
"0m \x1B[1m \x1B[22m\x1B[39m\x1B[49m");
|
||||||
}
|
}
|
||||||
animation::Params params(2s);
|
animation::Params params(2s);
|
||||||
@ -148,12 +148,12 @@ TEST(ButtonTest, Animation) {
|
|||||||
Render(screen, container->Render());
|
Render(screen, container->Render());
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
screen.ToString(),
|
screen.ToString(),
|
||||||
"\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m "
|
"\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m "
|
||||||
"\x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m "
|
"\x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m "
|
||||||
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m "
|
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m "
|
||||||
"btn1 \x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m btn2 "
|
"btn1 \x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m btn2 "
|
||||||
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m "
|
"\x1B[22m\x1B[39m\x1B[49m\r\n\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m "
|
||||||
" \x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m "
|
" \x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m "
|
||||||
"\x1B[22m\x1B[39m\x1B[49m");
|
"\x1B[22m\x1B[39m\x1B[49m");
|
||||||
}
|
}
|
||||||
EXPECT_EQ(selected, 1);
|
EXPECT_EQ(selected, 1);
|
||||||
@ -164,12 +164,12 @@ TEST(ButtonTest, Animation) {
|
|||||||
Render(screen, container->Render());
|
Render(screen, container->Render());
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
screen.ToString(),
|
screen.ToString(),
|
||||||
"\x1B[1m\x1B[38;2;223;223;223m\x1B[48;2;64;64;64m "
|
"\x1B[1m\x1B[38;2;226;226;226m\x1B[48;2;93;93;93m "
|
||||||
"\x1B[22m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m "
|
"\x1B[22m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m "
|
||||||
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;223;223;223m\x1B[48;2;64;64;64m "
|
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;226;226;226m\x1B[48;2;93;93;93m "
|
||||||
"btn1 \x1B[22m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m btn2 "
|
"btn1 \x1B[22m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m btn2 "
|
||||||
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;223;223;223m\x1B[48;2;64;64;64m "
|
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;226;226;226m\x1B[48;2;93;93;93m "
|
||||||
" \x1B[22m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m "
|
" \x1B[22m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m "
|
||||||
"\x1B[39m\x1B[49m");
|
"\x1B[39m\x1B[49m");
|
||||||
}
|
}
|
||||||
container->OnAnimation(params);
|
container->OnAnimation(params);
|
||||||
@ -178,12 +178,12 @@ TEST(ButtonTest, Animation) {
|
|||||||
Render(screen, container->Render());
|
Render(screen, container->Render());
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
screen.ToString(),
|
screen.ToString(),
|
||||||
"\x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;128m "
|
"\x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;127m "
|
||||||
"\x1B[22m\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m "
|
"\x1B[22m\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m "
|
||||||
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;"
|
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;"
|
||||||
"128m btn1 \x1B[22m\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m btn2 "
|
"127m btn1 \x1B[22m\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m btn2 "
|
||||||
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;255;255;255m\x1B[48;2;128;128;"
|
"\x1B[39m\x1B[49m\r\n\x1B[1m\x1B[38;2;254;254;254m\x1B[48;2;127;127;"
|
||||||
"128m \x1B[22m\x1B[38;2;192;192;192m\x1B[48;2;0;0;0m "
|
"127m \x1B[22m\x1B[38;2;191;191;191m\x1B[48;2;0;0;0m "
|
||||||
"\x1B[39m\x1B[49m");
|
"\x1B[39m\x1B[49m");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
#include <algorithm> // for max
|
#include <algorithm> // for max
|
||||||
#include <array> // for array
|
#include <array> // for array
|
||||||
#include <memory> // for allocator, make_shared, __shared_ptr_access
|
#include <ftxui/screen/color.hpp> // for Color
|
||||||
#include <optional>
|
#include <memory> // for allocator, make_shared, __shared_ptr_access
|
||||||
#include <string> // for basic_string, string
|
#include <optional> // for optional, nullopt
|
||||||
#include <utility> // for move
|
#include <string> // for basic_string, string
|
||||||
#include <vector> // for __alloc_traits<>::value_type
|
#include <utility> // for move
|
||||||
|
#include <vector> // for __alloc_traits<>::value_type
|
||||||
|
|
||||||
#include "ftxui/dom/elements.hpp" // for unpack, Element, Decorator, BorderStyle, ROUNDED, Elements, DOUBLE, EMPTY, HEAVY, LIGHT, border, borderDouble, borderEmpty, borderHeavy, borderLight, borderRounded, borderStyled, borderWith, window
|
#include "ftxui/dom/elements.hpp" // for unpack, Element, Decorator, BorderStyle, ROUNDED, borderStyled, Elements, DASHED, DOUBLE, EMPTY, HEAVY, LIGHT, border, borderDashed, borderDouble, borderEmpty, borderHeavy, borderLight, borderRounded, borderWith, window
|
||||||
#include "ftxui/dom/node.hpp" // for Node, Elements
|
#include "ftxui/dom/node.hpp" // for Node, Elements
|
||||||
#include "ftxui/dom/requirement.hpp" // for Requirement
|
#include "ftxui/dom/requirement.hpp" // for Requirement
|
||||||
#include "ftxui/screen/box.hpp" // for Box
|
#include "ftxui/screen/box.hpp" // for Box
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h> // for Test, EXPECT_EQ, Message, TestPartResult, TestInfo (ptr only), TEST
|
||||||
#include <string> // for allocator
|
#include <string> // for allocator
|
||||||
|
|
||||||
#include "ftxui/dom/elements.hpp" // for operator|, text, bgcolor, color, Element
|
#include "ftxui/dom/elements.hpp" // for operator|, text, bgcolor, color, Element
|
||||||
#include "ftxui/dom/node.hpp" // for Render
|
#include "ftxui/dom/node.hpp" // for Render
|
||||||
#include "ftxui/screen/color.hpp" // for Color, Color::Red
|
#include "ftxui/screen/color.hpp" // for Color, Color::Red, Color::RedLight
|
||||||
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
|
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
|
||||||
|
|
||||||
namespace ftxui {
|
namespace ftxui {
|
||||||
|
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.
|
@ -15,12 +15,12 @@ bool IsCell(int x, int y) {
|
|||||||
|
|
||||||
// NOLINTNEXTLINE
|
// NOLINTNEXTLINE
|
||||||
static std::string charset[6][6] = {
|
static std::string charset[6][6] = {
|
||||||
{"┌", "┐", "└", "┘", "─", "│"}, // LIGHT
|
{"┌", "┐", "└", "┘", "─", "│"}, // LIGHT
|
||||||
{"┏", "┓", "┗", "┛", "╍", "╏"}, // DASHED
|
{"┏", "┓", "┗", "┛", "╍", "╏"}, // DASHED
|
||||||
{"┏", "┓", "┗", "┛", "━", "┃"}, // HEAVY
|
{"┏", "┓", "┗", "┛", "━", "┃"}, // HEAVY
|
||||||
{"╔", "╗", "╚", "╝", "═", "║"}, // DOUBLE
|
{"╔", "╗", "╚", "╝", "═", "║"}, // DOUBLE
|
||||||
{"╭", "╮", "╰", "╯", "─", "│"}, // ROUNDED
|
{"╭", "╮", "╰", "╯", "─", "│"}, // ROUNDED
|
||||||
{" ", " ", " ", " ", " ", " "}, // EMPTY
|
{" ", " ", " ", " ", " ", " "}, // EMPTY
|
||||||
};
|
};
|
||||||
|
|
||||||
int Wrap(int input, int modulo) {
|
int Wrap(int input, int modulo) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "ftxui/screen/color.hpp"
|
#include "ftxui/screen/color.hpp"
|
||||||
|
|
||||||
#include <array> // for array
|
#include <array> // for array
|
||||||
|
#include <cmath>
|
||||||
#include <string_view> // for literals
|
#include <string_view> // for literals
|
||||||
|
|
||||||
#include "ftxui/screen/color_info.hpp" // for GetColorInfo, ColorInfo
|
#include "ftxui/screen/color_info.hpp" // for GetColorInfo, ColorInfo
|
||||||
@ -220,12 +221,12 @@ Color Color::Interpolate(float t, const Color& a, const Color& b) {
|
|||||||
get_color(a, &red_a, &green_a, &blue_a);
|
get_color(a, &red_a, &green_a, &blue_a);
|
||||||
get_color(b, &red_b, &green_b, &blue_b);
|
get_color(b, &red_b, &green_b, &blue_b);
|
||||||
|
|
||||||
return Color::RGB(static_cast<uint8_t>(static_cast<float>(red_a) * (1 - t) +
|
// Gamma correction:
|
||||||
static_cast<float>(red_b) * t),
|
// https://en.wikipedia.org/wiki/Gamma_correction
|
||||||
static_cast<uint8_t>(static_cast<float>(green_a) * (1 - t) +
|
return Color::RGB(
|
||||||
static_cast<float>(green_b) * t),
|
pow(pow(red_a, 2.2f) * (1 - t) + pow(red_b, 2.2f) * t, 1 / 2.2f),
|
||||||
static_cast<uint8_t>(static_cast<float>(blue_a) * (1 - t) +
|
pow(pow(green_a, 2.2f) * (1 - t) + pow(green_b, 2.2f) * t, 1 / 2.2f),
|
||||||
static_cast<float>(blue_b) * t));
|
pow(pow(blue_a, 2.2f) * (1 - t) + pow(blue_b, 2.2f) * t, 1 / 2.2f));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
|
@ -57,22 +57,22 @@ TEST(ColorTest, Interpolate) {
|
|||||||
Color::RGB(1, 2, 3), //
|
Color::RGB(1, 2, 3), //
|
||||||
Color::RGB(244, 244, 123)) //
|
Color::RGB(244, 244, 123)) //
|
||||||
.Print(false),
|
.Print(false),
|
||||||
"38;2;73;74;39");
|
"38;2;141;141;71");
|
||||||
EXPECT_EQ(Color::Interpolate(0.7f, //
|
EXPECT_EQ(Color::Interpolate(0.7f, //
|
||||||
Color::RGB(1, 2, 3), //
|
Color::RGB(1, 2, 3), //
|
||||||
Color::RGB(244, 244, 123)) //
|
Color::RGB(244, 244, 123)) //
|
||||||
.Print(false),
|
.Print(false),
|
||||||
"38;2;171;171;87");
|
"38;2;207;207;104");
|
||||||
EXPECT_EQ(Color::Interpolate(0.7f, //
|
EXPECT_EQ(Color::Interpolate(0.7f, //
|
||||||
Color(Color::Red), //
|
Color(Color::Red), //
|
||||||
Color::RGB(244, 244, 123)) //
|
Color::RGB(244, 244, 123)) //
|
||||||
.Print(false),
|
.Print(false),
|
||||||
"38;2;209;170;86");
|
"38;2;216;207;104");
|
||||||
EXPECT_EQ(Color::Interpolate(0.7f, //
|
EXPECT_EQ(Color::Interpolate(0.7f, //
|
||||||
Color::RGB(244, 244, 123), //
|
Color::RGB(244, 244, 123), //
|
||||||
Color(Color::Plum1)) //
|
Color(Color::Plum1)) //
|
||||||
.Print(false),
|
.Print(false),
|
||||||
"38;2;251;195;215");
|
"38;2;251;198;225");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ColorTest, HSV) {
|
TEST(ColorTest, HSV) {
|
||||||
|
Loading…
Reference in New Issue
Block a user