2026-05-03 02:45:38 +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;
|
2026-05-18 02:07:48 +02:00
|
|
|
|
2026-05-03 02:45:38 +02:00
|
|
|
export module Crafter.Graphics:InputField;
|
|
|
|
|
import std;
|
|
|
|
|
import :Types;
|
2026-05-12 00:24:48 +02:00
|
|
|
import :Keys;
|
2026-05-03 02:45:38 +02:00
|
|
|
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
|
2026-05-12 00:24:48 +02:00
|
|
|
// else is ignored. Safe to feed every key the host receives. The KeyCode
|
|
|
|
|
// is the raw platform key (Win32 PS/2 scancode + extended bit; Wayland
|
|
|
|
|
// kernel keycode) — typically the value delivered by
|
|
|
|
|
// `Window::onRawKeyDown`. Internal comparisons use `Key(CrafterKeys::X)`
|
|
|
|
|
// from :Keys so the code stays cross-platform.
|
|
|
|
|
void InputField_OnKey(InputField&, KeyCode);
|
2026-05-03 02:45:38 +02:00
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
}
|