-
Notifications
You must be signed in to change notification settings - Fork 57
/
cppwindows.h
485 lines (462 loc) · 21.4 KB
/
cppwindows.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
#ifndef CPPWINDOWS_H
#define CPPWINDOWS_H
/*!
* \file
* \ingroup elwindows
* \brief C++ abstraction for client windows
*/
#include <atomic>
#include <cstdint>
#include <functional>
#include "elwindows.h"
#include "exceptions/extendedexception.hpp"
#include "font.h"
//! Concatenate tokens \a a and \a b
#define CONCAT_IMPL(a, b) a ## b
//! Concatenate tokens \a a and \a b
#define CONCAT(a, b) CONCAT_IMPL(a, b)
//! Return the function name for a handler for \a action events
#define HANDLER_NAME(action) CONCAT(action, _handler)
//! Return the function name for a static handler for \a action events
#define STATIC_HANDLER_NAME(action) CONCAT(static_, HANDLER_NAME(action))
//! Return the name of a function that sets the callback handler for events of type \a action
#define SET_HANDLER_NAME(action) CONCAT(set_, HANDLER_NAME(action))
//! Macro for the declaration of a static handler for \a action events
#define DECLARE_STATIC_HANDLER(action, ...) \
static int STATIC_HANDLER_NAME(action)(window_info *win, ##__VA_ARGS__)
//! Extrcat the member function handler for events of type \a action from a \window_info structure
#define MEMBER_HANDLER(action) get_cpp_window(win).HANDLER_NAME(action)
//! Define a static callback handler for \a action events that takes no arguments
#define DEFINE_STATIC_HANDLER0(action) \
DECLARE_STATIC_HANDLER(action) { return MEMBER_HANDLER(action)(); }
//! Define a static callback handler for \a action events that takes a single argument of type T0
#define DEFINE_STATIC_HANDLER1(action, T0) \
DECLARE_STATIC_HANDLER(action, T0 a0) { return MEMBER_HANDLER(action)(a0); }
//! Define a static callback handler for \a action events that takes two arguments of type T0 and T1
#define DEFINE_STATIC_HANDLER2(action, T0, T1) \
DECLARE_STATIC_HANDLER(action, T0 a0, T1 a1) { return MEMBER_HANDLER(action)(a0, a1); }
//! Define a static callback handler for \a action events that takes three argument of type T0, T1, and T2
#define DEFINE_STATIC_HANDLER3(action, T0, T1, T2) \
DECLARE_STATIC_HANDLER(action, T0 a0, T1 a1, T2 a2) { return MEMBER_HANDLER(action)(a0, a1, a2); }
//! Check if class \c Derived ahs a member function named <em>action<tt>_handler</tt>,
#define HAS_HANDLER(action) \
std::is_member_function_pointer<decltype(&Derived::HANDLER_NAME(action))>::value
/*!
* \brief Macro to declare a handler setter
*
* Macro DECLARE_SET_HANDLER declares functions to set the callback function for an event of type
* \a action (identified by \a constant) in a Window instance. The Window<Derived> class uses SFINAE
* to determine which function is used: if an <em>action</em><tt>_handler</tt> member function
* is defined in \c Derived, the function that sets the handler is used, otherwise the variant
* that does nothing is used.
* \param action The event for which a callback setter is defined
* \param constant The associated constant that defines the type of event.
*/
#define DECLARE_SET_HANDLER(action, constant) \
template <typename Derived> \
typename std::enable_if<HAS_HANDLER(action), void>::type \
SET_HANDLER_NAME(action)(Window<Derived> *window) \
{ \
set_window_handler(window->id(), constant, \
(int(*)())&Window<Derived>::STATIC_HANDLER_NAME(action)); \
} \
inline void SET_HANDLER_NAME(action)(...) {}
namespace eternal_lands
{
namespace window_static_handlers
{
int button_click(widget_list *widget, int mx, int my, std::uint32_t flags, void* cb_ptr);
int delete_button_callback(widget_list *widget, void* cb_ptr);
int scrollbar_click(widget_list *widget, int mx, int my, std::uint32_t flags, void* cb_ptr);
int scrollbar_drag(widget_list *widget, int mx, int my, std::uint32_t flags, int dx, int dy, void* cb_ptr);
int delete_scrollbar_callback(widget_list *widget, void* cb_ptr);
} // namespace window_static_handlers
/*!
* \brief Base class for client windows
*
* Class Window is a template base class that represents a client window. The template argument
* \a Derived is the type name of the class deriving from Window. It provides
* access to the underlying data through getter and setter functions, and will automatically
* register event handlers if they are implementedin \a Derived (and are accessible to Window,
* meaning most likely they must be \c public).
*/
template <typename Derived>
class Window
{
public:
/*!
* \brief Constructor
*
* Create a new window with title \a title and size \a width × \a height at position
* (\a x, \a y) on the screen. The window has no parent window. Optional window flags
* can be set through \a flags; these then <em>replace</em> the default window flags entirely,
* so if any default window flags should be retained they should be included in \a flags as
* well.
* \param title The window title
* \param x The x coordinate of the upper left corner of the window
* \param y The y coordinate of the upper left corner of the window
* \param width The width of the window in pixels
* \param height The height of the window in pixels
* \param flags If set, the set of flags controlling th e windows appearance and behaviour
*/
Window(const std::string& title, int x, int y, int width, int height, std::uint32_t flags=ELW_WIN_DEFAULT):
_id(create_window(title.c_str(), -1, 0, x, y, width, height, flags)),
_next_widget_id(0)
{
check_and_set_handlers();
}
/*!
* \brief Constructor
*
* Create a new Window using the ID of a window previously created using create_window.
* \param id The unique identifier of the existing window
*/
Window(int id): _id(id), _next_widget_id(0)
{
check_and_set_handlers();
}
//! Destructor
~Window()
{
destroy_window(id());
}
//! Return a constant reference to the underlying window_info structure
const window_info& window() const { return windows_list.window[_id]; }
//! Return a variable reference to the underlying window_info structure
window_info& window() { return windows_list.window[_id]; }
//! Return a pointer to the underlying window_info structure
window_info* window_ptr() { return &window(); }
//! Return the identifier for this window
int id() const { return _id; }
//! Return the ID of the parent window of this window. Will be < 0 for root windows
int parent_id() const { return window().pos_id; }
//! Return the width in pixels of this window
int width() const { return window().len_x; }
//! Return the height in pixels of this window
int height() const { return window().len_y; }
//! Return the current UI scale factor for this window
float current_scale() const { return window().current_scale; }
//! Return the size (in pixels) of a close box in this window
int box_size() const { return window().box_size; }
//! Return the height of the title bar
int title_height() const { return window().title_height; }
//! Return the default text font category for this window
FontManager::Category font_category() const { return window().font_category; }
//! Return the maximum width of a character in the default sized font for this window
int default_font_max_char_width() const { return window().default_font_max_len_x; }
//! Return the height of a text line in the default sized font for this window
int default_font_height() const { return window().default_font_len_y; }
//! Return the maximum width of a character in the small sized font for this window
int small_font_max_char_width() const { return window().small_font_max_len_x; }
//! Return the height of a text line in the small sized font for this window
int small_font_height() const { return window().small_font_len_y; }
//! Return the width of the widget with ID \a id in this window
int widget_width(int widget_id) const { return widget_get_width(id(), widget_id); }
//! Return the height of the widget with ID \a id in this window
int widget_height(int widget_id) const { return widget_get_height(id(), widget_id); }
//! Return the flags controlling the appearance of the widget with ID \a id in this window
int widget_flags(int widget_id) const
{
widget_list *widget = widget_find(id(), widget_id);
return widget ? widget->Flags : 0;
}
//! Get the content scale factor for widget \a widget_id
float widget_size(int widget_id) const
{
widget_list *widget = widget_find(id(), widget_id);
return widget ? widget->size : 0.0;
}
//! Return the position of the scrolbbar with ID \a scrollbar_id in this window
int scrollbar_position(int scrollbar_id) { return vscrollbar_get_pos(id(), scrollbar_id); }
//! Return the number of lines visible in text field \a textfield_id
int textfield_visible_lines(int textfield_id) { return text_field_get_nr_visible_lines(id(), textfield_id); }
//! Show this window
void show() { show_window(id()); }
//! Hide this window
void hide() { hide_window(id()); }
//! Set the window flags in \a flags in addition to the current flags
void add_flags(std::uint32_t flags) { window().flags |= flags; }
//! Unset the window flags in \a flags, leaving the other flags as they are
void remove_flags(std::uint32_t flags) { window().flags &= ~flags; }
//! Move this window to position (\a x, \a y);
void move(int x, int y)
{
move_window(id(), parent_id(), 0, x, y);
}
//! Resize this window to \a width × \a height pixels
void resize(int width, int height)
{
resize_window(id(), width, height);
}
//! Set the minimum size of this window to \a min_width × \a min_height pixels.
void set_minimum_size(int min_width, int min_height)
{
if (min_width >= 0 && min_height >= 0)
{
window().min_len_x = min_width;
window().min_len_y = min_height;
}
}
//! Set the custom UI scale factor for this window to that defined for window type \a managed_win.
void set_custom_scale(managed_window_enum managed_win)
{
set_window_custom_scale(id(), managed_win);
}
//! Set the text font category for this window to \a font_cat.
void set_font_category(FontManager::Category font_cat)
{
set_window_font_category(id(), font_cat);
}
/*!
* \brief Add a text button
*
* Add a text button with label \a label to this window. When the button is clicked, \a callback
* is called without arguments, and should return a \c bool specifying whether the event has
* been handled.
* \param label The text string to draw on the button
* \param callback The function to call when the button is clicked
* \param x The x coordinate of the top left corner of the widget
* \param x The y coordinate of the top left corner of the widget
* \param width The width of the widget, in pixels
* \param height The height of the widget, in pixels
* \param flags Flags controlling the widget's appearance
* \param on_init Function to call on initialization of the widget
* \param size Scale factor for the text within the widget
* \return The ID of the new button
*/
template <typename Callback>
int add_button(const std::string& label, Callback&& callback,
std::uint16_t x, std::uint16_t y, std::uint32_t flags,
std::uint16_t width=0, std::uint16_t height=0, int (*on_init)()=nullptr, float size=-1.0)
{
float act_size = size >= 0.0 ? size : current_scale();
int button_id = button_add_extended(id(), _next_widget_id++, on_init, x, y, width, height,
flags, act_size, label.c_str());
widget_set_args(id(), button_id, new std::function<bool()>(callback));
widget_set_OnClick(id(), button_id, reinterpret_cast<int (*)()>(window_static_handlers::button_click));
widget_set_OnDestroy(id(), button_id, reinterpret_cast<int (*)()>(window_static_handlers::delete_button_callback));
return button_id;
}
/*!
* \brief Add a scrollbar
*
* Add a scrollbar to this window. When the bar is clicked or dragged, \a callback is called
* with the position of the scrollbar. It should return an \c bool specifying whether the event has
* been handled.
* \param bar_length The range of the scrollbar values
* \param increment The change in value between consecutive scrollbar positions
* \param callback The function to call when the bar is scrolled (by clicking or dragging)
* \param x The x coordinate of the top left corner of the widget
* \param x The y coordinate of the top left corner of the widget
* \param width The width of the widget, in pixels
* \param height The height of the widget, in pixels
* \param flags Flags controlling the widget's appearance
* \param on_init Function to call on initialization of the widget
* \param size Scale factor for the text within the widget
* \param init_position Initial value of the slider
* \return The ID of the new scrollbar
*/
template <typename Callback>
int add_scrollbar(int bar_length, int increment, Callback&& callback,
std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint32_t flags,
int (*on_init)()=nullptr, float size=-1.0, int init_position=0)
{
float act_size = size >= 0.0 ? size : current_scale();
int scrollbar_id = vscrollbar_add_extended(id(), _next_widget_id++, on_init, x, y,
width, height, flags, act_size, init_position, increment, bar_length);
widget_set_args(id(), scrollbar_id, new std::function<bool(int)>(callback));
widget_set_OnClick(id(), scrollbar_id, reinterpret_cast<int (*)()>(window_static_handlers::scrollbar_click));
widget_set_OnDrag(id(), scrollbar_id, reinterpret_cast<int (*)()>(window_static_handlers::scrollbar_drag));
widget_set_OnDestroy(id(), scrollbar_id, reinterpret_cast<int (*)()>(window_static_handlers::delete_scrollbar_callback));
return scrollbar_id;
}
/*!
* \brief Add a text field
*
* Add a new text field widget to this window.
* \param buf The text messages to draw
* \param buf_len The number of messages in \a buf
* \param x The x coordinate of the top left corner of the widget
* \param x The y coordinate of the top left corner of the widget
* \param width The width of the widget, in pixels
* \param height The height of the widget, in pixels
* \param x_margin Horizontal margin between border and text within the widget
* \param y_margin Vertical margin between border and text within the widget
* \param flags Flags controlling the widget's appearance
* \param font_cat The catgeory of the font in which the widget text is drawn
* \param on_init Function to call on initialization of the widget
* \param size Scale factor for the text within the widget
* \param filter Message channel filter to show only certain types of messages
* \return The ID of the new text widget
*/
int add_text_field(text_message *buf, size_t buf_len, std::uint16_t x, std::uint16_t y,
std::uint16_t width, std::uint16_t height, std::uint16_t x_margin, std::uint16_t y_margin,
std::uint32_t flags, FontManager::Category font_cat,
int (*on_init)()=nullptr, float size=-1.0, std::uint8_t filter=FILTER_ALL)
{
float act_size = size >= 0.0 ? size : current_scale();
return text_field_add_extended(id(), _next_widget_id++, on_init, x, y, width, height, flags,
font_cat, act_size, buf, buf_len, filter, x_margin, y_margin);
}
//! Move the widget with ID \a widget_id to position (\a x, \a y)
void move_widget(int widget_id, std::uint16_t x, std::uint16_t y)
{
widget_move(id(), widget_id, x, y);
}
//! Change the size of the widget with ID \a widget_id in this window to \a width × \a height.
void resize_widget(int widget_id, std::uint16_t width, std::uint16_t height)
{
widget_resize(id(), widget_id, width, height);
}
//! Remove the widget with ID \a widget_id from this window
void destroy_widget(int widget_id) { widget_destroy(id(), widget_id); }
//! Enable the widget with ID \a widget_id in this window
void enable_widget(int widget_id) { remove_widget_flags(widget_id, WIDGET_DISABLED); }
//! Disable the widget with ID \a widget_id in this window
void disable_widget(int widget_id) { add_widget_flags(widget_id, WIDGET_DISABLED); }
//! Add the flags in \a flags to the widget flags of the widget with ID \a id in this window.
void add_widget_flags(int widget_id, std::uint32_t flags)
{
std::uint32_t cur_flags = widget_flags(widget_id);
widget_set_flags(id(), widget_id, cur_flags | flags);
}
//! Remove the flags in \a flags from the widget flags of the widget with ID \a id in this window.
void remove_widget_flags(int widget_id, std::uint32_t flags)
{
widget_unset_flags(id(), widget_id, flags);
}
//! Set the widget specific arguments for widget \a widget_id to \a args.
void set_widget_arguments(int widget_id, void* args) { widget_set_args(id(), widget_id, args); }
/*!
* \brief Change the button size
*
* Change the size of button \a button_id to \a width × \a height, and set the font size to
* \a size. Both \a width and \a height can be zero, in which case the corresponding values
* for the button remain unchanged.
* \param button_id The ID of the button widget in this window
* \param width The new width of the button, or 0
* \param height The new width of the button, or 0
* \param size The new text size of the button label
*/
void resize_button(int button_id, std::uint16_t width, std::uint16_t height, float size)
{
button_resize(id(), button_id, width, height, size);
}
//! Set the maximum position scrollbar \a scrollbar_id can take
void set_scrollbar_length(int scrollbar_id, int length)
{
vscrollbar_set_bar_len(id(), scrollbar_id, length);
}
//! Scroll scrollbar \a scrollbar_id up one tick
void scroll_up(int scrollbar_id) { vscrollbar_scroll_up(id(), scrollbar_id); }
//! Scroll scrollbar \a scrollbar_id down one tick
void scroll_down(int scrollbar_id) { vscrollbar_scroll_down(id(), scrollbar_id); }
/*!
* \brief Set the display position of a text field
*
* Set text field \a textfield_id to start displaying at position \a position in the message
* at index \a msg_idx in its buffer.
*/
void set_textfield_position(int textfield_id, std::size_t msg_idx, std::size_t position)
{
text_field_set_buf_pos(id(), textfield_id, msg_idx, position);
}
//! Scroll textfield \a textfield_id to start displaying the text at line number \a line_nr.
void scroll_textfield_to_line(int textfield_id, int line_nr)
{
text_field_scroll_to_line(id(), textfield_id, line_nr);
}
/*!
* \brief Static display handler
*
* Static display handler that calls \c Derived::display_handler(). This function is installed
* as the display handler in the underlying window_info structure if a
* \c Derived::display_handler() exists.
*/
DEFINE_STATIC_HANDLER0(display)
/*!
* \brief Static click handler
*
* Static mouse click handler that calls \c Derived::click_handler(). This function is installed
* as the click handler in the underlying window_info structure if a
* \c Derived::click_handler() exists.
*/
DEFINE_STATIC_HANDLER3(click, int, int, std::uint32_t);
/*!
* \brief Static mouseover handler
*
* Static mouseover handler that calls \c Derived::mouseover_handler(). This function is
* installed as the mouseover handler in the underlying window_info structure if a
* \c Derived::mouseover_handler() exists.
*/
DEFINE_STATIC_HANDLER2(mouseover, int, int)
/*!
* \brief Static resize handler
*
* Static resize handler that calls \c Derived::resize_handler(). This function is installed
* as the resize handler in the underlying window_info structure if a
* \c Derived::resize_handler() exists.
*/
DEFINE_STATIC_HANDLER2(resize, int, int)
/*!
* \brief Static UI scale handler
*
* Static user interface scale handler that calls \c Derived::ui_scale_handler(). This function
* is installed as the UI scale handler in the underlying window_info structure if a
* \c Derived::ui_scale_handler() exists.
*/
DEFINE_STATIC_HANDLER0(ui_scale)
/*!
* \brief Static font change handler
*
* Static font change handler that calls \c Derived::font_change_handler(). This function is
* installed as the font change handler in the underlying window_info structure if a
* \c Derived::font_change_handler() exists.
*/
DEFINE_STATIC_HANDLER1(font_change, FontManager::Category)
protected:
/*!
* \brief Extract a Window from the underlying window_info structure
*
* Return the pointer to itself that a Window stores in the \c data member of its underlying
* window_info structure.
* \param win Pointer to a window_info structure represented by a Window.
* \return Pointer to the child class deriving from Window that was stored in \a win.
*/
static Derived& get_cpp_window(window_info *win)
{
return *reinterpret_cast<Derived*>(win->data);
}
private:
//! The unique identifier for this window
int _id;
//! ID counter for widgets added to this window
std::atomic_int _next_widget_id;
/*!
* \brief Check the window ID, and set event handlers
*
* This function is called by the constructors. It checks if the stored window ID is valid,
* stores a pointer to this window in the underlying window struct so this object can be
* retrieved from underlying data, and sets any window handlers defined by the \a Derived class.
*/
void check_and_set_handlers()
{
if (_id < 0 || _id >= windows_list.num_windows)
EXTENDED_EXCEPTION(ExtendedException::ec_invalid_parameter, "Invalid window id " << _id);
window().data = this;
set_display_handler(this);
set_mouseover_handler(this);
set_click_handler(this);
set_resize_handler(this);
set_ui_scale_handler(this);
set_font_change_handler(this);
}
};
DECLARE_SET_HANDLER(display, ELW_HANDLER_DISPLAY)
DECLARE_SET_HANDLER(click, ELW_HANDLER_CLICK)
DECLARE_SET_HANDLER(mouseover, ELW_HANDLER_MOUSEOVER)
DECLARE_SET_HANDLER(resize, ELW_HANDLER_RESIZE)
DECLARE_SET_HANDLER(ui_scale, ELW_HANDLER_UI_SCALE)
DECLARE_SET_HANDLER(font_change, ELW_HANDLER_FONT_CHANGE)
} // namespace eternal_lands
#endif // CPPWINDOWS_H