FreeRDP
Loading...
Searching...
No Matches
SDL3/dialogs/sdl_widget.cpp
1
20#include <cassert>
21#include <cstdio>
22#include <cstdlib>
23#include <algorithm>
24
25#include <SDL3/SDL.h>
26#include <SDL3_ttf/SDL_ttf.h>
27
28#include "sdl_widget.hpp"
29#include "../sdl_utils.hpp"
30
31#include "res/sdl3_resource_manager.hpp"
32
33#include <freerdp/log.h>
34
35#if defined(WITH_SDL_IMAGE_DIALOGS)
36#include <SDL3_image/SDL_image.h>
37#endif
38
39#define TAG CLIENT_TAG("SDL.widget")
40
41static const SDL_Color backgroundcolor = { 0x38, 0x36, 0x35, 0xff };
42
43static const Uint32 hpadding = 10;
44
45SdlWidget::SdlWidget([[maybe_unused]] SDL_Renderer* renderer, const SDL_FRect& rect, bool input)
46 : _rect(rect), _input(input)
47{
48 assert(renderer);
49
51 "OpenSans-VariableFont_wdth,wght.ttf");
52 if (!ops)
53 widget_log_error(false, "SDLResourceManager::get");
54 else
55 {
56 _font = TTF_OpenFontIO(ops, true, 64);
57 if (!_font)
58 widget_log_error(false, "TTF_OpenFontRW");
59 }
60}
61
62#if defined(WITH_SDL_IMAGE_DIALOGS)
63SdlWidget::SdlWidget(SDL_Renderer* renderer, const SDL_FRect& rect, SDL_IOStream* ops) : _rect(rect)
64{
65 if (ops)
66 {
67 _image = IMG_LoadTexture_IO(renderer, ops, 1);
68 if (!_image)
69 widget_log_error(false, "IMG_LoadTexture_IO");
70 }
71}
72#endif
73
74SdlWidget::SdlWidget(SdlWidget&& other) noexcept
75 : _font(other._font), _image(other._image), _rect(other._rect), _input(other._input),
76 _wrap(other._wrap), _text_width(other._text_width)
77{
78 other._font = nullptr;
79 other._image = nullptr;
80}
81
82SDL_Texture* SdlWidget::render_text(SDL_Renderer* renderer, const std::string& text,
83 SDL_Color fgcolor, SDL_FRect& src, SDL_FRect& dst)
84{
85 auto surface = TTF_RenderText_Blended(_font, text.c_str(), 0, fgcolor);
86 if (!surface)
87 {
88 widget_log_error(false, "TTF_RenderText_Blended");
89 return nullptr;
90 }
91
92 auto texture = SDL_CreateTextureFromSurface(renderer, surface);
93 SDL_DestroySurface(surface);
94 if (!texture)
95 {
96 widget_log_error(false, "SDL_CreateTextureFromSurface");
97 return nullptr;
98 }
99
100 std::unique_ptr<TTF_TextEngine, decltype(&TTF_DestroySurfaceTextEngine)> engine(
101 TTF_CreateRendererTextEngine(renderer), TTF_DestroySurfaceTextEngine);
102 if (!engine)
103 {
104 widget_log_error(false, "TTF_CreateRendererTextEngine");
105 return nullptr;
106 }
107
108 std::unique_ptr<TTF_Text, decltype(&TTF_DestroyText)> txt(
109 TTF_CreateText(engine.get(), _font, text.c_str(), text.size()), TTF_DestroyText);
110
111 if (!txt)
112 {
113 widget_log_error(false, "TTF_CreateText");
114 return nullptr;
115 }
116 int w = 0;
117 int h = 0;
118 if (!TTF_GetTextSize(txt.get(), &w, &h))
119 {
120 widget_log_error(false, "TTF_GetTextSize");
121 return nullptr;
122 }
123
124 src.w = static_cast<float>(w);
125 src.h = static_cast<float>(h);
126 /* Do some magic:
127 * - Add padding before and after text
128 * - if text is too long only show the last elements
129 * - if text is too short only update used space
130 */
131 dst = _rect;
132 dst.x += hpadding;
133 dst.w -= 2 * hpadding;
134 const float scale = dst.h / src.h;
135 const float sws = (src.w) * scale;
136 const float dws = (dst.w) / scale;
137 dst.w = std::min(dst.w, sws);
138 if (src.w > dws)
139 {
140 src.x = src.w - dws;
141 src.w = dws;
142 }
143 return texture;
144}
145
146static float scale(float dw, float dh)
147{
148 const auto scale = dh / dw;
149 const auto dr = dh * scale;
150 return dr;
151}
152
153SDL_Texture* SdlWidget::render_text_wrapped(SDL_Renderer* renderer, const std::string& text,
154 SDL_Color fgcolor, SDL_FRect& src, SDL_FRect& dst)
155{
156 assert(_text_width < INT32_MAX);
157
158 auto surface = TTF_RenderText_Blended_Wrapped(_font, text.c_str(), 0, fgcolor,
159 static_cast<int>(_text_width));
160 if (!surface)
161 {
162 widget_log_error(false, "TTF_RenderText_Blended");
163 return nullptr;
164 }
165
166 src.w = static_cast<float>(surface->w);
167 src.h = static_cast<float>(surface->h);
168
169 auto texture = SDL_CreateTextureFromSurface(renderer, surface);
170 SDL_DestroySurface(surface);
171 if (!texture)
172 {
173 widget_log_error(false, "SDL_CreateTextureFromSurface");
174 return nullptr;
175 }
176
177 /* Do some magic:
178 * - Add padding before and after text
179 * - if text is too long only show the last elements
180 * - if text is too short only update used space
181 */
182 dst = _rect;
183 dst.x += hpadding;
184 dst.w -= 2 * hpadding;
185 auto dh = scale(src.w, src.h);
186 dst.h = std::min<float>(dh, dst.h);
187
188 return texture;
189}
190
191SdlWidget::~SdlWidget()
192{
193 TTF_CloseFont(_font);
194 if (_image)
195 SDL_DestroyTexture(_image);
196}
197
198bool SdlWidget::error_ex(bool success, const char* what, const char* file, size_t line,
199 const char* fkt)
200{
201 if (success)
202 {
203 // Flip SDL3 convention to existing code convention to minimize code changes
204 return false;
205 }
206 static wLog* log = nullptr;
207 if (!log)
208 log = WLog_Get(TAG);
209 // Use -1 as it indicates error similar to SDL2 conventions
210 // sdl_log_error_ex treats any value other than 0 as SDL error
211 return sdl_log_error_ex(-1, log, what, file, line, fkt);
212}
213
214static bool draw_rect(SDL_Renderer* renderer, const SDL_FRect* rect, SDL_Color color)
215{
216 const auto drc = SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
217 if (widget_log_error(drc, "SDL_SetRenderDrawColor"))
218 return false;
219
220 const auto rc = SDL_RenderFillRect(renderer, rect);
221 return !widget_log_error(rc, "SDL_RenderFillRect");
222}
223
224bool SdlWidget::fill(SDL_Renderer* renderer, SDL_Color color)
225{
226 std::vector<SDL_Color> colors = { color };
227 return fill(renderer, colors);
228}
229
230bool SdlWidget::fill(SDL_Renderer* renderer, const std::vector<SDL_Color>& colors)
231{
232 assert(renderer);
233 SDL_BlendMode mode = SDL_BLENDMODE_INVALID;
234 SDL_GetRenderDrawBlendMode(renderer, &mode);
235 SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
236 for (auto color : colors)
237 {
238 draw_rect(renderer, &_rect, color);
239 SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_ADD);
240 }
241 SDL_SetRenderDrawBlendMode(renderer, mode);
242 return true;
243}
244
245bool SdlWidget::update_text(SDL_Renderer* renderer, const std::string& text, SDL_Color fgcolor,
246 SDL_Color bgcolor)
247{
248 assert(renderer);
249
250 if (!fill(renderer, bgcolor))
251 return false;
252 return update_text(renderer, text, fgcolor);
253}
254
255bool SdlWidget::wrap() const
256{
257 return _wrap;
258}
259
260bool SdlWidget::set_wrap(bool wrap, size_t width)
261{
262 _wrap = wrap;
263 _text_width = width;
264 return _wrap;
265}
266
267const SDL_FRect& SdlWidget::rect() const
268{
269 return _rect;
270}
271
272bool SdlWidget::update_text(SDL_Renderer* renderer, const std::string& text, SDL_Color fgcolor)
273{
274
275 if (text.empty())
276 return true;
277
278 SDL_FRect src{};
279 SDL_FRect dst{};
280
281 SDL_Texture* texture = nullptr;
282 if (_image)
283 {
284 texture = _image;
285 dst = _rect;
286 auto propId = SDL_GetTextureProperties(_image);
287 auto w = SDL_GetNumberProperty(propId, SDL_PROP_TEXTURE_WIDTH_NUMBER, -1);
288 auto h = SDL_GetNumberProperty(propId, SDL_PROP_TEXTURE_HEIGHT_NUMBER, -1);
289 if (w < 0 || h < 0)
290 widget_log_error(false, "SDL_GetTextureProperties");
291 src.w = static_cast<float>(w);
292 src.h = static_cast<float>(h);
293 }
294 else if (_wrap)
295 texture = render_text_wrapped(renderer, text, fgcolor, src, dst);
296 else
297 texture = render_text(renderer, text, fgcolor, src, dst);
298 if (!texture)
299 return false;
300
301 const auto rc = SDL_RenderTexture(renderer, texture, &src, &dst);
302 if (!_image)
303 SDL_DestroyTexture(texture);
304
305 return !widget_log_error(rc, "SDL_RenderCopy");
306}
307
308bool clear_window(SDL_Renderer* renderer)
309{
310 assert(renderer);
311
312 const auto drc = SDL_SetRenderDrawColor(renderer, backgroundcolor.r, backgroundcolor.g,
313 backgroundcolor.b, backgroundcolor.a);
314 if (widget_log_error(drc, "SDL_SetRenderDrawColor"))
315 return false;
316
317 const auto rcls = SDL_RenderClear(renderer);
318 return !widget_log_error(rcls, "SDL_RenderClear");
319}
static SDL_IOStream * get(const std::string &type, const std::string &id)
static std::string typeFonts()