FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
SDL3/dialogs/sdl_input_widgets.cpp
1#include <cassert>
2#include <algorithm>
3
4#include <winpr/cast.h>
5
6#include "sdl_input_widgets.hpp"
7
8static const Uint32 vpadding = 5;
9
10SdlInputWidgetList::SdlInputWidgetList(const std::string& title,
11 const std::vector<std::string>& labels,
12 const std::vector<std::string>& initial,
13 const std::vector<Uint32>& flags)
14 : _window(nullptr), _renderer(nullptr)
15{
16 assert(labels.size() == initial.size());
17 assert(labels.size() == flags.size());
18 const std::vector<int> buttonids = { INPUT_BUTTON_ACCEPT, INPUT_BUTTON_CANCEL };
19 const std::vector<std::string> buttonlabels = { "accept", "cancel" };
20
21 const size_t widget_width = 300;
22 const size_t widget_heigth = 50;
23
24 const size_t total_width = widget_width + widget_width;
25 const size_t input_height = labels.size() * (widget_heigth + vpadding) + vpadding;
26 const size_t total_height = input_height + widget_heigth;
27 assert(total_width <= INT32_MAX);
28 assert(total_height <= INT32_MAX);
29 auto rc = SDL_CreateWindowAndRenderer(
30 title.c_str(), total_width, static_cast<int>(total_height),
31 SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS, &_window,
32 &_renderer);
33 if (!rc)
34 widget_log_error(rc, "SDL_CreateWindowAndRenderer");
35 else
36 {
37 for (size_t x = 0; x < labels.size(); x++)
38 _list.emplace_back(_renderer, labels[x], initial[x], flags[x], x, widget_width,
39 widget_heigth);
40
41 _buttons.populate(_renderer, buttonlabels, buttonids, total_width,
42 static_cast<Sint32>(input_height), static_cast<Sint32>(widget_width),
43 static_cast<Sint32>(widget_heigth));
44 _buttons.set_highlight(0);
45 }
46}
47
48ssize_t SdlInputWidgetList::next(ssize_t current)
49{
50 size_t iteration = 0;
51 auto val = static_cast<size_t>(current);
52
53 do
54 {
55 if (iteration >= _list.size())
56 return -1;
57
58 if (iteration == 0)
59 {
60 if (current < 0)
61 val = 0;
62 else
63 val++;
64 }
65 else
66 val++;
67 iteration++;
68 val %= _list.size();
69 } while (!valid(static_cast<ssize_t>(val)));
70 return static_cast<ssize_t>(val);
71}
72
73bool SdlInputWidgetList::valid(ssize_t current) const
74{
75 if (current < 0)
76 return false;
77 auto s = static_cast<size_t>(current);
78 if (s >= _list.size())
79 return false;
80 return !_list[s].readonly();
81}
82
83SdlInputWidget* SdlInputWidgetList::get(ssize_t index)
84{
85 if (index < 0)
86 return nullptr;
87 auto s = static_cast<size_t>(index);
88 if (s >= _list.size())
89 return nullptr;
90 return &_list[s];
91}
92
93SdlInputWidgetList::~SdlInputWidgetList()
94{
95 _list.clear();
96 _buttons.clear();
97 SDL_DestroyRenderer(_renderer);
98 SDL_DestroyWindow(_window);
99}
100
101bool SdlInputWidgetList::update(SDL_Renderer* renderer)
102{
103 for (auto& btn : _list)
104 {
105 if (!btn.update_label(renderer))
106 return false;
107 if (!btn.update_input(renderer))
108 return false;
109 }
110
111 return _buttons.update(renderer);
112}
113
114ssize_t SdlInputWidgetList::get_index(const SDL_MouseButtonEvent& button)
115{
116 const auto x = button.x;
117 const auto y = button.y;
118 for (size_t i = 0; i < _list.size(); i++)
119 {
120 auto& cur = _list[i];
121 auto r = cur.input_rect();
122
123 if ((x >= r.x) && (x <= r.x + r.w) && (y >= r.y) && (y <= r.y + r.h))
124 return WINPR_ASSERTING_INT_CAST(ssize_t, i);
125 }
126 return -1;
127}
128
129int SdlInputWidgetList::run(std::vector<std::string>& result)
130{
131 int res = -1;
132 ssize_t LastActiveTextInput = -1;
133 ssize_t CurrentActiveTextInput = next(-1);
134
135 if (!_window || !_renderer)
136 return -2;
137
138 try
139 {
140 bool running = true;
141 std::vector<SDL_Keycode> pressed;
142 while (running)
143 {
144 if (!clear_window(_renderer))
145 throw;
146
147 if (!update(_renderer))
148 throw;
149
150 if (!_buttons.update(_renderer))
151 throw;
152
153 SDL_Event event = {};
154 SDL_WaitEvent(&event);
155 switch (event.type)
156 {
157 case SDL_EVENT_KEY_UP:
158 {
159 auto it = std::remove(pressed.begin(), pressed.end(), event.key.key);
160 pressed.erase(it, pressed.end());
161
162 switch (event.key.key)
163 {
164 case SDLK_BACKSPACE:
165 {
166 auto cur = get(CurrentActiveTextInput);
167 if (cur)
168 {
169 if (!cur->remove_str(_renderer, 1))
170 throw;
171 }
172 }
173 break;
174 case SDLK_TAB:
175 CurrentActiveTextInput = next(CurrentActiveTextInput);
176 break;
177 case SDLK_RETURN:
178 case SDLK_RETURN2:
179 case SDLK_KP_ENTER:
180 running = false;
181 res = INPUT_BUTTON_ACCEPT;
182 break;
183 case SDLK_ESCAPE:
184 running = false;
185 res = INPUT_BUTTON_CANCEL;
186 break;
187 case SDLK_V:
188 if (pressed.size() == 2)
189 {
190 if ((pressed[0] == SDLK_LCTRL) || (pressed[0] == SDLK_RCTRL))
191 {
192 auto cur = get(CurrentActiveTextInput);
193 if (cur)
194 {
195 auto text = SDL_GetClipboardText();
196 cur->set_str(_renderer, text);
197 }
198 }
199 }
200 break;
201 default:
202 break;
203 }
204 }
205 break;
206 case SDL_EVENT_KEY_DOWN:
207 pressed.push_back(event.key.key);
208 break;
209 case SDL_EVENT_TEXT_INPUT:
210 {
211 auto cur = get(CurrentActiveTextInput);
212 if (cur)
213 {
214 if (!cur->append_str(_renderer, event.text.text))
215 throw;
216 }
217 }
218 break;
219 case SDL_EVENT_MOUSE_MOTION:
220 {
221 auto TextInputIndex = get_index(event.button);
222 for (auto& cur : _list)
223 {
224 if (!cur.set_mouseover(_renderer, false))
225 throw;
226 }
227 if (TextInputIndex >= 0)
228 {
229 auto& cur = _list[static_cast<size_t>(TextInputIndex)];
230 if (!cur.set_mouseover(_renderer, true))
231 throw;
232 }
233
234 _buttons.set_mouseover(event.button.x, event.button.y);
235 }
236 break;
237 case SDL_EVENT_MOUSE_BUTTON_DOWN:
238 {
239 auto val = get_index(event.button);
240 if (valid(val))
241 CurrentActiveTextInput = val;
242
243 auto button = _buttons.get_selected(event.button);
244 if (button)
245 {
246 running = false;
247 if (button->id() == INPUT_BUTTON_CANCEL)
248 res = INPUT_BUTTON_CANCEL;
249 else
250 res = INPUT_BUTTON_ACCEPT;
251 }
252 }
253 break;
254 case SDL_EVENT_QUIT:
255 res = INPUT_BUTTON_CANCEL;
256 running = false;
257 break;
258 default:
259 break;
260 }
261
262 if (LastActiveTextInput != CurrentActiveTextInput)
263 {
264 if (CurrentActiveTextInput < 0)
265 SDL_StopTextInput(_window);
266 else
267 SDL_StartTextInput(_window);
268 LastActiveTextInput = CurrentActiveTextInput;
269 }
270
271 for (auto& cur : _list)
272 {
273 if (!cur.set_highlight(_renderer, false))
274 throw;
275 }
276 auto cur = get(CurrentActiveTextInput);
277 if (cur)
278 {
279 if (!cur->set_highlight(_renderer, true))
280 throw;
281 }
282
283 SDL_RenderPresent(_renderer);
284 }
285
286 for (auto& cur : _list)
287 result.push_back(cur.value());
288 }
289 catch (...)
290 {
291 res = -2;
292 }
293
294 return res;
295}