mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-22 10:40:00 +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
|
||||
- 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
|
||||
`flexbox`
|
||||
- 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
|
||||
control.
|
||||
- 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
|
||||
mouse.
|
||||
- Bugfix: Add implementation of `ButtonOption::Border()`. It was missing.
|
||||
|
@ -13,6 +13,7 @@ example(custom_loop)
|
||||
example(dropdown)
|
||||
example(flexbox_gallery)
|
||||
example(focus)
|
||||
example(focus_cursor)
|
||||
example(gallery)
|
||||
example(homescreen)
|
||||
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 };
|
||||
Decorator size(Direction, Constraint, int value);
|
||||
|
||||
// --
|
||||
Decorator reflect(Box& box);
|
||||
|
||||
// --- Frame ---
|
||||
// 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
|
||||
@ -139,7 +136,21 @@ Element yframe(Element);
|
||||
Element focus(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);
|
||||
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 --------------------------------------------------------------------
|
||||
Element hcenter(Element);
|
||||
@ -148,10 +159,6 @@ Element center(Element);
|
||||
Element align_right(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 {
|
||||
Dimensions Fit(Element&);
|
||||
} // namespace Dimension
|
||||
|
@ -80,6 +80,17 @@ class Screen {
|
||||
struct Cursor {
|
||||
int x = 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_; }
|
||||
void SetCursor(Cursor cursor) { cursor_ = cursor; }
|
||||
@ -91,8 +102,6 @@ class Screen {
|
||||
int dimy_;
|
||||
std::vector<std::vector<Pixel>> pixels_;
|
||||
Cursor cursor_;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace ftxui
|
||||
|
@ -96,24 +96,23 @@ class InputBase : public ComponentBase {
|
||||
|
||||
// placeholder.
|
||||
if (size == 0) {
|
||||
bool hovered = hovered_;
|
||||
Decorator decorator = dim | main_decorator;
|
||||
auto element = text(*placeholder_) | dim | main_decorator | reflect(box_);
|
||||
if (is_focused) {
|
||||
decorator = decorator | focus;
|
||||
element |= focus;
|
||||
}
|
||||
if (hovered || is_focused) {
|
||||
decorator = decorator | inverted;
|
||||
if (hovered_|| is_focused) {
|
||||
element |= inverted;
|
||||
}
|
||||
return text(*placeholder_) | decorator | reflect(box_);
|
||||
return element;
|
||||
}
|
||||
|
||||
// Not focused.
|
||||
if (!is_focused) {
|
||||
auto element = text(content) | main_decorator | reflect(box_);
|
||||
if (hovered_) {
|
||||
return text(content) | main_decorator | inverted | reflect(box_);
|
||||
} else {
|
||||
return text(content) | main_decorator | reflect(box_);
|
||||
}
|
||||
element |= inverted;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
int index_before_cursor = GlyphPosition(content, cursor_position());
|
||||
@ -125,10 +124,10 @@ class InputBase : public ComponentBase {
|
||||
index_after_cursor - index_before_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({
|
||||
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),
|
||||
}) |
|
||||
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({
|
||||
DECMode::kCursor,
|
||||
//DECMode::kCursor,
|
||||
DECMode::kLineWrap,
|
||||
});
|
||||
|
||||
@ -685,16 +690,26 @@ void ScreenInteractive::Draw(Component component) {
|
||||
set_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) {
|
||||
set_cursor_position += "\x1B[" + std::to_string(dx) + "D";
|
||||
reset_cursor_position += "\x1B[" + std::to_string(dx) + "C";
|
||||
}
|
||||
if (dy != 0) {
|
||||
set_cursor_position += "\x1B[" + std::to_string(dy) + "A";
|
||||
reset_cursor_position += "\x1B[" + std::to_string(dy) + "B";
|
||||
if (dy != 0) {
|
||||
set_cursor_position += "\x1B[" + std::to_string(dy) + "A";
|
||||
reset_cursor_position += "\x1B[" + std::to_string(dy) + "B";
|
||||
}
|
||||
|
||||
if (dx != 0) {
|
||||
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;
|
||||
|
@ -71,7 +71,11 @@ class Focus : public Select {
|
||||
// https://github.com/microsoft/terminal/issues/1203
|
||||
// https://github.com/microsoft/terminal/issues/3093
|
||||
#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
|
||||
}
|
||||
};
|
||||
@ -147,6 +151,48 @@ Element yframe(Element child) {
|
||||
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
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
|
Loading…
Reference in New Issue
Block a user