Smolgui
Immediate gui library based on SFML
Loading...
Searching...
No Matches
Gui.tpp
Go to the documentation of this file.
1namespace sgui {
2
3/**
4 * slider
5 */
6/////////////////////////////////////////////////
7template <typename Type>
8void Gui::slider (
9 Type& value,
10 const Type min,
11 const Type max,
12 const WidgetOptions& options)
13{
14 // initialize widget if and position
15 const auto name = initializeActivable ("Slider");
16 const auto position = computeRelativePosition (options.displacement);
17
18 // get status of the widget
19 auto dimVector = options.size;
20 if (options.horizontal) {
21 std::swap (dimVector.x, dimVector.y);
22 }
23 const auto size = textHeight () * dimVector;
24 const auto box = sf::FloatRect (position, size);
25 auto state = itemStatus (box, name, mInputState.mouseLeftDown, options.tooltip);
26 mRender.draw (box, drawOptions ({Widget::Slider, Slices::Three, state}, options.aspect, !options.horizontal));
27
28 // if active, update value depending on bar position
29 if (mGuiState.activeItem == name) {
30 state = ItemState::Active;
31 value = sliderValue (box, min, max, !options.horizontal);
32 }
33
34 // draw text next to the slider
35 const auto descrPos = position + sf::Vector2f (size.x + mPadding.x, 0.f);
36 const auto descrSize = widgetDescription (descrPos, options.description);
37 // update cursor position
38 updateSpacing ({size.x + descrSize.x, size.y});
39
40 // compute scrollBar relative position
41 const auto percent = sgui::remap (min, max, 0.05f, 0.95f, value);
42 sliderBar (box, state, percent, !options.horizontal);
43}
44
45/////////////////////////////////////////////////
46template <typename Type>
47Type Gui::sliderValue (
48 const sf::FloatRect& box,
49 const Type min,
50 const Type max,
51 const bool horizontal)
52{
53 // compute shift from x or y relative position
54 const auto relativePos = mInputState.mousePosition - box.position;
55 if (horizontal) {
56 return sgui::remap (0.f, box.size.x, min, max, relativePos.x);
57 }
58 return sgui::remap (0.f, box.size.y, min, max, relativePos.y);
59}
60
61
62/**
63 * input
64 */
65/////////////////////////////////////////////////
66template <typename Type>
67void Gui::inputNumber (
68 Type& number,
69 const WidgetOptions& options,
70 const Type min,
71 const Type max,
72 const std::string& label,
73 const bool fixedWidth)
74{
75 // Initialize widget name and position
76 auto numStr = formatNumberToString (number);
77 const auto name = initializeActivable ("NumberInput");
78 const auto position = computeRelativePosition (options.displacement);
79 if (!mTextCursorPositions.has (name)) {
80 mTextCursorPositions.emplace (name, numStr.length ());
81 mTextHasCursor.emplace (name, 1u);
82 }
83
84 // compute text box dimension
85 auto width = textSize (label + "10000").x;
86 if (!fixedWidth) {
87 const auto numStr = formatNumberToString (number);
88 width = std::max (width, textSize (label + numStr).x);
89 }
90 const auto boxSize = sf::Vector2f (width + 4.f*mPadding.x, textHeight ());
91
92 // get status of the widget
93 const auto box = sf::FloatRect (position, boxSize);
94 auto state = itemStatus (box, name, mInputState.mouseLeftDown);
95 // take keyboard focus if active
96 if (mGuiState.activeItem == name) {
97 if (mGuiState.keyboardFocus != name) {
98 mActiveInputNumberStr = formatNumberToString (number);
99 mTextCursorPositions.get (name) = mActiveInputNumberStr.length ();
100 }
101 mGuiState.keyboardFocus = name;
102 }
103
104 // if this widget has keyboard focus update its number
105 auto& cursorIndex = mTextCursorPositions.get (name);
106 const auto focused = mGuiState.keyboardFocus == name;
107 handleNumberKeyInput (number, cursorIndex, focused, min, max);
108
109 // draw text box
110 if (focused) {
111 state = ItemState::Active;
112 numStr = mActiveInputNumberStr;
113 } else {
114 numStr = formatNumberToString (number);
115 }
116 mRender.draw (box, drawOptions ({Widget::TextBox, Slices::Three, state}, options.aspect));
117
118 // draw label and number
119 const auto inputStr = label + numStr;
120 const auto numWidth = textSize (numStr).x;
121 const auto shiftToCenter = sf::Vector2f ((boxSize.x - numWidth) / 2.f - mPadding.x, mPadding.y);
122 handleTextDrawing (position + shiftToCenter, inputStr);
123 if (focused) {
124 const auto labelShift = sf::Vector2f (textSize (label).x, 0.f);
125 drawTextCursor (position + shiftToCenter + labelShift, name, numStr, {});
126 }
127
128 // draw description
129 const auto descrPos = position + sf::Vector2f (boxSize.x + mPadding.x, 0);
130 const auto descrSize = widgetDescription (descrPos, options.description);
131 // update cursor position
132 updateSpacing ({boxSize.x + descrSize.x, boxSize.y});
133}
134
135/////////////////////////////////////////////////
136template <typename Type>
137void Gui::inputVector2 (
138 sf::Vector2<Type>& vector,
139 const WidgetOptions& options,
140 const sf::Vector2<Type>& min,
141 const sf::Vector2<Type>& max)
142{
143 inputNumber (vector.x, {options.displacement}, min.x, max.x, "x: ", true);
144 sameLine ();
145 inputNumber (vector.y, {}, min.y, max.y, "y: ", true);
146
147 // draw description
148 if (options.description != "") {
149 sameLine ();
150 auto descriptionOptions = options;
151 descriptionOptions.displacement = {};
152 text (options.description, {}, descriptionOptions);
153 }
154}
155
156/////////////////////////////////////////////////
157template <typename Type>
158void Gui::inputVector3 (
159 sf::Vector3<Type>& vector,
160 const WidgetOptions& options,
161 const sf::Vector3<Type>& min,
162 const sf::Vector3<Type>& max)
163{
164 inputNumber (vector.x, {options.displacement}, min.x, max.x, "x: ", true);
165 sameLine ();
166 inputNumber (vector.y, {}, min.y, max.y, "y: ", true);
167 sameLine ();
168 inputNumber (vector.z, {}, min.z, max.z, "z: ", true);
169
170 // draw description
171 if (options.description != "") {
172 sameLine ();
173 auto descriptionOptions = options;
174 descriptionOptions.displacement = {};
175 text (options.description, {}, descriptionOptions);
176 }
177}
178
179/////////////////////////////////////////////////
180template <typename Type>
181void Gui::handleNumberKeyInput (
182 Type& number,
183 size_t& cursorIndex,
184 const bool focused,
185 const Type min,
186 const Type max)
187{
188 if (focused && (mInputState.textIsEntered || mInputState.keyIsPressed)) {
189 // if a key is pressed and is a digit handle if
190 const auto key = mInputState.keyPressed;
191 const auto isDigit = std::isdigit (static_cast<unsigned char> (key));
192 const auto validPoint = key == '.' && mActiveInputNumberStr.find (".") == std::string::npos;
193 if (isDigit || validPoint || key == L'\b') {
194 // handle key and convert it into number
195 handleKeyInput (mActiveInputNumberStr, cursorIndex);
196 number = convertKeyIntoNumber <Type> (mActiveInputNumberStr, min, max);
197 cursorIndex = clamp (size_t (0), mActiveInputNumberStr.length (), cursorIndex);
198 }
199 // if enter is pressed we lost focus
200 if (key == L'\n') {
201 mGuiState.keyboardFocus = NullID;
202 }
203 }
204}
205
206/////////////////////////////////////////////////
207template <typename Type>
208Type Gui::convertKeyIntoNumber (
209 std::string& key,
210 const Type min,
211 const Type max) const
212{
213 // convert string into number
214 auto number = Type (0);
215 const auto numPos = key.find_last_of (' ');
216 const auto numStr = key.substr (std::min (numPos + 1, key.length ()));
217 if (numStr != "" && key != "." && key != "0") {
218 // manage type
219 if constexpr (std::is_same_v <Type, int>) {
220 number = std::stoi (key);
221 } else if constexpr (std::is_same_v <Type, float>) {
222 number = std::stof (key);
223 } else if constexpr (std::is_same_v <Type, double>) {
224 number = std::stod (key);
225 } else {
226 number = std::stoul (key);
227 }
228 // clamp value if min and max are furnished and valid
229 if (max > min) {
230 number = sgui::clamp (min, max, number);
231 key = formatNumberToString (number);
232 }
233 }
234 // return computed number
235 return number;
236}
237
238/////////////////////////////////////////////////
239template <typename Type>
240std::string Gui::formatNumberToString (const Type& number) const
241{
242 if constexpr (std::is_floating_point_v <Type>) {
243 if (number < 1000.f) {
244 return fmt::format ("{:7.2f}", number);
245 }
246 return fmt::format ("{:7.2}", number);
247 }
248 return fmt::format ("{}", number);
249}
250
251} // namespace sgui