Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Selection2 the come back #926

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
75abd75
We have a basic decorator
clement-roblot Aug 27, 2024
1325256
We can catch mouse events
clement-roblot Aug 27, 2024
cb2436f
We can act on the screen
clement-roblot Aug 27, 2024
2339ead
It somewhat works
clement-roblot Aug 27, 2024
7af4446
Cleanup
clement-roblot Aug 27, 2024
eef0c3d
More cleanup
clement-roblot Aug 27, 2024
b352e13
Reformat + fix pending selection.
ArthurSonzogni Aug 31, 2024
011b9a1
Make use of Box instead of my custom Region struct
clement-roblot Oct 31, 2024
23a8c94
Implementation of the selection in the text node
clement-roblot Oct 31, 2024
195871e
Dirty wrap around implementation
clement-roblot Oct 31, 2024
c9fbd8d
Wrap around both directions
clement-roblot Oct 31, 2024
92586f7
More intuitive selection of text
clement-roblot Oct 31, 2024
437439c
Reverse selection are now possible
clement-roblot Oct 31, 2024
3e9bab4
Select only if the selection starts in my text widget
clement-roblot Nov 6, 2024
5ecac2e
Start the tree-aware selection.
ArthurSonzogni Nov 13, 2024
3d670e2
Easier to read HandleSelection
clement-roblot Nov 26, 2024
6c7a92c
Corrected typo
clement-roblot Nov 26, 2024
5c4a5b8
We can reverse select
clement-roblot Nov 26, 2024
b4c498d
Correct the title highlighting of hbox grabing to top border
clement-roblot Nov 26, 2024
fa742c7
More robust saturation mechanism
clement-roblot Nov 26, 2024
c1c4caf
Support for inverted selections.
ArthurSonzogni Dec 1, 2024
07202b7
Cleanup
ArthurSonzogni Dec 1, 2024
70a6a04
Fix build issues.
ArthurSonzogni Dec 1, 2024
dc70091
Added a callback on selection change
clement-roblot Dec 2, 2024
307d8b5
We can retrieve the content of the selection
clement-roblot Dec 2, 2024
d62dc6e
Added a decorator to prevent selectability
clement-roblot Dec 2, 2024
cbd2840
Added a option to set the style of the selection
clement-roblot Dec 2, 2024
f454810
Added a basic test
clement-roblot Dec 3, 2024
b4717f9
Added more tests
clement-roblot Dec 3, 2024
f345209
VBox testing
clement-roblot Dec 3, 2024
f21cfd4
Added more tests
clement-roblot Dec 3, 2024
d755ab3
More forgiving selection options
clement-roblot Dec 3, 2024
9f9effa
Add recursive selection style.
ArthurSonzogni Dec 22, 2024
fe7d67a
Fix + support for indpendant styles.
ArthurSonzogni Dec 23, 2024
a3cc6df
Fix tests.
ArthurSonzogni Dec 25, 2024
25476f7
Fix bug with "flex" element not settings their box.
ArthurSonzogni Dec 25, 2024
4c1d30b
Add Changelog.
ArthurSonzogni Dec 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ current (development)
- Feature: Add support for `Input`'s insert mode. Add `InputOption::insert`
option. Added by @mingsheng13.
- Feature: Add `DropdownOption` to configure the dropdown. See #826.
- Feature: Add support for Selection. Thanks @clement-roblot. See #926.
- See `ScreenInteractive::GetSelection()`.
- See `ScreenInteractive::SelectionChange(...)` listener.
- Bugfix/Breaking change: `Mouse transition`:
- Detect when the mouse move, as opposed to being pressed.
The Mouse::Moved motion was added.
Expand Down Expand Up @@ -49,6 +52,12 @@ current (development)
- Feature: Add `extend_beyond_screen` option to `Dimension::Fit(..)`, allowing
the element to be larger than the screen. Proposed by @LordWhiro. See #572 and
#949.
- Feature: Add support for Selection. Thanks @clement-roblot. See #926.
- See `selectionColor` decorator.
- See `selectionBackgroundColor` decorator.
- See `selectionForegroundColor` decorator.
- See `selectionStyle(style)` decorator.
- See `selectionStyleReset` decorator.

### Screen
- Feature: Add `Box::IsEmpty()`.
Expand Down
5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@ add_library(dom
include/ftxui/dom/flexbox_config.hpp
include/ftxui/dom/node.hpp
include/ftxui/dom/requirement.hpp
include/ftxui/dom/selection.hpp
include/ftxui/dom/take_any_args.hpp
src/ftxui/dom/automerge.cpp
src/ftxui/dom/selection_style.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
Expand All @@ -81,13 +82,15 @@ add_library(dom
src/ftxui/dom/graph.cpp
src/ftxui/dom/gridbox.cpp
src/ftxui/dom/hbox.cpp
src/ftxui/dom/hyperlink.cpp
src/ftxui/dom/inverted.cpp
src/ftxui/dom/linear_gradient.cpp
src/ftxui/dom/node.cpp
src/ftxui/dom/node_decorator.cpp
src/ftxui/dom/paragraph.cpp
src/ftxui/dom/reflect.cpp
src/ftxui/dom/scroll_indicator.cpp
src/ftxui/dom/selection.cpp
src/ftxui/dom/separator.cpp
src/ftxui/dom/size.cpp
src/ftxui/dom/spinner.cpp
Expand Down
1 change: 1 addition & 0 deletions cmake/ftxui_test.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ add_executable(ftxui-tests
src/ftxui/dom/hyperlink_test.cpp
src/ftxui/dom/linear_gradient_test.cpp
src/ftxui/dom/scroll_indicator_test.cpp
src/ftxui/dom/selection_test.cpp
src/ftxui/dom/separator_test.cpp
src/ftxui/dom/spinner_test.cpp
src/ftxui/dom/table_test.cpp
Expand Down
1 change: 1 addition & 0 deletions examples/component/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ example(radiobox)
example(radiobox_in_frame)
example(renderer)
example(resizable_split)
example(selection)
example(scrollbar)
example(slider)
example(slider_direction)
Expand Down
23 changes: 22 additions & 1 deletion examples/component/gallery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,25 @@ int main() {
});
sliders = Wrap("Slider", sliders);

// -- Layout -----------------------------------------------------------------
// A large text:
auto lorel_ipsum = Renderer([] {
return vbox({
text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. "),
text("Sed do eiusmod tempor incididunt ut labore et dolore magna "
"aliqua. "),
text("Ut enim ad minim veniam, quis nostrud exercitation ullamco "
"laboris nisi ut aliquip ex ea commodo consequat. "),
text("Duis aute irure dolor in reprehenderit in voluptate velit esse "
"cillum dolore eu fugiat nulla pariatur. "),
text("Excepteur sint occaecat cupidatat non proident, sunt in culpa "
"qui officia deserunt mollit anim id est laborum. "),

});
});
lorel_ipsum = Wrap("Lorel Ipsum", lorel_ipsum);

// -- Layout
// -----------------------------------------------------------------
auto layout = Container::Vertical({
menu,
toggle,
Expand All @@ -106,6 +124,7 @@ int main() {
input,
sliders,
button,
lorel_ipsum,
});

auto component = Renderer(layout, [&] {
Expand All @@ -123,6 +142,8 @@ int main() {
sliders->Render(),
separator(),
button->Render(),
separator(),
lorel_ipsum->Render(),
}) |
xflex | size(WIDTH, GREATER_THAN, 40) | border;
});
Expand Down
2 changes: 1 addition & 1 deletion examples/component/homescreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ int main() {
auto paragraph_renderer_left = Renderer([&] {
std::string str =
"Lorem Ipsum is simply dummy text of the printing and typesetting "
"industry. Lorem Ipsum has been the industry's standard dummy text "
"industry.\nLorem Ipsum has been the industry's standard dummy text "
"ever since the 1500s, when an unknown printer took a galley of type "
"and scrambled it to make a type specimen book.";
return vbox({
Expand Down
87 changes: 87 additions & 0 deletions examples/component/selection.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// 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.
#include <string> // for char_traits, operator+, string, basic_string

#include "ftxui/component/component.hpp" // for Input, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for InputOption
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for text, hbox, separator, Element, operator|, vbox, border
#include "ftxui/util/ref.hpp" // for Ref

using namespace ftxui;

Element LoremIpsum() {
return vbox({
text("FTXUI: A powerful library for building user interfaces."),
text("Enjoy a rich set of components and a declarative style."),
text("Create beautiful and responsive UIs with minimal effort."),
text("Join the community and experience the power of FTXUI."),
});
}

int main() {
auto screen = ScreenInteractive::TerminalOutput();

auto quit =
Button("Quit", screen.ExitLoopClosure(), ButtonOption::Animated());

int selection_change_counter = 0;
std::string selection_content = "";
screen.SelectionChange([&] {
selection_change_counter++;
selection_content = screen.GetSelection();
});

// The components:
auto renderer = Renderer(quit, [&] {
return vbox({
text("Select changed: " + std::to_string(selection_change_counter) +
" times"),
text("Currently selected: "),
paragraph(selection_content) | vscroll_indicator | frame | border |
size(HEIGHT, EQUAL, 10),
window(text("Horizontal split"), hbox({
LoremIpsum(),
separator(),
LoremIpsum(),
separator(),
LoremIpsum(),
})),
window(text("Vertical split"), vbox({
LoremIpsum(),
separator(),
LoremIpsum(),
separator(),
LoremIpsum(),
})),
window(text("Grid split with different style"),
vbox({
hbox({
LoremIpsum(),
separator(),
LoremIpsum() //
| selectionBackgroundColor(Color::Yellow) //
| selectionColor(Color::Black) //
| selectionStyleReset,
separator(),
LoremIpsum() | selectionColor(Color::Blue),
}),
separator(),
hbox({
LoremIpsum() | selectionColor(Color::Red),
separator(),
LoremIpsum() | selectionStyle([](Pixel& pixel) {
pixel.underlined_double = true;
}),
separator(),
LoremIpsum(),
}),
})),
quit->Render(),
});
});

screen.Loop(renderer);
}
23 changes: 23 additions & 0 deletions include/ftxui/component/screen_interactive.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/task.hpp" // for Task, Closure
#include "ftxui/dom/selection.hpp" // for SelectionOption
#include "ftxui/screen/screen.hpp" // for Screen

namespace ftxui {
Expand Down Expand Up @@ -68,6 +69,10 @@ class ScreenInteractive : public Screen {
void ForceHandleCtrlC(bool force);
void ForceHandleCtrlZ(bool force);

// Selection API.
std::string GetSelection();
void SelectionChange(std::function<void()> callback);

private:
void ExitNow();

Expand All @@ -82,6 +87,8 @@ class ScreenInteractive : public Screen {
void RunOnceBlocking(Component component);

void HandleTask(Component component, Task& task);
bool HandleSelection(bool handled, Event event);
void RefreshSelection();
void Draw(Component component);
void ResetCursorPosition();

Expand Down Expand Up @@ -129,6 +136,22 @@ class ScreenInteractive : public Screen {
// The style of the cursor to restore on exit.
int cursor_reset_shape_ = 1;

// Selection API:
CapturedMouse selection_pending_;
struct SelectionData {
int start_x = -1;
int start_y = -1;
int end_x = -2;
int end_y = -2;
bool empty = true;
bool operator==(const SelectionData& other) const;
bool operator!=(const SelectionData& other) const;
};
SelectionData selection_data_;
SelectionData selection_data_previous_;
std::unique_ptr<Selection> selection_;
std::function<void()> selection_on_change_;

friend class Loop;

public:
Expand Down
5 changes: 5 additions & 0 deletions include/ftxui/dom/elements.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ Decorator focusPositionRelative(float x, float y);
Element automerge(Element child);
Decorator hyperlink(std::string link);
Element hyperlink(std::string link, Element child);
Element selectionStyleReset(Element);
Decorator selectionColor(Color foreground);
Decorator selectionBackgroundColor(Color foreground);
Decorator selectionForegroundColor(Color foreground);
Decorator selectionStyle(std::function<void(Pixel&)> style);

// --- Layout is
// Horizontal, Vertical or stacked set of elements.
Expand Down
13 changes: 12 additions & 1 deletion include/ftxui/dom/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <vector> // for vector

#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/dom/selection.hpp" // for Selection
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp"

Expand Down Expand Up @@ -40,9 +41,15 @@ class Node {
// Propagated from Parents to Children.
virtual void SetBox(Box box);

// Step 3: Draw this element.
// Step 3: (optional) Selection
// Propagated from Parents to Children.
virtual void Select(Selection& selection);

// Step 4: Draw this element.
virtual void Render(Screen& screen);

virtual std::string GetSelectedContent(Selection& selection);

// Layout may not resolve within a single iteration for some elements. This
// allows them to request additionnal iterations. This signal must be
// forwarded to children at least once.
Expand All @@ -60,6 +67,10 @@ class Node {

void Render(Screen& screen, const Element& element);
void Render(Screen& screen, Node* node);
void Render(Screen& screen, Node* node, Selection& selection);
std::string GetNodeSelectedContent(Screen& screen,
Node* node,
Selection& selection);

} // namespace ftxui

Expand Down
50 changes: 50 additions & 0 deletions include/ftxui/dom/selection.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2024 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.

#ifndef FTXUI_DOM_SELECTION_HPP
#define FTXUI_DOM_SELECTION_HPP

#include <functional>

#include <sstream>
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/pixel.hpp" // for Pixel

namespace ftxui {

/// @brief Represent a selection in the terminal.
class Selection {
public:
Selection(); // Empty selection.
Selection(int start_x, int start_y, int end_x, int end_y);

const Box& GetBox() const;

Selection SaturateHorizontal(Box box);
Selection SaturateVertical(Box box);
bool IsEmpty() const { return empty_; }

void AddPart(const std::string& part, int y, int left, int right);
std::string GetParts() { return parts_.str(); }

private:
Selection(int start_x, int start_y, int end_x, int end_y, Selection* parent);

Selection* const parent_ = this;
const bool empty_ = true;
const int start_x_ = 0;
const int start_y_ = 0;
const int end_x_ = 0;
const int end_y_ = 0;
const Box box_ = {};
std::stringstream parts_;

// The position of the last inserted part.
int x_ = 0;
int y_ = 0;
};

} // namespace ftxui

#endif /* end of include guard: FTXUI_DOM_SELECTION_HPP */
17 changes: 14 additions & 3 deletions include/ftxui/screen/screen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
#ifndef FTXUI_SCREEN_SCREEN_HPP
#define FTXUI_SCREEN_SCREEN_HPP

#include <cstdint> // for uint8_t
#include <string> // for string, basic_string, allocator
#include <vector> // for vector
#include <cstdint> // for uint8_t
#include <functional> // for function
#include <string> // for string, basic_string, allocator
#include <vector> // for vector

#include "ftxui/screen/image.hpp" // for Pixel, Image
#include "ftxui/screen/terminal.hpp" // for Dimensions
#include "ftxui/util/autoreset.hpp" // for AutoReset

namespace ftxui {

Expand Down Expand Up @@ -67,9 +69,18 @@ class Screen : public Image {
uint8_t RegisterHyperlink(const std::string& link);
const std::string& Hyperlink(uint8_t id) const;

using SelectionStyle = std::function<void(Pixel&)>;
const SelectionStyle& GetSelectionStyle() const;
void SetSelectionStyle(SelectionStyle decorator);

protected:
Cursor cursor_;
std::vector<std::string> hyperlinks_ = {""};

// The current selection style. This is overridden by various dom elements.
SelectionStyle selection_style_ = [](Pixel& pixel) {
pixel.inverted ^= true;
};
};

} // namespace ftxui
Expand Down
Loading