6/////////////////////////////////////////////////
7template <typename Type>
12 const WidgetOptions& options)
14 // initialize widget if and position
15 const auto name = initializeActivable ("Slider");
16 const auto position = computeRelativePosition (options.displacement);
18 // get status of the widget
19 auto dimVector = options.size;
20 if (options.horizontal) {
21 std::swap (dimVector.x, dimVector.y);
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));
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);
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});
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);
45/////////////////////////////////////////////////
46template <typename Type>
47Type Gui::sliderValue (
48 const sf::FloatRect& box,
51 const bool horizontal)
53 // compute shift from x or y relative position
54 const auto relativePos = mInputState.mousePosition - box.position;
56 return sgui::remap (0.f, box.size.x, min, max, relativePos.x);
58 return sgui::remap (0.f, box.size.y, min, max, relativePos.y);
65/////////////////////////////////////////////////
66template <typename Type>
67void Gui::inputNumber (
69 const WidgetOptions& options,
72 const std::string& label,
73 const bool fixedWidth)
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);
84 // compute text box dimension
85 auto width = textSize (label + "10000").x;
87 const auto numStr = formatNumberToString (number);
88 width = std::max (width, textSize (label + numStr).x);
90 const auto boxSize = sf::Vector2f (width + 4.f*mPadding.x, textHeight ());
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 ();
101 mGuiState.keyboardFocus = name;
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);
111 state = ItemState::Active;
112 numStr = mActiveInputNumberStr;
114 numStr = formatNumberToString (number);
116 mRender.draw (box, drawOptions ({Widget::TextBox, Slices::Three, state}, options.aspect));
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);
124 const auto labelShift = sf::Vector2f (textSize (label).x, 0.f);
125 drawTextCursor (position + shiftToCenter + labelShift, name, numStr, {});
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});
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)
143 inputNumber (vector.x, {options.displacement}, min.x, max.x, "x: ", true);
145 inputNumber (vector.y, {}, min.y, max.y, "y: ", true);
148 if (options.description != "") {
150 auto descriptionOptions = options;
151 descriptionOptions.displacement = {};
152 text (options.description, {}, descriptionOptions);
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)
164 inputNumber (vector.x, {options.displacement}, min.x, max.x, "x: ", true);
166 inputNumber (vector.y, {}, min.y, max.y, "y: ", true);
168 inputNumber (vector.z, {}, min.z, max.z, "z: ", true);
171 if (options.description != "") {
173 auto descriptionOptions = options;
174 descriptionOptions.displacement = {};
175 text (options.description, {}, descriptionOptions);
179/////////////////////////////////////////////////
180template <typename Type>
181void Gui::handleNumberKeyInput (
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);
199 // if enter is pressed we lost focus
201 mGuiState.keyboardFocus = NullID;
206/////////////////////////////////////////////////
207template <typename Type>
208Type Gui::convertKeyIntoNumber (
211 const Type max) const
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") {
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);
226 number = std::stoul (key);
228 // clamp value if min and max are furnished and valid
230 number = sgui::clamp (min, max, number);
231 key = formatNumberToString (number);
234 // return computed number
238/////////////////////////////////////////////////
239template <typename Type>
240std::string Gui::formatNumberToString (const Type& number) const
242 if constexpr (std::is_floating_point_v <Type>) {
243 if (number < 1000.f) {
244 return fmt::format ("{:7.2f}", number);
246 return fmt::format ("{:7.2}", number);
248 return fmt::format ("{}", number);