mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-22 02:34:21 +08:00
Add clang-tidy. (#368)
This commit is contained in:
parent
62fb6298be
commit
aebde94352
22
.clang-tidy
Normal file
22
.clang-tidy
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
Checks: "*,
|
||||
-abseil-*,
|
||||
-altera-*,
|
||||
-android-*,
|
||||
-fuchsia-*,
|
||||
-google-*,
|
||||
-llvm*,
|
||||
-modernize-use-trailing-return-type,
|
||||
-zircon-*,
|
||||
-readability-else-after-return,
|
||||
-readability-static-accessed-through-instance,
|
||||
-readability-avoid-const-params-in-decls,
|
||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-modernize-use-nodiscard,
|
||||
-misc-no-recursion,
|
||||
-readability-implicit-bool-conversion,
|
||||
"
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: ''
|
||||
FormatStyle: none
|
@ -10,6 +10,7 @@ option(FTXUI_BUILD_EXAMPLES "Set to ON to build examples" ON)
|
||||
option(FTXUI_BUILD_TESTS "Set to ON to build tests" OFF)
|
||||
option(FTXUI_BUILD_TESTS_FUZZER "Set to ON to enable fuzzing" OFF)
|
||||
option(FTXUI_ENABLE_INSTALL "Generate the install target" ON)
|
||||
option(FTXUI_CLANG_TIDY "Execute clang-tidy" OFF)
|
||||
|
||||
set(FTXUI_MICROSOFT_TERMINAL_FALLBACK_HELP_TEXT "On windows, assume the \
|
||||
terminal used will be one of Microsoft and use a set of reasonnable fallback \
|
||||
@ -22,6 +23,7 @@ else()
|
||||
${FTXUI_MICROSOFT_TERMINAL_FALLBACK_HELP_TEXT} OFF)
|
||||
endif()
|
||||
|
||||
|
||||
add_library(screen
|
||||
include/ftxui/screen/box.hpp
|
||||
include/ftxui/screen/color.hpp
|
||||
|
@ -1,6 +1,19 @@
|
||||
find_program( CLANG_TIDY_EXE NAMES "clang-tidy" DOC "Path to clang-tidy executable" )
|
||||
if(NOT CLANG_TIDY_EXE)
|
||||
message(STATUS "clang-tidy not found.")
|
||||
else()
|
||||
message(STATUS "clang-tidy found: ${CLANG_TIDY_EXE}")
|
||||
endif()
|
||||
|
||||
|
||||
function(ftxui_set_options library)
|
||||
set_target_properties(${library} PROPERTIES OUTPUT_NAME "ftxui-${library}")
|
||||
|
||||
if(CLANG_TIDY_EXE AND FTXUI_CLANG_TIDY)
|
||||
set_target_properties(${library}
|
||||
PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_EXE};-warnings-as-errors=*"
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(${library}
|
||||
PUBLIC
|
||||
|
@ -12,6 +12,9 @@ add_subdirectory(dom)
|
||||
if (EMSCRIPTEN)
|
||||
# 32MB should be enough to run all the examples, in debug mode.
|
||||
target_link_options(component PUBLIC "SHELL: -s TOTAL_MEMORY=33554432")
|
||||
target_link_options(component PUBLIC "SHELL: -s ASSERTIONS=1")
|
||||
#string(APPEND CMAKE_EXE_LINKER_FLAGS " -s ALLOW_MEMORY_GROWTH=1")
|
||||
#target_link_options(component PUBLIC "SHELL: -s ALLOW_MEMORY_GROWTH=1")
|
||||
|
||||
get_property(EXAMPLES GLOBAL PROPERTY FTXUI::EXAMPLES)
|
||||
foreach(file
|
||||
|
@ -1,13 +1,12 @@
|
||||
#include <array> // for array
|
||||
#include <memory> // for shared_ptr, __shared_ptr_access, allocator_traits<>::value_type
|
||||
#include <memory> // for shared_ptr, __shared_ptr_access
|
||||
#include <string> // for operator+, to_string
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for ftxui
|
||||
#include "ftxui/component/component.hpp" // for Checkbox, Renderer, Vertical
|
||||
#include "ftxui/component/component_base.hpp" // for ComponentBase
|
||||
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
|
||||
#include "ftxui/dom/elements.hpp" // for operator|, Element, size, border, frame, HEIGHT, LESS_THAN
|
||||
#include "ftxui/dom/elements.hpp" // for operator|, Element, size, border, frame, vscroll_indicator, HEIGHT, LESS_THAN
|
||||
|
||||
using namespace ftxui;
|
||||
|
||||
|
@ -17,7 +17,7 @@ using namespace ftxui;
|
||||
MenuEntryOption Colored(ftxui::Color c) {
|
||||
MenuEntryOption option;
|
||||
option.transform = [c](EntryState state) {
|
||||
state.label = (state.active? "> " : " ") + state.label;
|
||||
state.label = (state.active ? "> " : " ") + state.label;
|
||||
Element e = text(state.label) | color(c);
|
||||
if (state.focused)
|
||||
e = e | inverted;
|
||||
|
@ -33,7 +33,7 @@ Component HMenu5(std::vector<std::string>* entries, int* selected);
|
||||
int main(int argc, const char* argv[]) {
|
||||
auto screen = ScreenInteractive::TerminalOutput();
|
||||
|
||||
std::vector<std::string> entries = {
|
||||
std::vector<std::string> entries{
|
||||
"Monkey", "Dog", "Cat", "Bird", "Elephant", "Cat",
|
||||
};
|
||||
std::array<int, 12> selected = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
@ -140,7 +140,7 @@ Component VMenu3(std::vector<std::string>* entries, int* selected) {
|
||||
auto option = MenuOption::Vertical();
|
||||
option.entries.transform = [](EntryState state) {
|
||||
Element e = state.active ? text("[" + state.label + "]")
|
||||
: text(" " + state.label + " ");
|
||||
: text(" " + state.label + " ");
|
||||
if (state.focused)
|
||||
e = e | bold;
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <stdlib.h> // for EXIT_SUCCESS
|
||||
#include <ftxui/dom/elements.hpp> // for text, operator|, vbox, border, Element, Fit, hbox
|
||||
#include <ftxui/screen/screen.hpp> // for Full, Screen
|
||||
#include <memory> // for allocator
|
||||
@ -5,7 +6,7 @@
|
||||
#include "ftxui/dom/node.hpp" // for Render
|
||||
#include "ftxui/screen/color.hpp" // for ftxui
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
int main() {
|
||||
using namespace ftxui;
|
||||
auto document = //
|
||||
hbox({
|
||||
@ -30,6 +31,7 @@ int main(int argc, const char* argv[]) {
|
||||
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
|
||||
Render(screen, document);
|
||||
screen.Print();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
|
@ -12,7 +12,7 @@
|
||||
using namespace ftxui;
|
||||
#include "./color_info_sorted_2d.ipp" // for ColorInfoSorted2D
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
int main() {
|
||||
// clang-format off
|
||||
auto basic_color_display =
|
||||
vbox(
|
||||
@ -83,14 +83,18 @@ int main(int argc, const char* argv[]) {
|
||||
// True color display.
|
||||
auto true_color_display = text("TrueColors: 24bits:");
|
||||
{
|
||||
int saturation = 255;
|
||||
const int max_value = 255;
|
||||
const int value_increment = 8;
|
||||
const int hue_increment = 6;
|
||||
int saturation = max_value;
|
||||
Elements array;
|
||||
for (int value = 0; value < 255; value += 16) {
|
||||
for (int value = 0; value < max_value; value += 2 * value_increment) {
|
||||
Elements line;
|
||||
for (int hue = 0; hue < 255; hue += 6) {
|
||||
line.push_back(text("▀") //
|
||||
| color(Color::HSV(hue, saturation, value)) //
|
||||
| bgcolor(Color::HSV(hue, saturation, value + 8)));
|
||||
for (int hue = 0; hue < max_value; hue += hue_increment) {
|
||||
line.push_back(
|
||||
text("▀") //
|
||||
| color(Color::HSV(hue, saturation, value)) //
|
||||
| bgcolor(Color::HSV(hue, saturation, value + value_increment)));
|
||||
}
|
||||
array.push_back(hbox(std::move(line)));
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
#include <chrono> // for operator""s, chrono_literals
|
||||
#include <cmath> // for sin
|
||||
#include <ftxui/dom/elements.hpp> // for operator|, graph, separator, color, Element, vbox, flex, inverted, Fit, hbox, size, border, GREATER_THAN, HEIGHT
|
||||
#include <ftxui/dom/elements.hpp> // for graph, operator|, separator, color, Element, vbox, flex, inverted, operator|=, Fit, hbox, size, border, GREATER_THAN, HEIGHT
|
||||
#include <ftxui/screen/screen.hpp> // for Full, Screen
|
||||
#include <functional> // for ref, reference_wrapper
|
||||
#include <iostream> // for cout, ostream
|
||||
#include <memory> // for shared_ptr
|
||||
#include <string> // for operator<<, string
|
||||
#include <thread> // for sleep_for
|
||||
#include <vector> // for vector
|
||||
@ -13,15 +14,15 @@
|
||||
|
||||
class Graph {
|
||||
public:
|
||||
std::vector<int> operator()(int width, int height) {
|
||||
std::vector<int> operator()(int width, int height) const {
|
||||
std::vector<int> output(width);
|
||||
for (int i = 0; i < width; ++i) {
|
||||
float v = 0;
|
||||
v += 0.1f * sin((i + shift) * 0.1f);
|
||||
v += 0.2f * sin((i + shift + 10) * 0.15f);
|
||||
v += 0.1f * sin((i + shift) * 0.03f);
|
||||
v *= height;
|
||||
v += 0.5f * height;
|
||||
v += 0.1f * sin((i + shift) * 0.1f); // NOLINT
|
||||
v += 0.2f * sin((i + shift + 10) * 0.15f); // NOLINT
|
||||
v += 0.1f * sin((i + shift) * 0.03f); // NOLINT
|
||||
v *= height; // NOLINT
|
||||
v += 0.5f * height; // NOLINT
|
||||
output[i] = static_cast<int>(v);
|
||||
}
|
||||
return output;
|
||||
@ -37,7 +38,7 @@ std::vector<int> triangle(int width, int height) {
|
||||
return output;
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
int main() {
|
||||
using namespace ftxui;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@ -45,23 +46,26 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
std::string reset_position;
|
||||
for (int i = 0;; ++i) {
|
||||
auto document =
|
||||
hbox({
|
||||
vbox({
|
||||
graph(std::ref(my_graph)),
|
||||
separator(),
|
||||
graph(triangle) | inverted,
|
||||
}) | flex,
|
||||
auto document = hbox({
|
||||
vbox({
|
||||
graph(std::ref(my_graph)),
|
||||
separator(),
|
||||
vbox({
|
||||
graph(std::ref(my_graph)) | color(Color::BlueLight),
|
||||
separator(),
|
||||
graph(std::ref(my_graph)) | color(Color::RedLight),
|
||||
separator(),
|
||||
graph(std::ref(my_graph)) | color(Color::YellowLight),
|
||||
}) | flex,
|
||||
}) |
|
||||
border | size(HEIGHT, GREATER_THAN, 40);
|
||||
graph(triangle) | inverted,
|
||||
}) | flex,
|
||||
separator(),
|
||||
vbox({
|
||||
graph(std::ref(my_graph)) | color(Color::BlueLight),
|
||||
separator(),
|
||||
graph(std::ref(my_graph)) | color(Color::RedLight),
|
||||
separator(),
|
||||
graph(std::ref(my_graph)) | color(Color::YellowLight),
|
||||
}) | flex,
|
||||
});
|
||||
|
||||
document |= border;
|
||||
|
||||
const int min_width = 40;
|
||||
document |= size(HEIGHT, GREATER_THAN, min_width);
|
||||
|
||||
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
|
||||
Render(screen, document);
|
||||
@ -69,7 +73,8 @@ int main(int argc, const char* argv[]) {
|
||||
screen.Print();
|
||||
reset_position = screen.ResetPosition();
|
||||
|
||||
std::this_thread::sleep_for(0.03s);
|
||||
const auto sleep_time = 0.03s;
|
||||
std::this_thread::sleep_for(sleep_time);
|
||||
my_graph.shift++;
|
||||
}
|
||||
|
||||
|
@ -1,25 +1,32 @@
|
||||
#include <ftxui/dom/elements.hpp> // for operator|, color, Element, bgcolor, graph, border
|
||||
#include <stdlib.h> // for EXIT_SUCCESS
|
||||
#include <ftxui/dom/elements.hpp> // for operator|=, Element, bgcolor, color, graph, border
|
||||
#include <ftxui/screen/screen.hpp> // for Fixed, Screen
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ftxui/dom/node.hpp" // for Render
|
||||
#include "ftxui/screen/color.hpp" // for Color, Color::DarkBlue, Color::Green, Color::Red, ftxui
|
||||
#include "ftxui/dom/node.hpp" // for Render
|
||||
#include "ftxui/screen/color.hpp" // for Color, Color::DarkBlue, Color::Red, ftxui
|
||||
|
||||
int main(void) {
|
||||
int main() {
|
||||
using namespace ftxui;
|
||||
Element document = graph([](int x, int y) {
|
||||
std::vector<int> result(x, 0);
|
||||
for (int i{0}; i < x; ++i) {
|
||||
result[i] = ((3 * i) / 2) % y;
|
||||
}
|
||||
return result;
|
||||
}) |
|
||||
color(Color::Red) | border | color(Color::Green) |
|
||||
bgcolor(Color::DarkBlue);
|
||||
std::vector<int> result(x, 0);
|
||||
for (int i{0}; i < x; ++i) {
|
||||
result[i] = ((3 * i) / 2) % y;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
auto screen = Screen::Create(Dimension::Fixed(80), Dimension::Fixed(10));
|
||||
document |= color(Color::Red);
|
||||
document |= bgcolor(Color::DarkBlue);
|
||||
document |= border;
|
||||
|
||||
const int width = 80;
|
||||
const int height = 10;
|
||||
auto screen =
|
||||
Screen::Create(Dimension::Fixed(width), Dimension::Fixed(height));
|
||||
Render(screen, document);
|
||||
screen.Print();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
|
@ -44,12 +44,12 @@
|
||||
});
|
||||
|
||||
let stdin_buffer = [];
|
||||
let stdin = () => {
|
||||
const stdin = () => {
|
||||
return stdin_buffer.shift() || 0;
|
||||
}
|
||||
|
||||
stdout_buffer = [];
|
||||
let stdout = code => {
|
||||
let stdout_buffer = [];
|
||||
const stdout = code => {
|
||||
if (code == 0) {
|
||||
term.write(new Uint8Array(stdout_buffer));
|
||||
stdout_buffer = [];
|
||||
@ -57,7 +57,15 @@
|
||||
stdout_buffer.push(code)
|
||||
}
|
||||
}
|
||||
const stderr = code => console.log(code);
|
||||
let stderrbuffer = [];
|
||||
const stderr = code => {
|
||||
if (code == 0 || code == 10) {
|
||||
console.error(String.fromCodePoint(...stderrbuffer));
|
||||
stderrbuffer = [];
|
||||
} else {
|
||||
stderrbuffer.push(code)
|
||||
}
|
||||
}
|
||||
const term = new Terminal();
|
||||
term.open(document.querySelector('#terminal'));
|
||||
term.resize(140,43);
|
||||
@ -69,7 +77,9 @@
|
||||
term.onBinary(onBinary);
|
||||
term.onData(onBinary)
|
||||
window.Module = {
|
||||
preRun: () => { FS.init(stdin, stdout, stderr); },
|
||||
preRun: () => {
|
||||
FS.init(stdin, stdout, stderr);
|
||||
},
|
||||
postRun: [],
|
||||
onRuntimeInitialized: () => {},
|
||||
};
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ftxui/component/component_base.hpp" // for Component, Components
|
||||
#include "ftxui/component/component_options.hpp" // for ButtonOption, CheckboxOption (ptr only), InputOption (ptr only), MenuEntryOption (ptr only), MenuOption, RadioboxOption (ptr only)
|
||||
#include "ftxui/component/component_options.hpp" // for ButtonOption, CheckboxOption, InputOption (ptr only), MenuEntryOption (ptr only), MenuOption, RadioboxOption (ptr only)
|
||||
#include "ftxui/dom/elements.hpp" // for Element
|
||||
#include "ftxui/util/ref.hpp" // for Ref, ConstStringRef, ConstStringListRef, StringRef
|
||||
|
||||
|
@ -3,11 +3,11 @@
|
||||
|
||||
#include <chrono> // for milliseconds
|
||||
#include <ftxui/component/animation.hpp> // for Duration, QuadraticInOut, Function
|
||||
#include <ftxui/dom/elements.hpp> // for Decorator, bold, inverted, operator|, Element, nothing
|
||||
#include <ftxui/util/ref.hpp> // for Ref
|
||||
#include <functional> // for function
|
||||
#include <optional> // for optional
|
||||
#include <string> // for string, allocator
|
||||
#include <ftxui/dom/elements.hpp> // for Element
|
||||
#include <ftxui/util/ref.hpp> // for Ref
|
||||
#include <functional> // for function
|
||||
#include <optional> // for optional
|
||||
#include <string> // for string
|
||||
|
||||
#include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::White
|
||||
|
||||
@ -17,10 +17,10 @@ namespace ftxui {
|
||||
/// |Radiobox::transform|, |MenuEntryOption::transform|,
|
||||
/// |MenuOption::transform|.
|
||||
struct EntryState {
|
||||
std::string label; /// < The label to display.
|
||||
bool state; /// < The state of the button/checkbox/radiobox
|
||||
bool active; /// < Whether the entry is the active one.
|
||||
bool focused; /// < Whether the entry is one focused by the user.
|
||||
std::string label; /// < The label to display.
|
||||
bool state; /// < The state of the button/checkbox/radiobox
|
||||
bool active; /// < Whether the entry is the active one.
|
||||
bool focused; /// < Whether the entry is one focused by the user.
|
||||
};
|
||||
|
||||
struct UnderlineOption {
|
||||
@ -70,7 +70,7 @@ struct AnimatedColorsOption {
|
||||
/// @brief Option for the MenuEntry component.
|
||||
/// @ingroup component
|
||||
struct MenuEntryOption {
|
||||
std::function<Element(EntryState state)> transform;
|
||||
std::function<Element(const EntryState& state)> transform;
|
||||
AnimatedColorsOption animated_colors;
|
||||
};
|
||||
|
||||
@ -115,7 +115,7 @@ struct ButtonOption {
|
||||
Color foreground_active);
|
||||
|
||||
// Style:
|
||||
std::function<Element(EntryState)> transform;
|
||||
std::function<Element(const EntryState&)> transform;
|
||||
AnimatedColorsOption animated_colors;
|
||||
};
|
||||
|
||||
@ -126,7 +126,7 @@ struct CheckboxOption {
|
||||
static CheckboxOption Simple();
|
||||
|
||||
// Style:
|
||||
std::function<Element(EntryState)> transform;
|
||||
std::function<Element(const EntryState&)> transform;
|
||||
|
||||
// Observer:
|
||||
/// Called when the user change the state.
|
||||
@ -156,7 +156,7 @@ struct RadioboxOption {
|
||||
static RadioboxOption Simple();
|
||||
|
||||
// Style:
|
||||
std::function<Element(EntryState)> transform;
|
||||
std::function<Element(const EntryState&)> transform;
|
||||
|
||||
// Observers:
|
||||
/// Called when the selected entry changes.
|
||||
|
@ -54,7 +54,7 @@ struct Event {
|
||||
static const Event PageDown;
|
||||
|
||||
// --- Custom ---
|
||||
static Event Custom;
|
||||
static const Event Custom;
|
||||
|
||||
//--- Method section ---------------------------------------------------------
|
||||
bool is_character() const { return type_ == Type::Character; }
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef FTXUI_DOM_CANVAS_HPP
|
||||
#define FTXUI_DOM_CANVAS_HPP
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <cstddef> // for size_t
|
||||
#include <functional> // for function
|
||||
#include <string> // for string
|
||||
#include <unordered_map> // for unordered_map
|
||||
@ -13,7 +13,7 @@ namespace ftxui {
|
||||
|
||||
struct Canvas {
|
||||
public:
|
||||
Canvas() {}
|
||||
Canvas() = default;
|
||||
Canvas(int width, int height);
|
||||
|
||||
// Getters:
|
||||
@ -114,7 +114,8 @@ struct Canvas {
|
||||
|
||||
struct XYHash {
|
||||
size_t operator()(const XY& xy) const {
|
||||
return static_cast<size_t>(xy.x * 1024 + xy.y);
|
||||
constexpr size_t shift = 1024;
|
||||
return size_t(xy.x) * shift + size_t(xy.y);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#ifndef FTXUI_DOM_DEPRECRATED_HPP
|
||||
#define FTXUI_DOM_DEPRECRATED_HPP
|
||||
#ifndef FTXUI_DOM_DEPRECATED_HPP
|
||||
#define FTXUI_DOM_DEPRECATED_HPP
|
||||
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
|
||||
@ -9,7 +9,7 @@ Element vtext(std::wstring text);
|
||||
Elements paragraph(std::wstring text);
|
||||
} // namespace ftxui
|
||||
|
||||
#endif /* end of include guard: FTXUI_DOM_DEPRECRATED_HPP */
|
||||
#endif // FTXUI_DOM_DEPRECATED_HPP
|
||||
|
||||
// Copyright 2021 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
|
@ -35,7 +35,7 @@ Decorator operator|(Decorator, Decorator);
|
||||
// --- Widget ---
|
||||
Element text(std::string text);
|
||||
Element vtext(std::string text);
|
||||
Element separator(void);
|
||||
Element separator();
|
||||
Element separatorLight();
|
||||
Element separatorHeavy();
|
||||
Element separatorDouble();
|
||||
@ -45,18 +45,18 @@ Element separator(Pixel);
|
||||
Element separatorCharacter(std::string);
|
||||
Element separatorHSelector(float left,
|
||||
float right,
|
||||
Color background,
|
||||
Color foreground);
|
||||
Color unselected_color,
|
||||
Color selected_color);
|
||||
Element separatorVSelector(float up,
|
||||
float down,
|
||||
Color background,
|
||||
Color foreground);
|
||||
Element gauge(float ratio);
|
||||
Element gaugeLeft(float ratio);
|
||||
Element gaugeRight(float ratio);
|
||||
Element gaugeUp(float ratio);
|
||||
Element gaugeDown(float ratio);
|
||||
Element gaugeDirection(float ratio, GaugeDirection);
|
||||
Color unselected_color,
|
||||
Color selected_color);
|
||||
Element gauge(float progress);
|
||||
Element gaugeLeft(float progress);
|
||||
Element gaugeRight(float progress);
|
||||
Element gaugeUp(float progress);
|
||||
Element gaugeDown(float progress);
|
||||
Element gaugeDirection(float progress, GaugeDirection);
|
||||
Element border(Element);
|
||||
Element borderLight(Element);
|
||||
Element borderHeavy(Element);
|
||||
@ -64,14 +64,14 @@ Element borderDouble(Element);
|
||||
Element borderRounded(Element);
|
||||
Element borderEmpty(Element);
|
||||
Decorator borderStyled(BorderStyle);
|
||||
Decorator borderWith(Pixel);
|
||||
Decorator borderWith(const Pixel&);
|
||||
Element window(Element title, Element content);
|
||||
Element spinner(int charset_index, size_t image_index);
|
||||
Element paragraph(std::string text);
|
||||
Element paragraphAlignLeft(std::string text);
|
||||
Element paragraphAlignRight(std::string text);
|
||||
Element paragraphAlignCenter(std::string text);
|
||||
Element paragraphAlignJustify(std::string text);
|
||||
Element paragraph(const std::string& text);
|
||||
Element paragraphAlignLeft(const std::string& text);
|
||||
Element paragraphAlignRight(const std::string& text);
|
||||
Element paragraphAlignCenter(const std::string& text);
|
||||
Element paragraphAlignJustify(const std::string& text);
|
||||
Element graph(GraphFunction);
|
||||
Element emptyElement();
|
||||
Element canvas(ConstRef<Canvas>);
|
||||
@ -90,7 +90,7 @@ Element color(Color, Element);
|
||||
Element bgcolor(Color, Element);
|
||||
Decorator focusPosition(int x, int y);
|
||||
Decorator focusPositionRelative(float x, float y);
|
||||
Element automerge(Element);
|
||||
Element automerge(Element child);
|
||||
|
||||
// --- Layout is
|
||||
// Horizontal, Vertical or stacked set of elements.
|
||||
@ -163,7 +163,7 @@ Dimensions Fit(Element&);
|
||||
|
||||
// Include old definitions using wstring.
|
||||
#include "ftxui/dom/deprecated.hpp"
|
||||
#endif /* end of include guard: FTXUI_DOM_ELEMENTS_HPP */
|
||||
#endif // FTXUI_DOM_ELEMENTS_HPP
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
|
@ -108,7 +108,7 @@ struct FlexboxConfig {
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
#endif /* end of include guard: FTXUI_DOM_FLEXBOX_CONFIG_HPP */
|
||||
#endif // FTXUI_DOM_FLEXBOX_CONFIG_HPP
|
||||
|
||||
// Copyright 2021 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
|
@ -20,6 +20,11 @@ class Node {
|
||||
public:
|
||||
Node();
|
||||
Node(Elements children);
|
||||
Node(const Node&) = delete;
|
||||
Node(const Node&&) = delete;
|
||||
Node& operator=(const Node&) = delete;
|
||||
Node& operator=(const Node&&) = delete;
|
||||
|
||||
virtual ~Node();
|
||||
|
||||
// Step 1: Compute layout requirement. Tell parent what dimensions this
|
||||
@ -50,12 +55,12 @@ class Node {
|
||||
Box box_;
|
||||
};
|
||||
|
||||
void Render(Screen& screen, const Element& node);
|
||||
void Render(Screen& screen, const Element& element);
|
||||
void Render(Screen& screen, Node* node);
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
#endif /* end of include guard: FTXUI_DOM_NODE_HPP */
|
||||
#endif // FTXUI_DOM_NODE_HPP
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
|
@ -28,7 +28,7 @@ struct Requirement {
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
#endif /* end of include guard: FTXUI_DOM_REQUIREMENT_HPP */
|
||||
#endif // FTXUI_DOM_REQUIREMENT_HPP
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
|
@ -52,10 +52,10 @@ class Table {
|
||||
void Initialize(std::vector<std::vector<Element>>);
|
||||
friend TableSelection;
|
||||
std::vector<std::vector<Element>> elements_;
|
||||
int input_dim_x_;
|
||||
int input_dim_y_;
|
||||
int dim_x_;
|
||||
int dim_y_;
|
||||
int input_dim_x_ = 0;
|
||||
int input_dim_y_ = 0;
|
||||
int dim_x_ = 0;
|
||||
int dim_y_ = 0;
|
||||
};
|
||||
|
||||
class TableSelection {
|
||||
|
@ -1,10 +1,13 @@
|
||||
#ifndef FTXUI_DOM_TAKE_ANY_ARGS_HPP
|
||||
#define FTXUI_DOM_TAKE_ANY_ARGS_HPP
|
||||
|
||||
// IWYU pragma: private, include "ftxui/dom/elements.hpp"
|
||||
#include <type_traits>
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
template <class T>
|
||||
void Merge(Elements&, T) {}
|
||||
void Merge(Elements& /*container*/, T /*element*/) {}
|
||||
|
||||
template <>
|
||||
inline void Merge(Elements& container, Element element) {
|
||||
@ -38,6 +41,8 @@ TAKE_ANY_ARGS(dbox)
|
||||
TAKE_ANY_ARGS(hflow)
|
||||
} // namespace ftxui
|
||||
|
||||
#endif // FTXUI_DOM_TAKE_ANY_ARGS_HPP
|
||||
|
||||
// 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.
|
||||
|
@ -9,15 +9,15 @@ struct Box {
|
||||
int y_min = 0;
|
||||
int y_max = 0;
|
||||
|
||||
static Box Intersection(Box a, Box b);
|
||||
bool Contain(int x, int y);
|
||||
static auto Intersection(Box a, Box b) -> Box;
|
||||
bool Contain(int x, int y) const;
|
||||
bool operator==(const Box& other) const;
|
||||
bool operator!=(const Box& other) const;
|
||||
};
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
#endif /* end of include guard: FTXUI_SCREEN_BOX_HPP */
|
||||
#endif // FTXUI_SCREEN_BOX_HPP
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
|
@ -1,8 +1,8 @@
|
||||
#ifndef FTXUI_SCREEN_COLOR
|
||||
#define FTXUI_SCREEN_COLOR
|
||||
#ifndef FTXUI_SCREEN_COLOR_HPP
|
||||
#define FTXUI_SCREEN_COLOR_HPP
|
||||
|
||||
#include <stdint.h> // for uint8_t
|
||||
#include <string> // for wstring
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <string> // for wstring
|
||||
|
||||
#ifdef RGB
|
||||
// Workaround for wingdi.h (via Windows.h) defining macros that break things.
|
||||
@ -313,12 +313,8 @@ class Color {
|
||||
Palette256,
|
||||
TrueColor,
|
||||
};
|
||||
|
||||
ColorType type_;
|
||||
union {
|
||||
uint8_t index_ = 0;
|
||||
uint8_t red_;
|
||||
};
|
||||
ColorType type_ = ColorType::Palette1;
|
||||
uint8_t red_ = 0;
|
||||
uint8_t green_ = 0;
|
||||
uint8_t blue_ = 0;
|
||||
};
|
||||
@ -333,7 +329,7 @@ Color operator""_rgb(unsigned long long int combined);
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
#endif /* end of include guard: FTXUI_COLOR_H_ */
|
||||
#endif // FTXUI_SCREEN_COLOR_HPP
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef FTXUI_SCREEN_COLOR_INFO_HPP
|
||||
#define FTXUI_SCREEN_COLOR_INFO_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include <ftxui/screen/color.hpp>
|
||||
|
||||
namespace ftxui {
|
||||
@ -23,7 +23,7 @@ ColorInfo GetColorInfo(Color::Palette16 index);
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
#endif /* end of include guard: FTXUI_SCREEN_COLOR_INFO_HPP */
|
||||
#endif // FTXUI_SCREEN_COLOR_INFO_HPP
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
|
@ -8,7 +8,7 @@ int wchar_width(wchar_t);
|
||||
int wstring_width(const std::wstring&);
|
||||
} // namespace ftxui
|
||||
|
||||
#endif /* end of include guard: FTXUI_SCREEN_DEPRECATED_HPP */
|
||||
#endif // FTXUI_SCREEN_DEPRECATED_HPP
|
||||
|
||||
// Copyright 2021 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
|
@ -1,5 +1,5 @@
|
||||
#ifndef FTXUI_SCREEN_SCREEN
|
||||
#define FTXUI_SCREEN_SCREEN
|
||||
#ifndef FTXUI_SCREEN_SCREEN_HPP
|
||||
#define FTXUI_SCREEN_SCREEN_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <string> // for string, allocator, basic_string
|
||||
@ -14,6 +14,8 @@ namespace ftxui {
|
||||
/// @brief A unicode character and its associated style.
|
||||
/// @ingroup screen
|
||||
struct Pixel {
|
||||
bool operator==(const Pixel& other) const;
|
||||
|
||||
// The graphemes stored into the pixel. To support combining characters,
|
||||
// like: a⃦, this can potentially contains multiple codepoitns.
|
||||
std::string character = " ";
|
||||
@ -68,13 +70,12 @@ class Screen {
|
||||
int dimy() const { return dimy_; }
|
||||
|
||||
// Move the terminal cursor n-lines up with n = dimy().
|
||||
std::string ResetPosition(bool clear = false);
|
||||
std::string ResetPosition(bool clear = false) const;
|
||||
|
||||
// Fill with space.
|
||||
void Clear();
|
||||
|
||||
void ApplyShader();
|
||||
Box stencil;
|
||||
|
||||
struct Cursor {
|
||||
int x = 0;
|
||||
@ -83,16 +84,20 @@ class Screen {
|
||||
Cursor cursor() const { return cursor_; }
|
||||
void SetCursor(Cursor cursor) { cursor_ = cursor; }
|
||||
|
||||
Box stencil;
|
||||
|
||||
protected:
|
||||
int dimx_;
|
||||
int dimy_;
|
||||
std::vector<std::vector<Pixel>> pixels_;
|
||||
Cursor cursor_;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
#endif /* end of include guard: FTXUI_SCREEN_SCREEN */
|
||||
#endif // FTXUI_SCREEN_SCREEN_HPP
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
|
@ -1,5 +1,5 @@
|
||||
#ifndef FTXUI_CORE_TERMINAL_HPP
|
||||
#define FTXUI_CORE_TERMINAL_HPP
|
||||
#ifndef FTXUI_SCREEN_TERMINAL_HPP
|
||||
#define FTXUI_SCREEN_TERMINAL_HPP
|
||||
|
||||
namespace ftxui {
|
||||
struct Dimensions {
|
||||
@ -22,7 +22,7 @@ Color ColorSupport();
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
#endif /* end of include guard: FTXUI_CORE_TERMINAL_HPP */
|
||||
#endif // FTXUI_SCREEN_TERMINAL_HPP
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
|
@ -1,14 +1,18 @@
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cmath> // for sin, pow, sqrt, M_PI_2, M_PI, cos
|
||||
#include <cmath>
|
||||
#include <ratio> // for ratio
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/animation.hpp"
|
||||
|
||||
#include <ratio> // for ratio
|
||||
|
||||
namespace ftxui {
|
||||
namespace animation {
|
||||
namespace ftxui::animation {
|
||||
|
||||
namespace easing {
|
||||
|
||||
namespace {
|
||||
constexpr float kPi = 3.14159265358979323846F;
|
||||
constexpr float kPi2 = kPi / 2.F;
|
||||
} // namespace
|
||||
|
||||
// Easing function have been taken out of:
|
||||
// https://github.com/warrenm/AHEasing/blob/master/AHEasing/easing.c
|
||||
//
|
||||
@ -40,7 +44,7 @@ float QuadraticOut(float p) {
|
||||
// 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.5) {
|
||||
if (p < 0.5F) { // NOLINT
|
||||
return 2 * p * p;
|
||||
} else {
|
||||
return (-2 * p * p) + (4 * p) - 1;
|
||||
@ -62,11 +66,11 @@ float CubicOut(float p) {
|
||||
// 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.5) {
|
||||
if (p < 0.5F) { // NOLINT
|
||||
return 4 * p * p * p;
|
||||
} else {
|
||||
float f = ((2 * p) - 2);
|
||||
return 0.5 * f * f * f + 1;
|
||||
return 0.5F * f * f * f + 1; // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,11 +89,11 @@ float QuarticOut(float p) {
|
||||
// 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.5) {
|
||||
return 8 * p * p * p * p;
|
||||
if (p < 0.5F) { // NOLINT
|
||||
return 8 * p * p * p * p; // NOLINT
|
||||
} else {
|
||||
float f = (p - 1);
|
||||
return -8 * f * f * f * f + 1;
|
||||
return -8 * f * f * f * f + 1; // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,119 +112,122 @@ float QuinticOut(float p) {
|
||||
// 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.5) {
|
||||
return 16 * p * p * p * p * p;
|
||||
} else {
|
||||
float f = ((2 * p) - 2);
|
||||
return 0.5 * f * f * f * f * f + 1;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Modeled after quarter-cycle of sine wave
|
||||
float SineIn(float p) {
|
||||
return sin((p - 1) * M_PI_2) + 1;
|
||||
return std::sin((p - 1) * kPi2) + 1;
|
||||
}
|
||||
|
||||
// Modeled after quarter-cycle of sine wave (different phase)
|
||||
float SineOut(float p) {
|
||||
return sin(p * M_PI_2);
|
||||
return std::sin(p * kPi2);
|
||||
}
|
||||
|
||||
// Modeled after half sine wave
|
||||
float SineInOut(float p) {
|
||||
return 0.5 * (1 - cos(p * M_PI));
|
||||
return 0.5F * (1 - std::cos(p * kPi)); // NOLINT
|
||||
}
|
||||
|
||||
// Modeled after shifted quadrant IV of unit circle
|
||||
float CircularIn(float p) {
|
||||
return 1 - sqrt(1 - (p * p));
|
||||
return 1 - std::sqrt(1 - (p * p));
|
||||
}
|
||||
|
||||
// Modeled after shifted quadrant II of unit circle
|
||||
float CircularOut(float p) {
|
||||
return sqrt((2 - p) * p);
|
||||
return std::sqrt((2 - 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.5) {
|
||||
return 0.5 * (1 - sqrt(1 - 4 * (p * p)));
|
||||
if (p < 0.5F) { // NOLINT
|
||||
return 0.5F * (1 - std::sqrt(1 - 4 * (p * p))); // NOLINT
|
||||
} else {
|
||||
return 0.5 * (sqrt(-((2 * p) - 3) * ((2 * p) - 1)) + 1);
|
||||
return 0.5F * (std::sqrt(-((2 * p) - 3) * ((2 * p) - 1)) + 1); // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
// Modeled after the exponential function y = 2^(10(x - 1))
|
||||
float ExponentialIn(float p) {
|
||||
return (p == 0.0) ? p : pow(2, 10 * (p - 1));
|
||||
return (p == 0.0) ? p : std::pow(2, 10 * (p - 1)); // NOLINT
|
||||
}
|
||||
|
||||
// Modeled after the exponential function y = -2^(-10x) + 1
|
||||
float ExponentialOut(float p) {
|
||||
return (p == 1.0) ? p : 1 - pow(2, -10 * p);
|
||||
return (p == 1.0) ? p : 1 - std::pow(2, -10 * 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.0)
|
||||
if (p == 0.0 || p == 1.F) {
|
||||
return p;
|
||||
}
|
||||
|
||||
if (p < 0.5) {
|
||||
return 0.5 * pow(2, (20 * p) - 10);
|
||||
} else {
|
||||
return -0.5 * pow(2, (-20 * p) + 10) + 1;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Modeled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1))
|
||||
float ElasticIn(float p) {
|
||||
return sin(13 * M_PI_2 * p) * pow(2, 10 * (p - 1));
|
||||
return std::sin(13.F * kPi2 * p) * std::pow(2.F, 10.F * (p - 1)); // NOLINT
|
||||
}
|
||||
|
||||
// Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) +
|
||||
// 1
|
||||
float ElasticOut(float p) {
|
||||
return sin(-13 * M_PI_2 * (p + 1)) * pow(2, -10 * p) + 1;
|
||||
return std::sin(-13.F * kPi2 * (p + 1)) * std::pow(2.F, -10.F * p) +
|
||||
1; // NOLINT
|
||||
}
|
||||
|
||||
// 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.5) {
|
||||
return 0.5 * sin(13 * M_PI_2 * (2 * p)) * pow(2, 10 * ((2 * p) - 1));
|
||||
} else {
|
||||
return 0.5 *
|
||||
(sin(-13 * M_PI_2 * ((2 * p - 1) + 1)) * pow(2, -10 * (2 * p - 1)) +
|
||||
2);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Modeled after the overshooting cubic y = x^3-x*sin(x*pi)
|
||||
float BackIn(float p) {
|
||||
return p * p * p - p * sin(p * M_PI);
|
||||
return p * p * p - p * std::sin(p * kPi);
|
||||
}
|
||||
|
||||
// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi))
|
||||
float BackOut(float p) {
|
||||
float f = (1 - p);
|
||||
return 1 - (f * f * f - f * sin(f * M_PI));
|
||||
return 1 - (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.5) {
|
||||
if (p < 0.5F) { // NOLINT
|
||||
float f = 2 * p;
|
||||
return 0.5 * (f * f * f - f * sin(f * M_PI));
|
||||
return 0.5F * (f * f * f - f * std::sin(f * kPi)); // NOLINT
|
||||
} else {
|
||||
float f = (1 - (2 * p - 1));
|
||||
return 0.5 * (1 - (f * f * f - f * sin(f * M_PI))) + 0.5;
|
||||
float f = (1 - (2 * p - 1)); // NOLINT
|
||||
return 0.5F * (1 - (f * f * f - f * std::sin(f * kPi))) + 0.5; // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,22 +236,23 @@ float BounceIn(float p) {
|
||||
}
|
||||
|
||||
float BounceOut(float p) {
|
||||
if (p < 4 / 11.0) {
|
||||
return (121 * p * p) / 16.0;
|
||||
} else if (p < 8 / 11.0) {
|
||||
return (363 / 40.0 * p * p) - (99 / 10.0 * p) + 17 / 5.0;
|
||||
} else if (p < 9 / 10.0) {
|
||||
return (4356 / 361.0 * p * p) - (35442 / 1805.0 * p) + 16061 / 1805.0;
|
||||
} else {
|
||||
return (54 / 5.0 * p * p) - (513 / 25.0 * p) + 268 / 25.0;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
float BounceInOut(float p) {
|
||||
if (p < 0.5) {
|
||||
return 0.5 * BounceIn(p * 2);
|
||||
} else {
|
||||
return 0.5 * BounceOut(p * 2 - 1) + 0.5;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,7 +267,7 @@ Animator::Animator(float* from,
|
||||
from_(*from),
|
||||
to_(to),
|
||||
duration_(duration),
|
||||
easing_function_(easing_function),
|
||||
easing_function_(std::move(easing_function)),
|
||||
current_(-delay) {
|
||||
RequestAnimationFrame();
|
||||
}
|
||||
@ -275,11 +283,11 @@ void Animator::OnAnimation(Params& params) {
|
||||
if (current_ <= Duration()) {
|
||||
*value_ = from_;
|
||||
} else {
|
||||
*value_ = from_ + (to_ - from_) * easing_function_(current_ / duration_);
|
||||
*value_ = from_ +
|
||||
(to_ - from_) * easing_function_(current_ / duration_); // NOLINT
|
||||
}
|
||||
|
||||
RequestAnimationFrame();
|
||||
}
|
||||
|
||||
} // namespace animation
|
||||
} // namespace ftxui
|
||||
} // namespace ftxui::animation
|
||||
|
@ -1,17 +1,16 @@
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr
|
||||
#include <string> // for string
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/animation.hpp" // for Animator, Params (ptr only)
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
#include "ftxui/component/component.hpp" // for Make, Button
|
||||
#include "ftxui/component/component_base.hpp" // for ComponentBase
|
||||
#include "ftxui/component/component_options.hpp" // for ButtonOption, AnimatedColorOption, AnimatedColorsOption
|
||||
#include "ftxui/component/component_options.hpp" // for ButtonOption, AnimatedColorOption, AnimatedColorsOption, EntryState
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::Return
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
|
||||
#include "ftxui/component/screen_interactive.hpp" // for Component
|
||||
#include "ftxui/dom/elements.hpp" // for operator|, Decorator, Element, bgcolor, color, operator|=, reflect, text, border, inverted, nothing
|
||||
#include "ftxui/dom/elements.hpp" // for operator|, Decorator, Element, operator|=, bgcolor, color, reflect, text, bold, border, inverted, nothing
|
||||
#include "ftxui/screen/box.hpp" // for Box
|
||||
#include "ftxui/screen/color.hpp" // for Color
|
||||
#include "ftxui/util/ref.hpp" // for Ref, ConstStringRef
|
||||
@ -20,12 +19,14 @@ namespace ftxui {
|
||||
|
||||
namespace {
|
||||
|
||||
Element DefaultTransform(EntryState params) {
|
||||
Element DefaultTransform(EntryState params) { // NOLINT
|
||||
auto element = text(params.label) | border;
|
||||
if (params.active)
|
||||
if (params.active) {
|
||||
element |= bold;
|
||||
if (params.focused)
|
||||
}
|
||||
if (params.focused) {
|
||||
element |= inverted;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
@ -62,13 +63,16 @@ Component Button(ConstStringRef label,
|
||||
Impl(ConstStringRef label,
|
||||
std::function<void()> on_click,
|
||||
Ref<ButtonOption> option)
|
||||
: label_(label), on_click_(on_click), option_(std::move(option)) {}
|
||||
: label_(std::move(label)),
|
||||
on_click_(std::move(on_click)),
|
||||
option_(std::move(option)) {}
|
||||
|
||||
// Component implementation:
|
||||
Element Render() override {
|
||||
float target = Focused() ? 1.0 : 0.f;
|
||||
if (target != animator_background_.to())
|
||||
float target = Focused() ? 1.F : 0.F; // NOLINT
|
||||
if (target != animator_background_.to()) {
|
||||
SetAnimationTarget(target);
|
||||
}
|
||||
|
||||
EntryState state = {
|
||||
*label_,
|
||||
@ -122,14 +126,15 @@ Component Button(ConstStringRef label,
|
||||
|
||||
void OnClick() {
|
||||
on_click_();
|
||||
animation_background_ = 0.5f;
|
||||
animation_foreground_ = 0.5f;
|
||||
SetAnimationTarget(1.f);
|
||||
animation_background_ = 0.5F; // NOLINT
|
||||
animation_foreground_ = 0.5F; // NOLINT
|
||||
SetAnimationTarget(1.F); // NOLINT
|
||||
}
|
||||
|
||||
bool OnEvent(Event event) override {
|
||||
if (event.is_mouse())
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(event);
|
||||
}
|
||||
|
||||
if (event == Event::Return) {
|
||||
OnClick();
|
||||
@ -142,8 +147,9 @@ Component Button(ConstStringRef label,
|
||||
mouse_hover_ =
|
||||
box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event);
|
||||
|
||||
if (!mouse_hover_)
|
||||
if (!mouse_hover_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TakeFocus();
|
||||
|
||||
@ -172,7 +178,7 @@ Component Button(ConstStringRef label,
|
||||
animation::Animator(&animation_foreground_);
|
||||
};
|
||||
|
||||
return Make<Impl>(label, std::move(on_click), std::move(option));
|
||||
return Make<Impl>(std::move(label), std::move(on_click), std::move(option));
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
@ -11,15 +11,16 @@ namespace ftxui {
|
||||
class CatchEventBase : public ComponentBase {
|
||||
public:
|
||||
// Constructor.
|
||||
CatchEventBase(std::function<bool(Event)> on_event)
|
||||
explicit CatchEventBase(std::function<bool(Event)> on_event)
|
||||
: on_event_(std::move(on_event)) {}
|
||||
|
||||
// Component implementation.
|
||||
bool OnEvent(Event event) override {
|
||||
if (on_event_(event))
|
||||
if (on_event_(event)) {
|
||||
return true;
|
||||
else
|
||||
} else {
|
||||
return ComponentBase::OnEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -76,8 +77,8 @@ Component CatchEvent(Component child,
|
||||
/// ```
|
||||
ComponentDecorator CatchEvent(std::function<bool(Event)> on_event) {
|
||||
return [on_event = std::move(on_event)](Component child) {
|
||||
return CatchEvent(child, [on_event = std::move(on_event)](Event event) {
|
||||
return on_event(event);
|
||||
return CatchEvent(std::move(child), [on_event = on_event](Event event) {
|
||||
return on_event(std::move(event));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ namespace {
|
||||
class CheckboxBase : public ComponentBase {
|
||||
public:
|
||||
CheckboxBase(ConstStringRef label, bool* state, Ref<CheckboxOption> option)
|
||||
: label_(label), state_(state), option_(std::move(option)) {}
|
||||
: label_(std::move(label)), state_(state), option_(std::move(option)) {}
|
||||
|
||||
private:
|
||||
// Component implementation.
|
||||
@ -32,18 +32,20 @@ class CheckboxBase : public ComponentBase {
|
||||
is_active,
|
||||
is_focused || hovered_,
|
||||
};
|
||||
auto element = (option_->transform
|
||||
? option_->transform
|
||||
: CheckboxOption::Simple().transform)(std::move(state));
|
||||
auto element =
|
||||
(option_->transform ? option_->transform
|
||||
: CheckboxOption::Simple().transform)(state);
|
||||
return element | focus_management | reflect(box_);
|
||||
}
|
||||
|
||||
bool OnEvent(Event event) override {
|
||||
if (!CaptureMouse(event))
|
||||
if (!CaptureMouse(event)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.is_mouse())
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(event);
|
||||
}
|
||||
|
||||
hovered_ = false;
|
||||
if (event == Event::Character(' ') || event == Event::Return) {
|
||||
@ -58,11 +60,13 @@ class CheckboxBase : public ComponentBase {
|
||||
bool OnMouseEvent(Event event) {
|
||||
hovered_ = box_.Contain(event.mouse().x, event.mouse().y);
|
||||
|
||||
if (!CaptureMouse(event))
|
||||
if (!CaptureMouse(event)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hovered_)
|
||||
if (!hovered_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.mouse().button == Mouse::Left &&
|
||||
event.mouse().motion == Mouse::Pressed) {
|
||||
@ -109,7 +113,7 @@ class CheckboxBase : public ComponentBase {
|
||||
Component Checkbox(ConstStringRef label,
|
||||
bool* checked,
|
||||
Ref<CheckboxOption> option) {
|
||||
return Make<CheckboxBase>(label, checked, std::move(option));
|
||||
return Make<CheckboxBase>(std::move(label), checked, std::move(option));
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
@ -1,10 +1,12 @@
|
||||
#include <string> // for string
|
||||
#include <utility> // for move
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr, allocator
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/component.hpp" // for Checkbox, Maybe, Make, Vertical, Collapsible
|
||||
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
|
||||
#include "ftxui/component/component_options.hpp" // for CheckboxOption
|
||||
#include "ftxui/util/ref.hpp" // for Ref, ConstStringRef
|
||||
#include "ftxui/component/component_options.hpp" // for CheckboxOption, EntryState
|
||||
#include "ftxui/dom/elements.hpp" // for operator|=, text, hbox, Element, bold, inverted
|
||||
#include "ftxui/util/ref.hpp" // for Ref, ConstStringRef
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
@ -28,27 +30,28 @@ namespace ftxui {
|
||||
Component Collapsible(ConstStringRef label, Component child, Ref<bool> show) {
|
||||
class Impl : public ComponentBase {
|
||||
public:
|
||||
Impl(ConstStringRef label, Component child, Ref<bool> show)
|
||||
: show_(std::move(show)) {
|
||||
Impl(ConstStringRef label, Component child, Ref<bool> show) : show_(show) {
|
||||
CheckboxOption opt;
|
||||
opt.transform = [](EntryState s) {
|
||||
auto prefix = text(s.state ? "▼ " : "▶ ");
|
||||
opt.transform = [](EntryState s) { // NOLINT
|
||||
auto prefix = text(s.state ? "▼ " : "▶ "); // NOLINT
|
||||
auto t = text(s.label);
|
||||
if (s.active)
|
||||
if (s.active) {
|
||||
t |= bold;
|
||||
if (s.focused)
|
||||
}
|
||||
if (s.focused) {
|
||||
t |= inverted;
|
||||
}
|
||||
return hbox({prefix, t});
|
||||
};
|
||||
Add(Container::Vertical({
|
||||
Checkbox(label, show_.operator->(), opt),
|
||||
Checkbox(std::move(label), show_.operator->(), opt),
|
||||
Maybe(std::move(child), show_.operator->()),
|
||||
}));
|
||||
}
|
||||
Ref<bool> show_;
|
||||
};
|
||||
|
||||
return Make<Impl>(label, std::move(child), std::move(show));
|
||||
return Make<Impl>(std::move(label), std::move(child), show);
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for find_if
|
||||
#include <cassert> // for assert
|
||||
#include <cstddef> // for size_t
|
||||
#include <iterator> // for begin, end
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector, __alloc_traits<>::value_type
|
||||
@ -37,7 +37,7 @@ ComponentBase* ComponentBase::Parent() const {
|
||||
/// @brief Access the child at index `i`.
|
||||
/// @ingroup component
|
||||
Component& ComponentBase::ChildAt(size_t i) {
|
||||
assert(i < ChildCount());
|
||||
assert(i < ChildCount()); // NOLINT
|
||||
return children_[i];
|
||||
}
|
||||
|
||||
@ -61,8 +61,9 @@ void ComponentBase::Add(Component child) {
|
||||
/// @see Parent
|
||||
/// @ingroup component
|
||||
void ComponentBase::Detach() {
|
||||
if (!parent_)
|
||||
if (parent_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
auto it = std::find_if(std::begin(parent_->children_), //
|
||||
std::end(parent_->children_), //
|
||||
[this](const Component& that) { //
|
||||
@ -76,8 +77,9 @@ void ComponentBase::Detach() {
|
||||
/// @brief Remove all children.
|
||||
/// @ingroup component
|
||||
void ComponentBase::DetachAllChildren() {
|
||||
while (!children_.empty())
|
||||
while (!children_.empty()) {
|
||||
children_[0]->Detach();
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Draw the component.
|
||||
@ -85,8 +87,9 @@ void ComponentBase::DetachAllChildren() {
|
||||
/// ftxui::ComponentBase.
|
||||
/// @ingroup component
|
||||
Element ComponentBase::Render() {
|
||||
if (children_.size() == 1)
|
||||
if (children_.size() == 1) {
|
||||
return children_.front()->Render();
|
||||
}
|
||||
|
||||
return text("Not implemented component");
|
||||
}
|
||||
@ -97,10 +100,11 @@ Element ComponentBase::Render() {
|
||||
/// The default implementation called OnEvent on every child until one return
|
||||
/// true. If none returns true, return false.
|
||||
/// @ingroup component
|
||||
bool ComponentBase::OnEvent(Event event) {
|
||||
for (Component& child : children_) {
|
||||
if (child->OnEvent(event))
|
||||
bool ComponentBase::OnEvent(Event event) { // NOLINT
|
||||
for (Component& child : children_) { // NOLINT
|
||||
if (child->OnEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -110,8 +114,9 @@ bool ComponentBase::OnEvent(Event event) {
|
||||
/// The default implementation dispatch the event to every child.
|
||||
/// @ingroup component
|
||||
void ComponentBase::OnAnimation(animation::Params& params) {
|
||||
for (Component& child : children_)
|
||||
for (Component& child : children_) {
|
||||
child->OnAnimation(params);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Return the currently Active child.
|
||||
@ -119,8 +124,9 @@ void ComponentBase::OnAnimation(animation::Params& params) {
|
||||
/// @ingroup component
|
||||
Component ComponentBase::ActiveChild() {
|
||||
for (auto& child : children_) {
|
||||
if (child->Focusable())
|
||||
if (child->Focusable()) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -130,9 +136,10 @@ Component ComponentBase::ActiveChild() {
|
||||
/// keyboard.
|
||||
/// @ingroup component
|
||||
bool ComponentBase::Focusable() const {
|
||||
for (const Component& child : children_) {
|
||||
if (child->Focusable())
|
||||
for (const Component& child : children_) { // NOLINT
|
||||
if (child->Focusable()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -140,7 +147,7 @@ bool ComponentBase::Focusable() const {
|
||||
/// @brief Returns if the element if the currently active child of its parent.
|
||||
/// @ingroup component
|
||||
bool ComponentBase::Active() const {
|
||||
return !parent_ || parent_->ActiveChild().get() == this;
|
||||
return parent_ == nullptr || parent_->ActiveChild().get() == this;
|
||||
}
|
||||
|
||||
/// @brief Returns if the elements if focused by the user.
|
||||
@ -149,7 +156,7 @@ bool ComponentBase::Active() const {
|
||||
/// Focusable().
|
||||
/// @ingroup component
|
||||
bool ComponentBase::Focused() const {
|
||||
auto current = this;
|
||||
const auto* current = this;
|
||||
while (current && current->Active()) {
|
||||
current = current->parent_;
|
||||
}
|
||||
@ -159,12 +166,12 @@ bool ComponentBase::Focused() const {
|
||||
/// @brief Make the |child| to be the "active" one.
|
||||
/// @param child the child to become active.
|
||||
/// @ingroup component
|
||||
void ComponentBase::SetActiveChild(ComponentBase*) {}
|
||||
void ComponentBase::SetActiveChild(ComponentBase* /*child*/) {}
|
||||
|
||||
/// @brief Make the |child| to be the "active" one.
|
||||
/// @param child the child to become active.
|
||||
/// @ingroup component
|
||||
void ComponentBase::SetActiveChild(Component child) {
|
||||
void ComponentBase::SetActiveChild(Component child) { // NOLINT
|
||||
SetActiveChild(child.get());
|
||||
}
|
||||
|
||||
@ -182,9 +189,10 @@ void ComponentBase::TakeFocus() {
|
||||
/// them. It represents a component taking priority over others.
|
||||
/// @param event
|
||||
/// @ingroup component
|
||||
CapturedMouse ComponentBase::CaptureMouse(const Event& event) {
|
||||
if (event.screen_)
|
||||
CapturedMouse ComponentBase::CaptureMouse(const Event& event) { // NOLINT
|
||||
if (event.screen_) {
|
||||
return event.screen_->CaptureMouse();
|
||||
}
|
||||
return std::make_unique<CaptureMouseImpl>();
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
#include "ftxui/component/component_options.hpp"
|
||||
|
||||
#include <memory> // for allocator, shared_ptr
|
||||
#include <memory> // for shared_ptr
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/animation.hpp" // for Function, Duration
|
||||
#include "ftxui/dom/elements.hpp" // for Element, operator|, text, bold, dim, inverted, automerge
|
||||
#include "ftxui/dom/elements.hpp" // for operator|=, text, Element, bold, inverted, operator|, dim, hbox, automerge, borderEmpty, borderLight
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
@ -15,13 +16,13 @@ void AnimatedColorOption::Set(Color a_inactive,
|
||||
inactive = a_inactive;
|
||||
active = a_active;
|
||||
duration = a_duration;
|
||||
function = a_function;
|
||||
function = std::move(a_function);
|
||||
}
|
||||
|
||||
void UnderlineOption::SetAnimation(animation::Duration d,
|
||||
animation::easing::Function f) {
|
||||
SetAnimationDuration(d);
|
||||
SetAnimationFunction(f);
|
||||
SetAnimationFunction(std::move(f));
|
||||
}
|
||||
|
||||
void UnderlineOption::SetAnimationDuration(animation::Duration d) {
|
||||
@ -31,28 +32,31 @@ void UnderlineOption::SetAnimationDuration(animation::Duration d) {
|
||||
|
||||
void UnderlineOption::SetAnimationFunction(animation::easing::Function f) {
|
||||
leader_function = f;
|
||||
follower_function = f;
|
||||
follower_function = std::move(f);
|
||||
}
|
||||
|
||||
void UnderlineOption::SetAnimationFunction(
|
||||
animation::easing::Function f_leader,
|
||||
animation::easing::Function f_follower) {
|
||||
leader_function = f_leader;
|
||||
follower_function = f_follower;
|
||||
leader_function = std::move(f_leader);
|
||||
follower_function = std::move(f_follower);
|
||||
}
|
||||
|
||||
// static
|
||||
MenuOption MenuOption::Horizontal() {
|
||||
MenuOption option;
|
||||
option.direction = Direction::Right;
|
||||
option.entries.transform = [](EntryState state) {
|
||||
option.entries.transform = [](const EntryState& state) {
|
||||
Element e = text(state.label);
|
||||
if (state.focused)
|
||||
if (state.focused) {
|
||||
e |= inverted;
|
||||
if (state.active)
|
||||
}
|
||||
if (state.active) {
|
||||
e |= bold;
|
||||
if (!state.focused && !state.active)
|
||||
}
|
||||
if (!state.focused && !state.active) {
|
||||
e |= dim;
|
||||
}
|
||||
return e;
|
||||
};
|
||||
option.elements_infix = [] { return text(" "); };
|
||||
@ -70,19 +74,17 @@ MenuOption MenuOption::HorizontalAnimated() {
|
||||
// static
|
||||
MenuOption MenuOption::Vertical() {
|
||||
MenuOption option;
|
||||
option.entries.transform = [](EntryState state) {
|
||||
if (state.active)
|
||||
state.label = "> " + state.label;
|
||||
else
|
||||
state.label = " " + state.label;
|
||||
|
||||
Element e = text(state.label);
|
||||
if (state.focused)
|
||||
option.entries.transform = [](const EntryState& state) {
|
||||
Element e = text((state.active ? "> " : " ") + state.label); // NOLINT
|
||||
if (state.focused) {
|
||||
e |= inverted;
|
||||
if (state.active)
|
||||
}
|
||||
if (state.active) {
|
||||
e |= bold;
|
||||
if (!state.focused && !state.active)
|
||||
}
|
||||
if (!state.focused && !state.active) {
|
||||
e |= dim;
|
||||
}
|
||||
return e;
|
||||
};
|
||||
return option;
|
||||
@ -91,14 +93,17 @@ MenuOption MenuOption::Vertical() {
|
||||
// static
|
||||
MenuOption MenuOption::VerticalAnimated() {
|
||||
auto option = MenuOption::Vertical();
|
||||
option.entries.transform = [](EntryState state) {
|
||||
option.entries.transform = [](const EntryState& state) {
|
||||
Element e = text(state.label);
|
||||
if (state.focused)
|
||||
if (state.focused) {
|
||||
e |= inverted;
|
||||
if (state.active)
|
||||
}
|
||||
if (state.active) {
|
||||
e |= bold;
|
||||
if (!state.focused && !state.active)
|
||||
}
|
||||
if (!state.focused && !state.active) {
|
||||
e |= dim;
|
||||
}
|
||||
return e;
|
||||
};
|
||||
option.underline.enabled = true;
|
||||
@ -116,10 +121,10 @@ MenuOption MenuOption::Toggle() {
|
||||
// static
|
||||
ButtonOption ButtonOption::Ascii() {
|
||||
ButtonOption option;
|
||||
option.transform = [](EntryState s) {
|
||||
s.label = s.focused ? "[" + s.label + "]" //
|
||||
: " " + s.label + " ";
|
||||
return text(s.label);
|
||||
option.transform = [](const EntryState& s) {
|
||||
std::string label = s.focused ? "[" + s.label + "]" //
|
||||
: " " + s.label + " ";
|
||||
return text(label);
|
||||
};
|
||||
return option;
|
||||
}
|
||||
@ -128,10 +133,11 @@ ButtonOption ButtonOption::Ascii() {
|
||||
// static
|
||||
ButtonOption ButtonOption::Simple() {
|
||||
ButtonOption option;
|
||||
option.transform = [](EntryState s) {
|
||||
option.transform = [](const EntryState& s) {
|
||||
auto element = text(s.label) | borderLight;
|
||||
if (s.focused)
|
||||
if (s.focused) {
|
||||
element |= inverted;
|
||||
}
|
||||
return element;
|
||||
};
|
||||
return option;
|
||||
@ -147,10 +153,11 @@ ButtonOption ButtonOption::Animated() {
|
||||
/// @brief Create a ButtonOption, using animated colors.
|
||||
// static
|
||||
ButtonOption ButtonOption::Animated(Color color) {
|
||||
return ButtonOption::Animated(Color::Interpolate(0.85f, color, Color::Black),
|
||||
Color::Interpolate(0.10f, color, Color::White),
|
||||
Color::Interpolate(0.10f, color, Color::Black),
|
||||
Color::Interpolate(0.85f, color, Color::White));
|
||||
return ButtonOption::Animated(
|
||||
Color::Interpolate(0.85F, color, Color::Black), // NOLINT
|
||||
Color::Interpolate(0.10F, color, Color::White), // NOLINT
|
||||
Color::Interpolate(0.10F, color, Color::Black), // NOLINT
|
||||
Color::Interpolate(0.85F, color, Color::White)); // NOLINT
|
||||
}
|
||||
|
||||
/// @brief Create a ButtonOption, using animated colors.
|
||||
@ -163,17 +170,18 @@ ButtonOption ButtonOption::Animated(Color background, Color foreground) {
|
||||
// static
|
||||
ButtonOption ButtonOption::Animated(Color background,
|
||||
Color foreground,
|
||||
Color background_focused,
|
||||
Color foreground_focused) {
|
||||
Color background_active,
|
||||
Color foreground_active) {
|
||||
ButtonOption option;
|
||||
option.transform = [](EntryState s) {
|
||||
option.transform = [](const EntryState& s) {
|
||||
auto element = text(s.label) | borderEmpty;
|
||||
if (s.focused)
|
||||
if (s.focused) {
|
||||
element |= bold;
|
||||
}
|
||||
return element;
|
||||
};
|
||||
option.animated_colors.foreground.Set(foreground, foreground_focused);
|
||||
option.animated_colors.background.Set(background, background_focused);
|
||||
option.animated_colors.foreground.Set(foreground, foreground_active);
|
||||
option.animated_colors.background.Set(background, background_active);
|
||||
return option;
|
||||
}
|
||||
|
||||
@ -181,19 +189,21 @@ ButtonOption ButtonOption::Animated(Color background,
|
||||
// static
|
||||
CheckboxOption CheckboxOption::Simple() {
|
||||
auto option = CheckboxOption();
|
||||
option.transform = [](EntryState s) {
|
||||
option.transform = [](const EntryState& s) {
|
||||
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
|
||||
// Microsoft terminal do not use fonts able to render properly the default
|
||||
// radiobox glyph.
|
||||
auto prefix = text(s.state ? "[X] " : "[ ] ");
|
||||
auto prefix = text(s.state ? "[X] " : "[ ] "); // NOLINT
|
||||
#else
|
||||
auto prefix = text(s.state ? "▣ " : "☐ ");
|
||||
auto prefix = text(s.state ? "▣ " : "☐ "); // NOLINT
|
||||
#endif
|
||||
auto t = text(s.label);
|
||||
if (s.active)
|
||||
if (s.active) {
|
||||
t |= bold;
|
||||
if (s.focused)
|
||||
}
|
||||
if (s.focused) {
|
||||
t |= inverted;
|
||||
}
|
||||
return hbox({prefix, t});
|
||||
};
|
||||
return option;
|
||||
@ -203,19 +213,21 @@ CheckboxOption CheckboxOption::Simple() {
|
||||
// static
|
||||
RadioboxOption RadioboxOption::Simple() {
|
||||
auto option = RadioboxOption();
|
||||
option.transform = [](EntryState s) {
|
||||
option.transform = [](const EntryState& s) {
|
||||
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
|
||||
// Microsoft terminal do not use fonts able to render properly the default
|
||||
// radiobox glyph.
|
||||
auto prefix = text(s.state ? "(*) " : "( ) ");
|
||||
auto prefix = text(s.state ? "(*) " : "( ) "); // NOLINT
|
||||
#else
|
||||
auto prefix = text(s.state ? "◉ " : "○ ");
|
||||
auto prefix = text(s.state ? "◉ " : "○ "); // NOLINT
|
||||
#endif
|
||||
auto t = text(s.label);
|
||||
if (s.active)
|
||||
if (s.active) {
|
||||
t |= bold;
|
||||
if (s.focused)
|
||||
}
|
||||
if (s.focused) {
|
||||
t |= inverted;
|
||||
}
|
||||
return hbox({prefix, t});
|
||||
};
|
||||
return option;
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for max, min
|
||||
#include <cstddef> // for size_t
|
||||
#include <memory> // for make_shared, __shared_ptr_access, allocator, shared_ptr, allocator_traits<>::value_type
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector, __alloc_traits<>::value_type
|
||||
@ -17,27 +17,32 @@ class ContainerBase : public ComponentBase {
|
||||
public:
|
||||
ContainerBase(Components children, int* selector)
|
||||
: selector_(selector ? selector : &selected_) {
|
||||
for (Component& child : children)
|
||||
for (Component& child : children) {
|
||||
Add(std::move(child));
|
||||
}
|
||||
}
|
||||
|
||||
// Component override.
|
||||
bool OnEvent(Event event) override {
|
||||
if (event.is_mouse())
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(event);
|
||||
}
|
||||
|
||||
if (!Focused())
|
||||
if (!Focused()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ActiveChild() && ActiveChild()->OnEvent(event))
|
||||
if (ActiveChild() && ActiveChild()->OnEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return EventHandler(event);
|
||||
}
|
||||
|
||||
Component ActiveChild() override {
|
||||
if (children_.size() == 0)
|
||||
if (children_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return children_[*selector_ % children_.size()];
|
||||
}
|
||||
@ -45,7 +50,7 @@ class ContainerBase : public ComponentBase {
|
||||
void SetActiveChild(ComponentBase* child) override {
|
||||
for (size_t i = 0; i < children_.size(); ++i) {
|
||||
if (children_[i].get() == child) {
|
||||
*selector_ = i;
|
||||
*selector_ = (int)i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -53,10 +58,10 @@ class ContainerBase : public ComponentBase {
|
||||
|
||||
protected:
|
||||
// Handlers
|
||||
virtual bool EventHandler(Event) { return false; }
|
||||
virtual bool EventHandler(Event /*unused*/) { return false; } // NOLINT
|
||||
|
||||
virtual bool OnMouseEvent(Event event) {
|
||||
return ComponentBase::OnEvent(event);
|
||||
return ComponentBase::OnEvent(std::move(event));
|
||||
}
|
||||
|
||||
int selected_ = 0;
|
||||
@ -71,11 +76,16 @@ class ContainerBase : public ComponentBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MoveSelectorWrap(int dir) {
|
||||
if (children_.empty()) {
|
||||
return;
|
||||
}
|
||||
for (size_t offset = 1; offset < children_.size(); ++offset) {
|
||||
int i = (*selector_ + offset * dir + children_.size()) % children_.size();
|
||||
size_t i = ((size_t(*selector_ + offset * dir + children_.size())) %
|
||||
children_.size());
|
||||
if (children_[i]->Focusable()) {
|
||||
*selector_ = i;
|
||||
*selector_ = (int)i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -88,60 +98,74 @@ class VerticalContainer : public ContainerBase {
|
||||
|
||||
Element Render() override {
|
||||
Elements elements;
|
||||
for (auto& it : children_)
|
||||
for (auto& it : children_) {
|
||||
elements.push_back(it->Render());
|
||||
if (elements.size() == 0)
|
||||
}
|
||||
if (elements.empty()) {
|
||||
return text("Empty container") | reflect(box_);
|
||||
}
|
||||
return vbox(std::move(elements)) | reflect(box_);
|
||||
}
|
||||
|
||||
bool EventHandler(Event event) override {
|
||||
int old_selected = *selector_;
|
||||
if (event == Event::ArrowUp || event == Event::Character('k'))
|
||||
if (event == Event::ArrowUp || event == Event::Character('k')) {
|
||||
MoveSelector(-1);
|
||||
if (event == Event::ArrowDown || event == Event::Character('j'))
|
||||
}
|
||||
if (event == Event::ArrowDown || event == Event::Character('j')) {
|
||||
MoveSelector(+1);
|
||||
}
|
||||
if (event == Event::PageUp) {
|
||||
for (int i = 0; i < box_.y_max - box_.y_min; ++i)
|
||||
for (int i = 0; i < box_.y_max - box_.y_min; ++i) {
|
||||
MoveSelector(-1);
|
||||
}
|
||||
}
|
||||
if (event == Event::PageDown) {
|
||||
for (int i = 0; i < box_.y_max - box_.y_min; ++i)
|
||||
for (int i = 0; i < box_.y_max - box_.y_min; ++i) {
|
||||
MoveSelector(1);
|
||||
}
|
||||
}
|
||||
if (event == Event::Home) {
|
||||
for (size_t i = 0; i < children_.size(); ++i)
|
||||
for (size_t i = 0; i < children_.size(); ++i) {
|
||||
MoveSelector(-1);
|
||||
}
|
||||
}
|
||||
if (event == Event::End) {
|
||||
for (size_t i = 0; i < children_.size(); ++i)
|
||||
for (size_t i = 0; i < children_.size(); ++i) {
|
||||
MoveSelector(1);
|
||||
}
|
||||
}
|
||||
if (event == Event::Tab && children_.size())
|
||||
if (event == Event::Tab) {
|
||||
MoveSelectorWrap(+1);
|
||||
if (event == Event::TabReverse && children_.size())
|
||||
}
|
||||
if (event == Event::TabReverse) {
|
||||
MoveSelectorWrap(-1);
|
||||
}
|
||||
|
||||
*selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
|
||||
return old_selected != *selector_;
|
||||
}
|
||||
|
||||
bool OnMouseEvent(Event event) override {
|
||||
if (ContainerBase::OnMouseEvent(event))
|
||||
if (ContainerBase::OnMouseEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.mouse().button != Mouse::WheelUp &&
|
||||
event.mouse().button != Mouse::WheelDown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!box_.Contain(event.mouse().x, event.mouse().y))
|
||||
if (!box_.Contain(event.mouse().x, event.mouse().y)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.mouse().button == Mouse::WheelUp)
|
||||
if (event.mouse().button == Mouse::WheelUp) {
|
||||
MoveSelector(-1);
|
||||
if (event.mouse().button == Mouse::WheelDown)
|
||||
}
|
||||
if (event.mouse().button == Mouse::WheelDown) {
|
||||
MoveSelector(+1);
|
||||
}
|
||||
*selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
|
||||
|
||||
return true;
|
||||
@ -156,23 +180,29 @@ class HorizontalContainer : public ContainerBase {
|
||||
|
||||
Element Render() override {
|
||||
Elements elements;
|
||||
for (auto& it : children_)
|
||||
for (auto& it : children_) {
|
||||
elements.push_back(it->Render());
|
||||
if (elements.size() == 0)
|
||||
}
|
||||
if (elements.empty()) {
|
||||
return text("Empty container");
|
||||
}
|
||||
return hbox(std::move(elements));
|
||||
}
|
||||
|
||||
bool EventHandler(Event event) override {
|
||||
int old_selected = *selector_;
|
||||
if (event == Event::ArrowLeft || event == Event::Character('h'))
|
||||
if (event == Event::ArrowLeft || event == Event::Character('h')) {
|
||||
MoveSelector(-1);
|
||||
if (event == Event::ArrowRight || event == Event::Character('l'))
|
||||
}
|
||||
if (event == Event::ArrowRight || event == Event::Character('l')) {
|
||||
MoveSelector(+1);
|
||||
if (event == Event::Tab && children_.size())
|
||||
}
|
||||
if (event == Event::Tab) {
|
||||
MoveSelectorWrap(+1);
|
||||
if (event == Event::TabReverse && children_.size())
|
||||
}
|
||||
if (event == Event::TabReverse) {
|
||||
MoveSelectorWrap(-1);
|
||||
}
|
||||
|
||||
*selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
|
||||
return old_selected != *selector_;
|
||||
@ -185,14 +215,16 @@ class TabContainer : public ContainerBase {
|
||||
|
||||
Element Render() override {
|
||||
Component active_child = ActiveChild();
|
||||
if (active_child)
|
||||
if (active_child) {
|
||||
return active_child->Render();
|
||||
}
|
||||
return text("Empty container");
|
||||
}
|
||||
|
||||
bool Focusable() const override {
|
||||
if (children_.size() == 0)
|
||||
if (children_.empty()) {
|
||||
return false;
|
||||
}
|
||||
return children_[*selector_ % children_.size()]->Focusable();
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
#include <algorithm> // for max, min
|
||||
#include <memory> // for __shared_ptr_access
|
||||
#include <string> // for string
|
||||
#include <utility> // for move
|
||||
#include <algorithm> // for max, min
|
||||
#include <functional> // for function
|
||||
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
|
||||
#include <string> // for string
|
||||
|
||||
#include "ftxui/component/component.hpp" // for Maybe, Checkbox, Make, Radiobox, Vertical, Dropdown
|
||||
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
|
||||
#include "ftxui/component/component_options.hpp" // for CheckboxOption
|
||||
#include "ftxui/dom/elements.hpp" // for operator|, Element, border, filler, separator, size, vbox, frame, vscroll_indicator, HEIGHT, LESS_THAN
|
||||
#include "ftxui/component/component_options.hpp" // for CheckboxOption, EntryState
|
||||
#include "ftxui/dom/elements.hpp" // for operator|, Element, border, filler, operator|=, separator, size, text, vbox, frame, vscroll_indicator, hbox, HEIGHT, LESS_THAN, bold, inverted
|
||||
#include "ftxui/util/ref.hpp" // for ConstStringListRef
|
||||
|
||||
namespace ftxui {
|
||||
@ -15,15 +15,17 @@ Component Dropdown(ConstStringListRef entries, int* selected) {
|
||||
class Impl : public ComponentBase {
|
||||
public:
|
||||
Impl(ConstStringListRef entries, int* selected)
|
||||
: entries_(std::move(entries)), selected_(selected) {
|
||||
: entries_(entries), selected_(selected) {
|
||||
CheckboxOption option;
|
||||
option.transform = [](EntryState s) {
|
||||
auto prefix = text(s.state ? "↓ " : "→ ");
|
||||
option.transform = [](const EntryState& s) {
|
||||
auto prefix = text(s.state ? "↓ " : "→ "); // NOLINT
|
||||
auto t = text(s.label);
|
||||
if (s.active)
|
||||
if (s.active) {
|
||||
t |= bold;
|
||||
if (s.focused)
|
||||
}
|
||||
if (s.focused) {
|
||||
t |= inverted;
|
||||
}
|
||||
return hbox({prefix, t});
|
||||
};
|
||||
checkbox_ = Checkbox(&title_, &show_, option),
|
||||
@ -39,11 +41,12 @@ Component Dropdown(ConstStringListRef entries, int* selected) {
|
||||
*selected_ = std::min((int)entries_.size() - 1, std::max(0, *selected_));
|
||||
title_ = entries_[*selected_];
|
||||
if (show_) {
|
||||
const int max_height = 12;
|
||||
return vbox({
|
||||
checkbox_->Render(),
|
||||
separator(),
|
||||
radiobox_->Render() | vscroll_indicator | frame |
|
||||
size(HEIGHT, LESS_THAN, 12),
|
||||
size(HEIGHT, LESS_THAN, max_height),
|
||||
}) |
|
||||
border;
|
||||
}
|
||||
@ -63,7 +66,7 @@ Component Dropdown(ConstStringListRef entries, int* selected) {
|
||||
Component radiobox_;
|
||||
};
|
||||
|
||||
return Make<Impl>(std::move(entries), selected);
|
||||
return Make<Impl>(entries, selected);
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
@ -29,7 +29,7 @@ Event Event::Mouse(std::string input, struct Mouse mouse) {
|
||||
Event event;
|
||||
event.input_ = std::move(input);
|
||||
event.type_ = Type::Mouse;
|
||||
event.mouse_ = mouse;
|
||||
event.mouse_ = mouse; // NOLINT
|
||||
return event;
|
||||
}
|
||||
|
||||
@ -45,40 +45,39 @@ Event Event::CursorReporting(std::string input, int x, int y) {
|
||||
Event event;
|
||||
event.input_ = std::move(input);
|
||||
event.type_ = Type::CursorReporting;
|
||||
event.cursor_.x = x;
|
||||
event.cursor_.y = y;
|
||||
event.cursor_.x = x; // NOLINT
|
||||
event.cursor_.y = y; // NOLINT
|
||||
return event;
|
||||
}
|
||||
|
||||
// --- Arrow ---
|
||||
const Event Event::ArrowLeft = Event::Special("\x1B[D");
|
||||
const Event Event::ArrowRight = Event::Special("\x1B[C");
|
||||
const Event Event::ArrowUp = Event::Special("\x1B[A");
|
||||
const Event Event::ArrowDown = Event::Special("\x1B[B");
|
||||
const Event Event::Backspace = Event::Special({127});
|
||||
const Event Event::Delete = Event::Special("\x1B[3~");
|
||||
const Event Event::Escape = Event::Special("\x1B");
|
||||
const Event Event::Return = Event::Special({10});
|
||||
const Event Event::Tab = Event::Special({9});
|
||||
const Event Event::TabReverse = Event::Special({27, 91, 90});
|
||||
const Event Event::F1 = Event::Special("\x1B[OP");
|
||||
const Event Event::F2 = Event::Special("\x1B[OQ");
|
||||
const Event Event::F3 = Event::Special("\x1B[OR");
|
||||
const Event Event::F4 = Event::Special("\x1B[OS");
|
||||
const Event Event::F5 = Event::Special("\x1B[15~");
|
||||
const Event Event::F6 = Event::Special("\x1B[17~");
|
||||
const Event Event::F7 = Event::Special("\x1B[18~");
|
||||
const Event Event::F8 = Event::Special("\x1B[19~");
|
||||
const Event Event::F9 = Event::Special("\x1B[20~");
|
||||
const Event Event::F10 = Event::Special("\x1B[21~");
|
||||
const Event Event::F11 = Event::Special("\x1B[21~"); // Doesn't exist
|
||||
const Event Event::F12 = Event::Special("\x1B[24~");
|
||||
const Event Event::Home = Event::Special({27, 91, 72});
|
||||
const Event Event::End = Event::Special({27, 91, 70});
|
||||
const Event Event::PageUp = Event::Special({27, 91, 53, 126});
|
||||
const Event Event::PageDown = Event::Special({27, 91, 54, 126});
|
||||
|
||||
Event Event::Custom = Event::Special({0});
|
||||
const Event Event::ArrowLeft = Event::Special("\x1B[D"); // NOLINT
|
||||
const Event Event::ArrowRight = Event::Special("\x1B[C"); // NOLINT
|
||||
const Event Event::ArrowUp = Event::Special("\x1B[A"); // NOLINT
|
||||
const Event Event::ArrowDown = Event::Special("\x1B[B"); // NOLINT
|
||||
const Event Event::Backspace = Event::Special({127}); // NOLINT
|
||||
const Event Event::Delete = Event::Special("\x1B[3~"); // NOLINT
|
||||
const Event Event::Escape = Event::Special("\x1B"); // NOLINT
|
||||
const Event Event::Return = Event::Special({10}); // NOLINT
|
||||
const Event Event::Tab = Event::Special({9}); // NOLINT
|
||||
const Event Event::TabReverse = Event::Special({27, 91, 90}); // NOLINT
|
||||
const Event Event::F1 = Event::Special("\x1B[OP"); // NOLINT
|
||||
const Event Event::F2 = Event::Special("\x1B[OQ"); // NOLINT
|
||||
const Event Event::F3 = Event::Special("\x1B[OR"); // NOLINT
|
||||
const Event Event::F4 = Event::Special("\x1B[OS"); // NOLINT
|
||||
const Event Event::F5 = Event::Special("\x1B[15~"); // NOLINT
|
||||
const Event Event::F6 = Event::Special("\x1B[17~"); // NOLINT
|
||||
const Event Event::F7 = Event::Special("\x1B[18~"); // NOLINT
|
||||
const Event Event::F8 = Event::Special("\x1B[19~"); // NOLINT
|
||||
const Event Event::F9 = Event::Special("\x1B[20~"); // NOLINT
|
||||
const Event Event::F10 = Event::Special("\x1B[21~"); // NOLINT
|
||||
const Event Event::F11 = Event::Special("\x1B[21~"); // Doesn't exist // NOLINT
|
||||
const Event Event::F12 = Event::Special("\x1B[24~"); // NOLINT
|
||||
const Event Event::Home = Event::Special({27, 91, 72}); // NOLINT
|
||||
const Event Event::End = Event::Special({27, 91, 70}); // NOLINT
|
||||
const Event Event::PageUp = Event::Special({27, 91, 53, 126}); // NOLINT
|
||||
const Event Event::PageDown = Event::Special({27, 91, 54, 126}); // NOLINT
|
||||
const Event Event::Custom = Event::Special({0}); // NOLINT
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for max, min
|
||||
#include <cstddef> // for size_t
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr, allocator
|
||||
#include <string> // for string, wstring
|
||||
@ -24,11 +24,12 @@ namespace ftxui {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string PasswordField(int size) {
|
||||
std::string PasswordField(size_t size) {
|
||||
std::string out;
|
||||
out.reserve(2 * size);
|
||||
while (size--)
|
||||
while (size--) {
|
||||
out += "•";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -38,21 +39,25 @@ class InputBase : public ComponentBase {
|
||||
InputBase(StringRef content,
|
||||
ConstStringRef placeholder,
|
||||
Ref<InputOption> option)
|
||||
: content_(content), placeholder_(placeholder), option_(option) {}
|
||||
: content_(std::move(content)),
|
||||
placeholder_(std::move(placeholder)),
|
||||
option_(std::move(option)) {}
|
||||
|
||||
int cursor_position_internal_ = 0;
|
||||
int& cursor_position() {
|
||||
int& opt = option_->cursor_position();
|
||||
if (opt != -1)
|
||||
if (opt != -1) {
|
||||
return opt;
|
||||
}
|
||||
return cursor_position_internal_;
|
||||
}
|
||||
|
||||
// Component implementation:
|
||||
Element Render() override {
|
||||
std::string password_content;
|
||||
if (option_->password())
|
||||
if (option_->password()) {
|
||||
password_content = PasswordField(content_->size());
|
||||
}
|
||||
std::string& content = option_->password() ? password_content : *content_;
|
||||
|
||||
int size = GlyphCount(content);
|
||||
@ -65,19 +70,22 @@ class InputBase : public ComponentBase {
|
||||
if (size == 0) {
|
||||
bool hovered = hovered_;
|
||||
Decorator decorator = dim | main_decorator;
|
||||
if (is_focused)
|
||||
if (is_focused) {
|
||||
decorator = decorator | focus;
|
||||
if (hovered || is_focused)
|
||||
}
|
||||
if (hovered || is_focused) {
|
||||
decorator = decorator | inverted;
|
||||
}
|
||||
return text(*placeholder_) | decorator | reflect(box_);
|
||||
}
|
||||
|
||||
// Not focused.
|
||||
if (!is_focused) {
|
||||
if (hovered_)
|
||||
if (hovered_) {
|
||||
return text(content) | main_decorator | inverted | reflect(box_);
|
||||
else
|
||||
} else {
|
||||
return text(content) | main_decorator | reflect(box_);
|
||||
}
|
||||
}
|
||||
|
||||
int index_before_cursor = GlyphPosition(content, cursor_position());
|
||||
@ -100,17 +108,19 @@ class InputBase : public ComponentBase {
|
||||
|
||||
bool OnEvent(Event event) override {
|
||||
cursor_position() =
|
||||
std::max(0, std::min<int>(content_->size(), cursor_position()));
|
||||
std::max(0, std::min<int>((int)content_->size(), cursor_position()));
|
||||
|
||||
if (event.is_mouse())
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(event);
|
||||
}
|
||||
|
||||
std::string c;
|
||||
|
||||
// Backspace.
|
||||
if (event == Event::Backspace) {
|
||||
if (cursor_position() == 0)
|
||||
if (cursor_position() == 0) {
|
||||
return false;
|
||||
}
|
||||
size_t start = GlyphPosition(*content_, cursor_position() - 1);
|
||||
size_t end = GlyphPosition(*content_, cursor_position());
|
||||
content_->erase(start, end - start);
|
||||
@ -121,8 +131,9 @@ class InputBase : public ComponentBase {
|
||||
|
||||
// Delete
|
||||
if (event == Event::Delete) {
|
||||
if (cursor_position() == int(content_->size()))
|
||||
if (cursor_position() == int(content_->size())) {
|
||||
return false;
|
||||
}
|
||||
size_t start = GlyphPosition(*content_, cursor_position());
|
||||
size_t end = GlyphPosition(*content_, cursor_position() + 1);
|
||||
content_->erase(start, end - start);
|
||||
@ -176,8 +187,9 @@ class InputBase : public ComponentBase {
|
||||
bool OnMouseEvent(Event event) {
|
||||
hovered_ =
|
||||
box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event);
|
||||
if (!hovered_)
|
||||
if (!hovered_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.mouse().button != Mouse::Left ||
|
||||
event.mouse().motion != Mouse::Pressed) {
|
||||
@ -185,22 +197,24 @@ class InputBase : public ComponentBase {
|
||||
}
|
||||
|
||||
TakeFocus();
|
||||
if (content_->size() == 0)
|
||||
if (content_->empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto mapping = CellToGlyphIndex(*content_);
|
||||
int original_glyph = cursor_position();
|
||||
original_glyph = util::clamp(original_glyph, 0, int(mapping.size()));
|
||||
int original_cell = 0;
|
||||
size_t original_cell = 0;
|
||||
for (size_t i = 0; i < mapping.size(); i++) {
|
||||
if (mapping[i] == original_glyph) {
|
||||
original_cell = i;
|
||||
original_cell = (int)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mapping[original_cell] != original_glyph)
|
||||
if (mapping[original_cell] != original_glyph) {
|
||||
original_cell = mapping.size();
|
||||
int target_cell = original_cell + event.mouse().x - cursor_box_.x_min;
|
||||
}
|
||||
int target_cell = int(original_cell) + event.mouse().x - cursor_box_.x_min;
|
||||
int target_glyph = target_cell < (int)mapping.size() ? mapping[target_cell]
|
||||
: (int)mapping.size();
|
||||
target_glyph = util::clamp(target_glyph, 0, GlyphCount(*content_));
|
||||
@ -279,7 +293,8 @@ class WideInputBase : public InputBase {
|
||||
Component Input(StringRef content,
|
||||
ConstStringRef placeholder,
|
||||
Ref<InputOption> option) {
|
||||
return Make<InputBase>(content, placeholder, std::move(option));
|
||||
return Make<InputBase>(std::move(content), std::move(placeholder),
|
||||
std::move(option));
|
||||
}
|
||||
|
||||
/// @brief . An input box for editing text.
|
||||
@ -307,7 +322,8 @@ Component Input(StringRef content,
|
||||
Component Input(WideStringRef content,
|
||||
ConstStringRef placeholder,
|
||||
Ref<InputOption> option) {
|
||||
return Make<WideInputBase>(content, placeholder, std::move(option));
|
||||
return Make<WideInputBase>(std::move(content), std::move(placeholder),
|
||||
std::move(option));
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
@ -13,7 +13,7 @@ namespace ftxui {
|
||||
Component Maybe(Component child, std::function<bool()> show) {
|
||||
class Impl : public ComponentBase {
|
||||
public:
|
||||
Impl(std::function<bool()> show) : show_(std::move(show)) {}
|
||||
explicit Impl(std::function<bool()> show) : show_(std::move(show)) {}
|
||||
|
||||
private:
|
||||
Element Render() override {
|
||||
@ -48,7 +48,7 @@ Component Maybe(Component child, std::function<bool()> show) {
|
||||
/// ```
|
||||
ComponentDecorator Maybe(std::function<bool()> show) {
|
||||
return [show = std::move(show)](Component child) mutable {
|
||||
return Maybe(child, std::move(show));
|
||||
return Maybe(std::move(child), std::move(show));
|
||||
};
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ ComponentDecorator Maybe(std::function<bool()> show) {
|
||||
/// auto maybe_component = Maybe(component, &show);
|
||||
/// ```
|
||||
Component Maybe(Component child, const bool* show) {
|
||||
return Maybe(child, [show] { return *show; });
|
||||
return Maybe(std::move(child), [show] { return *show; });
|
||||
}
|
||||
|
||||
/// @brief Decorate a component. It is shown only when |show| is true.
|
||||
@ -78,7 +78,7 @@ Component Maybe(Component child, const bool* show) {
|
||||
/// auto maybe_component = component | Maybe(&show);
|
||||
/// ```
|
||||
ComponentDecorator Maybe(const bool* show) {
|
||||
return [show](Component child) { return Maybe(child, show); };
|
||||
return [show](Component child) { return Maybe(std::move(child), show); };
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
@ -24,13 +24,15 @@ namespace ftxui {
|
||||
|
||||
namespace {
|
||||
|
||||
Element DefaultOptionTransform(EntryState state) {
|
||||
state.label = (state.active ? "> " : " ") + state.label;
|
||||
Element e = text(state.label);
|
||||
if (state.focused)
|
||||
Element DefaultOptionTransform(const EntryState& state) {
|
||||
std::string label = (state.active ? "> " : " ") + state.label; // NOLINT
|
||||
Element e = text(label);
|
||||
if (state.focused) {
|
||||
e = e | inverted;
|
||||
if (state.active)
|
||||
}
|
||||
if (state.active) {
|
||||
e = e | bold;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -43,7 +45,7 @@ bool IsInverted(MenuOption::Direction direction) {
|
||||
case MenuOption::Direction::Right:
|
||||
return false;
|
||||
}
|
||||
return false; // NOT_REACHED()
|
||||
return false; // NOT_REACHED()
|
||||
}
|
||||
|
||||
bool IsHorizontal(MenuOption::Direction direction) {
|
||||
@ -55,7 +57,7 @@ bool IsHorizontal(MenuOption::Direction direction) {
|
||||
case MenuOption::Direction::Up:
|
||||
return false;
|
||||
}
|
||||
return false; // NOT_REACHED()
|
||||
return false; // NOT_REACHED()
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -65,17 +67,19 @@ bool IsHorizontal(MenuOption::Direction direction) {
|
||||
class MenuBase : public ComponentBase {
|
||||
public:
|
||||
MenuBase(ConstStringListRef entries, int* selected, Ref<MenuOption> option)
|
||||
: entries_(entries), selected_(selected), option_(option) {}
|
||||
: entries_(entries), selected_(selected), option_(std::move(option)) {}
|
||||
|
||||
bool IsHorizontal() { return ftxui::IsHorizontal(option_->direction); }
|
||||
void OnChange() {
|
||||
if (option_->on_change)
|
||||
if (option_->on_change) {
|
||||
option_->on_change();
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnter() {
|
||||
if (option_->on_enter)
|
||||
if (option_->on_enter) {
|
||||
option_->on_enter();
|
||||
}
|
||||
}
|
||||
|
||||
void Clamp() {
|
||||
@ -87,10 +91,12 @@ class MenuBase : public ComponentBase {
|
||||
void OnAnimation(animation::Params& params) override {
|
||||
animator_first_.OnAnimation(params);
|
||||
animator_second_.OnAnimation(params);
|
||||
for (auto& animator : animator_background_)
|
||||
for (auto& animator : animator_background_) {
|
||||
animator.OnAnimation(params);
|
||||
for (auto& animator : animator_foreground_)
|
||||
}
|
||||
for (auto& animator : animator_foreground_) {
|
||||
animator.OnAnimation(params);
|
||||
}
|
||||
}
|
||||
|
||||
Element Render() override {
|
||||
@ -99,11 +105,13 @@ class MenuBase : public ComponentBase {
|
||||
|
||||
Elements elements;
|
||||
bool is_menu_focused = Focused();
|
||||
if (option_->elements_prefix)
|
||||
if (option_->elements_prefix) {
|
||||
elements.push_back(option_->elements_prefix());
|
||||
}
|
||||
for (int i = 0; i < size(); ++i) {
|
||||
if (i != 0 && option_->elements_infix)
|
||||
if (i != 0 && option_->elements_infix) {
|
||||
elements.push_back(option_->elements_infix());
|
||||
}
|
||||
bool is_focused = (focused_entry() == i) && is_menu_focused;
|
||||
bool is_selected = (*selected_ == i);
|
||||
|
||||
@ -120,21 +128,24 @@ class MenuBase : public ComponentBase {
|
||||
Element element =
|
||||
(option_->entries.transform ? option_->entries.transform
|
||||
: DefaultOptionTransform) //
|
||||
(std::move(state));
|
||||
(state);
|
||||
elements.push_back(element | AnimatedColorStyle(i) | reflect(boxes_[i]) |
|
||||
focus_management);
|
||||
}
|
||||
if (option_->elements_postfix)
|
||||
if (option_->elements_postfix) {
|
||||
elements.push_back(option_->elements_postfix());
|
||||
}
|
||||
|
||||
if (IsInverted(option_->direction))
|
||||
if (IsInverted(option_->direction)) {
|
||||
std::reverse(elements.begin(), elements.end());
|
||||
}
|
||||
|
||||
Element bar =
|
||||
IsHorizontal() ? hbox(std::move(elements)) : vbox(std::move(elements));
|
||||
|
||||
if (!option_->underline.enabled)
|
||||
if (!option_->underline.enabled) {
|
||||
return bar | reflect(box_);
|
||||
}
|
||||
|
||||
if (IsHorizontal()) {
|
||||
return vbox({
|
||||
@ -211,36 +222,49 @@ class MenuBase : public ComponentBase {
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
bool OnEvent(Event event) override {
|
||||
Clamp();
|
||||
if (!CaptureMouse(event))
|
||||
if (!CaptureMouse(event)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.is_mouse())
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(event);
|
||||
}
|
||||
|
||||
if (Focused()) {
|
||||
int old_selected = *selected_;
|
||||
if (event == Event::ArrowUp || event == Event::Character('k'))
|
||||
if (event == Event::ArrowUp || event == Event::Character('k')) {
|
||||
OnUp();
|
||||
if (event == Event::ArrowDown || event == Event::Character('j'))
|
||||
}
|
||||
if (event == Event::ArrowDown || event == Event::Character('j')) {
|
||||
OnDown();
|
||||
if (event == Event::ArrowLeft || event == Event::Character('h'))
|
||||
}
|
||||
if (event == Event::ArrowLeft || event == Event::Character('h')) {
|
||||
OnLeft();
|
||||
if (event == Event::ArrowRight || event == Event::Character('l'))
|
||||
}
|
||||
if (event == Event::ArrowRight || event == Event::Character('l')) {
|
||||
OnRight();
|
||||
if (event == Event::PageUp)
|
||||
}
|
||||
if (event == Event::PageUp) {
|
||||
(*selected_) -= box_.y_max - box_.y_min;
|
||||
if (event == Event::PageDown)
|
||||
}
|
||||
if (event == Event::PageDown) {
|
||||
(*selected_) += box_.y_max - box_.y_min;
|
||||
if (event == Event::Home)
|
||||
}
|
||||
if (event == Event::Home) {
|
||||
(*selected_) = 0;
|
||||
if (event == Event::End)
|
||||
}
|
||||
if (event == Event::End) {
|
||||
(*selected_) = size() - 1;
|
||||
if (event == Event::Tab && size())
|
||||
}
|
||||
if (event == Event::Tab && size()) {
|
||||
*selected_ = (*selected_ + 1) % size();
|
||||
if (event == Event::TabReverse && size())
|
||||
}
|
||||
if (event == Event::TabReverse && size()) {
|
||||
*selected_ = (*selected_ + size() - 1) % size();
|
||||
}
|
||||
|
||||
*selected_ = util::clamp(*selected_, 0, size() - 1);
|
||||
|
||||
@ -269,11 +293,13 @@ class MenuBase : public ComponentBase {
|
||||
event.mouse().button != Mouse::Left) {
|
||||
return false;
|
||||
}
|
||||
if (!CaptureMouse(event))
|
||||
if (!CaptureMouse(event)) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < size(); ++i) {
|
||||
if (!boxes_[i].Contain(event.mouse().x, event.mouse().y))
|
||||
if (!boxes_[i].Contain(event.mouse().x, event.mouse().y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TakeFocus();
|
||||
focused_entry() = i;
|
||||
@ -290,19 +316,23 @@ class MenuBase : public ComponentBase {
|
||||
}
|
||||
|
||||
bool OnMouseWheel(Event event) {
|
||||
if (!box_.Contain(event.mouse().x, event.mouse().y))
|
||||
if (!box_.Contain(event.mouse().x, event.mouse().y)) {
|
||||
return false;
|
||||
}
|
||||
int old_selected = *selected_;
|
||||
|
||||
if (event.mouse().button == Mouse::WheelUp)
|
||||
if (event.mouse().button == Mouse::WheelUp) {
|
||||
(*selected_)--;
|
||||
if (event.mouse().button == Mouse::WheelDown)
|
||||
}
|
||||
if (event.mouse().button == Mouse::WheelDown) {
|
||||
(*selected_)++;
|
||||
}
|
||||
|
||||
*selected_ = util::clamp(*selected_, 0, size() - 1);
|
||||
|
||||
if (*selected_ != old_selected)
|
||||
if (*selected_ != old_selected) {
|
||||
OnChange();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -319,12 +349,12 @@ class MenuBase : public ComponentBase {
|
||||
animator_foreground_.clear();
|
||||
|
||||
for (int i = 0; i < size(); ++i) {
|
||||
animation_background_[i] = 0.f;
|
||||
animation_foreground_[i] = 0.f;
|
||||
animator_background_.emplace_back(&animation_background_[i], 0.f,
|
||||
animation_background_[i] = 0.F;
|
||||
animation_foreground_[i] = 0.F;
|
||||
animator_background_.emplace_back(&animation_background_[i], 0.F,
|
||||
std::chrono::milliseconds(0),
|
||||
animation::easing::Linear);
|
||||
animator_foreground_.emplace_back(&animation_foreground_[i], 0.f,
|
||||
animator_foreground_.emplace_back(&animation_foreground_[i], 0.F,
|
||||
std::chrono::milliseconds(0),
|
||||
animation::easing::Linear);
|
||||
}
|
||||
@ -334,7 +364,7 @@ class MenuBase : public ComponentBase {
|
||||
for (int i = 0; i < size(); ++i) {
|
||||
bool is_focused = (focused_entry() == i) && is_menu_focused;
|
||||
bool is_selected = (*selected_ == i);
|
||||
float target = is_selected ? 1.f : is_focused ? 0.5f : 0.f;
|
||||
float target = is_selected ? 1.F : is_focused ? 0.5F : 0.F; // NOLINT
|
||||
if (animator_background_[i].to() != target) {
|
||||
animator_background_[i] = animation::Animator(
|
||||
&animation_background_[i], target,
|
||||
@ -367,8 +397,9 @@ class MenuBase : public ComponentBase {
|
||||
}
|
||||
|
||||
void UpdateUnderlineTarget() {
|
||||
if (!option_->underline.enabled)
|
||||
if (!option_->underline.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (FirstTarget() == animator_first_.to() &&
|
||||
SecondTarget() == animator_second_.to()) {
|
||||
@ -398,32 +429,36 @@ class MenuBase : public ComponentBase {
|
||||
|
||||
bool Focusable() const final { return entries_.size(); }
|
||||
int& focused_entry() { return option_->focused_entry(); }
|
||||
int size() const { return entries_.size(); }
|
||||
int FirstTarget() {
|
||||
if (boxes_.size() == 0)
|
||||
return 0;
|
||||
return IsHorizontal() ? boxes_[*selected_].x_min - box_.x_min
|
||||
: boxes_[*selected_].y_min - box_.y_min;
|
||||
int size() const { return int(entries_.size()); }
|
||||
float FirstTarget() {
|
||||
if (boxes_.empty()) {
|
||||
return 0.F;
|
||||
}
|
||||
int value = IsHorizontal() ? boxes_[*selected_].x_min - box_.x_min
|
||||
: boxes_[*selected_].y_min - box_.y_min;
|
||||
return float(value);
|
||||
}
|
||||
int SecondTarget() {
|
||||
if (boxes_.size() == 0)
|
||||
return 0;
|
||||
return IsHorizontal() ? boxes_[*selected_].x_max - box_.x_min
|
||||
: boxes_[*selected_].y_max - box_.y_min;
|
||||
float SecondTarget() {
|
||||
if (boxes_.empty()) {
|
||||
return 0.F;
|
||||
}
|
||||
int value = IsHorizontal() ? boxes_[*selected_].x_max - box_.x_min
|
||||
: boxes_[*selected_].y_max - box_.y_min;
|
||||
return float(value);
|
||||
}
|
||||
|
||||
protected:
|
||||
ConstStringListRef entries_;
|
||||
int* selected_ = 0;
|
||||
int* selected_ = nullptr;
|
||||
Ref<MenuOption> option_;
|
||||
|
||||
std::vector<Box> boxes_;
|
||||
Box box_;
|
||||
|
||||
float first_ = 0.f;
|
||||
float second_ = 0.f;
|
||||
animation::Animator animator_first_ = animation::Animator(&first_, 0.f);
|
||||
animation::Animator animator_second_ = animation::Animator(&second_, 0.f);
|
||||
float first_ = 0.F;
|
||||
float second_ = 0.F;
|
||||
animation::Animator animator_first_ = animation::Animator(&first_, 0.F);
|
||||
animation::Animator animator_second_ = animation::Animator(&second_, 0.F);
|
||||
|
||||
std::vector<animation::Animator> animator_background_;
|
||||
std::vector<animation::Animator> animator_foreground_;
|
||||
@ -519,7 +554,7 @@ Component MenuEntry(ConstStringRef label, Ref<MenuEntryOption> option) {
|
||||
|
||||
Element element =
|
||||
(option_->transform ? option_->transform : DefaultOptionTransform) //
|
||||
(std::move(state));
|
||||
(state);
|
||||
|
||||
auto focus_management = focused ? select : nothing;
|
||||
return element | AnimatedColorStyle() | focus_management | reflect(box_);
|
||||
@ -527,9 +562,10 @@ Component MenuEntry(ConstStringRef label, Ref<MenuEntryOption> option) {
|
||||
|
||||
void UpdateAnimationTarget() {
|
||||
bool focused = Focused();
|
||||
float target = focused ? 1.0f : hovered_ ? 0.5f : 0.0f;
|
||||
if (target == animator_background_.to())
|
||||
float target = focused ? 1.F : hovered_ ? 0.5F : 0.F; // NOLINT
|
||||
if (target == animator_background_.to()) {
|
||||
return;
|
||||
}
|
||||
animator_background_ =
|
||||
animation::Animator(&animation_background_, target,
|
||||
option_->animated_colors.background.duration,
|
||||
@ -560,13 +596,15 @@ Component MenuEntry(ConstStringRef label, Ref<MenuEntryOption> option) {
|
||||
|
||||
bool Focusable() const override { return true; }
|
||||
bool OnEvent(Event event) override {
|
||||
if (!event.is_mouse())
|
||||
if (!event.is_mouse()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hovered_ = box_.Contain(event.mouse().x, event.mouse().y);
|
||||
|
||||
if (!hovered_)
|
||||
if (!hovered_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.mouse().button == Mouse::Left &&
|
||||
event.mouse().motion == Mouse::Released) {
|
||||
@ -587,12 +625,12 @@ Component MenuEntry(ConstStringRef label, Ref<MenuEntryOption> option) {
|
||||
Box box_;
|
||||
bool hovered_ = false;
|
||||
|
||||
float animation_background_ = 0.f;
|
||||
float animation_foreground_ = 0.f;
|
||||
float animation_background_ = 0.F;
|
||||
float animation_foreground_ = 0.F;
|
||||
animation::Animator animator_background_ =
|
||||
animation::Animator(&animation_background_, 0.f);
|
||||
animation::Animator(&animation_background_, 0.F);
|
||||
animation::Animator animator_foreground_ =
|
||||
animation::Animator(&animation_foreground_, 0.f);
|
||||
animation::Animator(&animation_foreground_, 0.F);
|
||||
};
|
||||
|
||||
return Make<Impl>(std::move(label), std::move(option));
|
||||
|
@ -1,21 +1,20 @@
|
||||
#include <algorithm> // for max
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr, allocator_traits<>::value_type
|
||||
#include <string> // for string
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
#include "ftxui/component/component.hpp" // for Make, Radiobox
|
||||
#include "ftxui/component/component_base.hpp" // for ComponentBase
|
||||
#include "ftxui/component/component_options.hpp" // for RadioboxOption
|
||||
#include "ftxui/component/component_options.hpp" // for RadioboxOption, EntryState
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowUp, Event::End, Event::Home, Event::PageDown, Event::PageUp, Event::Return, Event::Tab, Event::TabReverse
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::WheelDown, Mouse::WheelUp, Mouse::Left, Mouse::Released
|
||||
#include "ftxui/component/screen_interactive.hpp" // for Component
|
||||
#include "ftxui/dom/elements.hpp" // for operator|, reflect, text, Element, hbox, vbox, Elements, focus, nothing, select
|
||||
#include "ftxui/screen/box.hpp" // for Box
|
||||
#include "ftxui/screen/util.hpp" // for clamp
|
||||
#include "ftxui/util/ref.hpp" // for Ref, ConstStringListRef
|
||||
#include "ftxui/dom/elements.hpp" // for operator|, reflect, Element, vbox, Elements, focus, nothing, select
|
||||
#include "ftxui/screen/box.hpp" // for Box
|
||||
#include "ftxui/screen/util.hpp" // for clamp
|
||||
#include "ftxui/util/ref.hpp" // for Ref, ConstStringListRef
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
@ -50,41 +49,51 @@ class RadioboxBase : public ComponentBase {
|
||||
is_focused,
|
||||
};
|
||||
auto element =
|
||||
(option_->transform
|
||||
? option_->transform
|
||||
: RadioboxOption::Simple().transform)(std::move(state));
|
||||
(option_->transform ? option_->transform
|
||||
: RadioboxOption::Simple().transform)(state);
|
||||
|
||||
elements.push_back(element | focus_management | reflect(boxes_[i]));
|
||||
}
|
||||
return vbox(std::move(elements)) | reflect(box_);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
bool OnEvent(Event event) override {
|
||||
Clamp();
|
||||
if (!CaptureMouse(event))
|
||||
if (!CaptureMouse(event)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.is_mouse())
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(event);
|
||||
}
|
||||
|
||||
if (Focused()) {
|
||||
int old_hovered = hovered_;
|
||||
if (event == Event::ArrowUp || event == Event::Character('k'))
|
||||
if (event == Event::ArrowUp || event == Event::Character('k')) {
|
||||
(hovered_)--;
|
||||
if (event == Event::ArrowDown || event == Event::Character('j'))
|
||||
}
|
||||
if (event == Event::ArrowDown || event == Event::Character('j')) {
|
||||
(hovered_)++;
|
||||
if (event == Event::PageUp)
|
||||
}
|
||||
if (event == Event::PageUp) {
|
||||
(hovered_) -= box_.y_max - box_.y_min;
|
||||
if (event == Event::PageDown)
|
||||
}
|
||||
if (event == Event::PageDown) {
|
||||
(hovered_) += box_.y_max - box_.y_min;
|
||||
if (event == Event::Home)
|
||||
}
|
||||
if (event == Event::Home) {
|
||||
(hovered_) = 0;
|
||||
if (event == Event::End)
|
||||
}
|
||||
if (event == Event::End) {
|
||||
(hovered_) = size() - 1;
|
||||
if (event == Event::Tab && size())
|
||||
}
|
||||
if (event == Event::Tab && size()) {
|
||||
hovered_ = (hovered_ + 1) % size();
|
||||
if (event == Event::TabReverse && size())
|
||||
}
|
||||
if (event == Event::TabReverse && size()) {
|
||||
hovered_ = (hovered_ + size() - 1) % size();
|
||||
}
|
||||
|
||||
hovered_ = util::clamp(hovered_, 0, size() - 1);
|
||||
|
||||
@ -111,8 +120,9 @@ class RadioboxBase : public ComponentBase {
|
||||
}
|
||||
|
||||
for (int i = 0; i < size(); ++i) {
|
||||
if (!boxes_[i].Contain(event.mouse().x, event.mouse().y))
|
||||
if (!boxes_[i].Contain(event.mouse().x, event.mouse().y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TakeFocus();
|
||||
focused_entry() = i;
|
||||
@ -130,20 +140,24 @@ class RadioboxBase : public ComponentBase {
|
||||
}
|
||||
|
||||
bool OnMouseWheel(Event event) {
|
||||
if (!box_.Contain(event.mouse().x, event.mouse().y))
|
||||
if (!box_.Contain(event.mouse().x, event.mouse().y)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int old_hovered = hovered_;
|
||||
|
||||
if (event.mouse().button == Mouse::WheelUp)
|
||||
if (event.mouse().button == Mouse::WheelUp) {
|
||||
(hovered_)--;
|
||||
if (event.mouse().button == Mouse::WheelDown)
|
||||
}
|
||||
if (event.mouse().button == Mouse::WheelDown) {
|
||||
(hovered_)++;
|
||||
}
|
||||
|
||||
hovered_ = util::clamp(hovered_, 0, size() - 1);
|
||||
|
||||
if (hovered_ != old_hovered)
|
||||
if (hovered_ != old_hovered) {
|
||||
option_->on_change();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -157,7 +171,7 @@ class RadioboxBase : public ComponentBase {
|
||||
|
||||
bool Focusable() const final { return entries_.size(); }
|
||||
int& focused_entry() { return option_->focused_entry(); }
|
||||
int size() const { return entries_.size(); }
|
||||
int size() const { return int(entries_.size()); }
|
||||
|
||||
ConstStringListRef entries_;
|
||||
int* selected_;
|
||||
|
@ -28,7 +28,8 @@ namespace ftxui {
|
||||
Component Renderer(std::function<Element()> render) {
|
||||
class Impl : public ComponentBase {
|
||||
public:
|
||||
Impl(std::function<Element()> render) : render_(std::move(render)) {}
|
||||
explicit Impl(std::function<Element()> render)
|
||||
: render_(std::move(render)) {}
|
||||
Element Render() override { return render_(); }
|
||||
std::function<Element()> render_;
|
||||
};
|
||||
@ -82,15 +83,17 @@ Component Renderer(Component child, std::function<Element()> render) {
|
||||
Component Renderer(std::function<Element(bool)> render) {
|
||||
class Impl : public ComponentBase {
|
||||
public:
|
||||
Impl(std::function<Element(bool)> render) : render_(std::move(render)) {}
|
||||
explicit Impl(std::function<Element(bool)> render)
|
||||
: render_(std::move(render)) {}
|
||||
|
||||
private:
|
||||
Element Render() override { return render_(Focused()) | reflect(box_); }
|
||||
bool Focusable() const override { return true; }
|
||||
bool OnEvent(Event event) override {
|
||||
if (event.is_mouse() && box_.Contain(event.mouse().x, event.mouse().y)) {
|
||||
if (!CaptureMouse(event))
|
||||
if (!CaptureMouse(event)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TakeFocus();
|
||||
}
|
||||
@ -118,8 +121,8 @@ Component Renderer(std::function<Element(bool)> render) {
|
||||
/// | Renderer(inverted);
|
||||
/// screen.Loop(renderer);
|
||||
/// ```
|
||||
ComponentDecorator Renderer(ElementDecorator decorator) {
|
||||
return [decorator](Component component) {
|
||||
ComponentDecorator Renderer(ElementDecorator decorator) { // NOLINT
|
||||
return [decorator](Component component) { // NOLINT
|
||||
return Renderer(component, [component, decorator] {
|
||||
return component->Render() | decorator;
|
||||
});
|
||||
|
@ -15,16 +15,19 @@ namespace {
|
||||
class ResizableSplitLeftBase : public ComponentBase {
|
||||
public:
|
||||
ResizableSplitLeftBase(Component main, Component child, int* main_size)
|
||||
: main_(main), child_(child), main_size_(main_size) {
|
||||
: main_(std::move(main)),
|
||||
child_(std::move(child)),
|
||||
main_size_(main_size) {
|
||||
Add(Container::Horizontal({
|
||||
main,
|
||||
child,
|
||||
main_,
|
||||
child_,
|
||||
}));
|
||||
}
|
||||
|
||||
bool OnEvent(Event event) final {
|
||||
if (event.is_mouse())
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(std::move(event));
|
||||
}
|
||||
return ComponentBase::OnEvent(std::move(event));
|
||||
}
|
||||
|
||||
@ -71,16 +74,19 @@ class ResizableSplitLeftBase : public ComponentBase {
|
||||
class ResizableSplitRightBase : public ComponentBase {
|
||||
public:
|
||||
ResizableSplitRightBase(Component main, Component child, int* main_size)
|
||||
: main_(main), child_(child), main_size_(main_size) {
|
||||
: main_(std::move(main)),
|
||||
child_(std::move(child)),
|
||||
main_size_(main_size) {
|
||||
Add(Container::Horizontal({
|
||||
child,
|
||||
main,
|
||||
child_,
|
||||
main_,
|
||||
}));
|
||||
}
|
||||
|
||||
bool OnEvent(Event event) final {
|
||||
if (event.is_mouse())
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(std::move(event));
|
||||
}
|
||||
return ComponentBase::OnEvent(std::move(event));
|
||||
}
|
||||
|
||||
@ -127,16 +133,19 @@ class ResizableSplitRightBase : public ComponentBase {
|
||||
class ResizableSplitTopBase : public ComponentBase {
|
||||
public:
|
||||
ResizableSplitTopBase(Component main, Component child, int* main_size)
|
||||
: main_(main), child_(child), main_size_(main_size) {
|
||||
: main_(std::move(main)),
|
||||
child_(std::move(child)),
|
||||
main_size_(main_size) {
|
||||
Add(Container::Vertical({
|
||||
main,
|
||||
child,
|
||||
main_,
|
||||
child_,
|
||||
}));
|
||||
}
|
||||
|
||||
bool OnEvent(Event event) final {
|
||||
if (event.is_mouse())
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(std::move(event));
|
||||
}
|
||||
return ComponentBase::OnEvent(std::move(event));
|
||||
}
|
||||
|
||||
@ -183,16 +192,19 @@ class ResizableSplitTopBase : public ComponentBase {
|
||||
class ResizableSplitBottomBase : public ComponentBase {
|
||||
public:
|
||||
ResizableSplitBottomBase(Component main, Component child, int* main_size)
|
||||
: main_(main), child_(child), main_size_(main_size) {
|
||||
: main_(std::move(main)),
|
||||
child_(std::move(child)),
|
||||
main_size_(main_size) {
|
||||
Add(Container::Vertical({
|
||||
child,
|
||||
main,
|
||||
child_,
|
||||
main_,
|
||||
}));
|
||||
}
|
||||
|
||||
bool OnEvent(Event event) final {
|
||||
if (event.is_mouse())
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(std::move(event));
|
||||
}
|
||||
return ComponentBase::OnEvent(std::move(event));
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
#include <stdio.h> // for fileno, stdin
|
||||
#include <algorithm> // for copy, max, min
|
||||
#include <chrono> // for operator-, duration, operator>=, milliseconds, time_point, common_type<>::type
|
||||
#include <array> // for array
|
||||
#include <chrono> // for operator-, milliseconds, duration, operator>=, time_point, common_type<>::type
|
||||
#include <csignal> // for signal, raise, SIGTSTP, SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, SIGWINCH
|
||||
#include <cstdlib> // for NULL
|
||||
#include <cstdio> // for fileno, size_t, stdin
|
||||
#include <functional> // for function
|
||||
#include <initializer_list> // for initializer_list
|
||||
#include <iostream> // for cout, ostream, basic_ostream, operator<<, endl, flush
|
||||
#include <stack> // for stack
|
||||
#include <thread> // for thread, sleep_for
|
||||
#include <type_traits> // for decay_t
|
||||
#include <utility> // for swap, move
|
||||
#include <utility> // for move, swap
|
||||
#include <variant> // for visit
|
||||
#include <vector> // for vector
|
||||
|
||||
@ -50,14 +50,15 @@ namespace ftxui {
|
||||
namespace animation {
|
||||
void RequestAnimationFrame() {
|
||||
auto* screen = ScreenInteractive::Active();
|
||||
if (screen)
|
||||
if (screen) {
|
||||
screen->RequestAnimationFrame();
|
||||
}
|
||||
}
|
||||
} // namespace animation
|
||||
|
||||
namespace {
|
||||
|
||||
ScreenInteractive* g_active_screen = nullptr;
|
||||
ScreenInteractive* g_active_screen = nullptr; // NOLINT
|
||||
|
||||
void Flush() {
|
||||
// Emscripten doesn't implement flush. We interpret zero as flush.
|
||||
@ -138,16 +139,14 @@ void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
|
||||
int CheckStdinReady(int usec_timeout) {
|
||||
timeval tv = {0, usec_timeout};
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(STDIN_FILENO, &fds);
|
||||
select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
|
||||
return FD_ISSET(STDIN_FILENO, &fds);
|
||||
FD_ZERO(&fds); // NOLINT
|
||||
FD_SET(STDIN_FILENO, &fds); // NOLINT
|
||||
select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &tv); // NOLINT
|
||||
return FD_ISSET(STDIN_FILENO, &fds); // NOLINT
|
||||
}
|
||||
|
||||
// Read char from the terminal.
|
||||
void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
|
||||
const int buffer_size = 100;
|
||||
|
||||
auto parser = TerminalInputParser(std::move(out));
|
||||
|
||||
while (!*quit) {
|
||||
@ -156,16 +155,18 @@ void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char buff[buffer_size];
|
||||
int l = read(fileno(stdin), buff, buffer_size);
|
||||
for (int i = 0; i < l; ++i)
|
||||
parser.Add(buff[i]);
|
||||
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) {
|
||||
parser.Add(buffer[i]); // NOLINT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const std::string CSI = "\x1b[";
|
||||
const std::string CSI = "\x1b["; // NOLINT
|
||||
|
||||
// DEC: Digital Equipment Corporation
|
||||
enum class DECMode {
|
||||
@ -186,12 +187,13 @@ enum class DSRMode {
|
||||
kCursor = 6,
|
||||
};
|
||||
|
||||
const std::string Serialize(std::vector<DECMode> parameters) {
|
||||
std::string Serialize(const std::vector<DECMode>& parameters) {
|
||||
bool first = true;
|
||||
std::string out;
|
||||
for (DECMode parameter : parameters) {
|
||||
if (!first)
|
||||
if (!first) {
|
||||
out += ";";
|
||||
}
|
||||
out += std::to_string(int(parameter));
|
||||
first = false;
|
||||
}
|
||||
@ -199,22 +201,22 @@ const std::string Serialize(std::vector<DECMode> parameters) {
|
||||
}
|
||||
|
||||
// DEC Private Mode Set (DECSET)
|
||||
const std::string Set(std::vector<DECMode> parameters) {
|
||||
std::string Set(const std::vector<DECMode>& parameters) {
|
||||
return CSI + "?" + Serialize(parameters) + "h";
|
||||
}
|
||||
|
||||
// DEC Private Mode Reset (DECRST)
|
||||
const std::string Reset(std::vector<DECMode> parameters) {
|
||||
std::string Reset(const std::vector<DECMode>& parameters) {
|
||||
return CSI + "?" + Serialize(parameters) + "l";
|
||||
}
|
||||
|
||||
// Device Status Report (DSR)
|
||||
const std::string DeviceStatusReport(DSRMode ps) {
|
||||
std::string DeviceStatusReport(DSRMode ps) {
|
||||
return CSI + std::to_string(int(ps)) + "n";
|
||||
}
|
||||
|
||||
using SignalHandler = void(int);
|
||||
std::stack<Closure> on_exit_functions;
|
||||
std::stack<Closure> on_exit_functions; // NOLINT
|
||||
void OnExit(int signal) {
|
||||
(void)signal;
|
||||
while (!on_exit_functions.empty()) {
|
||||
@ -223,14 +225,14 @@ void OnExit(int signal) {
|
||||
}
|
||||
}
|
||||
|
||||
auto install_signal_handler = [](int sig, SignalHandler handler) {
|
||||
const auto install_signal_handler = [](int sig, SignalHandler handler) {
|
||||
auto old_signal_handler = std::signal(sig, handler);
|
||||
on_exit_functions.push([=] { std::signal(sig, old_signal_handler); });
|
||||
};
|
||||
|
||||
Closure on_resize = [] {};
|
||||
Closure g_on_resize = [] {}; // NOLINT
|
||||
void OnResize(int /* signal */) {
|
||||
on_resize();
|
||||
g_on_resize();
|
||||
}
|
||||
|
||||
void OnSigStop(int /*signal*/) {
|
||||
@ -239,17 +241,24 @@ void OnSigStop(int /*signal*/) {
|
||||
|
||||
class CapturedMouseImpl : public CapturedMouseInterface {
|
||||
public:
|
||||
CapturedMouseImpl(std::function<void(void)> callback) : callback_(callback) {}
|
||||
explicit CapturedMouseImpl(std::function<void(void)> callback)
|
||||
: callback_(std::move(callback)) {}
|
||||
~CapturedMouseImpl() override { callback_(); }
|
||||
CapturedMouseImpl(const CapturedMouseImpl&) = delete;
|
||||
CapturedMouseImpl(CapturedMouseImpl&&) = delete;
|
||||
CapturedMouseImpl& operator=(const CapturedMouseImpl&) = delete;
|
||||
CapturedMouseImpl& operator=(CapturedMouseImpl&&) = delete;
|
||||
|
||||
private:
|
||||
std::function<void(void)> callback_;
|
||||
};
|
||||
|
||||
void AnimationListener(std::atomic<bool>* quit, Sender<Task> out) {
|
||||
// Animation at around 60fps.
|
||||
const auto time_delta = std::chrono::milliseconds(15);
|
||||
while (!*quit) {
|
||||
out->Send(AnimationTask());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(15));
|
||||
std::this_thread::sleep_for(time_delta);
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,31 +295,36 @@ ScreenInteractive ScreenInteractive::FitComponent() {
|
||||
}
|
||||
|
||||
void ScreenInteractive::Post(Task task) {
|
||||
if (!quit_)
|
||||
task_sender_->Send(task);
|
||||
if (!quit_) {
|
||||
task_sender_->Send(std::move(task));
|
||||
}
|
||||
}
|
||||
void ScreenInteractive::PostEvent(Event event) {
|
||||
Post(event);
|
||||
}
|
||||
|
||||
void ScreenInteractive::RequestAnimationFrame() {
|
||||
if (animation_requested_)
|
||||
if (animation_requested_) {
|
||||
return;
|
||||
}
|
||||
animation_requested_ = true;
|
||||
auto now = animation::Clock::now();
|
||||
if (now - previous_animation_time >= std::chrono::milliseconds(33))
|
||||
const auto time_histeresis = std::chrono::milliseconds(33);
|
||||
if (now - previous_animation_time >= time_histeresis) {
|
||||
previous_animation_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
CapturedMouse ScreenInteractive::CaptureMouse() {
|
||||
if (mouse_captured)
|
||||
if (mouse_captured) {
|
||||
return nullptr;
|
||||
}
|
||||
mouse_captured = true;
|
||||
return std::make_unique<CapturedMouseImpl>(
|
||||
[this] { mouse_captured = false; });
|
||||
}
|
||||
|
||||
void ScreenInteractive::Loop(Component component) {
|
||||
void ScreenInteractive::Loop(Component component) { // NOLINT
|
||||
// Suspend previously active screen:
|
||||
if (g_active_screen) {
|
||||
std::swap(suspended_screen_, g_active_screen);
|
||||
@ -324,7 +338,7 @@ void ScreenInteractive::Loop(Component component) {
|
||||
// This screen is now active:
|
||||
g_active_screen = this;
|
||||
g_active_screen->Install();
|
||||
g_active_screen->Main(component);
|
||||
g_active_screen->Main(std::move(component));
|
||||
g_active_screen->Uninstall();
|
||||
g_active_screen = nullptr;
|
||||
|
||||
@ -348,7 +362,7 @@ void ScreenInteractive::Loop(Component component) {
|
||||
/// @brief Decorate a function. It executes the same way, but with the currently
|
||||
/// active screen terminal hooks temporarilly uninstalled during its execution.
|
||||
/// @param fn The function to decorate.
|
||||
Closure ScreenInteractive::WithRestoredIO(Closure fn) {
|
||||
Closure ScreenInteractive::WithRestoredIO(Closure fn) { // NOLINT
|
||||
return [this, fn] {
|
||||
Uninstall();
|
||||
fn();
|
||||
@ -370,10 +384,11 @@ void ScreenInteractive::Install() {
|
||||
|
||||
// Install signal handlers to restore the terminal state on exit. The default
|
||||
// signal handlers are restored on exit.
|
||||
for (int signal : {SIGTERM, SIGSEGV, SIGINT, SIGILL, SIGABRT, SIGFPE})
|
||||
for (int signal : {SIGTERM, SIGSEGV, SIGINT, SIGILL, SIGABRT, SIGFPE}) {
|
||||
install_signal_handler(signal, OnExit);
|
||||
}
|
||||
|
||||
// Save the old terminal configuration and restore it on exit.
|
||||
// Save the old terminal configuration and restore it on exit.
|
||||
#if defined(_WIN32)
|
||||
// Enable VT processing on stdout and stdin
|
||||
auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
@ -405,12 +420,12 @@ void ScreenInteractive::Install() {
|
||||
SetConsoleMode(stdin_handle, in_mode);
|
||||
SetConsoleMode(stdout_handle, out_mode);
|
||||
#else
|
||||
struct termios terminal;
|
||||
struct termios terminal; // NOLINT
|
||||
tcgetattr(STDIN_FILENO, &terminal);
|
||||
on_exit_functions.push([=] { tcsetattr(STDIN_FILENO, TCSANOW, &terminal); });
|
||||
|
||||
terminal.c_lflag &= ~ICANON; // Non canonique terminal.
|
||||
terminal.c_lflag &= ~ECHO; // Do not print after a key press.
|
||||
terminal.c_lflag &= ~ICANON; // NOLINT Non canonique terminal.
|
||||
terminal.c_lflag &= ~ECHO; // NOLINT Do not print after a key press.
|
||||
terminal.c_cc[VMIN] = 0;
|
||||
terminal.c_cc[VTIME] = 0;
|
||||
// auto oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
|
||||
@ -420,19 +435,19 @@ void ScreenInteractive::Install() {
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &terminal);
|
||||
|
||||
// Handle resize.
|
||||
on_resize = [&] { task_sender_->Send(Event::Special({0})); };
|
||||
g_on_resize = [&] { task_sender_->Send(Event::Special({0})); };
|
||||
install_signal_handler(SIGWINCH, OnResize);
|
||||
|
||||
// Handle SIGTSTP/SIGCONT.
|
||||
install_signal_handler(SIGTSTP, OnSigStop);
|
||||
#endif
|
||||
|
||||
auto enable = [&](std::vector<DECMode> parameters) {
|
||||
auto enable = [&](const std::vector<DECMode>& parameters) {
|
||||
std::cout << Set(parameters);
|
||||
on_exit_functions.push([=] { std::cout << Reset(parameters); });
|
||||
};
|
||||
|
||||
auto disable = [&](std::vector<DECMode> parameters) {
|
||||
auto disable = [&](const std::vector<DECMode>& parameters) {
|
||||
std::cout << Reset(parameters);
|
||||
on_exit_functions.push([=] { std::cout << Set(parameters); });
|
||||
};
|
||||
@ -475,6 +490,7 @@ void ScreenInteractive::Uninstall() {
|
||||
OnExit(0);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void ScreenInteractive::Main(Component component) {
|
||||
previous_animation_time = animation::Clock::now();
|
||||
|
||||
@ -493,8 +509,9 @@ void ScreenInteractive::Main(Component component) {
|
||||
}
|
||||
|
||||
Task task;
|
||||
if (!task_receiver_->Receive(&task))
|
||||
if (!task_receiver_->Receive(&task)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
std::visit([&](auto&& arg) {
|
||||
@ -527,8 +544,9 @@ void ScreenInteractive::Main(Component component) {
|
||||
|
||||
// Handle Animation
|
||||
if constexpr (std::is_same_v<T, AnimationTask>) {
|
||||
if (!animation_requested_)
|
||||
if (!animation_requested_) {
|
||||
return;
|
||||
}
|
||||
|
||||
animation_requested_ = false;
|
||||
animation::TimePoint now = animation::Clock::now();
|
||||
@ -546,6 +564,7 @@ void ScreenInteractive::Main(Component component) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void ScreenInteractive::Draw(Component component) {
|
||||
auto document = component->Render();
|
||||
int dimx = 0;
|
||||
@ -596,13 +615,16 @@ void ScreenInteractive::Draw(Component component) {
|
||||
// https://github.com/ArthurSonzogni/FTXUI/issues/136
|
||||
static int i = -3;
|
||||
++i;
|
||||
if (!use_alternative_screen_ && (i % 150 == 0))
|
||||
if (!use_alternative_screen_ && (i % 150 == 0)) { // NOLINT
|
||||
std::cout << DeviceStatusReport(DSRMode::kCursor);
|
||||
}
|
||||
#else
|
||||
static int i = -3;
|
||||
++i;
|
||||
if (!use_alternative_screen_ && (previous_frame_resized_ || i % 40 == 0))
|
||||
if (!use_alternative_screen_ &&
|
||||
(previous_frame_resized_ || i % 40 == 0)) { // NOLINT
|
||||
std::cout << DeviceStatusReport(DSRMode::kCursor);
|
||||
}
|
||||
#endif
|
||||
previous_frame_resized_ = resized;
|
||||
|
||||
|
@ -18,13 +18,13 @@ template <class T>
|
||||
class SliderBase : public ComponentBase {
|
||||
public:
|
||||
SliderBase(ConstStringRef label, T* value, T min, T max, T increment)
|
||||
: label_(label),
|
||||
: label_(std::move(label)),
|
||||
value_(value),
|
||||
min_(min),
|
||||
max_(max),
|
||||
increment_(increment) {}
|
||||
|
||||
Element Render() {
|
||||
Element Render() override {
|
||||
auto gauge_color =
|
||||
Focused() ? color(Color::GrayLight) : color(Color::GrayDark);
|
||||
float percent = float(*value_ - min_) / float(max_ - min_);
|
||||
@ -40,8 +40,9 @@ class SliderBase : public ComponentBase {
|
||||
}
|
||||
|
||||
bool OnEvent(Event event) final {
|
||||
if (event.is_mouse())
|
||||
if (event.is_mouse()) {
|
||||
return OnMouseEvent(event);
|
||||
}
|
||||
|
||||
if (event == Event::ArrowLeft || event == Event::Character('h')) {
|
||||
*value_ -= increment_;
|
||||
|
@ -14,11 +14,14 @@ TerminalInputParser::TerminalInputParser(Sender<Task> out)
|
||||
|
||||
void TerminalInputParser::Timeout(int time) {
|
||||
timeout_ += time;
|
||||
if (timeout_ < 50)
|
||||
const int timeout_threshold = 50;
|
||||
if (timeout_ < timeout_threshold) {
|
||||
return;
|
||||
}
|
||||
timeout_ = 0;
|
||||
if (pending_.size())
|
||||
if (!pending_.empty()) {
|
||||
Send(SPECIAL);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalInputParser::Add(char c) {
|
||||
@ -56,21 +59,23 @@ void TerminalInputParser::Send(TerminalInputParser::Output output) {
|
||||
// key. This also happens with linux with the `bind` command:
|
||||
// See https://github.com/ArthurSonzogni/FTXUI/issues/337
|
||||
// Here, we uniformize the new line character to `\n`.
|
||||
if (pending_ == "\r")
|
||||
if (pending_ == "\r") {
|
||||
out_->Send(Event::Special("\n"));
|
||||
else
|
||||
} else {
|
||||
out_->Send(Event::Special(std::move(pending_)));
|
||||
}
|
||||
pending_.clear();
|
||||
return;
|
||||
|
||||
case MOUSE:
|
||||
out_->Send(Event::Mouse(std::move(pending_), output.mouse));
|
||||
out_->Send(Event::Mouse(std::move(pending_), output.mouse)); // NOLINT
|
||||
pending_.clear();
|
||||
return;
|
||||
|
||||
case CURSOR_REPORTING:
|
||||
out_->Send(Event::CursorReporting(std::move(pending_), output.cursor.x,
|
||||
output.cursor.y));
|
||||
out_->Send(Event::CursorReporting(std::move(pending_), // NOLINT
|
||||
output.cursor.x, // NOLINT
|
||||
output.cursor.y)); // NOLINT
|
||||
pending_.clear();
|
||||
return;
|
||||
}
|
||||
@ -78,12 +83,13 @@ void TerminalInputParser::Send(TerminalInputParser::Output output) {
|
||||
}
|
||||
|
||||
TerminalInputParser::Output TerminalInputParser::Parse() {
|
||||
if (!Eat())
|
||||
if (!Eat()) {
|
||||
return UNCOMPLETED;
|
||||
}
|
||||
|
||||
switch (Current()) {
|
||||
case 24: // CAN
|
||||
case 26: // SUB
|
||||
case 24: // CAN NOLINT
|
||||
case 26: // SUB NOLINT
|
||||
return DROP;
|
||||
|
||||
case '\x1B':
|
||||
@ -92,11 +98,13 @@ TerminalInputParser::Output TerminalInputParser::Parse() {
|
||||
break;
|
||||
}
|
||||
|
||||
if (Current() < 32) // C0
|
||||
if (Current() < 32) { // C0 NOLINT
|
||||
return SPECIAL;
|
||||
}
|
||||
|
||||
if (Current() == 127) // Delete
|
||||
if (Current() == 127) { // Delete // NOLINT
|
||||
return SPECIAL;
|
||||
}
|
||||
|
||||
return ParseUTF8();
|
||||
}
|
||||
@ -118,18 +126,18 @@ TerminalInputParser::Output TerminalInputParser::Parse() {
|
||||
// Then some sequences are illegal if it exist a shorter representation of the
|
||||
// same codepoint.
|
||||
TerminalInputParser::Output TerminalInputParser::ParseUTF8() {
|
||||
unsigned char head = static_cast<unsigned char>(Current());
|
||||
unsigned char selector = 0b1000'0000;
|
||||
auto head = static_cast<unsigned char>(Current());
|
||||
unsigned char selector = 0b1000'0000; // NOLINT
|
||||
|
||||
// The non code-point part of the first byte.
|
||||
unsigned char mask = selector;
|
||||
|
||||
// Find the first zero in the first byte.
|
||||
int first_zero = 8;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
unsigned int first_zero = 8; // NOLINT
|
||||
for (unsigned int i = 0; i < 8; ++i) { // NOLINT
|
||||
mask |= selector;
|
||||
if (head & selector) {
|
||||
selector >>= 1;
|
||||
selector >>= 1U;
|
||||
continue;
|
||||
}
|
||||
first_zero = i;
|
||||
@ -137,48 +145,54 @@ TerminalInputParser::Output TerminalInputParser::ParseUTF8() {
|
||||
}
|
||||
|
||||
// Accumulate the value of the first byte.
|
||||
uint32_t value = head & ~mask;
|
||||
auto value = uint32_t(head & ~mask); // NOLINT
|
||||
|
||||
// Invalid UTF8, with more than 5 bytes.
|
||||
if (first_zero == 1 || first_zero >= 5)
|
||||
const unsigned int max_utf8_bytes = 5;
|
||||
if (first_zero == 1 || first_zero >= max_utf8_bytes) {
|
||||
return DROP;
|
||||
}
|
||||
|
||||
// Multi byte UTF-8.
|
||||
for (int i = 2; i <= first_zero; ++i) {
|
||||
if (!Eat())
|
||||
for (unsigned int i = 2; i <= first_zero; ++i) {
|
||||
if (!Eat()) {
|
||||
return UNCOMPLETED;
|
||||
}
|
||||
|
||||
// Invalid continuation byte.
|
||||
head = static_cast<unsigned char>(Current());
|
||||
if ((head & 0b1100'0000) != 0b1000'0000)
|
||||
if ((head & 0b1100'0000) != 0b1000'0000) { // NOLINT
|
||||
return DROP;
|
||||
value <<= 6;
|
||||
value += head & 0b0011'1111;
|
||||
}
|
||||
value <<= 6; // NOLINT
|
||||
value += head & 0b0011'1111; // NOLINT
|
||||
}
|
||||
|
||||
// Check for overlong UTF8 encoding.
|
||||
int extra_byte;
|
||||
if (value <= 0b000'0000'0111'1111) {
|
||||
extra_byte = 0;
|
||||
} else if (value <= 0b000'0111'1111'1111) {
|
||||
extra_byte = 1;
|
||||
} else if (value <= 0b1111'1111'1111'1111) {
|
||||
extra_byte = 2;
|
||||
} else if (value <= 0b1'0000'1111'1111'1111'1111) {
|
||||
extra_byte = 3;
|
||||
} else {
|
||||
int extra_byte = 0;
|
||||
if (value <= 0b000'0000'0111'1111) { // NOLINT
|
||||
extra_byte = 0; // NOLINT
|
||||
} else if (value <= 0b000'0111'1111'1111) { // NOLINT
|
||||
extra_byte = 1; // NOLINT
|
||||
} else if (value <= 0b1111'1111'1111'1111) { // NOLINT
|
||||
extra_byte = 2; // NOLINT
|
||||
} else if (value <= 0b1'0000'1111'1111'1111'1111) { // NOLINT
|
||||
extra_byte = 3; // NOLINT
|
||||
} else { // NOLINT
|
||||
return DROP;
|
||||
}
|
||||
|
||||
if (extra_byte != position_)
|
||||
if (extra_byte != position_) {
|
||||
return DROP;
|
||||
}
|
||||
|
||||
return CHARACTER;
|
||||
}
|
||||
|
||||
TerminalInputParser::Output TerminalInputParser::ParseESC() {
|
||||
if (!Eat())
|
||||
if (!Eat()) {
|
||||
return UNCOMPLETED;
|
||||
}
|
||||
switch (Current()) {
|
||||
case 'P':
|
||||
return ParseDCS();
|
||||
@ -187,26 +201,32 @@ TerminalInputParser::Output TerminalInputParser::ParseESC() {
|
||||
case ']':
|
||||
return ParseOSC();
|
||||
default:
|
||||
if (!Eat())
|
||||
if (!Eat()) {
|
||||
return UNCOMPLETED;
|
||||
return SPECIAL;
|
||||
} else {
|
||||
return SPECIAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TerminalInputParser::Output TerminalInputParser::ParseDCS() {
|
||||
// Parse until the string terminator ST.
|
||||
while (1) {
|
||||
if (!Eat())
|
||||
while (true) {
|
||||
if (!Eat()) {
|
||||
return UNCOMPLETED;
|
||||
}
|
||||
|
||||
if (Current() != '\x1B')
|
||||
if (Current() != '\x1B') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Eat())
|
||||
if (!Eat()) {
|
||||
return UNCOMPLETED;
|
||||
}
|
||||
|
||||
if (Current() != '\\')
|
||||
if (Current() != '\\') {
|
||||
continue;
|
||||
}
|
||||
|
||||
return SPECIAL;
|
||||
}
|
||||
@ -217,8 +237,9 @@ TerminalInputParser::Output TerminalInputParser::ParseCSI() {
|
||||
int argument = 0;
|
||||
std::vector<int> arguments;
|
||||
while (true) {
|
||||
if (!Eat())
|
||||
if (!Eat()) {
|
||||
return UNCOMPLETED;
|
||||
}
|
||||
|
||||
if (Current() == '<') {
|
||||
altered = true;
|
||||
@ -226,7 +247,7 @@ TerminalInputParser::Output TerminalInputParser::ParseCSI() {
|
||||
}
|
||||
|
||||
if (Current() >= '0' && Current() <= '9') {
|
||||
argument *= 10;
|
||||
argument *= 10; // NOLINT
|
||||
argument += int(Current() - '0');
|
||||
continue;
|
||||
}
|
||||
@ -239,7 +260,7 @@ TerminalInputParser::Output TerminalInputParser::ParseCSI() {
|
||||
|
||||
if (Current() >= ' ' && Current() <= '~' && Current() != '<') {
|
||||
arguments.push_back(argument);
|
||||
argument = 0;
|
||||
argument = 0; // NOLINT
|
||||
switch (Current()) {
|
||||
case 'M':
|
||||
return ParseMouse(altered, true, std::move(arguments));
|
||||
@ -253,53 +274,61 @@ TerminalInputParser::Output TerminalInputParser::ParseCSI() {
|
||||
}
|
||||
|
||||
// Invalid ESC in CSI.
|
||||
if (Current() == '\x1B')
|
||||
if (Current() == '\x1B') {
|
||||
return SPECIAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TerminalInputParser::Output TerminalInputParser::ParseOSC() {
|
||||
// Parse until the string terminator ST.
|
||||
while (true) {
|
||||
if (!Eat())
|
||||
if (!Eat()) {
|
||||
return UNCOMPLETED;
|
||||
if (Current() != '\x1B')
|
||||
}
|
||||
if (Current() != '\x1B') {
|
||||
continue;
|
||||
if (!Eat())
|
||||
}
|
||||
if (!Eat()) {
|
||||
return UNCOMPLETED;
|
||||
if (Current() != '\\')
|
||||
}
|
||||
if (Current() != '\\') {
|
||||
continue;
|
||||
}
|
||||
return SPECIAL;
|
||||
}
|
||||
}
|
||||
|
||||
TerminalInputParser::Output TerminalInputParser::ParseMouse(
|
||||
TerminalInputParser::Output TerminalInputParser::ParseMouse( // NOLINT
|
||||
bool altered,
|
||||
bool pressed,
|
||||
std::vector<int> arguments) {
|
||||
if (arguments.size() != 3)
|
||||
if (arguments.size() != 3) {
|
||||
return SPECIAL;
|
||||
}
|
||||
|
||||
(void)altered;
|
||||
|
||||
Output output(MOUSE);
|
||||
output.mouse.button = Mouse::Button((arguments[0] & 3) + //
|
||||
((arguments[0] & 64) >> 4));
|
||||
output.mouse.motion = Mouse::Motion(pressed);
|
||||
output.mouse.shift = bool(arguments[0] & 4);
|
||||
output.mouse.meta = bool(arguments[0] & 8);
|
||||
output.mouse.x = arguments[1];
|
||||
output.mouse.y = arguments[2];
|
||||
output.mouse.button = Mouse::Button((arguments[0] & 3) + // NOLINT
|
||||
((arguments[0] & 64) >> 4)); // NOLINT
|
||||
output.mouse.motion = Mouse::Motion(pressed); // NOLINT
|
||||
output.mouse.shift = bool(arguments[0] & 4); // NOLINT
|
||||
output.mouse.meta = bool(arguments[0] & 8); // NOLINT
|
||||
output.mouse.x = arguments[1]; // NOLINT
|
||||
output.mouse.y = arguments[2]; // NOLINT
|
||||
return output;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
TerminalInputParser::Output TerminalInputParser::ParseCursorReporting(
|
||||
std::vector<int> arguments) {
|
||||
if (arguments.size() != 2)
|
||||
if (arguments.size() != 2) {
|
||||
return SPECIAL;
|
||||
}
|
||||
Output output(CURSOR_REPORTING);
|
||||
output.cursor.y = arguments[0];
|
||||
output.cursor.x = arguments[1];
|
||||
output.cursor.y = arguments[0]; // NOLINT
|
||||
output.cursor.x = arguments[1]; // NOLINT
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ class TerminalInputParser {
|
||||
Output(Type t) : type(t) {}
|
||||
};
|
||||
|
||||
void Send(Output type);
|
||||
void Send(Output output);
|
||||
Output Parse();
|
||||
Output ParseUTF8();
|
||||
Output ParseESC();
|
||||
|
@ -5,21 +5,25 @@
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
Component operator|(Component component, ComponentDecorator decorator) {
|
||||
return decorator(component);
|
||||
return decorator(component); // NOLINT
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
Component operator|(Component component, ElementDecorator decorator) {
|
||||
return component | Renderer(decorator);
|
||||
return component | Renderer(decorator); // NOLINT
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
Component& operator|=(Component& component, ComponentDecorator decorator) {
|
||||
component = component | decorator;
|
||||
component = component | decorator; // NOLINT
|
||||
return component;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
Component& operator|=(Component& component, ElementDecorator decorator) {
|
||||
component = component | decorator;
|
||||
component = component | decorator; // NOLINT
|
||||
return component;
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
#include <algorithm> // for max
|
||||
#include <iterator> // for begin, end
|
||||
#include <array> // for array
|
||||
#include <memory> // for allocator, make_shared, __shared_ptr_access
|
||||
#include <string> // for string, basic_string
|
||||
#include <string> // for basic_string, string
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector, __alloc_traits<>::value_type
|
||||
#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/node.hpp" // for Node, Elements
|
||||
@ -13,30 +13,25 @@
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
static std::string simple_border_charset[6][6] = {
|
||||
{"┌", "┐", "└", "┘", "─", "│"}, //
|
||||
{"┏", "┓", "┗", "┛", "━", "┃"}, //
|
||||
{"╔", "╗", "╚", "╝", "═", "║"}, //
|
||||
{"╭", "╮", "╰", "╯", "─", "│"}, //
|
||||
{" ", " ", " ", " ", " ", " "}, //
|
||||
using Charset = std::array<std::string, 6>; // NOLINT
|
||||
using Charsets = std::array<Charset, 6>; // NOLINT
|
||||
static Charsets simple_border_charset = // NOLINT
|
||||
{
|
||||
Charset{"┌", "┐", "└", "┘", "─", "│"},
|
||||
Charset{"┏", "┓", "┗", "┛", "━", "┃"},
|
||||
Charset{"╔", "╗", "╚", "╝", "═", "║"},
|
||||
Charset{"╭", "╮", "╰", "╯", "─", "│"},
|
||||
Charset{" ", " ", " ", " ", " ", " "},
|
||||
};
|
||||
|
||||
// For reference, here is the charset for normal border:
|
||||
// {"┌", "┐", "└", "┘", "─", "│", "┬", "┴", "┤", "├"};
|
||||
// TODO(arthursonzogni): Consider adding options to choose the kind of borders
|
||||
// to use.
|
||||
|
||||
class Border : public Node {
|
||||
public:
|
||||
Border(Elements children, BorderStyle style)
|
||||
: Node(std::move(children)),
|
||||
charset(std::begin(simple_border_charset[style]),
|
||||
std::end(simple_border_charset[style])) {}
|
||||
Border(Elements children, Pixel pixel)
|
||||
: Node(std::move(children)), charset_pixel(10, pixel) {}
|
||||
charset_(simple_border_charset[style]) {} // NOLINT
|
||||
|
||||
std::vector<Pixel> charset_pixel;
|
||||
std::vector<std::string> charset;
|
||||
const Charset& charset_; // NOLINT
|
||||
|
||||
void ComputeRequirement() override {
|
||||
Node::ComputeRequirement();
|
||||
@ -75,63 +70,101 @@ class Border : public Node {
|
||||
children_[0]->Render(screen);
|
||||
|
||||
// Draw the border.
|
||||
if (box_.x_min >= box_.x_max || box_.y_min >= box_.y_max)
|
||||
if (box_.x_min >= box_.x_max || box_.y_min >= box_.y_max) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!charset.empty())
|
||||
RenderPixel(screen);
|
||||
else
|
||||
RenderChar(screen);
|
||||
}
|
||||
screen.at(box_.x_min, box_.y_min) = charset_[0]; // NOLINT
|
||||
screen.at(box_.x_max, box_.y_min) = charset_[1]; // NOLINT
|
||||
screen.at(box_.x_min, box_.y_max) = charset_[2]; // NOLINT
|
||||
screen.at(box_.x_max, box_.y_max) = charset_[3]; // NOLINT
|
||||
|
||||
void RenderPixel(Screen& screen) {
|
||||
screen.at(box_.x_min, box_.y_min) = charset[0];
|
||||
screen.at(box_.x_max, box_.y_min) = charset[1];
|
||||
screen.at(box_.x_min, box_.y_max) = charset[2];
|
||||
screen.at(box_.x_max, box_.y_max) = charset[3];
|
||||
|
||||
for (float x = box_.x_min + 1; x < box_.x_max; ++x) {
|
||||
for (int x = box_.x_min + 1; x < box_.x_max; ++x) {
|
||||
Pixel& p1 = screen.PixelAt(x, box_.y_min);
|
||||
Pixel& p2 = screen.PixelAt(x, box_.y_max);
|
||||
p1.character = charset[4];
|
||||
p2.character = charset[4];
|
||||
p1.character = charset_[4]; // NOLINT
|
||||
p2.character = charset_[4]; // NOLINT
|
||||
p1.automerge = true;
|
||||
p2.automerge = true;
|
||||
}
|
||||
for (float y = box_.y_min + 1; y < box_.y_max; ++y) {
|
||||
for (int y = box_.y_min + 1; y < box_.y_max; ++y) {
|
||||
Pixel& p3 = screen.PixelAt(box_.x_min, y);
|
||||
Pixel& p4 = screen.PixelAt(box_.x_max, y);
|
||||
p3.character = charset[5];
|
||||
p4.character = charset[5];
|
||||
p3.character = charset_[5]; // NOLINT
|
||||
p4.character = charset_[5]; // NOLINT
|
||||
p3.automerge = true;
|
||||
p4.automerge = true;
|
||||
}
|
||||
|
||||
// Draw title.
|
||||
if (children_.size() == 2)
|
||||
if (children_.size() == 2) {
|
||||
children_[1]->Render(screen);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// For reference, here is the charset for normal border:
|
||||
class BorderPixel : public Node {
|
||||
public:
|
||||
BorderPixel(Elements children, Pixel pixel)
|
||||
: Node(std::move(children)), pixel_(std::move(pixel)) {}
|
||||
|
||||
private:
|
||||
Pixel pixel_;
|
||||
|
||||
void ComputeRequirement() override {
|
||||
Node::ComputeRequirement();
|
||||
requirement_ = children_[0]->requirement();
|
||||
requirement_.min_x += 2;
|
||||
requirement_.min_y += 2;
|
||||
if (children_.size() == 2) {
|
||||
requirement_.min_x =
|
||||
std::max(requirement_.min_x, children_[1]->requirement().min_x + 2);
|
||||
}
|
||||
requirement_.selected_box.x_min++;
|
||||
requirement_.selected_box.x_max++;
|
||||
requirement_.selected_box.y_min++;
|
||||
requirement_.selected_box.y_max++;
|
||||
}
|
||||
|
||||
void RenderChar(Screen& screen) {
|
||||
screen.PixelAt(box_.x_min, box_.y_min) = charset_pixel[0];
|
||||
screen.PixelAt(box_.x_max, box_.y_min) = charset_pixel[1];
|
||||
screen.PixelAt(box_.x_min, box_.y_max) = charset_pixel[2];
|
||||
screen.PixelAt(box_.x_max, box_.y_max) = charset_pixel[3];
|
||||
for (float x = box_.x_min + 1; x < box_.x_max; ++x) {
|
||||
Pixel& p1 = screen.PixelAt(x, box_.y_min);
|
||||
Pixel& p2 = screen.PixelAt(x, box_.y_max);
|
||||
p1 = charset_pixel[5];
|
||||
p2 = charset_pixel[5];
|
||||
p1.automerge = true;
|
||||
p2.automerge = true;
|
||||
void SetBox(Box box) override {
|
||||
Node::SetBox(box);
|
||||
if (children_.size() == 2) {
|
||||
Box title_box;
|
||||
title_box.x_min = box.x_min + 1;
|
||||
title_box.x_max = box.x_max - 1;
|
||||
title_box.y_min = box.y_min;
|
||||
title_box.y_max = box.y_min;
|
||||
children_[1]->SetBox(title_box);
|
||||
}
|
||||
for (float y = box_.y_min + 1; y < box_.y_max; ++y) {
|
||||
Pixel& p3 = screen.PixelAt(box_.x_min, y);
|
||||
Pixel& p4 = screen.PixelAt(box_.x_max, y);
|
||||
p3 = charset_pixel[5];
|
||||
p4 = charset_pixel[5];
|
||||
p3.automerge = true;
|
||||
p4.automerge = true;
|
||||
box.x_min++;
|
||||
box.x_max--;
|
||||
box.y_min++;
|
||||
box.y_max--;
|
||||
children_[0]->SetBox(box);
|
||||
}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
// Draw content.
|
||||
children_[0]->Render(screen);
|
||||
|
||||
// Draw the border.
|
||||
if (box_.x_min >= box_.x_max || box_.y_min >= box_.y_max) {
|
||||
return;
|
||||
}
|
||||
|
||||
screen.PixelAt(box_.x_min, box_.y_min) = pixel_;
|
||||
screen.PixelAt(box_.x_max, box_.y_min) = pixel_;
|
||||
screen.PixelAt(box_.x_min, box_.y_max) = pixel_;
|
||||
screen.PixelAt(box_.x_max, box_.y_max) = pixel_;
|
||||
|
||||
for (int x = box_.x_min + 1; x < box_.x_max; ++x) {
|
||||
screen.PixelAt(x, box_.y_min) = pixel_;
|
||||
screen.PixelAt(x, box_.y_max) = pixel_;
|
||||
}
|
||||
for (int y = box_.y_min + 1; y < box_.y_max; ++y) {
|
||||
screen.PixelAt(box_.x_min, y) = pixel_;
|
||||
screen.PixelAt(box_.x_max, y) = pixel_;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -171,9 +204,9 @@ Element border(Element child) {
|
||||
/// @brief Same as border but with a constant Pixel around the element.
|
||||
/// @ingroup dom
|
||||
/// @see border
|
||||
Decorator borderWith(Pixel pixel) {
|
||||
Decorator borderWith(const Pixel& pixel) {
|
||||
return [pixel](Element child) {
|
||||
return std::make_shared<Border>(unpack(std::move(child)), pixel);
|
||||
return std::make_shared<BorderPixel>(unpack(std::move(child)), pixel);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,7 @@
|
||||
|
||||
#include <algorithm> // for max
|
||||
|
||||
namespace ftxui {
|
||||
namespace box_helper {
|
||||
namespace ftxui::box_helper {
|
||||
|
||||
namespace {
|
||||
// Called when the size allowed is greater than the requested size. This
|
||||
@ -44,7 +43,7 @@ void ComputeShrinkHard(std::vector<Element>* elements,
|
||||
int extra_space,
|
||||
int size) {
|
||||
for (Element& element : *elements) {
|
||||
if (element.flex_shrink) {
|
||||
if (element.flex_shrink != 0) {
|
||||
element.size = 0;
|
||||
continue;
|
||||
}
|
||||
@ -68,23 +67,25 @@ void Compute(std::vector<Element>* elements, int target_size) {
|
||||
for (auto& element : *elements) {
|
||||
flex_grow_sum += element.flex_grow;
|
||||
flex_shrink_sum += element.min_size * element.flex_shrink;
|
||||
if (element.flex_shrink)
|
||||
if (element.flex_shrink != 0) {
|
||||
flex_shrink_size += element.min_size;
|
||||
}
|
||||
size += element.min_size;
|
||||
}
|
||||
|
||||
int extra_space = target_size - size;
|
||||
if (extra_space >= 0)
|
||||
if (extra_space >= 0) {
|
||||
ComputeGrow(elements, extra_space, flex_grow_sum);
|
||||
else if (flex_shrink_size + extra_space >= 0)
|
||||
} else if (flex_shrink_size + extra_space >= 0) {
|
||||
ComputeShrinkEasy(elements, extra_space, flex_shrink_sum);
|
||||
else
|
||||
|
||||
} else {
|
||||
ComputeShrinkHard(elements, extra_space + flex_shrink_size,
|
||||
size - flex_shrink_size);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace box_helper
|
||||
} // namespace ftxui
|
||||
} // namespace ftxui::box_helper
|
||||
|
||||
// Copyright 2021 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
|
@ -1,8 +1,8 @@
|
||||
#include "ftxui/dom/canvas.hpp"
|
||||
|
||||
#include <stdlib.h> // for abs
|
||||
#include <algorithm> // for max, min
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <cstdlib> // for abs
|
||||
#include <map> // for allocator, map
|
||||
#include <memory> // for make_shared
|
||||
#include <utility> // for move, pair
|
||||
@ -42,26 +42,29 @@ namespace {
|
||||
// 11100010 10100000 10100000 // dot6
|
||||
// 11100010 10100010 10000000 // dot0-2
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
uint8_t g_map_braille[2][4][2] = {
|
||||
{
|
||||
{0b00000000, 0b00000001}, // dot1
|
||||
{0b00000000, 0b00000010}, // dot2
|
||||
{0b00000000, 0b00000100}, // dot3
|
||||
{0b00000001, 0b00000000}, // dot0-1
|
||||
{0b00000000, 0b00000001}, // NOLINT | dot1
|
||||
{0b00000000, 0b00000010}, // NOLINT | dot2
|
||||
{0b00000000, 0b00000100}, // NOLINT | dot3
|
||||
{0b00000001, 0b00000000}, // NOLINT | dot0-1
|
||||
},
|
||||
{
|
||||
{0b00000000, 0b00001000}, // dot4
|
||||
{0b00000000, 0b00010000}, // dot5
|
||||
{0b00000000, 0b00100000}, // dot6
|
||||
{0b00000010, 0b00000000}, // dot0-2
|
||||
{0b00000000, 0b00001000}, // NOLINT | dot4
|
||||
{0b00000000, 0b00010000}, // NOLINT | dot5
|
||||
{0b00000000, 0b00100000}, // NOLINT | dot6
|
||||
{0b00000010, 0b00000000}, // NOLINT | dot0-2
|
||||
},
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
std::vector<std::string> g_map_block = {
|
||||
" ", "▘", "▖", "▌", "▝", "▀", "▞", "▛",
|
||||
"▗", "▚", "▄", "▙", "▐", "▜", "▟", "█",
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
const std::map<std::string, uint8_t> g_map_block_inversed = {
|
||||
{" ", 0b0000}, {"▘", 0b0001}, {"▖", 0b0010}, {"▌", 0b0011},
|
||||
{"▝", 0b0100}, {"▀", 0b0101}, {"▞", 0b0110}, {"▛", 0b0111},
|
||||
@ -69,13 +72,17 @@ const std::map<std::string, uint8_t> g_map_block_inversed = {
|
||||
{"▐", 0b1100}, {"▜", 0b1101}, {"▟", 0b1110}, {"█", 0b1111},
|
||||
};
|
||||
|
||||
constexpr auto nostyle = [](Pixel& /*pixel*/) {};
|
||||
|
||||
} // namespace
|
||||
|
||||
/// @brief Constructor.
|
||||
/// @param width the width of the canvas. A cell is a 2x8 braille dot.
|
||||
/// @param height the height of the canvas. A cell is a 2x8 braille dot.
|
||||
Canvas::Canvas(int width, int height)
|
||||
: width_(width), height_(height), storage_(width_ * height_ / 8) {}
|
||||
: width_(width),
|
||||
height_(height),
|
||||
storage_(width_ * height_ / 8 /* NOLINT */) {}
|
||||
|
||||
/// @brief Get the content of a cell.
|
||||
/// @param x the x coordinate of the cell.
|
||||
@ -90,7 +97,7 @@ Pixel Canvas::GetPixel(int x, int y) const {
|
||||
/// @param y the y coordinate of the dot.
|
||||
/// @param value whether the dot is filled or not.
|
||||
void Canvas::DrawPoint(int x, int y, bool value) {
|
||||
DrawPoint(x, y, value, [](Pixel&) {});
|
||||
DrawPoint(x, y, value, [](Pixel& /*pixel*/) {});
|
||||
}
|
||||
|
||||
/// @brief Draw a braille dot.
|
||||
@ -109,42 +116,45 @@ void Canvas::DrawPoint(int x, int y, bool value, const Color& color) {
|
||||
/// @param style the style of the cell.
|
||||
void Canvas::DrawPoint(int x, int y, bool value, const Stylizer& style) {
|
||||
Style(x, y, style);
|
||||
if (value)
|
||||
if (value) {
|
||||
DrawPointOn(x, y);
|
||||
else
|
||||
} else {
|
||||
DrawPointOff(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Draw a braille dot.
|
||||
/// @param x the x coordinate of the dot.
|
||||
/// @param y the y coordinate of the dot.
|
||||
void Canvas::DrawPointOn(int x, int y) {
|
||||
if (!IsIn(x, y))
|
||||
if (!IsIn(x, y)) {
|
||||
return;
|
||||
}
|
||||
Cell& cell = storage_[XY{x / 2, y / 4}];
|
||||
if (cell.type != CellType::kBraille) {
|
||||
cell.content.character = "⠀"; // 3 bytes.
|
||||
cell.type = CellType::kBraille;
|
||||
}
|
||||
|
||||
cell.content.character[1] |= g_map_braille[x % 2][y % 4][0];
|
||||
cell.content.character[2] |= g_map_braille[x % 2][y % 4][1];
|
||||
cell.content.character[1] |= g_map_braille[x % 2][y % 4][0]; // NOLINT
|
||||
cell.content.character[2] |= g_map_braille[x % 2][y % 4][1]; // NOLINT
|
||||
}
|
||||
|
||||
/// @brief Erase a braille dot.
|
||||
/// @param x the x coordinate of the dot.
|
||||
/// @param y the y coordinate of the dot.
|
||||
void Canvas::DrawPointOff(int x, int y) {
|
||||
if (!IsIn(x, y))
|
||||
if (!IsIn(x, y)) {
|
||||
return;
|
||||
}
|
||||
Cell& cell = storage_[XY{x / 2, y / 4}];
|
||||
if (cell.type != CellType::kBraille) {
|
||||
cell.content.character = "⠀"; // 3 byt
|
||||
cell.type = CellType::kBraille;
|
||||
}
|
||||
|
||||
cell.content.character[1] &= ~(g_map_braille[x % 2][y % 4][0]);
|
||||
cell.content.character[2] &= ~(g_map_braille[x % 2][y % 4][1]);
|
||||
cell.content.character[1] &= ~(g_map_braille[x % 2][y % 4][0]); // NOLINT
|
||||
cell.content.character[2] &= ~(g_map_braille[x % 2][y % 4][1]); // NOLINT
|
||||
}
|
||||
|
||||
/// @brief Toggle a braille dot. A filled one will be erased, and the other will
|
||||
@ -152,16 +162,17 @@ void Canvas::DrawPointOff(int x, int y) {
|
||||
/// @param x the x coordinate of the dot.
|
||||
/// @param y the y coordinate of the dot.
|
||||
void Canvas::DrawPointToggle(int x, int y) {
|
||||
if (!IsIn(x, y))
|
||||
if (!IsIn(x, y)) {
|
||||
return;
|
||||
}
|
||||
Cell& cell = storage_[XY{x / 2, y / 4}];
|
||||
if (cell.type != CellType::kBraille) {
|
||||
cell.content.character = "⠀"; // 3 byt
|
||||
cell.type = CellType::kBraille;
|
||||
}
|
||||
|
||||
cell.content.character[1] ^= g_map_braille[x % 2][y % 4][0];
|
||||
cell.content.character[2] ^= g_map_braille[x % 2][y % 4][1];
|
||||
cell.content.character[1] ^= g_map_braille[x % 2][y % 4][0]; // NOLINT
|
||||
cell.content.character[2] ^= g_map_braille[x % 2][y % 4][1]; // NOLINT
|
||||
}
|
||||
|
||||
/// @brief Draw a line made of braille dots.
|
||||
@ -170,7 +181,7 @@ void Canvas::DrawPointToggle(int x, int y) {
|
||||
/// @param x2 the x coordinate of the second dot.
|
||||
/// @param y2 the y coordinate of the second dot.
|
||||
void Canvas::DrawPointLine(int x1, int y1, int x2, int y2) {
|
||||
DrawPointLine(x1, y1, x2, y2, [](Pixel&) {});
|
||||
DrawPointLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
|
||||
}
|
||||
|
||||
/// @brief Draw a line made of braille dots.
|
||||
@ -201,10 +212,12 @@ void Canvas::DrawPointLine(int x1,
|
||||
const int sy = y1 < y2 ? 1 : -1;
|
||||
const int length = std::max(dx, dy);
|
||||
|
||||
if (!IsIn(x1, y1) && !IsIn(x2, y2))
|
||||
if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
|
||||
return;
|
||||
if (dx + dx > width_ * height_)
|
||||
}
|
||||
if (dx + dx > width_ * height_) {
|
||||
return;
|
||||
}
|
||||
|
||||
int error = dx - dy;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
@ -226,7 +239,7 @@ void Canvas::DrawPointLine(int x1,
|
||||
/// @param y the y coordinate of the center of the circle.
|
||||
/// @param radius the radius of the circle.
|
||||
void Canvas::DrawPointCircle(int x, int y, int radius) {
|
||||
DrawPointCircle(x, y, radius, [](Pixel&) {});
|
||||
DrawPointCircle(x, y, radius, [](Pixel& /*pixel*/) {});
|
||||
}
|
||||
|
||||
/// @brief Draw a circle made of braille dots.
|
||||
@ -253,7 +266,7 @@ void Canvas::DrawPointCircle(int x, int y, int radius, const Stylizer& style) {
|
||||
/// @param y the y coordinate of the center of the circle.
|
||||
/// @param radius the radius of the circle.
|
||||
void Canvas::DrawPointCircleFilled(int x, int y, int radius) {
|
||||
DrawPointCircleFilled(x, y, radius, [](Pixel&) {});
|
||||
DrawPointCircleFilled(x, y, radius, [](Pixel& /*pixel*/) {});
|
||||
}
|
||||
|
||||
/// @brief Draw a filled circle made of braille dots.
|
||||
@ -287,7 +300,7 @@ void Canvas::DrawPointCircleFilled(int x,
|
||||
/// @param r1 the radius of the ellipse along the x axis.
|
||||
/// @param r2 the radius of the ellipse along the y axis.
|
||||
void Canvas::DrawPointEllipse(int x, int y, int r1, int r2) {
|
||||
DrawPointEllipse(x, y, r1, r2, [](Pixel&) {});
|
||||
DrawPointEllipse(x, y, r1, r2, [](Pixel& /*pixel*/) {});
|
||||
}
|
||||
|
||||
/// @brief Draw an ellipse made of braille dots.
|
||||
@ -351,7 +364,7 @@ void Canvas::DrawPointEllipse(int x1,
|
||||
/// @param r1 the radius of the ellipse along the x axis.
|
||||
/// @param r2 the radius of the ellipse along the y axis.
|
||||
void Canvas::DrawPointEllipseFilled(int x1, int y1, int r1, int r2) {
|
||||
DrawPointEllipseFilled(x1, y1, r1, r2, [](Pixel&) {});
|
||||
DrawPointEllipseFilled(x1, y1, r1, r2, [](Pixel& /*pixel*/) {});
|
||||
}
|
||||
|
||||
/// @brief Draw a filled ellipse made of braille dots.
|
||||
@ -395,11 +408,11 @@ void Canvas::DrawPointEllipseFilled(int x1,
|
||||
e2 = 2 * err;
|
||||
if (e2 >= dx) {
|
||||
x++;
|
||||
err += dx += 2 * (long)r2 * r2;
|
||||
err += dx += 2 * (long)r2 * r2; // NOLINT
|
||||
}
|
||||
if (e2 <= dy) {
|
||||
y++;
|
||||
err += dy += 2 * (long)r1 * r1;
|
||||
err += dy += 2 * (long)r1 * r1; // NOLINT
|
||||
}
|
||||
} while (x <= 0);
|
||||
|
||||
@ -415,7 +428,7 @@ void Canvas::DrawPointEllipseFilled(int x1,
|
||||
/// @param y the y coordinate of the block.
|
||||
/// @param value whether the block is filled or not.
|
||||
void Canvas::DrawBlock(int x, int y, bool value) {
|
||||
DrawBlock(x, y, value, [](Pixel&) {});
|
||||
DrawBlock(x, y, value, [](Pixel& /*pixel*/) {});
|
||||
}
|
||||
|
||||
/// @brief Draw a block.
|
||||
@ -434,18 +447,20 @@ void Canvas::DrawBlock(int x, int y, bool value, const Color& color) {
|
||||
/// @param style the style of the block.
|
||||
void Canvas::DrawBlock(int x, int y, bool value, const Stylizer& style) {
|
||||
Style(x, y, style);
|
||||
if (value)
|
||||
if (value) {
|
||||
DrawBlockOn(x, y);
|
||||
else
|
||||
} else {
|
||||
DrawBlockOff(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Draw a block.
|
||||
/// @param x the x coordinate of the block.
|
||||
/// @param y the y coordinate of the block.
|
||||
void Canvas::DrawBlockOn(int x, int y) {
|
||||
if (!IsIn(x, y))
|
||||
if (!IsIn(x, y)) {
|
||||
return;
|
||||
}
|
||||
y /= 2;
|
||||
Cell& cell = storage_[XY{x / 2, y / 2}];
|
||||
if (cell.type != CellType::kBlock) {
|
||||
@ -453,9 +468,9 @@ void Canvas::DrawBlockOn(int x, int y) {
|
||||
cell.type = CellType::kBlock;
|
||||
}
|
||||
|
||||
int bit = (x % 2) * 2 + y % 2;
|
||||
uint8_t bit = (x % 2) * 2 + y % 2;
|
||||
uint8_t value = g_map_block_inversed.at(cell.content.character);
|
||||
value |= 1 << bit;
|
||||
value |= 1U << bit;
|
||||
cell.content.character = g_map_block[value];
|
||||
}
|
||||
|
||||
@ -463,8 +478,9 @@ void Canvas::DrawBlockOn(int x, int y) {
|
||||
/// @param x the x coordinate of the block.
|
||||
/// @param y the y coordinate of the block.
|
||||
void Canvas::DrawBlockOff(int x, int y) {
|
||||
if (!IsIn(x, y))
|
||||
if (!IsIn(x, y)) {
|
||||
return;
|
||||
}
|
||||
Cell& cell = storage_[XY{x / 2, y / 4}];
|
||||
if (cell.type != CellType::kBlock) {
|
||||
cell.content.character = " ";
|
||||
@ -472,9 +488,9 @@ void Canvas::DrawBlockOff(int x, int y) {
|
||||
}
|
||||
y /= 2;
|
||||
|
||||
int bit = (y % 2) * 2 + x % 2;
|
||||
uint8_t bit = (y % 2) * 2 + x % 2;
|
||||
uint8_t value = g_map_block_inversed.at(cell.content.character);
|
||||
value &= ~(1 << bit);
|
||||
value &= ~(1U << bit);
|
||||
cell.content.character = g_map_block[value];
|
||||
}
|
||||
|
||||
@ -483,8 +499,9 @@ void Canvas::DrawBlockOff(int x, int y) {
|
||||
/// @param x the x coordinate of the block.
|
||||
/// @param y the y coordinate of the block.
|
||||
void Canvas::DrawBlockToggle(int x, int y) {
|
||||
if (!IsIn(x, y))
|
||||
if (!IsIn(x, y)) {
|
||||
return;
|
||||
}
|
||||
Cell& cell = storage_[XY{x / 2, y / 4}];
|
||||
if (cell.type != CellType::kBlock) {
|
||||
cell.content.character = " ";
|
||||
@ -492,9 +509,9 @@ void Canvas::DrawBlockToggle(int x, int y) {
|
||||
}
|
||||
y /= 2;
|
||||
|
||||
int bit = (y % 2) * 2 + x % 2;
|
||||
uint8_t bit = (y % 2) * 2 + x % 2;
|
||||
uint8_t value = g_map_block_inversed.at(cell.content.character);
|
||||
value ^= 1 << bit;
|
||||
value ^= 1U << bit;
|
||||
cell.content.character = g_map_block[value];
|
||||
}
|
||||
|
||||
@ -504,7 +521,7 @@ void Canvas::DrawBlockToggle(int x, int y) {
|
||||
/// @param x2 the x coordinate of the second point of the line.
|
||||
/// @param y2 the y coordinate of the second point of the line.
|
||||
void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2) {
|
||||
DrawBlockLine(x1, y1, x2, y2, [](Pixel&) {});
|
||||
DrawBlockLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
|
||||
}
|
||||
|
||||
/// @brief Draw a line made of block characters.
|
||||
@ -538,10 +555,12 @@ void Canvas::DrawBlockLine(int x1,
|
||||
const int sy = y1 < y2 ? 1 : -1;
|
||||
const int length = std::max(dx, dy);
|
||||
|
||||
if (!IsIn(x1, y1) && !IsIn(x2, y2))
|
||||
if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
|
||||
return;
|
||||
if (dx + dx > width_ * height_)
|
||||
}
|
||||
if (dx + dx > width_ * height_) {
|
||||
return;
|
||||
}
|
||||
|
||||
int error = dx - dy;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
@ -563,7 +582,7 @@ void Canvas::DrawBlockLine(int x1,
|
||||
/// @param y the y coordinate of the center of the circle.
|
||||
/// @param radius the radius of the circle.
|
||||
void Canvas::DrawBlockCircle(int x, int y, int radius) {
|
||||
DrawBlockCircle(x, y, radius, [](Pixel&) {});
|
||||
DrawBlockCircle(x, y, radius, nostyle);
|
||||
}
|
||||
|
||||
/// @brief Draw a circle made of block characters.
|
||||
@ -590,7 +609,7 @@ void Canvas::DrawBlockCircle(int x, int y, int radius, const Stylizer& style) {
|
||||
/// @param y the y coordinate of the center of the circle.
|
||||
/// @param radius the radius of the circle.
|
||||
void Canvas::DrawBlockCircleFilled(int x, int y, int radius) {
|
||||
DrawBlockCircleFilled(x, y, radius, [](Pixel&) {});
|
||||
DrawBlockCircleFilled(x, y, radius, nostyle);
|
||||
}
|
||||
|
||||
/// @brief Draw a filled circle made of block characters.
|
||||
@ -624,7 +643,7 @@ void Canvas::DrawBlockCircleFilled(int x,
|
||||
/// @param r1 the radius of the ellipse along the x axis.
|
||||
/// @param r2 the radius of the ellipse along the y axis.
|
||||
void Canvas::DrawBlockEllipse(int x, int y, int r1, int r2) {
|
||||
DrawBlockEllipse(x, y, r1, r2, [](Pixel&) {});
|
||||
DrawBlockEllipse(x, y, r1, r2, nostyle);
|
||||
}
|
||||
|
||||
/// @brief Draw an ellipse made of block characters.
|
||||
@ -690,7 +709,7 @@ void Canvas::DrawBlockEllipse(int x1,
|
||||
/// @param r1 the radius of the ellipse along the x axis.
|
||||
/// @param r2 the radius of the ellipse along the y axis.
|
||||
void Canvas::DrawBlockEllipseFilled(int x, int y, int r1, int r2) {
|
||||
DrawBlockEllipseFilled(x, y, r1, r2, [](Pixel&) {});
|
||||
DrawBlockEllipseFilled(x, y, r1, r2, nostyle);
|
||||
}
|
||||
|
||||
/// @brief Draw a filled ellipse made of block characters.
|
||||
@ -756,7 +775,7 @@ void Canvas::DrawBlockEllipseFilled(int x1,
|
||||
/// @param y the y coordinate of the text.
|
||||
/// @param value the text to draw.
|
||||
void Canvas::DrawText(int x, int y, const std::string& value) {
|
||||
DrawText(x, y, value, [](Pixel&) {});
|
||||
DrawText(x, y, value, nostyle);
|
||||
}
|
||||
|
||||
/// @brief Draw a piece of text.
|
||||
@ -781,8 +800,9 @@ void Canvas::DrawText(int x,
|
||||
const std::string& value,
|
||||
const Stylizer& style) {
|
||||
for (const auto& it : Utf8ToGlyphs(value)) {
|
||||
if (!IsIn(x, y))
|
||||
if (!IsIn(x, y)) {
|
||||
continue;
|
||||
}
|
||||
Cell& cell = storage_[XY{x / 2, y / 4}];
|
||||
cell.type = CellType::kText;
|
||||
cell.content.character = it;
|
||||
@ -794,15 +814,16 @@ void Canvas::DrawText(int x,
|
||||
/// @brief Modify a pixel at a given location.
|
||||
/// @param style a function that modifies the pixel.
|
||||
void Canvas::Style(int x, int y, const Stylizer& style) {
|
||||
if (IsIn(x, y))
|
||||
if (IsIn(x, y)) {
|
||||
style(storage_[XY{x / 2, y / 4}].content);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class CanvasNodeBase : public Node {
|
||||
public:
|
||||
CanvasNodeBase() {}
|
||||
CanvasNodeBase() = default;
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
const Canvas& c = canvas();
|
||||
@ -824,7 +845,7 @@ class CanvasNodeBase : public Node {
|
||||
Element canvas(ConstRef<Canvas> canvas) {
|
||||
class Impl : public CanvasNodeBase {
|
||||
public:
|
||||
Impl(ConstRef<Canvas> canvas) : canvas_(canvas) {
|
||||
explicit Impl(ConstRef<Canvas> canvas) : canvas_(std::move(canvas)) {
|
||||
requirement_.min_x = (canvas_->width() + 1) / 2;
|
||||
requirement_.min_y = (canvas_->height() + 3) / 4;
|
||||
}
|
||||
@ -869,7 +890,8 @@ Element canvas(int width, int height, std::function<void(Canvas&)> fn) {
|
||||
/// @brief Produce an element drawing a canvas.
|
||||
/// @param fn a function drawing the canvas.
|
||||
Element canvas(std::function<void(Canvas&)> fn) {
|
||||
return canvas(12, 12, std::move(fn));
|
||||
const int default_dim = 12;
|
||||
return canvas(default_dim, default_dim, std::move(fn));
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
@ -29,8 +29,8 @@ class ClearUnder : public NodeDecorator {
|
||||
// combinaison with dbox.
|
||||
/// @see ftxui::dbox
|
||||
/// @ingroup dom
|
||||
Element clear_under(Element child) {
|
||||
return std::make_shared<ClearUnder>(std::move(child));
|
||||
Element clear_under(Element element) {
|
||||
return std::make_shared<ClearUnder>(std::move(element));
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
@ -12,7 +12,7 @@ namespace ftxui {
|
||||
|
||||
class DBox : public Node {
|
||||
public:
|
||||
DBox(Elements children) : Node(std::move(children)) {}
|
||||
explicit DBox(Elements children) : Node(std::move(children)) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min_x = 0;
|
||||
@ -38,8 +38,9 @@ class DBox : public Node {
|
||||
void SetBox(Box box) override {
|
||||
Node::SetBox(box);
|
||||
|
||||
for (auto& child : children_)
|
||||
for (auto& child : children_) {
|
||||
child->SetBox(box);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -67,7 +67,7 @@ void function_not_flex(Requirement& r) {
|
||||
|
||||
class Flex : public Node {
|
||||
public:
|
||||
Flex(FlexFunction f) : f_(f) {}
|
||||
explicit Flex(FlexFunction f) : f_(f) {}
|
||||
Flex(FlexFunction f, Element child) : Node(unpack(std::move(child))), f_(f) {}
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min_x = 0;
|
||||
@ -80,8 +80,9 @@ class Flex : public Node {
|
||||
}
|
||||
|
||||
void SetBox(Box box) override {
|
||||
if (children_.empty())
|
||||
if (children_.empty()) {
|
||||
return;
|
||||
}
|
||||
children_[0]->SetBox(box);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for min, max
|
||||
#include <cstddef> // for size_t
|
||||
#include <memory> // for __shared_ptr_access, shared_ptr, allocator_traits<>::value_type, make_shared
|
||||
#include <utility> // for move, swap
|
||||
#include <vector> // for vector
|
||||
@ -56,11 +56,12 @@ class Flexbox : public Node {
|
||||
requirement_.flex_grow_x = 1;
|
||||
requirement_.flex_grow_y = 0;
|
||||
|
||||
if (IsColumnOriented())
|
||||
if (IsColumnOriented()) {
|
||||
std::swap(requirement_.flex_grow_x, requirement_.flex_grow_y);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsColumnOriented() {
|
||||
bool IsColumnOriented() const {
|
||||
return config_.direction == FlexboxConfig::Direction::Column ||
|
||||
config_.direction == FlexboxConfig::Direction::ColumnInversed;
|
||||
}
|
||||
@ -84,20 +85,21 @@ class Flexbox : public Node {
|
||||
}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
for (auto& child : children_)
|
||||
for (auto& child : children_) {
|
||||
child->ComputeRequirement();
|
||||
}
|
||||
flexbox_helper::Global global;
|
||||
global.config = config_normalized_;
|
||||
if (IsColumnOriented()) {
|
||||
global.size_x = 100000;
|
||||
global.size_x = 100000; // NOLINT
|
||||
global.size_y = asked_;
|
||||
} else {
|
||||
global.size_x = asked_;
|
||||
global.size_y = 100000;
|
||||
global.size_y = 100000; // NOLINT
|
||||
}
|
||||
Layout(global, true);
|
||||
|
||||
if (global.blocks.size() == 0) {
|
||||
if (global.blocks.empty()) {
|
||||
requirement_.min_x = 0;
|
||||
requirement_.min_y = 0;
|
||||
return;
|
||||
@ -150,18 +152,19 @@ class Flexbox : public Node {
|
||||
}
|
||||
|
||||
void Check(Status* status) override {
|
||||
for (auto& child : children_)
|
||||
for (auto& child : children_) {
|
||||
child->Check(status);
|
||||
}
|
||||
|
||||
if (status->iteration == 0) {
|
||||
asked_ = 6000;
|
||||
asked_ = 6000; // NOLINT
|
||||
need_iteration_ = true;
|
||||
}
|
||||
|
||||
status->need_iteration |= need_iteration_;
|
||||
}
|
||||
|
||||
int asked_ = 6000;
|
||||
int asked_ = 6000; // NOLINT
|
||||
bool need_iteration_ = true;
|
||||
const FlexboxConfig config_;
|
||||
const FlexboxConfig config_normalized_;
|
||||
@ -190,7 +193,7 @@ class Flexbox : public Node {
|
||||
// )
|
||||
/// ```
|
||||
Element flexbox(Elements children, FlexboxConfig config) {
|
||||
return std::make_shared<Flexbox>(std::move(children), std::move(config));
|
||||
return std::make_shared<Flexbox>(std::move(children), config);
|
||||
}
|
||||
|
||||
/// @brief A container displaying elements in rows from left to right. When
|
||||
|
@ -1,14 +1,13 @@
|
||||
#include "ftxui/dom/flexbox_helper.hpp"
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for min, max
|
||||
#include <cstddef> // for size_t
|
||||
#include <memory> // for allocator_traits<>::value_type
|
||||
#include <utility> // for swap, move
|
||||
|
||||
#include "ftxui/dom/box_helper.hpp" // for Element, Compute
|
||||
|
||||
namespace ftxui {
|
||||
namespace flexbox_helper {
|
||||
namespace ftxui::flexbox_helper {
|
||||
|
||||
namespace {
|
||||
void SymmetryXY(FlexboxConfig& c) {
|
||||
@ -92,15 +91,17 @@ void SetX(Global& global, std::vector<Line> lines) {
|
||||
box_helper::Element element;
|
||||
element.min_size = block->min_size_x;
|
||||
element.flex_grow =
|
||||
block->flex_grow_x || global.config.justify_content ==
|
||||
FlexboxConfig::JustifyContent::Stretch;
|
||||
block->flex_grow_x != 0 || global.config.justify_content ==
|
||||
FlexboxConfig::JustifyContent::Stretch
|
||||
? 1
|
||||
: 0;
|
||||
element.flex_shrink = block->flex_shrink_x;
|
||||
elements.push_back(element);
|
||||
}
|
||||
|
||||
box_helper::Compute(
|
||||
&elements,
|
||||
global.size_x - global.config.gap_x * (line.blocks.size() - 1));
|
||||
global.size_x - global.config.gap_x * (int(line.blocks.size()) - 1));
|
||||
|
||||
int x = 0;
|
||||
for (size_t i = 0; i < line.blocks.size(); ++i) {
|
||||
@ -112,6 +113,7 @@ void SetX(Global& global, std::vector<Line> lines) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void SetY(Global& g, std::vector<Line> lines) {
|
||||
std::vector<box_helper::Element> elements;
|
||||
for (auto& line : lines) {
|
||||
@ -127,7 +129,7 @@ void SetY(Global& g, std::vector<Line> lines) {
|
||||
}
|
||||
|
||||
// box_helper::Compute(&elements, g.size_y);
|
||||
box_helper::Compute(&elements, 10000);
|
||||
box_helper::Compute(&elements, 10000); // NOLINT
|
||||
|
||||
// [Align-content]
|
||||
std::vector<int> ys(elements.size());
|
||||
@ -140,48 +142,57 @@ void SetY(Global& g, std::vector<Line> lines) {
|
||||
int remaining_space = std::max(0, g.size_y - y);
|
||||
switch (g.config.align_content) {
|
||||
case FlexboxConfig::AlignContent::FlexStart: {
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
|
||||
case FlexboxConfig::AlignContent::FlexEnd: {
|
||||
for (size_t i = 0; i < ys.size(); ++i)
|
||||
for (size_t i = 0; i < ys.size(); ++i) { // NOLINT
|
||||
ys[i] += remaining_space;
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FlexboxConfig::AlignContent::Center: {
|
||||
for (size_t i = 0; i < ys.size(); ++i)
|
||||
for (size_t i = 0; i < ys.size(); ++i) { // NOLINT
|
||||
ys[i] += remaining_space / 2;
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FlexboxConfig::AlignContent::Stretch: {
|
||||
for (int i = ys.size() - 1; i >= 0; --i) {
|
||||
for (int i = ys.size() - 1; i >= 0; --i) { // NOLINT
|
||||
int shifted = remaining_space * (i + 0) / (i + 1);
|
||||
ys[i] += shifted;
|
||||
int consumed = remaining_space - shifted;
|
||||
elements[i].size += consumed;
|
||||
remaining_space -= consumed;
|
||||
}
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
|
||||
case FlexboxConfig::AlignContent::SpaceBetween: {
|
||||
for (int i = ys.size() - 1; i >= 1; --i) {
|
||||
for (int i = ys.size() - 1; i >= 1; --i) { // NOLINT
|
||||
ys[i] += remaining_space;
|
||||
remaining_space = remaining_space * (i - 1) / i;
|
||||
}
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
|
||||
case FlexboxConfig::AlignContent::SpaceAround: {
|
||||
for (int i = ys.size() - 1; i >= 0; --i) {
|
||||
for (int i = 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);
|
||||
}
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
|
||||
case FlexboxConfig::AlignContent::SpaceEvenly: {
|
||||
for (int i = ys.size() - 1; i >= 0; --i) {
|
||||
for (int i = ys.size() - 1; i >= 0; --i) { // NOLINT
|
||||
ys[i] += remaining_space * (i + 1) / (i + 2);
|
||||
remaining_space = remaining_space * (i + 1) / (i + 2);
|
||||
}
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// [Align items]
|
||||
@ -189,7 +200,7 @@ void SetY(Global& g, std::vector<Line> lines) {
|
||||
auto& element = elements[i];
|
||||
for (auto* block : lines[i].blocks) {
|
||||
bool stretch =
|
||||
block->flex_grow_y ||
|
||||
block->flex_grow_y != 0 ||
|
||||
g.config.align_content == FlexboxConfig::AlignContent::Stretch;
|
||||
int size =
|
||||
stretch ? element.size : std::min(element.size, block->min_size_y);
|
||||
@ -197,22 +208,26 @@ void SetY(Global& g, std::vector<Line> lines) {
|
||||
case FlexboxConfig::AlignItems::FlexStart: {
|
||||
block->y = ys[i];
|
||||
block->dim_y = size;
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
|
||||
case FlexboxConfig::AlignItems::Center: {
|
||||
block->y = ys[i] + (element.size - size) / 2;
|
||||
block->dim_y = size;
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
|
||||
case FlexboxConfig::AlignItems::FlexEnd: {
|
||||
block->y = ys[i] + element.size - size;
|
||||
block->dim_y = size;
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
|
||||
case FlexboxConfig::AlignItems::Stretch: {
|
||||
block->y = ys[i];
|
||||
block->dim_y = element.size;
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -228,63 +243,74 @@ void JustifyContent(Global& g, std::vector<Line> lines) {
|
||||
break;
|
||||
|
||||
case FlexboxConfig::JustifyContent::FlexEnd: {
|
||||
for (auto* block : line.blocks)
|
||||
for (auto* block : line.blocks) {
|
||||
block->x += remaining_space;
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FlexboxConfig::JustifyContent::Center: {
|
||||
for (auto* block : line.blocks)
|
||||
for (auto* block : line.blocks) {
|
||||
block->x += remaining_space / 2;
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FlexboxConfig::JustifyContent::SpaceBetween: {
|
||||
for (int i = line.blocks.size() - 1; i >= 1; --i) {
|
||||
for (int i = (int)line.blocks.size() - 1; i >= 1; --i) {
|
||||
line.blocks[i]->x += remaining_space;
|
||||
remaining_space = remaining_space * (i - 1) / i;
|
||||
}
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
|
||||
case FlexboxConfig::JustifyContent::SpaceAround: {
|
||||
for (int i = line.blocks.size() - 1; i >= 0; --i) {
|
||||
for (int i = (int)line.blocks.size() - 1; i >= 0; --i) {
|
||||
line.blocks[i]->x += remaining_space * (2 * i + 1) / (2 * i + 2);
|
||||
remaining_space = remaining_space * (2 * i) / (2 * i + 2);
|
||||
}
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
|
||||
case FlexboxConfig::JustifyContent::SpaceEvenly: {
|
||||
for (int i = line.blocks.size() - 1; i >= 0; --i) {
|
||||
for (int i = (int)line.blocks.size() - 1; i >= 0; --i) {
|
||||
line.blocks[i]->x += remaining_space * (i + 1) / (i + 2);
|
||||
remaining_space = remaining_space * (i + 1) / (i + 2);
|
||||
}
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Compute(Global& global) {
|
||||
if (global.config.direction == FlexboxConfig::Direction::Column ||
|
||||
global.config.direction == FlexboxConfig::Direction::ColumnInversed) {
|
||||
SymmetryXY(global);
|
||||
Compute(global);
|
||||
SymmetryXY(global);
|
||||
return;
|
||||
}
|
||||
namespace {
|
||||
|
||||
void Compute1(Global& global);
|
||||
void Compute2(Global& global);
|
||||
void Compute3(Global& global);
|
||||
|
||||
void Compute1(Global& global) {
|
||||
if (global.config.direction == FlexboxConfig::Direction::RowInversed) {
|
||||
SymmetryX(global);
|
||||
Compute(global);
|
||||
Compute2(global);
|
||||
SymmetryX(global);
|
||||
return;
|
||||
}
|
||||
Compute2(global);
|
||||
}
|
||||
|
||||
void Compute2(Global& global) {
|
||||
if (global.config.wrap == FlexboxConfig::Wrap::WrapInversed) {
|
||||
SymmetryY(global);
|
||||
Compute(global);
|
||||
Compute3(global);
|
||||
SymmetryY(global);
|
||||
return;
|
||||
}
|
||||
Compute3(global);
|
||||
}
|
||||
|
||||
void Compute3(Global& global) {
|
||||
// Step 1: Lay out every elements into rows:
|
||||
std::vector<Line> lines;
|
||||
{
|
||||
@ -295,18 +321,20 @@ void Compute(Global& global) {
|
||||
// No? Then we need to start a new one:
|
||||
if (x + block.min_size_x > global.size_x) {
|
||||
x = 0;
|
||||
if (!line.blocks.empty())
|
||||
if (!line.blocks.empty()) {
|
||||
lines.push_back(std::move(line));
|
||||
}
|
||||
line = Line();
|
||||
}
|
||||
|
||||
block.line = lines.size();
|
||||
block.line_position = line.blocks.size();
|
||||
block.line = (int)lines.size();
|
||||
block.line_position = (int)line.blocks.size();
|
||||
line.blocks.push_back(&block);
|
||||
x += block.min_size_x + global.config.gap_x;
|
||||
}
|
||||
if (!line.blocks.empty())
|
||||
if (!line.blocks.empty()) {
|
||||
lines.push_back(std::move(line));
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Set positions on the X axis.
|
||||
@ -317,8 +345,21 @@ void Compute(Global& global) {
|
||||
SetY(global, lines);
|
||||
}
|
||||
|
||||
} // namespace flexbox_helper
|
||||
} // namespace ftxui
|
||||
} // namespace
|
||||
|
||||
void Compute(Global& global) {
|
||||
if (global.config.direction == FlexboxConfig::Direction::Column ||
|
||||
global.config.direction == FlexboxConfig::Direction::ColumnInversed) {
|
||||
SymmetryXY(global);
|
||||
Compute1(global);
|
||||
SymmetryXY(global);
|
||||
return;
|
||||
}
|
||||
Compute1(global);
|
||||
}
|
||||
|
||||
|
||||
} // namespace ftxui::flexbox_helper
|
||||
|
||||
// Copyright 2021 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
|
@ -29,17 +29,17 @@ Decorator focusPositionRelative(float x, float y) {
|
||||
class Impl : public NodeDecorator {
|
||||
public:
|
||||
Impl(Element child, float x, float y)
|
||||
: NodeDecorator(child), x_(x), y_(y) {}
|
||||
: NodeDecorator(std::move(child)), x_(x), y_(y) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
NodeDecorator::ComputeRequirement();
|
||||
requirement_.selection = Requirement::Selection::NORMAL;
|
||||
|
||||
Box& box = requirement_.selected_box;
|
||||
box.x_min = requirement_.min_x * x_;
|
||||
box.y_min = requirement_.min_y * y_;
|
||||
box.x_max = requirement_.min_x * x_;
|
||||
box.y_max = requirement_.min_y * y_;
|
||||
box.x_min = (int)((float)requirement_.min_x * x_);
|
||||
box.y_min = (int)((float)requirement_.min_y * y_);
|
||||
box.x_max = (int)((float)requirement_.min_x * x_);
|
||||
box.y_max = (int)((float)requirement_.min_y * y_);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -67,8 +67,8 @@ Decorator focusPositionRelative(float x, float y) {
|
||||
Decorator focusPosition(int x, int y) {
|
||||
class Impl : public NodeDecorator {
|
||||
public:
|
||||
Impl(Element child, float x, float y)
|
||||
: NodeDecorator(child), x_(x), y_(y) {}
|
||||
Impl(Element child, int x, int y)
|
||||
: NodeDecorator(std::move(child)), x_(x), y_(y) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
NodeDecorator::ComputeRequirement();
|
||||
|
@ -16,7 +16,7 @@ namespace ftxui {
|
||||
|
||||
class Select : public Node {
|
||||
public:
|
||||
Select(Elements children) : Node(std::move(children)) {}
|
||||
explicit Select(Elements children) : Node(std::move(children)) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
Node::ComputeRequirement();
|
||||
|
@ -1,17 +1,16 @@
|
||||
#include <iostream>
|
||||
#include <algorithm> // for max, min
|
||||
#include <memory> // for allocator, make_shared
|
||||
#include <string> // for string
|
||||
#include <memory> // for allocator, make_shared
|
||||
#include <string> // for string
|
||||
|
||||
#include "ftxui/dom/elements.hpp" // for Element, gauge
|
||||
#include "ftxui/dom/node.hpp" // for Node
|
||||
#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/requirement.hpp" // for Requirement
|
||||
#include "ftxui/screen/box.hpp" // for Box
|
||||
#include "ftxui/screen/screen.hpp" // for Screen
|
||||
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
static std::string charset_horizontal[11] = {
|
||||
// NOLINTNEXTLINE
|
||||
static const 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.
|
||||
@ -23,7 +22,8 @@ static std::string charset_horizontal[11] = {
|
||||
// int(9 * (limit - limit_int) = 9
|
||||
"█"};
|
||||
|
||||
static std::string charset_vertical[10] = {
|
||||
// NOLINTNEXTLINE
|
||||
static const std::string charset_vertical[10] = {
|
||||
"█",
|
||||
"▇",
|
||||
"▆",
|
||||
@ -43,10 +43,12 @@ class Gauge : public Node {
|
||||
Gauge(float progress, GaugeDirection direction)
|
||||
: progress_(progress), direction_(direction) {
|
||||
// This handle NAN correctly:
|
||||
if (!(progress_ > 0.f))
|
||||
progress_ = 0.f;
|
||||
if (!(progress_ < 1.f))
|
||||
progress_ = 1.f;
|
||||
if (!(progress_ > 0.F)) {
|
||||
progress_ = 0.F;
|
||||
}
|
||||
if (!(progress_ < 1.F)) {
|
||||
progress_ = 1.F;
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
@ -89,49 +91,61 @@ class Gauge : public Node {
|
||||
|
||||
void RenderHorizontal(Screen& screen, bool invert) {
|
||||
int y = box_.y_min;
|
||||
if (y > box_.y_max)
|
||||
if (y > box_.y_max) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
float progress = invert ? 1.F - progress_ : progress_;
|
||||
float limit =
|
||||
(float)box_.x_min + progress * (float)(box_.x_max - box_.x_min + 1);
|
||||
int limit_int = (int)limit;
|
||||
int x = box_.x_min;
|
||||
while (x < limit_int)
|
||||
screen.at(x++, y) = charset_horizontal[9];
|
||||
while (x < limit_int) {
|
||||
screen.at(x++, y) = charset_horizontal[9]; // NOLINT
|
||||
}
|
||||
// NOLINTNEXTLINE
|
||||
screen.at(x++, y) = charset_horizontal[int(9 * (limit - limit_int))];
|
||||
while (x <= box_.x_max)
|
||||
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++)
|
||||
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;
|
||||
if (x > box_.x_max)
|
||||
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;
|
||||
float progress = invert ? progress_ : 1.F - progress_;
|
||||
float limit =
|
||||
(float)box_.y_min + progress * (float)(box_.y_max - box_.y_min + 1);
|
||||
int limit_int = (int)limit;
|
||||
int y = box_.y_min;
|
||||
while (y < limit_int)
|
||||
screen.at(x, y++) = charset_vertical[8];
|
||||
while (y < limit_int) {
|
||||
screen.at(x, y++) = charset_vertical[8]; // NOLINT
|
||||
}
|
||||
// NOLINTNEXTLINE
|
||||
screen.at(x, y++) = charset_vertical[int(8 * (limit - limit_int))];
|
||||
while (y <= box_.y_max)
|
||||
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++)
|
||||
for (int y = box_.y_min; y <= box_.y_max; y++) {
|
||||
screen.PixelAt(x, y).inverted ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <functional> // for function
|
||||
#include <memory> // for allocator, make_shared
|
||||
#include <string> // for string
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ftxui/dom/elements.hpp" // for GraphFunction, Element, graph
|
||||
@ -11,6 +12,7 @@
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
static std::string charset[] =
|
||||
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
|
||||
// Microsoft's terminals often use fonts not handling the 8 unicode
|
||||
@ -22,7 +24,8 @@ static std::string charset[] =
|
||||
|
||||
class Graph : public Node {
|
||||
public:
|
||||
Graph(GraphFunction graph_function) : graph_function_(graph_function) {}
|
||||
explicit Graph(GraphFunction graph_function)
|
||||
: graph_function_(std::move(graph_function)) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.flex_grow_x = 1;
|
||||
@ -43,9 +46,9 @@ class Graph : public Node {
|
||||
int height_2 = 2 * box_.y_max - data[i++];
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
int yy = 2 * y;
|
||||
int i_1 = yy < height_1 ? 0 : yy == height_1 ? 3 : 6;
|
||||
int i_2 = yy < height_2 ? 0 : yy == height_2 ? 1 : 2;
|
||||
screen.at(x, y) = charset[i_1 + i_2];
|
||||
int i_1 = yy < height_1 ? 0 : yy == height_1 ? 3 : 6; // NOLINT
|
||||
int i_2 = yy < height_2 ? 0 : yy == height_2 ? 1 : 2; // NOLINT
|
||||
screen.at(x, y) = charset[i_1 + i_2]; // NOLINT
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -57,7 +60,7 @@ class Graph : public Node {
|
||||
/// @brief Draw a graph using a GraphFunction.
|
||||
/// @param graph_function the function to be called to get the data.
|
||||
Element graph(GraphFunction graph_function) {
|
||||
return std::make_shared<Graph>(graph_function);
|
||||
return std::make_shared<Graph>(std::move(graph_function));
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for max, min
|
||||
#include <cstddef> // for size_t
|
||||
#include <memory> // for __shared_ptr_access, shared_ptr, make_shared, allocator_traits<>::value_type
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector, __alloc_traits<>::value_type
|
||||
@ -15,10 +15,11 @@ class Screen;
|
||||
|
||||
class GridBox : public Node {
|
||||
public:
|
||||
GridBox(std::vector<Elements> lines) : Node(), lines_(std::move(lines)) {
|
||||
y_size = lines_.size();
|
||||
for (const auto& line : lines_)
|
||||
explicit GridBox(std::vector<Elements> lines) : lines_(std::move(lines)) {
|
||||
y_size = (int)lines_.size();
|
||||
for (const auto& line : lines_) {
|
||||
x_size = std::max(x_size, (int)line.size());
|
||||
}
|
||||
for (auto& line : lines_) {
|
||||
while (line.size() < (size_t)x_size) {
|
||||
line.push_back(filler());
|
||||
@ -39,8 +40,9 @@ class GridBox : public Node {
|
||||
cell->ComputeRequirement();
|
||||
|
||||
// Determine focus based on the focused child.
|
||||
if (requirement_.selection >= cell->requirement().selection)
|
||||
if (requirement_.selection >= cell->requirement().selection) {
|
||||
continue;
|
||||
}
|
||||
requirement_.selection = cell->requirement().selection;
|
||||
requirement_.selected_box = cell->requirement().selected_box;
|
||||
requirement_.selected_box.x_min += requirement_.min_x;
|
||||
@ -51,16 +53,18 @@ class GridBox : public Node {
|
||||
// Work on the x-axis.
|
||||
for (int x = 0; x < x_size; ++x) {
|
||||
int min_x = 0;
|
||||
for (int y = 0; y < y_size; ++y)
|
||||
for (int y = 0; y < y_size; ++y) {
|
||||
min_x = std::max(min_x, lines_[y][x]->requirement().min_x);
|
||||
}
|
||||
requirement_.min_x += min_x;
|
||||
}
|
||||
|
||||
// Work on the y-axis.
|
||||
for (int y = 0; y < y_size; ++y) {
|
||||
int min_y = 0;
|
||||
for (int x = 0; x < x_size; ++x)
|
||||
for (int x = 0; x < x_size; ++x) {
|
||||
min_y = std::max(min_y, lines_[y][x]->requirement().min_y);
|
||||
}
|
||||
requirement_.min_y += min_y;
|
||||
}
|
||||
}
|
||||
@ -70,8 +74,8 @@ class GridBox : public Node {
|
||||
|
||||
box_helper::Element init;
|
||||
init.min_size = 0;
|
||||
init.flex_grow = 1024;
|
||||
init.flex_shrink = 1024;
|
||||
init.flex_grow = 1024; // NOLINT
|
||||
init.flex_shrink = 1024; // NOLINT
|
||||
std::vector<box_helper::Element> elements_x(x_size, init);
|
||||
std::vector<box_helper::Element> elements_y(y_size, init);
|
||||
|
||||
@ -115,8 +119,9 @@ class GridBox : public Node {
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
for (auto& line : lines_) {
|
||||
for (auto& cell : line)
|
||||
for (auto& cell : line) {
|
||||
cell->Render(screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for max
|
||||
#include <cstddef> // for size_t
|
||||
#include <memory> // for __shared_ptr_access, shared_ptr, make_shared, allocator_traits<>::value_type
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector, __alloc_traits<>::value_type
|
||||
@ -14,7 +14,7 @@ namespace ftxui {
|
||||
|
||||
class HBox : public Node {
|
||||
public:
|
||||
HBox(Elements children) : Node(std::move(children)) {}
|
||||
explicit HBox(Elements children) : Node(std::move(children)) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min_x = 0;
|
||||
|
@ -5,15 +5,16 @@
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Node::Node() {}
|
||||
Node::Node() = default;
|
||||
Node::Node(Elements children) : children_(std::move(children)) {}
|
||||
Node::~Node() {}
|
||||
Node::~Node() = default;
|
||||
|
||||
/// @brief Compute how much space an elements needs.
|
||||
/// @ingroup dom
|
||||
void Node::ComputeRequirement() {
|
||||
for (auto& child : children_)
|
||||
for (auto& child : children_) {
|
||||
child->ComputeRequirement();
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Assign a position and a dimension to an element for drawing.
|
||||
@ -25,13 +26,15 @@ void Node::SetBox(Box box) {
|
||||
/// @brief Display an element on a ftxui::Screen.
|
||||
/// @ingroup dom
|
||||
void Node::Render(Screen& screen) {
|
||||
for (auto& child : children_)
|
||||
for (auto& child : children_) {
|
||||
child->Render(screen);
|
||||
}
|
||||
}
|
||||
|
||||
void Node::Check(Status* status) {
|
||||
for (auto& child : children_)
|
||||
for (auto& child : children_) {
|
||||
child->Check(status);
|
||||
}
|
||||
status->need_iteration |= (status->iteration == 0);
|
||||
}
|
||||
|
||||
@ -52,7 +55,8 @@ void Render(Screen& screen, Node* node) {
|
||||
|
||||
Node::Status status;
|
||||
node->Check(&status);
|
||||
while (status.need_iteration && status.iteration < 20) {
|
||||
const int max_iterations = 20;
|
||||
while (status.need_iteration && status.iteration < max_iterations) {
|
||||
// Step 1: Find what dimension this elements wants to be.
|
||||
node->ComputeRequirement();
|
||||
|
||||
|
@ -8,12 +8,13 @@
|
||||
namespace ftxui {
|
||||
|
||||
namespace {
|
||||
Elements Split(std::string the_text) {
|
||||
Elements Split(const std::string& the_text) {
|
||||
Elements output;
|
||||
std::stringstream ss(the_text);
|
||||
std::string word;
|
||||
while (std::getline(ss, word, ' '))
|
||||
while (std::getline(ss, word, ' ')) {
|
||||
output.push_back(text(word));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
} // namespace
|
||||
@ -21,37 +22,37 @@ Elements Split(std::string the_text) {
|
||||
/// @brief Return an element drawing the paragraph on multiple lines.
|
||||
/// @ingroup dom
|
||||
/// @see flexbox.
|
||||
Element paragraph(std::string the_text) {
|
||||
return paragraphAlignLeft(std::move(the_text));
|
||||
Element paragraph(const std::string& the_text) {
|
||||
return paragraphAlignLeft(the_text);
|
||||
}
|
||||
|
||||
/// @brief Return an element drawing the paragraph on multiple lines, aligned on
|
||||
/// the left.
|
||||
/// @ingroup dom
|
||||
/// @see flexbox.
|
||||
Element paragraphAlignLeft(std::string the_text) {
|
||||
Element paragraphAlignLeft(const std::string& the_text) {
|
||||
static const auto config = FlexboxConfig().SetGap(1, 0);
|
||||
return flexbox(Split(std::move(the_text)), config);
|
||||
return flexbox(Split(the_text), config);
|
||||
}
|
||||
|
||||
/// @brief Return an element drawing the paragraph on multiple lines, aligned on
|
||||
/// the right.
|
||||
/// @ingroup dom
|
||||
/// @see flexbox.
|
||||
Element paragraphAlignRight(std::string the_text) {
|
||||
Element paragraphAlignRight(const std::string& the_text) {
|
||||
static const auto config =
|
||||
FlexboxConfig().SetGap(1, 0).Set(FlexboxConfig::JustifyContent::FlexEnd);
|
||||
return flexbox(Split(std::move(the_text)), config);
|
||||
return flexbox(Split(the_text), config);
|
||||
}
|
||||
|
||||
/// @brief Return an element drawing the paragraph on multiple lines, aligned on
|
||||
/// the center.
|
||||
/// @ingroup dom
|
||||
/// @see flexbox.
|
||||
Element paragraphAlignCenter(std::string the_text) {
|
||||
Element paragraphAlignCenter(const std::string& the_text) {
|
||||
static const auto config =
|
||||
FlexboxConfig().SetGap(1, 0).Set(FlexboxConfig::JustifyContent::Center);
|
||||
return flexbox(Split(std::move(the_text)), config);
|
||||
return flexbox(Split(the_text), config);
|
||||
}
|
||||
|
||||
/// @brief Return an element drawing the paragraph on multiple lines, aligned
|
||||
@ -59,10 +60,10 @@ Element paragraphAlignCenter(std::string the_text) {
|
||||
/// the center.
|
||||
/// @ingroup dom
|
||||
/// @see flexbox.
|
||||
Element paragraphAlignJustify(std::string the_text) {
|
||||
Element paragraphAlignJustify(const std::string& the_text) {
|
||||
static const auto config = FlexboxConfig().SetGap(1, 0).Set(
|
||||
FlexboxConfig::JustifyContent::SpaceBetween);
|
||||
Elements words = Split(std::move(the_text));
|
||||
Elements words = Split(the_text);
|
||||
words.push_back(text("") | xflex);
|
||||
return flexbox(std::move(words), config);
|
||||
}
|
||||
|
@ -21,27 +21,29 @@ Element vscroll_indicator(Element child) {
|
||||
using NodeDecorator::NodeDecorator;
|
||||
|
||||
void ComputeRequirement() override {
|
||||
Node::ComputeRequirement();
|
||||
NodeDecorator::ComputeRequirement();
|
||||
requirement_ = children_[0]->requirement();
|
||||
requirement_.min_x++;
|
||||
}
|
||||
|
||||
void SetBox(Box box) override {
|
||||
Node::SetBox(box);
|
||||
if (box_.x_min > box_.x_max)
|
||||
NodeDecorator::SetBox(box);
|
||||
if (box_.x_min > box_.x_max) {
|
||||
box_.x_max--;
|
||||
}
|
||||
children_[0]->SetBox(box);
|
||||
}
|
||||
|
||||
void Render(Screen& screen) final {
|
||||
Node::Render(screen);
|
||||
NodeDecorator::Render(screen);
|
||||
|
||||
const Box& stencil = screen.stencil;
|
||||
|
||||
int size_inner = box_.y_max - box_.y_min;
|
||||
int size_outter = stencil.y_max - stencil.y_min + 1;
|
||||
if (size_outter >= size_inner)
|
||||
if (size_outter >= size_inner) {
|
||||
return;
|
||||
}
|
||||
|
||||
int size = 2 * size_outter * size_outter / size_inner;
|
||||
size = std::max(size, 1);
|
||||
@ -56,7 +58,7 @@ Element vscroll_indicator(Element child) {
|
||||
bool up = (start_y <= y_up) && (y_up <= start_y + size);
|
||||
bool down = (start_y <= y_down) && (y_down <= start_y + size);
|
||||
|
||||
const char* c = up ? (down ? "┃" : "╹") : (down ? "╻" : " ");
|
||||
const char* c = up ? (down ? "┃" : "╹") : (down ? "╻" : " "); // NOLINT
|
||||
screen.PixelAt(x, y) = Pixel();
|
||||
screen.PixelAt(x, y).character = c;
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include <memory> // for make_shared, allocator
|
||||
#include <string> // for string
|
||||
#include <array> // for array, array<>::value_type
|
||||
#include <memory> // for make_shared, allocator
|
||||
#include <string> // for basic_string, string
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/dom/elements.hpp" // for Element, BorderStyle, LIGHT, separator, DOUBLE, EMPTY, HEAVY, separatorCharacter, separatorDouble, separatorEmpty, separatorHSelector, separatorHeavy, separatorLight, separatorStyled, separatorVSelector
|
||||
#include "ftxui/dom/node.hpp" // for Node
|
||||
@ -10,19 +12,25 @@
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
namespace {
|
||||
using ftxui::Screen;
|
||||
|
||||
const std::string charset[][2] = {
|
||||
{"│", "─"}, //
|
||||
{"┃", "━"}, //
|
||||
{"║", "═"}, //
|
||||
{"│", "─"}, //
|
||||
{" ", " "}, //
|
||||
using Charset = std::array<std::string, 2>; // NOLINT
|
||||
using Charsets = std::array<Charset, 5>; // NOLINT
|
||||
const Charsets charsets = // NOLINT
|
||||
{
|
||||
Charset{"│", "─"}, //
|
||||
Charset{"┃", "━"}, //
|
||||
Charset{"║", "═"}, //
|
||||
Charset{"│", "─"}, //
|
||||
Charset{" ", " "}, //
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class Separator : public Node {
|
||||
public:
|
||||
Separator(std::string value) : value_(value) {}
|
||||
explicit Separator(std::string value) : value_(std::move(value)) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min_x = 1;
|
||||
@ -44,7 +52,7 @@ class Separator : public Node {
|
||||
|
||||
class SeparatorAuto : public Node {
|
||||
public:
|
||||
SeparatorAuto(BorderStyle style) : style_(style) {}
|
||||
explicit SeparatorAuto(BorderStyle style) : style_(style) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min_x = 1;
|
||||
@ -55,7 +63,7 @@ class SeparatorAuto : public Node {
|
||||
bool is_column = (box_.x_max == box_.x_min);
|
||||
bool is_line = (box_.y_min == box_.y_max);
|
||||
|
||||
const std::string c = charset[style_][is_line && !is_column];
|
||||
const std::string c = charsets[style_][int(is_line && !is_column)];
|
||||
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
@ -71,7 +79,8 @@ class SeparatorAuto : public Node {
|
||||
|
||||
class SeparatorWithPixel : public SeparatorAuto {
|
||||
public:
|
||||
SeparatorWithPixel(Pixel pixel) : SeparatorAuto(LIGHT), pixel_(pixel) {
|
||||
explicit SeparatorWithPixel(Pixel pixel)
|
||||
: SeparatorAuto(LIGHT), pixel_(std::move(pixel)) {
|
||||
pixel_.automerge = true;
|
||||
}
|
||||
void Render(Screen& screen) override {
|
||||
@ -337,7 +346,7 @@ Element separatorEmpty() {
|
||||
/// down
|
||||
/// ```
|
||||
Element separatorCharacter(std::string value) {
|
||||
return std::make_shared<Separator>(value);
|
||||
return std::make_shared<Separator>(std::move(value));
|
||||
}
|
||||
|
||||
/// @brief Draw a separator in between two element filled with a given pixel.
|
||||
@ -367,7 +376,7 @@ Element separatorCharacter(std::string value) {
|
||||
/// Down
|
||||
/// ```
|
||||
Element separator(Pixel pixel) {
|
||||
return std::make_shared<SeparatorWithPixel>(pixel);
|
||||
return std::make_shared<SeparatorWithPixel>(std::move(pixel));
|
||||
}
|
||||
|
||||
/// @brief Draw an horizontal bar, with the area in between left/right colored
|
||||
@ -384,27 +393,28 @@ Element separator(Pixel pixel) {
|
||||
/// ```
|
||||
Element separatorHSelector(float left,
|
||||
float right,
|
||||
Color selected_color,
|
||||
Color unselected_color) {
|
||||
Color unselected_color,
|
||||
Color selected_color) {
|
||||
class Impl : public Node {
|
||||
public:
|
||||
Impl(float left, float right, Color selected_color, Color unselected_color)
|
||||
: left_(left),
|
||||
right_(right),
|
||||
selected_color_(selected_color),
|
||||
unselected_color_(unselected_color) {}
|
||||
unselected_color_(unselected_color),
|
||||
selected_color_(selected_color) {}
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min_x = 1;
|
||||
requirement_.min_y = 1;
|
||||
}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
if (box_.y_max < box_.y_min)
|
||||
if (box_.y_max < box_.y_min) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This are the two location with an empty demi-cell.
|
||||
int demi_cell_left = left_ * 2 - 1;
|
||||
int demi_cell_right = right_ * 2 + 2;
|
||||
int demi_cell_left = int(left_ * 2.F - 1.F); // NOLINT
|
||||
int demi_cell_right = int(right_ * 2.F + 2.F); // NOLINT
|
||||
|
||||
int y = box_.y_min;
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
@ -419,23 +429,24 @@ Element separatorHSelector(float left,
|
||||
pixel.character = "─";
|
||||
pixel.automerge = true;
|
||||
} else {
|
||||
pixel.character = a_empty ? "╶" : "╴";
|
||||
pixel.character = a_empty ? "╶" : "╴"; // NOLINT
|
||||
pixel.automerge = false;
|
||||
}
|
||||
|
||||
if (demi_cell_left <= a && b <= demi_cell_right)
|
||||
if (demi_cell_left <= a && b <= demi_cell_right) {
|
||||
pixel.foreground_color = selected_color_;
|
||||
else
|
||||
} else {
|
||||
pixel.foreground_color = unselected_color_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float left_;
|
||||
float right_;
|
||||
Color selected_color_;
|
||||
Color unselected_color_;
|
||||
Color selected_color_;
|
||||
};
|
||||
return std::make_shared<Impl>(left, right, selected_color, unselected_color);
|
||||
return std::make_shared<Impl>(left, right, unselected_color, selected_color);
|
||||
}
|
||||
|
||||
/// @brief Draw an vertical bar, with the area in between up/downcolored
|
||||
@ -452,27 +463,28 @@ Element separatorHSelector(float left,
|
||||
/// ```
|
||||
Element separatorVSelector(float up,
|
||||
float down,
|
||||
Color selected_color,
|
||||
Color unselected_color) {
|
||||
Color unselected_color,
|
||||
Color selected_color) {
|
||||
class Impl : public Node {
|
||||
public:
|
||||
Impl(float up, float down, Color selected_color, Color unselected_color)
|
||||
Impl(float up, float down, Color unselected_color, Color selected_color)
|
||||
: up_(up),
|
||||
down_(down),
|
||||
selected_color_(selected_color),
|
||||
unselected_color_(unselected_color) {}
|
||||
unselected_color_(unselected_color),
|
||||
selected_color_(selected_color) {}
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min_x = 1;
|
||||
requirement_.min_y = 1;
|
||||
}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
if (box_.x_max < box_.x_min)
|
||||
if (box_.x_max < box_.x_min) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This are the two location with an empty demi-cell.
|
||||
int demi_cell_up = up_ * 2 - 1;
|
||||
int demi_cell_down = down_ * 2 + 2;
|
||||
int demi_cell_up = int(up_ * 2 - 1);
|
||||
int demi_cell_down = int(down_ * 2 + 2);
|
||||
|
||||
int x = box_.x_min;
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
@ -487,23 +499,24 @@ Element separatorVSelector(float up,
|
||||
pixel.character = "│";
|
||||
pixel.automerge = true;
|
||||
} else {
|
||||
pixel.character = a_empty ? "╷" : "╵";
|
||||
pixel.character = a_empty ? "╷" : "╵"; // NOLINT
|
||||
pixel.automerge = false;
|
||||
}
|
||||
|
||||
if (demi_cell_up <= a && b <= demi_cell_down)
|
||||
if (demi_cell_up <= a && b <= demi_cell_down) {
|
||||
pixel.foreground_color = selected_color_;
|
||||
else
|
||||
} else {
|
||||
pixel.foreground_color = unselected_color_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float up_;
|
||||
float down_;
|
||||
Color selected_color_;
|
||||
Color unselected_color_;
|
||||
Color selected_color_;
|
||||
};
|
||||
return std::make_shared<Impl>(up, down, selected_color, unselected_color);
|
||||
return std::make_shared<Impl>(up, down, unselected_color, selected_color);
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
@ -1,4 +1,3 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for min, max
|
||||
#include <memory> // for make_shared, __shared_ptr_access
|
||||
#include <utility> // for move
|
||||
@ -13,7 +12,7 @@ namespace ftxui {
|
||||
|
||||
class Size : public Node {
|
||||
public:
|
||||
Size(Element child, Direction direction, Constraint constraint, size_t value)
|
||||
Size(Element child, Direction direction, Constraint constraint, int value)
|
||||
: Node(unpack(std::move(child))),
|
||||
direction_(direction),
|
||||
constraint_(constraint),
|
||||
|
@ -1,14 +1,16 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <memory> // for allocator, allocator_traits<>::value_type
|
||||
#include <string> // for basic_string, string
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector, __alloc_traits<>::value_type
|
||||
#include <cstddef> // for size_t
|
||||
#include <memory> // for allocator, allocator_traits<>::value_type
|
||||
#include <string> // for basic_string, string
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector, __alloc_traits<>::value_type
|
||||
|
||||
#include "ftxui/dom/elements.hpp" // for Element, gauge, text, vbox, spinner
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
static const std::vector<std::vector<std::vector<std::string>>> elements = {
|
||||
namespace {
|
||||
// NOLINTNEXTLINE
|
||||
const std::vector<std::vector<std::vector<std::string>>> elements = {
|
||||
{
|
||||
{"Replaced by the gauge"},
|
||||
},
|
||||
@ -246,6 +248,8 @@ static const std::vector<std::vector<std::vector<std::string>>> elements = {
|
||||
" LOLLOL ",
|
||||
}}};
|
||||
|
||||
} // namespace
|
||||
|
||||
/// @brief Useful to represent the effect of time and/or events. This display an
|
||||
/// ASCII art "video".
|
||||
/// @param charset_index The type of "video".
|
||||
@ -254,16 +258,19 @@ static const std::vector<std::vector<std::vector<std::string>>> elements = {
|
||||
/// @ingroup dom
|
||||
Element spinner(int charset_index, size_t image_index) {
|
||||
if (charset_index == 0) {
|
||||
image_index %= 40;
|
||||
if (image_index > 20)
|
||||
image_index = 40 - image_index;
|
||||
return gauge(image_index * 0.05);
|
||||
const int progress_size = 40;
|
||||
image_index %= progress_size;
|
||||
if (image_index > progress_size / 2) {
|
||||
image_index = progress_size - image_index;
|
||||
}
|
||||
return gauge(float(image_index) * 0.05F); // NOLINT
|
||||
}
|
||||
charset_index %= elements.size();
|
||||
image_index %= elements[charset_index].size();
|
||||
charset_index %= (int)elements.size();
|
||||
image_index %= (int)elements[charset_index].size();
|
||||
std::vector<Element> lines;
|
||||
for (const auto& it : elements[charset_index][image_index])
|
||||
for (const auto& it : elements[charset_index][image_index]) {
|
||||
lines.push_back(text(it));
|
||||
}
|
||||
return vbox(std::move(lines));
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ bool IsCell(int x, int y) {
|
||||
return x % 2 == 1 && y % 2 == 1;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
static std::string charset[6][6] = {
|
||||
{"┌", "┐", "└", "┘", "─", "│"}, //
|
||||
{"┏", "┓", "┗", "┛", "━", "┃"}, //
|
||||
@ -29,8 +30,9 @@ int Wrap(int input, int modulo) {
|
||||
}
|
||||
|
||||
void Order(int& a, int& b) {
|
||||
if (a >= b)
|
||||
if (a >= b) {
|
||||
std::swap(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -42,10 +44,10 @@ Table::Table() {
|
||||
Table::Table(std::vector<std::vector<std::string>> input) {
|
||||
std::vector<std::vector<Element>> output;
|
||||
for (auto& row : input) {
|
||||
output.push_back({});
|
||||
output.emplace_back();
|
||||
auto& output_row = output.back();
|
||||
for (auto& cell : row) {
|
||||
output_row.push_back(text(cell));
|
||||
output_row.push_back(text(std::move(cell)));
|
||||
}
|
||||
}
|
||||
Initialize(std::move(output));
|
||||
@ -56,18 +58,20 @@ Table::Table(std::vector<std::vector<Element>> input) {
|
||||
}
|
||||
|
||||
void Table::Initialize(std::vector<std::vector<Element>> input) {
|
||||
input_dim_y_ = input.size();
|
||||
input_dim_y_ = (int)input.size();
|
||||
input_dim_x_ = 0;
|
||||
for (auto& row : input)
|
||||
for (auto& row : input) {
|
||||
input_dim_x_ = std::max(input_dim_x_, (int)row.size());
|
||||
}
|
||||
|
||||
dim_y_ = 2 * input_dim_y_ + 1;
|
||||
dim_x_ = 2 * input_dim_x_ + 1;
|
||||
|
||||
// Reserve space.
|
||||
elements_.resize(dim_y_);
|
||||
for (int y = 0; y < dim_y_; ++y)
|
||||
for (int y = 0; y < dim_y_; ++y) {
|
||||
elements_[y].resize(dim_x_);
|
||||
}
|
||||
|
||||
// Transfert elements_ from |input| toward |elements_|.
|
||||
{
|
||||
@ -88,8 +92,9 @@ void Table::Initialize(std::vector<std::vector<Element>> input) {
|
||||
auto& element = elements_[y][x];
|
||||
|
||||
if (IsCell(x, y)) {
|
||||
if (!element)
|
||||
if (!element) {
|
||||
element = emptyElement();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -129,7 +134,7 @@ TableSelection Table::SelectRectangle(int column_min,
|
||||
row_max = Wrap(row_max, input_dim_y_);
|
||||
Order(row_min, row_max);
|
||||
|
||||
TableSelection output;
|
||||
TableSelection output; // NOLINT
|
||||
output.table_ = this;
|
||||
output.x_min_ = 2 * column_min;
|
||||
output.x_max_ = 2 * column_max + 2;
|
||||
@ -139,7 +144,7 @@ TableSelection Table::SelectRectangle(int column_min,
|
||||
}
|
||||
|
||||
TableSelection Table::SelectAll() {
|
||||
TableSelection output;
|
||||
TableSelection output; // NOLINT
|
||||
output.table_ = this;
|
||||
output.x_min_ = 0;
|
||||
output.x_max_ = dim_x_ - 1;
|
||||
@ -172,6 +177,7 @@ Element Table::Render() {
|
||||
return gridbox(std::move(elements_));
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void TableSelection::Decorate(Decorator decorator) {
|
||||
for (int y = y_min_; y <= y_max_; ++y) {
|
||||
for (int x = x_min_; x <= x_max_; ++x) {
|
||||
@ -181,10 +187,11 @@ void TableSelection::Decorate(Decorator decorator) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void TableSelection::DecorateCells(Decorator decorator) {
|
||||
for (int y = y_min_; y <= y_max_; ++y) {
|
||||
for (int x = x_min_; x <= x_max_; ++x) {
|
||||
if (y % 2 && x % 2) {
|
||||
if (y % 2 == 1 && x % 2 == 1) {
|
||||
Element& e = table_->elements_[y][x];
|
||||
e = std::move(e) | decorator;
|
||||
}
|
||||
@ -192,12 +199,13 @@ void TableSelection::DecorateCells(Decorator decorator) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void TableSelection::DecorateAlternateColumn(Decorator decorator,
|
||||
int modulo,
|
||||
int shift) {
|
||||
for (int y = y_min_; y <= y_max_; ++y) {
|
||||
for (int x = x_min_; x <= x_max_; ++x) {
|
||||
if (y % 2 && (x / 2) % modulo == shift) {
|
||||
if (y % 2 == 1 && (x / 2) % modulo == shift) {
|
||||
Element& e = table_->elements_[y][x];
|
||||
e = std::move(e) | decorator;
|
||||
}
|
||||
@ -205,12 +213,13 @@ void TableSelection::DecorateAlternateColumn(Decorator decorator,
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void TableSelection::DecorateAlternateRow(Decorator decorator,
|
||||
int modulo,
|
||||
int shift) {
|
||||
for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
|
||||
for (int x = x_min_; x <= x_max_; ++x) {
|
||||
if (y % 2 && (y / 2) % modulo == shift) {
|
||||
if (y % 2 == 1 && (y / 2) % modulo == shift) {
|
||||
Element& e = table_->elements_[y][x];
|
||||
e = std::move(e) | decorator;
|
||||
}
|
||||
@ -218,12 +227,13 @@ void TableSelection::DecorateAlternateRow(Decorator decorator,
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void TableSelection::DecorateCellsAlternateColumn(Decorator decorator,
|
||||
int modulo,
|
||||
int shift) {
|
||||
for (int y = y_min_; y <= y_max_; ++y) {
|
||||
for (int x = x_min_; x <= x_max_; ++x) {
|
||||
if (y % 2 && x % 2 && ((x / 2) % modulo == shift)) {
|
||||
if (y % 2 == 1 && x % 2 == 1 && ((x / 2) % modulo == shift)) {
|
||||
Element& e = table_->elements_[y][x];
|
||||
e = std::move(e) | decorator;
|
||||
}
|
||||
@ -231,12 +241,13 @@ void TableSelection::DecorateCellsAlternateColumn(Decorator decorator,
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void TableSelection::DecorateCellsAlternateRow(Decorator decorator,
|
||||
int modulo,
|
||||
int shift) {
|
||||
for (int y = y_min_; y <= y_max_; ++y) {
|
||||
for (int x = x_min_; x <= x_max_; ++x) {
|
||||
if (y % 2 && x % 2 && ((y / 2) % modulo == shift)) {
|
||||
if (y % 2 == 1 && x % 2 == 1 && ((y / 2) % modulo == shift)) {
|
||||
Element& e = table_->elements_[y][x];
|
||||
e = std::move(e) | decorator;
|
||||
}
|
||||
@ -244,77 +255,82 @@ void TableSelection::DecorateCellsAlternateRow(Decorator decorator,
|
||||
}
|
||||
}
|
||||
|
||||
void TableSelection::Border(BorderStyle style) {
|
||||
BorderLeft(style);
|
||||
BorderRight(style);
|
||||
BorderTop(style);
|
||||
BorderBottom(style);
|
||||
void TableSelection::Border(BorderStyle border) {
|
||||
BorderLeft(border);
|
||||
BorderRight(border);
|
||||
BorderTop(border);
|
||||
BorderBottom(border);
|
||||
|
||||
table_->elements_[y_min_][x_min_] = text(charset[style][0]) | automerge;
|
||||
table_->elements_[y_min_][x_max_] = text(charset[style][1]) | automerge;
|
||||
table_->elements_[y_max_][x_min_] = text(charset[style][2]) | automerge;
|
||||
table_->elements_[y_max_][x_max_] = text(charset[style][3]) | automerge;
|
||||
// NOLINTNEXTLINE
|
||||
table_->elements_[y_min_][x_min_] = text(charset[border][0]) | automerge;
|
||||
// NOLINTNEXTLINE
|
||||
table_->elements_[y_min_][x_max_] = text(charset[border][1]) | automerge;
|
||||
// NOLINTNEXTLINE
|
||||
table_->elements_[y_max_][x_min_] = text(charset[border][2]) | automerge;
|
||||
// NOLINTNEXTLINE
|
||||
table_->elements_[y_max_][x_max_] = text(charset[border][3]) | automerge;
|
||||
}
|
||||
|
||||
void TableSelection::Separator(BorderStyle style) {
|
||||
void TableSelection::Separator(BorderStyle border) {
|
||||
for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
|
||||
for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
|
||||
if (y % 2 == 0 || x % 2 == 0) {
|
||||
Element& e = table_->elements_[y][x];
|
||||
e = (y % 2) ? separatorCharacter(charset[style][5]) | automerge
|
||||
: separatorCharacter(charset[style][4]) | automerge;
|
||||
e = (y % 2 == 1)
|
||||
? separatorCharacter(charset[border][5]) | automerge // NOLINT
|
||||
: separatorCharacter(charset[border][4]) | automerge; // NOLINT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TableSelection::SeparatorVertical(BorderStyle style) {
|
||||
void TableSelection::SeparatorVertical(BorderStyle border) {
|
||||
for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
|
||||
for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
|
||||
if (x % 2 == 0) {
|
||||
table_->elements_[y][x] =
|
||||
separatorCharacter(charset[style][5]) | automerge;
|
||||
separatorCharacter(charset[border][5]) | automerge; // NOLINT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TableSelection::SeparatorHorizontal(BorderStyle style) {
|
||||
void TableSelection::SeparatorHorizontal(BorderStyle border) {
|
||||
for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
|
||||
for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
|
||||
if (y % 2 == 0) {
|
||||
table_->elements_[y][x] =
|
||||
separatorCharacter(charset[style][4]) | automerge;
|
||||
separatorCharacter(charset[border][4]) | automerge; // NOLINT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TableSelection::BorderLeft(BorderStyle style) {
|
||||
void TableSelection::BorderLeft(BorderStyle border) {
|
||||
for (int y = y_min_; y <= y_max_; y++) {
|
||||
table_->elements_[y][x_min_] =
|
||||
separatorCharacter(charset[style][5]) | automerge;
|
||||
separatorCharacter(charset[border][5]) | automerge; // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
void TableSelection::BorderRight(BorderStyle style) {
|
||||
void TableSelection::BorderRight(BorderStyle border) {
|
||||
for (int y = y_min_; y <= y_max_; y++) {
|
||||
table_->elements_[y][x_max_] =
|
||||
separatorCharacter(charset[style][5]) | automerge;
|
||||
separatorCharacter(charset[border][5]) | automerge; // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
void TableSelection::BorderTop(BorderStyle style) {
|
||||
void TableSelection::BorderTop(BorderStyle border) {
|
||||
for (int x = x_min_; x <= x_max_; x++) {
|
||||
table_->elements_[y_min_][x] =
|
||||
separatorCharacter(charset[style][4]) | automerge;
|
||||
separatorCharacter(charset[border][4]) | automerge; // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
void TableSelection::BorderBottom(BorderStyle style) {
|
||||
void TableSelection::BorderBottom(BorderStyle border) {
|
||||
for (int x = x_min_; x <= x_max_; x++) {
|
||||
table_->elements_[y_max_][x] =
|
||||
separatorCharacter(charset[style][4]) | automerge;
|
||||
separatorCharacter(charset[border][4]) | automerge; // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <algorithm> // for min
|
||||
#include <memory> // for make_shared
|
||||
#include <string> // for string, wstring
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ftxui/dom/deprecated.hpp" // for text, vtext
|
||||
@ -17,7 +18,7 @@ using ftxui::Screen;
|
||||
|
||||
class Text : public Node {
|
||||
public:
|
||||
Text(std::string text) : text_(text) {}
|
||||
explicit Text(std::string text) : text_(std::move(text)) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min_x = string_width(text_);
|
||||
@ -27,11 +28,13 @@ class Text : public Node {
|
||||
void Render(Screen& screen) override {
|
||||
int x = box_.x_min;
|
||||
int y = box_.y_min;
|
||||
if (y > box_.y_max)
|
||||
if (y > box_.y_max) {
|
||||
return;
|
||||
}
|
||||
for (const auto& cell : Utf8ToGlyphs(text_)) {
|
||||
if (x > box_.x_max)
|
||||
if (x > box_.x_max) {
|
||||
return;
|
||||
}
|
||||
screen.PixelAt(x, y).character = cell;
|
||||
++x;
|
||||
}
|
||||
@ -43,8 +46,8 @@ class Text : public Node {
|
||||
|
||||
class VText : public Node {
|
||||
public:
|
||||
VText(std::string text)
|
||||
: text_(text), width_{std::min(string_width(text_), 1)} {}
|
||||
explicit VText(std::string text)
|
||||
: text_(std::move(text)), width_{std::min(string_width(text_), 1)} {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min_x = width_;
|
||||
@ -54,11 +57,13 @@ class VText : public Node {
|
||||
void Render(Screen& screen) override {
|
||||
int x = box_.x_min;
|
||||
int y = box_.y_min;
|
||||
if (x + width_ - 1 > box_.x_max)
|
||||
if (x + width_ - 1 > box_.x_max) {
|
||||
return;
|
||||
}
|
||||
for (const auto& it : Utf8ToGlyphs(text_)) {
|
||||
if (y > box_.y_max)
|
||||
if (y > box_.y_max) {
|
||||
return;
|
||||
}
|
||||
screen.PixelAt(x, y).character = it;
|
||||
y += 1;
|
||||
}
|
||||
@ -85,7 +90,7 @@ class VText : public Node {
|
||||
/// Hello world!
|
||||
/// ```
|
||||
Element text(std::string text) {
|
||||
return std::make_shared<Text>(text);
|
||||
return std::make_shared<Text>(std::move(text));
|
||||
}
|
||||
|
||||
/// @brief Display a piece of unicode text.
|
||||
@ -103,7 +108,7 @@ Element text(std::string text) {
|
||||
/// ```bash
|
||||
/// Hello world!
|
||||
/// ```
|
||||
Element text(std::wstring text) {
|
||||
Element text(std::wstring text) { // NOLINT
|
||||
return std::make_shared<Text>(to_string(text));
|
||||
}
|
||||
|
||||
@ -134,7 +139,7 @@ Element text(std::wstring text) {
|
||||
/// !
|
||||
/// ```
|
||||
Element vtext(std::string text) {
|
||||
return std::make_shared<VText>(text);
|
||||
return std::make_shared<VText>(std::move(text));
|
||||
}
|
||||
|
||||
/// @brief Display a piece unicode text vertically.
|
||||
@ -163,7 +168,7 @@ Element vtext(std::string text) {
|
||||
/// d
|
||||
/// !
|
||||
/// ```
|
||||
Element vtext(std::wstring text) {
|
||||
Element vtext(std::wstring text) { // NOLINT
|
||||
return std::make_shared<VText>(to_string(text));
|
||||
}
|
||||
|
||||
|
@ -36,16 +36,18 @@ Element nothing(Element element) {
|
||||
/// auto decorator = bold | blink;
|
||||
/// ```
|
||||
Decorator operator|(Decorator a, Decorator b) {
|
||||
return compose(a, b);
|
||||
return compose(std::move(a), //
|
||||
std::move(b));
|
||||
}
|
||||
|
||||
/// @brief From a set of element, apply a decorator to every elements.
|
||||
/// @return the set of decorated element.
|
||||
/// @ingroup dom
|
||||
Elements operator|(Elements elements, Decorator decorator) {
|
||||
Elements operator|(Elements elements, Decorator decorator) { // NOLINT
|
||||
Elements output;
|
||||
for (auto& it : elements)
|
||||
for (auto& it : elements) {
|
||||
output.push_back(std::move(it) | decorator);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
@ -62,7 +64,7 @@ Elements operator|(Elements elements, Decorator decorator) {
|
||||
/// ```cpp
|
||||
/// text("Hello") | bold;
|
||||
/// ```
|
||||
Element operator|(Element element, Decorator decorator) {
|
||||
Element operator|(Element element, Decorator decorator) { // NOLINT
|
||||
return decorator(std::move(element));
|
||||
}
|
||||
|
||||
@ -78,7 +80,7 @@ Element operator|(Element element, Decorator decorator) {
|
||||
/// element |= bold;
|
||||
/// ```
|
||||
Element& operator|=(Element& e, Decorator d) {
|
||||
e = e | d;
|
||||
e = e | std::move(d);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -95,7 +97,8 @@ Dimensions Dimension::Fit(Element& e) {
|
||||
|
||||
Node::Status status;
|
||||
e->Check(&status);
|
||||
while (status.need_iteration && status.iteration < 20) {
|
||||
const int max_iteration = 20;
|
||||
while (status.need_iteration && status.iteration < max_iteration) {
|
||||
e->ComputeRequirement();
|
||||
|
||||
// Don't give the element more space than it needs:
|
||||
@ -107,8 +110,9 @@ Dimensions Dimension::Fit(Element& e) {
|
||||
status.iteration++;
|
||||
e->Check(&status);
|
||||
|
||||
if (!status.need_iteration)
|
||||
if (!status.need_iteration) {
|
||||
break;
|
||||
}
|
||||
// Increase the size of the box until it fits, but not more than the with of
|
||||
// the terminal emulator:
|
||||
box.x_max = std::min(e->requirement().min_x, fullsize.dimx);
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for max
|
||||
#include <cstddef> // for size_t
|
||||
#include <memory> // for __shared_ptr_access, shared_ptr, make_shared, allocator_traits<>::value_type
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector, __alloc_traits<>::value_type
|
||||
@ -14,7 +14,7 @@ namespace ftxui {
|
||||
|
||||
class VBox : public Node {
|
||||
public:
|
||||
VBox(Elements children) : Node(std::move(children)) {}
|
||||
explicit VBox(Elements children) : Node(std::move(children)) {}
|
||||
|
||||
void ComputeRequirement() override {
|
||||
requirement_.min_x = 0;
|
||||
|
@ -17,7 +17,7 @@ Box Box::Intersection(Box a, Box b) {
|
||||
|
||||
/// @return whether (x,y) is contained inside the box.
|
||||
/// @ingroup screen
|
||||
bool Box::Contain(int x, int y) {
|
||||
bool Box::Contain(int x, int y) const {
|
||||
return x_min <= x && //
|
||||
x_max >= x && //
|
||||
y_min <= y && //
|
||||
|
@ -1,20 +1,36 @@
|
||||
#include "ftxui/screen/color.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <array> // for array
|
||||
#include <string_view> // for literals
|
||||
|
||||
#include "ftxui/screen/color_info.hpp" // for GetColorInfo, ColorInfo
|
||||
#include "ftxui/screen/terminal.hpp" // for Terminal, Terminal::Color, Terminal::Palette256, Terminal::TrueColor
|
||||
#include "ftxui/screen/terminal.hpp" // for ColorSupport, Color, Palette256, TrueColor
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace {
|
||||
const char* palette16code[16][2] = {
|
||||
{"30", "40"}, {"31", "41"}, {"32", "42"}, {"33", "43"},
|
||||
{"34", "44"}, {"35", "45"}, {"36", "46"}, {"37", "47"},
|
||||
{"90", "100"}, {"91", "101"}, {"92", "102"}, {"93", "103"},
|
||||
{"94", "104"}, {"95", "105"}, {"96", "106"}, {"97", "107"},
|
||||
const std::array<const char*, 33> palette16code = {
|
||||
"30", "40", //
|
||||
"31", "41", //
|
||||
"32", "42", //
|
||||
"33", "43", //
|
||||
"34", "44", //
|
||||
"35", "45", //
|
||||
"36", "46", //
|
||||
"37", "47", //
|
||||
"90", "100", //
|
||||
"91", "101", //
|
||||
"92", "102", //
|
||||
"93", "103", //
|
||||
"94", "104", //
|
||||
"95", "105", //
|
||||
"96", "106", //
|
||||
"97", "107", //
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Color::operator==(const Color& rhs) const {
|
||||
return red_ == rhs.red_ && green_ == rhs.green_ && blue_ == rhs.blue_ &&
|
||||
@ -28,42 +44,43 @@ bool Color::operator!=(const Color& rhs) const {
|
||||
std::string Color::Print(bool is_background_color) const {
|
||||
switch (type_) {
|
||||
case ColorType::Palette1:
|
||||
return is_background_color ? "49" : "39";
|
||||
return is_background_color ? "49"s : "39"s;
|
||||
|
||||
case ColorType::Palette16:
|
||||
return palette16code[index_][is_background_color];
|
||||
return palette16code[2 * red_ + is_background_color]; // NOLINT;
|
||||
|
||||
case ColorType::Palette256:
|
||||
return (is_background_color ? "48;5;" : "38;5;") + std::to_string(index_);
|
||||
return (is_background_color ? "48;5;"s : "38;5;"s) + std::to_string(red_);
|
||||
|
||||
case ColorType::TrueColor:
|
||||
return (is_background_color ? "48;2;" : "38;2;") //
|
||||
+ std::to_string(red_) + ";" //
|
||||
+ std::to_string(green_) + ";" //
|
||||
+ std::to_string(blue_); //
|
||||
return (is_background_color ? "48;2;"s : "38;2;"s) //
|
||||
+ std::to_string(red_) + ";" //
|
||||
+ std::to_string(green_) + ";" //
|
||||
+ std::to_string(blue_); //
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/// @brief Build a transparent color.
|
||||
/// @ingroup screen
|
||||
Color::Color() : type_(ColorType::Palette1) {}
|
||||
Color::Color() = default;
|
||||
|
||||
/// @brief Build a transparent color.
|
||||
/// @ingroup screen
|
||||
Color::Color(Palette1) : type_(ColorType::Palette1) {}
|
||||
Color::Color(Palette1 /*value*/) : Color() {}
|
||||
|
||||
/// @brief Build a transparent using Palette16 colors.
|
||||
/// @ingroup screen
|
||||
Color::Color(Palette16 index) : type_(ColorType::Palette16), index_(index) {}
|
||||
Color::Color(Palette16 index) : type_(ColorType::Palette16), red_(index) {}
|
||||
|
||||
/// @brief Build a transparent using Palette256 colors.
|
||||
/// @ingroup screen
|
||||
Color::Color(Palette256 index) : type_(ColorType::Palette256), index_(index) {
|
||||
if (Terminal::ColorSupport() >= Terminal::Color::Palette256)
|
||||
Color::Color(Palette256 index) : type_(ColorType::Palette256), red_(index) {
|
||||
if (Terminal::ColorSupport() >= Terminal::Color::Palette256) {
|
||||
return;
|
||||
}
|
||||
type_ = ColorType::Palette16;
|
||||
index_ = GetColorInfo(Color::Palette256(index_)).index_16;
|
||||
red_ = GetColorInfo(Color::Palette256(red_)).index_16;
|
||||
}
|
||||
|
||||
/// @brief Build a Color from its RGB representation.
|
||||
@ -75,12 +92,17 @@ Color::Color(Palette256 index) : type_(ColorType::Palette256), index_(index) {
|
||||
/// @ingroup screen
|
||||
Color::Color(uint8_t red, uint8_t green, uint8_t blue)
|
||||
: type_(ColorType::TrueColor), red_(red), green_(green), blue_(blue) {
|
||||
if (Terminal::ColorSupport() == Terminal::Color::TrueColor)
|
||||
if (Terminal::ColorSupport() == Terminal::Color::TrueColor) {
|
||||
return;
|
||||
}
|
||||
|
||||
int closest = 256 * 256 * 3;
|
||||
// Find the closest coor from the database:
|
||||
const int max_distance = 256 * 256 * 3;
|
||||
int closest = max_distance;
|
||||
int best = 0;
|
||||
for (int i = 16; i < 256; ++i) {
|
||||
const int database_begin = 16;
|
||||
const int database_end = 256;
|
||||
for (int i = database_begin; i < database_end; ++i) {
|
||||
ColorInfo color_info = GetColorInfo(Color::Palette256(i));
|
||||
int dr = color_info.red - red;
|
||||
int dg = color_info.green - green;
|
||||
@ -94,10 +116,10 @@ Color::Color(uint8_t red, uint8_t green, uint8_t blue)
|
||||
|
||||
if (Terminal::ColorSupport() == Terminal::Color::Palette256) {
|
||||
type_ = ColorType::Palette256;
|
||||
index_ = best;
|
||||
red_ = best;
|
||||
} else {
|
||||
type_ = ColorType::Palette16;
|
||||
index_ = GetColorInfo(Color::Palette256(best)).index_16;
|
||||
red_ = GetColorInfo(Color::Palette256(best)).index_16;
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,24 +144,25 @@ Color Color::RGB(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
/// @ingroup screen
|
||||
// static
|
||||
Color Color::HSV(uint8_t h, uint8_t s, uint8_t v) {
|
||||
if (s == 0)
|
||||
if (s == 0) {
|
||||
return Color(v, v, v);
|
||||
}
|
||||
|
||||
uint8_t region = h / 43;
|
||||
uint8_t remainder = (h - (region * 43)) * 6;
|
||||
uint8_t p = (v * (255 - s)) >> 8;
|
||||
uint8_t q = (v * (255 - ((s * remainder) >> 8))) >> 8;
|
||||
uint8_t t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
|
||||
uint8_t region = h / 43; // NOLINT
|
||||
uint8_t remainder = (h - (region * 43)) * 6; // NOLINT
|
||||
uint8_t p = (v * (255 - s)) >> 8; // NOLINT
|
||||
uint8_t q = (v * (255 - ((s * remainder) >> 8))) >> 8; // NOLINT
|
||||
uint8_t t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8; // NOLINT
|
||||
|
||||
// clang-format off
|
||||
switch (region) {
|
||||
case 0: return Color(v,t,p);
|
||||
case 1: return Color(q,v,p);
|
||||
case 2: return Color(p,v,t);
|
||||
case 3: return Color(p,q,v);
|
||||
case 4: return Color(t,p,v);
|
||||
case 5: return Color(v,p,q);
|
||||
}
|
||||
switch (region) { // NOLINT
|
||||
case 0: return Color(v,t,p); // NOLINT
|
||||
case 1: return Color(q,v,p); // NOLINT
|
||||
case 2: return Color(p,v,t); // NOLINT
|
||||
case 3: return Color(p,q,v); // NOLINT
|
||||
case 4: return Color(t,p,v); // NOLINT
|
||||
case 5: return Color(v,p,q); // NOLINT
|
||||
} // NOLINT
|
||||
// clang-format on
|
||||
|
||||
return Color(0, 0, 0);
|
||||
@ -147,81 +170,77 @@ Color Color::HSV(uint8_t h, uint8_t s, uint8_t v) {
|
||||
|
||||
// static
|
||||
Color Color::Interpolate(float t, const Color& a, const Color& b) {
|
||||
float red = 0.f;
|
||||
float green = 0.f;
|
||||
float blue = 0.f;
|
||||
switch (a.type_) {
|
||||
case ColorType::Palette1: {
|
||||
if (t < 0.5)
|
||||
return a;
|
||||
else
|
||||
return b;
|
||||
}
|
||||
|
||||
case ColorType::Palette16: {
|
||||
ColorInfo info = GetColorInfo(Color::Palette16(a.index_));
|
||||
red = info.red * (1 - t);
|
||||
green = info.green * (1 - t);
|
||||
blue = info.blue * (1 - t);
|
||||
break;
|
||||
}
|
||||
|
||||
case ColorType::Palette256: {
|
||||
ColorInfo info = GetColorInfo(Color::Palette256(a.index_));
|
||||
red = info.red * (1 - t);
|
||||
green = info.green * (1 - t);
|
||||
blue = info.blue * (1 - t);
|
||||
break;
|
||||
}
|
||||
|
||||
case ColorType::TrueColor: {
|
||||
red = a.red_ * (1 - t);
|
||||
green = a.green_ * (1 - t);
|
||||
blue = a.blue_ * (1 - t);
|
||||
break;
|
||||
if (a.type_ == ColorType::Palette1) {
|
||||
if (t < 0.5F) { // NOLINT
|
||||
return a;
|
||||
} else {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
switch (b.type_) {
|
||||
case ColorType::Palette1: {
|
||||
if (t > 0.5)
|
||||
return a;
|
||||
else
|
||||
return b;
|
||||
}
|
||||
|
||||
case ColorType::Palette16: {
|
||||
ColorInfo info = GetColorInfo(Color::Palette16(b.index_));
|
||||
red += info.red * t;
|
||||
green += info.green * t;
|
||||
blue += info.blue * t;
|
||||
break;
|
||||
}
|
||||
|
||||
case ColorType::Palette256: {
|
||||
ColorInfo info = GetColorInfo(Color::Palette256(b.index_));
|
||||
red += info.red * t;
|
||||
green += info.green * t;
|
||||
blue += info.blue * t;
|
||||
break;
|
||||
}
|
||||
|
||||
case ColorType::TrueColor: {
|
||||
red += b.red_ * t;
|
||||
green += b.green_ * t;
|
||||
blue += b.blue_ * t;
|
||||
break;
|
||||
if (b.type_ == ColorType::Palette1) {
|
||||
if (t > 0.5F) { // NOLINT
|
||||
return a;
|
||||
} else {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return Color::RGB(red, green, blue);
|
||||
|
||||
auto get_color = [](const Color& color, //
|
||||
uint8_t* red, uint8_t* green, uint8_t* blue) {
|
||||
switch (color.type_) {
|
||||
case ColorType::Palette1: {
|
||||
return;
|
||||
}
|
||||
|
||||
case ColorType::Palette16: {
|
||||
ColorInfo info = GetColorInfo(Color::Palette16(color.red_));
|
||||
*red = info.red;
|
||||
*green = info.green;
|
||||
*blue = info.blue;
|
||||
return;
|
||||
}
|
||||
|
||||
case ColorType::Palette256: {
|
||||
ColorInfo info = GetColorInfo(Color::Palette256(color.red_));
|
||||
*red = info.red;
|
||||
*green = info.green;
|
||||
*blue = info.blue;
|
||||
return;
|
||||
}
|
||||
|
||||
case ColorType::TrueColor: {
|
||||
*red = color.red_;
|
||||
*green = color.green_;
|
||||
*blue = color.blue_;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
uint8_t red_a = 0;
|
||||
uint8_t green_a = 0;
|
||||
uint8_t blue_a = 0;
|
||||
uint8_t red_b = 0;
|
||||
uint8_t green_b = 0;
|
||||
uint8_t blue_b = 0;
|
||||
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));
|
||||
}
|
||||
|
||||
inline namespace literals {
|
||||
|
||||
Color operator""_rgb(unsigned long long int combined) {
|
||||
assert(combined <= 0xffffffU);
|
||||
auto const red = static_cast<uint8_t>(combined >> 16);
|
||||
auto const green = static_cast<uint8_t>(combined >> 8);
|
||||
// assert(combined <= 0xffffffU);
|
||||
auto const red = static_cast<uint8_t>(combined >> 16U);
|
||||
auto const green = static_cast<uint8_t>(combined >> 8U);
|
||||
auto const blue = static_cast<uint8_t>(combined);
|
||||
return Color(red, green, blue);
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
#include "ftxui/screen/color_info.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "ftxui/screen/color.hpp" // for Color, Color::Palette16, Color::Palette256
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
// clang-format off
|
||||
const ColorInfo palette256[] = {
|
||||
const std::array<ColorInfo, 256> palette256 = {{
|
||||
{"Black" , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ,
|
||||
{"Red" , 1 , 1 , 128 , 0 , 0 , 0 , 255 , 128 } ,
|
||||
{"Green" , 2 , 2 , 0 , 128 , 0 , 85 , 255 , 128 } ,
|
||||
@ -261,14 +264,14 @@ const ColorInfo palette256[] = {
|
||||
{"Grey85" , 253 , 7 , 218 , 218 , 218 , 0 , 0 , 218 } ,
|
||||
{"Grey89" , 254 , 15 , 228 , 228 , 228 , 0 , 0 , 228 } ,
|
||||
{"Grey93" , 255 , 15 , 238 , 238 , 238 , 0 , 0 , 238 } ,
|
||||
} ;
|
||||
}};
|
||||
|
||||
ColorInfo GetColorInfo(Color::Palette256 index) {
|
||||
return palette256[int(index)];
|
||||
return palette256[index]; // NOLINT;
|
||||
}
|
||||
|
||||
ColorInfo GetColorInfo(Color::Palette16 index) {
|
||||
return palette256[int(index)];
|
||||
return palette256[index]; // NOLINT;
|
||||
}
|
||||
// clang-format off
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <stdint.h> // for uint8_t
|
||||
#include <iostream> // for operator<<, stringstream, basic_ostream, flush, cout, ostream
|
||||
#include <map> // for _Rb_tree_const_iterator, map, operator!=, operator==
|
||||
#include <memory> // for allocator, allocator_traits<>::value_type
|
||||
#include <memory> // for allocator
|
||||
#include <sstream> // IWYU pragma: keep
|
||||
#include <utility> // for pair
|
||||
|
||||
@ -19,26 +20,11 @@
|
||||
namespace ftxui {
|
||||
|
||||
namespace {
|
||||
static const char BOLD_SET[] = "\x1B[1m";
|
||||
static const char BOLD_RESET[] = "\x1B[22m"; // Can't use 21 here.
|
||||
|
||||
static const char DIM_SET[] = "\x1B[2m";
|
||||
static const char DIM_RESET[] = "\x1B[22m";
|
||||
|
||||
static const char UNDERLINED_SET[] = "\x1B[4m";
|
||||
static const char UNDERLINED_RESET[] = "\x1B[24m";
|
||||
|
||||
static const char BLINK_SET[] = "\x1B[5m";
|
||||
static const char BLINK_RESET[] = "\x1B[25m";
|
||||
|
||||
static const char INVERTED_SET[] = "\x1B[7m";
|
||||
static const char INVERTED_RESET[] = "\x1B[27m";
|
||||
|
||||
static const char MOVE_LEFT[] = "\r";
|
||||
static const char MOVE_UP[] = "\x1B[1A";
|
||||
static const char CLEAR_LINE[] = "\x1B[2K";
|
||||
|
||||
Pixel dev_null_pixel;
|
||||
Pixel& dev_null_pixel() {
|
||||
static Pixel pixel;
|
||||
return pixel;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
void WindowsEmulateVT100Terminal() {
|
||||
@ -66,20 +52,49 @@ void WindowsEmulateVT100Terminal() {
|
||||
void UpdatePixelStyle(std::stringstream& ss,
|
||||
Pixel& previous,
|
||||
const Pixel& next) {
|
||||
if (next.bold != previous.bold)
|
||||
ss << (next.bold ? BOLD_SET : BOLD_RESET);
|
||||
if (next == previous) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (next.dim != previous.dim)
|
||||
ss << (next.dim ? DIM_SET : DIM_RESET);
|
||||
if (next.bold && !previous.bold) {
|
||||
ss << "\x1B[1m"; // BOLD_SET
|
||||
}
|
||||
|
||||
if (next.underlined != previous.underlined)
|
||||
ss << (next.underlined ? UNDERLINED_SET : UNDERLINED_RESET);
|
||||
if (!next.bold && previous.bold) {
|
||||
ss << "\x1B[22m"; // BOLD_RESET
|
||||
}
|
||||
|
||||
if (next.blink != previous.blink)
|
||||
ss << (next.blink ? BLINK_SET : BLINK_RESET);
|
||||
if (next.dim && !previous.dim) {
|
||||
ss << "\x1B[2m"; // DIM_SET
|
||||
}
|
||||
|
||||
if (next.inverted != previous.inverted)
|
||||
ss << (next.inverted ? INVERTED_SET : INVERTED_RESET);
|
||||
if (!next.dim && previous.dim) {
|
||||
ss << "\x1B[22m"; // DIM_RESET
|
||||
}
|
||||
|
||||
if (next.underlined && !previous.underlined) {
|
||||
ss << "\x1B[4m"; // UNDERLINED_SET
|
||||
}
|
||||
|
||||
if (!next.underlined && previous.underlined) {
|
||||
ss << "\x1B[24m"; // UNDERLINED_RESET
|
||||
}
|
||||
|
||||
if (next.blink && !previous.blink) {
|
||||
ss << "\x1B[5m"; // BLINK_SET
|
||||
}
|
||||
|
||||
if (!next.blink && previous.blink) {
|
||||
ss << "\x1B[25m"; // BLINK_RESET
|
||||
}
|
||||
|
||||
if (next.inverted && !previous.inverted) {
|
||||
ss << "\x1B[7m"; // INVERTED_SET
|
||||
}
|
||||
|
||||
if (!next.inverted && previous.inverted) {
|
||||
ss << "\x1B[27m"; // INVERTED_RESET
|
||||
}
|
||||
|
||||
if (next.foreground_color != previous.foreground_color ||
|
||||
next.background_color != previous.background_color) {
|
||||
@ -91,31 +106,31 @@ void UpdatePixelStyle(std::stringstream& ss,
|
||||
}
|
||||
|
||||
struct TileEncoding {
|
||||
unsigned int left : 2;
|
||||
unsigned int top : 2;
|
||||
unsigned int right : 2;
|
||||
unsigned int down : 2;
|
||||
unsigned int round : 1;
|
||||
uint8_t left : 2;
|
||||
uint8_t top : 2;
|
||||
uint8_t right : 2;
|
||||
uint8_t down : 2;
|
||||
uint8_t round : 1;
|
||||
|
||||
// clang-format off
|
||||
bool operator<(const TileEncoding& other) const {
|
||||
if (left < other.left) return true;
|
||||
if (left > other.left) return false;
|
||||
if (top < other.top) return true;
|
||||
if (top > other.top) return false;
|
||||
if (right < other.right) return true;
|
||||
if (right > other.right) return false;
|
||||
if (down < other.down) return true;
|
||||
if (down > other.down) return false;
|
||||
if (round < other.round) return true;
|
||||
if (round > other.round) return false;
|
||||
if (left < other.left) { return true; }
|
||||
if (left > other.left) { return false; }
|
||||
if (top < other.top) { return true; }
|
||||
if (top > other.top) { return false; }
|
||||
if (right < other.right) { return true; }
|
||||
if (right > other.right) { return false; }
|
||||
if (down < other.down) { return true; }
|
||||
if (down > other.down) { return false; }
|
||||
if (round < other.round) { return true; }
|
||||
if (round > other.round) { return false; }
|
||||
return false;
|
||||
}
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
const std::map<std::string, TileEncoding> tile_encoding = {
|
||||
const std::map<std::string, TileEncoding> tile_encoding = { // NOLINT
|
||||
{"─", {1, 0, 1, 0, 0}},
|
||||
{"━", {2, 0, 2, 0, 0}},
|
||||
|
||||
@ -257,63 +272,72 @@ const std::map<std::string, TileEncoding> tile_encoding = {
|
||||
// clang-format on
|
||||
|
||||
template <class A, class B>
|
||||
const std::map<B, A> InvertMap(const std::map<A, B> input) {
|
||||
std::map<B, A> InvertMap(const std::map<A, B> input) {
|
||||
std::map<B, A> output;
|
||||
for (const auto& it : input)
|
||||
for (const auto& it : input) {
|
||||
output[it.second] = it.first;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
const std::map<TileEncoding, std::string> tile_encoding_inverse =
|
||||
const std::map<TileEncoding, std::string> tile_encoding_inverse = // NOLINT
|
||||
InvertMap(tile_encoding);
|
||||
|
||||
void UpgradeLeftRight(std::string& left, std::string& right) {
|
||||
const auto it_left = tile_encoding.find(left);
|
||||
if (it_left == tile_encoding.end())
|
||||
if (it_left == tile_encoding.end()) {
|
||||
return;
|
||||
}
|
||||
const auto it_right = tile_encoding.find(right);
|
||||
if (it_right == tile_encoding.end())
|
||||
if (it_right == tile_encoding.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (it_left->second.right == 0 && it_right->second.left != 0) {
|
||||
TileEncoding encoding_left = it_left->second;
|
||||
encoding_left.right = it_right->second.left;
|
||||
const auto it_left_upgrade = tile_encoding_inverse.find(encoding_left);
|
||||
if (it_left_upgrade != tile_encoding_inverse.end())
|
||||
if (it_left_upgrade != tile_encoding_inverse.end()) {
|
||||
left = it_left_upgrade->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (it_right->second.left == 0 && it_left->second.right != 0) {
|
||||
TileEncoding encoding_right = it_right->second;
|
||||
encoding_right.left = it_left->second.right;
|
||||
const auto it_right_upgrade = tile_encoding_inverse.find(encoding_right);
|
||||
if (it_right_upgrade != tile_encoding_inverse.end())
|
||||
if (it_right_upgrade != tile_encoding_inverse.end()) {
|
||||
right = it_right_upgrade->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpgradeTopDown(std::string& top, std::string& down) {
|
||||
const auto it_top = tile_encoding.find(top);
|
||||
if (it_top == tile_encoding.end())
|
||||
if (it_top == tile_encoding.end()) {
|
||||
return;
|
||||
}
|
||||
const auto it_down = tile_encoding.find(down);
|
||||
if (it_down == tile_encoding.end())
|
||||
if (it_down == tile_encoding.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (it_top->second.down == 0 && it_down->second.top != 0) {
|
||||
TileEncoding encoding_top = it_top->second;
|
||||
encoding_top.down = it_down->second.top;
|
||||
const auto it_top_down = tile_encoding_inverse.find(encoding_top);
|
||||
if (it_top_down != tile_encoding_inverse.end())
|
||||
if (it_top_down != tile_encoding_inverse.end()) {
|
||||
top = it_top_down->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (it_down->second.top == 0 && it_top->second.down != 0) {
|
||||
TileEncoding encoding_down = it_down->second;
|
||||
encoding_down.top = it_top->second.down;
|
||||
const auto it_down_top = tile_encoding_inverse.find(encoding_down);
|
||||
if (it_down_top != tile_encoding_inverse.end())
|
||||
if (it_down_top != tile_encoding_inverse.end()) {
|
||||
down = it_down_top->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,6 +347,18 @@ bool ShouldAttemptAutoMerge(Pixel& pixel) {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Pixel::operator==(const Pixel& other) const {
|
||||
return character == other.character && //
|
||||
background_color == other.background_color && //
|
||||
foreground_color == other.foreground_color && //
|
||||
blink == other.blink && //
|
||||
bold == other.bold && //
|
||||
dim == other.dim && //
|
||||
inverted == other.inverted && //
|
||||
underlined == other.underlined && //
|
||||
automerge == other.automerge; //
|
||||
}
|
||||
|
||||
/// A fixed dimension.
|
||||
/// @see Fit
|
||||
/// @see Full
|
||||
@ -409,7 +445,7 @@ std::string& Screen::at(int x, int y) {
|
||||
/// @param x The pixel position along the x-axis.
|
||||
/// @param y The pixel position along the y-axis.
|
||||
Pixel& Screen::PixelAt(int x, int y) {
|
||||
return stencil.Contain(x, y) ? pixels_[y][x] : dev_null_pixel;
|
||||
return stencil.Contain(x, y) ? pixels_[y][x] : dev_null_pixel();
|
||||
}
|
||||
|
||||
/// @brief Return a string to be printed in order to reset the cursor position
|
||||
@ -431,17 +467,19 @@ Pixel& Screen::PixelAt(int x, int y) {
|
||||
///
|
||||
/// @return The string to print in order to reset the cursor position to the
|
||||
/// beginning.
|
||||
std::string Screen::ResetPosition(bool clear) {
|
||||
std::string Screen::ResetPosition(bool clear) const {
|
||||
std::stringstream ss;
|
||||
if (clear) {
|
||||
ss << MOVE_LEFT << CLEAR_LINE;
|
||||
ss << "\r"; // MOVE_LEFT;
|
||||
ss << "\x1b[2K"; // CLEAR_SCREEN;
|
||||
for (int y = 1; y < dimy_; ++y) {
|
||||
ss << MOVE_UP << CLEAR_LINE;
|
||||
ss << "\x1B[1A"; // MOVE_UP;
|
||||
ss << "\x1B[2K"; // CLEAR_LINE;
|
||||
}
|
||||
} else {
|
||||
ss << MOVE_LEFT;
|
||||
ss << "\r"; // MOVE_LEFT;
|
||||
for (int y = 1; y < dimy_; ++y) {
|
||||
ss << MOVE_UP;
|
||||
ss << "\x1B[1A"; // MOVE_UP;
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
@ -462,16 +500,19 @@ void Screen::ApplyShader() {
|
||||
for (int x = 1; x < dimx_; ++x) {
|
||||
// Box drawing character uses exactly 3 byte.
|
||||
Pixel& cur = pixels_[y][x];
|
||||
if (!ShouldAttemptAutoMerge(cur))
|
||||
if (!ShouldAttemptAutoMerge(cur)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pixel& left = pixels_[y][x-1];
|
||||
Pixel& top = pixels_[y-1][x];
|
||||
|
||||
if (ShouldAttemptAutoMerge(left))
|
||||
if (ShouldAttemptAutoMerge(left)) {
|
||||
UpgradeLeftRight(left.character, cur.character);
|
||||
if (ShouldAttemptAutoMerge(top))
|
||||
}
|
||||
if (ShouldAttemptAutoMerge(top)) {
|
||||
UpgradeTopDown(top.character, cur.character);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,11 +7,11 @@
|
||||
|
||||
#include "ftxui/screen/string.hpp"
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <stdint.h> // for uint32_t, uint8_t
|
||||
#include <codecvt> // for codecvt_utf8_utf16
|
||||
#include <locale> // for wstring_convert
|
||||
#include <string> // for string, basic_string, wstring, allocator
|
||||
#include <array> // for array
|
||||
#include <codecvt> // for codecvt_utf8_utf16
|
||||
#include <cstdint> // for uint32_t, uint8_t
|
||||
#include <locale> // for wstring_convert
|
||||
#include <string> // for string, basic_string, wstring
|
||||
|
||||
#include "ftxui/screen/deprecated.hpp" // for wchar_width, wstring_width
|
||||
|
||||
@ -24,115 +24,149 @@ struct Interval {
|
||||
|
||||
// Sorted list of non-overlapping intervals of non-spacing characters
|
||||
// generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c"
|
||||
static const Interval g_combining_characters[] = {
|
||||
{0x0300, 0x036F}, {0x0483, 0x0486}, {0x0488, 0x0489},
|
||||
{0x0591, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2},
|
||||
{0x05C4, 0x05C5}, {0x05C7, 0x05C7}, {0x0600, 0x0603},
|
||||
{0x0610, 0x0615}, {0x064B, 0x065E}, {0x0670, 0x0670},
|
||||
{0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED},
|
||||
{0x070F, 0x070F}, {0x0711, 0x0711}, {0x0730, 0x074A},
|
||||
{0x07A6, 0x07B0}, {0x07EB, 0x07F3}, {0x0901, 0x0902},
|
||||
{0x093C, 0x093C}, {0x0941, 0x0948}, {0x094D, 0x094D},
|
||||
{0x0951, 0x0954}, {0x0962, 0x0963}, {0x0981, 0x0981},
|
||||
{0x09BC, 0x09BC}, {0x09C1, 0x09C4}, {0x09CD, 0x09CD},
|
||||
{0x09E2, 0x09E3}, {0x0A01, 0x0A02}, {0x0A3C, 0x0A3C},
|
||||
{0x0A41, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D},
|
||||
{0x0A70, 0x0A71}, {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC},
|
||||
{0x0AC1, 0x0AC5}, {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD},
|
||||
{0x0AE2, 0x0AE3}, {0x0B01, 0x0B01}, {0x0B3C, 0x0B3C},
|
||||
{0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, {0x0B4D, 0x0B4D},
|
||||
{0x0B56, 0x0B56}, {0x0B82, 0x0B82}, {0x0BC0, 0x0BC0},
|
||||
{0x0BCD, 0x0BCD}, {0x0C3E, 0x0C40}, {0x0C46, 0x0C48},
|
||||
{0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0CBC, 0x0CBC},
|
||||
{0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD},
|
||||
{0x0CE2, 0x0CE3}, {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D},
|
||||
{0x0DCA, 0x0DCA}, {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6},
|
||||
{0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E},
|
||||
{0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC},
|
||||
{0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35},
|
||||
{0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F71, 0x0F7E},
|
||||
{0x0F80, 0x0F84}, {0x0F86, 0x0F87}, {0x0F90, 0x0F97},
|
||||
{0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102D, 0x1030},
|
||||
{0x1032, 0x1032}, {0x1036, 0x1037}, {0x1039, 0x1039},
|
||||
{0x1058, 0x1059}, {0x1160, 0x11FF}, {0x135F, 0x135F},
|
||||
{0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753},
|
||||
{0x1772, 0x1773}, {0x17B4, 0x17B5}, {0x17B7, 0x17BD},
|
||||
{0x17C6, 0x17C6}, {0x17C9, 0x17D3}, {0x17DD, 0x17DD},
|
||||
{0x180B, 0x180D}, {0x18A9, 0x18A9}, {0x1920, 0x1922},
|
||||
{0x1927, 0x1928}, {0x1932, 0x1932}, {0x1939, 0x193B},
|
||||
{0x1A17, 0x1A18}, {0x1B00, 0x1B03}, {0x1B34, 0x1B34},
|
||||
{0x1B36, 0x1B3A}, {0x1B3C, 0x1B3C}, {0x1B42, 0x1B42},
|
||||
{0x1B6B, 0x1B73}, {0x1DC0, 0x1DCA}, {0x1DFE, 0x1DFF},
|
||||
{0x200B, 0x200F}, {0x202A, 0x202E}, {0x2060, 0x2063},
|
||||
{0x206A, 0x206F}, {0x20D0, 0x20EF}, {0x302A, 0x302F},
|
||||
{0x3099, 0x309A}, {0xA806, 0xA806}, {0xA80B, 0xA80B},
|
||||
{0xA825, 0xA826}, {0xFB1E, 0xFB1E}, {0xFE00, 0xFE0F},
|
||||
{0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB},
|
||||
{0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F},
|
||||
{0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x1D167, 0x1D169},
|
||||
{0x1D173, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD},
|
||||
{0x1D242, 0x1D244}, {0xE0001, 0xE0001}, {0xE0020, 0xE007F},
|
||||
{0xE0100, 0xE01EF},
|
||||
const std::array<Interval, 142> g_combining_characters = {
|
||||
Interval{0x0300, 0x036F}, Interval{0x0483, 0x0486},
|
||||
Interval{0x0488, 0x0489}, Interval{0x0591, 0x05BD},
|
||||
Interval{0x05BF, 0x05BF}, Interval{0x05C1, 0x05C2},
|
||||
Interval{0x05C4, 0x05C5}, Interval{0x05C7, 0x05C7},
|
||||
Interval{0x0600, 0x0603}, Interval{0x0610, 0x0615},
|
||||
Interval{0x064B, 0x065E}, Interval{0x0670, 0x0670},
|
||||
Interval{0x06D6, 0x06E4}, Interval{0x06E7, 0x06E8},
|
||||
Interval{0x06EA, 0x06ED}, Interval{0x070F, 0x070F},
|
||||
Interval{0x0711, 0x0711}, Interval{0x0730, 0x074A},
|
||||
Interval{0x07A6, 0x07B0}, Interval{0x07EB, 0x07F3},
|
||||
Interval{0x0901, 0x0902}, Interval{0x093C, 0x093C},
|
||||
Interval{0x0941, 0x0948}, Interval{0x094D, 0x094D},
|
||||
Interval{0x0951, 0x0954}, Interval{0x0962, 0x0963},
|
||||
Interval{0x0981, 0x0981}, Interval{0x09BC, 0x09BC},
|
||||
Interval{0x09C1, 0x09C4}, Interval{0x09CD, 0x09CD},
|
||||
Interval{0x09E2, 0x09E3}, Interval{0x0A01, 0x0A02},
|
||||
Interval{0x0A3C, 0x0A3C}, Interval{0x0A41, 0x0A42},
|
||||
Interval{0x0A47, 0x0A48}, Interval{0x0A4B, 0x0A4D},
|
||||
Interval{0x0A70, 0x0A71}, Interval{0x0A81, 0x0A82},
|
||||
Interval{0x0ABC, 0x0ABC}, Interval{0x0AC1, 0x0AC5},
|
||||
Interval{0x0AC7, 0x0AC8}, Interval{0x0ACD, 0x0ACD},
|
||||
Interval{0x0AE2, 0x0AE3}, Interval{0x0B01, 0x0B01},
|
||||
Interval{0x0B3C, 0x0B3C}, Interval{0x0B3F, 0x0B3F},
|
||||
Interval{0x0B41, 0x0B43}, Interval{0x0B4D, 0x0B4D},
|
||||
Interval{0x0B56, 0x0B56}, Interval{0x0B82, 0x0B82},
|
||||
Interval{0x0BC0, 0x0BC0}, Interval{0x0BCD, 0x0BCD},
|
||||
Interval{0x0C3E, 0x0C40}, Interval{0x0C46, 0x0C48},
|
||||
Interval{0x0C4A, 0x0C4D}, Interval{0x0C55, 0x0C56},
|
||||
Interval{0x0CBC, 0x0CBC}, Interval{0x0CBF, 0x0CBF},
|
||||
Interval{0x0CC6, 0x0CC6}, Interval{0x0CCC, 0x0CCD},
|
||||
Interval{0x0CE2, 0x0CE3}, Interval{0x0D41, 0x0D43},
|
||||
Interval{0x0D4D, 0x0D4D}, Interval{0x0DCA, 0x0DCA},
|
||||
Interval{0x0DD2, 0x0DD4}, Interval{0x0DD6, 0x0DD6},
|
||||
Interval{0x0E31, 0x0E31}, Interval{0x0E34, 0x0E3A},
|
||||
Interval{0x0E47, 0x0E4E}, Interval{0x0EB1, 0x0EB1},
|
||||
Interval{0x0EB4, 0x0EB9}, Interval{0x0EBB, 0x0EBC},
|
||||
Interval{0x0EC8, 0x0ECD}, Interval{0x0F18, 0x0F19},
|
||||
Interval{0x0F35, 0x0F35}, Interval{0x0F37, 0x0F37},
|
||||
Interval{0x0F39, 0x0F39}, Interval{0x0F71, 0x0F7E},
|
||||
Interval{0x0F80, 0x0F84}, Interval{0x0F86, 0x0F87},
|
||||
Interval{0x0F90, 0x0F97}, Interval{0x0F99, 0x0FBC},
|
||||
Interval{0x0FC6, 0x0FC6}, Interval{0x102D, 0x1030},
|
||||
Interval{0x1032, 0x1032}, Interval{0x1036, 0x1037},
|
||||
Interval{0x1039, 0x1039}, Interval{0x1058, 0x1059},
|
||||
Interval{0x1160, 0x11FF}, Interval{0x135F, 0x135F},
|
||||
Interval{0x1712, 0x1714}, Interval{0x1732, 0x1734},
|
||||
Interval{0x1752, 0x1753}, Interval{0x1772, 0x1773},
|
||||
Interval{0x17B4, 0x17B5}, Interval{0x17B7, 0x17BD},
|
||||
Interval{0x17C6, 0x17C6}, Interval{0x17C9, 0x17D3},
|
||||
Interval{0x17DD, 0x17DD}, Interval{0x180B, 0x180D},
|
||||
Interval{0x18A9, 0x18A9}, Interval{0x1920, 0x1922},
|
||||
Interval{0x1927, 0x1928}, Interval{0x1932, 0x1932},
|
||||
Interval{0x1939, 0x193B}, Interval{0x1A17, 0x1A18},
|
||||
Interval{0x1B00, 0x1B03}, Interval{0x1B34, 0x1B34},
|
||||
Interval{0x1B36, 0x1B3A}, Interval{0x1B3C, 0x1B3C},
|
||||
Interval{0x1B42, 0x1B42}, Interval{0x1B6B, 0x1B73},
|
||||
Interval{0x1DC0, 0x1DCA}, Interval{0x1DFE, 0x1DFF},
|
||||
Interval{0x200B, 0x200F}, Interval{0x202A, 0x202E},
|
||||
Interval{0x2060, 0x2063}, Interval{0x206A, 0x206F},
|
||||
Interval{0x20D0, 0x20EF}, Interval{0x302A, 0x302F},
|
||||
Interval{0x3099, 0x309A}, Interval{0xA806, 0xA806},
|
||||
Interval{0xA80B, 0xA80B}, Interval{0xA825, 0xA826},
|
||||
Interval{0xFB1E, 0xFB1E}, Interval{0xFE00, 0xFE0F},
|
||||
Interval{0xFE20, 0xFE23}, Interval{0xFEFF, 0xFEFF},
|
||||
Interval{0xFFF9, 0xFFFB}, Interval{0x10A01, 0x10A03},
|
||||
Interval{0x10A05, 0x10A06}, Interval{0x10A0C, 0x10A0F},
|
||||
Interval{0x10A38, 0x10A3A}, Interval{0x10A3F, 0x10A3F},
|
||||
Interval{0x1D167, 0x1D169}, Interval{0x1D173, 0x1D182},
|
||||
Interval{0x1D185, 0x1D18B}, Interval{0x1D1AA, 0x1D1AD},
|
||||
Interval{0x1D242, 0x1D244}, Interval{0xE0001, 0xE0001},
|
||||
Interval{0xE0020, 0xE007F}, Interval{0xE0100, 0xE01EF},
|
||||
};
|
||||
|
||||
static const Interval g_full_width_characters[] = {
|
||||
{0x1100, 0x115f}, {0x2329, 0x2329}, {0x232a, 0x232a}, {0x2e80, 0x303e},
|
||||
{0x3040, 0xa4cf}, {0xac00, 0xd7a3}, {0xf900, 0xfaff}, {0xfe10, 0xfe19},
|
||||
{0xfe30, 0xfe6f}, {0xff00, 0xff60}, {0xffe0, 0xffe6}, {0x20000, 0x2fffd},
|
||||
{0x30000, 0x3fffd},
|
||||
const std::array<Interval, 13> g_full_width_characters = {
|
||||
Interval{0x1100, 0x115f}, Interval{0x2329, 0x2329},
|
||||
Interval{0x232a, 0x232a}, Interval{0x2e80, 0x303e},
|
||||
Interval{0x3040, 0xa4cf}, Interval{0xac00, 0xd7a3},
|
||||
Interval{0xf900, 0xfaff}, Interval{0xfe10, 0xfe19},
|
||||
Interval{0xfe30, 0xfe6f}, Interval{0xff00, 0xff60},
|
||||
Interval{0xffe0, 0xffe6}, Interval{0x20000, 0x2fffd},
|
||||
Interval{0x30000, 0x3fffd},
|
||||
};
|
||||
|
||||
// Find a codepoint inside a sorted list of Interval.
|
||||
int Bisearch(uint32_t ucs, const Interval* table, int max) {
|
||||
if (ucs < table[0].first || ucs > table[max].last)
|
||||
return 0;
|
||||
bool Bisearch(uint32_t ucs, const Interval* table, int max) {
|
||||
if (ucs < table[0].first || ucs > table[max].last) { // NOLINT
|
||||
return false;
|
||||
}
|
||||
|
||||
int min = 0;
|
||||
while (max >= min) {
|
||||
int mid = (min + max) / 2;
|
||||
if (ucs > table[mid].last)
|
||||
if (ucs > table[mid].last) { // NOLINT
|
||||
min = mid + 1;
|
||||
else if (ucs < table[mid].first)
|
||||
} else if (ucs < table[mid].first) { // NOLINT
|
||||
max = mid - 1;
|
||||
else
|
||||
return 1;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsCombining(uint32_t ucs) {
|
||||
return Bisearch(ucs, g_combining_characters,
|
||||
sizeof(g_combining_characters) / sizeof(Interval) - 1);
|
||||
return Bisearch(ucs, g_combining_characters.data(),
|
||||
g_combining_characters.size() - 1);
|
||||
}
|
||||
|
||||
bool IsFullWidth(uint32_t ucs) {
|
||||
if (ucs < 0x0300) // Quick path:
|
||||
if (ucs < 0x0300) // Quick path: // NOLINT
|
||||
return false;
|
||||
|
||||
return Bisearch(ucs, g_full_width_characters,
|
||||
sizeof(g_full_width_characters) / sizeof(Interval) - 1);
|
||||
return Bisearch(ucs, g_full_width_characters.data(),
|
||||
g_full_width_characters.size() - 1);
|
||||
}
|
||||
|
||||
bool IsControl(uint32_t ucs) {
|
||||
if (ucs == 0)
|
||||
if (ucs == 0) {
|
||||
return true;
|
||||
if (ucs < 32)
|
||||
}
|
||||
if (ucs < 32) { // NOLINT
|
||||
return true;
|
||||
if (ucs >= 0x7f && ucs < 0xa0)
|
||||
}
|
||||
if (ucs >= 0x7f && ucs < 0xa0) { // NOLINT
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int codepoint_width(uint32_t ucs) {
|
||||
if (IsControl(ucs))
|
||||
if (IsControl(ucs)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (IsCombining(ucs))
|
||||
if (IsCombining(ucs)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (IsFullWidth(ucs))
|
||||
if (IsFullWidth(ucs)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -152,50 +186,53 @@ bool EatCodePoint(const std::string& input,
|
||||
uint8_t byte_1 = input[start];
|
||||
|
||||
// 1 byte string.
|
||||
if ((byte_1 & 0b1000'0000) == 0b0000'0000) {
|
||||
*ucs = byte_1 & 0b0111'1111;
|
||||
if ((byte_1 & 0b1000'0000) == 0b0000'0000) { // NOLINT
|
||||
*ucs = byte_1 & 0b0111'1111; // NOLINT
|
||||
*end = start + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2 byte string.
|
||||
if ((byte_1 & 0b1110'0000) == 0b1100'0000 && start + 1 < input.size()) {
|
||||
if ((byte_1 & 0b1110'0000) == 0b1100'0000 && // NOLINT
|
||||
start + 1 < input.size()) {
|
||||
uint8_t byte_2 = input[start + 1];
|
||||
*ucs = 0;
|
||||
*ucs += byte_1 & 0b0001'1111;
|
||||
*ucs <<= 6;
|
||||
*ucs += byte_2 & 0b0011'1111;
|
||||
*ucs += byte_1 & 0b0001'1111; // NOLINT
|
||||
*ucs <<= 6; // NOLINT
|
||||
*ucs += byte_2 & 0b0011'1111; // NOLINT
|
||||
*end = start + 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3 byte string.
|
||||
if ((byte_1 & 0b1111'0000) == 0b1110'0000 && start + 2 < input.size()) {
|
||||
if ((byte_1 & 0b1111'0000) == 0b1110'0000 && // NOLINT
|
||||
start + 2 < input.size()) {
|
||||
uint8_t byte_2 = input[start + 1];
|
||||
uint8_t byte_3 = input[start + 2];
|
||||
*ucs = 0;
|
||||
*ucs += byte_1 & 0b0000'1111;
|
||||
*ucs <<= 6;
|
||||
*ucs += byte_2 & 0b0011'1111;
|
||||
*ucs <<= 6;
|
||||
*ucs += byte_3 & 0b0011'1111;
|
||||
*ucs += byte_1 & 0b0000'1111; // NOLINT
|
||||
*ucs <<= 6; // NOLINT
|
||||
*ucs += byte_2 & 0b0011'1111; // NOLINT
|
||||
*ucs <<= 6; // NOLINT
|
||||
*ucs += byte_3 & 0b0011'1111; // NOLINT
|
||||
*end = start + 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4 byte string.
|
||||
if ((byte_1 & 0b1111'1000) == 0b1111'0000 && start + 3 < input.size()) {
|
||||
if ((byte_1 & 0b1111'1000) == 0b1111'0000 && // NOLINT
|
||||
start + 3 < input.size()) {
|
||||
uint8_t byte_2 = input[start + 1];
|
||||
uint8_t byte_3 = input[start + 2];
|
||||
uint8_t byte_4 = input[start + 3];
|
||||
*ucs = 0;
|
||||
*ucs += byte_1 & 0b0000'0111;
|
||||
*ucs <<= 6;
|
||||
*ucs += byte_2 & 0b0011'1111;
|
||||
*ucs <<= 6;
|
||||
*ucs += byte_3 & 0b0011'1111;
|
||||
*ucs <<= 6;
|
||||
*ucs += byte_4 & 0b0011'1111;
|
||||
*ucs += byte_1 & 0b0000'0111; // NOLINT
|
||||
*ucs <<= 6; // NOLINT
|
||||
*ucs += byte_2 & 0b0011'1111; // NOLINT
|
||||
*ucs <<= 6; // NOLINT
|
||||
*ucs += byte_3 & 0b0011'1111; // NOLINT
|
||||
*ucs <<= 6; // NOLINT
|
||||
*ucs += byte_4 & 0b0011'1111; // NOLINT
|
||||
*end = start + 4;
|
||||
return true;
|
||||
}
|
||||
@ -216,8 +253,9 @@ int wstring_width(const std::wstring& text) {
|
||||
|
||||
for (const wchar_t& it : text) {
|
||||
int w = wchar_width(it);
|
||||
if (w < 0)
|
||||
if (w < 0) {
|
||||
return -1;
|
||||
}
|
||||
width += w;
|
||||
}
|
||||
return width;
|
||||
@ -228,14 +266,17 @@ int string_width(const std::string& input) {
|
||||
size_t start = 0;
|
||||
while (start < input.size()) {
|
||||
uint32_t codepoint = 0;
|
||||
if (!EatCodePoint(input, start, &start, &codepoint))
|
||||
if (!EatCodePoint(input, start, &start, &codepoint)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsControl(codepoint))
|
||||
if (IsControl(codepoint)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsCombining(codepoint))
|
||||
if (IsCombining(codepoint)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsFullWidth(codepoint)) {
|
||||
width += 2;
|
||||
@ -254,7 +295,7 @@ std::vector<std::string> Utf8ToGlyphs(const std::string& input) {
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
while (start < input.size()) {
|
||||
uint32_t codepoint;
|
||||
uint32_t codepoint = 0;
|
||||
if (!EatCodePoint(input, start, &end, &codepoint)) {
|
||||
start = end;
|
||||
continue;
|
||||
@ -264,13 +305,15 @@ std::vector<std::string> Utf8ToGlyphs(const std::string& input) {
|
||||
start = end;
|
||||
|
||||
// Ignore control characters.
|
||||
if (IsControl(codepoint))
|
||||
if (IsControl(codepoint)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Combining characters are put with the previous glyph they are modifying.
|
||||
if (IsCombining(codepoint)) {
|
||||
if (out.size() != 0)
|
||||
if (!out.empty()) {
|
||||
out.back() += append;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -278,7 +321,7 @@ std::vector<std::string> Utf8ToGlyphs(const std::string& input) {
|
||||
// string to reserve the space the first is taking.
|
||||
if (IsFullWidth(codepoint)) {
|
||||
out.push_back(append);
|
||||
out.push_back("");
|
||||
out.emplace_back("");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -288,14 +331,13 @@ std::vector<std::string> Utf8ToGlyphs(const std::string& input) {
|
||||
return out;
|
||||
}
|
||||
|
||||
int GlyphPosition(const std::string& input,
|
||||
size_t glyph_to_skip,
|
||||
size_t start) {
|
||||
if (glyph_to_skip <= 0)
|
||||
int GlyphPosition(const std::string& input, size_t glyph_index, size_t start) {
|
||||
if (glyph_index <= 0) {
|
||||
return 0;
|
||||
}
|
||||
size_t end = 0;
|
||||
while (start < input.size()) {
|
||||
uint32_t codepoint;
|
||||
uint32_t codepoint = 0;
|
||||
bool eaten = EatCodePoint(input, start, &end, &codepoint);
|
||||
|
||||
// Ignore invalid, control characters and combining characters.
|
||||
@ -306,14 +348,15 @@ int GlyphPosition(const std::string& input,
|
||||
|
||||
// We eat the beginning of the next glyph. If we are eating the one
|
||||
// requested, return its start position immediately.
|
||||
if (glyph_to_skip == 0)
|
||||
return start;
|
||||
if (glyph_index == 0) {
|
||||
return static_cast<int>(start);
|
||||
}
|
||||
|
||||
// Otherwise, skip this glyph and iterate:
|
||||
glyph_to_skip--;
|
||||
glyph_index--;
|
||||
start = end;
|
||||
}
|
||||
return input.size();
|
||||
return static_cast<int>(input.size());
|
||||
}
|
||||
|
||||
std::vector<int> CellToGlyphIndex(const std::string& input) {
|
||||
@ -323,13 +366,14 @@ std::vector<int> CellToGlyphIndex(const std::string& input) {
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
while (start < input.size()) {
|
||||
uint32_t codepoint;
|
||||
uint32_t codepoint = 0;
|
||||
bool eaten = EatCodePoint(input, start, &end, &codepoint);
|
||||
start = end;
|
||||
|
||||
// Ignore invalid / control characters.
|
||||
if (!eaten || IsControl(codepoint))
|
||||
if (!eaten || IsControl(codepoint)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Combining characters are put with the previous glyph they are modifying.
|
||||
if (IsCombining(codepoint)) {
|
||||
@ -361,19 +405,21 @@ int GlyphCount(const std::string& input) {
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
while (start < input.size()) {
|
||||
uint32_t codepoint;
|
||||
uint32_t codepoint = 0;
|
||||
bool eaten = EatCodePoint(input, start, &end, &codepoint);
|
||||
start = end;
|
||||
|
||||
// Ignore invalid characters:
|
||||
if (!eaten || IsControl(codepoint))
|
||||
if (!eaten || IsControl(codepoint)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore combining characters, except when they don't have a preceding to
|
||||
// combine with.
|
||||
if (IsCombining(codepoint)) {
|
||||
if (size == 0)
|
||||
if (size == 0) {
|
||||
size++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -18,89 +18,57 @@
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
// This dimension was chosen arbitrarily to be able to display:
|
||||
// https://arthursonzogni.com/FTXUI/examples
|
||||
// This will have to be improved when someone has time to implement and need
|
||||
// it.
|
||||
static Dimensions fallback_size{140, 43};
|
||||
Dimensions Terminal::Size() {
|
||||
return fallback_size;
|
||||
}
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
// The terminal size in VT100 was 80x24. It is still used nowadays by
|
||||
// default in many terminal emulator. That's a good choice for a fallback
|
||||
// value.
|
||||
static Dimensions fallback_size{80, 24};
|
||||
Dimensions Terminal::Size() {
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
|
||||
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
|
||||
return Dimensions{csbi.srWindow.Right - csbi.srWindow.Left + 1,
|
||||
csbi.srWindow.Bottom - csbi.srWindow.Top + 1};
|
||||
}
|
||||
|
||||
return fallback_size;
|
||||
}
|
||||
|
||||
#else
|
||||
// The terminal size in VT100 was 80x24. It is still used nowadays by
|
||||
// default in many terminal emulator. That's a good choice for a fallback
|
||||
// value.
|
||||
static Dimensions fallback_size{80, 24};
|
||||
Dimensions Terminal::Size() {
|
||||
winsize w{};
|
||||
const int status = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
// The ioctl return value result should be checked. Some operating systems
|
||||
// don't support TIOCGWINSZ.
|
||||
if (w.ws_col == 0 || w.ws_row == 0 || status < 0) {
|
||||
return fallback_size;
|
||||
}
|
||||
return Dimensions{w.ws_col, w.ws_row};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// @brief Override terminal size in case auto-detection fails
|
||||
/// @param fallbackSize Terminal dimensions to fallback to
|
||||
void Terminal::SetFallbackSize(const Dimensions& fallbackSize) {
|
||||
fallback_size = fallbackSize;
|
||||
}
|
||||
|
||||
namespace {
|
||||
Dimensions& FallbackSize() {
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
// This dimension was chosen arbitrarily to be able to display:
|
||||
// https://arthursonzogni.com/FTXUI/examples
|
||||
// This will have to be improved when someone has time to implement and need
|
||||
// it.
|
||||
constexpr int fallback_width = 140;
|
||||
constexpr int fallback_height = 43;
|
||||
#else
|
||||
// The terminal size in VT100 was 80x24. It is still used nowadays by
|
||||
// default in many terminal emulator. That's a good choice for a fallback
|
||||
// value.
|
||||
constexpr int fallback_width = 80;
|
||||
constexpr int fallback_height = 24;
|
||||
#endif
|
||||
static Dimensions g_fallback_size{fallback_width, fallback_height};
|
||||
return g_fallback_size;
|
||||
}
|
||||
|
||||
const char* Safe(const char* c) {
|
||||
return c ? c : "";
|
||||
return (c != nullptr) ? c : "";
|
||||
}
|
||||
|
||||
bool Contains(const std::string& s, const char* key) {
|
||||
return s.find(key) != std::string::npos;
|
||||
}
|
||||
|
||||
static bool cached = false;
|
||||
Terminal::Color cached_supported_color;
|
||||
Terminal::Color ComputeColorSupport() {
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
return Terminal::Color::TrueColor;
|
||||
#endif
|
||||
|
||||
std::string COLORTERM = Safe(std::getenv("COLORTERM"));
|
||||
if (Contains(COLORTERM, "24bit") || Contains(COLORTERM, "truecolor"))
|
||||
std::string COLORTERM = Safe(std::getenv("COLORTERM")); // NOLINT
|
||||
if (Contains(COLORTERM, "24bit") || Contains(COLORTERM, "truecolor")) {
|
||||
return Terminal::Color::TrueColor;
|
||||
}
|
||||
|
||||
std::string TERM = Safe(std::getenv("TERM"));
|
||||
if (Contains(COLORTERM, "256") || Contains(TERM, "256"))
|
||||
std::string TERM = Safe(std::getenv("TERM")); // NOLINT
|
||||
if (Contains(COLORTERM, "256") || Contains(TERM, "256")) {
|
||||
return Terminal::Color::Palette256;
|
||||
}
|
||||
|
||||
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
|
||||
// Microsoft terminals do not properly declare themselve supporting true
|
||||
// colors: https://github.com/microsoft/terminal/issues/1040
|
||||
// As a fallback, assume microsoft terminal are the ones not setting those
|
||||
// variables, and enable true colors.
|
||||
if (TERM == "" && COLORTERM == "")
|
||||
if (TERM == "" && COLORTERM == "") {
|
||||
return Terminal::Color::TrueColor;
|
||||
}
|
||||
#endif
|
||||
|
||||
return Terminal::Color::Palette16;
|
||||
@ -108,7 +76,43 @@ Terminal::Color ComputeColorSupport() {
|
||||
|
||||
} // namespace
|
||||
|
||||
Dimensions Terminal::Size() {
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
// This dimension was chosen arbitrarily to be able to display:
|
||||
// https://arthursonzogni.com/FTXUI/examples
|
||||
// This will have to be improved when someone has time to implement and need
|
||||
// it.
|
||||
return FallbackSize();
|
||||
#elif defined(_WIN32)
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
|
||||
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
|
||||
return Dimensions{csbi.srWindow.Right - csbi.srWindow.Left + 1,
|
||||
csbi.srWindow.Bottom - csbi.srWindow.Top + 1};
|
||||
}
|
||||
|
||||
return FallbackSize();
|
||||
#else
|
||||
winsize w{};
|
||||
const int status = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); // NOLINT
|
||||
// The ioctl return value result should be checked. Some operating systems
|
||||
// don't support TIOCGWINSZ.
|
||||
if (w.ws_col == 0 || w.ws_row == 0 || status < 0) {
|
||||
return FallbackSize();
|
||||
}
|
||||
return Dimensions{w.ws_col, w.ws_row};
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Override terminal size in case auto-detection fails
|
||||
/// @param fallbackSize Terminal dimensions to fallback to
|
||||
void Terminal::SetFallbackSize(const Dimensions& fallbackSize) {
|
||||
FallbackSize() = fallbackSize;
|
||||
}
|
||||
|
||||
Terminal::Color Terminal::ColorSupport() {
|
||||
static bool cached = false;
|
||||
static Terminal::Color cached_supported_color;
|
||||
if (!cached) {
|
||||
cached = true;
|
||||
cached_supported_color = ComputeColorSupport();
|
||||
|
Loading…
Reference in New Issue
Block a user