update
This commit is contained in:
parent
1f5697326c
commit
c054f1e0b3
9 changed files with 699 additions and 5 deletions
103
interfaces/Crafter.Graphics-InputField.cppm
Normal file
103
interfaces/Crafter.Graphics-InputField.cppm
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
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;
|
||||
export module Crafter.Graphics:InputField;
|
||||
import std;
|
||||
import :Types;
|
||||
import :Font;
|
||||
import :UI;
|
||||
import :UIComponents;
|
||||
|
||||
// Tier 3: single-line text input. Same contract as the other UI components —
|
||||
// state is a user-owned POD, the draw function is stateless presentation, and
|
||||
// input feeding is done via free functions called from the host's own event
|
||||
// listeners. Validation rules cover the common numeric cases (UInt/Int/Float)
|
||||
// and accept "in-progress" intermediate states (lone '-', '.', "1e") so the
|
||||
// user can type naturally; final-form parsing is on the caller via TryParse.
|
||||
|
||||
export namespace Crafter {
|
||||
enum class InputFieldType : std::uint8_t {
|
||||
Text, // any UTF-8
|
||||
UInt, // digits only
|
||||
Int, // optional leading '-', then digits
|
||||
Float, // signed float with optional exponent
|
||||
};
|
||||
|
||||
struct InputFieldColors {
|
||||
std::array<float, 4> bg;
|
||||
std::array<float, 4> bgFocused;
|
||||
std::array<float, 4> border;
|
||||
std::array<float, 4> borderFocused;
|
||||
std::array<float, 4> text;
|
||||
std::array<float, 4> caret;
|
||||
float cornerRadius = 6.0f;
|
||||
float borderThickness = 2.0f;
|
||||
float paddingX = 8.0f; // text inset from the rect's left edge
|
||||
};
|
||||
|
||||
struct InputField {
|
||||
std::string value;
|
||||
InputFieldType type = InputFieldType::Text;
|
||||
bool focused = false;
|
||||
std::size_t cursorPos = 0; // byte offset into `value`
|
||||
};
|
||||
|
||||
// Returns true if `s` is a valid candidate for the given type, including
|
||||
// "in-progress" states (lone '-', empty, "1e", "1.").
|
||||
bool InputField_IsValidCandidate(InputFieldType, std::string_view s);
|
||||
|
||||
// Try to parse the field's current value into T. Returns nullopt for empty
|
||||
// or in-progress strings. Defined for arithmetic types via std::from_chars.
|
||||
template <class T>
|
||||
std::optional<T> InputField_TryParse(const InputField& f) {
|
||||
if (f.value.empty()) return std::nullopt;
|
||||
T out{};
|
||||
const char* begin = f.value.data();
|
||||
const char* end = begin + f.value.size();
|
||||
auto [ptr, ec] = std::from_chars(begin, end, out);
|
||||
if (ec != std::errc{} || ptr != end) return std::nullopt;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Insert UTF-8 text at the cursor. The full would-be result is validated
|
||||
// against the field's type before committing — invalid candidates are
|
||||
// dropped silently. Cursor advances by the inserted byte count.
|
||||
void InputField_OnText(InputField&, std::string_view utf8);
|
||||
|
||||
// Edit-control keys: Backspace, Delete, Left, Right, Home, End. Anything
|
||||
// else is ignored. Safe to feed every key the host receives.
|
||||
void InputField_OnKey(InputField&, CrafterKeys);
|
||||
|
||||
// Map a click x-coord (in window pixels) to a cursor position. `rect` is
|
||||
// the field's current draw rect; `colors.paddingX` is consulted for the
|
||||
// text-start offset.
|
||||
std::size_t InputField_HitTestCursor(const InputField&,
|
||||
Rect rect,
|
||||
float clickX,
|
||||
Font&, float fontSize,
|
||||
const InputFieldColors&);
|
||||
|
||||
// Draw the field. `caretVisible` is provided by the caller so that blink
|
||||
// policy stays in user code (typical: `(steady_clock::now() / 500ms) & 1`).
|
||||
// The caret is only drawn if the field is focused AND caretVisible is true.
|
||||
void DrawInputField(UIBuffer& buf, const InputField& f, Rect rect,
|
||||
Font& font, float fontSize,
|
||||
const InputFieldColors& colors,
|
||||
bool caretVisible);
|
||||
}
|
||||
|
|
@ -38,8 +38,10 @@ import :FontAtlas;
|
|||
// function body into your code and modify it. There is no override hook.
|
||||
|
||||
export namespace Crafter {
|
||||
// Aggregate for the two item buffers + the optional text-shaping deps.
|
||||
// Build one per frame in onBuild and pass it to component calls.
|
||||
// Aggregate for the standard item buffers + the optional text-shaping
|
||||
// deps. Build one per frame in onBuild and pass it to component calls.
|
||||
// Any pointer left null causes the corresponding component to be a no-op
|
||||
// (so a frame that doesn't draw images can leave `images` unset).
|
||||
struct UIBuffer {
|
||||
QuadItem* quads = nullptr;
|
||||
std::uint32_t* quadCount = nullptr;
|
||||
|
|
@ -49,6 +51,10 @@ export namespace Crafter {
|
|||
std::uint32_t* glyphCount = nullptr;
|
||||
std::uint32_t glyphCap = 0;
|
||||
|
||||
ImageItem* images = nullptr;
|
||||
std::uint32_t* imageCount = nullptr;
|
||||
std::uint32_t imageCap = 0;
|
||||
|
||||
FontAtlas* atlas = nullptr; // for text-emitting components
|
||||
UIRenderer* renderer = nullptr; // for ShapeText
|
||||
};
|
||||
|
|
@ -92,6 +98,9 @@ export namespace Crafter {
|
|||
float cornerRadius = 0;
|
||||
};
|
||||
|
||||
// Where the X coordinate sits relative to the emitted glyph run.
|
||||
enum class TextAlign : std::uint8_t { Left, Center, Right };
|
||||
|
||||
// ─── component functions ───────────────────────────────────────────
|
||||
|
||||
// Background quad (color depends on state) + centered label glyphs.
|
||||
|
|
@ -112,4 +121,22 @@ export namespace Crafter {
|
|||
// Background quad + a filled quad clipped to t01 of the inner width.
|
||||
void DrawProgressBar(UIBuffer& buf, Rect r, float t01,
|
||||
const ProgressColors& c);
|
||||
|
||||
// Single-line text emit. `(x, baselineY)` is the start position; horizontal
|
||||
// alignment shifts the run after shaping. Returns the advance width that
|
||||
// was written. No line-wrap, no kerning — same shaping rules as
|
||||
// UIRenderer::ShapeText (which this calls).
|
||||
float DrawText(UIBuffer& buf, std::string_view text,
|
||||
float x, float baselineY,
|
||||
Font& font, float fontSize,
|
||||
std::array<float, 4> color,
|
||||
TextAlign align = TextAlign::Left);
|
||||
|
||||
// Sampled image quad. The texture and sampler must already have heap
|
||||
// slots (UIRenderer::RegisterImage / RegisterSampler). `uv` is
|
||||
// {u0, v0, u1, v1} into the source texture; defaults to the full image.
|
||||
void DrawImage(UIBuffer& buf, Rect r,
|
||||
std::uint32_t textureSlot, std::uint32_t samplerSlot,
|
||||
std::array<float, 4> tint = {1, 1, 1, 1},
|
||||
std::array<float, 4> uv = {0, 0, 1, 1});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,4 +41,5 @@ export import :RTPass;
|
|||
export import :FontAtlas;
|
||||
export import :ComputeShader;
|
||||
export import :UI;
|
||||
export import :UIComponents;
|
||||
export import :UIComponents;
|
||||
export import :InputField;
|
||||
Loading…
Add table
Add a link
Reference in a new issue