add 'line-numbers' property to textbox & make TextBox extend Container instead of Panel

This commit is contained in:
MihailRis 2024-11-19 04:21:05 +03:00
parent 9dc09e920c
commit 2cf51a6941
6 changed files with 135 additions and 25 deletions

View File

@ -104,6 +104,7 @@ Inner text - initially entered text
- `multiline` - allows display of multiline text.
- `text-wrap` - allows automatic text wrapping (works only with multiline: "true")
- `editable` - determines whether the text can be edited.
- `line-numbers` - enables line numbers display.
- `error-color` - color when entering incorrect data (the text does not pass the validator check). Type: RGBA color.
- `text-color` - text color. Type: RGBA color.
- `validator` - lua function that checks text for correctness. Takes a string as input, returns true if the text is correct.

View File

@ -105,6 +105,7 @@
- `multiline` - разрешает отображение многострочного текста.
- `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true")
- `editable`- определяет возможность редактирования текста.
- `line-numbers` - включает отображение номеров строк.
- `error-color` - цвет при вводе некорректных данных (текст не проходит проверку валидатора). Тип: RGBA цвет.
- `text-color` - цвет текста. Тип: RGBA цвет.
- `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен.

View File

@ -1,5 +1,6 @@
#include "TextBox.hpp"
#include <sstream>
#include <utility>
#include <algorithm>
@ -14,16 +15,30 @@
using namespace gui;
inline constexpr int LINE_NUMBERS_PANE_WIDTH = 40;
TextBox::TextBox(std::wstring placeholder, glm::vec4 padding)
: Panel(glm::vec2(200,32), padding, 0),
: Container(glm::vec2(200,32)),
padding(padding),
input(L""),
placeholder(std::move(placeholder))
{
setOnUpPressed(nullptr);
setOnDownPressed(nullptr);
setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.75f));
label = std::make_shared<Label>(L"");
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
label->setPos(glm::vec2(
padding.x + LINE_NUMBERS_PANE_WIDTH * showLineNumbers, padding.y
));
add(label);
lineNumbersLabel = std::make_shared<Label>(L"");
lineNumbersLabel->setMultiline(true);
lineNumbersLabel->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
lineNumbersLabel->setVerticalAlign(Align::top);
add(lineNumbersLabel);
setHoverColor(glm::vec4(0.05f, 0.1f, 0.2f, 0.75f));
textInitX = label->getPos().x;
@ -31,7 +46,7 @@ TextBox::TextBox(std::wstring placeholder, glm::vec4 padding)
}
void TextBox::draw(const DrawContext* pctx, Assets* assets) {
Panel::draw(pctx, assets);
Container::draw(pctx, assets);
font = assets->get<Font>(label->getFontName());
@ -76,6 +91,44 @@ void TextBox::draw(const DrawContext* pctx, Assets* assets) {
batch->rect(lcoord.x, lcoord.y+label->getLineYOffset(endLine), end, lineHeight);
}
}
if (isFocused() && multiline) {
auto selectionCtx = subctx.sub(batch);
selectionCtx.setBlendMode(BlendMode::addition);
batch->setColor(glm::vec4(1, 1, 1, 0.1f));
uint line = label->getLineByTextIndex(caret);
while (label->isFakeLine(line)) {
line--;
}
do {
int lineY = label->getLineYOffset(line);
int lineHeight = font->getLineHeight() * label->getLineInterval();
batch->setColor(glm::vec4(1, 1, 1, 0.05f));
if (showLineNumbers) {
batch->rect(
lcoord.x - 8,
lcoord.y + lineY,
label->getSize().x,
lineHeight
);
batch->setColor(glm::vec4(1, 1, 1, 0.10f));
batch->rect(
lcoord.x - LINE_NUMBERS_PANE_WIDTH,
lcoord.y + lineY,
LINE_NUMBERS_PANE_WIDTH - 8,
lineHeight
);
} else {
batch->rect(
lcoord.x, lcoord.y + lineY, label->getSize().x, lineHeight
);
}
line++;
} while (line < label->getLinesNumber() && label->isFakeLine(line));
}
}
void TextBox::drawBackground(const DrawContext* pctx, Assets*) {
@ -103,31 +156,31 @@ void TextBox::drawBackground(const DrawContext* pctx, Assets*) {
if (!isFocused() && supplier) {
input = supplier();
}
if (isFocused() && multiline) {
batch->setColor(glm::vec4(1, 1, 1, 0.1f));
glm::vec2 lcoord = label->calcPos();
lcoord.y -= 2;
uint line = label->getLineByTextIndex(caret);
while (label->isFakeLine(line)) {
line--;
}
batch->setColor(glm::vec4(1, 1, 1, 0.05f));
do {
int lineY = label->getLineYOffset(line);
int lineHeight = font->getLineHeight() * label->getLineInterval();
batch->rect(lcoord.x, lcoord.y+lineY, label->getSize().x, lineHeight);
line++;
} while (line < label->getLinesNumber() && label->isFakeLine(line));
}
refreshLabel();
}
void TextBox::refreshLabel() {
label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f));
label->setText(input.empty() && !hint.empty() ? hint : getText());
if (showLineNumbers) {
if (lineNumbersLabel->getLinesNumber() != label->getLinesNumber()) {
std::wstringstream ss;
int n = 1;
for (int i = 1; i <= label->getLinesNumber(); i++) {
if (!label->isFakeLine(i-1)) {
ss << n;
n++;
}
if (i + 1 <= label->getLinesNumber()) {
ss << "\n";
}
}
lineNumbersLabel->setText(ss.str());
}
lineNumbersLabel->setPos(padding);
lineNumbersLabel->setColor(glm::vec4(1, 1, 1, 0.25f));
}
if (autoresize && font) {
auto size = getSize();
@ -293,7 +346,7 @@ bool TextBox::isAutoResize() const {
}
void TextBox::onFocus(GUI* gui) {
Panel::onFocus(gui);
Container::onFocus(gui);
if (onEditStart){
setCaret(input.size());
onEditStart();
@ -302,8 +355,11 @@ void TextBox::onFocus(GUI* gui) {
}
void TextBox::refresh() {
Panel::refresh();
Container::refresh();
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
label->setPos(glm::vec2(
padding.x + LINE_NUMBERS_PANE_WIDTH * showLineNumbers, padding.y
));
}
/// @brief Clamp index to range [0, input.length()]
@ -705,3 +761,20 @@ void TextBox::setCaret(ptrdiff_t position) {
setCaret(static_cast<size_t>(position));
}
}
void TextBox::setPadding(glm::vec4 padding) {
this->padding = padding;
refresh();
}
glm::vec4 TextBox::getPadding() const {
return padding;
}
void TextBox::setShowLineNumbers(bool flag) {
showLineNumbers = flag;
}
bool TextBox::isShowLineNumbers() const {
return showLineNumbers;
}

View File

@ -8,12 +8,14 @@ class Font;
namespace gui {
class Label;
class TextBox : public Panel {
class TextBox : public Container {
protected:
glm::vec4 focusedColor {0.0f, 0.0f, 0.0f, 1.0f};
glm::vec4 invalidColor {0.1f, 0.05f, 0.03f, 1.0f};
glm::vec4 textColor {1.0f, 1.0f, 1.0f, 1.0f};
glm::vec4 padding {2};
std::shared_ptr<Label> label;
std::shared_ptr<Label> lineNumbersLabel;
/// @brief Current user input
std::wstring input;
/// @brief Text will be used if nothing entered
@ -53,6 +55,7 @@ namespace gui {
bool multiline = false;
bool editable = true;
bool autoresize = false;
bool showLineNumbers = false;
void stepLeft(bool shiftPressed, bool breakSelection);
void stepRight(bool shiftPressed, bool breakSelection);
@ -181,12 +184,18 @@ namespace gui {
/// @brief Check if text editing feature is enabled
virtual bool isEditable() const;
virtual void setPadding(glm::vec4 padding);
glm::vec4 getPadding() const;
/// @brief Set runnable called on textbox focus
virtual void setOnEditStart(runnable oneditstart);
virtual void setAutoResize(bool flag);
virtual bool isAutoResize() const;
virtual void setShowLineNumbers(bool flag);
virtual bool isShowLineNumbers() const;
virtual void onFocus(GUI*) override;
virtual void refresh() override;
virtual void doubleClick(GUI*, int x, int y) override;

View File

@ -342,7 +342,16 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f));
textbox->setHint(hint);
_readPanel(reader, element, *textbox);
_readContainer(reader, element, *textbox);
if (element->has("padding")) {
glm::vec4 padding = element->attr("padding").asVec4();
textbox->setPadding(padding);
glm::vec2 size = textbox->getSize();
textbox->setSize(glm::vec2(
size.x + padding.x + padding.z,
size.y + padding.y + padding.w
));
}
textbox->setText(text);
if (element->has("multiline")) {
@ -357,6 +366,9 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
if (element->has("autoresize")) {
textbox->setAutoResize(element->attr("autoresize").asBool());
}
if (element->has("line-numbers")) {
textbox->setShowLineNumbers(element->attr("line-numbers").asBool());
}
if (element->has("consumer")) {
textbox->setTextConsumer(scripting::create_wstring_consumer(
reader.getEnvironment(),

View File

@ -274,6 +274,13 @@ static int p_get_editable(UINode* node, lua::State* L) {
return 0;
}
static int p_get_line_numbers(UINode* node, lua::State* L) {
if (auto box = dynamic_cast<TextBox*>(node)) {
return lua::pushboolean(L, box->isShowLineNumbers());
}
return 0;
}
static int p_get_src(UINode* node, lua::State* L) {
if (auto image = dynamic_cast<Image*>(node)) {
return lua::pushstring(L, image->getTexture());
@ -383,6 +390,7 @@ static int l_gui_getattr(lua::State* L) {
{"caret", p_get_caret},
{"text", p_get_text},
{"editable", p_get_editable},
{"lineNumbers", p_get_line_numbers},
{"src", p_get_src},
{"value", p_get_value},
{"min", p_get_min},
@ -470,6 +478,11 @@ static void p_set_editable(UINode* node, lua::State* L, int idx) {
box->setEditable(lua::toboolean(L, idx));
}
}
static void p_set_line_numbers(UINode* node, lua::State* L, int idx) {
if (auto box = dynamic_cast<TextBox*>(node)) {
box->setShowLineNumbers(lua::toboolean(L, idx));
}
}
static void p_set_src(UINode* node, lua::State* L, int idx) {
if (auto image = dynamic_cast<Image*>(node)) {
image->setTexture(lua::require_string(L, idx));
@ -569,6 +582,7 @@ static int l_gui_setattr(lua::State* L) {
{"hint", p_set_hint},
{"text", p_set_text},
{"editable", p_set_editable},
{"lineNumbers", p_set_line_numbers},
{"src", p_set_src},
{"caret", p_set_caret},
{"value", p_set_value},