Crafter.Graphics/implementations/Crafter.Graphics-UIComponents.cpp

186 lines
7 KiB
C++
Raw Normal View History

2026-05-02 21:08:20 +02:00
/*
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,
});
}
}