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