add 'line-numbers' property to textbox & make TextBox extend Container instead of Panel
This commit is contained in:
parent
9dc09e920c
commit
2cf51a6941
@ -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.
|
||||
|
||||
@ -105,6 +105,7 @@
|
||||
- `multiline` - разрешает отображение многострочного текста.
|
||||
- `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true")
|
||||
- `editable`- определяет возможность редактирования текста.
|
||||
- `line-numbers` - включает отображение номеров строк.
|
||||
- `error-color` - цвет при вводе некорректных данных (текст не проходит проверку валидатора). Тип: RGBA цвет.
|
||||
- `text-color` - цвет текста. Тип: RGBA цвет.
|
||||
- `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен.
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user