FreeRDP
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 
8 static const Uint32 vpadding = 5;
9 
10 SdlInputWidgetList::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 != 0)
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 
48 ssize_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 
73 bool 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 
83 SdlInputWidget* 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 
93 SdlInputWidgetList::~SdlInputWidgetList()
94 {
95  _list.clear();
96  _buttons.clear();
97  SDL_DestroyRenderer(_renderer);
98  SDL_DestroyWindow(_window);
99 }
100 
101 bool 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 
114 ssize_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 
129 int 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 }