mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-09 13:01:49 +00:00
Merge remote-tracking branch 'origin/main' into sdl-try-2
This commit is contained in:
commit
9dad26232a
264 changed files with 8609 additions and 2047 deletions
|
|
@ -1,10 +1,21 @@
|
|||
#include "InventoryView.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <utility>
|
||||
|
||||
#include "assets/Assets.hpp"
|
||||
#include "assets/assets_util.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "frontend/LevelFrontend.hpp"
|
||||
#include "frontend/locale.hpp"
|
||||
#include "graphics/core/Atlas.hpp"
|
||||
#include "graphics/core/Batch2D.hpp"
|
||||
#include "graphics/core/DrawContext.hpp"
|
||||
#include "graphics/core/Font.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "graphics/render/BlocksPreview.hpp"
|
||||
#include "graphics/ui/GUI.hpp"
|
||||
#include "items/Inventories.hpp"
|
||||
#include "items/Inventory.hpp"
|
||||
#include "items/ItemDef.hpp"
|
||||
|
|
@ -15,17 +26,6 @@
|
|||
#include "voxels/Block.hpp"
|
||||
#include "window/input.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "graphics/core/Atlas.hpp"
|
||||
#include "graphics/core/Batch2D.hpp"
|
||||
#include "graphics/core/Font.hpp"
|
||||
#include "graphics/core/DrawContext.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "graphics/render/BlocksPreview.hpp"
|
||||
#include "graphics/ui/GUI.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <utility>
|
||||
|
||||
using namespace gui;
|
||||
|
||||
|
|
@ -37,21 +37,24 @@ SlotLayout::SlotLayout(
|
|||
slotcallback updateFunc,
|
||||
slotcallback shareFunc,
|
||||
slotcallback rightClick
|
||||
) : index(index),
|
||||
position(position),
|
||||
background(background),
|
||||
itemSource(itemSource),
|
||||
updateFunc(std::move(updateFunc)),
|
||||
shareFunc(std::move(shareFunc)),
|
||||
rightClick(std::move(rightClick)) {}
|
||||
)
|
||||
: index(index),
|
||||
position(position),
|
||||
background(background),
|
||||
itemSource(itemSource),
|
||||
updateFunc(std::move(updateFunc)),
|
||||
shareFunc(std::move(shareFunc)),
|
||||
rightClick(std::move(rightClick)) {
|
||||
}
|
||||
|
||||
InventoryBuilder::InventoryBuilder(GUI& gui) : gui(gui) {
|
||||
view = std::make_shared<InventoryView>(gui);
|
||||
}
|
||||
|
||||
void InventoryBuilder::addGrid(
|
||||
int cols, int count,
|
||||
glm::vec2 pos,
|
||||
int cols,
|
||||
int count,
|
||||
glm::vec2 pos,
|
||||
glm::vec4 padding,
|
||||
bool addpanel,
|
||||
const SlotLayout& slotLayout
|
||||
|
|
@ -61,9 +64,11 @@ void InventoryBuilder::addGrid(
|
|||
|
||||
int rows = ceildiv(count, cols);
|
||||
|
||||
uint width = cols * (slotSize + interval) - interval + padding.x + padding.z;
|
||||
uint height = rows * (slotSize + interval) - interval + padding.y + padding.w;
|
||||
|
||||
uint width =
|
||||
cols * (slotSize + interval) - interval + padding.x + padding.z;
|
||||
uint height =
|
||||
rows * (slotSize + interval) - interval + padding.y + padding.w;
|
||||
|
||||
glm::vec2 vsize = view->getSize();
|
||||
if (pos.x + width > vsize.x) {
|
||||
vsize.x = pos.x + width;
|
||||
|
|
@ -85,7 +90,7 @@ void InventoryBuilder::addGrid(
|
|||
if (row * cols + col >= count) {
|
||||
break;
|
||||
}
|
||||
glm::vec2 position (
|
||||
glm::vec2 position(
|
||||
col * (slotSize + interval) + padding.x,
|
||||
row * (slotSize + interval) + padding.y
|
||||
);
|
||||
|
|
@ -105,24 +110,72 @@ std::shared_ptr<InventoryView> InventoryBuilder::build() {
|
|||
return view;
|
||||
}
|
||||
|
||||
SlotView::SlotView(
|
||||
GUI& gui, SlotLayout layout
|
||||
) : UINode(gui, glm::vec2(InventoryView::SLOT_SIZE)),
|
||||
layout(std::move(layout))
|
||||
{
|
||||
SlotView::SlotView(GUI& gui, SlotLayout layout)
|
||||
: UINode(gui, glm::vec2(InventoryView::SLOT_SIZE)),
|
||||
layout(std::move(layout)) {
|
||||
setColor(glm::vec4(0, 0, 0, 0.2f));
|
||||
setTooltipDelay(0.0f);
|
||||
}
|
||||
// TODO: Refactor
|
||||
static std::wstring get_caption_string(
|
||||
const ItemStack& stack, const ItemDef& item
|
||||
) {
|
||||
dv::value* caption = stack.getField("caption");
|
||||
if (caption != nullptr) {
|
||||
return util::pascal_case(
|
||||
langs::get(util::str2wstr_utf8(caption->asString()))
|
||||
);
|
||||
} else {
|
||||
return util::pascal_case(
|
||||
langs::get(util::str2wstr_utf8(item.caption))
|
||||
);
|
||||
}
|
||||
}
|
||||
// TODO: Refactor
|
||||
static std::wstring get_description_string(
|
||||
const ItemStack& stack, const ItemDef& item
|
||||
) {
|
||||
dv::value* description = stack.getField("description");
|
||||
|
||||
if (description != nullptr) {
|
||||
return langs::get(util::str2wstr_utf8(description->asString()));
|
||||
} else {
|
||||
return langs::get(util::str2wstr_utf8(item.description));
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_same_tooltip(const ItemStack& stack, const ItemStack& cache) {
|
||||
if (stack.getItemId() != cache.getItemId()) {
|
||||
return false;
|
||||
}
|
||||
auto caption = stack.getField("caption");
|
||||
auto cCaption = cache.getField("caption");
|
||||
auto description = stack.getField("description");
|
||||
auto cDescription = cache.getField("description");
|
||||
|
||||
if (((caption != nullptr) != (cCaption != nullptr)) ||
|
||||
((description != nullptr) != (cDescription != nullptr))) {
|
||||
return false;
|
||||
}
|
||||
return (caption ? caption->asString() == cCaption->asString() : true) &&
|
||||
(description ? description->asString() == cDescription->asString()
|
||||
: true);
|
||||
}
|
||||
|
||||
void SlotView::refreshTooltip(const ItemStack& stack, const ItemDef& item) {
|
||||
itemid_t itemid = stack.getItemId();
|
||||
if (itemid == cache.stack.getItemId()) {
|
||||
|
||||
if (is_same_tooltip(stack, cache.stack)) {
|
||||
return;
|
||||
}
|
||||
if (itemid) {
|
||||
tooltip = util::pascal_case(
|
||||
langs::get(util::str2wstr_utf8(item.caption))
|
||||
);
|
||||
std::wstring caption = get_caption_string(stack, item);
|
||||
std::wstring description = get_description_string(stack, item);
|
||||
if (description.length() > 0) {
|
||||
tooltip = caption + L"\n" + description;
|
||||
} else {
|
||||
tooltip = caption;
|
||||
}
|
||||
} else {
|
||||
tooltip.clear();
|
||||
}
|
||||
|
|
@ -148,19 +201,37 @@ void SlotView::drawItemIcon(
|
|||
|
||||
UVRegion region = previews.get(block.name);
|
||||
batch.rect(
|
||||
pos.x, pos.y, SLOT_SIZE, SLOT_SIZE,
|
||||
0, 0, 0, region, false, true, tint
|
||||
pos.x,
|
||||
pos.y,
|
||||
SLOT_SIZE,
|
||||
SLOT_SIZE,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
region,
|
||||
false,
|
||||
true,
|
||||
tint
|
||||
);
|
||||
break;
|
||||
}
|
||||
case ItemIconType::SPRITE: {
|
||||
auto textureRegion =
|
||||
util::get_texture_region(assets, item.icon, "blocks:notfound");
|
||||
|
||||
|
||||
batch.texture(textureRegion.texture);
|
||||
batch.rect(
|
||||
pos.x, pos.y, SLOT_SIZE, SLOT_SIZE,
|
||||
0, 0, 0, textureRegion.region, false, true, tint
|
||||
pos.x,
|
||||
pos.y,
|
||||
SLOT_SIZE,
|
||||
SLOT_SIZE,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
textureRegion.region,
|
||||
false,
|
||||
true,
|
||||
tint
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
|
@ -179,12 +250,12 @@ void SlotView::draw(const DrawContext& pctx, const Assets& assets) {
|
|||
cache.countStr = std::to_wstring(stack.getCount());
|
||||
}
|
||||
refreshTooltip(stack, item);
|
||||
cache.stack.set(ItemStack(stack.getItemId(), stack.getCount()));
|
||||
cache.stack.set(stack);
|
||||
|
||||
glm::vec4 tint(1, 1, 1, isEnabled() ? 1 : 0.5f);
|
||||
glm::vec2 pos = calcPos();
|
||||
glm::vec4 color = getColor();
|
||||
|
||||
|
||||
if (hover || highlighted) {
|
||||
tint *= 1.333f;
|
||||
color = glm::vec4(1, 1, 1, 0.2f);
|
||||
|
|
@ -262,7 +333,7 @@ void SlotView::drawItemInfo(
|
|||
batch.setColor({0, 0, 0, 0.75f});
|
||||
batch.rect(pos.x - 2, pos.y - 2, 6, SLOT_SIZE + 4);
|
||||
float t = static_cast<float>(uses) / item.uses;
|
||||
|
||||
|
||||
int height = SLOT_SIZE * t;
|
||||
batch.setColor({(1.0f - t * 0.8f), 0.4f, t * 0.8f + 0.2f, 1.0f});
|
||||
batch.rect(pos.x, pos.y + SLOT_SIZE - height, 2, height);
|
||||
|
|
@ -317,8 +388,7 @@ void SlotView::performRightClick(ItemStack& stack, ItemStack& grabbed) {
|
|||
}
|
||||
return;
|
||||
}
|
||||
if (layout.itemSource)
|
||||
return;
|
||||
if (layout.itemSource) return;
|
||||
if (grabbed.isEmpty()) {
|
||||
if (!stack.isEmpty() && layout.taking) {
|
||||
grabbed.set(std::move(stack));
|
||||
|
|
@ -342,15 +412,15 @@ void SlotView::performRightClick(ItemStack& stack, ItemStack& grabbed) {
|
|||
} else {
|
||||
grabbed = ItemStack(stack.getItemId(), count - 1);
|
||||
}
|
||||
} else if (stack.accepts(grabbed) && stack.getCount() < stackDef.stackSize) {
|
||||
} else if (stack.accepts(grabbed) &&
|
||||
stack.getCount() < stackDef.stackSize) {
|
||||
stack.setCount(stack.getCount() + 1);
|
||||
grabbed.setCount(grabbed.getCount() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SlotView::clicked(Mousecode button) {
|
||||
if (bound == nullptr)
|
||||
return;
|
||||
if (bound == nullptr) return;
|
||||
auto exchangeSlot =
|
||||
std::dynamic_pointer_cast<SlotView>(gui.get(EXCHANGE_SLOT_NAME));
|
||||
if (exchangeSlot == nullptr) {
|
||||
|
|
@ -358,7 +428,7 @@ void SlotView::clicked(Mousecode button) {
|
|||
}
|
||||
ItemStack& grabbed = exchangeSlot->getStack();
|
||||
ItemStack& stack = *bound;
|
||||
|
||||
|
||||
if (button == Mousecode::BUTTON_1) {
|
||||
performLeftClick(stack, grabbed);
|
||||
} else if (button == Mousecode::BUTTON_2) {
|
||||
|
|
@ -382,9 +452,7 @@ const std::wstring& SlotView::getTooltip() const {
|
|||
}
|
||||
|
||||
void SlotView::bind(
|
||||
int64_t inventoryid,
|
||||
ItemStack& stack,
|
||||
const Content* content
|
||||
int64_t inventoryid, ItemStack& stack, const Content* content
|
||||
) {
|
||||
this->inventoryid = inventoryid;
|
||||
bound = &stack;
|
||||
|
|
@ -403,11 +471,11 @@ InventoryView::InventoryView(GUI& gui) : Container(gui, glm::vec2()) {
|
|||
setColor(glm::vec4(0, 0, 0, 0.0f));
|
||||
}
|
||||
|
||||
InventoryView::~InventoryView() {}
|
||||
|
||||
InventoryView::~InventoryView() {
|
||||
}
|
||||
|
||||
std::shared_ptr<SlotView> InventoryView::addSlot(const SlotLayout& layout) {
|
||||
uint width = InventoryView::SLOT_SIZE + layout.padding;
|
||||
uint width = InventoryView::SLOT_SIZE + layout.padding;
|
||||
uint height = InventoryView::SLOT_SIZE + layout.padding;
|
||||
|
||||
auto pos = layout.position;
|
||||
|
|
@ -432,14 +500,12 @@ std::shared_ptr<Inventory> InventoryView::getInventory() const {
|
|||
return inventory;
|
||||
}
|
||||
|
||||
|
||||
size_t InventoryView::getSlotsCount() const {
|
||||
return slots.size();
|
||||
}
|
||||
|
||||
void InventoryView::bind(
|
||||
const std::shared_ptr<Inventory>& inventory,
|
||||
const Content* content
|
||||
const std::shared_ptr<Inventory>& inventory, const Content* content
|
||||
) {
|
||||
this->inventory = inventory;
|
||||
this->content = content;
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@
|
|||
|
||||
using namespace gui;
|
||||
|
||||
void LabelCache::prepare(Font* font, size_t wrapWidth) {
|
||||
if (font != this->font) {
|
||||
void LabelCache::prepare(std::ptrdiff_t fontId, FontMetrics metrics, size_t wrapWidth) {
|
||||
if (fontId != this->fontId) {
|
||||
resetFlag = true;
|
||||
this->font = font;
|
||||
this->fontId = fontId;
|
||||
this->metrics = metrics;
|
||||
}
|
||||
if (wrapWidth != this->wrapWidth) {
|
||||
resetFlag = true;
|
||||
|
|
@ -41,7 +42,7 @@ void LabelCache::update(std::wstring_view text, bool multiline, bool wrap) {
|
|||
lines.clear();
|
||||
lines.push_back(LineScheme {0, false});
|
||||
|
||||
if (font == nullptr) {
|
||||
if (fontId == 0) {
|
||||
wrap = false;
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +53,7 @@ void LabelCache::update(std::wstring_view text, bool multiline, bool wrap) {
|
|||
lines.push_back(LineScheme {i+1, false});
|
||||
len = 0;
|
||||
} else if (i > 0 && i+1 < text.length() && wrap && text[i+1] != L'\n') {
|
||||
size_t width = font->calcWidth(text, i-len-1, i-(i-len)+2);
|
||||
size_t width = metrics.calcWidth(text, i-len-1, i-(i-len)+2);
|
||||
if (width >= wrapWidth) {
|
||||
// starting a fake line
|
||||
lines.push_back(LineScheme {i+1, true});
|
||||
|
|
@ -60,20 +61,20 @@ void LabelCache::update(std::wstring_view text, bool multiline, bool wrap) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (font != nullptr) {
|
||||
if (fontId != 0) {
|
||||
int maxWidth = 0;
|
||||
for (int i = 0; i < lines.size() - 1; i++) {
|
||||
const auto& next = lines[i + 1];
|
||||
const auto& cur = lines[i];
|
||||
maxWidth = std::max(
|
||||
font->calcWidth(
|
||||
metrics.calcWidth(
|
||||
text.substr(cur.offset, next.offset - cur.offset)
|
||||
),
|
||||
maxWidth
|
||||
);
|
||||
}
|
||||
maxWidth = std::max(
|
||||
font->calcWidth(
|
||||
metrics.calcWidth(
|
||||
text.substr(lines[lines.size() - 1].offset)
|
||||
),
|
||||
maxWidth
|
||||
|
|
@ -105,8 +106,8 @@ Label::Label(GUI& gui, const std::wstring& text, std::string fontName)
|
|||
Label::~Label() = default;
|
||||
|
||||
glm::vec2 Label::calcSize() {
|
||||
auto font = cache.font;
|
||||
uint lineHeight = font->getLineHeight();
|
||||
auto& metrics = cache.metrics;
|
||||
uint lineHeight = metrics.lineHeight;
|
||||
if (cache.lines.size() > 1) {
|
||||
lineHeight *= lineInterval;
|
||||
}
|
||||
|
|
@ -114,12 +115,12 @@ glm::vec2 Label::calcSize() {
|
|||
if (multiline) {
|
||||
return glm::vec2(
|
||||
cache.multilineWidth,
|
||||
lineHeight * cache.lines.size() + font->getYOffset()
|
||||
lineHeight * cache.lines.size() + metrics.yoffset
|
||||
);
|
||||
}
|
||||
return glm::vec2 (
|
||||
cache.font->calcWidth(view),
|
||||
lineHeight * cache.lines.size() + font->getYOffset()
|
||||
cache.metrics.calcWidth(view),
|
||||
lineHeight * cache.lines.size() + metrics.yoffset
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -135,7 +136,7 @@ void Label::setText(std::wstring text) {
|
|||
this->text = std::move(text);
|
||||
cache.update(this->text, multiline, textWrap);
|
||||
|
||||
if (cache.font && autoresize) {
|
||||
if (cache.fontId != 0 && autoresize) {
|
||||
setSize(calcSize());
|
||||
}
|
||||
}
|
||||
|
|
@ -203,7 +204,11 @@ uint Label::getLinesNumber() const {
|
|||
void Label::draw(const DrawContext& pctx, const Assets& assets) {
|
||||
auto batch = pctx.getBatch2D();
|
||||
auto font = assets.get<Font>(fontName);
|
||||
cache.prepare(font, static_cast<size_t>(glm::abs(getSize().x)));
|
||||
cache.prepare(
|
||||
reinterpret_cast<ptrdiff_t>(font),
|
||||
font->getMetrics(),
|
||||
static_cast<size_t>(glm::abs(getSize().x))
|
||||
);
|
||||
|
||||
if (supplier) {
|
||||
setText(supplier());
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
#include "UINode.hpp"
|
||||
#include "constants.hpp"
|
||||
|
||||
#include "graphics/core/FontMetics.hpp"
|
||||
|
||||
class Font;
|
||||
struct FontStylesScheme;
|
||||
|
||||
|
|
@ -13,14 +15,16 @@ namespace gui {
|
|||
};
|
||||
|
||||
struct LabelCache {
|
||||
Font* font = nullptr;
|
||||
ptrdiff_t fontId = 0;
|
||||
FontMetrics metrics;
|
||||
|
||||
std::vector<LineScheme> lines;
|
||||
/// @brief Reset cache flag
|
||||
bool resetFlag = true;
|
||||
size_t wrapWidth = -1;
|
||||
int multilineWidth = 0;
|
||||
|
||||
void prepare(Font* font, size_t wrapWidth);
|
||||
void prepare(std::ptrdiff_t fontId, FontMetrics metrics, size_t wrapWidth);
|
||||
void update(std::wstring_view text, bool multiline, bool wrap);
|
||||
|
||||
size_t getTextLineOffset(size_t line) const;
|
||||
|
|
|
|||
|
|
@ -231,8 +231,6 @@ TextBox::~TextBox() = default;
|
|||
void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
|
||||
Container::draw(pctx, assets);
|
||||
|
||||
font = assets.get<Font>(label->getFontName());
|
||||
|
||||
if (!isFocused()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -244,7 +242,8 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
|
|||
auto subctx = pctx.sub();
|
||||
subctx.setScissors(glm::vec4(pos.x, pos.y, size.x, size.y));
|
||||
|
||||
const int lineHeight = font->getLineHeight() * label->getLineInterval();
|
||||
const int lineHeight =
|
||||
rawTextCache.metrics.lineHeight * label->getLineInterval();
|
||||
glm::vec2 lcoord = label->calcPos();
|
||||
lcoord.y -= 2;
|
||||
auto batch = pctx.getBatch2D();
|
||||
|
|
@ -256,7 +255,7 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
|
|||
if (editable && static_cast<int>((time - caretLastMove) * 2) % 2 == 0) {
|
||||
uint line = rawTextCache.getLineByTextIndex(caret);
|
||||
uint lcaret = caret - rawTextCache.getTextLineOffset(line);
|
||||
int width = font->calcWidth(input, lcaret);
|
||||
int width = rawTextCache.metrics.calcWidth(input, 0, lcaret);
|
||||
batch->rect(
|
||||
lcoord.x + width,
|
||||
lcoord.y + label->getLineYOffset(line),
|
||||
|
|
@ -272,10 +271,10 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
|
|||
uint endLine = label->getLineByTextIndex(selectionEnd);
|
||||
|
||||
batch->setColor(glm::vec4(0.8f, 0.9f, 1.0f, 0.25f));
|
||||
int start = font->calcWidth(
|
||||
int start = rawTextCache.metrics.calcWidth(
|
||||
labelText, selectionStart - label->getTextLineOffset(startLine)
|
||||
);
|
||||
int end = font->calcWidth(
|
||||
int end = rawTextCache.metrics.calcWidth(
|
||||
labelText, selectionEnd - label->getTextLineOffset(endLine)
|
||||
);
|
||||
int lineY = label->getLineYOffset(startLine);
|
||||
|
|
@ -346,7 +345,14 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
|
|||
}
|
||||
}
|
||||
|
||||
void TextBox::drawBackground(const DrawContext& pctx, const Assets&) {
|
||||
void TextBox::drawBackground(const DrawContext& pctx, const Assets& assets) {
|
||||
auto font = assets.get<Font>(label->getFontName());
|
||||
rawTextCache.prepare(
|
||||
reinterpret_cast<ptrdiff_t>(font),
|
||||
font->getMetrics(),
|
||||
label->getSize().x
|
||||
);
|
||||
|
||||
glm::vec2 pos = calcPos();
|
||||
|
||||
auto batch = pctx.getBatch2D();
|
||||
|
|
@ -375,7 +381,11 @@ void TextBox::drawBackground(const DrawContext& pctx, const Assets&) {
|
|||
}
|
||||
|
||||
void TextBox::refreshLabel() {
|
||||
rawTextCache.prepare(font, static_cast<size_t>(getSize().x));
|
||||
rawTextCache.prepare(
|
||||
rawTextCache.fontId,
|
||||
rawTextCache.metrics,
|
||||
static_cast<size_t>(getSize().x)
|
||||
);
|
||||
rawTextCache.update(input, multiline, false);
|
||||
|
||||
label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f));
|
||||
|
|
@ -412,13 +422,13 @@ void TextBox::refreshLabel() {
|
|||
lineNumbersLabel->setColor(glm::vec4(1, 1, 1, 0.25f));
|
||||
}
|
||||
|
||||
if (autoresize && font) {
|
||||
if (autoresize && rawTextCache.fontId) {
|
||||
auto size = getSize();
|
||||
int newy = glm::min(
|
||||
static_cast<int>(parent->getSize().y),
|
||||
static_cast<int>(
|
||||
label->getLinesNumber() * label->getLineInterval() *
|
||||
font->getLineHeight()
|
||||
rawTextCache.metrics.lineHeight
|
||||
) + 1
|
||||
);
|
||||
if (newy != static_cast<int>(size.y)) {
|
||||
|
|
@ -430,9 +440,9 @@ void TextBox::refreshLabel() {
|
|||
}
|
||||
}
|
||||
|
||||
if (multiline && font) {
|
||||
if (multiline && rawTextCache.fontId) {
|
||||
setScrollable(true);
|
||||
uint height = label->getLinesNumber() * font->getLineHeight() *
|
||||
uint height = label->getLinesNumber() * rawTextCache.metrics.lineHeight *
|
||||
label->getLineInterval();
|
||||
label->setSize(glm::vec2(label->getSize().x, height));
|
||||
actualLength = height;
|
||||
|
|
@ -644,14 +654,14 @@ size_t TextBox::normalizeIndex(int index) {
|
|||
/// @param y screen Y position
|
||||
/// @return non-normalized character index
|
||||
int TextBox::calcIndexAt(int x, int y) const {
|
||||
if (font == nullptr) return 0;
|
||||
if (rawTextCache.fontId == 0) return 0;
|
||||
const auto& labelText = label->getText();
|
||||
glm::vec2 lcoord = label->calcPos();
|
||||
uint line = label->getLineByYOffset(y - lcoord.y);
|
||||
line = std::min(line, label->getLinesNumber() - 1);
|
||||
size_t lineLength = getLineLength(line);
|
||||
uint offset = 0;
|
||||
while (lcoord.x + font->calcWidth(labelText, offset) < x &&
|
||||
while (lcoord.x + rawTextCache.metrics.calcWidth(labelText, offset) < x &&
|
||||
offset < lineLength - 1) {
|
||||
offset++;
|
||||
}
|
||||
|
|
@ -810,6 +820,85 @@ void TextBox::stepDefaultUp(bool shiftPressed, bool breakSelection) {
|
|||
}
|
||||
}
|
||||
|
||||
static int calc_indent(int linestart, std::wstring_view input) {
|
||||
int indent = 0;
|
||||
while (linestart + indent < input.length() &&
|
||||
input[linestart + indent] == L' ')
|
||||
indent++;
|
||||
return indent;
|
||||
}
|
||||
|
||||
void TextBox::onTab(bool shiftPressed) {
|
||||
std::wstring indentStr = L" ";
|
||||
|
||||
if (!shiftPressed && getSelectionLength() == 0) {
|
||||
paste(indentStr);
|
||||
return;
|
||||
}
|
||||
if (getSelectionLength() == 0) {
|
||||
selectionStart = caret;
|
||||
selectionEnd = caret;
|
||||
selectionOrigin = caret;
|
||||
}
|
||||
|
||||
int lineA = getLineAt(selectionStart);
|
||||
int lineB = getLineAt(selectionEnd);
|
||||
int caretLine = getLineAt(caret);
|
||||
|
||||
size_t lineAStart = getLinePos(lineA);
|
||||
size_t lineBStart = getLinePos(lineB);
|
||||
size_t caretLineStart = getLinePos(caretLine);
|
||||
size_t caretIndent = calc_indent(caretLineStart, input);
|
||||
size_t aIndent = calc_indent(lineAStart, input);
|
||||
size_t bIndent = calc_indent(lineBStart, input);
|
||||
|
||||
int lastSelectionStart = selectionStart;
|
||||
int lastSelectionEnd = selectionEnd;
|
||||
size_t lastCaret = caret;
|
||||
|
||||
auto combination = history->beginCombination();
|
||||
|
||||
resetSelection();
|
||||
|
||||
for (int line = lineA; line <= lineB; line++) {
|
||||
size_t linestart = getLinePos(line);
|
||||
int indent = calc_indent(linestart, input);
|
||||
|
||||
if (shiftPressed) {
|
||||
if (indent >= indentStr.length()) {
|
||||
setCaret(linestart);
|
||||
select(linestart, linestart + indentStr.length());
|
||||
eraseSelected();
|
||||
}
|
||||
} else {
|
||||
setCaret(linestart);
|
||||
paste(indentStr);
|
||||
}
|
||||
refreshLabel(); // todo: replace with textbox cache
|
||||
}
|
||||
|
||||
int linestart = getLinePos(caretLine);
|
||||
int linestartA = getLinePos(lineA);
|
||||
int linestartB = getLinePos(lineB);
|
||||
int la = lastSelectionStart - lineAStart;
|
||||
int lb = lastSelectionEnd - lineBStart;
|
||||
if (shiftPressed) {
|
||||
setCaret(lastCaret - caretLineStart + linestart - std::min<int>(caretIndent, indentStr.length()));
|
||||
selectionStart = la + linestartA - std::min<int>(std::min<int>(la, aIndent), indentStr.length());
|
||||
selectionEnd = lb + linestartB - std::min<int>(std::min<int>(lb, bIndent), indentStr.length());
|
||||
} else {
|
||||
setCaret(lastCaret - caretLineStart + linestart + indentStr.length());
|
||||
selectionStart = la + linestartA + indentStr.length();
|
||||
selectionEnd = lb + linestartB + indentStr.length();
|
||||
}
|
||||
if (selectionOrigin == lastSelectionStart) {
|
||||
selectionOrigin = selectionStart;
|
||||
} else {
|
||||
selectionOrigin = selectionEnd;
|
||||
}
|
||||
historian->sync();
|
||||
}
|
||||
|
||||
void TextBox::refreshSyntax() {
|
||||
if (!syntax.empty()) {
|
||||
const auto& processor = gui.getEditor().getSyntaxProcessor();
|
||||
|
|
@ -868,7 +957,7 @@ void TextBox::performEditingKeyboardEvents(Keycode key) {
|
|||
}
|
||||
}
|
||||
} else if (key == Keycode::TAB) {
|
||||
paste(L" ");
|
||||
onTab(shiftPressed);
|
||||
} else if (key == Keycode::LEFT) {
|
||||
stepLeft(shiftPressed, breakSelection);
|
||||
} else if (key == Keycode::RIGHT) {
|
||||
|
|
@ -1098,19 +1187,19 @@ size_t TextBox::getCaret() const {
|
|||
void TextBox::setCaret(size_t position) {
|
||||
const auto& labelText = label->getText();
|
||||
caret = std::min(static_cast<size_t>(position), input.length());
|
||||
if (font == nullptr) {
|
||||
if (rawTextCache.fontId == 0) {
|
||||
return;
|
||||
}
|
||||
int width = label->getSize().x;
|
||||
|
||||
rawTextCache.prepare(font, width);
|
||||
rawTextCache.prepare(rawTextCache.fontId, rawTextCache.metrics, width);
|
||||
rawTextCache.update(input, multiline, label->isTextWrapping());
|
||||
|
||||
caretLastMove = gui.getWindow().time();
|
||||
|
||||
uint line = rawTextCache.getLineByTextIndex(caret);
|
||||
int offset = label->getLineYOffset(line) + getContentOffset().y;
|
||||
uint lineHeight = font->getLineHeight() * label->getLineInterval();
|
||||
uint lineHeight = rawTextCache.metrics.lineHeight * label->getLineInterval();
|
||||
if (scrollStep == 0) {
|
||||
scrollStep = lineHeight;
|
||||
}
|
||||
|
|
@ -1122,7 +1211,7 @@ void TextBox::setCaret(size_t position) {
|
|||
}
|
||||
int lcaret = caret - rawTextCache.getTextLineOffset(line);
|
||||
int realoffset =
|
||||
font->calcWidth(labelText, lcaret) - static_cast<int>(textOffset) + 2;
|
||||
rawTextCache.metrics.calcWidth(labelText, lcaret) - static_cast<int>(textOffset) + 2;
|
||||
|
||||
if (realoffset - width > 0) {
|
||||
setTextOffset(textOffset + realoffset - width);
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ namespace gui {
|
|||
int textInitX = 0;
|
||||
/// @brief Last time of the caret was moved (used for blink animation)
|
||||
double caretLastMove = 0.0;
|
||||
Font* font = nullptr;
|
||||
|
||||
// Note: selection does not include markup
|
||||
size_t selectionStart = 0;
|
||||
|
|
@ -71,6 +70,8 @@ namespace gui {
|
|||
void stepDefaultDown(bool shiftPressed, bool breakSelection);
|
||||
void stepDefaultUp(bool shiftPressed, bool breakSelection);
|
||||
|
||||
void onTab(bool shiftPressed);
|
||||
|
||||
size_t normalizeIndex(int index);
|
||||
|
||||
int calcIndexAt(int x, int y) const;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue