FreeRDP
SDL3/dialogs/sdl_selectlist.cpp
1 #include <cassert>
2 #include <winpr/cast.h>
3 #include "sdl_selectlist.hpp"
4 
5 static const Uint32 vpadding = 5;
6 
7 SdlSelectList::SdlSelectList(const std::string& title, const std::vector<std::string>& labels)
8  : _window(nullptr), _renderer(nullptr)
9 {
10  const size_t widget_height = 50;
11  const size_t widget_width = 600;
12 
13  const size_t total_height = labels.size() * (widget_height + vpadding) + vpadding;
14  const size_t height = total_height + widget_height;
15  assert(widget_width <= INT32_MAX);
16  assert(height <= INT32_MAX);
17  auto rc = SDL_CreateWindowAndRenderer(
18  title.c_str(), static_cast<int>(widget_width), static_cast<int>(height),
19  SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS, &_window,
20  &_renderer);
21  if (rc != 0)
22  widget_log_error(rc, "SDL_CreateWindowAndRenderer");
23  else
24  {
25  SDL_FRect rect = { 0, 0, widget_width, widget_height };
26  for (auto& label : labels)
27  {
28  _list.emplace_back(_renderer, label, rect);
29  rect.y += widget_height + vpadding;
30  }
31 
32  const std::vector<int> buttonids = { INPUT_BUTTON_ACCEPT, INPUT_BUTTON_CANCEL };
33  const std::vector<std::string> buttonlabels = { "accept", "cancel" };
34  _buttons.populate(_renderer, buttonlabels, buttonids, widget_width,
35  static_cast<Sint32>(total_height), static_cast<Sint32>(widget_width / 2),
36  static_cast<Sint32>(widget_height));
37  _buttons.set_highlight(0);
38  }
39 }
40 
41 SdlSelectList::~SdlSelectList()
42 {
43  _list.clear();
44  _buttons.clear();
45  SDL_DestroyRenderer(_renderer);
46  SDL_DestroyWindow(_window);
47 }
48 
49 int SdlSelectList::run()
50 {
51  int res = -2;
52  ssize_t CurrentActiveTextInput = 0;
53  bool running = true;
54 
55  if (!_window || !_renderer)
56  return -2;
57  try
58  {
59  while (running)
60  {
61  if (!clear_window(_renderer))
62  throw;
63 
64  if (!update_text())
65  throw;
66 
67  if (!_buttons.update(_renderer))
68  throw;
69 
70  SDL_Event event = {};
71  SDL_WaitEvent(&event);
72  switch (event.type)
73  {
74  case SDL_EVENT_KEY_DOWN:
75  switch (event.key.key)
76  {
77  case SDLK_UP:
78  case SDLK_BACKSPACE:
79  if (CurrentActiveTextInput > 0)
80  CurrentActiveTextInput--;
81  else if (_list.empty())
82  CurrentActiveTextInput = 0;
83  else
84  CurrentActiveTextInput =
85  WINPR_ASSERTING_INT_CAST(ssize_t, _list.size()) - 1;
86  break;
87  case SDLK_DOWN:
88  case SDLK_TAB:
89  if ((CurrentActiveTextInput < 0) || _list.empty())
90  CurrentActiveTextInput = 0;
91  else
92  CurrentActiveTextInput++;
93  CurrentActiveTextInput =
94  CurrentActiveTextInput %
95  WINPR_ASSERTING_INT_CAST(ssize_t, _list.size());
96  break;
97  case SDLK_RETURN:
98  case SDLK_RETURN2:
99  case SDLK_KP_ENTER:
100  running = false;
101  res = static_cast<int>(CurrentActiveTextInput);
102  break;
103  case SDLK_ESCAPE:
104  running = false;
105  res = INPUT_BUTTON_CANCEL;
106  break;
107  default:
108  break;
109  }
110  break;
111  case SDL_EVENT_MOUSE_MOTION:
112  {
113  ssize_t TextInputIndex = get_index(event.button);
114  reset_mouseover();
115  if (TextInputIndex >= 0)
116  {
117  auto& cur = _list[WINPR_ASSERTING_INT_CAST(size_t, TextInputIndex)];
118  if (!cur.set_mouseover(_renderer, true))
119  throw;
120  }
121 
122  _buttons.set_mouseover(event.button.x, event.button.y);
123  }
124  break;
125  case SDL_EVENT_MOUSE_BUTTON_DOWN:
126  {
127  auto button = _buttons.get_selected(event.button);
128  if (button)
129  {
130  running = false;
131  if (button->id() == INPUT_BUTTON_CANCEL)
132  res = INPUT_BUTTON_CANCEL;
133  else
134  res = static_cast<int>(CurrentActiveTextInput);
135  }
136  else
137  {
138  CurrentActiveTextInput = get_index(event.button);
139  }
140  }
141  break;
142  case SDL_EVENT_QUIT:
143  res = INPUT_BUTTON_CANCEL;
144  running = false;
145  break;
146  default:
147  break;
148  }
149 
150  reset_highlight();
151  if (CurrentActiveTextInput >= 0)
152  {
153  auto& cur = _list[WINPR_ASSERTING_INT_CAST(size_t, CurrentActiveTextInput)];
154  if (!cur.set_highlight(_renderer, true))
155  throw;
156  }
157 
158  SDL_RenderPresent(_renderer);
159  }
160  }
161  catch (...)
162  {
163  return -1;
164  }
165  return res;
166 }
167 
168 ssize_t SdlSelectList::get_index(const SDL_MouseButtonEvent& button)
169 {
170  const auto x = button.x;
171  const auto y = button.y;
172  for (size_t i = 0; i < _list.size(); i++)
173  {
174  auto& cur = _list[i];
175  auto r = cur.rect();
176 
177  if ((x >= r.x) && (x <= r.x + r.w) && (y >= r.y) && (y <= r.y + r.h))
178  return WINPR_ASSERTING_INT_CAST(ssize_t, i);
179  }
180  return -1;
181 }
182 
183 bool SdlSelectList::update_text()
184 {
185  for (auto& cur : _list)
186  {
187  if (!cur.update_text(_renderer))
188  return false;
189  }
190 
191  return true;
192 }
193 
194 void SdlSelectList::reset_mouseover()
195 {
196  for (auto& cur : _list)
197  {
198  cur.set_mouseover(_renderer, false);
199  }
200 }
201 
202 void SdlSelectList::reset_highlight()
203 {
204  for (auto& cur : _list)
205  {
206  cur.set_highlight(_renderer, false);
207  }
208 }