mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2025-09-18 09:08:08 +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:
@@ -1,6 +1,6 @@
|
||||
#include <stdint.h> // for uint32_t
|
||||
#include <algorithm> // for max, min
|
||||
#include <cstddef> // for size_t
|
||||
#include <cstdint> // for uint32_t
|
||||
#include <functional> // for function
|
||||
#include <memory> // for allocator, shared_ptr, allocator_traits<>::value_type
|
||||
#include <sstream> // for basic_istream, stringstream
|
||||
|
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.
|
||||
|
Reference in New Issue
Block a user