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