2022-03-14 05:30:05 +08:00
|
|
|
#include <iostream>
|
2021-09-17 02:45:26 +08:00
|
|
|
#include <algorithm> // for max, min
|
|
|
|
#include <memory> // for allocator, make_shared
|
|
|
|
#include <string> // for string
|
2021-05-02 02:40:35 +08:00
|
|
|
|
2021-05-10 02:32:27 +08:00
|
|
|
#include "ftxui/dom/elements.hpp" // for Element, gauge
|
|
|
|
#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
|
2018-09-20 03:52:25 +08:00
|
|
|
|
2019-01-12 22:00:08 +08:00
|
|
|
namespace ftxui {
|
2019-01-07 00:10:35 +08:00
|
|
|
|
2022-02-07 02:17:21 +08:00
|
|
|
static std::string charset_horizontal[11] = {
|
2021-07-04 23:38:31 +08:00
|
|
|
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
|
2021-08-09 05:25:20 +08:00
|
|
|
// Microsoft's terminals often use fonts not handling the 8 unicode
|
|
|
|
// characters for representing the whole gauge. Fallback with less.
|
2021-08-09 06:27:37 +08:00
|
|
|
" ", " ", " ", " ", "▌", "▌", "▌", "█", "█", "█",
|
2021-07-04 23:38:31 +08:00
|
|
|
#else
|
2021-08-09 06:27:37 +08:00
|
|
|
" ", " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█",
|
2021-07-04 23:38:31 +08:00
|
|
|
#endif
|
2021-08-09 06:27:37 +08:00
|
|
|
// An extra character in case when the fuzzer manage to have:
|
|
|
|
// int(9 * (limit - limit_int) = 9
|
|
|
|
"█"};
|
2018-09-22 15:49:43 +08:00
|
|
|
|
2022-02-07 02:17:21 +08:00
|
|
|
static std::string charset_vertical[10] = {
|
|
|
|
"█",
|
|
|
|
"▇",
|
|
|
|
"▆",
|
|
|
|
"▅",
|
|
|
|
"▄",
|
|
|
|
"▃",
|
|
|
|
"▂",
|
|
|
|
"▁",
|
|
|
|
" ",
|
|
|
|
// An extra character in case when the fuzzer manage to have:
|
|
|
|
// int(8 * (limit - limit_int) = 8
|
|
|
|
" ",
|
|
|
|
};
|
|
|
|
|
2018-09-20 03:52:25 +08:00
|
|
|
class Gauge : public Node {
|
|
|
|
public:
|
2022-02-07 02:17:21 +08:00
|
|
|
Gauge(float progress, GaugeDirection direction)
|
2022-03-14 05:30:05 +08:00
|
|
|
: progress_(progress), direction_(direction) {
|
|
|
|
// This handle NAN correctly:
|
|
|
|
if (!(progress_ > 0.f))
|
|
|
|
progress_ = 0.f;
|
|
|
|
if (!(progress_ < 1.f))
|
|
|
|
progress_ = 1.f;
|
|
|
|
}
|
2018-09-20 03:52:25 +08:00
|
|
|
|
|
|
|
void ComputeRequirement() override {
|
2022-02-07 02:17:21 +08:00
|
|
|
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;
|
|
|
|
}
|
2020-06-02 05:40:32 +08:00
|
|
|
requirement_.min_x = 1;
|
2020-06-01 22:13:29 +08:00
|
|
|
requirement_.min_y = 1;
|
2018-09-20 03:52:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Render(Screen& screen) override {
|
2022-02-07 02:17:21 +08:00
|
|
|
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) {
|
2021-09-05 01:58:02 +08:00
|
|
|
int y = box_.y_min;
|
|
|
|
if (y > box_.y_max)
|
|
|
|
return;
|
|
|
|
|
2022-02-07 02:17:21 +08:00
|
|
|
// 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) {
|
2019-01-20 05:06:05 +08:00
|
|
|
int x = box_.x_min;
|
2022-02-07 02:17:21 +08:00
|
|
|
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;
|
|
|
|
}
|
2018-09-20 03:52:25 +08:00
|
|
|
}
|
2020-03-23 05:32:44 +08:00
|
|
|
|
2018-09-20 03:52:25 +08:00
|
|
|
private:
|
|
|
|
float progress_;
|
2022-02-07 02:17:21 +08:00
|
|
|
GaugeDirection direction_;
|
2018-09-20 03:52:25 +08:00
|
|
|
};
|
|
|
|
|
2022-02-07 02:17:21 +08:00
|
|
|
/// @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);
|
|
|
|
}
|
|
|
|
|
2020-05-25 07:34:13 +08:00
|
|
|
/// @brief Draw a high definition progress bar.
|
|
|
|
/// @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(gauge(0.5))
|
|
|
|
/// ~~~
|
|
|
|
///
|
|
|
|
/// #### Output
|
|
|
|
///
|
|
|
|
/// ~~~bash
|
|
|
|
/// ┌──────────────────────────────────────────────────────────────────────────┐
|
|
|
|
/// │█████████████████████████████████████ │
|
|
|
|
/// └──────────────────────────────────────────────────────────────────────────┘
|
|
|
|
/// ~~~
|
2020-05-21 02:36:47 +08:00
|
|
|
Element gauge(float progress) {
|
2022-02-07 02:17:21 +08:00
|
|
|
return gaugeRight(progress);
|
2018-09-20 03:52:25 +08:00
|
|
|
}
|
|
|
|
|
2020-02-12 04:44:55 +08:00
|
|
|
} // namespace ftxui
|
2020-08-16 06:24:18 +08:00
|
|
|
|
|
|
|
// 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.
|