FreeRDP
Loading...
Searching...
No Matches
sdl_select_list.cpp
1#include <cassert>
2#include <optional>
3#include <winpr/cast.h>
4#include "sdl_select_list.hpp"
5#include "../sdl_utils.hpp"
6
7static const Uint32 vpadding = 5;
8
9SdlSelectList::SdlSelectList(const std::string& title, const std::vector<std::string>& labels)
10{
11 const size_t widget_height = 50;
12 const size_t widget_width = 600;
13
14 const size_t total_height = labels.size() * (widget_height + vpadding) + vpadding;
15 const size_t height = total_height + widget_height;
16 if (reset(title, widget_width, height))
17 {
18 SDL_FRect rect = { 0, 0, widget_width, widget_height };
19 for (auto& label : labels)
20 {
21 _list.emplace_back(_renderer, label, rect);
22 rect.y += widget_height + vpadding;
23 }
24
25 const std::vector<int> buttonids = { INPUT_BUTTON_ACCEPT, INPUT_BUTTON_CANCEL };
26 const std::vector<std::string> buttonlabels = { "accept", "cancel" };
27 std::ignore = _buttons.populate(
28 _renderer, buttonlabels, buttonids, widget_width, static_cast<Sint32>(total_height),
29 static_cast<Sint32>(widget_width / 2), static_cast<Sint32>(widget_height));
30 _buttons.set_highlight(0);
31 }
32}
33
34SdlSelectList::~SdlSelectList() = default;
35
36int SdlSelectList::run()
37{
38 int res = -2;
39 ssize_t CurrentActiveTextInput = 0;
40 bool running = true;
41 std::optional<SDL_Event> forward;
42
43 if (!_window || !_renderer)
44 return -2;
45 try
46 {
47 while (running)
48 {
49 if (!update())
50 throw std::exception();
51
52 SDL_Event event = {};
53 if (!SDL_WaitEventTimeout(&event, 30))
54 continue;
55 do
56 {
57 switch (event.type)
58 {
59 case SDL_EVENT_KEY_DOWN:
60 switch (event.key.key)
61 {
62 case SDLK_UP:
63 case SDLK_BACKSPACE:
64 if (CurrentActiveTextInput > 0)
65 CurrentActiveTextInput--;
66 else if (_list.empty())
67 CurrentActiveTextInput = 0;
68 else
69 {
70 auto s = _list.size();
71 CurrentActiveTextInput =
72 WINPR_ASSERTING_INT_CAST(ssize_t, s) - 1;
73 }
74 break;
75 case SDLK_DOWN:
76 case SDLK_TAB:
77 if ((CurrentActiveTextInput < 0) || _list.empty())
78 CurrentActiveTextInput = 0;
79 else
80 {
81 auto s = _list.size();
82 CurrentActiveTextInput++;
83 if (s > 0)
84 {
85 CurrentActiveTextInput =
86 CurrentActiveTextInput %
87 WINPR_ASSERTING_INT_CAST(ssize_t, s);
88 }
89 }
90 break;
91 case SDLK_RETURN:
92 case SDLK_RETURN2:
93 case SDLK_KP_ENTER:
94 running = false;
95 res = static_cast<int>(CurrentActiveTextInput);
96 break;
97 case SDLK_ESCAPE:
98 running = false;
99 res = INPUT_BUTTON_CANCEL;
100 break;
101 default:
102 break;
103 }
104 break;
105 case SDL_EVENT_MOUSE_MOTION:
106 {
107 auto TextInputIndex = get_index(event.button);
108 reset_mouseover();
109 if (TextInputIndex >= 0)
110 {
111 auto& cur = _list.at(WINPR_ASSERTING_INT_CAST(size_t, TextInputIndex));
112 if (!cur.mouseover(true))
113 throw std::exception();
114 }
115
116 _buttons.set_mouseover(event.button.x, event.button.y);
117 }
118 break;
119 case SDL_EVENT_MOUSE_BUTTON_DOWN:
120 {
121 auto button = _buttons.get_selected(event.button);
122 if (button)
123 {
124 running = false;
125 if (button->id() == INPUT_BUTTON_CANCEL)
126 res = INPUT_BUTTON_CANCEL;
127 else
128 res = static_cast<int>(CurrentActiveTextInput);
129 }
130 else
131 {
132 CurrentActiveTextInput = get_index(event.button);
133 }
134 }
135 break;
136 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
137 /* see SdlInputWidgetPairList::run() */
138 if (event.window.windowID != SDL_GetWindowID(_window.get()))
139 forward = event;
140 res = INPUT_BUTTON_CANCEL;
141 running = false;
142 break;
143 case SDL_EVENT_WINDOW_OCCLUDED:
144 case SDL_EVENT_WINDOW_MINIMIZED:
145 case SDL_EVENT_WINDOW_DESTROYED:
146 /* Only events of our own window cancel the dialog, see
147 * SdlInputWidgetPairList::run() */
148 if (event.window.windowID != SDL_GetWindowID(_window.get()))
149 break;
150 res = INPUT_BUTTON_CANCEL;
151 running = false;
152 break;
153 case SDL_EVENT_TERMINATING:
154 case SDL_EVENT_QUIT:
155 res = INPUT_BUTTON_CANCEL;
156 running = false;
157 break;
158 default:
159 break;
160 }
161 } while (running && SDL_PollEvent(&event));
162
163 reset_highlight();
164 if (CurrentActiveTextInput >= 0)
165 {
166 auto& cur = _list.at(WINPR_ASSERTING_INT_CAST(size_t, CurrentActiveTextInput));
167 if (!cur.highlight(true))
168 throw std::exception();
169 }
170 }
171
172 /* Drop the input that was meant for the dialog, so it does not reach the session.
173 * Other events, such as window and clipboard events, are still handled. */
174 SDL_PumpEvents();
175 SDL_FlushEvents(SDL_EVENT_KEY_DOWN, SDL_EVENT_MOUSE_REMOVED);
176 SDL_FlushEvents(SDL_EVENT_FINGER_DOWN, SDL_EVENT_FINGER_CANCELED);
177 SDL_FlushEvents(SDL_EVENT_PEN_PROXIMITY_IN, SDL_EVENT_PEN_AXIS);
178 if (forward)
179 std::ignore = SDL_PushEvent(&*forward);
180 }
181 catch (...)
182 {
183 return -1;
184 }
185 return res;
186}
187
188bool SdlSelectList::updateInternal()
189{
190 for (auto& cur : _list)
191 {
192 if (!cur.update())
193 return false;
194 }
195 return true;
196}
197
198ssize_t SdlSelectList::get_index(const SDL_MouseButtonEvent& button)
199{
200 const auto x = button.x;
201 const auto y = button.y;
202 for (size_t i = 0; i < _list.size(); i++)
203 {
204 auto& cur = _list.at(i);
205 auto r = cur.rect();
206
207 if ((x >= r.x) && (x <= r.x + r.w) && (y >= r.y) && (y <= r.y + r.h))
208 return WINPR_ASSERTING_INT_CAST(ssize_t, i);
209 }
210 return -1;
211}
212
213void SdlSelectList::reset_mouseover()
214{
215 for (auto& cur : _list)
216 {
217 cur.mouseover(false);
218 }
219}
220
221void SdlSelectList::reset_highlight()
222{
223 for (auto& cur : _list)
224 {
225 cur.highlight(false);
226 }
227}