/* 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 Pick(const std::array& a, const std::array& 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 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, }); } }