porcupine/demo/c/dr_libs/old/dr_gui.h
Go to the documentation of this file.
1 // Public Domain. See "unlicense" statement at the end of this file.
2 
3 // QUICK NOTES
4 //
5 // General
6 // - dr_gui is a low-level GUI system that works on generic objects referred to as "elements".
7 // - An element is the most basic unit in dr_gui. It contains basic information about it's layout and hierarchy.
8 // - Elements can be used as the building blocks for more complex controls such as list boxes and scrollbars.
9 // - The layout of elements use floats instead of integers. The rationale for this is that it makes it easier to do certain
10 // layout arithmetic. For example, if you want to evenly distribute 3 elements across a fixed area, the integer based
11 // arithmetic can cause rounding errors which cause the elements to not sit flush against the area. By using float-based
12 // arithmetic we can avoid that particular issue.
13 //
14 // Hierarchy
15 // - An element can have a parent and any number of children. If an element does not have a parent, it is referred to as the
16 // top-level element.
17 // - When an element is deleted, it's children will be deleted as well.
18 // - Top-level elements do not have siblings.
19 //
20 // Event Handling
21 // - The application must notify dr_gui of application-generated events such as key strokes and mouse movements. These are
22 // referred to as inbound events. An event that is generated by dr_gui are referred to as outbound events.
23 // - Inbound events are used to generate outbound events. For example, a mouse-move inbound event will generate an outbound
24 // mouse-move event, and perhaps a mouse leave/enter pair.
25 // - Outbound events are posted and handled immediately. A call to drgui_post_inbound_event() will not return until all of
26 // the outbound events it generates have been handled.
27 // - Inbound events are not thread safe, however an application is free to post an inbound event from any thread so long as
28 // it does it's own synchronization.
29 // - Inbound events will typically specify the relevant top-level element and let dr_gui do the relevant processing required
30 // to generate the appropriate outbound events. For example, the mouse-move event will be specified with respect to the top-
31 // level element, but dr_gui will determine the exact child element that the mouse moved on and thus should receive the
32 // relevant outbound mouse-move event.
33 // - There are some special events that are handled differently to normal events. The best example is the paint events. The
34 // paint event is only called from drgui_draw().
35 // - Key press/release events are only ever posted to the element that has the keyboard capture/focus which is set with
36 // drgui_capture_keyboard(). Thus, when posting an inbound key event, a top-level element is not required when posting
37 // those events. The relevant context is still required, however.
38 //
39 // Global Outbound Event Handling
40 // - At times dr_gui will need to notify the host application in order for certain functionality to work properly. For example.
41 // when the mouse is captured it won't work 100% correct unless the host application has a chance to capture the mouse against
42 // the container window. Because dr_gui has no notion of a window system it relies on the host application to handle this
43 // properly.
44 // - A global outbound event handler should be implemented for each of the following events:
45 // - on_dirty: Called when a region of an element is marked as dirty and needs to be redrawn. The application will want to
46 // invalidate the container window to trigger an operating system redraw. Set this with drgui_set_global_on_dirty().
47 // - on_capture_mouse: Called when the mouse is captured and gives the application the opportunity to capture the mouse against
48 // the container window at the operating system level. Set with drgui_set_global_on_capture_mouse().
49 // - on_release_mouse: Called when the mouse is released. The opposite of on_capture_mouse.
50 // - on_capture_keyboard: Called when an element is given the keyboard focus and gives the application the opportunity to
51 // apply the keyboard focus to the container window. Set with drgui_set_global_on_capture_keyboard().
52 // - on_release_keyboard: Called when an element loses the keyboard focus. The opposite of on_capture_keyboard.
53 // - on_change_cursor: Called when the current cursor needs to be changed as a result of the mouse moving over a new element.
54 //
55 // Layout
56 // - An element's data structure does not store it's relative position but instead stores it's absolute position. The rationale
57 // for this is that storing it as relative complicates absolute positioning calculations because it would need to do a recursive
58 // traversal of the element's ancestors.
59 // - Child elements can be scaled by setting an element's inner scale. The inner scale does not scale the element itself - only
60 // it's children.
61 // - When an element is drawn, everything is scaled by it's inner scale. For example, if the inner scale is 2x and a 100x100 quad
62 // is drawn, the quad will be scaled to 200x200. An exception to this rule is fonts, which are never scaled. This 0s because
63 // text is always drawn based on the size of the font.
64 // - Applications should only need to work on unscaled coordinates. That is, an application should never need to worry about
65 // manual scaling, except for fonts. When positioning and sizing child elements, they should be done based on unscaled
66 // coordinates.
67 // - Use the inner scale system for DPI awareness.
68 // - The inner scale is applied recursively. That is, if a top level element has it's inner scale set to 2x and one of it's
69 // children has an inner scale of 2x, the actual inner scale of the child element will be 4x.
70 //
71 //
72 // Drawing/Painting
73 // - Drawing is one of the more complex parts of the GUI because it can be a bit unintuitive regarding exactly when an element
74 // is drawn and when a drawing function is allowed to be called.
75 // - To draw an element, call drgui_draw(). This takes a pointer to the element to draw and the rectangle region that should
76 // be redrawn. Any children that fall inside the specified rectangle region will be redrawn as well. You do not want to call
77 // drgui_draw() on a parent element and then again on it's children because dr_gui will do that automatically.
78 // - drgui_draw() does not draw anything directly, but rather calls painting callback routines which is where the actual
79 // drawing takes place.
80 // - Sometimes an application will need to be told when a region of an element is dirty and needs redrawing. An example is
81 // event-driven, non real-time applications such as normal desktop applications. To mark an element as dirty, you call the
82 // drgui_dirty() function which takes the element that is dirty, and the rectangle region that needs to be redrawn. This
83 // does not redraw the element immediately, but instead posts an on_dirty event for the application. Marking regions as dirty
84 // is not strictly required, but you should prefer it for event-driven applications that require painting operations to be
85 // performed at specific times (such as inside Win32's WM_PAINT messages).
86 // - Some operations will cause a region of an element to become dirty - such as when it is resized. dr_gui will
87 // automatically mark the relevant regions as dirty which in turn will cause a paint message to be posted. If this is not
88 // required, it can be disabled with drgui_disable_auto_dirty(). You may want to disable automatic dirtying if you are
89 // running a real-time application like a game which would redraw the entire GUI every frame anyway and thus not require
90 // handling of the paint message.
91 // - Real-time application guidelines (games, etc.):
92 // - drgui_disable_auto_dirty()
93 // - drgui_draw(pTopLevelElement, 0, 0, viewportWidth, viewportHeight) at the end of every frame after your main loop.
94 //
95 
96 
97 //
98 // OPTIONS
99 //
100 // #define DRGUI_NO_DR_2D
101 // Disable dr_2d integration. Disabling dr_2d will require you to implement your own drawing callbacks.
102 //
103 // #define DRGUI_NO_TEXT_EDITING
104 // Disables the text box control and text engine.
105 
106 
107 //
108 // EXAMPLES
109 //
110 // Basic Drawing:
111 //
112 // drgui_draw(pTopLevelElement, 0, 0, drgui_get_width(pTopLevelElement), drgui_get_height(pTopLevelElement));
113 //
114 // -------------------------
115 //
116 // Event-Driven Drawing (Win32):
117 //
118 // void my_global_on_dirty_win32(drgui_element* pElement, drgui_rect relativeRect) {
119 // drgui_rect absoluteRect = relativeRect;
120 // drgui_make_rect_absolute(pElement, &absoluteRect);
121 //
122 // RECT rect;
123 // rect.left = absoluteRect.left;
124 // rect.top = absoluteRect.top;
125 // rect.right = absoluteRect.right;
126 // rect.height = absoluteRect.bottom;
127 // InvalidateRect((HWND)drgui_get_user_data(drgui_find_top_level_element(pElement)), &rect, FALSE);
128 // }
129 //
130 // ...
131 //
132 // LRESULT CALLBACK MyWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
133 // ...
134 // drgui_element* pTopLevelElement = (drgui_element*)GetWindowLongPtr(hWnd, 0);
135 // if (pTopLevelElement != NULL) {
136 // switch (msg) {
137 // ...
138 // case WM_PAINT:
139 // {
140 // RECT rect;
141 // if (GetUpdateRect(hWnd, &rect, FALSE)) {
142 // drgui_draw(pTopLevelElement, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
143 // }
144 //
145 // break;
146 // }
147 // ...
148 // }
149 // }
150 // ...
151 // }
152 //
153 
154 
155 
156 #ifndef dr_gui_h
157 #define dr_gui_h
158 
159 #ifndef DRGUI_NO_DR_2D
160 // If you're using easy_draw integration, set the path below to the relative location of dr_2d. By default, the
161 // following structure is assumed:
162 // <Base Directory>
163 // - dr_libs
164 // - dr_2d.h
165 // - dr_gui.h
166 #include "dr_2d.h"
167 #endif
168 
169 #include <stdbool.h>
170 
171 #ifndef DRGUI_MAX_FONT_FAMILY_LENGTH
172 #define DRGUI_MAX_FONT_FAMILY_LENGTH 128
173 #endif
174 
175 
176 #ifdef __cplusplus
177 extern "C" {
178 #endif
179 
182 typedef struct drgui_color drgui_color;
183 typedef struct drgui_rect drgui_rect;
185 typedef struct drgui_font drgui_font;
186 typedef struct drgui_image drgui_image;
189 
190 typedef unsigned char drgui_byte;
191 typedef unsigned int drgui_key;
192 
193 typedef void* drgui_resource;
194 
196 typedef enum
197 {
203  drgui_cursor_size_ns, // North/South resize arrows.
204  drgui_cursor_size_we, // West/East resize arrows.
205  drgui_cursor_size_nesw, // North/East, South/West resize arrows.
206  drgui_cursor_size_nwse // North/West, South/East resize arrows.
208 
210 typedef enum
211 {
223 
226 
228 
230 typedef enum
231 {
235 
237 
239 typedef enum
240 {
245 
246 
249 {
250  int ascent;
251  int descent;
254 };
255 
258 {
259  int width;
260  int height;
261  int originX;
262  int originY;
263  int advanceX;
264  int advanceY;
265 };
266 
267 
270 {
275 };
276 
279 {
280  float left;
281  float top;
282  float right;
283  float bottom;
284 };
285 
286 
287 #define DRGUI_IMAGE_DRAW_BACKGROUND (1 << 0)
288 #define DRGUI_IMAGE_HINT_NO_ALPHA (1 << 1)
289 #define DRGUI_IMAGE_DRAW_BOUNDS (1 << 2)
290 #define DRGUI_IMAGE_CLIP_BOUNDS (1 << 3) //< Clips the image to the bounds
291 #define DRGUI_IMAGE_ALIGN_CENTER (1 << 4)
292 
293 #define DRGUI_READ (1 << 0)
294 #define DRGUI_WRITE (1 << 1)
295 
296 #define DRGUI_FONT_NO_CLEARTYPE (1 << 0)
297 
298 typedef struct
299 {
301  float dstX;
302 
304  float dstY;
305 
307  float dstWidth;
308 
310  float dstHeight;
311 
312 
314  float srcX;
315 
317  float srcY;
318 
320  float srcWidth;
321 
323  float srcHeight;
324 
325 
327  float dstBoundsX;
328 
330  float dstBoundsY;
331 
334 
337 
338 
341 
344 
348 
349 
351  unsigned int options;
352 
354 
355 
356 typedef void (* drgui_callback)();
357 
358 typedef void (* drgui_on_move_proc) (drgui_element* pElement, float newRelativePosX, float newRelativePosY);
359 typedef void (* drgui_on_size_proc) (drgui_element* pElement, float newWidth, float newHeight);
360 typedef void (* drgui_on_mouse_enter_proc) (drgui_element* pElement);
361 typedef void (* drgui_on_mouse_leave_proc) (drgui_element* pElement);
362 typedef void (* drgui_on_mouse_move_proc) (drgui_element* pElement, int relativeMousePosX, int relativeMousePosY, int stateFlags);
363 typedef void (* drgui_on_mouse_button_down_proc) (drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
364 typedef void (* drgui_on_mouse_button_up_proc) (drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
365 typedef void (* drgui_on_mouse_button_dblclick_proc)(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
366 typedef void (* drgui_on_mouse_wheel_proc) (drgui_element* pElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags);
367 typedef void (* drgui_on_key_down_proc) (drgui_element* pElement, drgui_key key, int stateFlags);
368 typedef void (* drgui_on_key_up_proc) (drgui_element* pElement, drgui_key key, int stateFlags);
369 typedef void (* drgui_on_printable_key_down_proc) (drgui_element* pElement, unsigned int character, int stateFlags);
370 typedef void (* drgui_on_paint_proc) (drgui_element* pElement, drgui_rect relativeRect, void* pPaintData);
371 typedef void (* drgui_on_dirty_proc) (drgui_element* pElement, drgui_rect relativeRect);
372 typedef bool (* drgui_on_hittest_proc) (drgui_element* pElement, float relativePosX, float relativePosY);
373 typedef void (* drgui_on_capture_mouse_proc) (drgui_element* pElement);
374 typedef void (* drgui_on_release_mouse_proc) (drgui_element* pElement);
375 typedef void (* drgui_on_capture_keyboard_proc) (drgui_element* pElement, drgui_element* pPrevCapturedElement);
376 typedef void (* drgui_on_release_keyboard_proc) (drgui_element* pElement, drgui_element* pNewCapturedElement);
377 typedef void (* drgui_on_change_cursor_proc) (drgui_element* pElement, drgui_cursor_type cursor);
378 typedef void (* drgui_on_delete_element_proc) (drgui_element* pElement);
379 typedef void (* drgui_on_log) (drgui_context* pContext, const char* message);
380 
381 typedef void (* drgui_draw_begin_proc) (void* pPaintData);
382 typedef void (* drgui_draw_end_proc) (void* pPaintData);
383 typedef void (* drgui_set_clip_proc) (drgui_rect relativeRect, void* pPaintData);
384 typedef void (* drgui_get_clip_proc) (drgui_rect* pRectOut, void* pPaintData);
385 typedef void (* drgui_draw_line_proc) (float startX, float startY, float endX, float endY, float width, drgui_color color, void* pPaintData);
386 typedef void (* drgui_draw_rect_proc) (drgui_rect relativeRect, drgui_color color, void* pPaintData);
387 typedef void (* drgui_draw_rect_outline_proc) (drgui_rect relativeRect, drgui_color color, float outlineWidth, void* pPaintData);
388 typedef void (* drgui_draw_rect_with_outline_proc) (drgui_rect relativeRect, drgui_color color, float outlineWidth, drgui_color outlineColor, void* pPaintData);
389 typedef void (* drgui_draw_round_rect_proc) (drgui_rect relativeRect, drgui_color color, float radius, void* pPaintData);
390 typedef void (* drgui_draw_round_rect_outline_proc) (drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, void* pPaintData);
391 typedef void (* drgui_draw_round_rect_with_outline_proc) (drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, drgui_color outlineColor, void* pPaintData);
392 typedef void (* drgui_draw_text_proc) (drgui_resource font, const char* text, int textLengthInBytes, float posX, float posY, drgui_color color, drgui_color backgroundColor, void* pPaintData);
393 typedef void (* drgui_draw_image_proc) (drgui_resource image, drgui_draw_image_args* pArgs, void* pPaintData);
394 
395 typedef drgui_resource (* drgui_create_font_proc) (void* pPaintingContext, const char* family, unsigned int size, drgui_font_weight weight, drgui_font_slant slant, float rotation, unsigned int flags);
396 typedef void (* drgui_delete_font_proc) (drgui_resource font);
397 typedef unsigned int (* drgui_get_font_size_proc) (drgui_resource font);
398 typedef bool (* drgui_get_font_metrics_proc) (drgui_resource font, drgui_font_metrics* pMetricsOut);
399 typedef bool (* drgui_get_glyph_metrics_proc) (drgui_resource font, unsigned int utf32, drgui_glyph_metrics* pMetricsOut);
400 typedef bool (* drgui_measure_string_proc) (drgui_resource font, const char* text, size_t textSizeInBytes, float* pWidthOut, float* pHeightOut);
401 typedef bool (* drgui_get_text_cursor_position_from_point_proc)(drgui_resource font, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut);
402 typedef bool (* drgui_get_text_cursor_position_from_char_proc) (drgui_resource font, const char* text, size_t characterIndex, float* pTextCursorPosXOut);
403 
404 typedef drgui_resource (* drgui_create_image_proc) (void* pPaintingContext, unsigned int width, unsigned int height, drgui_image_format format, unsigned int stride, const void* pImageData);
405 typedef void (* drgui_delete_image_proc) (drgui_resource image);
406 typedef drgui_image_format (* drgui_get_optimal_image_format_proc)(void* pPaintingContext);
407 typedef void (* drgui_get_image_size_proc) (drgui_resource image, unsigned int* pWidthOut, unsigned int* pHeightOut);
408 typedef void* (* drgui_map_image_data_proc) (drgui_resource image, unsigned int accessFlags);
410 
411 typedef bool (* drgui_visible_iteration_proc)(drgui_element* pElement, drgui_rect *pRelativeRect, void* pUserData);
412 
413 
414 // Key state flags.
415 #define DRGUI_MOUSE_BUTTON_LEFT_DOWN (1 << 0)
416 #define DRGUI_MOUSE_BUTTON_RIGHT_DOWN (1 << 1)
417 #define DRGUI_MOUSE_BUTTON_MIDDLE_DOWN (1 << 2)
418 #define DRGUI_MOUSE_BUTTON_4_DOWN (1 << 3)
419 #define DRGUI_MOUSE_BUTTON_5_DOWN (1 << 4)
420 #define DRGUI_KEY_STATE_SHIFT_DOWN (1 << 5) // Whether or not a shift key is down at the time the input event is handled.
421 #define DRGUI_KEY_STATE_CTRL_DOWN (1 << 6) // Whether or not a ctrl key is down at the time the input event is handled.
422 #define DRGUI_KEY_STATE_ALT_DOWN (1 << 7) // Whether or not an alt key is down at the time the input event is handled.
423 #define DRGUI_KEY_STATE_AUTO_REPEATED (1 << 31) // Whether or not the key press is generated due to auto-repeating. Only used with key down events.
424 
425 // Common mouse buttons.
426 #define DRGUI_MOUSE_BUTTON_LEFT 1
427 #define DRGUI_MOUSE_BUTTON_RIGHT 2
428 #define DRGUI_MOUSE_BUTTON_MIDDLE 3
429 
430 // Common key codes.
431 #define DRGUI_BACKSPACE 0xff08
432 #define DRGUI_SHIFT 0xff10
433 #define DRGUI_ESCAPE 0xff1b
434 #define DRGUI_PAGE_UP 0xff55
435 #define DRGUI_PAGE_DOWN 0xff56
436 #define DRGUI_END 0xff57
437 #define DRGUI_HOME 0xff50
438 #define DRGUI_ARROW_LEFT 0x8fb
439 #define DRGUI_ARROW_UP 0x8fc
440 #define DRGUI_ARROW_RIGHT 0x8fd
441 #define DRGUI_ARROW_DOWN 0x8fe
442 #define DRGUI_DELETE 0xffff
443 #define DRGUI_F1 0xffbe
444 #define DRGUI_F2 0xffbf
445 #define DRGUI_F3 0xffc0
446 #define DRGUI_F4 0xffc1
447 #define DRGUI_F5 0xffc2
448 #define DRGUI_F6 0xffc3
449 #define DRGUI_F7 0xffc4
450 #define DRGUI_F8 0xffc5
451 #define DRGUI_F9 0xffc6
452 #define DRGUI_F10 0xffc7
453 #define DRGUI_F11 0xffc8
454 #define DRGUI_F12 0xffc9
455 
456 static size_t drgui_strcpy(char* dst, size_t dstSize, const char* src)
457 {
458  if (strcpy_s(dst, dstSize, src) == 0) {
459  return strlen(dst);
460  }
461 
462  return 0;
463 }
464 
465 static inline size_t drgui_key_to_string(drgui_key key, char* strOut, size_t strOutSize)
466 {
467  if (strOut == NULL || strOutSize == 0) {
468  return 0;
469  }
470 
471  if (strOutSize == 1) {
472  strOut[0] = '\0';
473  return 0;
474  }
475 
476 
477  switch (key)
478  {
479  case DRGUI_BACKSPACE: return drgui_strcpy(strOut, strOutSize, "Backspace");
480  case DRGUI_SHIFT: return drgui_strcpy(strOut, strOutSize, "Shift");
481  case DRGUI_ESCAPE: return drgui_strcpy(strOut, strOutSize, "Escape");
482  case DRGUI_PAGE_UP: return drgui_strcpy(strOut, strOutSize, "Page Up");
483  case DRGUI_PAGE_DOWN: return drgui_strcpy(strOut, strOutSize, "Page Down");
484  case DRGUI_END: return drgui_strcpy(strOut, strOutSize, "End");
485  case DRGUI_HOME: return drgui_strcpy(strOut, strOutSize, "Home");
486  case DRGUI_ARROW_LEFT: return drgui_strcpy(strOut, strOutSize, "Arrow Left");
487  case DRGUI_ARROW_UP: return drgui_strcpy(strOut, strOutSize, "Arrow Up");
488  case DRGUI_ARROW_RIGHT: return drgui_strcpy(strOut, strOutSize, "Arrow Right");
489  case DRGUI_ARROW_DOWN: return drgui_strcpy(strOut, strOutSize, "Arrow Down");
490  case DRGUI_DELETE: return drgui_strcpy(strOut, strOutSize, "Delete");
491  case DRGUI_F1: return drgui_strcpy(strOut, strOutSize, "F1");
492  case DRGUI_F2: return drgui_strcpy(strOut, strOutSize, "F2");
493  case DRGUI_F3: return drgui_strcpy(strOut, strOutSize, "F3");
494  case DRGUI_F4: return drgui_strcpy(strOut, strOutSize, "F4");
495  case DRGUI_F5: return drgui_strcpy(strOut, strOutSize, "F5");
496  case DRGUI_F6: return drgui_strcpy(strOut, strOutSize, "F6");
497  case DRGUI_F7: return drgui_strcpy(strOut, strOutSize, "F7");
498  case DRGUI_F8: return drgui_strcpy(strOut, strOutSize, "F8");
499  case DRGUI_F9: return drgui_strcpy(strOut, strOutSize, "F9");
500  case DRGUI_F10: return drgui_strcpy(strOut, strOutSize, "F10");
501  case DRGUI_F11: return drgui_strcpy(strOut, strOutSize, "F11");
502  case DRGUI_F12: return drgui_strcpy(strOut, strOutSize, "F12");
503  }
504 
505  if (key >= 32 && key <= 126) {
506  strOut[0] = (char)key;
507  strOut[1] = '\0';
508  return 1;
509  }
510 
511  // TODO: Non-ascii characters.
512  return 0;
513 }
514 
515 static inline drgui_key drgui_key_parse(const char* str)
516 {
517  if (str == NULL || str[0] == '\0') {
518  return 0;
519  }
520 
521  if (_stricmp(str, "backspace") == 0) return DRGUI_BACKSPACE;
522  if (_stricmp(str, "shift") == 0) return DRGUI_SHIFT;
523  if (_stricmp(str, "escape") == 0) return DRGUI_ESCAPE;
524  if (_stricmp(str, "page up") == 0 || _stricmp(str, "pageup") == 0) return DRGUI_PAGE_UP;
525  if (_stricmp(str, "page down") == 0 || _stricmp(str, "pagedown") == 0) return DRGUI_PAGE_DOWN;
526  if (_stricmp(str, "end") == 0) return DRGUI_END;
527  if (_stricmp(str, "home") == 0) return DRGUI_HOME;
528  if (_stricmp(str, "arrow left") == 0 || _stricmp(str, "arrowleft") == 0) return DRGUI_ARROW_LEFT;
529  if (_stricmp(str, "arrow up") == 0 || _stricmp(str, "arrowup") == 0) return DRGUI_ARROW_UP;
530  if (_stricmp(str, "arrow right") == 0 || _stricmp(str, "arrowright") == 0) return DRGUI_ARROW_RIGHT;
531  if (_stricmp(str, "arrow down") == 0 || _stricmp(str, "arrowdown") == 0) return DRGUI_ARROW_DOWN;
532  if (_stricmp(str, "delete") == 0) return DRGUI_BACKSPACE;
533 
534  if (str[0] == 'F' || str[0] == 'f') {
535  if (str[1] == '1') {
536  if (str[2] == '\0') {
537  return DRGUI_F1;
538  } else {
539  if (str[2] == '0' && str[2] == '\0') return DRGUI_F10;
540  if (str[2] == '1' && str[2] == '\0') return DRGUI_F11;
541  if (str[2] == '2' && str[2] == '\0') return DRGUI_F12;
542  }
543  }
544  if (str[1] == '2' && str[2] == '\0') return DRGUI_F2;
545  if (str[1] == '3' && str[2] == '\0') return DRGUI_F3;
546  if (str[1] == '4' && str[2] == '\0') return DRGUI_F4;
547  if (str[1] == '5' && str[2] == '\0') return DRGUI_F5;
548  if (str[1] == '6' && str[2] == '\0') return DRGUI_F6;
549  if (str[1] == '7' && str[2] == '\0') return DRGUI_F7;
550  if (str[1] == '8' && str[2] == '\0') return DRGUI_F8;
551  if (str[1] == '9' && str[2] == '\0') return DRGUI_F9;
552  }
553 
554 
555  // ASCII characters.
556  if (str[0] >= 32 && str[0] <= 126 && str[1] == '\0') {
557  return str[0];
558  }
559 
560  if (_stricmp(str, "tab") == 0) {
561  return '\t';
562  }
563 
564 
565  // TODO: Non-ascii characters.
566  return 0;
567 }
568 
569 
572 {
575 
578 
588 
597 
604 };
605 
607 {
610 
613 };
614 
616 {
619 
622 
624  unsigned int size;
625 
628 
631 
633  float rotation;
634 
637  unsigned int flags;
638 
641 };
642 
643 
645 {
648 
649 
652 
655 
658 
661 
664 
665 
671 
672 
675  char type[64];
676 
677 
680 
683 
685  float width;
686 
688  float height;
689 
690 
693 
695  unsigned int flags;
696 
697  // The region of the element that's dirty.
699 
700 
703 
706 
709 
712 
715 
718 
721 
724 
727 
730 
733 
736 
739 
742 
745 
748 
751 
754 
757 
758 
761 
764 };
765 
767 {
770 
773 
774 
778 
783 
786 
790 
793 
796 
801 
804 
806  unsigned int flags;
807 
808 
811 
814 
817 
820 
823 
826 
829 
830 
833 
834 
835 
838 
842 
843 
844  // A pointer to the list of dirty elements.
846 
847  // The size of the buffer containing the dirty elements.
849 
850  // The number of dirty top-level elements.
852 
856  unsigned int dirtyCounter;
857 };
858 
859 
860 
862 //
863 // CORE API
864 //
866 
869 
871 void drgui_delete_context(drgui_context* pContext);
872 
873 
874 
876 // Events
877 
884 
886 void drgui_post_inbound_event_mouse_move(drgui_element* pTopLevelElement, int mousePosX, int mousePosY, int stateFlags);
887 
889 void drgui_post_inbound_event_mouse_button_down(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags);
890 
892 void drgui_post_inbound_event_mouse_button_up(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags);
893 
895 void drgui_post_inbound_event_mouse_button_dblclick(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags);
896 
898 void drgui_post_inbound_event_mouse_wheel(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags);
899 
901 void drgui_post_inbound_event_key_down(drgui_context* pContext, drgui_key key, int stateFlags);
902 
904 void drgui_post_inbound_event_key_up(drgui_context* pContext, drgui_key key, int stateFlags);
905 
910 void drgui_post_inbound_event_printable_key_down(drgui_context* pContext, unsigned int character, int stateFlags);
911 
912 
919 
929 
939 
949 
959 
965 
968 
969 
971 void drgui_set_on_log(drgui_context* pContext, drgui_on_log onLog);
972 
973 
974 
975 
977 // Elements
978 
980 drgui_element* drgui_create_element(drgui_context* pContext, drgui_element* pParent, size_t extraDataSize, const void* pExtraData);
981 
983 void drgui_delete_element(drgui_element* pElement);
984 
985 
987 size_t drgui_get_extra_data_size(drgui_element* pElement);
988 
990 void* drgui_get_extra_data(drgui_element* pElement);
991 
992 
996 bool drgui_set_type(drgui_element* pElement, const char* type);
997 
999 const char* drgui_get_type(drgui_element* pElement);
1000 
1002 bool drgui_is_of_type(drgui_element* pElement, const char* type);
1003 
1004 
1006 void drgui_hide(drgui_element *pElement);
1007 
1009 void drgui_show(drgui_element* pElement);
1010 
1017 bool drgui_is_visible(const drgui_element* pElement);
1018 
1020 bool drgui_is_visible_recursive(const drgui_element* pElement);
1021 
1022 
1024 void drgui_disable_clipping(drgui_element* pElement);
1025 
1027 void drgui_enable_clipping(drgui_element* pElement);
1028 
1030 bool drgui_is_clipping_enabled(const drgui_element* pElement);
1031 
1032 
1037 void drgui_capture_mouse(drgui_element* pElement);
1038 
1040 void drgui_release_mouse(drgui_context* pContext);
1041 
1044 
1047 
1049 bool drgui_has_mouse_capture(drgui_element* pElement);
1050 
1051 
1056 void drgui_capture_keyboard(drgui_element* pElement);
1057 
1059 void drgui_release_keyboard(drgui_context* pContext);
1060 
1063 
1066 
1069 
1070 
1072 void drgui_set_cursor(drgui_element* pElement, drgui_cursor_type cursor);
1073 
1076 
1077 
1079 
1081 void drgui_set_on_move(drgui_element* pElement, drgui_on_move_proc callback);
1082 
1084 void drgui_set_on_size(drgui_element* pElement, drgui_on_size_proc callback);
1085 
1088 
1091 
1094 
1097 
1100 
1103 
1106 
1109 
1111 void drgui_set_on_key_up(drgui_element* pElement, drgui_on_key_up_proc callback);
1112 
1115 
1117 void drgui_set_on_paint(drgui_element* pElement, drgui_on_paint_proc callback);
1118 
1120 void drgui_set_on_dirty(drgui_element* pElement, drgui_on_dirty_proc callback);
1121 
1123 void drgui_set_on_hittest(drgui_element* pElement, drgui_on_hittest_proc callback);
1124 
1127 
1130 
1133 
1136 
1137 
1138 
1140 
1146 bool drgui_is_point_inside_element_bounds(const drgui_element* pElement, float absolutePosX, float absolutePosY);
1147 
1152 bool drgui_is_point_inside_element(drgui_element* pElement, float absolutePosX, float absolutePosY);
1153 
1155 drgui_element* drgui_find_element_under_point(drgui_element* pTopLevelElement, float absolutePosX, float absolutePosY);
1156 
1158 bool drgui_is_element_under_mouse(drgui_element* pTopLevelElement);
1159 
1160 
1161 
1163 
1164 // Retrieves the parent of the given element.
1166 
1168 void drgui_detach(drgui_element* pChildElement);
1169 
1171 void drgui_append(drgui_element* pChildElement, drgui_element* pParentElement);
1172 
1174 void drgui_prepend(drgui_element* pChildElement, drgui_element* pParentElement);
1175 
1177 void drgui_append_sibling(drgui_element* pElementToAppend, drgui_element* pElementToAppendTo);
1178 
1180 void drgui_prepend_sibling(drgui_element* pElementToPrepend, drgui_element* pElementToPrependTo);
1181 
1187 
1192 bool drgui_is_parent(drgui_element* pParentElement, drgui_element* pChildElement);
1193 
1198 bool drgui_is_child(drgui_element* pChildElement, drgui_element* pParentElement);
1199 
1201 bool drgui_is_ancestor(drgui_element* pAncestorElement, drgui_element* pChildElement);
1202 
1204 bool drgui_is_descendant(drgui_element* pChildElement, drgui_element* pAncestorElement);
1205 
1207 bool drgui_is_self_or_ancestor(drgui_element* pAncestorElement, drgui_element* pChildElement);
1208 
1210 bool drgui_is_self_or_descendant(drgui_element* pChildElement, drgui_element* pAncestorElement);
1211 
1212 
1213 
1215 
1217 void drgui_set_absolute_position(drgui_element* pElement, float positionX, float positionY);
1218 
1220 void drgui_get_absolute_position(const drgui_element* pElement, float* positionXOut, float* positionYOut);
1221 float drgui_get_absolute_position_x(const drgui_element* pElement);
1222 float drgui_get_absolute_position_y(const drgui_element* pElement);
1223 
1224 
1226 void drgui_set_relative_position(drgui_element* pElement, float relativePosX, float relativePosY);
1227 
1229 void drgui_get_relative_position(const drgui_element* pElement, float* relativePosXOut, float* relativePosYOut);
1230 float drgui_get_relative_position_x(const drgui_element* pElement);
1231 float drgui_get_relative_position_y(const drgui_element* pElement);
1232 
1233 
1235 void drgui_set_size(drgui_element* pElement, float width, float height);
1236 
1238 void drgui_get_size(const drgui_element* pElement, float* widthOut, float* heightOut);
1239 float drgui_get_width(const drgui_element* pElement);
1240 float drgui_get_height(const drgui_element* pElement);
1241 
1242 
1243 
1246 
1249 
1255 
1256 
1257 
1259 
1265 bool drgui_register_painting_callbacks(drgui_context* pContext, void* pPaintingContext, drgui_painting_callbacks callbacks);
1266 
1267 
1279 bool drgui_iterate_visible_elements(drgui_element* pParentElement, drgui_rect relativeRect, drgui_visible_iteration_proc callback, void* pUserData);
1280 
1281 
1283 void drgui_disable_auto_dirty(drgui_context* pContext);
1284 
1286 void drgui_enable_auto_dirty(drgui_context* pContext);
1287 
1290 
1291 
1296 
1298 void drgui_end_dirty(drgui_element* pElement);
1299 
1304 void drgui_dirty(drgui_element* pElement, drgui_rect relativeRect);
1305 
1306 
1316 void drgui_draw(drgui_element* pElement, drgui_rect relativeRect, void* pPaintData);
1317 
1319 void drgui_get_clip(drgui_element* pElement, drgui_rect* pRelativeRect, void* pPaintData);
1320 
1322 void drgui_set_clip(drgui_element* pElement, drgui_rect relativeRect, void* pPaintData);
1323 
1325 void drgui_draw_rect(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, void* pPaintData);
1326 
1328 void drgui_draw_rect_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float outlineWidth, void* pPaintData);
1329 
1331 void drgui_draw_rect_with_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float outlineWidth, drgui_color outlineColor, void* pPaintData);
1332 
1334 void drgui_draw_round_rect(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float radius, void* pPaintData);
1335 
1337 void drgui_draw_round_rect_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, void* pPaintData);
1338 
1340 void drgui_draw_round_rect_with_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, drgui_color outlineColor, void* pPaintData);
1341 
1349 void drgui_draw_text(drgui_element* pElement, drgui_font* pFont, const char* text, int textLengthInBytes, float posX, float posY, drgui_color color, drgui_color backgroundColor, void* pPaintData);
1350 
1352 void drgui_draw_image(drgui_element* pElement, drgui_image* pImage, drgui_draw_image_args* pArgs, void* pPaintData);
1353 
1354 
1356 drgui_font* drgui_create_font(drgui_context* pContext, const char* family, unsigned int size, drgui_font_weight weight, drgui_font_slant slant, float rotation, unsigned int flags);
1357 
1359 void drgui_delete_font(drgui_font* pFont);
1360 
1362 bool drgui_get_font_metrics(drgui_font* pFont, drgui_font_metrics* pMetricsOut);
1363 
1365 bool drgui_get_glyph_metrics(drgui_font* pFont, unsigned int utf32, drgui_glyph_metrics* pMetricsOut);
1366 
1371 bool drgui_measure_string(drgui_font* pFont, const char* text, size_t textLengthInBytes, float* pWidthOut, float* pHeightOut);
1372 
1374 bool drgui_get_text_cursor_position_from_point(drgui_font* pFont, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut);
1375 
1377 bool drgui_get_text_cursor_position_from_char(drgui_font* pFont, const char* text, size_t characterIndex, float* pTextCursorPosXOut);
1378 
1379 
1380 
1391 drgui_image* drgui_create_image(drgui_context* pContext, unsigned int width, unsigned int height, drgui_image_format format, unsigned int stride, const void* pData);
1392 
1394 void drgui_delete_image(drgui_image* pImage);
1395 
1397 void drgui_get_image_size(drgui_image* pImage, unsigned int* pWidthOut, unsigned int* pHeightOut);
1398 
1401 
1410 void* drgui_map_image_data(drgui_image* pImage, unsigned int accessFlags);
1411 
1413 void drgui_unmap_image_data(drgui_image* pImage);
1414 
1415 
1416 
1418 //
1419 // HIGH-LEVEL API
1420 //
1422 
1424 
1426 void drgui_on_size_fit_children_to_parent(drgui_element* pElement, float newWidth, float newHeight);
1427 
1429 bool drgui_pass_through_hit_test(drgui_element* pElement, float mousePosX, float mousePosY);
1430 
1431 
1433 
1435 void drgui_draw_border(drgui_element* pElement, float borderWidth, drgui_color color, void* pUserData);
1436 
1437 
1438 
1440 //
1441 // UTILITY API
1442 //
1444 
1447 
1450 
1453 
1455 bool drgui_clamp_rect_to_element(const drgui_element* pElement, drgui_rect* pRelativeRect);
1456 
1459 
1462 
1464 void drgui_make_point_relative(const drgui_element* pElement, float* positionX, float* positionY);
1465 
1467 void drgui_make_point_absolute(const drgui_element* pElement, float* positionX, float* positionY);
1468 
1470 drgui_rect drgui_make_rect(float left, float top, float right, float bottom);
1471 
1478 
1486 drgui_rect drgui_grow_rect(drgui_rect rect, float amount);
1487 
1496 drgui_rect drgui_scale_rect(drgui_rect rect, float scaleX, float scaleY);
1497 
1499 drgui_rect drgui_offset_rect(drgui_rect rect, float offsetX, float offsetY);
1500 
1503 
1511 bool drgui_rect_contains_point(drgui_rect rect, float posX, float posY);
1512 
1514 bool drgui_rect_equal(drgui_rect rect0, drgui_rect rect1);
1515 
1518 
1519 
1520 
1522 //
1523 // EASY_DRAW-SPECIFIC API
1524 //
1526 #ifndef DRGUI_NO_DR_2D
1527 
1533 
1538 void drgui_register_dr_2d_callbacks(drgui_context* pContext, dr2d_context* pDrawingContext);
1539 
1540 #endif
1541 
1542 
1543 #ifdef __cplusplus
1544 }
1545 #endif
1546 
1547 #endif //dr_gui_h
1548 
1549 
1550 
1552 //
1553 // IMPLEMENTATION
1554 //
1556 #ifdef DR_GUI_IMPLEMENTATION
1557 #include <stdlib.h>
1558 #include <assert.h>
1559 #include <string.h>
1560 #include <errno.h>
1561 #include <float.h>
1562 #include <math.h>
1563 
1564 #ifndef DRGUI_PRIVATE
1565 #define DRGUI_PRIVATE static
1566 #endif
1567 
1569 //
1570 // PRIVATE CORE API
1571 //
1573 
1574 
1575 // Context Flags
1576 #define IS_CONTEXT_DEAD (1U << 0)
1577 #define IS_AUTO_DIRTY_DISABLED (1U << 1)
1578 #define IS_RELEASING_KEYBOARD (1U << 2)
1579 
1580 // Element Flags
1581 #define IS_ELEMENT_HIDDEN (1U << 0)
1582 #define IS_ELEMENT_CLIPPING_DISABLED (1U << 1)
1583 #define IS_ELEMENT_DEAD (1U << 31)
1584 
1585 
1586 static int drgui__strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
1587 {
1588 #ifdef _MSC_VER
1589  return strcpy_s(dst, dstSizeInBytes, src);
1590 #else
1591  if (dst == 0) {
1592  return EINVAL;
1593  }
1594  if (dstSizeInBytes == 0) {
1595  return ERANGE;
1596  }
1597  if (src == 0) {
1598  dst[0] = '\0';
1599  return EINVAL;
1600  }
1601 
1602  size_t i;
1603  for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
1604  dst[i] = src[i];
1605  }
1606 
1607  if (i < dstSizeInBytes) {
1608  dst[i] = '\0';
1609  return 0;
1610  }
1611 
1612  dst[0] = '\0';
1613  return ERANGE;
1614 #endif
1615 }
1616 
1617 int drgui__strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
1618 {
1619 #ifdef _MSC_VER
1620  return strncpy_s(dst, dstSizeInBytes, src, count);
1621 #else
1622  if (dst == 0) {
1623  return EINVAL;
1624  }
1625  if (dstSizeInBytes == 0) {
1626  return EINVAL;
1627  }
1628  if (src == 0) {
1629  dst[0] = '\0';
1630  return EINVAL;
1631  }
1632 
1633  size_t maxcount = count;
1634  if (count == ((size_t)-1) || count >= dstSizeInBytes) { // -1 = _TRUNCATE
1635  maxcount = dstSizeInBytes - 1;
1636  }
1637 
1638  size_t i;
1639  for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
1640  dst[i] = src[i];
1641  }
1642 
1643  if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
1644  dst[i] = '\0';
1645  return 0;
1646  }
1647 
1648  dst[0] = '\0';
1649  return ERANGE;
1650 #endif
1651 }
1652 
1653 
1662 void drgui_begin_inbound_event(drgui_context* pContext);
1663 
1670 void drgui_end_inbound_event(drgui_context* pContext);
1671 
1677 bool drgui_is_handling_inbound_event(const drgui_context* pContext);
1678 
1679 
1688 bool drgui_begin_outbound_event(drgui_element* pElement);
1689 
1691 void drgui_end_outbound_event(drgui_element* pElement);
1692 
1694 bool drgui_is_handling_outbound_event(drgui_context* pContext);
1695 
1696 
1698 void drgui_mark_element_as_dead(drgui_element* pElement);
1699 
1701 bool drgui_is_element_marked_as_dead(const drgui_element* pElement);
1702 
1704 void drgui_delete_elements_marked_as_dead(drgui_context* pContext);
1705 
1706 
1708 void drgui_mark_context_as_dead(drgui_context* pContext);
1709 
1711 bool drgui_is_context_marked_as_dead(const drgui_context* pContext);
1712 
1713 
1718 void drgui_delete_context_for_real(drgui_context* pContext);
1719 
1724 void drgui_delete_element_for_real(drgui_element* pElement);
1725 
1726 
1728 void drgui_detach_without_redraw(drgui_element* pChildElement);
1729 
1731 void drgui_append_without_detach_or_redraw(drgui_element* pChildElement, drgui_element* pParentElement);
1732 
1734 void drgui_append_without_detach(drgui_element* pChildElement, drgui_element* pParentElement);
1735 
1737 void drgui_prepend_without_detach_or_redraw(drgui_element* pChildElement, drgui_element* pParentElement);
1738 
1740 void drgui_prepend_without_detach(drgui_element* pChildElement, drgui_element* pParentElement);
1741 
1743 void drgui_append_sibling_without_detach_or_redraw(drgui_element* pElementToAppend, drgui_element* pElementToAppendTo);
1744 
1746 void drgui_append_sibling_without_detach(drgui_element* pElementToAppend, drgui_element* pElementToAppendTo);
1747 
1749 void drgui_prepend_sibling_without_detach_or_redraw(drgui_element* pElementToPrepend, drgui_element* pElementToPrependTo);
1750 
1752 void drgui_prepend_sibling_without_detach(drgui_element* pElementToPrepend, drgui_element* pElementToPrependTo);
1753 
1754 
1756 void drgui_begin_auto_dirty(drgui_element* pElement);
1757 
1759 void drgui_end_auto_dirty(drgui_element* pElement);
1760 
1765 void drgui_auto_dirty(drgui_element* pTopLevelElement, drgui_rect rect);
1766 
1767 
1772 void drgui_apply_offset_to_children_recursive(drgui_element* pParentElement, float offsetX, float offsetY);
1773 
1774 
1776 void drgui_update_mouse_enter_and_leave_state(drgui_context* pContext, drgui_element* pNewElementUnderMouse);
1777 
1778 
1780 void drgui_post_outbound_event_move(drgui_element* pElement, float newRelativePosX, float newRelativePosY);
1781 void drgui_post_outbound_event_size(drgui_element* pElement, float newWidth, float newHeight);
1782 void drgui_post_outbound_event_mouse_enter(drgui_element* pElement);
1783 void drgui_post_outbound_event_mouse_leave(drgui_element* pElement);
1784 void drgui_post_outbound_event_mouse_move(drgui_element* pElement, int relativeMousePosX, int relativeMousePosY, int stateFlags);
1785 void drgui_post_outbound_event_mouse_button_down(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
1786 void drgui_post_outbound_event_mouse_button_up(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
1787 void drgui_post_outbound_event_mouse_button_dblclick(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
1788 void drgui_post_outbound_event_mouse_wheel(drgui_element* pElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags);
1789 void drgui_post_outbound_event_key_down(drgui_element* pElement, drgui_key key, int stateFlags);
1790 void drgui_post_outbound_event_key_up(drgui_element* pElement, drgui_key key, int stateFlags);
1791 void drgui_post_outbound_event_printable_key_down(drgui_element* pElement, unsigned int character, int stateFlags);
1792 void drgui_post_outbound_event_dirty(drgui_element* pElement, drgui_rect relativeRect);
1793 void drgui_post_outbound_event_dirty_global(drgui_element* pElement, drgui_rect relativeRect);
1794 void drgui_post_outbound_event_capture_mouse(drgui_element* pElement);
1795 void drgui_post_outbound_event_capture_mouse_global(drgui_element* pElement);
1796 void drgui_post_outbound_event_release_mouse(drgui_element* pElement);
1797 void drgui_post_outbound_event_release_mouse_global(drgui_element* pElement);
1798 void drgui_post_outbound_event_capture_keyboard(drgui_element* pElement, drgui_element* pPrevCapturedElement);
1799 void drgui_post_outbound_event_capture_keyboard_global(drgui_element* pElement, drgui_element* pPrevCapturedElement);
1800 void drgui_post_outbound_event_release_keyboard(drgui_element* pElement, drgui_element* pNewCapturedElement);
1801 void drgui_post_outbound_event_release_keyboard_global(drgui_element* pElement, drgui_element* pNewCapturedElement);
1802 
1804 void drgui_log(drgui_context* pContext, const char* message);
1805 
1806 
1807 void drgui_begin_inbound_event(drgui_context* pContext)
1808 {
1809  assert(pContext != NULL);
1810 
1811  pContext->inboundEventCounter += 1;
1812 }
1813 
1814 void drgui_end_inbound_event(drgui_context* pContext)
1815 {
1816  assert(pContext != NULL);
1817  assert(pContext->inboundEventCounter > 0);
1818 
1819  pContext->inboundEventCounter -= 1;
1820 
1821 
1822  // Here is where we want to clean up any elements that are marked as dead. When events are being handled elements are not deleted
1823  // immediately but instead only marked for deletion. This function will be called at the end of event processing which makes it
1824  // an appropriate place for cleaning up dead elements.
1825  if (!drgui_is_handling_inbound_event(pContext))
1826  {
1827  drgui_delete_elements_marked_as_dead(pContext);
1828 
1829  // If the context has been marked for deletion than we will need to delete that too.
1830  if (drgui_is_context_marked_as_dead(pContext))
1831  {
1832  drgui_delete_context_for_real(pContext);
1833  }
1834  }
1835 }
1836 
1837 bool drgui_is_handling_inbound_event(const drgui_context* pContext)
1838 {
1839  assert(pContext != NULL);
1840 
1841  return pContext->inboundEventCounter > 0;
1842 }
1843 
1844 
1845 
1846 bool drgui_begin_outbound_event(drgui_element* pElement)
1847 {
1848  assert(pElement != NULL);
1849  assert(pElement->pContext != NULL);
1850 
1851 
1852  // We want to cancel the outbound event if the element is marked as dead.
1853  if (drgui_is_element_marked_as_dead(pElement)) {
1854  drgui_log(pElement->pContext, "WARNING: Attemping to post an event to an element that is marked for deletion.");
1855  return false;
1856  }
1857 
1858 
1859  // At this point everything should be fine so we just increment the count (which should never go above 1) and return true.
1860  pElement->pContext->outboundEventLockCounter += 1;
1861 
1862  return true;
1863 }
1864 
1865 void drgui_end_outbound_event(drgui_element* pElement)
1866 {
1867  assert(pElement != NULL);
1868  assert(pElement->pContext != NULL);
1869  assert(pElement->pContext->outboundEventLockCounter > 0);
1870 
1871  pElement->pContext->outboundEventLockCounter -= 1;
1872 }
1873 
1874 bool drgui_is_handling_outbound_event(drgui_context* pContext)
1875 {
1876  assert(pContext != NULL);
1877  return pContext->outboundEventLockCounter > 0;
1878 }
1879 
1880 
1881 void drgui_mark_element_as_dead(drgui_element* pElement)
1882 {
1883  assert(pElement != NULL);
1884  assert(pElement->pContext != NULL);
1885 
1886  pElement->flags |= IS_ELEMENT_DEAD;
1887 
1888 
1889  if (pElement->pContext->pFirstDeadElement != NULL) {
1890  pElement->pNextDeadElement = pElement->pContext->pFirstDeadElement;
1891  }
1892 
1893  pElement->pContext->pFirstDeadElement = pElement;
1894 }
1895 
1896 bool drgui_is_element_marked_as_dead(const drgui_element* pElement)
1897 {
1898  if (pElement == NULL) {
1899  return false;
1900  }
1901 
1902  return (pElement->flags & IS_ELEMENT_DEAD) != 0;
1903 }
1904 
1905 void drgui_delete_elements_marked_as_dead(drgui_context* pContext)
1906 {
1907  assert(pContext != NULL);
1908 
1909  while (pContext->pFirstDeadElement != NULL)
1910  {
1911  drgui_element* pDeadElement = pContext->pFirstDeadElement;
1912  pContext->pFirstDeadElement = pContext->pFirstDeadElement->pNextDeadElement;
1913 
1914  drgui_delete_element_for_real(pDeadElement);
1915  }
1916 }
1917 
1918 
1919 void drgui_mark_context_as_dead(drgui_context* pContext)
1920 {
1921  assert(pContext != NULL);
1922  assert(!drgui_is_context_marked_as_dead(pContext));
1923 
1924  pContext->flags |= IS_CONTEXT_DEAD;
1925 }
1926 
1927 bool drgui_is_context_marked_as_dead(const drgui_context* pContext)
1928 {
1929  assert(pContext != NULL);
1930 
1931  return (pContext->flags & IS_CONTEXT_DEAD) != 0;
1932 }
1933 
1934 
1935 
1936 void drgui_delete_context_for_real(drgui_context* pContext)
1937 {
1938  assert(pContext != NULL);
1939 
1940  // All elements marked as dead need to be deleted.
1941  drgui_delete_elements_marked_as_dead(pContext);
1942 
1943  free(pContext);
1944 }
1945 
1946 void drgui_delete_element_for_real(drgui_element* pElementToDelete)
1947 {
1948  assert(pElementToDelete != NULL);
1949 
1950  drgui_context* pContext = pElementToDelete->pContext;
1951 
1952  // If the element is marked as dead
1953  if (drgui_is_element_marked_as_dead(pElementToDelete)) {
1954  if (pContext->pFirstDeadElement == pElementToDelete) {
1955  pContext->pFirstDeadElement = pContext->pFirstDeadElement->pNextDeadElement;
1956  } else {
1957  drgui_element* pPrevDeadElement = pContext->pFirstDeadElement;
1958  while (pPrevDeadElement != NULL) {
1959  if (pPrevDeadElement->pNextDeadElement == pElementToDelete) {
1960  break;
1961  }
1962 
1963  pPrevDeadElement = pPrevDeadElement->pNextDeadElement;
1964  }
1965 
1966  if (pPrevDeadElement != NULL) {
1967  pElementToDelete->pNextDeadElement = pElementToDelete->pNextDeadElement;
1968  }
1969  }
1970  }
1971 
1972  free(pElementToDelete);
1973 }
1974 
1975 
1976 void drgui_detach_without_redraw(drgui_element* pElement)
1977 {
1978  if (pElement->pParent != NULL) {
1979  if (pElement->pParent->pFirstChild == pElement) {
1980  pElement->pParent->pFirstChild = pElement->pNextSibling;
1981  }
1982 
1983  if (pElement->pParent->pLastChild == pElement) {
1984  pElement->pParent->pLastChild = pElement->pPrevSibling;
1985  }
1986 
1987 
1988  if (pElement->pPrevSibling != NULL) {
1989  pElement->pPrevSibling->pNextSibling = pElement->pNextSibling;
1990  }
1991 
1992  if (pElement->pNextSibling != NULL) {
1993  pElement->pNextSibling->pPrevSibling = pElement->pPrevSibling;
1994  }
1995  }
1996 
1997  pElement->pParent = NULL;
1998  pElement->pPrevSibling = NULL;
1999  pElement->pNextSibling = NULL;
2000 }
2001 
2002 void drgui_append_without_detach_or_redraw(drgui_element* pChildElement, drgui_element* pParentElement)
2003 {
2004  pChildElement->pParent = pParentElement;
2005  if (pChildElement->pParent != NULL) {
2006  if (pChildElement->pParent->pLastChild != NULL) {
2007  pChildElement->pPrevSibling = pChildElement->pParent->pLastChild;
2008  pChildElement->pPrevSibling->pNextSibling = pChildElement;
2009  }
2010 
2011  if (pChildElement->pParent->pFirstChild == NULL) {
2012  pChildElement->pParent->pFirstChild = pChildElement;
2013  }
2014 
2015  pChildElement->pParent->pLastChild = pChildElement;
2016  }
2017 }
2018 
2019 void drgui_append_without_detach(drgui_element* pChildElement, drgui_element* pParentElement)
2020 {
2021  drgui_append_without_detach_or_redraw(pChildElement, pParentElement);
2022  drgui_auto_dirty(pChildElement, drgui_make_rect(0, 0, pChildElement->width, pChildElement->height));
2023 }
2024 
2025 void drgui_prepend_without_detach_or_redraw(drgui_element* pChildElement, drgui_element* pParentElement)
2026 {
2027  pChildElement->pParent = pParentElement;
2028  if (pChildElement->pParent != NULL) {
2029  if (pChildElement->pParent->pFirstChild != NULL) {
2030  pChildElement->pNextSibling = pChildElement->pParent->pFirstChild;
2031  pChildElement->pNextSibling->pPrevSibling = pChildElement;
2032  }
2033 
2034  if (pChildElement->pParent->pLastChild == NULL) {
2035  pChildElement->pParent->pLastChild = pChildElement;
2036  }
2037 
2038  pChildElement->pParent->pFirstChild = pChildElement;
2039  }
2040 }
2041 
2042 void drgui_prepend_without_detach(drgui_element* pChildElement, drgui_element* pParentElement)
2043 {
2044  drgui_prepend_without_detach_or_redraw(pChildElement, pParentElement);
2045  drgui_auto_dirty(pChildElement, drgui_make_rect(0, 0, pChildElement->width, pChildElement->height));
2046 }
2047 
2048 void drgui_append_sibling_without_detach_or_redraw(drgui_element* pElementToAppend, drgui_element* pElementToAppendTo)
2049 {
2050  assert(pElementToAppend != NULL);
2051  assert(pElementToAppendTo != NULL);
2052 
2053  pElementToAppend->pParent = pElementToAppendTo->pParent;
2054  if (pElementToAppend->pParent != NULL)
2055  {
2056  pElementToAppend->pNextSibling = pElementToAppendTo->pNextSibling;
2057  pElementToAppend->pPrevSibling = pElementToAppendTo;
2058 
2059  pElementToAppendTo->pNextSibling->pPrevSibling = pElementToAppend;
2060  pElementToAppendTo->pNextSibling = pElementToAppend;
2061 
2062  if (pElementToAppend->pParent->pLastChild == pElementToAppendTo) {
2063  pElementToAppend->pParent->pLastChild = pElementToAppend;
2064  }
2065  }
2066 }
2067 
2068 void drgui_append_sibling_without_detach(drgui_element* pElementToAppend, drgui_element* pElementToAppendTo)
2069 {
2070  drgui_append_sibling_without_detach_or_redraw(pElementToAppend, pElementToAppendTo);
2071  drgui_auto_dirty(pElementToAppend, drgui_make_rect(0, 0, pElementToAppend->width, pElementToAppend->height));
2072 }
2073 
2074 void drgui_prepend_sibling_without_detach_or_redraw(drgui_element* pElementToPrepend, drgui_element* pElementToPrependTo)
2075 {
2076  assert(pElementToPrepend != NULL);
2077  assert(pElementToPrependTo != NULL);
2078 
2079  pElementToPrepend->pParent = pElementToPrependTo->pParent;
2080  if (pElementToPrepend->pParent != NULL)
2081  {
2082  pElementToPrepend->pPrevSibling = pElementToPrependTo->pNextSibling;
2083  pElementToPrepend->pNextSibling = pElementToPrependTo;
2084 
2085  pElementToPrependTo->pPrevSibling->pNextSibling = pElementToPrepend;
2086  pElementToPrependTo->pNextSibling = pElementToPrepend;
2087 
2088  if (pElementToPrepend->pParent->pFirstChild == pElementToPrependTo) {
2089  pElementToPrepend->pParent->pFirstChild = pElementToPrepend;
2090  }
2091  }
2092 }
2093 
2094 void drgui_prepend_sibling_without_detach(drgui_element* pElementToPrepend, drgui_element* pElementToPrependTo)
2095 {
2096  drgui_prepend_sibling_without_detach_or_redraw(pElementToPrepend, pElementToPrependTo);
2097  drgui_auto_dirty(pElementToPrepend, drgui_make_rect(0, 0, pElementToPrepend->width, pElementToPrepend->height));
2098 }
2099 
2100 
2101 void drgui_begin_auto_dirty(drgui_element* pElement)
2102 {
2103  assert(pElement != NULL);
2104  assert(pElement->pContext != NULL);
2105 
2106  if (drgui_is_auto_dirty_enabled(pElement->pContext)) {
2107  drgui_begin_dirty(pElement);
2108  }
2109 }
2110 
2111 void drgui_end_auto_dirty(drgui_element* pElement)
2112 {
2113  assert(pElement != NULL);
2114 
2115  drgui_context* pContext = pElement->pContext;
2116  assert(pContext != NULL);
2117 
2118  if (drgui_is_auto_dirty_enabled(pContext)) {
2119  drgui_end_dirty(pElement);
2120  }
2121 }
2122 
2123 void drgui_auto_dirty(drgui_element* pElement, drgui_rect relativeRect)
2124 {
2125  assert(pElement != NULL);
2126  assert(pElement->pContext != NULL);
2127 
2128  if (drgui_is_auto_dirty_enabled(pElement->pContext)) {
2129  drgui_dirty(pElement, relativeRect);
2130  }
2131 }
2132 
2133 
2134 void drgui__change_cursor(drgui_element* pElement, drgui_cursor_type cursor)
2135 {
2136  if (pElement == NULL || pElement->pContext == NULL) {
2137  return;
2138  }
2139 
2140  pElement->pContext->currentCursor = cursor;
2141 
2142  if (pElement->pContext->onChangeCursor) {
2143  pElement->pContext->onChangeCursor(pElement, cursor);
2144  }
2145 }
2146 
2147 
2148 
2149 void drgui_apply_offset_to_children_recursive(drgui_element* pParentElement, float offsetX, float offsetY)
2150 {
2151  assert(pParentElement != NULL);
2152 
2153  for (drgui_element* pChild = pParentElement->pFirstChild; pChild != NULL; pChild = pChild->pNextSibling)
2154  {
2155  drgui_begin_auto_dirty(pParentElement);
2156  {
2157  drgui_auto_dirty(pParentElement, drgui_get_local_rect(pParentElement));
2158  pChild->absolutePosX += offsetX;
2159  pChild->absolutePosY += offsetY;
2160 
2161  drgui_apply_offset_to_children_recursive(pChild, offsetX, offsetY);
2162  }
2163  drgui_end_auto_dirty(pParentElement);
2164  }
2165 }
2166 
2167 DRGUI_PRIVATE void drgui_post_on_mouse_leave_recursive(drgui_context* pContext, drgui_element* pNewElementUnderMouse, drgui_element* pOldElementUnderMouse)
2168 {
2169  (void)pContext;
2170 
2171  drgui_element* pOldAncestor = pOldElementUnderMouse;
2172  while (pOldAncestor != NULL)
2173  {
2174  bool isOldElementUnderMouse = pNewElementUnderMouse == pOldAncestor || drgui_is_ancestor(pOldAncestor, pNewElementUnderMouse);
2175  if (!isOldElementUnderMouse)
2176  {
2177  drgui_post_outbound_event_mouse_leave(pOldAncestor);
2178  }
2179 
2180  pOldAncestor = pOldAncestor->pParent;
2181  }
2182 }
2183 
2184 DRGUI_PRIVATE void drgui_post_on_mouse_enter_recursive(drgui_context* pContext, drgui_element* pNewElementUnderMouse, drgui_element* pOldElementUnderMouse)
2185 {
2186  if (pNewElementUnderMouse == NULL) {
2187  return;
2188  }
2189 
2190 
2191  if (pNewElementUnderMouse->pParent != NULL) {
2192  drgui_post_on_mouse_enter_recursive(pContext, pNewElementUnderMouse->pParent, pOldElementUnderMouse);
2193  }
2194 
2195  bool wasNewElementUnderMouse = pOldElementUnderMouse == pNewElementUnderMouse || drgui_is_ancestor(pNewElementUnderMouse, pOldElementUnderMouse);
2196  if (!wasNewElementUnderMouse)
2197  {
2198  drgui_post_outbound_event_mouse_enter(pNewElementUnderMouse);
2199  }
2200 }
2201 
2202 void drgui_update_mouse_enter_and_leave_state(drgui_context* pContext, drgui_element* pNewElementUnderMouse)
2203 {
2204  if (pContext == NULL) {
2205  return;
2206  }
2207 
2208  drgui_element* pOldElementUnderMouse = pContext->pElementUnderMouse;
2209  if (pOldElementUnderMouse != pNewElementUnderMouse)
2210  {
2211  // We don't change the enter and leave state if an element is capturing the mouse.
2212  if (pContext->pElementWithMouseCapture == NULL)
2213  {
2214  pContext->pElementUnderMouse = pNewElementUnderMouse;
2215 
2217  if (pNewElementUnderMouse != NULL) {
2218  newCursor = pNewElementUnderMouse->cursor;
2219  }
2220 
2221 
2222  // It's intuitive to check that the new cursor is different to the old one before trying to change it, but that is not actually
2223  // what we want to do. We'll let the event handler manage it themselves because it's possible the window manager might do some
2224  // window-specific cursor management and the old and new elements are on different windows.
2225  drgui__change_cursor(pNewElementUnderMouse, newCursor);
2226 
2227 
2228 
2229  // The the event handlers below, remember that ancestors are considered hovered if a descendant is the element under the mouse.
2230 
2231  // on_mouse_leave
2232  drgui_post_on_mouse_leave_recursive(pContext, pNewElementUnderMouse, pOldElementUnderMouse);
2233 
2234  // on_mouse_enter
2235  drgui_post_on_mouse_enter_recursive(pContext, pNewElementUnderMouse, pOldElementUnderMouse);
2236  }
2237  }
2238 }
2239 
2240 
2241 void drgui_post_outbound_event_move(drgui_element* pElement, float newRelativePosX, float newRelativePosY)
2242 {
2243  if (drgui_begin_outbound_event(pElement))
2244  {
2245  if (pElement->onMove) {
2246  pElement->onMove(pElement, newRelativePosX, newRelativePosY);
2247  }
2248 
2249  drgui_end_outbound_event(pElement);
2250  }
2251 }
2252 
2253 void drgui_post_outbound_event_size(drgui_element* pElement, float newWidth, float newHeight)
2254 {
2255  if (drgui_begin_outbound_event(pElement))
2256  {
2257  if (pElement->onSize) {
2258  pElement->onSize(pElement, newWidth, newHeight);
2259  }
2260 
2261  drgui_end_outbound_event(pElement);
2262  }
2263 }
2264 
2265 void drgui_post_outbound_event_mouse_enter(drgui_element* pElement)
2266 {
2267  if (drgui_begin_outbound_event(pElement))
2268  {
2269  if (pElement->onMouseEnter) {
2270  pElement->onMouseEnter(pElement);
2271  }
2272 
2273  drgui_end_outbound_event(pElement);
2274  }
2275 }
2276 
2277 void drgui_post_outbound_event_mouse_leave(drgui_element* pElement)
2278 {
2279  if (drgui_begin_outbound_event(pElement))
2280  {
2281  if (pElement->onMouseLeave) {
2282  pElement->onMouseLeave(pElement);
2283  }
2284 
2285  drgui_end_outbound_event(pElement);
2286  }
2287 }
2288 
2289 void drgui_post_outbound_event_mouse_move(drgui_element* pElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
2290 {
2291  if (drgui_begin_outbound_event(pElement))
2292 {
2293  if (pElement->onMouseMove) {
2294  pElement->onMouseMove(pElement, relativeMousePosX, relativeMousePosY, stateFlags);
2295  }
2296 
2297  drgui_end_outbound_event(pElement);
2298  }
2299 }
2300 
2301 void drgui_post_outbound_event_mouse_button_down(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
2302 {
2303  if (drgui_begin_outbound_event(pElement))
2304  {
2305  if (pElement->onMouseButtonDown) {
2306  pElement->onMouseButtonDown(pElement, mouseButton, relativeMousePosX, relativeMousePosY, stateFlags);
2307  }
2308 
2309  drgui_end_outbound_event(pElement);
2310  }
2311 }
2312 
2313 void drgui_post_outbound_event_mouse_button_up(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
2314 {
2315  if (drgui_begin_outbound_event(pElement))
2316  {
2317  if (pElement->onMouseButtonUp) {
2318  pElement->onMouseButtonUp(pElement, mouseButton, relativeMousePosX, relativeMousePosY, stateFlags);
2319  }
2320 
2321  drgui_end_outbound_event(pElement);
2322  }
2323 }
2324 
2325 void drgui_post_outbound_event_mouse_button_dblclick(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
2326 {
2327  if (drgui_begin_outbound_event(pElement))
2328  {
2329  if (pElement->onMouseButtonDblClick) {
2330  pElement->onMouseButtonDblClick(pElement, mouseButton, relativeMousePosX, relativeMousePosY, stateFlags);
2331  }
2332 
2333  drgui_end_outbound_event(pElement);
2334  }
2335 }
2336 
2337 void drgui_post_outbound_event_mouse_wheel(drgui_element* pElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags)
2338 {
2339  if (drgui_begin_outbound_event(pElement))
2340  {
2341  if (pElement->onMouseWheel) {
2342  pElement->onMouseWheel(pElement, delta, relativeMousePosX, relativeMousePosY, stateFlags);
2343  }
2344 
2345  drgui_end_outbound_event(pElement);
2346  }
2347 }
2348 
2349 void drgui_post_outbound_event_key_down(drgui_element* pElement, drgui_key key, int stateFlags)
2350 {
2351  if (drgui_begin_outbound_event(pElement))
2352  {
2353  if (pElement->onKeyDown) {
2354  pElement->onKeyDown(pElement, key, stateFlags);
2355  }
2356 
2357  drgui_end_outbound_event(pElement);
2358  }
2359 }
2360 
2361 void drgui_post_outbound_event_key_up(drgui_element* pElement, drgui_key key, int stateFlags)
2362 {
2363  if (drgui_begin_outbound_event(pElement))
2364  {
2365  if (pElement->onKeyUp) {
2366  pElement->onKeyUp(pElement, key, stateFlags);
2367  }
2368 
2369  drgui_end_outbound_event(pElement);
2370  }
2371 }
2372 
2373 void drgui_post_outbound_event_printable_key_down(drgui_element* pElement, unsigned int character, int stateFlags)
2374 {
2375  if (drgui_begin_outbound_event(pElement))
2376  {
2377  if (pElement->onPrintableKeyDown) {
2378  pElement->onPrintableKeyDown(pElement, character, stateFlags);
2379  }
2380 
2381  drgui_end_outbound_event(pElement);
2382  }
2383 }
2384 
2385 
2386 void drgui_post_outbound_event_dirty(drgui_element* pElement, drgui_rect relativeRect)
2387 {
2388  if (pElement != NULL)
2389  {
2390  if (pElement->onDirty) {
2391  pElement->onDirty(pElement, relativeRect);
2392  }
2393  }
2394 }
2395 
2396 void drgui_post_outbound_event_dirty_global(drgui_element* pElement, drgui_rect relativeRect)
2397 {
2398  if (pElement != NULL && pElement->pContext != NULL)
2399  {
2400  if (pElement->pContext->onGlobalDirty) {
2401  pElement->pContext->onGlobalDirty(pElement, relativeRect);
2402  }
2403  }
2404 }
2405 
2406 void drgui_post_outbound_event_capture_mouse(drgui_element* pElement)
2407 {
2408  if (pElement != NULL)
2409  {
2410  if (pElement->onCaptureMouse) {
2411  pElement->onCaptureMouse(pElement);
2412  }
2413  }
2414 }
2415 
2416 void drgui_post_outbound_event_capture_mouse_global(drgui_element* pElement)
2417 {
2418  if (pElement != NULL && pElement->pContext != NULL)
2419  {
2420  if (pElement->pContext->onGlobalCaptureMouse) {
2421  pElement->pContext->onGlobalCaptureMouse(pElement);
2422  }
2423  }
2424 }
2425 
2426 void drgui_post_outbound_event_release_mouse(drgui_element* pElement)
2427 {
2428  if (pElement != NULL)
2429  {
2430  if (pElement->onReleaseMouse) {
2431  pElement->onReleaseMouse(pElement);
2432  }
2433  }
2434 }
2435 
2436 void drgui_post_outbound_event_release_mouse_global(drgui_element* pElement)
2437 {
2438  if (pElement != NULL && pElement->pContext != NULL)
2439  {
2440  if (pElement->pContext->onGlobalReleaseMouse) {
2441  pElement->pContext->onGlobalReleaseMouse(pElement);
2442  }
2443  }
2444 }
2445 
2446 
2447 void drgui_post_outbound_event_capture_keyboard(drgui_element* pElement, drgui_element* pPrevCapturedElement)
2448 {
2449  if (pElement != NULL)
2450  {
2451  if (pElement->onCaptureKeyboard) {
2452  pElement->onCaptureKeyboard(pElement, pPrevCapturedElement);
2453  }
2454  }
2455 }
2456 
2457 void drgui_post_outbound_event_capture_keyboard_global(drgui_element* pElement, drgui_element* pPrevCapturedElement)
2458 {
2459  if (pElement != NULL && pElement->pContext != NULL)
2460  {
2461  if (pElement->pContext->onGlobalCaptureKeyboard) {
2462  pElement->pContext->onGlobalCaptureKeyboard(pElement, pPrevCapturedElement);
2463  }
2464  }
2465 }
2466 
2467 void drgui_post_outbound_event_release_keyboard(drgui_element* pElement, drgui_element* pNewCapturedElement)
2468 {
2469  if (pElement != NULL)
2470  {
2471  if (pElement->onReleaseKeyboard) {
2472  pElement->onReleaseKeyboard(pElement, pNewCapturedElement);
2473  }
2474  }
2475 }
2476 
2477 void drgui_post_outbound_event_release_keyboard_global(drgui_element* pElement, drgui_element* pNewCapturedElement)
2478 {
2479  if (pElement != NULL && pElement->pContext != NULL)
2480  {
2481  if (pElement->pContext->onGlobalReleaseKeyboard) {
2482  pElement->pContext->onGlobalReleaseKeyboard(pElement, pNewCapturedElement);
2483  }
2484  }
2485 }
2486 
2487 
2488 void drgui_log(drgui_context* pContext, const char* message)
2489 {
2490  if (pContext != NULL)
2491  {
2492  if (pContext->onLog) {
2493  pContext->onLog(pContext, message);
2494  }
2495  }
2496 }
2497 
2498 
2500 //
2501 // CORE API
2502 //
2504 
2506 {
2507  drgui_context* pContext = (drgui_context*)calloc(1, sizeof(drgui_context));
2508  if (pContext != NULL) {
2509  pContext->currentCursor = drgui_cursor_default;
2510  }
2511 
2512  return pContext;
2513 }
2514 
2515 void drgui_delete_context(drgui_context* pContext)
2516 {
2517  if (pContext == NULL) {
2518  return;
2519  }
2520 
2521 
2522  // Make sure the mouse capture is released.
2523  if (pContext->pElementWithMouseCapture != NULL)
2524  {
2525  drgui_log(pContext, "WARNING: Deleting the GUI context while an element still has the mouse capture.");
2526  drgui_release_mouse(pContext);
2527  }
2528 
2529  // Make sure the keyboard capture is released.
2530  if (pContext->pElementWithKeyboardCapture != NULL)
2531  {
2532  drgui_log(pContext, "WARNING: Deleting the GUI context while an element still has the keyboard capture.");
2533  drgui_release_keyboard(pContext);
2534  }
2535 
2536 
2537  if (drgui_is_handling_inbound_event(pContext))
2538  {
2539  // An inbound event is still being processed - we don't want to delete the context straight away because we can't
2540  // trust external event handlers to not try to access the context later on. To do this we just set the flag that
2541  // the context is deleted. It will then be deleted for real at the end of the inbound event handler.
2542  drgui_mark_context_as_dead(pContext);
2543  }
2544  else
2545  {
2546  // An inbound event is not being processed, so delete the context straight away.
2547  drgui_delete_context_for_real(pContext);
2548  }
2549 }
2550 
2551 
2552 
2554 // Events
2555 
2557 {
2558  if (pTopLevelElement == NULL) {
2559  return;
2560  }
2561 
2562  drgui_context* pContext = pTopLevelElement->pContext;
2563  if (pContext == NULL) {
2564  return;
2565  }
2566 
2567  drgui_begin_inbound_event(pContext);
2568  {
2569  // We assume that was previously under the mouse was either pTopLevelElement itself or one of it's descendants.
2570  drgui_update_mouse_enter_and_leave_state(pContext, NULL);
2571  }
2572  drgui_end_inbound_event(pContext);
2573 }
2574 
2575 void drgui_post_inbound_event_mouse_move(drgui_element* pTopLevelElement, int mousePosX, int mousePosY, int stateFlags)
2576 {
2577  if (pTopLevelElement == NULL || pTopLevelElement->pContext == NULL) {
2578  return;
2579  }
2580 
2581 
2582  drgui_begin_inbound_event(pTopLevelElement->pContext);
2583  {
2585  pTopLevelElement->pContext->pLastMouseMoveTopLevelElement = pTopLevelElement;
2586 
2588  pTopLevelElement->pContext->lastMouseMovePosX = (float)mousePosX;
2589  pTopLevelElement->pContext->lastMouseMovePosY = (float)mousePosY;
2590 
2591 
2592 
2593  // The first thing we need to do is find the new element that's sitting under the mouse.
2594  drgui_element* pNewElementUnderMouse = drgui_find_element_under_point(pTopLevelElement, (float)mousePosX, (float)mousePosY);
2595 
2596  // Now that we know which element is sitting under the mouse we need to check if the mouse has entered into a new element.
2597  drgui_update_mouse_enter_and_leave_state(pTopLevelElement->pContext, pNewElementUnderMouse);
2598 
2599 
2600  drgui_element* pEventReceiver = pTopLevelElement->pContext->pElementWithMouseCapture;
2601  if (pEventReceiver == NULL)
2602  {
2603  pEventReceiver = pNewElementUnderMouse;
2604  }
2605 
2606  if (pEventReceiver != NULL)
2607  {
2608  float relativeMousePosX = (float)mousePosX;
2609  float relativeMousePosY = (float)mousePosY;
2610  drgui_make_point_relative(pEventReceiver, &relativeMousePosX, &relativeMousePosY);
2611 
2612  drgui_post_outbound_event_mouse_move(pEventReceiver, (int)relativeMousePosX, (int)relativeMousePosY, stateFlags);
2613  }
2614  }
2615  drgui_end_inbound_event(pTopLevelElement->pContext);
2616 }
2617 
2618 void drgui_post_inbound_event_mouse_button_down(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags)
2619 {
2620  if (pTopLevelElement == NULL || pTopLevelElement->pContext == NULL) {
2621  return;
2622  }
2623 
2624  drgui_context* pContext = pTopLevelElement->pContext;
2625  drgui_begin_inbound_event(pContext);
2626  {
2627  drgui_element* pEventReceiver = pContext->pElementWithMouseCapture;
2628  if (pEventReceiver == NULL)
2629  {
2630  pEventReceiver = pContext->pElementUnderMouse;
2631 
2632  if (pEventReceiver == NULL)
2633  {
2634  // We'll get here if this message is posted without a prior mouse move event.
2635  pEventReceiver = drgui_find_element_under_point(pTopLevelElement, (float)mousePosX, (float)mousePosY);
2636  }
2637  }
2638 
2639 
2640  if (pEventReceiver != NULL)
2641  {
2642  float relativeMousePosX = (float)mousePosX;
2643  float relativeMousePosY = (float)mousePosY;
2644  drgui_make_point_relative(pEventReceiver, &relativeMousePosX, &relativeMousePosY);
2645 
2646  drgui_post_outbound_event_mouse_button_down(pEventReceiver, mouseButton, (int)relativeMousePosX, (int)relativeMousePosY, stateFlags);
2647  }
2648  }
2649  drgui_end_inbound_event(pContext);
2650 }
2651 
2652 void drgui_post_inbound_event_mouse_button_up(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags)
2653 {
2654  if (pTopLevelElement == NULL || pTopLevelElement->pContext == NULL) {
2655  return;
2656  }
2657 
2658  drgui_context* pContext = pTopLevelElement->pContext;
2659  drgui_begin_inbound_event(pContext);
2660  {
2661  drgui_element* pEventReceiver = pContext->pElementWithMouseCapture;
2662  if (pEventReceiver == NULL)
2663  {
2664  pEventReceiver = pContext->pElementUnderMouse;
2665 
2666  if (pEventReceiver == NULL)
2667  {
2668  // We'll get here if this message is posted without a prior mouse move event.
2669  pEventReceiver = drgui_find_element_under_point(pTopLevelElement, (float)mousePosX, (float)mousePosY);
2670  }
2671  }
2672 
2673 
2674  if (pEventReceiver != NULL)
2675  {
2676  float relativeMousePosX = (float)mousePosX;
2677  float relativeMousePosY = (float)mousePosY;
2678  drgui_make_point_relative(pEventReceiver, &relativeMousePosX, &relativeMousePosY);
2679 
2680  drgui_post_outbound_event_mouse_button_up(pEventReceiver, mouseButton, (int)relativeMousePosX, (int)relativeMousePosY, stateFlags);
2681  }
2682  }
2683  drgui_end_inbound_event(pContext);
2684 }
2685 
2686 void drgui_post_inbound_event_mouse_button_dblclick(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags)
2687 {
2688  if (pTopLevelElement == NULL || pTopLevelElement->pContext == NULL) {
2689  return;
2690  }
2691 
2692  drgui_context* pContext = pTopLevelElement->pContext;
2693  drgui_begin_inbound_event(pContext);
2694  {
2695  drgui_element* pEventReceiver = pContext->pElementWithMouseCapture;
2696  if (pEventReceiver == NULL)
2697  {
2698  pEventReceiver = pContext->pElementUnderMouse;
2699 
2700  if (pEventReceiver == NULL)
2701  {
2702  // We'll get here if this message is posted without a prior mouse move event.
2703  pEventReceiver = drgui_find_element_under_point(pTopLevelElement, (float)mousePosX, (float)mousePosY);
2704  }
2705  }
2706 
2707 
2708  if (pEventReceiver != NULL)
2709  {
2710  float relativeMousePosX = (float)mousePosX;
2711  float relativeMousePosY = (float)mousePosY;
2712  drgui_make_point_relative(pEventReceiver, &relativeMousePosX, &relativeMousePosY);
2713 
2714  drgui_post_outbound_event_mouse_button_dblclick(pEventReceiver, mouseButton, (int)relativeMousePosX, (int)relativeMousePosY, stateFlags);
2715  }
2716  }
2717  drgui_end_inbound_event(pContext);
2718 }
2719 
2720 void drgui_post_inbound_event_mouse_wheel(drgui_element* pTopLevelElement, int delta, int mousePosX, int mousePosY, int stateFlags)
2721 {
2722  if (pTopLevelElement == NULL || pTopLevelElement->pContext == NULL) {
2723  return;
2724  }
2725 
2726  drgui_context* pContext = pTopLevelElement->pContext;
2727  drgui_begin_inbound_event(pContext);
2728  {
2729  drgui_element* pEventReceiver = pContext->pElementWithMouseCapture;
2730  if (pEventReceiver == NULL)
2731  {
2732  pEventReceiver = pContext->pElementUnderMouse;
2733 
2734  if (pEventReceiver == NULL)
2735  {
2736  // We'll get here if this message is posted without a prior mouse move event.
2737  pEventReceiver = drgui_find_element_under_point(pTopLevelElement, (float)mousePosX, (float)mousePosY);
2738  }
2739  }
2740 
2741 
2742  if (pEventReceiver != NULL)
2743  {
2744  float relativeMousePosX = (float)mousePosX;
2745  float relativeMousePosY = (float)mousePosY;
2746  drgui_make_point_relative(pEventReceiver, &relativeMousePosX, &relativeMousePosY);
2747 
2748  drgui_post_outbound_event_mouse_wheel(pEventReceiver, delta, (int)relativeMousePosX, (int)relativeMousePosY, stateFlags);
2749  }
2750  }
2751  drgui_end_inbound_event(pContext);
2752 }
2753 
2754 void drgui_post_inbound_event_key_down(drgui_context* pContext, drgui_key key, int stateFlags)
2755 {
2756  if (pContext == NULL) {
2757  return;
2758  }
2759 
2760  drgui_begin_inbound_event(pContext);
2761  {
2762  if (pContext->pElementWithKeyboardCapture != NULL) {
2763  drgui_post_outbound_event_key_down(pContext->pElementWithKeyboardCapture, key, stateFlags);
2764  }
2765  }
2766  drgui_end_inbound_event(pContext);
2767 }
2768 
2769 void drgui_post_inbound_event_key_up(drgui_context* pContext, drgui_key key, int stateFlags)
2770 {
2771  if (pContext == NULL) {
2772  return;
2773  }
2774 
2775  drgui_begin_inbound_event(pContext);
2776  {
2777  if (pContext->pElementWithKeyboardCapture != NULL) {
2778  drgui_post_outbound_event_key_up(pContext->pElementWithKeyboardCapture, key, stateFlags);
2779  }
2780  }
2781  drgui_end_inbound_event(pContext);
2782 }
2783 
2784 void drgui_post_inbound_event_printable_key_down(drgui_context* pContext, unsigned int character, int stateFlags)
2785 {
2786  if (pContext == NULL) {
2787  return;
2788  }
2789 
2790  drgui_begin_inbound_event(pContext);
2791  {
2792  if (pContext->pElementWithKeyboardCapture != NULL) {
2793  drgui_post_outbound_event_printable_key_down(pContext->pElementWithKeyboardCapture, character, stateFlags);
2794  }
2795  }
2796  drgui_end_inbound_event(pContext);
2797 }
2798 
2799 
2800 
2802 {
2803  if (pContext != NULL) {
2804  pContext->onGlobalDirty = onDirty;
2805  }
2806 }
2807 
2809 {
2810  if (pContext != NULL) {
2811  pContext->onGlobalCaptureMouse = onCaptureMouse;
2812  }
2813 }
2814 
2816 {
2817  if (pContext != NULL) {
2818  pContext->onGlobalReleaseMouse = onReleaseMouse;
2819  }
2820 }
2821 
2823 {
2824  if (pContext != NULL) {
2825  pContext->onGlobalCaptureKeyboard = onCaptureKeyboard;
2826  }
2827 }
2828 
2830 {
2831  if (pContext != NULL) {
2832  pContext->onGlobalReleaseKeyboard = onReleaseKeyboard;
2833  }
2834 }
2835 
2837 {
2838  if (pContext != NULL) {
2839  pContext->onChangeCursor = onChangeCursor;
2840  }
2841 }
2842 
2844 {
2845  if (pContext != NULL) {
2846  pContext->onDeleteElement = onDeleteElement;
2847  }
2848 }
2849 
2850 void drgui_set_on_log(drgui_context* pContext, drgui_on_log onLog)
2851 {
2852  if (pContext != NULL) {
2853  pContext->onLog = onLog;
2854  }
2855 }
2856 
2857 
2858 
2860 // Elements
2861 
2862 drgui_element* drgui_create_element(drgui_context* pContext, drgui_element* pParent, size_t extraDataSize, const void* pExtraData)
2863 {
2864  if (pContext != NULL)
2865  {
2866  drgui_element* pElement = (drgui_element*)calloc(1, sizeof(drgui_element) + extraDataSize);
2867  if (pElement != NULL) {
2868  pElement->pContext = pContext;
2869  pElement->pParent = pParent;
2870  pElement->cursor = drgui_cursor_default;
2871  pElement->dirtyRect = drgui_make_inside_out_rect();
2872 
2873  pElement->extraDataSize = extraDataSize;
2874  if (pExtraData != NULL) {
2875  memcpy(pElement->pExtraData, pExtraData, extraDataSize);
2876  }
2877 
2878  // Add to the the hierarchy.
2879  drgui_append_without_detach_or_redraw(pElement, pElement->pParent);
2880 
2881 
2882  // Have the element positioned at 0,0 relative to the parent by default.
2883  if (pParent != NULL) {
2884  pElement->absolutePosX = pParent->absolutePosX;
2885  pElement->absolutePosY = pParent->absolutePosY;
2886  }
2887 
2888 
2889  return pElement;
2890  }
2891  }
2892 
2893  return NULL;
2894 }
2895 
2896 void drgui_delete_element(drgui_element* pElement)
2897 {
2898  if (pElement == NULL) {
2899  return;
2900  }
2901 
2902  drgui_context* pContext = pElement->pContext;
2903  if (pContext == NULL) {
2904  return;
2905  }
2906 
2907  if (drgui_is_element_marked_as_dead(pElement)) {
2908  drgui_log(pContext, "WARNING: Attempting to delete an element that is already marked for deletion.");
2909  return;
2910  }
2911 
2912  drgui_mark_element_as_dead(pElement);
2913 
2914 
2915  // Notify the application that the element is being deleted. Do this at the top so the event handler can access things like the hierarchy and
2916  // whatnot in case it needs it.
2917  if (pContext->onDeleteElement) {
2918  pContext->onDeleteElement(pElement);
2919  }
2920 
2921 
2922  // If this was element is marked as the one that was last under the mouse it needs to be unset.
2923  bool needsMouseUpdate = false;
2924  if (pContext->pElementUnderMouse == pElement)
2925  {
2926  pContext->pElementUnderMouse = NULL;
2927  needsMouseUpdate = true;
2928  }
2929 
2930  if (pContext->pLastMouseMoveTopLevelElement == pElement)
2931  {
2932  pContext->pLastMouseMoveTopLevelElement = NULL;
2933  pContext->lastMouseMovePosX = 0;
2934  pContext->lastMouseMovePosY = 0;
2935  needsMouseUpdate = false; // It was a top-level element so the mouse enter/leave state doesn't need an update.
2936  }
2937 
2938 
2939  // If this element has the mouse capture it needs to be released.
2940  if (pContext->pElementWithMouseCapture == pElement)
2941  {
2942  drgui_log(pContext, "WARNING: Deleting an element while it still has the mouse capture.");
2943  drgui_release_mouse(pContext);
2944  }
2945 
2946  // If this element has the keyboard capture it needs to be released.
2947  if (pContext->pElementWithKeyboardCapture == pElement)
2948  {
2949  drgui_log(pContext, "WARNING: Deleting an element while it still has the keyboard capture.");
2950  drgui_release_keyboard(pContext);
2951  }
2952 
2953  // Is this element in the middle of being marked as dirty?
2954  for (size_t iDirtyElement = 0; iDirtyElement < pContext->dirtyElementCount; ++iDirtyElement) {
2955  if (pContext->ppDirtyElements[iDirtyElement] == pElement) {
2956  drgui_log(pContext, "WARNING: Deleting an element while it is being marked as dirty.");
2957  for (size_t iDirtyElement2 = iDirtyElement; iDirtyElement2+1 < pContext->dirtyElementCount; ++iDirtyElement2) {
2958  pContext->ppDirtyElements[iDirtyElement2] = pContext->ppDirtyElements[iDirtyElement2+1];
2959  }
2960 
2961  pContext->dirtyElementCount -= 1;
2962  break;
2963  }
2964  }
2965 
2966 #if 0
2967  if (pContext->pDirtyTopLevelElement == pElement)
2968  {
2969  drgui_log(pContext, "WARNING: Deleting an element while it is being marked as dirty.");
2970  pContext->pDirtyTopLevelElement = NULL;
2971  }
2972 #endif
2973 
2974 
2975 
2976  // Deleting this element may have resulted in the mouse entering a new element. Here is where we do a mouse enter/leave update.
2977  if (needsMouseUpdate)
2978  {
2979  pElement->onHitTest = drgui_pass_through_hit_test; // <-- This ensures we don't include this element when searching for the new element under the mouse.
2980  drgui_update_mouse_enter_and_leave_state(pContext, drgui_find_element_under_point(pContext->pLastMouseMoveTopLevelElement, pContext->lastMouseMovePosX, pContext->lastMouseMovePosY));
2981  }
2982 
2983 
2984  // Here is where we need to detach the element from the hierarchy. When doing this we want to ensure the element is not redrawn when
2985  // it's children are detached. To do this we simply detach the event handler.
2986  pElement->onPaint = NULL;
2987 
2988  // The parent needs to be redraw after detaching.
2989  drgui_element* pParent = pElement->pParent;
2990  drgui_rect relativeRect = drgui_get_relative_rect(pElement);
2991 
2992 
2993  // Orphan the element first.
2994  drgui_detach_without_redraw(pElement);
2995 
2996  // Children need to be deleted before deleting the element itself.
2997  while (pElement->pLastChild != NULL) {
2998  drgui_delete_element(pElement->pLastChild);
2999  }
3000 
3001 
3002  // The parent needs to be redrawn.
3003  if (pParent) {
3004  drgui_dirty(pParent, relativeRect);
3005  }
3006 
3007 
3008  // Finally, we to decided whether or not the element should be deleted for real straight away or not. If the element is being
3009  // deleted within an event handler it should be delayed because the event handlers may try referencing it afterwards.
3010  if (!drgui_is_handling_inbound_event(pContext)) {
3011  drgui_delete_element_for_real(pElement);
3012  }
3013 }
3014 
3015 
3016 size_t drgui_get_extra_data_size(drgui_element* pElement)
3017 {
3018  if (pElement != NULL) {
3019  return pElement->extraDataSize;
3020  }
3021 
3022  return 0;
3023 }
3024 
3025 void* drgui_get_extra_data(drgui_element* pElement)
3026 {
3027  if (pElement != NULL) {
3028  return pElement->pExtraData;
3029  }
3030 
3031  return NULL;
3032 }
3033 
3034 
3035 bool drgui_set_type(drgui_element* pElement, const char* type)
3036 {
3037  if (pElement == NULL) {
3038  return false;
3039  }
3040 
3041  return drgui__strcpy_s(pElement->type, sizeof(pElement->type), (type == NULL) ? "" : type) == 0;
3042 }
3043 
3044 const char* drgui_get_type(drgui_element* pElement)
3045 {
3046  if (pElement == NULL) {
3047  return NULL;
3048  }
3049 
3050  return pElement->type;
3051 }
3052 
3053 bool drgui_is_of_type(drgui_element* pElement, const char* type)
3054 {
3055  if (pElement == NULL || type == NULL) {
3056  return false;
3057  }
3058 
3059  return strncmp(pElement->type, type, strlen(type)) == 0;
3060 }
3061 
3062 
3063 void drgui_hide(drgui_element* pElement)
3064 {
3065  if (pElement != NULL) {
3066  pElement->flags |= IS_ELEMENT_HIDDEN;
3067  drgui_auto_dirty(pElement, drgui_get_local_rect(pElement));
3068  }
3069 }
3070 
3071 void drgui_show(drgui_element* pElement)
3072 {
3073  if (pElement != NULL) {
3074  pElement->flags &= ~IS_ELEMENT_HIDDEN;
3075  drgui_auto_dirty(pElement, drgui_get_local_rect(pElement));
3076  }
3077 }
3078 
3079 bool drgui_is_visible(const drgui_element* pElement)
3080 {
3081  if (pElement != NULL) {
3082  return (pElement->flags & IS_ELEMENT_HIDDEN) == 0;
3083  }
3084 
3085  return false;
3086 }
3087 
3088 bool drgui_is_visible_recursive(const drgui_element* pElement)
3089 {
3090  if (drgui_is_visible(pElement))
3091  {
3092  assert(pElement->pParent != NULL);
3093 
3094  if (pElement->pParent != NULL) {
3095  return drgui_is_visible(pElement->pParent);
3096  }
3097  }
3098 
3099  return false;
3100 }
3101 
3102 
3103 void drgui_disable_clipping(drgui_element* pElement)
3104 {
3105  if (pElement != NULL) {
3106  pElement->flags |= IS_ELEMENT_CLIPPING_DISABLED;
3107  }
3108 }
3109 
3110 void drgui_enable_clipping(drgui_element* pElement)
3111 {
3112  if (pElement != NULL) {
3113  pElement->flags &= ~IS_ELEMENT_CLIPPING_DISABLED;
3114  }
3115 }
3116 
3117 bool drgui_is_clipping_enabled(const drgui_element* pElement)
3118 {
3119  if (pElement != NULL) {
3120  return (pElement->flags & IS_ELEMENT_CLIPPING_DISABLED) == 0;
3121  }
3122 
3123  return true;
3124 }
3125 
3126 
3127 
3128 void drgui_capture_mouse(drgui_element* pElement)
3129 {
3130  if (pElement == NULL) {
3131  return;
3132  }
3133 
3134  if (pElement->pContext == NULL) {
3135  return;
3136  }
3137 
3138 
3139  if (pElement->pContext->pElementWithMouseCapture != pElement)
3140  {
3141  // Release the previous capture first.
3142  if (pElement->pContext->pElementWithMouseCapture != NULL) {
3143  drgui_release_mouse(pElement->pContext);
3144  }
3145 
3146  assert(pElement->pContext->pElementWithMouseCapture == NULL);
3147 
3148  pElement->pContext->pElementWithMouseCapture = pElement;
3149 
3150  // Two events need to be posted - the global on_capture_mouse event and the local on_capture_mouse event.
3151  drgui_post_outbound_event_capture_mouse(pElement);
3152 
3153  if (pElement == pElement->pContext->pElementWithMouseCapture) { // <-- Only post the global event handler if the element still has the capture.
3154  drgui_post_outbound_event_capture_mouse_global(pElement);
3155  }
3156  }
3157 }
3158 
3159 void drgui_release_mouse(drgui_context* pContext)
3160 {
3161  if (pContext == NULL) {
3162  return;
3163  }
3164 
3165 
3166  // Events need to be posted before setting the internal pointer.
3167  if (!drgui_is_element_marked_as_dead(pContext->pElementWithMouseCapture)) { // <-- There's a chace the element is releaseing the keyboard due to being deleted. Don't want to post an event in this case.
3168  drgui_post_outbound_event_release_mouse(pContext->pElementWithMouseCapture);
3169  drgui_post_outbound_event_release_mouse_global(pContext->pElementWithMouseCapture);
3170  }
3171 
3172  // We want to set the internal pointer to NULL after posting the events since that is when it has truly released the mouse.
3173  pContext->pElementWithMouseCapture = NULL;
3174 
3175 
3176  // After releasing the mouse the cursor may be sitting on top of a different element - we want to recheck that.
3177  drgui_update_mouse_enter_and_leave_state(pContext, drgui_find_element_under_point(pContext->pLastMouseMoveTopLevelElement, pContext->lastMouseMovePosX, pContext->lastMouseMovePosY));
3178 }
3179 
3181 {
3182  if (pContext == NULL) {
3183  return;
3184  }
3185 
3187  pContext->onGlobalReleaseMouse = NULL;
3188  drgui_release_mouse(pContext);
3189  pContext->onGlobalReleaseMouse = prevProc;
3190 }
3191 
3193 {
3194  if (pContext == NULL) {
3195  return NULL;
3196  }
3197 
3198  return pContext->pElementWithMouseCapture;
3199 }
3200 
3201 bool drgui_has_mouse_capture(drgui_element* pElement)
3202 {
3203  if (pElement == NULL) {
3204  return false;
3205  }
3206 
3207  return drgui_get_element_with_mouse_capture(pElement->pContext) == pElement;
3208 }
3209 
3210 
3211 DRGUI_PRIVATE void drgui_release_keyboard_private(drgui_context* pContext, drgui_element* pNewCapturedElement)
3212 {
3213  assert(pContext != NULL);
3214 
3215  // It is reasonable to expect that an application will want to change keyboard focus from within the release_keyboard
3216  // event handler. The problem with this is that is can cause a infinite dependency chain. We need to handle that case
3217  // by setting a flag that keeps track of whether or not we are in the middle of a release_keyboard event. At the end
3218  // we look at the element that want's the keyboard focuse and explicitly capture it at the end.
3219 
3220  pContext->flags |= IS_RELEASING_KEYBOARD;
3221  {
3222  drgui_element* pPrevCapturedElement = pContext->pElementWithKeyboardCapture;
3223  pContext->pElementWithKeyboardCapture = NULL;
3224 
3225  if (!drgui_is_element_marked_as_dead(pPrevCapturedElement)) { // <-- There's a chace the element is releaseing the keyboard due to being deleted. Don't want to post an event in this case.
3226  drgui_post_outbound_event_release_keyboard(pPrevCapturedElement, pNewCapturedElement);
3227  drgui_post_outbound_event_release_keyboard_global(pPrevCapturedElement, pNewCapturedElement);
3228  }
3229  }
3230  pContext->flags &= ~IS_RELEASING_KEYBOARD;
3231 
3232  // Explicitly capture the keyboard.
3235 }
3236 
3237 void drgui_capture_keyboard(drgui_element* pElement)
3238 {
3239  if (pElement == NULL) {
3240  return;
3241  }
3242 
3243  if (pElement->pContext == NULL) {
3244  return;
3245  }
3246 
3247 
3248  if ((pElement->pContext->flags & IS_RELEASING_KEYBOARD) != 0) {
3249  pElement->pContext->pElementWantingKeyboardCapture = pElement;
3250  return;
3251  }
3252 
3253 
3254  if (pElement->pContext->pElementWithKeyboardCapture != pElement)
3255  {
3256  // Release the previous capture first.
3257  drgui_element* pPrevElementWithKeyboardCapture = pElement->pContext->pElementWithKeyboardCapture;
3258  if (pPrevElementWithKeyboardCapture != NULL) {
3259  drgui_release_keyboard_private(pElement->pContext, pElement);
3260  }
3261 
3262  assert(pElement->pContext->pElementWithKeyboardCapture == NULL);
3263 
3264  pElement->pContext->pElementWithKeyboardCapture = pElement;
3266 
3267  // Two events need to be posted - the global on_capture event and the local on_capture event. The problem, however, is that the
3268  // local event handler may change the keyboard capture internally, such as if it wants to pass it's focus onto an internal child
3269  // element or whatnot. In this case we don't want to fire the global event handler because it will result in superfluous event
3270  // posting, and could also be posted with an incorrect element.
3271  drgui_post_outbound_event_capture_keyboard(pElement, pPrevElementWithKeyboardCapture);
3272 
3273  if (pElement == pElement->pContext->pElementWithKeyboardCapture) { // <-- Only post the global event handler if the element still has the capture.
3274  drgui_post_outbound_event_capture_keyboard_global(pElement, pPrevElementWithKeyboardCapture);
3275  }
3276  }
3277 }
3278 
3279 void drgui_release_keyboard(drgui_context* pContext)
3280 {
3281  if (pContext == NULL) {
3282  return;
3283  }
3284 
3285  drgui_release_keyboard_private(pContext, NULL);
3286 }
3287 
3289 {
3290  if (pContext == NULL) {
3291  return;
3292  }
3293 
3295  pContext->onGlobalReleaseKeyboard = NULL;
3296  drgui_release_keyboard(pContext);
3297  pContext->onGlobalReleaseKeyboard = prevProc;
3298 }
3299 
3301 {
3302  if (pContext == NULL) {
3303  return NULL;
3304  }
3305 
3306  return pContext->pElementWithKeyboardCapture;
3307 }
3308 
3310 {
3311  if (pElement == NULL) {
3312  return false;
3313  }
3314 
3315  return drgui_get_element_with_keyboard_capture(pElement->pContext) == pElement;
3316 }
3317 
3318 
3319 void drgui_set_cursor(drgui_element* pElement, drgui_cursor_type cursor)
3320 {
3321  if (pElement == NULL) {
3322  return;
3323  }
3324 
3325  pElement->cursor = cursor;
3326 
3327  if (drgui_is_element_under_mouse(pElement) && pElement->pContext->currentCursor != cursor) {
3328  drgui__change_cursor(pElement, cursor);
3329  }
3330 }
3331 
3333 {
3334  if (pElement == NULL) {
3335  return drgui_cursor_none;
3336  }
3337 
3338  return pElement->cursor;
3339 }
3340 
3341 
3342 
3344 
3345 void drgui_set_on_move(drgui_element * pElement, drgui_on_move_proc callback)
3346 {
3347  if (pElement != NULL) {
3348  pElement->onMove = callback;
3349  }
3350 }
3351 
3352 void drgui_set_on_size(drgui_element * pElement, drgui_on_size_proc callback)
3353 {
3354  if (pElement != NULL) {
3355  pElement->onSize = callback;
3356  }
3357 }
3358 
3360 {
3361  if (pElement != NULL) {
3362  pElement->onMouseEnter = callback;
3363  }
3364 }
3365 
3367 {
3368  if (pElement != NULL) {
3369  pElement->onMouseLeave = callback;
3370  }
3371 }
3372 
3374 {
3375  if (pElement != NULL) {
3376  pElement->onMouseMove = callback;
3377  }
3378 }
3379 
3381 {
3382  if (pElement != NULL) {
3383  pElement->onMouseButtonDown = callback;
3384  }
3385 }
3386 
3388 {
3389  if (pElement != NULL) {
3390  pElement->onMouseButtonUp = callback;
3391  }
3392 }
3393 
3395 {
3396  if (pElement != NULL) {
3397  pElement->onMouseButtonDblClick = callback;
3398  }
3399 }
3400 
3402 {
3403  if (pElement != NULL) {
3404  pElement->onMouseWheel = callback;
3405  }
3406 }
3407 
3409 {
3410  if (pElement != NULL) {
3411  pElement->onKeyDown = callback;
3412  }
3413 }
3414 
3415 void drgui_set_on_key_up(drgui_element* pElement, drgui_on_key_up_proc callback)
3416 {
3417  if (pElement != NULL) {
3418  pElement->onKeyUp = callback;
3419  }
3420 }
3421 
3423 {
3424  if (pElement != NULL) {
3425  pElement->onPrintableKeyDown = callback;
3426  }
3427 }
3428 
3429 void drgui_set_on_paint(drgui_element* pElement, drgui_on_paint_proc callback)
3430 {
3431  if (pElement != NULL) {
3432  pElement->onPaint = callback;
3433  }
3434 }
3435 
3436 void drgui_set_on_dirty(drgui_element * pElement, drgui_on_dirty_proc callback)
3437 {
3438  if (pElement != NULL) {
3439  pElement->onDirty = callback;
3440  }
3441 }
3442 
3444 {
3445  if (pElement != NULL) {
3446  pElement->onHitTest = callback;
3447  }
3448 }
3449 
3451 {
3452  if (pElement != NULL) {
3453  pElement->onCaptureMouse = callback;
3454  }
3455 }
3456 
3458 {
3459  if (pElement != NULL) {
3460  pElement->onReleaseMouse = callback;
3461  }
3462 }
3463 
3465 {
3466  if (pElement != NULL) {
3467  pElement->onCaptureKeyboard = callback;
3468  }
3469 }
3470 
3472 {
3473  if (pElement != NULL) {
3474  pElement->onReleaseKeyboard = callback;
3475  }
3476 }
3477 
3478 
3479 
3480 bool drgui_is_point_inside_element_bounds(const drgui_element* pElement, float absolutePosX, float absolutePosY)
3481 {
3482  if (absolutePosX < pElement->absolutePosX ||
3483  absolutePosX < pElement->absolutePosY)
3484  {
3485  return false;
3486  }
3487 
3488  if (absolutePosX >= pElement->absolutePosX + pElement->width ||
3489  absolutePosY >= pElement->absolutePosY + pElement->height)
3490  {
3491  return false;
3492  }
3493 
3494  return true;
3495 }
3496 
3497 bool drgui_is_point_inside_element(drgui_element* pElement, float absolutePosX, float absolutePosY)
3498 {
3499  if (drgui_is_point_inside_element_bounds(pElement, absolutePosX, absolutePosY))
3500  {
3501  // It is valid for onHitTest to be null, in which case we use the default hit test which assumes the element is just a rectangle
3502  // equal to the size of it's bounds. It's equivalent to onHitTest always returning true.
3503 
3504  if (pElement->onHitTest) {
3505  return pElement->onHitTest(pElement, absolutePosX - pElement->absolutePosX, absolutePosY - pElement->absolutePosY);
3506  }
3507 
3508  return true;
3509  }
3510 
3511  return false;
3512 }
3513 
3514 
3515 
3516 typedef struct
3517 {
3518  drgui_element* pElementUnderPoint;
3519  float absolutePosX;
3520  float absolutePosY;
3521 }drgui_find_element_under_point_data;
3522 
3523 bool drgui_find_element_under_point_iterator(drgui_element* pElement, drgui_rect* pRelativeVisibleRect, void* pUserData)
3524 {
3525  assert(pElement != NULL);
3526  assert(pRelativeVisibleRect != NULL);
3527 
3528  drgui_find_element_under_point_data* pData = (drgui_find_element_under_point_data*)pUserData;
3529  assert(pData != NULL);
3530 
3531  float relativePosX = pData->absolutePosX;
3532  float relativePosY = pData->absolutePosY;
3533  drgui_make_point_relative(pElement, &relativePosX, &relativePosY);
3534 
3535  if (drgui_rect_contains_point(*pRelativeVisibleRect, relativePosX, relativePosY))
3536  {
3537  if (pElement->onHitTest) {
3538  if (pElement->onHitTest(pElement, relativePosX, relativePosY)) {
3539  pData->pElementUnderPoint = pElement;
3540  }
3541  } else {
3542  pData->pElementUnderPoint = pElement;
3543  }
3544  }
3545 
3546 
3547  // Always return true to ensure the entire hierarchy is checked.
3548  return true;
3549 }
3550 
3551 drgui_element* drgui_find_element_under_point(drgui_element* pTopLevelElement, float absolutePosX, float absolutePosY)
3552 {
3553  if (pTopLevelElement == NULL) {
3554  return NULL;
3555  }
3556 
3557  drgui_find_element_under_point_data data;
3558  data.pElementUnderPoint = NULL;
3559  data.absolutePosX = absolutePosX;
3560  data.absolutePosY = absolutePosY;
3561  drgui_iterate_visible_elements(pTopLevelElement, drgui_get_absolute_rect(pTopLevelElement), drgui_find_element_under_point_iterator, &data);
3562 
3563  return data.pElementUnderPoint;
3564 }
3565 
3567 {
3568  if (pElement == NULL) {
3569  return false;
3570  }
3571 
3573 }
3574 
3575 
3576 
3578 
3580 {
3581  if (pChildElement == NULL) {
3582  return NULL;
3583  }
3584 
3585  return pChildElement->pParent;
3586 }
3587 
3588 void drgui_detach(drgui_element* pChildElement)
3589 {
3590  if (pChildElement == NULL) {
3591  return;
3592  }
3593 
3594  drgui_element* pOldParent = pChildElement->pParent;
3595 
3596 
3597  // We orphan the element using the private API. This will not mark the parent element as dirty so we need to do that afterwards.
3598  drgui_detach_without_redraw(pChildElement);
3599 
3600  // The region of the old parent needs to be redrawn.
3601  if (pOldParent != NULL) {
3602  drgui_auto_dirty(pOldParent, drgui_get_relative_rect(pOldParent));
3603  }
3604 }
3605 
3606 void drgui_append(drgui_element* pChildElement, drgui_element* pParentElement)
3607 {
3608  if (pChildElement == NULL) {
3609  return;
3610  }
3611 
3612  // We first need to orphan the element. If the parent element is the new parent is the same as the old one, as in we
3613  // are just moving the child element to the end of the children list, we want to delay the repaint until the end. To
3614  // do this we use drgui_detach_without_redraw() because that will not trigger a redraw.
3615  if (pChildElement->pParent != pParentElement) {
3616  drgui_detach(pChildElement);
3617  } else {
3618  drgui_detach_without_redraw(pChildElement);
3619  }
3620 
3621 
3622  // Now we attach it to the end of the new parent.
3623  if (pParentElement != NULL) {
3624  drgui_append_without_detach(pChildElement, pParentElement);
3625  }
3626 }
3627 
3628 void drgui_prepend(drgui_element* pChildElement, drgui_element* pParentElement)
3629 {
3630  if (pChildElement == NULL) {
3631  return;
3632  }
3633 
3634  // See comment in drgui_append() for explanation on this.
3635  if (pChildElement->pParent != pParentElement) {
3636  drgui_detach(pChildElement);
3637  } else {
3638  drgui_detach_without_redraw(pChildElement);
3639  }
3640 
3641 
3642  // Now we need to attach the element to the beginning of the parent.
3643  if (pParentElement != NULL) {
3644  drgui_prepend_without_detach(pChildElement, pParentElement);
3645  }
3646 }
3647 
3648 void drgui_append_sibling(drgui_element* pElementToAppend, drgui_element* pElementToAppendTo)
3649 {
3650  if (pElementToAppend == NULL || pElementToAppendTo == NULL) {
3651  return;
3652  }
3653 
3654  // See comment in drgui_append() for explanation on this.
3655  if (pElementToAppend->pParent != pElementToAppendTo->pParent) {
3656  drgui_detach(pElementToAppend);
3657  } else {
3658  drgui_detach_without_redraw(pElementToAppend);
3659  }
3660 
3661 
3662  // Now we need to attach the element such that it comes just after pElementToAppendTo
3663  drgui_append_sibling_without_detach(pElementToAppend, pElementToAppendTo);
3664 }
3665 
3666 void drgui_prepend_sibling(drgui_element* pElementToPrepend, drgui_element* pElementToPrependTo)
3667 {
3668  if (pElementToPrepend == NULL || pElementToPrependTo == NULL) {
3669  return;
3670  }
3671 
3672  // See comment in drgui_append() for explanation on this.
3673  if (pElementToPrepend->pParent != pElementToPrependTo->pParent) {
3674  drgui_detach(pElementToPrepend);
3675  } else {
3676  drgui_detach_without_redraw(pElementToPrepend);
3677  }
3678 
3679 
3680  // Now we need to attach the element such that it comes just after pElementToPrependTo
3681  drgui_prepend_sibling_without_detach(pElementToPrepend, pElementToPrependTo);
3682 }
3683 
3685 {
3686  if (pElement == NULL) {
3687  return NULL;
3688  }
3689 
3690  if (pElement->pParent != NULL) {
3691  return drgui_find_top_level_element(pElement->pParent);
3692  }
3693 
3694  return pElement;
3695 }
3696 
3697 bool drgui_is_parent(drgui_element* pParentElement, drgui_element* pChildElement)
3698 {
3699  if (pParentElement == NULL || pChildElement == NULL) {
3700  return false;
3701  }
3702 
3703  return pParentElement == pChildElement->pParent;
3704 }
3705 
3706 bool drgui_is_child(drgui_element* pChildElement, drgui_element* pParentElement)
3707 {
3708  return drgui_is_parent(pParentElement, pChildElement);
3709 }
3710 
3711 bool drgui_is_ancestor(drgui_element* pAncestorElement, drgui_element* pChildElement)
3712 {
3713  if (pAncestorElement == NULL || pChildElement == NULL) {
3714  return false;
3715  }
3716 
3717  drgui_element* pParent = pChildElement->pParent;
3718  while (pParent != NULL)
3719  {
3720  if (pParent == pAncestorElement) {
3721  return true;
3722  }
3723 
3724  pParent = pParent->pParent;
3725  }
3726 
3727 
3728  return false;
3729 }
3730 
3731 bool drgui_is_descendant(drgui_element* pChildElement, drgui_element* pAncestorElement)
3732 {
3733  return drgui_is_ancestor(pAncestorElement, pChildElement);
3734 }
3735 
3736 bool drgui_is_self_or_ancestor(drgui_element* pAncestorElement, drgui_element* pChildElement)
3737 {
3738  return pAncestorElement == pChildElement || drgui_is_ancestor(pAncestorElement, pChildElement);
3739 }
3740 
3741 bool drgui_is_self_or_descendant(drgui_element* pChildElement, drgui_element* pAncestorElement)
3742 {
3743  return pChildElement == pAncestorElement || drgui_is_descendant(pChildElement, pAncestorElement);
3744 }
3745 
3746 
3747 
3749 
3750 void drgui_set_absolute_position(drgui_element* pElement, float positionX, float positionY)
3751 {
3752  if (pElement != NULL)
3753  {
3754  if (pElement->absolutePosX != positionX || pElement->absolutePosY != positionY)
3755  {
3756  float oldRelativePosX = drgui_get_relative_position_x(pElement);
3757  float oldRelativePosY = drgui_get_relative_position_y(pElement);
3758 
3759  drgui_begin_auto_dirty(pElement);
3760  {
3761  drgui_auto_dirty(pElement, drgui_get_local_rect(pElement)); // <-- Previous rectangle.
3762 
3763  float offsetX = positionX - pElement->absolutePosX;
3764  float offsetY = positionY - pElement->absolutePosY;
3765 
3766  pElement->absolutePosX = positionX;
3767  pElement->absolutePosY = positionY;
3768  drgui_auto_dirty(pElement, drgui_get_local_rect(pElement)); // <-- New rectangle.
3769 
3770 
3771  float newRelativePosX = drgui_get_relative_position_x(pElement);
3772  float newRelativePosY = drgui_get_relative_position_y(pElement);
3773 
3774  if (newRelativePosX != oldRelativePosX || newRelativePosY != oldRelativePosY) {
3775  drgui_post_outbound_event_move(pElement, newRelativePosX, newRelativePosY);
3776  }
3777 
3778 
3779  drgui_apply_offset_to_children_recursive(pElement, offsetX, offsetY);
3780  }
3781  drgui_end_auto_dirty(pElement);
3782  }
3783  }
3784 }
3785 
3786 void drgui_get_absolute_position(const drgui_element* pElement, float * positionXOut, float * positionYOut)
3787 {
3788  if (pElement != NULL)
3789  {
3790  if (positionXOut != NULL) {
3791  *positionXOut = pElement->absolutePosX;
3792  }
3793 
3794  if (positionYOut != NULL) {
3795  *positionYOut = pElement->absolutePosY;
3796  }
3797  }
3798 }
3799 
3800 float drgui_get_absolute_position_x(const drgui_element* pElement)
3801 {
3802  if (pElement != NULL) {
3803  return pElement->absolutePosX;
3804  }
3805 
3806  return 0.0f;
3807 }
3808 
3809 float drgui_get_absolute_position_y(const drgui_element* pElement)
3810 {
3811  if (pElement != NULL) {
3812  return pElement->absolutePosY;
3813  }
3814 
3815  return 0.0f;
3816 }
3817 
3818 
3819 void drgui_set_relative_position(drgui_element* pElement, float relativePosX, float relativePosY)
3820 {
3821  if (pElement != NULL) {
3822  if (pElement->pParent != NULL) {
3823  drgui_set_absolute_position(pElement, pElement->pParent->absolutePosX + relativePosX, pElement->pParent->absolutePosY + relativePosY);
3824  } else {
3825  drgui_set_absolute_position(pElement, relativePosX, relativePosY);
3826  }
3827  }
3828 }
3829 
3830 void drgui_get_relative_position(const drgui_element* pElement, float* positionXOut, float* positionYOut)
3831 {
3832  if (pElement != NULL)
3833  {
3834  if (pElement->pParent != NULL)
3835  {
3836  if (positionXOut != NULL) {
3837  *positionXOut = pElement->absolutePosX - pElement->pParent->absolutePosX;
3838  }
3839 
3840  if (positionYOut != NULL) {
3841  *positionYOut = pElement->absolutePosY - pElement->pParent->absolutePosY;
3842  }
3843  }
3844  else
3845  {
3846  if (positionXOut != NULL) {
3847  *positionXOut = pElement->absolutePosX;
3848  }
3849 
3850  if (positionYOut != NULL) {
3851  *positionYOut = pElement->absolutePosY;
3852  }
3853  }
3854  }
3855 }
3856 
3857 float drgui_get_relative_position_x(const drgui_element* pElement)
3858 {
3859  if (pElement != NULL) {
3860  if (pElement->pParent != NULL) {
3861  return pElement->absolutePosX - pElement->pParent->absolutePosX;
3862  } else {
3863  return pElement->absolutePosX;
3864  }
3865  }
3866 
3867  return 0;
3868 }
3869 
3870 float drgui_get_relative_position_y(const drgui_element* pElement)
3871 {
3872  if (pElement != NULL) {
3873  if (pElement->pParent != NULL) {
3874  return pElement->absolutePosY - pElement->pParent->absolutePosY;
3875  } else {
3876  return pElement->absolutePosY;
3877  }
3878  }
3879 
3880  return 0;
3881 }
3882 
3883 
3884 void drgui_set_size(drgui_element* pElement, float width, float height)
3885 {
3886  if (pElement != NULL)
3887  {
3888  if (pElement->width != width || pElement->height != height)
3889  {
3890  drgui_begin_auto_dirty(pElement);
3891  {
3892  drgui_auto_dirty(pElement, drgui_get_local_rect(pElement)); // <-- Previous rectangle.
3893 
3894  pElement->width = width;
3895  pElement->height = height;
3896  drgui_auto_dirty(pElement, drgui_get_local_rect(pElement)); // <-- New rectangle.
3897 
3898  drgui_post_outbound_event_size(pElement, width, height);
3899  }
3900  drgui_end_auto_dirty(pElement);
3901  }
3902  }
3903 }
3904 
3905 void drgui_get_size(const drgui_element* pElement, float* widthOut, float* heightOut)
3906 {
3907  if (pElement != NULL) {
3908  if (widthOut) *widthOut = pElement->width;
3909  if (heightOut) *heightOut = pElement->height;
3910  } else {
3911  if (widthOut) *widthOut = 0;
3912  if (heightOut) *heightOut = 0;
3913  }
3914 }
3915 
3916 float drgui_get_width(const drgui_element * pElement)
3917 {
3918  if (pElement != NULL) {
3919  return pElement->width;
3920  }
3921 
3922  return 0;
3923 }
3924 
3925 float drgui_get_height(const drgui_element * pElement)
3926 {
3927  if (pElement != NULL) {
3928  return pElement->height;
3929  }
3930 
3931  return 0;
3932 }
3933 
3934 
3936 {
3937  drgui_rect rect;
3938  if (pElement != NULL)
3939  {
3940  rect.left = pElement->absolutePosX;
3941  rect.top = pElement->absolutePosY;
3942  rect.right = rect.left + pElement->width;
3943  rect.bottom = rect.top + pElement->height;
3944  }
3945  else
3946  {
3947  rect.left = 0;
3948  rect.top = 0;
3949  rect.right = 0;
3950  rect.bottom = 0;
3951  }
3952 
3953  return rect;
3954 }
3955 
3957 {
3958  drgui_rect rect;
3959  if (pElement != NULL)
3960  {
3961  rect.left = drgui_get_relative_position_x(pElement);
3962  rect.top = drgui_get_relative_position_y(pElement);
3963  rect.right = rect.left + pElement->width;
3964  rect.bottom = rect.top + pElement->height;
3965  }
3966  else
3967  {
3968  rect.left = 0;
3969  rect.top = 0;
3970  rect.right = 0;
3971  rect.bottom = 0;
3972  }
3973 
3974  return rect;
3975 }
3976 
3978 {
3979  drgui_rect rect;
3980  rect.left = 0;
3981  rect.top = 0;
3982 
3983  if (pElement != NULL)
3984  {
3985  rect.right = pElement->width;
3986  rect.bottom = pElement->height;
3987  }
3988  else
3989  {
3990  rect.right = 0;
3991  rect.bottom = 0;
3992  }
3993 
3994  return rect;
3995 }
3996 
3997 
3998 
4000 
4001 bool drgui_register_painting_callbacks(drgui_context* pContext, void* pPaintingContext, drgui_painting_callbacks callbacks)
4002 {
4003  if (pContext == NULL) {
4004  return false;
4005  }
4006 
4007  // Fail if the painting callbacks have already been registered.
4008  if (pContext->pPaintingContext != NULL) {
4009  return false;
4010  }
4011 
4012 
4013  pContext->pPaintingContext = pPaintingContext;
4014  pContext->paintingCallbacks = callbacks;
4015 
4016  return true;
4017 }
4018 
4019 
4020 bool drgui_iterate_visible_elements(drgui_element* pParentElement, drgui_rect relativeRect, drgui_visible_iteration_proc callback, void* pUserData)
4021 {
4022  if (pParentElement == NULL) {
4023  return false;
4024  }
4025 
4026  if (callback == NULL) {
4027  return false;
4028  }
4029 
4030 
4031  if (!drgui_is_visible(pParentElement)) {
4032  return true;
4033  }
4034 
4035  drgui_rect clampedRelativeRect = relativeRect;
4036  if (drgui_clamp_rect_to_element(pParentElement, &clampedRelativeRect))
4037  {
4038  // We'll only get here if some part of the rectangle was inside the element.
4039  if (!callback(pParentElement, &clampedRelativeRect, pUserData)) {
4040  return false;
4041  }
4042  }
4043 
4044  for (drgui_element* pChild = pParentElement->pFirstChild; pChild != NULL; pChild = pChild->pNextSibling)
4045  {
4046  float childRelativePosX = drgui_get_relative_position_x(pChild);
4047  float childRelativePosY = drgui_get_relative_position_y(pChild);
4048 
4049  drgui_rect childRect;
4050  if (drgui_is_clipping_enabled(pChild)) {
4051  childRect = clampedRelativeRect;
4052  } else {
4053  childRect = relativeRect;
4054  }
4055 
4056 
4057  childRect.left -= childRelativePosX;
4058  childRect.top -= childRelativePosY;
4059  childRect.right -= childRelativePosX;
4060  childRect.bottom -= childRelativePosY;
4061 
4062  if (!drgui_iterate_visible_elements(pChild, childRect, callback, pUserData)) {
4063  return false;
4064  }
4065  }
4066 
4067 
4068  return true;
4069 }
4070 
4072 {
4073  if (pContext != NULL) {
4074  pContext->flags |= IS_AUTO_DIRTY_DISABLED;
4075  }
4076 }
4077 
4078 void drgui_enable_auto_dirty(drgui_context* pContext)
4079 {
4080  if (pContext != NULL) {
4081  pContext->flags &= ~IS_AUTO_DIRTY_DISABLED;
4082  }
4083 }
4084 
4086 {
4087  if (pContext != NULL) {
4088  return (pContext->flags & IS_AUTO_DIRTY_DISABLED) == 0;
4089  }
4090 
4091  return false;
4092 }
4093 
4094 
4096 {
4097  if (pElement == NULL) {
4098  return NULL;
4099  }
4100 
4101  drgui_context* pContext = pElement->pContext;
4102  assert(pContext != NULL);
4103 
4104  drgui_element* pTopLevelElement = drgui_find_top_level_element(pElement);
4105  assert(pTopLevelElement != NULL);
4106 
4107  // The element needs to be added to the list of dirty elements if it doesn't exist already.
4108  bool isAlreadyDirty = false;
4109  for (size_t iDirtyElementCount = 0; iDirtyElementCount < pContext->dirtyElementCount; ++iDirtyElementCount) {
4110  if (pContext->ppDirtyElements[iDirtyElementCount] == pTopLevelElement) {
4111  isAlreadyDirty = true;
4112  break;
4113  }
4114  }
4115 
4116  if (!isAlreadyDirty) {
4117  if (pContext->dirtyElementCount == pContext->dirtyElementBufferSize) {
4118  size_t newBufferSize = pContext->dirtyElementBufferSize == 0 ? 1 : pContext->dirtyElementBufferSize*2;
4119  drgui_element** ppNewDirtyElements = (drgui_element**)realloc(pContext->ppDirtyElements, newBufferSize * sizeof(*ppNewDirtyElements));
4120  if (ppNewDirtyElements == NULL) {
4121  return NULL;
4122  }
4123 
4124  pContext->ppDirtyElements = ppNewDirtyElements;
4125  pContext->dirtyElementBufferSize = newBufferSize;
4126  }
4127 
4128  pContext->ppDirtyElements[pContext->dirtyCounter] = pTopLevelElement;
4129  pContext->dirtyElementCount += 1;
4130  }
4131 
4132 
4133  pContext->dirtyCounter += 1;
4134  return pTopLevelElement;
4135 }
4136 
4137 void drgui_end_dirty(drgui_element* pElement)
4138 {
4139  if (pElement == NULL) {
4140  return;
4141  }
4142 
4143  drgui_context* pContext = pElement->pContext;
4144  assert(pContext != NULL);
4145 
4146  assert(pContext->dirtyElementCount > 0);
4147  assert(pContext->dirtyCounter > 0);
4148 
4149  pContext->dirtyCounter -= 1;
4150  if (pContext->dirtyCounter == 0)
4151  {
4152  for (size_t i = 0; i < pContext->dirtyElementCount; ++i) {
4153  drgui_post_outbound_event_dirty_global(pContext->ppDirtyElements[i], pContext->ppDirtyElements[i]->dirtyRect);
4155  }
4156 
4157  pContext->dirtyElementCount = 0;
4158  }
4159 }
4160 
4161 void drgui_dirty(drgui_element* pElement, drgui_rect relativeRect)
4162 {
4163  if (pElement == NULL) {
4164  return;
4165  }
4166 
4167  //drgui_context* pContext = pElement->pContext;
4168  //assert(pContext != NULL);
4169 
4170  drgui_element* pTopLevelElement = drgui_begin_dirty(pElement);
4171  if (pTopLevelElement == NULL) {
4172  return;
4173  }
4174 
4175  pTopLevelElement->dirtyRect = drgui_rect_union(pTopLevelElement->dirtyRect, drgui_make_rect_absolute(pElement, &relativeRect));
4176  drgui_end_dirty(pElement);
4177 }
4178 
4179 
4180 bool drgui_draw_iteration_callback(drgui_element* pElement, drgui_rect* pRelativeRect, void* pUserData)
4181 {
4182  assert(pElement != NULL);
4183  assert(pRelativeRect != NULL);
4184 
4185  if (pElement->onPaint != NULL)
4186  {
4187  // We want to set the initial clipping rectangle before drawing.
4188  drgui_set_clip(pElement, *pRelativeRect, pUserData);
4189 
4190  // We now call the painting function, but only after setting the clipping rectangle.
4191  pElement->onPaint(pElement, *pRelativeRect, pUserData);
4192 
4193  // The on_paint event handler may have adjusted the clipping rectangle so we need to ensure it's restored.
4194  drgui_set_clip(pElement, *pRelativeRect, pUserData);
4195  }
4196 
4197  return true;
4198 }
4199 
4200 void drgui_draw(drgui_element* pElement, drgui_rect relativeRect, void* pPaintData)
4201 {
4202  if (pElement == NULL) {
4203  return;
4204  }
4205 
4206  drgui_context* pContext = pElement->pContext;
4207  if (pContext == NULL) {
4208  return;
4209  }
4210 
4211  assert(pContext->paintingCallbacks.drawBegin != NULL);
4212  assert(pContext->paintingCallbacks.drawEnd != NULL);
4213 
4214  pContext->paintingCallbacks.drawBegin(pPaintData);
4215  {
4216  drgui_iterate_visible_elements(pElement, relativeRect, drgui_draw_iteration_callback, pPaintData);
4217  }
4218  pContext->paintingCallbacks.drawEnd(pPaintData);
4219 }
4220 
4221 void drgui_get_clip(drgui_element* pElement, drgui_rect* pRelativeRect, void* pPaintData)
4222 {
4223  if (pElement == NULL || pElement->pContext == NULL) {
4224  return;
4225  }
4226 
4227  pElement->pContext->paintingCallbacks.getClip(pRelativeRect, pPaintData);
4228 
4229  // The clip returned by the drawing callback will be absolute so we'll need to convert that to relative.
4230  drgui_make_rect_relative(pElement, pRelativeRect);
4231 }
4232 
4233 void drgui_set_clip(drgui_element* pElement, drgui_rect relativeRect, void* pPaintData)
4234 {
4235  if (pElement == NULL || pElement->pContext == NULL) {
4236  return;
4237  }
4238 
4239 
4240  // Make sure the rectangle is not negative.
4241  if (relativeRect.right < relativeRect.left) {
4242  relativeRect.right = relativeRect.left;
4243  }
4244 
4245  if (relativeRect.bottom < relativeRect.top) {
4246  relativeRect.bottom = relativeRect.top;
4247  }
4248 
4249  drgui_rect absoluteRect = relativeRect;
4250  drgui_make_rect_absolute(pElement, &absoluteRect);
4251 
4252  pElement->pContext->paintingCallbacks.setClip(absoluteRect, pPaintData);
4253 }
4254 
4255 void drgui_draw_rect(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, void* pPaintData)
4256 {
4257  if (pElement == NULL) {
4258  return;
4259  }
4260 
4261  assert(pElement->pContext != NULL);
4262 
4263  drgui_rect absoluteRect = relativeRect;
4264  drgui_make_rect_absolute(pElement, &absoluteRect);
4265 
4266  pElement->pContext->paintingCallbacks.drawRect(absoluteRect, color, pPaintData);
4267 }
4268 
4269 void drgui_draw_rect_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float outlineWidth, void* pPaintData)
4270 {
4271  if (pElement == NULL) {
4272  return;
4273  }
4274 
4275  assert(pElement->pContext != NULL);
4276 
4277  drgui_rect absoluteRect = relativeRect;
4278  drgui_make_rect_absolute(pElement, &absoluteRect);
4279 
4280  pElement->pContext->paintingCallbacks.drawRectOutline(absoluteRect, color, outlineWidth, pPaintData);
4281 }
4282 
4283 void drgui_draw_rect_with_outline(drgui_element * pElement, drgui_rect relativeRect, drgui_color color, float outlineWidth, drgui_color outlineColor, void * pPaintData)
4284 {
4285  if (pElement == NULL) {
4286  return;
4287  }
4288 
4289  assert(pElement->pContext != NULL);
4290 
4291  drgui_rect absoluteRect = relativeRect;
4292  drgui_make_rect_absolute(pElement, &absoluteRect);
4293 
4294  pElement->pContext->paintingCallbacks.drawRectWithOutline(absoluteRect, color, outlineWidth, outlineColor, pPaintData);
4295 }
4296 
4297 void drgui_draw_round_rect(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float radius, void* pPaintData)
4298 {
4299  if (pElement == NULL) {
4300  return;
4301  }
4302 
4303  assert(pElement->pContext != NULL);
4304 
4305  drgui_rect absoluteRect = relativeRect;
4306  drgui_make_rect_absolute(pElement, &absoluteRect);
4307 
4308  pElement->pContext->paintingCallbacks.drawRoundRect(absoluteRect, color, radius, pPaintData);
4309 }
4310 
4311 void drgui_draw_round_rect_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, void* pPaintData)
4312 {
4313  if (pElement == NULL) {
4314  return;
4315  }
4316 
4317  assert(pElement->pContext != NULL);
4318 
4319  drgui_rect absoluteRect = relativeRect;
4320  drgui_make_rect_absolute(pElement, &absoluteRect);
4321 
4322  pElement->pContext->paintingCallbacks.drawRoundRectOutline(absoluteRect, color, radius, outlineWidth, pPaintData);
4323 }
4324 
4325 void drgui_draw_round_rect_with_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, drgui_color outlineColor, void* pPaintData)
4326 {
4327  if (pElement == NULL) {
4328  return;
4329  }
4330 
4331  assert(pElement->pContext != NULL);
4332 
4333  drgui_rect absoluteRect = relativeRect;
4334  drgui_make_rect_absolute(pElement, &absoluteRect);
4335 
4336  pElement->pContext->paintingCallbacks.drawRoundRectWithOutline(absoluteRect, color, radius, outlineWidth, outlineColor, pPaintData);
4337 }
4338 
4339 void drgui_draw_text(drgui_element* pElement, drgui_font* pFont, const char* text, int textLengthInBytes, float posX, float posY, drgui_color color, drgui_color backgroundColor, void* pPaintData)
4340 {
4341  if (pElement == NULL || pFont == NULL) {
4342  return;
4343  }
4344 
4345  assert(pElement->pContext != NULL);
4346 
4347  float absolutePosX = posX;
4348  float absolutePosY = posY;
4349  drgui_make_point_absolute(pElement, &absolutePosX, &absolutePosY);
4350 
4351  pElement->pContext->paintingCallbacks.drawText(pFont->internalFont, text, textLengthInBytes, absolutePosX, absolutePosY, color, backgroundColor, pPaintData);
4352 }
4353 
4354 void drgui_draw_image(drgui_element* pElement, drgui_image* pImage, drgui_draw_image_args* pArgs, void* pPaintData)
4355 {
4356  if (pElement == NULL || pImage == NULL || pArgs == NULL) {
4357  return;
4358  }
4359 
4360  assert(pElement->pContext != NULL);
4361 
4362  drgui_make_point_absolute(pElement, &pArgs->dstX, &pArgs->dstY);
4363  drgui_make_point_absolute(pElement, &pArgs->dstBoundsX, &pArgs->dstBoundsY);
4364 
4365  if ((pArgs->options & DRGUI_IMAGE_ALIGN_CENTER) != 0)
4366  {
4367  pArgs->dstX = pArgs->dstBoundsX + (pArgs->dstBoundsWidth - pArgs->dstWidth) / 2;
4368  pArgs->dstY = pArgs->dstBoundsY + (pArgs->dstBoundsHeight - pArgs->dstHeight) / 2;
4369  }
4370 
4371  drgui_rect prevClip;
4372  pElement->pContext->paintingCallbacks.getClip(&prevClip, pPaintData);
4373 
4374  bool restoreClip = false;
4375  if ((pArgs->options & DRGUI_IMAGE_CLIP_BOUNDS) != 0)
4376  {
4377  // We only need to clip if part of the destination rectangle falls outside of the bounds.
4378  if (pArgs->dstX < pArgs->dstBoundsX || pArgs->dstX + pArgs->dstWidth > pArgs->dstBoundsX + pArgs->dstBoundsWidth ||
4379  pArgs->dstY < pArgs->dstBoundsY || pArgs->dstY + pArgs->dstHeight > pArgs->dstBoundsY + pArgs->dstBoundsHeight)
4380  {
4381  restoreClip = true;
4382  pElement->pContext->paintingCallbacks.setClip(drgui_make_rect(pArgs->dstBoundsX, pArgs->dstBoundsY, pArgs->dstBoundsX + pArgs->dstBoundsWidth, pArgs->dstBoundsY + pArgs->dstBoundsHeight), pPaintData);
4383  }
4384  }
4385 
4386  if ((pArgs->options & DRGUI_IMAGE_DRAW_BOUNDS) != 0)
4387  {
4388  // The bounds is the area sitting around the outside of the destination rectangle.
4389  const float boundsLeft = pArgs->dstBoundsX;
4390  const float boundsTop = pArgs->dstBoundsY;
4391  const float boundsRight = boundsLeft + pArgs->dstBoundsWidth;
4392  const float boundsBottom = boundsTop + pArgs->dstBoundsHeight;
4393 
4394  const float imageLeft = pArgs->dstX;
4395  const float imageTop = pArgs->dstY;
4396  const float imageRight = imageLeft + pArgs->dstWidth;
4397  const float imageBottom = imageTop + pArgs->dstHeight;
4398 
4399  // Left.
4400  if (boundsLeft < imageLeft) {
4401  pElement->pContext->paintingCallbacks.drawRect(drgui_make_rect(boundsLeft, boundsTop, imageLeft, boundsBottom), pArgs->boundsColor, pPaintData);
4402  }
4403 
4404  // Right.
4405  if (boundsRight > imageRight) {
4406  pElement->pContext->paintingCallbacks.drawRect(drgui_make_rect(imageRight, boundsTop, boundsRight, boundsBottom), pArgs->boundsColor, pPaintData);
4407  }
4408 
4409  // Top.
4410  if (boundsTop < imageTop) {
4411  pElement->pContext->paintingCallbacks.drawRect(drgui_make_rect(imageLeft, boundsTop, imageRight, imageTop), pArgs->boundsColor, pPaintData);
4412  }
4413 
4414  // Bottom.
4415  if (boundsBottom > imageBottom) {
4416  pElement->pContext->paintingCallbacks.drawRect(drgui_make_rect(imageLeft, imageBottom, imageRight, boundsBottom), pArgs->boundsColor, pPaintData);
4417  }
4418  }
4419 
4420  pElement->pContext->paintingCallbacks.drawImage(pImage->hResource, pArgs, pPaintData);
4421 
4422  if (restoreClip) {
4423  pElement->pContext->paintingCallbacks.setClip(prevClip, pPaintData);
4424  }
4425 }
4426 
4427 
4428 drgui_font* drgui_create_font(drgui_context* pContext, const char* family, unsigned int size, drgui_font_weight weight, drgui_font_slant slant, float rotation, unsigned int flags)
4429 {
4430  if (pContext == NULL) {
4431  return NULL;
4432  }
4433 
4434  if (pContext->paintingCallbacks.createFont == NULL) {
4435  return NULL;
4436  }
4437 
4438 
4439  drgui_resource internalFont = pContext->paintingCallbacks.createFont(pContext->pPaintingContext, family, size, weight, slant, rotation, flags);
4440  if (internalFont == NULL) {
4441  return NULL;
4442  }
4443 
4444  drgui_font* pFont = (drgui_font*)malloc(sizeof(drgui_font));
4445  if (pFont == NULL) {
4446  return NULL;
4447  }
4448 
4449  pFont->pContext = pContext;
4450  pFont->family[0] = '\0';
4451  pFont->size = size;
4452  pFont->weight = weight;
4453  pFont->slant = slant;
4454  pFont->rotation = rotation;
4455  pFont->flags = flags;
4456  pFont->internalFont = internalFont;
4457 
4458  if (family != NULL) {
4459  drgui__strcpy_s(pFont->family, sizeof(pFont->family), family);
4460  }
4461 
4462  return pFont;
4463 }
4464 
4465 void drgui_delete_font(drgui_font* pFont)
4466 {
4467  if (pFont == NULL) {
4468  return;
4469  }
4470 
4471  assert(pFont->pContext != NULL);
4472 
4473  // Delete the internal font objects first.
4474  if (pFont->pContext->paintingCallbacks.deleteFont) {
4476  }
4477 
4478  free(pFont);
4479 }
4480 
4481 bool drgui_get_font_metrics(drgui_font* pFont, drgui_font_metrics* pMetricsOut)
4482 {
4483  if (pFont == NULL || pMetricsOut == NULL) {
4484  return false;
4485  }
4486 
4487  assert(pFont->pContext != NULL);
4488 
4489  if (pFont->pContext->paintingCallbacks.getFontMetrics == NULL) {
4490  return false;
4491  }
4492 
4493  return pFont->pContext->paintingCallbacks.getFontMetrics(pFont->internalFont, pMetricsOut);
4494 }
4495 
4496 bool drgui_get_glyph_metrics(drgui_font* pFont, unsigned int utf32, drgui_glyph_metrics* pMetricsOut)
4497 {
4498  if (pFont == NULL || pMetricsOut == NULL) {
4499  return false;
4500  }
4501 
4502  assert(pFont->pContext != NULL);
4503 
4505  return false;
4506  }
4507 
4508  return pFont->pContext->paintingCallbacks.getGlyphMetrics(pFont->internalFont, utf32, pMetricsOut);
4509 }
4510 
4511 bool drgui_measure_string(drgui_font* pFont, const char* text, size_t textLengthInBytes, float* pWidthOut, float* pHeightOut)
4512 {
4513  if (pFont == NULL) {
4514  return false;
4515  }
4516 
4517  if (text == NULL || textLengthInBytes == 0)
4518  {
4519  drgui_font_metrics metrics;
4520  if (!drgui_get_font_metrics(pFont, &metrics)) {
4521  return false;
4522  }
4523 
4524  if (pWidthOut) {
4525  *pWidthOut = 0;
4526  }
4527  if (pHeightOut) {
4528  *pHeightOut = (float)metrics.lineHeight;
4529  }
4530 
4531  return true;
4532  }
4533 
4534 
4535 
4536  assert(pFont->pContext != NULL);
4537 
4538  if (pFont->pContext->paintingCallbacks.measureString == NULL) {
4539  return false;
4540  }
4541 
4542  return pFont->pContext->paintingCallbacks.measureString(pFont->internalFont, text, textLengthInBytes, pWidthOut, pHeightOut);
4543 }
4544 
4545 bool drgui_get_text_cursor_position_from_point(drgui_font* pFont, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut)
4546 {
4547  if (pFont == NULL) {
4548  return false;
4549  }
4550 
4551  assert(pFont->pContext != NULL);
4552 
4554  return pFont->pContext->paintingCallbacks.getTextCursorPositionFromPoint(pFont->internalFont, text, textSizeInBytes, maxWidth, inputPosX, pTextCursorPosXOut, pCharacterIndexOut);
4555  }
4556 
4557  return false;
4558 }
4559 
4560 bool drgui_get_text_cursor_position_from_char(drgui_font* pFont, const char* text, size_t characterIndex, float* pTextCursorPosXOut)
4561 {
4562  if (pFont == NULL) {
4563  return false;
4564  }
4565 
4566  assert(pFont->pContext != NULL);
4567 
4569  return pFont->pContext->paintingCallbacks.getTextCursorPositionFromChar(pFont->internalFont, text, characterIndex, pTextCursorPosXOut);
4570  }
4571 
4572  return false;
4573 }
4574 
4575 
4576 
4577 drgui_image* drgui_create_image(drgui_context* pContext, unsigned int width, unsigned int height, drgui_image_format format, unsigned int stride, const void* pData)
4578 {
4579  if (pContext == NULL) {
4580  return NULL;
4581  }
4582 
4583  if (pContext->paintingCallbacks.createImage == NULL) {
4584  return NULL;
4585  }
4586 
4587 
4588  // If the stride is 0, assume tightly packed.
4589  if (stride == 0) {
4590  stride = width * 4;
4591  }
4592 
4593 
4594  drgui_resource internalImage = pContext->paintingCallbacks.createImage(pContext->pPaintingContext, width, height, format, stride, pData);
4595  if (internalImage == NULL) {
4596  return NULL;
4597  }
4598 
4599  drgui_image* pImage = (drgui_image*)malloc(sizeof(*pImage));
4600  if (pImage == NULL) {
4601  return NULL;
4602  }
4603 
4604  pImage->pContext = pContext;
4605  pImage->hResource = internalImage;
4606 
4607 
4608  return pImage;
4609 }
4610 
4611 void drgui_delete_image(drgui_image* pImage)
4612 {
4613  if (pImage == NULL) {
4614  return;
4615  }
4616 
4617  assert(pImage->pContext != NULL);
4618 
4619  // Delete the internal font object.
4620  if (pImage->pContext->paintingCallbacks.deleteImage) {
4621  pImage->pContext->paintingCallbacks.deleteImage(pImage->hResource);
4622  }
4623 
4624  // Free the font object last.
4625  free(pImage);
4626 }
4627 
4628 void drgui_get_image_size(drgui_image* pImage, unsigned int* pWidthOut, unsigned int* pHeightOut)
4629 {
4630  if (pWidthOut) *pWidthOut = 0;
4631  if (pHeightOut) *pHeightOut = 0;
4632 
4633  if (pImage == NULL) {
4634  return;
4635  }
4636 
4637  assert(pImage->pContext != NULL);
4638 
4639  if (pImage->pContext->paintingCallbacks.getImageSize == NULL) {
4640  return;
4641  }
4642 
4643  pImage->pContext->paintingCallbacks.getImageSize(pImage->hResource, pWidthOut, pHeightOut);
4644 }
4645 
4647 {
4648  if (pContext == NULL || pContext->paintingCallbacks.getOptimalImageFormat == NULL) {
4649  return drgui_image_format_rgba8;
4650  }
4651 
4652  return pContext->paintingCallbacks.getOptimalImageFormat(pContext->pPaintingContext);
4653 }
4654 
4655 void* drgui_map_image_data(drgui_image* pImage, unsigned int accessFlags)
4656 {
4657  if (pImage == NULL) {
4658  return NULL;
4659  }
4660 
4662  return NULL;
4663  }
4664 
4665  return pImage->pContext->paintingCallbacks.mapImageData(pImage->hResource, accessFlags);
4666 }
4667 
4668 void drgui_unmap_image_data(drgui_image* pImage)
4669 {
4670  if (pImage == NULL) {
4671  return;
4672  }
4673 
4674  if (pImage->pContext->paintingCallbacks.unmapImageData == NULL) {
4675  return;
4676  }
4677 
4679 }
4680 
4681 
4682 
4684 //
4685 // HIGH-LEVEL API
4686 //
4688 
4690 
4691 void drgui_on_size_fit_children_to_parent(drgui_element* pElement, float newWidth, float newHeight)
4692 {
4693  for (drgui_element* pChild = pElement->pFirstChild; pChild != NULL; pChild = pChild->pNextSibling) {
4694  drgui_set_size(pChild, newWidth, newHeight);
4695  }
4696 }
4697 
4698 bool drgui_pass_through_hit_test(drgui_element* pElement, float mousePosX, float mousePosY)
4699 {
4700  (void)pElement;
4701  (void)mousePosX;
4702  (void)mousePosY;
4703 
4704  return false;
4705 }
4706 
4707 
4708 
4710 
4711 void drgui_draw_border(drgui_element* pElement, float borderWidth, drgui_color color, void* pUserData)
4712 {
4713  drgui_draw_rect_outline(pElement, drgui_get_local_rect(pElement), color, borderWidth, pUserData);
4714 }
4715 
4716 
4717 
4719 //
4720 // UTILITY API
4721 //
4723 
4725 {
4726  drgui_color color;
4727  color.r = r;
4728  color.g = g;
4729  color.b = b;
4730  color.a = a;
4731 
4732  return color;
4733 }
4734 
4736 {
4737  drgui_color color;
4738  color.r = r;
4739  color.g = g;
4740  color.b = b;
4741  color.a = 255;
4742 
4743  return color;
4744 }
4745 
4747 {
4748  drgui_rect result;
4749  result.left = (rect.left >= other.left) ? rect.left : other.left;
4750  result.top = (rect.top >= other.top) ? rect.top : other.top;
4751  result.right = (rect.right <= other.right) ? rect.right : other.right;
4752  result.bottom = (rect.bottom <= other.bottom) ? rect.bottom : other.bottom;
4753 
4754  return result;
4755 }
4756 
4757 bool drgui_clamp_rect_to_element(const drgui_element* pElement, drgui_rect* pRelativeRect)
4758 {
4759  if (pElement == NULL || pRelativeRect == NULL) {
4760  return false;
4761  }
4762 
4763 
4764  if (pRelativeRect->left < 0) {
4765  pRelativeRect->left = 0;
4766  }
4767  if (pRelativeRect->top < 0) {
4768  pRelativeRect->top = 0;
4769  }
4770 
4771  if (pRelativeRect->right > pElement->width) {
4772  pRelativeRect->right = pElement->width;
4773  }
4774  if (pRelativeRect->bottom > pElement->height) {
4775  pRelativeRect->bottom = pElement->height;
4776  }
4777 
4778 
4779  return (pRelativeRect->right - pRelativeRect->left > 0) && (pRelativeRect->bottom - pRelativeRect->top > 0);
4780 }
4781 
4783 {
4784  if (pElement == NULL || pRect == NULL) {
4785  return drgui_make_rect(0, 0, 0, 0);
4786  }
4787 
4788  pRect->left -= pElement->absolutePosX;
4789  pRect->top -= pElement->absolutePosY;
4790  pRect->right -= pElement->absolutePosX;
4791  pRect->bottom -= pElement->absolutePosY;
4792 
4793  return *pRect;
4794 }
4795 
4797 {
4798  if (pElement == NULL || pRect == NULL) {
4799  return drgui_make_rect(0, 0, 0, 0);
4800  }
4801 
4802  pRect->left += pElement->absolutePosX;
4803  pRect->top += pElement->absolutePosY;
4804  pRect->right += pElement->absolutePosX;
4805  pRect->bottom += pElement->absolutePosY;
4806 
4807  return *pRect;
4808 }
4809 
4810 void drgui_make_point_relative(const drgui_element* pElement, float* positionX, float* positionY)
4811 {
4812  if (pElement != NULL)
4813  {
4814  if (positionX != NULL) {
4815  *positionX -= pElement->absolutePosX;
4816  }
4817 
4818  if (positionY != NULL) {
4819  *positionY -= pElement->absolutePosY;
4820  }
4821  }
4822 }
4823 
4824 void drgui_make_point_absolute(const drgui_element* pElement, float* positionX, float* positionY)
4825 {
4826  if (pElement != NULL)
4827  {
4828  if (positionX != NULL) {
4829  *positionX += pElement->absolutePosX;
4830  }
4831 
4832  if (positionY != NULL) {
4833  *positionY += pElement->absolutePosY;
4834  }
4835  }
4836 }
4837 
4838 drgui_rect drgui_make_rect(float left, float top, float right, float bottom)
4839 {
4840  drgui_rect rect;
4841  rect.left = left;
4842  rect.top = top;
4843  rect.right = right;
4844  rect.bottom = bottom;
4845 
4846  return rect;
4847 }
4848 
4850 {
4851  drgui_rect rect;
4852  rect.left = FLT_MAX;
4853  rect.top = FLT_MAX;
4854  rect.right = -FLT_MAX;
4855  rect.bottom = -FLT_MAX;
4856 
4857  return rect;
4858 }
4859 
4860 drgui_rect drgui_grow_rect(drgui_rect rect, float amount)
4861 {
4862  drgui_rect result = rect;
4863  result.left -= amount;
4864  result.top -= amount;
4865  result.right += amount;
4866  result.bottom += amount;
4867 
4868  return result;
4869 }
4870 
4871 drgui_rect drgui_scale_rect(drgui_rect rect, float scaleX, float scaleY)
4872 {
4873  drgui_rect result = rect;
4874  result.left *= scaleX;
4875  result.top *= scaleY;
4876  result.right *= scaleX;
4877  result.bottom *= scaleY;
4878 
4879  return result;
4880 }
4881 
4882 drgui_rect drgui_offset_rect(drgui_rect rect, float offsetX, float offsetY)
4883 {
4884  return drgui_make_rect(rect.left + offsetX, rect.top + offsetY, rect.right + offsetX, rect.bottom + offsetY);
4885 }
4886 
4888 {
4889  drgui_rect result;
4890  result.left = (rect0.left < rect1.left) ? rect0.left : rect1.left;
4891  result.top = (rect0.top < rect1.top) ? rect0.top : rect1.top;
4892  result.right = (rect0.right > rect1.right) ? rect0.right : rect1.right;
4893  result.bottom = (rect0.bottom > rect1.bottom) ? rect0.bottom : rect1.bottom;
4894 
4895  return result;
4896 }
4897 
4898 bool drgui_rect_contains_point(drgui_rect rect, float posX, float posY)
4899 {
4900  if (posX < rect.left || posY < rect.top) {
4901  return false;
4902  }
4903 
4904  if (posX >= rect.right || posY >= rect.bottom) {
4905  return false;
4906  }
4907 
4908  return true;
4909 }
4910 
4911 bool drgui_rect_equal(drgui_rect rect0, drgui_rect rect1)
4912 {
4913  return
4914  rect0.left == rect1.left &&
4915  rect0.top == rect1.top &&
4916  rect0.right == rect1.right &&
4917  rect0.bottom == rect1.bottom;
4918 }
4919 
4921 {
4922  return rect.right > rect.left && rect.bottom > rect.top;
4923 }
4924 
4925 
4926 
4927 
4929 //
4930 // EASY_DRAW-SPECIFIC API
4931 //
4933 #ifndef DRGUI_NO_DR_2D
4934 
4935 void drgui_draw_begin_dr_2d(void* pPaintData);
4936 void drgui_draw_end_dr_2d(void* pPaintData);
4937 void drgui_set_clip_dr_2d(drgui_rect rect, void* pPaintData);
4938 void drgui_get_clip_dr_2d(drgui_rect* pRectOut, void* pPaintData);
4939 void drgui_draw_rect_dr_2d(drgui_rect rect, drgui_color color, void* pPaintData);
4940 void drgui_draw_rect_outline_dr_2d(drgui_rect, drgui_color, float, void*);
4941 void drgui_draw_rect_with_outline_dr_2d(drgui_rect, drgui_color, float, drgui_color, void*);
4942 void drgui_draw_round_rect_dr_2d(drgui_rect, drgui_color, float, void*);
4943 void drgui_draw_round_rect_outline_dr_2d(drgui_rect, drgui_color, float, float, void*);
4944 void drgui_draw_round_rect_with_outline_dr_2d(drgui_rect, drgui_color, float, float, drgui_color, void*);
4945 void drgui_draw_text_dr_2d(drgui_resource, const char*, int, float, float, drgui_color, drgui_color, void*);
4946 void drgui_draw_image_dr_2d(drgui_resource image, drgui_draw_image_args* pArgs, void* pPaintData);
4947 
4948 drgui_resource drgui_create_font_dr_2d(void*, const char*, unsigned int, drgui_font_weight, drgui_font_slant, float, unsigned int flags);
4949 void drgui_delete_font_dr_2d(drgui_resource);
4950 unsigned int drgui_get_font_size_dr_2d(drgui_resource hFont);
4951 bool drgui_get_font_metrics_dr_2d(drgui_resource, drgui_font_metrics*);
4952 bool drgui_get_glyph_metrics_dr_2d(drgui_resource, unsigned int, drgui_glyph_metrics*);
4953 bool drgui_measure_string_dr_2d(drgui_resource, const char*, size_t, float*, float*);
4954 bool drgui_get_text_cursor_position_from_point_dr_2d(drgui_resource font, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut);
4955 bool drgui_get_text_cursor_position_from_char_dr_2d(drgui_resource font, const char* text, size_t characterIndex, float* pTextCursorPosXOut);
4956 
4957 drgui_resource drgui_create_image_dr_2d(void* pPaintingContext, unsigned int width, unsigned int height, drgui_image_format format, unsigned int stride, const void* pImageData);
4958 void drgui_delete_image_dr_2d(drgui_resource image);
4959 void drgui_get_image_size_dr_2d(drgui_resource image, unsigned int* pWidthOut, unsigned int* pHeightOut);
4960 drgui_image_format drgui_get_optimal_image_format_dr_2d(void* pPaintingContext);
4961 void* drgui_map_image_data_dr_2d(drgui_resource image, unsigned int accessFlags);
4962 void drgui_unmap_image_data_dr_2d(drgui_resource image);
4963 
4965 {
4966  drgui_context* pContext = drgui_create_context();
4967  if (pContext != NULL) {
4968  drgui_register_dr_2d_callbacks(pContext, pDrawingContext);
4969  }
4970 
4971  return pContext;
4972 }
4973 
4974 void drgui_register_dr_2d_callbacks(drgui_context* pContext, dr2d_context* pDrawingContext)
4975 {
4976  drgui_painting_callbacks callbacks;
4977  callbacks.drawBegin = drgui_draw_begin_dr_2d;
4978  callbacks.drawEnd = drgui_draw_end_dr_2d;
4979  callbacks.setClip = drgui_set_clip_dr_2d;
4980  callbacks.getClip = drgui_get_clip_dr_2d;
4981  callbacks.drawRect = drgui_draw_rect_dr_2d;
4982  callbacks.drawRectOutline = drgui_draw_rect_outline_dr_2d;
4983  callbacks.drawRectWithOutline = drgui_draw_rect_with_outline_dr_2d;
4984  callbacks.drawRoundRect = drgui_draw_round_rect_dr_2d;
4985  callbacks.drawRoundRectOutline = drgui_draw_round_rect_outline_dr_2d;
4986  callbacks.drawRoundRectWithOutline = drgui_draw_round_rect_with_outline_dr_2d;
4987  callbacks.drawText = drgui_draw_text_dr_2d;
4988  callbacks.drawImage = drgui_draw_image_dr_2d;
4989 
4990  callbacks.createFont = drgui_create_font_dr_2d;
4991  callbacks.deleteFont = drgui_delete_font_dr_2d;
4992  callbacks.getFontSize = drgui_get_font_size_dr_2d;
4993  callbacks.getFontMetrics = drgui_get_font_metrics_dr_2d;
4994  callbacks.getGlyphMetrics = drgui_get_glyph_metrics_dr_2d;
4995  callbacks.measureString = drgui_measure_string_dr_2d;
4996 
4997  callbacks.createImage = drgui_create_image_dr_2d;
4998  callbacks.deleteImage = drgui_delete_image_dr_2d;
4999  callbacks.getImageSize = drgui_get_image_size_dr_2d;
5000  callbacks.getOptimalImageFormat = drgui_get_optimal_image_format_dr_2d;
5001  callbacks.mapImageData = drgui_map_image_data_dr_2d;
5002  callbacks.unmapImageData = drgui_unmap_image_data_dr_2d;
5003 
5004  callbacks.getTextCursorPositionFromPoint = drgui_get_text_cursor_position_from_point_dr_2d;
5005  callbacks.getTextCursorPositionFromChar = drgui_get_text_cursor_position_from_char_dr_2d;
5006 
5007  drgui_register_painting_callbacks(pContext, pDrawingContext, callbacks);
5008 }
5009 
5010 
5011 void drgui_draw_begin_dr_2d(void* pPaintData)
5012 {
5013  dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
5014  assert(pSurface != NULL);
5015 
5016  dr2d_begin_draw(pSurface);
5017 }
5018 
5019 void drgui_draw_end_dr_2d(void* pPaintData)
5020 {
5021  dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
5022  assert(pSurface != NULL);
5023 
5024  dr2d_end_draw(pSurface);
5025 }
5026 
5027 void drgui_set_clip_dr_2d(drgui_rect rect, void* pPaintData)
5028 {
5029  dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
5030  assert(pSurface != NULL);
5031 
5032  dr2d_set_clip(pSurface, rect.left, rect.top, rect.right, rect.bottom);
5033 }
5034 
5035 void drgui_get_clip_dr_2d(drgui_rect* pRectOut, void* pPaintData)
5036 {
5037  assert(pRectOut != NULL);
5038 
5039  dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
5040  assert(pSurface != NULL);
5041 
5042  dr2d_get_clip(pSurface, &pRectOut->left, &pRectOut->top, &pRectOut->right, &pRectOut->bottom);
5043 }
5044 
5045 void drgui_draw_rect_dr_2d(drgui_rect rect, drgui_color color, void* pPaintData)
5046 {
5047  dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
5048  assert(pSurface != NULL);
5049 
5050  dr2d_draw_rect(pSurface, rect.left, rect.top, rect.right, rect.bottom, dr2d_rgba(color.r, color.g, color.b, color.a));
5051 }
5052 
5053 void drgui_draw_rect_outline_dr_2d(drgui_rect rect, drgui_color color, float outlineWidth, void* pPaintData)
5054 {
5055  dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
5056  assert(pSurface != NULL);
5057 
5058  dr2d_draw_rect_outline(pSurface, rect.left, rect.top, rect.right, rect.bottom, dr2d_rgba(color.r, color.g, color.b, color.a), outlineWidth);
5059 }
5060 
5061 void drgui_draw_rect_with_outline_dr_2d(drgui_rect rect, drgui_color color, float outlineWidth, drgui_color outlineColor, void* pPaintData)
5062 {
5063  dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
5064  assert(pSurface != NULL);
5065 
5066  dr2d_draw_rect_with_outline(pSurface, rect.left, rect.top, rect.right, rect.bottom, dr2d_rgba(color.r, color.g, color.b, color.a), outlineWidth, dr2d_rgba(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a));
5067 }
5068 
5069 void drgui_draw_round_rect_dr_2d(drgui_rect rect, drgui_color color, float radius, void* pPaintData)
5070 {
5071  dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
5072  assert(pSurface != NULL);
5073 
5074  dr2d_draw_round_rect(pSurface, rect.left, rect.top, rect.right, rect.bottom, dr2d_rgba(color.r, color.g, color.b, color.a), radius);
5075 }
5076 
5077 void drgui_draw_round_rect_outline_dr_2d(drgui_rect rect, drgui_color color, float radius, float outlineWidth, void* pPaintData)
5078 {
5079  dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
5080  assert(pSurface != NULL);
5081 
5082  dr2d_draw_round_rect_outline(pSurface, rect.left, rect.top, rect.right, rect.bottom, dr2d_rgba(color.r, color.g, color.b, color.a), radius, outlineWidth);
5083 }
5084 
5085 void drgui_draw_round_rect_with_outline_dr_2d(drgui_rect rect, drgui_color color, float radius, float outlineWidth, drgui_color outlineColor, void* pPaintData)
5086 {
5087  dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
5088  assert(pSurface != NULL);
5089 
5090  dr2d_draw_round_rect_with_outline(pSurface, rect.left, rect.top, rect.right, rect.bottom, dr2d_rgba(color.r, color.g, color.b, color.a), radius, outlineWidth, dr2d_rgba(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a));
5091 }
5092 
5093 void drgui_draw_text_dr_2d(drgui_resource font, const char* text, int textSizeInBytes, float posX, float posY, drgui_color color, drgui_color backgroundColor, void* pPaintData)
5094 {
5095  dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
5096  assert(pSurface != NULL);
5097 
5098  dr2d_draw_text(pSurface, (dr2d_font*)font, text, textSizeInBytes, posX, posY, dr2d_rgba(color.r, color.g, color.b, color.a), dr2d_rgba(backgroundColor.r, backgroundColor.g, backgroundColor.b, backgroundColor.a));
5099 }
5100 
5101 void drgui_draw_image_dr_2d(drgui_resource image, drgui_draw_image_args* pArgs, void* pPaintData)
5102 {
5103  dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
5104  assert(pSurface != NULL);
5105 
5107  args.dstX = pArgs->dstX;
5108  args.dstY = pArgs->dstY;
5109  args.dstWidth = pArgs->dstWidth;
5110  args.dstHeight = pArgs->dstHeight;
5111  args.srcX = pArgs->srcX;
5112  args.srcY = pArgs->srcY;
5113  args.srcWidth = pArgs->srcWidth;
5114  args.srcHeight = pArgs->srcHeight;
5115  args.foregroundTint = dr2d_rgba(pArgs->foregroundTint.r, pArgs->foregroundTint.g, pArgs->foregroundTint.b, pArgs->foregroundTint.a);
5116  args.backgroundColor = dr2d_rgba(pArgs->backgroundColor.r, pArgs->backgroundColor.g, pArgs->backgroundColor.b, pArgs->backgroundColor.a);
5117  args.options = pArgs->options;
5118  dr2d_draw_image(pSurface, (dr2d_image*)image, &args);
5119 }
5120 
5121 
5122 drgui_resource drgui_create_font_dr_2d(void* pPaintingContext, const char* family, unsigned int size, drgui_font_weight weight, drgui_font_slant slant, float rotation, unsigned int flags)
5123 {
5124  return dr2d_create_font((dr2d_context*)pPaintingContext, family, size, (dr2d_font_weight)weight, (dr2d_font_slant)slant, rotation, flags);
5125 }
5126 
5127 void drgui_delete_font_dr_2d(drgui_resource font)
5128 {
5129  dr2d_delete_font((dr2d_font*)font);
5130 }
5131 
5132 unsigned int drgui_get_font_size_dr_2d(drgui_resource font)
5133 {
5134  return dr2d_get_font_size((dr2d_font*)font);
5135 }
5136 
5137 bool drgui_get_font_metrics_dr_2d(drgui_resource font, drgui_font_metrics* pMetricsOut)
5138 {
5139  assert(pMetricsOut != NULL);
5140 
5141  dr2d_font_metrics metrics;
5142  if (!dr2d_get_font_metrics((dr2d_font*)font, &metrics)) {
5143  return false;
5144  }
5145 
5146  pMetricsOut->ascent = metrics.ascent;
5147  pMetricsOut->descent = metrics.descent;
5148  pMetricsOut->lineHeight = metrics.lineHeight;
5149  pMetricsOut->spaceWidth = metrics.spaceWidth;
5150 
5151  return true;
5152 }
5153 
5154 bool drgui_get_glyph_metrics_dr_2d(drgui_resource font, unsigned int utf32, drgui_glyph_metrics* pMetricsOut)
5155 {
5156  assert(pMetricsOut != NULL);
5157 
5158  dr2d_glyph_metrics metrics;
5159  if (!dr2d_get_glyph_metrics((dr2d_font*)font, utf32, &metrics)) {
5160  return false;
5161  }
5162 
5163  pMetricsOut->width = metrics.width;
5164  pMetricsOut->height = metrics.height;
5165  pMetricsOut->originX = metrics.originX;
5166  pMetricsOut->originY = metrics.originY;
5167  pMetricsOut->advanceX = metrics.advanceX;
5168  pMetricsOut->advanceY = metrics.advanceY;
5169 
5170  return true;
5171 }
5172 
5173 bool drgui_measure_string_dr_2d(drgui_resource font, const char* text, size_t textSizeInBytes, float* pWidthOut, float* pHeightOut)
5174 {
5175  return dr2d_measure_string((dr2d_font*)font, text, textSizeInBytes, pWidthOut, pHeightOut);
5176 }
5177 
5178 bool drgui_get_text_cursor_position_from_point_dr_2d(drgui_resource font, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut)
5179 {
5180  return dr2d_get_text_cursor_position_from_point((dr2d_font*)font, text, textSizeInBytes, maxWidth, inputPosX, pTextCursorPosXOut, pCharacterIndexOut);
5181 }
5182 
5183 bool drgui_get_text_cursor_position_from_char_dr_2d(drgui_resource font, const char* text, size_t characterIndex, float* pTextCursorPosXOut)
5184 {
5185  return dr2d_get_text_cursor_position_from_char((dr2d_font*)font, text, characterIndex, pTextCursorPosXOut);
5186 }
5187 
5188 
5189 drgui_resource drgui_create_image_dr_2d(void* pPaintingContext, unsigned int width, unsigned int height, drgui_image_format format, unsigned int stride, const void* pImageData)
5190 {
5191  dr2d_image_format dr2dFormat;
5192  switch (format)
5193  {
5194  case drgui_image_format_bgra8: dr2dFormat = dr2d_image_format_bgra8; break;
5195  case drgui_image_format_argb8: dr2dFormat = dr2d_image_format_argb8; break;
5196  default: dr2dFormat = dr2d_image_format_rgba8;
5197  }
5198 
5199  return dr2d_create_image((dr2d_context*)pPaintingContext, width, height, dr2dFormat, stride, pImageData);
5200 }
5201 
5202 void drgui_delete_image_dr_2d(drgui_resource image)
5203 {
5204  dr2d_delete_image((dr2d_image*)image);
5205 }
5206 
5207 void drgui_get_image_size_dr_2d(drgui_resource image, unsigned int* pWidthOut, unsigned int* pHeightOut)
5208 {
5209  dr2d_get_image_size((dr2d_image*)image, pWidthOut, pHeightOut);
5210 }
5211 
5212 drgui_image_format drgui_get_optimal_image_format_dr_2d(void* pPaintingContext)
5213 {
5215 }
5216 
5217 void* drgui_map_image_data_dr_2d(drgui_resource image, unsigned int accessFlags)
5218 {
5219  return dr2d_map_image_data((dr2d_image*)image, accessFlags);
5220 }
5221 
5222 void drgui_unmap_image_data_dr_2d(drgui_resource image)
5223 {
5225 }
5226 
5227 #endif //DRGUI_NO_DR_2D
5228 #endif //DR_GUI_IMPLEMENTATION
5229 
5230 
5231 
5232 
5235 //
5236 // Text Engine
5237 //
5240 #ifndef DRGUI_NO_TEXT_EDITING
5241 // QUICK NOTES
5242 //
5243 // - Text engines are used to make it easier to manage the layout of a block of text.
5244 // - Text engines support basic editing which requires inbound events to be posted from the higher level application.
5245 // - Text engines are not GUI elements. They are lower level objects that are used by higher level GUI elements.
5246 // - Text engines normalize line endings to \n format. Keep this in mind when retrieving the text of a layout.
5247 // - Text engine use the notion of a container which is used for determining which text runs are visible.
5248 
5249 #ifndef drgui_text_engine_h
5250 #define drgui_text_engine_h
5251 
5252 #ifdef __cplusplus
5253 extern "C" {
5254 #endif
5255 
5257 
5258 typedef enum
5259 {
5266 
5267 typedef struct
5268 {
5270  const char* text;
5271 
5273  size_t textLength;
5274 
5275 
5278 
5281 
5284 
5285 
5287  float posX;
5288 
5290  float posY;
5291 
5293  float width;
5294 
5296  float height;
5297 
5298 
5299  // PROPERTIES BELOW ARE FOR INTERNAL USE ONLY
5300 
5303  size_t iLine;
5304 
5306  size_t iChar;
5307 
5309  size_t iCharEnd;
5310 
5311 } drgui_text_run;
5312 
5313 typedef void (* drgui_text_engine_on_paint_text_proc) (drgui_text_engine* pTL, drgui_text_run* pRun, drgui_element* pElement, void* pPaintData);
5314 typedef void (* drgui_text_engine_on_paint_rect_proc) (drgui_text_engine* pTL, drgui_rect rect, drgui_color color, drgui_element* pElement, void* pPaintData);
5318 typedef void (* drgui_text_engine_on_undo_point_changed_proc)(drgui_text_engine* pTL, unsigned int iUndoPoint);
5319 
5320 
5322 drgui_text_engine* drgui_create_text_engine(drgui_context* pContext, size_t extraDataSize, void* pExtraData);
5323 
5326 
5327 
5330 
5333 
5334 
5336 void drgui_text_engine_set_text(drgui_text_engine* pTL, const char* text);
5337 
5344 size_t drgui_text_engine_get_text(drgui_text_engine* pTL, char* textOut, size_t textOutSize);
5345 
5346 
5349 
5352 
5355 
5356 
5358 void drgui_text_engine_set_container_size(drgui_text_engine* pTL, float containerWidth, float containerHeight);
5359 
5361 void drgui_text_engine_get_container_size(drgui_text_engine* pTL, float* pContainerWidthOut, float* pContainerHeightOut);
5362 
5365 
5368 
5369 
5371 void drgui_text_engine_set_inner_offset(drgui_text_engine* pTL, float innerOffsetX, float innerOffsetY);
5372 
5374 void drgui_text_engine_set_inner_offset_x(drgui_text_engine* pTL, float innerOffsetX);
5375 
5377 void drgui_text_engine_set_inner_offset_y(drgui_text_engine* pTL, float innerOffsetY);
5378 
5380 void drgui_text_engine_get_inner_offset(drgui_text_engine* pTL, float* pInnerOffsetX, float* pInnerOffsetY);
5381 
5384 
5387 
5388 
5391 
5394 
5397 
5400 
5403 
5406 
5409 
5412 
5415 
5418 
5419 
5421 void drgui_text_engine_set_tab_size(drgui_text_engine* pTL, unsigned int sizeInSpaces);
5422 
5425 
5426 
5429 
5432 
5435 
5438 
5439 
5442 
5443 
5445 void drgui_text_engine_set_cursor_width(drgui_text_engine* pTL, float cursorWidth);
5446 
5449 
5452 
5455 
5457 void drgui_text_engine_set_cursor_blink_rate(drgui_text_engine* pTL, unsigned int blinkRateInMilliseconds);
5458 
5461 
5464 
5467 
5470 
5472 void drgui_text_engine_get_cursor_position(drgui_text_engine* pTL, float* pPosXOut, float* pPosYOut);
5473 
5476 
5479 
5482 
5485 
5487 void drgui_text_engine_move_cursor_to_point(drgui_text_engine* pTL, float posX, float posY);
5488 
5491 
5494 
5497 
5500 
5502 bool drgui_text_engine_move_cursor_y(drgui_text_engine* pTL, int amount);
5503 
5506 
5509 
5512 
5515 
5518 
5521 
5524 
5527 
5529 void drgui_text_engine_move_cursor_to_character(drgui_text_engine* pTL, size_t characterIndex);
5530 
5533 
5536 
5539 
5542 
5545 
5546 
5550 bool drgui_text_engine_insert_character(drgui_text_engine* pTL, unsigned int character, size_t insertIndex);
5551 
5555 bool drgui_text_engine_insert_text(drgui_text_engine* pTL, const char* text, size_t insertIndex);
5556 
5560 bool drgui_text_engine_delete_text_range(drgui_text_engine* pTL, size_t iFirstCh, size_t iLastChPlus1);
5561 
5565 bool drgui_text_engine_insert_character_at_cursor(drgui_text_engine* pTL, unsigned int character);
5566 
5570 bool drgui_text_engine_insert_text_at_cursor(drgui_text_engine* pTL, const char* text);
5571 
5576 
5581 
5586 
5587 
5597 
5604 
5607 
5610 
5613 
5616 
5618 void drgui_text_engine_select(drgui_text_engine* pTL, size_t firstCharacter, size_t lastCharacter);
5619 
5627 size_t drgui_text_engine_get_selected_text(drgui_text_engine* pTL, char* textOut, size_t textOutLength);
5628 
5631 
5634 
5637 
5640 
5643 
5644 
5650 
5653 
5656 
5659 
5662 
5665 
5668 
5669 
5670 
5673 
5679 
5684 float drgui_text_engine_get_line_pos_y(drgui_text_engine* pTL, size_t iLine);
5685 
5688 
5691 
5694 
5696 void drgui_text_engine_get_line_character_range(drgui_text_engine* pTL, size_t iLine, size_t* pCharStartOut, size_t* pCharEndOut);
5697 
5698 
5701 
5704 
5711 void drgui_text_engine_paint(drgui_text_engine* pTL, drgui_rect rect, drgui_element* pElement, void* pPaintData);
5712 
5713 
5718 void drgui_text_engine_step(drgui_text_engine* pTL, unsigned int milliseconds);
5719 
5720 
5722 void drgui_text_engine_paint_line_numbers(drgui_text_engine* pTL, float lineNumbersWidth, float lineNumbersHeight, drgui_color textColor, drgui_color backgroundColor, drgui_text_engine_on_paint_text_proc onPaintText, drgui_text_engine_on_paint_rect_proc onPaintRect, drgui_element* pElement, void* pPaintData);
5723 
5724 
5726 bool drgui_text_engine_find_next(drgui_text_engine* pTL, const char* text, size_t* pSelectionStartOut, size_t* pSelectionEndOut);
5727 
5729 bool drgui_text_engine_find_next_no_loop(drgui_text_engine* pTL, const char* text, size_t* pSelectionStartOut, size_t* pSelectionEndOut);
5730 
5731 #ifdef __cplusplus
5732 }
5733 #endif
5734 #endif //drgui_text_engine_h
5735 
5736 
5737 #ifdef DR_GUI_IMPLEMENTATION
5738 typedef struct
5739 {
5741  size_t iRun;
5742 
5744  size_t iChar;
5745 
5747  float relativePosX;
5748 
5751  float absoluteSickyPosX;
5752 
5753 } drgui_text_marker;
5754 
5756 typedef struct
5757 {
5759  char* text;
5760 
5762  size_t cursorPos;
5763 
5765  size_t selectionAnchorPos;
5766 
5768  bool isAnythingSelected;
5769 
5770 } drgui_text_engine_state;
5771 
5772 typedef struct
5773 {
5776  size_t diffPos;
5777 
5780  char* oldText;
5781 
5784  char* newText;
5785 
5788  drgui_text_engine_state oldState;
5789 
5792  drgui_text_engine_state newState;
5793 
5794 } drgui_text_engine_undo_state;
5795 
5796 struct drgui_text_engine
5797 {
5799  char* text;
5800 
5802  size_t textLength;
5803 
5804 
5807 
5810 
5813 
5814 
5816  float containerWidth;
5817 
5819  float containerHeight;
5820 
5822  float innerOffsetX;
5823 
5825  float innerOffsetY;
5826 
5827 
5829  drgui_font* pDefaultFont;
5830 
5832  drgui_color defaultTextColor;
5833 
5835  drgui_color defaultBackgroundColor;
5836 
5838  drgui_color selectionBackgroundColor;
5839 
5841  drgui_color lineBackgroundColor;
5842 
5844  unsigned int tabSizeInSpaces;
5845 
5847  drgui_text_engine_alignment horzAlign;
5848 
5850  drgui_text_engine_alignment vertAlign;
5851 
5853  float cursorWidth;
5854 
5856  drgui_color cursorColor;
5857 
5859  unsigned int cursorBlinkRate;
5860 
5862  unsigned int timeToNextCursorBlink;
5863 
5865  bool isCursorBlinkOn;
5866 
5868  bool isShowingCursor;
5869 
5870 
5872  float textBoundsWidth;
5873 
5875  float textBoundsHeight;
5876 
5877 
5879  drgui_text_marker cursor;
5880 
5882  drgui_text_marker selectionAnchor;
5883 
5884 
5887  unsigned int selectionModeCounter;
5888 
5890  bool isAnythingSelected;
5891 
5892 
5895 
5898 
5901 
5902 
5904  drgui_text_engine_state preparedState;
5905 
5907  drgui_text_engine_undo_state* pUndoStack;
5908 
5910  unsigned int undoStackCount;
5911 
5913  unsigned int iUndoState;
5914 
5915 
5917  unsigned int dirtyCounter;
5918 
5920  drgui_rect accumulatedDirtyRect;
5921 
5922 
5924  drgui_text_run* pRuns;
5925 
5927  size_t runCount;
5928 
5931  size_t runBufferSize;
5932 
5933 
5935  size_t extraDataSize;
5936 
5938  char pExtraData[1];
5939 };
5940 
5942 typedef struct
5943 {
5945  size_t index;
5946 
5948  float posY;
5949 
5951  float height;
5952 
5954  size_t iFirstRun;
5955 
5957  size_t iLastRun;
5958 
5959 } drgui_text_engine_line;
5960 
5961 
5966 DRGUI_PRIVATE void drgui_text_engine__refresh(drgui_text_engine* pTL);
5967 
5969 DRGUI_PRIVATE void drgui_text_engine__refresh_alignment(drgui_text_engine* pTL);
5970 
5972 DRGUI_PRIVATE void drgui_text_engine__push_text_run(drgui_text_engine* pTL, drgui_text_run* pRun);
5973 
5975 DRGUI_PRIVATE void drgui_text_engine__clear_text_runs(drgui_text_engine* pTL);
5976 
5978 DRGUI_PRIVATE void drgui_text_engine__calculate_line_alignment_offset(drgui_text_engine* pTL, float lineWidth, float* pOffsetXOut, float* pOffsetYOut);
5979 
5981 DRGUI_PRIVATE bool drgui_text_engine__is_text_run_whitespace(drgui_text_engine* pTL, drgui_text_run* pRun);
5982 
5984 DRGUI_PRIVATE float drgui_text_engine__get_tab_width(drgui_text_engine* pTL);
5985 
5986 
5988 DRGUI_PRIVATE bool drgui_text_engine__find_closest_line_to_point(drgui_text_engine* pTL, float inputPosYRelativeToText, size_t* pFirstRunIndexOnLineOut, size_t* pLastRunIndexOnLinePlus1Out);
5989 
5991 DRGUI_PRIVATE bool drgui_text_engine__find_closest_run_to_point(drgui_text_engine* pTL, float inputPosXRelativeToText, float inputPosYRelativeToText, size_t* pRunIndexOut);
5992 
5994 DRGUI_PRIVATE bool drgui_text_engine__find_line_info(drgui_text_engine* pTL, size_t iFirstRunOnLine, size_t* pLastRunIndexOnLinePlus1Out, float* pLineHeightOut);
5995 
5997 DRGUI_PRIVATE bool drgui_text_engine__find_line_info_by_index(drgui_text_engine* pTL, size_t iLine, drgui_rect* pRectOut, size_t* pFirstRunIndexOut, size_t* pLastRunIndexPlus1Out);
5998 
6000 DRGUI_PRIVATE bool drgui_text_engine__find_last_run_on_line_starting_from_run(drgui_text_engine* pTL, size_t iRun, size_t* pLastRunIndexOnLineOut);
6001 
6003 DRGUI_PRIVATE bool drgui_text_engine__find_first_run_on_line_starting_from_run(drgui_text_engine* pTL, size_t iRun, size_t* pFirstRunIndexOnLineOut);
6004 
6006 DRGUI_PRIVATE bool drgui_text_engine__find_run_at_character(drgui_text_engine* pTL, size_t iChar, size_t* pRunIndexOut);
6007 
6008 
6010 DRGUI_PRIVATE drgui_text_marker drgui_text_engine__new_marker();
6011 
6013 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_point_relative_to_container(drgui_text_engine* pTL, drgui_text_marker* pMarker, float inputPosX, float inputPosY);
6014 
6016 DRGUI_PRIVATE void drgui_text_engine__get_marker_position_relative_to_container(drgui_text_engine* pTL, drgui_text_marker* pMarker, float* pPosXOut, float* pPosYOut);
6017 
6019 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_point(drgui_text_engine* pTL, drgui_text_marker* pMarker, float inputPosXRelativeToText, float inputPosYRelativeToText);
6020 
6022 DRGUI_PRIVATE bool drgui_text_engine__move_marker_left(drgui_text_engine* pTL, drgui_text_marker* pMarker);
6023 
6025 DRGUI_PRIVATE bool drgui_text_engine__move_marker_right(drgui_text_engine* pTL, drgui_text_marker* pMarker);
6026 
6028 DRGUI_PRIVATE bool drgui_text_engine__move_marker_up(drgui_text_engine* pTL, drgui_text_marker* pMarker);
6029 
6031 DRGUI_PRIVATE bool drgui_text_engine__move_marker_down(drgui_text_engine* pTL, drgui_text_marker* pMarker);
6032 
6034 DRGUI_PRIVATE bool drgui_text_engine__move_marker_y(drgui_text_engine* pTL, drgui_text_marker* pMarker, int amount);
6035 
6037 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_end_of_line(drgui_text_engine* pTL, drgui_text_marker* pMarker);
6038 
6040 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_start_of_line(drgui_text_engine* pTL, drgui_text_marker* pMarker);
6041 
6043 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_end_of_line_by_index(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iLine);
6044 
6046 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_start_of_line_by_index(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iLine);
6047 
6049 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_end_of_text(drgui_text_engine* pTL, drgui_text_marker* pMarker);
6050 
6052 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_start_of_text(drgui_text_engine* pTL, drgui_text_marker* pMarker);
6053 
6055 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_last_character_of_run(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iRun);
6056 
6058 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_first_character_of_run(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iRun);
6059 
6061 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_last_character_of_prev_run(drgui_text_engine* pTL, drgui_text_marker* pMarker);
6062 
6064 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_first_character_of_next_run(drgui_text_engine* pTL, drgui_text_marker* pMarker);
6065 
6067 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_character(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iChar);
6068 
6069 
6074 DRGUI_PRIVATE bool drgui_text_engine__update_marker_relative_position(drgui_text_engine* pTL, drgui_text_marker* pMarker);
6075 
6077 DRGUI_PRIVATE void drgui_text_engine__update_marker_sticky_position(drgui_text_engine* pTL, drgui_text_marker* pMarker);
6078 
6079 
6081 DRGUI_PRIVATE size_t drgui_text_engine__get_marker_absolute_char_index(drgui_text_engine* pTL, drgui_text_marker* pMarker);
6082 
6083 
6085 DRGUI_PRIVATE bool drgui_text_engine__has_spacing_between_selection_markers(drgui_text_engine* pTL);
6086 
6088 DRGUI_PRIVATE size_t drgui_text_engine__split_text_run_by_selection(drgui_text_engine* pTL, drgui_text_run* pRunToSplit, drgui_text_run pSubRunsOut[3]);
6089 
6090 
6092 DRGUI_PRIVATE bool drgui_text_engine__get_selection_markers(drgui_text_engine* pTL, drgui_text_marker** ppSelectionMarker0Out, drgui_text_marker** ppSelectionMarker1Out);
6093 
6094 
6096 DRGUI_PRIVATE bool drgui_text_engine__first_line(drgui_text_engine* pTL, drgui_text_engine_line* pLine);
6097 
6099 DRGUI_PRIVATE bool drgui_text_engine__next_line(drgui_text_engine* pTL, drgui_text_engine_line* pLine);
6100 
6101 
6103 DRGUI_PRIVATE void drgui_text_engine__trim_undo_stack(drgui_text_engine* pTL);
6104 
6106 DRGUI_PRIVATE bool drgui_text_engine__diff_states(drgui_text_engine_state* pPrevState, drgui_text_engine_state* pCurrentState, drgui_text_engine_undo_state* pUndoStateOut);
6107 
6109 DRGUI_PRIVATE void drgui_text_engine__uninit_undo_state(drgui_text_engine_undo_state* pUndoState);
6110 
6112 DRGUI_PRIVATE void drgui_text_engine__push_undo_state(drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState);
6113 
6115 DRGUI_PRIVATE void drgui_text_engine__apply_undo_state(drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState);
6116 
6118 DRGUI_PRIVATE void drgui_text_engine__apply_redo_state(drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState);
6119 
6120 
6122 DRGUI_PRIVATE drgui_rect drgui_text_engine__local_rect(drgui_text_engine* pTL);
6123 
6124 
6126 DRGUI_PRIVATE void drgui_text_engine__on_cursor_move(drgui_text_engine* pTL);
6127 
6129 DRGUI_PRIVATE void drgui_text_engine__on_dirty(drgui_text_engine* pTL, drgui_rect rect);
6130 
6132 DRGUI_PRIVATE void drgui_text_engine__begin_dirty(drgui_text_engine* pTL);
6133 
6135 DRGUI_PRIVATE void drgui_text_engine__end_dirty(drgui_text_engine* pTL);
6136 
6137 
6138 
6139 
6140 drgui_text_engine* drgui_create_text_engine(drgui_context* pContext, size_t extraDataSize, void* pExtraData)
6141 {
6142  if (pContext == NULL) {
6143  return NULL;
6144  }
6145 
6146  drgui_text_engine* pTL = (drgui_text_engine*)malloc(sizeof(drgui_text_engine) + extraDataSize);
6147  if (pTL == NULL) {
6148  return NULL;
6149  }
6150 
6151  pTL->text = NULL;
6152  pTL->textLength = 0;
6153  pTL->onDirty = NULL;
6154  pTL->onTextChanged = NULL;
6155  pTL->onUndoPointChanged = NULL;
6156  pTL->containerWidth = 0;
6157  pTL->containerHeight = 0;
6158  pTL->innerOffsetX = 0;
6159  pTL->innerOffsetY = 0;
6160  pTL->pDefaultFont = NULL;
6161  pTL->defaultTextColor = drgui_rgb(224, 224, 224);
6162  pTL->defaultBackgroundColor = drgui_rgb(48, 48, 48);
6163  pTL->selectionBackgroundColor = drgui_rgb(64, 128, 192);
6164  pTL->lineBackgroundColor = drgui_rgb(40, 40, 40);
6165  pTL->tabSizeInSpaces = 4;
6166  pTL->horzAlign = drgui_text_engine_alignment_left;
6167  pTL->vertAlign = drgui_text_engine_alignment_top;
6168  pTL->cursorWidth = 1;
6169  pTL->cursorColor = drgui_rgb(224, 224, 224);
6170  pTL->cursorBlinkRate = 500;
6171  pTL->timeToNextCursorBlink = pTL->cursorBlinkRate;
6172  pTL->isCursorBlinkOn = true;
6173  pTL->isShowingCursor = false;
6174  pTL->textBoundsWidth = 0;
6175  pTL->textBoundsHeight = 0;
6176  pTL->cursor = drgui_text_engine__new_marker();
6177  pTL->selectionAnchor = drgui_text_engine__new_marker();
6178  pTL->selectionModeCounter = 0;
6179  pTL->isAnythingSelected = false;
6180  pTL->onPaintText = NULL;
6181  pTL->onPaintRect = NULL;
6182  pTL->onCursorMove = NULL;
6183  pTL->preparedState.text = NULL;
6184  pTL->pUndoStack = NULL;
6185  pTL->undoStackCount = 0;
6186  pTL->iUndoState = 0;
6187  pTL->dirtyCounter = 0;
6188  pTL->accumulatedDirtyRect = drgui_make_inside_out_rect();
6189  pTL->pRuns = NULL;
6190  pTL->runCount = 0;
6191  pTL->runBufferSize = 0;
6192 
6193  pTL->extraDataSize = extraDataSize;
6194  if (pExtraData != NULL) {
6195  memcpy(pTL->pExtraData, pExtraData, extraDataSize);
6196  }
6197 
6198  return pTL;
6199 }
6200 
6202 {
6203  if (pTL == NULL) {
6204  return;
6205  }
6206 
6208 
6209  free(pTL->pRuns);
6210  free(pTL->preparedState.text);
6211  free(pTL->text);
6212  free(pTL);
6213 }
6214 
6215 
6217 {
6218  if (pTL == NULL) {
6219  return 0;
6220  }
6221 
6222  return pTL->extraDataSize;
6223 }
6224 
6226 {
6227  if (pTL == NULL) {
6228  return NULL;
6229  }
6230 
6231  return pTL->pExtraData;
6232 }
6233 
6234 
6235 void drgui_text_engine_set_text(drgui_text_engine* pTL, const char* text)
6236 {
6237  if (pTL == NULL) {
6238  return;
6239  }
6240 
6241  size_t textLength = strlen(text);
6242 
6243  free(pTL->text);
6244  pTL->text = (char*)malloc(textLength + 1); // +1 for null terminator.
6245 
6246  // We now need to copy over the text, however we need to skip past \r characters in order to normalize line endings
6247  // and keep everything simple.
6248  char* dst = pTL->text;
6249  const char* src = text;
6250  while (*src != '\0')
6251  {
6252  if (*src != '\r') {
6253  *dst++ = *src;
6254  }
6255 
6256  src++;
6257  }
6258  *dst = '\0';
6259 
6260  pTL->textLength = dst - pTL->text;
6261 
6262  // A change in text means we need to refresh the layout.
6263  drgui_text_engine__refresh(pTL);
6264 
6265  // If the position of the cursor is past the last character we'll need to move it.
6266  if (drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor) >= pTL->textLength) {
6268  }
6269 
6270  if (pTL->onTextChanged) {
6271  pTL->onTextChanged(pTL);
6272  }
6273 
6274  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6275 }
6276 
6277 size_t drgui_text_engine_get_text(drgui_text_engine* pTL, char* textOut, size_t textOutSize)
6278 {
6279  if (pTL == NULL) {
6280  return 0;
6281  }
6282 
6283  if (textOut == NULL) {
6284  return pTL->textLength;
6285  }
6286 
6287 
6288  if (drgui__strcpy_s(textOut, textOutSize, (pTL->text != NULL) ? pTL->text : "") == 0) {
6289  return pTL->textLength;
6290  }
6291 
6292  return 0; // Error with strcpy_s().
6293 }
6294 
6295 
6297 {
6298  if (pTL == NULL) {
6299  return;
6300  }
6301 
6302  pTL->onDirty = proc;
6303 }
6304 
6306 {
6307  if (pTL == NULL) {
6308  return;
6309  }
6310 
6311  pTL->onTextChanged = proc;
6312 }
6313 
6315 {
6316  if (pTL == NULL) {
6317  return;
6318  }
6319 
6320  pTL->onUndoPointChanged = proc;
6321 }
6322 
6323 
6324 void drgui_text_engine_set_container_size(drgui_text_engine* pTL, float containerWidth, float containerHeight)
6325 {
6326  if (pTL == NULL) {
6327  return;
6328  }
6329 
6330  pTL->containerWidth = containerWidth;
6331  pTL->containerHeight = containerHeight;
6332 
6333  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6334 }
6335 
6336 void drgui_text_engine_get_container_size(drgui_text_engine* pTL, float* pContainerWidthOut, float* pContainerHeightOut)
6337 {
6338  float containerWidth = 0;
6339  float containerHeight = 0;
6340 
6341  if (pTL != NULL)
6342  {
6343  containerWidth = pTL->containerWidth;
6344  containerHeight = pTL->containerHeight;
6345  }
6346 
6347 
6348  if (pContainerWidthOut) {
6349  *pContainerWidthOut = containerWidth;
6350  }
6351  if (pContainerHeightOut) {
6352  *pContainerHeightOut = containerHeight;
6353  }
6354 }
6355 
6357 {
6358  if (pTL == NULL) {
6359  return 0;
6360  }
6361 
6362  return pTL->containerWidth;
6363 }
6364 
6366 {
6367  if (pTL == NULL) {
6368  return 0;
6369  }
6370 
6371  return pTL->containerHeight;
6372 }
6373 
6374 
6375 void drgui_text_engine_set_inner_offset(drgui_text_engine* pTL, float innerOffsetX, float innerOffsetY)
6376 {
6377  if (pTL == NULL) {
6378  return;
6379  }
6380 
6381  pTL->innerOffsetX = innerOffsetX;
6382  pTL->innerOffsetY = innerOffsetY;
6383 
6384  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6385 }
6386 
6387 void drgui_text_engine_set_inner_offset_x(drgui_text_engine* pTL, float innerOffsetX)
6388 {
6389  if (pTL == NULL) {
6390  return;
6391  }
6392 
6393  pTL->innerOffsetX = innerOffsetX;
6394 
6395  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6396 }
6397 
6398 void drgui_text_engine_set_inner_offset_y(drgui_text_engine* pTL, float innerOffsetY)
6399 {
6400  if (pTL == NULL) {
6401  return;
6402  }
6403 
6404  pTL->innerOffsetY = innerOffsetY;
6405 
6406  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6407 }
6408 
6409 void drgui_text_engine_get_inner_offset(drgui_text_engine* pTL, float* pInnerOffsetX, float* pInnerOffsetY)
6410 {
6411  float innerOffsetX = 0;
6412  float innerOffsetY = 0;
6413 
6414  if (pTL != NULL)
6415  {
6416  innerOffsetX = pTL->innerOffsetX;
6417  innerOffsetY = pTL->innerOffsetY;
6418  }
6419 
6420 
6421  if (pInnerOffsetX) {
6422  *pInnerOffsetX = innerOffsetX;
6423  }
6424  if (pInnerOffsetY) {
6425  *pInnerOffsetY = innerOffsetY;
6426  }
6427 }
6428 
6430 {
6431  if (pTL == NULL) {
6432  return 0;
6433  }
6434 
6435  return pTL->innerOffsetX;
6436 }
6437 
6439 {
6440  if (pTL == NULL) {
6441  return 0;
6442  }
6443 
6444  return pTL->innerOffsetY;
6445 }
6446 
6447 
6449 {
6450  if (pTL == NULL) {
6451  return;
6452  }
6453 
6454  pTL->pDefaultFont = pFont;
6455 
6456  // A change in font requires a layout refresh.
6457  drgui_text_engine__refresh(pTL);
6458  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6459 }
6460 
6462 {
6463  if (pTL == NULL) {
6464  return NULL;
6465  }
6466 
6467  return pTL->pDefaultFont;
6468 }
6469 
6471 {
6472  if (pTL == NULL) {
6473  return;
6474  }
6475 
6476  pTL->defaultTextColor = color;
6477 
6478  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6479 }
6480 
6482 {
6483  if (pTL == NULL) {
6484  return drgui_rgb(0, 0, 0);
6485  }
6486 
6487  return pTL->defaultTextColor;
6488 }
6489 
6491 {
6492  if (pTL == NULL) {
6493  return;
6494  }
6495 
6496  pTL->defaultBackgroundColor = color;
6497 
6498  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6499 }
6500 
6502 {
6503  if (pTL == NULL) {
6504  return drgui_rgb(0, 0, 0);
6505  }
6506 
6507  return pTL->defaultBackgroundColor;
6508 }
6509 
6511 {
6512  if (pTL == NULL) {
6513  return;
6514  }
6515 
6516  pTL->selectionBackgroundColor = color;
6517 
6519  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6520  }
6521 }
6522 
6524 {
6525  if (pTL == NULL) {
6526  return drgui_rgb(0, 0, 0);
6527  }
6528 
6529  return pTL->selectionBackgroundColor;
6530 }
6531 
6533 {
6534  if (pTL == NULL) {
6535  return;
6536  }
6537 
6538  pTL->lineBackgroundColor = color;
6539 
6540  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6541 }
6542 
6544 {
6545  if (pTL == NULL) {
6546  return drgui_rgb(0, 0, 0);
6547  }
6548 
6549  return pTL->lineBackgroundColor;
6550 }
6551 
6552 
6553 void drgui_text_engine_set_tab_size(drgui_text_engine* pTL, unsigned int sizeInSpaces)
6554 {
6555  if (pTL == NULL) {
6556  return;
6557  }
6558 
6559  if (pTL->tabSizeInSpaces != sizeInSpaces)
6560  {
6561  pTL->tabSizeInSpaces = sizeInSpaces;
6562 
6563  drgui_text_engine__refresh(pTL);
6564  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6565  }
6566 }
6567 
6569 {
6570  if (pTL == NULL) {
6571  return 0;
6572  }
6573 
6574  return pTL->tabSizeInSpaces;
6575 }
6576 
6577 
6579 {
6580  if (pTL == NULL) {
6581  return;
6582  }
6583 
6584  if (pTL->horzAlign != alignment)
6585  {
6586  pTL->horzAlign = alignment;
6587 
6588  drgui_text_engine__refresh_alignment(pTL);
6589  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6590  }
6591 }
6592 
6594 {
6595  if (pTL == NULL) {
6597  }
6598 
6599  return pTL->horzAlign;
6600 }
6601 
6603 {
6604  if (pTL == NULL) {
6605  return;
6606  }
6607 
6608  if (pTL->vertAlign != alignment)
6609  {
6610  pTL->vertAlign = alignment;
6611 
6612  drgui_text_engine__refresh_alignment(pTL);
6613  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6614  }
6615 }
6616 
6618 {
6619  if (pTL == NULL) {
6621  }
6622 
6623  return pTL->vertAlign;
6624 }
6625 
6626 
6628 {
6629  if (pTL == NULL) {
6630  return drgui_make_rect(0, 0, 0, 0);
6631  }
6632 
6633  drgui_rect rect;
6634  rect.left = 0;
6635  rect.top = 0;
6636 
6637 
6638  switch (pTL->horzAlign)
6639  {
6641  {
6642  rect.left = pTL->containerWidth - pTL->textBoundsWidth;
6643  break;
6644  }
6645 
6647  {
6648  rect.left = (pTL->containerWidth - pTL->textBoundsWidth) / 2;
6649  break;
6650  }
6651 
6653  case drgui_text_engine_alignment_top: // Invalid for horizontal align.
6654  case drgui_text_engine_alignment_bottom: // Invalid for horizontal align.
6655  default:
6656  {
6657  break;
6658  }
6659  }
6660 
6661 
6662  switch (pTL->vertAlign)
6663  {
6665  {
6666  rect.top = pTL->containerHeight - pTL->textBoundsHeight;
6667  break;
6668  }
6669 
6671  {
6672  rect.top = (pTL->containerHeight - pTL->textBoundsHeight) / 2;
6673  break;
6674  }
6675 
6677  case drgui_text_engine_alignment_left: // Invalid for vertical align.
6678  case drgui_text_engine_alignment_right: // Invalid for vertical align.
6679  default:
6680  {
6681  break;
6682  }
6683  }
6684 
6685 
6686  rect.left += pTL->innerOffsetX;
6687  rect.top += pTL->innerOffsetY;
6688  rect.right = rect.left + pTL->textBoundsWidth;
6689  rect.bottom = rect.top + pTL->textBoundsHeight;
6690 
6691  return rect;
6692 }
6693 
6694 
6695 void drgui_text_engine_set_cursor_width(drgui_text_engine* pTL, float cursorWidth)
6696 {
6697  if (pTL == NULL) {
6698  return;
6699  }
6700 
6701  drgui_rect oldCursorRect = drgui_text_engine_get_cursor_rect(pTL);
6702  pTL->cursorWidth = cursorWidth;
6703  if (pTL->cursorWidth > 0 && pTL->cursorWidth < 1) {
6704  pTL->cursorWidth = 1;
6705  }
6706 
6707  drgui_text_engine__on_dirty(pTL, drgui_rect_union(oldCursorRect, drgui_text_engine_get_cursor_rect(pTL)));
6708 }
6709 
6711 {
6712  if (pTL == NULL) {
6713  return 0;
6714  }
6715 
6716  return pTL->cursorWidth;
6717 }
6718 
6720 {
6721  if (pTL == NULL) {
6722  return;
6723  }
6724 
6725  pTL->cursorColor = cursorColor;
6726 
6727  drgui_text_engine__on_dirty(pTL, drgui_text_engine_get_cursor_rect(pTL));
6728 }
6729 
6731 {
6732  if (pTL == NULL) {
6733  return drgui_rgb(0, 0, 0);
6734  }
6735 
6736  return pTL->cursorColor;
6737 }
6738 
6739 void drgui_text_engine_set_cursor_blink_rate(drgui_text_engine* pTL, unsigned int blinkRateInMilliseconds)
6740 {
6741  if (pTL == NULL) {
6742  return;
6743  }
6744 
6745  pTL->cursorBlinkRate = blinkRateInMilliseconds;
6746 }
6747 
6749 {
6750  if (pTL == NULL) {
6751  return 0;
6752  }
6753 
6754  return pTL->cursorBlinkRate;
6755 }
6756 
6758 {
6759  if (pTL == NULL) {
6760  return;
6761  }
6762 
6763  if (!pTL->isShowingCursor)
6764  {
6765  pTL->isShowingCursor = true;
6766 
6767  pTL->timeToNextCursorBlink = pTL->cursorBlinkRate;
6768  pTL->isCursorBlinkOn = true;
6769 
6770  drgui_text_engine__on_dirty(pTL, drgui_text_engine_get_cursor_rect(pTL));
6771  }
6772 }
6773 
6775 {
6776  if (pTL == NULL) {
6777  return;
6778  }
6779 
6780  if (pTL->isShowingCursor)
6781  {
6782  pTL->isShowingCursor = false;
6783 
6784  drgui_text_engine__on_dirty(pTL, drgui_text_engine_get_cursor_rect(pTL));
6785  }
6786 }
6787 
6789 {
6790  if (pTL == NULL) {
6791  return false;
6792  }
6793 
6794  return pTL->isShowingCursor;
6795 }
6796 
6797 void drgui_text_engine_get_cursor_position(drgui_text_engine* pTL, float* pPosXOut, float* pPosYOut)
6798 {
6799  if (pTL == NULL) {
6800  return;
6801  }
6802 
6803  drgui_text_engine__get_marker_position_relative_to_container(pTL, &pTL->cursor, pPosXOut, pPosYOut);
6804 }
6805 
6807 {
6808  if (pTL == NULL) {
6809  return drgui_make_rect(0, 0, 0, 0);
6810  }
6811 
6812  drgui_rect lineRect = drgui_make_rect(0, 0, 0, 0);
6813 
6814  if (pTL->runCount > 0)
6815  {
6816  drgui_text_engine__find_line_info_by_index(pTL, pTL->pRuns[pTL->cursor.iRun].iLine, &lineRect, NULL, NULL);
6817  }
6818  else if (pTL->pDefaultFont != NULL)
6819  {
6820  drgui_font_metrics defaultFontMetrics;
6821  drgui_get_font_metrics(pTL->pDefaultFont, &defaultFontMetrics);
6822 
6823  lineRect.bottom = (float)defaultFontMetrics.lineHeight;
6824  }
6825 
6826 
6827 
6828  float cursorPosX;
6829  float cursorPosY;
6830  drgui_text_engine_get_cursor_position(pTL, &cursorPosX, &cursorPosY);
6831 
6832  return drgui_make_rect(cursorPosX, cursorPosY, cursorPosX + pTL->cursorWidth, cursorPosY + (lineRect.bottom - lineRect.top));
6833 }
6834 
6836 {
6837  if (pTL == NULL || pTL->runCount == 0) {
6838  return 0;
6839  }
6840 
6841  return pTL->pRuns[pTL->cursor.iRun].iLine;
6842 }
6843 
6845 {
6846  if (pTL == NULL) {
6847  return 0;
6848  }
6849 
6850  float posX;
6851  float posY;
6852  drgui_text_engine_get_cursor_position(pTL, &posX, &posY);
6853 
6854  drgui_font_metrics fontMetrics;
6855  drgui_get_font_metrics(pTL->pDefaultFont, &fontMetrics);
6856 
6857  return (unsigned int)((int)posX / fontMetrics.spaceWidth);
6858 }
6859 
6861 {
6862  if (pTL == NULL) {
6863  return 0;
6864  }
6865 
6866  return drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
6867 }
6868 
6869 void drgui_text_engine_move_cursor_to_point(drgui_text_engine* pTL, float posX, float posY)
6870 {
6871  if (pTL == NULL) {
6872  return;
6873  }
6874 
6875  size_t iRunOld = pTL->cursor.iRun;
6876  size_t iCharOld = pTL->cursor.iChar;
6877  drgui_text_engine__move_marker_to_point_relative_to_container(pTL, &pTL->cursor, posX, posY);
6878 
6880  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
6881  }
6882 
6883  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
6884  drgui_text_engine__on_cursor_move(pTL);
6885  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6886  }
6887 }
6888 
6890 {
6891  if (pTL == NULL) {
6892  return false;
6893  }
6894 
6895  size_t iRunOld = pTL->cursor.iRun;
6896  size_t iCharOld = pTL->cursor.iChar;
6897  if (drgui_text_engine__move_marker_left(pTL, &pTL->cursor)) {
6899  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
6900  }
6901 
6902  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
6903  drgui_text_engine__on_cursor_move(pTL);
6904  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6905  }
6906 
6907  return true;
6908  }
6909 
6910  return false;
6911 }
6912 
6914 {
6915  if (pTL == NULL) {
6916  return false;
6917  }
6918 
6919  size_t iRunOld = pTL->cursor.iRun;
6920  size_t iCharOld = pTL->cursor.iChar;
6921  if (drgui_text_engine__move_marker_right(pTL, &pTL->cursor)) {
6923  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
6924  }
6925 
6926  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
6927  drgui_text_engine__on_cursor_move(pTL);
6928  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6929  }
6930 
6931  return true;
6932  }
6933 
6934  return false;
6935 }
6936 
6938 {
6939  if (pTL == NULL) {
6940  return false;
6941  }
6942 
6943  size_t iRunOld = pTL->cursor.iRun;
6944  size_t iCharOld = pTL->cursor.iChar;
6945  if (drgui_text_engine__move_marker_up(pTL, &pTL->cursor)) {
6947  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
6948  }
6949 
6950  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
6951  drgui_text_engine__on_cursor_move(pTL);
6952  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6953  }
6954 
6955  return true;
6956  }
6957 
6958  return false;
6959 }
6960 
6962 {
6963  if (pTL == NULL) {
6964  return false;
6965  }
6966 
6967  size_t iRunOld = pTL->cursor.iRun;
6968  size_t iCharOld = pTL->cursor.iChar;
6969  if (drgui_text_engine__move_marker_down(pTL, &pTL->cursor)) {
6971  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
6972  }
6973 
6974  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
6975  drgui_text_engine__on_cursor_move(pTL);
6976  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6977  }
6978 
6979  return true;
6980  }
6981 
6982  return false;
6983 }
6984 
6986 {
6987  if (pTL == NULL) {
6988  return false;
6989  }
6990 
6991  size_t iRunOld = pTL->cursor.iRun;
6992  size_t iCharOld = pTL->cursor.iChar;
6993  if (drgui_text_engine__move_marker_y(pTL, &pTL->cursor, amount)) {
6995  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
6996  }
6997 
6998  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
6999  drgui_text_engine__on_cursor_move(pTL);
7000  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7001  }
7002 
7003  return true;
7004  }
7005 
7006  return false;
7007 }
7008 
7010 {
7011  if (pTL == NULL) {
7012  return false;
7013  }
7014 
7015  size_t iRunOld = pTL->cursor.iRun;
7016  size_t iCharOld = pTL->cursor.iChar;
7017  if (drgui_text_engine__move_marker_to_end_of_line(pTL, &pTL->cursor)) {
7019  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7020  }
7021 
7022  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
7023  drgui_text_engine__on_cursor_move(pTL);
7024  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7025  }
7026 
7027  return true;
7028  }
7029 
7030  return false;
7031 }
7032 
7034 {
7035  if (pTL == NULL) {
7036  return false;
7037  }
7038 
7039  size_t iRunOld = pTL->cursor.iRun;
7040  size_t iCharOld = pTL->cursor.iChar;
7041  if (drgui_text_engine__move_marker_to_start_of_line(pTL, &pTL->cursor)) {
7043  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7044  }
7045 
7046  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
7047  drgui_text_engine__on_cursor_move(pTL);
7048  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7049  }
7050 
7051  return true;
7052  }
7053 
7054  return false;
7055 }
7056 
7058 {
7059  if (pTL == NULL) {
7060  return false;
7061  }
7062 
7063  size_t iRunOld = pTL->cursor.iRun;
7064  size_t iCharOld = pTL->cursor.iChar;
7065  if (drgui_text_engine__move_marker_to_end_of_line_by_index(pTL, &pTL->cursor, iLine)) {
7067  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7068  }
7069 
7070  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
7071  drgui_text_engine__on_cursor_move(pTL);
7072  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7073  }
7074 
7075  return true;
7076  }
7077 
7078  return false;
7079 }
7080 
7082 {
7083  if (pTL == NULL) {
7084  return false;
7085  }
7086 
7087  size_t iRunOld = pTL->cursor.iRun;
7088  size_t iCharOld = pTL->cursor.iChar;
7089  if (drgui_text_engine__move_marker_to_start_of_line_by_index(pTL, &pTL->cursor, iLine)) {
7091  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7092  }
7093 
7094  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
7095  drgui_text_engine__on_cursor_move(pTL);
7096  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7097  }
7098 
7099  return true;
7100  }
7101 
7102  return false;
7103 }
7104 
7106 {
7107  if (pTL == NULL) {
7108  return false;
7109  }
7110 
7111  size_t iRunOld = pTL->cursor.iRun;
7112  size_t iCharOld = pTL->cursor.iChar;
7113  if (drgui_text_engine__move_marker_to_end_of_text(pTL, &pTL->cursor)) {
7115  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7116  }
7117 
7118  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
7119  drgui_text_engine__on_cursor_move(pTL);
7120  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7121  }
7122 
7123  return true;
7124  }
7125 
7126  return false;
7127 }
7128 
7130 {
7131  if (pTL == NULL) {
7132  return false;
7133  }
7134 
7135  size_t iRunOld = pTL->cursor.iRun;
7136  size_t iCharOld = pTL->cursor.iChar;
7137  if (drgui_text_engine__move_marker_to_start_of_text(pTL, &pTL->cursor)) {
7139  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7140  }
7141 
7142  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
7143  drgui_text_engine__on_cursor_move(pTL);
7144  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7145  }
7146 
7147  return true;
7148  }
7149 
7150  return false;
7151 }
7152 
7154 {
7155  if (pTL == NULL) {
7156  return;
7157  }
7158 
7159  drgui_text_marker* pSelectionMarker0;
7160  drgui_text_marker* pSelectionMarker1;
7161  if (drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1))
7162  {
7163  pTL->cursor = *pSelectionMarker0;
7164  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7165 
7166  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7167  }
7168 }
7169 
7171 {
7172  if (pTL == NULL) {
7173  return;
7174  }
7175 
7176  drgui_text_marker* pSelectionMarker0;
7177  drgui_text_marker* pSelectionMarker1;
7178  if (drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1))
7179  {
7180  pTL->cursor = *pSelectionMarker1;
7181  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7182 
7183  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7184  }
7185 }
7186 
7187 void drgui_text_engine_move_cursor_to_character(drgui_text_engine* pTL, size_t characterIndex)
7188 {
7189  if (pTL == NULL) {
7190  return;
7191  }
7192 
7193  size_t iRunOld = pTL->cursor.iRun;
7194  size_t iCharOld = pTL->cursor.iChar;
7195  if (drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, characterIndex)) {
7197  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7198  }
7199 
7200  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
7201  drgui_text_engine__on_cursor_move(pTL);
7202  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7203  }
7204  }
7205 }
7206 
7208 {
7209  drgui_text_marker* pSelectionMarker0;
7210  drgui_text_marker* pSelectionMarker1;
7211  if (drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
7212  return &pTL->cursor == pSelectionMarker0;
7213  }
7214 
7215  return false;
7216 }
7217 
7219 {
7220  drgui_text_marker* pSelectionMarker0;
7221  drgui_text_marker* pSelectionMarker1;
7222  if (drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
7223  return &pTL->cursor == pSelectionMarker1;
7224  }
7225 
7226  return false;
7227 }
7228 
7230 {
7231  if (pTL == NULL) {
7232  return;
7233  }
7234 
7235  drgui_text_marker* pSelectionMarker0;
7236  drgui_text_marker* pSelectionMarker1;
7237  if (drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1))
7238  {
7239  size_t iRunOld = pTL->cursor.iRun;
7240  size_t iCharOld = pTL->cursor.iChar;
7241 
7242  drgui_text_marker temp = *pSelectionMarker0;
7243  *pSelectionMarker0 = *pSelectionMarker1;
7244  *pSelectionMarker1 = temp;
7245 
7246  if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
7247  drgui_text_engine__on_cursor_move(pTL);
7248  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7249  }
7250  }
7251 }
7252 
7254 {
7255  if (pTL == NULL) {
7256  return;
7257  }
7258 
7259  pTL->onCursorMove = proc;
7260 }
7261 
7263 {
7264  if (pTL == NULL || pTL->pRuns == NULL) {
7265  return;
7266  }
7267 
7268  // Cursor.
7269  drgui_text_run* pRun = pTL->pRuns + pTL->cursor.iRun;
7270  drgui_get_text_cursor_position_from_char(pRun->pFont, pTL->text + pRun->iChar, pTL->cursor.iChar, OUT &pTL->cursor.relativePosX);
7271 
7272  pRun = pTL->pRuns + pTL->selectionAnchor.iRun;
7273  drgui_get_text_cursor_position_from_char(pRun->pFont, pTL->text + pRun->iChar, pTL->selectionAnchor.iChar, OUT &pTL->selectionAnchor.relativePosX);
7274 }
7275 
7276 bool drgui_text_engine_insert_character(drgui_text_engine* pTL, unsigned int character, size_t insertIndex)
7277 {
7278  if (pTL == NULL) {
7279  return false;
7280  }
7281 
7282  // Transform '\r' to '\n'.
7283  if (character == '\r') {
7284  character = '\n';
7285  }
7286 
7287 
7288  // TODO: Add proper support for UTF-8.
7289  char* pOldText = pTL->text;
7290  char* pNewText = (char*)malloc(pTL->textLength + 1 + 1); // +1 for the new character and +1 for the null terminator.
7291 
7292  if (insertIndex > 0) {
7293  memcpy(pNewText, pOldText, insertIndex);
7294  }
7295 
7296  pNewText[insertIndex] = (char)character;
7297 
7298  if (insertIndex < pTL->textLength) {
7299  memcpy(pNewText + insertIndex + 1, pOldText + insertIndex, pTL->textLength - insertIndex);
7300  }
7301 
7302  pTL->textLength += 1;
7303  pTL->text = pNewText;
7304  pNewText[pTL->textLength] = '\0';
7305 
7306  free(pOldText);
7307 
7308 
7309 
7310 
7311  // The layout will have changed so it needs to be refreshed.
7312  drgui_text_engine__refresh(pTL);
7313 
7314  if (pTL->onTextChanged) {
7315  pTL->onTextChanged(pTL);
7316  }
7317 
7318  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7319 
7320  return true;
7321 }
7322 
7323 bool drgui_text_engine_insert_text(drgui_text_engine* pTL, const char* text, size_t insertIndex)
7324 {
7325  if (pTL == NULL || text == NULL) {
7326  return false;;
7327  }
7328 
7329  size_t newTextLength = strlen(text);
7330  if (newTextLength == 0) {
7331  return false;
7332  }
7333 
7334 
7335  // TODO: Add proper support for UTF-8.
7336  char* pOldText = pTL->text;
7337  char* pNewText = (char*)malloc(pTL->textLength + newTextLength + 1); // +1 for the new character and +1 for the null terminator.
7338 
7339  if (insertIndex > 0) {
7340  memcpy(pNewText, pOldText, insertIndex);
7341  }
7342 
7343 
7344  // Replace \r\n with \n.
7345  {
7346  char* dst = pNewText + insertIndex;
7347  const char* src = text;
7348  size_t srcLen = newTextLength;
7349  while (*src != '\0' && srcLen > 0)
7350  {
7351  if (*src != '\r') {
7352  *dst++ = *src;
7353  }
7354 
7355  src++;
7356  srcLen -= 1;
7357  }
7358 
7359  newTextLength = dst - (pNewText + insertIndex);
7360  }
7361 
7362  if (insertIndex < pTL->textLength) {
7363  memcpy(pNewText + insertIndex + newTextLength, pOldText + insertIndex, pTL->textLength - insertIndex);
7364  }
7365 
7366  pTL->textLength += newTextLength;
7367  pTL->text = pNewText;
7368  pNewText[pTL->textLength] = '\0';
7369 
7370  free(pOldText);
7371 
7372 
7373  // The layout will have changed so it needs to be refreshed.
7374  drgui_text_engine__refresh(pTL);
7375 
7376  if (pTL->onTextChanged) {
7377  pTL->onTextChanged(pTL);
7378  }
7379 
7380  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7381 
7382  return true;
7383 }
7384 
7385 bool drgui_text_engine_delete_text_range(drgui_text_engine* pTL, size_t iFirstCh, size_t iLastChPlus1)
7386 {
7387  if (pTL == NULL || iLastChPlus1 == iFirstCh) {
7388  return false;
7389  }
7390 
7391  if (iFirstCh > iLastChPlus1) {
7392  size_t temp = iFirstCh;
7393  iFirstCh = iLastChPlus1;
7394  iLastChPlus1 = temp;
7395  }
7396 
7397 
7398  size_t bytesToRemove = iLastChPlus1 - iFirstCh;
7399  if (bytesToRemove > 0)
7400  {
7401  memmove(pTL->text + iFirstCh, pTL->text + iLastChPlus1, pTL->textLength - iLastChPlus1);
7402  pTL->textLength -= bytesToRemove;
7403  pTL->text[pTL->textLength] = '\0';
7404 
7405  // The layout will have changed.
7406  drgui_text_engine__refresh(pTL);
7407 
7408  if (pTL->onTextChanged) {
7409  pTL->onTextChanged(pTL);
7410  }
7411 
7412  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7413 
7414  return true;
7415  }
7416 
7417  return false;
7418 }
7419 
7420 bool drgui_text_engine_insert_character_at_cursor(drgui_text_engine* pTL, unsigned int character)
7421 {
7422  if (pTL == NULL) {
7423  return false;
7424  }
7425 
7426  size_t iAbsoluteMarkerChar = 0;
7427 
7428  drgui_text_run* pRun = pTL->pRuns + pTL->cursor.iRun;
7429  if (pTL->runCount > 0 && pRun != NULL) {
7430  iAbsoluteMarkerChar = pRun->iChar + pTL->cursor.iChar;
7431  }
7432 
7433  drgui_text_engine__begin_dirty(pTL);
7434  {
7435  drgui_text_engine_insert_character(pTL, character, iAbsoluteMarkerChar);
7436  drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, iAbsoluteMarkerChar + 1);
7437  }
7438  drgui_text_engine__end_dirty(pTL);
7439 
7440 
7441  // The cursor's sticky position needs to be updated whenever the text is edited.
7442  drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
7443 
7444 
7445  drgui_text_engine__on_cursor_move(pTL);
7446 
7447  return true;
7448 }
7449 
7451 {
7452  if (pTL == NULL || text == NULL) {
7453  return false;
7454  }
7455 
7456  drgui_text_engine__begin_dirty(pTL);
7457  {
7458  size_t cursorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
7459  drgui_text_engine_insert_text(pTL, text, cursorPos);
7460  drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, cursorPos + strlen(text));
7461  }
7462  drgui_text_engine__end_dirty(pTL);
7463 
7464 
7465  // The cursor's sticky position needs to be updated whenever the text is edited.
7466  drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
7467 
7468  drgui_text_engine__on_cursor_move(pTL);
7469 
7470  return true;
7471 }
7472 
7474 {
7475  if (pTL == NULL) {
7476  return false;
7477  }
7478 
7479  // We just move the cursor to the left, and then delete the character to the right.
7482  return true;
7483  }
7484 
7485  return false;
7486 }
7487 
7489 {
7490  if (pTL == NULL || pTL->runCount == 0) {
7491  return false;
7492  }
7493 
7494  drgui_text_run* pRun = pTL->pRuns + pTL->cursor.iRun;
7495  size_t iAbsoluteMarkerChar = pRun->iChar + pTL->cursor.iChar;
7496 
7497  if (iAbsoluteMarkerChar < pTL->textLength)
7498  {
7499  // TODO: Add proper support for UTF-8.
7500  memmove(pTL->text + iAbsoluteMarkerChar, pTL->text + iAbsoluteMarkerChar + 1, pTL->textLength - iAbsoluteMarkerChar);
7501  pTL->textLength -= 1;
7502  pTL->text[pTL->textLength] = '\0';
7503 
7504 
7505 
7506  // The layout will have changed.
7507  drgui_text_engine__refresh(pTL);
7508  drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, iAbsoluteMarkerChar);
7509 
7510  if (pTL->onTextChanged) {
7511  pTL->onTextChanged(pTL);
7512  }
7513 
7514  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7515 
7516  return true;
7517  }
7518 
7519  return false;
7520 }
7521 
7523 {
7524  // Don't do anything if nothing is selected.
7526  return false;
7527  }
7528 
7529  drgui_text_marker* pSelectionMarker0 = &pTL->selectionAnchor;
7530  drgui_text_marker* pSelectionMarker1 = &pTL->cursor;
7531  if (pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar > pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar)
7532  {
7533  drgui_text_marker* temp = pSelectionMarker0;
7534  pSelectionMarker0 = pSelectionMarker1;
7535  pSelectionMarker1 = temp;
7536  }
7537 
7538  size_t iSelectionChar0 = pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar;
7539  size_t iSelectionChar1 = pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar;
7540 
7541  drgui_text_engine__begin_dirty(pTL);
7542  bool wasTextChanged = drgui_text_engine_delete_text_range(pTL, iSelectionChar0, iSelectionChar1);
7543  if (wasTextChanged)
7544  {
7545  // The marker needs to be updated based on the new layout.
7546  drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, iSelectionChar0);
7547 
7548  // The cursor's sticky position also needs to be updated.
7549  drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
7550 
7551  drgui_text_engine__on_cursor_move(pTL);
7552 
7553 
7554  // Reset the selection marker.
7555  pTL->selectionAnchor = pTL->cursor;
7556  pTL->isAnythingSelected = false;
7557  }
7558 
7559  drgui_text_engine__end_dirty(pTL);
7560  return wasTextChanged;
7561 }
7562 
7563 
7565 {
7566  if (pTL == NULL) {
7567  return;
7568  }
7569 
7570  // If we've just entered selection mode and nothing is currently selected, we want to set the selection anchor to the current cursor position.
7571  if (!drgui_text_engine_is_in_selection_mode(pTL) && !pTL->isAnythingSelected) {
7572  pTL->selectionAnchor = pTL->cursor;
7573  }
7574 
7575  pTL->selectionModeCounter += 1;
7576 }
7577 
7579 {
7580  if (pTL == NULL) {
7581  return;
7582  }
7583 
7584  if (pTL->selectionModeCounter > 0) {
7585  pTL->selectionModeCounter -= 1;
7586  }
7587 }
7588 
7590 {
7591  if (pTL == NULL) {
7592  return false;
7593  }
7594 
7595  return pTL->selectionModeCounter > 0;
7596 }
7597 
7599 {
7600  if (pTL == NULL) {
7601  return false;
7602  }
7603 
7604  return pTL->isAnythingSelected;
7605 }
7606 
7608 {
7609  if (pTL == NULL) {
7610  return;
7611  }
7612 
7613  pTL->isAnythingSelected = false;
7614 
7615  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7616 }
7617 
7619 {
7620  if (pTL == NULL) {
7621  return;
7622  }
7623 
7624  drgui_text_engine__move_marker_to_start_of_text(pTL, &pTL->selectionAnchor);
7625  drgui_text_engine__move_marker_to_end_of_text(pTL, &pTL->cursor);
7626 
7627  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7628 
7629  drgui_text_engine__on_cursor_move(pTL);
7630  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7631 }
7632 
7633 void drgui_text_engine_select(drgui_text_engine* pTL, size_t firstCharacter, size_t lastCharacter)
7634 {
7635  if (pTL == NULL) {
7636  return;
7637  }
7638 
7639  drgui_text_engine__move_marker_to_character(pTL, &pTL->selectionAnchor, firstCharacter);
7640  drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, lastCharacter);
7641 
7642  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7643 
7644  drgui_text_engine__on_cursor_move(pTL);
7645  drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7646 }
7647 
7648 size_t drgui_text_engine_get_selected_text(drgui_text_engine* pTL, char* textOut, size_t textOutSize)
7649 {
7650  if (pTL == NULL || (textOut != NULL && textOutSize == 0)) {
7651  return 0;
7652  }
7653 
7655  return 0;
7656  }
7657 
7658 
7659  drgui_text_marker* pSelectionMarker0;
7660  drgui_text_marker* pSelectionMarker1;
7661  if (!drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
7662  return false;
7663  }
7664 
7665  size_t iSelectionChar0 = pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar;
7666  size_t iSelectionChar1 = pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar;
7667 
7668  size_t selectedTextLength = iSelectionChar1 - iSelectionChar0;
7669 
7670  if (textOut != NULL) {
7671  drgui__strncpy_s(textOut, textOutSize, pTL->text + iSelectionChar0, selectedTextLength);
7672  }
7673 
7674  return selectedTextLength;
7675 }
7676 
7678 {
7679  if (pTL == NULL || pTL->runCount == 0) {
7680  return 0;
7681  }
7682 
7683  drgui_text_marker* pSelectionMarker0;
7684  drgui_text_marker* pSelectionMarker1;
7685  if (!drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
7686  return 0;
7687  }
7688 
7689  return pTL->pRuns[pSelectionMarker0->iRun].iLine;
7690 }
7691 
7693 {
7694  if (pTL == NULL || pTL->runCount == 0) {
7695  return 0;
7696  }
7697 
7698  drgui_text_marker* pSelectionMarker0;
7699  drgui_text_marker* pSelectionMarker1;
7700  if (!drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
7701  return 0;
7702  }
7703 
7704  return pTL->pRuns[pSelectionMarker1->iRun].iLine;
7705 }
7706 
7708 {
7709  if (pTL == NULL) {
7710  return;
7711  }
7712 
7713  drgui_text_engine__move_marker_to_end_of_line_by_index(pTL, &pTL->selectionAnchor, iLine);
7714  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7715 }
7716 
7718 {
7719  if (pTL == NULL) {
7720  return;
7721  }
7722 
7723  drgui_text_engine__move_marker_to_start_of_line_by_index(pTL, &pTL->selectionAnchor, iLine);
7724  pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7725 }
7726 
7728 {
7729  if (pTL == NULL || pTL->runCount == 0) {
7730  return 0;
7731  }
7732 
7733  return pTL->pRuns[pTL->selectionAnchor.iRun].iLine;
7734 }
7735 
7736 
7737 
7739 {
7740  if (pTL == NULL) {
7741  return false;
7742  }
7743 
7744  // If we have a previously prepared state we'll need to clear it.
7745  if (pTL->preparedState.text != NULL) {
7746  free(pTL->preparedState.text);
7747  }
7748 
7749  pTL->preparedState.text = (char*)malloc(pTL->textLength + 1);
7750  drgui__strcpy_s(pTL->preparedState.text, pTL->textLength + 1, (pTL->text != NULL) ? pTL->text : "");
7751 
7752  pTL->preparedState.cursorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
7753  pTL->preparedState.selectionAnchorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->selectionAnchor);
7754  pTL->preparedState.isAnythingSelected = pTL->isAnythingSelected;
7755 
7756  return true;
7757 }
7758 
7760 {
7761  if (pTL == NULL) {
7762  return false;
7763  }
7764 
7765  // The undo point must have been prepared earlier.
7766  if (pTL->preparedState.text == NULL) {
7767  return false;
7768  }
7769 
7770 
7771  // The undo state is creating by diff-ing the prepared state and the current state.
7772  drgui_text_engine_state currentState;
7773  currentState.text = pTL->text;
7774  currentState.cursorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
7775  currentState.selectionAnchorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->selectionAnchor);
7776  currentState.isAnythingSelected = pTL->isAnythingSelected;
7777 
7778  drgui_text_engine_undo_state undoState;
7779  if (!drgui_text_engine__diff_states(&pTL->preparedState, &currentState, &undoState)) {
7780  return false;
7781  }
7782 
7783 
7784  // At this point we have the undo state ready and we just need to add it the undo stack. Before doing so, however,
7785  // we need to trim the end fo the stack.
7786  drgui_text_engine__trim_undo_stack(pTL);
7787  drgui_text_engine__push_undo_state(pTL, &undoState);
7788 
7789  return true;
7790 }
7791 
7793 {
7794  if (pTL == NULL || pTL->pUndoStack == NULL) {
7795  return false;
7796  }
7797 
7799  {
7800  drgui_text_engine_undo_state* pUndoState = pTL->pUndoStack + (pTL->iUndoState - 1);
7801  assert(pUndoState != NULL);
7802 
7803  drgui_text_engine__apply_undo_state(pTL, pUndoState);
7804  pTL->iUndoState -= 1;
7805 
7806  if (pTL->onUndoPointChanged) {
7807  pTL->onUndoPointChanged(pTL, pTL->iUndoState);
7808  }
7809 
7810  return true;
7811  }
7812 
7813  return false;
7814 }
7815 
7817 {
7818  if (pTL == NULL || pTL->pUndoStack == NULL) {
7819  return false;
7820  }
7821 
7823  {
7824  drgui_text_engine_undo_state* pUndoState = pTL->pUndoStack + pTL->iUndoState;
7825  assert(pUndoState != NULL);
7826 
7827  drgui_text_engine__apply_redo_state(pTL, pUndoState);
7828  pTL->iUndoState += 1;
7829 
7830  if (pTL->onUndoPointChanged) {
7831  pTL->onUndoPointChanged(pTL, pTL->iUndoState);
7832  }
7833 
7834  return true;
7835  }
7836 
7837  return false;
7838 }
7839 
7841 {
7842  if (pTL == NULL) {
7843  return 0;
7844  }
7845 
7846  return pTL->iUndoState;
7847 }
7848 
7850 {
7851  if (pTL == NULL) {
7852  return 0;
7853  }
7854 
7855  if (pTL->undoStackCount > 0)
7856  {
7857  assert(pTL->iUndoState <= pTL->undoStackCount);
7858  return pTL->undoStackCount - pTL->iUndoState;
7859  }
7860 
7861  return 0;
7862 }
7863 
7865 {
7866  if (pTL == NULL || pTL->pUndoStack == NULL) {
7867  return;
7868  }
7869 
7870  for (unsigned int i = 0; i < pTL->undoStackCount; ++i) {
7871  drgui_text_engine__uninit_undo_state(pTL->pUndoStack + i);
7872  }
7873 
7874  free(pTL->pUndoStack);
7875 
7876  pTL->pUndoStack = NULL;
7877  pTL->undoStackCount = 0;
7878 
7879  if (pTL->iUndoState > 0) {
7880  pTL->iUndoState = 0;
7881 
7882  if (pTL->onUndoPointChanged) {
7883  pTL->onUndoPointChanged(pTL, pTL->iUndoState);
7884  }
7885  }
7886 }
7887 
7888 
7889 
7891 {
7892  if (pTL == NULL || pTL->runCount == 0) {
7893  return 0;
7894  }
7895 
7896  return pTL->pRuns[pTL->runCount - 1].iLine + 1;
7897 }
7898 
7900 {
7901  if (pTL == NULL || pTL->runCount == 0) {
7902  return 0;
7903  }
7904 
7905  unsigned int count = 0;
7906  float lastLineBottom = 0;
7907 
7908  // First thing we do is find the first line.
7909  unsigned int iLine = 0;
7910  drgui_text_engine_line line;
7911  if (drgui_text_engine__first_line(pTL, &line))
7912  {
7913  do
7914  {
7915  if (iLine >= iFirstLine) {
7916  break;
7917  }
7918 
7919  iLine += 1;
7920  } while (drgui_text_engine__next_line(pTL, &line));
7921 
7922 
7923  // At this point we are at the first line and we need to start counting.
7924  do
7925  {
7926  if (line.posY + pTL->innerOffsetY >= pTL->containerHeight) {
7927  break;
7928  }
7929 
7930  count += 1;
7931  lastLineBottom = line.posY + line.height;
7932 
7933  } while (drgui_text_engine__next_line(pTL, &line));
7934  }
7935 
7936 
7937  // At this point there may be some empty space below the last line, in which case we use the line height of the default font to fill
7938  // out the remaining space.
7939  if (lastLineBottom + pTL->innerOffsetY < pTL->containerHeight)
7940  {
7941  drgui_font_metrics defaultFontMetrics;
7942  if (drgui_get_font_metrics(pTL->pDefaultFont, &defaultFontMetrics)) {
7943  count += (unsigned int)((pTL->containerHeight - (lastLineBottom + pTL->innerOffsetY)) / defaultFontMetrics.lineHeight);
7944  }
7945  }
7946 
7947 
7948 
7949  if (count == 0) {
7950  return 1;
7951  }
7952 
7953  return count;
7954 }
7955 
7956 float drgui_text_engine_get_line_pos_y(drgui_text_engine* pTL, size_t iLine)
7957 {
7958  drgui_rect lineRect;
7959  if (!drgui_text_engine__find_line_info_by_index(pTL, iLine, &lineRect, NULL, NULL)) {
7960  return 0;
7961  }
7962 
7963  return lineRect.top;
7964 }
7965 
7967 {
7968  if (pTL == NULL || pTL->runCount == 0) {
7969  return 0;
7970  }
7971 
7973 
7974  size_t iRun;
7975 
7976  float inputPosYRelativeToText = posY - textRect.top;
7977  if (!drgui_text_engine__find_closest_run_to_point(pTL, 0, inputPosYRelativeToText, &iRun)) {
7978  return 0;
7979  }
7980 
7981  return pTL->pRuns[iRun].iLine;
7982 }
7983 
7985 {
7986  if (pTL == NULL || pTL->runCount == 0) {
7987  return 0;
7988  }
7989 
7990  size_t firstRunIndex0;
7991  size_t lastRunIndexPlus1;
7992  if (drgui_text_engine__find_line_info_by_index(pTL, iLine, NULL, &firstRunIndex0, &lastRunIndexPlus1)) {
7993  return pTL->pRuns[firstRunIndex0].iChar;
7994  }
7995 
7996  return 0;
7997 }
7998 
8000 {
8001  if (pTL == NULL || pTL->runCount == 0) {
8002  return 0;
8003  }
8004 
8005  size_t firstRunIndex0;
8006  size_t lastRunIndexPlus1;
8007  if (drgui_text_engine__find_line_info_by_index(pTL, iLine, NULL, &firstRunIndex0, &lastRunIndexPlus1)) {
8008  size_t charEnd = pTL->pRuns[lastRunIndexPlus1 - 1].iCharEnd;
8009  if (charEnd > 0) {
8010  charEnd -= 1;
8011  }
8012 
8013  return charEnd;
8014  }
8015 
8016  return 0;
8017 }
8018 
8019 void drgui_text_engine_get_line_character_range(drgui_text_engine* pTL, size_t iLine, size_t* pCharStartOut, size_t* pCharEndOut)
8020 {
8021  if (pTL == NULL || pTL->runCount == 0) {
8022  return;
8023  }
8024 
8025  size_t charStart = 0;
8026  size_t charEnd = 0;
8027 
8028  size_t firstRunIndex0;
8029  size_t lastRunIndexPlus1;
8030  if (drgui_text_engine__find_line_info_by_index(pTL, iLine, NULL, &firstRunIndex0, &lastRunIndexPlus1)) {
8031  charStart = pTL->pRuns[firstRunIndex0].iChar;
8032  charEnd = pTL->pRuns[lastRunIndexPlus1 - 1].iCharEnd;
8033  if (charEnd > 0) {
8034  charEnd -= 1;
8035  }
8036  }
8037 
8038  if (pCharStartOut) {
8039  *pCharStartOut = charStart;
8040  }
8041  if (pCharEndOut) {
8042  *pCharEndOut = charEnd;
8043  }
8044 }
8045 
8046 
8048 {
8049  if (pTL == NULL) {
8050  return;
8051  }
8052 
8053  pTL->onPaintText = proc;
8054 }
8055 
8057 {
8058  if (pTL == NULL) {
8059  return;
8060  }
8061 
8062  pTL->onPaintRect = proc;
8063 }
8064 
8065 void drgui_text_engine_paint(drgui_text_engine* pTL, drgui_rect rect, drgui_element* pElement, void* pPaintData)
8066 {
8067  if (pTL == NULL || pTL->onPaintText == NULL || pTL->onPaintRect == NULL) {
8068  return;
8069  }
8070 
8071  if (rect.left < 0) {
8072  rect.left = 0;
8073  }
8074  if (rect.top < 0) {
8075  rect.top = 0;
8076  }
8077  if (rect.right > pTL->containerWidth) {
8078  rect.right = pTL->containerWidth;
8079  }
8080  if (rect.bottom > pTL->containerHeight) {
8081  rect.bottom = pTL->containerHeight;
8082  }
8083 
8084  if (rect.right <= rect.left || rect.bottom <= rect.top) {
8085  return;
8086  }
8087 
8088 
8089  // The position of each run will be relative to the text bounds. We want to make it relative to the container bounds.
8091 
8092  // We draw a rectangle above and below the text rectangle. The main text rectangle will be drawn by iterating over each visible run.
8093  drgui_rect rectTop = drgui_make_rect(0, 0, pTL->containerWidth, textRect.top);
8094  drgui_rect rectBottom = drgui_make_rect(0, textRect.bottom, pTL->containerWidth, pTL->containerHeight);
8095 
8096  if (rectTop.bottom > rect.top) {
8097  pTL->onPaintRect(pTL, rectTop, pTL->defaultBackgroundColor, pElement, pPaintData);
8098  }
8099 
8100  if (rectBottom.top < rect.bottom) {
8101  pTL->onPaintRect(pTL, rectBottom, pTL->defaultBackgroundColor, pElement, pPaintData);
8102  }
8103 
8104 
8105  // We draw line-by-line, starting from the first visible line.
8106  drgui_text_engine_line line;
8107  if (drgui_text_engine__first_line(pTL, &line))
8108  {
8109  do
8110  {
8111  float lineTop = line.posY + textRect.top;
8112  float lineBottom = lineTop + line.height;
8113 
8114  if (lineTop < rect.bottom)
8115  {
8116  if (lineBottom > rect.top)
8117  {
8118  // The line is visible. We draw in 3 main parts - 1) the blank space to the left of the first run; 2) the runs themselves; 3) the blank
8119  // space to the right of the last run.
8120 
8121  drgui_color bgcolor = pTL->defaultBackgroundColor;
8122  if (line.index == drgui_text_engine_get_cursor_line(pTL)) {
8123  bgcolor = pTL->lineBackgroundColor;
8124  }
8125 
8126  float lineSelectionOverhangLeft = 0;
8127  float lineSelectionOverhangRight = 0;
8128 
8130  {
8131  drgui_text_marker* pSelectionMarker0 = &pTL->selectionAnchor;
8132  drgui_text_marker* pSelectionMarker1 = &pTL->cursor;
8133  if (pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar > pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar)
8134  {
8135  drgui_text_marker* temp = pSelectionMarker0;
8136  pSelectionMarker0 = pSelectionMarker1;
8137  pSelectionMarker1 = temp;
8138  }
8139 
8140  size_t iSelectionLine0 = pTL->pRuns[pSelectionMarker0->iRun].iLine;
8141  size_t iSelectionLine1 = pTL->pRuns[pSelectionMarker1->iRun].iLine;
8142 
8143  if (line.index >= iSelectionLine0 && line.index < iSelectionLine1)
8144  {
8145  drgui_font_metrics defaultFontMetrics;
8146  drgui_get_font_metrics(pTL->pDefaultFont, &defaultFontMetrics);
8147 
8148  if (pTL->horzAlign == drgui_text_engine_alignment_right)
8149  {
8150  if (line.index > iSelectionLine0) {
8151  lineSelectionOverhangLeft = (float)defaultFontMetrics.spaceWidth;
8152  }
8153  }
8154  else if (pTL->horzAlign == drgui_text_engine_alignment_center)
8155  {
8156  lineSelectionOverhangRight = (float)defaultFontMetrics.spaceWidth;
8157 
8158  if (line.index > iSelectionLine0) {
8159  lineSelectionOverhangLeft = (float)defaultFontMetrics.spaceWidth;
8160  }
8161  }
8162  else
8163  {
8164  lineSelectionOverhangRight = (float)defaultFontMetrics.spaceWidth;
8165  }
8166  }
8167  }
8168 
8169 
8170  drgui_text_run* pFirstRun = pTL->pRuns + line.iFirstRun;
8171  drgui_text_run* pLastRun = pTL->pRuns + line.iLastRun;
8172 
8173  float lineLeft = pFirstRun->posX + textRect.left;
8174  float lineRight = pLastRun->posX + pLastRun->width + textRect.left;
8175 
8176  // 1) The blank space to the left of the first run.
8177  if (lineLeft > 0)
8178  {
8179  if (lineSelectionOverhangLeft > 0) {
8180  pTL->onPaintRect(pTL, drgui_make_rect(lineLeft - lineSelectionOverhangLeft, lineTop, lineLeft, lineBottom), pTL->selectionBackgroundColor, pElement, pPaintData);
8181  }
8182 
8183  pTL->onPaintRect(pTL, drgui_make_rect(0, lineTop, lineLeft - lineSelectionOverhangLeft, lineBottom), bgcolor, pElement, pPaintData);
8184  }
8185 
8186 
8187  // 2) The runs themselves.
8188  for (size_t iRun = line.iFirstRun; iRun <= line.iLastRun; ++iRun)
8189  {
8190  drgui_text_run* pRun = pTL->pRuns + iRun;
8191 
8192  float runLeft = pRun->posX + textRect.left;
8193  float runRight = runLeft + pRun->width;
8194 
8195  if (runRight > 0 && runLeft < pTL->containerWidth)
8196  {
8197  // The run is visible.
8198  if (!drgui_text_engine__is_text_run_whitespace(pTL, pRun) || pTL->text[pRun->iChar] == '\t')
8199  {
8200  drgui_text_run run = pTL->pRuns[iRun];
8201  run.pFont = pTL->pDefaultFont;
8202  run.textColor = pTL->defaultTextColor;
8203  run.backgroundColor = bgcolor;
8204  run.text = pTL->text + run.iChar;
8205  run.posX = runLeft;
8206  run.posY = lineTop;
8207 
8208  // We paint the run differently depending on whether or not anything is selected. If something is selected
8209  // we need to split the run into a maximum of 3 sub-runs so that the selection rectangle can be drawn correctly.
8211  {
8212  drgui_text_run subruns[3];
8213  size_t subrunCount = drgui_text_engine__split_text_run_by_selection(pTL, &run, subruns);
8214  for (size_t iSubRun = 0; iSubRun < subrunCount; ++iSubRun)
8215  {
8216  drgui_text_run* pSubRun = subruns + iSubRun;
8217 
8218  if (!drgui_text_engine__is_text_run_whitespace(pTL, pRun)) {
8219  pTL->onPaintText(pTL, pSubRun, pElement, pPaintData);
8220  } else {
8221  pTL->onPaintRect(pTL, drgui_make_rect(pSubRun->posX, lineTop, pSubRun->posX + pSubRun->width, lineBottom), pSubRun->backgroundColor, pElement, pPaintData);
8222  }
8223  }
8224  }
8225  else
8226  {
8227  // Nothing is selected.
8228  if (!drgui_text_engine__is_text_run_whitespace(pTL, &run)) {
8229  pTL->onPaintText(pTL, &run, pElement, pPaintData);
8230  } else {
8231  pTL->onPaintRect(pTL, drgui_make_rect(run.posX, lineTop, run.posX + run.width, lineBottom), run.backgroundColor, pElement, pPaintData);
8232  }
8233  }
8234  }
8235  }
8236  }
8237 
8238 
8239  // 3) The blank space to the right of the last run.
8240  if (lineRight < pTL->containerWidth)
8241  {
8242  if (lineSelectionOverhangRight > 0) {
8243  pTL->onPaintRect(pTL, drgui_make_rect(lineRight, lineTop, lineRight + lineSelectionOverhangRight, lineBottom), pTL->selectionBackgroundColor, pElement, pPaintData);
8244  }
8245 
8246  pTL->onPaintRect(pTL, drgui_make_rect(lineRight + lineSelectionOverhangRight, lineTop, pTL->containerWidth, lineBottom), bgcolor, pElement, pPaintData);
8247  }
8248  }
8249  }
8250  else
8251  {
8252  // The line is below the rectangle which means no other line will be visible and we can terminate early.
8253  break;
8254  }
8255 
8256  } while (drgui_text_engine__next_line(pTL, &line));
8257  }
8258  else
8259  {
8260  // There are no lines so we do a simplified branch here.
8261  float lineTop = textRect.top;
8262  float lineBottom = textRect.bottom;
8263  pTL->onPaintRect(pTL, drgui_make_rect(0, lineTop, pTL->containerWidth, lineBottom), pTL->lineBackgroundColor, pElement, pPaintData);
8264  }
8265 
8266  // The cursor.
8267  if (pTL->isShowingCursor && pTL->isCursorBlinkOn) {
8268  pTL->onPaintRect(pTL, drgui_text_engine_get_cursor_rect(pTL), pTL->cursorColor, pElement, pPaintData);
8269  }
8270 }
8271 
8272 
8273 void drgui_text_engine_step(drgui_text_engine* pTL, unsigned int milliseconds)
8274 {
8275  if (pTL == NULL || milliseconds == 0) {
8276  return;
8277  }
8278 
8279  if (pTL->timeToNextCursorBlink < milliseconds)
8280  {
8281  pTL->isCursorBlinkOn = !pTL->isCursorBlinkOn;
8282  pTL->timeToNextCursorBlink = pTL->cursorBlinkRate;
8283 
8284  drgui_text_engine__on_dirty(pTL, drgui_text_engine_get_cursor_rect(pTL));
8285  }
8286  else
8287  {
8288  pTL->timeToNextCursorBlink -= milliseconds;
8289  }
8290 }
8291 
8292 
8293 
8294 void drgui_text_engine_paint_line_numbers(drgui_text_engine* pTL, float lineNumbersWidth, float lineNumbersHeight, drgui_color textColor, drgui_color backgroundColor, drgui_text_engine_on_paint_text_proc onPaintText, drgui_text_engine_on_paint_rect_proc onPaintRect, drgui_element* pElement, void* pPaintData)
8295 {
8296  if (pTL == NULL || onPaintText == NULL || onPaintRect == NULL) {
8297  return;
8298  }
8299 
8300 
8301  // The position of each run will be relative to the text bounds. We want to make it relative to the container bounds.
8303 
8304  // We draw a rectangle above and below the text rectangle. The main text rectangle will be drawn by iterating over each visible run.
8305  drgui_rect rectTop = drgui_make_rect(0, 0, lineNumbersWidth, textRect.top);
8306  drgui_rect rectBottom = drgui_make_rect(0, textRect.bottom, lineNumbersWidth, lineNumbersHeight);
8307 
8308  if (pTL->onPaintRect)
8309  {
8310  if (rectTop.bottom > 0) {
8311  onPaintRect(pTL, rectTop, backgroundColor, pElement, pPaintData);
8312  }
8313 
8314  if (rectBottom.top < lineNumbersHeight) {
8315  onPaintRect(pTL, rectBottom, backgroundColor, pElement, pPaintData);
8316  }
8317  }
8318 
8319 
8320  // Now we draw each line.
8321  int iLine = 1;
8322  drgui_text_engine_line line;
8323  if (!drgui_text_engine__first_line(pTL, &line))
8324  {
8325  // We failed to retrieve the first line which is probably due to the text engine being empty. We just fake the first line to
8326  // ensure we get the number 1 to be drawn.
8327  drgui_font_metrics fontMetrics;
8328  drgui_get_font_metrics(pTL->pDefaultFont, &fontMetrics);
8329 
8330  line.height = (float)fontMetrics.lineHeight;
8331  line.posY = 0;
8332  }
8333 
8334  do
8335  {
8336  float lineTop = line.posY + textRect.top;
8337  float lineBottom = lineTop + line.height;
8338 
8339  if (lineTop < lineNumbersHeight)
8340  {
8341  if (lineBottom > 0)
8342  {
8343  char iLineStr[64];
8344  #ifdef _MSC_VER
8345  _itoa_s(iLine, iLineStr, sizeof(iLineStr), 10);
8346  #else
8347  snprintf(iLineStr, sizeof(iLineStr), "%d", iLine);
8348  #endif
8349 
8350  drgui_font* pFont = pTL->pDefaultFont;
8351 
8352  float textWidth;
8353  float textHeight;
8354  drgui_measure_string(pFont, iLineStr, strlen(iLineStr), &textWidth, &textHeight);
8355 
8356  drgui_text_run run = {0};
8357  run.pFont = pFont;
8358  run.textColor = textColor;
8359  run.backgroundColor = backgroundColor;
8360  run.text = iLineStr;
8361  run.textLength = strlen(iLineStr);
8362  run.posX = lineNumbersWidth - textWidth;
8363  run.posY = lineTop;
8364  onPaintText(pTL, &run, pElement, pPaintData);
8365  onPaintRect(pTL, drgui_make_rect(0, lineTop, run.posX, lineBottom), run.backgroundColor, pElement, pPaintData);
8366  }
8367  }
8368  else
8369  {
8370  // The line is below the rectangle which means no other line will be visible and we can terminate early.
8371  break;
8372  }
8373 
8374  iLine += 1;
8375  } while (drgui_text_engine__next_line(pTL, &line));
8376 }
8377 
8378 
8379 bool drgui_text_engine_find_next(drgui_text_engine* pTL, const char* text, size_t* pSelectionStartOut, size_t* pSelectionEndOut)
8380 {
8381  if (pTL == NULL || pTL->text == NULL || text == NULL || text[0] == '\0') {
8382  return false;
8383  }
8384 
8385  size_t cursorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
8386  char* nextOccurance = strstr(pTL->text + cursorPos, text);
8387  if (nextOccurance == NULL) {
8388  nextOccurance = strstr(pTL->text, text);
8389  }
8390 
8391  if (nextOccurance == NULL) {
8392  return false;
8393  }
8394 
8395  if (pSelectionStartOut) {
8396  *pSelectionStartOut = nextOccurance - pTL->text;
8397  }
8398  if (pSelectionEndOut) {
8399  *pSelectionEndOut = (nextOccurance - pTL->text) + strlen(text);
8400  }
8401 
8402  return true;
8403 }
8404 
8405 bool drgui_text_engine_find_next_no_loop(drgui_text_engine* pTL, const char* text, size_t* pSelectionStartOut, size_t* pSelectionEndOut)
8406 {
8407  if (pTL == NULL || pTL->text == NULL || text == NULL || text[0] == '\0') {
8408  return false;
8409  }
8410 
8411  size_t cursorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
8412 
8413  char* nextOccurance = strstr(pTL->text + cursorPos, text);
8414  if (nextOccurance == NULL) {
8415  return false;
8416  }
8417 
8418  if (pSelectionStartOut) {
8419  *pSelectionStartOut = nextOccurance - pTL->text;
8420  }
8421  if (pSelectionEndOut) {
8422  *pSelectionEndOut = (nextOccurance - pTL->text) + strlen(text);
8423  }
8424 
8425  return true;
8426 }
8427 
8428 
8429 
8430 
8431 DRGUI_PRIVATE bool drgui_next_run_string(const char* runStart, const char* textEndPastNullTerminator, const char** pRunEndOut)
8432 {
8433  assert(runStart <= textEndPastNullTerminator);
8434 
8435  if (runStart == NULL || runStart == textEndPastNullTerminator)
8436  {
8437  // String is empty.
8438  return false;
8439  }
8440 
8441 
8442  char firstChar = runStart[0];
8443  if (firstChar == '\t')
8444  {
8445  // We loop until we hit anything that is not a tab character (tabs will be grouped together into a single run).
8446  do
8447  {
8448  runStart += 1;
8449  *pRunEndOut = runStart;
8450  } while (runStart[0] != '\0' && runStart[0] == '\t');
8451  }
8452  else if (firstChar == '\n')
8453  {
8454  runStart += 1;
8455  *pRunEndOut = runStart;
8456  }
8457  else if (firstChar == '\0')
8458  {
8459  assert(runStart + 1 == textEndPastNullTerminator);
8460  *pRunEndOut = textEndPastNullTerminator;
8461  }
8462  else
8463  {
8464  do
8465  {
8466  runStart += 1;
8467  *pRunEndOut = runStart;
8468  } while (runStart[0] != '\0' && runStart[0] != '\t' && runStart[0] != '\n');
8469  }
8470 
8471  return true;
8472 }
8473 
8474 DRGUI_PRIVATE void drgui_text_engine__refresh(drgui_text_engine* pTL)
8475 {
8476  if (pTL == NULL) {
8477  return;
8478  }
8479 
8480  // We split the runs based on tabs and new-lines. We want to create runs for tabs and new-line characters as well because we want
8481  // to have the entire string covered by runs for the sake of simplicity when it comes to editing.
8482  //
8483  // The first pass positions the runs based on a top-to-bottom, left-to-right alignment. The second pass then repositions the runs
8484  // based on alignment.
8485 
8486  // Runs need to be cleared first.
8487  drgui_text_engine__clear_text_runs(pTL);
8488 
8489  // The text bounds also need to be reset at the top.
8490  pTL->textBoundsWidth = 0;
8491  pTL->textBoundsHeight = 0;
8492 
8493  drgui_font_metrics defaultFontMetrics;
8494  drgui_get_font_metrics(pTL->pDefaultFont, &defaultFontMetrics);
8495 
8496  pTL->textBoundsHeight = (float)defaultFontMetrics.lineHeight;
8497 
8498  const float tabWidth = drgui_text_engine__get_tab_width(pTL);
8499 
8500  size_t iCurrentLine = 0;
8501  float runningPosY = 0;
8502  float runningLineHeight = 0;
8503 
8504  const char* nextRunStart = pTL->text;
8505  const char* nextRunEnd;
8506  while (drgui_next_run_string(nextRunStart, pTL->text + pTL->textLength + 1, OUT &nextRunEnd))
8507  {
8509  run.iLine = iCurrentLine;
8510  run.iChar = nextRunStart - pTL->text;
8511  run.iCharEnd = nextRunEnd - pTL->text;
8512  run.textLength = nextRunEnd - nextRunStart;
8513  run.width = 0;
8514  run.height = 0;
8515  run.posX = 0;
8516  run.posY = runningPosY;
8517  run.pFont = pTL->pDefaultFont;
8518 
8519  // X position
8520  //
8521  // The x position depends on the previous run that's on the same line.
8522  if (pTL->runCount > 0)
8523  {
8524  drgui_text_run* pPrevRun = pTL->pRuns + (pTL->runCount - 1);
8525  if (pPrevRun->iLine == iCurrentLine)
8526  {
8527  run.posX = pPrevRun->posX + pPrevRun->width;
8528  }
8529  else
8530  {
8531  // It's the first run on the line.
8532  run.posX = 0;
8533  }
8534  }
8535 
8536 
8537  // Width and height.
8538  assert(nextRunEnd > nextRunStart);
8539  if (nextRunStart[0] == '\t')
8540  {
8541  // Tab.
8542  size_t tabCount = run.iCharEnd - run.iChar;
8543  run.width = (float)(((tabCount*(size_t)tabWidth) - ((size_t)run.posX % (size_t)tabWidth)));
8544  run.height = (float)defaultFontMetrics.lineHeight;
8545  }
8546  else if (nextRunStart[0] == '\n')
8547  {
8548  // New line.
8549  iCurrentLine += 1;
8550  run.width = 0;
8551  run.height = (float)defaultFontMetrics.lineHeight;
8552  }
8553  else if (nextRunStart[0] == '\0')
8554  {
8555  // Null terminator.
8556  run.width = 0;
8557  run.height = (float)defaultFontMetrics.lineHeight;
8558  run.textLength = 0;
8559  }
8560  else
8561  {
8562  // Normal run.
8563  drgui_measure_string(pTL->pDefaultFont, nextRunStart, run.textLength, &run.width, &run.height);
8564  }
8565 
8566 
8567  // The running line height needs to be updated by setting to the maximum size.
8568  runningLineHeight = (run.height > runningLineHeight) ? run.height : runningLineHeight;
8569 
8570 
8571  // Update the text bounds.
8572  if (pTL->textBoundsWidth < run.posX + run.width) {
8573  pTL->textBoundsWidth = run.posX + run.width;
8574  }
8575  pTL->textBoundsHeight = runningPosY + runningLineHeight;
8576 
8577 
8578  // A new line means we need to increment the running y position by the running line height.
8579  if (nextRunStart[0] == '\n')
8580  {
8581  runningPosY += runningLineHeight;
8582  runningLineHeight = 0;
8583  }
8584 
8585  // Add the run to the internal list.
8586  drgui_text_engine__push_text_run(pTL, &run);
8587 
8588  // Go to the next run string.
8589  nextRunStart = nextRunEnd;
8590  }
8591 
8592  // If we were to return now the text would be alignment top/left. If the alignment is not top/left we need to refresh the layout.
8593  if (pTL->horzAlign != drgui_text_engine_alignment_left || pTL->vertAlign != drgui_text_engine_alignment_top)
8594  {
8595  drgui_text_engine__refresh_alignment(pTL);
8596  }
8597 }
8598 
8599 DRGUI_PRIVATE void drgui_text_engine__refresh_alignment(drgui_text_engine* pTL)
8600 {
8601  if (pTL == NULL) {
8602  return;
8603  }
8604 
8605  float runningPosY = 0;
8606 
8607  unsigned int iCurrentLine = 0;
8608  for (size_t iRun = 0; iRun < pTL->runCount; /* Do Nothing*/) // iRun is incremented from within the loop.
8609  {
8610  float lineWidth = 0;
8611  float lineHeight = 0;
8612 
8613  // This loop does a few things. First, it defines the end point for the loop after this one (jRun). Second, it calculates
8614  // the line width which is needed for center and right alignment. Third it resets the position of each run to their
8615  // unaligned equivalents which will be offsetted in the second loop.
8616  size_t jRun;
8617  for (jRun = iRun; jRun < pTL->runCount && pTL->pRuns[jRun].iLine == iCurrentLine; ++jRun)
8618  {
8619  drgui_text_run* pRun = pTL->pRuns + jRun;
8620  pRun->posX = lineWidth;
8621  pRun->posY = runningPosY;
8622 
8623  lineWidth += pRun->width;
8624  lineHeight = (lineHeight > pRun->height) ? lineHeight : pRun->height;
8625  }
8626 
8627 
8628  // The actual alignment is done here.
8629  float lineOffsetX;
8630  float lineOffsetY;
8631  drgui_text_engine__calculate_line_alignment_offset(pTL, lineWidth, OUT &lineOffsetX, OUT &lineOffsetY);
8632 
8633  for (/* Do Nothing*/; iRun < jRun; ++iRun)
8634  {
8635  drgui_text_run* pRun = pTL->pRuns + iRun;
8636  pRun->posX += lineOffsetX;
8637  pRun->posY += lineOffsetY;
8638  }
8639 
8640 
8641  // Go to the next line.
8642  iCurrentLine += 1;
8643  runningPosY += lineHeight;
8644  }
8645 }
8646 
8647 void drgui_text_engine__calculate_line_alignment_offset(drgui_text_engine* pTL, float lineWidth, float* pOffsetXOut, float* pOffsetYOut)
8648 {
8649  if (pTL == NULL) {
8650  return;
8651  }
8652 
8653  float offsetX = 0;
8654  float offsetY = 0;
8655 
8656  switch (pTL->horzAlign)
8657  {
8659  {
8660  offsetX = pTL->textBoundsWidth - lineWidth;
8661  break;
8662  }
8663 
8665  {
8666  offsetX = (pTL->textBoundsWidth - lineWidth) / 2;
8667  break;
8668  }
8669 
8671  case drgui_text_engine_alignment_top: // Invalid for horizontal alignment.
8672  case drgui_text_engine_alignment_bottom: // Invalid for horizontal alignment.
8673  default:
8674  {
8675  offsetX = 0;
8676  break;
8677  }
8678  }
8679 
8680 
8681  switch (pTL->vertAlign)
8682  {
8684  {
8685  offsetY = pTL->textBoundsHeight - pTL->textBoundsHeight;
8686  break;
8687  }
8688 
8690  {
8691  offsetY = (pTL->textBoundsHeight - pTL->textBoundsHeight) / 2;
8692  break;
8693  }
8694 
8696  case drgui_text_engine_alignment_left: // Invalid for vertical alignment.
8697  case drgui_text_engine_alignment_right: // Invalid for vertical alignment.
8698  default:
8699  {
8700  offsetY = 0;
8701  break;
8702  }
8703  }
8704 
8705 
8706  if (pOffsetXOut) {
8707  *pOffsetXOut = offsetX;
8708  }
8709  if (pOffsetYOut) {
8710  *pOffsetYOut = offsetY;
8711  }
8712 }
8713 
8714 
8715 DRGUI_PRIVATE void drgui_text_engine__push_text_run(drgui_text_engine* pTL, drgui_text_run* pRun)
8716 {
8717  if (pTL == NULL) {
8718  return;
8719  }
8720 
8721  if (pTL->runBufferSize == pTL->runCount)
8722  {
8723  pTL->runBufferSize = pTL->runBufferSize*2;
8724  if (pTL->runBufferSize == 0) {
8725  pTL->runBufferSize = 1;
8726  }
8727 
8728  pTL->pRuns = (drgui_text_run*)realloc(pTL->pRuns, sizeof(drgui_text_run) * pTL->runBufferSize);
8729  if (pTL->pRuns == NULL) {
8730  pTL->runCount = 0;
8731  pTL->runBufferSize = 0;
8732  return;
8733  }
8734  }
8735 
8736  pTL->pRuns[pTL->runCount] = *pRun;
8737  pTL->runCount += 1;
8738 }
8739 
8740 DRGUI_PRIVATE void drgui_text_engine__clear_text_runs(drgui_text_engine* pTL)
8741 {
8742  if (pTL == NULL) {
8743  return;
8744  }
8745 
8746  pTL->runCount = 0;
8747 }
8748 
8749 DRGUI_PRIVATE bool drgui_text_engine__is_text_run_whitespace(drgui_text_engine* pTL, drgui_text_run* pRun)
8750 {
8751  if (pRun == NULL) {
8752  return false;
8753  }
8754 
8755  if (pTL->text[pRun->iChar] != '\t' && pTL->text[pRun->iChar] != '\n')
8756  {
8757  return false;
8758  }
8759 
8760  return true;
8761 }
8762 
8763 DRGUI_PRIVATE float drgui_text_engine__get_tab_width(drgui_text_engine* pTL)
8764 {
8765  drgui_font_metrics defaultFontMetrics;
8766  drgui_get_font_metrics(pTL->pDefaultFont, &defaultFontMetrics);
8767 
8768  return (float)(defaultFontMetrics.spaceWidth * pTL->tabSizeInSpaces);
8769 }
8770 
8771 
8772 DRGUI_PRIVATE bool drgui_text_engine__find_closest_line_to_point(drgui_text_engine* pTL, float inputPosYRelativeToText, size_t* pFirstRunIndexOnLineOut, size_t* pLastRunIndexOnLinePlus1Out)
8773 {
8774  size_t iFirstRunOnLine = 0;
8775  size_t iLastRunOnLinePlus1 = 0;
8776 
8777  bool result = true;
8778  if (pTL == NULL || pTL->runCount == 0)
8779  {
8780  result = false;
8781  }
8782  else
8783  {
8784  float runningLineTop = 0;
8785 
8786  float lineHeight;
8787  while (drgui_text_engine__find_line_info(pTL, iFirstRunOnLine, OUT &iLastRunOnLinePlus1, OUT &lineHeight))
8788  {
8789  const float lineTop = runningLineTop;
8790  const float lineBottom = lineTop + lineHeight;
8791 
8792  if (inputPosYRelativeToText < lineBottom)
8793  {
8794  // It's on this line.
8795  break;
8796  }
8797  else
8798  {
8799  // It's not on this line - go to the next one, unless we're already on the last line.
8800  if (iLastRunOnLinePlus1 == pTL->runCount) {
8801  break;
8802  }
8803 
8804  iFirstRunOnLine = iLastRunOnLinePlus1;
8805  runningLineTop = lineBottom;
8806  }
8807  }
8808  }
8809 
8810  if (pFirstRunIndexOnLineOut) {
8811  *pFirstRunIndexOnLineOut = iFirstRunOnLine;
8812  }
8813  if (pLastRunIndexOnLinePlus1Out) {
8814  *pLastRunIndexOnLinePlus1Out = iLastRunOnLinePlus1;
8815  }
8816 
8817  return result;
8818 }
8819 
8820 DRGUI_PRIVATE bool drgui_text_engine__find_closest_run_to_point(drgui_text_engine* pTL, float inputPosXRelativeToText, float inputPosYRelativeToText, size_t* pRunIndexOut)
8821 {
8822  if (pTL == NULL) {
8823  return false;
8824  }
8825 
8826  size_t iFirstRunOnLine;
8827  size_t iLastRunOnLinePlus1;
8828  if (drgui_text_engine__find_closest_line_to_point(pTL, inputPosYRelativeToText, OUT &iFirstRunOnLine, OUT &iLastRunOnLinePlus1))
8829  {
8830  size_t iRunOut = 0;
8831 
8832  const drgui_text_run* pFirstRunOnLine = pTL->pRuns + iFirstRunOnLine;
8833  const drgui_text_run* pLastRunOnLine = pTL->pRuns + (iLastRunOnLinePlus1 - 1);
8834 
8835  if (inputPosXRelativeToText < pFirstRunOnLine->posX)
8836  {
8837  // It's to the left of the first run.
8838  iRunOut = iFirstRunOnLine;
8839  }
8840  else if (inputPosXRelativeToText > pLastRunOnLine->posX + pLastRunOnLine->width)
8841  {
8842  // It's to the right of the last run.
8843  iRunOut = iLastRunOnLinePlus1 - 1;
8844  }
8845  else
8846  {
8847  // It's in the middle of the line. We just iterate over each run on the line and return the first one that contains the point.
8848  for (size_t iRun = iFirstRunOnLine; iRun < iLastRunOnLinePlus1; ++iRun)
8849  {
8850  const drgui_text_run* pRun = pTL->pRuns + iRun;
8851  iRunOut = iRun;
8852 
8853  if (inputPosXRelativeToText >= pRun->posX && inputPosXRelativeToText <= pRun->posX + pRun->width) {
8854  break;
8855  }
8856  }
8857  }
8858 
8859  if (pRunIndexOut) {
8860  *pRunIndexOut = iRunOut;
8861  }
8862 
8863  return true;
8864  }
8865  else
8866  {
8867  // There was an error finding the closest line.
8868  return false;
8869  }
8870 }
8871 
8872 DRGUI_PRIVATE bool drgui_text_engine__find_line_info(drgui_text_engine* pTL, size_t iFirstRunOnLine, size_t* pLastRunIndexOnLinePlus1Out, float* pLineHeightOut)
8873 {
8874  if (pTL == NULL) {
8875  return false;
8876  }
8877 
8878  if (iFirstRunOnLine < pTL->runCount)
8879  {
8880  const size_t iLine = pTL->pRuns[iFirstRunOnLine].iLine;
8881  float lineHeight = 0;
8882 
8883  size_t iRun;
8884  for (iRun = iFirstRunOnLine; iRun < pTL->runCount && pTL->pRuns[iRun].iLine == iLine; ++iRun)
8885  {
8886  if (lineHeight < pTL->pRuns[iRun].height) {
8887  lineHeight = pTL->pRuns[iRun].height;
8888  }
8889  }
8890 
8891  assert(iRun > iFirstRunOnLine);
8892 
8893  if (pLastRunIndexOnLinePlus1Out) {
8894  *pLastRunIndexOnLinePlus1Out = iRun;
8895  }
8896  if (pLineHeightOut) {
8897  *pLineHeightOut = lineHeight;
8898  }
8899 
8900  return true;
8901  }
8902 
8903  return false;
8904 }
8905 
8906 DRGUI_PRIVATE bool drgui_text_engine__find_line_info_by_index(drgui_text_engine* pTL, size_t iLine, drgui_rect* pRectOut, size_t* pFirstRunIndexOut, size_t* pLastRunIndexPlus1Out)
8907 {
8908  if (pTL == NULL) {
8909  return false;
8910  }
8911 
8912  size_t iFirstRunOnLine = 0;
8913  size_t iLastRunOnLinePlus1 = 0;
8914 
8915  float lineTop = 0;
8916  float lineHeight = 0;
8917 
8918  for (size_t iCurrentLine = 0; iCurrentLine <= iLine; ++iCurrentLine)
8919  {
8920  iFirstRunOnLine = iLastRunOnLinePlus1;
8921  lineTop += lineHeight;
8922 
8923  if (!drgui_text_engine__find_line_info(pTL, iFirstRunOnLine, &iLastRunOnLinePlus1, &lineHeight))
8924  {
8925  // There was an error retrieving information about the line.
8926  return false;
8927  }
8928  }
8929 
8930 
8931  // At this point we have the first and last runs that make up the line and we can generate our output.
8932  if (iLastRunOnLinePlus1 > iFirstRunOnLine)
8933  {
8934  if (pFirstRunIndexOut) {
8935  *pFirstRunIndexOut = iFirstRunOnLine;
8936  }
8937  if (pLastRunIndexPlus1Out) {
8938  *pLastRunIndexPlus1Out = iLastRunOnLinePlus1;
8939  }
8940 
8941  if (pRectOut != NULL)
8942  {
8943  pRectOut->left = pTL->pRuns[iFirstRunOnLine].posX;
8944  pRectOut->right = pTL->pRuns[iLastRunOnLinePlus1 - 1].posX + pTL->pRuns[iLastRunOnLinePlus1 - 1].width;
8945  pRectOut->top = lineTop;
8946  pRectOut->bottom = pRectOut->top + lineHeight;
8947  }
8948 
8949  return true;
8950  }
8951  else
8952  {
8953  // We couldn't find any runs.
8954  return false;
8955  }
8956 }
8957 
8958 DRGUI_PRIVATE bool drgui_text_engine__find_last_run_on_line_starting_from_run(drgui_text_engine* pTL, size_t iRun, size_t* pLastRunIndexOnLineOut)
8959 {
8960  if (pTL == NULL || pTL->pRuns == NULL) {
8961  return false;
8962  }
8963 
8964  size_t result = iRun;
8965 
8966  size_t iLine = pTL->pRuns[iRun].iLine;
8967  for (/* Do Nothing*/; iRun < pTL->runCount && pTL->pRuns[iRun].iLine == iLine; ++iRun)
8968  {
8969  result = iRun;
8970  }
8971 
8972  if (pLastRunIndexOnLineOut) {
8973  *pLastRunIndexOnLineOut = result;
8974  }
8975 
8976  return true;
8977 }
8978 
8979 DRGUI_PRIVATE bool drgui_text_engine__find_first_run_on_line_starting_from_run(drgui_text_engine* pTL, size_t iRun, size_t* pFirstRunIndexOnLineOut)
8980 {
8981  if (pTL == NULL) {
8982  return false;
8983  }
8984 
8985  size_t result = iRun;
8986 
8987  size_t iLine = pTL->pRuns[iRun].iLine;
8988  for (/* Do Nothing*/; iRun > 0 && pTL->pRuns[iRun - 1].iLine == iLine; --iRun)
8989  {
8990  result = iRun - 1;
8991  }
8992 
8993  if (pFirstRunIndexOnLineOut) {
8994  *pFirstRunIndexOnLineOut = result;
8995  }
8996 
8997  return true;
8998 }
8999 
9000 DRGUI_PRIVATE bool drgui_text_engine__find_run_at_character(drgui_text_engine* pTL, size_t iChar, size_t* pRunIndexOut)
9001 {
9002  if (pTL == NULL || pTL->runCount == 0) {
9003  return false;
9004  }
9005 
9006  size_t result = 0;
9007  if (iChar < pTL->textLength)
9008  {
9009  for (size_t iRun = 0; iRun < pTL->runCount; ++iRun)
9010  {
9011  const drgui_text_run* pRun = pTL->pRuns + iRun;
9012 
9013  if (iChar < pRun->iCharEnd)
9014  {
9015  result = iRun;
9016  break;
9017  }
9018  }
9019  }
9020  else
9021  {
9022  // The character index is too high. Return the last run.
9023  result = pTL->runCount - 1;
9024  }
9025 
9026  if (pRunIndexOut) {
9027  *pRunIndexOut = result;
9028  }
9029 
9030  return true;
9031 }
9032 
9033 
9034 DRGUI_PRIVATE drgui_text_marker drgui_text_engine__new_marker()
9035 {
9036  drgui_text_marker marker;
9037  marker.iRun = 0;
9038  marker.iChar = 0;
9039  marker.relativePosX = 0;
9040  marker.absoluteSickyPosX = 0;
9041 
9042  return marker;
9043 }
9044 
9045 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_point_relative_to_container(drgui_text_engine* pTL, drgui_text_marker* pMarker, float inputPosX, float inputPosY)
9046 {
9047  if (pTL == NULL || pMarker == NULL) {
9048  return false;
9049  }
9050 
9051  pMarker->iRun = 0;
9052  pMarker->iChar = 0;
9053  pMarker->relativePosX = 0;
9054  pMarker->absoluteSickyPosX = 0;
9055 
9057 
9058  float inputPosXRelativeToText = inputPosX - textRect.left;
9059  float inputPosYRelativeToText = inputPosY - textRect.top;
9060  if (drgui_text_engine__move_marker_to_point(pTL, pMarker, inputPosXRelativeToText, inputPosYRelativeToText))
9061  {
9062  drgui_text_engine__update_marker_sticky_position(pTL, pMarker);
9063  return true;
9064  }
9065 
9066  return false;
9067 }
9068 
9069 DRGUI_PRIVATE void drgui_text_engine__get_marker_position_relative_to_container(drgui_text_engine* pTL, drgui_text_marker* pMarker, float* pPosXOut, float* pPosYOut)
9070 {
9071  if (pTL == NULL || pMarker == NULL) {
9072  return;
9073  }
9074 
9075  float posX = 0;
9076  float posY = 0;
9077 
9078  if (pMarker->iRun < pTL->runCount)
9079  {
9080  posX = pTL->pRuns[pMarker->iRun].posX + pMarker->relativePosX;
9081  posY = pTL->pRuns[pMarker->iRun].posY;
9082  }
9083 
9085  posX += textRect.left;
9086  posY += textRect.top;
9087 
9088 
9089  if (pPosXOut) {
9090  *pPosXOut = posX;
9091  }
9092  if (pPosYOut) {
9093  *pPosYOut = posY;
9094  }
9095 }
9096 
9097 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_point(drgui_text_engine* pTL, drgui_text_marker* pMarker, float inputPosXRelativeToText, float inputPosYRelativeToText)
9098 {
9099  if (pTL == NULL || pMarker == NULL) {
9100  return false;
9101  }
9102 
9103  size_t iClosestRunToPoint;
9104  if (drgui_text_engine__find_closest_run_to_point(pTL, inputPosXRelativeToText, inputPosYRelativeToText, OUT &iClosestRunToPoint))
9105  {
9106  const drgui_text_run* pRun = pTL->pRuns + iClosestRunToPoint;
9107 
9108  pMarker->iRun = iClosestRunToPoint;
9109 
9110  if (inputPosXRelativeToText < pRun->posX)
9111  {
9112  // It's to the left of the run.
9113  pMarker->iChar = 0;
9114  pMarker->relativePosX = 0;
9115  }
9116  else if (inputPosXRelativeToText > pRun->posX + pRun->width)
9117  {
9118  // It's to the right of the run. It may be a new-line run. If so, we need to move the marker to the front of it, not the back.
9119  pMarker->iChar = pRun->textLength;
9120  pMarker->relativePosX = pRun->width;
9121 
9122  if (pTL->text[pRun->iChar] == '\n') {
9123  assert(pMarker->iChar == 1);
9124  pMarker->iChar = 0;
9125  pMarker->relativePosX = 0;
9126  }
9127  }
9128  else
9129  {
9130  // It's somewhere in the middle of the run. We need to handle this a little different for tab runs since they are aligned differently.
9131  if (pTL->text[pRun->iChar] == '\n')
9132  {
9133  // It's a new line character. It needs to be placed at the beginning of it.
9134  pMarker->iChar = 0;
9135  pMarker->relativePosX = 0;
9136  }
9137  else if (pTL->text[pRun->iChar] == '\t')
9138  {
9139  // It's a tab run.
9140  pMarker->iChar = 0;
9141  pMarker->relativePosX = 0;
9142 
9143  const float tabWidth = drgui_text_engine__get_tab_width(pTL);
9144 
9145  float tabLeft = pRun->posX + pMarker->relativePosX;
9146  for (/* Do Nothing*/; pMarker->iChar < pRun->textLength; ++pMarker->iChar)
9147  {
9148  float tabRight = tabWidth * ((pRun->posX + (tabWidth*(pMarker->iChar + 1))) / tabWidth);
9149  if (tabRight > pRun->posX + pRun->width) {
9150  tabRight = pRun->posX + pRun->width;
9151  }
9152 
9153  if (inputPosXRelativeToText >= tabLeft && inputPosXRelativeToText <= tabRight)
9154  {
9155  // The input position is somewhere on top of this character. If it's positioned on the left side of the character, set the output
9156  // value to the character at iChar. Otherwise it should be set to the character at iChar + 1.
9157  float charBoundsRightHalf = tabLeft + ceilf(((tabRight - tabLeft) / 2.0f));
9158  if (inputPosXRelativeToText <= charBoundsRightHalf) {
9159  pMarker->relativePosX = tabLeft - pRun->posX;
9160  } else {
9161  pMarker->relativePosX = tabRight - pRun->posX;
9162  pMarker->iChar += 1;
9163  }
9164 
9165  break;
9166  }
9167 
9168  tabLeft = tabRight;
9169  }
9170 
9171  // If we're past the last character in the tab run, we want to move to the start of the next run.
9172  if (pMarker->iChar == pRun->textLength) {
9173  drgui_text_engine__move_marker_to_first_character_of_next_run(pTL, pMarker);
9174  }
9175  }
9176  else
9177  {
9178  // It's a standard run.
9179  float inputPosXRelativeToRun = inputPosXRelativeToText - pRun->posX;
9180  if (drgui_get_text_cursor_position_from_point(pRun->pFont, pTL->text + pRun->iChar, pRun->textLength, pRun->width, inputPosXRelativeToRun, OUT &pMarker->relativePosX, OUT &pMarker->iChar))
9181  {
9182  // If the marker is past the last character of the run it needs to be moved to the start of the next one.
9183  if (pMarker->iChar == pRun->textLength) {
9184  drgui_text_engine__move_marker_to_first_character_of_next_run(pTL, pMarker);
9185  }
9186  }
9187  else
9188  {
9189  // An error occured somehow.
9190  return false;
9191  }
9192  }
9193  }
9194 
9195  return true;
9196  }
9197  else
9198  {
9199  // Couldn't find a run.
9200  return false;
9201  }
9202 }
9203 
9204 DRGUI_PRIVATE bool drgui_text_engine__move_marker_left(drgui_text_engine* pTL, drgui_text_marker* pMarker)
9205 {
9206  if (pTL == NULL || pMarker == NULL) {
9207  return false;
9208  }
9209 
9210  if (pTL->runCount > 0)
9211  {
9212  if (pMarker->iChar > 0)
9213  {
9214  pMarker->iChar -= 1;
9215 
9216  const drgui_text_run* pRun = pTL->pRuns + pMarker->iRun;
9217  if (pTL->text[pRun->iChar] == '\t')
9218  {
9219  const float tabWidth = drgui_text_engine__get_tab_width(pTL);
9220 
9221  if (pMarker->iChar == 0)
9222  {
9223  // Simple case - it's the first tab character which means the relative position is just 0.
9224  pMarker->relativePosX = 0;
9225  }
9226  else
9227  {
9228  pMarker->relativePosX = tabWidth * ((pRun->posX + (tabWidth*(pMarker->iChar + 0))) / tabWidth);
9229  pMarker->relativePosX -= pRun->posX;
9230  }
9231  }
9232  else
9233  {
9234  if (!drgui_get_text_cursor_position_from_char(pRun->pFont, pTL->text + pTL->pRuns[pMarker->iRun].iChar, pMarker->iChar, OUT &pMarker->relativePosX)) {
9235  return false;
9236  }
9237  }
9238  }
9239  else
9240  {
9241  // We're at the beginning of the run which means we need to transfer the cursor to the end of the previous run.
9242  if (!drgui_text_engine__move_marker_to_last_character_of_prev_run(pTL, pMarker)) {
9243  return false;
9244  }
9245  }
9246 
9247  drgui_text_engine__update_marker_sticky_position(pTL, pMarker);
9248  return true;
9249  }
9250 
9251  return false;
9252 }
9253 
9254 DRGUI_PRIVATE bool drgui_text_engine__move_marker_right(drgui_text_engine* pTL, drgui_text_marker* pMarker)
9255 {
9256  if (pTL == NULL || pMarker == NULL) {
9257  return false;
9258  }
9259 
9260  if (pTL->runCount > 0)
9261  {
9262  if (pMarker->iChar + 1 < pTL->pRuns[pMarker->iRun].textLength)
9263  {
9264  pMarker->iChar += 1;
9265 
9266  const drgui_text_run* pRun = pTL->pRuns + pMarker->iRun;
9267  if (pTL->text[pRun->iChar] == '\t')
9268  {
9269  const float tabWidth = drgui_text_engine__get_tab_width(pTL);
9270 
9271  pMarker->relativePosX = tabWidth * ((pRun->posX + (tabWidth*(pMarker->iChar + 0))) / tabWidth);
9272  pMarker->relativePosX -= pRun->posX;
9273  }
9274  else
9275  {
9276  if (!drgui_get_text_cursor_position_from_char(pRun->pFont, pTL->text + pTL->pRuns[pMarker->iRun].iChar, pMarker->iChar, OUT &pMarker->relativePosX)) {
9277  return false;
9278  }
9279  }
9280  }
9281  else
9282  {
9283  // We're at the end of the run which means we need to transfer the cursor to the beginning of the next run.
9284  if (!drgui_text_engine__move_marker_to_first_character_of_next_run(pTL, pMarker)) {
9285  return false;
9286  }
9287  }
9288 
9289  drgui_text_engine__update_marker_sticky_position(pTL, pMarker);
9290  return true;
9291  }
9292 
9293  return false;
9294 }
9295 
9296 DRGUI_PRIVATE bool drgui_text_engine__move_marker_up(drgui_text_engine* pTL, drgui_text_marker* pMarker)
9297 {
9298  if (pTL == NULL || pMarker == NULL || pTL->runCount == 0) {
9299  return false;
9300  }
9301 
9302  return drgui_text_engine__move_marker_y(pTL, pMarker, -1);
9303 }
9304 
9305 DRGUI_PRIVATE bool drgui_text_engine__move_marker_down(drgui_text_engine* pTL, drgui_text_marker* pMarker)
9306 {
9307  if (pTL == NULL || pMarker == NULL || pTL->runCount == 0) {
9308  return false;
9309  }
9310 
9311  return drgui_text_engine__move_marker_y(pTL, pMarker, 1);
9312 }
9313 
9314 DRGUI_PRIVATE bool drgui_text_engine__move_marker_y(drgui_text_engine* pTL, drgui_text_marker* pMarker, int amount)
9315 {
9316  if (pTL == NULL || pMarker == NULL || pTL->runCount == 0) {
9317  return false;
9318  }
9319 
9320  const drgui_text_run* pOldRun = pTL->pRuns + pMarker->iRun;
9321 
9322  int iNewLine = (int)pOldRun->iLine + amount;
9323  if (iNewLine >= (int)drgui_text_engine_get_line_count(pTL)) {
9324  iNewLine = (int)drgui_text_engine_get_line_count(pTL) - 1;
9325  }
9326  if (iNewLine < 0) {
9327  iNewLine = 0;
9328  }
9329 
9330  if ((int)pOldRun->iLine == iNewLine) {
9331  return false; // The lines are the same.
9332  }
9333 
9334  drgui_rect lineRect;
9335  size_t iFirstRunOnLine;
9336  size_t iLastRunOnLinePlus1;
9337  if (drgui_text_engine__find_line_info_by_index(pTL, iNewLine, OUT &lineRect, OUT &iFirstRunOnLine, OUT &iLastRunOnLinePlus1))
9338  {
9339  float newMarkerPosX = pMarker->absoluteSickyPosX;
9340  float newMarkerPosY = lineRect.top;
9341  drgui_text_engine__move_marker_to_point(pTL, pMarker, newMarkerPosX, newMarkerPosY);
9342 
9343  return true;
9344  }
9345  else
9346  {
9347  // An error occured while finding information about the line above.
9348  return false;
9349  }
9350 }
9351 
9352 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_end_of_line(drgui_text_engine* pTL, drgui_text_marker* pMarker)
9353 {
9354  if (pTL == NULL || pMarker == NULL) {
9355  return false;
9356  }
9357 
9358  size_t iLastRunOnLine;
9359  if (drgui_text_engine__find_last_run_on_line_starting_from_run(pTL, pMarker->iRun, &iLastRunOnLine))
9360  {
9361  return drgui_text_engine__move_marker_to_last_character_of_run(pTL, pMarker, iLastRunOnLine);
9362  }
9363 
9364  return false;
9365 }
9366 
9367 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_start_of_line(drgui_text_engine* pTL, drgui_text_marker* pMarker)
9368 {
9369  if (pTL == NULL || pMarker == NULL) {
9370  return false;
9371  }
9372 
9373  size_t iFirstRunOnLine;
9374  if (drgui_text_engine__find_first_run_on_line_starting_from_run(pTL, pMarker->iRun, &iFirstRunOnLine))
9375  {
9376  return drgui_text_engine__move_marker_to_first_character_of_run(pTL, pMarker, iFirstRunOnLine);
9377  }
9378 
9379  return false;
9380 }
9381 
9382 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_end_of_line_by_index(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iLine)
9383 {
9384  if (pTL == NULL || pMarker == NULL) {
9385  return false;
9386  }
9387 
9388  size_t iFirstRun;
9389  size_t iLastRunPlus1;
9390  if (drgui_text_engine__find_line_info_by_index(pTL, iLine, NULL, &iFirstRun, &iLastRunPlus1))
9391  {
9392  return drgui_text_engine__move_marker_to_last_character_of_run(pTL, pMarker, iLastRunPlus1 - 1);
9393  }
9394 
9395  return false;
9396 }
9397 
9398 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_start_of_line_by_index(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iLine)
9399 {
9400  if (pTL == NULL || pMarker == NULL) {
9401  return false;
9402  }
9403 
9404  size_t iFirstRun;
9405  size_t iLastRunPlus1;
9406  if (drgui_text_engine__find_line_info_by_index(pTL, iLine, NULL, &iFirstRun, &iLastRunPlus1))
9407  {
9408  return drgui_text_engine__move_marker_to_first_character_of_run(pTL, pMarker, iFirstRun);
9409  }
9410 
9411  return false;
9412 }
9413 
9414 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_end_of_text(drgui_text_engine* pTL, drgui_text_marker* pMarker)
9415 {
9416  if (pTL == NULL || pMarker == NULL) {
9417  return false;
9418  }
9419 
9420  if (pTL->runCount > 0) {
9421  return drgui_text_engine__move_marker_to_last_character_of_run(pTL, pMarker, pTL->runCount - 1);
9422  }
9423 
9424  return false;
9425 }
9426 
9427 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_start_of_text(drgui_text_engine* pTL, drgui_text_marker* pMarker)
9428 {
9429  if (pTL == NULL || pMarker == NULL) {
9430  return false;
9431  }
9432 
9433  return drgui_text_engine__move_marker_to_first_character_of_run(pTL, pMarker, 0);
9434 }
9435 
9436 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_last_character_of_run(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iRun)
9437 {
9438  if (pTL == NULL || pMarker == NULL) {
9439  return false;
9440  }
9441 
9442  if (iRun < pTL->runCount)
9443  {
9444  pMarker->iRun = iRun;
9445  pMarker->iChar = pTL->pRuns[pMarker->iRun].textLength;
9446  pMarker->relativePosX = pTL->pRuns[pMarker->iRun].width;
9447 
9448  if (pMarker->iChar > 0)
9449  {
9450  // At this point we are located one character past the last character - we need to move it left.
9451  return drgui_text_engine__move_marker_left(pTL, pMarker);
9452  }
9453 
9454  return true;
9455  }
9456 
9457  return false;
9458 }
9459 
9460 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_first_character_of_run(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iRun)
9461 {
9462  if (pTL == NULL || pMarker == NULL) {
9463  return false;
9464  }
9465 
9466  if (iRun < pTL->runCount)
9467  {
9468  pMarker->iRun = iRun;
9469  pMarker->iChar = 0;
9470  pMarker->relativePosX = 0;
9471 
9472  drgui_text_engine__update_marker_sticky_position(pTL, pMarker);
9473 
9474  return true;
9475  }
9476 
9477  return false;
9478 }
9479 
9480 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_last_character_of_prev_run(drgui_text_engine* pTL, drgui_text_marker* pMarker)
9481 {
9482  if (pTL == NULL || pMarker == NULL) {
9483  return false;
9484  }
9485 
9486  if (pMarker->iRun > 0) {
9487  return drgui_text_engine__move_marker_to_last_character_of_run(pTL, pMarker, pMarker->iRun - 1);
9488  }
9489 
9490  return false;
9491 }
9492 
9493 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_first_character_of_next_run(drgui_text_engine* pTL, drgui_text_marker* pMarker)
9494 {
9495  if (pTL == NULL || pMarker == NULL) {
9496  return false;
9497  }
9498 
9499  if (pTL->runCount > 0 && pMarker->iRun < pTL->runCount - 1) {
9500  return drgui_text_engine__move_marker_to_first_character_of_run(pTL, pMarker, pMarker->iRun + 1);
9501  }
9502 
9503  return false;
9504 }
9505 
9506 DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_character(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iChar)
9507 {
9508  if (pTL == NULL || pMarker == NULL || pTL->runCount == 0) {
9509  return false;
9510  }
9511 
9512  // Clamp the character to the end of the string.
9513  if (iChar > pTL->textLength) {
9514  iChar = pTL->textLength;
9515  }
9516 
9517  drgui_text_engine__find_run_at_character(pTL, iChar, &pMarker->iRun);
9518 
9519  assert(pMarker->iRun < pTL->runCount);
9520  pMarker->iChar = iChar - pTL->pRuns[pMarker->iRun].iChar;
9521 
9522 
9523  // The relative position depends on whether or not the run is a tab character.
9524  return drgui_text_engine__update_marker_relative_position(pTL, pMarker);
9525 }
9526 
9527 
9528 DRGUI_PRIVATE bool drgui_text_engine__update_marker_relative_position(drgui_text_engine* pTL, drgui_text_marker* pMarker)
9529 {
9530  if (pTL == NULL || pMarker == NULL || pTL->runCount == 0) {
9531  return false;
9532  }
9533 
9534  const drgui_text_run* pRun = pTL->pRuns + pMarker->iRun;
9535  if (pTL->text[pRun->iChar] == '\t')
9536  {
9537  const float tabWidth = drgui_text_engine__get_tab_width(pTL);
9538 
9539  if (pMarker->iChar == 0)
9540  {
9541  // Simple case - it's the first tab character which means the relative position is just 0.
9542  pMarker->relativePosX = 0;
9543  }
9544  else
9545  {
9546  pMarker->relativePosX = tabWidth * ((pRun->posX + (tabWidth*(pMarker->iChar + 0))) / tabWidth);
9547  pMarker->relativePosX -= pRun->posX;
9548  }
9549 
9550  return true;
9551  }
9552  else
9553  {
9554  return drgui_get_text_cursor_position_from_char(pRun->pFont, pTL->text + pTL->pRuns[pMarker->iRun].iChar, pMarker->iChar, OUT &pMarker->relativePosX);
9555  }
9556 }
9557 
9558 DRGUI_PRIVATE void drgui_text_engine__update_marker_sticky_position(drgui_text_engine* pTL, drgui_text_marker* pMarker)
9559 {
9560  if (pTL == NULL || pMarker == NULL) {
9561  return;
9562  }
9563 
9564  pMarker->absoluteSickyPosX = pTL->pRuns[pMarker->iRun].posX + pMarker->relativePosX;
9565 }
9566 
9567 DRGUI_PRIVATE size_t drgui_text_engine__get_marker_absolute_char_index(drgui_text_engine* pTL, drgui_text_marker* pMarker)
9568 {
9569  if (pTL == NULL || pMarker == NULL || pTL->runCount == 0) {
9570  return 0;
9571  }
9572 
9573  return pTL->pRuns[pMarker->iRun].iChar + pMarker->iChar;
9574 }
9575 
9576 
9577 DRGUI_PRIVATE bool drgui_text_engine__has_spacing_between_selection_markers(drgui_text_engine* pTL)
9578 {
9579  if (pTL == NULL) {
9580  return false;
9581  }
9582 
9583  return (pTL->cursor.iRun != pTL->selectionAnchor.iRun || pTL->cursor.iChar != pTL->selectionAnchor.iChar);
9584 }
9585 
9586 DRGUI_PRIVATE size_t drgui_text_engine__split_text_run_by_selection(drgui_text_engine* pTL, drgui_text_run* pRunToSplit, drgui_text_run pSubRunsOut[3])
9587 {
9588  if (pTL == NULL || pRunToSplit == NULL || pSubRunsOut == NULL) {
9589  return 0;
9590  }
9591 
9592  drgui_text_marker* pSelectionMarker0 = &pTL->selectionAnchor;
9593  drgui_text_marker* pSelectionMarker1 = &pTL->cursor;
9594  if (pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar > pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar)
9595  {
9596  drgui_text_marker* temp = pSelectionMarker0;
9597  pSelectionMarker0 = pSelectionMarker1;
9598  pSelectionMarker1 = temp;
9599  }
9600 
9601  drgui_text_run* pSelectionRun0 = pTL->pRuns + pSelectionMarker0->iRun;
9602  drgui_text_run* pSelectionRun1 = pTL->pRuns + pSelectionMarker1->iRun;
9603 
9604  size_t iSelectionChar0 = pSelectionRun0->iChar + pSelectionMarker0->iChar;
9605  size_t iSelectionChar1 = pSelectionRun1->iChar + pSelectionMarker1->iChar;
9606 
9608  {
9609  if (pRunToSplit->iChar < iSelectionChar1 && pRunToSplit->iCharEnd > iSelectionChar0)
9610  {
9611  // The run is somewhere inside the selection region.
9612  for (int i = 0; i < 3; ++i) {
9613  pSubRunsOut[i] = *pRunToSplit;
9614  }
9615 
9616  if (pRunToSplit->iChar >= iSelectionChar0)
9617  {
9618  // The first part of the run is selected.
9619  if (pRunToSplit->iCharEnd <= iSelectionChar1)
9620  {
9621  // The entire run is selected.
9622  pSubRunsOut[0].backgroundColor = pTL->selectionBackgroundColor;
9623  return 1;
9624  }
9625  else
9626  {
9627  // The head part of the run is selected, and the tail is deselected.
9628 
9629  // Head.
9630  pSubRunsOut[0].backgroundColor = pTL->selectionBackgroundColor;
9631  pSubRunsOut[0].iCharEnd = iSelectionChar1;
9632  pSubRunsOut[0].width = pSelectionMarker1->relativePosX;
9633  pSubRunsOut[0].text = pTL->text + pSubRunsOut[0].iChar;
9634  pSubRunsOut[0].textLength = pSubRunsOut[0].iCharEnd - pSubRunsOut[0].iChar;
9635 
9636  // Tail.
9637  pSubRunsOut[1].iChar = iSelectionChar1;
9638  pSubRunsOut[1].width = pRunToSplit->width - pSelectionMarker1->relativePosX;
9639  pSubRunsOut[1].posX = pSubRunsOut[0].posX + pSubRunsOut[0].width;
9640  pSubRunsOut[1].text = pTL->text + pSubRunsOut[1].iChar;
9641  pSubRunsOut[1].textLength = pSubRunsOut[1].iCharEnd - pSubRunsOut[1].iChar;
9642 
9643  return 2;
9644  }
9645  }
9646  else
9647  {
9648  // The first part of the run is deselected. There will be at least 2, but possibly 3 sub-runs in this case.
9649  if (pRunToSplit->iCharEnd <= iSelectionChar1)
9650  {
9651  // The head of the run is deselected and the tail is selected.
9652 
9653  // Head.
9654  pSubRunsOut[0].iCharEnd = iSelectionChar0;
9655  pSubRunsOut[0].width = pSelectionMarker0->relativePosX;
9656  pSubRunsOut[0].text = pTL->text + pSubRunsOut[0].iChar;
9657  pSubRunsOut[0].textLength = pSubRunsOut[0].iCharEnd - pSubRunsOut[0].iChar;
9658 
9659  // Tail.
9660  pSubRunsOut[1].backgroundColor = pTL->selectionBackgroundColor;
9661  pSubRunsOut[1].iChar = iSelectionChar0;
9662  pSubRunsOut[1].width = pRunToSplit->width - pSubRunsOut[0].width;
9663  pSubRunsOut[1].posX = pSubRunsOut[0].posX + pSubRunsOut[0].width;
9664  pSubRunsOut[1].text = pTL->text + pSubRunsOut[1].iChar;
9665  pSubRunsOut[1].textLength = pSubRunsOut[1].iCharEnd - pSubRunsOut[1].iChar;
9666 
9667  return 2;
9668  }
9669  else
9670  {
9671  // The head and tail are both deselected, and the middle section is selected.
9672 
9673  // Head.
9674  pSubRunsOut[0].iCharEnd = iSelectionChar0;
9675  pSubRunsOut[0].width = pSelectionMarker0->relativePosX;
9676  pSubRunsOut[0].text = pTL->text + pSubRunsOut[0].iChar;
9677  pSubRunsOut[0].textLength = pSubRunsOut[0].iCharEnd - pSubRunsOut[0].iChar;
9678 
9679  // Mid.
9680  pSubRunsOut[1].iChar = iSelectionChar0;
9681  pSubRunsOut[1].iCharEnd = iSelectionChar1;
9682  pSubRunsOut[1].backgroundColor = pTL->selectionBackgroundColor;
9683  pSubRunsOut[1].width = pSelectionMarker1->relativePosX - pSelectionMarker0->relativePosX;
9684  pSubRunsOut[1].posX = pSubRunsOut[0].posX + pSubRunsOut[0].width;
9685  pSubRunsOut[1].text = pTL->text + pSubRunsOut[1].iChar;
9686  pSubRunsOut[1].textLength = pSubRunsOut[1].iCharEnd - pSubRunsOut[1].iChar;
9687 
9688  // Tail.
9689  pSubRunsOut[2].iChar = iSelectionChar1;
9690  pSubRunsOut[2].width = pRunToSplit->width - pSelectionMarker1->relativePosX;
9691  pSubRunsOut[2].posX = pSubRunsOut[1].posX + pSubRunsOut[1].width;
9692  pSubRunsOut[2].text = pTL->text + pSubRunsOut[2].iChar;
9693  pSubRunsOut[2].textLength = pSubRunsOut[2].iCharEnd - pSubRunsOut[2].iChar;
9694 
9695  return 3;
9696  }
9697  }
9698  }
9699  }
9700 
9701  // If we get here it means the run is not within the selected region.
9702  pSubRunsOut[0] = *pRunToSplit;
9703  return 1;
9704 }
9705 
9706 #if 0
9707 DRGUI_PRIVATE bool drgui_text_engine__is_run_selected(drgui_text_engine* pTL, unsigned int iRun)
9708 {
9710  {
9711  drgui_text_marker* pSelectionMarker0;
9712  drgui_text_marker* pSelectionMarker1;
9713  if (!drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
9714  return false;
9715  }
9716 
9717  unsigned int iSelectionChar0 = pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar;
9718  unsigned int iSelectionChar1 = pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar;
9719 
9720  return pTL->pRuns[iRun].iChar < iSelectionChar1 && pTL->pRuns[iRun].iCharEnd > iSelectionChar0;
9721  }
9722 
9723  return false;
9724 }
9725 #endif
9726 
9727 DRGUI_PRIVATE bool drgui_text_engine__get_selection_markers(drgui_text_engine* pTL, drgui_text_marker** ppSelectionMarker0Out, drgui_text_marker** ppSelectionMarker1Out)
9728 {
9729  bool result = false;
9730 
9731  drgui_text_marker* pSelectionMarker0 = NULL;
9732  drgui_text_marker* pSelectionMarker1 = NULL;
9733  if (pTL != NULL && drgui_text_engine_is_anything_selected(pTL))
9734  {
9735  pSelectionMarker0 = &pTL->selectionAnchor;
9736  pSelectionMarker1 = &pTL->cursor;
9737  if (pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar > pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar)
9738  {
9739  drgui_text_marker* temp = pSelectionMarker0;
9740  pSelectionMarker0 = pSelectionMarker1;
9741  pSelectionMarker1 = temp;
9742  }
9743 
9744  result = true;
9745  }
9746 
9747  if (ppSelectionMarker0Out) {
9748  *ppSelectionMarker0Out = pSelectionMarker0;
9749  }
9750  if (ppSelectionMarker1Out) {
9751  *ppSelectionMarker1Out = pSelectionMarker1;
9752  }
9753 
9754  return result;
9755 }
9756 
9757 
9758 DRGUI_PRIVATE bool drgui_text_engine__first_line(drgui_text_engine* pTL, drgui_text_engine_line* pLine)
9759 {
9760  if (pTL == NULL || pLine == NULL || pTL->runCount == 0) {
9761  return false;
9762  }
9763 
9764  pLine->index = 0;
9765  pLine->posY = 0;
9766  pLine->height = 0;
9767  pLine->iFirstRun = 0;
9768  pLine->iLastRun = 0;
9769 
9770  // We need to find the last run in the line and the height. The height is determined by the run with the largest height.
9771  while (pLine->iLastRun < pTL->runCount)
9772  {
9773  if (pLine->height < pTL->pRuns[pLine->iLastRun].height) {
9774  pLine->height = pTL->pRuns[pLine->iLastRun].height;
9775  }
9776 
9777  pLine->iLastRun += 1;
9778 
9779  if (pTL->pRuns[pLine->iLastRun].iLine != pLine->index) {
9780  break;
9781  }
9782  }
9783 
9784  if (pLine->iLastRun > 0) {
9785  pLine->iLastRun -= 1;
9786  }
9787 
9788  return true;
9789 }
9790 
9791 DRGUI_PRIVATE bool drgui_text_engine__next_line(drgui_text_engine* pTL, drgui_text_engine_line* pLine)
9792 {
9793  if (pTL == NULL || pLine == NULL || pTL->runCount == 0) {
9794  return false;
9795  }
9796 
9797  // If there's no more runs, there is no next line.
9798  if (pLine->iLastRun == pTL->runCount - 1) {
9799  return false;
9800  }
9801 
9802  pLine->index += 1;
9803  pLine->posY += pLine->height;
9804  pLine->height = 0;
9805  pLine->iFirstRun = pLine->iLastRun + 1;
9806  pLine->iLastRun = pLine->iFirstRun;
9807 
9808  while (pLine->iLastRun < pTL->runCount)
9809  {
9810  if (pLine->height < pTL->pRuns[pLine->iLastRun].height) {
9811  pLine->height = pTL->pRuns[pLine->iLastRun].height;
9812  }
9813 
9814  pLine->iLastRun += 1;
9815 
9816  if (pTL->pRuns[pLine->iLastRun].iLine != pLine->index) {
9817  break;
9818  }
9819  }
9820 
9821  if (pLine->iLastRun > 0) {
9822  pLine->iLastRun -= 1;
9823  }
9824 
9825  return true;
9826 }
9827 
9828 DRGUI_PRIVATE void drgui_text_engine__trim_undo_stack(drgui_text_engine* pTL)
9829 {
9830  if (pTL == NULL) {
9831  return;
9832  }
9833 
9834  while (pTL->undoStackCount > pTL->iUndoState)
9835  {
9836  unsigned int iLastItem = pTL->undoStackCount - 1;
9837 
9838  drgui_text_engine__uninit_undo_state(pTL->pUndoStack + iLastItem);
9839  pTL->undoStackCount -= 1;
9840  }
9841 }
9842 
9843 DRGUI_PRIVATE bool drgui_text_engine__diff_states(drgui_text_engine_state* pPrevState, drgui_text_engine_state* pCurrentState, drgui_text_engine_undo_state* pUndoStateOut)
9844 {
9845  if (pPrevState == NULL || pCurrentState == NULL || pUndoStateOut == NULL) {
9846  return false;
9847  }
9848 
9849  if (pPrevState->text == NULL || pCurrentState->text == NULL) {
9850  return false;
9851  }
9852 
9853  const char* prevText = pPrevState->text;
9854  const char* currText = pCurrentState->text;
9855 
9856  const size_t prevLen = strlen(prevText);
9857  const size_t currLen = strlen(currText);
9858 
9859 
9860  // The first step is to find the position of the first differing character.
9861  size_t sameChCountStart;
9862  for (sameChCountStart = 0; sameChCountStart < prevLen && sameChCountStart < currLen; ++sameChCountStart)
9863  {
9864  char prevCh = prevText[sameChCountStart];
9865  char currCh = currText[sameChCountStart];
9866 
9867  if (prevCh != currCh) {
9868  break;
9869  }
9870  }
9871 
9872  // The next step is to find the position of the last differing character.
9873  size_t sameChCountEnd;
9874  for (sameChCountEnd = 0; sameChCountEnd < prevLen && sameChCountEnd < currLen; ++sameChCountEnd)
9875  {
9876  // Don't move beyond the first differing character.
9877  if (prevLen - sameChCountEnd <= sameChCountStart ||
9878  currLen - sameChCountEnd <= sameChCountStart)
9879  {
9880  break;
9881  }
9882 
9883  char prevCh = prevText[prevLen - sameChCountEnd - 1];
9884  char currCh = currText[currLen - sameChCountEnd - 1];
9885 
9886  if (prevCh != currCh) {
9887  break;
9888  }
9889  }
9890 
9891 
9892  // At this point we know which section of the text is different. We now need to initialize the undo state object.
9893  pUndoStateOut->diffPos = sameChCountStart;
9894  pUndoStateOut->newState = *pCurrentState;
9895  pUndoStateOut->newState.text = NULL;
9896  pUndoStateOut->oldState = *pPrevState;
9897  pUndoStateOut->oldState.text = NULL;
9898 
9899  size_t oldTextLen = prevLen - sameChCountStart - sameChCountEnd;
9900  pUndoStateOut->oldText = (char*)malloc(oldTextLen + 1);
9901  drgui__strncpy_s(pUndoStateOut->oldText, oldTextLen + 1, prevText + sameChCountStart, oldTextLen);
9902 
9903  size_t newTextLen = currLen - sameChCountStart - sameChCountEnd;
9904  pUndoStateOut->newText = (char*)malloc(newTextLen + 1);
9905  drgui__strncpy_s(pUndoStateOut->newText, newTextLen + 1, currText + sameChCountStart, newTextLen);
9906 
9907  return true;
9908 }
9909 
9910 DRGUI_PRIVATE void drgui_text_engine__uninit_undo_state(drgui_text_engine_undo_state* pUndoState)
9911 {
9912  if (pUndoState == NULL) {
9913  return;
9914  }
9915 
9916  free(pUndoState->oldText);
9917  pUndoState->oldText = NULL;
9918 
9919  free(pUndoState->newText);
9920  pUndoState->newText = NULL;
9921 }
9922 
9923 DRGUI_PRIVATE void drgui_text_engine__push_undo_state(drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState)
9924 {
9925  if (pTL == NULL || pUndoState == NULL) {
9926  return;
9927  }
9928 
9929  assert(pTL->iUndoState == pTL->undoStackCount);
9930 
9931 
9932  drgui_text_engine_undo_state* pOldStack = pTL->pUndoStack;
9933  drgui_text_engine_undo_state* pNewStack = (drgui_text_engine_undo_state*)malloc(sizeof(*pNewStack) * (pTL->undoStackCount + 1));
9934 
9935  if (pTL->undoStackCount > 0) {
9936  memcpy(pNewStack, pOldStack, sizeof(*pNewStack) * (pTL->undoStackCount));
9937  }
9938 
9939  pNewStack[pTL->undoStackCount] = *pUndoState;
9940  pTL->pUndoStack = pNewStack;
9941  pTL->undoStackCount += 1;
9942  pTL->iUndoState += 1;
9943 
9944  if (pTL->onUndoPointChanged) {
9945  pTL->onUndoPointChanged(pTL, pTL->iUndoState);
9946  }
9947 
9948  free(pOldStack);
9949 }
9950 
9951 DRGUI_PRIVATE void drgui_text_engine__apply_undo_state(drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState)
9952 {
9953  if (pTL == NULL || pUndoState == NULL) {
9954  return;
9955  }
9956 
9957  // When undoing we want to remove the new text and replace it with the old text.
9958 
9959  size_t iFirstCh = pUndoState->diffPos;
9960  size_t iLastChPlus1 = pUndoState->diffPos + strlen(pUndoState->newText);
9961  size_t bytesToRemove = iLastChPlus1 - iFirstCh;
9962  if (bytesToRemove > 0)
9963  {
9964  memmove(pTL->text + iFirstCh, pTL->text + iLastChPlus1, pTL->textLength - iLastChPlus1);
9965  pTL->textLength -= bytesToRemove;
9966  pTL->text[pTL->textLength] = '\0';
9967  }
9968 
9969  // TODO: This needs improving because it results in multiple onTextChanged and onDirty events being posted.
9970 
9971  // Insert the old text.
9972  drgui_text_engine_insert_text(pTL, pUndoState->oldText, pUndoState->diffPos);
9973 
9974 
9975  // The layout will have changed so it needs to be refreshed.
9976  drgui_text_engine__refresh(pTL);
9977 
9978 
9979  // Markers needs to be updated after refreshing the layout.
9980  drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, pUndoState->oldState.cursorPos);
9981  drgui_text_engine__move_marker_to_character(pTL, &pTL->selectionAnchor, pUndoState->oldState.selectionAnchorPos);
9982 
9983  // The cursor's sticky position needs to be updated whenever the text is edited.
9984  drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
9985 
9986  // Ensure we mark the text as selected if appropriate.
9987  pTL->isAnythingSelected = pUndoState->oldState.isAnythingSelected;
9988 
9989 
9990  if (pTL->onTextChanged) {
9991  pTL->onTextChanged(pTL);
9992  }
9993 
9994  drgui_text_engine__on_cursor_move(pTL);
9995 
9996  if (pTL->onDirty) {
9997  pTL->onDirty(pTL, drgui_text_engine__local_rect(pTL));
9998  }
9999 }
10000 
10001 DRGUI_PRIVATE void drgui_text_engine__apply_redo_state(drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState)
10002 {
10003  if (pTL == NULL || pUndoState == NULL) {
10004  return;
10005  }
10006 
10007  // An redo is just the opposite of an undo. We want to remove the old text and replace it with the new text.
10008 
10009  size_t iFirstCh = pUndoState->diffPos;
10010  size_t iLastChPlus1 = pUndoState->diffPos + strlen(pUndoState->oldText);
10011  size_t bytesToRemove = iLastChPlus1 - iFirstCh;
10012  if (bytesToRemove > 0)
10013  {
10014  memmove(pTL->text + iFirstCh, pTL->text + iLastChPlus1, pTL->textLength - iLastChPlus1);
10015  pTL->textLength -= bytesToRemove;
10016  pTL->text[pTL->textLength] = '\0';
10017  }
10018 
10019  // TODO: This needs improving because it results in multiple onTextChanged and onDirty events being posted.
10020 
10021  // Insert the new text.
10022  drgui_text_engine_insert_text(pTL, pUndoState->newText, pUndoState->diffPos);
10023 
10024 
10025  // The layout will have changed so it needs to be refreshed.
10026  drgui_text_engine__refresh(pTL);
10027 
10028 
10029  // Markers needs to be updated after refreshing the layout.
10030  drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, pUndoState->newState.cursorPos);
10031  drgui_text_engine__move_marker_to_character(pTL, &pTL->selectionAnchor, pUndoState->newState.selectionAnchorPos);
10032 
10033  // The cursor's sticky position needs to be updated whenever the text is edited.
10034  drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
10035 
10036  // Ensure we mark the text as selected if appropriate.
10037  pTL->isAnythingSelected = pUndoState->newState.isAnythingSelected;
10038 
10039 
10040  if (pTL->onTextChanged) {
10041  pTL->onTextChanged(pTL);
10042  }
10043 
10044  drgui_text_engine__on_cursor_move(pTL);
10045 
10046  if (pTL->onDirty) {
10047  pTL->onDirty(pTL, drgui_text_engine__local_rect(pTL));
10048  }
10049 }
10050 
10051 
10052 DRGUI_PRIVATE drgui_rect drgui_text_engine__local_rect(drgui_text_engine* pTL)
10053 {
10054  if (pTL == NULL) {
10055  return drgui_make_rect(0, 0, 0, 0);
10056  }
10057 
10058  return drgui_make_rect(0, 0, pTL->containerWidth, pTL->containerHeight);
10059 }
10060 
10061 
10062 DRGUI_PRIVATE void drgui_text_engine__on_cursor_move(drgui_text_engine* pTL)
10063 {
10064  if (pTL == NULL) {
10065  return;
10066  }
10067 
10068  // When the cursor moves we want to reset the cursor's blink state.
10069  pTL->timeToNextCursorBlink = pTL->cursorBlinkRate;
10070  pTL->isCursorBlinkOn = true;
10071 
10072  if (pTL->onCursorMove) {
10073  pTL->onCursorMove(pTL);
10074  }
10075 
10076  drgui_text_engine__on_dirty(pTL, drgui_text_engine_get_cursor_rect(pTL));
10077 }
10078 
10079 DRGUI_PRIVATE void drgui_text_engine__on_dirty(drgui_text_engine* pTL, drgui_rect rect)
10080 {
10081  drgui_text_engine__begin_dirty(pTL);
10082  {
10083  pTL->accumulatedDirtyRect = drgui_rect_union(pTL->accumulatedDirtyRect, rect);
10084  }
10085  drgui_text_engine__end_dirty(pTL);
10086 }
10087 
10088 DRGUI_PRIVATE void drgui_text_engine__begin_dirty(drgui_text_engine* pTL)
10089 {
10090  if (pTL == NULL) {
10091  return;
10092  }
10093 
10094  pTL->dirtyCounter += 1;
10095 }
10096 
10097 DRGUI_PRIVATE void drgui_text_engine__end_dirty(drgui_text_engine* pTL)
10098 {
10099  if (pTL == NULL) {
10100  return;
10101  }
10102 
10103  assert(pTL->dirtyCounter > 0);
10104 
10105  pTL->dirtyCounter -= 1;
10106 
10107  if (pTL->dirtyCounter == 0) {
10108  if (pTL->onDirty) {
10109  pTL->onDirty(pTL, pTL->accumulatedDirtyRect);
10110  }
10111 
10112  pTL->accumulatedDirtyRect = drgui_make_inside_out_rect();
10113  }
10114 }
10115 #endif //DR_GUI_IMPLEMENTATION
10116 #endif
10117 
10118 
10119 
10120 
10123 //
10124 // Scrollbar
10125 //
10128 
10129 #ifndef drgui_scrollbar_h
10130 #define drgui_scrollbar_h
10131 
10132 #ifdef __cplusplus
10133 extern "C" {
10134 #endif
10135 
10136 typedef enum
10137 {
10141 
10143 
10144 typedef void (* drgui_sb_on_scroll_proc)(drgui_element* pSBElement, int scrollPos);
10145 
10146 
10148 drgui_element* drgui_create_scrollbar(drgui_context* pContext, drgui_element* pParent, drgui_sb_orientation orientation, size_t extraDataSize, const void* pExtraData);
10149 
10151 void drgui_delete_scrollbar(drgui_element* pSBElement);
10152 
10153 
10155 size_t drgui_sb_get_extra_data_size(drgui_element* pSBElement);
10156 
10158 void* drgui_sb_get_extra_data(drgui_element* pSBElement);
10159 
10160 
10163 
10164 
10166 void drgui_sb_set_range(drgui_element* pSBElement, int rangeMin, int rangeMax);
10167 
10169 void drgui_sb_get_range(drgui_element* pSBElement, int* pRangeMinOut, int* pRangeMaxOut);
10170 
10171 
10173 void drgui_sb_set_page_size(drgui_element* pSBElement, int pageSize);
10174 
10176 int drgui_sb_get_page_size(drgui_element* pSBElement);
10177 
10178 
10183 void drgui_sb_set_range_and_page_size(drgui_element* pSBElement, int rangeMin, int rangeMax, int pageSize);
10184 
10185 
10192 void drgui_sb_set_scroll_position(drgui_element* pSBElement, int position);
10193 
10196 
10197 
10202 void drgui_sb_scroll(drgui_element* pSBElement, int offset);
10203 
10210 void drgui_sb_scroll_to(drgui_element* pSBElement, int newScrollPos);
10211 
10212 
10215 
10218 
10221 
10226 bool drgui_sb_is_thumb_visible(drgui_element* pSBElement);
10227 
10228 
10233 void drgui_sb_set_mouse_wheel_scele(drgui_element* pSBElement, int scale);
10234 
10237 
10238 
10240 void drgui_sb_set_track_color(drgui_element* pSBElement, drgui_color color);
10241 
10244 
10247 
10250 
10251 
10254 
10257 
10258 
10261 
10262 
10264 void drgui_sb_on_size(drgui_element* pSBElement, float newWidth, float newHeight);
10265 
10267 void drgui_sb_on_mouse_leave(drgui_element* pSBElement);
10268 
10270 void drgui_sb_on_mouse_move(drgui_element* pSBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags);
10271 
10273 void drgui_sb_on_mouse_button_down(drgui_element* pSBElement, int button, int relativeMousePosX, int relativeMousePosY, int stateFlags);
10274 
10276 void drgui_sb_on_mouse_button_up(drgui_element* pSBElement, int button, int relativeMousePosX, int relativeMousePosY, int stateFlags);
10277 
10279 void drgui_sb_on_mouse_wheel(drgui_element* pSBElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags);
10280 
10282 void drgui_sb_on_paint(drgui_element* pSBElement, drgui_rect relativeClippingRect, void* pPaintData);
10283 
10284 
10285 #ifdef __cplusplus
10286 }
10287 #endif
10288 #endif //drgui_scrollbar_h
10289 
10290 
10291 #ifdef DR_GUI_IMPLEMENTATION
10292 
10293 #define DRGUI_MIN_SCROLLBAR_THUMB_SIZE 16
10294 
10295 typedef struct
10296 {
10298  drgui_sb_orientation orientation;
10299 
10301  int rangeMin;
10302 
10304  int rangeMax;
10305 
10307  int pageSize;
10308 
10310  int scrollPos;
10311 
10313  bool autoHideThumb;
10314 
10316  int mouseWheelScale;
10317 
10319  drgui_color trackColor;
10320 
10322  drgui_color thumbColor;
10323 
10325  drgui_color thumbColorHovered;
10326 
10328  drgui_color thumbColorPressed;
10329 
10331  drgui_sb_on_scroll_proc onScroll;
10332 
10333 
10335  float thumbSize;
10336 
10338  float thumbPos;
10339 
10341  float thumbPadding;
10342 
10344  bool thumbHovered;
10345 
10347  bool thumbPressed;
10348 
10350  float thumbClickPosX;
10351 
10353  float thumbClickPosY;
10354 
10355 
10357  size_t extraDataSize;
10358 
10360  char pExtraData[1];
10361 
10362 } drgui_scrollbar;
10363 
10364 
10366 DRGUI_PRIVATE void drgui_sb_refresh_thumb(drgui_element* pSBElement);
10367 
10369 DRGUI_PRIVATE float drgui_sb_calculate_thumb_size(drgui_element* pSBElement);
10370 
10372 DRGUI_PRIVATE float drgui_sb_calculate_thumb_position(drgui_element* pSBElement);
10373 
10375 DRGUI_PRIVATE float drgui_sb_get_track_size(drgui_element* pSBElement);
10376 
10378 DRGUI_PRIVATE void drgui_sb_make_relative_to_thumb(drgui_element* pSBElement, float* pPosX, float* pPosY);
10379 
10381 DRGUI_PRIVATE int drgui_sb_calculate_scroll_pos_from_thumb_pos(drgui_element* pScrollba, float thumbPosr);
10382 
10384 DRGUI_PRIVATE float drgui_sb_clampf(float n, float lower, float upper)
10385 {
10386  return n <= lower ? lower : n >= upper ? upper : n;
10387 }
10388 
10390 DRGUI_PRIVATE int drgui_sb_clampi(int n, int lower, int upper)
10391 {
10392  return n <= lower ? lower : n >= upper ? upper : n;
10393 }
10394 
10396 DRGUI_PRIVATE int drgui_sb_maxi(int x, int y)
10397 {
10398  return (x > y) ? x : y;
10399 }
10400 
10401 
10402 drgui_element* drgui_create_scrollbar(drgui_context* pContext, drgui_element* pParent, drgui_sb_orientation orientation, size_t extraDataSize, const void* pExtraData)
10403 {
10404  if (pContext == NULL || orientation == drgui_sb_orientation_none) {
10405  return NULL;
10406  }
10407 
10408  drgui_element* pSBElement = drgui_create_element(pContext, pParent, sizeof(drgui_scrollbar) + extraDataSize, NULL);
10409  if (pSBElement == NULL) {
10410  return NULL;
10411  }
10412 
10413  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10414  assert(pSB != NULL);
10415 
10416  pSB->orientation = orientation;
10417  pSB->rangeMin = 0;
10418  pSB->rangeMax = 0;
10419  pSB->pageSize = 0;
10420  pSB->scrollPos = 0;
10421  pSB->autoHideThumb = true;
10422  pSB->mouseWheelScale = 1;
10423  pSB->trackColor = drgui_rgb(80, 80, 80);
10424  pSB->thumbColor = drgui_rgb(112, 112, 112);
10425  pSB->thumbColorHovered = drgui_rgb(144, 144, 144);
10426  pSB->thumbColorPressed = drgui_rgb(180, 180, 180);
10427  pSB->onScroll = NULL;
10428 
10429  pSB->thumbSize = DRGUI_MIN_SCROLLBAR_THUMB_SIZE;
10430  pSB->thumbPos = 0;
10431  pSB->thumbPadding = 2;
10432  pSB->thumbHovered = false;
10433  pSB->thumbPressed = false;
10434  pSB->thumbClickPosX = 0;
10435  pSB->thumbClickPosY = 0;
10436 
10437  pSB->extraDataSize = extraDataSize;
10438  if (pExtraData != NULL) {
10439  memcpy(pSB->pExtraData, pExtraData, extraDataSize);
10440  }
10441 
10442 
10443  // Default event handlers.
10444  drgui_set_on_size(pSBElement, drgui_sb_on_size);
10451 
10452 
10453  return pSBElement;
10454 }
10455 
10456 void drgui_delete_scrollbar(drgui_element* pSBElement)
10457 {
10458  if (pSBElement == NULL) {
10459  return;
10460  }
10461 
10462  drgui_delete_element(pSBElement);
10463 }
10464 
10465 
10466 size_t drgui_sb_get_extra_data_size(drgui_element* pSBElement)
10467 {
10468  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10469  if (pSB == NULL) {
10470  return 0;
10471  }
10472 
10473  return pSB->extraDataSize;
10474 }
10475 
10476 void* drgui_sb_get_extra_data(drgui_element* pSBElement)
10477 {
10478  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10479  if (pSB == NULL) {
10480  return NULL;
10481  }
10482 
10483  return pSB->pExtraData;
10484 }
10485 
10486 
10488 {
10489  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10490  if (pSB == NULL) {
10492  }
10493 
10494  return pSB->orientation;
10495 }
10496 
10497 
10498 void drgui_sb_set_range(drgui_element* pSBElement, int rangeMin, int rangeMax)
10499 {
10500  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10501  if (pSB == NULL) {
10502  return;
10503  }
10504 
10505  pSB->rangeMin = rangeMin;
10506  pSB->rangeMax = rangeMax;
10507 
10508 
10509  // Make sure the scroll position is still valid.
10510  drgui_sb_scroll_to(pSBElement, drgui_sb_get_scroll_position(pSBElement));
10511 
10512  // The thumb may have changed, so refresh it.
10513  drgui_sb_refresh_thumb(pSBElement);
10514 }
10515 
10516 void drgui_sb_get_range(drgui_element* pSBElement, int* pRangeMinOut, int* pRangeMaxOut)
10517 {
10518  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10519  if (pSB == NULL) {
10520  return;
10521  }
10522 
10523  if (pRangeMinOut != NULL) {
10524  *pRangeMinOut = pSB->rangeMin;
10525  }
10526 
10527  if (pRangeMaxOut != NULL) {
10528  *pRangeMaxOut = pSB->rangeMax;
10529  }
10530 }
10531 
10532 
10533 void drgui_sb_set_page_size(drgui_element* pSBElement, int pageSize)
10534 {
10535  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10536  if (pSB == NULL) {
10537  return;
10538  }
10539 
10540  pSB->pageSize = pageSize;
10541 
10542 
10543  // Make sure the scroll position is still valid.
10544  drgui_sb_scroll_to(pSBElement, drgui_sb_get_scroll_position(pSBElement));
10545 
10546  // The thumb may have changed, so refresh it.
10547  drgui_sb_refresh_thumb(pSBElement);
10548 }
10549 
10550 int drgui_sb_get_page_size(drgui_element* pSBElement)
10551 {
10552  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10553  if (pSB == NULL) {
10554  return 0;
10555  }
10556 
10557  return pSB->pageSize;
10558 }
10559 
10560 
10561 void drgui_sb_set_range_and_page_size(drgui_element* pSBElement, int rangeMin, int rangeMax, int pageSize)
10562 {
10563  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10564  if (pSB == NULL) {
10565  return;
10566  }
10567 
10568  pSB->rangeMin = rangeMin;
10569  pSB->rangeMax = rangeMax;
10570  pSB->pageSize = pageSize;
10571 
10572 
10573  // Make sure the scroll position is still valid.
10574  drgui_sb_scroll_to(pSBElement, drgui_sb_get_scroll_position(pSBElement));
10575 
10576  // The thumb may have changed, so refresh it.
10577  drgui_sb_refresh_thumb(pSBElement);
10578 }
10579 
10580 
10581 void drgui_sb_set_scroll_position(drgui_element* pSBElement, int position)
10582 {
10583  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10584  if (pSB == NULL) {
10585  return;
10586  }
10587 
10588  int newScrollPos = drgui_sb_clampi(position, pSB->rangeMin, drgui_sb_maxi(0, pSB->rangeMax - pSB->pageSize + 1));
10589  if (newScrollPos != pSB->scrollPos)
10590  {
10591  pSB->scrollPos = newScrollPos;
10592 
10593  // The position of the thumb has changed, so refresh it.
10594  drgui_sb_refresh_thumb(pSBElement);
10595  }
10596 }
10597 
10599 {
10600  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10601  if (pSB == NULL) {
10602  return 0;
10603  }
10604 
10605  return pSB->scrollPos;
10606 }
10607 
10608 
10609 void drgui_sb_scroll(drgui_element* pSBElement, int offset)
10610 {
10611  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10612  if (pSB == NULL) {
10613  return;
10614  }
10615 
10616  drgui_sb_scroll_to(pSBElement, pSB->scrollPos + offset);
10617 }
10618 
10619 void drgui_sb_scroll_to(drgui_element* pSBElement, int newScrollPos)
10620 {
10621  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10622  if (pSB == NULL) {
10623  return;
10624  }
10625 
10626  int oldScrollPos = pSB->scrollPos;
10627  drgui_sb_set_scroll_position(pSBElement, newScrollPos);
10628 
10629  if (oldScrollPos != pSB->scrollPos)
10630  {
10631  if (pSB->onScroll) {
10632  pSB->onScroll(pSBElement, pSB->scrollPos);
10633  }
10634  }
10635 }
10636 
10637 
10639 {
10640  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10641  if (pSB == NULL) {
10642  return;
10643  }
10644 
10645  if (pSB->autoHideThumb != true)
10646  {
10647  pSB->autoHideThumb = true;
10648 
10649  // The thumb needs to be refreshed in order to show the correct state.
10650  drgui_sb_refresh_thumb(pSBElement);
10651  }
10652 }
10653 
10655 {
10656  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10657  if (pSB == NULL) {
10658  return;
10659  }
10660 
10661  if (pSB->autoHideThumb != false)
10662  {
10663  pSB->autoHideThumb = false;
10664 
10665  // The thumb needs to be refreshed in order to show the correct state.
10666  drgui_sb_refresh_thumb(pSBElement);
10667  }
10668 }
10669 
10671 {
10672  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10673  if (pSB == NULL) {
10674  return false;
10675  }
10676 
10677  return pSB->autoHideThumb;
10678 }
10679 
10680 bool drgui_sb_is_thumb_visible(drgui_element* pSBElement)
10681 {
10682  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10683  if (pSB == NULL) {
10684  return false;
10685  }
10686 
10687  // Always visible if auto-hiding is disabled.
10688  if (!pSB->autoHideThumb) {
10689  return true;
10690  }
10691 
10692  return pSB->pageSize < (pSB->rangeMax - pSB->rangeMin + 1) && pSB->pageSize > 0;
10693 }
10694 
10695 
10696 void drgui_sb_set_mouse_wheel_scele(drgui_element* pSBElement, int scale)
10697 {
10698  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10699  if (pSB == NULL) {
10700  return;
10701  }
10702 
10703  pSB->mouseWheelScale = scale;
10704 }
10705 
10707 {
10708  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10709  if (pSB == NULL) {
10710  return 1;
10711  }
10712 
10713  return pSB->mouseWheelScale;
10714 }
10715 
10716 
10717 void drgui_sb_set_track_color(drgui_element* pSBElement, drgui_color color)
10718 {
10719  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10720  if (pSB == NULL) {
10721  return;
10722  }
10723 
10724  pSB->trackColor = color;
10725 }
10726 
10728 {
10729  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10730  if (pSB == NULL) {
10731  return;
10732  }
10733 
10734  pSB->thumbColor = color;
10735 }
10736 
10738 {
10739  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10740  if (pSB == NULL) {
10741  return;
10742  }
10743 
10744  pSB->thumbColorHovered = color;
10745 }
10746 
10748 {
10749  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10750  if (pSB == NULL) {
10751  return;
10752  }
10753 
10754  pSB->thumbColorPressed = color;
10755 }
10756 
10757 
10759 {
10760  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10761  if (pSB == NULL) {
10762  return;
10763  }
10764 
10765  pSB->onScroll = onScroll;
10766 }
10767 
10769 {
10770  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10771  if (pSB == NULL) {
10772  return NULL;
10773  }
10774 
10775  return pSB->onScroll;
10776 }
10777 
10778 
10780 {
10781  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10782  if (pSB == NULL) {
10783  return drgui_make_rect(0, 0, 0, 0);
10784  }
10785 
10786  drgui_rect rect = {0, 0, 0, 0};
10787  rect.left = pSB->thumbPadding;
10788  rect.top = pSB->thumbPadding;
10789 
10790  if (pSB->orientation == drgui_sb_orientation_vertical)
10791  {
10792  // Vertical.
10793  rect.left = pSB->thumbPadding;
10794  rect.right = drgui_get_width(pSBElement) - pSB->thumbPadding;
10795  rect.top = pSB->thumbPadding + pSB->thumbPos;
10796  rect.bottom = rect.top + pSB->thumbSize;
10797  }
10798  else
10799  {
10800  // Horizontal.
10801  rect.left = pSB->thumbPadding + pSB->thumbPos;
10802  rect.right = rect.left + pSB->thumbSize;
10803  rect.top = pSB->thumbPadding;
10804  rect.bottom = drgui_get_height(pSBElement) - pSB->thumbPadding;
10805  }
10806 
10807  return rect;
10808 }
10809 
10810 
10811 void drgui_sb_on_size(drgui_element* pSBElement, float newWidth, float newHeight)
10812 {
10813  (void)newWidth;
10814  (void)newHeight;
10815 
10816  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10817  if (pSB == NULL) {
10818  return;
10819  }
10820 
10821  drgui_sb_refresh_thumb(pSBElement);
10822 }
10823 
10824 void drgui_sb_on_mouse_leave(drgui_element* pSBElement)
10825 {
10826  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10827  if (pSB == NULL) {
10828  return;
10829  }
10830 
10831  bool needsRedraw = false;
10832  if (pSB->thumbHovered)
10833  {
10834  needsRedraw = true;
10835  pSB->thumbHovered = false;
10836  }
10837 
10838  if (pSB->thumbPressed)
10839  {
10840  needsRedraw = true;
10841  pSB->thumbPressed = false;
10842  }
10843 
10844  if (needsRedraw) {
10845  drgui_dirty(pSBElement, drgui_sb_get_thumb_rect(pSBElement));
10846  }
10847 }
10848 
10849 void drgui_sb_on_mouse_move(drgui_element* pSBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
10850 {
10851  (void)stateFlags;
10852 
10853  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10854  if (pSB == NULL) {
10855  return;
10856  }
10857 
10858  if (pSB->thumbPressed)
10859  {
10860  // The thumb is pressed. Drag it.
10861  float thumbRelativeMousePosX = (float)relativeMousePosX;
10862  float thumbRelativeMousePosY = (float)relativeMousePosY;
10863  drgui_sb_make_relative_to_thumb(pSBElement, &thumbRelativeMousePosX, &thumbRelativeMousePosY);
10864 
10865  float dragOffsetX = thumbRelativeMousePosX - pSB->thumbClickPosX;
10866  float dragOffsetY = thumbRelativeMousePosY - pSB->thumbClickPosY;
10867 
10868  float destTrackPos = pSB->thumbPos;
10869  if (pSB->orientation == drgui_sb_orientation_vertical) {
10870  destTrackPos += dragOffsetY;
10871  } else {
10872  destTrackPos += dragOffsetX;
10873  }
10874 
10875  int destScrollPos = drgui_sb_calculate_scroll_pos_from_thumb_pos(pSBElement, destTrackPos);
10876  if (destScrollPos != pSB->scrollPos)
10877  {
10878  drgui_sb_scroll_to(pSBElement, destScrollPos);
10879  }
10880  }
10881  else
10882  {
10883  // The thumb is not pressed. We just need to check if the hovered state has change and redraw if required.
10884  if (drgui_sb_is_thumb_visible(pSBElement))
10885  {
10886  bool wasThumbHovered = pSB->thumbHovered;
10887 
10888  drgui_rect thumbRect = drgui_sb_get_thumb_rect(pSBElement);
10889  pSB->thumbHovered = drgui_rect_contains_point(thumbRect, (float)relativeMousePosX, (float)relativeMousePosY);
10890 
10891  if (wasThumbHovered != pSB->thumbHovered) {
10892  drgui_dirty(pSBElement, thumbRect);
10893  }
10894  }
10895  }
10896 }
10897 
10898 void drgui_sb_on_mouse_button_down(drgui_element* pSBElement, int button, int relativeMousePosX, int relativeMousePosY, int stateFlags)
10899 {
10900  (void)stateFlags;
10901 
10902  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10903  if (pSB == NULL) {
10904  return;
10905  }
10906 
10907  if (button == DRGUI_MOUSE_BUTTON_LEFT)
10908  {
10909  if (drgui_sb_is_thumb_visible(pSBElement))
10910  {
10911  drgui_rect thumbRect = drgui_sb_get_thumb_rect(pSBElement);
10912  if (drgui_rect_contains_point(thumbRect, (float)relativeMousePosX, (float)relativeMousePosY))
10913  {
10914  if (!pSB->thumbPressed)
10915  {
10916  drgui_capture_mouse(pSBElement);
10917  pSB->thumbPressed = true;
10918 
10919  pSB->thumbClickPosX = (float)relativeMousePosX;
10920  pSB->thumbClickPosY = (float)relativeMousePosY;
10921  drgui_sb_make_relative_to_thumb(pSBElement, &pSB->thumbClickPosX, &pSB->thumbClickPosY);
10922 
10923  drgui_dirty(pSBElement, drgui_sb_get_thumb_rect(pSBElement));
10924  }
10925  }
10926  else
10927  {
10928  // If the click position is above the thumb we want to scroll up by a page. If it's below the thumb, we scroll down by a page.
10929  if (relativeMousePosY < thumbRect.top) {
10930  drgui_sb_scroll(pSBElement, -drgui_sb_get_page_size(pSBElement));
10931  } else if (relativeMousePosY >= thumbRect.bottom) {
10932  drgui_sb_scroll(pSBElement, drgui_sb_get_page_size(pSBElement));
10933  }
10934  }
10935  }
10936  }
10937 }
10938 
10939 void drgui_sb_on_mouse_button_up(drgui_element* pSBElement, int button, int relativeMousePosX, int relativeMousePosY, int stateFlags)
10940 {
10941  (void)relativeMousePosX;
10942  (void)relativeMousePosY;
10943  (void)stateFlags;
10944 
10945  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10946  if (pSB == NULL) {
10947  return;
10948  }
10949 
10950  if (button == DRGUI_MOUSE_BUTTON_LEFT)
10951  {
10952  if (pSB->thumbPressed && drgui_get_element_with_mouse_capture(pSBElement->pContext) == pSBElement)
10953  {
10954  drgui_release_mouse(pSBElement->pContext);
10955  pSB->thumbPressed = false;
10956 
10957  drgui_dirty(pSBElement, drgui_sb_get_thumb_rect(pSBElement));
10958  }
10959  }
10960 }
10961 
10962 void drgui_sb_on_mouse_wheel(drgui_element* pSBElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags)
10963 {
10964  (void)relativeMousePosX;
10965  (void)relativeMousePosY;
10966  (void)stateFlags;
10967 
10968  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10969  if (pSB == NULL) {
10970  return;
10971  }
10972 
10973  drgui_sb_scroll(pSBElement, -delta * drgui_sb_get_mouse_wheel_scale(pSBElement));
10974 }
10975 
10976 void drgui_sb_on_paint(drgui_element* pSBElement, drgui_rect relativeClippingRect, void* pPaintData)
10977 {
10978  (void)relativeClippingRect;
10979 
10980  const drgui_scrollbar* pSB = (const drgui_scrollbar*)drgui_get_extra_data(pSBElement);
10981  if (pSB == NULL) {
10982  return;
10983  }
10984 
10985  drgui_rect thumbRect = drgui_sb_get_thumb_rect(pSBElement);
10986 
10987  if (drgui_sb_is_thumb_visible(pSBElement))
10988  {
10989  // The thumb is visible.
10990 
10991  // Track. We draw this in 4 seperate pieces so we can avoid overdraw with the thumb.
10992  drgui_draw_rect(pSBElement, drgui_make_rect(0, 0, drgui_get_width(pSBElement), thumbRect.top), pSB->trackColor, pPaintData); // Top
10993  drgui_draw_rect(pSBElement, drgui_make_rect(0, thumbRect.bottom, drgui_get_width(pSBElement), drgui_get_height(pSBElement)), pSB->trackColor, pPaintData); // Bottom
10994  drgui_draw_rect(pSBElement, drgui_make_rect(0, thumbRect.top, thumbRect.left, thumbRect.bottom), pSB->trackColor, pPaintData); // Left
10995  drgui_draw_rect(pSBElement, drgui_make_rect(thumbRect.right, thumbRect.top, drgui_get_width(pSBElement), thumbRect.bottom), pSB->trackColor, pPaintData); // Right
10996 
10997  // Thumb.
10998  drgui_color thumbColor;
10999  if (pSB->thumbPressed) {
11000  thumbColor = pSB->thumbColorPressed;
11001  } else if (pSB->thumbHovered) {
11002  thumbColor = pSB->thumbColorHovered;
11003  } else {
11004  thumbColor = pSB->thumbColor;
11005  }
11006 
11007  drgui_draw_rect(pSBElement, thumbRect, thumbColor, pPaintData);
11008  }
11009  else
11010  {
11011  // The thumb is not visible - just draw the track as one quad.
11012  drgui_draw_rect(pSBElement, drgui_get_local_rect(pSBElement), pSB->trackColor, pPaintData);
11013  }
11014 }
11015 
11016 
11017 
11018 DRGUI_PRIVATE void drgui_sb_refresh_thumb(drgui_element* pSBElement)
11019 {
11020  drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
11021  assert(pSB != NULL);
11022 
11023  drgui_rect oldThumbRect = drgui_sb_get_thumb_rect(pSBElement);
11024 
11025  pSB->thumbSize = drgui_sb_calculate_thumb_size(pSBElement);
11026  pSB->thumbPos = drgui_sb_calculate_thumb_position(pSBElement);
11027 
11028  drgui_rect newThumbRect = drgui_sb_get_thumb_rect(pSBElement);
11029  if (!drgui_rect_equal(oldThumbRect, newThumbRect))
11030  {
11031  drgui_dirty(pSBElement, drgui_rect_union(oldThumbRect, newThumbRect));
11032  }
11033 }
11034 
11035 DRGUI_PRIVATE float drgui_sb_calculate_thumb_size(drgui_element* pSBElement)
11036 {
11037  const drgui_scrollbar* pSB = (const drgui_scrollbar*)drgui_get_extra_data(pSBElement);
11038  assert(pSB != NULL);
11039 
11040  float trackSize = drgui_sb_get_track_size(pSBElement);
11041  float range = (float)(pSB->rangeMax - pSB->rangeMin + 1);
11042 
11043  float thumbSize = DRGUI_MIN_SCROLLBAR_THUMB_SIZE;
11044  if (range > 0)
11045  {
11046  thumbSize = roundf((trackSize / range) * pSB->pageSize);
11047  thumbSize = drgui_sb_clampf(thumbSize, DRGUI_MIN_SCROLLBAR_THUMB_SIZE, trackSize);
11048  }
11049 
11050  return thumbSize;
11051 }
11052 
11053 DRGUI_PRIVATE float drgui_sb_calculate_thumb_position(drgui_element* pSBElement)
11054 {
11055  const drgui_scrollbar* pSB = (const drgui_scrollbar*)drgui_get_extra_data(pSBElement);
11056  assert(pSB != NULL);
11057 
11058  float trackSize = drgui_sb_get_track_size(pSBElement);
11059  float thumbSize = drgui_sb_calculate_thumb_size(pSBElement);
11060  float range = (float)(pSB->rangeMax - pSB->rangeMin + 1);
11061 
11062  float thumbPos = 0;
11063  if (range > pSB->pageSize)
11064  {
11065  thumbPos = roundf((trackSize / range) * pSB->scrollPos);
11066  thumbPos = drgui_sb_clampf(thumbPos, 0, trackSize - thumbSize);
11067  }
11068 
11069  return thumbPos;
11070 }
11071 
11072 DRGUI_PRIVATE float drgui_sb_get_track_size(drgui_element* pSBElement)
11073 {
11074  const drgui_scrollbar* pSB = (const drgui_scrollbar*)drgui_get_extra_data(pSBElement);
11075  assert(pSB != NULL);
11076 
11077  if (pSB->orientation == drgui_sb_orientation_vertical) {
11078  return drgui_get_height(pSBElement) - (pSB->thumbPadding*2);
11079  } else {
11080  return drgui_get_width(pSBElement) - (pSB->thumbPadding*2);
11081  }
11082 }
11083 
11084 DRGUI_PRIVATE void drgui_sb_make_relative_to_thumb(drgui_element* pSBElement, float* pPosX, float* pPosY)
11085 {
11086  drgui_rect thumbRect = drgui_sb_get_thumb_rect(pSBElement);
11087 
11088  if (pPosX != NULL) {
11089  *pPosX -= thumbRect.left;
11090  }
11091 
11092  if (pPosY != NULL) {
11093  *pPosY -= thumbRect.top;
11094  }
11095 }
11096 
11097 DRGUI_PRIVATE int drgui_sb_calculate_scroll_pos_from_thumb_pos(drgui_element* pSBElement, float thumbPos)
11098 {
11099  const drgui_scrollbar* pSB = (const drgui_scrollbar*)drgui_get_extra_data(pSBElement);
11100  assert(pSB != NULL);
11101 
11102  float trackSize = drgui_sb_get_track_size(pSBElement);
11103  float range = (float)(pSB->rangeMax - pSB->rangeMin + 1);
11104 
11105  return (int)roundf(thumbPos / (trackSize / range));
11106 }
11107 #endif //DR_GUI_IMPLEMENTATION
11108 
11109 
11110 
11111 
11114 //
11115 // Tab Bar
11116 //
11119 
11120 // QUICK NOTES
11121 //
11122 // - This control is only the tab bar itself - this does not handle tab pages and content switching and whatnot.
11123 
11124 #ifndef drgui_tab_bar_h
11125 #define drgui_tab_bar_h
11126 
11127 #ifdef __cplusplus
11128 extern "C" {
11129 #endif
11130 
11131 #define DRGUI_MAX_TAB_TEXT_LENGTH 256
11132 
11133 typedef enum
11134 {
11140 
11141 typedef struct drgui_tab drgui_tab;
11142 
11143 typedef void (* drgui_tabbar_on_measure_tab_proc) (drgui_element* pTBElement, drgui_tab* pTab, float* pWidthOut, float* pHeightOut);
11144 typedef void (* drgui_tabbar_on_paint_tab_proc) (drgui_element* pTBElement, drgui_tab* pTab, drgui_rect relativeClippingRect, float offsetX, float offsetY, float width, float height, void* pPaintData);
11145 typedef void (* drgui_tabbar_on_tab_activated_proc) (drgui_element* pTBElement, drgui_tab* pTab, drgui_tab* pOldActiveTab);
11146 typedef void (* drgui_tabbar_on_tab_deactivated_proc) (drgui_element* pTBElement, drgui_tab* pTab, drgui_tab* pNewActiveTab);
11147 typedef void (* drgui_tabbar_on_tab_close_proc) (drgui_element* pTBElement, drgui_tab* pTab);
11148 typedef void (* drgui_tabbar_on_tab_mouse_button_up_proc)(drgui_element* pTBElement, drgui_tab* pTab, int mouseButton, int mouseRelativePosX, int mouseRelativePosY, int stateFlags);
11149 
11150 
11152 //
11153 // Tab Bar
11154 //
11156 
11158 drgui_element* drgui_create_tab_bar(drgui_context* pContext, drgui_element* pParent, drgui_tabbar_orientation orientation, size_t extraDataSize, const void* pExtraData);
11159 
11161 void drgui_delete_tab_bar(drgui_element* pTBElement);
11162 
11163 
11166 
11168 void* drgui_tabbar_get_extra_data(drgui_element* pTBElement);
11169 
11172 
11173 
11175 void drgui_tabbar_set_font(drgui_element* pTBElement, drgui_font* pFont);
11176 
11179 
11180 // Sets the color of the text to use on tabs.
11181 void drgui_tabbar_set_text_color(drgui_element* pTBElement, drgui_color color);
11182 
11183 // Retrieves the color of the text to use on tabs.
11185 
11186 // Sets the color of the text to use on active tabs.
11188 
11189 // Sets the color of the text to use on hovered tabs.
11191 
11194 
11197 
11198 // Sets the default color of the close button.
11200 
11201 // Sets the padding to apply the the text of each tab.
11202 void drgui_tabbar_set_tab_padding(drgui_element* pTBElement, float padding);
11203 
11204 // Retrieves the padding to apply to the text of each tab.
11205 float drgui_tabbar_get_tab_padding(drgui_element* pTBElement);
11206 
11207 // Sets the padding to apply the the left of the close button.
11208 void drgui_tabbar_set_close_button_left_padding(drgui_element* pTBElement, float padding);
11209 
11210 // Retrieves the padding to apply to the left of the close button.
11212 
11213 // Sets the default background color of tabs. This is the color of inactive tabs.
11215 
11216 // Retrieves the default background color of tabs while inactive.
11218 
11219 // Sets the background color of tabs while hovered.
11221 
11222 // Retrieves the background color of tabs while hovered.
11224 
11225 // Sets the background color of tabs while activated.
11227 
11228 // Retrieves the background color of tabs while activated.
11230 
11231 
11234 
11237 
11240 
11243 
11246 
11247 // Sets the function to call when a tab has a mouse button released on it.
11249 
11250 
11252 void drgui_tabbar_measure_tab(drgui_element* pTBElement, drgui_tab* pTab, float* pWidthOut, float* pHeightOut);
11253 
11255 void drgui_tabbar_paint_tab(drgui_element* pTBElement, drgui_tab* pTab, drgui_rect relativeClippingRect, float offsetX, float offsetY, float width, float height, void* pPaintData);
11256 
11257 
11265 void drgui_tabbar_resize_by_tabs(drgui_element* pTBElement);
11266 
11275 
11278 
11281 
11282 
11283 // Retrieves a pointer to the first tab in the given tab bar.
11285 
11286 // Retrieves a pointer to the last tab in the given tab bar.
11288 
11289 // Retrieves a pointer to the next tab in the given tab bar.
11291 
11292 // Retrieves a pointer to the previous tab in the given tab bar.
11294 
11295 
11297 void drgui_tabbar_activate_tab(drgui_element* pTBElement, drgui_tab* pTab);
11298 
11299 // Activates the tab to the right of the currently active tab, looping back to the start if necessary.
11301 
11302 // Activates the tab to the left of the currently active tab, looping back to the end if necessary.
11304 
11307 
11308 
11310 bool drgui_tabbar_is_tab_in_view(drgui_element* pTBElement, drgui_tab* pTab);
11311 
11312 
11315 
11318 
11321 
11324 
11327 
11328 
11330 void drgui_tabbar_on_mouse_leave(drgui_element* pTBElement);
11331 
11333 void drgui_tabbar_on_mouse_move(drgui_element* pTBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags);
11334 
11336 void drgui_tabbar_on_mouse_button_down(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
11337 
11339 void drgui_tabbar_on_mouse_button_up(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
11340 
11342 void drgui_tabbar_on_paint(drgui_element* pTBElement, drgui_rect relativeClippingRect, void* pPaintData);
11343 
11344 
11345 
11346 
11348 //
11349 // Tab
11350 //
11352 
11354 drgui_tab* drgui_tabbar_create_and_append_tab(drgui_element* pTBElement, const char* text, size_t extraDataSize, const void* pExtraData);
11355 
11357 drgui_tab* drgui_tabbar_create_and_prepend_tab(drgui_element* pTBElement, const char* text, size_t extraDataSize, const void* pExtraData);
11358 
11360 void drgui_tab_delete(drgui_tab* pTab);
11361 
11364 
11367 
11369 void* drgui_tab_get_extra_data(drgui_tab* pTab);
11370 
11371 
11373 void drgui_tab_set_text(drgui_tab* pTab, const char* text);
11374 
11376 const char* drgui_tab_get_text(drgui_tab* pTab);
11377 
11378 
11381 
11384 
11385 
11388 
11390 bool drgui_tab_is_in_view(drgui_tab* pTab);
11391 
11396 
11397 
11398 #ifdef __cplusplus
11399 }
11400 #endif
11401 #endif //drgui_tab_bar_h
11402 
11403 
11404 #ifdef DR_GUI_IMPLEMENTATION
11405 typedef struct drgui_tab_bar drgui_tab_bar;
11406 
11407 struct drgui_tab_bar
11408 {
11410  drgui_tabbar_orientation orientation;
11411 
11412 
11414  drgui_tab* pFirstTab;
11415 
11417  drgui_tab* pLastTab;
11418 
11419 
11421  drgui_tab* pHoveredTab;
11422 
11424  drgui_tab* pActiveTab;
11425 
11427  drgui_tab* pTabWithCloseButtonPressed;
11428 
11429 
11431  drgui_font* pFont;
11432 
11434  drgui_color tabTextColor;
11435 
11437  drgui_color tabTextColorActivated;
11438 
11440  drgui_color tabTextColorHovered;
11441 
11443  drgui_color tabBackgroundColor;
11444 
11446  drgui_color tabBackgroundColorHovered;
11447 
11449  drgui_color tabBackbroundColorActivated;
11450 
11452  float tabPadding;
11453 
11455  drgui_image* pCloseButtonImage;
11456 
11458  float closeButtonPaddingLeft;
11459 
11461  drgui_color closeButtonColorDefault;
11462 
11464  drgui_color closeButtonColorTabHovered;
11465 
11467  drgui_color closeButtonColorHovered;
11468 
11470  drgui_color closeButtonColorPressed;
11471 
11472 
11474  bool isAutoSizeEnabled;
11475 
11477  bool isShowingCloseButton;
11478 
11480  bool isCloseOnMiddleClickEnabled;
11481 
11483  bool isCloseButtonHovered;
11484 
11485 
11487  drgui_tabbar_on_measure_tab_proc onMeasureTab;
11488 
11490  drgui_tabbar_on_paint_tab_proc onPaintTab;
11491 
11493  drgui_tabbar_on_tab_activated_proc onTabActivated;
11494 
11496  drgui_tabbar_on_tab_deactivated_proc onTabDeactivated;
11497 
11499  drgui_tabbar_on_tab_close_proc onTabClose;
11500 
11501  // The function to call when a mouse button is released while over a tab.
11502  drgui_tabbar_on_tab_mouse_button_up_proc onTabMouseButtonUp;
11503 
11504 
11506  size_t extraDataSize;
11507 
11509  char pExtraData[1];
11510 };
11511 
11512 struct drgui_tab
11513 {
11515  drgui_element* pTBElement;
11516 
11518  drgui_tab* pNextTab;
11519 
11521  drgui_tab* pPrevTab;
11522 
11523 
11525  char text[DRGUI_MAX_TAB_TEXT_LENGTH];
11526 
11527 
11529  size_t extraDataSize;
11530 
11532  char pExtraData[1];
11533 };
11534 
11535 
11537 //
11538 // Tab
11539 //
11541 
11543 DRGUI_PRIVATE void drgui_tabbar_on_measure_tab_default(drgui_element* pTBElement, drgui_tab* pTab, float* pWidthOut, float* pHeightOut);
11544 
11546 DRGUI_PRIVATE void drgui_tabbar_on_paint_tab_default(drgui_element* pTBElement, drgui_tab* pTab, drgui_rect relativeClippingRect, float offsetX, float offsetY, float width, float height, void* pPaintData);
11547 
11549 DRGUI_PRIVATE drgui_tab* drgui_tabbar_find_tab_under_point(drgui_element* pTBElement, float relativePosX, float relativePosY, bool* pIsOverCloseButtonOut);
11550 
11551 drgui_element* drgui_create_tab_bar(drgui_context* pContext, drgui_element* pParent, drgui_tabbar_orientation orientation, size_t extraDataSize, const void* pExtraData)
11552 {
11553  if (pContext == NULL) {
11554  return NULL;
11555  }
11556 
11557  drgui_element* pTBElement = drgui_create_element(pContext, pParent, sizeof(drgui_tab_bar) + extraDataSize, NULL);
11558  if (pTBElement == NULL) {
11559  return NULL;
11560  }
11561 
11562  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11563  assert(pTB != NULL);
11564 
11565  pTB->orientation = orientation;
11566  pTB->pFirstTab = NULL;
11567  pTB->pLastTab = NULL;
11568  pTB->pHoveredTab = NULL;
11569  pTB->pActiveTab = NULL;
11570  pTB->pTabWithCloseButtonPressed = NULL;
11571 
11572  pTB->pFont = NULL;
11573  pTB->tabTextColor = drgui_rgb(224, 224, 224);
11574  pTB->tabTextColorActivated = drgui_rgb(224, 224, 224);
11575  pTB->tabTextColorHovered = drgui_rgb(224, 224, 224);
11576  pTB->tabBackgroundColor = drgui_rgb(58, 58, 58);
11577  pTB->tabBackgroundColorHovered = drgui_rgb(16, 92, 160);
11578  pTB->tabBackbroundColorActivated = drgui_rgb(32, 128, 192); //drgui_rgb(80, 80, 80);
11579  pTB->tabPadding = 4;
11580  pTB->pCloseButtonImage = NULL;
11581  pTB->closeButtonPaddingLeft = 6;
11582  pTB->closeButtonColorDefault = pTB->tabBackgroundColor;
11583  pTB->closeButtonColorTabHovered = drgui_rgb(192, 192, 192);
11584  pTB->closeButtonColorHovered = drgui_rgb(255, 96, 96);
11585  pTB->closeButtonColorPressed = drgui_rgb(192, 32, 32);
11586  pTB->isAutoSizeEnabled = false;
11587  pTB->isShowingCloseButton = false;
11588  pTB->isCloseOnMiddleClickEnabled = false;
11589  pTB->isCloseButtonHovered = false;
11590 
11591  pTB->onMeasureTab = drgui_tabbar_on_measure_tab_default;
11592  pTB->onPaintTab = drgui_tabbar_on_paint_tab_default;
11593  pTB->onTabActivated = NULL;
11594  pTB->onTabDeactivated = NULL;
11595  pTB->onTabClose = NULL;
11596 
11597 
11598  pTB->extraDataSize = extraDataSize;
11599  if (pExtraData != NULL) {
11600  memcpy(pTB->pExtraData, pExtraData, extraDataSize);
11601  }
11602 
11603 
11604  // Event handlers.
11610 
11611  return pTBElement;
11612 }
11613 
11614 void drgui_delete_tab_bar(drgui_element* pTBElement)
11615 {
11616  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11617  if (pTB == NULL) {
11618  return;
11619  }
11620 
11621  while (pTB->pFirstTab != NULL) {
11622  drgui_tab_delete(pTB->pFirstTab);
11623  }
11624 
11625 
11626  drgui_delete_element(pTBElement);
11627 }
11628 
11629 
11631 {
11632  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11633  if (pTB == NULL) {
11634  return 0;
11635  }
11636 
11637  return pTB->extraDataSize;
11638 }
11639 
11640 void* drgui_tabbar_get_extra_data(drgui_element* pTBElement)
11641 {
11642  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11643  if (pTB == NULL) {
11644  return NULL;
11645  }
11646 
11647  return pTB->pExtraData;
11648 }
11649 
11651 {
11652  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11653  if (pTB == NULL) {
11655  }
11656 
11657  return pTB->orientation;
11658 }
11659 
11660 
11661 void drgui_tabbar_set_font(drgui_element* pTBElement, drgui_font* pFont)
11662 {
11663  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11664  if (pTB == NULL) {
11665  return;
11666  }
11667 
11668  pTB->pFont = pFont;
11669 
11670  // A change in font may have changed the size of the tabbar.
11671  if (pTB->isAutoSizeEnabled) {
11672  drgui_tabbar_resize_by_tabs(pTBElement);
11673  }
11674 
11675  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
11676  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
11677  }
11678 }
11679 
11681 {
11682  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11683  if (pTB == NULL) {
11684  return NULL;
11685  }
11686 
11687  return pTB->pFont;
11688 }
11689 
11690 
11691 void drgui_tabbar_set_text_color(drgui_element* pTBElement, drgui_color color)
11692 {
11693  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11694  if (pTB == NULL) {
11695  return;
11696  }
11697 
11698  pTB->tabTextColor = color;
11699 
11700  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
11701  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
11702  }
11703 }
11704 
11706 {
11707  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11708  if (pTB == NULL) {
11709  return drgui_rgb(0, 0, 0);
11710  }
11711 
11712  return pTB->tabTextColor;
11713 }
11714 
11716 {
11717  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11718  if (pTB == NULL) {
11719  return;
11720  }
11721 
11722  pTB->tabTextColorActivated = color;
11723 
11724  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
11725  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
11726  }
11727 }
11728 
11730 {
11731  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11732  if (pTB == NULL) {
11733  return;
11734  }
11735 
11736  pTB->tabTextColorHovered = color;
11737 
11738  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
11739  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
11740  }
11741 }
11742 
11743 
11745 {
11746  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11747  if (pTB == NULL) {
11748  return;
11749  }
11750 
11751  pTB->pCloseButtonImage = pImage;
11752 
11753  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
11754  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
11755  }
11756 }
11757 
11759 {
11760  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11761  if (pTB == NULL) {
11762  return NULL;
11763  }
11764 
11765  return pTB->pCloseButtonImage;
11766 }
11767 
11769 {
11770  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11771  if (pTB == NULL) {
11772  return;
11773  }
11774 
11775  pTB->closeButtonColorDefault = color;
11776 
11777  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
11778  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
11779  }
11780 }
11781 
11782 
11783 void drgui_tabbar_set_tab_padding(drgui_element* pTBElement, float padding)
11784 {
11785  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11786  if (pTB == NULL) {
11787  return;
11788  }
11789 
11790  pTB->tabPadding = padding;
11791 
11792  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
11793  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
11794  }
11795 }
11796 
11798 {
11799  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11800  if (pTB == NULL) {
11801  return 0;
11802  }
11803 
11804  return pTB->tabPadding;
11805 }
11806 
11807 void drgui_tabbar_set_close_button_left_padding(drgui_element* pTBElement, float padding)
11808 {
11809  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11810  if (pTB == NULL) {
11811  return;
11812  }
11813 
11814  pTB->closeButtonPaddingLeft = padding;
11815 
11816  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
11817  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
11818  }
11819 }
11820 
11822 {
11823  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11824  if (pTB == NULL) {
11825  return 0;
11826  }
11827 
11828  return pTB->closeButtonPaddingLeft;
11829 }
11830 
11831 
11833 {
11834  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11835  if (pTB == NULL) {
11836  return;
11837  }
11838 
11839  pTB->tabBackgroundColor = color;
11840 
11841  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
11842  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
11843  }
11844 }
11845 
11847 {
11848  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11849  if (pTB == NULL) {
11850  return drgui_rgb(0, 0, 0);
11851  }
11852 
11853  return pTB->tabBackgroundColor;
11854 }
11855 
11857 {
11858  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11859  if (pTB == NULL) {
11860  return;
11861  }
11862 
11863  pTB->tabBackgroundColorHovered = color;
11864 
11865  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
11866  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
11867  }
11868 }
11869 
11871 {
11872  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11873  if (pTB == NULL) {
11874  return drgui_rgb(0, 0, 0);
11875  }
11876 
11877  return pTB->tabBackgroundColorHovered;
11878 }
11879 
11881 {
11882  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11883  if (pTB == NULL) {
11884  return;
11885  }
11886 
11887  pTB->tabBackbroundColorActivated = color;
11888 
11889  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
11890  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
11891  }
11892 }
11893 
11895 {
11896  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11897  if (pTB == NULL) {
11898  return drgui_rgb(0, 0, 0);
11899  }
11900 
11901  return pTB->tabBackbroundColorActivated;
11902 }
11903 
11904 
11906 {
11907  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11908  if (pTB == NULL) {
11909  return;
11910  }
11911 
11912  pTB->onMeasureTab = proc;
11913 }
11914 
11916 {
11917  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11918  if (pTB == NULL) {
11919  return;
11920  }
11921 
11922  pTB->onPaintTab = proc;
11923 }
11924 
11926 {
11927  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11928  if (pTB == NULL) {
11929  return;
11930  }
11931 
11932  pTB->onTabActivated = proc;
11933 }
11934 
11936 {
11937  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11938  if (pTB == NULL) {
11939  return;
11940  }
11941 
11942  pTB->onTabDeactivated = proc;
11943 }
11944 
11946 {
11947  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11948  if (pTB == NULL) {
11949  return;
11950  }
11951 
11952  pTB->onTabClose = proc;
11953 }
11954 
11956 {
11957  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11958  if (pTB == NULL) {
11959  return;
11960  }
11961 
11962  pTB->onTabMouseButtonUp = proc;
11963 }
11964 
11965 
11966 void drgui_tabbar_measure_tab(drgui_element* pTBElement, drgui_tab* pTab, float* pWidthOut, float* pHeightOut)
11967 {
11968  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11969  if (pTB == NULL) {
11970  return;
11971  }
11972 
11973  if (pTB->onMeasureTab) {
11974  pTB->onMeasureTab(pTBElement, pTab, pWidthOut, pHeightOut);
11975  }
11976 }
11977 
11978 void drgui_tabbar_paint_tab(drgui_element* pTBElement, drgui_tab* pTab, drgui_rect relativeClippingRect, float offsetX, float offsetY, float width, float height, void* pPaintData)
11979 {
11980  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11981  if (pTB == NULL) {
11982  return;
11983  }
11984 
11985  if (pTB->onPaintTab) {
11986  pTB->onPaintTab(pTBElement, pTab, relativeClippingRect, offsetX, offsetY, width, height, pPaintData);
11987  }
11988 }
11989 
11990 
11992 {
11993  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
11994  if (pTB == NULL) {
11995  return;
11996  }
11997 
11998  if (pTB->onMeasureTab == NULL) {
11999  return;
12000  }
12001 
12002  float maxWidth = 0;
12003  float maxHeight = 0;
12004  if (pTB->pFirstTab == NULL) {
12005  // There are no tabs. Set initial size based on the line height of the font.
12006  drgui_font_metrics fontMetrics;
12007  if (drgui_get_font_metrics(pTB->pFont, &fontMetrics)) {
12008  if (pTB->orientation == drgui_tabbar_orientation_top || pTB->orientation == drgui_tabbar_orientation_bottom) {
12009  maxHeight = fontMetrics.lineHeight + (pTB->tabPadding*2);
12010  } else {
12011  maxWidth = fontMetrics.lineHeight + (pTB->tabPadding*2);
12012  }
12013  }
12014  } else {
12015  for (drgui_tab* pTab = pTB->pFirstTab; pTab != NULL; pTab = pTab->pNextTab) {
12016  float tabWidth = 0;
12017  float tabHeight = 0;
12018  drgui_tabbar_measure_tab(pTBElement, pTab, &tabWidth, &tabHeight);
12019 
12020  maxWidth = (tabWidth > maxWidth) ? tabWidth : maxWidth;
12021  maxHeight = (tabHeight > maxHeight) ? tabHeight : maxHeight;
12022  }
12023  }
12024 
12025 
12026 
12027 
12028  if (pTB->orientation == drgui_tabbar_orientation_top || pTB->orientation == drgui_tabbar_orientation_bottom) {
12029  drgui_set_size(pTBElement, drgui_get_width(pTBElement), maxHeight);
12030  } else {
12031  drgui_set_size(pTBElement, maxWidth, drgui_get_height(pTBElement));
12032  }
12033 }
12034 
12036 {
12037  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12038  if (pTB == NULL) {
12039  return;
12040  }
12041 
12042  pTB->isAutoSizeEnabled = true;
12043 }
12044 
12046 {
12047  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12048  if (pTB == NULL) {
12049  return;
12050  }
12051 
12052  pTB->isAutoSizeEnabled = false;
12053 }
12054 
12056 {
12057  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12058  if (pTB == NULL) {
12059  return false;
12060  }
12061 
12062  return pTB->isAutoSizeEnabled;
12063 }
12064 
12065 
12067 {
12068  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12069  if (pTB == NULL) {
12070  return NULL;
12071  }
12072 
12073  return pTB->pFirstTab;
12074 }
12075 
12077 {
12078  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12079  if (pTB == NULL) {
12080  return NULL;
12081  }
12082 
12083  return pTB->pLastTab;
12084 }
12085 
12087 {
12088  (void)pTBElement;
12089  return drgui_tab_get_next_tab(pTab);
12090 }
12091 
12093 {
12094  (void)pTBElement;
12095  return drgui_tab_get_prev_tab(pTab);
12096 }
12097 
12098 
12099 void drgui_tabbar_activate_tab(drgui_element* pTBElement, drgui_tab* pTab)
12100 {
12101  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12102  if (pTB == NULL) {
12103  return;
12104  }
12105 
12106  drgui_tab* pOldActiveTab = pTB->pActiveTab;
12107  drgui_tab* pNewActiveTab = pTab;
12108 
12109  if (pOldActiveTab == pNewActiveTab) {
12110  return; // The tab is already active - nothing to do.
12111  }
12112 
12113 
12114  pTB->pActiveTab = pNewActiveTab;
12115 
12116  if (pTB->onTabDeactivated && pOldActiveTab != NULL) {
12117  pTB->onTabDeactivated(pTBElement, pOldActiveTab, pNewActiveTab);
12118  }
12119 
12120  if (pTB->onTabActivated && pNewActiveTab != NULL) {
12121  pTB->onTabActivated(pTBElement, pNewActiveTab, pOldActiveTab);
12122  }
12123 
12124 
12125  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
12126  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
12127  }
12128 }
12129 
12131 {
12132  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12133  if (pTB == NULL) {
12134  return;
12135  }
12136 
12137  if (pTB->pActiveTab == NULL) {
12138  drgui_tabbar_activate_tab(pTBElement, pTB->pFirstTab);
12139  return;
12140  }
12141 
12142 
12143  drgui_tab* pNextTab = pTB->pActiveTab->pNextTab;
12144  if (pNextTab == NULL) {
12145  pNextTab = pTB->pFirstTab;
12146  }
12147 
12148  drgui_tabbar_activate_tab(pTBElement, pNextTab);
12149 }
12150 
12152 {
12153  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12154  if (pTB == NULL) {
12155  return;
12156  }
12157 
12158  if (pTB->pActiveTab == NULL) {
12159  drgui_tabbar_activate_tab(pTBElement, pTB->pLastTab);
12160  return;
12161  }
12162 
12163 
12164  drgui_tab* pPrevTab = pTB->pActiveTab->pPrevTab;
12165  if (pPrevTab == NULL) {
12166  pPrevTab = pTB->pLastTab;
12167  }
12168 
12169  drgui_tabbar_activate_tab(pTBElement, pPrevTab);
12170 }
12171 
12173 {
12174  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12175  if (pTB == NULL) {
12176  return NULL;
12177  }
12178 
12179  return pTB->pActiveTab;
12180 }
12181 
12182 
12183 bool drgui_tabbar_is_tab_in_view(drgui_element* pTBElement, drgui_tab* pTabIn)
12184 {
12185  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12186  if (pTB == NULL) {
12187  return false;
12188  }
12189 
12190  float tabbarWidth = 0;
12191  float tabbarHeight = 0;
12192  drgui_get_size(pTBElement, &tabbarWidth, &tabbarHeight);
12193 
12194 
12195  // Each tab.
12196  float runningPosX = 0;
12197  float runningPosY = 0;
12198  for (drgui_tab* pTab = pTB->pFirstTab; pTab != NULL; pTab = pTab->pNextTab)
12199  {
12200  float tabWidth = 0;
12201  float tabHeight = 0;
12202  drgui_tabbar_measure_tab(pTBElement, pTab, &tabWidth, &tabHeight);
12203 
12204  if (pTab == pTabIn) {
12205  return runningPosX + tabWidth <= tabbarWidth && runningPosY + tabHeight <= tabbarHeight;
12206  }
12207 
12208 
12209  if (pTB->orientation == drgui_tabbar_orientation_top || pTB->orientation == drgui_tabbar_orientation_bottom) {
12210  runningPosX += tabWidth;
12211  } else {
12212  runningPosY += tabHeight;
12213  }
12214  }
12215 
12216  return false;
12217 }
12218 
12219 
12221 {
12222  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12223  if (pTB == NULL) {
12224  return;
12225  }
12226 
12227  pTB->isShowingCloseButton = true;
12228 
12229  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
12230  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
12231  }
12232 }
12233 
12235 {
12236  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12237  if (pTB == NULL) {
12238  return;
12239  }
12240 
12241  pTB->isShowingCloseButton = false;
12242 
12243  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
12244  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
12245  }
12246 }
12247 
12248 
12250 {
12251  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12252  if (pTB == NULL) {
12253  return;
12254  }
12255 
12256  pTB->isCloseOnMiddleClickEnabled = true;
12257 }
12258 
12260 {
12261  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12262  if (pTB == NULL) {
12263  return;
12264  }
12265 
12266  pTB->isCloseOnMiddleClickEnabled = false;
12267 }
12268 
12270 {
12271  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12272  if (pTB == NULL) {
12273  return false;
12274  }
12275 
12276  return pTB->isCloseOnMiddleClickEnabled;
12277 }
12278 
12279 
12281 {
12282  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12283  if (pTB == NULL) {
12284  return;
12285  }
12286 
12287  if (pTB->pHoveredTab != NULL)
12288  {
12289  pTB->pHoveredTab = NULL;
12290  pTB->isCloseButtonHovered = false;
12291 
12292  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
12293  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
12294  }
12295  }
12296 }
12297 
12298 void drgui_tabbar_on_mouse_move(drgui_element* pTBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
12299 {
12300  (void)stateFlags;
12301 
12302  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12303  if (pTB == NULL) {
12304  return;
12305  }
12306 
12307  bool isCloseButtonHovered = false;
12308 
12309  drgui_tab* pOldHoveredTab = pTB->pHoveredTab;
12310  drgui_tab* pNewHoveredTab = drgui_tabbar_find_tab_under_point(pTBElement, (float)relativeMousePosX, (float)relativeMousePosY, &isCloseButtonHovered);
12311 
12312  if (pOldHoveredTab != pNewHoveredTab || pTB->isCloseButtonHovered != isCloseButtonHovered)
12313  {
12314  pTB->pHoveredTab = pNewHoveredTab;
12315  pTB->isCloseButtonHovered = isCloseButtonHovered;
12316 
12317  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
12318  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
12319  }
12320  }
12321 }
12322 
12323 void drgui_tabbar_on_mouse_button_down(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
12324 {
12325  (void)stateFlags;
12326 
12327  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12328  if (pTB == NULL) {
12329  return;
12330  }
12331 
12332  if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT || mouseButton == DRGUI_MOUSE_BUTTON_RIGHT)
12333  {
12334  bool isOverCloseButton = false;
12335 
12336  drgui_tab* pOldActiveTab = pTB->pActiveTab;
12337  drgui_tab* pNewActiveTab = drgui_tabbar_find_tab_under_point(pTBElement, (float)relativeMousePosX, (float)relativeMousePosY, &isOverCloseButton);
12338 
12339  if (pNewActiveTab != NULL && pOldActiveTab != pNewActiveTab && !isOverCloseButton) {
12340  drgui_tabbar_activate_tab(pTBElement, pNewActiveTab);
12341  }
12342 
12343  if (isOverCloseButton && mouseButton == DRGUI_MOUSE_BUTTON_LEFT) {
12344  pTB->pTabWithCloseButtonPressed = pNewActiveTab;
12345 
12346  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
12347  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
12348  }
12349  }
12350  }
12351  else if (mouseButton == DRGUI_MOUSE_BUTTON_MIDDLE)
12352  {
12353  if (pTB->isCloseOnMiddleClickEnabled)
12354  {
12355  drgui_tab* pHoveredTab = drgui_tabbar_find_tab_under_point(pTBElement, (float)relativeMousePosX, (float)relativeMousePosY, NULL);
12356  if (pHoveredTab != NULL) {
12357  if (pTB->onTabClose) {
12358  pTB->onTabClose(pTBElement, pHoveredTab);
12359  }
12360  }
12361  }
12362  }
12363 }
12364 
12365 void drgui_tabbar_on_mouse_button_up(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
12366 {
12367  (void)stateFlags;
12368 
12369  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12370  if (pTB == NULL) {
12371  return;
12372  }
12373 
12374 
12375  bool releasedOverCloseButton = false;
12376  drgui_tab* pTabUnderMouse = drgui_tabbar_find_tab_under_point(pTBElement, (float)relativeMousePosX, (float)relativeMousePosY, &releasedOverCloseButton);
12377 
12378  if (pTB->pTabWithCloseButtonPressed != NULL && mouseButton == DRGUI_MOUSE_BUTTON_LEFT)
12379  {
12380  if (releasedOverCloseButton && pTabUnderMouse == pTB->pTabWithCloseButtonPressed) {
12381  if (pTB->onTabClose) {
12382  pTB->onTabClose(pTBElement, pTB->pTabWithCloseButtonPressed);
12383  }
12384  }
12385 
12386 
12387  pTB->pTabWithCloseButtonPressed = NULL;
12388 
12389  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
12390  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
12391  }
12392  }
12393  else
12394  {
12395  if (!releasedOverCloseButton && pTB->onTabMouseButtonUp) {
12396  // TODO: Improve this by passing the mouse position relative to the tab. Currently it is relative to the tab BAR. Can have
12397  // the drgui_tabbar_find_tab_under_point() function return the position relative to the tab.
12398  pTB->onTabMouseButtonUp(pTBElement, pTabUnderMouse, mouseButton, relativeMousePosX, relativeMousePosY, stateFlags);
12399  }
12400  }
12401 }
12402 
12403 void drgui_tabbar_on_paint(drgui_element* pTBElement, drgui_rect relativeClippingRect, void* pPaintData)
12404 {
12405  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12406  if (pTB == NULL) {
12407  return;
12408  }
12409 
12410 
12411  float tabbarWidth = 0;
12412  float tabbarHeight = 0;
12413  drgui_get_size(pTBElement, &tabbarWidth, &tabbarHeight);
12414 
12415 
12416  // Each tab.
12417  float runningPosX = 0;
12418  float runningPosY = 0;
12419  for (drgui_tab* pTab = pTB->pFirstTab; pTab != NULL; pTab = pTab->pNextTab)
12420  {
12421  float tabWidth = 0;
12422  float tabHeight = 0;
12423  drgui_tabbar_measure_tab(pTBElement, pTab, &tabWidth, &tabHeight);
12424 
12425  // If a part of the tab is out of bounds, stop drawing.
12426  if (runningPosX > tabbarWidth || runningPosY > tabbarHeight) {
12427  break;
12428  }
12429 
12430 
12431  drgui_tabbar_paint_tab(pTBElement, pTab, relativeClippingRect, runningPosX, runningPosY, tabWidth, tabHeight, pPaintData);
12432 
12433  // After painting the tab, there may be a region of the background that was not drawn by the tab painting callback. We'll need to
12434  // draw that here.
12435  if (pTB->orientation == drgui_tabbar_orientation_top || pTB->orientation == drgui_tabbar_orientation_bottom) {
12436  drgui_draw_rect(pTBElement, drgui_make_rect(runningPosX, runningPosY + tabHeight, tabbarWidth, tabbarHeight), pTB->tabBackgroundColor, pPaintData);
12437  } else {
12438  drgui_draw_rect(pTBElement, drgui_make_rect(runningPosX + tabWidth, runningPosY, tabbarWidth, runningPosY + tabHeight), pTB->tabBackgroundColor, pPaintData);
12439  }
12440 
12441 
12442 
12443  if (pTB->orientation == drgui_tabbar_orientation_top || pTB->orientation == drgui_tabbar_orientation_bottom) {
12444  runningPosX += tabWidth;
12445  } else {
12446  runningPosY += tabHeight;
12447  }
12448  }
12449 
12450 
12451  // Background. We just draw a quad around the region that is not covered by items.
12452  drgui_draw_rect(pTBElement, drgui_make_rect(runningPosX, runningPosY, tabbarWidth, tabbarHeight), pTB->tabBackgroundColor, pPaintData);
12453 }
12454 
12455 
12456 DRGUI_PRIVATE void drgui_tabbar_on_measure_tab_default(drgui_element* pTBElement, drgui_tab* pTab, float* pWidthOut, float* pHeightOut)
12457 {
12458  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12459  if (pTB == NULL) {
12460  return;
12461  }
12462 
12463  float textWidth = 0;
12464  float textHeight = 0;
12465 
12466  if (pTab != NULL) {
12467  drgui_measure_string(pTB->pFont, pTab->text, strlen(pTab->text), &textWidth, &textHeight);
12468  }
12469 
12470 
12471  float closeButtonWidth = 0;
12472  if (pTB->isShowingCloseButton && pTB->pCloseButtonImage != NULL) {
12473  unsigned int closeImageWidth;
12474  drgui_get_image_size(pTB->pCloseButtonImage, &closeImageWidth, NULL);
12475 
12476  closeButtonWidth = closeImageWidth + pTB->closeButtonPaddingLeft;
12477  }
12478 
12479 
12480  if (pWidthOut) {
12481  *pWidthOut = textWidth + closeButtonWidth + pTB->tabPadding*2;
12482  }
12483  if (pHeightOut) {
12484  *pHeightOut = textHeight + pTB->tabPadding*2;
12485  }
12486 }
12487 
12488 DRGUI_PRIVATE void drgui_tabbar_on_paint_tab_default(drgui_element* pTBElement, drgui_tab* pTab, drgui_rect relativeClippingRect, float offsetX, float offsetY, float width, float height, void* pPaintData)
12489 {
12490  (void)relativeClippingRect;
12491 
12492  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12493  if (pTB == NULL) {
12494  return;
12495  }
12496 
12497  // Background.
12498  drgui_color bgcolor = pTB->tabBackgroundColor;
12499  drgui_color closeButtonColor = pTB->closeButtonColorDefault;
12500  drgui_color textColor = pTB->tabTextColor;
12501 
12502  if (pTB->pHoveredTab == pTab) {
12503  bgcolor = pTB->tabBackgroundColorHovered;
12504  closeButtonColor = pTB->closeButtonColorTabHovered;
12505  textColor = pTB->tabTextColorHovered;
12506  }
12507  if (pTB->pActiveTab == pTab) {
12508  bgcolor = pTB->tabBackbroundColorActivated;
12509  closeButtonColor = pTB->closeButtonColorTabHovered;
12510  textColor = pTB->tabTextColorActivated;
12511  }
12512 
12513  if (pTB->pHoveredTab == pTab && pTB->isCloseButtonHovered) {
12514  closeButtonColor = pTB->closeButtonColorHovered;
12515 
12516  if (pTB->pTabWithCloseButtonPressed == pTB->pHoveredTab) {
12517  closeButtonColor = pTB->closeButtonColorPressed;
12518  }
12519  }
12520 
12521  drgui_draw_rect_outline(pTBElement, drgui_make_rect(offsetX, offsetY, offsetX + width, offsetY + height), bgcolor, pTB->tabPadding, pPaintData);
12522 
12523 
12524  // Text.
12525  float textPosX = offsetX + pTB->tabPadding;
12526  float textPosY = offsetY + pTB->tabPadding;
12527  if (pTab != NULL) {
12528  drgui_draw_text(pTBElement, pTB->pFont, pTab->text, (int)strlen(pTab->text), textPosX, textPosY, textColor, bgcolor, pPaintData);
12529  }
12530 
12531 
12532  // Close button.
12533  if (pTB->isShowingCloseButton && pTB->pCloseButtonImage != NULL)
12534  {
12535  float textWidth = 0;
12536  float textHeight = 0;
12537  if (pTab != NULL) {
12538  drgui_measure_string(pTB->pFont, pTab->text, strlen(pTab->text), &textWidth, &textHeight);
12539  }
12540 
12541  float closeButtonPosX = textPosX + textWidth + pTB->closeButtonPaddingLeft;
12542  float closeButtonPosY = textPosY;
12543 
12544  unsigned int iconWidth;
12545  unsigned int iconHeight;
12546  drgui_get_image_size(pTB->pCloseButtonImage, &iconWidth, &iconHeight);
12547 
12549  args.dstX = closeButtonPosX;
12550  args.dstY = closeButtonPosY;
12551  args.dstWidth = (float)iconWidth;
12552  args.dstHeight = (float)iconHeight;
12553  args.srcX = 0;
12554  args.srcY = 0;
12555  args.srcWidth = (float)iconWidth;
12556  args.srcHeight = (float)iconHeight;
12557  args.dstBoundsX = args.dstX;
12558  args.dstBoundsY = args.dstY;
12559  args.dstBoundsWidth = (float)iconWidth;
12560  args.dstBoundsHeight = height - (pTB->tabPadding*2);
12561  args.foregroundTint = closeButtonColor;
12562  args.backgroundColor = bgcolor;
12563  args.boundsColor = bgcolor;
12565  drgui_draw_image(pTBElement, pTB->pCloseButtonImage, &args, pPaintData);
12566 
12567 
12569  drgui_draw_rect(pTBElement, drgui_make_rect(textPosX + textWidth, textPosY, closeButtonPosX, textPosY + textHeight), bgcolor, pPaintData);
12570  }
12571 }
12572 
12573 DRGUI_PRIVATE drgui_tab* drgui_tabbar_find_tab_under_point(drgui_element* pTBElement, float relativePosX, float relativePosY, bool* pIsOverCloseButtonOut)
12574 {
12575  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12576  if (pTB == NULL) {
12577  return NULL;
12578  }
12579 
12580  unsigned int closeButtonWidth;
12581  unsigned int closeButtonHeight;
12582  drgui_get_image_size(pTB->pCloseButtonImage, &closeButtonWidth, &closeButtonHeight);
12583 
12584  float runningPosX = 0;
12585  float runningPosY = 0;
12586  for (drgui_tab* pTab = pTB->pFirstTab; pTab != NULL; pTab = pTab->pNextTab)
12587  {
12588  float tabWidth = 0;
12589  float tabHeight = 0;
12590  drgui_tabbar_measure_tab(pTBElement, pTab, &tabWidth, &tabHeight);
12591 
12592  if (relativePosX >= runningPosX && relativePosX < runningPosX + tabWidth && relativePosY >= runningPosY && relativePosY < runningPosY + tabHeight)
12593  {
12594  if (pIsOverCloseButtonOut)
12595  {
12596  // The close button is in the center, vertically.
12597  drgui_rect closeButtonRect;
12598  closeButtonRect.left = runningPosX + tabWidth - (pTB->tabPadding + closeButtonWidth);
12599  closeButtonRect.right = closeButtonRect.left + closeButtonWidth;
12600  closeButtonRect.top = runningPosY + (tabHeight - (pTB->tabPadding + closeButtonHeight))/2;
12601  closeButtonRect.bottom = closeButtonRect.top + closeButtonHeight;
12602 
12603  if (pTB->isShowingCloseButton && drgui_rect_contains_point(closeButtonRect, relativePosX, relativePosY)) {
12604  *pIsOverCloseButtonOut = true;
12605  } else {
12606  *pIsOverCloseButtonOut = false;
12607  }
12608  }
12609 
12610  return pTab;
12611  }
12612 
12613  if (pTB->orientation == drgui_tabbar_orientation_top || pTB->orientation == drgui_tabbar_orientation_bottom) {
12614  runningPosX += tabWidth;
12615  } else {
12616  runningPosY += tabHeight;
12617  }
12618  }
12619 
12620 
12621  if (pIsOverCloseButtonOut) {
12622  *pIsOverCloseButtonOut = false;
12623  }
12624 
12625  return NULL;
12626 }
12627 
12628 
12630 //
12631 // Tab
12632 //
12634 
12636 DRGUI_PRIVATE void tab_append(drgui_tab* pTab, drgui_element* pTBElement);
12637 
12639 DRGUI_PRIVATE void tab_prepend(drgui_tab* pTab, drgui_element* pTBElement);
12640 
12645 DRGUI_PRIVATE void tab_detach_from_hierarchy(drgui_tab* pTab);
12646 
12648 DRGUI_PRIVATE void tab_detach(drgui_tab* pTab);
12649 
12650 DRGUI_PRIVATE drgui_tab* tb_create_tab(drgui_element* pTBElement, const char* text, size_t extraDataSize, const void* pExtraData)
12651 {
12652  if (pTBElement == NULL) {
12653  return NULL;
12654  }
12655 
12656  drgui_tab* pTab = (drgui_tab*)malloc(sizeof(*pTab) + extraDataSize);
12657  if (pTab == NULL) {
12658  return NULL;
12659  }
12660 
12661  pTab->pTBElement = NULL;
12662  pTab->pNextTab = NULL;
12663  pTab->pPrevTab = NULL;
12664  pTab->text[0] = '\0';
12665 
12666  pTab->extraDataSize = extraDataSize;
12667  if (pExtraData) {
12668  memcpy(pTab->pExtraData, pExtraData, extraDataSize);
12669  }
12670 
12671  if (text != NULL) {
12672  drgui__strncpy_s(pTab->text, sizeof(pTab->text), text, (size_t)-1); // -1 = _TRUNCATE
12673  }
12674 
12675  return pTab;
12676 }
12677 
12678 drgui_tab* drgui_tabbar_create_and_append_tab(drgui_element* pTBElement, const char* text, size_t extraDataSize, const void* pExtraData)
12679 {
12680  drgui_tab* pTab = (drgui_tab*)tb_create_tab(pTBElement, text, extraDataSize, pExtraData);
12681  if (pTab != NULL)
12682  {
12683  tab_append(pTab, pTBElement);
12684  }
12685 
12686  return pTab;
12687 }
12688 
12689 drgui_tab* drgui_tabbar_create_and_prepend_tab(drgui_element* pTBElement, const char* text, size_t extraDataSize, const void* pExtraData)
12690 {
12691  drgui_tab* pTab = (drgui_tab*)tb_create_tab(pTBElement, text, extraDataSize, pExtraData);
12692  if (pTab != NULL)
12693  {
12694  tab_prepend(pTab, pTBElement);
12695  }
12696 
12697  return pTab;
12698 }
12699 
12700 void drgui_tab_delete(drgui_tab* pTab)
12701 {
12702  if (pTab == NULL) {
12703  return;
12704  }
12705 
12706  tab_detach(pTab);
12707  free(pTab);
12708 }
12709 
12711 {
12712  if (pTab == NULL) {
12713  return NULL;
12714  }
12715 
12716  return pTab->pTBElement;
12717 }
12718 
12720 {
12721  if (pTab == NULL) {
12722  return 0;
12723  }
12724 
12725  return pTab->extraDataSize;
12726 }
12727 
12729 {
12730  if (pTab == NULL) {
12731  return NULL;
12732  }
12733 
12734  return pTab->pExtraData;
12735 }
12736 
12737 
12738 void drgui_tab_set_text(drgui_tab* pTab, const char* text)
12739 {
12740  if (pTab == NULL) {
12741  return;
12742  }
12743 
12744  if (text != NULL) {
12745  drgui__strncpy_s(pTab->text, sizeof(pTab->text), text, (size_t)-1); // -1 = _TRUNCATE
12746  } else {
12747  pTab->text[0] = '\0';
12748  }
12749 
12750  // The content of the menu has changed so we'll need to schedule a redraw.
12751  if (drgui_is_auto_dirty_enabled(pTab->pTBElement->pContext)) {
12752  drgui_dirty(pTab->pTBElement, drgui_get_local_rect(pTab->pTBElement));
12753  }
12754 }
12755 
12756 const char* drgui_tab_get_text(drgui_tab* pTab)
12757 {
12758  if (pTab == NULL) {
12759  return NULL;
12760  }
12761 
12762  return pTab->text;
12763 }
12764 
12765 
12767 {
12768  if (pTab == NULL) {
12769  return NULL;
12770  }
12771 
12772  return pTab->pNextTab;
12773 }
12774 
12776 {
12777  if (pTab == NULL) {
12778  return NULL;
12779  }
12780 
12781  return pTab->pPrevTab;
12782 }
12783 
12784 
12786 {
12787  if (pTab == NULL) {
12788  return;
12789  }
12790 
12791  drgui_element* pTBElement = pTab->pTBElement;
12792 
12793  tab_detach_from_hierarchy(pTab);
12794  tab_prepend(pTab, pTBElement);
12795 }
12796 
12797 bool drgui_tab_is_in_view(drgui_tab* pTab)
12798 {
12799  if (pTab == NULL) {
12800  return false;
12801  }
12802 
12803  return drgui_tabbar_is_tab_in_view(pTab->pTBElement, pTab);
12804 }
12805 
12807 {
12808  if (!drgui_tab_is_in_view(pTab)) {
12810  }
12811 }
12812 
12813 
12814 
12815 
12816 DRGUI_PRIVATE void tab_append(drgui_tab* pTab, drgui_element* pTBElement)
12817 {
12818  if (pTab == NULL || pTBElement == NULL) {
12819  return;
12820  }
12821 
12822  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12823  assert(pTB != NULL);
12824 
12825  pTab->pTBElement = pTBElement;
12826  if (pTB->pFirstTab == NULL)
12827  {
12828  assert(pTB->pLastTab == NULL);
12829 
12830  pTB->pFirstTab = pTab;
12831  pTB->pLastTab = pTab;
12832  }
12833  else
12834  {
12835  assert(pTB->pLastTab != NULL);
12836 
12837  pTab->pPrevTab = pTB->pLastTab;
12838 
12839  pTB->pLastTab->pNextTab = pTab;
12840  pTB->pLastTab = pTab;
12841  }
12842 
12843 
12844  if (pTB->isAutoSizeEnabled) {
12845  drgui_tabbar_resize_by_tabs(pTBElement);
12846  }
12847 
12848  // The content of the menu has changed so we'll need to schedule a redraw.
12849  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
12850  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
12851  }
12852 }
12853 
12854 DRGUI_PRIVATE void tab_prepend(drgui_tab* pTab, drgui_element* pTBElement)
12855 {
12856  if (pTab == NULL || pTBElement == NULL) {
12857  return;
12858  }
12859 
12860  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12861  assert(pTB != NULL);
12862 
12863  pTab->pTBElement = pTBElement;
12864  if (pTB->pFirstTab == NULL)
12865  {
12866  assert(pTB->pLastTab == NULL);
12867 
12868  pTB->pFirstTab = pTab;
12869  pTB->pLastTab = pTab;
12870  }
12871  else
12872  {
12873  assert(pTB->pLastTab != NULL);
12874 
12875  pTab->pNextTab = pTB->pFirstTab;
12876 
12877  pTB->pFirstTab->pPrevTab = pTab;
12878  pTB->pFirstTab = pTab;
12879  }
12880 
12881 
12882  if (pTB->isAutoSizeEnabled) {
12883  drgui_tabbar_resize_by_tabs(pTBElement);
12884  }
12885 
12886  // The content of the menu has changed so we'll need to schedule a redraw.
12887  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
12888  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
12889  }
12890 }
12891 
12892 DRGUI_PRIVATE void tab_detach_from_hierarchy(drgui_tab* pTab)
12893 {
12894  if (pTab == NULL) {
12895  return;
12896  }
12897 
12898  drgui_element* pTBElement = pTab->pTBElement;
12899  if (pTBElement == NULL) {
12900  return;
12901  }
12902 
12903  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12904  assert(pTB != NULL);
12905 
12906 
12907  if (pTab->pNextTab != NULL) {
12908  pTab->pNextTab->pPrevTab = pTab->pPrevTab;
12909  }
12910 
12911  if (pTab->pPrevTab != NULL) {
12912  pTab->pPrevTab->pNextTab = pTab->pNextTab;
12913  }
12914 
12915 
12916  if (pTab == pTB->pFirstTab) {
12917  pTB->pFirstTab = pTab->pNextTab;
12918  }
12919 
12920  if (pTab == pTB->pLastTab) {
12921  pTB->pLastTab = pTab->pPrevTab;
12922  }
12923 
12924 
12925  pTab->pNextTab = NULL;
12926  pTab->pPrevTab = NULL;
12927  pTab->pTBElement = NULL;
12928 }
12929 
12930 DRGUI_PRIVATE void tab_detach(drgui_tab* pTab)
12931 {
12932  if (pTab == NULL) {
12933  return;
12934  }
12935 
12936  drgui_element* pTBElement = pTab->pTBElement;
12937  if (pTBElement == NULL) {
12938  return;
12939  }
12940 
12941  drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
12942  assert(pTB != NULL);
12943 
12944  if (pTB->pHoveredTab == pTab) {
12945  pTB->pHoveredTab = NULL;
12946  pTB->isCloseButtonHovered = false;
12947  }
12948 
12949  if (pTB->pActiveTab == pTab) {
12950  pTB->pActiveTab = NULL;
12951  }
12952 
12953  if (pTB->pTabWithCloseButtonPressed == pTab) {
12954  pTB->pTabWithCloseButtonPressed = NULL;
12955  }
12956 
12957 
12958  tab_detach_from_hierarchy(pTab);
12959 
12960 
12961  if (pTB->isAutoSizeEnabled) {
12962  drgui_tabbar_resize_by_tabs(pTBElement);
12963  }
12964 
12965  // The content of the menu has changed so we'll need to schedule a redraw.
12966  if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
12967  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
12968  }
12969 }
12970 #endif //DR_GUI_IMPLEMENTATION
12971 
12972 
12973 
12976 //
12977 // Text Box
12978 //
12981 #ifndef DRGUI_NO_TEXT_EDITING
12982 // QUICK NOTES
12983 //
12984 // - By default the cursor/caret does not blink automatically. Instead, the application must "step" the text box by
12985 // calling drgui_textbox_step().
12986 
12987 #ifndef drgui_textbox_h
12988 #define drgui_textbox_h
12989 
12990 #ifdef __cplusplus
12991 extern "C" {
12992 #endif
12993 
12994 typedef void (* drgui_textbox_on_cursor_move_proc)(drgui_element* pTBElement);
12995 typedef void (* drgui_textbox_on_undo_point_changed_proc)(drgui_element* pTBElement, unsigned int iUndoPoint);
12996 
12997 
12999 drgui_element* drgui_create_textbox(drgui_context* pContext, drgui_element* pParent, size_t extraDataSize, const void* pExtraData);
13000 
13002 void drgui_delete_textbox(drgui_element* pTBElement);
13003 
13004 
13007 
13009 void* drgui_textbox_get_extra_data(drgui_element* pTBElement);
13010 
13011 
13013 void drgui_textbox_set_font(drgui_element* pTBElement, drgui_font* pFont);
13014 
13017 
13019 void drgui_textbox_set_text_color(drgui_element* pTBElement, drgui_color color);
13020 
13023 
13026 
13029 
13032 
13034 void drgui_textbox_set_cursor_width(drgui_element* pTBElement, float cursorWidth);
13035 
13038 
13041 
13044 
13046 void drgui_textbox_set_border_width(drgui_element* pTBElement, float borderWidth);
13047 
13049 void drgui_textbox_set_padding(drgui_element* pTBElement, float padding);
13050 
13053 
13056 
13059 
13062 
13063 // Sets the width of the line numbers.
13064 void drgui_textbox_set_line_numbers_width(drgui_element* pTBElement, float lineNumbersWidth);
13065 
13066 // Retrieves the width of the line numbers.
13068 
13069 // Sets the padding to apply between the line numbers and the text.
13070 void drgui_textbox_set_line_numbers_padding(drgui_element* pTBElement, float lineNumbersPadding);
13071 
13072 // Retrieves the padding to apply between the line numbers and the text.
13074 
13075 // Sets the color of the text of the line numbers.
13077 
13078 // Retrieves the color of the text of the line numbers.
13080 
13081 // Sets the color of the background of the line numbers.
13083 
13084 // Retrieves the color of the background of the line numbers.
13086 
13087 
13089 void drgui_textbox_set_text(drgui_element* pTBElement, const char* text);
13090 
13092 size_t drgui_textbox_get_text(drgui_element* pTBElement, char* pTextOut, size_t textOutSize);
13093 
13095 void drgui_textbox_step(drgui_element* pTBElement, unsigned int milliseconds);
13096 
13098 void drgui_textbox_set_cursor_blink_rate(drgui_element* pTBElement, unsigned int blinkRateInMilliseconds);
13099 
13102 
13105 
13108 
13110 void drgui_textbox_select_all(drgui_element* pTBElement);
13111 
13113 void drgui_textbox_deselect_all(drgui_element* pTBElement);
13114 
13122 size_t drgui_textbox_get_selected_text(drgui_element* pTBElement, char* textOut, size_t textOutLength);
13123 
13128 
13133 
13137 bool drgui_textbox_insert_text_at_cursor(drgui_element* pTBElement, const char* text);
13138 
13140 bool drgui_textbox_undo(drgui_element* pTBElement);
13141 
13143 bool drgui_textbox_redo(drgui_element* pTBElement);
13144 
13147 
13150 
13153 
13155 size_t drgui_textbox_get_cursor_line(drgui_element* pTBElement);
13156 
13159 
13161 size_t drgui_textbox_get_line_count(drgui_element* pTBElement);
13162 
13163 
13165 bool drgui_textbox_find_and_select_next(drgui_element* pTBElement, const char* text);
13166 
13168 bool drgui_textbox_find_and_replace_next(drgui_element* pTBElement, const char* text, const char* replacement);
13169 
13171 bool drgui_textbox_find_and_replace_all(drgui_element* pTBElement, const char* text, const char* replacement);
13172 
13173 
13176 
13179 
13180 
13183 
13186 
13189 
13192 
13193 // Retrieves the vertical scrollbar.
13195 
13196 // Retrieves the horizontal scrollbar.
13198 
13199 // Sets the size of both the vertical and horizontal scrollbars.
13200 void drgui_textbox_set_scrollbar_size(drgui_element* pTBElement, float size);
13201 
13202 
13205 
13208 
13209 
13210 
13212 void drgui_textbox_on_size(drgui_element* pTBElement, float newWidth, float newHeight);
13213 
13215 void drgui_textbox_on_mouse_move(drgui_element* pTBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags);
13216 
13218 void drgui_textbox_on_mouse_button_down(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
13219 
13221 void drgui_textbox_on_mouse_button_up(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
13222 
13224 void drgui_textbox_on_mouse_button_dblclick(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
13225 
13227 void drgui_textbox_on_mouse_wheel(drgui_element* pTBElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags);
13228 
13230 void drgui_textbox_on_key_down(drgui_element* pTBElement, drgui_key key, int stateFlags);
13231 
13233 void drgui_textbox_on_key_up(drgui_element* pTBElement, drgui_key key, int stateFlags);
13234 
13236 void drgui_textbox_on_printable_key_down(drgui_element* pTBElement, unsigned int utf32, int stateFlags);
13237 
13239 void drgui_textbox_on_paint(drgui_element* pTBElement, drgui_rect relativeRect, void* pPaintData);
13240 
13242 void drgui_textbox_on_capture_keyboard(drgui_element* pTBElement, drgui_element* pPrevCapturedElement);
13243 
13245 void drgui_textbox_on_release_keyboard(drgui_element* pTBElement, drgui_element* pNewCapturedElement);
13246 
13249 
13252 
13253 
13254 
13255 #ifdef __cplusplus
13256 }
13257 #endif
13258 #endif //drgui_textbox_h
13259 
13260 
13261 #ifdef DR_GUI_IMPLEMENTATION
13262 typedef struct
13263 {
13265  drgui_text_engine* pTL;
13266 
13268  drgui_element* pVertScrollbar;
13269 
13271  drgui_element* pHorzScrollbar;
13272 
13274  drgui_element* pLineNumbers;
13275 
13276 
13278  drgui_color borderColor;
13279 
13281  float borderWidth;
13282 
13284  float padding;
13285 
13286  // The width of the line numbers.
13287  float lineNumbersWidth;
13288 
13290  float lineNumbersPaddingRight;
13291 
13292  // The color of the text of the line numbers.
13293  drgui_color lineNumbersColor;
13294 
13295  // The color of the background of the line numbers.
13296  drgui_color lineNumbersBackgroundColor;
13297 
13298 
13300  float vertScrollbarSize;
13301 
13303  float horzScrollbarSize;
13304 
13306  bool isVertScrollbarEnabled;
13307 
13309  bool isHorzScrollbarEnabled;
13310 
13311 
13313  size_t iLineSelectAnchor;
13314 
13315 
13317  drgui_textbox_on_cursor_move_proc onCursorMove;
13318 
13320  drgui_textbox_on_undo_point_changed_proc onUndoPointChanged;
13321 
13322 
13324  size_t extraDataSize;
13325 
13327  char pExtraData[1];
13328 
13329 } drgui_textbox;
13330 
13331 
13333 DRGUI_PRIVATE void drgui_textbox__get_text_offset(drgui_element* pTBElement, float* pOffsetXOut, float* pOffsetYOut);
13334 
13336 DRGUI_PRIVATE void drgui_textbox__calculate_text_engine_container_size(drgui_element* pTBElement, float* pWidthOut, float* pHeightOut);
13337 
13339 DRGUI_PRIVATE drgui_rect drgui_textbox__get_text_rect(drgui_element* pTBElement);
13340 
13342 DRGUI_PRIVATE void drgui_textbox__refresh_scrollbars(drgui_element* pTBElement);
13343 
13345 DRGUI_PRIVATE void drgui_textbox__refresh_scrollbar_ranges(drgui_element* pTBElement);
13346 
13348 DRGUI_PRIVATE void drgui_textbox__refresh_scrollbar_layouts(drgui_element* pTBElement);
13349 
13351 DRGUI_PRIVATE drgui_rect drgui_textbox__get_scrollbar_dead_space_rect(drgui_element* pTBElement);
13352 
13353 
13355 DRGUI_PRIVATE void drgui_textbox__on_mouse_move_line_numbers(drgui_element* pLineNumbers, int relativeMousePosX, int relativeMousePosY, int stateFlags);
13356 
13358 DRGUI_PRIVATE void drgui_textbox__on_mouse_button_down_line_numbers(drgui_element* pLineNumbers, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
13359 
13361 DRGUI_PRIVATE void drgui_textbox__on_mouse_button_up_line_numbers(drgui_element* pLineNumbers, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
13362 
13364 DRGUI_PRIVATE void drgui_textbox__on_paint_line_numbers(drgui_element* pLineNumbers, drgui_rect relativeRect, void* pPaintData);
13365 
13367 DRGUI_PRIVATE void drgui_textbox__refresh_line_numbers(drgui_element* pTBElement);
13368 
13369 
13371 DRGUI_PRIVATE void drgui_textbox__on_text_engine_paint_rect(drgui_text_engine* pLayout, drgui_rect rect, drgui_color color, drgui_element* pTBElement, void* pPaintData);
13372 
13374 DRGUI_PRIVATE void drgui_textbox__on_text_engine_paint_text(drgui_text_engine* pTL, drgui_text_run* pRun, drgui_element* pTBElement, void* pPaintData);
13375 
13377 DRGUI_PRIVATE void drgui_textbox__on_text_engine_dirty(drgui_text_engine* pTL, drgui_rect rect);
13378 
13380 DRGUI_PRIVATE void drgui_textbox__on_text_engine_cursor_move(drgui_text_engine* pTL);
13381 
13383 DRGUI_PRIVATE void drgui_textbox__on_text_engine_text_changed(drgui_text_engine* pTL);
13384 
13386 DRGUI_PRIVATE void drgui_textbox__on_text_engine_undo_point_changed(drgui_text_engine* pTL, unsigned int iUndoPoint);
13387 
13388 
13389 DRGUI_PRIVATE void drgui_textbox__on_vscroll(drgui_element* pSBElement, int scrollPos)
13390 {
13391  drgui_element* pTBElement = *(drgui_element**)drgui_sb_get_extra_data(pSBElement);
13392  assert(pTBElement != NULL);
13393 
13394  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13395  assert(pTB != NULL);
13396 
13398 
13399  // The line numbers need to be redrawn.
13400  drgui_dirty(pTB->pLineNumbers, drgui_get_local_rect(pTB->pLineNumbers));
13401 }
13402 
13403 DRGUI_PRIVATE void drgui_textbox__on_hscroll(drgui_element* pSBElement, int scrollPos)
13404 {
13405  drgui_element* pTBElement = *(drgui_element**)drgui_sb_get_extra_data(pSBElement);
13406  assert(pTBElement != NULL);
13407 
13408  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13409  assert(pTB != NULL);
13410 
13411  drgui_text_engine_set_inner_offset_x(pTB->pTL, (float)-scrollPos);
13412 }
13413 
13414 
13415 
13416 drgui_element* drgui_create_textbox(drgui_context* pContext, drgui_element* pParent, size_t extraDataSize, const void* pExtraData)
13417 {
13418  if (pContext == NULL) {
13419  return NULL;
13420  }
13421 
13422  drgui_element* pTBElement = drgui_create_element(pContext, pParent, sizeof(drgui_textbox) + extraDataSize, NULL);
13423  if (pTBElement == NULL) {
13424  return NULL;
13425  }
13426 
13427  drgui_set_cursor(pTBElement, drgui_cursor_text);
13441 
13442  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13443  assert(pTB != NULL);
13444 
13445  pTB->pVertScrollbar = drgui_create_scrollbar(pContext, pTBElement, drgui_sb_orientation_vertical, sizeof(pTBElement), &pTBElement);
13446  drgui_sb_set_on_scroll(pTB->pVertScrollbar, drgui_textbox__on_vscroll);
13447  drgui_sb_set_mouse_wheel_scele(pTB->pVertScrollbar, 3);
13448 
13449  pTB->pHorzScrollbar = drgui_create_scrollbar(pContext, pTBElement, drgui_sb_orientation_horizontal, sizeof(pTBElement), &pTBElement);
13450  drgui_sb_set_on_scroll(pTB->pHorzScrollbar, drgui_textbox__on_hscroll);
13451 
13452  pTB->pLineNumbers = drgui_create_element(pContext, pTBElement, sizeof(pTBElement), &pTBElement);
13453  drgui_hide(pTB->pLineNumbers);
13454  drgui_set_on_mouse_move(pTB->pLineNumbers, drgui_textbox__on_mouse_move_line_numbers);
13455  drgui_set_on_mouse_button_down(pTB->pLineNumbers, drgui_textbox__on_mouse_button_down_line_numbers);
13456  drgui_set_on_mouse_button_up(pTB->pLineNumbers, drgui_textbox__on_mouse_button_up_line_numbers);
13457  drgui_set_on_paint(pTB->pLineNumbers, drgui_textbox__on_paint_line_numbers);
13458 
13459  pTB->pTL = drgui_create_text_engine(pContext, sizeof(pTBElement), &pTBElement);
13460  if (pTB->pTL == NULL) {
13461  drgui_delete_element(pTBElement);
13462  return NULL;
13463  }
13464 
13465  drgui_text_engine_set_on_paint_rect(pTB->pTL, drgui_textbox__on_text_engine_paint_rect);
13466  drgui_text_engine_set_on_paint_text(pTB->pTL, drgui_textbox__on_text_engine_paint_text);
13467  drgui_text_engine_set_on_dirty(pTB->pTL, drgui_textbox__on_text_engine_dirty);
13468  drgui_text_engine_set_on_cursor_move(pTB->pTL, drgui_textbox__on_text_engine_cursor_move);
13469  drgui_text_engine_set_on_text_changed(pTB->pTL, drgui_textbox__on_text_engine_text_changed);
13470  drgui_text_engine_set_on_undo_point_changed(pTB->pTL, drgui_textbox__on_text_engine_undo_point_changed);
13472  drgui_text_engine_set_cursor_color(pTB->pTL, drgui_rgb(0, 0, 0));
13473  drgui_text_engine_set_default_bg_color(pTB->pTL, drgui_rgb(255, 255, 255));
13474  drgui_text_engine_set_active_line_bg_color(pTB->pTL, drgui_rgb(255, 255, 255));
13476 
13477  pTB->borderColor = drgui_rgb(0, 0, 0);
13478  pTB->borderWidth = 1;
13479  pTB->padding = 2;
13480  pTB->lineNumbersWidth = 64;
13481  pTB->lineNumbersPaddingRight = 16;
13482  pTB->lineNumbersColor = drgui_rgb(80, 160, 192);
13483  pTB->lineNumbersBackgroundColor = drgui_text_engine_get_default_bg_color(pTB->pTL);
13484  pTB->vertScrollbarSize = 16;
13485  pTB->horzScrollbarSize = 16;
13486  pTB->isVertScrollbarEnabled = true;
13487  pTB->isHorzScrollbarEnabled = true;
13488  pTB->iLineSelectAnchor = 0;
13489  pTB->onCursorMove = NULL;
13490  pTB->onUndoPointChanged = NULL;
13491 
13492  pTB->extraDataSize = extraDataSize;
13493  if (pExtraData != NULL) {
13494  memcpy(pTB->pExtraData, pExtraData, extraDataSize);
13495  }
13496 
13497  return pTBElement;
13498 }
13499 
13500 void drgui_delete_textbox(drgui_element* pTBElement)
13501 {
13502  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13503  if (pTB == NULL) {
13504  return;
13505  }
13506 
13507  if (pTB->pTL) {
13508  drgui_delete_text_engine(pTB->pTL);
13509  pTB->pTL = NULL;
13510  }
13511 
13512  if (pTB->pLineNumbers) {
13513  drgui_delete_element(pTB->pLineNumbers);
13514  pTB->pLineNumbers = NULL;
13515  }
13516 
13517  if (pTB->pHorzScrollbar) {
13518  drgui_delete_element(pTB->pHorzScrollbar);
13519  pTB->pHorzScrollbar = NULL;
13520  }
13521 
13522  if (pTB->pVertScrollbar) {
13523  drgui_delete_element(pTB->pVertScrollbar);
13524  pTB->pVertScrollbar = NULL;
13525  }
13526 
13527  drgui_delete_element(pTBElement);
13528 }
13529 
13531 {
13532  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13533  if (pTB == NULL) {
13534  return 0;
13535  }
13536 
13537  return pTB->extraDataSize;
13538 }
13539 
13541 {
13542  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13543  if (pTB == NULL) {
13544  return NULL;
13545  }
13546 
13547  return pTB->pExtraData;
13548 }
13549 
13550 
13551 void drgui_textbox_set_font(drgui_element* pTBElement, drgui_font* pFont)
13552 {
13553  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13554  if (pTB == NULL) {
13555  return;
13556  }
13557 
13558  drgui_begin_dirty(pTBElement);
13559  {
13560  drgui_text_engine_set_default_font(pTB->pTL, pFont);
13561 
13562  // The font used for line numbers are tied to the main font at the moment.
13563  drgui_textbox__refresh_line_numbers(pTBElement);
13564 
13565  // Emulate a scroll to ensure the scroll position is pinned to a line.
13566  drgui_textbox__on_vscroll(pTB->pVertScrollbar, drgui_sb_get_scroll_position(pTB->pVertScrollbar));
13567  drgui_textbox__refresh_scrollbars(pTBElement);
13568 
13569  // The caret position needs to be refreshes. We'll cheat here a little bit and just do a full refresh of the text engine.
13570  //drgui_text_engine__refresh(pTB->pTL);
13572  }
13573  drgui_end_dirty(pTBElement);
13574 }
13575 
13577 {
13578  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13579  if (pTB == NULL) {
13580  return NULL;
13581  }
13582 
13583  return drgui_text_engine_get_default_font(pTB->pTL);
13584 }
13585 
13587 {
13588  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13589  if (pTB == NULL) {
13590  return;
13591  }
13592 
13594 }
13595 
13597 {
13598  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13599  if (pTB == NULL) {
13600  return;
13601  }
13602 
13603  drgui_text_engine_set_default_bg_color(pTB->pTL, color);
13604 }
13605 
13607 {
13608  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13609  if (pTB == NULL) {
13610  return;
13611  }
13612 
13614 }
13615 
13617 {
13618  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13619  if (pTB == NULL) {
13620  return drgui_rgb(0, 0, 0);
13621  }
13622 
13624 }
13625 
13627 {
13628  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13629  if (pTB == NULL) {
13630  return;
13631  }
13632 
13634 }
13635 
13636 void drgui_textbox_set_cursor_width(drgui_element* pTBElement, float cursorWidth)
13637 {
13638  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13639  if (pTB == NULL) {
13640  return;
13641  }
13642 
13643  drgui_text_engine_set_cursor_width(pTB->pTL, cursorWidth);
13644 }
13645 
13647 {
13648  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13649  if (pTB == NULL) {
13650  return 0;
13651  }
13652 
13653  return drgui_text_engine_get_cursor_width(pTB->pTL);
13654 }
13655 
13657 {
13658  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13659  if (pTB == NULL) {
13660  return;
13661  }
13662 
13663  drgui_text_engine_set_cursor_color(pTB->pTL, color);
13664 }
13665 
13667 {
13668  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13669  if (pTB == NULL) {
13670  return;
13671  }
13672 
13673  pTB->borderColor = color;
13674 }
13675 
13676 void drgui_textbox_set_border_width(drgui_element* pTBElement, float borderWidth)
13677 {
13678  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13679  if (pTB == NULL) {
13680  return;
13681  }
13682 
13683  pTB->borderWidth = borderWidth;
13684 }
13685 
13686 void drgui_textbox_set_padding(drgui_element* pTBElement, float padding)
13687 {
13688  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13689  if (pTB == NULL) {
13690  return;
13691  }
13692 
13693  pTB->padding = padding;
13694 }
13695 
13697 {
13698  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13699  if (pTB == NULL) {
13700  return 0;
13701  }
13702 
13703  return pTB->padding;
13704 }
13705 
13707 {
13708  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13709  if (pTB == NULL) {
13710  return 0;
13711  }
13712 
13713  return pTB->padding;
13714 }
13715 
13717 {
13718  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13719  if (pTB == NULL) {
13720  return;
13721  }
13722 
13723  drgui_text_engine_set_vertical_align(pTB->pTL, align);
13724 }
13725 
13727 {
13728  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13729  if (pTB == NULL) {
13730  return;
13731  }
13732 
13733  drgui_text_engine_set_horizontal_align(pTB->pTL, align);
13734 }
13735 
13736 void drgui_textbox_set_line_numbers_width(drgui_element* pTBElement, float lineNumbersWidth)
13737 {
13738  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13739  if (pTB == NULL) {
13740  return;
13741  }
13742 
13743  pTB->lineNumbersWidth = lineNumbersWidth;
13744 }
13745 
13747 {
13748  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13749  if (pTB == NULL) {
13750  return 0;
13751  }
13752 
13753  return pTB->lineNumbersWidth;
13754 }
13755 
13756 void drgui_textbox_set_line_numbers_padding(drgui_element* pTBElement, float lineNumbersPadding)
13757 {
13758  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13759  if (pTB == NULL) {
13760  return;
13761  }
13762 
13763  pTB->lineNumbersPaddingRight = lineNumbersPadding;
13764 }
13765 
13767 {
13768  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13769  if (pTB == NULL) {
13770  return 0;
13771  }
13772 
13773  return pTB->lineNumbersPaddingRight;
13774 }
13775 
13777 {
13778  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13779  if (pTB == NULL) {
13780  return;
13781  }
13782 
13783  pTB->lineNumbersColor = color;
13784  drgui_textbox__refresh_line_numbers(pTBElement);
13785 }
13786 
13788 {
13789  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13790  if (pTB == NULL) {
13791  return drgui_rgb(0, 0, 0);
13792  }
13793 
13794  return pTB->lineNumbersColor;
13795 }
13796 
13798 {
13799  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13800  if (pTB == NULL) {
13801  return;
13802  }
13803 
13804  pTB->lineNumbersBackgroundColor = color;
13805  drgui_textbox__refresh_line_numbers(pTBElement);
13806 }
13807 
13809 {
13810  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13811  if (pTB == NULL) {
13812  return drgui_rgb(0, 0, 0);
13813  }
13814 
13815  return pTB->lineNumbersBackgroundColor;
13816 }
13817 
13818 
13819 
13820 void drgui_textbox_set_text(drgui_element* pTBElement, const char* text)
13821 {
13822  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13823  if (pTB == NULL) {
13824  return;
13825  }
13826 
13828  {
13829  drgui_text_engine_set_text(pTB->pTL, text);
13830  }
13832 }
13833 
13834 size_t drgui_textbox_get_text(drgui_element* pTBElement, char* pTextOut, size_t textOutSize)
13835 {
13836  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13837  if (pTB == NULL) {
13838  return 0;
13839  }
13840 
13841  return drgui_text_engine_get_text(pTB->pTL, pTextOut, textOutSize);
13842 }
13843 
13844 void drgui_textbox_step(drgui_element* pTBElement, unsigned int milliseconds)
13845 {
13846  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13847  if (pTB == NULL) {
13848  return;
13849  }
13850 
13851  drgui_text_engine_step(pTB->pTL, milliseconds);
13852 }
13853 
13854 void drgui_textbox_set_cursor_blink_rate(drgui_element* pTBElement, unsigned int blinkRateInMilliseconds)
13855 {
13856  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13857  if (pTB == NULL) {
13858  return;
13859  }
13860 
13861  drgui_text_engine_set_cursor_blink_rate(pTB->pTL, blinkRateInMilliseconds);
13862 }
13863 
13865 {
13866  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13867  if (pTB == NULL) {
13868  return;
13869  }
13870 
13872 }
13873 
13875 {
13876  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13877  if (pTB == NULL) {
13878  return;
13879  }
13880 
13882 }
13883 
13884 
13886 {
13887  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13888  if (pTB == NULL) {
13889  return false;
13890  }
13891 
13892  return drgui_text_engine_is_anything_selected(pTB->pTL);
13893 }
13894 
13895 void drgui_textbox_select_all(drgui_element* pTBElement)
13896 {
13897  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13898  if (pTB == NULL) {
13899  return;
13900  }
13901 
13902  drgui_text_engine_select_all(pTB->pTL);
13903 }
13904 
13905 void drgui_textbox_deselect_all(drgui_element* pTBElement)
13906 {
13907  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13908  if (pTB == NULL) {
13909  return;
13910  }
13911 
13913 }
13914 
13915 size_t drgui_textbox_get_selected_text(drgui_element* pTBElement, char* textOut, size_t textOutLength)
13916 {
13917  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13918  if (pTB == NULL) {
13919  return 0;
13920  }
13921 
13922  return drgui_text_engine_get_selected_text(pTB->pTL, textOut, textOutLength);
13923 }
13924 
13926 {
13927  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13928  if (pTB == NULL) {
13929  return false;
13930  }
13931 
13932  bool wasTextChanged = false;
13934  {
13935  wasTextChanged = drgui_text_engine_delete_character_to_right_of_cursor(pTB->pTL);
13936  }
13937  if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
13938 
13939  return wasTextChanged;
13940 }
13941 
13943 {
13944  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13945  if (pTB == NULL) {
13946  return false;
13947  }
13948 
13949  bool wasTextChanged = false;
13951  {
13952  wasTextChanged = drgui_text_engine_delete_selected_text(pTB->pTL);
13953  }
13954  if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
13955 
13956  return wasTextChanged;
13957 }
13958 
13959 bool drgui_textbox_insert_text_at_cursor(drgui_element* pTBElement, const char* text)
13960 {
13961  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13962  if (pTB == NULL) {
13963  return false;;
13964  }
13965 
13966  bool wasTextChanged = false;
13968  {
13969  wasTextChanged = drgui_text_engine_insert_text_at_cursor(pTB->pTL, text);
13970  }
13971  if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
13972 
13973  return wasTextChanged;
13974 }
13975 
13976 bool drgui_textbox_undo(drgui_element* pTBElement)
13977 {
13978  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13979  if (pTB == NULL) {
13980  return false;
13981  }
13982 
13983  return drgui_text_engine_undo(pTB->pTL);
13984 }
13985 
13986 bool drgui_textbox_redo(drgui_element* pTBElement)
13987 {
13988  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13989  if (pTB == NULL) {
13990  return false;
13991  }
13992 
13993  return drgui_text_engine_redo(pTB->pTL);
13994 }
13995 
13997 {
13998  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
13999  if (pTB == NULL) {
14000  return false;
14001  }
14002 
14004 }
14005 
14007 {
14008  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14009  if (pTB == NULL) {
14010  return false;
14011  }
14012 
14014 }
14015 
14017 {
14018  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14019  if (pTB == NULL) {
14020  return;
14021  }
14022 
14024 }
14025 
14026 
14027 size_t drgui_textbox_get_cursor_line(drgui_element* pTBElement)
14028 {
14029  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14030  if (pTB == NULL) {
14031  return 0;
14032  }
14033 
14034  return drgui_text_engine_get_cursor_line(pTB->pTL);
14035 }
14036 
14038 {
14039  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14040  if (pTB == NULL) {
14041  return 0;
14042  }
14043 
14044  return drgui_text_engine_get_cursor_column(pTB->pTL);
14045 }
14046 
14047 size_t drgui_textbox_get_line_count(drgui_element* pTBElement)
14048 {
14049  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14050  if (pTB == NULL) {
14051  return 0;
14052  }
14053 
14054  return drgui_text_engine_get_line_count(pTB->pTL);
14055 }
14056 
14057 
14058 bool drgui_textbox_find_and_select_next(drgui_element* pTBElement, const char* text)
14059 {
14060  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14061  if (pTB == NULL) {
14062  return 0;
14063  }
14064 
14065  size_t selectionStart;
14066  size_t selectionEnd;
14067  if (drgui_text_engine_find_next(pTB->pTL, text, &selectionStart, &selectionEnd))
14068  {
14069  drgui_text_engine_select(pTB->pTL, selectionStart, selectionEnd);
14071 
14072  return true;
14073  }
14074 
14075  return false;
14076 }
14077 
14078 bool drgui_textbox_find_and_replace_next(drgui_element* pTBElement, const char* text, const char* replacement)
14079 {
14080  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14081  if (pTB == NULL) {
14082  return 0;
14083  }
14084 
14085  bool wasTextChanged = false;
14087  {
14088  size_t selectionStart;
14089  size_t selectionEnd;
14090  if (drgui_text_engine_find_next(pTB->pTL, text, &selectionStart, &selectionEnd))
14091  {
14092  drgui_text_engine_select(pTB->pTL, selectionStart, selectionEnd);
14094 
14095  wasTextChanged = drgui_text_engine_delete_selected_text(pTB->pTL) || wasTextChanged;
14096  wasTextChanged = drgui_text_engine_insert_text_at_cursor(pTB->pTL, replacement) || wasTextChanged;
14097  }
14098  }
14099  if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
14100 
14101  return wasTextChanged;
14102 }
14103 
14104 bool drgui_textbox_find_and_replace_all(drgui_element* pTBElement, const char* text, const char* replacement)
14105 {
14106  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14107  if (pTB == NULL) {
14108  return 0;
14109  }
14110 
14111  size_t originalCursorLine = drgui_text_engine_get_cursor_line(pTB->pTL);
14112  size_t originalCursorPos = drgui_text_engine_get_cursor_character(pTB->pTL) - drgui_text_engine_get_line_first_character(pTB->pTL, originalCursorLine);
14113  int originalScrollPosX = drgui_sb_get_scroll_position(pTB->pHorzScrollbar);
14114  int originalScrollPosY = drgui_sb_get_scroll_position(pTB->pVertScrollbar);
14115 
14116  bool wasTextChanged = false;
14118  {
14119  // It's important that we don't replace the replacement text. To handle this, we just move the cursor to the top of the text and find
14120  // and replace every occurance without looping.
14122 
14123  size_t selectionStart;
14124  size_t selectionEnd;
14125  while (drgui_text_engine_find_next_no_loop(pTB->pTL, text, &selectionStart, &selectionEnd))
14126  {
14127  drgui_text_engine_select(pTB->pTL, selectionStart, selectionEnd);
14129 
14130  wasTextChanged = drgui_text_engine_delete_selected_text(pTB->pTL) || wasTextChanged;
14131  wasTextChanged = drgui_text_engine_insert_text_at_cursor(pTB->pTL, replacement) || wasTextChanged;
14132  }
14133 
14134  // The cursor may have moved so we'll need to restore it.
14135  size_t lineCharStart;
14136  size_t lineCharEnd;
14137  drgui_text_engine_get_line_character_range(pTB->pTL, originalCursorLine, &lineCharStart, &lineCharEnd);
14138 
14139  size_t newCursorPos = lineCharStart + originalCursorPos;
14140  if (newCursorPos > lineCharEnd) {
14141  newCursorPos = lineCharEnd;
14142  }
14143  drgui_text_engine_move_cursor_to_character(pTB->pTL, newCursorPos);
14144  }
14145  if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
14146 
14147 
14148  // The scroll positions may have moved so we'll need to restore them.
14149  drgui_sb_scroll_to(pTB->pHorzScrollbar, originalScrollPosX);
14150  drgui_sb_scroll_to(pTB->pVertScrollbar, originalScrollPosY);
14151 
14152  return wasTextChanged;
14153 }
14154 
14155 
14157 {
14158  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14159  if (pTB == NULL) {
14160  return;
14161  }
14162 
14163  drgui_show(pTB->pLineNumbers);
14164  drgui_textbox__refresh_line_numbers(pTBElement);
14165 }
14166 
14168 {
14169  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14170  if (pTB == NULL) {
14171  return;
14172  }
14173 
14174  drgui_hide(pTB->pLineNumbers);
14175  drgui_textbox__refresh_line_numbers(pTBElement);
14176 }
14177 
14178 
14180 {
14181  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14182  if (pTB == NULL) {
14183  return;
14184  }
14185 
14186  if (pTB->isVertScrollbarEnabled) {
14187  pTB->isVertScrollbarEnabled = false;
14188  drgui_textbox__refresh_scrollbars(pTBElement);
14189  }
14190 }
14191 
14193 {
14194  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14195  if (pTB == NULL) {
14196  return;
14197  }
14198 
14199  if (!pTB->isVertScrollbarEnabled) {
14200  pTB->isVertScrollbarEnabled = true;
14201  drgui_textbox__refresh_scrollbars(pTBElement);
14202  }
14203 }
14204 
14206 {
14207  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14208  if (pTB == NULL) {
14209  return;
14210  }
14211 
14212  if (pTB->isHorzScrollbarEnabled) {
14213  pTB->isHorzScrollbarEnabled = false;
14214  drgui_textbox__refresh_scrollbars(pTBElement);
14215  }
14216 }
14217 
14219 {
14220  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14221  if (pTB == NULL) {
14222  return;
14223  }
14224 
14225  if (!pTB->isHorzScrollbarEnabled) {
14226  pTB->isHorzScrollbarEnabled = true;
14227  drgui_textbox__refresh_scrollbars(pTBElement);
14228  }
14229 }
14230 
14232 {
14233  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14234  if (pTB == NULL) {
14235  return NULL;
14236  }
14237 
14238  return pTB->pVertScrollbar;
14239 }
14240 
14242 {
14243  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14244  if (pTB == NULL) {
14245  return NULL;
14246  }
14247 
14248  return pTB->pHorzScrollbar;
14249 }
14250 
14251 void drgui_textbox_set_scrollbar_size(drgui_element* pTBElement, float size)
14252 {
14253  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14254  if (pTB == NULL) {
14255  return;
14256  }
14257 
14258  pTB->horzScrollbarSize = size;
14259  pTB->vertScrollbarSize = size;
14260 
14261  drgui_textbox__refresh_scrollbars(pTBElement);
14262 }
14263 
14264 
14266 {
14267  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14268  if (pTB == NULL) {
14269  return;
14270  }
14271 
14272  pTB->onCursorMove = proc;
14273 }
14274 
14276 {
14277  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14278  if (pTB == NULL) {
14279  return;
14280  }
14281 
14282  pTB->onUndoPointChanged = proc;
14283 }
14284 
14285 
14286 void drgui_textbox_on_size(drgui_element* pTBElement, float newWidth, float newHeight)
14287 {
14288  (void)newWidth;
14289  (void)newHeight;
14290 
14291  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14292  if (pTB == NULL) {
14293  return;
14294  }
14295 
14296  // The text engine needs to be resized.
14297  float containerWidth;
14298  float containerHeight;
14299  drgui_textbox__calculate_text_engine_container_size(pTBElement, &containerWidth, &containerHeight);
14300  drgui_text_engine_set_container_size(pTB->pTL, containerWidth, containerHeight);
14301 
14302  // Scrollbars need to be refreshed first.
14303  drgui_textbox__refresh_scrollbars(pTBElement);
14304 
14305  // Line numbers need to be refreshed.
14306  drgui_textbox__refresh_line_numbers(pTBElement);
14307 }
14308 
14309 void drgui_textbox_on_mouse_move(drgui_element* pTBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
14310 {
14311  (void)stateFlags;
14312 
14313  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14314  if (pTB == NULL) {
14315  return;
14316  }
14317 
14318  if (drgui_get_element_with_mouse_capture(pTBElement->pContext) == pTBElement)
14319  {
14320  float offsetX;
14321  float offsetY;
14322  drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
14323 
14324  drgui_text_engine_move_cursor_to_point(pTB->pTL, (float)relativeMousePosX - offsetX, (float)relativeMousePosY - offsetY);
14325  }
14326 }
14327 
14328 void drgui_textbox_on_mouse_button_down(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
14329 {
14330  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14331  if (pTB == NULL) {
14332  return;
14333  }
14334 
14335  // Focus the text editor.
14336  drgui_capture_keyboard(pTBElement);
14337 
14338  if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT)
14339  {
14340  // If we are not in selection mode, make sure everything is deselected.
14341  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) == 0) {
14344  } else {
14346  }
14347 
14348  float offsetX;
14349  float offsetY;
14350  drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
14351  drgui_text_engine_move_cursor_to_point(pTB->pTL, (float)relativeMousePosX - offsetX, (float)relativeMousePosY - offsetY);
14352 
14353  // In order to support selection with the mouse we need to capture the mouse and enter selection mode.
14354  drgui_capture_mouse(pTBElement);
14355 
14356  // If we didn't previously enter selection mode we'll need to do that now so we can drag select.
14357  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) == 0) {
14359  }
14360  }
14361 
14362  if (mouseButton == DRGUI_MOUSE_BUTTON_RIGHT)
14363  {
14365  }
14366 }
14367 
14368 void drgui_textbox_on_mouse_button_up(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
14369 {
14370  (void)relativeMousePosX;
14371  (void)relativeMousePosY;
14372  (void)stateFlags;
14373 
14374  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14375  if (pTB == NULL) {
14376  return;
14377  }
14378 
14379  if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT)
14380  {
14381  if (drgui_get_element_with_mouse_capture(pTBElement->pContext) == pTBElement)
14382  {
14383  // Releasing the mouse will leave selectionmode.
14384  drgui_release_mouse(pTBElement->pContext);
14385  }
14386  }
14387 }
14388 
14389 void drgui_textbox_on_mouse_button_dblclick(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
14390 {
14391  (void)mouseButton;
14392  (void)relativeMousePosX;
14393  (void)relativeMousePosY;
14394  (void)stateFlags;
14395 
14396  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14397  if (pTB == NULL) {
14398  return;
14399  }
14400 }
14401 
14402 void drgui_textbox_on_mouse_wheel(drgui_element* pTBElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags)
14403 {
14404  (void)relativeMousePosX;
14405  (void)relativeMousePosY;
14406  (void)stateFlags;
14407 
14408  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14409  if (pTB == NULL) {
14410  return;
14411  }
14412 
14413  drgui_sb_scroll(pTB->pVertScrollbar, -delta * drgui_sb_get_mouse_wheel_scale(pTB->pVertScrollbar));
14414 }
14415 
14416 void drgui_textbox_on_key_down(drgui_element* pTBElement, drgui_key key, int stateFlags)
14417 {
14418  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14419  if (pTB == NULL) {
14420  return;
14421  }
14422 
14423  switch (key)
14424  {
14425  case DRGUI_BACKSPACE:
14426  {
14427  bool wasTextChanged = false;
14429  {
14431  wasTextChanged = drgui_text_engine_delete_selected_text(pTB->pTL);
14432  } else {
14433  wasTextChanged = drgui_text_engine_delete_character_to_left_of_cursor(pTB->pTL);
14434  }
14435  }
14436  if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
14437  } break;
14438 
14439  case DRGUI_DELETE:
14440  {
14441  bool wasTextChanged = false;
14443  {
14445  wasTextChanged = drgui_text_engine_delete_selected_text(pTB->pTL);
14446  } else {
14447  wasTextChanged = drgui_text_engine_delete_character_to_right_of_cursor(pTB->pTL);
14448  }
14449  }
14450  if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
14451  } break;
14452 
14453 
14454  case DRGUI_ARROW_LEFT:
14455  {
14456  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14458  }
14459 
14463  } else {
14465  }
14466 
14467  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14469  }
14470  } break;
14471 
14472  case DRGUI_ARROW_RIGHT:
14473  {
14474  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14476  }
14477 
14481  } else {
14483  }
14484 
14485  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14487  }
14488  } break;
14489 
14490  case DRGUI_ARROW_UP:
14491  {
14492  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14494  }
14495 
14498  }
14499 
14501 
14502  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14504  }
14505  } break;
14506 
14507  case DRGUI_ARROW_DOWN:
14508  {
14509  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14511  }
14512 
14515  }
14516 
14518 
14519  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14521  }
14522  } break;
14523 
14524 
14525  case DRGUI_END:
14526  {
14527  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14529  }
14530 
14533  }
14534 
14535  if ((stateFlags & DRGUI_KEY_STATE_CTRL_DOWN) != 0) {
14537  } else {
14539  }
14540 
14541  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14543  }
14544  } break;
14545 
14546  case DRGUI_HOME:
14547  {
14548  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14550  }
14551 
14554  }
14555 
14556  if ((stateFlags & DRGUI_KEY_STATE_CTRL_DOWN) != 0) {
14558  } else {
14560  }
14561 
14562  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14564  }
14565  } break;
14566 
14567  case DRGUI_PAGE_UP:
14568  {
14569  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14571  }
14572 
14575  }
14576 
14577  int scrollOffset = drgui_sb_get_page_size(pTB->pVertScrollbar);
14578  if ((stateFlags & DRGUI_KEY_STATE_CTRL_DOWN) == 0) {
14579  drgui_sb_scroll(pTB->pVertScrollbar, -scrollOffset);
14580  }
14581 
14582  drgui_text_engine_move_cursor_y(pTB->pTL, -drgui_sb_get_page_size(pTB->pVertScrollbar));
14583 
14584  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14586  }
14587  } break;
14588 
14589  case DRGUI_PAGE_DOWN:
14590  {
14591  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14593  }
14594 
14597  }
14598 
14599  int scrollOffset = drgui_sb_get_page_size(pTB->pVertScrollbar);
14600  if (scrollOffset > (int)(drgui_text_engine_get_line_count(pTB->pTL) - drgui_text_engine_get_cursor_line(pTB->pTL))) {
14601  scrollOffset = 0;
14602  }
14603 
14604  if ((stateFlags & DRGUI_KEY_STATE_CTRL_DOWN) == 0) {
14605  drgui_sb_scroll(pTB->pVertScrollbar, scrollOffset);
14606  }
14607 
14608  drgui_text_engine_move_cursor_y(pTB->pTL, drgui_sb_get_page_size(pTB->pVertScrollbar));
14609 
14610  if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
14612  }
14613  } break;
14614 
14615  default: break;
14616  }
14617 }
14618 
14619 void drgui_textbox_on_key_up(drgui_element* pTBElement, drgui_key key, int stateFlags)
14620 {
14621  (void)pTBElement;
14622  (void)key;
14623  (void)stateFlags;
14624 }
14625 
14626 void drgui_textbox_on_printable_key_down(drgui_element* pTBElement, unsigned int utf32, int stateFlags)
14627 {
14628  (void)stateFlags;
14629 
14630  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14631  if (pTB == NULL) {
14632  return;
14633  }
14634 
14636  {
14639  }
14640 
14642  }
14644 }
14645 
14646 
14647 DRGUI_PRIVATE void drgui_textbox__on_text_engine_paint_rect(drgui_text_engine* pTL, drgui_rect rect, drgui_color color, drgui_element* pTBElement, void* pPaintData)
14648 {
14649  (void)pTL;
14650 
14651  float offsetX;
14652  float offsetY;
14653  drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
14654 
14655  drgui_draw_rect(pTBElement, drgui_offset_rect(rect, offsetX, offsetY), color, pPaintData);
14656 }
14657 
14658 DRGUI_PRIVATE void drgui_textbox__on_text_engine_paint_text(drgui_text_engine* pTL, drgui_text_run* pRun, drgui_element* pTBElement, void* pPaintData)
14659 {
14660  (void)pTL;
14661 
14662  float offsetX;
14663  float offsetY;
14664  drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
14665 
14666  drgui_draw_text(pTBElement, pRun->pFont, pRun->text, (int)pRun->textLength, (float)pRun->posX + offsetX, (float)pRun->posY + offsetY, pRun->textColor, pRun->backgroundColor, pPaintData);
14667 }
14668 
14669 DRGUI_PRIVATE void drgui_textbox__on_text_engine_dirty(drgui_text_engine* pTL, drgui_rect rect)
14670 {
14672  if (pTBElement == NULL) {
14673  return;
14674  }
14675 
14676  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14677  if (pTB == NULL) {
14678  return;
14679  }
14680 
14681  float offsetX;
14682  float offsetY;
14683  drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
14684 
14685  drgui_dirty(pTBElement, drgui_offset_rect(rect, offsetX, offsetY));
14686 }
14687 
14688 DRGUI_PRIVATE void drgui_textbox__on_text_engine_cursor_move(drgui_text_engine* pTL)
14689 {
14690  // If the cursor is off the edge of the container we want to scroll it into position.
14692  if (pTBElement == NULL) {
14693  return;
14694  }
14695 
14696  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14697  if (pTB == NULL) {
14698  return;
14699  }
14700 
14701  // If the cursor is above or below the container, we need to scroll vertically.
14702  int iLine = (int)drgui_text_engine_get_cursor_line(pTB->pTL);
14703  if (iLine < drgui_sb_get_scroll_position(pTB->pVertScrollbar)) {
14704  drgui_sb_scroll_to(pTB->pVertScrollbar, iLine);
14705  }
14706 
14707  int iBottomLine = drgui_sb_get_scroll_position(pTB->pVertScrollbar) + drgui_sb_get_page_size(pTB->pVertScrollbar) - 1;
14708  if (iLine >= iBottomLine) {
14709  drgui_sb_scroll_to(pTB->pVertScrollbar, iLine - (drgui_sb_get_page_size(pTB->pVertScrollbar) - 1) + 1);
14710  }
14711 
14712 
14713  // If the cursor is to the left or right of the container we need to scroll horizontally.
14714  float cursorPosX;
14715  float cursorPosY;
14716  drgui_text_engine_get_cursor_position(pTB->pTL, &cursorPosX, &cursorPosY);
14717 
14718  if (cursorPosX < 0) {
14719  drgui_sb_scroll_to(pTB->pHorzScrollbar, (int)(cursorPosX - drgui_text_engine_get_inner_offset_x(pTB->pTL)) - 100);
14720  }
14721  if (cursorPosX >= drgui_text_engine_get_container_width(pTB->pTL)) {
14722  drgui_sb_scroll_to(pTB->pHorzScrollbar, (int)(cursorPosX - drgui_text_engine_get_inner_offset_x(pTB->pTL) - drgui_text_engine_get_container_width(pTB->pTL)) + 100);
14723  }
14724 
14725 
14726  if (pTB->onCursorMove) {
14727  pTB->onCursorMove(pTBElement);
14728  }
14729 }
14730 
14731 DRGUI_PRIVATE void drgui_textbox__on_text_engine_text_changed(drgui_text_engine* pTL)
14732 {
14734  if (pTBElement == NULL) {
14735  return;
14736  }
14737 
14738  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14739  if (pTB == NULL) {
14740  return;
14741  }
14742 
14743  // Scrollbars need to be refreshed whenever text is changed.
14744  drgui_textbox__refresh_scrollbars(pTBElement);
14745 
14746  // The line numbers need to be redrawn.
14747  // TODO: This can probably be optimized a bit so that it is only redrawn if a line was inserted or deleted.
14748  drgui_dirty(pTB->pLineNumbers, drgui_get_local_rect(pTB->pLineNumbers));
14749 }
14750 
14751 DRGUI_PRIVATE void drgui_textbox__on_text_engine_undo_point_changed(drgui_text_engine* pTL, unsigned int iUndoPoint)
14752 {
14754  if (pTBElement == NULL) {
14755  return;
14756  }
14757 
14758  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14759  if (pTB == NULL) {
14760  return;
14761  }
14762 
14763  if (pTB->onUndoPointChanged) {
14764  pTB->onUndoPointChanged(pTBElement, iUndoPoint);
14765  }
14766 }
14767 
14768 
14769 void drgui_textbox_on_paint(drgui_element* pTBElement, drgui_rect relativeRect, void* pPaintData)
14770 {
14771  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14772  if (pTB == NULL) {
14773  return;
14774  }
14775 
14776  drgui_rect textRect = drgui_textbox__get_text_rect(pTBElement);
14777 
14778  // The dead space between the scrollbars should always be drawn with the default background color.
14779  drgui_draw_rect(pTBElement, drgui_textbox__get_scrollbar_dead_space_rect(pTBElement), drgui_text_engine_get_default_bg_color(pTB->pTL), pPaintData);
14780 
14781  // Border.
14782  drgui_rect borderRect = drgui_get_local_rect(pTBElement);
14783  drgui_draw_rect_outline(pTBElement, borderRect, pTB->borderColor, pTB->borderWidth, pPaintData);
14784 
14785  // Padding.
14786  drgui_rect paddingRect = drgui_grow_rect(textRect, pTB->padding);
14787  drgui_draw_rect_outline(pTBElement, paddingRect, drgui_text_engine_get_default_bg_color(pTB->pTL), pTB->padding, pPaintData);
14788 
14789  // Text.
14790  drgui_set_clip(pTBElement, drgui_clamp_rect(textRect, relativeRect), pPaintData);
14791  drgui_text_engine_paint(pTB->pTL, drgui_offset_rect(drgui_clamp_rect(textRect, relativeRect), -textRect.left, -textRect.top), pTBElement, pPaintData);
14792 }
14793 
14794 void drgui_textbox_on_capture_keyboard(drgui_element* pTBElement, drgui_element* pPrevCapturedElement)
14795 {
14796  (void)pPrevCapturedElement;
14797 
14798  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14799  if (pTB == NULL) {
14800  return;
14801  }
14802 
14804 }
14805 
14806 void drgui_textbox_on_release_keyboard(drgui_element* pTBElement, drgui_element* pNewCapturedElement)
14807 {
14808  (void)pNewCapturedElement;
14809 
14810  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14811  if (pTB == NULL) {
14812  return;
14813  }
14814 
14816 }
14817 
14819 {
14820  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14821  if (pTB == NULL) {
14822  return;
14823  }
14824 
14825 }
14826 
14828 {
14829  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14830  if (pTB == NULL) {
14831  return;
14832  }
14833 
14835 }
14836 
14837 
14838 
14839 DRGUI_PRIVATE void drgui_textbox__get_text_offset(drgui_element* pTBElement, float* pOffsetXOut, float* pOffsetYOut)
14840 {
14841  float offsetX = 0;
14842  float offsetY = 0;
14843 
14844  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14845  if (pTB != NULL)
14846  {
14847  float lineNumbersWidth = 0;
14848  if (drgui_is_visible(pTB->pLineNumbers)) {
14849  lineNumbersWidth = drgui_get_width(pTB->pLineNumbers);
14850  }
14851 
14852  offsetX = pTB->borderWidth + pTB->padding + lineNumbersWidth;
14853  offsetY = pTB->borderWidth + pTB->padding;
14854  }
14855 
14856 
14857  if (pOffsetXOut != NULL) {
14858  *pOffsetXOut = offsetX;
14859  }
14860  if (pOffsetYOut != NULL) {
14861  *pOffsetYOut = offsetY;
14862  }
14863 }
14864 
14865 DRGUI_PRIVATE void drgui_textbox__calculate_text_engine_container_size(drgui_element* pTBElement, float* pWidthOut, float* pHeightOut)
14866 {
14867  float width = 0;
14868  float height = 0;
14869 
14870  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14871  if (pTB != NULL)
14872  {
14873  float horzScrollbarSize = 0;
14874  if (drgui_is_visible(pTB->pHorzScrollbar)) {
14875  horzScrollbarSize = drgui_get_height(pTB->pHorzScrollbar);
14876  }
14877 
14878  float vertScrollbarSize = 0;
14879  if (drgui_is_visible(pTB->pVertScrollbar)) {
14880  vertScrollbarSize = drgui_get_width(pTB->pVertScrollbar);
14881  }
14882 
14883  float lineNumbersWidth = 0;
14884  if (drgui_is_visible(pTB->pLineNumbers)) {
14885  lineNumbersWidth = drgui_get_width(pTB->pLineNumbers);
14886  }
14887 
14888  width = drgui_get_width(pTBElement) - (pTB->borderWidth + pTB->padding)*2 - vertScrollbarSize - lineNumbersWidth;
14889  height = drgui_get_height(pTBElement) - (pTB->borderWidth + pTB->padding)*2 - horzScrollbarSize;
14890  }
14891 
14892  if (pWidthOut != NULL) {
14893  *pWidthOut = width;
14894  }
14895  if (pHeightOut != NULL) {
14896  *pHeightOut = height;
14897  }
14898 }
14899 
14900 DRGUI_PRIVATE drgui_rect drgui_textbox__get_text_rect(drgui_element* pTBElement)
14901 {
14902  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14903  if (pTB == NULL) {
14904  return drgui_make_rect(0, 0, 0, 0);
14905  }
14906 
14907  float offsetX;
14908  float offsetY;
14909  drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
14910 
14911  float width;
14912  float height;
14913  drgui_textbox__calculate_text_engine_container_size(pTBElement, &width, &height);
14914 
14915  return drgui_make_rect(offsetX, offsetY, offsetX + width, offsetY + height);
14916 }
14917 
14918 
14919 DRGUI_PRIVATE void drgui_textbox__refresh_scrollbars(drgui_element* pTBElement)
14920 {
14921  // The layout depends on the range because we may be dynamically hiding and showing the scrollbars depending on the range. Thus, we
14922  // refresh the range first. However, dynamically showing and hiding the scrollbars (which is done when the layout is refreshed) affects
14923  // the size of the text box, which in turn affects the range. Thus, we need to refresh the ranges a second time after the layouts.
14924 
14925  drgui_textbox__refresh_scrollbar_ranges(pTBElement);
14926  drgui_textbox__refresh_scrollbar_layouts(pTBElement);
14927  drgui_textbox__refresh_scrollbar_ranges(pTBElement);
14928 }
14929 
14930 DRGUI_PRIVATE void drgui_textbox__refresh_scrollbar_ranges(drgui_element* pTBElement)
14931 {
14932  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14933  assert(pTB != NULL);
14934 
14935  // The vertical scrollbar is based on the line count.
14936  size_t lineCount = drgui_text_engine_get_line_count(pTB->pTL);
14937  size_t pageSize = drgui_text_engine_get_visible_line_count_starting_at(pTB->pTL, drgui_sb_get_scroll_position(pTB->pVertScrollbar));
14938  drgui_sb_set_range_and_page_size(pTB->pVertScrollbar, 0, (int)(lineCount + pageSize - 1 - 1), (int)pageSize); // -1 to make the range 0 based. -1 to ensure at least one line is visible.
14939 
14940  if (drgui_sb_is_thumb_visible(pTB->pVertScrollbar)) {
14941  if (!drgui_is_visible(pTB->pVertScrollbar)) {
14942  drgui_show(pTB->pVertScrollbar);
14943  }
14944  } else {
14945  if (drgui_is_visible(pTB->pVertScrollbar)) {
14946  drgui_hide(pTB->pVertScrollbar);
14947  }
14948  }
14949 
14950 
14951  // The horizontal scrollbar is a per-pixel scrollbar, and is based on the width of the text versus the width of the container.
14953  float containerWidth;
14954  drgui_text_engine_get_container_size(pTB->pTL, &containerWidth, NULL);
14955  drgui_sb_set_range_and_page_size(pTB->pHorzScrollbar, 0, (int)(textRect.right - textRect.left + (containerWidth/2)), (int)containerWidth);
14956 
14957  if (drgui_sb_is_thumb_visible(pTB->pHorzScrollbar)) {
14958  if (!drgui_is_visible(pTB->pHorzScrollbar)) {
14959  drgui_show(pTB->pHorzScrollbar);
14960  drgui_textbox__refresh_line_numbers(pTBElement);
14961  }
14962  } else {
14963  if (drgui_is_visible(pTB->pHorzScrollbar)) {
14964  drgui_hide(pTB->pHorzScrollbar);
14965  drgui_textbox__refresh_line_numbers(pTBElement);
14966  }
14967  }
14968 }
14969 
14970 DRGUI_PRIVATE void drgui_textbox__refresh_scrollbar_layouts(drgui_element* pTBElement)
14971 {
14972  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14973  assert(pTB != NULL);
14974 
14975  float offsetLeft = pTB->borderWidth;
14976  float offsetTop = pTB->borderWidth;
14977  float offsetRight = pTB->borderWidth;
14978  float offsetBottom = pTB->borderWidth;
14979 
14980  float scrollbarSizeH = (drgui_sb_is_thumb_visible(pTB->pHorzScrollbar) && pTB->isHorzScrollbarEnabled) ? pTB->horzScrollbarSize : 0;
14981  float scrollbarSizeV = (drgui_sb_is_thumb_visible(pTB->pVertScrollbar) && pTB->isVertScrollbarEnabled) ? pTB->vertScrollbarSize : 0;
14982 
14983  drgui_set_size(pTB->pVertScrollbar, scrollbarSizeV, drgui_get_height(pTBElement) - scrollbarSizeH - (offsetTop + offsetBottom));
14984  drgui_set_size(pTB->pHorzScrollbar, drgui_get_width(pTBElement) - scrollbarSizeV - (offsetLeft + offsetRight), scrollbarSizeH);
14985 
14986  drgui_set_relative_position(pTB->pVertScrollbar, drgui_get_width(pTBElement) - scrollbarSizeV - offsetRight, offsetTop);
14987  drgui_set_relative_position(pTB->pHorzScrollbar, offsetLeft, drgui_get_height(pTBElement) - scrollbarSizeH - offsetBottom);
14988 
14989 
14990  // A change in the layout of the horizontal scrollbar will affect the layout of the line numbers.
14991  drgui_textbox__refresh_line_numbers(pTBElement);
14992 }
14993 
14994 DRGUI_PRIVATE drgui_rect drgui_textbox__get_scrollbar_dead_space_rect(drgui_element* pTBElement)
14995 {
14996  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
14997  assert(pTB != NULL);
14998 
14999  float offsetLeft = pTB->borderWidth;
15000  float offsetTop = pTB->borderWidth;
15001  float offsetRight = pTB->borderWidth;
15002  float offsetBottom = pTB->borderWidth;
15003 
15004  float scrollbarSizeH = (drgui_is_visible(pTB->pHorzScrollbar) && pTB->isHorzScrollbarEnabled) ? drgui_get_width(pTB->pHorzScrollbar) : 0;
15005  float scrollbarSizeV = (drgui_is_visible(pTB->pVertScrollbar) && pTB->isHorzScrollbarEnabled) ? drgui_get_height(pTB->pVertScrollbar) : 0;
15006 
15007  if (scrollbarSizeH == 0 && scrollbarSizeV == 0) {
15008  return drgui_make_rect(0, 0, 0, 0);
15009  }
15010 
15011  return drgui_make_rect(scrollbarSizeH + offsetLeft, scrollbarSizeV + offsetTop, drgui_get_width(pTBElement) - offsetRight, drgui_get_height(pTBElement) - offsetBottom);
15012 }
15013 
15014 
15015 DRGUI_PRIVATE void drgui_textbox__on_mouse_move_line_numbers(drgui_element* pLineNumbers, int relativeMousePosX, int relativeMousePosY, int stateFlags)
15016 {
15017  (void)relativeMousePosX;
15018 
15019  drgui_element* pTBElement = *(drgui_element**)drgui_get_extra_data(pLineNumbers);
15020  assert(pTBElement != NULL);
15021 
15022  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
15023  assert(pTB != NULL);
15024 
15025  if ((stateFlags & DRGUI_MOUSE_BUTTON_LEFT_DOWN) != 0)
15026  {
15027  if (drgui_get_element_with_mouse_capture(pLineNumbers->pContext) == pLineNumbers)
15028  {
15029  // We just move the cursor around based on the line number we've moved over.
15031  {
15032  //float offsetX = pTextEditorData->padding;
15033  float offsetY = pTB->padding;
15034  size_t iLine = drgui_text_engine_get_line_at_pos_y(pTB->pTL, relativeMousePosY - offsetY);
15035  size_t iAnchorLine = pTB->iLineSelectAnchor;
15036  size_t lineCount = drgui_text_engine_get_line_count(pTB->pTL);
15037 
15038  size_t iSelectionFirstLine = drgui_text_engine_get_selection_first_line(pTB->pTL);
15039  size_t iSelectionLastLine = drgui_text_engine_get_selection_last_line(pTB->pTL);
15040  if (iSelectionLastLine != iSelectionFirstLine) {
15041  iSelectionLastLine -= 1;
15042  }
15043 
15044  // If we're moving updwards we want to position the cursor at the start of the line. Otherwise we want to move the cursor to the start
15045  // of the next line, or the end of the text.
15046  bool movingUp = false;
15047  if (iLine < iAnchorLine) {
15048  movingUp = true;
15049  }
15050 
15051  // If we're moving up the selection anchor needs to be placed at the end of the last line. Otherwise we need to move it to the start
15052  // of the first line.
15053  if (movingUp) {
15054  if (iAnchorLine + 1 < lineCount) {
15056  } else {
15058  }
15059  } else {
15061  }
15062 
15063 
15064  // If we're moving up we want the cursor to be placed at the start of the selection range. Otherwise we want to place the cursor
15065  // at the end of the selection range.
15066  if (movingUp) {
15068  } else {
15069  if (iLine + 1 < lineCount) {
15071  } else {
15073  }
15074  }
15075  }
15077  }
15078  }
15079 }
15080 
15081 DRGUI_PRIVATE void drgui_textbox__on_mouse_button_down_line_numbers(drgui_element* pLineNumbers, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
15082 {
15083  (void)relativeMousePosX;
15084  (void)stateFlags;
15085 
15086  drgui_element* pTBElement = *(drgui_element**)drgui_get_extra_data(pLineNumbers);
15087  assert(pTBElement != NULL);
15088 
15089  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
15090  assert(pTB != NULL);
15091 
15092  if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT)
15093  {
15094  //float offsetX = pTextEditorData->padding;
15095  float offsetY = pTB->padding;
15096  pTB->iLineSelectAnchor = drgui_text_engine_get_line_at_pos_y(pTB->pTL, relativeMousePosY - offsetY);
15097 
15099 
15100  drgui_text_engine_move_cursor_to_start_of_line_by_index(pTB->pTL, pTB->iLineSelectAnchor);
15101 
15103  {
15104  if (pTB->iLineSelectAnchor + 1 < drgui_text_engine_get_line_count(pTB->pTL)) {
15105  drgui_text_engine_move_cursor_to_start_of_line_by_index(pTB->pTL, pTB->iLineSelectAnchor + 1);
15106  } else {
15108  }
15109  }
15111 
15112  drgui_capture_mouse(pLineNumbers);
15113  }
15114 }
15115 
15116 DRGUI_PRIVATE void drgui_textbox__on_mouse_button_up_line_numbers(drgui_element* pLineNumbers, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
15117 {
15118  (void)relativeMousePosX;
15119  (void)relativeMousePosY;
15120  (void)stateFlags;
15121 
15122  if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT) {
15123  if (drgui_has_mouse_capture(pLineNumbers)) {
15124  drgui_release_mouse(pLineNumbers->pContext);
15125  }
15126  }
15127 }
15128 
15129 DRGUI_PRIVATE void drgui_textbox__on_paint_rect_line_numbers(drgui_text_engine* pLayout, drgui_rect rect, drgui_color color, drgui_element* pTBElement, void* pPaintData)
15130 {
15131  (void)pLayout;
15132 
15133  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
15134  assert(pTB != NULL);
15135 
15136  float offsetX = pTB->padding;
15137  float offsetY = pTB->padding;
15138 
15139  drgui_draw_rect(pTB->pLineNumbers, drgui_offset_rect(rect, offsetX, offsetY), color, pPaintData);
15140 }
15141 
15142 DRGUI_PRIVATE void drgui_textbox__on_paint_text_line_numbers(drgui_text_engine* pLayout, drgui_text_run* pRun, drgui_element* pTBElement, void* pPaintData)
15143 {
15144  (void)pLayout;
15145 
15146  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
15147  assert(pTB != NULL);
15148 
15149  float offsetX = pTB->padding;
15150  float offsetY = pTB->padding;
15151  drgui_draw_text(pTB->pLineNumbers, pRun->pFont, pRun->text, (int)pRun->textLength, (float)pRun->posX + offsetX, (float)pRun->posY + offsetY, pRun->textColor, pRun->backgroundColor, pPaintData);
15152 }
15153 
15154 DRGUI_PRIVATE void drgui_textbox__on_paint_line_numbers(drgui_element* pLineNumbers, drgui_rect relativeRect, void* pPaintData)
15155 {
15156  (void)relativeRect;
15157 
15158  drgui_element* pTBElement = *((drgui_element**)drgui_get_extra_data(pLineNumbers));
15159  assert(pTBElement != NULL);
15160 
15161  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
15162  assert(pTB != NULL);
15163 
15164  float lineNumbersWidth = drgui_get_width(pLineNumbers) - (pTB->padding*2) - pTB->lineNumbersPaddingRight;
15165  float lineNumbersHeight = drgui_get_height(pLineNumbers) - (pTB->padding*2);
15166 
15167  drgui_text_engine_paint_line_numbers(pTB->pTL, lineNumbersWidth, lineNumbersHeight, pTB->lineNumbersColor, pTB->lineNumbersBackgroundColor, drgui_textbox__on_paint_text_line_numbers, drgui_textbox__on_paint_rect_line_numbers, pTBElement, pPaintData);
15168 
15169  drgui_draw_rect_outline(pLineNumbers, drgui_get_local_rect(pLineNumbers), pTB->lineNumbersBackgroundColor, pTB->padding, pPaintData);
15170 
15171  // Right padding.
15172  drgui_rect rightPaddingRect = drgui_get_local_rect(pLineNumbers);
15173  rightPaddingRect.right -= pTB->padding;
15174  rightPaddingRect.left = rightPaddingRect.right - pTB->lineNumbersPaddingRight;
15175  drgui_draw_rect(pLineNumbers, rightPaddingRect, pTB->lineNumbersBackgroundColor, pPaintData);
15176 }
15177 
15178 DRGUI_PRIVATE void drgui_textbox__refresh_line_numbers(drgui_element* pTBElement)
15179 {
15180  drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
15181  assert(pTB != NULL);
15182 
15183  float lineNumbersWidth = 0;
15184  if (drgui_is_visible(pTB->pLineNumbers)) {
15185  lineNumbersWidth = pTB->lineNumbersWidth;
15186  }
15187 
15188  float scrollbarHeight = drgui_is_visible(pTB->pHorzScrollbar) ? drgui_get_height(pTB->pHorzScrollbar) : 0;
15189  drgui_set_size(pTB->pLineNumbers, lineNumbersWidth, drgui_get_height(pTBElement) - scrollbarHeight);
15190 
15191 
15192  // The size of the text container may have changed.
15193  float textEditorWidth;
15194  float textEditorHeight;
15195  drgui_textbox__calculate_text_engine_container_size(pTBElement, &textEditorWidth, &textEditorHeight);
15196  drgui_text_engine_set_container_size(pTB->pTL, textEditorWidth, textEditorHeight);
15197 
15198 
15199  // Force a redraw just to be sure everything is in a valid state.
15200  drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
15201 }
15202 #endif //DR_GUI_IMPLEMENTATION
15203 #endif //DRGUI_NO_TEXT_EDITING
15204 
15205 
15208 //
15209 // Tree View
15210 //
15213 
15214 // QUICK NOTES
15215 //
15216 // Tree-View Controls
15217 // - A tree-view control is a complex control with a hierarchy of items. They are typically used for file explorers.
15218 
15219 #ifndef drgui_tree_view_h
15220 #define drgui_tree_view_h
15221 
15222 #ifdef __cplusplus
15223 extern "C" {
15224 #endif
15225 
15226 #define EG_MAX_TREE_VIEW_ITEM_TEXT_LENGTH 256
15227 
15229 
15230 typedef void (* drgui_tvi_on_mouse_move_proc) (drgui_tree_view_item* pItem, int relativeMousePosX, int relativeMousePosY, bool* pIsOverArrow);
15232 typedef void (* drgui_tvi_on_paint_proc) (drgui_element* pTVElement, drgui_tree_view_item* pItem, drgui_rect relativeClippingRect, drgui_color backgroundColor, float offsetX, float offsetY, float width, float height, void* pPaintData);
15233 typedef void (* drgui_tvi_measure_proc) (drgui_tree_view_item* pItem, float* pWidthOut, float* pHeightOut);
15235 
15237 //
15238 // Tree-View
15239 //
15241 
15243 drgui_element* drgui_create_tree_view(drgui_context* pContext, drgui_element* pParent, size_t extraDataSize, const void* pExtraData);
15244 
15246 void drgui_delete_tree_view(drgui_element* pTVElement);
15247 
15248 
15250 size_t drgui_tv_get_extra_data_size(drgui_element* pTVElement);
15251 
15253 void* drgui_tv_get_extra_data(drgui_element* pTVElement);
15254 
15257 
15260 
15263 
15264 
15267 
15270 
15273 
15276 
15279 
15282 
15284 void drgui_tv_set_child_offset_x(drgui_element* pTVElement, float childOffsetX);
15285 
15287 float drgui_tv_get_child_offset_x(drgui_element* pTVElement);
15288 
15289 
15291 bool drgui_tv_measure_item(drgui_element* pTVElement, drgui_tree_view_item* pItem, float* pWidthOut, float* pHeightOut);
15292 
15294 void drgui_tv_deselect_all_items(drgui_element* pTVElement);
15295 
15302 
15305 
15308 
15309 
15315 
15324 
15325 
15328 
15331 
15334 
15337 
15343 
15344 
15346 void drgui_tv_on_size(drgui_element* pTVElement, float newWidth, float newHeight);
15347 
15349 void drgui_tv_on_mouse_leave(drgui_element* pTVElement);
15350 
15352 void drgui_tv_on_mouse_move(drgui_element* pTVElement, int relativeMousePosX, int relativeMousePosY, int stateFlags);
15353 
15355 void drgui_tv_on_mouse_button_down(drgui_element* pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
15356 
15358 void drgui_tv_on_mouse_button_up(drgui_element* pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
15359 
15361 void drgui_tv_on_mouse_button_dblclick(drgui_element* pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
15362 
15364 void drgui_tv_on_mouse_wheel(drgui_element* pTVElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags);
15365 
15367 void drgui_tv_on_paint(drgui_element* pTVElement, drgui_rect relativeClippingRect, void* pPaintData);
15368 
15369 
15371 //
15372 // Tree-View Item
15373 //
15375 
15381 drgui_tree_view_item* drgui_tv_create_item(drgui_element* pTVElement, drgui_tree_view_item* pParent, size_t extraDataSize, const void* pExtraData);
15382 
15385 
15388 
15391 
15394 
15395 
15398 
15401 
15404 
15407 
15410 
15413 
15416 
15418 void drgui_tvi_append_sibling(drgui_tree_view_item* pItemToAppend, drgui_tree_view_item* pItemToAppendTo);
15419 
15421 void drgui_tvi_prepend_sibling(drgui_tree_view_item* pItemToPrepend, drgui_tree_view_item* pItemToPrependTo);
15422 
15423 
15426 
15432 
15440 
15441 
15444 
15447 
15450 
15453 
15456 
15459 
15460 
15461 
15462 #ifdef __cplusplus
15463 }
15464 #endif
15465 #endif //drgui_tree_view_h
15466 
15467 
15468 #ifdef DR_GUI_IMPLEMENTATION
15469 typedef struct drgui_tree_view drgui_tree_view;
15470 
15471 struct drgui_tree_view
15472 {
15474  drgui_tree_view_item* pRootItem;
15475 
15477  drgui_element* pScrollbarV;
15478 
15480  drgui_element* pScrollbarH;
15481 
15482 
15484  drgui_color defaultBGColor;
15485 
15487  drgui_color hoveredBGColor;
15488 
15490  drgui_color selectedBGColor;
15491 
15493  float childOffsetX;
15494 
15495 
15497  drgui_tvi_on_mouse_move_proc onItemMouseMove;
15498 
15500  drgui_tvi_on_mouse_leave_proc onItemMouseLeave;
15501 
15503  drgui_tvi_on_paint_proc onItemPaint;
15504 
15506  drgui_tvi_measure_proc onItemMeasure;
15507 
15509  drgui_tvi_on_picked_proc onItemPicked;
15510 
15511 
15513  drgui_tree_view_item* pHoveredItem;
15514 
15516  bool isMouseOverArrow;
15517 
15519  bool isMouseOver;
15520 
15522  int relativeMousePosX;
15523 
15525  int relativeMousePosY;
15526 
15527 
15529  bool isMultiSelectEnabled;
15530 
15532  bool isRangeSelectEnabled;
15533 
15534 
15536  size_t extraDataSize;
15537 
15539  char pExtraData[1];
15540 };
15541 
15542 struct drgui_tree_view_item
15543 {
15545  drgui_element* pTVElement;
15546 
15547 
15549  drgui_tree_view_item* pParent;
15550 
15552  drgui_tree_view_item* pFirstChild;
15553 
15555  drgui_tree_view_item* pLastChild;
15556 
15558  drgui_tree_view_item* pNextSibling;
15559 
15561  drgui_tree_view_item* pPrevSibling;
15562 
15563 
15565  bool isSelected;
15566 
15568  bool isExpanded;
15569 
15570 
15572  size_t extraDataSize;
15573 
15575  char pExtraData[1];
15576 };
15577 
15578 typedef struct
15579 {
15581  drgui_tree_view_item* pItem;
15582 
15584  float width;
15585 
15587  float height;
15588 
15590  float posX;
15591 
15593  float posY;
15594 
15596  int depth;
15597 
15598 } drgui_tree_view_iterator;
15599 
15600 typedef struct
15601 {
15603  float width;
15604 
15606  float height;
15607 
15609  float posX;
15610 
15612  float posY;
15613 
15614 } drgui_tree_view_item_metrics;
15615 
15616 typedef struct
15617 {
15619  drgui_element* pTVElement;
15620 
15621 } drgui_tree_view_scrollbar_data;
15622 
15623 
15624 
15625 
15627 //
15628 // Tree-View
15629 //
15631 
15633 static void drgui_tv_refresh_and_redraw(drgui_element* pTVElement);
15634 
15636 static void drgui_tv_refresh_scrollbar_layouts(drgui_element* pTVElement);
15637 
15639 static void drgui_tv_refresh_scrollbar_ranges(drgui_element* pTVElement);
15640 
15642 static drgui_rect drgui_tv_get_scrollbar_dead_space_rect(drgui_element* pTVElement);
15643 
15645 static drgui_rect drgui_tv_get_inner_rect(drgui_element* pTVElement);
15646 
15648 static void drgui_tv_paint_items(drgui_element* pTVElement, drgui_rect relativeClippingRect, void* pPaintData, float* pItemsBottomOut);
15649 
15651 static bool drgui_tv_begin_at(drgui_tree_view_item* pFirst, drgui_tree_view_iterator* pIteratorOut);
15652 
15654 static bool drgui_tv_next_visible(drgui_tree_view_iterator* pIterator);
15655 
15657 static void drgui_tv_paint_item(drgui_element* pTVElement, drgui_tree_view_item* pItem, drgui_rect relativeClippingRect, float posX, float posY, float width, float height, void* pPaintData);
15658 
15660 static drgui_tree_view_item* drgui_tv_find_item_under_point(drgui_element* pTV, float relativePosX, float relativePosY, drgui_tree_view_item_metrics* pMetricsOut);
15661 
15663 static void drgui_tv_deselect_all_items_recursive(drgui_tree_view_item* pItem);
15664 
15667 static void drgui_tv_on_mouse_enter_scrollbar(drgui_element* pSBElement);
15668 
15670 static void drgui_tv_on_scroll_v(drgui_element* pSBElement, int scrollPos);
15671 
15673 static void drgui_tv_on_scroll_h(drgui_element* pSBElement, int scrollPos);
15674 
15676 static drgui_tree_view_item* drgui_tv_find_first_visible_item_on_page(drgui_element* pTVElement);
15677 
15678 
15679 drgui_element* drgui_create_tree_view(drgui_context* pContext, drgui_element* pParent, size_t extraDataSize, const void* pExtraData)
15680 {
15681  drgui_element* pTVElement = drgui_create_element(pContext, pParent, sizeof(drgui_tree_view) + extraDataSize, NULL);
15682  if (pTVElement == NULL) {
15683  return NULL;
15684  }
15685 
15686  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15687  if (pTV == NULL) {
15688  return NULL;
15689  }
15690 
15691  pTV->pRootItem = drgui_tv_create_item(pTVElement, NULL, 0, NULL);
15692  if (pTV->pRootItem == NULL) {
15693  return NULL;
15694  }
15695 
15696 
15697  drgui_tree_view_scrollbar_data sbdata;
15698  sbdata.pTVElement = pTVElement;
15699 
15700  pTV->pScrollbarV = drgui_create_scrollbar(pContext, pTVElement, drgui_sb_orientation_vertical, sizeof(sbdata), &sbdata);
15701  drgui_set_on_mouse_enter(pTV->pScrollbarV, drgui_tv_on_mouse_enter_scrollbar);
15702  drgui_sb_set_on_scroll(pTV->pScrollbarV, drgui_tv_on_scroll_v);
15703 
15704  pTV->pScrollbarH = drgui_create_scrollbar(pContext, pTVElement, drgui_sb_orientation_horizontal, sizeof(sbdata), &sbdata);
15705  drgui_set_on_mouse_enter(pTV->pScrollbarH, drgui_tv_on_mouse_enter_scrollbar);
15706  drgui_sb_set_on_scroll(pTV->pScrollbarH, drgui_tv_on_scroll_h);
15707 
15708 
15709  pTV->defaultBGColor = drgui_rgb(96, 96, 96);
15710  pTV->hoveredBGColor = drgui_rgb(112, 112, 112);
15711  pTV->selectedBGColor = drgui_rgb(80, 160, 255);
15712  pTV->childOffsetX = 16;
15713 
15714  pTV->onItemMouseMove = NULL;
15715  pTV->onItemMouseLeave = NULL;
15716  pTV->onItemPaint = NULL;
15717  pTV->onItemMeasure = NULL;
15718  pTV->onItemPicked = NULL;
15719 
15720  pTV->pHoveredItem = NULL;
15721  pTV->isMouseOverArrow = false;
15722  pTV->isMouseOver = false;
15723  pTV->relativeMousePosX = 0;
15724  pTV->relativeMousePosY = 0;
15725 
15726  pTV->isMultiSelectEnabled = false;
15727  pTV->isRangeSelectEnabled = false;
15728 
15729  pTV->extraDataSize = extraDataSize;
15730  if (pExtraData != NULL) {
15731  memcpy(pTV->pExtraData, pExtraData, extraDataSize);
15732  }
15733 
15734 
15735  // Default event handlers.
15736  drgui_set_on_size(pTVElement, drgui_tv_on_size);
15744 
15745 
15746  // Set the mouse wheel scale to 3 by default for the vertical scrollbar.
15747  drgui_sb_set_mouse_wheel_scele(pTV->pScrollbarV, 3);
15748 
15749 
15750  return pTVElement;
15751 }
15752 
15753 void drgui_delete_tree_view(drgui_element* pTVElement)
15754 {
15755  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15756  if (pTV == NULL) {
15757  return;
15758  }
15759 
15760  // Recursively delete the tree view items.
15761  drgui_tvi_delete(pTV->pRootItem);
15762 
15763  // Delete the element last.
15764  drgui_delete_element(pTVElement);
15765 }
15766 
15767 
15768 size_t drgui_tv_get_extra_data_size(drgui_element* pTVElement)
15769 {
15770  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15771  if (pTV == NULL) {
15772  return 0;
15773  }
15774 
15775  return pTV->extraDataSize;
15776 }
15777 
15778 void* drgui_tv_get_extra_data(drgui_element* pTVElement)
15779 {
15780  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15781  if (pTV == NULL) {
15782  return NULL;
15783  }
15784 
15785  return pTV->pExtraData;
15786 }
15787 
15789 {
15790  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15791  if (pTV == NULL) {
15792  return NULL;
15793  }
15794 
15795  return pTV->pRootItem;
15796 }
15797 
15799 {
15800  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15801  if (pTV == NULL) {
15802  return NULL;
15803  }
15804 
15805  return pTV->pScrollbarV;
15806 }
15807 
15809 {
15810  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15811  if (pTV == NULL) {
15812  return NULL;
15813  }
15814 
15815  return pTV->pScrollbarH;
15816 }
15817 
15818 
15820 {
15821  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15822  if (pTV == NULL) {
15823  return;
15824  }
15825 
15826  pTV->defaultBGColor = color;
15827 }
15828 
15830 {
15831  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15832  if (pTV == NULL) {
15833  return drgui_rgb(0, 0, 0);
15834  }
15835 
15836  return pTV->defaultBGColor;
15837 }
15838 
15840 {
15841  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15842  if (pTV == NULL) {
15843  return;
15844  }
15845 
15846  pTV->hoveredBGColor = color;
15847 }
15848 
15850 {
15851  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15852  if (pTV == NULL) {
15853  return drgui_rgb(0, 0, 0);
15854  }
15855 
15856  return pTV->hoveredBGColor;
15857 }
15858 
15860 {
15861  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15862  if (pTV == NULL) {
15863  return;
15864  }
15865 
15866  pTV->selectedBGColor = color;
15867 }
15868 
15870 {
15871  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15872  if (pTV == NULL) {
15873  return drgui_rgb(0, 0, 0);
15874  }
15875 
15876  return pTV->selectedBGColor;
15877 }
15878 
15879 void drgui_tv_set_child_offset_x(drgui_element* pTVElement, float childOffsetX)
15880 {
15881  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15882  if (pTV == NULL) {
15883  return;
15884  }
15885 
15886  pTV->childOffsetX = childOffsetX;
15887 }
15888 
15889 float drgui_tv_get_child_offset_x(drgui_element* pTVElement)
15890 {
15891  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15892  if (pTV == NULL) {
15893  return 0;
15894  }
15895 
15896  return pTV->childOffsetX;
15897 }
15898 
15899 
15900 bool drgui_tv_measure_item(drgui_element* pTVElement, drgui_tree_view_item* pItem, float* pWidthOut, float* pHeightOut)
15901 {
15902  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15903  if (pTV == NULL) {
15904  return false;
15905  }
15906 
15907  if (pItem == NULL || pItem->pTVElement != pTVElement) {
15908  return false;
15909  }
15910 
15911  if (pTV->onItemMeasure)
15912  {
15913  pTV->onItemMeasure(pItem, pWidthOut, pHeightOut);
15914  return true;
15915  }
15916 
15917  return false;
15918 }
15919 
15921 {
15922  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15923  if (pTV == NULL) {
15924  return;
15925  }
15926 
15927  drgui_tv_deselect_all_items_recursive(pTV->pRootItem);
15928 
15929  // TODO: Only redraw the region that actually changed.
15930  drgui_dirty(pTVElement, drgui_get_local_rect(pTVElement));
15931 }
15932 
15933 
15935 {
15936  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15937  if (pTV == NULL) {
15938  return;
15939  }
15940 
15941  pTV->isMultiSelectEnabled = true;
15942 }
15943 
15945 {
15946  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15947  if (pTV == NULL) {
15948  return;
15949  }
15950 
15951  pTV->isMultiSelectEnabled = false;
15952 }
15953 
15955 {
15956  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15957  if (pTV == NULL) {
15958  return false;
15959  }
15960 
15961  return pTV->isMultiSelectEnabled;
15962 }
15963 
15965 {
15966  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15967  if (pTV == NULL) {
15968  return NULL;
15969  }
15970 
15971  drgui_tree_view_iterator i;
15972  if (drgui_tv_begin_at(pTV->pRootItem->pFirstChild, &i))
15973  {
15974  do
15975  {
15976  if (drgui_tvi_is_selected(i.pItem)) {
15977  return i.pItem;
15978  }
15979 
15980  } while (drgui_tv_next_visible(&i));
15981  }
15982 
15983  return NULL;
15984 }
15985 
15987 {
15988  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
15989  if (pTV == NULL) {
15990  return NULL;
15991  }
15992 
15993  drgui_tree_view_iterator i;
15994  if (drgui_tv_begin_at(pItem, &i))
15995  {
15996  // Note that we're not including <pItem> in this iteration.
15997  while (drgui_tv_next_visible(&i))
15998  {
15999  if (drgui_tvi_is_selected(i.pItem)) {
16000  return i.pItem;
16001  }
16002  }
16003  }
16004 
16005  return NULL;
16006 }
16007 
16008 
16010 {
16011  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16012  if (pTV == NULL) {
16013  return;
16014  }
16015 
16016  pTV->onItemMouseMove = proc;
16017 }
16018 
16020 {
16021  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16022  if (pTV == NULL) {
16023  return;
16024  }
16025 
16026  pTV->onItemMouseLeave = proc;
16027 }
16028 
16030 {
16031  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16032  if (pTV == NULL) {
16033  return;
16034  }
16035 
16036  pTV->onItemPaint = proc;
16037 }
16038 
16040 {
16041  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16042  if (pTV == NULL) {
16043  return;
16044  }
16045 
16046  pTV->onItemMeasure = proc;
16047 }
16048 
16050 {
16051  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16052  if (pTV == NULL) {
16053  return;
16054  }
16055 
16056  pTV->onItemPicked = proc;
16057 }
16058 
16059 
16060 void drgui_tv_on_size(drgui_element* pTVElement, float newWidth, float newHeight)
16061 {
16062  (void)newWidth;
16063  (void)newHeight;
16064 
16065  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16066  if (pTV == NULL) {
16067  return;
16068  }
16069 
16070  // Move the scrollbars.
16071  drgui_tv_refresh_scrollbar_layouts(pTVElement);
16072 
16073  // Refresh the scrollbar ranges.
16074  drgui_tv_refresh_scrollbar_ranges(pTVElement);
16075 }
16076 
16077 void drgui_tv_on_mouse_leave(drgui_element* pTVElement)
16078 {
16079  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16080  if (pTV == NULL) {
16081  return;
16082  }
16083 
16084  pTV->isMouseOver = false;
16085 
16086  if (pTV->pHoveredItem != NULL || pTV->isMouseOverArrow)
16087  {
16088  if (pTV->onItemMouseLeave) {
16089  pTV->onItemMouseLeave(pTV->pHoveredItem);
16090  }
16091 
16092  pTV->pHoveredItem = NULL;
16093  pTV->isMouseOverArrow = false;
16094 
16095  // For now just redraw the entire control, but should optimize this to only redraw the regions of the new and old hovered items.
16096  drgui_dirty(pTVElement, drgui_get_local_rect(pTVElement));
16097  }
16098 }
16099 
16100 void drgui_tv_on_mouse_move(drgui_element* pTVElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
16101 {
16102  (void)stateFlags;
16103 
16104  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16105  if (pTV == NULL) {
16106  return;
16107  }
16108 
16109  pTV->isMouseOver = true;
16110  pTV->relativeMousePosX = relativeMousePosX;
16111  pTV->relativeMousePosY = relativeMousePosY;
16112 
16113  // If the mouse has entered into the dead space between the scrollbars, we just pretend the mouse has left the tree-view
16114  // control entirely by posting a manual on_mouse_leave event and returning straight away.
16115  if (drgui_rect_contains_point(drgui_tv_get_scrollbar_dead_space_rect(pTVElement), (float)relativeMousePosX, (float)relativeMousePosY)) {
16116  drgui_tv_on_mouse_leave(pTVElement);
16117  return;
16118  }
16119 
16120 
16121  drgui_tree_view_item_metrics newHoveredItemMetrics;
16122  drgui_tree_view_item* pNewHoveredItem = drgui_tv_find_item_under_point(pTVElement, (float)relativeMousePosX, (float)relativeMousePosY, &newHoveredItemMetrics);
16123  drgui_tree_view_item* pOldHoveredItem = pTV->pHoveredItem;
16124 
16125  bool wasMouseOverArrow = pTV->isMouseOverArrow;
16126  pTV->isMouseOverArrow = false;
16127 
16128  if (pNewHoveredItem != NULL)
16129  {
16130  if (pTV->onItemMouseMove)
16131  {
16132  float relativeMousePosXToItem = (float)relativeMousePosX - newHoveredItemMetrics.posX + drgui_sb_get_scroll_position(pTV->pScrollbarH);
16133  float relativeMousePosYToItem = (float)relativeMousePosY - newHoveredItemMetrics.posY;
16134 
16135  if (relativeMousePosXToItem >= 0 && relativeMousePosXToItem < newHoveredItemMetrics.width &&
16136  relativeMousePosYToItem >= 0 && relativeMousePosYToItem < newHoveredItemMetrics.height)
16137  {
16138  pTV->onItemMouseMove(pNewHoveredItem, (int)relativeMousePosXToItem, (int)relativeMousePosYToItem, &pTV->isMouseOverArrow);
16139  }
16140  }
16141  }
16142 
16143  if (pNewHoveredItem != pOldHoveredItem || wasMouseOverArrow != pTV->isMouseOverArrow)
16144  {
16145  if (pNewHoveredItem != pOldHoveredItem && pOldHoveredItem != NULL)
16146  {
16147  if (pTV->onItemMouseLeave) {
16148  pTV->onItemMouseLeave(pOldHoveredItem);
16149  }
16150  }
16151 
16152 
16153  pTV->pHoveredItem = pNewHoveredItem;
16154 
16155  // TODO: Optimize this so that only the rectangle region encompassing the two relevant items is marked as dirty.
16156  drgui_dirty(pTVElement, drgui_get_local_rect(pTVElement));
16157  }
16158 }
16159 
16160 void drgui_tv_on_mouse_button_down(drgui_element* pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
16161 {
16162  (void)relativeMousePosX;
16163  (void)relativeMousePosY;
16164  (void)stateFlags;
16165 
16166  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16167  if (pTV == NULL) {
16168  return;
16169  }
16170 
16171  if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT)
16172  {
16173  if (pTV->isMouseOverArrow)
16174  {
16175  if (drgui_tvi_is_expanded(pTV->pHoveredItem)) {
16176  drgui_tvi_collapse(pTV->pHoveredItem);
16177  } else {
16178  drgui_tvi_expand(pTV->pHoveredItem);
16179  }
16180  }
16181  else
16182  {
16183  if (pTV->isMultiSelectEnabled)
16184  {
16185  if (drgui_tvi_is_selected(pTV->pHoveredItem)) {
16186  drgui_tvi_deselect(pTV->pHoveredItem);
16187  } else {
16188  drgui_tvi_select(pTV->pHoveredItem);
16189  }
16190  }
16191  else
16192  {
16193  // TODO: Check if range selection is enabled and handle it here.
16194 
16195  drgui_tv_deselect_all_items(pTVElement);
16196  drgui_tvi_select(pTV->pHoveredItem);
16197  }
16198  }
16199  }
16200 }
16201 
16202 void drgui_tv_on_mouse_button_up(drgui_element* pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
16203 {
16204  (void)mouseButton;
16205  (void)relativeMousePosX;
16206  (void)relativeMousePosY;
16207  (void)stateFlags;
16208 
16209  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16210  if (pTV == NULL) {
16211  return;
16212  }
16213 
16214  // TODO: Implement me.
16215 }
16216 
16217 void drgui_tv_on_mouse_button_dblclick(drgui_element* pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
16218 {
16219  (void)relativeMousePosX;
16220  (void)relativeMousePosY;
16221  (void)stateFlags;
16222 
16223  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16224  if (pTV == NULL) {
16225  return;
16226  }
16227 
16228  if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT)
16229  {
16230  if (!pTV->isMouseOverArrow)
16231  {
16232  if (drgui_tvi_has_children(pTV->pHoveredItem))
16233  {
16234  // It is a parent item, so toggle it.
16235  if (drgui_tvi_is_expanded(pTV->pHoveredItem)) {
16236  drgui_tvi_collapse(pTV->pHoveredItem);
16237  } else {
16238  drgui_tvi_expand(pTV->pHoveredItem);
16239  }
16240  }
16241  else
16242  {
16243  // It is a leaf item, so pick it.
16244  if (pTV->onItemPicked) {
16245  pTV->onItemPicked(pTV->pHoveredItem);
16246  }
16247  }
16248  }
16249  }
16250 }
16251 
16252 void drgui_tv_on_mouse_wheel(drgui_element* pTVElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags)
16253 {
16254  (void)relativeMousePosX;
16255  (void)relativeMousePosY;
16256  (void)stateFlags;
16257 
16258  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16259  if (pTV == NULL) {
16260  return;
16261  }
16262 
16263  drgui_sb_scroll(pTV->pScrollbarV, -delta * drgui_sb_get_mouse_wheel_scale(pTV->pScrollbarV));
16264 }
16265 
16266 void drgui_tv_on_paint(drgui_element* pTVElement, drgui_rect relativeClippingRect, void* pPaintData)
16267 {
16268  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16269  if (pTV == NULL) {
16270  return;
16271  }
16272 
16273  // The dead space between the scrollbars should always be drawn with the default background color.
16274  drgui_draw_rect(pTVElement, drgui_tv_get_scrollbar_dead_space_rect(pTVElement), pTV->defaultBGColor, pPaintData);
16275 
16276  // The clipping rectangle needs to be clamped to the local rectangle that is shrunk such that it does not
16277  // include the scrollbars. If we don't do this we'll end up drawing underneath the scrollbars which will
16278  // cause flickering.
16279  drgui_rect innerClippingRect = drgui_clamp_rect(drgui_tv_get_inner_rect(pTVElement), relativeClippingRect);
16280  drgui_set_clip(pTVElement, innerClippingRect, pPaintData);
16281 
16282 
16283  // The main content of the tree-view is drawn in two parts. The first part (the top part) contains all of
16284  // the tree-view items. The second part (the bottom part) is just the background region that is not covered
16285  // by items.
16286 
16287  // We draw the tree-view items (the top part) first. This will retrieve the position of the bottom of the
16288  // items which is used to determine how much empty space is remaining below it so we can draw a quad over
16289  // that part.
16290  float itemsBottom = 0;
16291  drgui_tv_paint_items(pTVElement, innerClippingRect, pPaintData, &itemsBottom);
16292 
16293 
16294  // At this point the items have been drawn. All that remains is the part of the background that is not
16295  // covered by items. We can determine this by looking at <itemsBottom>.
16296  if (itemsBottom < relativeClippingRect.bottom && itemsBottom < drgui_get_relative_position_y(pTV->pScrollbarH))
16297  {
16298  drgui_draw_rect(pTVElement, drgui_make_rect(0, itemsBottom, drgui_get_relative_position_x(pTV->pScrollbarV), drgui_get_relative_position_y(pTV->pScrollbarH)), pTV->defaultBGColor, pPaintData);
16299  }
16300 }
16301 
16302 
16303 static void drgui_tv_refresh_and_redraw(drgui_element* pTVElement)
16304 {
16305  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16306  if (pTV == NULL) {
16307  return;
16308  }
16309 
16310 
16311  // Refresh scrollbar ranges and page sizes.
16312  drgui_tv_refresh_scrollbar_ranges(pTVElement);
16313 
16314  // For now, just redraw the entire control.
16315  drgui_dirty(pTVElement, drgui_get_local_rect(pTVElement));
16316 }
16317 
16318 static void drgui_tv_refresh_scrollbar_layouts(drgui_element* pTVElement)
16319 {
16320  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16321  if (pTV == NULL) {
16322  return;
16323  }
16324 
16325 
16326  // Vertical scrollbar.
16327  drgui_set_size(pTV->pScrollbarV, 16, drgui_get_height(pTVElement) - 16);
16328  drgui_set_relative_position(pTV->pScrollbarV, drgui_get_width(pTVElement) - drgui_get_width(pTV->pScrollbarV), 0);
16329 
16330  // Horizontal scrollbar.
16331  drgui_set_size(pTV->pScrollbarH, drgui_get_width(pTVElement) - 16, 16);
16332  drgui_set_relative_position(pTV->pScrollbarH, 0, drgui_get_height(pTVElement) - drgui_get_height(pTV->pScrollbarH));
16333 }
16334 
16335 static void drgui_tv_refresh_scrollbar_ranges(drgui_element* pTVElement)
16336 {
16337  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16338  if (pTV == NULL) {
16339  return;
16340  }
16341 
16342  float innerWidth = 0;
16343  unsigned int totalItemCount = 0;
16344  unsigned int pageItemCount = 0;
16345 
16346  drgui_tree_view_iterator i;
16347  if (drgui_tv_begin_at(pTV->pRootItem->pFirstChild, &i))
16348  {
16349  do
16350  {
16351  float itemRight = i.posX + i.width;
16352  if (itemRight > innerWidth) {
16353  innerWidth = itemRight;
16354  }
16355 
16356  float itemBottom = i.posY + i.height;
16357  if (itemBottom > 0 && itemBottom < drgui_get_relative_position_y(pTV->pScrollbarH)) {
16358  pageItemCount += 1;
16359  }
16360 
16361  totalItemCount += 1;
16362 
16363  } while (drgui_tv_next_visible(&i));
16364  }
16365 
16366  if (totalItemCount == 0)
16367  {
16368  // Vertical.
16369  drgui_sb_set_range(pTV->pScrollbarV, 0, 0);
16370  drgui_sb_set_page_size(pTV->pScrollbarV, 0);
16371 
16372  // Horizontal.
16373  drgui_sb_set_range(pTV->pScrollbarH, 0, 0);
16374  drgui_sb_set_page_size(pTV->pScrollbarH, 0);
16375  }
16376  else
16377  {
16378  // Vertical.
16379  drgui_sb_set_range(pTV->pScrollbarV, 0, (int)totalItemCount - 1); // - 1 because it's a 0-based range.
16380  drgui_sb_set_page_size(pTV->pScrollbarV, pageItemCount);
16381 
16382  // Horizontal.
16383  drgui_sb_set_range(pTV->pScrollbarH, 0, (int)innerWidth);
16384  drgui_sb_set_page_size(pTV->pScrollbarH, (int)drgui_get_relative_position_x(pTV->pScrollbarV));
16385  }
16386 }
16387 
16388 static drgui_rect drgui_tv_get_scrollbar_dead_space_rect(drgui_element* pTVElement)
16389 {
16390  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16391  if (pTV == NULL) {
16392  return drgui_make_rect(0, 0, 0, 0);
16393  }
16394 
16395  return drgui_make_rect(drgui_get_width(pTV->pScrollbarH), drgui_get_height(pTV->pScrollbarV), drgui_get_width(pTVElement), drgui_get_height(pTVElement));
16396 }
16397 
16398 static drgui_rect drgui_tv_get_inner_rect(drgui_element* pTVElement)
16399 {
16400  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16401  if (pTV == NULL) {
16402  return drgui_make_rect(0, 0, 0, 0);
16403  }
16404 
16405  drgui_rect result = drgui_get_local_rect(pTVElement);
16406  result.right -= drgui_get_width(pTV->pScrollbarV);
16407  result.bottom -= drgui_get_height(pTV->pScrollbarH);
16408 
16409  return result;
16410 }
16411 
16412 static void drgui_tv_paint_items(drgui_element* pTVElement, drgui_rect relativeClippingRect, void* pPaintData, float* pItemsBottomOut)
16413 {
16414  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16415  if (pTV == NULL) {
16416  return;
16417  }
16418 
16419  float itemsBottom = 0;
16420 
16421  // For now we will begin at the root item, but later we want to begin at the first visible item which will be based on the
16422  // scroll position.
16423  drgui_tree_view_iterator i;
16424  if (drgui_tv_begin_at(drgui_tv_find_first_visible_item_on_page(pTVElement), &i))
16425  {
16426  do
16427  {
16428  drgui_tv_paint_item(pTVElement, i.pItem, relativeClippingRect, i.posX, i.posY, i.width, i.height, pPaintData);
16429 
16430  // Restore the clipping rectangle in case the application changed the clipping rectangle.
16431  drgui_set_clip(pTVElement, relativeClippingRect, pPaintData);
16432 
16433  itemsBottom = i.posY + i.height;
16434 
16435  } while (itemsBottom < relativeClippingRect.bottom && drgui_tv_next_visible(&i));
16436  }
16437 
16438 
16439  if (pItemsBottomOut != NULL) {
16440  *pItemsBottomOut = itemsBottom;
16441  }
16442 }
16443 
16444 static bool drgui_tv_begin_at(drgui_tree_view_item* pFirst, drgui_tree_view_iterator* pIteratorOut)
16445 {
16446  if (pFirst == NULL || pIteratorOut == NULL) {
16447  return false;
16448  }
16449 
16450  if (!drgui_tv_measure_item(pFirst->pTVElement, pFirst, &pIteratorOut->width, &pIteratorOut->height)) {
16451  return false;
16452  }
16453 
16454  const int depth = drgui_tvi_get_depth(pFirst);
16455 
16456  pIteratorOut->pItem = pFirst;
16457  pIteratorOut->depth = depth;
16458  pIteratorOut->posX = depth * drgui_tv_get_child_offset_x(pFirst->pTVElement);
16459  pIteratorOut->posY = 0;
16460 
16461  return true;
16462 }
16463 
16464 static bool drgui_tv_next_visible(drgui_tree_view_iterator* pIterator)
16465 {
16466  assert(pIterator != NULL);
16467 
16468  if (pIterator->pItem == NULL) {
16469  return false;
16470  }
16471 
16472  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pIterator->pItem->pTVElement);
16473  if (pTV == NULL) {
16474  return false;
16475  }
16476 
16477  if (drgui_tvi_has_children(pIterator->pItem) && drgui_tvi_is_expanded(pIterator->pItem))
16478  {
16479  pIterator->pItem = pIterator->pItem->pFirstChild;
16480  pIterator->depth += 1;
16481  }
16482  else
16483  {
16484  pIterator->pItem = drgui_tvi_next_visible_non_child(pIterator->pItem, &pIterator->depth);
16485  }
16486 
16487 
16488  if (pIterator->pItem == NULL) {
16489  return false;
16490  }
16491 
16492  pIterator->posX = pIterator->depth * drgui_tv_get_child_offset_x(pIterator->pItem->pTVElement);
16493  pIterator->posY += pIterator->height;
16494 
16495  if (!drgui_tv_measure_item(pIterator->pItem->pTVElement, pIterator->pItem, &pIterator->width, &pIterator->height)) {
16496  return false;
16497  }
16498 
16499  return true;
16500 }
16501 
16502 static void drgui_tv_paint_item(drgui_element* pTVElement, drgui_tree_view_item* pItem, drgui_rect relativeClippingRect, float posX, float posY, float width, float height, void* pPaintData)
16503 {
16504  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16505  if (pTV == NULL) {
16506  return;
16507  }
16508 
16509  if (pTV->onItemPaint)
16510  {
16511  // We draw an item in two main parts, with the first part being the background section to the left and right of the item and the
16512  // second part being the item itself. The first part we do ourselves, whereas the second part we pass off to the host application.
16513 
16514  // The background section to the left and right of the main content is done first, by us.
16515  drgui_color bgcolor;
16516  if (drgui_tvi_is_selected(pItem)) {
16517  bgcolor = pTV->selectedBGColor;
16518  } else if (pTV->pHoveredItem == pItem) {
16519  bgcolor = pTV->hoveredBGColor;
16520  } else {
16521  bgcolor = pTV->defaultBGColor;
16522  }
16523 
16524  float innerOffsetX = (float)-drgui_sb_get_scroll_position(pTV->pScrollbarH);
16525 
16526  // Left.
16527  if (posX + innerOffsetX > 0) {
16528  drgui_draw_rect(pTVElement, drgui_make_rect(0, posY, posX + innerOffsetX, posY + height), bgcolor, pPaintData);
16529  }
16530 
16531  // Right.
16532  if (posX + width + innerOffsetX < drgui_get_relative_position_x(pTV->pScrollbarV)) {
16533  drgui_draw_rect(pTVElement, drgui_make_rect(posX + width + innerOffsetX, posY, drgui_get_relative_position_x(pTV->pScrollbarV), posY + height), bgcolor, pPaintData);
16534  }
16535 
16536 
16537  // At this point if were to finish drawing we'd have a hole where the main content of the item should be. To fill this we need to
16538  // let the host application do it.
16539  pTV->onItemPaint(pTVElement, pItem, relativeClippingRect, bgcolor, posX + innerOffsetX, posY, width, height, pPaintData);
16540  }
16541 }
16542 
16543 static drgui_tree_view_item* drgui_tv_find_item_under_point(drgui_element* pTVElement, float relativePosX, float relativePosY, drgui_tree_view_item_metrics* pMetricsOut)
16544 {
16545  (void)relativePosX; // <-- Unused because we treat items as though they are infinitely wide.
16546 
16547  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16548  if (pTV == NULL) {
16549  return NULL;
16550  }
16551 
16552  // For now we will begin at the root item, but later we want to begin at the first visible item which will be based on the
16553  // scroll position.
16554  drgui_tree_view_iterator i;
16555  if (drgui_tv_begin_at(drgui_tv_find_first_visible_item_on_page(pTVElement), &i))
16556  {
16557  do
16558  {
16559  if (relativePosY >= i.posY && relativePosY < i.posY + i.height)
16560  {
16561  if (pMetricsOut != NULL)
16562  {
16563  pMetricsOut->posX = i.posX;
16564  pMetricsOut->posY = i.posY;
16565  pMetricsOut->width = i.width;
16566  pMetricsOut->height = i.height;
16567  }
16568 
16569  return i.pItem;
16570  }
16571 
16572  } while ((i.posY + i.height < drgui_get_relative_position_y(pTV->pScrollbarH)) && drgui_tv_next_visible(&i));
16573  }
16574 
16575  return NULL;
16576 }
16577 
16578 static void drgui_tv_deselect_all_items_recursive(drgui_tree_view_item* pItem)
16579 {
16580  pItem->isSelected = false;
16581 
16582  for (drgui_tree_view_item* pChild = pItem->pFirstChild; pChild != NULL; pChild = pChild->pNextSibling)
16583  {
16584  drgui_tv_deselect_all_items_recursive(pChild);
16585  }
16586 }
16587 
16588 static void drgui_tv_on_mouse_enter_scrollbar(drgui_element* pSBElement)
16589 {
16590  drgui_tree_view_scrollbar_data* pSB = (drgui_tree_view_scrollbar_data*)drgui_sb_get_extra_data(pSBElement);
16591  if (pSB == NULL) {
16592  return;
16593  }
16594 
16595  // We just pretend the mouse has left the tree-view entirely. This will ensure any item marked as hovered is unmarked and redrawn.
16596  drgui_tv_on_mouse_leave(pSB->pTVElement);
16597 }
16598 
16599 static void drgui_tv_on_scroll_v(drgui_element* pSBElement, int scrollPos)
16600 {
16601  (void)scrollPos;
16602 
16603  drgui_tree_view_scrollbar_data* pSB = (drgui_tree_view_scrollbar_data*)drgui_sb_get_extra_data(pSBElement);
16604  if (pSB == NULL) {
16605  return;
16606  }
16607 
16608  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pSB->pTVElement);
16609  if (pTV == NULL) {
16610  return;
16611  }
16612 
16613  // As we scroll, the mouse will be placed over a different item. We just post a manual mouse_move event to trigger a refresh.
16614  if (pTV->isMouseOver) {
16615  drgui_tv_on_mouse_move(pSB->pTVElement, pTV->relativeMousePosX, pTV->relativeMousePosY, 0);
16616  }
16617 
16618  // The paint routine is tied directly to the scrollbars, so all we need to do is mark it as dirty to trigger a redraw.
16619  drgui_dirty(pSB->pTVElement, drgui_get_local_rect(pSB->pTVElement));
16620 }
16621 
16622 static void drgui_tv_on_scroll_h(drgui_element* pSBElement, int scrollPos)
16623 {
16624  (void)scrollPos;
16625 
16626  drgui_tree_view_scrollbar_data* pSB = (drgui_tree_view_scrollbar_data*)drgui_sb_get_extra_data(pSBElement);
16627  if (pSB == NULL) {
16628  return;
16629  }
16630 
16631  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pSB->pTVElement);
16632  if (pTV == NULL) {
16633  return;
16634  }
16635 
16636  // The paint routine is tied directly to the scrollbars, so all we need to do is mark it as dirty to trigger a redraw.
16637  drgui_dirty(pSB->pTVElement, drgui_get_local_rect(pSB->pTVElement));
16638 }
16639 
16640 static drgui_tree_view_item* drgui_tv_find_first_visible_item_on_page(drgui_element* pTVElement)
16641 {
16642  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
16643  if (pTV == NULL) {
16644  return NULL;
16645  }
16646 
16647  // We just keep iterating until we hit the index of the scroll position.
16648  int index = 0;
16649 
16650  drgui_tree_view_iterator i;
16651  if (drgui_tv_begin_at(pTV->pRootItem->pFirstChild, &i))
16652  {
16653  do
16654  {
16655  if (index == drgui_sb_get_scroll_position(pTV->pScrollbarV)) {
16656  return i.pItem;
16657  }
16658 
16659  index += 1;
16660 
16661  } while (drgui_tv_next_visible(&i));
16662  }
16663 
16664  return NULL;
16665 }
16666 
16667 
16668 
16670 //
16671 // Tree-View Item
16672 //
16674 
16676 static void drgui_tvi_detach(drgui_tree_view_item* pItem);
16677 
16678 drgui_tree_view_item* drgui_tv_create_item(drgui_element* pTVElement, drgui_tree_view_item* pParent, size_t extraDataSize, const void* pExtraData)
16679 {
16680  if (pTVElement == NULL) {
16681  return NULL;
16682  }
16683 
16684  if (pParent != NULL && pParent->pTVElement != pTVElement) {
16685  return NULL;
16686  }
16687 
16688 
16689  drgui_tree_view_item* pItem = (drgui_tree_view_item*)malloc(sizeof(*pItem) + extraDataSize);
16690  if (pItem == NULL) {
16691  return NULL;
16692  }
16693 
16694  pItem->pTVElement = pTVElement;
16695  pItem->pParent = NULL;
16696  pItem->pFirstChild = NULL;
16697  pItem->pLastChild = NULL;
16698  pItem->pNextSibling = NULL;
16699  pItem->pPrevSibling = NULL;
16700  pItem->isSelected = false;
16701  pItem->isExpanded = false;
16702 
16703  pItem->extraDataSize = extraDataSize;
16704  if (pExtraData != NULL) {
16705  memcpy(pItem->pExtraData, pExtraData, extraDataSize);
16706  }
16707 
16708 
16709  // Append the item to the end of the parent item.
16710  drgui_tvi_append(pItem, pParent);
16711 
16712  return pItem;
16713 }
16714 
16716 {
16717  if (pItem == NULL) {
16718  return;
16719  }
16720 
16721 
16722  // Children need to be deleted first.
16723  while (pItem->pFirstChild != NULL) {
16724  drgui_tvi_delete(pItem->pFirstChild);
16725  }
16726 
16727  // We need to grab a pointer to the main tree-view control so we can refresh and redraw it after we have detached the item.
16728  drgui_element* pTVElement = pItem->pTVElement;
16729 
16730  // The item needs to be completely detached first.
16731  drgui_tvi_detach(pItem);
16732 
16733  // Refresh the layout and redraw the tree-view control.
16734  drgui_tv_refresh_and_redraw(pTVElement);
16735 
16736  // Free the item last for safety.
16737  free(pItem);
16738 }
16739 
16741 {
16742  if (pItem == NULL) {
16743  return NULL;
16744  }
16745 
16746  return pItem->pTVElement;
16747 }
16748 
16750 {
16751  if (pItem == NULL) {
16752  return 0;
16753  }
16754 
16755  return pItem->extraDataSize;
16756 }
16757 
16759 {
16760  if (pItem == NULL) {
16761  return NULL;
16762  }
16763 
16764  return pItem->pExtraData;
16765 }
16766 
16767 
16769 {
16770  if (pItem == NULL) {
16771  return NULL;
16772  }
16773 
16774  return pItem->pParent;
16775 }
16776 
16778 {
16779  if (pItem == NULL) {
16780  return NULL;
16781  }
16782 
16783  return pItem->pFirstChild;
16784 }
16785 
16787 {
16788  if (pItem == NULL) {
16789  return NULL;
16790  }
16791 
16792  return pItem->pLastChild;
16793 }
16794 
16796 {
16797  if (pItem == NULL) {
16798  return NULL;
16799  }
16800 
16801  return pItem->pNextSibling;
16802 }
16803 
16805 {
16806  if (pItem == NULL) {
16807  return NULL;
16808  }
16809 
16810  return pItem->pPrevSibling;
16811 }
16812 
16814 {
16815  if (pItem == NULL) {
16816  return;
16817  }
16818 
16819  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pItem->pTVElement);
16820  if (pTV == NULL) {
16821  return;
16822  }
16823 
16824 
16825  // If a parent was not specified, append to the root item.
16826  if (pParent == NULL)
16827  {
16828  if (pTV->pRootItem != NULL) {
16829  drgui_tvi_append(pItem, pTV->pRootItem);
16830  }
16831  }
16832  else
16833  {
16834  assert(pItem->pTVElement == pParent->pTVElement);
16835 
16836  // Detach the child from it's current parent first.
16837  drgui_tvi_detach(pItem);
16838 
16839  pItem->pParent = pParent;
16840  assert(pItem->pParent != NULL);
16841 
16842  if (pItem->pParent->pLastChild != NULL) {
16843  pItem->pPrevSibling = pItem->pParent->pLastChild;
16844  pItem->pPrevSibling->pNextSibling = pItem;
16845  }
16846 
16847  if (pItem->pParent->pFirstChild == NULL) {
16848  pItem->pParent->pFirstChild = pItem;
16849  }
16850 
16851  pItem->pParent->pLastChild = pItem;
16852 
16853 
16854  // Refresh the layout and redraw the tree-view control.
16855  drgui_tv_refresh_and_redraw(pItem->pTVElement);
16856  }
16857 }
16858 
16860 {
16861  if (pItem == NULL) {
16862  return;
16863  }
16864 
16865  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pItem->pTVElement);
16866  if (pTV == NULL) {
16867  return;
16868  }
16869 
16870 
16871  // If a parent was not specified, prepend to the root item.
16872  if (pParent == NULL)
16873  {
16874  if (pTV->pRootItem != NULL) {
16875  drgui_tvi_prepend(pItem, pTV->pRootItem);
16876  }
16877  }
16878  else
16879  {
16880  assert(pItem->pTVElement == pParent->pTVElement);
16881 
16882  // Detach the child from it's current parent first.
16883  drgui_tvi_detach(pItem);
16884 
16885  pItem->pParent = pParent;
16886  assert(pItem->pParent != NULL);
16887 
16888  if (pItem->pParent->pFirstChild != NULL) {
16889  pItem->pNextSibling = pItem->pParent->pFirstChild;
16890  pItem->pNextSibling->pPrevSibling = pItem;
16891  }
16892 
16893  if (pItem->pParent->pLastChild == NULL) {
16894  pItem->pParent->pLastChild = pItem;
16895  }
16896 
16897  pItem->pParent->pFirstChild = pItem;
16898 
16899 
16900  // Refresh the layout and redraw the tree-view control.
16901  drgui_tv_refresh_and_redraw(pItem->pTVElement);
16902  }
16903 }
16904 
16905 void drgui_tvi_append_sibling(drgui_tree_view_item* pItemToAppend, drgui_tree_view_item* pItemToAppendTo)
16906 {
16907  if (pItemToAppend == NULL) {
16908  return;
16909  }
16910 
16911  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pItemToAppend->pTVElement);
16912  if (pTV == NULL) {
16913  return;
16914  }
16915 
16916 
16917  // If a parent was not specified, append to the root item.
16918  if (pItemToAppendTo == NULL)
16919  {
16920  if (pTV->pRootItem != NULL) {
16921  drgui_tvi_append(pItemToAppend, pTV->pRootItem);
16922  }
16923  }
16924  else
16925  {
16926  assert(pItemToAppend->pTVElement == pItemToAppendTo->pTVElement);
16927 
16928  // Detach the child from it's current parent first.
16929  drgui_tvi_detach(pItemToAppend);
16930 
16931 
16932  pItemToAppend->pParent = pItemToAppendTo->pParent;
16933  assert(pItemToAppend->pParent != NULL);
16934 
16935  pItemToAppend->pNextSibling = pItemToAppendTo->pNextSibling;
16936  pItemToAppend->pPrevSibling = pItemToAppendTo;
16937 
16938  pItemToAppendTo->pNextSibling->pPrevSibling = pItemToAppend;
16939  pItemToAppendTo->pNextSibling = pItemToAppend;
16940 
16941  if (pItemToAppend->pParent->pLastChild == pItemToAppendTo) {
16942  pItemToAppend->pParent->pLastChild = pItemToAppend;
16943  }
16944 
16945 
16946  // Refresh the layout and redraw the tree-view control.
16947  drgui_tv_refresh_and_redraw(pItemToAppend->pTVElement);
16948  }
16949 }
16950 
16951 void drgui_tvi_prepend_sibling(drgui_tree_view_item* pItemToPrepend, drgui_tree_view_item* pItemToPrependTo)
16952 {
16953  if (pItemToPrepend == NULL) {
16954  return;
16955  }
16956 
16957  drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pItemToPrepend->pTVElement);
16958  if (pTV == NULL) {
16959  return;
16960  }
16961 
16962 
16963  // If a parent was not specified, prepend to the root item.
16964  if (pItemToPrependTo == NULL)
16965  {
16966  if (pTV->pRootItem != NULL) {
16967  drgui_tvi_prepend(pItemToPrepend, pTV->pRootItem);
16968  }
16969  }
16970  else
16971  {
16972  assert(pItemToPrepend->pTVElement == pItemToPrependTo->pTVElement);
16973 
16974  // Detach the child from it's current parent first.
16975  drgui_tvi_detach(pItemToPrepend);
16976 
16977 
16978  pItemToPrepend->pParent = pItemToPrependTo->pParent;
16979  assert(pItemToPrepend->pParent != NULL);
16980 
16981  pItemToPrepend->pPrevSibling = pItemToPrependTo->pNextSibling;
16982  pItemToPrepend->pNextSibling = pItemToPrependTo;
16983 
16984  pItemToPrependTo->pPrevSibling->pNextSibling = pItemToPrepend;
16985  pItemToPrependTo->pNextSibling = pItemToPrepend;
16986 
16987  if (pItemToPrepend->pParent->pFirstChild == pItemToPrependTo) {
16988  pItemToPrepend->pParent->pFirstChild = pItemToPrepend;
16989  }
16990 
16991 
16992  // Refresh the layout and redraw the tree-view control.
16993  drgui_tv_refresh_and_redraw(pItemToPrepend->pTVElement);
16994  }
16995 }
16996 
16997 
16999 {
17000  if (pItem == NULL) {
17001  return false;
17002  }
17003 
17004  return pItem->pFirstChild != NULL;
17005 }
17006 
17008 {
17009  if (pItem->pParent == NULL || pItem->pParent == drgui_tv_get_root_item(pItem->pTVElement)) {
17010  return 0;
17011  }
17012 
17013  return drgui_tvi_get_depth(pItem->pParent) + 1;
17014 }
17015 
17017 {
17018  if (pItem == NULL) {
17019  return NULL;
17020  }
17021 
17022  if (pItem->pNextSibling != NULL) {
17023  return pItem->pNextSibling;
17024  }
17025 
17026 
17027  if (pDepthInOut != NULL) {
17028  *pDepthInOut -= 1;
17029  }
17030 
17031  return drgui_tvi_next_visible_non_child(pItem->pParent, pDepthInOut);
17032 }
17033 
17034 
17036 {
17037  if (pItem == NULL) {
17038  return;
17039  }
17040 
17041  if (!pItem->isSelected)
17042  {
17043  pItem->isSelected = true;
17044  drgui_dirty(pItem->pTVElement, drgui_get_local_rect(pItem->pTVElement));
17045  }
17046 }
17047 
17049 {
17050  if (pItem == NULL) {
17051  return;
17052  }
17053 
17054  if (pItem->isSelected)
17055  {
17056  pItem->isSelected = false;
17057  drgui_dirty(pItem->pTVElement, drgui_get_local_rect(pItem->pTVElement));
17058  }
17059 }
17060 
17062 {
17063  if (pItem == NULL) {
17064  return false;
17065  }
17066 
17067  return pItem->isSelected;
17068 }
17069 
17071 {
17072  if (pItem == NULL) {
17073  return;
17074  }
17075 
17076  if (!pItem->isExpanded)
17077  {
17078  pItem->isExpanded = true;
17079  drgui_tv_refresh_and_redraw(pItem->pTVElement);
17080  }
17081 }
17082 
17084 {
17085  if (pItem == NULL) {
17086  return;
17087  }
17088 
17089  if (pItem->isExpanded)
17090  {
17091  pItem->isExpanded = false;
17092  drgui_tv_refresh_and_redraw(pItem->pTVElement);
17093  }
17094 }
17095 
17097 {
17098  if (pItem == NULL) {
17099  return false;
17100  }
17101 
17102  return pItem->isExpanded;
17103 }
17104 
17105 
17106 
17107 static void drgui_tvi_detach(drgui_tree_view_item* pItem)
17108 {
17109  assert(pItem != NULL);
17110 
17111  if (pItem->pParent != NULL)
17112  {
17113  if (pItem->pParent->pFirstChild == pItem) {
17114  pItem->pParent->pFirstChild = pItem->pNextSibling;
17115  }
17116 
17117  if (pItem->pParent->pLastChild == pItem) {
17118  pItem->pParent->pLastChild = pItem->pPrevSibling;
17119  }
17120 
17121 
17122  if (pItem->pPrevSibling != NULL) {
17123  pItem->pPrevSibling->pNextSibling = pItem->pNextSibling;
17124  }
17125 
17126  if (pItem->pNextSibling != NULL) {
17127  pItem->pNextSibling->pPrevSibling = pItem->pPrevSibling;
17128  }
17129  }
17130 
17131  pItem->pParent = NULL;
17132  pItem->pPrevSibling = NULL;
17133  pItem->pNextSibling = NULL;
17134 }
17135 #endif //DR_GUI_IMPLEMENTATION
17136 
17137 
17138 #ifdef DR_GUI_INCLUDE_WIP
17139 // #include WIP files here.
17140 #endif //DR_GUI_INCLUDE_WIP
17141 
17142 /*
17143 This is free and unencumbered software released into the public domain.
17144 
17145 Anyone is free to copy, modify, publish, use, compile, sell, or
17146 distribute this software, either in source code form or as a compiled
17147 binary, for any purpose, commercial or non-commercial, and by any
17148 means.
17149 
17150 In jurisdictions that recognize copyright laws, the author or authors
17151 of this software dedicate any and all copyright interest in the
17152 software to the public domain. We make this dedication for the benefit
17153 of the public at large and to the detriment of our heirs and
17154 successors. We intend this dedication to be an overt act of
17155 relinquishment in perpetuity of all present and future rights to this
17156 software under copyright law.
17157 
17158 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17159 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17160 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17161 IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
17162 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
17163 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
17164 OTHER DEALINGS IN THE SOFTWARE.
17165 
17166 For more information, please refer to <http://unlicense.org/>
17167 */
drgui_text_engine_set_cursor_color
void drgui_text_engine_set_cursor_color(drgui_text_engine *pTL, drgui_color cursorColor)
Sets the color of the text cursor.
drgui_textbox_get_selection_background_color
drgui_color drgui_textbox_get_selection_background_color(drgui_element *pTBElement)
Retrieves the background color of selected text.
drgui_context::pElementWithKeyboardCapture
drgui_element * pElementWithKeyboardCapture
A pointer to the element with the keyboard focus.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:795
dr2d_font_metrics::ascent
int ascent
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:100
drgui_sb_on_mouse_wheel
void drgui_sb_on_mouse_wheel(drgui_element *pSBElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Called when the mouse wheel event needs to be processed for the given scrollbar.
drgui_draw_image_args::boundsColor
drgui_color boundsColor
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:347
drgui_context::lastMouseMovePosX
float lastMouseMovePosX
The position of the mouse that was passed in from the last inbound mouse move event.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:840
drgui_sb_is_thumb_auto_hide_enabled
bool drgui_sb_is_thumb_auto_hide_enabled(drgui_element *pSBElement)
Determines whether or not thumb auto-hiding is enabled.
drgui_tabbar_on_paint
void drgui_tabbar_on_paint(drgui_element *pTBElement, drgui_rect relativeClippingRect, void *pPaintData)
Called when the paint event needs to be processed for the given tab control.
drgui_tabbar_get_text_color
drgui_color drgui_tabbar_get_text_color(drgui_element *pTBElement)
drgui_text_engine_set_default_text_color
void drgui_text_engine_set_default_text_color(drgui_text_engine *pTL, drgui_color color)
Sets the default text color of the given text engine.
DRGUI_F9
#define DRGUI_F9
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:451
drgui_sb_set_track_color
void drgui_sb_set_track_color(drgui_element *pSBElement, drgui_color color)
Sets the color of the track.
drgui_draw_begin_proc
void(* drgui_draw_begin_proc)(void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:381
drgui_text_engine_get_selection_anchor_line
size_t drgui_text_engine_get_selection_anchor_line(drgui_text_engine *pTL)
Retrieves the line the selection anchor is sitting on.
drgui_text_engine_get_redo_points_remaining_count
unsigned int drgui_text_engine_get_redo_points_remaining_count(drgui_text_engine *pTL)
Retrieves the number of redo points remaining in the stack.
drgui_textbox_set_text
void drgui_textbox_set_text(drgui_element *pTBElement, const char *text)
Sets the text of the given text box.
drgui_text_engine_set_inner_offset_x
void drgui_text_engine_set_inner_offset_x(drgui_text_engine *pTL, float innerOffsetX)
Sets the inner offset of the given text engine on the x axis.
drgui_text_engine_get_tab_size
unsigned int drgui_text_engine_get_tab_size(drgui_text_engine *pTL)
Retrieves the size of a tab in spaces.
drgui_append_sibling
void drgui_append_sibling(drgui_element *pElementToAppend, drgui_element *pElementToAppendTo)
Appends the given element to the given sibling.
drgui_text_engine_insert_character_at_cursor
bool drgui_text_engine_insert_character_at_cursor(drgui_text_engine *pTL, unsigned int character)
dr2d_font_slant
dr2d_font_slant
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:135
drgui_end_dirty
void drgui_end_dirty(drgui_element *pElement)
Ends accumulating a dirty rectangle, and requests a redraw from the backend if the counter reaches ze...
dr2d_draw_rect_outline
void dr2d_draw_rect_outline(dr2d_surface *pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth)
Draws the outline of the given rectangle.
drgui_text_engine_move_cursor_to_start_of_text
bool drgui_text_engine_move_cursor_to_start_of_text(drgui_text_engine *pTL)
Moves the cursor of the given text engine to the end of the text.
dr2d_image_format
dr2d_image_format
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:143
drgui_context::onGlobalReleaseMouse
drgui_on_release_mouse_proc onGlobalReleaseMouse
The global event handler to call when an element releases the mouse.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:816
drgui_tv_set_on_item_paint
void drgui_tv_set_on_item_paint(drgui_element *pTVElement, drgui_tvi_on_paint_proc proc)
Sets the function to call when a tree-view item needs to be drawn.
drgui_make_point_relative
void drgui_make_point_relative(const drgui_element *pElement, float *positionX, float *positionY)
Converts the given point from absolute to relative to the given element.
drgui_tv_get_default_background_color
drgui_color drgui_tv_get_default_background_color(drgui_element *pTVElement)
Retrieves the default background color.
drgui_text_run::backgroundColor
drgui_color backgroundColor
The backgorund color of the text.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5283
drgui_sb_orientation_horizontal
@ drgui_sb_orientation_horizontal
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:10140
drgui_element::onMouseEnter
drgui_on_mouse_enter_proc onMouseEnter
The function to call when the mouse enters the given element.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:708
drgui_draw_rect_with_outline
void drgui_draw_rect_with_outline(drgui_element *pElement, drgui_rect relativeRect, drgui_color color, float outlineWidth, drgui_color outlineColor, void *pPaintData)
Draws a filled rectangle with an outline on the given element.
drgui_tv_create_item
drgui_tree_view_item * drgui_tv_create_item(drgui_element *pTVElement, drgui_tree_view_item *pParent, size_t extraDataSize, const void *pExtraData)
drgui_text_engine_get_text
size_t drgui_text_engine_get_text(drgui_text_engine *pTL, char *textOut, size_t textOutSize)
dr2d_draw_text
void dr2d_draw_text(dr2d_surface *pSurface, dr2d_font *pFont, const char *text, size_t textSizeInBytes, float posX, float posY, dr2d_color color, dr2d_color backgroundColor)
Draws a run of text.
drgui_tv_get_next_selected_item
drgui_tree_view_item * drgui_tv_get_next_selected_item(drgui_element *pTVElement, drgui_tree_view_item *pItem)
drgui_tabbar_enable_auto_size
void drgui_tabbar_enable_auto_size(drgui_element *pTBElement)
drgui_textbox_set_line_numbers_color
void drgui_textbox_set_line_numbers_color(drgui_element *pTBElement, drgui_color color)
drgui_text_engine_on_undo_point_changed_proc
void(* drgui_text_engine_on_undo_point_changed_proc)(drgui_text_engine *pTL, unsigned int iUndoPoint)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5318
drgui_text_engine_alignment
drgui_text_engine_alignment
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5258
drgui_get_text_cursor_position_from_char
bool drgui_get_text_cursor_position_from_char(drgui_font *pFont, const char *text, size_t characterIndex, float *pTextCursorPosXOut)
Retrieves the position to palce a text cursor based on the character at the given index for the given...
drgui_context::onGlobalDirty
drgui_on_dirty_proc onGlobalDirty
The global event callback to call when an element is marked as dirty.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:810
drgui_textbox_get_vertical_scrollbar
drgui_element * drgui_textbox_get_vertical_scrollbar(drgui_element *pTBElement)
drgui_set_on_key_down
void drgui_set_on_key_down(drgui_element *pElement, drgui_on_key_down_proc callback)
Registers the on_key_down event callback.
drgui_tab_is_in_view
bool drgui_tab_is_in_view(drgui_tab *pTab)
Determines whether or not the given tab is in view.
drgui_capture_mouse
void drgui_capture_mouse(drgui_element *pElement)
drgui_tv_set_hovered_background_color
void drgui_tv_set_hovered_background_color(drgui_element *pTVElement, drgui_color color)
Sets the default background color of hovered items.
drgui_text_engine_get_cursor_width
float drgui_text_engine_get_cursor_width(drgui_text_engine *pTL)
Retrieves the width of the text cursor.
drgui_text_engine_set_inner_offset
void drgui_text_engine_set_inner_offset(drgui_text_engine *pTL, float innerOffsetX, float innerOffsetY)
Sets the inner offset of the given text engine.
drgui_tab_get_extra_data
void * drgui_tab_get_extra_data(drgui_tab *pTab)
Retrieves a pointer to the extra data associated with the given tree-view item.
drgui_cursor_size_nwse
@ drgui_cursor_size_nwse
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:206
drgui_text_engine_alignment_center
@ drgui_text_engine_alignment_center
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5262
drgui_disable_auto_dirty
void drgui_disable_auto_dirty(drgui_context *pContext)
Disable's automatic dirtying of elements.
drgui_tvi_get_tree_view_element
drgui_element * drgui_tvi_get_tree_view_element(drgui_tree_view_item *pItem)
Retrieves the tree-view GUI element that owns the given item.
run
void run(class_loader::ClassLoader *loader)
drgui_element::width
float width
The width of the element.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:685
drgui_text_engine_move_cursor_to_start_of_line_by_index
bool drgui_text_engine_move_cursor_to_start_of_line_by_index(drgui_text_engine *pTL, size_t iLine)
Moves the cursor of the given text engine to the start of the line at the given index.
drgui_key_parse
static drgui_key drgui_key_parse(const char *str)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:515
drgui_text_engine_get_default_text_color
drgui_color drgui_text_engine_get_default_text_color(drgui_text_engine *pTL)
Retrieves the default text color of the given text engine.
drgui_byte
unsigned char drgui_byte
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:190
drgui_tv_get_root_item
drgui_tree_view_item * drgui_tv_get_root_item(drgui_element *pTVElement)
Retrieves a pointer to the root element of the given tree view control.
drgui_tvi_append_sibling
void drgui_tvi_append_sibling(drgui_tree_view_item *pItemToAppend, drgui_tree_view_item *pItemToAppendTo)
Appends the given tree view item to the given sibling.
drgui_enable_auto_dirty
void drgui_enable_auto_dirty(drgui_context *pContext)
Enable's automatic dirtying of elements.
drgui_measure_string_proc
bool(* drgui_measure_string_proc)(drgui_resource font, const char *text, size_t textSizeInBytes, float *pWidthOut, float *pHeightOut)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:400
drgui_painting_callbacks::drawRoundRectOutline
drgui_draw_round_rect_outline_proc drawRoundRectOutline
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:584
drgui_tabbar_set_tab_padding
void drgui_tabbar_set_tab_padding(drgui_element *pTBElement, float padding)
drgui_delete_font_proc
void(* drgui_delete_font_proc)(drgui_resource font)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:396
drgui_textbox_show_line_numbers
void drgui_textbox_show_line_numbers(drgui_element *pTBElement)
Shows the line numbers.
drgui_tv_on_size
void drgui_tv_on_size(drgui_element *pTVElement, float newWidth, float newHeight)
Called when the size event needs to be processed for the given tree-view control.
drgui_draw_image_args::dstY
float dstY
The destination position on the y axis. This is ignored if the DR2D_IMAGE_ALIGN_CENTER option is set.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:304
drgui_text_engine_alignment_right
@ drgui_text_engine_alignment_right
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5263
drgui_font::family
char family[DRGUI_MAX_FONT_FAMILY_LENGTH]
The font family.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:621
NULL
#define NULL
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/speex_resampler/thirdparty/resample.c:92
drgui_text_engine_get_vertical_align
drgui_text_engine_alignment drgui_text_engine_get_vertical_align(drgui_text_engine *pTL)
Retrieves the vertical aligment of the given text engine.
DRGUI_PAGE_UP
#define DRGUI_PAGE_UP
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:434
drgui_textbox_deselect_all
void drgui_textbox_deselect_all(drgui_element *pTBElement)
Deselect everything.
drgui_text_engine_deselect_all
void drgui_text_engine_deselect_all(drgui_text_engine *pTL)
Deselects everything in the given text engine.
drgui_tvi_collapse
void drgui_tvi_collapse(drgui_tree_view_item *pItem)
Collapses the given item.
drgui_text_engine_insert_character
bool drgui_text_engine_insert_character(drgui_text_engine *pTL, unsigned int character, size_t insertIndex)
drgui_text_engine_move_cursor_up
bool drgui_text_engine_move_cursor_up(drgui_text_engine *pTL)
Moves the cursor of the given text engine up one line.
drgui_font::internalFont
drgui_resource internalFont
The internal font. This is created by the rendering backend.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:640
drgui_get_clip
void drgui_get_clip(drgui_element *pElement, drgui_rect *pRelativeRect, void *pPaintData)
Retrieves the current clipping rectangle.
drgui_textbox_set_selection_background_color
void drgui_textbox_set_selection_background_color(drgui_element *pTBElement, drgui_color color)
Sets the background color of selected text.
drgui_tv_set_on_item_mouse_leave
void drgui_tv_set_on_item_mouse_leave(drgui_element *pTVElement, drgui_tvi_on_mouse_leave_proc proc)
Sets the function call when the mouse leaves a tree-view item.
drgui_text_engine_is_showing_cursor
bool drgui_text_engine_is_showing_cursor(drgui_text_engine *pTL)
Determines whether or not the cursor is visible.
drgui_text_engine_get_cursor_color
drgui_color drgui_text_engine_get_cursor_color(drgui_text_engine *pTL)
Retrieves the color of the text cursor.
drgui_get_text_cursor_position_from_point_proc
bool(* drgui_get_text_cursor_position_from_point_proc)(drgui_resource font, const char *text, size_t textSizeInBytes, float maxWidth, float inputPosX, float *pTextCursorPosXOut, size_t *pCharacterIndexOut)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:401
drgui_context::pElementWithMouseCapture
drgui_element * pElementWithMouseCapture
A pointer to the element with the mouse capture.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:792
drgui_tv_set_default_background_color
void drgui_tv_set_default_background_color(drgui_element *pTVElement, drgui_color color)
Sets the default background color.
dr2d_image
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:267
DRGUI_IMAGE_DRAW_BOUNDS
#define DRGUI_IMAGE_DRAW_BOUNDS
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:289
drgui_textbox_disable_vertical_scrollbar
void drgui_textbox_disable_vertical_scrollbar(drgui_element *pTBElement)
Disables the vertical scrollbar.
drgui_sb_enable_thumb_auto_hide
void drgui_sb_enable_thumb_auto_hide(drgui_element *pSBElement)
Enables auto-hiding of the thumb.
drgui_set_on_capture_mouse
void drgui_set_on_capture_mouse(drgui_element *pElement, drgui_on_capture_mouse_proc callback)
Registers the on_capture_mouse event callback.
drgui_tv_disable_multi_select
void drgui_tv_disable_multi_select(drgui_element *pTVElement)
Disables multi-select.
DRGUI_BACKSPACE
#define DRGUI_BACKSPACE
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:431
DRGUI_MOUSE_BUTTON_LEFT_DOWN
#define DRGUI_MOUSE_BUTTON_LEFT_DOWN
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:415
dr2d_image_format_bgra8
@ dr2d_image_format_bgra8
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:146
drgui_glyph_metrics::advanceX
int advanceX
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:263
drgui_text_run::iChar
size_t iChar
Index in the main text string of the first character of the run.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5306
drgui_context::lastMouseMovePosY
float lastMouseMovePosY
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:841
drgui_image::pContext
drgui_context * pContext
A pointer to the context that owns this image.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:609
drgui_element::onSize
drgui_on_size_proc onSize
The function to call when the element's size changes.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:705
drgui_text_run::textLength
size_t textLength
The length of the string, in bytes.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5273
dr2d_draw_round_rect_outline
void dr2d_draw_round_rect_outline(dr2d_surface *pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth)
Draws the outline of the given rectangle with rounded corners.
drgui_register_dr_2d_callbacks
void drgui_register_dr_2d_callbacks(drgui_context *pContext, dr2d_context *pDrawingContext)
drgui_painting_callbacks::drawRoundRectWithOutline
drgui_draw_round_rect_with_outline_proc drawRoundRectWithOutline
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:585
drgui_tab_set_text
void drgui_tab_set_text(drgui_tab *pTab, const char *text)
Sets the text of the given tab bar item.
drgui_text_engine_alignment_top
@ drgui_text_engine_alignment_top
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5261
drgui_sb_is_thumb_visible
bool drgui_sb_is_thumb_visible(drgui_element *pSBElement)
DRGUI_IMAGE_CLIP_BOUNDS
#define DRGUI_IMAGE_CLIP_BOUNDS
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:290
drgui_text_engine_is_anything_selected
bool drgui_text_engine_is_anything_selected(drgui_text_engine *pTL)
Determines whether or not anything is selected in the given text engine.
drgui_text_engine_set_text
void drgui_text_engine_set_text(drgui_text_engine *pTL, const char *text)
Sets the given text engine's text.
drgui_tabbar_on_measure_tab_proc
void(* drgui_tabbar_on_measure_tab_proc)(drgui_element *pTBElement, drgui_tab *pTab, float *pWidthOut, float *pHeightOut)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:11143
drgui_set_on_paint
void drgui_set_on_paint(drgui_element *pElement, drgui_on_paint_proc callback)
Registers the on_paint event callback.
drgui_rect_union
drgui_rect drgui_rect_union(drgui_rect rect0, drgui_rect rect1)
Creates a rectangle that contains both of the given rectangles.
drgui_get_local_rect
drgui_rect drgui_get_local_rect(const drgui_element *pElement)
drgui_tabbar_set_close_button_left_padding
void drgui_tabbar_set_close_button_left_padding(drgui_element *pTBElement, float padding)
drgui_tabbar_hide_close_buttons
void drgui_tabbar_hide_close_buttons(drgui_element *pTBElement)
Hides the close buttons on each tab.
drgui_tv_on_mouse_button_dblclick
void drgui_tv_on_mouse_button_dblclick(drgui_element *pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Called when the mouse button double-click event needs to be processed for the given tree-view control...
dr2d_font_weight
dr2d_font_weight
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:116
drgui_textbox_on_capture_mouse
void drgui_textbox_on_capture_mouse(drgui_element *pTBElement)
on_capture_mouse
_stricmp
DR_INLINE int _stricmp(const char *string1, const char *string2)
Definition: porcupine/demo/c/dr_libs/old/dr.h:178
drgui_text_engine_delete_character_to_right_of_cursor
bool drgui_text_engine_delete_character_to_right_of_cursor(drgui_text_engine *pTL)
drgui_set_on_printable_key_down
void drgui_set_on_printable_key_down(drgui_element *pElement, drgui_on_printable_key_down_proc callback)
Registers the on_printable_key_down event callback.
drgui_measure_string
bool drgui_measure_string(drgui_font *pFont, const char *text, size_t textLengthInBytes, float *pWidthOut, float *pHeightOut)
dr2d_surface
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:316
drgui_tvi_get_depth
int drgui_tvi_get_depth(drgui_tree_view_item *pItem)
drgui_tabbar_activate_tab
void drgui_tabbar_activate_tab(drgui_element *pTBElement, drgui_tab *pTab)
Activates the given tab.
drgui_textbox_on_mouse_button_dblclick
void drgui_textbox_on_mouse_button_dblclick(drgui_element *pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
on_mouse_button_dblclick.
dr2d_glyph_metrics::advanceX
int advanceX
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:112
drgui_text_engine_get_cursor_position
void drgui_text_engine_get_cursor_position(drgui_text_engine *pTL, float *pPosXOut, float *pPosYOut)
Retrieves the position of the cursor, relative to the container.
dr2d_unmap_image_data
void dr2d_unmap_image_data(dr2d_image *pImage)
drgui_tvi_next_visible_non_child
drgui_tree_view_item * drgui_tvi_next_visible_non_child(drgui_tree_view_item *pItem, int *pDepthInOut)
drgui_textbox_clear_undo_stack
void drgui_textbox_clear_undo_stack(drgui_element *pTBElement)
Clears the undo/redo stack.
drgui_tabbar_on_mouse_button_down
void drgui_tabbar_on_mouse_button_down(drgui_element *pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Called when the mouse button down event needs to be processed for the given tab bar control.
drgui_get_absolute_position_y
float drgui_get_absolute_position_y(const drgui_element *pElement)
drgui_text_engine_move_cursor_to_end_of_line_by_index
bool drgui_text_engine_move_cursor_to_end_of_line_by_index(drgui_text_engine *pTL, size_t iLine)
Moves the cursor of the given text engine to the end of the line at the given index.
dr2d_draw_rect
void dr2d_draw_rect(dr2d_surface *pSurface, float left, float top, float right, float bottom, dr2d_color color)
Draws a filled rectangle without an outline.
drgui_tabbar_show_close_buttons
void drgui_tabbar_show_close_buttons(drgui_element *pTBElement)
Shows the close buttons on each tab.
drgui_tvi_on_mouse_move_proc
void(* drgui_tvi_on_mouse_move_proc)(drgui_tree_view_item *pItem, int relativeMousePosX, int relativeMousePosY, bool *pIsOverArrow)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:15230
drgui_element::flags
unsigned int flags
Boolean flags.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:695
drgui_textbox_set_line_numbers_background_color
void drgui_textbox_set_line_numbers_background_color(drgui_element *pTBElement, drgui_color color)
drgui_tabbar_set_on_tab_deactivated
void drgui_tabbar_set_on_tab_deactivated(drgui_element *pTBElement, drgui_tabbar_on_tab_deactivated_proc proc)
Sets the function to call when a tab is deactivated.
drgui_set_on_release_mouse
void drgui_set_on_release_mouse(drgui_element *pElement, drgui_on_release_mouse_proc callback)
Registers the on_release_mouse event callback.
drgui_textbox_set_line_numbers_padding
void drgui_textbox_set_line_numbers_padding(drgui_element *pTBElement, float lineNumbersPadding)
drgui_delete_tree_view
void drgui_delete_tree_view(drgui_element *pTVElement)
Deletes the given tree-view control.
drgui_text_engine_set_inner_offset_y
void drgui_text_engine_set_inner_offset_y(drgui_text_engine *pTL, float innerOffsetY)
Sets the inner offset of the given text engine on the y axis.
drgui_delete_tab_bar
void drgui_delete_tab_bar(drgui_element *pTBElement)
Deletes the given tab bar control.
drgui_element::pParent
drgui_element * pParent
A pointer to the parent element. This can be null in which case this element is the parent.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:651
drgui_painting_callbacks::getImageSize
drgui_get_image_size_proc getImageSize
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:601
drgui_text_engine_set_default_font
void drgui_text_engine_set_default_font(drgui_text_engine *pTL, drgui_font *pFont)
Sets the default font to use for text runs.
drgui_textbox_set_line_numbers_width
void drgui_textbox_set_line_numbers_width(drgui_element *pTBElement, float lineNumbersWidth)
drgui_post_inbound_event_mouse_move
void drgui_post_inbound_event_mouse_move(drgui_element *pTopLevelElement, int mousePosX, int mousePosY, int stateFlags)
Posts a mouse move inbound event.
drgui_font_weight_semi_light
@ drgui_font_weight_semi_light
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:216
drgui_draw_image_args::foregroundTint
drgui_color foregroundTint
The foreground tint color. This is not applied to the background color, and the alpha component is ig...
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:340
drgui_sb_on_size
void drgui_sb_on_size(drgui_element *pSBElement, float newWidth, float newHeight)
Called when the size event needs to be processed for the given scrollbar.
drgui_textbox_get_cursor_line
size_t drgui_textbox_get_cursor_line(drgui_element *pTBElement)
Retrieves the index of the line the cursor is current sitting on.
drgui_create_image_proc
drgui_resource(* drgui_create_image_proc)(void *pPaintingContext, unsigned int width, unsigned int height, drgui_image_format format, unsigned int stride, const void *pImageData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:404
drgui_release_keyboard_no_global_notify
void drgui_release_keyboard_no_global_notify(drgui_context *pContext)
Releases the keyboard capture without posting the global-scoped event. Should only be used in very sp...
drgui_tabbar_get_last_tab
drgui_tab * drgui_tabbar_get_last_tab(drgui_element *pTBElement)
drgui_tabbar_create_and_prepend_tab
drgui_tab * drgui_tabbar_create_and_prepend_tab(drgui_element *pTBElement, const char *text, size_t extraDataSize, const void *pExtraData)
Creates and prepends a tab.
drgui_set_on_mouse_leave
void drgui_set_on_mouse_leave(drgui_element *pElement, drgui_on_mouse_leave_proc callback)
Registers the on_mouse_leave event callback.
drgui_get_text_cursor_position_from_point
bool drgui_get_text_cursor_position_from_point(drgui_font *pFont, const char *text, size_t textSizeInBytes, float maxWidth, float inputPosX, float *pTextCursorPosXOut, size_t *pCharacterIndexOut)
Retrieves the position to place a text cursor based on the given point for the given string when draw...
dr2d_rgba
dr2d_color dr2d_rgba(dr2d_byte r, dr2d_byte g, dr2d_byte b, dr2d_byte a)
Creates a color object from a set of RGBA color components.
drgui_font_metrics::lineHeight
int lineHeight
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:252
drgui_textbox_get_line_numbers_color
drgui_color drgui_textbox_get_line_numbers_color(drgui_element *pTBElement)
drgui_draw_image_args::dstBoundsY
float dstBoundsY
The position of the destination's bounds on the y axis.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:330
drgui_textbox_set_scrollbar_size
void drgui_textbox_set_scrollbar_size(drgui_element *pTBElement, float size)
drgui_draw_image_args::dstBoundsX
float dstBoundsX
The position of the destination's bounds on the x axis.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:327
DRGUI_F5
#define DRGUI_F5
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:447
drgui_font_weight_book
@ drgui_font_weight_book
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:217
drgui_pass_through_hit_test
bool drgui_pass_through_hit_test(drgui_element *pElement, float mousePosX, float mousePosY)
An on_hit_test event callback that can be used to always fail the mouse hit test.
DRGUI_HOME
#define DRGUI_HOME
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:437
drgui_tv_on_mouse_wheel
void drgui_tv_on_mouse_wheel(drgui_element *pTVElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Called when the mouse wheel event needs to be processed for the given tree-view control.
drgui_tabbar_set_text_color
void drgui_tabbar_set_text_color(drgui_element *pTBElement, drgui_color color)
drgui_context::onGlobalCaptureKeyboard
drgui_on_capture_keyboard_proc onGlobalCaptureKeyboard
The global event handler to call when an element captures the keyboard.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:819
drgui_tab_move_to_front
void drgui_tab_move_to_front(drgui_tab *pTab)
Moves the given tab to the front of the tab bar that owns it.
drgui_is_visible_recursive
bool drgui_is_visible_recursive(const drgui_element *pElement)
Recursively determines whether or not the element is marked as visible.
drgui_color::g
drgui_byte g
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:272
drgui_text_run::text
const char * text
A pointer to the start of the string. This is NOT null terminated.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5270
drgui_glyph_metrics::originX
int originX
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:261
drgui_textbox_select_all
void drgui_textbox_select_all(drgui_element *pTBElement)
Selects all of the text inside the text box.
drgui_draw_image_args::dstWidth
float dstWidth
The destination width.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:307
drgui_iterate_visible_elements
bool drgui_iterate_visible_elements(drgui_element *pParentElement, drgui_rect relativeRect, drgui_visible_iteration_proc callback, void *pUserData)
drgui_font_weight_semi_bold
@ drgui_font_weight_semi_bold
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:218
drgui_post_inbound_event_mouse_button_dblclick
void drgui_post_inbound_event_mouse_button_dblclick(drgui_element *pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags)
Posts a mouse button double-clicked inbound event.
drgui_text_engine_get_cursor_character
size_t drgui_text_engine_get_cursor_character(drgui_text_engine *pTL)
Retrieves the index of the character the cursor is currently sitting on.
drgui_tabbar_get_tab_padding
float drgui_tabbar_get_tab_padding(drgui_element *pTBElement)
drgui_get_absolute_position
void drgui_get_absolute_position(const drgui_element *pElement, float *positionXOut, float *positionYOut)
Retrieves the absolute position of the given element.
drgui_textbox_on_key_down
void drgui_textbox_on_key_down(drgui_element *pTBElement, drgui_key key, int stateFlags)
on_key_down.
drgui_text_engine_is_cursor_at_start_of_selection
bool drgui_text_engine_is_cursor_at_start_of_selection(drgui_text_engine *pTL)
Determines whether or not the cursor is sitting at the start of the selection.
drgui_release_keyboard
void drgui_release_keyboard(drgui_context *pContext)
Releases the keyboard capture.
drgui_hide
void drgui_hide(drgui_element *pElement)
Hides the given element.
drgui_on_mouse_leave_proc
void(* drgui_on_mouse_leave_proc)(drgui_element *pElement)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:361
drgui_text_run
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5267
drgui_tabbar_on_tab_close_proc
void(* drgui_tabbar_on_tab_close_proc)(drgui_element *pTBElement, drgui_tab *pTab)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:11147
drgui_make_point_absolute
void drgui_make_point_absolute(const drgui_element *pElement, float *positionX, float *positionY)
Converts the given point from relative to absolute based on the given element.
drgui_context::inboundEventCounter
int inboundEventCounter
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:777
drgui_get_image_size_proc
void(* drgui_get_image_size_proc)(drgui_resource image, unsigned int *pWidthOut, unsigned int *pHeightOut)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:407
OUT
#define OUT
Definition: porcupine/demo/c/dr_libs/old/dr.h:88
drgui_tabbar_create_and_append_tab
drgui_tab * drgui_tabbar_create_and_append_tab(drgui_element *pTBElement, const char *text, size_t extraDataSize, const void *pExtraData)
Creates and appends a tab.
DRGUI_ARROW_LEFT
#define DRGUI_ARROW_LEFT
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:438
drgui_painting_callbacks::createFont
drgui_create_font_proc createFont
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:589
drgui_sb_set_range
void drgui_sb_set_range(drgui_element *pSBElement, int rangeMin, int rangeMax)
Sets the given scrollbar's range.
drgui_draw_round_rect_with_outline
void drgui_draw_round_rect_with_outline(drgui_element *pElement, drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, drgui_color outlineColor, void *pPaintData)
Draws a filled rectangle and it's outline with rounded corners on the given element.
drgui_text_engine_on_text_changed_proc
void(* drgui_text_engine_on_text_changed_proc)(drgui_text_engine *pTL)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5317
drgui_draw_rect_proc
void(* drgui_draw_rect_proc)(drgui_rect relativeRect, drgui_color color, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:386
DRGUI_MAX_FONT_FAMILY_LENGTH
#define DRGUI_MAX_FONT_FAMILY_LENGTH
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:172
drgui_tabbar_on_paint_tab_proc
void(* drgui_tabbar_on_paint_tab_proc)(drgui_element *pTBElement, drgui_tab *pTab, drgui_rect relativeClippingRect, float offsetX, float offsetY, float width, float height, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:11144
drgui_textbox_redo
bool drgui_textbox_redo(drgui_element *pTBElement)
Performs a redo operation.
drgui_tabbar_is_auto_size_enabled
bool drgui_tabbar_is_auto_size_enabled(drgui_element *pTBElement)
Determines whether or not auto-sizing is enabled.
drgui_on_dirty_proc
void(* drgui_on_dirty_proc)(drgui_element *pElement, drgui_rect relativeRect)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:371
drgui_textbox_is_anything_selected
bool drgui_textbox_is_anything_selected(drgui_element *pTBElement)
Determines whether or not anything is selected in the given text box.
drgui_post_inbound_event_printable_key_down
void drgui_post_inbound_event_printable_key_down(drgui_context *pContext, unsigned int character, int stateFlags)
drgui_text_engine_find_next
bool drgui_text_engine_find_next(drgui_text_engine *pTL, const char *text, size_t *pSelectionStartOut, size_t *pSelectionEndOut)
Finds the given string starting from the cursor and then looping back.
drgui_on_key_up_proc
void(* drgui_on_key_up_proc)(drgui_element *pElement, drgui_key key, int stateFlags)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:368
drgui_textbox_get_line_numbers_padding
float drgui_textbox_get_line_numbers_padding(drgui_element *pTBElement)
drgui_text_engine_get_extra_data_size
size_t drgui_text_engine_get_extra_data_size(drgui_text_engine *pTL)
Retrieves the size of the extra data associated with the given text engine.
drgui_element::pExtraData
drgui_byte pExtraData[1]
A pointer to the extra data.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:763
drgui_text_engine_get_visible_line_count_starting_at
size_t drgui_text_engine_get_visible_line_count_starting_at(drgui_text_engine *pTL, size_t iFirstLine)
drgui_tabbar_set_on_measure_tab
void drgui_tabbar_set_on_measure_tab(drgui_element *pTBElement, drgui_tabbar_on_measure_tab_proc proc)
Sets the function to call when a tab needs to be measured.
drgui_painting_callbacks::measureString
drgui_measure_string_proc measureString
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:594
drgui_text_engine_enter_selection_mode
void drgui_text_engine_enter_selection_mode(drgui_text_engine *pTL)
drgui_tabbar_get_first_tab
drgui_tab * drgui_tabbar_get_first_tab(drgui_element *pTBElement)
DRGUI_F11
#define DRGUI_F11
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:453
drgui_font_slant_oblique
@ drgui_font_slant_oblique
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:234
drgui_draw_rect_outline
void drgui_draw_rect_outline(drgui_element *pElement, drgui_rect relativeRect, drgui_color color, float outlineWidth, void *pPaintData)
Draws the outline of a rectangle on the given element.
drgui_element::onHitTest
drgui_on_hittest_proc onHitTest
The function to call when a hit test needs to be performed.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:744
drgui_is_point_inside_element
bool drgui_is_point_inside_element(drgui_element *pElement, float absolutePosX, float absolutePosY)
drgui_get_cursor
drgui_cursor_type drgui_get_cursor(drgui_element *pElement)
Retrieves the cursor to use when the mouse enters the given GUI element.
drgui_tabbar_get_active_tab
drgui_tab * drgui_tabbar_get_active_tab(drgui_element *pTBElement)
Retrieves a pointer to the currently active tab.
drgui_on_mouse_enter_proc
void(* drgui_on_mouse_enter_proc)(drgui_element *pElement)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:360
drgui_tabbar_is_tab_in_view
bool drgui_tabbar_is_tab_in_view(drgui_element *pTBElement, drgui_tab *pTab)
Determines whether or not the given tab is in view.
drgui_font_weight
drgui_font_weight
Font weights.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:210
drgui_painting_callbacks::drawImage
drgui_draw_image_proc drawImage
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:587
drgui_text_engine_set_container_size
void drgui_text_engine_set_container_size(drgui_text_engine *pTL, float containerWidth, float containerHeight)
Sets the size of the container.
drgui_textbox_get_undo_points_remaining_count
unsigned int drgui_textbox_get_undo_points_remaining_count(drgui_element *pTBElement)
Retrieves the number of undo points remaining.
strncpy_s
DR_INLINE int strncpy_s(char *dst, size_t dstSizeInBytes, const char *src, size_t count)
Definition: porcupine/demo/c/dr_libs/old/dr.h:162
drgui_tvi_get_extra_data
void * drgui_tvi_get_extra_data(drgui_tree_view_item *pItem)
Retrieves a pointer to the extra data associated with the given tree-view item.
drgui_tab_move_into_view
void drgui_tab_move_into_view(drgui_tab *pTab)
drgui_is_point_inside_element_bounds
bool drgui_is_point_inside_element_bounds(const drgui_element *pElement, float absolutePosX, float absolutePosY)
drgui_textbox_set_on_undo_point_changed
void drgui_textbox_set_on_undo_point_changed(drgui_element *pTBElement, drgui_textbox_on_undo_point_changed_proc proc)
Sets the function to call when the undo point changes.
drgui_is_auto_dirty_enabled
bool drgui_is_auto_dirty_enabled(drgui_context *pContext)
Determines whether or not automatic dirtying is enabled.
drgui_sb_orientation_none
@ drgui_sb_orientation_none
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:10138
drgui_tree_view_item
struct drgui_tree_view_item drgui_tree_view_item
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:15228
drgui_painting_callbacks::drawText
drgui_draw_text_proc drawText
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:586
drgui_set_on_dirty
void drgui_set_on_dirty(drgui_element *pElement, drgui_on_dirty_proc callback)
Registers the on_dirty event callback.
drgui_is_child
bool drgui_is_child(drgui_element *pChildElement, drgui_element *pParentElement)
drgui_element::pLastChild
drgui_element * pLastChild
A pointer to the last child element.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:657
drgui_cursor_none
@ drgui_cursor_none
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:198
drgui_textbox_enable_vertical_scrollbar
void drgui_textbox_enable_vertical_scrollbar(drgui_element *pTBElement)
Enables the vertical scrollbar.
drgui_sb_set_mouse_wheel_scele
void drgui_sb_set_mouse_wheel_scele(drgui_element *pSBElement, int scale)
drgui_textbox_step
void drgui_textbox_step(drgui_element *pTBElement, unsigned int milliseconds)
Steps the text box to allow it to blink the cursor.
drgui_offset_rect
drgui_rect drgui_offset_rect(drgui_rect rect, float offsetX, float offsetY)
Offsets the given rectangle.
DRGUI_KEY_STATE_CTRL_DOWN
#define DRGUI_KEY_STATE_CTRL_DOWN
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:421
drgui_cursor_default
@ drgui_cursor_default
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:199
drgui_painting_callbacks::getTextCursorPositionFromChar
drgui_get_text_cursor_position_from_char_proc getTextCursorPositionFromChar
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:596
drgui_sb_get_on_scroll
drgui_sb_on_scroll_proc drgui_sb_get_on_scroll(drgui_element *pSBElement)
Retrieves the function call when the given scrollbar is scrolled.
drgui_text_engine_get_inner_offset_y
float drgui_text_engine_get_inner_offset_y(drgui_text_engine *pTL)
Retrieves the inner offset of the given text engine on the x axis.
drgui_text_engine_get_cursor_line
size_t drgui_text_engine_get_cursor_line(drgui_text_engine *pTL)
Retrieves the index of the line the cursor is currently sitting on.
drgui_textbox_delete_selected_text
bool drgui_textbox_delete_selected_text(drgui_element *pTBElement)
drgui_textbox_set_vertical_align
void drgui_textbox_set_vertical_align(drgui_element *pTBElement, drgui_text_engine_alignment align)
Sets the vertical alignment of the given text box.
drgui_rect_contains_point
bool drgui_rect_contains_point(drgui_rect rect, float posX, float posY)
drgui_tabbar_set_close_button_color
void drgui_tabbar_set_close_button_color(drgui_element *pTBElement, drgui_color color)
drgui_on_capture_keyboard_proc
void(* drgui_on_capture_keyboard_proc)(drgui_element *pElement, drgui_element *pPrevCapturedElement)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:375
drgui_tv_on_mouse_button_up
void drgui_tv_on_mouse_button_up(drgui_element *pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Called when the mouse button up event needs to be processed for the given tree-view control.
drgui_tabbar_get_close_button_image
drgui_image * drgui_tabbar_get_close_button_image(drgui_element *pTBElement)
Retrieves the image being used for the close buttons.
drgui_sb_scroll_to
void drgui_sb_scroll_to(drgui_element *pSBElement, int newScrollPos)
drgui_set_global_on_change_cursor
void drgui_set_global_on_change_cursor(drgui_context *pContext, drgui_on_change_cursor_proc onChangeCursor)
drgui_text_engine_select
void drgui_text_engine_select(drgui_text_engine *pTL, size_t firstCharacter, size_t lastCharacter)
Selects the given range of text.
drgui_text_engine_delete_character_to_left_of_cursor
bool drgui_text_engine_delete_character_to_left_of_cursor(drgui_text_engine *pTL)
drgui_text_engine_clear_undo_stack
void drgui_text_engine_clear_undo_stack(drgui_text_engine *pTL)
Clears the undo stack.
drgui_sb_on_mouse_move
void drgui_sb_on_mouse_move(drgui_element *pSBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Called when the mouse move event needs to be processed for the given scrollbar.
drgui_tabbar_set_on_paint_tab
void drgui_tabbar_set_on_paint_tab(drgui_element *pTBElement, drgui_tabbar_on_paint_tab_proc proc)
Sets the function to call when a tab needs to be painted.
drgui_sb_scroll
void drgui_sb_scroll(drgui_element *pSBElement, int offset)
drgui_tabbar_set_tab_background_color_hovered
void drgui_tabbar_set_tab_background_color_hovered(drgui_element *pTBElement, drgui_color color)
drgui_font_slant_italic
@ drgui_font_slant_italic
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:233
drgui_on_key_down_proc
void(* drgui_on_key_down_proc)(drgui_element *pElement, drgui_key key, int stateFlags)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:367
drgui_textbox_get_cursor_column
size_t drgui_textbox_get_cursor_column(drgui_element *pTBElement)
Retrieves the index of the column the cursor is current sitting on.
dr2d_glyph_metrics::originY
int originY
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:111
dr2d_font_metrics::lineHeight
int lineHeight
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:102
dr2d_create_image
dr2d_image * dr2d_create_image(dr2d_context *pContext, unsigned int width, unsigned int height, dr2d_image_format format, unsigned int stride, const void *pData)
drgui_color::a
drgui_byte a
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:274
drgui_disable_clipping
void drgui_disable_clipping(drgui_element *pElement)
Disables clipping against the parent for the given element.
drgui_textbox_set_background_color
void drgui_textbox_set_background_color(drgui_element *pTBElement, drgui_color color)
Sets the background color of the given text box.
drgui_get_font_size_proc
unsigned int(* drgui_get_font_size_proc)(drgui_resource font)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:397
dr2d_get_font_size
unsigned int dr2d_get_font_size(dr2d_font *pFont)
Retrieves the size of the given font.
drgui_text_engine_paint
void drgui_text_engine_paint(drgui_text_engine *pTL, drgui_rect rect, drgui_element *pElement, void *pPaintData)
drgui_text_engine_set_tab_size
void drgui_text_engine_set_tab_size(drgui_text_engine *pTL, unsigned int sizeInSpaces)
Sets the size of a tab in spaces.
drgui_tabbar_on_mouse_leave
void drgui_tabbar_on_mouse_leave(drgui_element *pTBElement)
Called when the mouse leave event needs to be processed for the given tab bar control.
drgui_tvi_is_selected
bool drgui_tvi_is_selected(drgui_tree_view_item *pItem)
Determines whether or not the given tree view item is selected.
drgui_on_mouse_button_up_proc
void(* drgui_on_mouse_button_up_proc)(drgui_element *pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:364
drgui_tvi_delete
void drgui_tvi_delete(drgui_tree_view_item *pItem)
Recursively deletes a tree view item.
drgui_tabbar_get_next_tab
drgui_tab * drgui_tabbar_get_next_tab(drgui_element *pTBElement, drgui_tab *pTab)
DRGUI_MOUSE_BUTTON_RIGHT
#define DRGUI_MOUSE_BUTTON_RIGHT
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:427
drgui_tab
struct drgui_tab drgui_tab
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:11141
drgui_textbox_set_cursor_blink_rate
void drgui_textbox_set_cursor_blink_rate(drgui_element *pTBElement, unsigned int blinkRateInMilliseconds)
Sets the blink rate of the cursor in milliseconds.
drgui_text_engine_commit_undo_point
bool drgui_text_engine_commit_undo_point(drgui_text_engine *pTL)
Creates a snapshot of the current state of the text engine and pushes it to the top of the undo/redo ...
drgui_register_painting_callbacks
bool drgui_register_painting_callbacks(drgui_context *pContext, void *pPaintingContext, drgui_painting_callbacks callbacks)
dr2d_get_glyph_metrics
bool dr2d_get_glyph_metrics(dr2d_font *pFont, unsigned int utf32, dr2d_glyph_metrics *pMetricsOut)
Retrieves the metrics of the glyph for the given character when rendered with the given font.
drgui_text_engine_on_paint_text_proc
void(* drgui_text_engine_on_paint_text_proc)(drgui_text_engine *pTL, drgui_text_run *pRun, drgui_element *pElement, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5313
drgui_sb_set_scroll_position
void drgui_sb_set_scroll_position(drgui_element *pSBElement, int position)
drgui_sb_set_range_and_page_size
void drgui_sb_set_range_and_page_size(drgui_element *pSBElement, int rangeMin, int rangeMax, int pageSize)
drgui_create_element
drgui_element * drgui_create_element(drgui_context *pContext, drgui_element *pParent, size_t extraDataSize, const void *pExtraData)
Creates an element.
drgui_text_run::textColor
drgui_color textColor
The foreground color of the text.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5280
drgui_element::height
float height
The height of the element.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:688
drgui_tv_set_on_item_picked
void drgui_tv_set_on_item_picked(drgui_element *pTVElement, drgui_tvi_on_picked_proc proc)
drgui_visible_iteration_proc
bool(* drgui_visible_iteration_proc)(drgui_element *pElement, drgui_rect *pRelativeRect, void *pUserData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:411
drgui_get_relative_position_y
float drgui_get_relative_position_y(const drgui_element *pElement)
drgui_font_weight_heavy
@ drgui_font_weight_heavy
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:221
drgui_draw_rect_outline_proc
void(* drgui_draw_rect_outline_proc)(drgui_rect relativeRect, drgui_color color, float outlineWidth, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:387
drgui_tv_set_child_offset_x
void drgui_tv_set_child_offset_x(drgui_element *pTVElement, float childOffsetX)
Sets the amount of indentation to apply to each child item in the given tree-view.
drgui_make_rect
drgui_rect drgui_make_rect(float left, float top, float right, float bottom)
Creates a drgui_rect object.
drgui_textbox_set_padding
void drgui_textbox_set_padding(drgui_element *pTBElement, float padding)
Sets the amount of padding to apply to given text box.
drgui_on_delete_element_proc
void(* drgui_on_delete_element_proc)(drgui_element *pElement)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:378
drgui_textbox_get_redo_points_remaining_count
unsigned int drgui_textbox_get_redo_points_remaining_count(drgui_element *pTBElement)
Retrieves the number of redo points remaining.
drgui_tabbar_set_text_color_active
void drgui_tabbar_set_text_color_active(drgui_element *pTBElement, drgui_color color)
dr2d_glyph_metrics
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:106
drgui_set_on_log
void drgui_set_on_log(drgui_context *pContext, drgui_on_log onLog)
Registers the callback to call when a log message is posted.
dr2d_font_metrics::spaceWidth
int spaceWidth
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:103
drgui_textbox_get_extra_data
void * drgui_textbox_get_extra_data(drgui_element *pTBElement)
Retrieves a pointer to the extra data associated with the given text box.
drgui_is_element_under_mouse
bool drgui_is_element_under_mouse(drgui_element *pTopLevelElement)
Determines whether or not the given element is currently sitting directly under the mouse.
drgui_on_size_proc
void(* drgui_on_size_proc)(drgui_element *pElement, float newWidth, float newHeight)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:359
drgui_textbox_set_active_line_background_color
void drgui_textbox_set_active_line_background_color(drgui_element *pTBElement, drgui_color color)
Sets the background color for the line the caret is currently sitting on.
drgui_tv_on_paint
void drgui_tv_on_paint(drgui_element *pTVElement, drgui_rect relativeClippingRect, void *pPaintData)
Called when the paint event needs to be processed for the given tree-view control.
drgui_painting_callbacks::createImage
drgui_create_image_proc createImage
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:598
drgui_post_inbound_event_key_up
void drgui_post_inbound_event_key_up(drgui_context *pContext, drgui_key key, int stateFlags)
Posts a key up inbound event.
drgui_tabbar_set_on_tab_mouse_button_up
void drgui_tabbar_set_on_tab_mouse_button_up(drgui_element *pTBElement, drgui_tabbar_on_tab_mouse_button_up_proc proc)
drgui_release_mouse_no_global_notify
void drgui_release_mouse_no_global_notify(drgui_context *pContext)
Releases the mouse capture without posting the global-scoped event. Should only be used in very speci...
drgui_painting_callbacks::getGlyphMetrics
drgui_get_glyph_metrics_proc getGlyphMetrics
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:593
drgui_font_slant
drgui_font_slant
Font slants.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:230
drgui_font_weight_extra_heavy
@ drgui_font_weight_extra_heavy
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:222
DRGUI_MAX_TAB_TEXT_LENGTH
#define DRGUI_MAX_TAB_TEXT_LENGTH
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:11131
drgui_draw_border
void drgui_draw_border(drgui_element *pElement, float borderWidth, drgui_color color, void *pUserData)
Draws a border around the given element.
drgui_draw_end_proc
void(* drgui_draw_end_proc)(void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:382
drgui_cursor_arrow
@ drgui_cursor_arrow
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:200
drgui_font::size
unsigned int size
The base size of the font. This is set to the value that was used to create the font in the first pla...
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:624
DRGUI_PAGE_DOWN
#define DRGUI_PAGE_DOWN
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:435
drgui_sb_disable_thumb_auto_hide
void drgui_sb_disable_thumb_auto_hide(drgui_element *pSBElement)
Disables auto-hiding of the thumb.
drgui_tabbar_get_tab_background_color_hovered
drgui_color drgui_tabbar_get_tab_background_color_hovered(drgui_element *pTBElement)
drgui_textbox_on_release_mouse
void drgui_textbox_on_release_mouse(drgui_element *pTBElement)
on_release_mouse
drgui_tv_on_mouse_button_down
void drgui_tv_on_mouse_button_down(drgui_element *pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Called when the mouse button down event needs to be processed for the given tree-view control.
drgui_get_relative_position_x
float drgui_get_relative_position_x(const drgui_element *pElement)
drgui_set_size
void drgui_set_size(drgui_element *pElement, float width, float height)
Sets the size of the given element.
drgui_set_global_on_capture_keyboard
void drgui_set_global_on_capture_keyboard(drgui_context *pContext, drgui_on_capture_keyboard_proc onCaptureKeyboard)
drgui_post_inbound_event_mouse_wheel
void drgui_post_inbound_event_mouse_wheel(drgui_element *pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags)
Posts a mouse wheel inbound event.
drgui_textbox_set_text_color
void drgui_textbox_set_text_color(drgui_element *pTBElement, drgui_color color)
Sets the color of the text in teh given text box.
drgui_textbox_set_border_color
void drgui_textbox_set_border_color(drgui_element *pTBElement, drgui_color color)
Sets the border color of the given text box.
drgui_find_top_level_element
drgui_element * drgui_find_top_level_element(drgui_element *pElement)
drgui_cursor_text
@ drgui_cursor_text
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:201
drgui_tabbar_orientation_left
@ drgui_tabbar_orientation_left
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:11137
drgui_context::onLog
drgui_on_log onLog
The function to call when a log message is posted.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:832
drgui_font_metrics::descent
int descent
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:251
drgui_get_clip_proc
void(* drgui_get_clip_proc)(drgui_rect *pRectOut, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:384
drgui_text_engine_on_cursor_move_proc
void(* drgui_text_engine_on_cursor_move_proc)(drgui_text_engine *pTL)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5315
drgui_tab_get_extra_data_size
size_t drgui_tab_get_extra_data_size(drgui_tab *pTab)
Retrieves the size of the extra data associated with the given tree-view item.
drgui_set_on_mouse_button_dblclick
void drgui_set_on_mouse_button_dblclick(drgui_element *pElement, drgui_on_mouse_button_dblclick_proc callback)
Registers the on_mouse_button_down event callback.
drgui_text_run::width
float width
The width of the run.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5293
drgui_image
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:606
drgui_font_weight_normal
@ drgui_font_weight_normal
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:224
drgui_element::onReleaseMouse
drgui_on_release_mouse_proc onReleaseMouse
The event handler to call when an element loses the mouse focus.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:750
drgui_draw_image_args::srcHeight
float srcHeight
The source height.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:323
drgui_set_absolute_position
void drgui_set_absolute_position(drgui_element *pElement, float positionX, float positionY)
Sets the absolute position of the given element.
drgui_rgb
drgui_color drgui_rgb(drgui_byte r, drgui_byte g, drgui_byte b)
Creates a color object from a set of RGB color components.
drgui_prepend
void drgui_prepend(drgui_element *pChildElement, drgui_element *pParentElement)
Attaches the given element as a child of the given parent element, and prepends it to the end of the ...
drgui_painting_callbacks::drawRectWithOutline
drgui_draw_rect_with_outline_proc drawRectWithOutline
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:582
drgui_draw_image_args::dstHeight
float dstHeight
The destination height.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:310
drgui_text_engine_insert_text
bool drgui_text_engine_insert_text(drgui_text_engine *pTL, const char *text, size_t insertIndex)
drgui_tv_deselect_all_items
void drgui_tv_deselect_all_items(drgui_element *pTVElement)
Deselects every tree-view item.
drgui_element::onKeyUp
drgui_on_key_up_proc onKeyUp
The function to call when a key on the keyboard is released.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:732
dr2d_delete_font
void dr2d_delete_font(dr2d_font *pFont)
Deletes a font that was previously created with dr2d_create_font()
drgui_draw_line_proc
void(* drgui_draw_line_proc)(float startX, float startY, float endX, float endY, float width, drgui_color color, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:385
drgui_tvi_append
void drgui_tvi_append(drgui_tree_view_item *pItem, drgui_tree_view_item *pParent)
Appends a tree view item as a child of the given parent item.
drgui_cursor_size_nesw
@ drgui_cursor_size_nesw
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:205
drgui_draw_image_args::srcY
float srcY
The source offset on the y axis.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:317
drgui_text_engine_undo
bool drgui_text_engine_undo(drgui_text_engine *pTL)
Performs an undo operation.
drgui_delete_image_proc
void(* drgui_delete_image_proc)(drgui_resource image)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:405
drgui_text_engine_get_text_rect_relative_to_bounds
drgui_rect drgui_text_engine_get_text_rect_relative_to_bounds(drgui_text_engine *pTL)
Retrieves the rectangle of the text relative to the bounds, taking alignment into account.
drgui_make_rect_relative
drgui_rect drgui_make_rect_relative(const drgui_element *pElement, drgui_rect *pRect)
Converts the given rectangle from absolute to relative to the given element.
drgui_is_descendant
bool drgui_is_descendant(drgui_element *pChildElement, drgui_element *pAncestorElement)
Determines whether or not the given element is a descendant of the other.
drgui_set_on_mouse_move
void drgui_set_on_mouse_move(drgui_element *pElement, drgui_on_mouse_move_proc callback)
Registers the on_mouse_move event callback.
drgui_painting_callbacks::drawLine
drgui_draw_line_proc drawLine
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:579
drgui_draw_text_proc
void(* drgui_draw_text_proc)(drgui_resource font, const char *text, int textLengthInBytes, float posX, float posY, drgui_color color, drgui_color backgroundColor, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:392
drgui_sb_set_pressed_thumb_color
void drgui_sb_set_pressed_thumb_color(drgui_element *pSBElement, drgui_color color)
Sets the pressed color of the thumb.
drgui_on_release_keyboard_proc
void(* drgui_on_release_keyboard_proc)(drgui_element *pElement, drgui_element *pNewCapturedElement)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:376
drgui_glyph_metrics::originY
int originY
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:262
drgui_get_size
void drgui_get_size(const drgui_element *pElement, float *widthOut, float *heightOut)
Retrieves the size of the given element.
dr2d_draw_round_rect
void dr2d_draw_round_rect(dr2d_surface *pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius)
Draws a filled rectangle without an outline with rounded corners.
dr2d_set_clip
void dr2d_set_clip(dr2d_surface *pSurface, float left, float top, float right, float bottom)
Sets the clipping rectangle.
drgui_sb_on_scroll_proc
void(* drgui_sb_on_scroll_proc)(drgui_element *pSBElement, int scrollPos)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:10144
drgui_textbox_on_undo_point_changed_proc
void(* drgui_textbox_on_undo_point_changed_proc)(drgui_element *pTBElement, unsigned int iUndoPoint)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:12995
drgui_clamp_rect_to_element
bool drgui_clamp_rect_to_element(const drgui_element *pElement, drgui_rect *pRelativeRect)
Clamps the given rectangle to the given element and returns whether or not any of it is contained wit...
drgui_set_clip
void drgui_set_clip(drgui_element *pElement, drgui_rect relativeRect, void *pPaintData)
Sets the clipping rectangle to apply to all future draw operations on this element.
dr2d_delete_image
void dr2d_delete_image(dr2d_image *pImage)
Deletes the given image.
drgui_sb_get_range
void drgui_sb_get_range(drgui_element *pSBElement, int *pRangeMinOut, int *pRangeMaxOut)
Retrieves the given scrollbar's range.
drgui_text_engine_get_container_height
float drgui_text_engine_get_container_height(drgui_text_engine *pTL)
Retrieves the height of the container.
drgui_tvi_has_children
bool drgui_tvi_has_children(drgui_tree_view_item *pItem)
Determines whether or not the given item has any children.
drgui_tabbar_get_orientation
drgui_tabbar_orientation drgui_tabbar_get_orientation(drgui_element *pTBElement)
Retrieves the orientation of the given scrollbar.
drgui_has_mouse_capture
bool drgui_has_mouse_capture(drgui_element *pElement)
Determines whether or not the given element has the mouse capture.
drgui_font_metrics::ascent
int ascent
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:250
drgui_cursor_size_we
@ drgui_cursor_size_we
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:204
drgui_text_engine_set_on_paint_rect
void drgui_text_engine_set_on_paint_rect(drgui_text_engine *pTL, drgui_text_engine_on_paint_rect_proc proc)
Sets the function to call when a quad needs to the be painted for the given text engine.
drgui_painting_callbacks::drawRect
drgui_draw_rect_proc drawRect
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:580
drgui_get_absolute_rect
drgui_rect drgui_get_absolute_rect(const drgui_element *pElement)
Retrieves the absolute rectangle for the given element.
drgui_set_on_mouse_button_up
void drgui_set_on_mouse_button_up(drgui_element *pElement, drgui_on_mouse_button_up_proc callback)
Registers the on_mouse_button_up event callback.
drgui_strcpy
static size_t drgui_strcpy(char *dst, size_t dstSize, const char *src)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:456
drgui_set_relative_position
void drgui_set_relative_position(drgui_element *pElement, float relativePosX, float relativePosY)
Sets the relative position of the given element.
strcpy_s
DR_INLINE int strcpy_s(char *dst, size_t dstSizeInBytes, const char *src)
Definition: porcupine/demo/c/dr_libs/old/dr.h:157
drgui_context::onGlobalReleaseKeyboard
drgui_on_release_keyboard_proc onGlobalReleaseKeyboard
The global event handler to call when an element releases the keyboard.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:822
drgui_text_engine_hide_cursor
void drgui_text_engine_hide_cursor(drgui_text_engine *pTL)
Hides the cursor.
drgui_text_engine_get_inner_offset
void drgui_text_engine_get_inner_offset(drgui_text_engine *pTL, float *pInnerOffsetX, float *pInnerOffsetY)
Retrieves the inner offset of the given text engine.
drgui_element::onReleaseKeyboard
drgui_on_release_keyboard_proc onReleaseKeyboard
The event handler to call when an element loses the keyboard focus.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:756
drgui_tabbar_get_extra_data
void * drgui_tabbar_get_extra_data(drgui_element *pTBElement)
Retrieves a pointer to the extra data associated with the scrollbar.
drgui_tabbar_on_tab_deactivated_proc
void(* drgui_tabbar_on_tab_deactivated_proc)(drgui_element *pTBElement, drgui_tab *pTab, drgui_tab *pNewActiveTab)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:11146
drgui_text_engine_get_line_pos_y
float drgui_text_engine_get_line_pos_y(drgui_text_engine *pTL, size_t iLine)
drgui_textbox_set_horizontal_align
void drgui_textbox_set_horizontal_align(drgui_element *pTBElement, drgui_text_engine_alignment align)
Sets the horizontal alignment of the given text box.
dr2d_glyph_metrics::originX
int originX
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:110
drgui_get_font_metrics
bool drgui_get_font_metrics(drgui_font *pFont, drgui_font_metrics *pMetricsOut)
Retrieves the metrics of the given font.
drgui_painting_callbacks
Structure containing callbacks for painting routines.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:571
drgui_textbox_set_cursor_width
void drgui_textbox_set_cursor_width(drgui_element *pTBElement, float cursorWidth)
Sets the width of the text cursor.
drgui_textbox_get_line_numbers_background_color
drgui_color drgui_textbox_get_line_numbers_background_color(drgui_element *pTBElement)
drgui_is_ancestor
bool drgui_is_ancestor(drgui_element *pAncestorElement, drgui_element *pChildElement)
Determines whether or not the given element is an ancestor of the other.
drgui_find_element_under_point
drgui_element * drgui_find_element_under_point(drgui_element *pTopLevelElement, float absolutePosX, float absolutePosY)
Finds the element under the given point taking mouse pass-through and hit testing into account.
drgui_delete_font
void drgui_delete_font(drgui_font *pFont)
Deletes a font resource.
drgui_text_engine_insert_text_at_cursor
bool drgui_text_engine_insert_text_at_cursor(drgui_text_engine *pTL, const char *text)
drgui_textbox_on_mouse_wheel
void drgui_textbox_on_mouse_wheel(drgui_element *pTBElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags)
on_mouse_wheel
drgui_draw_image
void drgui_draw_image(drgui_element *pElement, drgui_image *pImage, drgui_draw_image_args *pArgs, void *pPaintData)
Draws an image.
drgui_context::flags
unsigned int flags
Boolean flags.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:806
drgui_text_engine_get_line_first_character
size_t drgui_text_engine_get_line_first_character(drgui_text_engine *pTL, size_t iLine)
Retrieves the index of the first character of the line at the given index.
drgui_text_engine_get_horizontal_align
drgui_text_engine_alignment drgui_text_engine_get_horizontal_align(drgui_text_engine *pTL)
Retrieves the horizontal aligment of the given text engine.
drgui_tabbar_set_text_color_hovered
void drgui_tabbar_set_text_color_hovered(drgui_element *pTBElement, drgui_color color)
drgui_textbox_get_extra_data_size
size_t drgui_textbox_get_extra_data_size(drgui_element *pTBElement)
Retrieves the size of the extra data associated with the given text box.
drgui_element::onPaint
drgui_on_paint_proc onPaint
The function to call when the paint event is received.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:738
drgui_tabbar_activate_next_tab
void drgui_tabbar_activate_next_tab(drgui_element *pTBElement)
drgui_text_engine_refresh_markers
void drgui_text_engine_refresh_markers(drgui_text_engine *pTL)
Refreshes the cursor and selection marker positions.
drgui_text_engine_show_cursor
void drgui_text_engine_show_cursor(drgui_text_engine *pTL)
Shows the cursor.
dr2d_glyph_metrics::height
int height
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:109
_itoa_s
DR_INLINE int _itoa_s(int value, char *dst, size_t dstSizeInBytes, int radix)
Definition: porcupine/demo/c/dr_libs/old/dr.h:184
drgui_textbox_enable_horizontal_scrollbar
void drgui_textbox_enable_horizontal_scrollbar(drgui_element *pTBElement)
Enables the horizontal scrollbar.
drgui_draw
void drgui_draw(drgui_element *pElement, drgui_rect relativeRect, void *pPaintData)
drgui_is_of_type
bool drgui_is_of_type(drgui_element *pElement, const char *type)
Determines whether or not the given element is of the given type.
drgui_draw_image_args::dstBoundsWidth
float dstBoundsWidth
The width of the destination's bounds.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:333
drgui_textbox_get_line_count
size_t drgui_textbox_get_line_count(drgui_element *pTBElement)
Retrieves the number of lines in the given text box.
drgui_sb_get_extra_data
void * drgui_sb_get_extra_data(drgui_element *pSBElement)
Retrieves a pointer to the extra data associated with the scrollbar.
count
size_t count
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/tests/test_common/ma_test_common.c:31
drgui_tvi_on_picked_proc
void(* drgui_tvi_on_picked_proc)(drgui_tree_view_item *pItem)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:15234
drgui_image_format
drgui_image_format
Image formats.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:239
drgui_sb_get_scroll_position
int drgui_sb_get_scroll_position(drgui_element *pSBElement)
Retrieves the scroll position.
drgui_tabbar_disable_auto_size
void drgui_tabbar_disable_auto_size(drgui_element *pTBElement)
Disables auto-resizing based on tabs.
drgui_create_scrollbar
drgui_element * drgui_create_scrollbar(drgui_context *pContext, drgui_element *pParent, drgui_sb_orientation orientation, size_t extraDataSize, const void *pExtraData)
Creates a scrollbar element.
drgui_context::dirtyCounter
unsigned int dirtyCounter
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:856
drgui_textbox_on_mouse_button_up
void drgui_textbox_on_mouse_button_up(drgui_element *pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
on_mouse_button_up.
drgui_text_engine_set_on_text_changed
void drgui_text_engine_set_on_text_changed(drgui_text_engine *pTL, drgui_text_engine_on_text_changed_proc proc)
Sets the function to call when the content of the given text engine has changed.
drgui_tabbar_measure_tab
void drgui_tabbar_measure_tab(drgui_element *pTBElement, drgui_tab *pTab, float *pWidthOut, float *pHeightOut)
Measures the given tab.
DRGUI_MOUSE_BUTTON_LEFT
#define DRGUI_MOUSE_BUTTON_LEFT
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:426
drgui_text_engine_paint_line_numbers
void drgui_text_engine_paint_line_numbers(drgui_text_engine *pTL, float lineNumbersWidth, float lineNumbersHeight, drgui_color textColor, drgui_color backgroundColor, drgui_text_engine_on_paint_text_proc onPaintText, drgui_text_engine_on_paint_rect_proc onPaintRect, drgui_element *pElement, void *pPaintData)
Calls the given painting callbacks for the line numbers of the given text engine.
drgui_font::rotation
float rotation
The fon't rotation.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:633
drgui_get_absolute_position_x
float drgui_get_absolute_position_x(const drgui_element *pElement)
drgui_rect_equal
bool drgui_rect_equal(drgui_rect rect0, drgui_rect rect1)
Determines whether or not two rectangles are equal.
drgui_context::dirtyElementBufferSize
size_t dirtyElementBufferSize
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:848
drgui_set_on_size
void drgui_set_on_size(drgui_element *pElement, drgui_on_size_proc callback)
Registers the on_size event callback.
drgui_font_metrics::spaceWidth
int spaceWidth
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:253
drgui_tab_get_text
const char * drgui_tab_get_text(drgui_tab *pTab)
Retrieves the text of the given tab bar item.
drgui_text_engine_find_next_no_loop
bool drgui_text_engine_find_next_no_loop(drgui_text_engine *pTL, const char *text, size_t *pSelectionStartOut, size_t *pSelectionEndOut)
Finds the given string starting from the cursor, but does not loop back.
drgui_get_glyph_metrics
bool drgui_get_glyph_metrics(drgui_font *pFont, unsigned int utf32, drgui_glyph_metrics *pMetricsOut)
Retrieves the metrics of the glyph for the given character when rendered with the given font.
drgui_textbox_hide_line_numbers
void drgui_textbox_hide_line_numbers(drgui_element *pTBElement)
Hides the line numbers.
drgui_element::absolutePosX
float absolutePosX
The absolute position of the element on the x axis. A position of 0 is the left side of the surface i...
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:679
drgui_tabbar_set_on_tab_activated
void drgui_tabbar_set_on_tab_activated(drgui_element *pTBElement, drgui_tabbar_on_tab_activated_proc proc)
Sets the function to call when a tab is activated.
drgui_sb_on_mouse_button_down
void drgui_sb_on_mouse_button_down(drgui_element *pSBElement, int button, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Called when the mouse button down event needs to be processed for the given scrollbar.
drgui_textbox_get_padding_vert
float drgui_textbox_get_padding_vert(drgui_element *pTBElement)
Retrieves the amound of vertical padding to apply to the given text box.
drgui_element::onPrintableKeyDown
drgui_on_printable_key_down_proc onPrintableKeyDown
The function to call when a printable character is pressed or auto-repeated. This would be used for t...
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:735
drgui_tabbar_set_tab_background_color_active
void drgui_tabbar_set_tab_background_color_active(drgui_element *pTBElement, drgui_color color)
drgui_text_engine_set_cursor_width
void drgui_text_engine_set_cursor_width(drgui_text_engine *pTL, float cursorWidth)
Sets the width of the text cursor.
drgui_rect
Structure representing a rectangle.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:278
drgui_text_run::iLine
size_t iLine
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5303
drgui_text_run::posY
float posY
The position to draw the text on the y axis.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5290
drgui_make_rect_absolute
drgui_rect drgui_make_rect_absolute(const drgui_element *pElement, drgui_rect *pRect)
Converts the given rectangle from relative to absolute based on the given element.
dr2d_create_font
dr2d_font * dr2d_create_font(dr2d_context *pContext, const char *family, unsigned int size, dr2d_font_weight weight, dr2d_font_slant slant, float rotation, unsigned int flags)
Creates a font that can be passed to dr2d_draw_text().
drgui_textbox_find_and_select_next
bool drgui_textbox_find_and_select_next(drgui_element *pTBElement, const char *text)
Finds and selects the next occurance of the given string, starting from the cursor and looping back t...
drgui_tabbar_orientation_top
@ drgui_tabbar_orientation_top
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:11135
drgui_textbox_on_release_keyboard
void drgui_textbox_on_release_keyboard(drgui_element *pTBElement, drgui_element *pNewCapturedElement)
on_release_keyboard
drgui_tvi_deselect
void drgui_tvi_deselect(drgui_tree_view_item *pItem)
Deselects the given item.
drgui_cursor_type
drgui_cursor_type
Common system cursors.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:196
dr2d_get_image_size
void dr2d_get_image_size(dr2d_image *pImage, unsigned int *pWidthOut, unsigned int *pHeightOut)
Retrieves the size of the given image.
drgui_set_on_move
void drgui_set_on_move(drgui_element *pElement, drgui_on_move_proc callback)
Registers the on_move event callback.
drgui_tv_get_selected_background_color
drgui_color drgui_tv_get_selected_background_color(drgui_element *pTVElement)
Retrieves the default background color of selected items.
drgui_text_engine_get_selected_text
size_t drgui_text_engine_get_selected_text(drgui_text_engine *pTL, char *textOut, size_t textOutLength)
drgui_sb_get_orientation
drgui_sb_orientation drgui_sb_get_orientation(drgui_element *pSBElement)
Retrieves the orientation of the given scrollbar.
drgui_get_image_size
void drgui_get_image_size(drgui_image *pImage, unsigned int *pWidthOut, unsigned int *pHeightOut)
Retrieves the size of the given image.
drgui_tabbar_get_tab_background_color_actived
drgui_color drgui_tabbar_get_tab_background_color_actived(drgui_element *pTBElement)
drgui_text_engine_get_container_width
float drgui_text_engine_get_container_width(drgui_text_engine *pTL)
Retrieves the width of the container.
drgui_font::slant
drgui_font_slant slant
The fon't slant.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:630
drgui_textbox_get_cursor_width
float drgui_textbox_get_cursor_width(drgui_element *pTBElement)
Retrieves the width of the text cursor.
drgui_text_run::posX
float posX
The position to draw the text on the x axis.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5287
drgui_tvi_select
void drgui_tvi_select(drgui_tree_view_item *pItem)
Selects the given item.
drgui_set_type
bool drgui_set_type(drgui_element *pElement, const char *type)
dr2d_glyph_metrics::advanceY
int advanceY
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:113
drgui_sb_on_paint
void drgui_sb_on_paint(drgui_element *pSBElement, drgui_rect relativeClippingRect, void *pPaintData)
Called when the paint event needs to be processed.
drgui_get_text_cursor_position_from_char_proc
bool(* drgui_get_text_cursor_position_from_char_proc)(drgui_resource font, const char *text, size_t characterIndex, float *pTextCursorPosXOut)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:402
drgui_context::onGlobalCaptureMouse
drgui_on_capture_mouse_proc onGlobalCaptureMouse
The global event handler to call when an element captures the mouse.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:813
drgui_sb_get_thumb_rect
drgui_rect drgui_sb_get_thumb_rect(drgui_element *pSBElement)
Calculates the relative rectangle of the given scrollbar's thumb.
drgui_delete_text_engine
void drgui_delete_text_engine(drgui_text_engine *pTL)
Deletes the given text engine.
drgui_textbox_get_line_numbers_width
float drgui_textbox_get_line_numbers_width(drgui_element *pTBElement)
drgui_textbox_set_cursor_color
void drgui_textbox_set_cursor_color(drgui_element *pTBElement, drgui_color color)
Sets the color of the cursor of the given text box.
drgui_draw_image_args::backgroundColor
drgui_color backgroundColor
The background color. Only used if the DR2D_IMAGE_DRAW_BACKGROUND option is set.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:343
drgui_get_extra_data_size
size_t drgui_get_extra_data_size(drgui_element *pElement)
Retrieves the size of the extra data of the given element, in bytes.
drgui_element::onCaptureKeyboard
drgui_on_capture_keyboard_proc onCaptureKeyboard
The event handler to call when an element receives the keyboard focus.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:753
drgui_tv_get_horizontal_scrollbar
drgui_element * drgui_tv_get_horizontal_scrollbar(drgui_element *pTVElement)
Retrieves a pointer to the horizontal scrollbar.
drgui_sb_orientation_vertical
@ drgui_sb_orientation_vertical
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:10139
drgui_map_image_data_proc
void *(* drgui_map_image_data_proc)(drgui_resource image, unsigned int accessFlags)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:408
drgui_tabbar_set_font
void drgui_tabbar_set_font(drgui_element *pTBElement, drgui_font *pFont)
Sets the default font to use for tabs.
drgui_on_printable_key_down_proc
void(* drgui_on_printable_key_down_proc)(drgui_element *pElement, unsigned int character, int stateFlags)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:369
drgui_text_engine_set_horizontal_align
void drgui_text_engine_set_horizontal_align(drgui_text_engine *pTL, drgui_text_engine_alignment alignment)
Sets the horizontal alignment of the given text engine.
drgui_text_engine_set_vertical_align
void drgui_text_engine_set_vertical_align(drgui_text_engine *pTL, drgui_text_engine_alignment alignment)
Sets the vertical alignment of the given text engine.
drgui_text_engine_get_container_size
void drgui_text_engine_get_container_size(drgui_text_engine *pTL, float *pContainerWidthOut, float *pContainerHeightOut)
Retrieves the size of the container.
drgui_rect::top
float top
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:281
drgui_on_mouse_wheel_proc
void(* drgui_on_mouse_wheel_proc)(drgui_element *pElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:366
drgui_text_engine_leave_selection_mode
void drgui_text_engine_leave_selection_mode(drgui_text_engine *pTL)
drgui_sb_get_page_size
int drgui_sb_get_page_size(drgui_element *pSBElement)
Retrieves the page size of the given scrollbar's page.
drgui_tv_set_selected_background_color
void drgui_tv_set_selected_background_color(drgui_element *pTVElement, drgui_color color)
Sets the default background color of selected items.
drgui_tabbar_get_close_button_left_padding
float drgui_tabbar_get_close_button_left_padding(drgui_element *pTBElement)
drgui_font_metrics
Font metrics.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:248
drgui_text_engine_move_cursor_to_character
void drgui_text_engine_move_cursor_to_character(drgui_text_engine *pTL, size_t characterIndex)
Moves the cursor to the given character index.
drgui_sb_orientation
drgui_sb_orientation
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:10136
drgui_on_mouse_button_down_proc
void(* drgui_on_mouse_button_down_proc)(drgui_element *pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:363
drgui_is_parent
bool drgui_is_parent(drgui_element *pParentElement, drgui_element *pChildElement)
drgui_font_weight_default
@ drgui_font_weight_default
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:225
drgui_text_engine_get_inner_offset_x
float drgui_text_engine_get_inner_offset_x(drgui_text_engine *pTL)
Retrieves the inner offset of the given text engine on the x axis.
drgui_color::r
drgui_byte r
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:271
drgui_context::paintingCallbacks
drgui_painting_callbacks paintingCallbacks
The painting callbacks.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:772
drgui_element::onMouseButtonDblClick
drgui_on_mouse_button_dblclick_proc onMouseButtonDblClick
The function to call when a mouse button is double-clicked while over the element.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:723
drgui_tv_enable_multi_select
void drgui_tv_enable_multi_select(drgui_element *pTVElement)
drgui_textbox_on_printable_key_down
void drgui_textbox_on_printable_key_down(drgui_element *pTBElement, unsigned int utf32, int stateFlags)
on_printable_key_down.
drgui_textbox_get_horizontal_scrollbar
drgui_element * drgui_textbox_get_horizontal_scrollbar(drgui_element *pTBElement)
drgui_post_inbound_event_mouse_button_down
void drgui_post_inbound_event_mouse_button_down(drgui_element *pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags)
Posts a mouse button down inbound event.
drgui_tabbar_is_close_on_middle_click_enabled
bool drgui_tabbar_is_close_on_middle_click_enabled(drgui_element *pTBElement)
Determines whether or not close-on-middle-click is enabled.
drgui_cursor_size_ns
@ drgui_cursor_size_ns
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:203
dr2d_end_draw
void dr2d_end_draw(dr2d_surface *pSurface)
Marks the end of a paint operation.
drgui_context::dirtyElementCount
size_t dirtyElementCount
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:851
drgui_tvi_get_parent
drgui_tree_view_item * drgui_tvi_get_parent(drgui_tree_view_item *pItem)
Retrieves the parent tree-view item.
drgui_textbox_move_cursor_to_end_of_text
void drgui_textbox_move_cursor_to_end_of_text(drgui_element *pTBElement)
Moves the caret to the end of the text.
drgui_text_engine_get_selection_bg_color
drgui_color drgui_text_engine_get_selection_bg_color(drgui_text_engine *pTL)
Retrieves the background color of selected text.
drgui_font::flags
unsigned int flags
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:637
drgui_key_to_string
static size_t drgui_key_to_string(drgui_key key, char *strOut, size_t strOutSize)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:465
drgui_set_on_delete_element
void drgui_set_on_delete_element(drgui_context *pContext, drgui_on_delete_element_proc onDeleteElement)
Sets the function to call when an element is deleted.
drgui_element::extraDataSize
size_t extraDataSize
The size of the extra data.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:760
DRGUI_ARROW_UP
#define DRGUI_ARROW_UP
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:439
drgui_get_element_with_mouse_capture
drgui_element * drgui_get_element_with_mouse_capture(drgui_context *pContext)
Retrieves a pointer to the element with the mouse capture.
drgui_create_context_dr_2d
drgui_context * drgui_create_context_dr_2d(dr2d_context *pDrawingContext)
drgui_font_weight_bold
@ drgui_font_weight_bold
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:219
dr2d_font_metrics
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:98
drgui_text_engine_is_in_selection_mode
bool drgui_text_engine_is_in_selection_mode(drgui_text_engine *pTL)
Determines whether or not the given text engine is in selection mode.
drgui_painting_callbacks::drawRoundRect
drgui_draw_round_rect_proc drawRoundRect
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:583
drgui_text_engine_set_cursor_blink_rate
void drgui_text_engine_set_cursor_blink_rate(drgui_text_engine *pTL, unsigned int blinkRateInMilliseconds)
Sets the blink rate of the cursor in milliseconds.
drgui_delete_context
void drgui_delete_context(drgui_context *pContext)
Deletes a context and everything that it created.
drgui_tabbar_activate_prev_tab
void drgui_tabbar_activate_prev_tab(drgui_element *pTBElement)
drgui_text_engine_set_default_bg_color
void drgui_text_engine_set_default_bg_color(drgui_text_engine *pTL, drgui_color color)
Sets the default background color of the given text engine.
drgui_text_engine_move_selection_anchor_to_end_of_line
void drgui_text_engine_move_selection_anchor_to_end_of_line(drgui_text_engine *pTL, size_t iLine)
Moves the selection anchor to the end of the given line.
drgui_text_engine_get_cursor_column
size_t drgui_text_engine_get_cursor_column(drgui_text_engine *pTL)
Retrieves the index of the column the cursor is currently sitting on.
drgui_context::ppDirtyElements
drgui_element ** ppDirtyElements
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:845
drgui_tv_set_on_item_measure
void drgui_tv_set_on_item_measure(drgui_element *pTVElement, drgui_tvi_measure_proc proc)
Sets the function to call when a tree-view item needs to be measured.
drgui_tabbar_on_tab_mouse_button_up_proc
void(* drgui_tabbar_on_tab_mouse_button_up_proc)(drgui_element *pTBElement, drgui_tab *pTab, int mouseButton, int mouseRelativePosX, int mouseRelativePosY, int stateFlags)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:11148
drgui_text_engine_get_line_last_character
size_t drgui_text_engine_get_line_last_character(drgui_text_engine *pTL, size_t iLine)
Retrieves the index of the last character of the line at the given index.
DRGUI_F1
#define DRGUI_F1
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:443
drgui_map_image_data
void * drgui_map_image_data(drgui_image *pImage, unsigned int accessFlags)
drgui_tabbar_disable_close_on_middle_click
void drgui_tabbar_disable_close_on_middle_click(drgui_element *pTBElement)
Disables the on_close event on middle click.
dr2d_draw_rect_with_outline
void dr2d_draw_rect_with_outline(dr2d_surface *pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth, dr2d_color outlineColor)
Draws a filled rectangle with an outline.
DRGUI_ARROW_RIGHT
#define DRGUI_ARROW_RIGHT
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:440
drgui_tabbar_set_close_button_image
void drgui_tabbar_set_close_button_image(drgui_element *pTBElement, drgui_image *pImage)
Sets the image to use for close buttons.
drgui_on_move_proc
void(* drgui_on_move_proc)(drgui_element *pElement, float newRelativePosX, float newRelativePosY)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:358
drgui_create_image
drgui_image * drgui_create_image(drgui_context *pContext, unsigned int width, unsigned int height, drgui_image_format format, unsigned int stride, const void *pData)
drgui_font_weight_extra_light
@ drgui_font_weight_extra_light
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:214
drgui_textbox_disable_horizontal_scrollbar
void drgui_textbox_disable_horizontal_scrollbar(drgui_element *pTBElement)
Disables the horizontal scrollbar.
drgui_element::onMouseLeave
drgui_on_mouse_leave_proc onMouseLeave
The function to call when the mouse leaves the given element.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:711
drgui_create_font
drgui_font * drgui_create_font(drgui_context *pContext, const char *family, unsigned int size, drgui_font_weight weight, drgui_font_slant slant, float rotation, unsigned int flags)
Creates a font resource.
drgui_tabbar_orientation_right
@ drgui_tabbar_orientation_right
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:11138
drgui_textbox_find_and_replace_all
bool drgui_textbox_find_and_replace_all(drgui_element *pTBElement, const char *text, const char *replacement)
Finds every occurance of the given string and replaces it with another.
drgui_textbox_on_mouse_button_down
void drgui_textbox_on_mouse_button_down(drgui_element *pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
on_mouse_button_down.
drgui_text_engine_move_cursor_left
bool drgui_text_engine_move_cursor_left(drgui_text_engine *pTL)
Moves the cursor of the given text engine to the left by one character.
dr2d_get_clip
void dr2d_get_clip(dr2d_surface *pSurface, float *pLeftOut, float *pTopOut, float *pRightOut, float *pBottomOut)
Retrieves the clipping rectangle.
drgui_set_on_key_up
void drgui_set_on_key_up(drgui_element *pElement, drgui_on_key_up_proc callback)
Registers the on_key_up event callback.
drgui_get_optimal_image_format_proc
drgui_image_format(* drgui_get_optimal_image_format_proc)(void *pPaintingContext)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:406
drgui_set_on_capture_keyboard
void drgui_set_on_capture_keyboard(drgui_element *pElement, drgui_on_capture_keyboard_proc callback)
Registers the on_capture_keyboard event callback.
drgui_text_engine_get_active_line_bg_color
drgui_color drgui_text_engine_get_active_line_bg_color(drgui_text_engine *pTL)
Retrieves the background color of the line the cursor is sitting on.
drgui_tab_get_prev_tab
drgui_tab * drgui_tab_get_prev_tab(drgui_tab *pTab)
Retrieves a pointer to the previous tab in the tab bar.
drgui_set_global_on_capture_mouse
void drgui_set_global_on_capture_mouse(drgui_context *pContext, drgui_on_capture_mouse_proc onCaptureMouse)
drgui_tabbar_on_mouse_move
void drgui_tabbar_on_mouse_move(drgui_element *pTBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Called when the mouse move event needs to be processed for the given tab bar control.
drgui_text_engine_set_on_paint_text
void drgui_text_engine_set_on_paint_text(drgui_text_engine *pTL, drgui_text_engine_on_paint_text_proc proc)
Sets the function to call when a run of text needs to be painted for the given text engine.
drgui_key
unsigned int drgui_key
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:191
drgui_tvi_get_prev_sibling
drgui_tree_view_item * drgui_tvi_get_prev_sibling(drgui_tree_view_item *pItem)
Retrieves a pointer to the previous sibling of the given tree-view item.
dr2d_get_font_metrics
bool dr2d_get_font_metrics(dr2d_font *pFont, dr2d_font_metrics *pMetricsOut)
Retrieves the metrics of the given font.
drgui_get_type
const char * drgui_get_type(drgui_element *pElement)
Retrieves the type fo the element.
drgui_post_inbound_event_key_down
void drgui_post_inbound_event_key_down(drgui_context *pContext, drgui_key key, int stateFlags)
Posts a key down inbound event.
DRGUI_F12
#define DRGUI_F12
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:454
drgui_tv_get_first_selected_item
drgui_tree_view_item * drgui_tv_get_first_selected_item(drgui_element *pTVElement)
DRGUI_F8
#define DRGUI_F8
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:450
drgui_textbox_get_font
drgui_font * drgui_textbox_get_font(drgui_element *pTBElement)
Retrieves the font being used with the given text box.
drgui_clamp_rect
drgui_rect drgui_clamp_rect(drgui_rect rect, drgui_rect other)
Clamps the given rectangle to another.
drgui_painting_callbacks::deleteFont
drgui_delete_font_proc deleteFont
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:590
drgui_scale_rect
drgui_rect drgui_scale_rect(drgui_rect rect, float scaleX, float scaleY)
drgui_textbox_move_cursor_to_start_of_line_by_index
void drgui_textbox_move_cursor_to_start_of_line_by_index(drgui_element *pTBElement, size_t iLine)
Moves the caret to the beginning of the line at the given index.
drgui_painting_callbacks::drawEnd
drgui_draw_end_proc drawEnd
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:574
drgui_glyph_metrics::height
int height
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:260
drgui_get_optimal_image_format
drgui_image_format drgui_get_optimal_image_format(drgui_context *pContext)
Retrieves the optimal image format for the given context.
drgui_unmap_image_data
void drgui_unmap_image_data(drgui_image *pImage)
Unmaps the given image data.
drgui_tabbar_enable_close_on_middle_click
void drgui_tabbar_enable_close_on_middle_click(drgui_element *pTBElement)
Enables the on_close event on middle click.
drgui_tv_get_hovered_background_color
drgui_color drgui_tv_get_hovered_background_color(drgui_element *pTVElement)
Retrieves the default background color of hovered items.
drgui_tvi_on_paint_proc
void(* drgui_tvi_on_paint_proc)(drgui_element *pTVElement, drgui_tree_view_item *pItem, drgui_rect relativeClippingRect, drgui_color backgroundColor, float offsetX, float offsetY, float width, float height, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:15232
drgui_on_mouse_move_proc
void(* drgui_on_mouse_move_proc)(drgui_element *pElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:362
drgui_painting_callbacks::deleteImage
drgui_delete_image_proc deleteImage
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:599
dr2d_font_metrics::descent
int descent
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:101
drgui_draw_image_args::options
unsigned int options
Flags for controlling how the image should be drawn.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:351
drgui_element::onMouseButtonUp
drgui_on_mouse_button_up_proc onMouseButtonUp
The function to call when a mouse button is released while over the element.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:720
drgui_text_engine_is_cursor_at_end_of_selection
bool drgui_text_engine_is_cursor_at_end_of_selection(drgui_text_engine *pTL)
Determines whether or not the cursor is sitting at the end fo the selection.
DRGUI_F2
#define DRGUI_F2
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:444
drgui_draw_text
void drgui_draw_text(drgui_element *pElement, drgui_font *pFont, const char *text, int textLengthInBytes, float posX, float posY, drgui_color color, drgui_color backgroundColor, void *pPaintData)
drgui_unmap_image_data_proc
void(* drgui_unmap_image_data_proc)(drgui_resource image)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:409
drgui_text_engine_move_cursor_down
bool drgui_text_engine_move_cursor_down(drgui_text_engine *pTL)
Moves the cursor of the given text engine down one line.
drgui_set_clip_proc
void(* drgui_set_clip_proc)(drgui_rect relativeRect, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:383
drgui_draw_image_args::srcWidth
float srcWidth
The source width.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:320
drgui_text_engine_move_selection_anchor_to_start_of_line
void drgui_text_engine_move_selection_anchor_to_start_of_line(drgui_text_engine *pTL, size_t iLine)
Moves the selection anchor to the start of the given line.
drgui_get_font_metrics_proc
bool(* drgui_get_font_metrics_proc)(drgui_resource font, drgui_font_metrics *pMetricsOut)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:398
drgui_get_extra_data
void * drgui_get_extra_data(drgui_element *pElement)
Retrieves a pointer to the extra data of the given element.
drgui_draw_round_rect
void drgui_draw_round_rect(drgui_element *pElement, drgui_rect relativeRect, drgui_color color, float radius, void *pPaintData)
Draws a rectangle with rounded corners on the given element.
dr2d_context
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:331
drgui_get_element_with_keyboard_capture
drgui_element * drgui_get_element_with_keyboard_capture(drgui_context *pContext)
Retrieves a pointer to the element with the keyboard capture.
drgui_draw_round_rect_outline
void drgui_draw_round_rect_outline(drgui_element *pElement, drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, void *pPaintData)
Draws the outline of a rectangle with rounded corners on the given element.
drgui_element::onMove
drgui_on_move_proc onMove
The function to call when the element's relative position moves.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:702
drgui_painting_callbacks::getFontSize
drgui_get_font_size_proc getFontSize
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:591
drgui_textbox_on_capture_keyboard
void drgui_textbox_on_capture_keyboard(drgui_element *pTBElement, drgui_element *pPrevCapturedElement)
on_capture_keyboard
drgui_textbox_on_mouse_move
void drgui_textbox_on_mouse_move(drgui_element *pTBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
on_mouse_move.
DRGUI_IMAGE_DRAW_BACKGROUND
#define DRGUI_IMAGE_DRAW_BACKGROUND
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:287
dr2d_get_optimal_image_format
dr2d_image_format dr2d_get_optimal_image_format(dr2d_context *pContext)
Retrieves the optimal image format for the given context. This depends on the backend.
drgui_text_engine_get_line_count
size_t drgui_text_engine_get_line_count(drgui_text_engine *pTL)
Retrieves the number of lines in the given text engine.
drgui_image_format_rgba8
@ drgui_image_format_rgba8
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:241
drgui_text_engine_get_cursor_rect
drgui_rect drgui_text_engine_get_cursor_rect(drgui_text_engine *pTL)
Retrieves the rectangle of the cursor, relative to the container.
DRGUI_F7
#define DRGUI_F7
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:449
drgui_on_size_fit_children_to_parent
void drgui_on_size_fit_children_to_parent(drgui_element *pElement, float newWidth, float newHeight)
An on_size event callback that resizes every child element to that of the parent.
drgui_text_engine_swap_selection_markers
void drgui_text_engine_swap_selection_markers(drgui_text_engine *pTL)
Swaps the position of the cursor based on the current selection.
DRGUI_IMAGE_ALIGN_CENTER
#define DRGUI_IMAGE_ALIGN_CENTER
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:291
drgui_delete_scrollbar
void drgui_delete_scrollbar(drgui_element *pSBElement)
Deletes the given scrollbar element.
drgui_draw_image_args::srcX
float srcX
The source offset on the x axis.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:314
drgui_tv_get_extra_data_size
size_t drgui_tv_get_extra_data_size(drgui_element *pTVElement)
Retrieves the size of the extra data associated with the given tree-view control.
drgui_tabbar_get_tab_background_color
drgui_color drgui_tabbar_get_tab_background_color(drgui_element *pTBElement)
dr_2d.h
drgui_context::onDeleteElement
drgui_on_delete_element_proc onDeleteElement
The function to call when an element is deleted.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:828
drgui_text_engine_get_undo_points_remaining_count
unsigned int drgui_text_engine_get_undo_points_remaining_count(drgui_text_engine *pTL)
Retrieves the number of undo points remaining in the stack.
dr2d_begin_draw
void dr2d_begin_draw(dr2d_surface *pSurface)
Marks the beginning of a paint operation.
drgui_painting_callbacks::mapImageData
drgui_map_image_data_proc mapImageData
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:602
dr2d_image_format_argb8
@ dr2d_image_format_argb8
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:147
drgui_show
void drgui_show(drgui_element *pElement)
Shows the given element.
drgui_text_run::pFont
drgui_font * pFont
The font.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5277
drgui_tv_get_extra_data
void * drgui_tv_get_extra_data(drgui_element *pTVElement)
Retrieves a pointer to the buffer containing the given tree-view's extra data.
dr2d_glyph_metrics::width
int width
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:108
drgui_textbox_insert_text_at_cursor
bool drgui_textbox_insert_text_at_cursor(drgui_element *pTBElement, const char *text)
drgui_text_engine_alignment_left
@ drgui_text_engine_alignment_left
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5260
drgui_context::pFirstDeadElement
drgui_element * pFirstDeadElement
A pointer to the first element that has been marked as dead. Elements marked as dead are stored as a ...
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:785
drgui_get_parent
drgui_element * drgui_get_parent(drgui_element *pChildElement)
drgui_on_hittest_proc
bool(* drgui_on_hittest_proc)(drgui_element *pElement, float relativePosX, float relativePosY)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:372
drgui_textbox_get_selected_text
size_t drgui_textbox_get_selected_text(drgui_element *pTBElement, char *textOut, size_t textOutLength)
DRGUI_ARROW_DOWN
#define DRGUI_ARROW_DOWN
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:441
drgui_set_on_release_keyboard
void drgui_set_on_release_keyboard(drgui_element *pElement, drgui_on_release_keyboard_proc callback)
Registers the on_release_keyboard event callback.
drgui_element::onMouseMove
drgui_on_mouse_move_proc onMouseMove
The function to call when the mouse is moved while over the element.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:714
drgui_draw_round_rect_outline_proc
void(* drgui_draw_round_rect_outline_proc)(drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:390
drgui_on_mouse_button_dblclick_proc
void(* drgui_on_mouse_button_dblclick_proc)(drgui_element *pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:365
drgui_create_context
drgui_context * drgui_create_context()
Creates a context.
drgui_prepend_sibling
void drgui_prepend_sibling(drgui_element *pElementToPrepend, drgui_element *pElementToPrependTo)
Prepends the given element to the given sibling.
drgui_text_engine_select_all
void drgui_text_engine_select_all(drgui_text_engine *pTL)
Selects everything in the given text engine.
drgui_text_engine_get_line_character_range
void drgui_text_engine_get_line_character_range(drgui_text_engine *pTL, size_t iLine, size_t *pCharStartOut, size_t *pCharEndOut)
Retrieves teh index of the first and last character of the line at the given index.
drgui_draw_image_args
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:298
drgui_tvi_get_first_child
drgui_tree_view_item * drgui_tvi_get_first_child(drgui_tree_view_item *pItem)
Retrieves a pointer to the first child of the given tree-view item.
drgui_font::pContext
drgui_context * pContext
A pointer to the context that owns this font.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:618
drgui_tvi_expand
void drgui_tvi_expand(drgui_tree_view_item *pItem)
Expands the given item.
drgui_capture_keyboard
void drgui_capture_keyboard(drgui_element *pElement)
DRGUI_F4
#define DRGUI_F4
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:446
drgui_rect_has_volume
bool drgui_rect_has_volume(drgui_rect rect)
Determines whether or not the given rectangle has any volume (width and height > 0).
drgui_draw_round_rect_proc
void(* drgui_draw_round_rect_proc)(drgui_rect relativeRect, drgui_color color, float radius, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:389
drgui_rect::left
float left
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:280
DRGUI_MOUSE_BUTTON_MIDDLE
#define DRGUI_MOUSE_BUTTON_MIDDLE
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:428
drgui_font_weight_medium
@ drgui_font_weight_medium
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:212
drgui_text_engine_get_selection_last_line
size_t drgui_text_engine_get_selection_last_line(drgui_text_engine *pTL)
Retrieves the index of the last line of the current selection.
drgui_tv_get_child_offset_x
float drgui_tv_get_child_offset_x(drgui_element *pTVElement)
Retrieves the amount of indentation to apply to each child item in the given tree-view.
drgui_glyph_metrics::width
int width
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:259
drgui_text_engine_move_cursor_to_point
void drgui_text_engine_move_cursor_to_point(drgui_text_engine *pTL, float posX, float posY)
Moves the cursor to the closest character based on the given input position.
drgui_textbox_find_and_replace_next
bool drgui_textbox_find_and_replace_next(drgui_element *pTBElement, const char *text, const char *replacement)
Finds the next occurance of the given string and replaces it with another.
drgui_textbox_on_cursor_move_proc
void(* drgui_textbox_on_cursor_move_proc)(drgui_element *pTBElement)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:12994
drgui_textbox_set_on_cursor_move
void drgui_textbox_set_on_cursor_move(drgui_element *pTBElement, drgui_textbox_on_cursor_move_proc proc)
Sets the function to call when the cursor moves.
drgui_text_engine_get_cursor_blink_rate
unsigned int drgui_text_engine_get_cursor_blink_rate(drgui_text_engine *pTL)
Retrieves the blink rate of the cursor in milliseconds.
drgui_image::hResource
drgui_resource hResource
The resource handle that is passed around to the callback functions.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:612
drgui_delete_element
void drgui_delete_element(drgui_element *pElement)
Deletes and element.
drgui_is_clipping_enabled
bool drgui_is_clipping_enabled(const drgui_element *pElement)
Determines whether or not clipping is enabled for the given element.
dr2d_get_text_cursor_position_from_char
bool dr2d_get_text_cursor_position_from_char(dr2d_font *pFont, const char *text, size_t characterIndex, float *pTextCursorPosXOut)
Retrieves the position to palce a text cursor based on the character at the given index for the given...
drgui_sb_on_mouse_button_up
void drgui_sb_on_mouse_button_up(drgui_element *pSBElement, int button, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Called when the mouse button up event needs to be processed for the given scrollbar.
drgui_sb_set_page_size
void drgui_sb_set_page_size(drgui_element *pSBElement, int pageSize)
Sets the page size of the given scrollbar's page.
drgui_sb_on_mouse_leave
void drgui_sb_on_mouse_leave(drgui_element *pSBElement)
Called when the mouse leave event needs to be processed for the given scrollbar.
dr2d_map_image_data
void * dr2d_map_image_data(dr2d_image *pImage, unsigned int accessFlags)
drgui_text_engine_get_default_font
drgui_font * drgui_text_engine_get_default_font(drgui_text_engine *pTL)
Retrieves the default font to use for text runs.
drgui_set_cursor
void drgui_set_cursor(drgui_element *pElement, drgui_cursor_type cursor)
Sets the cursor to use when the mouse enters the given GUI element.
drgui_tabbar_set_tab_background_color
void drgui_tabbar_set_tab_background_color(drgui_element *pTBElement, drgui_color color)
drgui_callback
void(* drgui_callback)()
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:356
drgui_text_engine_set_active_line_bg_color
void drgui_text_engine_set_active_line_bg_color(drgui_text_engine *pTL, drgui_color color)
Sets the background color of the line the cursor is sitting on.
drgui_text_engine_delete_selected_text
bool drgui_text_engine_delete_selected_text(drgui_text_engine *pTL)
drgui_element::absolutePosY
float absolutePosY
The absolute position of the element on the y axis. A position of 0 is the top of the surface it is a...
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:682
drgui_create_tab_bar
drgui_element * drgui_create_tab_bar(drgui_context *pContext, drgui_element *pParent, drgui_tabbar_orientation orientation, size_t extraDataSize, const void *pExtraData)
Creates a new tab bar control.
drgui_painting_callbacks::getOptimalImageFormat
drgui_get_optimal_image_format_proc getOptimalImageFormat
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:600
drgui_set_global_on_release_keyboard
void drgui_set_global_on_release_keyboard(drgui_context *pContext, drgui_on_capture_keyboard_proc onReleaseKeyboard)
drgui_context::pElementWantingKeyboardCapture
drgui_element * pElementWantingKeyboardCapture
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:800
drgui_tabbar_on_mouse_button_up
void drgui_tabbar_on_mouse_button_up(drgui_element *pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Called when the mouse button up event needs to be processed for the given tab bar control.
drgui_text_engine_move_cursor_to_start_of_selection
void drgui_text_engine_move_cursor_to_start_of_selection(drgui_text_engine *pTL)
Moves the cursor to the start of the selected text.
drgui_set_global_on_release_mouse
void drgui_set_global_on_release_mouse(drgui_context *pContext, drgui_on_release_mouse_proc onReleaseMouse)
drgui_tvi_get_last_child
drgui_tree_view_item * drgui_tvi_get_last_child(drgui_tree_view_item *pItem)
Retrieves a pointer to the last child of the given tree-view item.
drgui_text_engine_move_cursor_to_start_of_line
bool drgui_text_engine_move_cursor_to_start_of_line(drgui_text_engine *pTL)
Moves the cursor of the given text engine to the start of the line.
drgui_sb_get_extra_data_size
size_t drgui_sb_get_extra_data_size(drgui_element *pSBElement)
Retrieves the size of the extra data associated with the scrollbar.
drgui_context::onChangeCursor
drgui_on_change_cursor_proc onChangeCursor
The global event handler to call when the system cursor needs to change.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:825
drgui_tv_on_mouse_leave
void drgui_tv_on_mouse_leave(drgui_element *pTVElement)
Called when the mouse leave event needs to be processed for the given tree-view control.
drgui_get_height
float drgui_get_height(const drgui_element *pElement)
DRGUI_F3
#define DRGUI_F3
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:445
dr2d_font
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:288
drgui_tv_is_multi_select_enabled
bool drgui_tv_is_multi_select_enabled(drgui_element *pTVElement)
Determines whether or not multi-select is enabled.
drgui_sb_get_mouse_wheel_scale
int drgui_sb_get_mouse_wheel_scale(drgui_element *pSBElement)
Retrieves the mouse wheel scale.
drgui_draw_rect_with_outline_proc
void(* drgui_draw_rect_with_outline_proc)(drgui_rect relativeRect, drgui_color color, float outlineWidth, drgui_color outlineColor, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:388
drgui_text_engine
struct drgui_text_engine drgui_text_engine
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5256
drgui_tabbar_get_extra_data_size
size_t drgui_tabbar_get_extra_data_size(drgui_element *pTBElement)
Retrieves the size of the extra data associated with the scrollbar.
drgui_font
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:615
drgui_text_engine_move_cursor_right
bool drgui_text_engine_move_cursor_right(drgui_text_engine *pTL)
Moves the cursor of the given text engine to the right by one character.
DRGUI_DELETE
#define DRGUI_DELETE
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:442
drgui_painting_callbacks::getFontMetrics
drgui_get_font_metrics_proc getFontMetrics
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:592
drgui_create_font_proc
drgui_resource(* drgui_create_font_proc)(void *pPaintingContext, const char *family, unsigned int size, drgui_font_weight weight, drgui_font_slant slant, float rotation, unsigned int flags)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:395
drgui_has_keyboard_capture
bool drgui_has_keyboard_capture(drgui_element *pElement)
Determines whether or not the given element has the keyboard capture.
DRGUI_F10
#define DRGUI_F10
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:452
dr2d_draw_image_args
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:159
drgui_create_text_engine
drgui_text_engine * drgui_create_text_engine(drgui_context *pContext, size_t extraDataSize, void *pExtraData)
Creates a new text engine object.
drgui_context::pElementUnderMouse
drgui_element * pElementUnderMouse
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:789
drgui_post_inbound_event_mouse_button_up
void drgui_post_inbound_event_mouse_button_up(drgui_element *pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags)
Posts a mouse button up inbound event.
drgui_tv_measure_item
bool drgui_tv_measure_item(drgui_element *pTVElement, drgui_tree_view_item *pItem, float *pWidthOut, float *pHeightOut)
Measures the given item.
DRGUI_END
#define DRGUI_END
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:436
drgui_sb_set_on_scroll
void drgui_sb_set_on_scroll(drgui_element *pSBElement, drgui_sb_on_scroll_proc onScroll)
Sets the function to call when the given scrollbar is scrolled.
drgui_detach
void drgui_detach(drgui_element *pChildElement)
Detaches the given element from it's parent.
drgui_on_capture_mouse_proc
void(* drgui_on_capture_mouse_proc)(drgui_element *pElement)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:373
DRGUI_ESCAPE
#define DRGUI_ESCAPE
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:433
drgui_rect::bottom
float bottom
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:283
drgui_tvi_is_expanded
bool drgui_tvi_is_expanded(drgui_tree_view_item *pItem)
Determines whether or not the given item is expanded.
drgui_element
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:644
drgui_tvi_prepend_sibling
void drgui_tvi_prepend_sibling(drgui_tree_view_item *pItemToPrepend, drgui_tree_view_item *pItemToPrependTo)
Prepends the given tree view item to the given sibling.
drgui_element::onDirty
drgui_on_dirty_proc onDirty
The function to call when the element is marked as dirty.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:741
args
drgui_tab_delete
void drgui_tab_delete(drgui_tab *pTab)
Recursively deletes a tree view item.
drgui_text_engine_step
void drgui_text_engine_step(drgui_text_engine *pTL, unsigned int milliseconds)
drgui_text_engine_move_cursor_to_end_of_line
bool drgui_text_engine_move_cursor_to_end_of_line(drgui_text_engine *pTL)
Moves the cursor of the given text engine to the end of the line.
drgui_delete_image
void drgui_delete_image(drgui_image *pImage)
Deletes the given image.
drgui_tab_get_next_tab
drgui_tab * drgui_tab_get_next_tab(drgui_tab *pTab)
Retrieves a pointer to the next tab in the tab bar.
drgui_textbox_set_font
void drgui_textbox_set_font(drgui_element *pTBElement, drgui_font *pFont)
Sets the font to use with the given text box.
drgui_dirty
void drgui_dirty(drgui_element *pElement, drgui_rect relativeRect)
drgui_context::currentCursor
drgui_cursor_type currentCursor
The current cursor.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:803
drgui_tvi_get_extra_data_size
size_t drgui_tvi_get_extra_data_size(drgui_tree_view_item *pItem)
Retrieves the size of the extra data associated with the given tree-view item.
drgui_context::pLastMouseMoveTopLevelElement
drgui_element * pLastMouseMoveTopLevelElement
A pointer to the top level element that was passed in from the last inbound mouse move event.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:837
drgui_element::onMouseButtonDown
drgui_on_mouse_button_down_proc onMouseButtonDown
The function to call when a mouse buttonis pressed while over the element.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:717
drgui_context::pPaintingContext
void * pPaintingContext
The paiting context.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:769
drgui_textbox_on_size
void drgui_textbox_on_size(drgui_element *pTBElement, float newWidth, float newHeight)
on_size.
drgui_element::cursor
drgui_cursor_type cursor
The cursor. Defaults to drge_cursor_default.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:692
drgui_element::onKeyDown
drgui_on_key_down_proc onKeyDown
The function to call when a key on the keyboard is pressed or auto-repeated.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:729
drgui_text_engine_move_cursor_to_end_of_selection
void drgui_text_engine_move_cursor_to_end_of_selection(drgui_text_engine *pTL)
Moves the cursor to the end of the selected text.
drgui_context
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:766
drgui_textbox_on_paint
void drgui_textbox_on_paint(drgui_element *pTBElement, drgui_rect relativeRect, void *pPaintData)
on_paint.
drgui_set_on_hittest
void drgui_set_on_hittest(drgui_element *pElement, drgui_on_hittest_proc callback)
Registers the on_hittest event callback.
drgui_text_engine_set_on_undo_point_changed
void drgui_text_engine_set_on_undo_point_changed(drgui_text_engine *pTL, drgui_text_engine_on_undo_point_changed_proc proc)
Sets the function to call when the content of the given text engine's current undo point has moved.
drgui_color::b
drgui_byte b
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:273
drgui_is_self_or_descendant
bool drgui_is_self_or_descendant(drgui_element *pChildElement, drgui_element *pAncestorElement)
Determines whether or not the given element is itself or a descendant.
drgui_context::outboundEventLockCounter
int outboundEventLockCounter
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:782
assert.h
drgui_tabbar_orientation
drgui_tabbar_orientation
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:11133
drgui_tabbar_orientation_bottom
@ drgui_tabbar_orientation_bottom
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:11136
drgui_release_mouse
void drgui_release_mouse(drgui_context *pContext)
Releases the mouse capture.
DRGUI_SHIFT
#define DRGUI_SHIFT
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:432
drgui_textbox_delete_character_to_right_of_cursor
bool drgui_textbox_delete_character_to_right_of_cursor(drgui_element *pTBElement)
drgui_painting_callbacks::drawRectOutline
drgui_draw_rect_outline_proc drawRectOutline
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:581
drgui_element::dirtyRect
drgui_rect dirtyRect
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:698
drgui_on_paint_proc
void(* drgui_on_paint_proc)(drgui_element *pElement, drgui_rect relativeRect, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:370
drgui_text_run::height
float height
The height of the run.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5296
drgui_set_on_mouse_wheel
void drgui_set_on_mouse_wheel(drgui_element *pElement, drgui_on_mouse_wheel_proc callback)
Registers the on_mouse_wheel event callback.
drgui_element::pPrevSibling
drgui_element * pPrevSibling
A pointer ot the previous sibing element.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:663
drgui_set_global_on_dirty
void drgui_set_global_on_dirty(drgui_context *pContext, drgui_on_dirty_proc onDirty)
drgui_tv_set_on_item_mouse_move
void drgui_tv_set_on_item_mouse_move(drgui_element *pTVElement, drgui_tvi_on_mouse_move_proc proc)
Sets the function to call when the mouse is moved while over a tree-view item.
drgui_font_weight_light
@ drgui_font_weight_light
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:215
drgui_text_engine_move_cursor_to_end_of_text
bool drgui_text_engine_move_cursor_to_end_of_text(drgui_text_engine *pTL)
Moves the cursor of the given text engine to the end of the text.
drgui_rect::right
float right
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:282
drgui_on_release_mouse_proc
void(* drgui_on_release_mouse_proc)(drgui_element *pElement)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:374
drgui_text_engine_set_on_dirty
void drgui_text_engine_set_on_dirty(drgui_text_engine *pTL, drgui_text_engine_on_dirty_proc proc)
Sets the function to call when a region of the text engine needs to be redrawn.
drgui_draw_image_proc
void(* drgui_draw_image_proc)(drgui_resource image, drgui_draw_image_args *pArgs, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:393
drgui_textbox_get_padding_horz
float drgui_textbox_get_padding_horz(drgui_element *pTBElement)
Retrieves the amound of horizontal padding to apply to the given text box.
drgui_tabbar_get_prev_tab
drgui_tab * drgui_tabbar_get_prev_tab(drgui_element *pTBElement, drgui_tab *pTab)
drgui_glyph_metrics::advanceY
int advanceY
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:264
drgui_element::onMouseWheel
drgui_on_mouse_wheel_proc onMouseWheel
The function to call when the mouse wheel it turned while over the element.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:726
dr2d_draw_round_rect_with_outline
void dr2d_draw_round_rect_with_outline(dr2d_surface *pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth, dr2d_color outlineColor)
Draws a filled rectangle with an outline.
drgui_on_log
void(* drgui_on_log)(drgui_context *pContext, const char *message)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:379
drgui_textbox_on_key_up
void drgui_textbox_on_key_up(drgui_element *pTBElement, drgui_key key, int stateFlags)
on_key_up.
drgui_painting_callbacks::unmapImageData
drgui_unmap_image_data_proc unmapImageData
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:603
drgui_text_engine_get_default_bg_color
drgui_color drgui_text_engine_get_default_bg_color(drgui_text_engine *pTL)
Retrieves the default background color of the given text engine.
drgui_tabbar_get_font
drgui_font * drgui_tabbar_get_font(drgui_element *pTBElement)
Retrieves the default font to use for tabs.
drgui_set_on_mouse_button_down
void drgui_set_on_mouse_button_down(drgui_element *pElement, drgui_on_mouse_button_down_proc callback)
Registers the on_mouse_button_down event callback.
drgui_textbox_undo
bool drgui_textbox_undo(drgui_element *pTBElement)
Performs an undo operation.
drgui_tv_get_vertical_scrollbar
drgui_element * drgui_tv_get_vertical_scrollbar(drgui_element *pTVElement)
Retrieves a pointer to the vertical scrollbar.
dr2d_image_format_rgba8
@ dr2d_image_format_rgba8
Definition: porcupine/demo/c/dr_libs/old/dr_2d.h:145
drgui_text_engine_get_selection_first_line
size_t drgui_text_engine_get_selection_first_line(drgui_text_engine *pTL)
Retrieves the index of the first line of the current selection.
drgui_delete_textbox
void drgui_delete_textbox(drgui_element *pTBElement)
Deletest the given text box control.
drgui_on_change_cursor_proc
void(* drgui_on_change_cursor_proc)(drgui_element *pElement, drgui_cursor_type cursor)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:377
DRGUI_F6
#define DRGUI_F6
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:448
drgui_tab_get_tab_bar_element
drgui_element * drgui_tab_get_tab_bar_element(drgui_tab *pTab)
Retrieves the tab bar GUI element that owns the given item.
dr2d_draw_image
void dr2d_draw_image(dr2d_surface *pSurface, dr2d_image *pImage, dr2d_draw_image_args *pArgs)
Draws an image.
drgui_element::pNextSibling
drgui_element * pNextSibling
A pointer to the next sibling element.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:660
drgui_enable_clipping
void drgui_enable_clipping(drgui_element *pElement)
Enables clipping against the parent for the given element.
drgui_textbox_set_border_width
void drgui_textbox_set_border_width(drgui_element *pTBElement, float borderWidth)
Sets the border width of the given text box.
drgui_text_engine_on_dirty_proc
void(* drgui_text_engine_on_dirty_proc)(drgui_text_engine *pTL, drgui_rect rect)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5316
drgui_element::pNextDeadElement
drgui_element * pNextDeadElement
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:670
drgui_make_inside_out_rect
drgui_rect drgui_make_inside_out_rect()
drgui_text_engine_prepare_undo_point
bool drgui_text_engine_prepare_undo_point(drgui_text_engine *pTL)
drgui_text_engine_set_selection_bg_color
void drgui_text_engine_set_selection_bg_color(drgui_text_engine *pTL, drgui_color color)
Sets the background color of selected text.
drgui_grow_rect
drgui_rect drgui_grow_rect(drgui_rect rect, float amount)
DRGUI_KEY_STATE_SHIFT_DOWN
#define DRGUI_KEY_STATE_SHIFT_DOWN
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:420
drgui_tabbar_set_on_tab_closed
void drgui_tabbar_set_on_tab_closed(drgui_element *pTBElement, drgui_tabbar_on_tab_close_proc proc)
Sets the function to call when a tab is closed with the close button.
drgui_create_tree_view
drgui_element * drgui_create_tree_view(drgui_context *pContext, drgui_element *pParent, size_t extraDataSize, const void *pExtraData)
Creates a tree-view control.
drgui_tvi_on_mouse_leave_proc
void(* drgui_tvi_on_mouse_leave_proc)(drgui_tree_view_item *pItem)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:15231
drgui_rgba
drgui_color drgui_rgba(drgui_byte r, drgui_byte g, drgui_byte b, drgui_byte a)
Creates a color object from a set of RGBA color components.
dr2d_get_text_cursor_position_from_point
bool dr2d_get_text_cursor_position_from_point(dr2d_font *pFont, const char *text, size_t textSizeInBytes, float maxWidth, float inputPosX, float *pTextCursorPosXOut, size_t *pCharacterIndexOut)
Retrieves the position to place a text cursor based on the given point for the given string when draw...
drgui_font_weight_thin
@ drgui_font_weight_thin
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:213
drgui_text_engine_delete_text_range
bool drgui_text_engine_delete_text_range(drgui_text_engine *pTL, size_t iFirstCh, size_t iLastChPlus1)
drgui_tvi_prepend
void drgui_tvi_prepend(drgui_tree_view_item *pItem, drgui_tree_view_item *pParent)
Prepends a tree view item as a child of the given parent item.
drgui_glyph_metrics
Glyph metrics.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:257
drgui_resource
void * drgui_resource
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:193
drgui_font_weight_extra_bold
@ drgui_font_weight_extra_bold
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:220
drgui_textbox_get_text
size_t drgui_textbox_get_text(drgui_element *pTBElement, char *pTextOut, size_t textOutSize)
Retrieves the text of the given text box.
drgui_sb_set_hovered_thumb_color
void drgui_sb_set_hovered_thumb_color(drgui_element *pSBElement, drgui_color color)
Sets the hovered color of the thumb.
drgui_tv_on_mouse_move
void drgui_tv_on_mouse_move(drgui_element *pTVElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
Called when the mouse move event needs to be processed for the given tree-view control.
drgui_image_format_bgra8
@ drgui_image_format_bgra8
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:242
drgui_append
void drgui_append(drgui_element *pChildElement, drgui_element *pParentElement)
Attaches the given element as a child of the given parent element, and appends it to the end of the c...
drgui_draw_image_args::dstBoundsHeight
float dstBoundsHeight
The height of the destination's bounds.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:336
drgui_draw_rect
void drgui_draw_rect(drgui_element *pElement, drgui_rect relativeRect, drgui_color color, void *pPaintData)
Draws a rectangle on the given element.
drgui_is_visible
bool drgui_is_visible(const drgui_element *pElement)
drgui_is_self_or_ancestor
bool drgui_is_self_or_ancestor(drgui_element *pAncestorElement, drgui_element *pChildElement)
Determines whether or not the given element is itself or a descendant.
drgui_draw_round_rect_with_outline_proc
void(* drgui_draw_round_rect_with_outline_proc)(drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, drgui_color outlineColor, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:391
drgui_text_engine_move_cursor_y
bool drgui_text_engine_move_cursor_y(drgui_text_engine *pTL, int amount)
Moves the cursor up or down the given number of lines.
drgui_draw_image_args::dstX
float dstX
The destination position on the x axis. This is ignored if the DR2D_IMAGE_ALIGN_CENTER option is set.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:301
drgui_element::pFirstChild
drgui_element * pFirstChild
A pointer to the first child element.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:654
drgui_painting_callbacks::getTextCursorPositionFromPoint
drgui_get_text_cursor_position_from_point_proc getTextCursorPositionFromPoint
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:595
drgui_cursor_cross
@ drgui_cursor_cross
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:202
drgui_get_width
float drgui_get_width(const drgui_element *pElement)
drgui_text_engine_get_extra_data
void * drgui_text_engine_get_extra_data(drgui_text_engine *pTL)
Retrieves a pointer to the extra data associated with the given text engine.
drgui_color
Structure representing an RGBA color. Color components are specified in the range of 0 - 255.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:269
drgui_font::weight
drgui_font_weight weight
The font's weight.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:627
drgui_text_engine_redo
bool drgui_text_engine_redo(drgui_text_engine *pTL)
Performs a redo operation.
drgui_element::onCaptureMouse
drgui_on_capture_mouse_proc onCaptureMouse
The event handler to call when an element receives the mouse focus.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:747
drgui_text_run::iCharEnd
size_t iCharEnd
Index in the main text string of the character just past the last character in the run.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5309
drgui_get_relative_rect
drgui_rect drgui_get_relative_rect(const drgui_element *pElement)
Retrieves the relative rectangle for the given element.
drgui_element::pContext
drgui_context * pContext
A pointer to the context that owns this element. This should never be null for valid elements.
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:647
drgui_image_format_argb8
@ drgui_image_format_argb8
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:243
drgui_font_slant_none
@ drgui_font_slant_none
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:232
drgui_tvi_get_next_sibling
drgui_tree_view_item * drgui_tvi_get_next_sibling(drgui_tree_view_item *pItem)
Retrieves a pointer to the next sibling of the given tree-view item.
drgui_text_engine_on_paint_rect_proc
void(* drgui_text_engine_on_paint_rect_proc)(drgui_text_engine *pTL, drgui_rect rect, drgui_color color, drgui_element *pElement, void *pPaintData)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5314
drgui_create_textbox
drgui_element * drgui_create_textbox(drgui_context *pContext, drgui_element *pParent, size_t extraDataSize, const void *pExtraData)
Creates a new text box control.
drgui_text_engine_get_line_at_pos_y
size_t drgui_text_engine_get_line_at_pos_y(drgui_text_engine *pTL, float posY)
Finds the line under the given point on the y axis relative to the container.
drgui_set_on_mouse_enter
void drgui_set_on_mouse_enter(drgui_element *pElement, drgui_on_mouse_enter_proc callback)
Registers the on_mouse_enter event callback.
drgui_sb_set_default_thumb_color
void drgui_sb_set_default_thumb_color(drgui_element *pSBElement, drgui_color color)
Sets the default color of the thumb.
drgui_text_engine_alignment_bottom
@ drgui_text_engine_alignment_bottom
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:5264
drgui_painting_callbacks::setClip
drgui_set_clip_proc setClip
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:576
drgui_painting_callbacks::getClip
drgui_get_clip_proc getClip
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:577
drgui_tvi_measure_proc
void(* drgui_tvi_measure_proc)(drgui_tree_view_item *pItem, float *pWidthOut, float *pHeightOut)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:15233
drgui_get_glyph_metrics_proc
bool(* drgui_get_glyph_metrics_proc)(drgui_resource font, unsigned int utf32, drgui_glyph_metrics *pMetricsOut)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:399
dr2d_measure_string
bool dr2d_measure_string(dr2d_font *pFont, const char *text, size_t textSizeInBytes, float *pWidthOut, float *pHeightOut)
Retrieves the dimensions of the given string when drawn with the given font.
drgui_tabbar_paint_tab
void drgui_tabbar_paint_tab(drgui_element *pTBElement, drgui_tab *pTab, drgui_rect relativeClippingRect, float offsetX, float offsetY, float width, float height, void *pPaintData)
Paints the given tab.
drgui_get_relative_position
void drgui_get_relative_position(const drgui_element *pElement, float *relativePosXOut, float *relativePosYOut)
Retrieves the relative position of the given element.
drgui_post_inbound_event_mouse_leave
void drgui_post_inbound_event_mouse_leave(drgui_element *pTopLevelElement)
drgui_painting_callbacks::drawBegin
drgui_draw_begin_proc drawBegin
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:573
drgui_text_engine_set_on_cursor_move
void drgui_text_engine_set_on_cursor_move(drgui_text_engine *pTL, drgui_text_engine_on_cursor_move_proc proc)
Sets the function to call when the cursor in the given text engine is mvoed.
drgui_tabbar_resize_by_tabs
void drgui_tabbar_resize_by_tabs(drgui_element *pTBElement)
drgui_tabbar_on_tab_activated_proc
void(* drgui_tabbar_on_tab_activated_proc)(drgui_element *pTBElement, drgui_tab *pTab, drgui_tab *pOldActiveTab)
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:11145
drgui_begin_dirty
drgui_element * drgui_begin_dirty(drgui_element *pElement)
drgui_element::type
char type[64]
Definition: porcupine/demo/c/dr_libs/old/dr_gui.h:675


picovoice_driver
Author(s):
autogenerated on Fri Apr 1 2022 02:13:53