animated example
This commit is contained in:
parent
216972e73a
commit
c9fd1b1585
17 changed files with 576 additions and 465 deletions
|
|
@ -376,6 +376,7 @@ export namespace Crafter::UI {
|
|||
Font* font_ = nullptr;
|
||||
float size_ = 16.0f;
|
||||
Color color_{1, 1, 1, 1};
|
||||
Observable<std::string>* boundText_ = nullptr;
|
||||
|
||||
Text() = default;
|
||||
Text(std::string s) { runs_.emplace_back(std::move(s)); }
|
||||
|
|
@ -385,6 +386,11 @@ export namespace Crafter::UI {
|
|||
Text& size(float s) { size_ = s; return *this; }
|
||||
Text& color(Color c) { color_ = c; return *this; }
|
||||
|
||||
// Bind to an observable; the text is sourced from `obs` each frame
|
||||
// (overriding any `runs_`). Useful for live-updating labels driven
|
||||
// by a game-state observable.
|
||||
Text& bind(Observable<std::string>& obs) { boundText_ = &obs; return *this; }
|
||||
|
||||
// Replace the run list with a parameter pack of styled runs.
|
||||
template<typename... Rs>
|
||||
requires (std::convertible_to<std::decay_t<Rs>, TextRun> && ...)
|
||||
|
|
@ -398,6 +404,9 @@ export namespace Crafter::UI {
|
|||
Size Measure(Size avail, const LayoutContext& ctx) override {
|
||||
float ownW = ResolveLength(width_, avail.w, ctx.scale, [&]{
|
||||
if (!font_) return 0.0f;
|
||||
if (boundText_) {
|
||||
return static_cast<float>(font_->GetLineWidth(boundText_->Get(), size_ * ctx.scale));
|
||||
}
|
||||
float w = 0;
|
||||
for (auto& r : runs_) {
|
||||
float rs = (r.size_.value_or(size_)) * ctx.scale;
|
||||
|
|
@ -407,6 +416,9 @@ export namespace Crafter::UI {
|
|||
});
|
||||
float ownH = ResolveLength(height_, avail.h, ctx.scale, [&]{
|
||||
if (!font_) return 0.0f;
|
||||
if (boundText_) {
|
||||
return font_->LineHeight(size_ * ctx.scale);
|
||||
}
|
||||
// Tallest run dictates the line height.
|
||||
float h = 0;
|
||||
for (auto& r : runs_) {
|
||||
|
|
@ -425,6 +437,12 @@ export namespace Crafter::UI {
|
|||
|
||||
void Emit(DrawList& dl) const override {
|
||||
if (!font_) return;
|
||||
if (boundText_) {
|
||||
detail::EmitText(dl, font_, boundText_->Get(),
|
||||
size_ * dl.scale, color_,
|
||||
computedRect.x, computedRect.y);
|
||||
return;
|
||||
}
|
||||
float cursorX = computedRect.x;
|
||||
for (auto& r : runs_) {
|
||||
float rs = r.size_.value_or(size_) * dl.scale;
|
||||
|
|
@ -574,6 +592,7 @@ export namespace Crafter::UI {
|
|||
bool focused_ = false;
|
||||
std::size_t cursor_ = 0; // codepoint index within text_
|
||||
std::string placeholder_;
|
||||
float scrollX_ = 0.0f; // device-px offset to keep caret in view
|
||||
std::function<void(const std::string&)> onChange_;
|
||||
std::function<void(const std::string&)> onSubmit_;
|
||||
|
||||
|
|
@ -618,8 +637,32 @@ export namespace Crafter::UI {
|
|||
return desiredSize;
|
||||
}
|
||||
|
||||
void Arrange(Rect rect, const LayoutContext& /*ctx*/) override {
|
||||
void Arrange(Rect rect, const LayoutContext& ctx) override {
|
||||
computedRect = rect;
|
||||
|
||||
// Adjust horizontal scroll so the caret is always visible.
|
||||
if (font_) {
|
||||
EdgesPx p = ResolveEdges(padding_, ctx.scale);
|
||||
float visibleW = std::max(0.0f, rect.w - p.Horiz());
|
||||
float devSize = fontSize_ * ctx.scale;
|
||||
float caretW = std::max(1.0f, ctx.scale);
|
||||
|
||||
std::string_view before(text_.data(), cursor_);
|
||||
float prefixW = static_cast<float>(font_->GetLineWidth(before, devSize));
|
||||
|
||||
// If the caret has run off the right edge, scroll right.
|
||||
if (prefixW + caretW > scrollX_ + visibleW) {
|
||||
scrollX_ = prefixW + caretW - visibleW;
|
||||
}
|
||||
// If the caret has moved left of the visible window, scroll left.
|
||||
if (prefixW < scrollX_) {
|
||||
scrollX_ = prefixW;
|
||||
}
|
||||
// Don't scroll past the start; don't scroll past content end.
|
||||
float totalW = static_cast<float>(font_->GetLineWidth(text_, devSize));
|
||||
float maxScroll = std::max(0.0f, totalW - visibleW);
|
||||
scrollX_ = std::clamp(scrollX_, 0.0f, maxScroll);
|
||||
}
|
||||
}
|
||||
|
||||
// Interaction. UIScene's focus manager flips `focused_` via
|
||||
|
|
@ -709,19 +752,36 @@ export namespace Crafter::UI {
|
|||
float devSize = fontSize_ * dl.scale;
|
||||
float originX = computedRect.x + p.left;
|
||||
float originY = computedRect.y + p.top;
|
||||
float visibleW = std::max(0.0f, computedRect.w - p.Horiz());
|
||||
float visibleH = std::max(0.0f, computedRect.h - p.Vert());
|
||||
|
||||
// Clip the text + caret to the content rect — once the
|
||||
// string overflows we slide it left under scrollX_, and
|
||||
// anything past the left/right edges must not draw outside
|
||||
// the field.
|
||||
dl.PushClip({originX, computedRect.y + p.top, visibleW, visibleH});
|
||||
|
||||
std::string_view show = !text_.empty() ? std::string_view(text_)
|
||||
: std::string_view(placeholder_);
|
||||
Color col = !text_.empty() ? textColor_
|
||||
: Color{textColor_.r, textColor_.g, textColor_.b, textColor_.a * 0.5f};
|
||||
detail::EmitText(dl, font_, show, devSize, col, originX, originY);
|
||||
detail::EmitText(dl, font_, show, devSize, col, originX - scrollX_, originY);
|
||||
|
||||
// Caret bar at the cursor position when focused.
|
||||
// Caret bar at the cursor position when focused. Blink at
|
||||
// ~530ms on / 530ms off (1.06s period) — standard rate
|
||||
// across most desktop apps.
|
||||
if (focused_) {
|
||||
std::string_view before(text_.data(), cursor_);
|
||||
float prefixW = static_cast<float>(font_->GetLineWidth(before, devSize));
|
||||
float caretX = originX + prefixW;
|
||||
dl.AddRect({caretX, originY, t, font_->LineHeight(devSize)}, focusBorderColor_);
|
||||
constexpr float kBlinkPeriod = 1.06f;
|
||||
float phase = std::fmod(dl.time, kBlinkPeriod);
|
||||
if (phase < kBlinkPeriod * 0.5f) {
|
||||
std::string_view before(text_.data(), cursor_);
|
||||
float prefixW = static_cast<float>(font_->GetLineWidth(before, devSize));
|
||||
float caretX = originX - scrollX_ + prefixW;
|
||||
dl.AddRect({caretX, originY, t, font_->LineHeight(devSize)}, focusBorderColor_);
|
||||
}
|
||||
}
|
||||
|
||||
dl.PopClip();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue