mirror of
				https://github.com/ArthurSonzogni/FTXUI.git
				synced 2025-11-04 21:48:15 +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