mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-25 04:08:39 +08:00
Feature: hyperlink
support. (#665)
See the [OSC 8 page](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda). FTXUI support proposed by @aaleino in [#662](https://github.com/ArthurSonzogni/FTXUI/issues/662). API: ```cpp auto link = text("Click here") | hyperlink("https://github.com/FTXUI") ``` Fixed:https://github.com/ArthurSonzogni/FTXUI/issues/662
This commit is contained in:
parent
1651ae56ca
commit
46042ce74a
20
CHANGELOG.md
20
CHANGELOG.md
@ -1,6 +1,26 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
4.2
|
||||
---
|
||||
|
||||
### Component
|
||||
|
||||
### Dom
|
||||
- Feature: Add `hyperlink` decorator. For instance:
|
||||
```cpp
|
||||
auto link = text("Click here") | hyperlink("https://github.com/FTXUI")
|
||||
```
|
||||
See the [OSC 8 page](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda).
|
||||
FTXUI support proposed by @aaleino in [#662](https://github.com/ArthurSonzogni/FTXUI/issues/662).
|
||||
|
||||
|
||||
### Build
|
||||
- Check version compatibility when using cmake find_package()
|
||||
- Add `FTXUI_DEV_WARNING` options to turn on warnings when building FTXUI
|
||||
- Turn OFF by default `FTXUI_BUILD_DOCS`
|
||||
- Turn OFF by default `FTXUI_BUILD_EXAMPLE`
|
||||
|
||||
4.1.1
|
||||
-----
|
||||
|
||||
|
@ -52,6 +52,7 @@ add_library(dom
|
||||
src/ftxui/dom/automerge.cpp
|
||||
src/ftxui/dom/blink.cpp
|
||||
src/ftxui/dom/bold.cpp
|
||||
src/ftxui/dom/hyperlink.cpp
|
||||
src/ftxui/dom/border.cpp
|
||||
src/ftxui/dom/box_helper.cpp
|
||||
src/ftxui/dom/box_helper.hpp
|
||||
|
@ -127,6 +127,7 @@ An element can be decorated using the functions:
|
||||
- `strikethrough`
|
||||
- `color`
|
||||
- `bgcolor`
|
||||
- `hyperlink`
|
||||
|
||||
[Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2style_gallery_8cpp-example.html)
|
||||
|
||||
|
@ -36,6 +36,7 @@ add_executable(ftxui-tests
|
||||
src/ftxui/dom/gauge_test.cpp
|
||||
src/ftxui/dom/gridbox_test.cpp
|
||||
src/ftxui/dom/hbox_test.cpp
|
||||
src/ftxui/dom/hyperlink_test.cpp
|
||||
src/ftxui/dom/linear_gradient_test.cpp
|
||||
src/ftxui/dom/scroll_indicator_test.cpp
|
||||
src/ftxui/dom/separator_test.cpp
|
||||
|
@ -27,6 +27,7 @@ example(style_bold)
|
||||
example(style_color)
|
||||
example(style_dim)
|
||||
example(style_gallery)
|
||||
example(style_hyperlink)
|
||||
example(style_inverted)
|
||||
example(style_strikethrough)
|
||||
example(style_underlined)
|
||||
|
@ -19,7 +19,8 @@ int main() {
|
||||
text("blink") | blink , text(" ") ,
|
||||
text("strikethrough") | strikethrough , text(" ") ,
|
||||
text("color") | color(Color::Blue) , text(" ") ,
|
||||
text("bgcolor") | bgcolor(Color::Blue) ,
|
||||
text("bgcolor") | bgcolor(Color::Blue) , text(" ") ,
|
||||
text("hyperlink") | hyperlink("https://github.com/ArthurSonzogni/FTXUI"),
|
||||
});
|
||||
// clang-format on
|
||||
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
|
||||
|
25
examples/dom/style_hyperlink.cpp
Normal file
25
examples/dom/style_hyperlink.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include <ftxui/dom/elements.hpp> // for text, operator|, bold, Fit, hbox, Element
|
||||
#include <ftxui/screen/screen.hpp> // for Full, Screen
|
||||
#include <memory> // for allocator
|
||||
|
||||
#include "ftxui/dom/node.hpp" // for Render
|
||||
#include "ftxui/screen/color.hpp" // for ftxui
|
||||
|
||||
int main() {
|
||||
using namespace ftxui;
|
||||
auto document = //
|
||||
hbox({
|
||||
text("This text is an "),
|
||||
text("hyperlink") | hyperlink("https://www.google.com"),
|
||||
text(". Do you like it?"),
|
||||
});
|
||||
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
|
||||
Render(screen, document);
|
||||
screen.Print();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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.
|
@ -110,6 +110,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);
|
||||
|
||||
// --- Layout is
|
||||
// Horizontal, Vertical or stacked set of elements.
|
||||
|
@ -1,8 +1,9 @@
|
||||
#ifndef FTXUI_SCREEN_SCREEN_HPP
|
||||
#define FTXUI_SCREEN_SCREEN_HPP
|
||||
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <memory>
|
||||
#include <string> // for string, allocator, basic_string
|
||||
#include <string> // for string, basic_string, allocator
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ftxui/screen/box.hpp" // for Box
|
||||
@ -20,6 +21,10 @@ struct Pixel {
|
||||
// like: a⃦, this can potentially contains multiple codepoitns.
|
||||
std::string character = " ";
|
||||
|
||||
// The hyperlink associated with the pixel.
|
||||
// 0 is the default value, meaning no hyperlink.
|
||||
uint8_t hyperlink = 0;
|
||||
|
||||
// Colors:
|
||||
Color background_color = Color::Default;
|
||||
Color foreground_color = Color::Default;
|
||||
@ -99,6 +104,11 @@ class Screen {
|
||||
Cursor cursor() const { return cursor_; }
|
||||
void SetCursor(Cursor cursor) { cursor_ = cursor; }
|
||||
|
||||
// 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(std::string link);
|
||||
const std::string& Hyperlink(uint8_t id) const;
|
||||
|
||||
Box stencil;
|
||||
|
||||
protected:
|
||||
@ -106,6 +116,7 @@ class Screen {
|
||||
int dimy_;
|
||||
std::vector<std::vector<Pixel>> pixels_;
|
||||
Cursor cursor_;
|
||||
std::vector<std::string> hyperlinks_ = {""};
|
||||
};
|
||||
|
||||
} // namespace ftxui
|
||||
|
26
iwyu.imp
26
iwyu.imp
@ -14,6 +14,32 @@
|
||||
{ include: ["<gtest/gtest-printers.h>", "private", "<gtest/gtest.h>", "public" ] },
|
||||
{ include: ["<gtest/gtest-test-part.h>", "private", "<gtest/gtest.h>", "public" ] },
|
||||
{ include: ["<gtest/gtest-typed-test.h>", "private", "<gtest/gtest.h>", "public" ] },
|
||||
{ include: ["<assert.h>", "private", "<cassert>", "public" ] },
|
||||
{ include: ["<complex.h>", "private", "<ccomplex>", "public" ] },
|
||||
{ include: ["<ctype.h>", "private", "<cctype>", "public" ] },
|
||||
{ include: ["<errno.h>", "private", "<cerrno>", "public" ] },
|
||||
{ include: ["<fenv.h>", "private", "<cfenv>", "public" ] },
|
||||
{ include: ["<float.h>", "private", "<cfloat>", "public" ] },
|
||||
{ include: ["<inttypes.h>", "private", "<cinttypes>", "public" ] },
|
||||
{ include: ["<iso646.h>", "private", "<ciso646>", "public" ] },
|
||||
{ include: ["<limits.h>", "private", "<climits>", "public" ] },
|
||||
{ include: ["<locale.h>", "private", "<clocale>", "public" ] },
|
||||
{ include: ["<math.h>", "private", "<cmath>", "public" ] },
|
||||
{ include: ["<setjmp.h>", "private", "<csetjmp>", "public" ] },
|
||||
{ include: ["<signal.h>", "private", "<csignal>", "public" ] },
|
||||
{ include: ["<stdalign.h>", "private", "<cstdalign>", "public" ] },
|
||||
{ include: ["<stdarg.h>", "private", "<cstdarg>", "public" ] },
|
||||
{ include: ["<stdbool.h>", "private", "<cstdbool>", "public" ] },
|
||||
{ include: ["<stddef.h>", "private", "<cstddef>", "public" ] },
|
||||
{ include: ["<stdint.h>", "private", "<cstdint>", "public" ] },
|
||||
{ include: ["<stdio.h>", "private", "<cstdio>", "public" ] },
|
||||
{ include: ["<stdlib.h>", "private", "<cstdlib>", "public" ] },
|
||||
{ include: ["<string.h>", "private", "<cstring>", "public" ] },
|
||||
{ include: ["<tgmath.h>", "private", "<ctgmath>", "public" ] },
|
||||
{ include: ["<time.h>", "private", "<ctime>", "public" ] },
|
||||
{ include: ["<uchar.h>", "private", "<cuchar>", "public" ] },
|
||||
{ include: ["<wchar.h>", "private", "<cwchar>", "public" ] },
|
||||
{ include: ["<wctype.h>", "private", "<cwctype>", "public" ] },
|
||||
{ symbol: ["ftxui", "private", "", "public" ] },
|
||||
{ symbol: ["char_traits", "private", "<string>", "public" ] },
|
||||
{ symbol: ["ECHO", "private", "<termios.h>", "public" ] },
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <algorithm> // for max, min
|
||||
#include <cstddef> // for size_t
|
||||
#include <cstdint> // for uint32_t
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr
|
||||
#include <string> // for string, allocator
|
||||
|
72
src/ftxui/dom/hyperlink.cpp
Normal file
72
src/ftxui/dom/hyperlink.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <memory> // for make_shared
|
||||
#include <string> // for string
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/dom/elements.hpp" // for Element, Decorator, hyperlink
|
||||
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
|
||||
#include "ftxui/screen/box.hpp" // for Box
|
||||
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
class Hyperlink : public NodeDecorator {
|
||||
public:
|
||||
Hyperlink(Element child, std::string link)
|
||||
: NodeDecorator(std::move(child)), link_(link) {}
|
||||
|
||||
void Render(Screen& screen) override {
|
||||
uint8_t hyperlink_id = screen.RegisterHyperlink(link_);
|
||||
for (int y = box_.y_min; y <= box_.y_max; ++y) {
|
||||
for (int x = box_.x_min; x <= box_.x_max; ++x) {
|
||||
screen.PixelAt(x, y).hyperlink = hyperlink_id;
|
||||
}
|
||||
}
|
||||
NodeDecorator::Render(screen);
|
||||
}
|
||||
|
||||
std::string link_;
|
||||
};
|
||||
|
||||
/// @brief Make the rendered area clickable using a web browser.
|
||||
/// The link will be opened when the user click on it.
|
||||
/// This is supported only on a limited set of terminal emulator.
|
||||
/// List: https://github.com/Alhadis/OSC8-Adoption/
|
||||
/// @param link The link
|
||||
/// @param child The input element.
|
||||
/// @return The output element with the link.
|
||||
/// @ingroup dom
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// Element document =
|
||||
/// hyperlink("https://github.com/ArthurSonzogni/FTXUI", "link");
|
||||
/// ```
|
||||
Element hyperlink(std::string link, Element child) {
|
||||
return std::make_shared<Hyperlink>(std::move(child), link);
|
||||
}
|
||||
|
||||
/// @brief Decorate using an hyperlink.
|
||||
/// The link will be opened when the user click on it.
|
||||
/// This is supported only on a limited set of terminal emulator.
|
||||
/// List: https://github.com/Alhadis/OSC8-Adoption/
|
||||
/// @param link The link to redirect the users to.
|
||||
/// @return The Decorator applying the hyperlink.
|
||||
/// @ingroup dom
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// Element document =
|
||||
/// text("red") | hyperlink("https://github.com/Arthursonzogni/FTXUI");
|
||||
/// ```
|
||||
Decorator hyperlink(std::string link) {
|
||||
return [link](Element child) { return hyperlink(link, std::move(child)); };
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
// Copyright 2023 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
// the LICENSE file.
|
43
src/ftxui/dom/hyperlink_test.cpp
Normal file
43
src/ftxui/dom/hyperlink_test.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include <gtest/gtest.h> // for Test, EXPECT_EQ, Message, TestPartResult, TestInfo (ptr only), TEST
|
||||
#include <string> // for allocator, string
|
||||
|
||||
#include "ftxui/dom/elements.hpp" // for text, hyperlink, operator|, Element, hbox
|
||||
#include "ftxui/dom/node.hpp" // for Render
|
||||
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
TEST(HyperlinkTest, Basic) {
|
||||
auto element = hbox({
|
||||
text("text 1") | hyperlink("https://a.com"),
|
||||
text("text 2") | hyperlink("https://b.com"),
|
||||
text("text 3"),
|
||||
text("text 4") | hyperlink("https://c.com"),
|
||||
});
|
||||
|
||||
Screen screen(6 * 4, 1);
|
||||
Render(screen, element);
|
||||
|
||||
EXPECT_EQ(screen.PixelAt(0, 0).hyperlink, 1u);
|
||||
EXPECT_EQ(screen.PixelAt(5, 0).hyperlink, 1u);
|
||||
EXPECT_EQ(screen.PixelAt(6, 0).hyperlink, 2u);
|
||||
EXPECT_EQ(screen.PixelAt(11, 0).hyperlink, 2u);
|
||||
|
||||
std::string output = screen.ToString();
|
||||
EXPECT_EQ(output,
|
||||
"\x1B]8;;https://a.com\x1B\\"
|
||||
"text 1"
|
||||
"\x1B]8;;https://b.com\x1B\\"
|
||||
"text 2"
|
||||
"\x1B]8;;\x1B\\"
|
||||
"text 3"
|
||||
"\x1B]8;;https://c.com\x1B\\"
|
||||
"text 4"
|
||||
"\x1B]8;;\x1B\\");
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
// Copyright 2023 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
// the LICENSE file.
|
@ -1,7 +1,7 @@
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <cstdint> // for size_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
|
||||
#include <memory> // for allocator, allocator_traits<>::value_type
|
||||
#include <sstream> // IWYU pragma: keep
|
||||
#include <utility> // for pair
|
||||
|
||||
@ -50,11 +50,13 @@ void WindowsEmulateVT100Terminal() {
|
||||
#endif
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void UpdatePixelStyle(std::stringstream& ss,
|
||||
void UpdatePixelStyle(const Screen* screen,
|
||||
std::stringstream& ss,
|
||||
Pixel& previous,
|
||||
const Pixel& next) {
|
||||
if (next == previous) {
|
||||
return;
|
||||
// See https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
|
||||
if (next.hyperlink != previous.hyperlink) {
|
||||
ss << "\x1B]8;;" << screen->Hyperlink(next.hyperlink) << "\x1B\\";
|
||||
}
|
||||
|
||||
if ((!next.bold && previous.bold) || //
|
||||
@ -435,20 +437,20 @@ std::string Screen::ToString() {
|
||||
|
||||
for (int y = 0; y < dimy_; ++y) {
|
||||
if (y != 0) {
|
||||
UpdatePixelStyle(ss, previous_pixel, final_pixel);
|
||||
UpdatePixelStyle(this, ss, previous_pixel, final_pixel);
|
||||
ss << "\r\n";
|
||||
}
|
||||
bool previous_fullwidth = false;
|
||||
for (const auto& pixel : pixels_[y]) {
|
||||
if (!previous_fullwidth) {
|
||||
UpdatePixelStyle(ss, previous_pixel, pixel);
|
||||
UpdatePixelStyle(this, ss, previous_pixel, pixel);
|
||||
ss << pixel.character;
|
||||
}
|
||||
previous_fullwidth = (string_width(pixel.character) == 2);
|
||||
}
|
||||
}
|
||||
|
||||
UpdatePixelStyle(ss, previous_pixel, final_pixel);
|
||||
UpdatePixelStyle(this, ss, previous_pixel, final_pixel);
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
@ -517,6 +519,10 @@ void Screen::Clear() {
|
||||
}
|
||||
cursor_.x = dimx_ - 1;
|
||||
cursor_.y = dimy_ - 1;
|
||||
|
||||
hyperlinks_ = {
|
||||
"",
|
||||
};
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
@ -545,9 +551,28 @@ void Screen::ApplyShader() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
uint8_t Screen::RegisterHyperlink(std::string link) {
|
||||
for (size_t i = 0; i < hyperlinks_.size(); ++i) {
|
||||
if (hyperlinks_[i] == link) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if (hyperlinks_.size() == 255) {
|
||||
return 0;
|
||||
}
|
||||
hyperlinks_.push_back(link);
|
||||
return hyperlinks_.size() - 1;
|
||||
}
|
||||
|
||||
const std::string& Screen::Hyperlink(uint8_t id) const {
|
||||
if (id >= hyperlinks_.size()) {
|
||||
return hyperlinks_[0];
|
||||
}
|
||||
return hyperlinks_[id];
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
|
Loading…
Reference in New Issue
Block a user