Add clang-tidy. (#368)

This commit is contained in:
Arthur Sonzogni 2022-03-31 02:17:43 +02:00 committed by GitHub
parent 62fb6298be
commit aebde94352
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
80 changed files with 1958 additions and 1376 deletions

22
.clang-tidy Normal file
View 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

View File

@ -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 "Set to ON to build tests" OFF)
option(FTXUI_BUILD_TESTS_FUZZER "Set to ON to enable fuzzing" OFF) option(FTXUI_BUILD_TESTS_FUZZER "Set to ON to enable fuzzing" OFF)
option(FTXUI_ENABLE_INSTALL "Generate the install target" ON) 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 \ 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 \ 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) ${FTXUI_MICROSOFT_TERMINAL_FALLBACK_HELP_TEXT} OFF)
endif() endif()
add_library(screen add_library(screen
include/ftxui/screen/box.hpp include/ftxui/screen/box.hpp
include/ftxui/screen/color.hpp include/ftxui/screen/color.hpp

View File

@ -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) function(ftxui_set_options library)
set_target_properties(${library} PROPERTIES OUTPUT_NAME "ftxui-${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} target_include_directories(${library}
PUBLIC PUBLIC

View File

@ -12,6 +12,9 @@ add_subdirectory(dom)
if (EMSCRIPTEN) if (EMSCRIPTEN)
# 32MB should be enough to run all the examples, in debug mode. # 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 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) get_property(EXAMPLES GLOBAL PROPERTY FTXUI::EXAMPLES)
foreach(file foreach(file

View File

@ -1,13 +1,12 @@
#include <array> // for array #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 <string> // for operator+, to_string
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Checkbox, Renderer, Vertical #include "ftxui/component/component.hpp" // for Checkbox, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase #include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive #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; using namespace ftxui;

View File

@ -17,7 +17,7 @@ using namespace ftxui;
MenuEntryOption Colored(ftxui::Color c) { MenuEntryOption Colored(ftxui::Color c) {
MenuEntryOption option; MenuEntryOption option;
option.transform = [c](EntryState state) { option.transform = [c](EntryState state) {
state.label = (state.active? "> " : " ") + state.label; state.label = (state.active ? "> " : " ") + state.label;
Element e = text(state.label) | color(c); Element e = text(state.label) | color(c);
if (state.focused) if (state.focused)
e = e | inverted; e = e | inverted;

View File

@ -33,7 +33,7 @@ Component HMenu5(std::vector<std::string>* entries, int* selected);
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::TerminalOutput(); auto screen = ScreenInteractive::TerminalOutput();
std::vector<std::string> entries = { std::vector<std::string> entries{
"Monkey", "Dog", "Cat", "Bird", "Elephant", "Cat", "Monkey", "Dog", "Cat", "Bird", "Elephant", "Cat",
}; };
std::array<int, 12> selected = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; std::array<int, 12> selected = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

View File

@ -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/dom/elements.hpp> // for text, operator|, vbox, border, Element, Fit, hbox
#include <ftxui/screen/screen.hpp> // for Full, Screen #include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator #include <memory> // for allocator
@ -5,7 +6,7 @@
#include "ftxui/dom/node.hpp" // for Render #include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui #include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) { int main() {
using namespace ftxui; using namespace ftxui;
auto document = // auto document = //
hbox({ hbox({
@ -30,6 +31,7 @@ int main(int argc, const char* argv[]) {
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document)); auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document); Render(screen, document);
screen.Print(); screen.Print();
return EXIT_SUCCESS;
} }
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -12,7 +12,7 @@
using namespace ftxui; using namespace ftxui;
#include "./color_info_sorted_2d.ipp" // for ColorInfoSorted2D #include "./color_info_sorted_2d.ipp" // for ColorInfoSorted2D
int main(int argc, const char* argv[]) { int main() {
// clang-format off // clang-format off
auto basic_color_display = auto basic_color_display =
vbox( vbox(
@ -83,14 +83,18 @@ int main(int argc, const char* argv[]) {
// True color display. // True color display.
auto true_color_display = text("TrueColors: 24bits:"); 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; Elements array;
for (int value = 0; value < 255; value += 16) { for (int value = 0; value < max_value; value += 2 * value_increment) {
Elements line; Elements line;
for (int hue = 0; hue < 255; hue += 6) { for (int hue = 0; hue < max_value; hue += hue_increment) {
line.push_back(text("") // line.push_back(
text("") //
| color(Color::HSV(hue, saturation, value)) // | color(Color::HSV(hue, saturation, value)) //
| bgcolor(Color::HSV(hue, saturation, value + 8))); | bgcolor(Color::HSV(hue, saturation, value + value_increment)));
} }
array.push_back(hbox(std::move(line))); array.push_back(hbox(std::move(line)));
} }

View File

@ -1,9 +1,10 @@
#include <chrono> // for operator""s, chrono_literals #include <chrono> // for operator""s, chrono_literals
#include <cmath> // for sin #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 <ftxui/screen/screen.hpp> // for Full, Screen
#include <functional> // for ref, reference_wrapper #include <functional> // for ref, reference_wrapper
#include <iostream> // for cout, ostream #include <iostream> // for cout, ostream
#include <memory> // for shared_ptr
#include <string> // for operator<<, string #include <string> // for operator<<, string
#include <thread> // for sleep_for #include <thread> // for sleep_for
#include <vector> // for vector #include <vector> // for vector
@ -13,15 +14,15 @@
class Graph { class Graph {
public: public:
std::vector<int> operator()(int width, int height) { std::vector<int> operator()(int width, int height) const {
std::vector<int> output(width); std::vector<int> output(width);
for (int i = 0; i < width; ++i) { for (int i = 0; i < width; ++i) {
float v = 0; float v = 0;
v += 0.1f * sin((i + shift) * 0.1f); v += 0.1f * sin((i + shift) * 0.1f); // NOLINT
v += 0.2f * sin((i + shift + 10) * 0.15f); v += 0.2f * sin((i + shift + 10) * 0.15f); // NOLINT
v += 0.1f * sin((i + shift) * 0.03f); v += 0.1f * sin((i + shift) * 0.03f); // NOLINT
v *= height; v *= height; // NOLINT
v += 0.5f * height; v += 0.5f * height; // NOLINT
output[i] = static_cast<int>(v); output[i] = static_cast<int>(v);
} }
return output; return output;
@ -37,7 +38,7 @@ std::vector<int> triangle(int width, int height) {
return output; return output;
} }
int main(int argc, const char* argv[]) { int main() {
using namespace ftxui; using namespace ftxui;
using namespace std::chrono_literals; using namespace std::chrono_literals;
@ -45,8 +46,7 @@ int main(int argc, const char* argv[]) {
std::string reset_position; std::string reset_position;
for (int i = 0;; ++i) { for (int i = 0;; ++i) {
auto document = auto document = hbox({
hbox({
vbox({ vbox({
graph(std::ref(my_graph)), graph(std::ref(my_graph)),
separator(), separator(),
@ -60,8 +60,12 @@ int main(int argc, const char* argv[]) {
separator(), separator(),
graph(std::ref(my_graph)) | color(Color::YellowLight), graph(std::ref(my_graph)) | color(Color::YellowLight),
}) | flex, }) | flex,
}) | });
border | size(HEIGHT, GREATER_THAN, 40);
document |= border;
const int min_width = 40;
document |= size(HEIGHT, GREATER_THAN, min_width);
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document)); auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document); Render(screen, document);
@ -69,7 +73,8 @@ int main(int argc, const char* argv[]) {
screen.Print(); screen.Print();
reset_position = screen.ResetPosition(); 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++; my_graph.shift++;
} }

View File

@ -1,11 +1,12 @@
#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 <ftxui/screen/screen.hpp> // for Fixed, Screen
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/dom/node.hpp" // for Render #include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for Color, Color::DarkBlue, Color::Green, Color::Red, ftxui #include "ftxui/screen/color.hpp" // for Color, Color::DarkBlue, Color::Red, ftxui
int main(void) { int main() {
using namespace ftxui; using namespace ftxui;
Element document = graph([](int x, int y) { Element document = graph([](int x, int y) {
std::vector<int> result(x, 0); std::vector<int> result(x, 0);
@ -13,13 +14,19 @@ int main(void) {
result[i] = ((3 * i) / 2) % y; result[i] = ((3 * i) / 2) % y;
} }
return result; return result;
}) | });
color(Color::Red) | border | color(Color::Green) |
bgcolor(Color::DarkBlue);
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); Render(screen, document);
screen.Print(); screen.Print();
return EXIT_SUCCESS;
} }
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@ -44,12 +44,12 @@
}); });
let stdin_buffer = []; let stdin_buffer = [];
let stdin = () => { const stdin = () => {
return stdin_buffer.shift() || 0; return stdin_buffer.shift() || 0;
} }
stdout_buffer = []; let stdout_buffer = [];
let stdout = code => { const stdout = code => {
if (code == 0) { if (code == 0) {
term.write(new Uint8Array(stdout_buffer)); term.write(new Uint8Array(stdout_buffer));
stdout_buffer = []; stdout_buffer = [];
@ -57,7 +57,15 @@
stdout_buffer.push(code) 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(); const term = new Terminal();
term.open(document.querySelector('#terminal')); term.open(document.querySelector('#terminal'));
term.resize(140,43); term.resize(140,43);
@ -69,7 +77,9 @@
term.onBinary(onBinary); term.onBinary(onBinary);
term.onData(onBinary) term.onData(onBinary)
window.Module = { window.Module = {
preRun: () => { FS.init(stdin, stdout, stderr); }, preRun: () => {
FS.init(stdin, stdout, stderr);
},
postRun: [], postRun: [],
onRuntimeInitialized: () => {}, onRuntimeInitialized: () => {},
}; };

View File

@ -8,7 +8,7 @@
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/component_base.hpp" // for Component, Components #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/dom/elements.hpp" // for Element
#include "ftxui/util/ref.hpp" // for Ref, ConstStringRef, ConstStringListRef, StringRef #include "ftxui/util/ref.hpp" // for Ref, ConstStringRef, ConstStringListRef, StringRef

View File

@ -3,11 +3,11 @@
#include <chrono> // for milliseconds #include <chrono> // for milliseconds
#include <ftxui/component/animation.hpp> // for Duration, QuadraticInOut, Function #include <ftxui/component/animation.hpp> // for Duration, QuadraticInOut, Function
#include <ftxui/dom/elements.hpp> // for Decorator, bold, inverted, operator|, Element, nothing #include <ftxui/dom/elements.hpp> // for Element
#include <ftxui/util/ref.hpp> // for Ref #include <ftxui/util/ref.hpp> // for Ref
#include <functional> // for function #include <functional> // for function
#include <optional> // for optional #include <optional> // for optional
#include <string> // for string, allocator #include <string> // for string
#include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::White #include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::White
@ -70,7 +70,7 @@ struct AnimatedColorsOption {
/// @brief Option for the MenuEntry component. /// @brief Option for the MenuEntry component.
/// @ingroup component /// @ingroup component
struct MenuEntryOption { struct MenuEntryOption {
std::function<Element(EntryState state)> transform; std::function<Element(const EntryState& state)> transform;
AnimatedColorsOption animated_colors; AnimatedColorsOption animated_colors;
}; };
@ -115,7 +115,7 @@ struct ButtonOption {
Color foreground_active); Color foreground_active);
// Style: // Style:
std::function<Element(EntryState)> transform; std::function<Element(const EntryState&)> transform;
AnimatedColorsOption animated_colors; AnimatedColorsOption animated_colors;
}; };
@ -126,7 +126,7 @@ struct CheckboxOption {
static CheckboxOption Simple(); static CheckboxOption Simple();
// Style: // Style:
std::function<Element(EntryState)> transform; std::function<Element(const EntryState&)> transform;
// Observer: // Observer:
/// Called when the user change the state. /// Called when the user change the state.
@ -156,7 +156,7 @@ struct RadioboxOption {
static RadioboxOption Simple(); static RadioboxOption Simple();
// Style: // Style:
std::function<Element(EntryState)> transform; std::function<Element(const EntryState&)> transform;
// Observers: // Observers:
/// Called when the selected entry changes. /// Called when the selected entry changes.

View File

@ -54,7 +54,7 @@ struct Event {
static const Event PageDown; static const Event PageDown;
// --- Custom --- // --- Custom ---
static Event Custom; static const Event Custom;
//--- Method section --------------------------------------------------------- //--- Method section ---------------------------------------------------------
bool is_character() const { return type_ == Type::Character; } bool is_character() const { return type_ == Type::Character; }

View File

@ -1,7 +1,7 @@
#ifndef FTXUI_DOM_CANVAS_HPP #ifndef FTXUI_DOM_CANVAS_HPP
#define 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 <functional> // for function
#include <string> // for string #include <string> // for string
#include <unordered_map> // for unordered_map #include <unordered_map> // for unordered_map
@ -13,7 +13,7 @@ namespace ftxui {
struct Canvas { struct Canvas {
public: public:
Canvas() {} Canvas() = default;
Canvas(int width, int height); Canvas(int width, int height);
// Getters: // Getters:
@ -114,7 +114,8 @@ struct Canvas {
struct XYHash { struct XYHash {
size_t operator()(const XY& xy) const { 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);
} }
}; };

View File

@ -1,5 +1,5 @@
#ifndef FTXUI_DOM_DEPRECRATED_HPP #ifndef FTXUI_DOM_DEPRECATED_HPP
#define FTXUI_DOM_DEPRECRATED_HPP #define FTXUI_DOM_DEPRECATED_HPP
#include "ftxui/dom/elements.hpp" #include "ftxui/dom/elements.hpp"
@ -9,7 +9,7 @@ Element vtext(std::wstring text);
Elements paragraph(std::wstring text); Elements paragraph(std::wstring text);
} // namespace ftxui } // namespace ftxui
#endif /* end of include guard: FTXUI_DOM_DEPRECRATED_HPP */ #endif // FTXUI_DOM_DEPRECATED_HPP
// Copyright 2021 Arthur Sonzogni. All rights reserved. // Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in

View File

@ -35,7 +35,7 @@ Decorator operator|(Decorator, Decorator);
// --- Widget --- // --- Widget ---
Element text(std::string text); Element text(std::string text);
Element vtext(std::string text); Element vtext(std::string text);
Element separator(void); Element separator();
Element separatorLight(); Element separatorLight();
Element separatorHeavy(); Element separatorHeavy();
Element separatorDouble(); Element separatorDouble();
@ -45,18 +45,18 @@ Element separator(Pixel);
Element separatorCharacter(std::string); Element separatorCharacter(std::string);
Element separatorHSelector(float left, Element separatorHSelector(float left,
float right, float right,
Color background, Color unselected_color,
Color foreground); Color selected_color);
Element separatorVSelector(float up, Element separatorVSelector(float up,
float down, float down,
Color background, Color unselected_color,
Color foreground); Color selected_color);
Element gauge(float ratio); Element gauge(float progress);
Element gaugeLeft(float ratio); Element gaugeLeft(float progress);
Element gaugeRight(float ratio); Element gaugeRight(float progress);
Element gaugeUp(float ratio); Element gaugeUp(float progress);
Element gaugeDown(float ratio); Element gaugeDown(float progress);
Element gaugeDirection(float ratio, GaugeDirection); Element gaugeDirection(float progress, GaugeDirection);
Element border(Element); Element border(Element);
Element borderLight(Element); Element borderLight(Element);
Element borderHeavy(Element); Element borderHeavy(Element);
@ -64,14 +64,14 @@ Element borderDouble(Element);
Element borderRounded(Element); Element borderRounded(Element);
Element borderEmpty(Element); Element borderEmpty(Element);
Decorator borderStyled(BorderStyle); Decorator borderStyled(BorderStyle);
Decorator borderWith(Pixel); Decorator borderWith(const Pixel&);
Element window(Element title, Element content); Element window(Element title, Element content);
Element spinner(int charset_index, size_t image_index); Element spinner(int charset_index, size_t image_index);
Element paragraph(std::string text); Element paragraph(const std::string& text);
Element paragraphAlignLeft(std::string text); Element paragraphAlignLeft(const std::string& text);
Element paragraphAlignRight(std::string text); Element paragraphAlignRight(const std::string& text);
Element paragraphAlignCenter(std::string text); Element paragraphAlignCenter(const std::string& text);
Element paragraphAlignJustify(std::string text); Element paragraphAlignJustify(const std::string& text);
Element graph(GraphFunction); Element graph(GraphFunction);
Element emptyElement(); Element emptyElement();
Element canvas(ConstRef<Canvas>); Element canvas(ConstRef<Canvas>);
@ -90,7 +90,7 @@ Element color(Color, Element);
Element bgcolor(Color, Element); Element bgcolor(Color, Element);
Decorator focusPosition(int x, int y); Decorator focusPosition(int x, int y);
Decorator focusPositionRelative(float x, float y); Decorator focusPositionRelative(float x, float y);
Element automerge(Element); Element automerge(Element child);
// --- Layout is // --- Layout is
// Horizontal, Vertical or stacked set of elements. // Horizontal, Vertical or stacked set of elements.
@ -163,7 +163,7 @@ Dimensions Fit(Element&);
// Include old definitions using wstring. // Include old definitions using wstring.
#include "ftxui/dom/deprecated.hpp" #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. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in

View File

@ -108,7 +108,7 @@ struct FlexboxConfig {
} // namespace ftxui } // 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. // Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in

View File

@ -20,6 +20,11 @@ class Node {
public: public:
Node(); Node();
Node(Elements children); Node(Elements children);
Node(const Node&) = delete;
Node(const Node&&) = delete;
Node& operator=(const Node&) = delete;
Node& operator=(const Node&&) = delete;
virtual ~Node(); virtual ~Node();
// Step 1: Compute layout requirement. Tell parent what dimensions this // Step 1: Compute layout requirement. Tell parent what dimensions this
@ -50,12 +55,12 @@ class Node {
Box box_; Box box_;
}; };
void Render(Screen& screen, const Element& node); void Render(Screen& screen, const Element& element);
void Render(Screen& screen, Node* node); void Render(Screen& screen, Node* node);
} // namespace ftxui } // namespace ftxui
#endif /* end of include guard: FTXUI_DOM_NODE_HPP */ #endif // FTXUI_DOM_NODE_HPP
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in

View File

@ -28,7 +28,7 @@ struct Requirement {
} // namespace ftxui } // namespace ftxui
#endif /* end of include guard: FTXUI_DOM_REQUIREMENT_HPP */ #endif // FTXUI_DOM_REQUIREMENT_HPP
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in

View File

@ -52,10 +52,10 @@ class Table {
void Initialize(std::vector<std::vector<Element>>); void Initialize(std::vector<std::vector<Element>>);
friend TableSelection; friend TableSelection;
std::vector<std::vector<Element>> elements_; std::vector<std::vector<Element>> elements_;
int input_dim_x_; int input_dim_x_ = 0;
int input_dim_y_; int input_dim_y_ = 0;
int dim_x_; int dim_x_ = 0;
int dim_y_; int dim_y_ = 0;
}; };
class TableSelection { class TableSelection {

View File

@ -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" // IWYU pragma: private, include "ftxui/dom/elements.hpp"
#include <type_traits> #include <type_traits>
namespace ftxui { namespace ftxui {
template <class T> template <class T>
void Merge(Elements&, T) {} void Merge(Elements& /*container*/, T /*element*/) {}
template <> template <>
inline void Merge(Elements& container, Element element) { inline void Merge(Elements& container, Element element) {
@ -38,6 +41,8 @@ TAKE_ANY_ARGS(dbox)
TAKE_ANY_ARGS(hflow) TAKE_ANY_ARGS(hflow)
} // namespace ftxui } // namespace ftxui
#endif // FTXUI_DOM_TAKE_ANY_ARGS_HPP
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.

View File

@ -9,15 +9,15 @@ struct Box {
int y_min = 0; int y_min = 0;
int y_max = 0; int y_max = 0;
static Box Intersection(Box a, Box b); static auto Intersection(Box a, Box b) -> Box;
bool Contain(int x, int y); bool Contain(int x, int y) const;
bool operator==(const Box& other) const; bool operator==(const Box& other) const;
bool operator!=(const Box& other) const; bool operator!=(const Box& other) const;
}; };
} // namespace ftxui } // namespace ftxui
#endif /* end of include guard: FTXUI_SCREEN_BOX_HPP */ #endif // FTXUI_SCREEN_BOX_HPP
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in

View File

@ -1,7 +1,7 @@
#ifndef FTXUI_SCREEN_COLOR #ifndef FTXUI_SCREEN_COLOR_HPP
#define FTXUI_SCREEN_COLOR #define FTXUI_SCREEN_COLOR_HPP
#include <stdint.h> // for uint8_t #include <cstdint> // for uint8_t
#include <string> // for wstring #include <string> // for wstring
#ifdef RGB #ifdef RGB
@ -313,12 +313,8 @@ class Color {
Palette256, Palette256,
TrueColor, TrueColor,
}; };
ColorType type_ = ColorType::Palette1;
ColorType type_; uint8_t red_ = 0;
union {
uint8_t index_ = 0;
uint8_t red_;
};
uint8_t green_ = 0; uint8_t green_ = 0;
uint8_t blue_ = 0; uint8_t blue_ = 0;
}; };
@ -333,7 +329,7 @@ Color operator""_rgb(unsigned long long int combined);
} // namespace ftxui } // namespace ftxui
#endif /* end of include guard: FTXUI_COLOR_H_ */ #endif // FTXUI_SCREEN_COLOR_HPP
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in

View File

@ -1,7 +1,7 @@
#ifndef FTXUI_SCREEN_COLOR_INFO_HPP #ifndef FTXUI_SCREEN_COLOR_INFO_HPP
#define FTXUI_SCREEN_COLOR_INFO_HPP #define FTXUI_SCREEN_COLOR_INFO_HPP
#include <stdint.h> #include <cstdint>
#include <ftxui/screen/color.hpp> #include <ftxui/screen/color.hpp>
namespace ftxui { namespace ftxui {
@ -23,7 +23,7 @@ ColorInfo GetColorInfo(Color::Palette16 index);
} // namespace ftxui } // 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. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in

View File

@ -8,7 +8,7 @@ int wchar_width(wchar_t);
int wstring_width(const std::wstring&); int wstring_width(const std::wstring&);
} // namespace ftxui } // namespace ftxui
#endif /* end of include guard: FTXUI_SCREEN_DEPRECATED_HPP */ #endif // FTXUI_SCREEN_DEPRECATED_HPP
// Copyright 2021 Arthur Sonzogni. All rights reserved. // Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in

View File

@ -1,5 +1,5 @@
#ifndef FTXUI_SCREEN_SCREEN #ifndef FTXUI_SCREEN_SCREEN_HPP
#define FTXUI_SCREEN_SCREEN #define FTXUI_SCREEN_SCREEN_HPP
#include <memory> #include <memory>
#include <string> // for string, allocator, basic_string #include <string> // for string, allocator, basic_string
@ -14,6 +14,8 @@ namespace ftxui {
/// @brief A unicode character and its associated style. /// @brief A unicode character and its associated style.
/// @ingroup screen /// @ingroup screen
struct Pixel { struct Pixel {
bool operator==(const Pixel& other) const;
// The graphemes stored into the pixel. To support combining characters, // The graphemes stored into the pixel. To support combining characters,
// like: a⃦, this can potentially contains multiple codepoitns. // like: a⃦, this can potentially contains multiple codepoitns.
std::string character = " "; std::string character = " ";
@ -68,13 +70,12 @@ class Screen {
int dimy() const { return dimy_; } int dimy() const { return dimy_; }
// Move the terminal cursor n-lines up with n = 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. // Fill with space.
void Clear(); void Clear();
void ApplyShader(); void ApplyShader();
Box stencil;
struct Cursor { struct Cursor {
int x = 0; int x = 0;
@ -83,16 +84,20 @@ class Screen {
Cursor cursor() const { return cursor_; } Cursor cursor() const { return cursor_; }
void SetCursor(Cursor cursor) { cursor_ = cursor; } void SetCursor(Cursor cursor) { cursor_ = cursor; }
Box stencil;
protected: protected:
int dimx_; int dimx_;
int dimy_; int dimy_;
std::vector<std::vector<Pixel>> pixels_; std::vector<std::vector<Pixel>> pixels_;
Cursor cursor_; Cursor cursor_;
private:
}; };
} // namespace ftxui } // namespace ftxui
#endif /* end of include guard: FTXUI_SCREEN_SCREEN */ #endif // FTXUI_SCREEN_SCREEN_HPP
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in

View File

@ -1,5 +1,5 @@
#ifndef FTXUI_CORE_TERMINAL_HPP #ifndef FTXUI_SCREEN_TERMINAL_HPP
#define FTXUI_CORE_TERMINAL_HPP #define FTXUI_SCREEN_TERMINAL_HPP
namespace ftxui { namespace ftxui {
struct Dimensions { struct Dimensions {
@ -22,7 +22,7 @@ Color ColorSupport();
} // namespace ftxui } // namespace ftxui
#endif /* end of include guard: FTXUI_CORE_TERMINAL_HPP */ #endif // FTXUI_SCREEN_TERMINAL_HPP
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in

View File

@ -1,14 +1,18 @@
#define _USE_MATH_DEFINES #include <cmath>
#include <cmath> // for sin, pow, sqrt, M_PI_2, M_PI, cos #include <ratio> // for ratio
#include <utility> // for move
#include "ftxui/component/animation.hpp" #include "ftxui/component/animation.hpp"
#include <ratio> // for ratio namespace ftxui::animation {
namespace ftxui {
namespace animation {
namespace easing { namespace easing {
namespace {
constexpr float kPi = 3.14159265358979323846F;
constexpr float kPi2 = kPi / 2.F;
} // namespace
// Easing function have been taken out of: // Easing function have been taken out of:
// https://github.com/warrenm/AHEasing/blob/master/AHEasing/easing.c // 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)^2) ; [0, 0.5)
// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1] // y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1]
float QuadraticInOut(float p) { float QuadraticInOut(float p) {
if (p < 0.5) { if (p < 0.5F) { // NOLINT
return 2 * p * p; return 2 * p * p;
} else { } else {
return (-2 * p * p) + (4 * p) - 1; 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)^3) ; [0, 0.5)
// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1] // y = (1/2)((2x-2)^3 + 2) ; [0.5, 1]
float CubicInOut(float p) { float CubicInOut(float p) {
if (p < 0.5) { if (p < 0.5F) { // NOLINT
return 4 * p * p * p; return 4 * p * p * p;
} else { } else {
float f = ((2 * p) - 2); 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)^4) ; [0, 0.5)
// y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1] // y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1]
float QuarticInOut(float p) { float QuarticInOut(float p) {
if (p < 0.5) { if (p < 0.5F) { // NOLINT
return 8 * p * p * p * p; return 8 * p * p * p * p; // NOLINT
} else { } else {
float f = (p - 1); 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)^5) ; [0, 0.5)
// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1] // y = (1/2)((2x-2)^5 + 2) ; [0.5, 1]
float QuinticInOut(float p) { float QuinticInOut(float p) {
if (p < 0.5) { if (p < 0.5F) { // NOLINT
return 16 * p * p * p * p * p; return 16 * p * p * p * p * p; // NOLINT
} else { } else { // NOLINT
float f = ((2 * p) - 2); float f = ((2 * p) - 2); // NOLINT
return 0.5 * f * f * f * f * f + 1; return 0.5 * f * f * f * f * f + 1; // NOLINT
} }
} }
// Modeled after quarter-cycle of sine wave // Modeled after quarter-cycle of sine wave
float SineIn(float p) { 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) // Modeled after quarter-cycle of sine wave (different phase)
float SineOut(float p) { float SineOut(float p) {
return sin(p * M_PI_2); return std::sin(p * kPi2);
} }
// Modeled after half sine wave // Modeled after half sine wave
float SineInOut(float p) { 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 // Modeled after shifted quadrant IV of unit circle
float CircularIn(float p) { 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 // Modeled after shifted quadrant II of unit circle
float CircularOut(float p) { float CircularOut(float p) {
return sqrt((2 - p) * p); return std::sqrt((2 - p) * p);
} }
// Modeled after the piecewise circular function // Modeled after the piecewise circular function
// y = (1/2)(1 - sqrt(1 - 4x^2)) ; [0, 0.5) // y = (1/2)(1 - sqrt(1 - 4x^2)) ; [0, 0.5)
// y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1] // y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1]
float CircularInOut(float p) { float CircularInOut(float p) {
if (p < 0.5) { if (p < 0.5F) { // NOLINT
return 0.5 * (1 - sqrt(1 - 4 * (p * p))); return 0.5F * (1 - std::sqrt(1 - 4 * (p * p))); // NOLINT
} else { } 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)) // Modeled after the exponential function y = 2^(10(x - 1))
float ExponentialIn(float p) { 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 // Modeled after the exponential function y = -2^(-10x) + 1
float ExponentialOut(float p) { 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 // Modeled after the piecewise exponential
// y = (1/2)2^(10(2x - 1)) ; [0,0.5) // y = (1/2)2^(10(2x - 1)) ; [0,0.5)
// y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1] // y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1]
float ExponentialInOut(float p) { float ExponentialInOut(float p) {
if (p == 0.0 || p == 1.0) if (p == 0.0 || p == 1.F) {
return p; return p;
}
if (p < 0.5) { if (p < 0.5F) { // NOLINT
return 0.5 * pow(2, (20 * p) - 10); return 0.5 * std::pow(2, (20 * p) - 10); // NOLINT
} else { } else { // NOLINT
return -0.5 * pow(2, (-20 * p) + 10) + 1; 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)) // Modeled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1))
float ElasticIn(float p) { 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) + // Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) +
// 1 // 1
float ElasticOut(float p) { 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: // 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*(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] // y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1]
float ElasticInOut(float p) { float ElasticInOut(float p) {
if (p < 0.5) { if (p < 0.5F) { // NOLINT
return 0.5 * sin(13 * M_PI_2 * (2 * p)) * pow(2, 10 * ((2 * p) - 1)); return 0.5 * std::sin(13.F * kPi2 * (2 * p)) * // NOLINT
} else { std::pow(2, 10 * ((2 * p) - 1)); // NOLINT
return 0.5 * } else { // NOLINT
(sin(-13 * M_PI_2 * ((2 * p - 1) + 1)) * pow(2, -10 * (2 * p - 1)) + return 0.5 * (std::sin(-13.F * kPi2 * ((2 * p - 1) + 1)) * // NOLINT
2); std::pow(2, -10 * (2 * p - 1)) + // NOLINT
2); // NOLINT
} }
} }
// Modeled after the overshooting cubic y = x^3-x*sin(x*pi) // Modeled after the overshooting cubic y = x^3-x*sin(x*pi)
float BackIn(float p) { 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)) // Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi))
float BackOut(float p) { float BackOut(float p) {
float f = (1 - 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: // Modeled after the piecewise overshooting cubic function:
// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5) // 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] // y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1]
float BackInOut(float p) { float BackInOut(float p) {
if (p < 0.5) { if (p < 0.5F) { // NOLINT
float f = 2 * p; 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 { } else {
float f = (1 - (2 * p - 1)); float f = (1 - (2 * p - 1)); // NOLINT
return 0.5 * (1 - (f * f * f - f * sin(f * M_PI))) + 0.5; 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) { float BounceOut(float p) {
if (p < 4 / 11.0) { if (p < 4 / 11.0) { // NOLINT
return (121 * p * p) / 16.0; return (121 * p * p) / 16.0; // NOLINT
} else if (p < 8 / 11.0) { } else if (p < 8 / 11.0) { // NOLINT
return (363 / 40.0 * p * p) - (99 / 10.0 * p) + 17 / 5.0; return (363 / 40.0 * p * p) - (99 / 10.0 * p) + 17 / 5.0; // NOLINT
} else if (p < 9 / 10.0) { } else if (p < 9 / 10.0) { // NOLINT
return (4356 / 361.0 * p * p) - (35442 / 1805.0 * p) + 16061 / 1805.0; return (4356 / 361.0 * p * p) - (35442 / 1805.0 * p) + // NOLINT
} else { 16061 / 1805.0; // NOLINT
return (54 / 5.0 * p * p) - (513 / 25.0 * p) + 268 / 25.0; } else { // NOLINT
return (54 / 5.0 * p * p) - (513 / 25.0 * p) + 268 / 25.0; // NOLINT
} }
} }
float BounceInOut(float p) { float BounceInOut(float p) { // NOLINT
if (p < 0.5) { if (p < 0.5F) { // NOLINT
return 0.5 * BounceIn(p * 2); return 0.5F * BounceIn(p * 2); // NOLINT
} else { } else { // NOLINT
return 0.5 * BounceOut(p * 2 - 1) + 0.5; return 0.5F * BounceOut(p * 2 - 1) + 0.5F; // NOLINT
} }
} }
@ -259,7 +267,7 @@ Animator::Animator(float* from,
from_(*from), from_(*from),
to_(to), to_(to),
duration_(duration), duration_(duration),
easing_function_(easing_function), easing_function_(std::move(easing_function)),
current_(-delay) { current_(-delay) {
RequestAnimationFrame(); RequestAnimationFrame();
} }
@ -275,11 +283,11 @@ void Animator::OnAnimation(Params& params) {
if (current_ <= Duration()) { if (current_ <= Duration()) {
*value_ = from_; *value_ = from_;
} else { } else {
*value_ = from_ + (to_ - from_) * easing_function_(current_ / duration_); *value_ = from_ +
(to_ - from_) * easing_function_(current_ / duration_); // NOLINT
} }
RequestAnimationFrame(); RequestAnimationFrame();
} }
} // namespace animation } // namespace ftxui::animation
} // namespace ftxui

View File

@ -1,17 +1,16 @@
#include <functional> // for function #include <functional> // for function
#include <memory> // for shared_ptr #include <memory> // for shared_ptr
#include <string> // for string
#include <utility> // for move #include <utility> // for move
#include "ftxui/component/animation.hpp" // for Animator, Params (ptr only) #include "ftxui/component/animation.hpp" // for Animator, Params (ptr only)
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Button #include "ftxui/component/component.hpp" // for Make, Button
#include "ftxui/component/component_base.hpp" // for ComponentBase #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/event.hpp" // for Event, Event::Return
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed #include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
#include "ftxui/component/screen_interactive.hpp" // for Component #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/box.hpp" // for Box
#include "ftxui/screen/color.hpp" // for Color #include "ftxui/screen/color.hpp" // for Color
#include "ftxui/util/ref.hpp" // for Ref, ConstStringRef #include "ftxui/util/ref.hpp" // for Ref, ConstStringRef
@ -20,12 +19,14 @@ namespace ftxui {
namespace { namespace {
Element DefaultTransform(EntryState params) { Element DefaultTransform(EntryState params) { // NOLINT
auto element = text(params.label) | border; auto element = text(params.label) | border;
if (params.active) if (params.active) {
element |= bold; element |= bold;
if (params.focused) }
if (params.focused) {
element |= inverted; element |= inverted;
}
return element; return element;
} }
@ -62,13 +63,16 @@ Component Button(ConstStringRef label,
Impl(ConstStringRef label, Impl(ConstStringRef label,
std::function<void()> on_click, std::function<void()> on_click,
Ref<ButtonOption> option) 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: // Component implementation:
Element Render() override { Element Render() override {
float target = Focused() ? 1.0 : 0.f; float target = Focused() ? 1.F : 0.F; // NOLINT
if (target != animator_background_.to()) if (target != animator_background_.to()) {
SetAnimationTarget(target); SetAnimationTarget(target);
}
EntryState state = { EntryState state = {
*label_, *label_,
@ -122,14 +126,15 @@ Component Button(ConstStringRef label,
void OnClick() { void OnClick() {
on_click_(); on_click_();
animation_background_ = 0.5f; animation_background_ = 0.5F; // NOLINT
animation_foreground_ = 0.5f; animation_foreground_ = 0.5F; // NOLINT
SetAnimationTarget(1.f); SetAnimationTarget(1.F); // NOLINT
} }
bool OnEvent(Event event) override { bool OnEvent(Event event) override {
if (event.is_mouse()) if (event.is_mouse()) {
return OnMouseEvent(event); return OnMouseEvent(event);
}
if (event == Event::Return) { if (event == Event::Return) {
OnClick(); OnClick();
@ -142,8 +147,9 @@ Component Button(ConstStringRef label,
mouse_hover_ = mouse_hover_ =
box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event); box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event);
if (!mouse_hover_) if (!mouse_hover_) {
return false; return false;
}
TakeFocus(); TakeFocus();
@ -172,7 +178,7 @@ Component Button(ConstStringRef label,
animation::Animator(&animation_foreground_); 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 } // namespace ftxui

View File

@ -11,16 +11,17 @@ namespace ftxui {
class CatchEventBase : public ComponentBase { class CatchEventBase : public ComponentBase {
public: public:
// Constructor. // Constructor.
CatchEventBase(std::function<bool(Event)> on_event) explicit CatchEventBase(std::function<bool(Event)> on_event)
: on_event_(std::move(on_event)) {} : on_event_(std::move(on_event)) {}
// Component implementation. // Component implementation.
bool OnEvent(Event event) override { bool OnEvent(Event event) override {
if (on_event_(event)) if (on_event_(event)) {
return true; return true;
else } else {
return ComponentBase::OnEvent(event); return ComponentBase::OnEvent(event);
} }
}
protected: protected:
std::function<bool(Event)> on_event_; std::function<bool(Event)> on_event_;
@ -76,8 +77,8 @@ Component CatchEvent(Component child,
/// ``` /// ```
ComponentDecorator CatchEvent(std::function<bool(Event)> on_event) { ComponentDecorator CatchEvent(std::function<bool(Event)> on_event) {
return [on_event = std::move(on_event)](Component child) { return [on_event = std::move(on_event)](Component child) {
return CatchEvent(child, [on_event = std::move(on_event)](Event event) { return CatchEvent(std::move(child), [on_event = on_event](Event event) {
return on_event(event); return on_event(std::move(event));
}); });
}; };
} }

View File

@ -18,7 +18,7 @@ namespace {
class CheckboxBase : public ComponentBase { class CheckboxBase : public ComponentBase {
public: public:
CheckboxBase(ConstStringRef label, bool* state, Ref<CheckboxOption> option) 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: private:
// Component implementation. // Component implementation.
@ -32,18 +32,20 @@ class CheckboxBase : public ComponentBase {
is_active, is_active,
is_focused || hovered_, is_focused || hovered_,
}; };
auto element = (option_->transform auto element =
? option_->transform (option_->transform ? option_->transform
: CheckboxOption::Simple().transform)(std::move(state)); : CheckboxOption::Simple().transform)(state);
return element | focus_management | reflect(box_); return element | focus_management | reflect(box_);
} }
bool OnEvent(Event event) override { bool OnEvent(Event event) override {
if (!CaptureMouse(event)) if (!CaptureMouse(event)) {
return false; return false;
}
if (event.is_mouse()) if (event.is_mouse()) {
return OnMouseEvent(event); return OnMouseEvent(event);
}
hovered_ = false; hovered_ = false;
if (event == Event::Character(' ') || event == Event::Return) { if (event == Event::Character(' ') || event == Event::Return) {
@ -58,11 +60,13 @@ class CheckboxBase : public ComponentBase {
bool OnMouseEvent(Event event) { bool OnMouseEvent(Event event) {
hovered_ = box_.Contain(event.mouse().x, event.mouse().y); hovered_ = box_.Contain(event.mouse().x, event.mouse().y);
if (!CaptureMouse(event)) if (!CaptureMouse(event)) {
return false; return false;
}
if (!hovered_) if (!hovered_) {
return false; return false;
}
if (event.mouse().button == Mouse::Left && if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) { event.mouse().motion == Mouse::Pressed) {
@ -109,7 +113,7 @@ class CheckboxBase : public ComponentBase {
Component Checkbox(ConstStringRef label, Component Checkbox(ConstStringRef label,
bool* checked, bool* checked,
Ref<CheckboxOption> option) { Ref<CheckboxOption> option) {
return Make<CheckboxBase>(label, checked, std::move(option)); return Make<CheckboxBase>(std::move(label), checked, std::move(option));
} }
} // namespace ftxui } // namespace ftxui

View File

@ -1,9 +1,11 @@
#include <string> // for string #include <functional> // for function
#include <memory> // for shared_ptr, allocator
#include <utility> // for move #include <utility> // for move
#include "ftxui/component/component.hpp" // for Checkbox, Maybe, Make, Vertical, Collapsible #include "ftxui/component/component.hpp" // for Checkbox, Maybe, Make, Vertical, Collapsible
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase #include "ftxui/component/component_base.hpp" // for Component, ComponentBase
#include "ftxui/component/component_options.hpp" // for CheckboxOption #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 #include "ftxui/util/ref.hpp" // for Ref, ConstStringRef
namespace ftxui { namespace ftxui {
@ -28,27 +30,28 @@ namespace ftxui {
Component Collapsible(ConstStringRef label, Component child, Ref<bool> show) { Component Collapsible(ConstStringRef label, Component child, Ref<bool> show) {
class Impl : public ComponentBase { class Impl : public ComponentBase {
public: public:
Impl(ConstStringRef label, Component child, Ref<bool> show) Impl(ConstStringRef label, Component child, Ref<bool> show) : show_(show) {
: show_(std::move(show)) {
CheckboxOption opt; CheckboxOption opt;
opt.transform = [](EntryState s) { opt.transform = [](EntryState s) { // NOLINT
auto prefix = text(s.state ? "" : ""); auto prefix = text(s.state ? "" : ""); // NOLINT
auto t = text(s.label); auto t = text(s.label);
if (s.active) if (s.active) {
t |= bold; t |= bold;
if (s.focused) }
if (s.focused) {
t |= inverted; t |= inverted;
}
return hbox({prefix, t}); return hbox({prefix, t});
}; };
Add(Container::Vertical({ Add(Container::Vertical({
Checkbox(label, show_.operator->(), opt), Checkbox(std::move(label), show_.operator->(), opt),
Maybe(std::move(child), show_.operator->()), Maybe(std::move(child), show_.operator->()),
})); }));
} }
Ref<bool> show_; 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 } // namespace ftxui

View File

@ -1,6 +1,6 @@
#include <stddef.h> // for size_t
#include <algorithm> // for find_if #include <algorithm> // for find_if
#include <cassert> // for assert #include <cassert> // for assert
#include <cstddef> // for size_t
#include <iterator> // for begin, end #include <iterator> // for begin, end
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type #include <vector> // for vector, __alloc_traits<>::value_type
@ -37,7 +37,7 @@ ComponentBase* ComponentBase::Parent() const {
/// @brief Access the child at index `i`. /// @brief Access the child at index `i`.
/// @ingroup component /// @ingroup component
Component& ComponentBase::ChildAt(size_t i) { Component& ComponentBase::ChildAt(size_t i) {
assert(i < ChildCount()); assert(i < ChildCount()); // NOLINT
return children_[i]; return children_[i];
} }
@ -61,8 +61,9 @@ void ComponentBase::Add(Component child) {
/// @see Parent /// @see Parent
/// @ingroup component /// @ingroup component
void ComponentBase::Detach() { void ComponentBase::Detach() {
if (!parent_) if (parent_ == nullptr) {
return; return;
}
auto it = std::find_if(std::begin(parent_->children_), // auto it = std::find_if(std::begin(parent_->children_), //
std::end(parent_->children_), // std::end(parent_->children_), //
[this](const Component& that) { // [this](const Component& that) { //
@ -76,8 +77,9 @@ void ComponentBase::Detach() {
/// @brief Remove all children. /// @brief Remove all children.
/// @ingroup component /// @ingroup component
void ComponentBase::DetachAllChildren() { void ComponentBase::DetachAllChildren() {
while (!children_.empty()) while (!children_.empty()) {
children_[0]->Detach(); children_[0]->Detach();
}
} }
/// @brief Draw the component. /// @brief Draw the component.
@ -85,8 +87,9 @@ void ComponentBase::DetachAllChildren() {
/// ftxui::ComponentBase. /// ftxui::ComponentBase.
/// @ingroup component /// @ingroup component
Element ComponentBase::Render() { Element ComponentBase::Render() {
if (children_.size() == 1) if (children_.size() == 1) {
return children_.front()->Render(); return children_.front()->Render();
}
return text("Not implemented component"); return text("Not implemented component");
} }
@ -97,11 +100,12 @@ Element ComponentBase::Render() {
/// The default implementation called OnEvent on every child until one return /// The default implementation called OnEvent on every child until one return
/// true. If none returns true, return false. /// true. If none returns true, return false.
/// @ingroup component /// @ingroup component
bool ComponentBase::OnEvent(Event event) { bool ComponentBase::OnEvent(Event event) { // NOLINT
for (Component& child : children_) { for (Component& child : children_) { // NOLINT
if (child->OnEvent(event)) if (child->OnEvent(event)) {
return true; return true;
} }
}
return false; return false;
} }
@ -110,8 +114,9 @@ bool ComponentBase::OnEvent(Event event) {
/// The default implementation dispatch the event to every child. /// The default implementation dispatch the event to every child.
/// @ingroup component /// @ingroup component
void ComponentBase::OnAnimation(animation::Params& params) { void ComponentBase::OnAnimation(animation::Params& params) {
for (Component& child : children_) for (Component& child : children_) {
child->OnAnimation(params); child->OnAnimation(params);
}
} }
/// @brief Return the currently Active child. /// @brief Return the currently Active child.
@ -119,9 +124,10 @@ void ComponentBase::OnAnimation(animation::Params& params) {
/// @ingroup component /// @ingroup component
Component ComponentBase::ActiveChild() { Component ComponentBase::ActiveChild() {
for (auto& child : children_) { for (auto& child : children_) {
if (child->Focusable()) if (child->Focusable()) {
return child; return child;
} }
}
return nullptr; return nullptr;
} }
@ -130,17 +136,18 @@ Component ComponentBase::ActiveChild() {
/// keyboard. /// keyboard.
/// @ingroup component /// @ingroup component
bool ComponentBase::Focusable() const { bool ComponentBase::Focusable() const {
for (const Component& child : children_) { for (const Component& child : children_) { // NOLINT
if (child->Focusable()) if (child->Focusable()) {
return true; return true;
} }
}
return false; return false;
} }
/// @brief Returns if the element if the currently active child of its parent. /// @brief Returns if the element if the currently active child of its parent.
/// @ingroup component /// @ingroup component
bool ComponentBase::Active() const { 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. /// @brief Returns if the elements if focused by the user.
@ -149,7 +156,7 @@ bool ComponentBase::Active() const {
/// Focusable(). /// Focusable().
/// @ingroup component /// @ingroup component
bool ComponentBase::Focused() const { bool ComponentBase::Focused() const {
auto current = this; const auto* current = this;
while (current && current->Active()) { while (current && current->Active()) {
current = current->parent_; current = current->parent_;
} }
@ -159,12 +166,12 @@ bool ComponentBase::Focused() const {
/// @brief Make the |child| to be the "active" one. /// @brief Make the |child| to be the "active" one.
/// @param child the child to become active. /// @param child the child to become active.
/// @ingroup component /// @ingroup component
void ComponentBase::SetActiveChild(ComponentBase*) {} void ComponentBase::SetActiveChild(ComponentBase* /*child*/) {}
/// @brief Make the |child| to be the "active" one. /// @brief Make the |child| to be the "active" one.
/// @param child the child to become active. /// @param child the child to become active.
/// @ingroup component /// @ingroup component
void ComponentBase::SetActiveChild(Component child) { void ComponentBase::SetActiveChild(Component child) { // NOLINT
SetActiveChild(child.get()); SetActiveChild(child.get());
} }
@ -182,9 +189,10 @@ void ComponentBase::TakeFocus() {
/// them. It represents a component taking priority over others. /// them. It represents a component taking priority over others.
/// @param event /// @param event
/// @ingroup component /// @ingroup component
CapturedMouse ComponentBase::CaptureMouse(const Event& event) { CapturedMouse ComponentBase::CaptureMouse(const Event& event) { // NOLINT
if (event.screen_) if (event.screen_) {
return event.screen_->CaptureMouse(); return event.screen_->CaptureMouse();
}
return std::make_unique<CaptureMouseImpl>(); return std::make_unique<CaptureMouseImpl>();
} }

View File

@ -1,9 +1,10 @@
#include "ftxui/component/component_options.hpp" #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/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 { namespace ftxui {
@ -15,13 +16,13 @@ void AnimatedColorOption::Set(Color a_inactive,
inactive = a_inactive; inactive = a_inactive;
active = a_active; active = a_active;
duration = a_duration; duration = a_duration;
function = a_function; function = std::move(a_function);
} }
void UnderlineOption::SetAnimation(animation::Duration d, void UnderlineOption::SetAnimation(animation::Duration d,
animation::easing::Function f) { animation::easing::Function f) {
SetAnimationDuration(d); SetAnimationDuration(d);
SetAnimationFunction(f); SetAnimationFunction(std::move(f));
} }
void UnderlineOption::SetAnimationDuration(animation::Duration d) { void UnderlineOption::SetAnimationDuration(animation::Duration d) {
@ -31,28 +32,31 @@ void UnderlineOption::SetAnimationDuration(animation::Duration d) {
void UnderlineOption::SetAnimationFunction(animation::easing::Function f) { void UnderlineOption::SetAnimationFunction(animation::easing::Function f) {
leader_function = f; leader_function = f;
follower_function = f; follower_function = std::move(f);
} }
void UnderlineOption::SetAnimationFunction( void UnderlineOption::SetAnimationFunction(
animation::easing::Function f_leader, animation::easing::Function f_leader,
animation::easing::Function f_follower) { animation::easing::Function f_follower) {
leader_function = f_leader; leader_function = std::move(f_leader);
follower_function = f_follower; follower_function = std::move(f_follower);
} }
// static // static
MenuOption MenuOption::Horizontal() { MenuOption MenuOption::Horizontal() {
MenuOption option; MenuOption option;
option.direction = Direction::Right; option.direction = Direction::Right;
option.entries.transform = [](EntryState state) { option.entries.transform = [](const EntryState& state) {
Element e = text(state.label); Element e = text(state.label);
if (state.focused) if (state.focused) {
e |= inverted; e |= inverted;
if (state.active) }
if (state.active) {
e |= bold; e |= bold;
if (!state.focused && !state.active) }
if (!state.focused && !state.active) {
e |= dim; e |= dim;
}
return e; return e;
}; };
option.elements_infix = [] { return text(" "); }; option.elements_infix = [] { return text(" "); };
@ -70,19 +74,17 @@ MenuOption MenuOption::HorizontalAnimated() {
// static // static
MenuOption MenuOption::Vertical() { MenuOption MenuOption::Vertical() {
MenuOption option; MenuOption option;
option.entries.transform = [](EntryState state) { option.entries.transform = [](const EntryState& state) {
if (state.active) Element e = text((state.active ? "> " : " ") + state.label); // NOLINT
state.label = "> " + state.label; if (state.focused) {
else
state.label = " " + state.label;
Element e = text(state.label);
if (state.focused)
e |= inverted; e |= inverted;
if (state.active) }
if (state.active) {
e |= bold; e |= bold;
if (!state.focused && !state.active) }
if (!state.focused && !state.active) {
e |= dim; e |= dim;
}
return e; return e;
}; };
return option; return option;
@ -91,14 +93,17 @@ MenuOption MenuOption::Vertical() {
// static // static
MenuOption MenuOption::VerticalAnimated() { MenuOption MenuOption::VerticalAnimated() {
auto option = MenuOption::Vertical(); auto option = MenuOption::Vertical();
option.entries.transform = [](EntryState state) { option.entries.transform = [](const EntryState& state) {
Element e = text(state.label); Element e = text(state.label);
if (state.focused) if (state.focused) {
e |= inverted; e |= inverted;
if (state.active) }
if (state.active) {
e |= bold; e |= bold;
if (!state.focused && !state.active) }
if (!state.focused && !state.active) {
e |= dim; e |= dim;
}
return e; return e;
}; };
option.underline.enabled = true; option.underline.enabled = true;
@ -116,10 +121,10 @@ MenuOption MenuOption::Toggle() {
// static // static
ButtonOption ButtonOption::Ascii() { ButtonOption ButtonOption::Ascii() {
ButtonOption option; ButtonOption option;
option.transform = [](EntryState s) { option.transform = [](const EntryState& s) {
s.label = s.focused ? "[" + s.label + "]" // std::string label = s.focused ? "[" + s.label + "]" //
: " " + s.label + " "; : " " + s.label + " ";
return text(s.label); return text(label);
}; };
return option; return option;
} }
@ -128,10 +133,11 @@ ButtonOption ButtonOption::Ascii() {
// static // static
ButtonOption ButtonOption::Simple() { ButtonOption ButtonOption::Simple() {
ButtonOption option; ButtonOption option;
option.transform = [](EntryState s) { option.transform = [](const EntryState& s) {
auto element = text(s.label) | borderLight; auto element = text(s.label) | borderLight;
if (s.focused) if (s.focused) {
element |= inverted; element |= inverted;
}
return element; return element;
}; };
return option; return option;
@ -147,10 +153,11 @@ ButtonOption ButtonOption::Animated() {
/// @brief Create a ButtonOption, using animated colors. /// @brief Create a ButtonOption, using animated colors.
// static // static
ButtonOption ButtonOption::Animated(Color color) { ButtonOption ButtonOption::Animated(Color color) {
return ButtonOption::Animated(Color::Interpolate(0.85f, color, Color::Black), return ButtonOption::Animated(
Color::Interpolate(0.10f, color, Color::White), Color::Interpolate(0.85F, color, Color::Black), // NOLINT
Color::Interpolate(0.10f, color, Color::Black), Color::Interpolate(0.10F, color, Color::White), // NOLINT
Color::Interpolate(0.85f, color, Color::White)); Color::Interpolate(0.10F, color, Color::Black), // NOLINT
Color::Interpolate(0.85F, color, Color::White)); // NOLINT
} }
/// @brief Create a ButtonOption, using animated colors. /// @brief Create a ButtonOption, using animated colors.
@ -163,17 +170,18 @@ ButtonOption ButtonOption::Animated(Color background, Color foreground) {
// static // static
ButtonOption ButtonOption::Animated(Color background, ButtonOption ButtonOption::Animated(Color background,
Color foreground, Color foreground,
Color background_focused, Color background_active,
Color foreground_focused) { Color foreground_active) {
ButtonOption option; ButtonOption option;
option.transform = [](EntryState s) { option.transform = [](const EntryState& s) {
auto element = text(s.label) | borderEmpty; auto element = text(s.label) | borderEmpty;
if (s.focused) if (s.focused) {
element |= bold; element |= bold;
}
return element; return element;
}; };
option.animated_colors.foreground.Set(foreground, foreground_focused); option.animated_colors.foreground.Set(foreground, foreground_active);
option.animated_colors.background.Set(background, background_focused); option.animated_colors.background.Set(background, background_active);
return option; return option;
} }
@ -181,19 +189,21 @@ ButtonOption ButtonOption::Animated(Color background,
// static // static
CheckboxOption CheckboxOption::Simple() { CheckboxOption CheckboxOption::Simple() {
auto option = CheckboxOption(); auto option = CheckboxOption();
option.transform = [](EntryState s) { option.transform = [](const EntryState& s) {
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK) #if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
// Microsoft terminal do not use fonts able to render properly the default // Microsoft terminal do not use fonts able to render properly the default
// radiobox glyph. // radiobox glyph.
auto prefix = text(s.state ? "[X] " : "[ ] "); auto prefix = text(s.state ? "[X] " : "[ ] "); // NOLINT
#else #else
auto prefix = text(s.state ? "" : ""); auto prefix = text(s.state ? "" : ""); // NOLINT
#endif #endif
auto t = text(s.label); auto t = text(s.label);
if (s.active) if (s.active) {
t |= bold; t |= bold;
if (s.focused) }
if (s.focused) {
t |= inverted; t |= inverted;
}
return hbox({prefix, t}); return hbox({prefix, t});
}; };
return option; return option;
@ -203,19 +213,21 @@ CheckboxOption CheckboxOption::Simple() {
// static // static
RadioboxOption RadioboxOption::Simple() { RadioboxOption RadioboxOption::Simple() {
auto option = RadioboxOption(); auto option = RadioboxOption();
option.transform = [](EntryState s) { option.transform = [](const EntryState& s) {
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK) #if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
// Microsoft terminal do not use fonts able to render properly the default // Microsoft terminal do not use fonts able to render properly the default
// radiobox glyph. // radiobox glyph.
auto prefix = text(s.state ? "(*) " : "( ) "); auto prefix = text(s.state ? "(*) " : "( ) "); // NOLINT
#else #else
auto prefix = text(s.state ? "" : ""); auto prefix = text(s.state ? "" : ""); // NOLINT
#endif #endif
auto t = text(s.label); auto t = text(s.label);
if (s.active) if (s.active) {
t |= bold; t |= bold;
if (s.focused) }
if (s.focused) {
t |= inverted; t |= inverted;
}
return hbox({prefix, t}); return hbox({prefix, t});
}; };
return option; return option;

View File

@ -1,5 +1,5 @@
#include <stddef.h> // for size_t
#include <algorithm> // for max, min #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 <memory> // for make_shared, __shared_ptr_access, allocator, shared_ptr, allocator_traits<>::value_type
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type #include <vector> // for vector, __alloc_traits<>::value_type
@ -17,27 +17,32 @@ class ContainerBase : public ComponentBase {
public: public:
ContainerBase(Components children, int* selector) ContainerBase(Components children, int* selector)
: selector_(selector ? selector : &selected_) { : selector_(selector ? selector : &selected_) {
for (Component& child : children) for (Component& child : children) {
Add(std::move(child)); Add(std::move(child));
} }
}
// Component override. // Component override.
bool OnEvent(Event event) override { bool OnEvent(Event event) override {
if (event.is_mouse()) if (event.is_mouse()) {
return OnMouseEvent(event); return OnMouseEvent(event);
}
if (!Focused()) if (!Focused()) {
return false; return false;
}
if (ActiveChild() && ActiveChild()->OnEvent(event)) if (ActiveChild() && ActiveChild()->OnEvent(event)) {
return true; return true;
}
return EventHandler(event); return EventHandler(event);
} }
Component ActiveChild() override { Component ActiveChild() override {
if (children_.size() == 0) if (children_.empty()) {
return nullptr; return nullptr;
}
return children_[*selector_ % children_.size()]; return children_[*selector_ % children_.size()];
} }
@ -45,7 +50,7 @@ class ContainerBase : public ComponentBase {
void SetActiveChild(ComponentBase* child) override { void SetActiveChild(ComponentBase* child) override {
for (size_t i = 0; i < children_.size(); ++i) { for (size_t i = 0; i < children_.size(); ++i) {
if (children_[i].get() == child) { if (children_[i].get() == child) {
*selector_ = i; *selector_ = (int)i;
return; return;
} }
} }
@ -53,10 +58,10 @@ class ContainerBase : public ComponentBase {
protected: protected:
// Handlers // Handlers
virtual bool EventHandler(Event) { return false; } virtual bool EventHandler(Event /*unused*/) { return false; } // NOLINT
virtual bool OnMouseEvent(Event event) { virtual bool OnMouseEvent(Event event) {
return ComponentBase::OnEvent(event); return ComponentBase::OnEvent(std::move(event));
} }
int selected_ = 0; int selected_ = 0;
@ -71,11 +76,16 @@ class ContainerBase : public ComponentBase {
} }
} }
} }
void MoveSelectorWrap(int dir) { void MoveSelectorWrap(int dir) {
if (children_.empty()) {
return;
}
for (size_t offset = 1; offset < children_.size(); ++offset) { 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()) { if (children_[i]->Focusable()) {
*selector_ = i; *selector_ = (int)i;
return; return;
} }
} }
@ -88,60 +98,74 @@ class VerticalContainer : public ContainerBase {
Element Render() override { Element Render() override {
Elements elements; Elements elements;
for (auto& it : children_) for (auto& it : children_) {
elements.push_back(it->Render()); elements.push_back(it->Render());
if (elements.size() == 0) }
if (elements.empty()) {
return text("Empty container") | reflect(box_); return text("Empty container") | reflect(box_);
}
return vbox(std::move(elements)) | reflect(box_); return vbox(std::move(elements)) | reflect(box_);
} }
bool EventHandler(Event event) override { bool EventHandler(Event event) override {
int old_selected = *selector_; int old_selected = *selector_;
if (event == Event::ArrowUp || event == Event::Character('k')) if (event == Event::ArrowUp || event == Event::Character('k')) {
MoveSelector(-1); MoveSelector(-1);
if (event == Event::ArrowDown || event == Event::Character('j')) }
if (event == Event::ArrowDown || event == Event::Character('j')) {
MoveSelector(+1); MoveSelector(+1);
}
if (event == Event::PageUp) { 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); MoveSelector(-1);
} }
}
if (event == Event::PageDown) { 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); MoveSelector(1);
} }
}
if (event == Event::Home) { if (event == Event::Home) {
for (size_t i = 0; i < children_.size(); ++i) for (size_t i = 0; i < children_.size(); ++i) {
MoveSelector(-1); MoveSelector(-1);
} }
}
if (event == Event::End) { if (event == Event::End) {
for (size_t i = 0; i < children_.size(); ++i) for (size_t i = 0; i < children_.size(); ++i) {
MoveSelector(1); MoveSelector(1);
} }
if (event == Event::Tab && children_.size()) }
if (event == Event::Tab) {
MoveSelectorWrap(+1); MoveSelectorWrap(+1);
if (event == Event::TabReverse && children_.size()) }
if (event == Event::TabReverse) {
MoveSelectorWrap(-1); MoveSelectorWrap(-1);
}
*selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_)); *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
return old_selected != *selector_; return old_selected != *selector_;
} }
bool OnMouseEvent(Event event) override { bool OnMouseEvent(Event event) override {
if (ContainerBase::OnMouseEvent(event)) if (ContainerBase::OnMouseEvent(event)) {
return true; return true;
}
if (event.mouse().button != Mouse::WheelUp && if (event.mouse().button != Mouse::WheelUp &&
event.mouse().button != Mouse::WheelDown) { event.mouse().button != Mouse::WheelDown) {
return false; return false;
} }
if (!box_.Contain(event.mouse().x, event.mouse().y)) if (!box_.Contain(event.mouse().x, event.mouse().y)) {
return false; return false;
}
if (event.mouse().button == Mouse::WheelUp) if (event.mouse().button == Mouse::WheelUp) {
MoveSelector(-1); MoveSelector(-1);
if (event.mouse().button == Mouse::WheelDown) }
if (event.mouse().button == Mouse::WheelDown) {
MoveSelector(+1); MoveSelector(+1);
}
*selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_)); *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
return true; return true;
@ -156,23 +180,29 @@ class HorizontalContainer : public ContainerBase {
Element Render() override { Element Render() override {
Elements elements; Elements elements;
for (auto& it : children_) for (auto& it : children_) {
elements.push_back(it->Render()); elements.push_back(it->Render());
if (elements.size() == 0) }
if (elements.empty()) {
return text("Empty container"); return text("Empty container");
}
return hbox(std::move(elements)); return hbox(std::move(elements));
} }
bool EventHandler(Event event) override { bool EventHandler(Event event) override {
int old_selected = *selector_; int old_selected = *selector_;
if (event == Event::ArrowLeft || event == Event::Character('h')) if (event == Event::ArrowLeft || event == Event::Character('h')) {
MoveSelector(-1); MoveSelector(-1);
if (event == Event::ArrowRight || event == Event::Character('l')) }
if (event == Event::ArrowRight || event == Event::Character('l')) {
MoveSelector(+1); MoveSelector(+1);
if (event == Event::Tab && children_.size()) }
if (event == Event::Tab) {
MoveSelectorWrap(+1); MoveSelectorWrap(+1);
if (event == Event::TabReverse && children_.size()) }
if (event == Event::TabReverse) {
MoveSelectorWrap(-1); MoveSelectorWrap(-1);
}
*selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_)); *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
return old_selected != *selector_; return old_selected != *selector_;
@ -185,14 +215,16 @@ class TabContainer : public ContainerBase {
Element Render() override { Element Render() override {
Component active_child = ActiveChild(); Component active_child = ActiveChild();
if (active_child) if (active_child) {
return active_child->Render(); return active_child->Render();
}
return text("Empty container"); return text("Empty container");
} }
bool Focusable() const override { bool Focusable() const override {
if (children_.size() == 0) if (children_.empty()) {
return false; return false;
}
return children_[*selector_ % children_.size()]->Focusable(); return children_[*selector_ % children_.size()]->Focusable();
} }

View File

@ -1,12 +1,12 @@
#include <algorithm> // for max, min #include <algorithm> // for max, min
#include <memory> // for __shared_ptr_access #include <functional> // for function
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
#include <string> // for string #include <string> // for string
#include <utility> // for move
#include "ftxui/component/component.hpp" // for Maybe, Checkbox, Make, Radiobox, Vertical, Dropdown #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_base.hpp" // for Component, ComponentBase
#include "ftxui/component/component_options.hpp" // for CheckboxOption #include "ftxui/component/component_options.hpp" // for CheckboxOption, EntryState
#include "ftxui/dom/elements.hpp" // for operator|, Element, border, filler, separator, size, vbox, frame, vscroll_indicator, HEIGHT, LESS_THAN #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 #include "ftxui/util/ref.hpp" // for ConstStringListRef
namespace ftxui { namespace ftxui {
@ -15,15 +15,17 @@ Component Dropdown(ConstStringListRef entries, int* selected) {
class Impl : public ComponentBase { class Impl : public ComponentBase {
public: public:
Impl(ConstStringListRef entries, int* selected) Impl(ConstStringListRef entries, int* selected)
: entries_(std::move(entries)), selected_(selected) { : entries_(entries), selected_(selected) {
CheckboxOption option; CheckboxOption option;
option.transform = [](EntryState s) { option.transform = [](const EntryState& s) {
auto prefix = text(s.state ? "" : ""); auto prefix = text(s.state ? "" : ""); // NOLINT
auto t = text(s.label); auto t = text(s.label);
if (s.active) if (s.active) {
t |= bold; t |= bold;
if (s.focused) }
if (s.focused) {
t |= inverted; t |= inverted;
}
return hbox({prefix, t}); return hbox({prefix, t});
}; };
checkbox_ = Checkbox(&title_, &show_, option), 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_)); *selected_ = std::min((int)entries_.size() - 1, std::max(0, *selected_));
title_ = entries_[*selected_]; title_ = entries_[*selected_];
if (show_) { if (show_) {
const int max_height = 12;
return vbox({ return vbox({
checkbox_->Render(), checkbox_->Render(),
separator(), separator(),
radiobox_->Render() | vscroll_indicator | frame | radiobox_->Render() | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, 12), size(HEIGHT, LESS_THAN, max_height),
}) | }) |
border; border;
} }
@ -63,7 +66,7 @@ Component Dropdown(ConstStringListRef entries, int* selected) {
Component radiobox_; Component radiobox_;
}; };
return Make<Impl>(std::move(entries), selected); return Make<Impl>(entries, selected);
} }
} // namespace ftxui } // namespace ftxui

View File

@ -29,7 +29,7 @@ Event Event::Mouse(std::string input, struct Mouse mouse) {
Event event; Event event;
event.input_ = std::move(input); event.input_ = std::move(input);
event.type_ = Type::Mouse; event.type_ = Type::Mouse;
event.mouse_ = mouse; event.mouse_ = mouse; // NOLINT
return event; return event;
} }
@ -45,40 +45,39 @@ Event Event::CursorReporting(std::string input, int x, int y) {
Event event; Event event;
event.input_ = std::move(input); event.input_ = std::move(input);
event.type_ = Type::CursorReporting; event.type_ = Type::CursorReporting;
event.cursor_.x = x; event.cursor_.x = x; // NOLINT
event.cursor_.y = y; event.cursor_.y = y; // NOLINT
return event; return event;
} }
// --- Arrow --- // --- Arrow ---
const Event Event::ArrowLeft = Event::Special("\x1B[D"); const Event Event::ArrowLeft = Event::Special("\x1B[D"); // NOLINT
const Event Event::ArrowRight = Event::Special("\x1B[C"); const Event Event::ArrowRight = Event::Special("\x1B[C"); // NOLINT
const Event Event::ArrowUp = Event::Special("\x1B[A"); const Event Event::ArrowUp = Event::Special("\x1B[A"); // NOLINT
const Event Event::ArrowDown = Event::Special("\x1B[B"); const Event Event::ArrowDown = Event::Special("\x1B[B"); // NOLINT
const Event Event::Backspace = Event::Special({127}); const Event Event::Backspace = Event::Special({127}); // NOLINT
const Event Event::Delete = Event::Special("\x1B[3~"); const Event Event::Delete = Event::Special("\x1B[3~"); // NOLINT
const Event Event::Escape = Event::Special("\x1B"); const Event Event::Escape = Event::Special("\x1B"); // NOLINT
const Event Event::Return = Event::Special({10}); const Event Event::Return = Event::Special({10}); // NOLINT
const Event Event::Tab = Event::Special({9}); const Event Event::Tab = Event::Special({9}); // NOLINT
const Event Event::TabReverse = Event::Special({27, 91, 90}); const Event Event::TabReverse = Event::Special({27, 91, 90}); // NOLINT
const Event Event::F1 = Event::Special("\x1B[OP"); const Event Event::F1 = Event::Special("\x1B[OP"); // NOLINT
const Event Event::F2 = Event::Special("\x1B[OQ"); const Event Event::F2 = Event::Special("\x1B[OQ"); // NOLINT
const Event Event::F3 = Event::Special("\x1B[OR"); const Event Event::F3 = Event::Special("\x1B[OR"); // NOLINT
const Event Event::F4 = Event::Special("\x1B[OS"); const Event Event::F4 = Event::Special("\x1B[OS"); // NOLINT
const Event Event::F5 = Event::Special("\x1B[15~"); const Event Event::F5 = Event::Special("\x1B[15~"); // NOLINT
const Event Event::F6 = Event::Special("\x1B[17~"); const Event Event::F6 = Event::Special("\x1B[17~"); // NOLINT
const Event Event::F7 = Event::Special("\x1B[18~"); const Event Event::F7 = Event::Special("\x1B[18~"); // NOLINT
const Event Event::F8 = Event::Special("\x1B[19~"); const Event Event::F8 = Event::Special("\x1B[19~"); // NOLINT
const Event Event::F9 = Event::Special("\x1B[20~"); const Event Event::F9 = Event::Special("\x1B[20~"); // NOLINT
const Event Event::F10 = Event::Special("\x1B[21~"); const Event Event::F10 = Event::Special("\x1B[21~"); // NOLINT
const Event Event::F11 = Event::Special("\x1B[21~"); // Doesn't exist const Event Event::F11 = Event::Special("\x1B[21~"); // Doesn't exist // NOLINT
const Event Event::F12 = Event::Special("\x1B[24~"); const Event Event::F12 = Event::Special("\x1B[24~"); // NOLINT
const Event Event::Home = Event::Special({27, 91, 72}); const Event Event::Home = Event::Special({27, 91, 72}); // NOLINT
const Event Event::End = Event::Special({27, 91, 70}); const Event Event::End = Event::Special({27, 91, 70}); // NOLINT
const Event Event::PageUp = Event::Special({27, 91, 53, 126}); const Event Event::PageUp = Event::Special({27, 91, 53, 126}); // NOLINT
const Event Event::PageDown = Event::Special({27, 91, 54, 126}); const Event Event::PageDown = Event::Special({27, 91, 54, 126}); // NOLINT
const Event Event::Custom = Event::Special({0}); // NOLINT
Event Event::Custom = Event::Special({0});
} // namespace ftxui } // namespace ftxui

View File

@ -1,5 +1,5 @@
#include <stddef.h> // for size_t
#include <algorithm> // for max, min #include <algorithm> // for max, min
#include <cstddef> // for size_t
#include <functional> // for function #include <functional> // for function
#include <memory> // for shared_ptr, allocator #include <memory> // for shared_ptr, allocator
#include <string> // for string, wstring #include <string> // for string, wstring
@ -24,11 +24,12 @@ namespace ftxui {
namespace { namespace {
std::string PasswordField(int size) { std::string PasswordField(size_t size) {
std::string out; std::string out;
out.reserve(2 * size); out.reserve(2 * size);
while (size--) while (size--) {
out += ""; out += "";
}
return out; return out;
} }
@ -38,21 +39,25 @@ class InputBase : public ComponentBase {
InputBase(StringRef content, InputBase(StringRef content,
ConstStringRef placeholder, ConstStringRef placeholder,
Ref<InputOption> option) 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_internal_ = 0;
int& cursor_position() { int& cursor_position() {
int& opt = option_->cursor_position(); int& opt = option_->cursor_position();
if (opt != -1) if (opt != -1) {
return opt; return opt;
}
return cursor_position_internal_; return cursor_position_internal_;
} }
// Component implementation: // Component implementation:
Element Render() override { Element Render() override {
std::string password_content; std::string password_content;
if (option_->password()) if (option_->password()) {
password_content = PasswordField(content_->size()); password_content = PasswordField(content_->size());
}
std::string& content = option_->password() ? password_content : *content_; std::string& content = option_->password() ? password_content : *content_;
int size = GlyphCount(content); int size = GlyphCount(content);
@ -65,20 +70,23 @@ class InputBase : public ComponentBase {
if (size == 0) { if (size == 0) {
bool hovered = hovered_; bool hovered = hovered_;
Decorator decorator = dim | main_decorator; Decorator decorator = dim | main_decorator;
if (is_focused) if (is_focused) {
decorator = decorator | focus; decorator = decorator | focus;
if (hovered || is_focused) }
if (hovered || is_focused) {
decorator = decorator | inverted; decorator = decorator | inverted;
}
return text(*placeholder_) | decorator | reflect(box_); return text(*placeholder_) | decorator | reflect(box_);
} }
// Not focused. // Not focused.
if (!is_focused) { if (!is_focused) {
if (hovered_) if (hovered_) {
return text(content) | main_decorator | inverted | reflect(box_); return text(content) | main_decorator | inverted | reflect(box_);
else } else {
return text(content) | main_decorator | reflect(box_); return text(content) | main_decorator | reflect(box_);
} }
}
int index_before_cursor = GlyphPosition(content, cursor_position()); int index_before_cursor = GlyphPosition(content, cursor_position());
int index_after_cursor = GlyphPosition(content, 1, index_before_cursor); int index_after_cursor = GlyphPosition(content, 1, index_before_cursor);
@ -100,17 +108,19 @@ class InputBase : public ComponentBase {
bool OnEvent(Event event) override { bool OnEvent(Event event) override {
cursor_position() = 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); return OnMouseEvent(event);
}
std::string c; std::string c;
// Backspace. // Backspace.
if (event == Event::Backspace) { if (event == Event::Backspace) {
if (cursor_position() == 0) if (cursor_position() == 0) {
return false; return false;
}
size_t start = GlyphPosition(*content_, cursor_position() - 1); size_t start = GlyphPosition(*content_, cursor_position() - 1);
size_t end = GlyphPosition(*content_, cursor_position()); size_t end = GlyphPosition(*content_, cursor_position());
content_->erase(start, end - start); content_->erase(start, end - start);
@ -121,8 +131,9 @@ class InputBase : public ComponentBase {
// Delete // Delete
if (event == Event::Delete) { if (event == Event::Delete) {
if (cursor_position() == int(content_->size())) if (cursor_position() == int(content_->size())) {
return false; return false;
}
size_t start = GlyphPosition(*content_, cursor_position()); size_t start = GlyphPosition(*content_, cursor_position());
size_t end = GlyphPosition(*content_, cursor_position() + 1); size_t end = GlyphPosition(*content_, cursor_position() + 1);
content_->erase(start, end - start); content_->erase(start, end - start);
@ -176,8 +187,9 @@ class InputBase : public ComponentBase {
bool OnMouseEvent(Event event) { bool OnMouseEvent(Event event) {
hovered_ = hovered_ =
box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event); box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event);
if (!hovered_) if (!hovered_) {
return false; return false;
}
if (event.mouse().button != Mouse::Left || if (event.mouse().button != Mouse::Left ||
event.mouse().motion != Mouse::Pressed) { event.mouse().motion != Mouse::Pressed) {
@ -185,22 +197,24 @@ class InputBase : public ComponentBase {
} }
TakeFocus(); TakeFocus();
if (content_->size() == 0) if (content_->empty()) {
return true; return true;
}
auto mapping = CellToGlyphIndex(*content_); auto mapping = CellToGlyphIndex(*content_);
int original_glyph = cursor_position(); int original_glyph = cursor_position();
original_glyph = util::clamp(original_glyph, 0, int(mapping.size())); 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++) { for (size_t i = 0; i < mapping.size(); i++) {
if (mapping[i] == original_glyph) { if (mapping[i] == original_glyph) {
original_cell = i; original_cell = (int)i;
break; break;
} }
} }
if (mapping[original_cell] != original_glyph) if (mapping[original_cell] != original_glyph) {
original_cell = mapping.size(); 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 target_glyph = target_cell < (int)mapping.size() ? mapping[target_cell]
: (int)mapping.size(); : (int)mapping.size();
target_glyph = util::clamp(target_glyph, 0, GlyphCount(*content_)); target_glyph = util::clamp(target_glyph, 0, GlyphCount(*content_));
@ -279,7 +293,8 @@ class WideInputBase : public InputBase {
Component Input(StringRef content, Component Input(StringRef content,
ConstStringRef placeholder, ConstStringRef placeholder,
Ref<InputOption> option) { 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. /// @brief . An input box for editing text.
@ -307,7 +322,8 @@ Component Input(StringRef content,
Component Input(WideStringRef content, Component Input(WideStringRef content,
ConstStringRef placeholder, ConstStringRef placeholder,
Ref<InputOption> option) { 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 } // namespace ftxui

View File

@ -13,7 +13,7 @@ namespace ftxui {
Component Maybe(Component child, std::function<bool()> show) { Component Maybe(Component child, std::function<bool()> show) {
class Impl : public ComponentBase { class Impl : public ComponentBase {
public: public:
Impl(std::function<bool()> show) : show_(std::move(show)) {} explicit Impl(std::function<bool()> show) : show_(std::move(show)) {}
private: private:
Element Render() override { Element Render() override {
@ -48,7 +48,7 @@ Component Maybe(Component child, std::function<bool()> show) {
/// ``` /// ```
ComponentDecorator Maybe(std::function<bool()> show) { ComponentDecorator Maybe(std::function<bool()> show) {
return [show = std::move(show)](Component child) mutable { 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); /// auto maybe_component = Maybe(component, &show);
/// ``` /// ```
Component Maybe(Component child, const bool* 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. /// @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); /// auto maybe_component = component | Maybe(&show);
/// ``` /// ```
ComponentDecorator Maybe(const bool* 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 } // namespace ftxui

View File

@ -24,13 +24,15 @@ namespace ftxui {
namespace { namespace {
Element DefaultOptionTransform(EntryState state) { Element DefaultOptionTransform(const EntryState& state) {
state.label = (state.active ? "> " : " ") + state.label; std::string label = (state.active ? "> " : " ") + state.label; // NOLINT
Element e = text(state.label); Element e = text(label);
if (state.focused) if (state.focused) {
e = e | inverted; e = e | inverted;
if (state.active) }
if (state.active) {
e = e | bold; e = e | bold;
}
return e; return e;
} }
@ -65,18 +67,20 @@ bool IsHorizontal(MenuOption::Direction direction) {
class MenuBase : public ComponentBase { class MenuBase : public ComponentBase {
public: public:
MenuBase(ConstStringListRef entries, int* selected, Ref<MenuOption> option) 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); } bool IsHorizontal() { return ftxui::IsHorizontal(option_->direction); }
void OnChange() { void OnChange() {
if (option_->on_change) if (option_->on_change) {
option_->on_change(); option_->on_change();
} }
}
void OnEnter() { void OnEnter() {
if (option_->on_enter) if (option_->on_enter) {
option_->on_enter(); option_->on_enter();
} }
}
void Clamp() { void Clamp() {
boxes_.resize(size()); boxes_.resize(size());
@ -87,11 +91,13 @@ class MenuBase : public ComponentBase {
void OnAnimation(animation::Params& params) override { void OnAnimation(animation::Params& params) override {
animator_first_.OnAnimation(params); animator_first_.OnAnimation(params);
animator_second_.OnAnimation(params); animator_second_.OnAnimation(params);
for (auto& animator : animator_background_) for (auto& animator : animator_background_) {
animator.OnAnimation(params); animator.OnAnimation(params);
for (auto& animator : animator_foreground_) }
for (auto& animator : animator_foreground_) {
animator.OnAnimation(params); animator.OnAnimation(params);
} }
}
Element Render() override { Element Render() override {
Clamp(); Clamp();
@ -99,11 +105,13 @@ class MenuBase : public ComponentBase {
Elements elements; Elements elements;
bool is_menu_focused = Focused(); bool is_menu_focused = Focused();
if (option_->elements_prefix) if (option_->elements_prefix) {
elements.push_back(option_->elements_prefix()); elements.push_back(option_->elements_prefix());
}
for (int i = 0; i < size(); ++i) { 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()); elements.push_back(option_->elements_infix());
}
bool is_focused = (focused_entry() == i) && is_menu_focused; bool is_focused = (focused_entry() == i) && is_menu_focused;
bool is_selected = (*selected_ == i); bool is_selected = (*selected_ == i);
@ -120,21 +128,24 @@ class MenuBase : public ComponentBase {
Element element = Element element =
(option_->entries.transform ? option_->entries.transform (option_->entries.transform ? option_->entries.transform
: DefaultOptionTransform) // : DefaultOptionTransform) //
(std::move(state)); (state);
elements.push_back(element | AnimatedColorStyle(i) | reflect(boxes_[i]) | elements.push_back(element | AnimatedColorStyle(i) | reflect(boxes_[i]) |
focus_management); focus_management);
} }
if (option_->elements_postfix) if (option_->elements_postfix) {
elements.push_back(option_->elements_postfix()); elements.push_back(option_->elements_postfix());
}
if (IsInverted(option_->direction)) if (IsInverted(option_->direction)) {
std::reverse(elements.begin(), elements.end()); std::reverse(elements.begin(), elements.end());
}
Element bar = Element bar =
IsHorizontal() ? hbox(std::move(elements)) : vbox(std::move(elements)); IsHorizontal() ? hbox(std::move(elements)) : vbox(std::move(elements));
if (!option_->underline.enabled) if (!option_->underline.enabled) {
return bar | reflect(box_); return bar | reflect(box_);
}
if (IsHorizontal()) { if (IsHorizontal()) {
return vbox({ return vbox({
@ -211,36 +222,49 @@ class MenuBase : public ComponentBase {
} }
} }
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
bool OnEvent(Event event) override { bool OnEvent(Event event) override {
Clamp(); Clamp();
if (!CaptureMouse(event)) if (!CaptureMouse(event)) {
return false; return false;
}
if (event.is_mouse()) if (event.is_mouse()) {
return OnMouseEvent(event); return OnMouseEvent(event);
}
if (Focused()) { if (Focused()) {
int old_selected = *selected_; int old_selected = *selected_;
if (event == Event::ArrowUp || event == Event::Character('k')) if (event == Event::ArrowUp || event == Event::Character('k')) {
OnUp(); OnUp();
if (event == Event::ArrowDown || event == Event::Character('j')) }
if (event == Event::ArrowDown || event == Event::Character('j')) {
OnDown(); OnDown();
if (event == Event::ArrowLeft || event == Event::Character('h')) }
if (event == Event::ArrowLeft || event == Event::Character('h')) {
OnLeft(); OnLeft();
if (event == Event::ArrowRight || event == Event::Character('l')) }
if (event == Event::ArrowRight || event == Event::Character('l')) {
OnRight(); OnRight();
if (event == Event::PageUp) }
if (event == Event::PageUp) {
(*selected_) -= box_.y_max - box_.y_min; (*selected_) -= box_.y_max - box_.y_min;
if (event == Event::PageDown) }
if (event == Event::PageDown) {
(*selected_) += box_.y_max - box_.y_min; (*selected_) += box_.y_max - box_.y_min;
if (event == Event::Home) }
if (event == Event::Home) {
(*selected_) = 0; (*selected_) = 0;
if (event == Event::End) }
if (event == Event::End) {
(*selected_) = size() - 1; (*selected_) = size() - 1;
if (event == Event::Tab && size()) }
if (event == Event::Tab && size()) {
*selected_ = (*selected_ + 1) % size(); *selected_ = (*selected_ + 1) % size();
if (event == Event::TabReverse && size()) }
if (event == Event::TabReverse && size()) {
*selected_ = (*selected_ + size() - 1) % size(); *selected_ = (*selected_ + size() - 1) % size();
}
*selected_ = util::clamp(*selected_, 0, size() - 1); *selected_ = util::clamp(*selected_, 0, size() - 1);
@ -269,11 +293,13 @@ class MenuBase : public ComponentBase {
event.mouse().button != Mouse::Left) { event.mouse().button != Mouse::Left) {
return false; return false;
} }
if (!CaptureMouse(event)) if (!CaptureMouse(event)) {
return false; return false;
}
for (int i = 0; i < size(); ++i) { 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; continue;
}
TakeFocus(); TakeFocus();
focused_entry() = i; focused_entry() = i;
@ -290,19 +316,23 @@ class MenuBase : public ComponentBase {
} }
bool OnMouseWheel(Event event) { bool OnMouseWheel(Event event) {
if (!box_.Contain(event.mouse().x, event.mouse().y)) if (!box_.Contain(event.mouse().x, event.mouse().y)) {
return false; return false;
}
int old_selected = *selected_; int old_selected = *selected_;
if (event.mouse().button == Mouse::WheelUp) if (event.mouse().button == Mouse::WheelUp) {
(*selected_)--; (*selected_)--;
if (event.mouse().button == Mouse::WheelDown) }
if (event.mouse().button == Mouse::WheelDown) {
(*selected_)++; (*selected_)++;
}
*selected_ = util::clamp(*selected_, 0, size() - 1); *selected_ = util::clamp(*selected_, 0, size() - 1);
if (*selected_ != old_selected) if (*selected_ != old_selected) {
OnChange(); OnChange();
}
return true; return true;
} }
@ -319,12 +349,12 @@ class MenuBase : public ComponentBase {
animator_foreground_.clear(); animator_foreground_.clear();
for (int i = 0; i < size(); ++i) { for (int i = 0; i < size(); ++i) {
animation_background_[i] = 0.f; animation_background_[i] = 0.F;
animation_foreground_[i] = 0.f; animation_foreground_[i] = 0.F;
animator_background_.emplace_back(&animation_background_[i], 0.f, animator_background_.emplace_back(&animation_background_[i], 0.F,
std::chrono::milliseconds(0), std::chrono::milliseconds(0),
animation::easing::Linear); 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), std::chrono::milliseconds(0),
animation::easing::Linear); animation::easing::Linear);
} }
@ -334,7 +364,7 @@ class MenuBase : public ComponentBase {
for (int i = 0; i < size(); ++i) { for (int i = 0; i < size(); ++i) {
bool is_focused = (focused_entry() == i) && is_menu_focused; bool is_focused = (focused_entry() == i) && is_menu_focused;
bool is_selected = (*selected_ == i); 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) { if (animator_background_[i].to() != target) {
animator_background_[i] = animation::Animator( animator_background_[i] = animation::Animator(
&animation_background_[i], target, &animation_background_[i], target,
@ -367,8 +397,9 @@ class MenuBase : public ComponentBase {
} }
void UpdateUnderlineTarget() { void UpdateUnderlineTarget() {
if (!option_->underline.enabled) if (!option_->underline.enabled) {
return; return;
}
if (FirstTarget() == animator_first_.to() && if (FirstTarget() == animator_first_.to() &&
SecondTarget() == animator_second_.to()) { SecondTarget() == animator_second_.to()) {
@ -398,32 +429,36 @@ class MenuBase : public ComponentBase {
bool Focusable() const final { return entries_.size(); } bool Focusable() const final { return entries_.size(); }
int& focused_entry() { return option_->focused_entry(); } int& focused_entry() { return option_->focused_entry(); }
int size() const { return entries_.size(); } int size() const { return int(entries_.size()); }
int FirstTarget() { float FirstTarget() {
if (boxes_.size() == 0) if (boxes_.empty()) {
return 0; return 0.F;
return IsHorizontal() ? boxes_[*selected_].x_min - box_.x_min
: boxes_[*selected_].y_min - box_.y_min;
} }
int SecondTarget() { int value = IsHorizontal() ? boxes_[*selected_].x_min - box_.x_min
if (boxes_.size() == 0) : boxes_[*selected_].y_min - box_.y_min;
return 0; return float(value);
return IsHorizontal() ? boxes_[*selected_].x_max - box_.x_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; : boxes_[*selected_].y_max - box_.y_min;
return float(value);
} }
protected: protected:
ConstStringListRef entries_; ConstStringListRef entries_;
int* selected_ = 0; int* selected_ = nullptr;
Ref<MenuOption> option_; Ref<MenuOption> option_;
std::vector<Box> boxes_; std::vector<Box> boxes_;
Box box_; Box box_;
float first_ = 0.f; float first_ = 0.F;
float second_ = 0.f; float second_ = 0.F;
animation::Animator animator_first_ = animation::Animator(&first_, 0.f); animation::Animator animator_first_ = animation::Animator(&first_, 0.F);
animation::Animator animator_second_ = animation::Animator(&second_, 0.f); animation::Animator animator_second_ = animation::Animator(&second_, 0.F);
std::vector<animation::Animator> animator_background_; std::vector<animation::Animator> animator_background_;
std::vector<animation::Animator> animator_foreground_; std::vector<animation::Animator> animator_foreground_;
@ -519,7 +554,7 @@ Component MenuEntry(ConstStringRef label, Ref<MenuEntryOption> option) {
Element element = Element element =
(option_->transform ? option_->transform : DefaultOptionTransform) // (option_->transform ? option_->transform : DefaultOptionTransform) //
(std::move(state)); (state);
auto focus_management = focused ? select : nothing; auto focus_management = focused ? select : nothing;
return element | AnimatedColorStyle() | focus_management | reflect(box_); return element | AnimatedColorStyle() | focus_management | reflect(box_);
@ -527,9 +562,10 @@ Component MenuEntry(ConstStringRef label, Ref<MenuEntryOption> option) {
void UpdateAnimationTarget() { void UpdateAnimationTarget() {
bool focused = Focused(); bool focused = Focused();
float target = focused ? 1.0f : hovered_ ? 0.5f : 0.0f; float target = focused ? 1.F : hovered_ ? 0.5F : 0.F; // NOLINT
if (target == animator_background_.to()) if (target == animator_background_.to()) {
return; return;
}
animator_background_ = animator_background_ =
animation::Animator(&animation_background_, target, animation::Animator(&animation_background_, target,
option_->animated_colors.background.duration, option_->animated_colors.background.duration,
@ -560,13 +596,15 @@ Component MenuEntry(ConstStringRef label, Ref<MenuEntryOption> option) {
bool Focusable() const override { return true; } bool Focusable() const override { return true; }
bool OnEvent(Event event) override { bool OnEvent(Event event) override {
if (!event.is_mouse()) if (!event.is_mouse()) {
return false; return false;
}
hovered_ = box_.Contain(event.mouse().x, event.mouse().y); hovered_ = box_.Contain(event.mouse().x, event.mouse().y);
if (!hovered_) if (!hovered_) {
return false; return false;
}
if (event.mouse().button == Mouse::Left && if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Released) { event.mouse().motion == Mouse::Released) {
@ -587,12 +625,12 @@ Component MenuEntry(ConstStringRef label, Ref<MenuEntryOption> option) {
Box box_; Box box_;
bool hovered_ = false; bool hovered_ = false;
float animation_background_ = 0.f; float animation_background_ = 0.F;
float animation_foreground_ = 0.f; float animation_foreground_ = 0.F;
animation::Animator animator_background_ = animation::Animator animator_background_ =
animation::Animator(&animation_background_, 0.f); animation::Animator(&animation_background_, 0.F);
animation::Animator animator_foreground_ = 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)); return Make<Impl>(std::move(label), std::move(option));

View File

@ -1,18 +1,17 @@
#include <algorithm> // for max #include <algorithm> // for max
#include <functional> // for function #include <functional> // for function
#include <memory> // for shared_ptr, allocator_traits<>::value_type #include <memory> // for shared_ptr, allocator_traits<>::value_type
#include <string> // for string
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Radiobox #include "ftxui/component/component.hpp" // for Make, Radiobox
#include "ftxui/component/component_base.hpp" // for ComponentBase #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/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/mouse.hpp" // for Mouse, Mouse::WheelDown, Mouse::WheelUp, Mouse::Left, Mouse::Released
#include "ftxui/component/screen_interactive.hpp" // for Component #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/dom/elements.hpp" // for operator|, reflect, Element, vbox, Elements, focus, nothing, select
#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/util.hpp" // for clamp #include "ftxui/screen/util.hpp" // for clamp
#include "ftxui/util/ref.hpp" // for Ref, ConstStringListRef #include "ftxui/util/ref.hpp" // for Ref, ConstStringListRef
@ -50,41 +49,51 @@ class RadioboxBase : public ComponentBase {
is_focused, is_focused,
}; };
auto element = auto element =
(option_->transform (option_->transform ? option_->transform
? option_->transform : RadioboxOption::Simple().transform)(state);
: RadioboxOption::Simple().transform)(std::move(state));
elements.push_back(element | focus_management | reflect(boxes_[i])); elements.push_back(element | focus_management | reflect(boxes_[i]));
} }
return vbox(std::move(elements)) | reflect(box_); return vbox(std::move(elements)) | reflect(box_);
} }
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
bool OnEvent(Event event) override { bool OnEvent(Event event) override {
Clamp(); Clamp();
if (!CaptureMouse(event)) if (!CaptureMouse(event)) {
return false; return false;
}
if (event.is_mouse()) if (event.is_mouse()) {
return OnMouseEvent(event); return OnMouseEvent(event);
}
if (Focused()) { if (Focused()) {
int old_hovered = hovered_; int old_hovered = hovered_;
if (event == Event::ArrowUp || event == Event::Character('k')) if (event == Event::ArrowUp || event == Event::Character('k')) {
(hovered_)--; (hovered_)--;
if (event == Event::ArrowDown || event == Event::Character('j')) }
if (event == Event::ArrowDown || event == Event::Character('j')) {
(hovered_)++; (hovered_)++;
if (event == Event::PageUp) }
if (event == Event::PageUp) {
(hovered_) -= box_.y_max - box_.y_min; (hovered_) -= box_.y_max - box_.y_min;
if (event == Event::PageDown) }
if (event == Event::PageDown) {
(hovered_) += box_.y_max - box_.y_min; (hovered_) += box_.y_max - box_.y_min;
if (event == Event::Home) }
if (event == Event::Home) {
(hovered_) = 0; (hovered_) = 0;
if (event == Event::End) }
if (event == Event::End) {
(hovered_) = size() - 1; (hovered_) = size() - 1;
if (event == Event::Tab && size()) }
if (event == Event::Tab && size()) {
hovered_ = (hovered_ + 1) % size(); hovered_ = (hovered_ + 1) % size();
if (event == Event::TabReverse && size()) }
if (event == Event::TabReverse && size()) {
hovered_ = (hovered_ + size() - 1) % size(); hovered_ = (hovered_ + size() - 1) % size();
}
hovered_ = util::clamp(hovered_, 0, size() - 1); hovered_ = util::clamp(hovered_, 0, size() - 1);
@ -111,8 +120,9 @@ class RadioboxBase : public ComponentBase {
} }
for (int i = 0; i < size(); ++i) { 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; continue;
}
TakeFocus(); TakeFocus();
focused_entry() = i; focused_entry() = i;
@ -130,20 +140,24 @@ class RadioboxBase : public ComponentBase {
} }
bool OnMouseWheel(Event event) { bool OnMouseWheel(Event event) {
if (!box_.Contain(event.mouse().x, event.mouse().y)) if (!box_.Contain(event.mouse().x, event.mouse().y)) {
return false; return false;
}
int old_hovered = hovered_; int old_hovered = hovered_;
if (event.mouse().button == Mouse::WheelUp) if (event.mouse().button == Mouse::WheelUp) {
(hovered_)--; (hovered_)--;
if (event.mouse().button == Mouse::WheelDown) }
if (event.mouse().button == Mouse::WheelDown) {
(hovered_)++; (hovered_)++;
}
hovered_ = util::clamp(hovered_, 0, size() - 1); hovered_ = util::clamp(hovered_, 0, size() - 1);
if (hovered_ != old_hovered) if (hovered_ != old_hovered) {
option_->on_change(); option_->on_change();
}
return true; return true;
} }
@ -157,7 +171,7 @@ class RadioboxBase : public ComponentBase {
bool Focusable() const final { return entries_.size(); } bool Focusable() const final { return entries_.size(); }
int& focused_entry() { return option_->focused_entry(); } int& focused_entry() { return option_->focused_entry(); }
int size() const { return entries_.size(); } int size() const { return int(entries_.size()); }
ConstStringListRef entries_; ConstStringListRef entries_;
int* selected_; int* selected_;

View File

@ -28,7 +28,8 @@ namespace ftxui {
Component Renderer(std::function<Element()> render) { Component Renderer(std::function<Element()> render) {
class Impl : public ComponentBase { class Impl : public ComponentBase {
public: 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_(); } Element Render() override { return render_(); }
std::function<Element()> render_; std::function<Element()> render_;
}; };
@ -82,15 +83,17 @@ Component Renderer(Component child, std::function<Element()> render) {
Component Renderer(std::function<Element(bool)> render) { Component Renderer(std::function<Element(bool)> render) {
class Impl : public ComponentBase { class Impl : public ComponentBase {
public: public:
Impl(std::function<Element(bool)> render) : render_(std::move(render)) {} explicit Impl(std::function<Element(bool)> render)
: render_(std::move(render)) {}
private: private:
Element Render() override { return render_(Focused()) | reflect(box_); } Element Render() override { return render_(Focused()) | reflect(box_); }
bool Focusable() const override { return true; } bool Focusable() const override { return true; }
bool OnEvent(Event event) override { bool OnEvent(Event event) override {
if (event.is_mouse() && box_.Contain(event.mouse().x, event.mouse().y)) { if (event.is_mouse() && box_.Contain(event.mouse().x, event.mouse().y)) {
if (!CaptureMouse(event)) if (!CaptureMouse(event)) {
return false; return false;
}
TakeFocus(); TakeFocus();
} }
@ -118,8 +121,8 @@ Component Renderer(std::function<Element(bool)> render) {
/// | Renderer(inverted); /// | Renderer(inverted);
/// screen.Loop(renderer); /// screen.Loop(renderer);
/// ``` /// ```
ComponentDecorator Renderer(ElementDecorator decorator) { ComponentDecorator Renderer(ElementDecorator decorator) { // NOLINT
return [decorator](Component component) { return [decorator](Component component) { // NOLINT
return Renderer(component, [component, decorator] { return Renderer(component, [component, decorator] {
return component->Render() | decorator; return component->Render() | decorator;
}); });

View File

@ -15,16 +15,19 @@ namespace {
class ResizableSplitLeftBase : public ComponentBase { class ResizableSplitLeftBase : public ComponentBase {
public: public:
ResizableSplitLeftBase(Component main, Component child, int* main_size) 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({ Add(Container::Horizontal({
main, main_,
child, child_,
})); }));
} }
bool OnEvent(Event event) final { bool OnEvent(Event event) final {
if (event.is_mouse()) if (event.is_mouse()) {
return OnMouseEvent(std::move(event)); return OnMouseEvent(std::move(event));
}
return ComponentBase::OnEvent(std::move(event)); return ComponentBase::OnEvent(std::move(event));
} }
@ -71,16 +74,19 @@ class ResizableSplitLeftBase : public ComponentBase {
class ResizableSplitRightBase : public ComponentBase { class ResizableSplitRightBase : public ComponentBase {
public: public:
ResizableSplitRightBase(Component main, Component child, int* main_size) 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({ Add(Container::Horizontal({
child, child_,
main, main_,
})); }));
} }
bool OnEvent(Event event) final { bool OnEvent(Event event) final {
if (event.is_mouse()) if (event.is_mouse()) {
return OnMouseEvent(std::move(event)); return OnMouseEvent(std::move(event));
}
return ComponentBase::OnEvent(std::move(event)); return ComponentBase::OnEvent(std::move(event));
} }
@ -127,16 +133,19 @@ class ResizableSplitRightBase : public ComponentBase {
class ResizableSplitTopBase : public ComponentBase { class ResizableSplitTopBase : public ComponentBase {
public: public:
ResizableSplitTopBase(Component main, Component child, int* main_size) 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({ Add(Container::Vertical({
main, main_,
child, child_,
})); }));
} }
bool OnEvent(Event event) final { bool OnEvent(Event event) final {
if (event.is_mouse()) if (event.is_mouse()) {
return OnMouseEvent(std::move(event)); return OnMouseEvent(std::move(event));
}
return ComponentBase::OnEvent(std::move(event)); return ComponentBase::OnEvent(std::move(event));
} }
@ -183,16 +192,19 @@ class ResizableSplitTopBase : public ComponentBase {
class ResizableSplitBottomBase : public ComponentBase { class ResizableSplitBottomBase : public ComponentBase {
public: public:
ResizableSplitBottomBase(Component main, Component child, int* main_size) 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({ Add(Container::Vertical({
child, child_,
main, main_,
})); }));
} }
bool OnEvent(Event event) final { bool OnEvent(Event event) final {
if (event.is_mouse()) if (event.is_mouse()) {
return OnMouseEvent(std::move(event)); return OnMouseEvent(std::move(event));
}
return ComponentBase::OnEvent(std::move(event)); return ComponentBase::OnEvent(std::move(event));
} }

View File

@ -1,15 +1,15 @@
#include <stdio.h> // for fileno, stdin
#include <algorithm> // for copy, max, min #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 <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 <functional> // for function
#include <initializer_list> // for initializer_list #include <initializer_list> // for initializer_list
#include <iostream> // for cout, ostream, basic_ostream, operator<<, endl, flush #include <iostream> // for cout, ostream, basic_ostream, operator<<, endl, flush
#include <stack> // for stack #include <stack> // for stack
#include <thread> // for thread, sleep_for #include <thread> // for thread, sleep_for
#include <type_traits> // for decay_t #include <type_traits> // for decay_t
#include <utility> // for swap, move #include <utility> // for move, swap
#include <variant> // for visit #include <variant> // for visit
#include <vector> // for vector #include <vector> // for vector
@ -50,14 +50,15 @@ namespace ftxui {
namespace animation { namespace animation {
void RequestAnimationFrame() { void RequestAnimationFrame() {
auto* screen = ScreenInteractive::Active(); auto* screen = ScreenInteractive::Active();
if (screen) if (screen) {
screen->RequestAnimationFrame(); screen->RequestAnimationFrame();
}
} }
} // namespace animation } // namespace animation
namespace { namespace {
ScreenInteractive* g_active_screen = nullptr; ScreenInteractive* g_active_screen = nullptr; // NOLINT
void Flush() { void Flush() {
// Emscripten doesn't implement flush. We interpret zero as 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) { int CheckStdinReady(int usec_timeout) {
timeval tv = {0, usec_timeout}; timeval tv = {0, usec_timeout};
fd_set fds; fd_set fds;
FD_ZERO(&fds); FD_ZERO(&fds); // NOLINT
FD_SET(STDIN_FILENO, &fds); FD_SET(STDIN_FILENO, &fds); // NOLINT
select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &tv); // NOLINT
return FD_ISSET(STDIN_FILENO, &fds); return FD_ISSET(STDIN_FILENO, &fds); // NOLINT
} }
// Read char from the terminal. // Read char from the terminal.
void EventListener(std::atomic<bool>* quit, Sender<Task> out) { void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
const int buffer_size = 100;
auto parser = TerminalInputParser(std::move(out)); auto parser = TerminalInputParser(std::move(out));
while (!*quit) { while (!*quit) {
@ -156,16 +155,18 @@ void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
continue; continue;
} }
char buff[buffer_size]; const size_t buffer_size = 100;
int l = read(fileno(stdin), buff, buffer_size); std::array<char, buffer_size> buffer; // NOLINT;
for (int i = 0; i < l; ++i) int l = read(fileno(stdin), buffer.data(), buffer_size); // NOLINT
parser.Add(buff[i]); for (int i = 0; i < l; ++i) {
parser.Add(buffer[i]); // NOLINT
}
} }
} }
#endif #endif
const std::string CSI = "\x1b["; const std::string CSI = "\x1b["; // NOLINT
// DEC: Digital Equipment Corporation // DEC: Digital Equipment Corporation
enum class DECMode { enum class DECMode {
@ -186,12 +187,13 @@ enum class DSRMode {
kCursor = 6, kCursor = 6,
}; };
const std::string Serialize(std::vector<DECMode> parameters) { std::string Serialize(const std::vector<DECMode>& parameters) {
bool first = true; bool first = true;
std::string out; std::string out;
for (DECMode parameter : parameters) { for (DECMode parameter : parameters) {
if (!first) if (!first) {
out += ";"; out += ";";
}
out += std::to_string(int(parameter)); out += std::to_string(int(parameter));
first = false; first = false;
} }
@ -199,22 +201,22 @@ const std::string Serialize(std::vector<DECMode> parameters) {
} }
// DEC Private Mode Set (DECSET) // 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"; return CSI + "?" + Serialize(parameters) + "h";
} }
// DEC Private Mode Reset (DECRST) // 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"; return CSI + "?" + Serialize(parameters) + "l";
} }
// Device Status Report (DSR) // Device Status Report (DSR)
const std::string DeviceStatusReport(DSRMode ps) { std::string DeviceStatusReport(DSRMode ps) {
return CSI + std::to_string(int(ps)) + "n"; return CSI + std::to_string(int(ps)) + "n";
} }
using SignalHandler = void(int); using SignalHandler = void(int);
std::stack<Closure> on_exit_functions; std::stack<Closure> on_exit_functions; // NOLINT
void OnExit(int signal) { void OnExit(int signal) {
(void)signal; (void)signal;
while (!on_exit_functions.empty()) { 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); auto old_signal_handler = std::signal(sig, handler);
on_exit_functions.push([=] { std::signal(sig, old_signal_handler); }); on_exit_functions.push([=] { std::signal(sig, old_signal_handler); });
}; };
Closure on_resize = [] {}; Closure g_on_resize = [] {}; // NOLINT
void OnResize(int /* signal */) { void OnResize(int /* signal */) {
on_resize(); g_on_resize();
} }
void OnSigStop(int /*signal*/) { void OnSigStop(int /*signal*/) {
@ -239,17 +241,24 @@ void OnSigStop(int /*signal*/) {
class CapturedMouseImpl : public CapturedMouseInterface { class CapturedMouseImpl : public CapturedMouseInterface {
public: public:
CapturedMouseImpl(std::function<void(void)> callback) : callback_(callback) {} explicit CapturedMouseImpl(std::function<void(void)> callback)
: callback_(std::move(callback)) {}
~CapturedMouseImpl() override { callback_(); } ~CapturedMouseImpl() override { callback_(); }
CapturedMouseImpl(const CapturedMouseImpl&) = delete;
CapturedMouseImpl(CapturedMouseImpl&&) = delete;
CapturedMouseImpl& operator=(const CapturedMouseImpl&) = delete;
CapturedMouseImpl& operator=(CapturedMouseImpl&&) = delete;
private: private:
std::function<void(void)> callback_; std::function<void(void)> callback_;
}; };
void AnimationListener(std::atomic<bool>* quit, Sender<Task> out) { void AnimationListener(std::atomic<bool>* quit, Sender<Task> out) {
// Animation at around 60fps.
const auto time_delta = std::chrono::milliseconds(15);
while (!*quit) { while (!*quit) {
out->Send(AnimationTask()); 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) { void ScreenInteractive::Post(Task task) {
if (!quit_) if (!quit_) {
task_sender_->Send(task); task_sender_->Send(std::move(task));
}
} }
void ScreenInteractive::PostEvent(Event event) { void ScreenInteractive::PostEvent(Event event) {
Post(event); Post(event);
} }
void ScreenInteractive::RequestAnimationFrame() { void ScreenInteractive::RequestAnimationFrame() {
if (animation_requested_) if (animation_requested_) {
return; return;
}
animation_requested_ = true; animation_requested_ = true;
auto now = animation::Clock::now(); 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; previous_animation_time = now;
}
} }
CapturedMouse ScreenInteractive::CaptureMouse() { CapturedMouse ScreenInteractive::CaptureMouse() {
if (mouse_captured) if (mouse_captured) {
return nullptr; return nullptr;
}
mouse_captured = true; mouse_captured = true;
return std::make_unique<CapturedMouseImpl>( return std::make_unique<CapturedMouseImpl>(
[this] { mouse_captured = false; }); [this] { mouse_captured = false; });
} }
void ScreenInteractive::Loop(Component component) { void ScreenInteractive::Loop(Component component) { // NOLINT
// Suspend previously active screen: // Suspend previously active screen:
if (g_active_screen) { if (g_active_screen) {
std::swap(suspended_screen_, g_active_screen); std::swap(suspended_screen_, g_active_screen);
@ -324,7 +338,7 @@ void ScreenInteractive::Loop(Component component) {
// This screen is now active: // This screen is now active:
g_active_screen = this; g_active_screen = this;
g_active_screen->Install(); g_active_screen->Install();
g_active_screen->Main(component); g_active_screen->Main(std::move(component));
g_active_screen->Uninstall(); g_active_screen->Uninstall();
g_active_screen = nullptr; 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 /// @brief Decorate a function. It executes the same way, but with the currently
/// active screen terminal hooks temporarilly uninstalled during its execution. /// active screen terminal hooks temporarilly uninstalled during its execution.
/// @param fn The function to decorate. /// @param fn The function to decorate.
Closure ScreenInteractive::WithRestoredIO(Closure fn) { Closure ScreenInteractive::WithRestoredIO(Closure fn) { // NOLINT
return [this, fn] { return [this, fn] {
Uninstall(); Uninstall();
fn(); fn();
@ -370,8 +384,9 @@ void ScreenInteractive::Install() {
// Install signal handlers to restore the terminal state on exit. The default // Install signal handlers to restore the terminal state on exit. The default
// signal handlers are restored on exit. // 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); 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) #if defined(_WIN32)
@ -405,12 +420,12 @@ void ScreenInteractive::Install() {
SetConsoleMode(stdin_handle, in_mode); SetConsoleMode(stdin_handle, in_mode);
SetConsoleMode(stdout_handle, out_mode); SetConsoleMode(stdout_handle, out_mode);
#else #else
struct termios terminal; struct termios terminal; // NOLINT
tcgetattr(STDIN_FILENO, &terminal); tcgetattr(STDIN_FILENO, &terminal);
on_exit_functions.push([=] { tcsetattr(STDIN_FILENO, TCSANOW, &terminal); }); on_exit_functions.push([=] { tcsetattr(STDIN_FILENO, TCSANOW, &terminal); });
terminal.c_lflag &= ~ICANON; // Non canonique terminal. terminal.c_lflag &= ~ICANON; // NOLINT Non canonique terminal.
terminal.c_lflag &= ~ECHO; // Do not print after a key press. terminal.c_lflag &= ~ECHO; // NOLINT Do not print after a key press.
terminal.c_cc[VMIN] = 0; terminal.c_cc[VMIN] = 0;
terminal.c_cc[VTIME] = 0; terminal.c_cc[VTIME] = 0;
// auto oldf = fcntl(STDIN_FILENO, F_GETFL, 0); // auto oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
@ -420,19 +435,19 @@ void ScreenInteractive::Install() {
tcsetattr(STDIN_FILENO, TCSANOW, &terminal); tcsetattr(STDIN_FILENO, TCSANOW, &terminal);
// Handle resize. // Handle resize.
on_resize = [&] { task_sender_->Send(Event::Special({0})); }; g_on_resize = [&] { task_sender_->Send(Event::Special({0})); };
install_signal_handler(SIGWINCH, OnResize); install_signal_handler(SIGWINCH, OnResize);
// Handle SIGTSTP/SIGCONT. // Handle SIGTSTP/SIGCONT.
install_signal_handler(SIGTSTP, OnSigStop); install_signal_handler(SIGTSTP, OnSigStop);
#endif #endif
auto enable = [&](std::vector<DECMode> parameters) { auto enable = [&](const std::vector<DECMode>& parameters) {
std::cout << Set(parameters); std::cout << Set(parameters);
on_exit_functions.push([=] { std::cout << Reset(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); std::cout << Reset(parameters);
on_exit_functions.push([=] { std::cout << Set(parameters); }); on_exit_functions.push([=] { std::cout << Set(parameters); });
}; };
@ -475,6 +490,7 @@ void ScreenInteractive::Uninstall() {
OnExit(0); OnExit(0);
} }
// NOLINTNEXTLINE
void ScreenInteractive::Main(Component component) { void ScreenInteractive::Main(Component component) {
previous_animation_time = animation::Clock::now(); previous_animation_time = animation::Clock::now();
@ -493,8 +509,9 @@ void ScreenInteractive::Main(Component component) {
} }
Task task; Task task;
if (!task_receiver_->Receive(&task)) if (!task_receiver_->Receive(&task)) {
break; break;
}
// clang-format off // clang-format off
std::visit([&](auto&& arg) { std::visit([&](auto&& arg) {
@ -527,8 +544,9 @@ void ScreenInteractive::Main(Component component) {
// Handle Animation // Handle Animation
if constexpr (std::is_same_v<T, AnimationTask>) { if constexpr (std::is_same_v<T, AnimationTask>) {
if (!animation_requested_) if (!animation_requested_) {
return; return;
}
animation_requested_ = false; animation_requested_ = false;
animation::TimePoint now = animation::Clock::now(); animation::TimePoint now = animation::Clock::now();
@ -546,6 +564,7 @@ void ScreenInteractive::Main(Component component) {
} }
} }
// NOLINTNEXTLINE
void ScreenInteractive::Draw(Component component) { void ScreenInteractive::Draw(Component component) {
auto document = component->Render(); auto document = component->Render();
int dimx = 0; int dimx = 0;
@ -596,13 +615,16 @@ void ScreenInteractive::Draw(Component component) {
// https://github.com/ArthurSonzogni/FTXUI/issues/136 // https://github.com/ArthurSonzogni/FTXUI/issues/136
static int i = -3; static int i = -3;
++i; ++i;
if (!use_alternative_screen_ && (i % 150 == 0)) if (!use_alternative_screen_ && (i % 150 == 0)) { // NOLINT
std::cout << DeviceStatusReport(DSRMode::kCursor); std::cout << DeviceStatusReport(DSRMode::kCursor);
}
#else #else
static int i = -3; static int i = -3;
++i; ++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); std::cout << DeviceStatusReport(DSRMode::kCursor);
}
#endif #endif
previous_frame_resized_ = resized; previous_frame_resized_ = resized;

View File

@ -18,13 +18,13 @@ template <class T>
class SliderBase : public ComponentBase { class SliderBase : public ComponentBase {
public: public:
SliderBase(ConstStringRef label, T* value, T min, T max, T increment) SliderBase(ConstStringRef label, T* value, T min, T max, T increment)
: label_(label), : label_(std::move(label)),
value_(value), value_(value),
min_(min), min_(min),
max_(max), max_(max),
increment_(increment) {} increment_(increment) {}
Element Render() { Element Render() override {
auto gauge_color = auto gauge_color =
Focused() ? color(Color::GrayLight) : color(Color::GrayDark); Focused() ? color(Color::GrayLight) : color(Color::GrayDark);
float percent = float(*value_ - min_) / float(max_ - min_); float percent = float(*value_ - min_) / float(max_ - min_);
@ -40,8 +40,9 @@ class SliderBase : public ComponentBase {
} }
bool OnEvent(Event event) final { bool OnEvent(Event event) final {
if (event.is_mouse()) if (event.is_mouse()) {
return OnMouseEvent(event); return OnMouseEvent(event);
}
if (event == Event::ArrowLeft || event == Event::Character('h')) { if (event == Event::ArrowLeft || event == Event::Character('h')) {
*value_ -= increment_; *value_ -= increment_;

View File

@ -14,11 +14,14 @@ TerminalInputParser::TerminalInputParser(Sender<Task> out)
void TerminalInputParser::Timeout(int time) { void TerminalInputParser::Timeout(int time) {
timeout_ += time; timeout_ += time;
if (timeout_ < 50) const int timeout_threshold = 50;
if (timeout_ < timeout_threshold) {
return; return;
}
timeout_ = 0; timeout_ = 0;
if (pending_.size()) if (!pending_.empty()) {
Send(SPECIAL); Send(SPECIAL);
}
} }
void TerminalInputParser::Add(char c) { 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: // key. This also happens with linux with the `bind` command:
// See https://github.com/ArthurSonzogni/FTXUI/issues/337 // See https://github.com/ArthurSonzogni/FTXUI/issues/337
// Here, we uniformize the new line character to `\n`. // Here, we uniformize the new line character to `\n`.
if (pending_ == "\r") if (pending_ == "\r") {
out_->Send(Event::Special("\n")); out_->Send(Event::Special("\n"));
else } else {
out_->Send(Event::Special(std::move(pending_))); out_->Send(Event::Special(std::move(pending_)));
}
pending_.clear(); pending_.clear();
return; return;
case MOUSE: case MOUSE:
out_->Send(Event::Mouse(std::move(pending_), output.mouse)); out_->Send(Event::Mouse(std::move(pending_), output.mouse)); // NOLINT
pending_.clear(); pending_.clear();
return; return;
case CURSOR_REPORTING: case CURSOR_REPORTING:
out_->Send(Event::CursorReporting(std::move(pending_), output.cursor.x, out_->Send(Event::CursorReporting(std::move(pending_), // NOLINT
output.cursor.y)); output.cursor.x, // NOLINT
output.cursor.y)); // NOLINT
pending_.clear(); pending_.clear();
return; return;
} }
@ -78,12 +83,13 @@ void TerminalInputParser::Send(TerminalInputParser::Output output) {
} }
TerminalInputParser::Output TerminalInputParser::Parse() { TerminalInputParser::Output TerminalInputParser::Parse() {
if (!Eat()) if (!Eat()) {
return UNCOMPLETED; return UNCOMPLETED;
}
switch (Current()) { switch (Current()) {
case 24: // CAN case 24: // CAN NOLINT
case 26: // SUB case 26: // SUB NOLINT
return DROP; return DROP;
case '\x1B': case '\x1B':
@ -92,11 +98,13 @@ TerminalInputParser::Output TerminalInputParser::Parse() {
break; break;
} }
if (Current() < 32) // C0 if (Current() < 32) { // C0 NOLINT
return SPECIAL; return SPECIAL;
}
if (Current() == 127) // Delete if (Current() == 127) { // Delete // NOLINT
return SPECIAL; return SPECIAL;
}
return ParseUTF8(); return ParseUTF8();
} }
@ -118,18 +126,18 @@ TerminalInputParser::Output TerminalInputParser::Parse() {
// Then some sequences are illegal if it exist a shorter representation of the // Then some sequences are illegal if it exist a shorter representation of the
// same codepoint. // same codepoint.
TerminalInputParser::Output TerminalInputParser::ParseUTF8() { TerminalInputParser::Output TerminalInputParser::ParseUTF8() {
unsigned char head = static_cast<unsigned char>(Current()); auto head = static_cast<unsigned char>(Current());
unsigned char selector = 0b1000'0000; unsigned char selector = 0b1000'0000; // NOLINT
// The non code-point part of the first byte. // The non code-point part of the first byte.
unsigned char mask = selector; unsigned char mask = selector;
// Find the first zero in the first byte. // Find the first zero in the first byte.
int first_zero = 8; unsigned int first_zero = 8; // NOLINT
for (int i = 0; i < 8; ++i) { for (unsigned int i = 0; i < 8; ++i) { // NOLINT
mask |= selector; mask |= selector;
if (head & selector) { if (head & selector) {
selector >>= 1; selector >>= 1U;
continue; continue;
} }
first_zero = i; first_zero = i;
@ -137,48 +145,54 @@ TerminalInputParser::Output TerminalInputParser::ParseUTF8() {
} }
// Accumulate the value of the first byte. // 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. // 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; return DROP;
}
// Multi byte UTF-8. // Multi byte UTF-8.
for (int i = 2; i <= first_zero; ++i) { for (unsigned int i = 2; i <= first_zero; ++i) {
if (!Eat()) if (!Eat()) {
return UNCOMPLETED; return UNCOMPLETED;
}
// Invalid continuation byte. // Invalid continuation byte.
head = static_cast<unsigned char>(Current()); head = static_cast<unsigned char>(Current());
if ((head & 0b1100'0000) != 0b1000'0000) if ((head & 0b1100'0000) != 0b1000'0000) { // NOLINT
return DROP; return DROP;
value <<= 6; }
value += head & 0b0011'1111; value <<= 6; // NOLINT
value += head & 0b0011'1111; // NOLINT
} }
// Check for overlong UTF8 encoding. // Check for overlong UTF8 encoding.
int extra_byte; int extra_byte = 0;
if (value <= 0b000'0000'0111'1111) { if (value <= 0b000'0000'0111'1111) { // NOLINT
extra_byte = 0; extra_byte = 0; // NOLINT
} else if (value <= 0b000'0111'1111'1111) { } else if (value <= 0b000'0111'1111'1111) { // NOLINT
extra_byte = 1; extra_byte = 1; // NOLINT
} else if (value <= 0b1111'1111'1111'1111) { } else if (value <= 0b1111'1111'1111'1111) { // NOLINT
extra_byte = 2; extra_byte = 2; // NOLINT
} else if (value <= 0b1'0000'1111'1111'1111'1111) { } else if (value <= 0b1'0000'1111'1111'1111'1111) { // NOLINT
extra_byte = 3; extra_byte = 3; // NOLINT
} else { } else { // NOLINT
return DROP; return DROP;
} }
if (extra_byte != position_) if (extra_byte != position_) {
return DROP; return DROP;
}
return CHARACTER; return CHARACTER;
} }
TerminalInputParser::Output TerminalInputParser::ParseESC() { TerminalInputParser::Output TerminalInputParser::ParseESC() {
if (!Eat()) if (!Eat()) {
return UNCOMPLETED; return UNCOMPLETED;
}
switch (Current()) { switch (Current()) {
case 'P': case 'P':
return ParseDCS(); return ParseDCS();
@ -187,26 +201,32 @@ TerminalInputParser::Output TerminalInputParser::ParseESC() {
case ']': case ']':
return ParseOSC(); return ParseOSC();
default: default:
if (!Eat()) if (!Eat()) {
return UNCOMPLETED; return UNCOMPLETED;
} else {
return SPECIAL; return SPECIAL;
} }
}
} }
TerminalInputParser::Output TerminalInputParser::ParseDCS() { TerminalInputParser::Output TerminalInputParser::ParseDCS() {
// Parse until the string terminator ST. // Parse until the string terminator ST.
while (1) { while (true) {
if (!Eat()) if (!Eat()) {
return UNCOMPLETED; return UNCOMPLETED;
}
if (Current() != '\x1B') if (Current() != '\x1B') {
continue; continue;
}
if (!Eat()) if (!Eat()) {
return UNCOMPLETED; return UNCOMPLETED;
}
if (Current() != '\\') if (Current() != '\\') {
continue; continue;
}
return SPECIAL; return SPECIAL;
} }
@ -217,8 +237,9 @@ TerminalInputParser::Output TerminalInputParser::ParseCSI() {
int argument = 0; int argument = 0;
std::vector<int> arguments; std::vector<int> arguments;
while (true) { while (true) {
if (!Eat()) if (!Eat()) {
return UNCOMPLETED; return UNCOMPLETED;
}
if (Current() == '<') { if (Current() == '<') {
altered = true; altered = true;
@ -226,7 +247,7 @@ TerminalInputParser::Output TerminalInputParser::ParseCSI() {
} }
if (Current() >= '0' && Current() <= '9') { if (Current() >= '0' && Current() <= '9') {
argument *= 10; argument *= 10; // NOLINT
argument += int(Current() - '0'); argument += int(Current() - '0');
continue; continue;
} }
@ -239,7 +260,7 @@ TerminalInputParser::Output TerminalInputParser::ParseCSI() {
if (Current() >= ' ' && Current() <= '~' && Current() != '<') { if (Current() >= ' ' && Current() <= '~' && Current() != '<') {
arguments.push_back(argument); arguments.push_back(argument);
argument = 0; argument = 0; // NOLINT
switch (Current()) { switch (Current()) {
case 'M': case 'M':
return ParseMouse(altered, true, std::move(arguments)); return ParseMouse(altered, true, std::move(arguments));
@ -253,53 +274,61 @@ TerminalInputParser::Output TerminalInputParser::ParseCSI() {
} }
// Invalid ESC in CSI. // Invalid ESC in CSI.
if (Current() == '\x1B') if (Current() == '\x1B') {
return SPECIAL; return SPECIAL;
} }
}
} }
TerminalInputParser::Output TerminalInputParser::ParseOSC() { TerminalInputParser::Output TerminalInputParser::ParseOSC() {
// Parse until the string terminator ST. // Parse until the string terminator ST.
while (true) { while (true) {
if (!Eat()) if (!Eat()) {
return UNCOMPLETED; return UNCOMPLETED;
if (Current() != '\x1B') }
if (Current() != '\x1B') {
continue; continue;
if (!Eat()) }
if (!Eat()) {
return UNCOMPLETED; return UNCOMPLETED;
if (Current() != '\\') }
if (Current() != '\\') {
continue; continue;
}
return SPECIAL; return SPECIAL;
} }
} }
TerminalInputParser::Output TerminalInputParser::ParseMouse( TerminalInputParser::Output TerminalInputParser::ParseMouse( // NOLINT
bool altered, bool altered,
bool pressed, bool pressed,
std::vector<int> arguments) { std::vector<int> arguments) {
if (arguments.size() != 3) if (arguments.size() != 3) {
return SPECIAL; return SPECIAL;
}
(void)altered; (void)altered;
Output output(MOUSE); Output output(MOUSE);
output.mouse.button = Mouse::Button((arguments[0] & 3) + // output.mouse.button = Mouse::Button((arguments[0] & 3) + // NOLINT
((arguments[0] & 64) >> 4)); ((arguments[0] & 64) >> 4)); // NOLINT
output.mouse.motion = Mouse::Motion(pressed); output.mouse.motion = Mouse::Motion(pressed); // NOLINT
output.mouse.shift = bool(arguments[0] & 4); output.mouse.shift = bool(arguments[0] & 4); // NOLINT
output.mouse.meta = bool(arguments[0] & 8); output.mouse.meta = bool(arguments[0] & 8); // NOLINT
output.mouse.x = arguments[1]; output.mouse.x = arguments[1]; // NOLINT
output.mouse.y = arguments[2]; output.mouse.y = arguments[2]; // NOLINT
return output; return output;
} }
// NOLINTNEXTLINE
TerminalInputParser::Output TerminalInputParser::ParseCursorReporting( TerminalInputParser::Output TerminalInputParser::ParseCursorReporting(
std::vector<int> arguments) { std::vector<int> arguments) {
if (arguments.size() != 2) if (arguments.size() != 2) {
return SPECIAL; return SPECIAL;
}
Output output(CURSOR_REPORTING); Output output(CURSOR_REPORTING);
output.cursor.y = arguments[0]; output.cursor.y = arguments[0]; // NOLINT
output.cursor.x = arguments[1]; output.cursor.x = arguments[1]; // NOLINT
return output; return output;
} }

View File

@ -48,7 +48,7 @@ class TerminalInputParser {
Output(Type t) : type(t) {} Output(Type t) : type(t) {}
}; };
void Send(Output type); void Send(Output output);
Output Parse(); Output Parse();
Output ParseUTF8(); Output ParseUTF8();
Output ParseESC(); Output ParseESC();

View File

@ -5,21 +5,25 @@
namespace ftxui { namespace ftxui {
// NOLINTNEXTLINE
Component operator|(Component component, ComponentDecorator decorator) { Component operator|(Component component, ComponentDecorator decorator) {
return decorator(component); return decorator(component); // NOLINT
} }
// NOLINTNEXTLINE
Component operator|(Component component, ElementDecorator decorator) { Component operator|(Component component, ElementDecorator decorator) {
return component | Renderer(decorator); return component | Renderer(decorator); // NOLINT
} }
// NOLINTNEXTLINE
Component& operator|=(Component& component, ComponentDecorator decorator) { Component& operator|=(Component& component, ComponentDecorator decorator) {
component = component | decorator; component = component | decorator; // NOLINT
return component; return component;
} }
// NOLINTNEXTLINE
Component& operator|=(Component& component, ElementDecorator decorator) { Component& operator|=(Component& component, ElementDecorator decorator) {
component = component | decorator; component = component | decorator; // NOLINT
return component; return component;
} }

View File

@ -1,9 +1,9 @@
#include <algorithm> // for max #include <algorithm> // for max
#include <iterator> // for begin, end #include <array> // for array
#include <memory> // for allocator, make_shared, __shared_ptr_access #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 <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/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 #include "ftxui/dom/node.hpp" // for Node, Elements
@ -13,30 +13,25 @@
namespace ftxui { 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: // 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 { class Border : public Node {
public: public:
Border(Elements children, BorderStyle style) Border(Elements children, BorderStyle style)
: Node(std::move(children)), : Node(std::move(children)),
charset(std::begin(simple_border_charset[style]), charset_(simple_border_charset[style]) {} // NOLINT
std::end(simple_border_charset[style])) {}
Border(Elements children, Pixel pixel)
: Node(std::move(children)), charset_pixel(10, pixel) {}
std::vector<Pixel> charset_pixel; const Charset& charset_; // NOLINT
std::vector<std::string> charset;
void ComputeRequirement() override { void ComputeRequirement() override {
Node::ComputeRequirement(); Node::ComputeRequirement();
@ -75,63 +70,101 @@ class Border : public Node {
children_[0]->Render(screen); children_[0]->Render(screen);
// Draw the border. // 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; return;
if (!charset.empty())
RenderPixel(screen);
else
RenderChar(screen);
} }
void RenderPixel(Screen& screen) { screen.at(box_.x_min, box_.y_min) = charset_[0]; // NOLINT
screen.at(box_.x_min, box_.y_min) = charset[0]; screen.at(box_.x_max, box_.y_min) = charset_[1]; // NOLINT
screen.at(box_.x_max, box_.y_min) = charset[1]; screen.at(box_.x_min, box_.y_max) = charset_[2]; // NOLINT
screen.at(box_.x_min, box_.y_max) = charset[2]; screen.at(box_.x_max, box_.y_max) = charset_[3]; // NOLINT
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& p1 = screen.PixelAt(x, box_.y_min);
Pixel& p2 = screen.PixelAt(x, box_.y_max); Pixel& p2 = screen.PixelAt(x, box_.y_max);
p1.character = charset[4]; p1.character = charset_[4]; // NOLINT
p2.character = charset[4]; p2.character = charset_[4]; // NOLINT
p1.automerge = true; p1.automerge = true;
p2.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& p3 = screen.PixelAt(box_.x_min, y);
Pixel& p4 = screen.PixelAt(box_.x_max, y); Pixel& p4 = screen.PixelAt(box_.x_max, y);
p3.character = charset[5]; p3.character = charset_[5]; // NOLINT
p4.character = charset[5]; p4.character = charset_[5]; // NOLINT
p3.automerge = true; p3.automerge = true;
p4.automerge = true; p4.automerge = true;
} }
// Draw title. // Draw title.
if (children_.size() == 2) if (children_.size() == 2) {
children_[1]->Render(screen); children_[1]->Render(screen);
} }
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;
} }
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); // For reference, here is the charset for normal border:
p3 = charset_pixel[5]; class BorderPixel : public Node {
p4 = charset_pixel[5]; public:
p3.automerge = true; BorderPixel(Elements children, Pixel pixel)
p4.automerge = true; : 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 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);
}
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. /// @brief Same as border but with a constant Pixel around the element.
/// @ingroup dom /// @ingroup dom
/// @see border /// @see border
Decorator borderWith(Pixel pixel) { Decorator borderWith(const Pixel& pixel) {
return [pixel](Element child) { return [pixel](Element child) {
return std::make_shared<Border>(unpack(std::move(child)), pixel); return std::make_shared<BorderPixel>(unpack(std::move(child)), pixel);
}; };
} }

View File

@ -2,8 +2,7 @@
#include <algorithm> // for max #include <algorithm> // for max
namespace ftxui { namespace ftxui::box_helper {
namespace box_helper {
namespace { namespace {
// Called when the size allowed is greater than the requested size. This // 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 extra_space,
int size) { int size) {
for (Element& element : *elements) { for (Element& element : *elements) {
if (element.flex_shrink) { if (element.flex_shrink != 0) {
element.size = 0; element.size = 0;
continue; continue;
} }
@ -68,23 +67,25 @@ void Compute(std::vector<Element>* elements, int target_size) {
for (auto& element : *elements) { for (auto& element : *elements) {
flex_grow_sum += element.flex_grow; flex_grow_sum += element.flex_grow;
flex_shrink_sum += element.min_size * element.flex_shrink; flex_shrink_sum += element.min_size * element.flex_shrink;
if (element.flex_shrink) if (element.flex_shrink != 0) {
flex_shrink_size += element.min_size; flex_shrink_size += element.min_size;
}
size += element.min_size; size += element.min_size;
} }
int extra_space = target_size - size; int extra_space = target_size - size;
if (extra_space >= 0) if (extra_space >= 0) {
ComputeGrow(elements, extra_space, flex_grow_sum); 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); ComputeShrinkEasy(elements, extra_space, flex_shrink_sum);
else
} else {
ComputeShrinkHard(elements, extra_space + flex_shrink_size, ComputeShrinkHard(elements, extra_space + flex_shrink_size,
size - flex_shrink_size); size - flex_shrink_size);
}
} }
} // namespace box_helper } // namespace ftxui::box_helper
} // namespace ftxui
// Copyright 2021 Arthur Sonzogni. All rights reserved. // Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in

View File

@ -1,8 +1,8 @@
#include "ftxui/dom/canvas.hpp" #include "ftxui/dom/canvas.hpp"
#include <stdlib.h> // for abs
#include <algorithm> // for max, min #include <algorithm> // for max, min
#include <cstdint> // for uint8_t #include <cstdint> // for uint8_t
#include <cstdlib> // for abs
#include <map> // for allocator, map #include <map> // for allocator, map
#include <memory> // for make_shared #include <memory> // for make_shared
#include <utility> // for move, pair #include <utility> // for move, pair
@ -42,26 +42,29 @@ namespace {
// 11100010 10100000 10100000 // dot6 // 11100010 10100000 10100000 // dot6
// 11100010 10100010 10000000 // dot0-2 // 11100010 10100010 10000000 // dot0-2
// NOLINTNEXTLINE
uint8_t g_map_braille[2][4][2] = { uint8_t g_map_braille[2][4][2] = {
{ {
{0b00000000, 0b00000001}, // dot1 {0b00000000, 0b00000001}, // NOLINT | dot1
{0b00000000, 0b00000010}, // dot2 {0b00000000, 0b00000010}, // NOLINT | dot2
{0b00000000, 0b00000100}, // dot3 {0b00000000, 0b00000100}, // NOLINT | dot3
{0b00000001, 0b00000000}, // dot0-1 {0b00000001, 0b00000000}, // NOLINT | dot0-1
}, },
{ {
{0b00000000, 0b00001000}, // dot4 {0b00000000, 0b00001000}, // NOLINT | dot4
{0b00000000, 0b00010000}, // dot5 {0b00000000, 0b00010000}, // NOLINT | dot5
{0b00000000, 0b00100000}, // dot6 {0b00000000, 0b00100000}, // NOLINT | dot6
{0b00000010, 0b00000000}, // dot0-2 {0b00000010, 0b00000000}, // NOLINT | dot0-2
}, },
}; };
// NOLINTNEXTLINE
std::vector<std::string> g_map_block = { std::vector<std::string> g_map_block = {
" ", "", "", "", "", "", "", "", " ", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
}; };
// NOLINTNEXTLINE
const std::map<std::string, uint8_t> g_map_block_inversed = { const std::map<std::string, uint8_t> g_map_block_inversed = {
{" ", 0b0000}, {"", 0b0001}, {"", 0b0010}, {"", 0b0011}, {" ", 0b0000}, {"", 0b0001}, {"", 0b0010}, {"", 0b0011},
{"", 0b0100}, {"", 0b0101}, {"", 0b0110}, {"", 0b0111}, {"", 0b0100}, {"", 0b0101}, {"", 0b0110}, {"", 0b0111},
@ -69,13 +72,17 @@ const std::map<std::string, uint8_t> g_map_block_inversed = {
{"", 0b1100}, {"", 0b1101}, {"", 0b1110}, {"", 0b1111}, {"", 0b1100}, {"", 0b1101}, {"", 0b1110}, {"", 0b1111},
}; };
constexpr auto nostyle = [](Pixel& /*pixel*/) {};
} // namespace } // namespace
/// @brief Constructor. /// @brief Constructor.
/// @param width the width of the canvas. A cell is a 2x8 braille dot. /// @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. /// @param height the height of the canvas. A cell is a 2x8 braille dot.
Canvas::Canvas(int width, int height) 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. /// @brief Get the content of a cell.
/// @param x the x coordinate of the 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 y the y coordinate of the dot.
/// @param value whether the dot is filled or not. /// @param value whether the dot is filled or not.
void Canvas::DrawPoint(int x, int y, bool value) { 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. /// @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. /// @param style the style of the cell.
void Canvas::DrawPoint(int x, int y, bool value, const Stylizer& style) { void Canvas::DrawPoint(int x, int y, bool value, const Stylizer& style) {
Style(x, y, style); Style(x, y, style);
if (value) if (value) {
DrawPointOn(x, y); DrawPointOn(x, y);
else } else {
DrawPointOff(x, y); DrawPointOff(x, y);
}
} }
/// @brief Draw a braille dot. /// @brief Draw a braille dot.
/// @param x the x coordinate of the dot. /// @param x the x coordinate of the dot.
/// @param y the y coordinate of the dot. /// @param y the y coordinate of the dot.
void Canvas::DrawPointOn(int x, int y) { void Canvas::DrawPointOn(int x, int y) {
if (!IsIn(x, y)) if (!IsIn(x, y)) {
return; return;
}
Cell& cell = storage_[XY{x / 2, y / 4}]; Cell& cell = storage_[XY{x / 2, y / 4}];
if (cell.type != CellType::kBraille) { if (cell.type != CellType::kBraille) {
cell.content.character = ""; // 3 bytes. cell.content.character = ""; // 3 bytes.
cell.type = CellType::kBraille; cell.type = CellType::kBraille;
} }
cell.content.character[1] |= g_map_braille[x % 2][y % 4][0]; 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]; cell.content.character[2] |= g_map_braille[x % 2][y % 4][1]; // NOLINT
} }
/// @brief Erase a braille dot. /// @brief Erase a braille dot.
/// @param x the x coordinate of the dot. /// @param x the x coordinate of the dot.
/// @param y the y coordinate of the dot. /// @param y the y coordinate of the dot.
void Canvas::DrawPointOff(int x, int y) { void Canvas::DrawPointOff(int x, int y) {
if (!IsIn(x, y)) if (!IsIn(x, y)) {
return; return;
}
Cell& cell = storage_[XY{x / 2, y / 4}]; Cell& cell = storage_[XY{x / 2, y / 4}];
if (cell.type != CellType::kBraille) { if (cell.type != CellType::kBraille) {
cell.content.character = ""; // 3 byt cell.content.character = ""; // 3 byt
cell.type = CellType::kBraille; cell.type = CellType::kBraille;
} }
cell.content.character[1] &= ~(g_map_braille[x % 2][y % 4][0]); 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]); 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 /// @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 x the x coordinate of the dot.
/// @param y the y coordinate of the dot. /// @param y the y coordinate of the dot.
void Canvas::DrawPointToggle(int x, int y) { void Canvas::DrawPointToggle(int x, int y) {
if (!IsIn(x, y)) if (!IsIn(x, y)) {
return; return;
}
Cell& cell = storage_[XY{x / 2, y / 4}]; Cell& cell = storage_[XY{x / 2, y / 4}];
if (cell.type != CellType::kBraille) { if (cell.type != CellType::kBraille) {
cell.content.character = ""; // 3 byt cell.content.character = ""; // 3 byt
cell.type = CellType::kBraille; cell.type = CellType::kBraille;
} }
cell.content.character[1] ^= g_map_braille[x % 2][y % 4][0]; 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]; cell.content.character[2] ^= g_map_braille[x % 2][y % 4][1]; // NOLINT
} }
/// @brief Draw a line made of braille dots. /// @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 x2 the x coordinate of the second dot.
/// @param y2 the y 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) { 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. /// @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 sy = y1 < y2 ? 1 : -1;
const int length = std::max(dx, dy); const int length = std::max(dx, dy);
if (!IsIn(x1, y1) && !IsIn(x2, y2)) if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
return; return;
if (dx + dx > width_ * height_) }
if (dx + dx > width_ * height_) {
return; return;
}
int error = dx - dy; int error = dx - dy;
for (int i = 0; i < length; ++i) { 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 y the y coordinate of the center of the circle.
/// @param radius the radius of the circle. /// @param radius the radius of the circle.
void Canvas::DrawPointCircle(int x, int y, int radius) { 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. /// @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 y the y coordinate of the center of the circle.
/// @param radius the radius of the circle. /// @param radius the radius of the circle.
void Canvas::DrawPointCircleFilled(int x, int y, int radius) { 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. /// @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 r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis. /// @param r2 the radius of the ellipse along the y axis.
void Canvas::DrawPointEllipse(int x, int y, int r1, int r2) { 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. /// @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 r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis. /// @param r2 the radius of the ellipse along the y axis.
void Canvas::DrawPointEllipseFilled(int x1, int y1, int r1, int r2) { 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. /// @brief Draw a filled ellipse made of braille dots.
@ -395,11 +408,11 @@ void Canvas::DrawPointEllipseFilled(int x1,
e2 = 2 * err; e2 = 2 * err;
if (e2 >= dx) { if (e2 >= dx) {
x++; x++;
err += dx += 2 * (long)r2 * r2; err += dx += 2 * (long)r2 * r2; // NOLINT
} }
if (e2 <= dy) { if (e2 <= dy) {
y++; y++;
err += dy += 2 * (long)r1 * r1; err += dy += 2 * (long)r1 * r1; // NOLINT
} }
} while (x <= 0); } while (x <= 0);
@ -415,7 +428,7 @@ void Canvas::DrawPointEllipseFilled(int x1,
/// @param y the y coordinate of the block. /// @param y the y coordinate of the block.
/// @param value whether the block is filled or not. /// @param value whether the block is filled or not.
void Canvas::DrawBlock(int x, int y, bool value) { void Canvas::DrawBlock(int x, int y, bool value) {
DrawBlock(x, y, value, [](Pixel&) {}); DrawBlock(x, y, value, [](Pixel& /*pixel*/) {});
} }
/// @brief Draw a block. /// @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. /// @param style the style of the block.
void Canvas::DrawBlock(int x, int y, bool value, const Stylizer& style) { void Canvas::DrawBlock(int x, int y, bool value, const Stylizer& style) {
Style(x, y, style); Style(x, y, style);
if (value) if (value) {
DrawBlockOn(x, y); DrawBlockOn(x, y);
else } else {
DrawBlockOff(x, y); DrawBlockOff(x, y);
}
} }
/// @brief Draw a block. /// @brief Draw a block.
/// @param x the x coordinate of the block. /// @param x the x coordinate of the block.
/// @param y the y coordinate of the block. /// @param y the y coordinate of the block.
void Canvas::DrawBlockOn(int x, int y) { void Canvas::DrawBlockOn(int x, int y) {
if (!IsIn(x, y)) if (!IsIn(x, y)) {
return; return;
}
y /= 2; y /= 2;
Cell& cell = storage_[XY{x / 2, y / 2}]; Cell& cell = storage_[XY{x / 2, y / 2}];
if (cell.type != CellType::kBlock) { if (cell.type != CellType::kBlock) {
@ -453,9 +468,9 @@ void Canvas::DrawBlockOn(int x, int y) {
cell.type = CellType::kBlock; 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); uint8_t value = g_map_block_inversed.at(cell.content.character);
value |= 1 << bit; value |= 1U << bit;
cell.content.character = g_map_block[value]; 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 x the x coordinate of the block.
/// @param y the y coordinate of the block. /// @param y the y coordinate of the block.
void Canvas::DrawBlockOff(int x, int y) { void Canvas::DrawBlockOff(int x, int y) {
if (!IsIn(x, y)) if (!IsIn(x, y)) {
return; return;
}
Cell& cell = storage_[XY{x / 2, y / 4}]; Cell& cell = storage_[XY{x / 2, y / 4}];
if (cell.type != CellType::kBlock) { if (cell.type != CellType::kBlock) {
cell.content.character = " "; cell.content.character = " ";
@ -472,9 +488,9 @@ void Canvas::DrawBlockOff(int x, int y) {
} }
y /= 2; 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); uint8_t value = g_map_block_inversed.at(cell.content.character);
value &= ~(1 << bit); value &= ~(1U << bit);
cell.content.character = g_map_block[value]; 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 x the x coordinate of the block.
/// @param y the y coordinate of the block. /// @param y the y coordinate of the block.
void Canvas::DrawBlockToggle(int x, int y) { void Canvas::DrawBlockToggle(int x, int y) {
if (!IsIn(x, y)) if (!IsIn(x, y)) {
return; return;
}
Cell& cell = storage_[XY{x / 2, y / 4}]; Cell& cell = storage_[XY{x / 2, y / 4}];
if (cell.type != CellType::kBlock) { if (cell.type != CellType::kBlock) {
cell.content.character = " "; cell.content.character = " ";
@ -492,9 +509,9 @@ void Canvas::DrawBlockToggle(int x, int y) {
} }
y /= 2; 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); uint8_t value = g_map_block_inversed.at(cell.content.character);
value ^= 1 << bit; value ^= 1U << bit;
cell.content.character = g_map_block[value]; 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 x2 the x coordinate of the second point of the line.
/// @param y2 the y 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) { 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. /// @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 sy = y1 < y2 ? 1 : -1;
const int length = std::max(dx, dy); const int length = std::max(dx, dy);
if (!IsIn(x1, y1) && !IsIn(x2, y2)) if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
return; return;
if (dx + dx > width_ * height_) }
if (dx + dx > width_ * height_) {
return; return;
}
int error = dx - dy; int error = dx - dy;
for (int i = 0; i < length; ++i) { 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 y the y coordinate of the center of the circle.
/// @param radius the radius of the circle. /// @param radius the radius of the circle.
void Canvas::DrawBlockCircle(int x, int y, int radius) { 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. /// @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 y the y coordinate of the center of the circle.
/// @param radius the radius of the circle. /// @param radius the radius of the circle.
void Canvas::DrawBlockCircleFilled(int x, int y, int radius) { 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. /// @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 r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis. /// @param r2 the radius of the ellipse along the y axis.
void Canvas::DrawBlockEllipse(int x, int y, int r1, int r2) { 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. /// @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 r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis. /// @param r2 the radius of the ellipse along the y axis.
void Canvas::DrawBlockEllipseFilled(int x, int y, int r1, int r2) { 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. /// @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 y the y coordinate of the text.
/// @param value the text to draw. /// @param value the text to draw.
void Canvas::DrawText(int x, int y, const std::string& value) { 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. /// @brief Draw a piece of text.
@ -781,8 +800,9 @@ void Canvas::DrawText(int x,
const std::string& value, const std::string& value,
const Stylizer& style) { const Stylizer& style) {
for (const auto& it : Utf8ToGlyphs(value)) { for (const auto& it : Utf8ToGlyphs(value)) {
if (!IsIn(x, y)) if (!IsIn(x, y)) {
continue; continue;
}
Cell& cell = storage_[XY{x / 2, y / 4}]; Cell& cell = storage_[XY{x / 2, y / 4}];
cell.type = CellType::kText; cell.type = CellType::kText;
cell.content.character = it; cell.content.character = it;
@ -794,15 +814,16 @@ void Canvas::DrawText(int x,
/// @brief Modify a pixel at a given location. /// @brief Modify a pixel at a given location.
/// @param style a function that modifies the pixel. /// @param style a function that modifies the pixel.
void Canvas::Style(int x, int y, const Stylizer& style) { 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); style(storage_[XY{x / 2, y / 4}].content);
}
} }
namespace { namespace {
class CanvasNodeBase : public Node { class CanvasNodeBase : public Node {
public: public:
CanvasNodeBase() {} CanvasNodeBase() = default;
void Render(Screen& screen) override { void Render(Screen& screen) override {
const Canvas& c = canvas(); const Canvas& c = canvas();
@ -824,7 +845,7 @@ class CanvasNodeBase : public Node {
Element canvas(ConstRef<Canvas> canvas) { Element canvas(ConstRef<Canvas> canvas) {
class Impl : public CanvasNodeBase { class Impl : public CanvasNodeBase {
public: 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_x = (canvas_->width() + 1) / 2;
requirement_.min_y = (canvas_->height() + 3) / 4; 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. /// @brief Produce an element drawing a canvas.
/// @param fn a function drawing the canvas. /// @param fn a function drawing the canvas.
Element canvas(std::function<void(Canvas&)> fn) { 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 } // namespace ftxui

View File

@ -29,8 +29,8 @@ class ClearUnder : public NodeDecorator {
// combinaison with dbox. // combinaison with dbox.
/// @see ftxui::dbox /// @see ftxui::dbox
/// @ingroup dom /// @ingroup dom
Element clear_under(Element child) { Element clear_under(Element element) {
return std::make_shared<ClearUnder>(std::move(child)); return std::make_shared<ClearUnder>(std::move(element));
} }
} // namespace ftxui } // namespace ftxui

View File

@ -12,7 +12,7 @@ namespace ftxui {
class DBox : public Node { class DBox : public Node {
public: public:
DBox(Elements children) : Node(std::move(children)) {} explicit DBox(Elements children) : Node(std::move(children)) {}
void ComputeRequirement() override { void ComputeRequirement() override {
requirement_.min_x = 0; requirement_.min_x = 0;
@ -38,9 +38,10 @@ class DBox : public Node {
void SetBox(Box box) override { void SetBox(Box box) override {
Node::SetBox(box); Node::SetBox(box);
for (auto& child : children_) for (auto& child : children_) {
child->SetBox(box); child->SetBox(box);
} }
}
}; };
/// @brief Stack several element on top of each other. /// @brief Stack several element on top of each other.

View File

@ -67,7 +67,7 @@ void function_not_flex(Requirement& r) {
class Flex : public Node { class Flex : public Node {
public: public:
Flex(FlexFunction f) : f_(f) {} explicit Flex(FlexFunction f) : f_(f) {}
Flex(FlexFunction f, Element child) : Node(unpack(std::move(child))), f_(f) {} Flex(FlexFunction f, Element child) : Node(unpack(std::move(child))), f_(f) {}
void ComputeRequirement() override { void ComputeRequirement() override {
requirement_.min_x = 0; requirement_.min_x = 0;
@ -80,8 +80,9 @@ class Flex : public Node {
} }
void SetBox(Box box) override { void SetBox(Box box) override {
if (children_.empty()) if (children_.empty()) {
return; return;
}
children_[0]->SetBox(box); children_[0]->SetBox(box);
} }

View File

@ -1,5 +1,5 @@
#include <stddef.h> // for size_t
#include <algorithm> // for min, max #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 <memory> // for __shared_ptr_access, shared_ptr, allocator_traits<>::value_type, make_shared
#include <utility> // for move, swap #include <utility> // for move, swap
#include <vector> // for vector #include <vector> // for vector
@ -56,11 +56,12 @@ class Flexbox : public Node {
requirement_.flex_grow_x = 1; requirement_.flex_grow_x = 1;
requirement_.flex_grow_y = 0; requirement_.flex_grow_y = 0;
if (IsColumnOriented()) if (IsColumnOriented()) {
std::swap(requirement_.flex_grow_x, requirement_.flex_grow_y); std::swap(requirement_.flex_grow_x, requirement_.flex_grow_y);
} }
}
bool IsColumnOriented() { bool IsColumnOriented() const {
return config_.direction == FlexboxConfig::Direction::Column || return config_.direction == FlexboxConfig::Direction::Column ||
config_.direction == FlexboxConfig::Direction::ColumnInversed; config_.direction == FlexboxConfig::Direction::ColumnInversed;
} }
@ -84,20 +85,21 @@ class Flexbox : public Node {
} }
void ComputeRequirement() override { void ComputeRequirement() override {
for (auto& child : children_) for (auto& child : children_) {
child->ComputeRequirement(); child->ComputeRequirement();
}
flexbox_helper::Global global; flexbox_helper::Global global;
global.config = config_normalized_; global.config = config_normalized_;
if (IsColumnOriented()) { if (IsColumnOriented()) {
global.size_x = 100000; global.size_x = 100000; // NOLINT
global.size_y = asked_; global.size_y = asked_;
} else { } else {
global.size_x = asked_; global.size_x = asked_;
global.size_y = 100000; global.size_y = 100000; // NOLINT
} }
Layout(global, true); Layout(global, true);
if (global.blocks.size() == 0) { if (global.blocks.empty()) {
requirement_.min_x = 0; requirement_.min_x = 0;
requirement_.min_y = 0; requirement_.min_y = 0;
return; return;
@ -150,18 +152,19 @@ class Flexbox : public Node {
} }
void Check(Status* status) override { void Check(Status* status) override {
for (auto& child : children_) for (auto& child : children_) {
child->Check(status); child->Check(status);
}
if (status->iteration == 0) { if (status->iteration == 0) {
asked_ = 6000; asked_ = 6000; // NOLINT
need_iteration_ = true; need_iteration_ = true;
} }
status->need_iteration |= need_iteration_; status->need_iteration |= need_iteration_;
} }
int asked_ = 6000; int asked_ = 6000; // NOLINT
bool need_iteration_ = true; bool need_iteration_ = true;
const FlexboxConfig config_; const FlexboxConfig config_;
const FlexboxConfig config_normalized_; const FlexboxConfig config_normalized_;
@ -190,7 +193,7 @@ class Flexbox : public Node {
// ) // )
/// ``` /// ```
Element flexbox(Elements children, FlexboxConfig config) { 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 /// @brief A container displaying elements in rows from left to right. When

View File

@ -1,14 +1,13 @@
#include "ftxui/dom/flexbox_helper.hpp" #include "ftxui/dom/flexbox_helper.hpp"
#include <stddef.h> // for size_t
#include <algorithm> // for min, max #include <algorithm> // for min, max
#include <cstddef> // for size_t
#include <memory> // for allocator_traits<>::value_type #include <memory> // for allocator_traits<>::value_type
#include <utility> // for swap, move #include <utility> // for swap, move
#include "ftxui/dom/box_helper.hpp" // for Element, Compute #include "ftxui/dom/box_helper.hpp" // for Element, Compute
namespace ftxui { namespace ftxui::flexbox_helper {
namespace flexbox_helper {
namespace { namespace {
void SymmetryXY(FlexboxConfig& c) { void SymmetryXY(FlexboxConfig& c) {
@ -92,15 +91,17 @@ void SetX(Global& global, std::vector<Line> lines) {
box_helper::Element element; box_helper::Element element;
element.min_size = block->min_size_x; element.min_size = block->min_size_x;
element.flex_grow = element.flex_grow =
block->flex_grow_x || global.config.justify_content == block->flex_grow_x != 0 || global.config.justify_content ==
FlexboxConfig::JustifyContent::Stretch; FlexboxConfig::JustifyContent::Stretch
? 1
: 0;
element.flex_shrink = block->flex_shrink_x; element.flex_shrink = block->flex_shrink_x;
elements.push_back(element); elements.push_back(element);
} }
box_helper::Compute( box_helper::Compute(
&elements, &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; int x = 0;
for (size_t i = 0; i < line.blocks.size(); ++i) { 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) { void SetY(Global& g, std::vector<Line> lines) {
std::vector<box_helper::Element> elements; std::vector<box_helper::Element> elements;
for (auto& line : lines) { 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, g.size_y);
box_helper::Compute(&elements, 10000); box_helper::Compute(&elements, 10000); // NOLINT
// [Align-content] // [Align-content]
std::vector<int> ys(elements.size()); 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); int remaining_space = std::max(0, g.size_y - y);
switch (g.config.align_content) { switch (g.config.align_content) {
case FlexboxConfig::AlignContent::FlexStart: { case FlexboxConfig::AlignContent::FlexStart: {
} break; break;
}
case FlexboxConfig::AlignContent::FlexEnd: { 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; ys[i] += remaining_space;
} break; }
break;
}
case FlexboxConfig::AlignContent::Center: { 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; ys[i] += remaining_space / 2;
} break; }
break;
}
case FlexboxConfig::AlignContent::Stretch: { 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); int shifted = remaining_space * (i + 0) / (i + 1);
ys[i] += shifted; ys[i] += shifted;
int consumed = remaining_space - shifted; int consumed = remaining_space - shifted;
elements[i].size += consumed; elements[i].size += consumed;
remaining_space -= consumed; remaining_space -= consumed;
} }
} break; break;
}
case FlexboxConfig::AlignContent::SpaceBetween: { 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; ys[i] += remaining_space;
remaining_space = remaining_space * (i - 1) / i; remaining_space = remaining_space * (i - 1) / i;
} }
} break; break;
}
case FlexboxConfig::AlignContent::SpaceAround: { 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); ys[i] += remaining_space * (2 * i + 1) / (2 * i + 2);
remaining_space = remaining_space * (2 * i) / (2 * i + 2); remaining_space = remaining_space * (2 * i) / (2 * i + 2);
} }
} break; break;
}
case FlexboxConfig::AlignContent::SpaceEvenly: { 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); ys[i] += remaining_space * (i + 1) / (i + 2);
remaining_space = remaining_space * (i + 1) / (i + 2); remaining_space = remaining_space * (i + 1) / (i + 2);
} }
} break; break;
}
} }
// [Align items] // [Align items]
@ -189,7 +200,7 @@ void SetY(Global& g, std::vector<Line> lines) {
auto& element = elements[i]; auto& element = elements[i];
for (auto* block : lines[i].blocks) { for (auto* block : lines[i].blocks) {
bool stretch = bool stretch =
block->flex_grow_y || block->flex_grow_y != 0 ||
g.config.align_content == FlexboxConfig::AlignContent::Stretch; g.config.align_content == FlexboxConfig::AlignContent::Stretch;
int size = int size =
stretch ? element.size : std::min(element.size, block->min_size_y); 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: { case FlexboxConfig::AlignItems::FlexStart: {
block->y = ys[i]; block->y = ys[i];
block->dim_y = size; block->dim_y = size;
} break; break;
}
case FlexboxConfig::AlignItems::Center: { case FlexboxConfig::AlignItems::Center: {
block->y = ys[i] + (element.size - size) / 2; block->y = ys[i] + (element.size - size) / 2;
block->dim_y = size; block->dim_y = size;
} break; break;
}
case FlexboxConfig::AlignItems::FlexEnd: { case FlexboxConfig::AlignItems::FlexEnd: {
block->y = ys[i] + element.size - size; block->y = ys[i] + element.size - size;
block->dim_y = size; block->dim_y = size;
} break; break;
}
case FlexboxConfig::AlignItems::Stretch: { case FlexboxConfig::AlignItems::Stretch: {
block->y = ys[i]; block->y = ys[i];
block->dim_y = element.size; block->dim_y = element.size;
} break; break;
}
} }
} }
} }
@ -228,63 +243,74 @@ void JustifyContent(Global& g, std::vector<Line> lines) {
break; break;
case FlexboxConfig::JustifyContent::FlexEnd: { case FlexboxConfig::JustifyContent::FlexEnd: {
for (auto* block : line.blocks) for (auto* block : line.blocks) {
block->x += remaining_space; block->x += remaining_space;
} break; }
break;
}
case FlexboxConfig::JustifyContent::Center: { case FlexboxConfig::JustifyContent::Center: {
for (auto* block : line.blocks) for (auto* block : line.blocks) {
block->x += remaining_space / 2; block->x += remaining_space / 2;
} break; }
break;
}
case FlexboxConfig::JustifyContent::SpaceBetween: { 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; line.blocks[i]->x += remaining_space;
remaining_space = remaining_space * (i - 1) / i; remaining_space = remaining_space * (i - 1) / i;
} }
} break; break;
}
case FlexboxConfig::JustifyContent::SpaceAround: { 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); line.blocks[i]->x += remaining_space * (2 * i + 1) / (2 * i + 2);
remaining_space = remaining_space * (2 * i) / (2 * i + 2); remaining_space = remaining_space * (2 * i) / (2 * i + 2);
} }
} break; break;
}
case FlexboxConfig::JustifyContent::SpaceEvenly: { 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); line.blocks[i]->x += remaining_space * (i + 1) / (i + 2);
remaining_space = remaining_space * (i + 1) / (i + 2); remaining_space = remaining_space * (i + 1) / (i + 2);
} }
} break; break;
}
} }
} }
} }
} // namespace } // namespace
void Compute(Global& global) { namespace {
if (global.config.direction == FlexboxConfig::Direction::Column ||
global.config.direction == FlexboxConfig::Direction::ColumnInversed) {
SymmetryXY(global);
Compute(global);
SymmetryXY(global);
return;
}
void Compute1(Global& global);
void Compute2(Global& global);
void Compute3(Global& global);
void Compute1(Global& global) {
if (global.config.direction == FlexboxConfig::Direction::RowInversed) { if (global.config.direction == FlexboxConfig::Direction::RowInversed) {
SymmetryX(global); SymmetryX(global);
Compute(global); Compute2(global);
SymmetryX(global); SymmetryX(global);
return; return;
} }
Compute2(global);
}
void Compute2(Global& global) {
if (global.config.wrap == FlexboxConfig::Wrap::WrapInversed) { if (global.config.wrap == FlexboxConfig::Wrap::WrapInversed) {
SymmetryY(global); SymmetryY(global);
Compute(global); Compute3(global);
SymmetryY(global); SymmetryY(global);
return; return;
} }
Compute3(global);
}
void Compute3(Global& global) {
// Step 1: Lay out every elements into rows: // Step 1: Lay out every elements into rows:
std::vector<Line> lines; std::vector<Line> lines;
{ {
@ -295,19 +321,21 @@ void Compute(Global& global) {
// No? Then we need to start a new one: // No? Then we need to start a new one:
if (x + block.min_size_x > global.size_x) { if (x + block.min_size_x > global.size_x) {
x = 0; x = 0;
if (!line.blocks.empty()) if (!line.blocks.empty()) {
lines.push_back(std::move(line)); lines.push_back(std::move(line));
}
line = Line(); line = Line();
} }
block.line = lines.size(); block.line = (int)lines.size();
block.line_position = line.blocks.size(); block.line_position = (int)line.blocks.size();
line.blocks.push_back(&block); line.blocks.push_back(&block);
x += block.min_size_x + global.config.gap_x; x += block.min_size_x + global.config.gap_x;
} }
if (!line.blocks.empty()) if (!line.blocks.empty()) {
lines.push_back(std::move(line)); lines.push_back(std::move(line));
} }
}
// Step 2: Set positions on the X axis. // Step 2: Set positions on the X axis.
SetX(global, lines); SetX(global, lines);
@ -317,8 +345,21 @@ void Compute(Global& global) {
SetY(global, lines); SetY(global, lines);
} }
} // namespace flexbox_helper } // namespace
} // namespace ftxui
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. // Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in

View File

@ -29,17 +29,17 @@ Decorator focusPositionRelative(float x, float y) {
class Impl : public NodeDecorator { class Impl : public NodeDecorator {
public: public:
Impl(Element child, float x, float y) Impl(Element child, float x, float y)
: NodeDecorator(child), x_(x), y_(y) {} : NodeDecorator(std::move(child)), x_(x), y_(y) {}
void ComputeRequirement() override { void ComputeRequirement() override {
NodeDecorator::ComputeRequirement(); NodeDecorator::ComputeRequirement();
requirement_.selection = Requirement::Selection::NORMAL; requirement_.selection = Requirement::Selection::NORMAL;
Box& box = requirement_.selected_box; Box& box = requirement_.selected_box;
box.x_min = requirement_.min_x * x_; box.x_min = (int)((float)requirement_.min_x * x_);
box.y_min = requirement_.min_y * y_; box.y_min = (int)((float)requirement_.min_y * y_);
box.x_max = requirement_.min_x * x_; box.x_max = (int)((float)requirement_.min_x * x_);
box.y_max = requirement_.min_y * y_; box.y_max = (int)((float)requirement_.min_y * y_);
} }
private: private:
@ -67,8 +67,8 @@ Decorator focusPositionRelative(float x, float y) {
Decorator focusPosition(int x, int y) { Decorator focusPosition(int x, int y) {
class Impl : public NodeDecorator { class Impl : public NodeDecorator {
public: public:
Impl(Element child, float x, float y) Impl(Element child, int x, int y)
: NodeDecorator(child), x_(x), y_(y) {} : NodeDecorator(std::move(child)), x_(x), y_(y) {}
void ComputeRequirement() override { void ComputeRequirement() override {
NodeDecorator::ComputeRequirement(); NodeDecorator::ComputeRequirement();

View File

@ -16,7 +16,7 @@ namespace ftxui {
class Select : public Node { class Select : public Node {
public: public:
Select(Elements children) : Node(std::move(children)) {} explicit Select(Elements children) : Node(std::move(children)) {}
void ComputeRequirement() override { void ComputeRequirement() override {
Node::ComputeRequirement(); Node::ComputeRequirement();

View File

@ -1,17 +1,16 @@
#include <iostream>
#include <algorithm> // for max, min
#include <memory> // for allocator, make_shared #include <memory> // for allocator, make_shared
#include <string> // for string #include <string> // for string
#include "ftxui/dom/elements.hpp" // for Element, gauge #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/node.hpp" // for Node
#include "ftxui/dom/requirement.hpp" // for Requirement #include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box #include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" // for Screen #include "ftxui/screen/screen.hpp" // for Screen, Pixel
namespace ftxui { namespace ftxui {
static std::string charset_horizontal[11] = { // NOLINTNEXTLINE
static const std::string charset_horizontal[11] = {
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK) #if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
// Microsoft's terminals often use fonts not handling the 8 unicode // Microsoft's terminals often use fonts not handling the 8 unicode
// characters for representing the whole gauge. Fallback with less. // 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 // 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) Gauge(float progress, GaugeDirection direction)
: progress_(progress), direction_(direction) { : progress_(progress), direction_(direction) {
// This handle NAN correctly: // This handle NAN correctly:
if (!(progress_ > 0.f)) if (!(progress_ > 0.F)) {
progress_ = 0.f; progress_ = 0.F;
if (!(progress_ < 1.f)) }
progress_ = 1.f; if (!(progress_ < 1.F)) {
progress_ = 1.F;
}
} }
void ComputeRequirement() override { void ComputeRequirement() override {
@ -89,51 +91,63 @@ class Gauge : public Node {
void RenderHorizontal(Screen& screen, bool invert) { void RenderHorizontal(Screen& screen, bool invert) {
int y = box_.y_min; int y = box_.y_min;
if (y > box_.y_max) if (y > box_.y_max) {
return; return;
}
// Draw the progress bar horizontally. // Draw the progress bar horizontally.
{ {
float progress = invert ? 1.f - progress_ : progress_; float progress = invert ? 1.F - progress_ : progress_;
float limit = box_.x_min + progress * (box_.x_max - box_.x_min + 1); float limit =
int limit_int = limit; (float)box_.x_min + progress * (float)(box_.x_max - box_.x_min + 1);
int limit_int = (int)limit;
int x = box_.x_min; int x = box_.x_min;
while (x < limit_int) while (x < limit_int) {
screen.at(x++, y) = charset_horizontal[9]; screen.at(x++, y) = charset_horizontal[9]; // NOLINT
}
// NOLINTNEXTLINE
screen.at(x++, y) = charset_horizontal[int(9 * (limit - limit_int))]; 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]; screen.at(x++, y) = charset_horizontal[0];
} }
}
if (invert) { 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; screen.PixelAt(x, y).inverted ^= true;
} }
} }
}
void RenderVertical(Screen& screen, bool invert) { void RenderVertical(Screen& screen, bool invert) {
int x = box_.x_min; int x = box_.x_min;
if (x > box_.x_max) if (x > box_.x_max) {
return; return;
}
// Draw the progress bar vertically: // Draw the progress bar vertically:
{ {
float progress = invert ? progress_ : 1.f - progress_; float progress = invert ? progress_ : 1.F - progress_;
float limit = box_.y_min + progress * (box_.y_max - box_.y_min + 1); float limit =
int limit_int = limit; (float)box_.y_min + progress * (float)(box_.y_max - box_.y_min + 1);
int limit_int = (int)limit;
int y = box_.y_min; int y = box_.y_min;
while (y < limit_int) while (y < limit_int) {
screen.at(x, y++) = charset_vertical[8]; screen.at(x, y++) = charset_vertical[8]; // NOLINT
}
// NOLINTNEXTLINE
screen.at(x, y++) = charset_vertical[int(8 * (limit - limit_int))]; 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]; screen.at(x, y++) = charset_vertical[0];
} }
}
if (invert) { 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; screen.PixelAt(x, y).inverted ^= true;
} }
} }
}
private: private:
float progress_; float progress_;

View File

@ -1,6 +1,7 @@
#include <functional> // for function #include <functional> // for function
#include <memory> // for allocator, make_shared #include <memory> // for allocator, make_shared
#include <string> // for string #include <string> // for string
#include <utility> // for move
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for GraphFunction, Element, graph #include "ftxui/dom/elements.hpp" // for GraphFunction, Element, graph
@ -11,6 +12,7 @@
namespace ftxui { namespace ftxui {
// NOLINTNEXTLINE
static std::string charset[] = static std::string charset[] =
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK) #if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
// Microsoft's terminals often use fonts not handling the 8 unicode // Microsoft's terminals often use fonts not handling the 8 unicode
@ -22,7 +24,8 @@ static std::string charset[] =
class Graph : public Node { class Graph : public Node {
public: public:
Graph(GraphFunction graph_function) : graph_function_(graph_function) {} explicit Graph(GraphFunction graph_function)
: graph_function_(std::move(graph_function)) {}
void ComputeRequirement() override { void ComputeRequirement() override {
requirement_.flex_grow_x = 1; requirement_.flex_grow_x = 1;
@ -43,9 +46,9 @@ class Graph : public Node {
int height_2 = 2 * box_.y_max - data[i++]; int height_2 = 2 * box_.y_max - data[i++];
for (int y = box_.y_min; y <= box_.y_max; ++y) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
int yy = 2 * y; int yy = 2 * y;
int i_1 = yy < height_1 ? 0 : yy == height_1 ? 3 : 6; 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; int i_2 = yy < height_2 ? 0 : yy == height_2 ? 1 : 2; // NOLINT
screen.at(x, y) = charset[i_1 + i_2]; 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. /// @brief Draw a graph using a GraphFunction.
/// @param graph_function the function to be called to get the data. /// @param graph_function the function to be called to get the data.
Element graph(GraphFunction graph_function) { Element graph(GraphFunction graph_function) {
return std::make_shared<Graph>(graph_function); return std::make_shared<Graph>(std::move(graph_function));
} }
} // namespace ftxui } // namespace ftxui

View File

@ -1,5 +1,5 @@
#include <stddef.h> // for size_t
#include <algorithm> // for max, min #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 <memory> // for __shared_ptr_access, shared_ptr, make_shared, allocator_traits<>::value_type
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type #include <vector> // for vector, __alloc_traits<>::value_type
@ -15,10 +15,11 @@ class Screen;
class GridBox : public Node { class GridBox : public Node {
public: public:
GridBox(std::vector<Elements> lines) : Node(), lines_(std::move(lines)) { explicit GridBox(std::vector<Elements> lines) : lines_(std::move(lines)) {
y_size = lines_.size(); y_size = (int)lines_.size();
for (const auto& line : lines_) for (const auto& line : lines_) {
x_size = std::max(x_size, (int)line.size()); x_size = std::max(x_size, (int)line.size());
}
for (auto& line : lines_) { for (auto& line : lines_) {
while (line.size() < (size_t)x_size) { while (line.size() < (size_t)x_size) {
line.push_back(filler()); line.push_back(filler());
@ -39,8 +40,9 @@ class GridBox : public Node {
cell->ComputeRequirement(); cell->ComputeRequirement();
// Determine focus based on the focused child. // Determine focus based on the focused child.
if (requirement_.selection >= cell->requirement().selection) if (requirement_.selection >= cell->requirement().selection) {
continue; continue;
}
requirement_.selection = cell->requirement().selection; requirement_.selection = cell->requirement().selection;
requirement_.selected_box = cell->requirement().selected_box; requirement_.selected_box = cell->requirement().selected_box;
requirement_.selected_box.x_min += requirement_.min_x; requirement_.selected_box.x_min += requirement_.min_x;
@ -51,16 +53,18 @@ class GridBox : public Node {
// Work on the x-axis. // Work on the x-axis.
for (int x = 0; x < x_size; ++x) { for (int x = 0; x < x_size; ++x) {
int min_x = 0; 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); min_x = std::max(min_x, lines_[y][x]->requirement().min_x);
}
requirement_.min_x += min_x; requirement_.min_x += min_x;
} }
// Work on the y-axis. // Work on the y-axis.
for (int y = 0; y < y_size; ++y) { for (int y = 0; y < y_size; ++y) {
int min_y = 0; 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); min_y = std::max(min_y, lines_[y][x]->requirement().min_y);
}
requirement_.min_y += min_y; requirement_.min_y += min_y;
} }
} }
@ -70,8 +74,8 @@ class GridBox : public Node {
box_helper::Element init; box_helper::Element init;
init.min_size = 0; init.min_size = 0;
init.flex_grow = 1024; init.flex_grow = 1024; // NOLINT
init.flex_shrink = 1024; init.flex_shrink = 1024; // NOLINT
std::vector<box_helper::Element> elements_x(x_size, init); std::vector<box_helper::Element> elements_x(x_size, init);
std::vector<box_helper::Element> elements_y(y_size, init); std::vector<box_helper::Element> elements_y(y_size, init);
@ -115,10 +119,11 @@ class GridBox : public Node {
void Render(Screen& screen) override { void Render(Screen& screen) override {
for (auto& line : lines_) { for (auto& line : lines_) {
for (auto& cell : line) for (auto& cell : line) {
cell->Render(screen); cell->Render(screen);
} }
} }
}
int x_size = 0; int x_size = 0;
int y_size = 0; int y_size = 0;

View File

@ -1,5 +1,5 @@
#include <stddef.h> // for size_t
#include <algorithm> // for max #include <algorithm> // for max
#include <cstddef> // for size_t
#include <memory> // for __shared_ptr_access, shared_ptr, make_shared, allocator_traits<>::value_type #include <memory> // for __shared_ptr_access, shared_ptr, make_shared, allocator_traits<>::value_type
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type #include <vector> // for vector, __alloc_traits<>::value_type
@ -14,7 +14,7 @@ namespace ftxui {
class HBox : public Node { class HBox : public Node {
public: public:
HBox(Elements children) : Node(std::move(children)) {} explicit HBox(Elements children) : Node(std::move(children)) {}
void ComputeRequirement() override { void ComputeRequirement() override {
requirement_.min_x = 0; requirement_.min_x = 0;

View File

@ -5,15 +5,16 @@
namespace ftxui { namespace ftxui {
Node::Node() {} Node::Node() = default;
Node::Node(Elements children) : children_(std::move(children)) {} Node::Node(Elements children) : children_(std::move(children)) {}
Node::~Node() {} Node::~Node() = default;
/// @brief Compute how much space an elements needs. /// @brief Compute how much space an elements needs.
/// @ingroup dom /// @ingroup dom
void Node::ComputeRequirement() { void Node::ComputeRequirement() {
for (auto& child : children_) for (auto& child : children_) {
child->ComputeRequirement(); child->ComputeRequirement();
}
} }
/// @brief Assign a position and a dimension to an element for drawing. /// @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. /// @brief Display an element on a ftxui::Screen.
/// @ingroup dom /// @ingroup dom
void Node::Render(Screen& screen) { void Node::Render(Screen& screen) {
for (auto& child : children_) for (auto& child : children_) {
child->Render(screen); child->Render(screen);
}
} }
void Node::Check(Status* status) { void Node::Check(Status* status) {
for (auto& child : children_) for (auto& child : children_) {
child->Check(status); child->Check(status);
}
status->need_iteration |= (status->iteration == 0); status->need_iteration |= (status->iteration == 0);
} }
@ -52,7 +55,8 @@ void Render(Screen& screen, Node* node) {
Node::Status status; Node::Status status;
node->Check(&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. // Step 1: Find what dimension this elements wants to be.
node->ComputeRequirement(); node->ComputeRequirement();

View File

@ -8,12 +8,13 @@
namespace ftxui { namespace ftxui {
namespace { namespace {
Elements Split(std::string the_text) { Elements Split(const std::string& the_text) {
Elements output; Elements output;
std::stringstream ss(the_text); std::stringstream ss(the_text);
std::string word; std::string word;
while (std::getline(ss, word, ' ')) while (std::getline(ss, word, ' ')) {
output.push_back(text(word)); output.push_back(text(word));
}
return output; return output;
} }
} // namespace } // namespace
@ -21,37 +22,37 @@ Elements Split(std::string the_text) {
/// @brief Return an element drawing the paragraph on multiple lines. /// @brief Return an element drawing the paragraph on multiple lines.
/// @ingroup dom /// @ingroup dom
/// @see flexbox. /// @see flexbox.
Element paragraph(std::string the_text) { Element paragraph(const std::string& the_text) {
return paragraphAlignLeft(std::move(the_text)); return paragraphAlignLeft(the_text);
} }
/// @brief Return an element drawing the paragraph on multiple lines, aligned on /// @brief Return an element drawing the paragraph on multiple lines, aligned on
/// the left. /// the left.
/// @ingroup dom /// @ingroup dom
/// @see flexbox. /// @see flexbox.
Element paragraphAlignLeft(std::string the_text) { Element paragraphAlignLeft(const std::string& the_text) {
static const auto config = FlexboxConfig().SetGap(1, 0); 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 /// @brief Return an element drawing the paragraph on multiple lines, aligned on
/// the right. /// the right.
/// @ingroup dom /// @ingroup dom
/// @see flexbox. /// @see flexbox.
Element paragraphAlignRight(std::string the_text) { Element paragraphAlignRight(const std::string& the_text) {
static const auto config = static const auto config =
FlexboxConfig().SetGap(1, 0).Set(FlexboxConfig::JustifyContent::FlexEnd); 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 /// @brief Return an element drawing the paragraph on multiple lines, aligned on
/// the center. /// the center.
/// @ingroup dom /// @ingroup dom
/// @see flexbox. /// @see flexbox.
Element paragraphAlignCenter(std::string the_text) { Element paragraphAlignCenter(const std::string& the_text) {
static const auto config = static const auto config =
FlexboxConfig().SetGap(1, 0).Set(FlexboxConfig::JustifyContent::Center); 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 /// @brief Return an element drawing the paragraph on multiple lines, aligned
@ -59,10 +60,10 @@ Element paragraphAlignCenter(std::string the_text) {
/// the center. /// the center.
/// @ingroup dom /// @ingroup dom
/// @see flexbox. /// @see flexbox.
Element paragraphAlignJustify(std::string the_text) { Element paragraphAlignJustify(const std::string& the_text) {
static const auto config = FlexboxConfig().SetGap(1, 0).Set( static const auto config = FlexboxConfig().SetGap(1, 0).Set(
FlexboxConfig::JustifyContent::SpaceBetween); FlexboxConfig::JustifyContent::SpaceBetween);
Elements words = Split(std::move(the_text)); Elements words = Split(the_text);
words.push_back(text("") | xflex); words.push_back(text("") | xflex);
return flexbox(std::move(words), config); return flexbox(std::move(words), config);
} }

View File

@ -21,27 +21,29 @@ Element vscroll_indicator(Element child) {
using NodeDecorator::NodeDecorator; using NodeDecorator::NodeDecorator;
void ComputeRequirement() override { void ComputeRequirement() override {
Node::ComputeRequirement(); NodeDecorator::ComputeRequirement();
requirement_ = children_[0]->requirement(); requirement_ = children_[0]->requirement();
requirement_.min_x++; requirement_.min_x++;
} }
void SetBox(Box box) override { void SetBox(Box box) override {
Node::SetBox(box); NodeDecorator::SetBox(box);
if (box_.x_min > box_.x_max) if (box_.x_min > box_.x_max) {
box_.x_max--; box_.x_max--;
}
children_[0]->SetBox(box); children_[0]->SetBox(box);
} }
void Render(Screen& screen) final { void Render(Screen& screen) final {
Node::Render(screen); NodeDecorator::Render(screen);
const Box& stencil = screen.stencil; const Box& stencil = screen.stencil;
int size_inner = box_.y_max - box_.y_min; int size_inner = box_.y_max - box_.y_min;
int size_outter = stencil.y_max - stencil.y_min + 1; int size_outter = stencil.y_max - stencil.y_min + 1;
if (size_outter >= size_inner) if (size_outter >= size_inner) {
return; return;
}
int size = 2 * size_outter * size_outter / size_inner; int size = 2 * size_outter * size_outter / size_inner;
size = std::max(size, 1); 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 up = (start_y <= y_up) && (y_up <= start_y + size);
bool down = (start_y <= y_down) && (y_down <= 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) = Pixel();
screen.PixelAt(x, y).character = c; screen.PixelAt(x, y).character = c;
} }

View File

@ -1,5 +1,7 @@
#include <array> // for array, array<>::value_type
#include <memory> // for make_shared, allocator #include <memory> // for make_shared, allocator
#include <string> // for string #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/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 #include "ftxui/dom/node.hpp" // for Node
@ -10,19 +12,25 @@
namespace ftxui { namespace ftxui {
namespace {
using ftxui::Screen; 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 { class Separator : public Node {
public: public:
Separator(std::string value) : value_(value) {} explicit Separator(std::string value) : value_(std::move(value)) {}
void ComputeRequirement() override { void ComputeRequirement() override {
requirement_.min_x = 1; requirement_.min_x = 1;
@ -44,7 +52,7 @@ class Separator : public Node {
class SeparatorAuto : public Node { class SeparatorAuto : public Node {
public: public:
SeparatorAuto(BorderStyle style) : style_(style) {} explicit SeparatorAuto(BorderStyle style) : style_(style) {}
void ComputeRequirement() override { void ComputeRequirement() override {
requirement_.min_x = 1; requirement_.min_x = 1;
@ -55,7 +63,7 @@ class SeparatorAuto : public Node {
bool is_column = (box_.x_max == box_.x_min); bool is_column = (box_.x_max == box_.x_min);
bool is_line = (box_.y_min == box_.y_max); 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 y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) { for (int x = box_.x_min; x <= box_.x_max; ++x) {
@ -71,7 +79,8 @@ class SeparatorAuto : public Node {
class SeparatorWithPixel : public SeparatorAuto { class SeparatorWithPixel : public SeparatorAuto {
public: public:
SeparatorWithPixel(Pixel pixel) : SeparatorAuto(LIGHT), pixel_(pixel) { explicit SeparatorWithPixel(Pixel pixel)
: SeparatorAuto(LIGHT), pixel_(std::move(pixel)) {
pixel_.automerge = true; pixel_.automerge = true;
} }
void Render(Screen& screen) override { void Render(Screen& screen) override {
@ -337,7 +346,7 @@ Element separatorEmpty() {
/// down /// down
/// ``` /// ```
Element separatorCharacter(std::string value) { 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. /// @brief Draw a separator in between two element filled with a given pixel.
@ -367,7 +376,7 @@ Element separatorCharacter(std::string value) {
/// Down /// Down
/// ``` /// ```
Element separator(Pixel pixel) { 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 /// @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, Element separatorHSelector(float left,
float right, float right,
Color selected_color, Color unselected_color,
Color unselected_color) { Color selected_color) {
class Impl : public Node { class Impl : public Node {
public: public:
Impl(float left, float right, Color selected_color, Color unselected_color) Impl(float left, float right, Color selected_color, Color unselected_color)
: left_(left), : left_(left),
right_(right), right_(right),
selected_color_(selected_color), unselected_color_(unselected_color),
unselected_color_(unselected_color) {} selected_color_(selected_color) {}
void ComputeRequirement() override { void ComputeRequirement() override {
requirement_.min_x = 1; requirement_.min_x = 1;
requirement_.min_y = 1; requirement_.min_y = 1;
} }
void Render(Screen& screen) override { void Render(Screen& screen) override {
if (box_.y_max < box_.y_min) if (box_.y_max < box_.y_min) {
return; return;
}
// This are the two location with an empty demi-cell. // This are the two location with an empty demi-cell.
int demi_cell_left = left_ * 2 - 1; int demi_cell_left = int(left_ * 2.F - 1.F); // NOLINT
int demi_cell_right = right_ * 2 + 2; int demi_cell_right = int(right_ * 2.F + 2.F); // NOLINT
int y = box_.y_min; int y = box_.y_min;
for (int x = box_.x_min; x <= box_.x_max; ++x) { for (int x = box_.x_min; x <= box_.x_max; ++x) {
@ -419,23 +429,24 @@ Element separatorHSelector(float left,
pixel.character = ""; pixel.character = "";
pixel.automerge = true; pixel.automerge = true;
} else { } else {
pixel.character = a_empty ? "" : ""; pixel.character = a_empty ? "" : ""; // NOLINT
pixel.automerge = false; 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_; pixel.foreground_color = selected_color_;
else } else {
pixel.foreground_color = unselected_color_; pixel.foreground_color = unselected_color_;
} }
} }
}
float left_; float left_;
float right_; float right_;
Color selected_color_;
Color unselected_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 /// @brief Draw an vertical bar, with the area in between up/downcolored
@ -452,27 +463,28 @@ Element separatorHSelector(float left,
/// ``` /// ```
Element separatorVSelector(float up, Element separatorVSelector(float up,
float down, float down,
Color selected_color, Color unselected_color,
Color unselected_color) { Color selected_color) {
class Impl : public Node { class Impl : public Node {
public: 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), : up_(up),
down_(down), down_(down),
selected_color_(selected_color), unselected_color_(unselected_color),
unselected_color_(unselected_color) {} selected_color_(selected_color) {}
void ComputeRequirement() override { void ComputeRequirement() override {
requirement_.min_x = 1; requirement_.min_x = 1;
requirement_.min_y = 1; requirement_.min_y = 1;
} }
void Render(Screen& screen) override { void Render(Screen& screen) override {
if (box_.x_max < box_.x_min) if (box_.x_max < box_.x_min) {
return; return;
}
// This are the two location with an empty demi-cell. // This are the two location with an empty demi-cell.
int demi_cell_up = up_ * 2 - 1; int demi_cell_up = int(up_ * 2 - 1);
int demi_cell_down = down_ * 2 + 2; int demi_cell_down = int(down_ * 2 + 2);
int x = box_.x_min; int x = box_.x_min;
for (int y = box_.y_min; y <= box_.y_max; ++y) { for (int y = box_.y_min; y <= box_.y_max; ++y) {
@ -487,23 +499,24 @@ Element separatorVSelector(float up,
pixel.character = ""; pixel.character = "";
pixel.automerge = true; pixel.automerge = true;
} else { } else {
pixel.character = a_empty ? "" : ""; pixel.character = a_empty ? "" : ""; // NOLINT
pixel.automerge = false; 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_; pixel.foreground_color = selected_color_;
else } else {
pixel.foreground_color = unselected_color_; pixel.foreground_color = unselected_color_;
} }
} }
}
float up_; float up_;
float down_; float down_;
Color selected_color_;
Color unselected_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 } // namespace ftxui

View File

@ -1,4 +1,3 @@
#include <stddef.h> // for size_t
#include <algorithm> // for min, max #include <algorithm> // for min, max
#include <memory> // for make_shared, __shared_ptr_access #include <memory> // for make_shared, __shared_ptr_access
#include <utility> // for move #include <utility> // for move
@ -13,7 +12,7 @@ namespace ftxui {
class Size : public Node { class Size : public Node {
public: 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))), : Node(unpack(std::move(child))),
direction_(direction), direction_(direction),
constraint_(constraint), constraint_(constraint),

View File

@ -1,4 +1,4 @@
#include <stddef.h> // for size_t #include <cstddef> // for size_t
#include <memory> // for allocator, allocator_traits<>::value_type #include <memory> // for allocator, allocator_traits<>::value_type
#include <string> // for basic_string, string #include <string> // for basic_string, string
#include <utility> // for move #include <utility> // for move
@ -8,7 +8,9 @@
namespace ftxui { 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"}, {"Replaced by the gauge"},
}, },
@ -246,6 +248,8 @@ static const std::vector<std::vector<std::vector<std::string>>> elements = {
" LOLLOL ", " LOLLOL ",
}}}; }}};
} // namespace
/// @brief Useful to represent the effect of time and/or events. This display an /// @brief Useful to represent the effect of time and/or events. This display an
/// ASCII art "video". /// ASCII art "video".
/// @param charset_index The type of "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 /// @ingroup dom
Element spinner(int charset_index, size_t image_index) { Element spinner(int charset_index, size_t image_index) {
if (charset_index == 0) { if (charset_index == 0) {
image_index %= 40; const int progress_size = 40;
if (image_index > 20) image_index %= progress_size;
image_index = 40 - image_index; if (image_index > progress_size / 2) {
return gauge(image_index * 0.05); image_index = progress_size - image_index;
} }
charset_index %= elements.size(); return gauge(float(image_index) * 0.05F); // NOLINT
image_index %= elements[charset_index].size(); }
charset_index %= (int)elements.size();
image_index %= (int)elements[charset_index].size();
std::vector<Element> lines; 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)); lines.push_back(text(it));
}
return vbox(std::move(lines)); return vbox(std::move(lines));
} }

View File

@ -13,6 +13,7 @@ bool IsCell(int x, int y) {
return x % 2 == 1 && y % 2 == 1; return x % 2 == 1 && y % 2 == 1;
} }
// NOLINTNEXTLINE
static std::string charset[6][6] = { static std::string charset[6][6] = {
{"", "", "", "", "", ""}, // {"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, // {"", "", "", "", "", ""}, //
@ -29,8 +30,9 @@ int Wrap(int input, int modulo) {
} }
void Order(int& a, int& b) { void Order(int& a, int& b) {
if (a >= b) if (a >= b) {
std::swap(a, b); std::swap(a, b);
}
} }
} // namespace } // namespace
@ -42,10 +44,10 @@ Table::Table() {
Table::Table(std::vector<std::vector<std::string>> input) { Table::Table(std::vector<std::vector<std::string>> input) {
std::vector<std::vector<Element>> output; std::vector<std::vector<Element>> output;
for (auto& row : input) { for (auto& row : input) {
output.push_back({}); output.emplace_back();
auto& output_row = output.back(); auto& output_row = output.back();
for (auto& cell : row) { for (auto& cell : row) {
output_row.push_back(text(cell)); output_row.push_back(text(std::move(cell)));
} }
} }
Initialize(std::move(output)); 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) { void Table::Initialize(std::vector<std::vector<Element>> input) {
input_dim_y_ = input.size(); input_dim_y_ = (int)input.size();
input_dim_x_ = 0; input_dim_x_ = 0;
for (auto& row : input) for (auto& row : input) {
input_dim_x_ = std::max(input_dim_x_, (int)row.size()); input_dim_x_ = std::max(input_dim_x_, (int)row.size());
}
dim_y_ = 2 * input_dim_y_ + 1; dim_y_ = 2 * input_dim_y_ + 1;
dim_x_ = 2 * input_dim_x_ + 1; dim_x_ = 2 * input_dim_x_ + 1;
// Reserve space. // Reserve space.
elements_.resize(dim_y_); 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_); elements_[y].resize(dim_x_);
}
// Transfert elements_ from |input| toward |elements_|. // Transfert elements_ from |input| toward |elements_|.
{ {
@ -88,8 +92,9 @@ void Table::Initialize(std::vector<std::vector<Element>> input) {
auto& element = elements_[y][x]; auto& element = elements_[y][x];
if (IsCell(x, y)) { if (IsCell(x, y)) {
if (!element) if (!element) {
element = emptyElement(); element = emptyElement();
}
continue; continue;
} }
@ -129,7 +134,7 @@ TableSelection Table::SelectRectangle(int column_min,
row_max = Wrap(row_max, input_dim_y_); row_max = Wrap(row_max, input_dim_y_);
Order(row_min, row_max); Order(row_min, row_max);
TableSelection output; TableSelection output; // NOLINT
output.table_ = this; output.table_ = this;
output.x_min_ = 2 * column_min; output.x_min_ = 2 * column_min;
output.x_max_ = 2 * column_max + 2; output.x_max_ = 2 * column_max + 2;
@ -139,7 +144,7 @@ TableSelection Table::SelectRectangle(int column_min,
} }
TableSelection Table::SelectAll() { TableSelection Table::SelectAll() {
TableSelection output; TableSelection output; // NOLINT
output.table_ = this; output.table_ = this;
output.x_min_ = 0; output.x_min_ = 0;
output.x_max_ = dim_x_ - 1; output.x_max_ = dim_x_ - 1;
@ -172,6 +177,7 @@ Element Table::Render() {
return gridbox(std::move(elements_)); return gridbox(std::move(elements_));
} }
// NOLINTNEXTLINE
void TableSelection::Decorate(Decorator decorator) { void TableSelection::Decorate(Decorator decorator) {
for (int y = y_min_; y <= y_max_; ++y) { for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) { for (int x = x_min_; x <= x_max_; ++x) {
@ -181,10 +187,11 @@ void TableSelection::Decorate(Decorator decorator) {
} }
} }
// NOLINTNEXTLINE
void TableSelection::DecorateCells(Decorator decorator) { void TableSelection::DecorateCells(Decorator decorator) {
for (int y = y_min_; y <= y_max_; ++y) { for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) { 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]; Element& e = table_->elements_[y][x];
e = std::move(e) | decorator; e = std::move(e) | decorator;
} }
@ -192,12 +199,13 @@ void TableSelection::DecorateCells(Decorator decorator) {
} }
} }
// NOLINTNEXTLINE
void TableSelection::DecorateAlternateColumn(Decorator decorator, void TableSelection::DecorateAlternateColumn(Decorator decorator,
int modulo, int modulo,
int shift) { int shift) {
for (int y = y_min_; y <= y_max_; ++y) { for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) { 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]; Element& e = table_->elements_[y][x];
e = std::move(e) | decorator; e = std::move(e) | decorator;
} }
@ -205,12 +213,13 @@ void TableSelection::DecorateAlternateColumn(Decorator decorator,
} }
} }
// NOLINTNEXTLINE
void TableSelection::DecorateAlternateRow(Decorator decorator, void TableSelection::DecorateAlternateRow(Decorator decorator,
int modulo, int modulo,
int shift) { int shift) {
for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) { for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
for (int x = x_min_; x <= x_max_; ++x) { 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]; Element& e = table_->elements_[y][x];
e = std::move(e) | decorator; e = std::move(e) | decorator;
} }
@ -218,12 +227,13 @@ void TableSelection::DecorateAlternateRow(Decorator decorator,
} }
} }
// NOLINTNEXTLINE
void TableSelection::DecorateCellsAlternateColumn(Decorator decorator, void TableSelection::DecorateCellsAlternateColumn(Decorator decorator,
int modulo, int modulo,
int shift) { int shift) {
for (int y = y_min_; y <= y_max_; ++y) { for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) { 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]; Element& e = table_->elements_[y][x];
e = std::move(e) | decorator; e = std::move(e) | decorator;
} }
@ -231,12 +241,13 @@ void TableSelection::DecorateCellsAlternateColumn(Decorator decorator,
} }
} }
// NOLINTNEXTLINE
void TableSelection::DecorateCellsAlternateRow(Decorator decorator, void TableSelection::DecorateCellsAlternateRow(Decorator decorator,
int modulo, int modulo,
int shift) { int shift) {
for (int y = y_min_; y <= y_max_; ++y) { for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) { 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]; Element& e = table_->elements_[y][x];
e = std::move(e) | decorator; e = std::move(e) | decorator;
} }
@ -244,77 +255,82 @@ void TableSelection::DecorateCellsAlternateRow(Decorator decorator,
} }
} }
void TableSelection::Border(BorderStyle style) { void TableSelection::Border(BorderStyle border) {
BorderLeft(style); BorderLeft(border);
BorderRight(style); BorderRight(border);
BorderTop(style); BorderTop(border);
BorderBottom(style); BorderBottom(border);
table_->elements_[y_min_][x_min_] = text(charset[style][0]) | automerge; // NOLINTNEXTLINE
table_->elements_[y_min_][x_max_] = text(charset[style][1]) | automerge; table_->elements_[y_min_][x_min_] = text(charset[border][0]) | automerge;
table_->elements_[y_max_][x_min_] = text(charset[style][2]) | automerge; // NOLINTNEXTLINE
table_->elements_[y_max_][x_max_] = text(charset[style][3]) | automerge; 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 y = y_min_ + 1; y <= y_max_ - 1; ++y) {
for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) { for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
if (y % 2 == 0 || x % 2 == 0) { if (y % 2 == 0 || x % 2 == 0) {
Element& e = table_->elements_[y][x]; Element& e = table_->elements_[y][x];
e = (y % 2) ? separatorCharacter(charset[style][5]) | automerge e = (y % 2 == 1)
: separatorCharacter(charset[style][4]) | automerge; ? 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 y = y_min_ + 1; y <= y_max_ - 1; ++y) {
for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) { for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
if (x % 2 == 0) { if (x % 2 == 0) {
table_->elements_[y][x] = 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 y = y_min_ + 1; y <= y_max_ - 1; ++y) {
for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) { for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
if (y % 2 == 0) { if (y % 2 == 0) {
table_->elements_[y][x] = 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++) { for (int y = y_min_; y <= y_max_; y++) {
table_->elements_[y][x_min_] = 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++) { for (int y = y_min_; y <= y_max_; y++) {
table_->elements_[y][x_max_] = 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++) { for (int x = x_min_; x <= x_max_; x++) {
table_->elements_[y_min_][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++) { for (int x = x_min_; x <= x_max_; x++) {
table_->elements_[y_max_][x] = table_->elements_[y_max_][x] =
separatorCharacter(charset[style][4]) | automerge; separatorCharacter(charset[border][4]) | automerge; // NOLINT
} }
} }

View File

@ -1,6 +1,7 @@
#include <algorithm> // for min #include <algorithm> // for min
#include <memory> // for make_shared #include <memory> // for make_shared
#include <string> // for string, wstring #include <string> // for string, wstring
#include <utility> // for move
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/dom/deprecated.hpp" // for text, vtext #include "ftxui/dom/deprecated.hpp" // for text, vtext
@ -17,7 +18,7 @@ using ftxui::Screen;
class Text : public Node { class Text : public Node {
public: public:
Text(std::string text) : text_(text) {} explicit Text(std::string text) : text_(std::move(text)) {}
void ComputeRequirement() override { void ComputeRequirement() override {
requirement_.min_x = string_width(text_); requirement_.min_x = string_width(text_);
@ -27,11 +28,13 @@ class Text : public Node {
void Render(Screen& screen) override { void Render(Screen& screen) override {
int x = box_.x_min; int x = box_.x_min;
int y = box_.y_min; int y = box_.y_min;
if (y > box_.y_max) if (y > box_.y_max) {
return; return;
}
for (const auto& cell : Utf8ToGlyphs(text_)) { for (const auto& cell : Utf8ToGlyphs(text_)) {
if (x > box_.x_max) if (x > box_.x_max) {
return; return;
}
screen.PixelAt(x, y).character = cell; screen.PixelAt(x, y).character = cell;
++x; ++x;
} }
@ -43,8 +46,8 @@ class Text : public Node {
class VText : public Node { class VText : public Node {
public: public:
VText(std::string text) explicit VText(std::string text)
: text_(text), width_{std::min(string_width(text_), 1)} {} : text_(std::move(text)), width_{std::min(string_width(text_), 1)} {}
void ComputeRequirement() override { void ComputeRequirement() override {
requirement_.min_x = width_; requirement_.min_x = width_;
@ -54,11 +57,13 @@ class VText : public Node {
void Render(Screen& screen) override { void Render(Screen& screen) override {
int x = box_.x_min; int x = box_.x_min;
int y = box_.y_min; int y = box_.y_min;
if (x + width_ - 1 > box_.x_max) if (x + width_ - 1 > box_.x_max) {
return; return;
}
for (const auto& it : Utf8ToGlyphs(text_)) { for (const auto& it : Utf8ToGlyphs(text_)) {
if (y > box_.y_max) if (y > box_.y_max) {
return; return;
}
screen.PixelAt(x, y).character = it; screen.PixelAt(x, y).character = it;
y += 1; y += 1;
} }
@ -85,7 +90,7 @@ class VText : public Node {
/// Hello world! /// Hello world!
/// ``` /// ```
Element text(std::string text) { 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. /// @brief Display a piece of unicode text.
@ -103,7 +108,7 @@ Element text(std::string text) {
/// ```bash /// ```bash
/// Hello world! /// Hello world!
/// ``` /// ```
Element text(std::wstring text) { Element text(std::wstring text) { // NOLINT
return std::make_shared<Text>(to_string(text)); return std::make_shared<Text>(to_string(text));
} }
@ -134,7 +139,7 @@ Element text(std::wstring text) {
/// ! /// !
/// ``` /// ```
Element vtext(std::string 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. /// @brief Display a piece unicode text vertically.
@ -163,7 +168,7 @@ Element vtext(std::string text) {
/// d /// d
/// ! /// !
/// ``` /// ```
Element vtext(std::wstring text) { Element vtext(std::wstring text) { // NOLINT
return std::make_shared<VText>(to_string(text)); return std::make_shared<VText>(to_string(text));
} }

View File

@ -36,16 +36,18 @@ Element nothing(Element element) {
/// auto decorator = bold | blink; /// auto decorator = bold | blink;
/// ``` /// ```
Decorator operator|(Decorator a, Decorator b) { 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. /// @brief From a set of element, apply a decorator to every elements.
/// @return the set of decorated element. /// @return the set of decorated element.
/// @ingroup dom /// @ingroup dom
Elements operator|(Elements elements, Decorator decorator) { Elements operator|(Elements elements, Decorator decorator) { // NOLINT
Elements output; Elements output;
for (auto& it : elements) for (auto& it : elements) {
output.push_back(std::move(it) | decorator); output.push_back(std::move(it) | decorator);
}
return output; return output;
} }
@ -62,7 +64,7 @@ Elements operator|(Elements elements, Decorator decorator) {
/// ```cpp /// ```cpp
/// text("Hello") | bold; /// text("Hello") | bold;
/// ``` /// ```
Element operator|(Element element, Decorator decorator) { Element operator|(Element element, Decorator decorator) { // NOLINT
return decorator(std::move(element)); return decorator(std::move(element));
} }
@ -78,7 +80,7 @@ Element operator|(Element element, Decorator decorator) {
/// element |= bold; /// element |= bold;
/// ``` /// ```
Element& operator|=(Element& e, Decorator d) { Element& operator|=(Element& e, Decorator d) {
e = e | d; e = e | std::move(d);
return e; return e;
} }
@ -95,7 +97,8 @@ Dimensions Dimension::Fit(Element& e) {
Node::Status status; Node::Status status;
e->Check(&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(); e->ComputeRequirement();
// Don't give the element more space than it needs: // Don't give the element more space than it needs:
@ -107,8 +110,9 @@ Dimensions Dimension::Fit(Element& e) {
status.iteration++; status.iteration++;
e->Check(&status); e->Check(&status);
if (!status.need_iteration) if (!status.need_iteration) {
break; break;
}
// Increase the size of the box until it fits, but not more than the with of // Increase the size of the box until it fits, but not more than the with of
// the terminal emulator: // the terminal emulator:
box.x_max = std::min(e->requirement().min_x, fullsize.dimx); box.x_max = std::min(e->requirement().min_x, fullsize.dimx);

View File

@ -1,5 +1,5 @@
#include <stddef.h> // for size_t
#include <algorithm> // for max #include <algorithm> // for max
#include <cstddef> // for size_t
#include <memory> // for __shared_ptr_access, shared_ptr, make_shared, allocator_traits<>::value_type #include <memory> // for __shared_ptr_access, shared_ptr, make_shared, allocator_traits<>::value_type
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type #include <vector> // for vector, __alloc_traits<>::value_type
@ -14,7 +14,7 @@ namespace ftxui {
class VBox : public Node { class VBox : public Node {
public: public:
VBox(Elements children) : Node(std::move(children)) {} explicit VBox(Elements children) : Node(std::move(children)) {}
void ComputeRequirement() override { void ComputeRequirement() override {
requirement_.min_x = 0; requirement_.min_x = 0;

View File

@ -17,7 +17,7 @@ Box Box::Intersection(Box a, Box b) {
/// @return whether (x,y) is contained inside the box. /// @return whether (x,y) is contained inside the box.
/// @ingroup screen /// @ingroup screen
bool Box::Contain(int x, int y) { bool Box::Contain(int x, int y) const {
return x_min <= x && // return x_min <= x && //
x_max >= x && // x_max >= x && //
y_min <= y && // y_min <= y && //

View File

@ -1,20 +1,36 @@
#include "ftxui/screen/color.hpp" #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/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 { namespace ftxui {
using namespace std::literals;
namespace { namespace {
const char* palette16code[16][2] = { const std::array<const char*, 33> palette16code = {
{"30", "40"}, {"31", "41"}, {"32", "42"}, {"33", "43"}, "30", "40", //
{"34", "44"}, {"35", "45"}, {"36", "46"}, {"37", "47"}, "31", "41", //
{"90", "100"}, {"91", "101"}, {"92", "102"}, {"93", "103"}, "32", "42", //
{"94", "104"}, {"95", "105"}, {"96", "106"}, {"97", "107"}, "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 { bool Color::operator==(const Color& rhs) const {
return red_ == rhs.red_ && green_ == rhs.green_ && blue_ == rhs.blue_ && return red_ == rhs.red_ && green_ == rhs.green_ && blue_ == rhs.blue_ &&
@ -28,16 +44,16 @@ bool Color::operator!=(const Color& rhs) const {
std::string Color::Print(bool is_background_color) const { std::string Color::Print(bool is_background_color) const {
switch (type_) { switch (type_) {
case ColorType::Palette1: case ColorType::Palette1:
return is_background_color ? "49" : "39"; return is_background_color ? "49"s : "39"s;
case ColorType::Palette16: case ColorType::Palette16:
return palette16code[index_][is_background_color]; return palette16code[2 * red_ + is_background_color]; // NOLINT;
case ColorType::Palette256: 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: case ColorType::TrueColor:
return (is_background_color ? "48;2;" : "38;2;") // return (is_background_color ? "48;2;"s : "38;2;"s) //
+ std::to_string(red_) + ";" // + std::to_string(red_) + ";" //
+ std::to_string(green_) + ";" // + std::to_string(green_) + ";" //
+ std::to_string(blue_); // + std::to_string(blue_); //
@ -47,23 +63,24 @@ std::string Color::Print(bool is_background_color) const {
/// @brief Build a transparent color. /// @brief Build a transparent color.
/// @ingroup screen /// @ingroup screen
Color::Color() : type_(ColorType::Palette1) {} Color::Color() = default;
/// @brief Build a transparent color. /// @brief Build a transparent color.
/// @ingroup screen /// @ingroup screen
Color::Color(Palette1) : type_(ColorType::Palette1) {} Color::Color(Palette1 /*value*/) : Color() {}
/// @brief Build a transparent using Palette16 colors. /// @brief Build a transparent using Palette16 colors.
/// @ingroup screen /// @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. /// @brief Build a transparent using Palette256 colors.
/// @ingroup screen /// @ingroup screen
Color::Color(Palette256 index) : type_(ColorType::Palette256), index_(index) { Color::Color(Palette256 index) : type_(ColorType::Palette256), red_(index) {
if (Terminal::ColorSupport() >= Terminal::Color::Palette256) if (Terminal::ColorSupport() >= Terminal::Color::Palette256) {
return; return;
}
type_ = ColorType::Palette16; 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. /// @brief Build a Color from its RGB representation.
@ -75,12 +92,17 @@ Color::Color(Palette256 index) : type_(ColorType::Palette256), index_(index) {
/// @ingroup screen /// @ingroup screen
Color::Color(uint8_t red, uint8_t green, uint8_t blue) Color::Color(uint8_t red, uint8_t green, uint8_t blue)
: type_(ColorType::TrueColor), red_(red), green_(green), blue_(blue) { : type_(ColorType::TrueColor), red_(red), green_(green), blue_(blue) {
if (Terminal::ColorSupport() == Terminal::Color::TrueColor) if (Terminal::ColorSupport() == Terminal::Color::TrueColor) {
return; 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; 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)); ColorInfo color_info = GetColorInfo(Color::Palette256(i));
int dr = color_info.red - red; int dr = color_info.red - red;
int dg = color_info.green - green; 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) { if (Terminal::ColorSupport() == Terminal::Color::Palette256) {
type_ = ColorType::Palette256; type_ = ColorType::Palette256;
index_ = best; red_ = best;
} else { } else {
type_ = ColorType::Palette16; 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 /// @ingroup screen
// static // static
Color Color::HSV(uint8_t h, uint8_t s, uint8_t v) { Color Color::HSV(uint8_t h, uint8_t s, uint8_t v) {
if (s == 0) if (s == 0) {
return Color(v, v, v); return Color(v, v, v);
}
uint8_t region = h / 43; uint8_t region = h / 43; // NOLINT
uint8_t remainder = (h - (region * 43)) * 6; uint8_t remainder = (h - (region * 43)) * 6; // NOLINT
uint8_t p = (v * (255 - s)) >> 8; uint8_t p = (v * (255 - s)) >> 8; // NOLINT
uint8_t q = (v * (255 - ((s * remainder) >> 8))) >> 8; uint8_t q = (v * (255 - ((s * remainder) >> 8))) >> 8; // NOLINT
uint8_t t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8; uint8_t t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8; // NOLINT
// clang-format off // clang-format off
switch (region) { switch (region) { // NOLINT
case 0: return Color(v,t,p); case 0: return Color(v,t,p); // NOLINT
case 1: return Color(q,v,p); case 1: return Color(q,v,p); // NOLINT
case 2: return Color(p,v,t); case 2: return Color(p,v,t); // NOLINT
case 3: return Color(p,q,v); case 3: return Color(p,q,v); // NOLINT
case 4: return Color(t,p,v); case 4: return Color(t,p,v); // NOLINT
case 5: return Color(v,p,q); case 5: return Color(v,p,q); // NOLINT
} } // NOLINT
// clang-format on // clang-format on
return Color(0, 0, 0); return Color(0, 0, 0);
@ -147,81 +170,77 @@ Color Color::HSV(uint8_t h, uint8_t s, uint8_t v) {
// static // static
Color Color::Interpolate(float t, const Color& a, const Color& b) { Color Color::Interpolate(float t, const Color& a, const Color& b) {
float red = 0.f; if (a.type_ == ColorType::Palette1) {
float green = 0.f; if (t < 0.5F) { // NOLINT
float blue = 0.f;
switch (a.type_) {
case ColorType::Palette1: {
if (t < 0.5)
return a; return a;
else } else {
return b; return b;
} }
}
if (b.type_ == ColorType::Palette1) {
if (t > 0.5F) { // NOLINT
return a;
} else {
return b;
}
}
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: { case ColorType::Palette16: {
ColorInfo info = GetColorInfo(Color::Palette16(a.index_)); ColorInfo info = GetColorInfo(Color::Palette16(color.red_));
red = info.red * (1 - t); *red = info.red;
green = info.green * (1 - t); *green = info.green;
blue = info.blue * (1 - t); *blue = info.blue;
break; return;
} }
case ColorType::Palette256: { case ColorType::Palette256: {
ColorInfo info = GetColorInfo(Color::Palette256(a.index_)); ColorInfo info = GetColorInfo(Color::Palette256(color.red_));
red = info.red * (1 - t); *red = info.red;
green = info.green * (1 - t); *green = info.green;
blue = info.blue * (1 - t); *blue = info.blue;
break; return;
} }
case ColorType::TrueColor: { case ColorType::TrueColor: {
red = a.red_ * (1 - t); *red = color.red_;
green = a.green_ * (1 - t); *green = color.green_;
blue = a.blue_ * (1 - t); *blue = color.blue_;
break; return;
} }
} }
};
switch (b.type_) { uint8_t red_a = 0;
case ColorType::Palette1: { uint8_t green_a = 0;
if (t > 0.5) uint8_t blue_a = 0;
return a; uint8_t red_b = 0;
else uint8_t green_b = 0;
return b; uint8_t blue_b = 0;
} get_color(a, &red_a, &green_a, &blue_a);
get_color(b, &red_b, &green_b, &blue_b);
case ColorType::Palette16: { return Color::RGB(static_cast<uint8_t>(static_cast<float>(red_a) * (1 - t) +
ColorInfo info = GetColorInfo(Color::Palette16(b.index_)); static_cast<float>(red_b) * t),
red += info.red * t; static_cast<uint8_t>(static_cast<float>(green_a) * (1 - t) +
green += info.green * t; static_cast<float>(green_b) * t),
blue += info.blue * t; static_cast<uint8_t>(static_cast<float>(blue_a) * (1 - t) +
break; static_cast<float>(blue_b) * t));
}
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;
}
}
return Color::RGB(red, green, blue);
} }
inline namespace literals { inline namespace literals {
Color operator""_rgb(unsigned long long int combined) { Color operator""_rgb(unsigned long long int combined) {
assert(combined <= 0xffffffU); // assert(combined <= 0xffffffU);
auto const red = static_cast<uint8_t>(combined >> 16); auto const red = static_cast<uint8_t>(combined >> 16U);
auto const green = static_cast<uint8_t>(combined >> 8); auto const green = static_cast<uint8_t>(combined >> 8U);
auto const blue = static_cast<uint8_t>(combined); auto const blue = static_cast<uint8_t>(combined);
return Color(red, green, blue); return Color(red, green, blue);
} }

View File

@ -1,10 +1,13 @@
#include "ftxui/screen/color_info.hpp" #include "ftxui/screen/color_info.hpp"
#include <array>
#include "ftxui/screen/color.hpp" // for Color, Color::Palette16, Color::Palette256 #include "ftxui/screen/color.hpp" // for Color, Color::Palette16, Color::Palette256
namespace ftxui { namespace ftxui {
// clang-format off // clang-format off
const ColorInfo palette256[] = { const std::array<ColorInfo, 256> palette256 = {{
{"Black" , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } , {"Black" , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ,
{"Red" , 1 , 1 , 128 , 0 , 0 , 0 , 255 , 128 } , {"Red" , 1 , 1 , 128 , 0 , 0 , 0 , 255 , 128 } ,
{"Green" , 2 , 2 , 0 , 128 , 0 , 85 , 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 } , {"Grey85" , 253 , 7 , 218 , 218 , 218 , 0 , 0 , 218 } ,
{"Grey89" , 254 , 15 , 228 , 228 , 228 , 0 , 0 , 228 } , {"Grey89" , 254 , 15 , 228 , 228 , 228 , 0 , 0 , 228 } ,
{"Grey93" , 255 , 15 , 238 , 238 , 238 , 0 , 0 , 238 } , {"Grey93" , 255 , 15 , 238 , 238 , 238 , 0 , 0 , 238 } ,
} ; }};
ColorInfo GetColorInfo(Color::Palette256 index) { ColorInfo GetColorInfo(Color::Palette256 index) {
return palette256[int(index)]; return palette256[index]; // NOLINT;
} }
ColorInfo GetColorInfo(Color::Palette16 index) { ColorInfo GetColorInfo(Color::Palette16 index) {
return palette256[int(index)]; return palette256[index]; // NOLINT;
} }
// clang-format off // clang-format off

View File

@ -1,6 +1,7 @@
#include <stdint.h> // for uint8_t
#include <iostream> // for operator<<, stringstream, basic_ostream, flush, cout, ostream #include <iostream> // for operator<<, stringstream, basic_ostream, flush, cout, ostream
#include <map> // for _Rb_tree_const_iterator, map, operator!=, operator== #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 <sstream> // IWYU pragma: keep
#include <utility> // for pair #include <utility> // for pair
@ -19,26 +20,11 @@
namespace ftxui { namespace ftxui {
namespace { 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"; Pixel& dev_null_pixel() {
static const char DIM_RESET[] = "\x1B[22m"; static Pixel pixel;
return pixel;
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;
#if defined(_WIN32) #if defined(_WIN32)
void WindowsEmulateVT100Terminal() { void WindowsEmulateVT100Terminal() {
@ -66,20 +52,49 @@ void WindowsEmulateVT100Terminal() {
void UpdatePixelStyle(std::stringstream& ss, void UpdatePixelStyle(std::stringstream& ss,
Pixel& previous, Pixel& previous,
const Pixel& next) { const Pixel& next) {
if (next.bold != previous.bold) if (next == previous) {
ss << (next.bold ? BOLD_SET : BOLD_RESET); return;
}
if (next.dim != previous.dim) if (next.bold && !previous.bold) {
ss << (next.dim ? DIM_SET : DIM_RESET); ss << "\x1B[1m"; // BOLD_SET
}
if (next.underlined != previous.underlined) if (!next.bold && previous.bold) {
ss << (next.underlined ? UNDERLINED_SET : UNDERLINED_RESET); ss << "\x1B[22m"; // BOLD_RESET
}
if (next.blink != previous.blink) if (next.dim && !previous.dim) {
ss << (next.blink ? BLINK_SET : BLINK_RESET); ss << "\x1B[2m"; // DIM_SET
}
if (next.inverted != previous.inverted) if (!next.dim && previous.dim) {
ss << (next.inverted ? INVERTED_SET : INVERTED_RESET); 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 || if (next.foreground_color != previous.foreground_color ||
next.background_color != previous.background_color) { next.background_color != previous.background_color) {
@ -91,31 +106,31 @@ void UpdatePixelStyle(std::stringstream& ss,
} }
struct TileEncoding { struct TileEncoding {
unsigned int left : 2; uint8_t left : 2;
unsigned int top : 2; uint8_t top : 2;
unsigned int right : 2; uint8_t right : 2;
unsigned int down : 2; uint8_t down : 2;
unsigned int round : 1; uint8_t round : 1;
// clang-format off // clang-format off
bool operator<(const TileEncoding& other) const { bool operator<(const TileEncoding& other) const {
if (left < other.left) return true; if (left < other.left) { return true; }
if (left > other.left) return false; if (left > other.left) { return false; }
if (top < other.top) return true; if (top < other.top) { return true; }
if (top > other.top) return false; if (top > other.top) { return false; }
if (right < other.right) return true; if (right < other.right) { return true; }
if (right > other.right) return false; if (right > other.right) { return false; }
if (down < other.down) return true; if (down < other.down) { return true; }
if (down > other.down) return false; if (down > other.down) { return false; }
if (round < other.round) return true; if (round < other.round) { return true; }
if (round > other.round) return false; if (round > other.round) { return false; }
return false; return false;
} }
// clang-format on // clang-format on
}; };
// clang-format off // 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}}, {"", {1, 0, 1, 0, 0}},
{"", {2, 0, 2, 0, 0}}, {"", {2, 0, 2, 0, 0}},
@ -257,64 +272,73 @@ const std::map<std::string, TileEncoding> tile_encoding = {
// clang-format on // clang-format on
template <class A, class B> 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; std::map<B, A> output;
for (const auto& it : input) for (const auto& it : input) {
output[it.second] = it.first; output[it.second] = it.first;
}
return output; return output;
} }
const std::map<TileEncoding, std::string> tile_encoding_inverse = const std::map<TileEncoding, std::string> tile_encoding_inverse = // NOLINT
InvertMap(tile_encoding); InvertMap(tile_encoding);
void UpgradeLeftRight(std::string& left, std::string& right) { void UpgradeLeftRight(std::string& left, std::string& right) {
const auto it_left = tile_encoding.find(left); const auto it_left = tile_encoding.find(left);
if (it_left == tile_encoding.end()) if (it_left == tile_encoding.end()) {
return; return;
}
const auto it_right = tile_encoding.find(right); const auto it_right = tile_encoding.find(right);
if (it_right == tile_encoding.end()) if (it_right == tile_encoding.end()) {
return; return;
}
if (it_left->second.right == 0 && it_right->second.left != 0) { if (it_left->second.right == 0 && it_right->second.left != 0) {
TileEncoding encoding_left = it_left->second; TileEncoding encoding_left = it_left->second;
encoding_left.right = it_right->second.left; encoding_left.right = it_right->second.left;
const auto it_left_upgrade = tile_encoding_inverse.find(encoding_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; left = it_left_upgrade->second;
} }
}
if (it_right->second.left == 0 && it_left->second.right != 0) { if (it_right->second.left == 0 && it_left->second.right != 0) {
TileEncoding encoding_right = it_right->second; TileEncoding encoding_right = it_right->second;
encoding_right.left = it_left->second.right; encoding_right.left = it_left->second.right;
const auto it_right_upgrade = tile_encoding_inverse.find(encoding_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; right = it_right_upgrade->second;
} }
}
} }
void UpgradeTopDown(std::string& top, std::string& down) { void UpgradeTopDown(std::string& top, std::string& down) {
const auto it_top = tile_encoding.find(top); const auto it_top = tile_encoding.find(top);
if (it_top == tile_encoding.end()) if (it_top == tile_encoding.end()) {
return; return;
}
const auto it_down = tile_encoding.find(down); const auto it_down = tile_encoding.find(down);
if (it_down == tile_encoding.end()) if (it_down == tile_encoding.end()) {
return; return;
}
if (it_top->second.down == 0 && it_down->second.top != 0) { if (it_top->second.down == 0 && it_down->second.top != 0) {
TileEncoding encoding_top = it_top->second; TileEncoding encoding_top = it_top->second;
encoding_top.down = it_down->second.top; encoding_top.down = it_down->second.top;
const auto it_top_down = tile_encoding_inverse.find(encoding_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; top = it_top_down->second;
} }
}
if (it_down->second.top == 0 && it_top->second.down != 0) { if (it_down->second.top == 0 && it_top->second.down != 0) {
TileEncoding encoding_down = it_down->second; TileEncoding encoding_down = it_down->second;
encoding_down.top = it_top->second.down; encoding_down.top = it_top->second.down;
const auto it_down_top = tile_encoding_inverse.find(encoding_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; down = it_down_top->second;
} }
}
} }
bool ShouldAttemptAutoMerge(Pixel& pixel) { bool ShouldAttemptAutoMerge(Pixel& pixel) {
@ -323,6 +347,18 @@ bool ShouldAttemptAutoMerge(Pixel& pixel) {
} // namespace } // 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. /// A fixed dimension.
/// @see Fit /// @see Fit
/// @see Full /// @see Full
@ -409,7 +445,7 @@ std::string& Screen::at(int x, int y) {
/// @param x The pixel position along the x-axis. /// @param x The pixel position along the x-axis.
/// @param y The pixel position along the y-axis. /// @param y The pixel position along the y-axis.
Pixel& Screen::PixelAt(int x, int y) { 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 /// @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 /// @return The string to print in order to reset the cursor position to the
/// beginning. /// beginning.
std::string Screen::ResetPosition(bool clear) { std::string Screen::ResetPosition(bool clear) const {
std::stringstream ss; std::stringstream ss;
if (clear) { if (clear) {
ss << MOVE_LEFT << CLEAR_LINE; ss << "\r"; // MOVE_LEFT;
ss << "\x1b[2K"; // CLEAR_SCREEN;
for (int y = 1; y < dimy_; ++y) { for (int y = 1; y < dimy_; ++y) {
ss << MOVE_UP << CLEAR_LINE; ss << "\x1B[1A"; // MOVE_UP;
ss << "\x1B[2K"; // CLEAR_LINE;
} }
} else { } else {
ss << MOVE_LEFT; ss << "\r"; // MOVE_LEFT;
for (int y = 1; y < dimy_; ++y) { for (int y = 1; y < dimy_; ++y) {
ss << MOVE_UP; ss << "\x1B[1A"; // MOVE_UP;
} }
} }
return ss.str(); return ss.str();
@ -462,18 +500,21 @@ void Screen::ApplyShader() {
for (int x = 1; x < dimx_; ++x) { for (int x = 1; x < dimx_; ++x) {
// Box drawing character uses exactly 3 byte. // Box drawing character uses exactly 3 byte.
Pixel& cur = pixels_[y][x]; Pixel& cur = pixels_[y][x];
if (!ShouldAttemptAutoMerge(cur)) if (!ShouldAttemptAutoMerge(cur)) {
continue; continue;
}
Pixel& left = pixels_[y][x-1]; Pixel& left = pixels_[y][x-1];
Pixel& top = pixels_[y-1][x]; Pixel& top = pixels_[y-1][x];
if (ShouldAttemptAutoMerge(left)) if (ShouldAttemptAutoMerge(left)) {
UpgradeLeftRight(left.character, cur.character); UpgradeLeftRight(left.character, cur.character);
if (ShouldAttemptAutoMerge(top)) }
if (ShouldAttemptAutoMerge(top)) {
UpgradeTopDown(top.character, cur.character); UpgradeTopDown(top.character, cur.character);
} }
} }
}
} }
// clang-format on // clang-format on

View File

@ -7,11 +7,11 @@
#include "ftxui/screen/string.hpp" #include "ftxui/screen/string.hpp"
#include <stddef.h> // for size_t #include <array> // for array
#include <stdint.h> // for uint32_t, uint8_t
#include <codecvt> // for codecvt_utf8_utf16 #include <codecvt> // for codecvt_utf8_utf16
#include <cstdint> // for uint32_t, uint8_t
#include <locale> // for wstring_convert #include <locale> // for wstring_convert
#include <string> // for string, basic_string, wstring, allocator #include <string> // for string, basic_string, wstring
#include "ftxui/screen/deprecated.hpp" // for wchar_width, wstring_width #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 // Sorted list of non-overlapping intervals of non-spacing characters
// generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" // generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c"
static const Interval g_combining_characters[] = { const std::array<Interval, 142> g_combining_characters = {
{0x0300, 0x036F}, {0x0483, 0x0486}, {0x0488, 0x0489}, Interval{0x0300, 0x036F}, Interval{0x0483, 0x0486},
{0x0591, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, Interval{0x0488, 0x0489}, Interval{0x0591, 0x05BD},
{0x05C4, 0x05C5}, {0x05C7, 0x05C7}, {0x0600, 0x0603}, Interval{0x05BF, 0x05BF}, Interval{0x05C1, 0x05C2},
{0x0610, 0x0615}, {0x064B, 0x065E}, {0x0670, 0x0670}, Interval{0x05C4, 0x05C5}, Interval{0x05C7, 0x05C7},
{0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, Interval{0x0600, 0x0603}, Interval{0x0610, 0x0615},
{0x070F, 0x070F}, {0x0711, 0x0711}, {0x0730, 0x074A}, Interval{0x064B, 0x065E}, Interval{0x0670, 0x0670},
{0x07A6, 0x07B0}, {0x07EB, 0x07F3}, {0x0901, 0x0902}, Interval{0x06D6, 0x06E4}, Interval{0x06E7, 0x06E8},
{0x093C, 0x093C}, {0x0941, 0x0948}, {0x094D, 0x094D}, Interval{0x06EA, 0x06ED}, Interval{0x070F, 0x070F},
{0x0951, 0x0954}, {0x0962, 0x0963}, {0x0981, 0x0981}, Interval{0x0711, 0x0711}, Interval{0x0730, 0x074A},
{0x09BC, 0x09BC}, {0x09C1, 0x09C4}, {0x09CD, 0x09CD}, Interval{0x07A6, 0x07B0}, Interval{0x07EB, 0x07F3},
{0x09E2, 0x09E3}, {0x0A01, 0x0A02}, {0x0A3C, 0x0A3C}, Interval{0x0901, 0x0902}, Interval{0x093C, 0x093C},
{0x0A41, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, Interval{0x0941, 0x0948}, Interval{0x094D, 0x094D},
{0x0A70, 0x0A71}, {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC}, Interval{0x0951, 0x0954}, Interval{0x0962, 0x0963},
{0x0AC1, 0x0AC5}, {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD}, Interval{0x0981, 0x0981}, Interval{0x09BC, 0x09BC},
{0x0AE2, 0x0AE3}, {0x0B01, 0x0B01}, {0x0B3C, 0x0B3C}, Interval{0x09C1, 0x09C4}, Interval{0x09CD, 0x09CD},
{0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, {0x0B4D, 0x0B4D}, Interval{0x09E2, 0x09E3}, Interval{0x0A01, 0x0A02},
{0x0B56, 0x0B56}, {0x0B82, 0x0B82}, {0x0BC0, 0x0BC0}, Interval{0x0A3C, 0x0A3C}, Interval{0x0A41, 0x0A42},
{0x0BCD, 0x0BCD}, {0x0C3E, 0x0C40}, {0x0C46, 0x0C48}, Interval{0x0A47, 0x0A48}, Interval{0x0A4B, 0x0A4D},
{0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0CBC, 0x0CBC}, Interval{0x0A70, 0x0A71}, Interval{0x0A81, 0x0A82},
{0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD}, Interval{0x0ABC, 0x0ABC}, Interval{0x0AC1, 0x0AC5},
{0x0CE2, 0x0CE3}, {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D}, Interval{0x0AC7, 0x0AC8}, Interval{0x0ACD, 0x0ACD},
{0x0DCA, 0x0DCA}, {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6}, Interval{0x0AE2, 0x0AE3}, Interval{0x0B01, 0x0B01},
{0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, Interval{0x0B3C, 0x0B3C}, Interval{0x0B3F, 0x0B3F},
{0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, Interval{0x0B41, 0x0B43}, Interval{0x0B4D, 0x0B4D},
{0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, Interval{0x0B56, 0x0B56}, Interval{0x0B82, 0x0B82},
{0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F71, 0x0F7E}, Interval{0x0BC0, 0x0BC0}, Interval{0x0BCD, 0x0BCD},
{0x0F80, 0x0F84}, {0x0F86, 0x0F87}, {0x0F90, 0x0F97}, Interval{0x0C3E, 0x0C40}, Interval{0x0C46, 0x0C48},
{0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102D, 0x1030}, Interval{0x0C4A, 0x0C4D}, Interval{0x0C55, 0x0C56},
{0x1032, 0x1032}, {0x1036, 0x1037}, {0x1039, 0x1039}, Interval{0x0CBC, 0x0CBC}, Interval{0x0CBF, 0x0CBF},
{0x1058, 0x1059}, {0x1160, 0x11FF}, {0x135F, 0x135F}, Interval{0x0CC6, 0x0CC6}, Interval{0x0CCC, 0x0CCD},
{0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753}, Interval{0x0CE2, 0x0CE3}, Interval{0x0D41, 0x0D43},
{0x1772, 0x1773}, {0x17B4, 0x17B5}, {0x17B7, 0x17BD}, Interval{0x0D4D, 0x0D4D}, Interval{0x0DCA, 0x0DCA},
{0x17C6, 0x17C6}, {0x17C9, 0x17D3}, {0x17DD, 0x17DD}, Interval{0x0DD2, 0x0DD4}, Interval{0x0DD6, 0x0DD6},
{0x180B, 0x180D}, {0x18A9, 0x18A9}, {0x1920, 0x1922}, Interval{0x0E31, 0x0E31}, Interval{0x0E34, 0x0E3A},
{0x1927, 0x1928}, {0x1932, 0x1932}, {0x1939, 0x193B}, Interval{0x0E47, 0x0E4E}, Interval{0x0EB1, 0x0EB1},
{0x1A17, 0x1A18}, {0x1B00, 0x1B03}, {0x1B34, 0x1B34}, Interval{0x0EB4, 0x0EB9}, Interval{0x0EBB, 0x0EBC},
{0x1B36, 0x1B3A}, {0x1B3C, 0x1B3C}, {0x1B42, 0x1B42}, Interval{0x0EC8, 0x0ECD}, Interval{0x0F18, 0x0F19},
{0x1B6B, 0x1B73}, {0x1DC0, 0x1DCA}, {0x1DFE, 0x1DFF}, Interval{0x0F35, 0x0F35}, Interval{0x0F37, 0x0F37},
{0x200B, 0x200F}, {0x202A, 0x202E}, {0x2060, 0x2063}, Interval{0x0F39, 0x0F39}, Interval{0x0F71, 0x0F7E},
{0x206A, 0x206F}, {0x20D0, 0x20EF}, {0x302A, 0x302F}, Interval{0x0F80, 0x0F84}, Interval{0x0F86, 0x0F87},
{0x3099, 0x309A}, {0xA806, 0xA806}, {0xA80B, 0xA80B}, Interval{0x0F90, 0x0F97}, Interval{0x0F99, 0x0FBC},
{0xA825, 0xA826}, {0xFB1E, 0xFB1E}, {0xFE00, 0xFE0F}, Interval{0x0FC6, 0x0FC6}, Interval{0x102D, 0x1030},
{0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, Interval{0x1032, 0x1032}, Interval{0x1036, 0x1037},
{0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, Interval{0x1039, 0x1039}, Interval{0x1058, 0x1059},
{0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x1D167, 0x1D169}, Interval{0x1160, 0x11FF}, Interval{0x135F, 0x135F},
{0x1D173, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD}, Interval{0x1712, 0x1714}, Interval{0x1732, 0x1734},
{0x1D242, 0x1D244}, {0xE0001, 0xE0001}, {0xE0020, 0xE007F}, Interval{0x1752, 0x1753}, Interval{0x1772, 0x1773},
{0xE0100, 0xE01EF}, 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[] = { const std::array<Interval, 13> g_full_width_characters = {
{0x1100, 0x115f}, {0x2329, 0x2329}, {0x232a, 0x232a}, {0x2e80, 0x303e}, Interval{0x1100, 0x115f}, Interval{0x2329, 0x2329},
{0x3040, 0xa4cf}, {0xac00, 0xd7a3}, {0xf900, 0xfaff}, {0xfe10, 0xfe19}, Interval{0x232a, 0x232a}, Interval{0x2e80, 0x303e},
{0xfe30, 0xfe6f}, {0xff00, 0xff60}, {0xffe0, 0xffe6}, {0x20000, 0x2fffd}, Interval{0x3040, 0xa4cf}, Interval{0xac00, 0xd7a3},
{0x30000, 0x3fffd}, 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. // Find a codepoint inside a sorted list of Interval.
int Bisearch(uint32_t ucs, const Interval* table, int max) { bool Bisearch(uint32_t ucs, const Interval* table, int max) {
if (ucs < table[0].first || ucs > table[max].last) if (ucs < table[0].first || ucs > table[max].last) { // NOLINT
return 0; return false;
}
int min = 0; int min = 0;
while (max >= min) { while (max >= min) {
int mid = (min + max) / 2; int mid = (min + max) / 2;
if (ucs > table[mid].last) if (ucs > table[mid].last) { // NOLINT
min = mid + 1; min = mid + 1;
else if (ucs < table[mid].first) } else if (ucs < table[mid].first) { // NOLINT
max = mid - 1; max = mid - 1;
else } else {
return 1; return true;
}
} }
return 0; return false;
} }
bool IsCombining(uint32_t ucs) { bool IsCombining(uint32_t ucs) {
return Bisearch(ucs, g_combining_characters, return Bisearch(ucs, g_combining_characters.data(),
sizeof(g_combining_characters) / sizeof(Interval) - 1); g_combining_characters.size() - 1);
} }
bool IsFullWidth(uint32_t ucs) { bool IsFullWidth(uint32_t ucs) {
if (ucs < 0x0300) // Quick path: if (ucs < 0x0300) // Quick path: // NOLINT
return false; return false;
return Bisearch(ucs, g_full_width_characters, return Bisearch(ucs, g_full_width_characters.data(),
sizeof(g_full_width_characters) / sizeof(Interval) - 1); g_full_width_characters.size() - 1);
} }
bool IsControl(uint32_t ucs) { bool IsControl(uint32_t ucs) {
if (ucs == 0) if (ucs == 0) {
return true; return true;
if (ucs < 32) }
if (ucs < 32) { // NOLINT
return true; return true;
if (ucs >= 0x7f && ucs < 0xa0) }
if (ucs >= 0x7f && ucs < 0xa0) { // NOLINT
return true; return true;
}
return false; return false;
} }
int codepoint_width(uint32_t ucs) { int codepoint_width(uint32_t ucs) {
if (IsControl(ucs)) if (IsControl(ucs)) {
return -1; return -1;
}
if (IsCombining(ucs)) if (IsCombining(ucs)) {
return 0; return 0;
}
if (IsFullWidth(ucs)) if (IsFullWidth(ucs)) {
return 2; return 2;
}
return 1; return 1;
} }
@ -152,50 +186,53 @@ bool EatCodePoint(const std::string& input,
uint8_t byte_1 = input[start]; uint8_t byte_1 = input[start];
// 1 byte string. // 1 byte string.
if ((byte_1 & 0b1000'0000) == 0b0000'0000) { if ((byte_1 & 0b1000'0000) == 0b0000'0000) { // NOLINT
*ucs = byte_1 & 0b0111'1111; *ucs = byte_1 & 0b0111'1111; // NOLINT
*end = start + 1; *end = start + 1;
return true; return true;
} }
// 2 byte string. // 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]; uint8_t byte_2 = input[start + 1];
*ucs = 0; *ucs = 0;
*ucs += byte_1 & 0b0001'1111; *ucs += byte_1 & 0b0001'1111; // NOLINT
*ucs <<= 6; *ucs <<= 6; // NOLINT
*ucs += byte_2 & 0b0011'1111; *ucs += byte_2 & 0b0011'1111; // NOLINT
*end = start + 2; *end = start + 2;
return true; return true;
} }
// 3 byte string. // 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_2 = input[start + 1];
uint8_t byte_3 = input[start + 2]; uint8_t byte_3 = input[start + 2];
*ucs = 0; *ucs = 0;
*ucs += byte_1 & 0b0000'1111; *ucs += byte_1 & 0b0000'1111; // NOLINT
*ucs <<= 6; *ucs <<= 6; // NOLINT
*ucs += byte_2 & 0b0011'1111; *ucs += byte_2 & 0b0011'1111; // NOLINT
*ucs <<= 6; *ucs <<= 6; // NOLINT
*ucs += byte_3 & 0b0011'1111; *ucs += byte_3 & 0b0011'1111; // NOLINT
*end = start + 3; *end = start + 3;
return true; return true;
} }
// 4 byte string. // 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_2 = input[start + 1];
uint8_t byte_3 = input[start + 2]; uint8_t byte_3 = input[start + 2];
uint8_t byte_4 = input[start + 3]; uint8_t byte_4 = input[start + 3];
*ucs = 0; *ucs = 0;
*ucs += byte_1 & 0b0000'0111; *ucs += byte_1 & 0b0000'0111; // NOLINT
*ucs <<= 6; *ucs <<= 6; // NOLINT
*ucs += byte_2 & 0b0011'1111; *ucs += byte_2 & 0b0011'1111; // NOLINT
*ucs <<= 6; *ucs <<= 6; // NOLINT
*ucs += byte_3 & 0b0011'1111; *ucs += byte_3 & 0b0011'1111; // NOLINT
*ucs <<= 6; *ucs <<= 6; // NOLINT
*ucs += byte_4 & 0b0011'1111; *ucs += byte_4 & 0b0011'1111; // NOLINT
*end = start + 4; *end = start + 4;
return true; return true;
} }
@ -216,8 +253,9 @@ int wstring_width(const std::wstring& text) {
for (const wchar_t& it : text) { for (const wchar_t& it : text) {
int w = wchar_width(it); int w = wchar_width(it);
if (w < 0) if (w < 0) {
return -1; return -1;
}
width += w; width += w;
} }
return width; return width;
@ -228,14 +266,17 @@ int string_width(const std::string& input) {
size_t start = 0; size_t start = 0;
while (start < input.size()) { while (start < input.size()) {
uint32_t codepoint = 0; uint32_t codepoint = 0;
if (!EatCodePoint(input, start, &start, &codepoint)) if (!EatCodePoint(input, start, &start, &codepoint)) {
continue; continue;
}
if (IsControl(codepoint)) if (IsControl(codepoint)) {
continue; continue;
}
if (IsCombining(codepoint)) if (IsCombining(codepoint)) {
continue; continue;
}
if (IsFullWidth(codepoint)) { if (IsFullWidth(codepoint)) {
width += 2; width += 2;
@ -254,7 +295,7 @@ std::vector<std::string> Utf8ToGlyphs(const std::string& input) {
size_t start = 0; size_t start = 0;
size_t end = 0; size_t end = 0;
while (start < input.size()) { while (start < input.size()) {
uint32_t codepoint; uint32_t codepoint = 0;
if (!EatCodePoint(input, start, &end, &codepoint)) { if (!EatCodePoint(input, start, &end, &codepoint)) {
start = end; start = end;
continue; continue;
@ -264,13 +305,15 @@ std::vector<std::string> Utf8ToGlyphs(const std::string& input) {
start = end; start = end;
// Ignore control characters. // Ignore control characters.
if (IsControl(codepoint)) if (IsControl(codepoint)) {
continue; continue;
}
// Combining characters are put with the previous glyph they are modifying. // Combining characters are put with the previous glyph they are modifying.
if (IsCombining(codepoint)) { if (IsCombining(codepoint)) {
if (out.size() != 0) if (!out.empty()) {
out.back() += append; out.back() += append;
}
continue; continue;
} }
@ -278,7 +321,7 @@ std::vector<std::string> Utf8ToGlyphs(const std::string& input) {
// string to reserve the space the first is taking. // string to reserve the space the first is taking.
if (IsFullWidth(codepoint)) { if (IsFullWidth(codepoint)) {
out.push_back(append); out.push_back(append);
out.push_back(""); out.emplace_back("");
continue; continue;
} }
@ -288,14 +331,13 @@ std::vector<std::string> Utf8ToGlyphs(const std::string& input) {
return out; return out;
} }
int GlyphPosition(const std::string& input, int GlyphPosition(const std::string& input, size_t glyph_index, size_t start) {
size_t glyph_to_skip, if (glyph_index <= 0) {
size_t start) {
if (glyph_to_skip <= 0)
return 0; return 0;
}
size_t end = 0; size_t end = 0;
while (start < input.size()) { while (start < input.size()) {
uint32_t codepoint; uint32_t codepoint = 0;
bool eaten = EatCodePoint(input, start, &end, &codepoint); bool eaten = EatCodePoint(input, start, &end, &codepoint);
// Ignore invalid, control characters and combining characters. // 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 // We eat the beginning of the next glyph. If we are eating the one
// requested, return its start position immediately. // requested, return its start position immediately.
if (glyph_to_skip == 0) if (glyph_index == 0) {
return start; return static_cast<int>(start);
}
// Otherwise, skip this glyph and iterate: // Otherwise, skip this glyph and iterate:
glyph_to_skip--; glyph_index--;
start = end; start = end;
} }
return input.size(); return static_cast<int>(input.size());
} }
std::vector<int> CellToGlyphIndex(const std::string& input) { 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 start = 0;
size_t end = 0; size_t end = 0;
while (start < input.size()) { while (start < input.size()) {
uint32_t codepoint; uint32_t codepoint = 0;
bool eaten = EatCodePoint(input, start, &end, &codepoint); bool eaten = EatCodePoint(input, start, &end, &codepoint);
start = end; start = end;
// Ignore invalid / control characters. // Ignore invalid / control characters.
if (!eaten || IsControl(codepoint)) if (!eaten || IsControl(codepoint)) {
continue; continue;
}
// Combining characters are put with the previous glyph they are modifying. // Combining characters are put with the previous glyph they are modifying.
if (IsCombining(codepoint)) { if (IsCombining(codepoint)) {
@ -361,19 +405,21 @@ int GlyphCount(const std::string& input) {
size_t start = 0; size_t start = 0;
size_t end = 0; size_t end = 0;
while (start < input.size()) { while (start < input.size()) {
uint32_t codepoint; uint32_t codepoint = 0;
bool eaten = EatCodePoint(input, start, &end, &codepoint); bool eaten = EatCodePoint(input, start, &end, &codepoint);
start = end; start = end;
// Ignore invalid characters: // Ignore invalid characters:
if (!eaten || IsControl(codepoint)) if (!eaten || IsControl(codepoint)) {
continue; continue;
}
// Ignore combining characters, except when they don't have a preceding to // Ignore combining characters, except when they don't have a preceding to
// combine with. // combine with.
if (IsCombining(codepoint)) { if (IsCombining(codepoint)) {
if (size == 0) if (size == 0) {
size++; size++;
}
continue; continue;
} }

View File

@ -18,89 +18,57 @@
namespace ftxui { 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 { 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) { const char* Safe(const char* c) {
return c ? c : ""; return (c != nullptr) ? c : "";
} }
bool Contains(const std::string& s, const char* key) { bool Contains(const std::string& s, const char* key) {
return s.find(key) != std::string::npos; return s.find(key) != std::string::npos;
} }
static bool cached = false;
Terminal::Color cached_supported_color;
Terminal::Color ComputeColorSupport() { Terminal::Color ComputeColorSupport() {
#if defined(__EMSCRIPTEN__) #if defined(__EMSCRIPTEN__)
return Terminal::Color::TrueColor; return Terminal::Color::TrueColor;
#endif #endif
std::string COLORTERM = Safe(std::getenv("COLORTERM")); std::string COLORTERM = Safe(std::getenv("COLORTERM")); // NOLINT
if (Contains(COLORTERM, "24bit") || Contains(COLORTERM, "truecolor")) if (Contains(COLORTERM, "24bit") || Contains(COLORTERM, "truecolor")) {
return Terminal::Color::TrueColor; return Terminal::Color::TrueColor;
}
std::string TERM = Safe(std::getenv("TERM")); std::string TERM = Safe(std::getenv("TERM")); // NOLINT
if (Contains(COLORTERM, "256") || Contains(TERM, "256")) if (Contains(COLORTERM, "256") || Contains(TERM, "256")) {
return Terminal::Color::Palette256; return Terminal::Color::Palette256;
}
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK) #if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
// Microsoft terminals do not properly declare themselve supporting true // Microsoft terminals do not properly declare themselve supporting true
// colors: https://github.com/microsoft/terminal/issues/1040 // colors: https://github.com/microsoft/terminal/issues/1040
// As a fallback, assume microsoft terminal are the ones not setting those // As a fallback, assume microsoft terminal are the ones not setting those
// variables, and enable true colors. // variables, and enable true colors.
if (TERM == "" && COLORTERM == "") if (TERM == "" && COLORTERM == "") {
return Terminal::Color::TrueColor; return Terminal::Color::TrueColor;
}
#endif #endif
return Terminal::Color::Palette16; return Terminal::Color::Palette16;
@ -108,7 +76,43 @@ Terminal::Color ComputeColorSupport() {
} // namespace } // 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() { Terminal::Color Terminal::ColorSupport() {
static bool cached = false;
static Terminal::Color cached_supported_color;
if (!cached) { if (!cached) {
cached = true; cached = true;
cached_supported_color = ComputeColorSupport(); cached_supported_color = ComputeColorSupport();