KTech 1.1.0
C++ 2D terminal game engine library
Loading...
Searching...
No Matches
intfield.hpp
1/*
2 KTech, Kaup's C++ 2D terminal game engine library.
3 Copyright (C) 2023-2025 Ethan Kaufman (AKA Kaup)
4
5 This file is part of KTech.
6
7 KTech is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 any later version.
11
12 KTech is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with KTech. If not, see <https://www.gnu.org/licenses/>.
19*/
20
21#pragma once
22
23#include "../ktech.hpp"
24
28class IntField : public KTech::Widget
29{
30public:
31 size_t m_number = 0;
32
33 std::function<void()> m_OnInsert;
34
52 std::function<void()> OnInsert,
53 size_t min,
54 size_t max,
55 const std::string& defaultNum = "0",
56 KTech::Point position = {0, 0},
57 const std::string& text = "Value = ",
58 bool withFrame = false,
59 KTech::RGBA unselected = KTech::RGBAColors::gray,
60 KTech::RGBA selected = KTech::RGBAColors::white)
61 : Widget(engine, ui, position), m_OnInsert(std::move(OnInsert)), m_min(min), m_max(max), m_unselectedRGBA(unselected), m_selectedRGBA(selected)
62 {
63 // Find max allowed digits
64 for (size_t i = 1; max / i > 0; i *= 10)
65 {
66 m_maxDigits++;
67 }
68 // Find min allowed digits
69 for (size_t i = 1; min / i > 0; i *= 10)
70 {
71 m_minDigits++;
72 }
73 // Textures
74 SetText(text, withFrame);
75 // Default value
76 SetValue(defaultNum);
77 // Input handlers
78 m_callbackGroup.RegisterRangedCallback('0', '9', [this]() -> bool { return Insert(); });
79 m_callbackGroup.RegisterCallback(KTech::Keys::backspace, [this]() -> bool { return Insert(); });
80 m_callbackGroup.RegisterCallback(KTech::Keys::delete_, [this]() -> bool { return Insert(); });
81 }
82
89 void SetText(const std::string& text, bool withFrame)
90 {
91 KTech::RGBA tempRGBA = (m_selected ? m_selectedRGBA : m_unselectedRGBA);
92 if (withFrame)
93 {
94 m_textures.resize(TEXTURES_SIZE_FRAMED);
95 m_textures[ti_topLeftCorner].Simple(KTech::UPoint(1, 1), KTech::CellA('#', tempRGBA), KTech::Point(0, 0));
96 m_textures[ti_topRightCorner].Simple(KTech::UPoint(1, 1), KTech::CellA('#', tempRGBA), KTech::Point(1 + text.length() + m_maxDigits, 0));
97 m_textures[ti_bottomLeftCorner].Simple(KTech::UPoint(1, 1), KTech::CellA('#', tempRGBA), KTech::Point(0, 2));
98 m_textures[ti_bottomRightCorner].Simple(KTech::UPoint(1, 1), KTech::CellA('#', tempRGBA), KTech::Point(1 + text.length() + m_maxDigits, 2));
99 m_textures[ti_topFrame].Simple(KTech::UPoint(text.length() + m_maxDigits, 1), KTech::CellA('-', tempRGBA), KTech::Point(1, 0));
100 m_textures[ti_leftFrame].Simple(KTech::UPoint(1, 1), KTech::CellA('|', tempRGBA), KTech::Point(0, 1));
101 m_textures[ti_bottomFrame].Simple(KTech::UPoint(text.length() + m_maxDigits, 1), KTech::CellA('-', tempRGBA), KTech::Point(1, 2));
102 m_textures[ti_rightFrame].Simple(KTech::UPoint(1, 1), KTech::CellA('|', tempRGBA), KTech::Point(1 + text.length() + m_maxDigits, 1));
103 }
104 else
105 {
106 m_textures.resize(TEXTURES_SIZE_FRAMELESS);
107 }
108 m_textures[ti_input].m_rPos = KTech::Point(1 + text.length(), 1);
109 m_textures[ti_text].Write({text}, tempRGBA, KTech::RGBAColors::transparent, KTech::Point(1, 1));
110 }
111
116 void SetValue(const std::string& number)
117 {
118 m_currentDigit = 0;
119 m_number = 0;
120 KTech::RGBA tempRGBA = (m_selected ? m_selectedRGBA : m_unselectedRGBA);
121 m_textures[ti_input].Rectangle(KTech::UPoint(m_maxDigits, 1), KTech::CellA(' ', tempRGBA));
122 for (size_t x = 0; x < m_maxDigits && x < number.length(); x++)
123 {
124 m_currentDigit++;
125 m_number *= 10;
126 m_number += number[x] - '0';
127 m_textures[ti_input](x, 0) = KTech::CellA(number[x], tempRGBA);
128 }
129 m_visibleNumber = m_number;
130 }
131
132private:
133 enum TextureIndex : size_t
134 {
135 ti_input,
136 ti_text,
137 TEXTURES_SIZE_FRAMELESS,
138 ti_topLeftCorner = TEXTURES_SIZE_FRAMELESS,
139 ti_topRightCorner,
140 ti_bottomLeftCorner,
141 ti_bottomRightCorner,
142 ti_topFrame,
143 ti_leftFrame,
144 ti_bottomFrame,
145 ti_rightFrame,
146 TEXTURES_SIZE_FRAMED
147 };
148
149 KTech::RGBA m_unselectedRGBA, m_selectedRGBA;
150 size_t m_visibleNumber = 0;
151 uint32_t m_min, m_max;
152 uint8_t m_maxDigits = 0, m_minDigits = 0;
153 uint8_t m_currentDigit = 0;
154
155 void OnSelect() override
156 {
157 RenderSelected();
158 }
159
160 void OnDeselect() override
161 {
162 RenderUnselected();
163 }
164
165 auto Insert() -> bool
166 {
167 if (engine.input.Is(KTech::Keys::backspace) || engine.input.Is(KTech::Keys::delete_))
168 {
169 if (m_currentDigit == 0)
170 {
171 return false;
172 }
173
174 m_visibleNumber /= 10;
175
176 m_currentDigit--;
177 m_textures[ti_input](m_currentDigit, 0).c = ' ';
178 }
179 else if (engine.input.Between('0', '9'))
180 {
181 if (m_currentDigit == m_maxDigits)
182 {
183 return false;
184 }
185
186 m_textures[ti_input](m_currentDigit, 0).c = engine.input.input.at(0);
187 m_currentDigit++;
188
189 m_visibleNumber = m_visibleNumber * 10 + engine.input.GetInt();
190 }
191
192 if (m_visibleNumber < m_min)
193 {
194 m_number = m_min;
195 }
196 else if (m_visibleNumber > m_max)
197 {
198 m_number = m_max;
199 }
200 else
201 {
202 m_number = m_visibleNumber;
203 }
204
205 if (m_OnInsert)
206 {
207 m_OnInsert();
208 }
209 return true;
210 }
211
212 void RenderSelected()
213 {
214 for (KTech::Texture& texture : m_textures)
215 {
216 texture.Transform([&](KTech::CellA& cell){ cell.f = m_selectedRGBA; });
217 }
218 }
219
220 void RenderUnselected()
221 {
222 // Correct visible number
223 if (m_visibleNumber < m_min)
224 {
225 m_visibleNumber = m_min;
226
227 m_currentDigit = m_minDigits;
228
229 std::string newTexture = std::to_string(m_min);
230 newTexture.resize(m_maxDigits, ' ');
231 m_textures[ti_input].Write({ newTexture }, m_unselectedRGBA, KTech::RGBA(), m_textures[0].m_rPos);
232 }
233 else if (m_visibleNumber > m_max)
234 {
235 m_visibleNumber = m_max;
236
237 m_currentDigit = m_maxDigits;
238
239 std::string newTexture = std::to_string(m_max);
240 newTexture.resize(m_maxDigits, ' ');
241 m_textures[ti_input].Write({ newTexture }, m_unselectedRGBA, KTech::RGBA(), m_textures[0].m_rPos);
242 }
243 else if (m_visibleNumber >= m_min && m_visibleNumber <= m_max)
244 {
245 m_number = m_visibleNumber;
246 }
247
248 // Change color
249 for (KTech::Texture& texture : m_textures)
250 {
251 texture.Transform([&](KTech::CellA& cell){ cell.f = m_unselectedRGBA; });
252 }
253 }
254};
Widget for entering a number.
Definition intfield.hpp:29
void SetValue(const std::string &number)
Change the entered number.
Definition intfield.hpp:116
std::function< void()> m_OnInsert
Function to call when the user inserts or removes a digit.
Definition intfield.hpp:33
size_t m_number
The entered number adjusted to given maximum and minimum limits.
Definition intfield.hpp:31
IntField(KTech::Engine &engine, KTech::ID< KTech::UI > ui, std::function< void()> OnInsert, size_t min, size_t max, const std::string &defaultNum="0", KTech::Point position={0, 0}, const std::string &text="Value = ", bool withFrame=false, KTech::RGBA unselected=KTech::RGBAColors::gray, KTech::RGBA selected=KTech::RGBAColors::white)
Construct an IntField.
Definition intfield.hpp:50
void SetText(const std::string &text, bool withFrame)
Change the displayed text.
Definition intfield.hpp:89
Complete engine containing all engine components.
Definition engine.hpp:41
Input input
Input engine component.
Definition engine.hpp:47
void RegisterCallback(const std::string &stringKey, const std::function< bool()> &callback)
Register a function to be called back when an input is received.
Definition callbackgroup.cpp:70
void RegisterRangedCallback(char start, char end, const std::function< bool()> &callback)
Register a function to be called back when an input within a characters range is received.
Definition callbackgroup.cpp:106
auto Between(char start, char end) const -> bool
Checks if input is between range of characters.
Definition input.cpp:119
auto Is(const std::string &stringKey) const -> bool
Checks if input equals given string.
Definition input.cpp:54
std::string input
Input for the last-called callback function.
Definition input.hpp:48
auto GetInt() const -> uint8_t
Get the first character of input as a 1-digit number.
Definition input.cpp:79
World structure that comprises Textures, behaves as a user interface element, and exists within UI.
Definition widget.hpp:45
Engine & engine
Parent Engine.
Definition widget.hpp:57
std::vector< Texture > m_textures
Comprising Textures.
Definition widget.hpp:67
Input::CallbackGroup m_callbackGroup
Group of all input callbacks, which are enabled and disabled in correspondence to Widget::m_selected.
Definition widget.hpp:68
Widget(Engine &engine, Point position=Point(0, 0), std::string name="")
Construct a Widget.
Definition widget.cpp:34
bool m_selected
true: player input reaches the Widget. false: player input doesn't.
Definition widget.hpp:63
Like Cell, but with RGBA foreground and background colors, instead of RGB.
Definition cella.hpp:32
RGBA f
Foreground (character) color.
Definition cella.hpp:33
Serializable world structure identifier.
Definition id.hpp:38
2D vector, mostly used to store positions and directions.
Definition point.hpp:30
Like RGB, but also has an alpha channel representing transparency.
Definition rgba.hpp:30
A CellA-based sprite.
Definition texture.hpp:48
Unsigned 2D vector, mostly used to store sizes and 2D indexes.
Definition upoint.hpp:29