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_FUZZER "Set to ON to enable fuzzing" OFF)
option(FTXUI_ENABLE_INSTALL "Generate the install target" ON)
option(FTXUI_CLANG_TIDY "Execute clang-tidy" OFF)
set(FTXUI_MICROSOFT_TERMINAL_FALLBACK_HELP_TEXT "On windows, assume the \
terminal used will be one of Microsoft and use a set of reasonnable fallback \
@ -22,6 +23,7 @@ else()
${FTXUI_MICROSOFT_TERMINAL_FALLBACK_HELP_TEXT} OFF)
endif()
add_library(screen
include/ftxui/screen/box.hpp
include/ftxui/screen/color.hpp

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)
set_target_properties(${library} PROPERTIES OUTPUT_NAME "ftxui-${library}")
if(CLANG_TIDY_EXE AND FTXUI_CLANG_TIDY)
set_target_properties(${library}
PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_EXE};-warnings-as-errors=*"
)
endif()
target_include_directories(${library}
PUBLIC

View File

@ -12,6 +12,9 @@ add_subdirectory(dom)
if (EMSCRIPTEN)
# 32MB should be enough to run all the examples, in debug mode.
target_link_options(component PUBLIC "SHELL: -s TOTAL_MEMORY=33554432")
target_link_options(component PUBLIC "SHELL: -s ASSERTIONS=1")
#string(APPEND CMAKE_EXE_LINKER_FLAGS " -s ALLOW_MEMORY_GROWTH=1")
#target_link_options(component PUBLIC "SHELL: -s ALLOW_MEMORY_GROWTH=1")
get_property(EXAMPLES GLOBAL PROPERTY FTXUI::EXAMPLES)
foreach(file

View File

@ -1,13 +1,12 @@
#include <array> // for array
#include <memory> // for shared_ptr, __shared_ptr_access, allocator_traits<>::value_type
#include <memory> // for shared_ptr, __shared_ptr_access
#include <string> // for operator+, to_string
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Checkbox, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for operator|, Element, size, border, frame, HEIGHT, LESS_THAN
#include "ftxui/dom/elements.hpp" // for operator|, Element, size, border, frame, vscroll_indicator, HEIGHT, LESS_THAN
using namespace ftxui;

View File

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

View File

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

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

View File

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

View File

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

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

View File

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

View File

@ -8,7 +8,7 @@
#include <vector> // for vector
#include "ftxui/component/component_base.hpp" // for Component, Components
#include "ftxui/component/component_options.hpp" // for ButtonOption, CheckboxOption (ptr only), InputOption (ptr only), MenuEntryOption (ptr only), MenuOption, RadioboxOption (ptr only)
#include "ftxui/component/component_options.hpp" // for ButtonOption, CheckboxOption, InputOption (ptr only), MenuEntryOption (ptr only), MenuOption, RadioboxOption (ptr only)
#include "ftxui/dom/elements.hpp" // for Element
#include "ftxui/util/ref.hpp" // for Ref, ConstStringRef, ConstStringListRef, StringRef

View File

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

View File

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

View File

@ -1,7 +1,7 @@
#ifndef FTXUI_DOM_CANVAS_HPP
#define FTXUI_DOM_CANVAS_HPP
#include <stddef.h> // for size_t
#include <cstddef> // for size_t
#include <functional> // for function
#include <string> // for string
#include <unordered_map> // for unordered_map
@ -13,7 +13,7 @@ namespace ftxui {
struct Canvas {
public:
Canvas() {}
Canvas() = default;
Canvas(int width, int height);
// Getters:
@ -114,7 +114,8 @@ struct Canvas {
struct XYHash {
size_t operator()(const XY& xy) const {
return static_cast<size_t>(xy.x * 1024 + xy.y);
constexpr size_t shift = 1024;
return size_t(xy.x) * shift + size_t(xy.y);
}
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,7 @@ namespace ftxui {
Component Maybe(Component child, std::function<bool()> show) {
class Impl : public ComponentBase {
public:
Impl(std::function<bool()> show) : show_(std::move(show)) {}
explicit Impl(std::function<bool()> show) : show_(std::move(show)) {}
private:
Element Render() override {
@ -48,7 +48,7 @@ Component Maybe(Component child, std::function<bool()> show) {
/// ```
ComponentDecorator Maybe(std::function<bool()> show) {
return [show = std::move(show)](Component child) mutable {
return Maybe(child, std::move(show));
return Maybe(std::move(child), std::move(show));
};
}
@ -64,7 +64,7 @@ ComponentDecorator Maybe(std::function<bool()> show) {
/// auto maybe_component = Maybe(component, &show);
/// ```
Component Maybe(Component child, const bool* show) {
return Maybe(child, [show] { return *show; });
return Maybe(std::move(child), [show] { return *show; });
}
/// @brief Decorate a component. It is shown only when |show| is true.
@ -78,7 +78,7 @@ Component Maybe(Component child, const bool* show) {
/// auto maybe_component = component | Maybe(&show);
/// ```
ComponentDecorator Maybe(const bool* show) {
return [show](Component child) { return Maybe(child, show); };
return [show](Component child) { return Maybe(std::move(child), show); };
}
} // namespace ftxui

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
#include <algorithm> // for max
#include <iterator> // for begin, end
#include <array> // for array
#include <memory> // for allocator, make_shared, __shared_ptr_access
#include <string> // for string, basic_string
#include <string> // for basic_string, string
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for unpack, Element, Decorator, BorderStyle, ROUNDED, Elements, DOUBLE, EMPTY, HEAVY, LIGHT, border, borderDouble, borderEmpty, borderHeavy, borderLight, borderRounded, borderStyled, borderWith, window
#include "ftxui/dom/node.hpp" // for Node, Elements
@ -13,30 +13,25 @@
namespace ftxui {
static std::string simple_border_charset[6][6] = {
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{" ", " ", " ", " ", " ", " "}, //
using Charset = std::array<std::string, 6>; // NOLINT
using Charsets = std::array<Charset, 6>; // NOLINT
static Charsets simple_border_charset = // NOLINT
{
Charset{"", "", "", "", "", ""},
Charset{"", "", "", "", "", ""},
Charset{"", "", "", "", "", ""},
Charset{"", "", "", "", "", ""},
Charset{" ", " ", " ", " ", " ", " "},
};
// For reference, here is the charset for normal border:
// {"┌", "┐", "└", "┘", "─", "│", "┬", "┴", "┤", "├"};
// TODO(arthursonzogni): Consider adding options to choose the kind of borders
// to use.
class Border : public Node {
public:
Border(Elements children, BorderStyle style)
: Node(std::move(children)),
charset(std::begin(simple_border_charset[style]),
std::end(simple_border_charset[style])) {}
Border(Elements children, Pixel pixel)
: Node(std::move(children)), charset_pixel(10, pixel) {}
charset_(simple_border_charset[style]) {} // NOLINT
std::vector<Pixel> charset_pixel;
std::vector<std::string> charset;
const Charset& charset_; // NOLINT
void ComputeRequirement() override {
Node::ComputeRequirement();
@ -75,63 +70,101 @@ class Border : public Node {
children_[0]->Render(screen);
// Draw the border.
if (box_.x_min >= box_.x_max || box_.y_min >= box_.y_max)
if (box_.x_min >= box_.x_max || box_.y_min >= box_.y_max) {
return;
if (!charset.empty())
RenderPixel(screen);
else
RenderChar(screen);
}
void RenderPixel(Screen& screen) {
screen.at(box_.x_min, box_.y_min) = charset[0];
screen.at(box_.x_max, box_.y_min) = charset[1];
screen.at(box_.x_min, box_.y_max) = charset[2];
screen.at(box_.x_max, box_.y_max) = charset[3];
screen.at(box_.x_min, box_.y_min) = charset_[0]; // NOLINT
screen.at(box_.x_max, box_.y_min) = charset_[1]; // NOLINT
screen.at(box_.x_min, box_.y_max) = charset_[2]; // NOLINT
screen.at(box_.x_max, box_.y_max) = charset_[3]; // NOLINT
for (float x = box_.x_min + 1; x < box_.x_max; ++x) {
for (int x = box_.x_min + 1; x < box_.x_max; ++x) {
Pixel& p1 = screen.PixelAt(x, box_.y_min);
Pixel& p2 = screen.PixelAt(x, box_.y_max);
p1.character = charset[4];
p2.character = charset[4];
p1.character = charset_[4]; // NOLINT
p2.character = charset_[4]; // NOLINT
p1.automerge = true;
p2.automerge = true;
}
for (float y = box_.y_min + 1; y < box_.y_max; ++y) {
for (int y = box_.y_min + 1; y < box_.y_max; ++y) {
Pixel& p3 = screen.PixelAt(box_.x_min, y);
Pixel& p4 = screen.PixelAt(box_.x_max, y);
p3.character = charset[5];
p4.character = charset[5];
p3.character = charset_[5]; // NOLINT
p4.character = charset_[5]; // NOLINT
p3.automerge = true;
p4.automerge = true;
}
// Draw title.
if (children_.size() == 2)
if (children_.size() == 2) {
children_[1]->Render(screen);
}
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);
p3 = charset_pixel[5];
p4 = charset_pixel[5];
p3.automerge = true;
p4.automerge = true;
};
// For reference, here is the charset for normal border:
class BorderPixel : public Node {
public:
BorderPixel(Elements children, Pixel pixel)
: Node(std::move(children)), pixel_(std::move(pixel)) {}
private:
Pixel pixel_;
void ComputeRequirement() override {
Node::ComputeRequirement();
requirement_ = children_[0]->requirement();
requirement_.min_x += 2;
requirement_.min_y += 2;
if (children_.size() == 2) {
requirement_.min_x =
std::max(requirement_.min_x, children_[1]->requirement().min_x + 2);
}
requirement_.selected_box.x_min++;
requirement_.selected_box.x_max++;
requirement_.selected_box.y_min++;
requirement_.selected_box.y_max++;
}
void 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.
/// @ingroup dom
/// @see border
Decorator borderWith(Pixel pixel) {
Decorator borderWith(const Pixel& pixel) {
return [pixel](Element child) {
return std::make_shared<Border>(unpack(std::move(child)), pixel);
return std::make_shared<BorderPixel>(unpack(std::move(child)), pixel);
};
}

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@ namespace ftxui {
class DBox : public Node {
public:
DBox(Elements children) : Node(std::move(children)) {}
explicit DBox(Elements children) : Node(std::move(children)) {}
void ComputeRequirement() override {
requirement_.min_x = 0;
@ -38,9 +38,10 @@ class DBox : public Node {
void SetBox(Box box) override {
Node::SetBox(box);
for (auto& child : children_)
for (auto& child : children_) {
child->SetBox(box);
}
}
};
/// @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 {
public:
Flex(FlexFunction f) : f_(f) {}
explicit Flex(FlexFunction f) : f_(f) {}
Flex(FlexFunction f, Element child) : Node(unpack(std::move(child))), f_(f) {}
void ComputeRequirement() override {
requirement_.min_x = 0;
@ -80,8 +80,9 @@ class Flex : public Node {
}
void SetBox(Box box) override {
if (children_.empty())
if (children_.empty()) {
return;
}
children_[0]->SetBox(box);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,89 +18,57 @@
namespace ftxui {
#if defined(__EMSCRIPTEN__)
// This dimension was chosen arbitrarily to be able to display:
// https://arthursonzogni.com/FTXUI/examples
// This will have to be improved when someone has time to implement and need
// it.
static Dimensions fallback_size{140, 43};
Dimensions Terminal::Size() {
return fallback_size;
}
#elif defined(_WIN32)
// The terminal size in VT100 was 80x24. It is still used nowadays by
// default in many terminal emulator. That's a good choice for a fallback
// value.
static Dimensions fallback_size{80, 24};
Dimensions Terminal::Size() {
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
return Dimensions{csbi.srWindow.Right - csbi.srWindow.Left + 1,
csbi.srWindow.Bottom - csbi.srWindow.Top + 1};
}
return fallback_size;
}
#else
// The terminal size in VT100 was 80x24. It is still used nowadays by
// default in many terminal emulator. That's a good choice for a fallback
// value.
static Dimensions fallback_size{80, 24};
Dimensions Terminal::Size() {
winsize w{};
const int status = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
// The ioctl return value result should be checked. Some operating systems
// don't support TIOCGWINSZ.
if (w.ws_col == 0 || w.ws_row == 0 || status < 0) {
return fallback_size;
}
return Dimensions{w.ws_col, w.ws_row};
}
#endif
/// @brief Override terminal size in case auto-detection fails
/// @param fallbackSize Terminal dimensions to fallback to
void Terminal::SetFallbackSize(const Dimensions& fallbackSize) {
fallback_size = fallbackSize;
}
namespace {
Dimensions& FallbackSize() {
#if defined(__EMSCRIPTEN__)
// This dimension was chosen arbitrarily to be able to display:
// https://arthursonzogni.com/FTXUI/examples
// This will have to be improved when someone has time to implement and need
// it.
constexpr int fallback_width = 140;
constexpr int fallback_height = 43;
#else
// The terminal size in VT100 was 80x24. It is still used nowadays by
// default in many terminal emulator. That's a good choice for a fallback
// value.
constexpr int fallback_width = 80;
constexpr int fallback_height = 24;
#endif
static Dimensions g_fallback_size{fallback_width, fallback_height};
return g_fallback_size;
}
const char* Safe(const char* c) {
return c ? c : "";
return (c != nullptr) ? c : "";
}
bool Contains(const std::string& s, const char* key) {
return s.find(key) != std::string::npos;
}
static bool cached = false;
Terminal::Color cached_supported_color;
Terminal::Color ComputeColorSupport() {
#if defined(__EMSCRIPTEN__)
return Terminal::Color::TrueColor;
#endif
std::string COLORTERM = Safe(std::getenv("COLORTERM"));
if (Contains(COLORTERM, "24bit") || Contains(COLORTERM, "truecolor"))
std::string COLORTERM = Safe(std::getenv("COLORTERM")); // NOLINT
if (Contains(COLORTERM, "24bit") || Contains(COLORTERM, "truecolor")) {
return Terminal::Color::TrueColor;
}
std::string TERM = Safe(std::getenv("TERM"));
if (Contains(COLORTERM, "256") || Contains(TERM, "256"))
std::string TERM = Safe(std::getenv("TERM")); // NOLINT
if (Contains(COLORTERM, "256") || Contains(TERM, "256")) {
return Terminal::Color::Palette256;
}
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
// Microsoft terminals do not properly declare themselve supporting true
// colors: https://github.com/microsoft/terminal/issues/1040
// As a fallback, assume microsoft terminal are the ones not setting those
// variables, and enable true colors.
if (TERM == "" && COLORTERM == "")
if (TERM == "" && COLORTERM == "") {
return Terminal::Color::TrueColor;
}
#endif
return Terminal::Color::Palette16;
@ -108,7 +76,43 @@ Terminal::Color ComputeColorSupport() {
} // namespace
Dimensions Terminal::Size() {
#if defined(__EMSCRIPTEN__)
// This dimension was chosen arbitrarily to be able to display:
// https://arthursonzogni.com/FTXUI/examples
// This will have to be improved when someone has time to implement and need
// it.
return FallbackSize();
#elif defined(_WIN32)
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
return Dimensions{csbi.srWindow.Right - csbi.srWindow.Left + 1,
csbi.srWindow.Bottom - csbi.srWindow.Top + 1};
}
return FallbackSize();
#else
winsize w{};
const int status = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); // NOLINT
// The ioctl return value result should be checked. Some operating systems
// don't support TIOCGWINSZ.
if (w.ws_col == 0 || w.ws_row == 0 || status < 0) {
return FallbackSize();
}
return Dimensions{w.ws_col, w.ws_row};
#endif
}
/// @brief Override terminal size in case auto-detection fails
/// @param fallbackSize Terminal dimensions to fallback to
void Terminal::SetFallbackSize(const Dimensions& fallbackSize) {
FallbackSize() = fallbackSize;
}
Terminal::Color Terminal::ColorSupport() {
static bool cached = false;
static Terminal::Color cached_supported_color;
if (!cached) {
cached = true;
cached_supported_color = ComputeColorSupport();