186 lines
7 KiB
C++
186 lines
7 KiB
C++
|
|
/*
|
||
|
|
Crafter®.Graphics
|
||
|
|
Copyright (C) 2026 Catcrafts®
|
||
|
|
catcrafts.net
|
||
|
|
|
||
|
|
This library is free software; you can redistribute it and/or
|
||
|
|
modify it under the terms of the GNU Lesser General Public
|
||
|
|
License version 3.0 as published by the Free Software Foundation;
|
||
|
|
|
||
|
|
This library is distributed in the hope that it will be useful,
|
||
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
|
Lesser General Public License for more details.
|
||
|
|
|
||
|
|
You should have received a copy of the GNU Lesser General Public
|
||
|
|
License along with this library; if not, write to the Free Software
|
||
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
|
*/
|
||
|
|
module;
|
||
|
|
module Crafter.Graphics:UIComponents_impl;
|
||
|
|
import :UIComponents;
|
||
|
|
import :UI;
|
||
|
|
import :Font;
|
||
|
|
import :FontAtlas;
|
||
|
|
import std;
|
||
|
|
|
||
|
|
using namespace Crafter;
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
// Push one QuadItem into buf if there's room. No-op if full.
|
||
|
|
inline void PushQuad(UIBuffer& buf, const QuadItem& q) {
|
||
|
|
if (buf.quads == nullptr || buf.quadCount == nullptr) return;
|
||
|
|
if (*buf.quadCount >= buf.quadCap) return;
|
||
|
|
buf.quads[(*buf.quadCount)++] = q;
|
||
|
|
}
|
||
|
|
|
||
|
|
inline std::array<float, 4> Pick(const std::array<float, 4>& a,
|
||
|
|
const std::array<float, 4>& b, bool useB) {
|
||
|
|
return useB ? b : a;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Centered single-line text emit. Appends glyphs at the buffer's tail,
|
||
|
|
// then offsets them so the run is horizontally centered around `centerX`.
|
||
|
|
// Vertical baseline is placed at the centerY plus a coarse ascent estimate
|
||
|
|
// (fontSize * 0.32). Returns the advance width that was written.
|
||
|
|
float EmitCenteredLabel(UIBuffer& buf, std::string_view label,
|
||
|
|
Font& font, float fontSize,
|
||
|
|
float centerX, float centerY,
|
||
|
|
std::array<float, 4> color)
|
||
|
|
{
|
||
|
|
if (label.empty() || buf.atlas == nullptr || buf.renderer == nullptr) return 0.0f;
|
||
|
|
if (buf.glyphs == nullptr || buf.glyphCount == nullptr) return 0.0f;
|
||
|
|
|
||
|
|
std::uint32_t before = *buf.glyphCount;
|
||
|
|
std::uint32_t cap = (buf.glyphCap > before) ? (buf.glyphCap - before) : 0;
|
||
|
|
if (cap == 0) return 0.0f;
|
||
|
|
|
||
|
|
GlyphItem* writePos = buf.glyphs + before;
|
||
|
|
float baseline = centerY + fontSize * 0.32f;
|
||
|
|
float advance = 0.0f;
|
||
|
|
std::uint32_t n = buf.renderer->ShapeText(
|
||
|
|
font, fontSize, centerX, baseline,
|
||
|
|
label, color, writePos, cap, &advance
|
||
|
|
);
|
||
|
|
*buf.glyphCount = before + n;
|
||
|
|
|
||
|
|
// Center: shift each glyph's x by -advance/2.
|
||
|
|
float shift = -advance * 0.5f;
|
||
|
|
for (std::uint32_t i = 0; i < n; ++i) {
|
||
|
|
writePos[i].x += shift;
|
||
|
|
}
|
||
|
|
return advance;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ─── DrawButton ─────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
void Crafter::DrawButton(UIBuffer& buf, Rect r, std::string_view label,
|
||
|
|
bool hovered, bool pressed,
|
||
|
|
Font& font, float fontSize,
|
||
|
|
const ButtonColors& c)
|
||
|
|
{
|
||
|
|
auto bg = pressed ? c.bgPressed : (hovered ? c.bgHover : c.bg);
|
||
|
|
PushQuad(buf, QuadItem{
|
||
|
|
r.x, r.y, r.w, r.h,
|
||
|
|
bg[0], bg[1], bg[2], bg[3],
|
||
|
|
c.cornerRadius, c.cornerRadius, c.cornerRadius, c.cornerRadius,
|
||
|
|
c.borderThickness, c.border[0], c.border[1], c.border[2],
|
||
|
|
});
|
||
|
|
|
||
|
|
EmitCenteredLabel(buf, label, font, fontSize,
|
||
|
|
r.x + r.w * 0.5f, r.y + r.h * 0.5f, c.text);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ─── DrawCheckbox ───────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
void Crafter::DrawCheckbox(UIBuffer& buf, Rect r, bool checked, bool hovered,
|
||
|
|
const CheckboxColors& c)
|
||
|
|
{
|
||
|
|
auto bg = Pick(c.bg, c.bgHover, hovered);
|
||
|
|
PushQuad(buf, QuadItem{
|
||
|
|
r.x, r.y, r.w, r.h,
|
||
|
|
bg[0], bg[1], bg[2], bg[3],
|
||
|
|
c.cornerRadius, c.cornerRadius, c.cornerRadius, c.cornerRadius,
|
||
|
|
c.borderThickness, c.border[0], c.border[1], c.border[2],
|
||
|
|
});
|
||
|
|
if (checked) {
|
||
|
|
Rect inner = r.Inset(c.checkInset);
|
||
|
|
if (inner.w > 0 && inner.h > 0) {
|
||
|
|
float innerR = std::max(0.0f, c.cornerRadius - c.checkInset);
|
||
|
|
PushQuad(buf, QuadItem{
|
||
|
|
inner.x, inner.y, inner.w, inner.h,
|
||
|
|
c.check[0], c.check[1], c.check[2], c.check[3],
|
||
|
|
innerR, innerR, innerR, innerR,
|
||
|
|
0, 0, 0, 0,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ─── DrawSlider ─────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
void Crafter::DrawSlider(UIBuffer& buf, Rect r, float t01, bool dragging,
|
||
|
|
const SliderColors& c)
|
||
|
|
{
|
||
|
|
t01 = std::clamp(t01, 0.0f, 1.0f);
|
||
|
|
|
||
|
|
// Track is a thin centered horizontal strip.
|
||
|
|
float trackY = r.y + (r.h - c.trackHeight) * 0.5f;
|
||
|
|
float trackR = c.trackHeight * 0.5f;
|
||
|
|
float fillW = r.w * t01;
|
||
|
|
|
||
|
|
if (fillW > 0.0f) {
|
||
|
|
PushQuad(buf, QuadItem{
|
||
|
|
r.x, trackY, fillW, c.trackHeight,
|
||
|
|
c.trackFilled[0], c.trackFilled[1], c.trackFilled[2], c.trackFilled[3],
|
||
|
|
trackR, trackR, trackR, trackR,
|
||
|
|
0, 0, 0, 0,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
if (fillW < r.w) {
|
||
|
|
PushQuad(buf, QuadItem{
|
||
|
|
r.x + fillW, trackY, r.w - fillW, c.trackHeight,
|
||
|
|
c.track[0], c.track[1], c.track[2], c.track[3],
|
||
|
|
trackR, trackR, trackR, trackR,
|
||
|
|
0, 0, 0, 0,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Thumb: a quad with cornerRadius = thumbRadius (= a circle).
|
||
|
|
auto thumbColor = Pick(c.thumb, c.thumbHover, dragging);
|
||
|
|
float thumbCx = r.x + r.w * t01;
|
||
|
|
float thumbCy = r.y + r.h * 0.5f;
|
||
|
|
float d = c.thumbRadius * 2.0f;
|
||
|
|
PushQuad(buf, QuadItem{
|
||
|
|
thumbCx - c.thumbRadius, thumbCy - c.thumbRadius, d, d,
|
||
|
|
thumbColor[0], thumbColor[1], thumbColor[2], thumbColor[3],
|
||
|
|
c.thumbRadius, c.thumbRadius, c.thumbRadius, c.thumbRadius,
|
||
|
|
0, 0, 0, 0,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// ─── DrawProgressBar ────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
void Crafter::DrawProgressBar(UIBuffer& buf, Rect r, float t01,
|
||
|
|
const ProgressColors& c)
|
||
|
|
{
|
||
|
|
t01 = std::clamp(t01, 0.0f, 1.0f);
|
||
|
|
|
||
|
|
PushQuad(buf, QuadItem{
|
||
|
|
r.x, r.y, r.w, r.h,
|
||
|
|
c.bg[0], c.bg[1], c.bg[2], c.bg[3],
|
||
|
|
c.cornerRadius, c.cornerRadius, c.cornerRadius, c.cornerRadius,
|
||
|
|
0, 0, 0, 0,
|
||
|
|
});
|
||
|
|
|
||
|
|
float fillW = r.w * t01;
|
||
|
|
if (fillW > 0.0f) {
|
||
|
|
PushQuad(buf, QuadItem{
|
||
|
|
r.x, r.y, fillW, r.h,
|
||
|
|
c.fill[0], c.fill[1], c.fill[2], c.fill[3],
|
||
|
|
c.cornerRadius, c.cornerRadius, c.cornerRadius, c.cornerRadius,
|
||
|
|
0, 0, 0, 0,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|