mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-22 18:59:59 +08:00
Support multiple kind of cursor shapes. (#505)
https://github.com/ArthurSonzogni/FTXUI/issues/424
This commit is contained in:
parent
9babfea36b
commit
1689802349
@ -5,6 +5,13 @@ current (development)
|
|||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
### DOM
|
### DOM
|
||||||
|
- Feature: Customize the cursor. Add the following decorators:
|
||||||
|
- `focusCursorBlock`
|
||||||
|
- `focusCursorBlockBlinking`
|
||||||
|
- `focusCursorBar`
|
||||||
|
- `focusCursorBarBlinking`
|
||||||
|
- `focusCursorUnderline`
|
||||||
|
- `focusCursorUnderlineBlinking`
|
||||||
- Bugfix: Fix `focus`/`select` when the `vbox`/`hbox`/`dbox` contains a
|
- Bugfix: Fix `focus`/`select` when the `vbox`/`hbox`/`dbox` contains a
|
||||||
`flexbox`
|
`flexbox`
|
||||||
- Bugfix: Fix the selected/focused area. It used to be 1 cell larger/longer than
|
- Bugfix: Fix the selected/focused area. It used to be 1 cell larger/longer than
|
||||||
@ -25,6 +32,7 @@ current (development)
|
|||||||
can be used to integrate FTXUI into another main loop, without taking the full
|
can be used to integrate FTXUI into another main loop, without taking the full
|
||||||
control.
|
control.
|
||||||
- Feature: `Input` supports CTRL+Left and CTRL+Right
|
- Feature: `Input` supports CTRL+Left and CTRL+Right
|
||||||
|
- Feature: Use a blinking bar in the `Input` component.
|
||||||
- Improvement: The `Menu` keeps the focus when an entry is selected with the
|
- Improvement: The `Menu` keeps the focus when an entry is selected with the
|
||||||
mouse.
|
mouse.
|
||||||
- Bugfix: Add implementation of `ButtonOption::Border()`. It was missing.
|
- Bugfix: Add implementation of `ButtonOption::Border()`. It was missing.
|
||||||
|
@ -13,6 +13,7 @@ example(custom_loop)
|
|||||||
example(dropdown)
|
example(dropdown)
|
||||||
example(flexbox_gallery)
|
example(flexbox_gallery)
|
||||||
example(focus)
|
example(focus)
|
||||||
|
example(focus_cursor)
|
||||||
example(gallery)
|
example(gallery)
|
||||||
example(homescreen)
|
example(homescreen)
|
||||||
example(input)
|
example(input)
|
||||||
|
39
examples/component/focus_cursor.cpp
Normal file
39
examples/component/focus_cursor.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include <memory> // for allocator, shared_ptr, __shared_ptr_access
|
||||||
|
#include <string> // for operator+, char_traits, to_string, string
|
||||||
|
#include <vector> // for vector
|
||||||
|
|
||||||
|
#include "ftxui/component/component.hpp" // for Slider, Renderer, Vertical
|
||||||
|
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
|
||||||
|
#include "ftxui/dom/elements.hpp" // for Elements, Element, operator|, separator, text, focusPositionRelative, size, border, flex, frame, bgcolor, gridbox, vbox, EQUAL, center, HEIGHT, WIDTH
|
||||||
|
|
||||||
|
using namespace ftxui;
|
||||||
|
|
||||||
|
Component Instance(std::string label, Decorator focusCursor) {
|
||||||
|
return Renderer([=](bool focused) {
|
||||||
|
if (focused) {
|
||||||
|
return hbox({
|
||||||
|
text("> " + label + " "),
|
||||||
|
focusCursor(text(" ")),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return text(" " + label + " ");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, const char* argv[]) {
|
||||||
|
auto screen = ScreenInteractive::Fullscreen();
|
||||||
|
screen.Loop(Container::Vertical({
|
||||||
|
Instance("focus", focus),
|
||||||
|
Instance("focusCursorBlock", focusCursorBlock),
|
||||||
|
Instance("focusCursorBlockBlinking", focusCursorBlockBlinking),
|
||||||
|
Instance("focusCursorBar", focusCursorBar),
|
||||||
|
Instance("focusCursorBarBlinking", focusCursorBarBlinking),
|
||||||
|
Instance("focusCursorUnderline", focusCursorUnderline),
|
||||||
|
Instance("focusCursorUnderlineBlinking", focusCursorUnderlineBlinking),
|
||||||
|
}));
|
||||||
|
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.
|
@ -126,9 +126,6 @@ enum Direction { WIDTH, HEIGHT };
|
|||||||
enum Constraint { LESS_THAN, EQUAL, GREATER_THAN };
|
enum Constraint { LESS_THAN, EQUAL, GREATER_THAN };
|
||||||
Decorator size(Direction, Constraint, int value);
|
Decorator size(Direction, Constraint, int value);
|
||||||
|
|
||||||
// --
|
|
||||||
Decorator reflect(Box& box);
|
|
||||||
|
|
||||||
// --- Frame ---
|
// --- Frame ---
|
||||||
// A frame is a scrollable area. The internal area is potentially larger than
|
// A frame is a scrollable area. The internal area is potentially larger than
|
||||||
// the external one. The internal area is scrolled in order to make visible the
|
// the external one. The internal area is scrolled in order to make visible the
|
||||||
@ -139,7 +136,21 @@ Element yframe(Element);
|
|||||||
Element focus(Element);
|
Element focus(Element);
|
||||||
Element select(Element);
|
Element select(Element);
|
||||||
|
|
||||||
|
// --- Cursor ---
|
||||||
|
// Those are similar to `focus`, but also change the shape of the cursor.
|
||||||
|
Element focusCursorBlock(Element);
|
||||||
|
Element focusCursorBlockBlinking(Element);
|
||||||
|
Element focusCursorBar(Element);
|
||||||
|
Element focusCursorBarBlinking(Element);
|
||||||
|
Element focusCursorUnderline(Element);
|
||||||
|
Element focusCursorUnderlineBlinking(Element);
|
||||||
|
|
||||||
|
// --- Misc ---
|
||||||
Element vscroll_indicator(Element);
|
Element vscroll_indicator(Element);
|
||||||
|
Decorator reflect(Box& box);
|
||||||
|
// Before drawing the |element| clear the pixel below. This is useful in
|
||||||
|
// combinaison with dbox.
|
||||||
|
Element clear_under(Element element);
|
||||||
|
|
||||||
// --- Util --------------------------------------------------------------------
|
// --- Util --------------------------------------------------------------------
|
||||||
Element hcenter(Element);
|
Element hcenter(Element);
|
||||||
@ -148,10 +159,6 @@ Element center(Element);
|
|||||||
Element align_right(Element);
|
Element align_right(Element);
|
||||||
Element nothing(Element element);
|
Element nothing(Element element);
|
||||||
|
|
||||||
// Before drawing the |element| clear the pixel below. This is useful in
|
|
||||||
// combinaison with dbox.
|
|
||||||
Element clear_under(Element element);
|
|
||||||
|
|
||||||
namespace Dimension {
|
namespace Dimension {
|
||||||
Dimensions Fit(Element&);
|
Dimensions Fit(Element&);
|
||||||
} // namespace Dimension
|
} // namespace Dimension
|
||||||
|
@ -80,6 +80,17 @@ class Screen {
|
|||||||
struct Cursor {
|
struct Cursor {
|
||||||
int x = 0;
|
int x = 0;
|
||||||
int y = 0;
|
int y = 0;
|
||||||
|
|
||||||
|
enum Shape {
|
||||||
|
Hidden = 0,
|
||||||
|
BlockBlinking = 1,
|
||||||
|
Block = 2,
|
||||||
|
UnderlineBlinking = 3,
|
||||||
|
Underline = 4,
|
||||||
|
Bar = 5,
|
||||||
|
BarBlinking = 6,
|
||||||
|
};
|
||||||
|
Shape shape;
|
||||||
};
|
};
|
||||||
Cursor cursor() const { return cursor_; }
|
Cursor cursor() const { return cursor_; }
|
||||||
void SetCursor(Cursor cursor) { cursor_ = cursor; }
|
void SetCursor(Cursor cursor) { cursor_ = cursor; }
|
||||||
@ -91,8 +102,6 @@ class Screen {
|
|||||||
int dimy_;
|
int dimy_;
|
||||||
std::vector<std::vector<Pixel>> pixels_;
|
std::vector<std::vector<Pixel>> pixels_;
|
||||||
Cursor cursor_;
|
Cursor cursor_;
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ftxui
|
} // namespace ftxui
|
||||||
|
@ -96,24 +96,23 @@ class InputBase : public ComponentBase {
|
|||||||
|
|
||||||
// placeholder.
|
// placeholder.
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
bool hovered = hovered_;
|
auto element = text(*placeholder_) | dim | main_decorator | reflect(box_);
|
||||||
Decorator decorator = dim | main_decorator;
|
|
||||||
if (is_focused) {
|
if (is_focused) {
|
||||||
decorator = decorator | focus;
|
element |= focus;
|
||||||
}
|
}
|
||||||
if (hovered || is_focused) {
|
if (hovered_|| is_focused) {
|
||||||
decorator = decorator | inverted;
|
element |= inverted;
|
||||||
}
|
}
|
||||||
return text(*placeholder_) | decorator | reflect(box_);
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not focused.
|
// Not focused.
|
||||||
if (!is_focused) {
|
if (!is_focused) {
|
||||||
|
auto element = text(content) | main_decorator | reflect(box_);
|
||||||
if (hovered_) {
|
if (hovered_) {
|
||||||
return text(content) | main_decorator | inverted | reflect(box_);
|
element |= inverted;
|
||||||
} else {
|
|
||||||
return text(content) | main_decorator | reflect(box_);
|
|
||||||
}
|
}
|
||||||
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
int index_before_cursor = GlyphPosition(content, cursor_position());
|
int index_before_cursor = GlyphPosition(content, cursor_position());
|
||||||
@ -125,10 +124,10 @@ class InputBase : public ComponentBase {
|
|||||||
index_after_cursor - index_before_cursor);
|
index_after_cursor - index_before_cursor);
|
||||||
}
|
}
|
||||||
std::string part_after_cursor = content.substr(index_after_cursor);
|
std::string part_after_cursor = content.substr(index_after_cursor);
|
||||||
auto focused = (is_focused || hovered_) ? focus : select;
|
auto focused = (is_focused || hovered_) ? focusCursorBarBlinking : select;
|
||||||
return hbox({
|
return hbox({
|
||||||
text(part_before_cursor),
|
text(part_before_cursor),
|
||||||
text(part_at_cursor) | focused | inverted | reflect(cursor_box_),
|
text(part_at_cursor) | focused | reflect(cursor_box_),
|
||||||
text(part_after_cursor),
|
text(part_after_cursor),
|
||||||
}) |
|
}) |
|
||||||
flex | frame | bold | main_decorator | reflect(box_);
|
flex | frame | bold | main_decorator | reflect(box_);
|
||||||
|
@ -512,8 +512,13 @@ void ScreenInteractive::Install() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
on_exit_functions.push([=] {
|
||||||
|
std::cout << "\033[?25h"; // Enable cursor.
|
||||||
|
std::cout << "\033[?1 q"; // Cursor block blinking.
|
||||||
|
});
|
||||||
|
|
||||||
disable({
|
disable({
|
||||||
DECMode::kCursor,
|
//DECMode::kCursor,
|
||||||
DECMode::kLineWrap,
|
DECMode::kLineWrap,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -685,16 +690,26 @@ void ScreenInteractive::Draw(Component component) {
|
|||||||
set_cursor_position = "";
|
set_cursor_position = "";
|
||||||
reset_cursor_position = "";
|
reset_cursor_position = "";
|
||||||
|
|
||||||
int dx = dimx_ - 1 - cursor_.x;
|
{
|
||||||
int dy = dimy_ - 1 - cursor_.y;
|
int dx = dimx_ - 1 - cursor_.x;
|
||||||
|
int dy = dimy_ - 1 - cursor_.y;
|
||||||
|
|
||||||
if (dx != 0) {
|
if (dy != 0) {
|
||||||
set_cursor_position += "\x1B[" + std::to_string(dx) + "D";
|
set_cursor_position += "\x1B[" + std::to_string(dy) + "A";
|
||||||
reset_cursor_position += "\x1B[" + std::to_string(dx) + "C";
|
reset_cursor_position += "\x1B[" + std::to_string(dy) + "B";
|
||||||
}
|
}
|
||||||
if (dy != 0) {
|
|
||||||
set_cursor_position += "\x1B[" + std::to_string(dy) + "A";
|
if (dx != 0) {
|
||||||
reset_cursor_position += "\x1B[" + std::to_string(dy) + "B";
|
set_cursor_position += "\x1B[" + std::to_string(dx) + "D";
|
||||||
|
reset_cursor_position += "\x1B[" + std::to_string(dx) + "C";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor_.shape == Cursor::Hidden) {
|
||||||
|
set_cursor_position += "\033[?25l";
|
||||||
|
} else {
|
||||||
|
set_cursor_position += "\033[?25h";
|
||||||
|
set_cursor_position += "\033[" + std::to_string(int(cursor_.shape)) + " q";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << ToString() << set_cursor_position;
|
std::cout << ToString() << set_cursor_position;
|
||||||
|
@ -71,7 +71,11 @@ class Focus : public Select {
|
|||||||
// https://github.com/microsoft/terminal/issues/1203
|
// https://github.com/microsoft/terminal/issues/1203
|
||||||
// https://github.com/microsoft/terminal/issues/3093
|
// https://github.com/microsoft/terminal/issues/3093
|
||||||
#if !defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
|
#if !defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
|
||||||
screen.SetCursor(Screen::Cursor{box_.x_min, box_.y_min});
|
screen.SetCursor(Screen::Cursor{
|
||||||
|
box_.x_min,
|
||||||
|
box_.y_min,
|
||||||
|
Screen::Cursor::Shape::Hidden,
|
||||||
|
});
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -147,6 +151,48 @@ Element yframe(Element child) {
|
|||||||
return std::make_shared<Frame>(unpack(std::move(child)), false, true);
|
return std::make_shared<Frame>(unpack(std::move(child)), false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FocusCursor : public Focus {
|
||||||
|
public:
|
||||||
|
FocusCursor(Elements children, Screen::Cursor::Shape shape)
|
||||||
|
: Focus(std::move(children)), shape_(shape) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Render(Screen& screen) override {
|
||||||
|
Select::Render(screen);
|
||||||
|
screen.SetCursor(Screen::Cursor{
|
||||||
|
box_.x_min,
|
||||||
|
box_.y_min,
|
||||||
|
shape_,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Screen::Cursor::Shape shape_;
|
||||||
|
};
|
||||||
|
|
||||||
|
Element focusCursorBlock(Element child) {
|
||||||
|
return std::make_shared<FocusCursor>(unpack(std::move(child)),
|
||||||
|
Screen::Cursor::Block);
|
||||||
|
}
|
||||||
|
Element focusCursorBlockBlinking(Element child) {
|
||||||
|
return std::make_shared<FocusCursor>(unpack(std::move(child)),
|
||||||
|
Screen::Cursor::BlockBlinking);
|
||||||
|
}
|
||||||
|
Element focusCursorBar(Element child) {
|
||||||
|
return std::make_shared<FocusCursor>(unpack(std::move(child)),
|
||||||
|
Screen::Cursor::Bar);
|
||||||
|
}
|
||||||
|
Element focusCursorBarBlinking(Element child) {
|
||||||
|
return std::make_shared<FocusCursor>(unpack(std::move(child)),
|
||||||
|
Screen::Cursor::BarBlinking);
|
||||||
|
}
|
||||||
|
Element focusCursorUnderline(Element child) {
|
||||||
|
return std::make_shared<FocusCursor>(unpack(std::move(child)),
|
||||||
|
Screen::Cursor::Underline);
|
||||||
|
}
|
||||||
|
Element focusCursorUnderlineBlinking(Element child) {
|
||||||
|
return std::make_shared<FocusCursor>(unpack(std::move(child)),
|
||||||
|
Screen::Cursor::UnderlineBlinking);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ftxui
|
} // namespace ftxui
|
||||||
|
|
||||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||||
|
Loading…
Reference in New Issue
Block a user