From 9f4b2bcf96dfffad5bfffb2b8b82539660626749 Mon Sep 17 00:00:00 2001 From: Miko <110693261+mikomikotaishi@users.noreply.github.com> Date: Sat, 13 Dec 2025 11:22:11 -0800 Subject: [PATCH] Add string view overloads (#1154) This is better ergonomic, as `std::string_view` is lightweight and accept more conversion than `const std::string&`. Co-authored-by: ArthurSonzogni --- CHANGELOG.md | 5 + include/ftxui/component/event.hpp | 11 +- include/ftxui/dom/canvas.hpp | 6 +- include/ftxui/dom/elements.hpp | 21 ++-- include/ftxui/dom/selection.hpp | 2 +- include/ftxui/screen/screen.hpp | 2 +- include/ftxui/screen/string.hpp | 21 ++-- include/ftxui/util/ref.hpp | 105 +++++++++++++----- src/ftxui/component/button.cpp | 2 +- src/ftxui/component/checkbox.cpp | 2 +- src/ftxui/component/event.cpp | 38 +++---- src/ftxui/component/menu.cpp | 7 +- src/ftxui/component/screen_interactive.cpp | 2 +- src/ftxui/component/task_runner.hpp | 4 +- .../component/terminal_input_parser_test.cpp | 2 +- src/ftxui/dom/canvas.cpp | 6 +- src/ftxui/dom/hbox_test.cpp | 4 +- src/ftxui/dom/hyperlink.cpp | 10 +- src/ftxui/dom/paragraph.cpp | 18 +-- src/ftxui/dom/selection.cpp | 2 +- src/ftxui/dom/separator.cpp | 4 +- src/ftxui/dom/table_test.cpp | 4 +- src/ftxui/dom/text.cpp | 65 ++++++++++- src/ftxui/dom/text_test.cpp | 11 +- src/ftxui/screen/screen.cpp | 4 +- src/ftxui/screen/string.cpp | 31 +++--- src/ftxui/screen/string_internal.hpp | 19 ++-- src/ftxui/screen/string_test.cpp | 16 +-- src/ftxui/util/ref_test.cpp | 2 +- 29 files changed, 275 insertions(+), 151 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 698749a20..51bc5f9b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,11 @@ Next - Remove dependency on 'pthread'. - Bugfix: Bazel target @ftxui is now visible. Thanks @dskkato in #1157. +### General +- Breaking. Move to `std::string_view` instead of `const std::string&` where + applicable. This yields better interoperability with string literals and + avoids unnecessary copies. Thanks @mikomikotaishi for PR #1154 + ### Component - Feature: POSIX Piped Input Handling. - Allows FTXUI applications to read data from stdin (when piped) while still receiving keyboard input from the terminal. diff --git a/include/ftxui/component/event.hpp b/include/ftxui/component/event.hpp index f95af3104..285f8a26d 100644 --- a/include/ftxui/component/event.hpp +++ b/include/ftxui/component/event.hpp @@ -6,6 +6,7 @@ #include // for Mouse #include // for string, operator== +#include namespace ftxui { @@ -28,13 +29,13 @@ class ComponentBase; /// @ingroup component struct Event { // --- Constructor section --------------------------------------------------- - static Event Character(std::string); + static Event Character(std::string_view); static Event Character(char); static Event Character(wchar_t); - static Event Special(std::string); - static Event Mouse(std::string, Mouse mouse); - static Event CursorPosition(std::string, int x, int y); // Internal - static Event CursorShape(std::string, int shape); // Internal + static Event Special(std::string_view); + static Event Mouse(std::string_view, Mouse mouse); + static Event CursorPosition(std::string_view, int x, int y); // Internal + static Event CursorShape(std::string_view, int shape); // Internal // --- Arrow --- static const Event ArrowLeft; diff --git a/include/ftxui/dom/canvas.hpp b/include/ftxui/dom/canvas.hpp index 2a5ccd8e7..ed8fd6624 100644 --- a/include/ftxui/dom/canvas.hpp +++ b/include/ftxui/dom/canvas.hpp @@ -106,9 +106,9 @@ struct Canvas { // Draw using character of size 2x4 at position (x,y) // x is considered to be a multiple of 2. // y is considered to be a multiple of 4. - void DrawText(int x, int y, const std::string& value); - void DrawText(int x, int y, const std::string& value, const Color& color); - void DrawText(int x, int y, const std::string& value, const Stylizer& style); + void DrawText(int x, int y, std::string_view value); + void DrawText(int x, int y, std::string_view value, const Color& color); + void DrawText(int x, int y, std::string_view value, const Stylizer& style); // Draw using directly pixels or images -------------------------------------- // x is considered to be a multiple of 2. diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index 63d5bf0be..e04fb4487 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -7,6 +7,7 @@ #include #include +#include #include "ftxui/dom/canvas.hpp" #include "ftxui/dom/direction.hpp" #include "ftxui/dom/flexbox_config.hpp" @@ -51,8 +52,8 @@ Elements operator|(Elements, Decorator); Decorator operator|(Decorator, Decorator); // --- Widget --- -Element text(std::string text); -Element vtext(std::string text); +Element text(std::string_view text); +Element vtext(std::string_view text); Element separator(); Element separatorLight(); Element separatorDashed(); @@ -61,7 +62,7 @@ Element separatorDouble(); Element separatorEmpty(); Element separatorStyled(BorderStyle); Element separator(Pixel); -Element separatorCharacter(std::string); +Element separatorCharacter(std::string_view); Element separatorHSelector(float left, float right, Color unselected_color, @@ -89,11 +90,11 @@ Decorator borderStyled(Color); Decorator borderWith(const Pixel&); Element window(Element title, Element content, BorderStyle border = ROUNDED); Element spinner(int charset_index, size_t image_index); -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 paragraph(std::string_view text); +Element paragraphAlignLeft(std::string_view text); +Element paragraphAlignRight(std::string_view text); +Element paragraphAlignCenter(std::string_view text); +Element paragraphAlignJustify(std::string_view text); Element graph(GraphFunction); Element emptyElement(); Element canvas(ConstRef); @@ -120,8 +121,8 @@ Element bgcolor(const LinearGradient&, Element); Decorator focusPosition(int x, int y); Decorator focusPositionRelative(float x, float y); Element automerge(Element child); -Decorator hyperlink(std::string link); -Element hyperlink(std::string link, Element child); +Decorator hyperlink(std::string_view link); +Element hyperlink(std::string_view link, Element child); Element selectionStyleReset(Element); Decorator selectionColor(Color foreground); Decorator selectionBackgroundColor(Color foreground); diff --git a/include/ftxui/dom/selection.hpp b/include/ftxui/dom/selection.hpp index 7e7c6299e..1e098475c 100644 --- a/include/ftxui/dom/selection.hpp +++ b/include/ftxui/dom/selection.hpp @@ -30,7 +30,7 @@ class Selection { Selection SaturateVertical(Box box); bool IsEmpty() const { return empty_; } - void AddPart(const std::string& part, int y, int left, int right); + void AddPart(std::string_view part, int y, int left, int right); std::string GetParts() { return parts_.str(); } private: diff --git a/include/ftxui/screen/screen.hpp b/include/ftxui/screen/screen.hpp index 27bca9936..6ad08cb5d 100644 --- a/include/ftxui/screen/screen.hpp +++ b/include/ftxui/screen/screen.hpp @@ -68,7 +68,7 @@ class Screen : public Image { // Store an hyperlink in the screen. Return the id of the hyperlink. The id is // used to identify the hyperlink when the user click on it. - uint8_t RegisterHyperlink(const std::string& link); + uint8_t RegisterHyperlink(std::string_view link); const std::string& Hyperlink(uint8_t id) const; using SelectionStyle = std::function; diff --git a/include/ftxui/screen/string.hpp b/include/ftxui/screen/string.hpp index ca5397bc1..02bd8163f 100644 --- a/include/ftxui/screen/string.hpp +++ b/include/ftxui/screen/string.hpp @@ -4,27 +4,32 @@ #ifndef FTXUI_SCREEN_STRING_HPP #define FTXUI_SCREEN_STRING_HPP -#include // for string, wstring, to_string -#include // for vector +#include // for string, wstring, to_string +#include // for string_view +#include // for vector namespace ftxui { -std::string to_string(const std::wstring& s); -std::wstring to_wstring(const std::string& s); +std::string to_string(std::wstring_view s); +std::wstring to_wstring(std::string_view s); template std::wstring to_wstring(T s) { - return to_wstring(std::to_string(s)); + return to_wstring(std::string_view(std::to_string(s))); +} +template <> +inline std::wstring to_wstring(const char* s) { + return to_wstring(std::string_view(s)); } -int string_width(const std::string&); +int string_width(std::string_view); // Split the string into a its glyphs. An empty one is inserted ater fullwidth // ones. -std::vector Utf8ToGlyphs(const std::string& input); +std::vector Utf8ToGlyphs(std::string_view input); // Map every cells drawn by |input| to their corresponding Glyphs. Half-size // Glyphs takes one cell, full-size Glyphs take two cells. -std::vector CellToGlyphIndex(const std::string& input); +std::vector CellToGlyphIndex(std::string_view input); } // namespace ftxui diff --git a/include/ftxui/util/ref.hpp b/include/ftxui/util/ref.hpp index 42a593814..d0a615a69 100644 --- a/include/ftxui/util/ref.hpp +++ b/include/ftxui/util/ref.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -125,13 +126,15 @@ class ConstStringListRef { Adapter& operator=(Adapter&&) = default; virtual ~Adapter() = default; virtual size_t size() const = 0; - virtual std::string operator[](size_t i) const = 0; + virtual std::string_view operator[](size_t i) const = 0; }; - using Variant = std::variant, // - const std::vector*, // - const std::vector*, // - Adapter*, // - std::unique_ptr // + using Variant = std::variant, // + const std::vector*, // + const std::vector, // + const std::vector*, // + const std::vector*, // + Adapter*, // + std::unique_ptr // >; ConstStringListRef() = default; @@ -149,6 +152,14 @@ class ConstStringListRef { { variant_ = std::make_shared(value); } + ConstStringListRef(std::vector value) // NOLINT + { + variant_ = std::make_shared(value); + } + ConstStringListRef(const std::vector* value) // NOLINT + { + variant_ = std::make_shared(value); + } ConstStringListRef(const std::vector* value) // NOLINT { variant_ = std::make_shared(value); @@ -169,7 +180,62 @@ class ConstStringListRef { } std::string operator[](size_t i) const { - return variant_ ? std::visit(IndexedGetter(i), *variant_) : ""; + if (!variant_) { + return ""; + } + auto& v = *variant_; + if (std::holds_alternative>(v)) { + return std::get>(v)[i]; + } + if (std::holds_alternative*>(v)) { + return (*std::get*>(v))[i]; + } + if (std::holds_alternative>(v)) { + return std::string(std::get>(v)[i]); + } + if (std::holds_alternative*>(v)) { + return std::string( + (*std::get*>(v))[i]); + } + if (std::holds_alternative*>(v)) { + return to_string((*std::get*>(v))[i]); + } + if (std::holds_alternative(v)) { + return std::string((*std::get(v))[i]); + } + if (std::holds_alternative>(v)) { + return std::string((*std::get>(v))[i]); + } + return ""; + } + + std::string_view at(size_t i) const { + if (!variant_) { + return ""; + } + auto& v = *variant_; + if (std::holds_alternative>(v)) { + return std::get>(v)[i]; + } + if (std::holds_alternative*>(v)) { + return (*std::get*>(v))[i]; + } + if (std::holds_alternative>(v)) { + return std::get>(v)[i]; + } + if (std::holds_alternative*>(v)) { + return (*std::get*>(v))[i]; + } + if (std::holds_alternative*>(v)) { + return {}; + } + if (std::holds_alternative(v)) { + return (*std::get(v))[i]; + } + if (std::holds_alternative>(v)) { + return (*std::get>(v))[i]; + } + return {}; } private: @@ -180,6 +246,12 @@ class ConstStringListRef { size_t operator()(const std::vector* v) const { return v->size(); } + size_t operator()(const std::vector& v) const { + return v.size(); + } + size_t operator()(const std::vector* v) const { + return v->size(); + } size_t operator()(const std::vector* v) const { return v->size(); } @@ -189,25 +261,6 @@ class ConstStringListRef { } }; - struct IndexedGetter { - IndexedGetter(size_t index) // NOLINT - : index_(index) {} - size_t index_; - std::string operator()(const std::vector& v) const { - return v[index_]; - } - std::string operator()(const std::vector* v) const { - return (*v)[index_]; - } - std::string operator()(const std::vector* v) const { - return to_string((*v)[index_]); - } - std::string operator()(const Adapter* v) const { return (*v)[index_]; } - std::string operator()(const std::unique_ptr& v) const { - return (*v)[index_]; - } - }; - std::shared_ptr variant_; }; diff --git a/src/ftxui/component/button.cpp b/src/ftxui/component/button.cpp index 7a3d20110..fc6be104d 100644 --- a/src/ftxui/component/button.cpp +++ b/src/ftxui/component/button.cpp @@ -48,7 +48,7 @@ class ButtonBase : public ComponentBase, public ButtonOption { } const EntryState state{ - *label, false, active, focused_or_hover, Index(), + std::string(*label), false, active, focused_or_hover, Index(), }; auto element = (transform ? transform : DefaultTransform) // diff --git a/src/ftxui/component/checkbox.cpp b/src/ftxui/component/checkbox.cpp index 41a4e9579..716c8255f 100644 --- a/src/ftxui/component/checkbox.cpp +++ b/src/ftxui/component/checkbox.cpp @@ -27,7 +27,7 @@ class CheckboxBase : public ComponentBase, public CheckboxOption { const bool is_focused = Focused(); const bool is_active = Active(); auto entry_state = EntryState{ - *label, *checked, is_active, is_focused || hovered_, -1, + std::string(*label), *checked, is_active, is_focused || hovered_, -1, }; auto element = (transform ? transform : CheckboxOption::Simple().transform)( entry_state); diff --git a/src/ftxui/component/event.cpp b/src/ftxui/component/event.cpp index dfcb6b27c..147f62736 100644 --- a/src/ftxui/component/event.cpp +++ b/src/ftxui/component/event.cpp @@ -25,9 +25,9 @@ namespace ftxui { /// @brief An event corresponding to a given typed character. /// @param input The character typed by the user. // static -Event Event::Character(std::string input) { +Event Event::Character(std::string_view input) { Event event; - event.input_ = std::move(input); + event.input_ = std::string(input); event.type_ = Type::Character; return event; } @@ -50,9 +50,9 @@ Event Event::Character(wchar_t c) { /// @param input The sequence of character send by the terminal. /// @param mouse The mouse state. // static -Event Event::Mouse(std::string input, struct Mouse mouse) { +Event Event::Mouse(std::string_view input, struct Mouse mouse) { Event event; - event.input_ = std::move(input); + event.input_ = std::string(input); event.type_ = Type::Mouse; event.data_.mouse = mouse; // NOLINT return event; @@ -60,9 +60,9 @@ Event Event::Mouse(std::string input, struct Mouse mouse) { /// @brief An event corresponding to a terminal DCS (Device Control String). // static -Event Event::CursorShape(std::string input, int shape) { +Event Event::CursorShape(std::string_view input, int shape) { Event event; - event.input_ = std::move(input); + event.input_ = std::string(input); event.type_ = Type::CursorShape; event.data_.cursor_shape = shape; // NOLINT return event; @@ -71,17 +71,17 @@ Event Event::CursorShape(std::string input, int shape) { /// @brief An custom event whose meaning is defined by the user of the library. /// @param input An arbitrary sequence of character defined by the developer. // static -Event Event::Special(std::string input) { +Event Event::Special(std::string_view input) { Event event; - event.input_ = std::move(input); + event.input_ = std::string(input); return event; } /// @internal // static -Event Event::CursorPosition(std::string input, int x, int y) { +Event Event::CursorPosition(std::string_view input, int x, int y) { Event event; - event.input_ = std::move(input); + event.input_ = std::string(input); event.type_ = Type::CursorPosition; event.data_.cursor = {x, y}; // NOLINT return event; @@ -292,12 +292,12 @@ const Event Event::ArrowLeftCtrl = Event::Special("\x1B[1;5D"); const Event Event::ArrowRightCtrl = Event::Special("\x1B[1;5C"); const Event Event::ArrowUpCtrl = Event::Special("\x1B[1;5A"); const Event Event::ArrowDownCtrl = Event::Special("\x1B[1;5B"); -const Event Event::Backspace = Event::Special({127}); +const Event Event::Backspace = Event::Special(std::string({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::Return = Event::Special(std::string({10})); +const Event Event::Tab = Event::Special(std::string({9})); +const Event Event::TabReverse = Event::Special(std::string({27, 91, 90})); // See https://invisible-island.net/xterm/xterm-function-keys.html // We follow xterm-new / vterm-xf86-v4 / mgt / screen @@ -315,11 +315,11 @@ const Event Event::F11 = Event::Special("\x1B[23~"); const Event Event::F12 = Event::Special("\x1B[24~"); const Event Event::Insert = Event::Special("\x1B[2~"); -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}); -const Event Event::Custom = Event::Special({0}); +const Event Event::Home = Event::Special(std::string({27, 91, 72})); +const Event Event::End = Event::Special(std::string({27, 91, 70})); +const Event Event::PageUp = Event::Special(std::string({27, 91, 53, 126})); +const Event Event::PageDown = Event::Special(std::string({27, 91, 54, 126})); +const Event Event::Custom = Event::Special(std::string({0})); const Event Event::a = Event::Character("a"); const Event Event::b = Event::Character("b"); diff --git a/src/ftxui/component/menu.cpp b/src/ftxui/component/menu.cpp index 413aa090e..e63076ecd 100644 --- a/src/ftxui/component/menu.cpp +++ b/src/ftxui/component/menu.cpp @@ -144,9 +144,8 @@ class MenuBase : public ComponentBase, public MenuOption { std::reverse(elements.begin(), elements.end()); } - const Element bar = IsHorizontal() - ? hbox(std::move(elements)) - : vbox(std::move(elements)); + const Element bar = + IsHorizontal() ? hbox(std::move(elements)) : vbox(std::move(elements)); if (!underline.enabled) { return bar | reflect(box_); @@ -623,7 +622,7 @@ Component MenuEntry(MenuEntryOption option) { UpdateAnimationTarget(); const EntryState state{ - label(), false, hovered_, is_focused, Index(), + std::string(label()), false, hovered_, is_focused, Index(), }; Element element = (transform ? transform : DefaultOptionTransform) // diff --git a/src/ftxui/component/screen_interactive.cpp b/src/ftxui/component/screen_interactive.cpp index e5207a141..8b7261e88 100644 --- a/src/ftxui/component/screen_interactive.cpp +++ b/src/ftxui/component/screen_interactive.cpp @@ -1054,7 +1054,7 @@ void ScreenInteractive::Signal(int signal) { } if (signal == SIGWINCH) { - Post(Event::Special({0})); + Post(Event::Special(std::string({0}))); return; } #endif diff --git a/src/ftxui/component/task_runner.hpp b/src/ftxui/component/task_runner.hpp index 9075ad1b5..11cf2fa73 100644 --- a/src/ftxui/component/task_runner.hpp +++ b/src/ftxui/component/task_runner.hpp @@ -21,8 +21,8 @@ class TaskRunner { auto PostTask(Task task) -> void; /// Schedules a task to be executed after a certain duration. - auto PostDelayedTask(Task task, - std::chrono::steady_clock::duration duration) -> void; + auto PostDelayedTask(Task task, std::chrono::steady_clock::duration duration) + -> void; /// Runs the tasks in the queue, return the delay until the next delayed task /// can be executed. diff --git a/src/ftxui/component/terminal_input_parser_test.cpp b/src/ftxui/component/terminal_input_parser_test.cpp index 831345916..f377bccf4 100644 --- a/src/ftxui/component/terminal_input_parser_test.cpp +++ b/src/ftxui/component/terminal_input_parser_test.cpp @@ -338,7 +338,7 @@ TEST(Event, Control) { EXPECT_TRUE(received_events.empty()); } else { EXPECT_EQ(1, received_events.size()); - EXPECT_EQ(received_events[0], Event::Special({test.input})); + EXPECT_EQ(received_events[0], Event::Special(std::string({test.input}))); } } } diff --git a/src/ftxui/dom/canvas.cpp b/src/ftxui/dom/canvas.cpp index ef726721b..02c6b255b 100644 --- a/src/ftxui/dom/canvas.cpp +++ b/src/ftxui/dom/canvas.cpp @@ -782,7 +782,7 @@ void Canvas::DrawBlockEllipseFilled(int x1, /// @param x the x coordinate of the text. /// @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) { +void Canvas::DrawText(int x, int y, std::string_view value) { DrawText(x, y, value, nostyle); } @@ -793,7 +793,7 @@ void Canvas::DrawText(int x, int y, const std::string& value) { /// @param color the color of the text. void Canvas::DrawText(int x, int y, - const std::string& value, + std::string_view value, const Color& color) { DrawText(x, y, value, [color](Pixel& p) { p.foreground_color = color; }); } @@ -805,7 +805,7 @@ void Canvas::DrawText(int x, /// @param style the style of the text. void Canvas::DrawText(int x, int y, - const std::string& value, + std::string_view value, const Stylizer& style) { for (const auto& it : Utf8ToGlyphs(value)) { if (!IsIn(x, y)) { diff --git a/src/ftxui/dom/hbox_test.cpp b/src/ftxui/dom/hbox_test.cpp index 36132786c..8c07dd9e8 100644 --- a/src/ftxui/dom/hbox_test.cpp +++ b/src/ftxui/dom/hbox_test.cpp @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be found in // the LICENSE file. #include // for Test, TestInfo (ptr only), EXPECT_EQ, Message, TEST, TestPartResult -#include // for array -#include // for size_t +#include // for array +#include // for size_t #include #include // for stack #include // for allocator, basic_string, string diff --git a/src/ftxui/dom/hyperlink.cpp b/src/ftxui/dom/hyperlink.cpp index 8ec4c15d8..c738546d5 100644 --- a/src/ftxui/dom/hyperlink.cpp +++ b/src/ftxui/dom/hyperlink.cpp @@ -48,8 +48,8 @@ class Hyperlink : public NodeDecorator { /// Element document = /// hyperlink("https://github.com/ArthurSonzogni/FTXUI", "link"); /// ``` -Element hyperlink(std::string link, Element child) { - return std::make_shared(std::move(child), std::move(link)); +Element hyperlink(std::string_view link, Element child) { + return std::make_shared(std::move(child), std::string(link)); } /// @brief Decorate using a hyperlink. @@ -67,8 +67,10 @@ Element hyperlink(std::string link, Element child) { /// text("red") | hyperlink("https://github.com/Arthursonzogni/FTXUI"); /// ``` // NOLINTNEXTLINE -Decorator hyperlink(std::string link) { - return [link](Element child) { return hyperlink(link, std::move(child)); }; +Decorator hyperlink(std::string_view link) { + return [link = std::string(link)](Element child) { + return hyperlink(link, std::move(child)); + }; } } // namespace ftxui diff --git a/src/ftxui/dom/paragraph.cpp b/src/ftxui/dom/paragraph.cpp index 4d2c154c4..62780bc4d 100644 --- a/src/ftxui/dom/paragraph.cpp +++ b/src/ftxui/dom/paragraph.cpp @@ -38,7 +38,7 @@ Element Split(const std::string& paragraph, /// @brief Return an element drawing the paragraph on multiple lines. /// @ingroup dom /// @see flexbox. -Element paragraph(const std::string& the_text) { +Element paragraph(std::string_view the_text) { return paragraphAlignLeft(the_text); } @@ -46,8 +46,8 @@ Element paragraph(const std::string& the_text) { /// the left. /// @ingroup dom /// @see flexbox. -Element paragraphAlignLeft(const std::string& the_text) { - return Split(the_text, [](const std::string& line) { +Element paragraphAlignLeft(std::string_view the_text) { + return Split(std::string(the_text), [](const std::string& line) { static const auto config = FlexboxConfig().SetGap(1, 0); return flexbox(Split(line), config); }); @@ -57,8 +57,8 @@ Element paragraphAlignLeft(const std::string& the_text) { /// the right. /// @ingroup dom /// @see flexbox. -Element paragraphAlignRight(const std::string& the_text) { - return Split(the_text, [](const std::string& line) { +Element paragraphAlignRight(std::string_view the_text) { + return Split(std::string(the_text), [](const std::string& line) { static const auto config = FlexboxConfig().SetGap(1, 0).Set( FlexboxConfig::JustifyContent::FlexEnd); return flexbox(Split(line), config); @@ -69,8 +69,8 @@ Element paragraphAlignRight(const std::string& the_text) { /// the center. /// @ingroup dom /// @see flexbox. -Element paragraphAlignCenter(const std::string& the_text) { - return Split(the_text, [](const std::string& line) { +Element paragraphAlignCenter(std::string_view the_text) { + return Split(std::string(the_text), [](const std::string& line) { static const auto config = FlexboxConfig().SetGap(1, 0).Set(FlexboxConfig::JustifyContent::Center); return flexbox(Split(line), config); @@ -82,8 +82,8 @@ Element paragraphAlignCenter(const std::string& the_text) { /// the center. /// @ingroup dom /// @see flexbox. -Element paragraphAlignJustify(const std::string& the_text) { - return Split(the_text, [](const std::string& line) { +Element paragraphAlignJustify(std::string_view the_text) { + return Split(std::string(the_text), [](const std::string& line) { static const auto config = FlexboxConfig().SetGap(1, 0).Set( FlexboxConfig::JustifyContent::SpaceBetween); Elements words = Split(line); diff --git a/src/ftxui/dom/selection.cpp b/src/ftxui/dom/selection.cpp index ff55fc414..7eee0b560 100644 --- a/src/ftxui/dom/selection.cpp +++ b/src/ftxui/dom/selection.cpp @@ -143,7 +143,7 @@ Selection Selection::SaturateVertical(Box box) { return {start_x, start_y, end_x, end_y, parent_}; } -void Selection::AddPart(const std::string& part, int y, int left, int right) { +void Selection::AddPart(std::string_view part, int y, int left, int right) { if (parent_ != this) { parent_->AddPart(part, y, left, right); return; diff --git a/src/ftxui/dom/separator.cpp b/src/ftxui/dom/separator.cpp index 13dbb4144..83d8549b9 100644 --- a/src/ftxui/dom/separator.cpp +++ b/src/ftxui/dom/separator.cpp @@ -392,8 +392,8 @@ Element separatorEmpty() { /// ──── /// down /// ``` -Element separatorCharacter(std::string value) { - return std::make_shared(std::move(value)); +Element separatorCharacter(std::string_view value) { + return std::make_shared(std::string(value)); } /// @brief Draw a separator in between two element filled with a given pixel. diff --git a/src/ftxui/dom/table_test.cpp b/src/ftxui/dom/table_test.cpp index bae224bb9..9e0b02646 100644 --- a/src/ftxui/dom/table_test.cpp +++ b/src/ftxui/dom/table_test.cpp @@ -25,12 +25,12 @@ TEST(TableTest, Empty) { } TEST(TableTest, Basic) { - auto table = Table({ + auto table = Table(std::initializer_list>({ {"a", "b", "c", "d"}, {"e", "f", "g", "h"}, {"i", "j", "k", "l"}, {"m", "n", "o", "p"}, - }); + })); Screen screen(10, 10); Render(screen, table.Render()); EXPECT_EQ( diff --git a/src/ftxui/dom/text.cpp b/src/ftxui/dom/text.cpp index 4b7a483c2..7a44008e7 100644 --- a/src/ftxui/dom/text.cpp +++ b/src/ftxui/dom/text.cpp @@ -4,8 +4,9 @@ #include // for min #include // for make_shared #include -#include // for string, wstring -#include // for move +#include // for string, wstring +#include // for string_view +#include // for move #include "ftxui/dom/deprecated.hpp" // for text, vtext #include "ftxui/dom/elements.hpp" // for Element, text, vtext @@ -24,6 +25,7 @@ using ftxui::Screen; class Text : public Node { public: explicit Text(std::string text) : text_(std::move(text)) {} + explicit Text(std::string_view sv) : Text(std::string(sv)) {} void ComputeRequirement() override { requirement_.min_x = string_width(text_); @@ -96,6 +98,8 @@ class VText : public Node { explicit VText(std::string text) : text_(std::move(text)), width_{std::min(string_width(text_), 1)} {} + explicit VText(std::string_view sv) : VText(std::string(sv)) {} + void ComputeRequirement() override { requirement_.min_x = width_; requirement_.min_y = string_width(text_); @@ -138,8 +142,8 @@ class VText : public Node { /// ```bash /// Hello world! /// ``` -Element text(std::string text) { - return std::make_shared(std::move(text)); +Element text(std::string_view text) { + return std::make_shared(std::string(text)); } /// @brief Display a piece of unicode text. @@ -161,6 +165,25 @@ Element text(std::wstring text) { // NOLINT return std::make_shared(to_string(text)); } +/// @brief Display a piece of unicode text. +/// @ingroup dom +/// @see ftxui::to_wstring +/// +/// ### Example +/// +/// ```cpp +/// Element document = text(L"Hello world!"); +/// ``` +/// +/// ### Output +/// +/// ```bash +/// Hello world! +/// ``` +Element text(std::wstring_view sv) { + return text(std::wstring(sv)); +} + /// @brief Display a piece of unicode text vertically. /// @ingroup dom /// @see ftxui::to_wstring @@ -187,8 +210,8 @@ Element text(std::wstring text) { // NOLINT /// d /// ! /// ``` -Element vtext(std::string text) { - return std::make_shared(std::move(text)); +Element vtext(std::string_view text) { + return std::make_shared(std::string(text)); } /// @brief Display a piece unicode text vertically. @@ -221,4 +244,34 @@ Element vtext(std::wstring text) { // NOLINT return std::make_shared(to_string(text)); } +/// @brief Display a piece unicode text vertically. +/// @ingroup dom +/// @see ftxui::to_wstring +/// +/// ### Example +/// +/// ```cpp +/// Element document = vtext(L"Hello world!"); +/// ``` +/// +/// ### Output +/// +/// ```bash +/// H +/// e +/// l +/// l +/// o +/// +/// w +/// o +/// r +/// l +/// d +/// ! +/// ``` +Element vtext(std::wstring_view text) { // NOLINT + return vtext(std::wstring(text)); +} + } // namespace ftxui diff --git a/src/ftxui/dom/text_test.cpp b/src/ftxui/dom/text_test.cpp index f5364afbb..926b99c62 100644 --- a/src/ftxui/dom/text_test.cpp +++ b/src/ftxui/dom/text_test.cpp @@ -2,7 +2,8 @@ // Use of this source code is governed by the MIT license that can be found in // the LICENSE file. #include -#include // for allocator, string +#include // for allocator, string +#include // for string_view #include "ftxui/dom/elements.hpp" // for text, operator|, border, Element #include "ftxui/dom/node.hpp" // for Render @@ -121,5 +122,13 @@ TEST(TextTest, CombiningCharactersWithSpace) { EXPECT_EQ(t, screen.ToString()); } +TEST(TextTest, WithStringViews) { + const std::string_view t = "Hello, world!"; + auto element = text(t); + Screen screen(13, 1); + Render(screen, element); + EXPECT_EQ(t, screen.ToString()); +} + } // namespace ftxui // NOLINTEND diff --git a/src/ftxui/screen/screen.cpp b/src/ftxui/screen/screen.cpp index d95b4fa78..fa06a6597 100644 --- a/src/ftxui/screen/screen.cpp +++ b/src/ftxui/screen/screen.cpp @@ -531,7 +531,7 @@ void Screen::ApplyShader() { } // clang-format on -std::uint8_t Screen::RegisterHyperlink(const std::string& link) { +std::uint8_t Screen::RegisterHyperlink(std::string_view link) { for (std::size_t i = 0; i < hyperlinks_.size(); ++i) { if (hyperlinks_[i] == link) { return i; @@ -540,7 +540,7 @@ std::uint8_t Screen::RegisterHyperlink(const std::string& link) { if (hyperlinks_.size() == std::numeric_limits::max()) { return 0; } - hyperlinks_.push_back(link); + hyperlinks_.push_back(std::string(link)); return hyperlinks_.size() - 1; } diff --git a/src/ftxui/screen/string.cpp b/src/ftxui/screen/string.cpp index 750872f21..b69e7e3c8 100644 --- a/src/ftxui/screen/string.cpp +++ b/src/ftxui/screen/string.cpp @@ -1171,7 +1171,7 @@ namespace ftxui { // one codepoint. Put the codepoint into |ucs|. Start at |start| and update // |end| to represent the beginning of the next byte to eat for consecutive // executions. -bool EatCodePoint(const std::string& input, +bool EatCodePoint(std::string_view input, size_t start, size_t* end, uint32_t* ucs) { @@ -1241,7 +1241,7 @@ bool EatCodePoint(const std::string& input, // one codepoint. Put the codepoint into |ucs|. Start at |start| and update // |end| to represent the beginning of the next byte to eat for consecutive // executions. -bool EatCodePoint(const std::wstring& input, +bool EatCodePoint(std::wstring_view input, size_t start, size_t* end, uint32_t* ucs) { @@ -1328,7 +1328,7 @@ int wstring_width(const std::wstring& text) { return width; } -int string_width(const std::string& input) { +int string_width(std::string_view input) { int width = 0; size_t start = 0; while (start < input.size()) { @@ -1355,7 +1355,7 @@ int string_width(const std::string& input) { return width; } -std::vector Utf8ToGlyphs(const std::string& input) { +std::vector Utf8ToGlyphs(std::string_view input) { std::vector out; out.reserve(input.size()); size_t start = 0; @@ -1367,7 +1367,7 @@ std::vector Utf8ToGlyphs(const std::string& input) { continue; } - const std::string append = input.substr(start, end - start); + const auto append = input.substr(start, end - start); start = end; // Ignore control characters. @@ -1386,18 +1386,18 @@ std::vector Utf8ToGlyphs(const std::string& input) { // Fullwidth characters take two cells. The second is made of the empty // string to reserve the space the first is taking. if (IsFullWidth(codepoint)) { - out.push_back(append); + out.push_back(std::string(append)); out.emplace_back(""); continue; } // Normal characters: - out.push_back(append); + out.push_back(std::string(append)); } return out; } -size_t GlyphPrevious(const std::string& input, size_t start) { +size_t GlyphPrevious(std::string_view input, size_t start) { while (true) { if (start == 0) { return 0; @@ -1422,7 +1422,7 @@ size_t GlyphPrevious(const std::string& input, size_t start) { } } -size_t GlyphNext(const std::string& input, size_t start) { +size_t GlyphNext(std::string_view input, size_t start) { bool glyph_found = false; while (start < input.size()) { size_t end = 0; @@ -1448,7 +1448,7 @@ size_t GlyphNext(const std::string& input, size_t start) { return static_cast(input.size()); } -size_t GlyphIterate(const std::string& input, int glyph_offset, size_t start) { +size_t GlyphIterate(std::string_view input, int glyph_offset, size_t start) { if (glyph_offset >= 0) { for (int i = 0; i < glyph_offset; ++i) { start = GlyphNext(input, start); @@ -1462,7 +1462,7 @@ size_t GlyphIterate(const std::string& input, int glyph_offset, size_t start) { } } -std::vector CellToGlyphIndex(const std::string& input) { +std::vector CellToGlyphIndex(std::string_view input) { int x = -1; std::vector out; out.reserve(input.size()); @@ -1503,7 +1503,7 @@ std::vector CellToGlyphIndex(const std::string& input) { return out; } -int GlyphCount(const std::string& input) { +int GlyphCount(std::string_view input) { int size = 0; size_t start = 0; size_t end = 0; @@ -1531,8 +1531,7 @@ int GlyphCount(const std::string& input) { return size; } -std::vector Utf8ToWordBreakProperty( - const std::string& input) { +std::vector Utf8ToWordBreakProperty(std::string_view input) { std::vector out; out.reserve(input.size()); size_t start = 0; @@ -1563,7 +1562,7 @@ std::vector Utf8ToWordBreakProperty( } /// Convert a std::wstring into a UTF8 std::string. -std::string to_string(const std::wstring& s) { +std::string to_string(std::wstring_view s) { std::string out; size_t i = 0; @@ -1635,7 +1634,7 @@ std::string to_string(const std::wstring& s) { } /// Convert a UTF8 std::string into a std::wstring. -std::wstring to_wstring(const std::string& s) { +std::wstring to_wstring(std::string_view s) { std::wstring out; size_t i = 0; diff --git a/src/ftxui/screen/string_internal.hpp b/src/ftxui/screen/string_internal.hpp index 048f32781..876d9364e 100644 --- a/src/ftxui/screen/string_internal.hpp +++ b/src/ftxui/screen/string_internal.hpp @@ -10,11 +10,11 @@ namespace ftxui { -bool EatCodePoint(const std::string& input, +bool EatCodePoint(std::string_view input, size_t start, size_t* end, uint32_t* ucs); -bool EatCodePoint(const std::wstring& input, +bool EatCodePoint(std::wstring_view input, size_t start, size_t* end, uint32_t* ucs); @@ -23,17 +23,15 @@ bool IsCombining(uint32_t ucs); bool IsFullWidth(uint32_t ucs); bool IsControl(uint32_t ucs); -size_t GlyphPrevious(const std::string& input, size_t start); -size_t GlyphNext(const std::string& input, size_t start); +size_t GlyphPrevious(std::string_view input, size_t start); +size_t GlyphNext(std::string_view input, size_t start); // Return the index in the |input| string of the glyph at |glyph_offset|, // starting at |start| -size_t GlyphIterate(const std::string& input, - int glyph_offset, - size_t start = 0); +size_t GlyphIterate(std::string_view input, int glyph_offset, size_t start = 0); // Returns the number of glyphs in |input|. -int GlyphCount(const std::string& input); +int GlyphCount(std::string_view input); // Properties from: // https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/WordBreakProperty.txt @@ -58,10 +56,9 @@ enum class WordBreakProperty : int8_t { ZWJ, }; WordBreakProperty CodepointToWordBreakProperty(uint32_t codepoint); -std::vector Utf8ToWordBreakProperty( - const std::string& input); +std::vector Utf8ToWordBreakProperty(std::string_view input); -bool IsWordBreakingCharacter(const std::string& input, size_t glyph_index); +bool IsWordBreakingCharacter(std::string_view input, size_t glyph_index); } // namespace ftxui #endif /* end of include guard: FTXUI_SCREEN_STRING_INTERNAL_HPP */ diff --git a/src/ftxui/screen/string_test.cpp b/src/ftxui/screen/string_test.cpp index ee8a50052..4cd2f466e 100644 --- a/src/ftxui/screen/string_test.cpp +++ b/src/ftxui/screen/string_test.cpp @@ -154,14 +154,14 @@ TEST(StringTest, to_string) { } TEST(StringTest, to_wstring) { - EXPECT_EQ(to_wstring(std::string("hello")), L"hello"); - EXPECT_EQ(to_wstring(std::string("€")), L"€"); - EXPECT_EQ(to_wstring(std::string("ÿ")), L"ÿ"); - EXPECT_EQ(to_wstring(std::string("߿")), L"߿"); - EXPECT_EQ(to_wstring(std::string("ɰɱ")), L"ɰɱ"); - EXPECT_EQ(to_wstring(std::string("«»")), L"«»"); - EXPECT_EQ(to_wstring(std::string("嵰嵲嵫")), L"嵰嵲嵫"); - EXPECT_EQ(to_wstring(std::string("🎅🎄")), L"🎅🎄"); + EXPECT_EQ(to_wstring("hello"), L"hello"); + EXPECT_EQ(to_wstring("€"), L"€"); + EXPECT_EQ(to_wstring("ÿ"), L"ÿ"); + EXPECT_EQ(to_wstring("߿"), L"߿"); + EXPECT_EQ(to_wstring("ɰɱ"), L"ɰɱ"); + EXPECT_EQ(to_wstring("«»"), L"«»"); + EXPECT_EQ(to_wstring("嵰嵲嵫"), L"嵰嵲嵫"); + EXPECT_EQ(to_wstring("🎅🎄"), L"🎅🎄"); } } // namespace ftxui diff --git a/src/ftxui/util/ref_test.cpp b/src/ftxui/util/ref_test.cpp index de9e1015a..50186c7eb 100644 --- a/src/ftxui/util/ref_test.cpp +++ b/src/ftxui/util/ref_test.cpp @@ -13,7 +13,7 @@ class Adapter : public ConstStringListRef::Adapter { public: Adapter(std::vector& entries) : entries(entries) {} size_t size() const override { return entries.size() * 2; } - std::string operator[](size_t index) const override { + std::string_view operator[](size_t index) const override { return entries[index / 2]; } std::vector& entries;