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