/* 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; #ifndef CRAFTER_GRAPHICS_WINDOW_DOM #endif // !CRAFTER_GRAPHICS_WINDOW_DOM export module Crafter.Graphics:InputField; #ifndef CRAFTER_GRAPHICS_WINDOW_DOM import std; import :Types; import :Keys; 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 bg; std::array bgFocused; std::array border; std::array borderFocused; std::array text; std::array 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 std::optional 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. 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); // 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); } #endif // !CRAFTER_GRAPHICS_WINDOW_DOM