mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2025-09-17 16:38:09 +08:00
Gauge direction (#326)
Add `gauge` with all the different directions. Co-authored-by: Aleksandar Brakmic <13668697+brakmic-aleksandar@users.noreply.github.com>
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
static std::string charset[11] = {
|
||||
static std::string charset_horizontal[11] = {
|
||||
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
|
||||
// Microsoft's terminals often use fonts not handling the 8 unicode
|
||||
// characters for representing the whole gauge. Fallback with less.
|
||||
@@ -22,38 +22,229 @@ static std::string charset[11] = {
|
||||
// int(9 * (limit - limit_int) = 9
|
||||
"█"};
|
||||
|
||||
static std::string charset_vertical[10] = {
|
||||
"█",
|
||||
"▇",
|
||||
"▆",
|
||||
"▅",
|
||||
"▄",
|
||||
"▃",
|
||||
"▂",
|
||||
"▁",
|
||||
" ",
|
||||
// An extra character in case when the fuzzer manage to have:
|
||||
// int(8 * (limit - limit_int) = 8
|
||||
" ",
|
||||
};
|
||||
|
||||
class Gauge : public Node {
|
||||
public:
|
||||
Gauge(float progress) : progress_(std::min(std::max(progress, 0.f), 1.f)) {}
|
||||
Gauge(float progress, GaugeDirection direction)
|
||||
: progress_(std::min(std::max(progress, 0.f), 1.f)),
|
||||
direction_(direction) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.flex_grow_x = 1;
|
||||
requirement_.flex_grow_y = 0;
|
||||
requirement_.flex_shrink_x = 1;
|
||||
requirement_.flex_shrink_y = 0;
|
||||
switch (direction_) {
|
||||
case GaugeDirection::Right:
|
||||
case GaugeDirection::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:
|
||||
requirement_.flex_grow_x = 0;
|
||||
requirement_.flex_grow_y = 1;
|
||||
requirement_.flex_shrink_x = 0;
|
||||
requirement_.flex_shrink_y = 1;
|
||||
break;
|
||||
}
|
||||
requirement_.min_x = 1;
|
||||
requirement_.min_y = 1;
|
||||
}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
switch (direction_) {
|
||||
case GaugeDirection::Right:
|
||||
RenderHorizontal(screen, /*invert=*/false);
|
||||
break;
|
||||
case GaugeDirection::Up:
|
||||
RenderVertical(screen, /*invert=*/false);
|
||||
break;
|
||||
case GaugeDirection::Left:
|
||||
RenderHorizontal(screen, /*invert=*/true);
|
||||
break;
|
||||
case GaugeDirection::Down:
|
||||
RenderVertical(screen, /*invert=*/true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderHorizontal(Screen& screen, bool invert) {
|
||||
int y = box_.y_min;
|
||||
if (y > box_.y_max)
|
||||
return;
|
||||
|
||||
float limit = box_.x_min + progress_ * (box_.x_max - box_.x_min + 1);
|
||||
int limit_int = limit;
|
||||
// Draw the progress bar horizontally.
|
||||
{
|
||||
float progress = invert ? 1.f - progress_ : progress_;
|
||||
float limit = box_.x_min + progress * (box_.x_max - box_.x_min + 1);
|
||||
int limit_int = limit;
|
||||
int x = box_.x_min;
|
||||
while (x < limit_int)
|
||||
screen.at(x++, y) = charset_horizontal[9];
|
||||
screen.at(x++, y) = charset_horizontal[int(9 * (limit - limit_int))];
|
||||
while (x <= box_.x_max)
|
||||
screen.at(x++, y) = charset_horizontal[0];
|
||||
}
|
||||
|
||||
if (invert) {
|
||||
for (int x = box_.x_min; x <= box_.x_max; x++)
|
||||
screen.PixelAt(x, y).inverted ^= true;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderVertical(Screen& screen, bool invert) {
|
||||
int x = box_.x_min;
|
||||
while (x < limit_int)
|
||||
screen.at(x++, y) = charset[9];
|
||||
screen.at(x++, y) = charset[int(9 * (limit - limit_int))];
|
||||
while (x <= box_.x_max)
|
||||
screen.at(x++, y) = charset[0];
|
||||
if (x > box_.x_max)
|
||||
return;
|
||||
|
||||
// Draw the progress bar vertically:
|
||||
{
|
||||
float progress = invert ? progress_ : 1.f - progress_;
|
||||
float limit = box_.y_min + progress * (box_.y_max - box_.y_min + 1);
|
||||
int limit_int = limit;
|
||||
int y = box_.y_min;
|
||||
while (y < limit_int)
|
||||
screen.at(x, y++) = charset_vertical[8];
|
||||
screen.at(x, y++) = charset_vertical[int(8 * (limit - limit_int))];
|
||||
while (y <= box_.y_max)
|
||||
screen.at(x, y++) = charset_vertical[0];
|
||||
}
|
||||
|
||||
if (invert) {
|
||||
for (int y = box_.y_min; y <= box_.y_max; y++)
|
||||
screen.PixelAt(x, y).inverted ^= true;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
float progress_;
|
||||
GaugeDirection direction_;
|
||||
};
|
||||
|
||||
/// @brief Draw a high definition progress bar progressing in specified
|
||||
/// direction.
|
||||
/// @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) {
|
||||
return std::make_shared<Gauge>(progress, direction);
|
||||
}
|
||||
|
||||
/// @brief Draw a high definition progress bar progressing from left to right.
|
||||
/// @param progress The proportion of the area to be filled. Belong to [0,1].
|
||||
/// @ingroup dom
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// A gauge. It can be used to represent a progress bar.
|
||||
/// ~~~cpp
|
||||
/// border(gaugeRight(0.5))
|
||||
/// ~~~
|
||||
///
|
||||
/// #### Output
|
||||
///
|
||||
/// ~~~bash
|
||||
/// ┌──────────────────────────────────────────────────────────────────────────┐
|
||||
/// │█████████████████████████████████████ │
|
||||
/// └──────────────────────────────────────────────────────────────────────────┘
|
||||
/// ~~~
|
||||
Element gaugeRight(float progress) {
|
||||
return gaugeDirection(progress, GaugeDirection::Right);
|
||||
}
|
||||
|
||||
/// @brief Draw a high definition progress bar progressing from right to left.
|
||||
/// @param progress The proportion of the area to be filled. Belong to [0,1].
|
||||
/// @ingroup dom
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// A gauge. It can be used to represent a progress bar.
|
||||
/// ~~~cpp
|
||||
/// border(gaugeLeft(0.5))
|
||||
/// ~~~
|
||||
///
|
||||
/// #### Output
|
||||
///
|
||||
/// ~~~bash
|
||||
/// ┌──────────────────────────────────────────────────────────────────────────┐
|
||||
/// │ █████████████████████████████████████│
|
||||
/// └──────────────────────────────────────────────────────────────────────────┘
|
||||
/// ~~~
|
||||
Element gaugeLeft(float progress) {
|
||||
return gaugeDirection(progress, GaugeDirection::Left);
|
||||
}
|
||||
|
||||
/// @brief Draw a high definition progress bar progressing from bottom to top.
|
||||
/// @param progress The proportion of the area to be filled. Belong to [0,1].
|
||||
/// @ingroup dom
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// A gauge. It can be used to represent a progress bar.
|
||||
/// ~~~cpp
|
||||
/// border(gaugeUp(0.5))
|
||||
/// ~~~
|
||||
///
|
||||
/// #### Output
|
||||
///
|
||||
/// ~~~bash
|
||||
/// ┌─┐
|
||||
/// │ │
|
||||
/// │ │
|
||||
/// │ │
|
||||
/// │ │
|
||||
/// │█│
|
||||
/// │█│
|
||||
/// │█│
|
||||
/// │█│
|
||||
/// └─┘
|
||||
/// ~~~
|
||||
Element gaugeUp(float progress) {
|
||||
return gaugeDirection(progress, GaugeDirection::Up);
|
||||
}
|
||||
|
||||
/// @brief Draw a high definition progress bar progressing from top to bottom.
|
||||
/// @param progress The proportion of the area to be filled. Belong to [0,1].
|
||||
/// @ingroup dom
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// A gauge. It can be used to represent a progress bar.
|
||||
/// ~~~cpp
|
||||
/// border(gaugeDown(0.5))
|
||||
/// ~~~
|
||||
///
|
||||
/// #### Output
|
||||
///
|
||||
/// ~~~bash
|
||||
/// ┌─┐
|
||||
/// │█│
|
||||
/// │█│
|
||||
/// │█│
|
||||
/// │█│
|
||||
/// │ │
|
||||
/// │ │
|
||||
/// │ │
|
||||
/// │ │
|
||||
/// └─┘
|
||||
/// ~~~
|
||||
Element gaugeDown(float progress) {
|
||||
return gaugeDirection(progress, GaugeDirection::Down);
|
||||
}
|
||||
|
||||
/// @brief Draw a high definition progress bar.
|
||||
/// @param progress The proportion of the area to be filled. Belong to [0,1].
|
||||
/// @ingroup dom
|
||||
@@ -73,7 +264,7 @@ class Gauge : public Node {
|
||||
/// └──────────────────────────────────────────────────────────────────────────┘
|
||||
/// ~~~
|
||||
Element gauge(float progress) {
|
||||
return std::make_shared<Gauge>(progress);
|
||||
return gaugeRight(progress);
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
@@ -11,7 +11,7 @@
|
||||
using namespace ftxui;
|
||||
using namespace ftxui;
|
||||
|
||||
TEST(GaugeTest, zero) {
|
||||
TEST(GaugeTest, ZeroHorizontal) {
|
||||
auto root = gauge(0);
|
||||
Screen screen(11, 1);
|
||||
Render(screen, root);
|
||||
@@ -19,16 +19,15 @@ TEST(GaugeTest, zero) {
|
||||
EXPECT_EQ(" ", screen.ToString());
|
||||
}
|
||||
|
||||
TEST(GaugeTest, half) {
|
||||
TEST(GaugeTest, HalfHorizontal) {
|
||||
auto root = gauge(0.5);
|
||||
Screen screen(11, 1);
|
||||
Render(screen, root);
|
||||
|
||||
EXPECT_EQ("█████▍ ", screen.ToString());
|
||||
//" ▏▎▍▌▊▉█";
|
||||
}
|
||||
|
||||
TEST(GaugeTest, one) {
|
||||
TEST(GaugeTest, OneHorizontal) {
|
||||
auto root = gauge(1.0);
|
||||
Screen screen(11, 1);
|
||||
Render(screen, root);
|
||||
@@ -36,6 +35,66 @@ TEST(GaugeTest, one) {
|
||||
EXPECT_EQ("███████████", screen.ToString());
|
||||
}
|
||||
|
||||
TEST(GaugeTest, ZeroVertical) {
|
||||
auto root = gaugeUp(0);
|
||||
Screen screen(1, 11);
|
||||
Render(screen, root);
|
||||
|
||||
EXPECT_EQ(
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
" ",
|
||||
screen.ToString());
|
||||
}
|
||||
|
||||
TEST(GaugeTest, HalfVertical) {
|
||||
auto root = gaugeUp(0.5);
|
||||
Screen screen(1, 11);
|
||||
Render(screen, root);
|
||||
|
||||
EXPECT_EQ(
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
" \r\n"
|
||||
"▄\r\n"
|
||||
"█\r\n"
|
||||
"█\r\n"
|
||||
"█\r\n"
|
||||
"█\r\n"
|
||||
"█",
|
||||
screen.ToString());
|
||||
}
|
||||
|
||||
TEST(GaugeTest, OneVertical) {
|
||||
auto root = gaugeUp(1.0);
|
||||
Screen screen(1, 11);
|
||||
Render(screen, root);
|
||||
|
||||
EXPECT_EQ(
|
||||
"█\r\n"
|
||||
"█\r\n"
|
||||
"█\r\n"
|
||||
"█\r\n"
|
||||
"█\r\n"
|
||||
"█\r\n"
|
||||
"█\r\n"
|
||||
"█\r\n"
|
||||
"█\r\n"
|
||||
"█\r\n"
|
||||
"█",
|
||||
screen.ToString());
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@@ -17,7 +17,7 @@ class Inverted : public NodeDecorator {
|
||||
Node::Render(screen);
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
screen.PixelAt(x, y).inverted = true;
|
||||
screen.PixelAt(x, y).inverted ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user