159 #ifndef DRGUI_NO_DR_2D
171 #ifndef DRGUI_MAX_FONT_FAMILY_LENGTH
172 #define DRGUI_MAX_FONT_FAMILY_LENGTH 128
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)
293 #define DRGUI_READ (1 << 0)
294 #define DRGUI_WRITE (1 << 1)
296 #define DRGUI_FONT_NO_CLEARTYPE (1 << 0)
408 typedef void* (* drgui_map_image_data_proc) (
drgui_resource image,
unsigned int accessFlags);
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.
426 #define DRGUI_MOUSE_BUTTON_LEFT 1
427 #define DRGUI_MOUSE_BUTTON_RIGHT 2
428 #define DRGUI_MOUSE_BUTTON_MIDDLE 3
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
458 if (
strcpy_s(dst, dstSize, src) == 0) {
467 if (strOut ==
NULL || strOutSize == 0) {
471 if (strOutSize == 1) {
505 if (key >= 32 && key <= 126) {
506 strOut[0] = (char)key;
517 if (str ==
NULL || str[0] ==
'\0') {
534 if (str[0] ==
'F' || str[0] ==
'f') {
536 if (str[2] ==
'\0') {
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;
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;
556 if (str[0] >= 32 && str[0] <= 126 && str[1] ==
'\0') {
1526 #ifndef DRGUI_NO_DR_2D
1556 #ifdef DR_GUI_IMPLEMENTATION
1564 #ifndef DRGUI_PRIVATE
1565 #define DRGUI_PRIVATE static
1576 #define IS_CONTEXT_DEAD (1U << 0)
1577 #define IS_AUTO_DIRTY_DISABLED (1U << 1)
1578 #define IS_RELEASING_KEYBOARD (1U << 2)
1581 #define IS_ELEMENT_HIDDEN (1U << 0)
1582 #define IS_ELEMENT_CLIPPING_DISABLED (1U << 1)
1583 #define IS_ELEMENT_DEAD (1U << 31)
1586 static int drgui__strcpy_s(
char* dst,
size_t dstSizeInBytes,
const char* src)
1589 return strcpy_s(dst, dstSizeInBytes, src);
1594 if (dstSizeInBytes == 0) {
1603 for (i = 0; i < dstSizeInBytes && src[i] !=
'\0'; ++i) {
1607 if (i < dstSizeInBytes) {
1617 int drgui__strncpy_s(
char* dst,
size_t dstSizeInBytes,
const char* src,
size_t count)
1625 if (dstSizeInBytes == 0) {
1633 size_t maxcount =
count;
1634 if (
count == ((
size_t)-1) ||
count >= dstSizeInBytes) {
1635 maxcount = dstSizeInBytes - 1;
1639 for (i = 0; i < maxcount && src[i] !=
'\0'; ++i) {
1643 if (src[i] ==
'\0' || i ==
count ||
count == ((
size_t)-1)) {
1677 bool drgui_is_handling_inbound_event(
const drgui_context* pContext);
1694 bool drgui_is_handling_outbound_event(
drgui_context* pContext);
1701 bool drgui_is_element_marked_as_dead(
const drgui_element* pElement);
1704 void drgui_delete_elements_marked_as_dead(
drgui_context* pContext);
1711 bool drgui_is_context_marked_as_dead(
const drgui_context* pContext);
1718 void drgui_delete_context_for_real(
drgui_context* pContext);
1724 void drgui_delete_element_for_real(
drgui_element* pElement);
1728 void drgui_detach_without_redraw(
drgui_element* pChildElement);
1772 void drgui_apply_offset_to_children_recursive(
drgui_element* pParentElement,
float offsetX,
float offsetY);
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);
1791 void drgui_post_outbound_event_printable_key_down(
drgui_element* pElement,
unsigned int character,
int stateFlags);
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);
1804 void drgui_log(
drgui_context* pContext,
const char* message);
1809 assert(pContext !=
NULL);
1816 assert(pContext !=
NULL);
1825 if (!drgui_is_handling_inbound_event(pContext))
1827 drgui_delete_elements_marked_as_dead(pContext);
1830 if (drgui_is_context_marked_as_dead(pContext))
1832 drgui_delete_context_for_real(pContext);
1837 bool drgui_is_handling_inbound_event(
const drgui_context* pContext)
1839 assert(pContext !=
NULL);
1848 assert(pElement !=
NULL);
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.");
1867 assert(pElement !=
NULL);
1874 bool drgui_is_handling_outbound_event(
drgui_context* pContext)
1876 assert(pContext !=
NULL);
1883 assert(pElement !=
NULL);
1886 pElement->
flags |= IS_ELEMENT_DEAD;
1896 bool drgui_is_element_marked_as_dead(
const drgui_element* pElement)
1898 if (pElement ==
NULL) {
1902 return (pElement->
flags & IS_ELEMENT_DEAD) != 0;
1905 void drgui_delete_elements_marked_as_dead(
drgui_context* pContext)
1907 assert(pContext !=
NULL);
1914 drgui_delete_element_for_real(pDeadElement);
1921 assert(pContext !=
NULL);
1922 assert(!drgui_is_context_marked_as_dead(pContext));
1924 pContext->
flags |= IS_CONTEXT_DEAD;
1927 bool drgui_is_context_marked_as_dead(
const drgui_context* pContext)
1929 assert(pContext !=
NULL);
1931 return (pContext->
flags & IS_CONTEXT_DEAD) != 0;
1938 assert(pContext !=
NULL);
1941 drgui_delete_elements_marked_as_dead(pContext);
1946 void drgui_delete_element_for_real(
drgui_element* pElementToDelete)
1948 assert(pElementToDelete !=
NULL);
1953 if (drgui_is_element_marked_as_dead(pElementToDelete)) {
1958 while (pPrevDeadElement !=
NULL) {
1966 if (pPrevDeadElement !=
NULL) {
1972 free(pElementToDelete);
2004 pChildElement->
pParent = pParentElement;
2021 drgui_append_without_detach_or_redraw(pChildElement, pParentElement);
2027 pChildElement->
pParent = pParentElement;
2044 drgui_prepend_without_detach_or_redraw(pChildElement, pParentElement);
2050 assert(pElementToAppend !=
NULL);
2051 assert(pElementToAppendTo !=
NULL);
2070 drgui_append_sibling_without_detach_or_redraw(pElementToAppend, pElementToAppendTo);
2076 assert(pElementToPrepend !=
NULL);
2077 assert(pElementToPrependTo !=
NULL);
2096 drgui_prepend_sibling_without_detach_or_redraw(pElementToPrepend, pElementToPrependTo);
2103 assert(pElement !=
NULL);
2113 assert(pElement !=
NULL);
2116 assert(pContext !=
NULL);
2125 assert(pElement !=
NULL);
2149 void drgui_apply_offset_to_children_recursive(
drgui_element* pParentElement,
float offsetX,
float offsetY)
2151 assert(pParentElement !=
NULL);
2155 drgui_begin_auto_dirty(pParentElement);
2158 pChild->absolutePosX += offsetX;
2159 pChild->absolutePosY += offsetY;
2161 drgui_apply_offset_to_children_recursive(pChild, offsetX, offsetY);
2163 drgui_end_auto_dirty(pParentElement);
2172 while (pOldAncestor !=
NULL)
2174 bool isOldElementUnderMouse = pNewElementUnderMouse == pOldAncestor ||
drgui_is_ancestor(pOldAncestor, pNewElementUnderMouse);
2175 if (!isOldElementUnderMouse)
2177 drgui_post_outbound_event_mouse_leave(pOldAncestor);
2180 pOldAncestor = pOldAncestor->
pParent;
2186 if (pNewElementUnderMouse ==
NULL) {
2192 drgui_post_on_mouse_enter_recursive(pContext, pNewElementUnderMouse->
pParent, pOldElementUnderMouse);
2195 bool wasNewElementUnderMouse = pOldElementUnderMouse == pNewElementUnderMouse ||
drgui_is_ancestor(pNewElementUnderMouse, pOldElementUnderMouse);
2196 if (!wasNewElementUnderMouse)
2198 drgui_post_outbound_event_mouse_enter(pNewElementUnderMouse);
2204 if (pContext ==
NULL) {
2209 if (pOldElementUnderMouse != pNewElementUnderMouse)
2217 if (pNewElementUnderMouse !=
NULL) {
2218 newCursor = pNewElementUnderMouse->
cursor;
2225 drgui__change_cursor(pNewElementUnderMouse, newCursor);
2232 drgui_post_on_mouse_leave_recursive(pContext, pNewElementUnderMouse, pOldElementUnderMouse);
2235 drgui_post_on_mouse_enter_recursive(pContext, pNewElementUnderMouse, pOldElementUnderMouse);
2241 void drgui_post_outbound_event_move(
drgui_element* pElement,
float newRelativePosX,
float newRelativePosY)
2243 if (drgui_begin_outbound_event(pElement))
2246 pElement->
onMove(pElement, newRelativePosX, newRelativePosY);
2249 drgui_end_outbound_event(pElement);
2253 void drgui_post_outbound_event_size(
drgui_element* pElement,
float newWidth,
float newHeight)
2255 if (drgui_begin_outbound_event(pElement))
2258 pElement->
onSize(pElement, newWidth, newHeight);
2261 drgui_end_outbound_event(pElement);
2265 void drgui_post_outbound_event_mouse_enter(
drgui_element* pElement)
2267 if (drgui_begin_outbound_event(pElement))
2273 drgui_end_outbound_event(pElement);
2277 void drgui_post_outbound_event_mouse_leave(
drgui_element* pElement)
2279 if (drgui_begin_outbound_event(pElement))
2285 drgui_end_outbound_event(pElement);
2289 void drgui_post_outbound_event_mouse_move(
drgui_element* pElement,
int relativeMousePosX,
int relativeMousePosY,
int stateFlags)
2291 if (drgui_begin_outbound_event(pElement))
2294 pElement->
onMouseMove(pElement, relativeMousePosX, relativeMousePosY, stateFlags);
2297 drgui_end_outbound_event(pElement);
2301 void drgui_post_outbound_event_mouse_button_down(
drgui_element* pElement,
int mouseButton,
int relativeMousePosX,
int relativeMousePosY,
int stateFlags)
2303 if (drgui_begin_outbound_event(pElement))
2306 pElement->
onMouseButtonDown(pElement, mouseButton, relativeMousePosX, relativeMousePosY, stateFlags);
2309 drgui_end_outbound_event(pElement);
2313 void drgui_post_outbound_event_mouse_button_up(
drgui_element* pElement,
int mouseButton,
int relativeMousePosX,
int relativeMousePosY,
int stateFlags)
2315 if (drgui_begin_outbound_event(pElement))
2318 pElement->
onMouseButtonUp(pElement, mouseButton, relativeMousePosX, relativeMousePosY, stateFlags);
2321 drgui_end_outbound_event(pElement);
2325 void drgui_post_outbound_event_mouse_button_dblclick(
drgui_element* pElement,
int mouseButton,
int relativeMousePosX,
int relativeMousePosY,
int stateFlags)
2327 if (drgui_begin_outbound_event(pElement))
2330 pElement->
onMouseButtonDblClick(pElement, mouseButton, relativeMousePosX, relativeMousePosY, stateFlags);
2333 drgui_end_outbound_event(pElement);
2337 void drgui_post_outbound_event_mouse_wheel(
drgui_element* pElement,
int delta,
int relativeMousePosX,
int relativeMousePosY,
int stateFlags)
2339 if (drgui_begin_outbound_event(pElement))
2342 pElement->
onMouseWheel(pElement, delta, relativeMousePosX, relativeMousePosY, stateFlags);
2345 drgui_end_outbound_event(pElement);
2351 if (drgui_begin_outbound_event(pElement))
2354 pElement->
onKeyDown(pElement, key, stateFlags);
2357 drgui_end_outbound_event(pElement);
2363 if (drgui_begin_outbound_event(pElement))
2366 pElement->
onKeyUp(pElement, key, stateFlags);
2369 drgui_end_outbound_event(pElement);
2373 void drgui_post_outbound_event_printable_key_down(
drgui_element* pElement,
unsigned int character,
int stateFlags)
2375 if (drgui_begin_outbound_event(pElement))
2381 drgui_end_outbound_event(pElement);
2388 if (pElement !=
NULL)
2391 pElement->
onDirty(pElement, relativeRect);
2406 void drgui_post_outbound_event_capture_mouse(
drgui_element* pElement)
2408 if (pElement !=
NULL)
2416 void drgui_post_outbound_event_capture_mouse_global(
drgui_element* pElement)
2426 void drgui_post_outbound_event_release_mouse(
drgui_element* pElement)
2428 if (pElement !=
NULL)
2436 void drgui_post_outbound_event_release_mouse_global(
drgui_element* pElement)
2449 if (pElement !=
NULL)
2469 if (pElement !=
NULL)
2488 void drgui_log(
drgui_context* pContext,
const char* message)
2490 if (pContext !=
NULL)
2492 if (pContext->
onLog) {
2493 pContext->
onLog(pContext, message);
2508 if (pContext !=
NULL) {
2517 if (pContext ==
NULL) {
2525 drgui_log(pContext,
"WARNING: Deleting the GUI context while an element still has the mouse capture.");
2532 drgui_log(pContext,
"WARNING: Deleting the GUI context while an element still has the keyboard capture.");
2537 if (drgui_is_handling_inbound_event(pContext))
2542 drgui_mark_context_as_dead(pContext);
2547 drgui_delete_context_for_real(pContext);
2558 if (pTopLevelElement ==
NULL) {
2563 if (pContext ==
NULL) {
2567 drgui_begin_inbound_event(pContext);
2570 drgui_update_mouse_enter_and_leave_state(pContext,
NULL);
2572 drgui_end_inbound_event(pContext);
2582 drgui_begin_inbound_event(pTopLevelElement->
pContext);
2597 drgui_update_mouse_enter_and_leave_state(pTopLevelElement->
pContext, pNewElementUnderMouse);
2601 if (pEventReceiver ==
NULL)
2603 pEventReceiver = pNewElementUnderMouse;
2606 if (pEventReceiver !=
NULL)
2608 float relativeMousePosX = (float)mousePosX;
2609 float relativeMousePosY = (float)mousePosY;
2612 drgui_post_outbound_event_mouse_move(pEventReceiver, (
int)relativeMousePosX, (
int)relativeMousePosY, stateFlags);
2615 drgui_end_inbound_event(pTopLevelElement->
pContext);
2625 drgui_begin_inbound_event(pContext);
2628 if (pEventReceiver ==
NULL)
2632 if (pEventReceiver ==
NULL)
2640 if (pEventReceiver !=
NULL)
2642 float relativeMousePosX = (float)mousePosX;
2643 float relativeMousePosY = (float)mousePosY;
2646 drgui_post_outbound_event_mouse_button_down(pEventReceiver, mouseButton, (
int)relativeMousePosX, (
int)relativeMousePosY, stateFlags);
2649 drgui_end_inbound_event(pContext);
2659 drgui_begin_inbound_event(pContext);
2662 if (pEventReceiver ==
NULL)
2666 if (pEventReceiver ==
NULL)
2674 if (pEventReceiver !=
NULL)
2676 float relativeMousePosX = (float)mousePosX;
2677 float relativeMousePosY = (float)mousePosY;
2680 drgui_post_outbound_event_mouse_button_up(pEventReceiver, mouseButton, (
int)relativeMousePosX, (
int)relativeMousePosY, stateFlags);
2683 drgui_end_inbound_event(pContext);
2693 drgui_begin_inbound_event(pContext);
2696 if (pEventReceiver ==
NULL)
2700 if (pEventReceiver ==
NULL)
2708 if (pEventReceiver !=
NULL)
2710 float relativeMousePosX = (float)mousePosX;
2711 float relativeMousePosY = (float)mousePosY;
2714 drgui_post_outbound_event_mouse_button_dblclick(pEventReceiver, mouseButton, (
int)relativeMousePosX, (
int)relativeMousePosY, stateFlags);
2717 drgui_end_inbound_event(pContext);
2727 drgui_begin_inbound_event(pContext);
2730 if (pEventReceiver ==
NULL)
2734 if (pEventReceiver ==
NULL)
2742 if (pEventReceiver !=
NULL)
2744 float relativeMousePosX = (float)mousePosX;
2745 float relativeMousePosY = (float)mousePosY;
2748 drgui_post_outbound_event_mouse_wheel(pEventReceiver, delta, (
int)relativeMousePosX, (
int)relativeMousePosY, stateFlags);
2751 drgui_end_inbound_event(pContext);
2756 if (pContext ==
NULL) {
2760 drgui_begin_inbound_event(pContext);
2766 drgui_end_inbound_event(pContext);
2771 if (pContext ==
NULL) {
2775 drgui_begin_inbound_event(pContext);
2781 drgui_end_inbound_event(pContext);
2786 if (pContext ==
NULL) {
2790 drgui_begin_inbound_event(pContext);
2796 drgui_end_inbound_event(pContext);
2803 if (pContext !=
NULL) {
2810 if (pContext !=
NULL) {
2817 if (pContext !=
NULL) {
2824 if (pContext !=
NULL) {
2831 if (pContext !=
NULL) {
2838 if (pContext !=
NULL) {
2845 if (pContext !=
NULL) {
2852 if (pContext !=
NULL) {
2853 pContext->
onLog = onLog;
2864 if (pContext !=
NULL)
2867 if (pElement !=
NULL) {
2874 if (pExtraData !=
NULL) {
2875 memcpy(pElement->
pExtraData, pExtraData, extraDataSize);
2879 drgui_append_without_detach_or_redraw(pElement, pElement->
pParent);
2883 if (pParent !=
NULL) {
2898 if (pElement ==
NULL) {
2903 if (pContext ==
NULL) {
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.");
2912 drgui_mark_element_as_dead(pElement);
2923 bool needsMouseUpdate =
false;
2927 needsMouseUpdate =
true;
2935 needsMouseUpdate =
false;
2942 drgui_log(pContext,
"WARNING: Deleting an element while it still has the mouse capture.");
2949 drgui_log(pContext,
"WARNING: Deleting an element while it still has the keyboard capture.");
2954 for (
size_t iDirtyElement = 0; iDirtyElement < pContext->
dirtyElementCount; ++iDirtyElement) {
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) {
2967 if (pContext->pDirtyTopLevelElement == pElement)
2969 drgui_log(pContext,
"WARNING: Deleting an element while it is being marked as dirty.");
2970 pContext->pDirtyTopLevelElement =
NULL;
2977 if (needsMouseUpdate)
2994 drgui_detach_without_redraw(pElement);
3010 if (!drgui_is_handling_inbound_event(pContext)) {
3011 drgui_delete_element_for_real(pElement);
3018 if (pElement !=
NULL) {
3027 if (pElement !=
NULL) {
3037 if (pElement ==
NULL) {
3041 return drgui__strcpy_s(pElement->
type,
sizeof(pElement->
type), (type ==
NULL) ?
"" : type) == 0;
3046 if (pElement ==
NULL) {
3050 return pElement->
type;
3055 if (pElement ==
NULL || type ==
NULL) {
3059 return strncmp(pElement->
type, type, strlen(type)) == 0;
3065 if (pElement !=
NULL) {
3066 pElement->
flags |= IS_ELEMENT_HIDDEN;
3073 if (pElement !=
NULL) {
3074 pElement->
flags &= ~IS_ELEMENT_HIDDEN;
3081 if (pElement !=
NULL) {
3082 return (pElement->
flags & IS_ELEMENT_HIDDEN) == 0;
3105 if (pElement !=
NULL) {
3106 pElement->
flags |= IS_ELEMENT_CLIPPING_DISABLED;
3112 if (pElement !=
NULL) {
3113 pElement->
flags &= ~IS_ELEMENT_CLIPPING_DISABLED;
3119 if (pElement !=
NULL) {
3120 return (pElement->
flags & IS_ELEMENT_CLIPPING_DISABLED) == 0;
3130 if (pElement ==
NULL) {
3151 drgui_post_outbound_event_capture_mouse(pElement);
3154 drgui_post_outbound_event_capture_mouse_global(pElement);
3161 if (pContext ==
NULL) {
3182 if (pContext ==
NULL) {
3194 if (pContext ==
NULL) {
3203 if (pElement ==
NULL) {
3213 assert(pContext !=
NULL);
3220 pContext->
flags |= IS_RELEASING_KEYBOARD;
3225 if (!drgui_is_element_marked_as_dead(pPrevCapturedElement)) {
3226 drgui_post_outbound_event_release_keyboard(pPrevCapturedElement, pNewCapturedElement);
3227 drgui_post_outbound_event_release_keyboard_global(pPrevCapturedElement, pNewCapturedElement);
3230 pContext->
flags &= ~IS_RELEASING_KEYBOARD;
3239 if (pElement ==
NULL) {
3248 if ((pElement->
pContext->
flags & IS_RELEASING_KEYBOARD) != 0) {
3258 if (pPrevElementWithKeyboardCapture !=
NULL) {
3259 drgui_release_keyboard_private(pElement->
pContext, pElement);
3271 drgui_post_outbound_event_capture_keyboard(pElement, pPrevElementWithKeyboardCapture);
3274 drgui_post_outbound_event_capture_keyboard_global(pElement, pPrevElementWithKeyboardCapture);
3281 if (pContext ==
NULL) {
3285 drgui_release_keyboard_private(pContext,
NULL);
3290 if (pContext ==
NULL) {
3302 if (pContext ==
NULL) {
3311 if (pElement ==
NULL) {
3321 if (pElement ==
NULL) {
3325 pElement->
cursor = cursor;
3328 drgui__change_cursor(pElement, cursor);
3334 if (pElement ==
NULL) {
3347 if (pElement !=
NULL) {
3348 pElement->
onMove = callback;
3354 if (pElement !=
NULL) {
3355 pElement->
onSize = callback;
3361 if (pElement !=
NULL) {
3368 if (pElement !=
NULL) {
3375 if (pElement !=
NULL) {
3382 if (pElement !=
NULL) {
3389 if (pElement !=
NULL) {
3396 if (pElement !=
NULL) {
3403 if (pElement !=
NULL) {
3410 if (pElement !=
NULL) {
3417 if (pElement !=
NULL) {
3424 if (pElement !=
NULL) {
3431 if (pElement !=
NULL) {
3438 if (pElement !=
NULL) {
3445 if (pElement !=
NULL) {
3452 if (pElement !=
NULL) {
3459 if (pElement !=
NULL) {
3466 if (pElement !=
NULL) {
3473 if (pElement !=
NULL) {
3482 if (absolutePosX < pElement->absolutePosX ||
3483 absolutePosX < pElement->absolutePosY)
3521 }drgui_find_element_under_point_data;
3523 bool drgui_find_element_under_point_iterator(
drgui_element* pElement,
drgui_rect* pRelativeVisibleRect,
void* pUserData)
3525 assert(pElement !=
NULL);
3526 assert(pRelativeVisibleRect !=
NULL);
3528 drgui_find_element_under_point_data* pData = (drgui_find_element_under_point_data*)pUserData;
3529 assert(pData !=
NULL);
3531 float relativePosX = pData->absolutePosX;
3532 float relativePosY = pData->absolutePosY;
3538 if (pElement->
onHitTest(pElement, relativePosX, relativePosY)) {
3539 pData->pElementUnderPoint = pElement;
3542 pData->pElementUnderPoint = pElement;
3553 if (pTopLevelElement ==
NULL) {
3557 drgui_find_element_under_point_data data;
3558 data.pElementUnderPoint =
NULL;
3559 data.absolutePosX = absolutePosX;
3560 data.absolutePosY = absolutePosY;
3563 return data.pElementUnderPoint;
3568 if (pElement ==
NULL) {
3581 if (pChildElement ==
NULL) {
3585 return pChildElement->
pParent;
3590 if (pChildElement ==
NULL) {
3598 drgui_detach_without_redraw(pChildElement);
3601 if (pOldParent !=
NULL) {
3608 if (pChildElement ==
NULL) {
3615 if (pChildElement->
pParent != pParentElement) {
3618 drgui_detach_without_redraw(pChildElement);
3623 if (pParentElement !=
NULL) {
3624 drgui_append_without_detach(pChildElement, pParentElement);
3630 if (pChildElement ==
NULL) {
3635 if (pChildElement->
pParent != pParentElement) {
3638 drgui_detach_without_redraw(pChildElement);
3643 if (pParentElement !=
NULL) {
3644 drgui_prepend_without_detach(pChildElement, pParentElement);
3650 if (pElementToAppend ==
NULL || pElementToAppendTo ==
NULL) {
3658 drgui_detach_without_redraw(pElementToAppend);
3663 drgui_append_sibling_without_detach(pElementToAppend, pElementToAppendTo);
3668 if (pElementToPrepend ==
NULL || pElementToPrependTo ==
NULL) {
3673 if (pElementToPrepend->
pParent != pElementToPrependTo->
pParent) {
3676 drgui_detach_without_redraw(pElementToPrepend);
3681 drgui_prepend_sibling_without_detach(pElementToPrepend, pElementToPrependTo);
3686 if (pElement ==
NULL) {
3699 if (pParentElement ==
NULL || pChildElement ==
NULL) {
3703 return pParentElement == pChildElement->
pParent;
3713 if (pAncestorElement ==
NULL || pChildElement ==
NULL) {
3718 while (pParent !=
NULL)
3720 if (pParent == pAncestorElement) {
3738 return pAncestorElement == pChildElement ||
drgui_is_ancestor(pAncestorElement, pChildElement);
3743 return pChildElement == pAncestorElement ||
drgui_is_descendant(pChildElement, pAncestorElement);
3752 if (pElement !=
NULL)
3759 drgui_begin_auto_dirty(pElement);
3774 if (newRelativePosX != oldRelativePosX || newRelativePosY != oldRelativePosY) {
3775 drgui_post_outbound_event_move(pElement, newRelativePosX, newRelativePosY);
3779 drgui_apply_offset_to_children_recursive(pElement, offsetX, offsetY);
3781 drgui_end_auto_dirty(pElement);
3788 if (pElement !=
NULL)
3790 if (positionXOut !=
NULL) {
3794 if (positionYOut !=
NULL) {
3802 if (pElement !=
NULL) {
3811 if (pElement !=
NULL) {
3821 if (pElement !=
NULL) {
3832 if (pElement !=
NULL)
3836 if (positionXOut !=
NULL) {
3840 if (positionYOut !=
NULL) {
3846 if (positionXOut !=
NULL) {
3850 if (positionYOut !=
NULL) {
3859 if (pElement !=
NULL) {
3872 if (pElement !=
NULL) {
3886 if (pElement !=
NULL)
3888 if (pElement->
width != width || pElement->
height != height)
3890 drgui_begin_auto_dirty(pElement);
3894 pElement->
width = width;
3895 pElement->
height = height;
3898 drgui_post_outbound_event_size(pElement, width, height);
3900 drgui_end_auto_dirty(pElement);
3907 if (pElement !=
NULL) {
3908 if (widthOut) *widthOut = pElement->
width;
3909 if (heightOut) *heightOut = pElement->
height;
3911 if (widthOut) *widthOut = 0;
3912 if (heightOut) *heightOut = 0;
3918 if (pElement !=
NULL) {
3919 return pElement->
width;
3927 if (pElement !=
NULL) {
3938 if (pElement !=
NULL)
3959 if (pElement !=
NULL)
3983 if (pElement !=
NULL)
4003 if (pContext ==
NULL) {
4022 if (pParentElement ==
NULL) {
4026 if (callback ==
NULL) {
4035 drgui_rect clampedRelativeRect = relativeRect;
4039 if (!callback(pParentElement, &clampedRelativeRect, pUserData)) {
4051 childRect = clampedRelativeRect;
4053 childRect = relativeRect;
4057 childRect.
left -= childRelativePosX;
4058 childRect.
top -= childRelativePosY;
4059 childRect.
right -= childRelativePosX;
4060 childRect.
bottom -= childRelativePosY;
4073 if (pContext !=
NULL) {
4074 pContext->
flags |= IS_AUTO_DIRTY_DISABLED;
4080 if (pContext !=
NULL) {
4081 pContext->
flags &= ~IS_AUTO_DIRTY_DISABLED;
4087 if (pContext !=
NULL) {
4088 return (pContext->
flags & IS_AUTO_DIRTY_DISABLED) == 0;
4097 if (pElement ==
NULL) {
4102 assert(pContext !=
NULL);
4105 assert(pTopLevelElement !=
NULL);
4108 bool isAlreadyDirty =
false;
4109 for (
size_t iDirtyElementCount = 0; iDirtyElementCount < pContext->
dirtyElementCount; ++iDirtyElementCount) {
4110 if (pContext->
ppDirtyElements[iDirtyElementCount] == pTopLevelElement) {
4111 isAlreadyDirty =
true;
4116 if (!isAlreadyDirty) {
4120 if (ppNewDirtyElements ==
NULL) {
4134 return pTopLevelElement;
4139 if (pElement ==
NULL) {
4144 assert(pContext !=
NULL);
4163 if (pElement ==
NULL) {
4171 if (pTopLevelElement ==
NULL) {
4182 assert(pElement !=
NULL);
4183 assert(pRelativeRect !=
NULL);
4191 pElement->
onPaint(pElement, *pRelativeRect, pUserData);
4202 if (pElement ==
NULL) {
4207 if (pContext ==
NULL) {
4241 if (relativeRect.
right < relativeRect.
left) {
4245 if (relativeRect.
bottom < relativeRect.
top) {
4257 if (pElement ==
NULL) {
4271 if (pElement ==
NULL) {
4285 if (pElement ==
NULL) {
4299 if (pElement ==
NULL) {
4313 if (pElement ==
NULL) {
4327 if (pElement ==
NULL) {
4341 if (pElement ==
NULL || pFont ==
NULL) {
4347 float absolutePosX = posX;
4348 float absolutePosY = posY;
4356 if (pElement ==
NULL || pImage ==
NULL || pArgs ==
NULL) {
4374 bool restoreClip =
false;
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;
4400 if (boundsLeft < imageLeft) {
4405 if (boundsRight > imageRight) {
4410 if (boundsTop < imageTop) {
4415 if (boundsBottom > imageBottom) {
4430 if (pContext ==
NULL) {
4440 if (internalFont ==
NULL) {
4445 if (pFont ==
NULL) {
4453 pFont->
slant = slant;
4455 pFont->
flags = flags;
4458 if (family !=
NULL) {
4459 drgui__strcpy_s(pFont->
family,
sizeof(pFont->
family), family);
4467 if (pFont ==
NULL) {
4483 if (pFont ==
NULL || pMetricsOut ==
NULL) {
4498 if (pFont ==
NULL || pMetricsOut ==
NULL) {
4513 if (pFont ==
NULL) {
4517 if (text ==
NULL || textLengthInBytes == 0)
4547 if (pFont ==
NULL) {
4562 if (pFont ==
NULL) {
4579 if (pContext ==
NULL) {
4595 if (internalImage ==
NULL) {
4600 if (pImage ==
NULL) {
4613 if (pImage ==
NULL) {
4630 if (pWidthOut) *pWidthOut = 0;
4631 if (pHeightOut) *pHeightOut = 0;
4633 if (pImage ==
NULL) {
4657 if (pImage ==
NULL) {
4670 if (pImage ==
NULL) {
4759 if (pElement ==
NULL || pRelativeRect ==
NULL) {
4764 if (pRelativeRect->
left < 0) {
4765 pRelativeRect->
left = 0;
4767 if (pRelativeRect->
top < 0) {
4768 pRelativeRect->
top = 0;
4771 if (pRelativeRect->
right > pElement->
width) {
4779 return (pRelativeRect->
right - pRelativeRect->
left > 0) && (pRelativeRect->
bottom - pRelativeRect->
top > 0);
4784 if (pElement ==
NULL || pRect ==
NULL) {
4798 if (pElement ==
NULL || pRect ==
NULL) {
4812 if (pElement !=
NULL)
4814 if (positionX !=
NULL) {
4818 if (positionY !=
NULL) {
4826 if (pElement !=
NULL)
4828 if (positionX !=
NULL) {
4832 if (positionY !=
NULL) {
4852 rect.
left = FLT_MAX;
4854 rect.
right = -FLT_MAX;
4863 result.
left -= amount;
4864 result.
top -= amount;
4865 result.
right += amount;
4874 result.
left *= scaleX;
4875 result.
top *= scaleY;
4876 result.
right *= scaleX;
4900 if (posX < rect.
left || posY < rect.
top) {
4915 rect0.
top == rect1.
top &&
4933 #ifndef DRGUI_NO_DR_2D
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);
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);
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);
4959 void drgui_get_image_size_dr_2d(
drgui_resource image,
unsigned int* pWidthOut,
unsigned int* pHeightOut);
4961 void* drgui_map_image_data_dr_2d(
drgui_resource image,
unsigned int accessFlags);
4967 if (pContext !=
NULL) {
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;
4987 callbacks.
drawText = drgui_draw_text_dr_2d;
4988 callbacks.
drawImage = drgui_draw_image_dr_2d;
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;
5011 void drgui_draw_begin_dr_2d(
void* pPaintData)
5014 assert(pSurface !=
NULL);
5019 void drgui_draw_end_dr_2d(
void* pPaintData)
5022 assert(pSurface !=
NULL);
5027 void drgui_set_clip_dr_2d(
drgui_rect rect,
void* pPaintData)
5030 assert(pSurface !=
NULL);
5035 void drgui_get_clip_dr_2d(
drgui_rect* pRectOut,
void* pPaintData)
5037 assert(pRectOut !=
NULL);
5040 assert(pSurface !=
NULL);
5048 assert(pSurface !=
NULL);
5053 void drgui_draw_rect_outline_dr_2d(
drgui_rect rect,
drgui_color color,
float outlineWidth,
void* pPaintData)
5056 assert(pSurface !=
NULL);
5064 assert(pSurface !=
NULL);
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));
5069 void drgui_draw_round_rect_dr_2d(
drgui_rect rect,
drgui_color color,
float radius,
void* pPaintData)
5072 assert(pSurface !=
NULL);
5077 void drgui_draw_round_rect_outline_dr_2d(
drgui_rect rect,
drgui_color color,
float radius,
float outlineWidth,
void* pPaintData)
5080 assert(pSurface !=
NULL);
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);
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)
5088 assert(pSurface !=
NULL);
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));
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)
5096 assert(pSurface !=
NULL);
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));
5104 assert(pSurface !=
NULL);
5139 assert(pMetricsOut !=
NULL);
5156 assert(pMetricsOut !=
NULL);
5173 bool drgui_measure_string_dr_2d(
drgui_resource font,
const char* text,
size_t textSizeInBytes,
float* pWidthOut,
float* pHeightOut)
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)
5183 bool drgui_get_text_cursor_position_from_char_dr_2d(
drgui_resource font,
const char* text,
size_t characterIndex,
float* pTextCursorPosXOut)
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)
5207 void drgui_get_image_size_dr_2d(
drgui_resource image,
unsigned int* pWidthOut,
unsigned int* pHeightOut)
5217 void* drgui_map_image_data_dr_2d(
drgui_resource image,
unsigned int accessFlags)
5227 #endif //DRGUI_NO_DR_2D
5228 #endif //DR_GUI_IMPLEMENTATION
5240 #ifndef DRGUI_NO_TEXT_EDITING
5249 #ifndef drgui_text_engine_h
5250 #define drgui_text_engine_h
5734 #endif //drgui_text_engine_h
5737 #ifdef DR_GUI_IMPLEMENTATION
5751 float absoluteSickyPosX;
5753 } drgui_text_marker;
5765 size_t selectionAnchorPos;
5768 bool isAnythingSelected;
5770 } drgui_text_engine_state;
5788 drgui_text_engine_state oldState;
5792 drgui_text_engine_state newState;
5794 } drgui_text_engine_undo_state;
5816 float containerWidth;
5819 float containerHeight;
5844 unsigned int tabSizeInSpaces;
5859 unsigned int cursorBlinkRate;
5862 unsigned int timeToNextCursorBlink;
5865 bool isCursorBlinkOn;
5868 bool isShowingCursor;
5872 float textBoundsWidth;
5875 float textBoundsHeight;
5879 drgui_text_marker cursor;
5882 drgui_text_marker selectionAnchor;
5887 unsigned int selectionModeCounter;
5890 bool isAnythingSelected;
5904 drgui_text_engine_state preparedState;
5907 drgui_text_engine_undo_state* pUndoStack;
5910 unsigned int undoStackCount;
5913 unsigned int iUndoState;
5917 unsigned int dirtyCounter;
5931 size_t runBufferSize;
5935 size_t extraDataSize;
5959 } drgui_text_engine_line;
5969 DRGUI_PRIVATE
void drgui_text_engine__refresh_alignment(
drgui_text_engine* pTL);
5978 DRGUI_PRIVATE
void drgui_text_engine__calculate_line_alignment_offset(
drgui_text_engine* pTL,
float lineWidth,
float* pOffsetXOut,
float* pOffsetYOut);
5988 DRGUI_PRIVATE
bool drgui_text_engine__find_closest_line_to_point(
drgui_text_engine* pTL,
float inputPosYRelativeToText,
size_t* pFirstRunIndexOnLineOut,
size_t* pLastRunIndexOnLinePlus1Out);
5991 DRGUI_PRIVATE
bool drgui_text_engine__find_closest_run_to_point(
drgui_text_engine* pTL,
float inputPosXRelativeToText,
float inputPosYRelativeToText,
size_t* pRunIndexOut);
5994 DRGUI_PRIVATE
bool drgui_text_engine__find_line_info(
drgui_text_engine* pTL,
size_t iFirstRunOnLine,
size_t* pLastRunIndexOnLinePlus1Out,
float* pLineHeightOut);
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);
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);
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);
6006 DRGUI_PRIVATE
bool drgui_text_engine__find_run_at_character(
drgui_text_engine* pTL,
size_t iChar,
size_t* pRunIndexOut);
6010 DRGUI_PRIVATE drgui_text_marker drgui_text_engine__new_marker();
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);
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);
6019 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_point(
drgui_text_engine* pTL, drgui_text_marker* pMarker,
float inputPosXRelativeToText,
float inputPosYRelativeToText);
6022 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_left(
drgui_text_engine* pTL, drgui_text_marker* pMarker);
6025 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_right(
drgui_text_engine* pTL, drgui_text_marker* pMarker);
6028 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_up(
drgui_text_engine* pTL, drgui_text_marker* pMarker);
6031 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_down(
drgui_text_engine* pTL, drgui_text_marker* pMarker);
6034 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_y(
drgui_text_engine* pTL, drgui_text_marker* pMarker,
int amount);
6037 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_end_of_line(
drgui_text_engine* pTL, drgui_text_marker* pMarker);
6040 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_start_of_line(
drgui_text_engine* pTL, drgui_text_marker* pMarker);
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);
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);
6049 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_end_of_text(
drgui_text_engine* pTL, drgui_text_marker* pMarker);
6052 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_start_of_text(
drgui_text_engine* pTL, drgui_text_marker* pMarker);
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);
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);
6061 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_last_character_of_prev_run(
drgui_text_engine* pTL, drgui_text_marker* pMarker);
6064 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_first_character_of_next_run(
drgui_text_engine* pTL, drgui_text_marker* pMarker);
6067 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_character(
drgui_text_engine* pTL, drgui_text_marker* pMarker,
size_t iChar);
6074 DRGUI_PRIVATE
bool drgui_text_engine__update_marker_relative_position(
drgui_text_engine* pTL, drgui_text_marker* pMarker);
6077 DRGUI_PRIVATE
void drgui_text_engine__update_marker_sticky_position(
drgui_text_engine* pTL, drgui_text_marker* pMarker);
6081 DRGUI_PRIVATE
size_t drgui_text_engine__get_marker_absolute_char_index(
drgui_text_engine* pTL, drgui_text_marker* pMarker);
6085 DRGUI_PRIVATE
bool drgui_text_engine__has_spacing_between_selection_markers(
drgui_text_engine* pTL);
6092 DRGUI_PRIVATE
bool drgui_text_engine__get_selection_markers(
drgui_text_engine* pTL, drgui_text_marker** ppSelectionMarker0Out, drgui_text_marker** ppSelectionMarker1Out);
6096 DRGUI_PRIVATE
bool drgui_text_engine__first_line(
drgui_text_engine* pTL, drgui_text_engine_line* pLine);
6099 DRGUI_PRIVATE
bool drgui_text_engine__next_line(
drgui_text_engine* pTL, drgui_text_engine_line* pLine);
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);
6109 DRGUI_PRIVATE
void drgui_text_engine__uninit_undo_state(drgui_text_engine_undo_state* pUndoState);
6112 DRGUI_PRIVATE
void drgui_text_engine__push_undo_state(
drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState);
6115 DRGUI_PRIVATE
void drgui_text_engine__apply_undo_state(
drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState);
6118 DRGUI_PRIVATE
void drgui_text_engine__apply_redo_state(
drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState);
6142 if (pContext ==
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;
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;
6191 pTL->runBufferSize = 0;
6193 pTL->extraDataSize = extraDataSize;
6194 if (pExtraData !=
NULL) {
6195 memcpy(pTL->pExtraData, pExtraData, extraDataSize);
6210 free(pTL->preparedState.text);
6222 return pTL->extraDataSize;
6231 return pTL->pExtraData;
6241 size_t textLength = strlen(text);
6244 pTL->text = (
char*)malloc(textLength + 1);
6248 char* dst = pTL->text;
6249 const char* src = text;
6250 while (*src !=
'\0')
6260 pTL->textLength = dst - pTL->text;
6263 drgui_text_engine__refresh(pTL);
6266 if (drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor) >= pTL->textLength) {
6270 if (pTL->onTextChanged) {
6271 pTL->onTextChanged(pTL);
6274 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6283 if (textOut ==
NULL) {
6284 return pTL->textLength;
6288 if (drgui__strcpy_s(textOut, textOutSize, (pTL->text !=
NULL) ? pTL->text :
"") == 0) {
6289 return pTL->textLength;
6302 pTL->onDirty = proc;
6311 pTL->onTextChanged = proc;
6320 pTL->onUndoPointChanged = proc;
6330 pTL->containerWidth = containerWidth;
6331 pTL->containerHeight = containerHeight;
6333 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6338 float containerWidth = 0;
6339 float containerHeight = 0;
6343 containerWidth = pTL->containerWidth;
6344 containerHeight = pTL->containerHeight;
6348 if (pContainerWidthOut) {
6349 *pContainerWidthOut = containerWidth;
6351 if (pContainerHeightOut) {
6352 *pContainerHeightOut = containerHeight;
6362 return pTL->containerWidth;
6371 return pTL->containerHeight;
6381 pTL->innerOffsetX = innerOffsetX;
6382 pTL->innerOffsetY = innerOffsetY;
6384 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6393 pTL->innerOffsetX = innerOffsetX;
6395 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6404 pTL->innerOffsetY = innerOffsetY;
6406 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6411 float innerOffsetX = 0;
6412 float innerOffsetY = 0;
6416 innerOffsetX = pTL->innerOffsetX;
6417 innerOffsetY = pTL->innerOffsetY;
6421 if (pInnerOffsetX) {
6422 *pInnerOffsetX = innerOffsetX;
6424 if (pInnerOffsetY) {
6425 *pInnerOffsetY = innerOffsetY;
6435 return pTL->innerOffsetX;
6444 return pTL->innerOffsetY;
6454 pTL->pDefaultFont = pFont;
6457 drgui_text_engine__refresh(pTL);
6458 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6467 return pTL->pDefaultFont;
6476 pTL->defaultTextColor = color;
6478 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6487 return pTL->defaultTextColor;
6496 pTL->defaultBackgroundColor = color;
6498 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6507 return pTL->defaultBackgroundColor;
6516 pTL->selectionBackgroundColor = color;
6519 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6529 return pTL->selectionBackgroundColor;
6538 pTL->lineBackgroundColor = color;
6540 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6549 return pTL->lineBackgroundColor;
6559 if (pTL->tabSizeInSpaces != sizeInSpaces)
6561 pTL->tabSizeInSpaces = sizeInSpaces;
6563 drgui_text_engine__refresh(pTL);
6564 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6574 return pTL->tabSizeInSpaces;
6584 if (pTL->horzAlign != alignment)
6586 pTL->horzAlign = alignment;
6588 drgui_text_engine__refresh_alignment(pTL);
6589 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6599 return pTL->horzAlign;
6608 if (pTL->vertAlign != alignment)
6610 pTL->vertAlign = alignment;
6612 drgui_text_engine__refresh_alignment(pTL);
6613 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
6623 return pTL->vertAlign;
6638 switch (pTL->horzAlign)
6642 rect.
left = pTL->containerWidth - pTL->textBoundsWidth;
6648 rect.
left = (pTL->containerWidth - pTL->textBoundsWidth) / 2;
6662 switch (pTL->vertAlign)
6666 rect.
top = pTL->containerHeight - pTL->textBoundsHeight;
6672 rect.
top = (pTL->containerHeight - pTL->textBoundsHeight) / 2;
6686 rect.
left += pTL->innerOffsetX;
6687 rect.
top += pTL->innerOffsetY;
6688 rect.
right = rect.
left + pTL->textBoundsWidth;
6689 rect.
bottom = rect.
top + pTL->textBoundsHeight;
6702 pTL->cursorWidth = cursorWidth;
6703 if (pTL->cursorWidth > 0 && pTL->cursorWidth < 1) {
6704 pTL->cursorWidth = 1;
6716 return pTL->cursorWidth;
6725 pTL->cursorColor = cursorColor;
6736 return pTL->cursorColor;
6745 pTL->cursorBlinkRate = blinkRateInMilliseconds;
6754 return pTL->cursorBlinkRate;
6763 if (!pTL->isShowingCursor)
6765 pTL->isShowingCursor =
true;
6767 pTL->timeToNextCursorBlink = pTL->cursorBlinkRate;
6768 pTL->isCursorBlinkOn =
true;
6780 if (pTL->isShowingCursor)
6782 pTL->isShowingCursor =
false;
6794 return pTL->isShowingCursor;
6803 drgui_text_engine__get_marker_position_relative_to_container(pTL, &pTL->cursor, pPosXOut, pPosYOut);
6814 if (pTL->runCount > 0)
6816 drgui_text_engine__find_line_info_by_index(pTL, pTL->pRuns[pTL->cursor.iRun].iLine, &lineRect,
NULL,
NULL);
6818 else if (pTL->pDefaultFont !=
NULL)
6832 return drgui_make_rect(cursorPosX, cursorPosY, cursorPosX + pTL->cursorWidth, cursorPosY + (lineRect.
bottom - lineRect.
top));
6837 if (pTL ==
NULL || pTL->runCount == 0) {
6841 return pTL->pRuns[pTL->cursor.iRun].iLine;
6857 return (
unsigned int)((int)posX / fontMetrics.
spaceWidth);
6866 return drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
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);
6880 pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
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));
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);
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));
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);
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));
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);
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));
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);
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));
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);
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));
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);
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));
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);
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));
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);
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));
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);
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));
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);
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));
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);
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));
7159 drgui_text_marker* pSelectionMarker0;
7160 drgui_text_marker* pSelectionMarker1;
7161 if (drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1))
7163 pTL->cursor = *pSelectionMarker0;
7164 pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7166 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7176 drgui_text_marker* pSelectionMarker0;
7177 drgui_text_marker* pSelectionMarker1;
7178 if (drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1))
7180 pTL->cursor = *pSelectionMarker1;
7181 pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7183 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
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);
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));
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;
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;
7235 drgui_text_marker* pSelectionMarker0;
7236 drgui_text_marker* pSelectionMarker1;
7237 if (drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1))
7239 size_t iRunOld = pTL->cursor.iRun;
7240 size_t iCharOld = pTL->cursor.iChar;
7242 drgui_text_marker temp = *pSelectionMarker0;
7243 *pSelectionMarker0 = *pSelectionMarker1;
7244 *pSelectionMarker1 = temp;
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));
7259 pTL->onCursorMove = proc;
7264 if (pTL ==
NULL || pTL->pRuns ==
NULL) {
7272 pRun = pTL->pRuns + pTL->selectionAnchor.iRun;
7283 if (character ==
'\r') {
7289 char* pOldText = pTL->text;
7290 char* pNewText = (
char*)malloc(pTL->textLength + 1 + 1);
7292 if (insertIndex > 0) {
7293 memcpy(pNewText, pOldText, insertIndex);
7296 pNewText[insertIndex] = (char)character;
7298 if (insertIndex < pTL->textLength) {
7299 memcpy(pNewText + insertIndex + 1, pOldText + insertIndex, pTL->textLength - insertIndex);
7302 pTL->textLength += 1;
7303 pTL->text = pNewText;
7304 pNewText[pTL->textLength] =
'\0';
7312 drgui_text_engine__refresh(pTL);
7314 if (pTL->onTextChanged) {
7315 pTL->onTextChanged(pTL);
7318 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7329 size_t newTextLength = strlen(text);
7330 if (newTextLength == 0) {
7336 char* pOldText = pTL->text;
7337 char* pNewText = (
char*)malloc(pTL->textLength + newTextLength + 1);
7339 if (insertIndex > 0) {
7340 memcpy(pNewText, pOldText, insertIndex);
7346 char* dst = pNewText + insertIndex;
7347 const char* src = text;
7348 size_t srcLen = newTextLength;
7349 while (*src !=
'\0' && srcLen > 0)
7359 newTextLength = dst - (pNewText + insertIndex);
7362 if (insertIndex < pTL->textLength) {
7363 memcpy(pNewText + insertIndex + newTextLength, pOldText + insertIndex, pTL->textLength - insertIndex);
7366 pTL->textLength += newTextLength;
7367 pTL->text = pNewText;
7368 pNewText[pTL->textLength] =
'\0';
7374 drgui_text_engine__refresh(pTL);
7376 if (pTL->onTextChanged) {
7377 pTL->onTextChanged(pTL);
7380 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7387 if (pTL ==
NULL || iLastChPlus1 == iFirstCh) {
7391 if (iFirstCh > iLastChPlus1) {
7392 size_t temp = iFirstCh;
7393 iFirstCh = iLastChPlus1;
7394 iLastChPlus1 = temp;
7398 size_t bytesToRemove = iLastChPlus1 - iFirstCh;
7399 if (bytesToRemove > 0)
7401 memmove(pTL->text + iFirstCh, pTL->text + iLastChPlus1, pTL->textLength - iLastChPlus1);
7402 pTL->textLength -= bytesToRemove;
7403 pTL->text[pTL->textLength] =
'\0';
7406 drgui_text_engine__refresh(pTL);
7408 if (pTL->onTextChanged) {
7409 pTL->onTextChanged(pTL);
7412 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7426 size_t iAbsoluteMarkerChar = 0;
7429 if (pTL->runCount > 0 && pRun !=
NULL) {
7430 iAbsoluteMarkerChar = pRun->
iChar + pTL->cursor.iChar;
7433 drgui_text_engine__begin_dirty(pTL);
7436 drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, iAbsoluteMarkerChar + 1);
7438 drgui_text_engine__end_dirty(pTL);
7442 drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
7445 drgui_text_engine__on_cursor_move(pTL);
7456 drgui_text_engine__begin_dirty(pTL);
7458 size_t cursorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
7460 drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, cursorPos + strlen(text));
7462 drgui_text_engine__end_dirty(pTL);
7466 drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
7468 drgui_text_engine__on_cursor_move(pTL);
7490 if (pTL ==
NULL || pTL->runCount == 0) {
7495 size_t iAbsoluteMarkerChar = pRun->
iChar + pTL->cursor.iChar;
7497 if (iAbsoluteMarkerChar < pTL->textLength)
7500 memmove(pTL->text + iAbsoluteMarkerChar, pTL->text + iAbsoluteMarkerChar + 1, pTL->textLength - iAbsoluteMarkerChar);
7501 pTL->textLength -= 1;
7502 pTL->text[pTL->textLength] =
'\0';
7507 drgui_text_engine__refresh(pTL);
7508 drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, iAbsoluteMarkerChar);
7510 if (pTL->onTextChanged) {
7511 pTL->onTextChanged(pTL);
7514 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
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)
7533 drgui_text_marker* temp = pSelectionMarker0;
7534 pSelectionMarker0 = pSelectionMarker1;
7535 pSelectionMarker1 = temp;
7538 size_t iSelectionChar0 = pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar;
7539 size_t iSelectionChar1 = pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar;
7541 drgui_text_engine__begin_dirty(pTL);
7546 drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, iSelectionChar0);
7549 drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
7551 drgui_text_engine__on_cursor_move(pTL);
7555 pTL->selectionAnchor = pTL->cursor;
7556 pTL->isAnythingSelected =
false;
7559 drgui_text_engine__end_dirty(pTL);
7560 return wasTextChanged;
7572 pTL->selectionAnchor = pTL->cursor;
7575 pTL->selectionModeCounter += 1;
7584 if (pTL->selectionModeCounter > 0) {
7585 pTL->selectionModeCounter -= 1;
7595 return pTL->selectionModeCounter > 0;
7604 return pTL->isAnythingSelected;
7613 pTL->isAnythingSelected =
false;
7615 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
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);
7627 pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7629 drgui_text_engine__on_cursor_move(pTL);
7630 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7639 drgui_text_engine__move_marker_to_character(pTL, &pTL->selectionAnchor, firstCharacter);
7640 drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, lastCharacter);
7642 pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
7644 drgui_text_engine__on_cursor_move(pTL);
7645 drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
7650 if (pTL ==
NULL || (textOut !=
NULL && textOutSize == 0)) {
7659 drgui_text_marker* pSelectionMarker0;
7660 drgui_text_marker* pSelectionMarker1;
7661 if (!drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
7665 size_t iSelectionChar0 = pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar;
7666 size_t iSelectionChar1 = pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar;
7668 size_t selectedTextLength = iSelectionChar1 - iSelectionChar0;
7670 if (textOut !=
NULL) {
7671 drgui__strncpy_s(textOut, textOutSize, pTL->text + iSelectionChar0, selectedTextLength);
7674 return selectedTextLength;
7679 if (pTL ==
NULL || pTL->runCount == 0) {
7683 drgui_text_marker* pSelectionMarker0;
7684 drgui_text_marker* pSelectionMarker1;
7685 if (!drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
7689 return pTL->pRuns[pSelectionMarker0->iRun].iLine;
7694 if (pTL ==
NULL || pTL->runCount == 0) {
7698 drgui_text_marker* pSelectionMarker0;
7699 drgui_text_marker* pSelectionMarker1;
7700 if (!drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
7704 return pTL->pRuns[pSelectionMarker1->iRun].iLine;
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);
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);
7729 if (pTL ==
NULL || pTL->runCount == 0) {
7733 return pTL->pRuns[pTL->selectionAnchor.iRun].iLine;
7745 if (pTL->preparedState.text !=
NULL) {
7746 free(pTL->preparedState.text);
7749 pTL->preparedState.text = (
char*)malloc(pTL->textLength + 1);
7750 drgui__strcpy_s(pTL->preparedState.text, pTL->textLength + 1, (pTL->text !=
NULL) ? pTL->text :
"");
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;
7766 if (pTL->preparedState.text ==
NULL) {
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;
7778 drgui_text_engine_undo_state undoState;
7779 if (!drgui_text_engine__diff_states(&pTL->preparedState, ¤tState, &undoState)) {
7786 drgui_text_engine__trim_undo_stack(pTL);
7787 drgui_text_engine__push_undo_state(pTL, &undoState);
7794 if (pTL ==
NULL || pTL->pUndoStack ==
NULL) {
7800 drgui_text_engine_undo_state* pUndoState = pTL->pUndoStack + (pTL->iUndoState - 1);
7801 assert(pUndoState !=
NULL);
7803 drgui_text_engine__apply_undo_state(pTL, pUndoState);
7804 pTL->iUndoState -= 1;
7806 if (pTL->onUndoPointChanged) {
7807 pTL->onUndoPointChanged(pTL, pTL->iUndoState);
7818 if (pTL ==
NULL || pTL->pUndoStack ==
NULL) {
7824 drgui_text_engine_undo_state* pUndoState = pTL->pUndoStack + pTL->iUndoState;
7825 assert(pUndoState !=
NULL);
7827 drgui_text_engine__apply_redo_state(pTL, pUndoState);
7828 pTL->iUndoState += 1;
7830 if (pTL->onUndoPointChanged) {
7831 pTL->onUndoPointChanged(pTL, pTL->iUndoState);
7846 return pTL->iUndoState;
7855 if (pTL->undoStackCount > 0)
7857 assert(pTL->iUndoState <= pTL->undoStackCount);
7858 return pTL->undoStackCount - pTL->iUndoState;
7866 if (pTL ==
NULL || pTL->pUndoStack ==
NULL) {
7870 for (
unsigned int i = 0; i < pTL->undoStackCount; ++i) {
7871 drgui_text_engine__uninit_undo_state(pTL->pUndoStack + i);
7874 free(pTL->pUndoStack);
7876 pTL->pUndoStack =
NULL;
7877 pTL->undoStackCount = 0;
7879 if (pTL->iUndoState > 0) {
7880 pTL->iUndoState = 0;
7882 if (pTL->onUndoPointChanged) {
7883 pTL->onUndoPointChanged(pTL, pTL->iUndoState);
7892 if (pTL ==
NULL || pTL->runCount == 0) {
7896 return pTL->pRuns[pTL->runCount - 1].iLine + 1;
7901 if (pTL ==
NULL || pTL->runCount == 0) {
7905 unsigned int count = 0;
7906 float lastLineBottom = 0;
7909 unsigned int iLine = 0;
7910 drgui_text_engine_line line;
7911 if (drgui_text_engine__first_line(pTL, &line))
7915 if (iLine >= iFirstLine) {
7920 }
while (drgui_text_engine__next_line(pTL, &line));
7926 if (line.posY + pTL->innerOffsetY >= pTL->containerHeight) {
7931 lastLineBottom = line.posY + line.height;
7933 }
while (drgui_text_engine__next_line(pTL, &line));
7939 if (lastLineBottom + pTL->innerOffsetY < pTL->containerHeight)
7943 count += (
unsigned int)((pTL->containerHeight - (lastLineBottom + pTL->innerOffsetY)) / defaultFontMetrics.
lineHeight);
7959 if (!drgui_text_engine__find_line_info_by_index(pTL, iLine, &lineRect,
NULL,
NULL)) {
7963 return lineRect.
top;
7968 if (pTL ==
NULL || pTL->runCount == 0) {
7976 float inputPosYRelativeToText = posY - textRect.
top;
7977 if (!drgui_text_engine__find_closest_run_to_point(pTL, 0, inputPosYRelativeToText, &iRun)) {
7981 return pTL->pRuns[iRun].iLine;
7986 if (pTL ==
NULL || pTL->runCount == 0) {
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;
8001 if (pTL ==
NULL || pTL->runCount == 0) {
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;
8021 if (pTL ==
NULL || pTL->runCount == 0) {
8025 size_t charStart = 0;
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;
8038 if (pCharStartOut) {
8039 *pCharStartOut = charStart;
8042 *pCharEndOut = charEnd;
8053 pTL->onPaintText = proc;
8062 pTL->onPaintRect = proc;
8067 if (pTL ==
NULL || pTL->onPaintText ==
NULL || pTL->onPaintRect ==
NULL) {
8071 if (rect.
left < 0) {
8077 if (rect.
right > pTL->containerWidth) {
8078 rect.
right = pTL->containerWidth;
8080 if (rect.
bottom > pTL->containerHeight) {
8081 rect.
bottom = pTL->containerHeight;
8097 pTL->onPaintRect(pTL, rectTop, pTL->defaultBackgroundColor, pElement, pPaintData);
8101 pTL->onPaintRect(pTL, rectBottom, pTL->defaultBackgroundColor, pElement, pPaintData);
8106 drgui_text_engine_line line;
8107 if (drgui_text_engine__first_line(pTL, &line))
8111 float lineTop = line.posY + textRect.
top;
8112 float lineBottom = lineTop + line.height;
8114 if (lineTop < rect.
bottom)
8116 if (lineBottom > rect.
top)
8121 drgui_color bgcolor = pTL->defaultBackgroundColor;
8123 bgcolor = pTL->lineBackgroundColor;
8126 float lineSelectionOverhangLeft = 0;
8127 float lineSelectionOverhangRight = 0;
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)
8135 drgui_text_marker* temp = pSelectionMarker0;
8136 pSelectionMarker0 = pSelectionMarker1;
8137 pSelectionMarker1 = temp;
8140 size_t iSelectionLine0 = pTL->pRuns[pSelectionMarker0->iRun].iLine;
8141 size_t iSelectionLine1 = pTL->pRuns[pSelectionMarker1->iRun].iLine;
8143 if (line.index >= iSelectionLine0 && line.index < iSelectionLine1)
8150 if (line.index > iSelectionLine0) {
8151 lineSelectionOverhangLeft = (float)defaultFontMetrics.
spaceWidth;
8156 lineSelectionOverhangRight = (float)defaultFontMetrics.
spaceWidth;
8158 if (line.index > iSelectionLine0) {
8159 lineSelectionOverhangLeft = (float)defaultFontMetrics.
spaceWidth;
8164 lineSelectionOverhangRight = (float)defaultFontMetrics.
spaceWidth;
8173 float lineLeft = pFirstRun->
posX + textRect.
left;
8174 float lineRight = pLastRun->
posX + pLastRun->
width + textRect.
left;
8179 if (lineSelectionOverhangLeft > 0) {
8180 pTL->onPaintRect(pTL,
drgui_make_rect(lineLeft - lineSelectionOverhangLeft, lineTop, lineLeft, lineBottom), pTL->selectionBackgroundColor, pElement, pPaintData);
8183 pTL->onPaintRect(pTL,
drgui_make_rect(0, lineTop, lineLeft - lineSelectionOverhangLeft, lineBottom), bgcolor, pElement, pPaintData);
8188 for (
size_t iRun = line.iFirstRun; iRun <= line.iLastRun; ++iRun)
8192 float runLeft = pRun->
posX + textRect.
left;
8193 float runRight = runLeft + pRun->
width;
8195 if (runRight > 0 && runLeft < pTL->containerWidth)
8198 if (!drgui_text_engine__is_text_run_whitespace(pTL, pRun) || pTL->text[pRun->
iChar] ==
'\t')
8201 run.pFont = pTL->pDefaultFont;
8202 run.textColor = pTL->defaultTextColor;
8203 run.backgroundColor = bgcolor;
8204 run.text = pTL->text +
run.iChar;
8213 size_t subrunCount = drgui_text_engine__split_text_run_by_selection(pTL, &run, subruns);
8214 for (
size_t iSubRun = 0; iSubRun < subrunCount; ++iSubRun)
8218 if (!drgui_text_engine__is_text_run_whitespace(pTL, pRun)) {
8219 pTL->onPaintText(pTL, pSubRun, pElement, pPaintData);
8228 if (!drgui_text_engine__is_text_run_whitespace(pTL, &run)) {
8229 pTL->onPaintText(pTL, &run, pElement, pPaintData);
8231 pTL->onPaintRect(pTL,
drgui_make_rect(
run.posX, lineTop,
run.posX +
run.width, lineBottom),
run.backgroundColor, pElement, pPaintData);
8240 if (lineRight < pTL->containerWidth)
8242 if (lineSelectionOverhangRight > 0) {
8243 pTL->onPaintRect(pTL,
drgui_make_rect(lineRight, lineTop, lineRight + lineSelectionOverhangRight, lineBottom), pTL->selectionBackgroundColor, pElement, pPaintData);
8246 pTL->onPaintRect(pTL,
drgui_make_rect(lineRight + lineSelectionOverhangRight, lineTop, pTL->containerWidth, lineBottom), bgcolor, pElement, pPaintData);
8256 }
while (drgui_text_engine__next_line(pTL, &line));
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);
8267 if (pTL->isShowingCursor && pTL->isCursorBlinkOn) {
8275 if (pTL ==
NULL || milliseconds == 0) {
8279 if (pTL->timeToNextCursorBlink < milliseconds)
8281 pTL->isCursorBlinkOn = !pTL->isCursorBlinkOn;
8282 pTL->timeToNextCursorBlink = pTL->cursorBlinkRate;
8288 pTL->timeToNextCursorBlink -= milliseconds;
8296 if (pTL ==
NULL || onPaintText ==
NULL || onPaintRect ==
NULL) {
8308 if (pTL->onPaintRect)
8310 if (rectTop.
bottom > 0) {
8311 onPaintRect(pTL, rectTop, backgroundColor, pElement, pPaintData);
8314 if (rectBottom.
top < lineNumbersHeight) {
8315 onPaintRect(pTL, rectBottom, backgroundColor, pElement, pPaintData);
8322 drgui_text_engine_line line;
8323 if (!drgui_text_engine__first_line(pTL, &line))
8336 float lineTop = line.posY + textRect.
top;
8337 float lineBottom = lineTop + line.height;
8339 if (lineTop < lineNumbersHeight)
8345 _itoa_s(iLine, iLineStr,
sizeof(iLineStr), 10);
8347 snprintf(iLineStr,
sizeof(iLineStr),
"%d", iLine);
8358 run.textColor = textColor;
8359 run.backgroundColor = backgroundColor;
8360 run.text = iLineStr;
8361 run.textLength = strlen(iLineStr);
8362 run.posX = lineNumbersWidth - textWidth;
8364 onPaintText(pTL, &run, pElement, pPaintData);
8365 onPaintRect(pTL,
drgui_make_rect(0, lineTop,
run.posX, lineBottom),
run.backgroundColor, pElement, pPaintData);
8375 }
while (drgui_text_engine__next_line(pTL, &line));
8381 if (pTL ==
NULL || pTL->text ==
NULL || text ==
NULL || text[0] ==
'\0') {
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);
8391 if (nextOccurance ==
NULL) {
8395 if (pSelectionStartOut) {
8396 *pSelectionStartOut = nextOccurance - pTL->text;
8398 if (pSelectionEndOut) {
8399 *pSelectionEndOut = (nextOccurance - pTL->text) + strlen(text);
8407 if (pTL ==
NULL || pTL->text ==
NULL || text ==
NULL || text[0] ==
'\0') {
8411 size_t cursorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
8413 char* nextOccurance = strstr(pTL->text + cursorPos, text);
8414 if (nextOccurance ==
NULL) {
8418 if (pSelectionStartOut) {
8419 *pSelectionStartOut = nextOccurance - pTL->text;
8421 if (pSelectionEndOut) {
8422 *pSelectionEndOut = (nextOccurance - pTL->text) + strlen(text);
8431 DRGUI_PRIVATE
bool drgui_next_run_string(
const char* runStart,
const char* textEndPastNullTerminator,
const char** pRunEndOut)
8433 assert(runStart <= textEndPastNullTerminator);
8435 if (runStart ==
NULL || runStart == textEndPastNullTerminator)
8442 char firstChar = runStart[0];
8443 if (firstChar ==
'\t')
8449 *pRunEndOut = runStart;
8450 }
while (runStart[0] !=
'\0' && runStart[0] ==
'\t');
8452 else if (firstChar ==
'\n')
8455 *pRunEndOut = runStart;
8457 else if (firstChar ==
'\0')
8459 assert(runStart + 1 == textEndPastNullTerminator);
8460 *pRunEndOut = textEndPastNullTerminator;
8467 *pRunEndOut = runStart;
8468 }
while (runStart[0] !=
'\0' && runStart[0] !=
'\t' && runStart[0] !=
'\n');
8487 drgui_text_engine__clear_text_runs(pTL);
8490 pTL->textBoundsWidth = 0;
8491 pTL->textBoundsHeight = 0;
8496 pTL->textBoundsHeight = (float)defaultFontMetrics.
lineHeight;
8498 const float tabWidth = drgui_text_engine__get_tab_width(pTL);
8500 size_t iCurrentLine = 0;
8501 float runningPosY = 0;
8502 float runningLineHeight = 0;
8504 const char* nextRunStart = pTL->text;
8505 const char* nextRunEnd;
8506 while (drgui_next_run_string(nextRunStart, pTL->text + pTL->textLength + 1,
OUT &nextRunEnd))
8509 run.iLine = iCurrentLine;
8510 run.iChar = nextRunStart - pTL->text;
8511 run.iCharEnd = nextRunEnd - pTL->text;
8512 run.textLength = nextRunEnd - nextRunStart;
8516 run.posY = runningPosY;
8517 run.pFont = pTL->pDefaultFont;
8522 if (pTL->runCount > 0)
8525 if (pPrevRun->
iLine == iCurrentLine)
8538 assert(nextRunEnd > nextRunStart);
8539 if (nextRunStart[0] ==
'\t')
8542 size_t tabCount =
run.iCharEnd -
run.iChar;
8543 run.width = (float)(((tabCount*(
size_t)tabWidth) - ((
size_t)
run.posX % (
size_t)tabWidth)));
8546 else if (nextRunStart[0] ==
'\n')
8553 else if (nextRunStart[0] ==
'\0')
8568 runningLineHeight = (
run.height > runningLineHeight) ?
run.height : runningLineHeight;
8572 if (pTL->textBoundsWidth <
run.posX +
run.width) {
8573 pTL->textBoundsWidth =
run.posX +
run.width;
8575 pTL->textBoundsHeight = runningPosY + runningLineHeight;
8579 if (nextRunStart[0] ==
'\n')
8581 runningPosY += runningLineHeight;
8582 runningLineHeight = 0;
8586 drgui_text_engine__push_text_run(pTL, &run);
8589 nextRunStart = nextRunEnd;
8595 drgui_text_engine__refresh_alignment(pTL);
8605 float runningPosY = 0;
8607 unsigned int iCurrentLine = 0;
8608 for (
size_t iRun = 0; iRun < pTL->runCount; )
8610 float lineWidth = 0;
8611 float lineHeight = 0;
8617 for (jRun = iRun; jRun < pTL->runCount && pTL->pRuns[jRun].iLine == iCurrentLine; ++jRun)
8620 pRun->
posX = lineWidth;
8621 pRun->
posY = runningPosY;
8623 lineWidth += pRun->
width;
8624 lineHeight = (lineHeight > pRun->
height) ? lineHeight : pRun->
height;
8631 drgui_text_engine__calculate_line_alignment_offset(pTL, lineWidth,
OUT &lineOffsetX,
OUT &lineOffsetY);
8633 for (; iRun < jRun; ++iRun)
8636 pRun->
posX += lineOffsetX;
8637 pRun->
posY += lineOffsetY;
8643 runningPosY += lineHeight;
8647 void drgui_text_engine__calculate_line_alignment_offset(
drgui_text_engine* pTL,
float lineWidth,
float* pOffsetXOut,
float* pOffsetYOut)
8656 switch (pTL->horzAlign)
8660 offsetX = pTL->textBoundsWidth - lineWidth;
8666 offsetX = (pTL->textBoundsWidth - lineWidth) / 2;
8681 switch (pTL->vertAlign)
8685 offsetY = pTL->textBoundsHeight - pTL->textBoundsHeight;
8691 offsetY = (pTL->textBoundsHeight - pTL->textBoundsHeight) / 2;
8707 *pOffsetXOut = offsetX;
8710 *pOffsetYOut = offsetY;
8721 if (pTL->runBufferSize == pTL->runCount)
8723 pTL->runBufferSize = pTL->runBufferSize*2;
8724 if (pTL->runBufferSize == 0) {
8725 pTL->runBufferSize = 1;
8729 if (pTL->pRuns ==
NULL) {
8731 pTL->runBufferSize = 0;
8736 pTL->pRuns[pTL->runCount] = *pRun;
8755 if (pTL->text[pRun->
iChar] !=
'\t' && pTL->text[pRun->
iChar] !=
'\n')
8768 return (
float)(defaultFontMetrics.
spaceWidth * pTL->tabSizeInSpaces);
8772 DRGUI_PRIVATE
bool drgui_text_engine__find_closest_line_to_point(
drgui_text_engine* pTL,
float inputPosYRelativeToText,
size_t* pFirstRunIndexOnLineOut,
size_t* pLastRunIndexOnLinePlus1Out)
8774 size_t iFirstRunOnLine = 0;
8775 size_t iLastRunOnLinePlus1 = 0;
8778 if (pTL ==
NULL || pTL->runCount == 0)
8784 float runningLineTop = 0;
8787 while (drgui_text_engine__find_line_info(pTL, iFirstRunOnLine,
OUT &iLastRunOnLinePlus1,
OUT &lineHeight))
8789 const float lineTop = runningLineTop;
8790 const float lineBottom = lineTop + lineHeight;
8792 if (inputPosYRelativeToText < lineBottom)
8800 if (iLastRunOnLinePlus1 == pTL->runCount) {
8804 iFirstRunOnLine = iLastRunOnLinePlus1;
8805 runningLineTop = lineBottom;
8810 if (pFirstRunIndexOnLineOut) {
8811 *pFirstRunIndexOnLineOut = iFirstRunOnLine;
8813 if (pLastRunIndexOnLinePlus1Out) {
8814 *pLastRunIndexOnLinePlus1Out = iLastRunOnLinePlus1;
8820 DRGUI_PRIVATE
bool drgui_text_engine__find_closest_run_to_point(
drgui_text_engine* pTL,
float inputPosXRelativeToText,
float inputPosYRelativeToText,
size_t* pRunIndexOut)
8826 size_t iFirstRunOnLine;
8827 size_t iLastRunOnLinePlus1;
8828 if (drgui_text_engine__find_closest_line_to_point(pTL, inputPosYRelativeToText,
OUT &iFirstRunOnLine,
OUT &iLastRunOnLinePlus1))
8832 const drgui_text_run* pFirstRunOnLine = pTL->pRuns + iFirstRunOnLine;
8833 const drgui_text_run* pLastRunOnLine = pTL->pRuns + (iLastRunOnLinePlus1 - 1);
8835 if (inputPosXRelativeToText < pFirstRunOnLine->posX)
8838 iRunOut = iFirstRunOnLine;
8840 else if (inputPosXRelativeToText > pLastRunOnLine->
posX + pLastRunOnLine->
width)
8843 iRunOut = iLastRunOnLinePlus1 - 1;
8848 for (
size_t iRun = iFirstRunOnLine; iRun < iLastRunOnLinePlus1; ++iRun)
8853 if (inputPosXRelativeToText >= pRun->
posX && inputPosXRelativeToText <= pRun->posX + pRun->
width) {
8860 *pRunIndexOut = iRunOut;
8872 DRGUI_PRIVATE
bool drgui_text_engine__find_line_info(
drgui_text_engine* pTL,
size_t iFirstRunOnLine,
size_t* pLastRunIndexOnLinePlus1Out,
float* pLineHeightOut)
8878 if (iFirstRunOnLine < pTL->runCount)
8880 const size_t iLine = pTL->pRuns[iFirstRunOnLine].iLine;
8881 float lineHeight = 0;
8884 for (iRun = iFirstRunOnLine; iRun < pTL->runCount && pTL->pRuns[iRun].iLine == iLine; ++iRun)
8886 if (lineHeight < pTL->pRuns[iRun].height) {
8887 lineHeight = pTL->pRuns[iRun].height;
8891 assert(iRun > iFirstRunOnLine);
8893 if (pLastRunIndexOnLinePlus1Out) {
8894 *pLastRunIndexOnLinePlus1Out = iRun;
8896 if (pLineHeightOut) {
8897 *pLineHeightOut = lineHeight;
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)
8912 size_t iFirstRunOnLine = 0;
8913 size_t iLastRunOnLinePlus1 = 0;
8916 float lineHeight = 0;
8918 for (
size_t iCurrentLine = 0; iCurrentLine <= iLine; ++iCurrentLine)
8920 iFirstRunOnLine = iLastRunOnLinePlus1;
8921 lineTop += lineHeight;
8923 if (!drgui_text_engine__find_line_info(pTL, iFirstRunOnLine, &iLastRunOnLinePlus1, &lineHeight))
8932 if (iLastRunOnLinePlus1 > iFirstRunOnLine)
8934 if (pFirstRunIndexOut) {
8935 *pFirstRunIndexOut = iFirstRunOnLine;
8937 if (pLastRunIndexPlus1Out) {
8938 *pLastRunIndexPlus1Out = iLastRunOnLinePlus1;
8941 if (pRectOut !=
NULL)
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;
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)
8960 if (pTL ==
NULL || pTL->pRuns ==
NULL) {
8964 size_t result = iRun;
8966 size_t iLine = pTL->pRuns[iRun].iLine;
8967 for (; iRun < pTL->runCount && pTL->pRuns[iRun].iLine == iLine; ++iRun)
8972 if (pLastRunIndexOnLineOut) {
8973 *pLastRunIndexOnLineOut = result;
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)
8985 size_t result = iRun;
8987 size_t iLine = pTL->pRuns[iRun].iLine;
8988 for (; iRun > 0 && pTL->pRuns[iRun - 1].iLine == iLine; --iRun)
8993 if (pFirstRunIndexOnLineOut) {
8994 *pFirstRunIndexOnLineOut = result;
9000 DRGUI_PRIVATE
bool drgui_text_engine__find_run_at_character(
drgui_text_engine* pTL,
size_t iChar,
size_t* pRunIndexOut)
9002 if (pTL ==
NULL || pTL->runCount == 0) {
9007 if (iChar < pTL->textLength)
9009 for (
size_t iRun = 0; iRun < pTL->runCount; ++iRun)
9013 if (iChar < pRun->iCharEnd)
9023 result = pTL->runCount - 1;
9027 *pRunIndexOut = result;
9034 DRGUI_PRIVATE drgui_text_marker drgui_text_engine__new_marker()
9036 drgui_text_marker marker;
9039 marker.relativePosX = 0;
9040 marker.absoluteSickyPosX = 0;
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)
9047 if (pTL ==
NULL || pMarker ==
NULL) {
9053 pMarker->relativePosX = 0;
9054 pMarker->absoluteSickyPosX = 0;
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))
9062 drgui_text_engine__update_marker_sticky_position(pTL, pMarker);
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)
9071 if (pTL ==
NULL || pMarker ==
NULL) {
9078 if (pMarker->iRun < pTL->runCount)
9080 posX = pTL->pRuns[pMarker->iRun].posX + pMarker->relativePosX;
9081 posY = pTL->pRuns[pMarker->iRun].posY;
9085 posX += textRect.
left;
9086 posY += textRect.
top;
9097 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_point(
drgui_text_engine* pTL, drgui_text_marker* pMarker,
float inputPosXRelativeToText,
float inputPosYRelativeToText)
9099 if (pTL ==
NULL || pMarker ==
NULL) {
9103 size_t iClosestRunToPoint;
9104 if (drgui_text_engine__find_closest_run_to_point(pTL, inputPosXRelativeToText, inputPosYRelativeToText,
OUT &iClosestRunToPoint))
9108 pMarker->iRun = iClosestRunToPoint;
9110 if (inputPosXRelativeToText < pRun->posX)
9114 pMarker->relativePosX = 0;
9116 else if (inputPosXRelativeToText > pRun->
posX + pRun->
width)
9120 pMarker->relativePosX = pRun->
width;
9122 if (pTL->text[pRun->
iChar] ==
'\n') {
9123 assert(pMarker->iChar == 1);
9125 pMarker->relativePosX = 0;
9131 if (pTL->text[pRun->
iChar] ==
'\n')
9135 pMarker->relativePosX = 0;
9137 else if (pTL->text[pRun->
iChar] ==
'\t')
9141 pMarker->relativePosX = 0;
9143 const float tabWidth = drgui_text_engine__get_tab_width(pTL);
9145 float tabLeft = pRun->
posX + pMarker->relativePosX;
9146 for (; pMarker->iChar < pRun->
textLength; ++pMarker->iChar)
9148 float tabRight = tabWidth * ((pRun->
posX + (tabWidth*(pMarker->iChar + 1))) / tabWidth);
9149 if (tabRight > pRun->
posX + pRun->
width) {
9153 if (inputPosXRelativeToText >= tabLeft && inputPosXRelativeToText <= tabRight)
9157 float charBoundsRightHalf = tabLeft + ceilf(((tabRight - tabLeft) / 2.0f));
9158 if (inputPosXRelativeToText <= charBoundsRightHalf) {
9159 pMarker->relativePosX = tabLeft - pRun->
posX;
9161 pMarker->relativePosX = tabRight - pRun->
posX;
9162 pMarker->iChar += 1;
9173 drgui_text_engine__move_marker_to_first_character_of_next_run(pTL, pMarker);
9179 float inputPosXRelativeToRun = inputPosXRelativeToText - pRun->
posX;
9184 drgui_text_engine__move_marker_to_first_character_of_next_run(pTL, pMarker);
9204 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_left(
drgui_text_engine* pTL, drgui_text_marker* pMarker)
9206 if (pTL ==
NULL || pMarker ==
NULL) {
9210 if (pTL->runCount > 0)
9212 if (pMarker->iChar > 0)
9214 pMarker->iChar -= 1;
9217 if (pTL->text[pRun->
iChar] ==
'\t')
9219 const float tabWidth = drgui_text_engine__get_tab_width(pTL);
9221 if (pMarker->iChar == 0)
9224 pMarker->relativePosX = 0;
9228 pMarker->relativePosX = tabWidth * ((pRun->
posX + (tabWidth*(pMarker->iChar + 0))) / tabWidth);
9229 pMarker->relativePosX -= pRun->
posX;
9242 if (!drgui_text_engine__move_marker_to_last_character_of_prev_run(pTL, pMarker)) {
9247 drgui_text_engine__update_marker_sticky_position(pTL, pMarker);
9254 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_right(
drgui_text_engine* pTL, drgui_text_marker* pMarker)
9256 if (pTL ==
NULL || pMarker ==
NULL) {
9260 if (pTL->runCount > 0)
9262 if (pMarker->iChar + 1 < pTL->pRuns[pMarker->iRun].textLength)
9264 pMarker->iChar += 1;
9267 if (pTL->text[pRun->
iChar] ==
'\t')
9269 const float tabWidth = drgui_text_engine__get_tab_width(pTL);
9271 pMarker->relativePosX = tabWidth * ((pRun->
posX + (tabWidth*(pMarker->iChar + 0))) / tabWidth);
9272 pMarker->relativePosX -= pRun->
posX;
9284 if (!drgui_text_engine__move_marker_to_first_character_of_next_run(pTL, pMarker)) {
9289 drgui_text_engine__update_marker_sticky_position(pTL, pMarker);
9296 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_up(
drgui_text_engine* pTL, drgui_text_marker* pMarker)
9298 if (pTL ==
NULL || pMarker ==
NULL || pTL->runCount == 0) {
9302 return drgui_text_engine__move_marker_y(pTL, pMarker, -1);
9305 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_down(
drgui_text_engine* pTL, drgui_text_marker* pMarker)
9307 if (pTL ==
NULL || pMarker ==
NULL || pTL->runCount == 0) {
9311 return drgui_text_engine__move_marker_y(pTL, pMarker, 1);
9314 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_y(
drgui_text_engine* pTL, drgui_text_marker* pMarker,
int amount)
9316 if (pTL ==
NULL || pMarker ==
NULL || pTL->runCount == 0) {
9322 int iNewLine = (int)pOldRun->
iLine + amount;
9330 if ((
int)pOldRun->
iLine == iNewLine) {
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))
9339 float newMarkerPosX = pMarker->absoluteSickyPosX;
9340 float newMarkerPosY = lineRect.
top;
9341 drgui_text_engine__move_marker_to_point(pTL, pMarker, newMarkerPosX, newMarkerPosY);
9352 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_end_of_line(
drgui_text_engine* pTL, drgui_text_marker* pMarker)
9354 if (pTL ==
NULL || pMarker ==
NULL) {
9358 size_t iLastRunOnLine;
9359 if (drgui_text_engine__find_last_run_on_line_starting_from_run(pTL, pMarker->iRun, &iLastRunOnLine))
9361 return drgui_text_engine__move_marker_to_last_character_of_run(pTL, pMarker, iLastRunOnLine);
9367 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_start_of_line(
drgui_text_engine* pTL, drgui_text_marker* pMarker)
9369 if (pTL ==
NULL || pMarker ==
NULL) {
9373 size_t iFirstRunOnLine;
9374 if (drgui_text_engine__find_first_run_on_line_starting_from_run(pTL, pMarker->iRun, &iFirstRunOnLine))
9376 return drgui_text_engine__move_marker_to_first_character_of_run(pTL, pMarker, iFirstRunOnLine);
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)
9384 if (pTL ==
NULL || pMarker ==
NULL) {
9389 size_t iLastRunPlus1;
9390 if (drgui_text_engine__find_line_info_by_index(pTL, iLine,
NULL, &iFirstRun, &iLastRunPlus1))
9392 return drgui_text_engine__move_marker_to_last_character_of_run(pTL, pMarker, iLastRunPlus1 - 1);
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)
9400 if (pTL ==
NULL || pMarker ==
NULL) {
9405 size_t iLastRunPlus1;
9406 if (drgui_text_engine__find_line_info_by_index(pTL, iLine,
NULL, &iFirstRun, &iLastRunPlus1))
9408 return drgui_text_engine__move_marker_to_first_character_of_run(pTL, pMarker, iFirstRun);
9414 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_end_of_text(
drgui_text_engine* pTL, drgui_text_marker* pMarker)
9416 if (pTL ==
NULL || pMarker ==
NULL) {
9420 if (pTL->runCount > 0) {
9421 return drgui_text_engine__move_marker_to_last_character_of_run(pTL, pMarker, pTL->runCount - 1);
9427 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_start_of_text(
drgui_text_engine* pTL, drgui_text_marker* pMarker)
9429 if (pTL ==
NULL || pMarker ==
NULL) {
9433 return drgui_text_engine__move_marker_to_first_character_of_run(pTL, pMarker, 0);
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)
9438 if (pTL ==
NULL || pMarker ==
NULL) {
9442 if (iRun < pTL->runCount)
9444 pMarker->iRun = iRun;
9445 pMarker->iChar = pTL->pRuns[pMarker->iRun].textLength;
9446 pMarker->relativePosX = pTL->pRuns[pMarker->iRun].width;
9448 if (pMarker->iChar > 0)
9451 return drgui_text_engine__move_marker_left(pTL, pMarker);
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)
9462 if (pTL ==
NULL || pMarker ==
NULL) {
9466 if (iRun < pTL->runCount)
9468 pMarker->iRun = iRun;
9470 pMarker->relativePosX = 0;
9472 drgui_text_engine__update_marker_sticky_position(pTL, pMarker);
9480 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_last_character_of_prev_run(
drgui_text_engine* pTL, drgui_text_marker* pMarker)
9482 if (pTL ==
NULL || pMarker ==
NULL) {
9486 if (pMarker->iRun > 0) {
9487 return drgui_text_engine__move_marker_to_last_character_of_run(pTL, pMarker, pMarker->iRun - 1);
9493 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_first_character_of_next_run(
drgui_text_engine* pTL, drgui_text_marker* pMarker)
9495 if (pTL ==
NULL || pMarker ==
NULL) {
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);
9506 DRGUI_PRIVATE
bool drgui_text_engine__move_marker_to_character(
drgui_text_engine* pTL, drgui_text_marker* pMarker,
size_t iChar)
9508 if (pTL ==
NULL || pMarker ==
NULL || pTL->runCount == 0) {
9513 if (iChar > pTL->textLength) {
9514 iChar = pTL->textLength;
9517 drgui_text_engine__find_run_at_character(pTL, iChar, &pMarker->iRun);
9519 assert(pMarker->iRun < pTL->runCount);
9520 pMarker->iChar = iChar - pTL->pRuns[pMarker->iRun].iChar;
9524 return drgui_text_engine__update_marker_relative_position(pTL, pMarker);
9528 DRGUI_PRIVATE
bool drgui_text_engine__update_marker_relative_position(
drgui_text_engine* pTL, drgui_text_marker* pMarker)
9530 if (pTL ==
NULL || pMarker ==
NULL || pTL->runCount == 0) {
9535 if (pTL->text[pRun->
iChar] ==
'\t')
9537 const float tabWidth = drgui_text_engine__get_tab_width(pTL);
9539 if (pMarker->iChar == 0)
9542 pMarker->relativePosX = 0;
9546 pMarker->relativePosX = tabWidth * ((pRun->
posX + (tabWidth*(pMarker->iChar + 0))) / tabWidth);
9547 pMarker->relativePosX -= pRun->
posX;
9558 DRGUI_PRIVATE
void drgui_text_engine__update_marker_sticky_position(
drgui_text_engine* pTL, drgui_text_marker* pMarker)
9560 if (pTL ==
NULL || pMarker ==
NULL) {
9564 pMarker->absoluteSickyPosX = pTL->pRuns[pMarker->iRun].posX + pMarker->relativePosX;
9567 DRGUI_PRIVATE
size_t drgui_text_engine__get_marker_absolute_char_index(
drgui_text_engine* pTL, drgui_text_marker* pMarker)
9569 if (pTL ==
NULL || pMarker ==
NULL || pTL->runCount == 0) {
9573 return pTL->pRuns[pMarker->iRun].iChar + pMarker->iChar;
9577 DRGUI_PRIVATE
bool drgui_text_engine__has_spacing_between_selection_markers(
drgui_text_engine* pTL)
9583 return (pTL->cursor.iRun != pTL->selectionAnchor.iRun || pTL->cursor.iChar != pTL->selectionAnchor.iChar);
9588 if (pTL ==
NULL || pRunToSplit ==
NULL || pSubRunsOut ==
NULL) {
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)
9596 drgui_text_marker* temp = pSelectionMarker0;
9597 pSelectionMarker0 = pSelectionMarker1;
9598 pSelectionMarker1 = temp;
9601 drgui_text_run* pSelectionRun0 = pTL->pRuns + pSelectionMarker0->iRun;
9602 drgui_text_run* pSelectionRun1 = pTL->pRuns + pSelectionMarker1->iRun;
9604 size_t iSelectionChar0 = pSelectionRun0->
iChar + pSelectionMarker0->iChar;
9605 size_t iSelectionChar1 = pSelectionRun1->
iChar + pSelectionMarker1->iChar;
9609 if (pRunToSplit->
iChar < iSelectionChar1 && pRunToSplit->
iCharEnd > iSelectionChar0)
9612 for (
int i = 0; i < 3; ++i) {
9613 pSubRunsOut[i] = *pRunToSplit;
9616 if (pRunToSplit->
iChar >= iSelectionChar0)
9619 if (pRunToSplit->
iCharEnd <= iSelectionChar1)
9631 pSubRunsOut[0].
iCharEnd = iSelectionChar1;
9632 pSubRunsOut[0].
width = pSelectionMarker1->relativePosX;
9633 pSubRunsOut[0].
text = pTL->text + pSubRunsOut[0].
iChar;
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;
9649 if (pRunToSplit->
iCharEnd <= iSelectionChar1)
9654 pSubRunsOut[0].
iCharEnd = iSelectionChar0;
9655 pSubRunsOut[0].
width = pSelectionMarker0->relativePosX;
9656 pSubRunsOut[0].
text = pTL->text + pSubRunsOut[0].
iChar;
9661 pSubRunsOut[1].
iChar = iSelectionChar0;
9663 pSubRunsOut[1].
posX = pSubRunsOut[0].
posX + pSubRunsOut[0].
width;
9664 pSubRunsOut[1].
text = pTL->text + pSubRunsOut[1].
iChar;
9674 pSubRunsOut[0].
iCharEnd = iSelectionChar0;
9675 pSubRunsOut[0].
width = pSelectionMarker0->relativePosX;
9676 pSubRunsOut[0].
text = pTL->text + pSubRunsOut[0].
iChar;
9680 pSubRunsOut[1].
iChar = iSelectionChar0;
9681 pSubRunsOut[1].
iCharEnd = iSelectionChar1;
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;
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;
9702 pSubRunsOut[0] = *pRunToSplit;
9707 DRGUI_PRIVATE
bool drgui_text_engine__is_run_selected(
drgui_text_engine* pTL,
unsigned int iRun)
9711 drgui_text_marker* pSelectionMarker0;
9712 drgui_text_marker* pSelectionMarker1;
9713 if (!drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
9717 unsigned int iSelectionChar0 = pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar;
9718 unsigned int iSelectionChar1 = pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar;
9720 return pTL->pRuns[iRun].iChar < iSelectionChar1 && pTL->pRuns[iRun].iCharEnd > iSelectionChar0;
9727 DRGUI_PRIVATE
bool drgui_text_engine__get_selection_markers(
drgui_text_engine* pTL, drgui_text_marker** ppSelectionMarker0Out, drgui_text_marker** ppSelectionMarker1Out)
9729 bool result =
false;
9731 drgui_text_marker* pSelectionMarker0 =
NULL;
9732 drgui_text_marker* pSelectionMarker1 =
NULL;
9735 pSelectionMarker0 = &pTL->selectionAnchor;
9736 pSelectionMarker1 = &pTL->cursor;
9737 if (pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar > pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar)
9739 drgui_text_marker* temp = pSelectionMarker0;
9740 pSelectionMarker0 = pSelectionMarker1;
9741 pSelectionMarker1 = temp;
9747 if (ppSelectionMarker0Out) {
9748 *ppSelectionMarker0Out = pSelectionMarker0;
9750 if (ppSelectionMarker1Out) {
9751 *ppSelectionMarker1Out = pSelectionMarker1;
9758 DRGUI_PRIVATE
bool drgui_text_engine__first_line(
drgui_text_engine* pTL, drgui_text_engine_line* pLine)
9760 if (pTL ==
NULL || pLine ==
NULL || pTL->runCount == 0) {
9767 pLine->iFirstRun = 0;
9768 pLine->iLastRun = 0;
9771 while (pLine->iLastRun < pTL->runCount)
9773 if (pLine->height < pTL->pRuns[pLine->iLastRun].height) {
9774 pLine->height = pTL->pRuns[pLine->iLastRun].height;
9777 pLine->iLastRun += 1;
9779 if (pTL->pRuns[pLine->iLastRun].iLine != pLine->index) {
9784 if (pLine->iLastRun > 0) {
9785 pLine->iLastRun -= 1;
9791 DRGUI_PRIVATE
bool drgui_text_engine__next_line(
drgui_text_engine* pTL, drgui_text_engine_line* pLine)
9793 if (pTL ==
NULL || pLine ==
NULL || pTL->runCount == 0) {
9798 if (pLine->iLastRun == pTL->runCount - 1) {
9803 pLine->posY += pLine->height;
9805 pLine->iFirstRun = pLine->iLastRun + 1;
9806 pLine->iLastRun = pLine->iFirstRun;
9808 while (pLine->iLastRun < pTL->runCount)
9810 if (pLine->height < pTL->pRuns[pLine->iLastRun].height) {
9811 pLine->height = pTL->pRuns[pLine->iLastRun].height;
9814 pLine->iLastRun += 1;
9816 if (pTL->pRuns[pLine->iLastRun].iLine != pLine->index) {
9821 if (pLine->iLastRun > 0) {
9822 pLine->iLastRun -= 1;
9834 while (pTL->undoStackCount > pTL->iUndoState)
9836 unsigned int iLastItem = pTL->undoStackCount - 1;
9838 drgui_text_engine__uninit_undo_state(pTL->pUndoStack + iLastItem);
9839 pTL->undoStackCount -= 1;
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)
9845 if (pPrevState ==
NULL || pCurrentState ==
NULL || pUndoStateOut ==
NULL) {
9849 if (pPrevState->text ==
NULL || pCurrentState->text ==
NULL) {
9853 const char* prevText = pPrevState->text;
9854 const char* currText = pCurrentState->text;
9856 const size_t prevLen = strlen(prevText);
9857 const size_t currLen = strlen(currText);
9861 size_t sameChCountStart;
9862 for (sameChCountStart = 0; sameChCountStart < prevLen && sameChCountStart < currLen; ++sameChCountStart)
9864 char prevCh = prevText[sameChCountStart];
9865 char currCh = currText[sameChCountStart];
9867 if (prevCh != currCh) {
9873 size_t sameChCountEnd;
9874 for (sameChCountEnd = 0; sameChCountEnd < prevLen && sameChCountEnd < currLen; ++sameChCountEnd)
9877 if (prevLen - sameChCountEnd <= sameChCountStart ||
9878 currLen - sameChCountEnd <= sameChCountStart)
9883 char prevCh = prevText[prevLen - sameChCountEnd - 1];
9884 char currCh = currText[currLen - sameChCountEnd - 1];
9886 if (prevCh != currCh) {
9893 pUndoStateOut->diffPos = sameChCountStart;
9894 pUndoStateOut->newState = *pCurrentState;
9895 pUndoStateOut->newState.text =
NULL;
9896 pUndoStateOut->oldState = *pPrevState;
9897 pUndoStateOut->oldState.text =
NULL;
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);
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);
9910 DRGUI_PRIVATE
void drgui_text_engine__uninit_undo_state(drgui_text_engine_undo_state* pUndoState)
9912 if (pUndoState ==
NULL) {
9916 free(pUndoState->oldText);
9917 pUndoState->oldText =
NULL;
9919 free(pUndoState->newText);
9920 pUndoState->newText =
NULL;
9923 DRGUI_PRIVATE
void drgui_text_engine__push_undo_state(
drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState)
9925 if (pTL ==
NULL || pUndoState ==
NULL) {
9929 assert(pTL->iUndoState == pTL->undoStackCount);
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));
9935 if (pTL->undoStackCount > 0) {
9936 memcpy(pNewStack, pOldStack,
sizeof(*pNewStack) * (pTL->undoStackCount));
9939 pNewStack[pTL->undoStackCount] = *pUndoState;
9940 pTL->pUndoStack = pNewStack;
9941 pTL->undoStackCount += 1;
9942 pTL->iUndoState += 1;
9944 if (pTL->onUndoPointChanged) {
9945 pTL->onUndoPointChanged(pTL, pTL->iUndoState);
9951 DRGUI_PRIVATE
void drgui_text_engine__apply_undo_state(
drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState)
9953 if (pTL ==
NULL || pUndoState ==
NULL) {
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)
9964 memmove(pTL->text + iFirstCh, pTL->text + iLastChPlus1, pTL->textLength - iLastChPlus1);
9965 pTL->textLength -= bytesToRemove;
9966 pTL->text[pTL->textLength] =
'\0';
9976 drgui_text_engine__refresh(pTL);
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);
9984 drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
9987 pTL->isAnythingSelected = pUndoState->oldState.isAnythingSelected;
9990 if (pTL->onTextChanged) {
9991 pTL->onTextChanged(pTL);
9994 drgui_text_engine__on_cursor_move(pTL);
9997 pTL->onDirty(pTL, drgui_text_engine__local_rect(pTL));
10001 DRGUI_PRIVATE
void drgui_text_engine__apply_redo_state(
drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState)
10003 if (pTL ==
NULL || pUndoState ==
NULL) {
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)
10014 memmove(pTL->text + iFirstCh, pTL->text + iLastChPlus1, pTL->textLength - iLastChPlus1);
10015 pTL->textLength -= bytesToRemove;
10016 pTL->text[pTL->textLength] =
'\0';
10026 drgui_text_engine__refresh(pTL);
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);
10034 drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
10037 pTL->isAnythingSelected = pUndoState->newState.isAnythingSelected;
10040 if (pTL->onTextChanged) {
10041 pTL->onTextChanged(pTL);
10044 drgui_text_engine__on_cursor_move(pTL);
10046 if (pTL->onDirty) {
10047 pTL->onDirty(pTL, drgui_text_engine__local_rect(pTL));
10058 return drgui_make_rect(0, 0, pTL->containerWidth, pTL->containerHeight);
10069 pTL->timeToNextCursorBlink = pTL->cursorBlinkRate;
10070 pTL->isCursorBlinkOn =
true;
10072 if (pTL->onCursorMove) {
10073 pTL->onCursorMove(pTL);
10081 drgui_text_engine__begin_dirty(pTL);
10083 pTL->accumulatedDirtyRect =
drgui_rect_union(pTL->accumulatedDirtyRect, rect);
10085 drgui_text_engine__end_dirty(pTL);
10094 pTL->dirtyCounter += 1;
10103 assert(pTL->dirtyCounter > 0);
10105 pTL->dirtyCounter -= 1;
10107 if (pTL->dirtyCounter == 0) {
10108 if (pTL->onDirty) {
10109 pTL->onDirty(pTL, pTL->accumulatedDirtyRect);
10115 #endif //DR_GUI_IMPLEMENTATION
10129 #ifndef drgui_scrollbar_h
10130 #define drgui_scrollbar_h
10288 #endif //drgui_scrollbar_h
10291 #ifdef DR_GUI_IMPLEMENTATION
10293 #define DRGUI_MIN_SCROLLBAR_THUMB_SIZE 16
10313 bool autoHideThumb;
10316 int mouseWheelScale;
10341 float thumbPadding;
10350 float thumbClickPosX;
10353 float thumbClickPosY;
10357 size_t extraDataSize;
10360 char pExtraData[1];
10366 DRGUI_PRIVATE
void drgui_sb_refresh_thumb(
drgui_element* pSBElement);
10369 DRGUI_PRIVATE
float drgui_sb_calculate_thumb_size(
drgui_element* pSBElement);
10372 DRGUI_PRIVATE
float drgui_sb_calculate_thumb_position(
drgui_element* pSBElement);
10375 DRGUI_PRIVATE
float drgui_sb_get_track_size(
drgui_element* pSBElement);
10378 DRGUI_PRIVATE
void drgui_sb_make_relative_to_thumb(
drgui_element* pSBElement,
float* pPosX,
float* pPosY);
10381 DRGUI_PRIVATE
int drgui_sb_calculate_scroll_pos_from_thumb_pos(
drgui_element* pScrollba,
float thumbPosr);
10384 DRGUI_PRIVATE
float drgui_sb_clampf(
float n,
float lower,
float upper)
10386 return n <= lower ? lower : n >= upper ? upper : n;
10390 DRGUI_PRIVATE
int drgui_sb_clampi(
int n,
int lower,
int upper)
10392 return n <= lower ? lower : n >= upper ? upper : n;
10396 DRGUI_PRIVATE
int drgui_sb_maxi(
int x,
int y)
10398 return (x > y) ? x : y;
10409 if (pSBElement ==
NULL) {
10414 assert(pSB !=
NULL);
10416 pSB->orientation = orientation;
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;
10429 pSB->thumbSize = DRGUI_MIN_SCROLLBAR_THUMB_SIZE;
10431 pSB->thumbPadding = 2;
10432 pSB->thumbHovered =
false;
10433 pSB->thumbPressed =
false;
10434 pSB->thumbClickPosX = 0;
10435 pSB->thumbClickPosY = 0;
10437 pSB->extraDataSize = extraDataSize;
10438 if (pExtraData !=
NULL) {
10439 memcpy(pSB->pExtraData, pExtraData, extraDataSize);
10458 if (pSBElement ==
NULL) {
10473 return pSB->extraDataSize;
10483 return pSB->pExtraData;
10494 return pSB->orientation;
10505 pSB->rangeMin = rangeMin;
10506 pSB->rangeMax = rangeMax;
10513 drgui_sb_refresh_thumb(pSBElement);
10523 if (pRangeMinOut !=
NULL) {
10524 *pRangeMinOut = pSB->rangeMin;
10527 if (pRangeMaxOut !=
NULL) {
10528 *pRangeMaxOut = pSB->rangeMax;
10540 pSB->pageSize = pageSize;
10547 drgui_sb_refresh_thumb(pSBElement);
10557 return pSB->pageSize;
10568 pSB->rangeMin = rangeMin;
10569 pSB->rangeMax = rangeMax;
10570 pSB->pageSize = pageSize;
10577 drgui_sb_refresh_thumb(pSBElement);
10588 int newScrollPos = drgui_sb_clampi(position, pSB->rangeMin, drgui_sb_maxi(0, pSB->rangeMax - pSB->pageSize + 1));
10589 if (newScrollPos != pSB->scrollPos)
10591 pSB->scrollPos = newScrollPos;
10594 drgui_sb_refresh_thumb(pSBElement);
10605 return pSB->scrollPos;
10626 int oldScrollPos = pSB->scrollPos;
10629 if (oldScrollPos != pSB->scrollPos)
10631 if (pSB->onScroll) {
10632 pSB->onScroll(pSBElement, pSB->scrollPos);
10645 if (pSB->autoHideThumb !=
true)
10647 pSB->autoHideThumb =
true;
10650 drgui_sb_refresh_thumb(pSBElement);
10661 if (pSB->autoHideThumb !=
false)
10663 pSB->autoHideThumb =
false;
10666 drgui_sb_refresh_thumb(pSBElement);
10677 return pSB->autoHideThumb;
10688 if (!pSB->autoHideThumb) {
10692 return pSB->pageSize < (pSB->rangeMax - pSB->rangeMin + 1) && pSB->pageSize > 0;
10703 pSB->mouseWheelScale = scale;
10713 return pSB->mouseWheelScale;
10724 pSB->trackColor = color;
10734 pSB->thumbColor = color;
10744 pSB->thumbColorHovered = color;
10754 pSB->thumbColorPressed = color;
10765 pSB->onScroll = onScroll;
10775 return pSB->onScroll;
10787 rect.
left = pSB->thumbPadding;
10788 rect.
top = pSB->thumbPadding;
10793 rect.
left = pSB->thumbPadding;
10795 rect.
top = pSB->thumbPadding + pSB->thumbPos;
10796 rect.
bottom = rect.
top + pSB->thumbSize;
10801 rect.
left = pSB->thumbPadding + pSB->thumbPos;
10802 rect.
right = rect.
left + pSB->thumbSize;
10803 rect.
top = pSB->thumbPadding;
10821 drgui_sb_refresh_thumb(pSBElement);
10831 bool needsRedraw =
false;
10832 if (pSB->thumbHovered)
10834 needsRedraw =
true;
10835 pSB->thumbHovered =
false;
10838 if (pSB->thumbPressed)
10840 needsRedraw =
true;
10841 pSB->thumbPressed =
false;
10858 if (pSB->thumbPressed)
10861 float thumbRelativeMousePosX = (float)relativeMousePosX;
10862 float thumbRelativeMousePosY = (float)relativeMousePosY;
10863 drgui_sb_make_relative_to_thumb(pSBElement, &thumbRelativeMousePosX, &thumbRelativeMousePosY);
10865 float dragOffsetX = thumbRelativeMousePosX - pSB->thumbClickPosX;
10866 float dragOffsetY = thumbRelativeMousePosY - pSB->thumbClickPosY;
10868 float destTrackPos = pSB->thumbPos;
10870 destTrackPos += dragOffsetY;
10872 destTrackPos += dragOffsetX;
10875 int destScrollPos = drgui_sb_calculate_scroll_pos_from_thumb_pos(pSBElement, destTrackPos);
10876 if (destScrollPos != pSB->scrollPos)
10886 bool wasThumbHovered = pSB->thumbHovered;
10891 if (wasThumbHovered != pSB->thumbHovered) {
10914 if (!pSB->thumbPressed)
10917 pSB->thumbPressed =
true;
10919 pSB->thumbClickPosX = (float)relativeMousePosX;
10920 pSB->thumbClickPosY = (float)relativeMousePosY;
10921 drgui_sb_make_relative_to_thumb(pSBElement, &pSB->thumbClickPosX, &pSB->thumbClickPosY);
10929 if (relativeMousePosY < thumbRect.
top) {
10931 }
else if (relativeMousePosY >= thumbRect.
bottom) {
10941 (void)relativeMousePosX;
10942 (void)relativeMousePosY;
10955 pSB->thumbPressed =
false;
10964 (void)relativeMousePosX;
10965 (void)relativeMousePosY;
10978 (void)relativeClippingRect;
10999 if (pSB->thumbPressed) {
11000 thumbColor = pSB->thumbColorPressed;
11001 }
else if (pSB->thumbHovered) {
11002 thumbColor = pSB->thumbColorHovered;
11004 thumbColor = pSB->thumbColor;
11018 DRGUI_PRIVATE
void drgui_sb_refresh_thumb(
drgui_element* pSBElement)
11021 assert(pSB !=
NULL);
11025 pSB->thumbSize = drgui_sb_calculate_thumb_size(pSBElement);
11026 pSB->thumbPos = drgui_sb_calculate_thumb_position(pSBElement);
11035 DRGUI_PRIVATE
float drgui_sb_calculate_thumb_size(
drgui_element* pSBElement)
11038 assert(pSB !=
NULL);
11040 float trackSize = drgui_sb_get_track_size(pSBElement);
11041 float range = (float)(pSB->rangeMax - pSB->rangeMin + 1);
11043 float thumbSize = DRGUI_MIN_SCROLLBAR_THUMB_SIZE;
11046 thumbSize = roundf((trackSize / range) * pSB->pageSize);
11047 thumbSize = drgui_sb_clampf(thumbSize, DRGUI_MIN_SCROLLBAR_THUMB_SIZE, trackSize);
11053 DRGUI_PRIVATE
float drgui_sb_calculate_thumb_position(
drgui_element* pSBElement)
11056 assert(pSB !=
NULL);
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);
11062 float thumbPos = 0;
11063 if (range > pSB->pageSize)
11065 thumbPos = roundf((trackSize / range) * pSB->scrollPos);
11066 thumbPos = drgui_sb_clampf(thumbPos, 0, trackSize - thumbSize);
11072 DRGUI_PRIVATE
float drgui_sb_get_track_size(
drgui_element* pSBElement)
11075 assert(pSB !=
NULL);
11084 DRGUI_PRIVATE
void drgui_sb_make_relative_to_thumb(
drgui_element* pSBElement,
float* pPosX,
float* pPosY)
11088 if (pPosX !=
NULL) {
11089 *pPosX -= thumbRect.
left;
11092 if (pPosY !=
NULL) {
11093 *pPosY -= thumbRect.
top;
11097 DRGUI_PRIVATE
int drgui_sb_calculate_scroll_pos_from_thumb_pos(
drgui_element* pSBElement,
float thumbPos)
11100 assert(pSB !=
NULL);
11102 float trackSize = drgui_sb_get_track_size(pSBElement);
11103 float range = (float)(pSB->rangeMax - pSB->rangeMin + 1);
11105 return (
int)roundf(thumbPos / (trackSize / range));
11107 #endif //DR_GUI_IMPLEMENTATION
11124 #ifndef drgui_tab_bar_h
11125 #define drgui_tab_bar_h
11131 #define DRGUI_MAX_TAB_TEXT_LENGTH 256
11401 #endif //drgui_tab_bar_h
11404 #ifdef DR_GUI_IMPLEMENTATION
11405 typedef struct drgui_tab_bar drgui_tab_bar;
11407 struct drgui_tab_bar
11458 float closeButtonPaddingLeft;
11474 bool isAutoSizeEnabled;
11477 bool isShowingCloseButton;
11480 bool isCloseOnMiddleClickEnabled;
11483 bool isCloseButtonHovered;
11506 size_t extraDataSize;
11509 char pExtraData[1];
11529 size_t extraDataSize;
11532 char pExtraData[1];
11543 DRGUI_PRIVATE
void drgui_tabbar_on_measure_tab_default(
drgui_element* pTBElement,
drgui_tab* pTab,
float* pWidthOut,
float* pHeightOut);
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);
11549 DRGUI_PRIVATE
drgui_tab* drgui_tabbar_find_tab_under_point(
drgui_element* pTBElement,
float relativePosX,
float relativePosY,
bool* pIsOverCloseButtonOut);
11553 if (pContext ==
NULL) {
11558 if (pTBElement ==
NULL) {
11563 assert(pTB !=
NULL);
11565 pTB->orientation = orientation;
11566 pTB->pFirstTab =
NULL;
11567 pTB->pLastTab =
NULL;
11568 pTB->pHoveredTab =
NULL;
11569 pTB->pActiveTab =
NULL;
11570 pTB->pTabWithCloseButtonPressed =
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);
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;
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;
11598 pTB->extraDataSize = extraDataSize;
11599 if (pExtraData !=
NULL) {
11600 memcpy(pTB->pExtraData, pExtraData, extraDataSize);
11621 while (pTB->pFirstTab !=
NULL) {
11637 return pTB->extraDataSize;
11647 return pTB->pExtraData;
11657 return pTB->orientation;
11668 pTB->pFont = pFont;
11671 if (pTB->isAutoSizeEnabled) {
11698 pTB->tabTextColor = color;
11712 return pTB->tabTextColor;
11722 pTB->tabTextColorActivated = color;
11736 pTB->tabTextColorHovered = color;
11751 pTB->pCloseButtonImage = pImage;
11765 return pTB->pCloseButtonImage;
11775 pTB->closeButtonColorDefault = color;
11790 pTB->tabPadding = padding;
11804 return pTB->tabPadding;
11814 pTB->closeButtonPaddingLeft = padding;
11828 return pTB->closeButtonPaddingLeft;
11839 pTB->tabBackgroundColor = color;
11853 return pTB->tabBackgroundColor;
11863 pTB->tabBackgroundColorHovered = color;
11877 return pTB->tabBackgroundColorHovered;
11887 pTB->tabBackbroundColorActivated = color;
11901 return pTB->tabBackbroundColorActivated;
11912 pTB->onMeasureTab = proc;
11922 pTB->onPaintTab = proc;
11932 pTB->onTabActivated = proc;
11942 pTB->onTabDeactivated = proc;
11952 pTB->onTabClose = proc;
11962 pTB->onTabMouseButtonUp = proc;
11973 if (pTB->onMeasureTab) {
11974 pTB->onMeasureTab(pTBElement, pTab, pWidthOut, pHeightOut);
11985 if (pTB->onPaintTab) {
11986 pTB->onPaintTab(pTBElement, pTab, relativeClippingRect, offsetX, offsetY, width, height, pPaintData);
11998 if (pTB->onMeasureTab ==
NULL) {
12002 float maxWidth = 0;
12003 float maxHeight = 0;
12004 if (pTB->pFirstTab ==
NULL) {
12009 maxHeight = fontMetrics.
lineHeight + (pTB->tabPadding*2);
12011 maxWidth = fontMetrics.
lineHeight + (pTB->tabPadding*2);
12015 for (
drgui_tab* pTab = pTB->pFirstTab; pTab !=
NULL; pTab = pTab->pNextTab) {
12016 float tabWidth = 0;
12017 float tabHeight = 0;
12020 maxWidth = (tabWidth > maxWidth) ? tabWidth : maxWidth;
12021 maxHeight = (tabHeight > maxHeight) ? tabHeight : maxHeight;
12042 pTB->isAutoSizeEnabled =
true;
12052 pTB->isAutoSizeEnabled =
false;
12062 return pTB->isAutoSizeEnabled;
12073 return pTB->pFirstTab;
12083 return pTB->pLastTab;
12106 drgui_tab* pOldActiveTab = pTB->pActiveTab;
12109 if (pOldActiveTab == pNewActiveTab) {
12114 pTB->pActiveTab = pNewActiveTab;
12116 if (pTB->onTabDeactivated && pOldActiveTab !=
NULL) {
12117 pTB->onTabDeactivated(pTBElement, pOldActiveTab, pNewActiveTab);
12120 if (pTB->onTabActivated && pNewActiveTab !=
NULL) {
12121 pTB->onTabActivated(pTBElement, pNewActiveTab, pOldActiveTab);
12137 if (pTB->pActiveTab ==
NULL) {
12143 drgui_tab* pNextTab = pTB->pActiveTab->pNextTab;
12144 if (pNextTab ==
NULL) {
12145 pNextTab = pTB->pFirstTab;
12158 if (pTB->pActiveTab ==
NULL) {
12164 drgui_tab* pPrevTab = pTB->pActiveTab->pPrevTab;
12165 if (pPrevTab ==
NULL) {
12166 pPrevTab = pTB->pLastTab;
12179 return pTB->pActiveTab;
12190 float tabbarWidth = 0;
12191 float tabbarHeight = 0;
12196 float runningPosX = 0;
12197 float runningPosY = 0;
12198 for (
drgui_tab* pTab = pTB->pFirstTab; pTab !=
NULL; pTab = pTab->pNextTab)
12200 float tabWidth = 0;
12201 float tabHeight = 0;
12204 if (pTab == pTabIn) {
12205 return runningPosX + tabWidth <= tabbarWidth && runningPosY + tabHeight <= tabbarHeight;
12210 runningPosX += tabWidth;
12212 runningPosY += tabHeight;
12227 pTB->isShowingCloseButton =
true;
12241 pTB->isShowingCloseButton =
false;
12256 pTB->isCloseOnMiddleClickEnabled =
true;
12266 pTB->isCloseOnMiddleClickEnabled =
false;
12276 return pTB->isCloseOnMiddleClickEnabled;
12287 if (pTB->pHoveredTab !=
NULL)
12289 pTB->pHoveredTab =
NULL;
12290 pTB->isCloseButtonHovered =
false;
12307 bool isCloseButtonHovered =
false;
12309 drgui_tab* pOldHoveredTab = pTB->pHoveredTab;
12310 drgui_tab* pNewHoveredTab = drgui_tabbar_find_tab_under_point(pTBElement, (
float)relativeMousePosX, (
float)relativeMousePosY, &isCloseButtonHovered);
12312 if (pOldHoveredTab != pNewHoveredTab || pTB->isCloseButtonHovered != isCloseButtonHovered)
12314 pTB->pHoveredTab = pNewHoveredTab;
12315 pTB->isCloseButtonHovered = isCloseButtonHovered;
12334 bool isOverCloseButton =
false;
12336 drgui_tab* pOldActiveTab = pTB->pActiveTab;
12337 drgui_tab* pNewActiveTab = drgui_tabbar_find_tab_under_point(pTBElement, (
float)relativeMousePosX, (
float)relativeMousePosY, &isOverCloseButton);
12339 if (pNewActiveTab !=
NULL && pOldActiveTab != pNewActiveTab && !isOverCloseButton) {
12344 pTB->pTabWithCloseButtonPressed = pNewActiveTab;
12353 if (pTB->isCloseOnMiddleClickEnabled)
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);
12375 bool releasedOverCloseButton =
false;
12376 drgui_tab* pTabUnderMouse = drgui_tabbar_find_tab_under_point(pTBElement, (
float)relativeMousePosX, (
float)relativeMousePosY, &releasedOverCloseButton);
12380 if (releasedOverCloseButton && pTabUnderMouse == pTB->pTabWithCloseButtonPressed) {
12381 if (pTB->onTabClose) {
12382 pTB->onTabClose(pTBElement, pTB->pTabWithCloseButtonPressed);
12387 pTB->pTabWithCloseButtonPressed =
NULL;
12395 if (!releasedOverCloseButton && pTB->onTabMouseButtonUp) {
12398 pTB->onTabMouseButtonUp(pTBElement, pTabUnderMouse, mouseButton, relativeMousePosX, relativeMousePosY, stateFlags);
12411 float tabbarWidth = 0;
12412 float tabbarHeight = 0;
12417 float runningPosX = 0;
12418 float runningPosY = 0;
12419 for (
drgui_tab* pTab = pTB->pFirstTab; pTab !=
NULL; pTab = pTab->pNextTab)
12421 float tabWidth = 0;
12422 float tabHeight = 0;
12426 if (runningPosX > tabbarWidth || runningPosY > tabbarHeight) {
12431 drgui_tabbar_paint_tab(pTBElement, pTab, relativeClippingRect, runningPosX, runningPosY, tabWidth, tabHeight, pPaintData);
12436 drgui_draw_rect(pTBElement,
drgui_make_rect(runningPosX, runningPosY + tabHeight, tabbarWidth, tabbarHeight), pTB->tabBackgroundColor, pPaintData);
12438 drgui_draw_rect(pTBElement,
drgui_make_rect(runningPosX + tabWidth, runningPosY, tabbarWidth, runningPosY + tabHeight), pTB->tabBackgroundColor, pPaintData);
12444 runningPosX += tabWidth;
12446 runningPosY += tabHeight;
12456 DRGUI_PRIVATE
void drgui_tabbar_on_measure_tab_default(
drgui_element* pTBElement,
drgui_tab* pTab,
float* pWidthOut,
float* pHeightOut)
12463 float textWidth = 0;
12464 float textHeight = 0;
12466 if (pTab !=
NULL) {
12471 float closeButtonWidth = 0;
12472 if (pTB->isShowingCloseButton && pTB->pCloseButtonImage !=
NULL) {
12473 unsigned int closeImageWidth;
12476 closeButtonWidth = closeImageWidth + pTB->closeButtonPaddingLeft;
12481 *pWidthOut = textWidth + closeButtonWidth + pTB->tabPadding*2;
12484 *pHeightOut = textHeight + pTB->tabPadding*2;
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)
12490 (void)relativeClippingRect;
12499 drgui_color closeButtonColor = pTB->closeButtonColorDefault;
12502 if (pTB->pHoveredTab == pTab) {
12503 bgcolor = pTB->tabBackgroundColorHovered;
12504 closeButtonColor = pTB->closeButtonColorTabHovered;
12505 textColor = pTB->tabTextColorHovered;
12507 if (pTB->pActiveTab == pTab) {
12508 bgcolor = pTB->tabBackbroundColorActivated;
12509 closeButtonColor = pTB->closeButtonColorTabHovered;
12510 textColor = pTB->tabTextColorActivated;
12513 if (pTB->pHoveredTab == pTab && pTB->isCloseButtonHovered) {
12514 closeButtonColor = pTB->closeButtonColorHovered;
12516 if (pTB->pTabWithCloseButtonPressed == pTB->pHoveredTab) {
12517 closeButtonColor = pTB->closeButtonColorPressed;
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);
12533 if (pTB->isShowingCloseButton && pTB->pCloseButtonImage !=
NULL)
12535 float textWidth = 0;
12536 float textHeight = 0;
12537 if (pTab !=
NULL) {
12541 float closeButtonPosX = textPosX + textWidth + pTB->closeButtonPaddingLeft;
12542 float closeButtonPosY = textPosY;
12544 unsigned int iconWidth;
12545 unsigned int iconHeight;
12549 args.dstX = closeButtonPosX;
12550 args.dstY = closeButtonPosY;
12551 args.dstWidth = (float)iconWidth;
12552 args.dstHeight = (float)iconHeight;
12555 args.srcWidth = (float)iconWidth;
12556 args.srcHeight = (float)iconHeight;
12559 args.dstBoundsWidth = (float)iconWidth;
12560 args.dstBoundsHeight = height - (pTB->tabPadding*2);
12561 args.foregroundTint = closeButtonColor;
12562 args.backgroundColor = bgcolor;
12563 args.boundsColor = bgcolor;
12573 DRGUI_PRIVATE
drgui_tab* drgui_tabbar_find_tab_under_point(
drgui_element* pTBElement,
float relativePosX,
float relativePosY,
bool* pIsOverCloseButtonOut)
12580 unsigned int closeButtonWidth;
12581 unsigned int closeButtonHeight;
12584 float runningPosX = 0;
12585 float runningPosY = 0;
12586 for (
drgui_tab* pTab = pTB->pFirstTab; pTab !=
NULL; pTab = pTab->pNextTab)
12588 float tabWidth = 0;
12589 float tabHeight = 0;
12592 if (relativePosX >= runningPosX && relativePosX < runningPosX + tabWidth && relativePosY >= runningPosY && relativePosY < runningPosY + tabHeight)
12594 if (pIsOverCloseButtonOut)
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;
12604 *pIsOverCloseButtonOut =
true;
12606 *pIsOverCloseButtonOut =
false;
12614 runningPosX += tabWidth;
12616 runningPosY += tabHeight;
12621 if (pIsOverCloseButtonOut) {
12622 *pIsOverCloseButtonOut =
false;
12645 DRGUI_PRIVATE
void tab_detach_from_hierarchy(
drgui_tab* pTab);
12648 DRGUI_PRIVATE
void tab_detach(
drgui_tab* pTab);
12650 DRGUI_PRIVATE
drgui_tab* tb_create_tab(
drgui_element* pTBElement,
const char* text,
size_t extraDataSize,
const void* pExtraData)
12652 if (pTBElement ==
NULL) {
12657 if (pTab ==
NULL) {
12661 pTab->pTBElement =
NULL;
12662 pTab->pNextTab =
NULL;
12663 pTab->pPrevTab =
NULL;
12664 pTab->text[0] =
'\0';
12666 pTab->extraDataSize = extraDataSize;
12668 memcpy(pTab->pExtraData, pExtraData, extraDataSize);
12671 if (text !=
NULL) {
12672 drgui__strncpy_s(pTab->text,
sizeof(pTab->text), text, (
size_t)-1);
12680 drgui_tab* pTab = (
drgui_tab*)tb_create_tab(pTBElement, text, extraDataSize, pExtraData);
12683 tab_append(pTab, pTBElement);
12691 drgui_tab* pTab = (
drgui_tab*)tb_create_tab(pTBElement, text, extraDataSize, pExtraData);
12694 tab_prepend(pTab, pTBElement);
12702 if (pTab ==
NULL) {
12712 if (pTab ==
NULL) {
12716 return pTab->pTBElement;
12721 if (pTab ==
NULL) {
12725 return pTab->extraDataSize;
12730 if (pTab ==
NULL) {
12734 return pTab->pExtraData;
12740 if (pTab ==
NULL) {
12744 if (text !=
NULL) {
12745 drgui__strncpy_s(pTab->text,
sizeof(pTab->text), text, (
size_t)-1);
12747 pTab->text[0] =
'\0';
12758 if (pTab ==
NULL) {
12768 if (pTab ==
NULL) {
12772 return pTab->pNextTab;
12777 if (pTab ==
NULL) {
12781 return pTab->pPrevTab;
12787 if (pTab ==
NULL) {
12793 tab_detach_from_hierarchy(pTab);
12794 tab_prepend(pTab, pTBElement);
12799 if (pTab ==
NULL) {
12818 if (pTab ==
NULL || pTBElement ==
NULL) {
12823 assert(pTB !=
NULL);
12825 pTab->pTBElement = pTBElement;
12826 if (pTB->pFirstTab ==
NULL)
12828 assert(pTB->pLastTab ==
NULL);
12830 pTB->pFirstTab = pTab;
12831 pTB->pLastTab = pTab;
12835 assert(pTB->pLastTab !=
NULL);
12837 pTab->pPrevTab = pTB->pLastTab;
12839 pTB->pLastTab->pNextTab = pTab;
12840 pTB->pLastTab = pTab;
12844 if (pTB->isAutoSizeEnabled) {
12856 if (pTab ==
NULL || pTBElement ==
NULL) {
12861 assert(pTB !=
NULL);
12863 pTab->pTBElement = pTBElement;
12864 if (pTB->pFirstTab ==
NULL)
12866 assert(pTB->pLastTab ==
NULL);
12868 pTB->pFirstTab = pTab;
12869 pTB->pLastTab = pTab;
12873 assert(pTB->pLastTab !=
NULL);
12875 pTab->pNextTab = pTB->pFirstTab;
12877 pTB->pFirstTab->pPrevTab = pTab;
12878 pTB->pFirstTab = pTab;
12882 if (pTB->isAutoSizeEnabled) {
12892 DRGUI_PRIVATE
void tab_detach_from_hierarchy(
drgui_tab* pTab)
12894 if (pTab ==
NULL) {
12899 if (pTBElement ==
NULL) {
12904 assert(pTB !=
NULL);
12907 if (pTab->pNextTab !=
NULL) {
12908 pTab->pNextTab->pPrevTab = pTab->pPrevTab;
12911 if (pTab->pPrevTab !=
NULL) {
12912 pTab->pPrevTab->pNextTab = pTab->pNextTab;
12916 if (pTab == pTB->pFirstTab) {
12917 pTB->pFirstTab = pTab->pNextTab;
12920 if (pTab == pTB->pLastTab) {
12921 pTB->pLastTab = pTab->pPrevTab;
12925 pTab->pNextTab =
NULL;
12926 pTab->pPrevTab =
NULL;
12927 pTab->pTBElement =
NULL;
12930 DRGUI_PRIVATE
void tab_detach(
drgui_tab* pTab)
12932 if (pTab ==
NULL) {
12937 if (pTBElement ==
NULL) {
12942 assert(pTB !=
NULL);
12944 if (pTB->pHoveredTab == pTab) {
12945 pTB->pHoveredTab =
NULL;
12946 pTB->isCloseButtonHovered =
false;
12949 if (pTB->pActiveTab == pTab) {
12950 pTB->pActiveTab =
NULL;
12953 if (pTB->pTabWithCloseButtonPressed == pTab) {
12954 pTB->pTabWithCloseButtonPressed =
NULL;
12958 tab_detach_from_hierarchy(pTab);
12961 if (pTB->isAutoSizeEnabled) {
12970 #endif //DR_GUI_IMPLEMENTATION
12981 #ifndef DRGUI_NO_TEXT_EDITING
12987 #ifndef drgui_textbox_h
12988 #define drgui_textbox_h
13258 #endif //drgui_textbox_h
13261 #ifdef DR_GUI_IMPLEMENTATION
13287 float lineNumbersWidth;
13290 float lineNumbersPaddingRight;
13300 float vertScrollbarSize;
13303 float horzScrollbarSize;
13306 bool isVertScrollbarEnabled;
13309 bool isHorzScrollbarEnabled;
13313 size_t iLineSelectAnchor;
13324 size_t extraDataSize;
13327 char pExtraData[1];
13333 DRGUI_PRIVATE
void drgui_textbox__get_text_offset(
drgui_element* pTBElement,
float* pOffsetXOut,
float* pOffsetYOut);
13336 DRGUI_PRIVATE
void drgui_textbox__calculate_text_engine_container_size(
drgui_element* pTBElement,
float* pWidthOut,
float* pHeightOut);
13342 DRGUI_PRIVATE
void drgui_textbox__refresh_scrollbars(
drgui_element* pTBElement);
13345 DRGUI_PRIVATE
void drgui_textbox__refresh_scrollbar_ranges(
drgui_element* pTBElement);
13348 DRGUI_PRIVATE
void drgui_textbox__refresh_scrollbar_layouts(
drgui_element* pTBElement);
13355 DRGUI_PRIVATE
void drgui_textbox__on_mouse_move_line_numbers(
drgui_element* pLineNumbers,
int relativeMousePosX,
int relativeMousePosY,
int stateFlags);
13358 DRGUI_PRIVATE
void drgui_textbox__on_mouse_button_down_line_numbers(
drgui_element* pLineNumbers,
int mouseButton,
int relativeMousePosX,
int relativeMousePosY,
int stateFlags);
13361 DRGUI_PRIVATE
void drgui_textbox__on_mouse_button_up_line_numbers(
drgui_element* pLineNumbers,
int mouseButton,
int relativeMousePosX,
int relativeMousePosY,
int stateFlags);
13364 DRGUI_PRIVATE
void drgui_textbox__on_paint_line_numbers(
drgui_element* pLineNumbers,
drgui_rect relativeRect,
void* pPaintData);
13367 DRGUI_PRIVATE
void drgui_textbox__refresh_line_numbers(
drgui_element* pTBElement);
13380 DRGUI_PRIVATE
void drgui_textbox__on_text_engine_cursor_move(
drgui_text_engine* pTL);
13383 DRGUI_PRIVATE
void drgui_textbox__on_text_engine_text_changed(
drgui_text_engine* pTL);
13386 DRGUI_PRIVATE
void drgui_textbox__on_text_engine_undo_point_changed(
drgui_text_engine* pTL,
unsigned int iUndoPoint);
13389 DRGUI_PRIVATE
void drgui_textbox__on_vscroll(
drgui_element* pSBElement,
int scrollPos)
13392 assert(pTBElement !=
NULL);
13395 assert(pTB !=
NULL);
13403 DRGUI_PRIVATE
void drgui_textbox__on_hscroll(
drgui_element* pSBElement,
int scrollPos)
13406 assert(pTBElement !=
NULL);
13409 assert(pTB !=
NULL);
13418 if (pContext ==
NULL) {
13423 if (pTBElement ==
NULL) {
13443 assert(pTB !=
NULL);
13452 pTB->pLineNumbers =
drgui_create_element(pContext, pTBElement,
sizeof(pTBElement), &pTBElement);
13460 if (pTB->pTL ==
NULL) {
13478 pTB->borderWidth = 1;
13480 pTB->lineNumbersWidth = 64;
13481 pTB->lineNumbersPaddingRight = 16;
13482 pTB->lineNumbersColor =
drgui_rgb(80, 160, 192);
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;
13492 pTB->extraDataSize = extraDataSize;
13493 if (pExtraData !=
NULL) {
13494 memcpy(pTB->pExtraData, pExtraData, extraDataSize);
13512 if (pTB->pLineNumbers) {
13514 pTB->pLineNumbers =
NULL;
13517 if (pTB->pHorzScrollbar) {
13519 pTB->pHorzScrollbar =
NULL;
13522 if (pTB->pVertScrollbar) {
13524 pTB->pVertScrollbar =
NULL;
13537 return pTB->extraDataSize;
13547 return pTB->pExtraData;
13563 drgui_textbox__refresh_line_numbers(pTBElement);
13567 drgui_textbox__refresh_scrollbars(pTBElement);
13673 pTB->borderColor = color;
13683 pTB->borderWidth = borderWidth;
13693 pTB->padding = padding;
13703 return pTB->padding;
13713 return pTB->padding;
13743 pTB->lineNumbersWidth = lineNumbersWidth;
13753 return pTB->lineNumbersWidth;
13763 pTB->lineNumbersPaddingRight = lineNumbersPadding;
13773 return pTB->lineNumbersPaddingRight;
13783 pTB->lineNumbersColor = color;
13784 drgui_textbox__refresh_line_numbers(pTBElement);
13794 return pTB->lineNumbersColor;
13804 pTB->lineNumbersBackgroundColor = color;
13805 drgui_textbox__refresh_line_numbers(pTBElement);
13815 return pTB->lineNumbersBackgroundColor;
13932 bool wasTextChanged =
false;
13939 return wasTextChanged;
13949 bool wasTextChanged =
false;
13956 return wasTextChanged;
13966 bool wasTextChanged =
false;
13973 return wasTextChanged;
14065 size_t selectionStart;
14066 size_t selectionEnd;
14085 bool wasTextChanged =
false;
14088 size_t selectionStart;
14089 size_t selectionEnd;
14101 return wasTextChanged;
14116 bool wasTextChanged =
false;
14123 size_t selectionStart;
14124 size_t selectionEnd;
14135 size_t lineCharStart;
14136 size_t lineCharEnd;
14139 size_t newCursorPos = lineCharStart + originalCursorPos;
14140 if (newCursorPos > lineCharEnd) {
14141 newCursorPos = lineCharEnd;
14152 return wasTextChanged;
14164 drgui_textbox__refresh_line_numbers(pTBElement);
14175 drgui_textbox__refresh_line_numbers(pTBElement);
14186 if (pTB->isVertScrollbarEnabled) {
14187 pTB->isVertScrollbarEnabled =
false;
14188 drgui_textbox__refresh_scrollbars(pTBElement);
14199 if (!pTB->isVertScrollbarEnabled) {
14200 pTB->isVertScrollbarEnabled =
true;
14201 drgui_textbox__refresh_scrollbars(pTBElement);
14212 if (pTB->isHorzScrollbarEnabled) {
14213 pTB->isHorzScrollbarEnabled =
false;
14214 drgui_textbox__refresh_scrollbars(pTBElement);
14225 if (!pTB->isHorzScrollbarEnabled) {
14226 pTB->isHorzScrollbarEnabled =
true;
14227 drgui_textbox__refresh_scrollbars(pTBElement);
14238 return pTB->pVertScrollbar;
14248 return pTB->pHorzScrollbar;
14258 pTB->horzScrollbarSize = size;
14259 pTB->vertScrollbarSize = size;
14261 drgui_textbox__refresh_scrollbars(pTBElement);
14272 pTB->onCursorMove = proc;
14282 pTB->onUndoPointChanged = proc;
14297 float containerWidth;
14298 float containerHeight;
14299 drgui_textbox__calculate_text_engine_container_size(pTBElement, &containerWidth, &containerHeight);
14303 drgui_textbox__refresh_scrollbars(pTBElement);
14306 drgui_textbox__refresh_line_numbers(pTBElement);
14322 drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
14350 drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
14370 (void)relativeMousePosX;
14371 (void)relativeMousePosY;
14392 (void)relativeMousePosX;
14393 (void)relativeMousePosY;
14404 (void)relativeMousePosX;
14405 (void)relativeMousePosY;
14427 bool wasTextChanged =
false;
14441 bool wasTextChanged =
false;
14653 drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
14664 drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
14672 if (pTBElement ==
NULL) {
14683 drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
14688 DRGUI_PRIVATE
void drgui_textbox__on_text_engine_cursor_move(
drgui_text_engine* pTL)
14692 if (pTBElement ==
NULL) {
14708 if (iLine >= iBottomLine) {
14718 if (cursorPosX < 0) {
14726 if (pTB->onCursorMove) {
14727 pTB->onCursorMove(pTBElement);
14731 DRGUI_PRIVATE
void drgui_textbox__on_text_engine_text_changed(
drgui_text_engine* pTL)
14734 if (pTBElement ==
NULL) {
14744 drgui_textbox__refresh_scrollbars(pTBElement);
14751 DRGUI_PRIVATE
void drgui_textbox__on_text_engine_undo_point_changed(
drgui_text_engine* pTL,
unsigned int iUndoPoint)
14754 if (pTBElement ==
NULL) {
14763 if (pTB->onUndoPointChanged) {
14764 pTB->onUndoPointChanged(pTBElement, iUndoPoint);
14776 drgui_rect textRect = drgui_textbox__get_text_rect(pTBElement);
14796 (void)pPrevCapturedElement;
14808 (void)pNewCapturedElement;
14839 DRGUI_PRIVATE
void drgui_textbox__get_text_offset(
drgui_element* pTBElement,
float* pOffsetXOut,
float* pOffsetYOut)
14847 float lineNumbersWidth = 0;
14852 offsetX = pTB->borderWidth + pTB->padding + lineNumbersWidth;
14853 offsetY = pTB->borderWidth + pTB->padding;
14857 if (pOffsetXOut !=
NULL) {
14858 *pOffsetXOut = offsetX;
14860 if (pOffsetYOut !=
NULL) {
14861 *pOffsetYOut = offsetY;
14865 DRGUI_PRIVATE
void drgui_textbox__calculate_text_engine_container_size(
drgui_element* pTBElement,
float* pWidthOut,
float* pHeightOut)
14873 float horzScrollbarSize = 0;
14878 float vertScrollbarSize = 0;
14883 float lineNumbersWidth = 0;
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;
14892 if (pWidthOut !=
NULL) {
14893 *pWidthOut = width;
14895 if (pHeightOut !=
NULL) {
14896 *pHeightOut = height;
14909 drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
14913 drgui_textbox__calculate_text_engine_container_size(pTBElement, &width, &height);
14915 return drgui_make_rect(offsetX, offsetY, offsetX + width, offsetY + height);
14919 DRGUI_PRIVATE
void drgui_textbox__refresh_scrollbars(
drgui_element* pTBElement)
14925 drgui_textbox__refresh_scrollbar_ranges(pTBElement);
14926 drgui_textbox__refresh_scrollbar_layouts(pTBElement);
14927 drgui_textbox__refresh_scrollbar_ranges(pTBElement);
14930 DRGUI_PRIVATE
void drgui_textbox__refresh_scrollbar_ranges(
drgui_element* pTBElement)
14933 assert(pTB !=
NULL);
14953 float containerWidth;
14960 drgui_textbox__refresh_line_numbers(pTBElement);
14965 drgui_textbox__refresh_line_numbers(pTBElement);
14970 DRGUI_PRIVATE
void drgui_textbox__refresh_scrollbar_layouts(
drgui_element* pTBElement)
14973 assert(pTB !=
NULL);
14975 float offsetLeft = pTB->borderWidth;
14976 float offsetTop = pTB->borderWidth;
14977 float offsetRight = pTB->borderWidth;
14978 float offsetBottom = pTB->borderWidth;
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;
14991 drgui_textbox__refresh_line_numbers(pTBElement);
14997 assert(pTB !=
NULL);
14999 float offsetLeft = pTB->borderWidth;
15000 float offsetTop = pTB->borderWidth;
15001 float offsetRight = pTB->borderWidth;
15002 float offsetBottom = pTB->borderWidth;
15007 if (scrollbarSizeH == 0 && scrollbarSizeV == 0) {
15015 DRGUI_PRIVATE
void drgui_textbox__on_mouse_move_line_numbers(
drgui_element* pLineNumbers,
int relativeMousePosX,
int relativeMousePosY,
int stateFlags)
15017 (void)relativeMousePosX;
15020 assert(pTBElement !=
NULL);
15023 assert(pTB !=
NULL);
15033 float offsetY = pTB->padding;
15035 size_t iAnchorLine = pTB->iLineSelectAnchor;
15040 if (iSelectionLastLine != iSelectionFirstLine) {
15041 iSelectionLastLine -= 1;
15046 bool movingUp =
false;
15047 if (iLine < iAnchorLine) {
15054 if (iAnchorLine + 1 < lineCount) {
15069 if (iLine + 1 < lineCount) {
15081 DRGUI_PRIVATE
void drgui_textbox__on_mouse_button_down_line_numbers(
drgui_element* pLineNumbers,
int mouseButton,
int relativeMousePosX,
int relativeMousePosY,
int stateFlags)
15083 (void)relativeMousePosX;
15087 assert(pTBElement !=
NULL);
15090 assert(pTB !=
NULL);
15095 float offsetY = pTB->padding;
15116 DRGUI_PRIVATE
void drgui_textbox__on_mouse_button_up_line_numbers(
drgui_element* pLineNumbers,
int mouseButton,
int relativeMousePosX,
int relativeMousePosY,
int stateFlags)
15118 (void)relativeMousePosX;
15119 (void)relativeMousePosY;
15134 assert(pTB !=
NULL);
15136 float offsetX = pTB->padding;
15137 float offsetY = pTB->padding;
15147 assert(pTB !=
NULL);
15149 float offsetX = pTB->padding;
15150 float offsetY = pTB->padding;
15154 DRGUI_PRIVATE
void drgui_textbox__on_paint_line_numbers(
drgui_element* pLineNumbers,
drgui_rect relativeRect,
void* pPaintData)
15156 (void)relativeRect;
15159 assert(pTBElement !=
NULL);
15162 assert(pTB !=
NULL);
15164 float lineNumbersWidth =
drgui_get_width(pLineNumbers) - (pTB->padding*2) - pTB->lineNumbersPaddingRight;
15165 float lineNumbersHeight =
drgui_get_height(pLineNumbers) - (pTB->padding*2);
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);
15173 rightPaddingRect.
right -= pTB->padding;
15174 rightPaddingRect.
left = rightPaddingRect.
right - pTB->lineNumbersPaddingRight;
15175 drgui_draw_rect(pLineNumbers, rightPaddingRect, pTB->lineNumbersBackgroundColor, pPaintData);
15178 DRGUI_PRIVATE
void drgui_textbox__refresh_line_numbers(
drgui_element* pTBElement)
15181 assert(pTB !=
NULL);
15183 float lineNumbersWidth = 0;
15185 lineNumbersWidth = pTB->lineNumbersWidth;
15193 float textEditorWidth;
15194 float textEditorHeight;
15195 drgui_textbox__calculate_text_engine_container_size(pTBElement, &textEditorWidth, &textEditorHeight);
15202 #endif //DR_GUI_IMPLEMENTATION
15203 #endif //DRGUI_NO_TEXT_EDITING
15219 #ifndef drgui_tree_view_h
15220 #define drgui_tree_view_h
15226 #define EG_MAX_TREE_VIEW_ITEM_TEXT_LENGTH 256
15465 #endif //drgui_tree_view_h
15468 #ifdef DR_GUI_IMPLEMENTATION
15469 typedef struct drgui_tree_view drgui_tree_view;
15471 struct drgui_tree_view
15493 float childOffsetX;
15516 bool isMouseOverArrow;
15522 int relativeMousePosX;
15525 int relativeMousePosY;
15529 bool isMultiSelectEnabled;
15532 bool isRangeSelectEnabled;
15536 size_t extraDataSize;
15539 char pExtraData[1];
15572 size_t extraDataSize;
15575 char pExtraData[1];
15598 } drgui_tree_view_iterator;
15614 } drgui_tree_view_item_metrics;
15621 } drgui_tree_view_scrollbar_data;
15633 static void drgui_tv_refresh_and_redraw(
drgui_element* pTVElement);
15636 static void drgui_tv_refresh_scrollbar_layouts(
drgui_element* pTVElement);
15639 static void drgui_tv_refresh_scrollbar_ranges(
drgui_element* pTVElement);
15648 static void drgui_tv_paint_items(
drgui_element* pTVElement,
drgui_rect relativeClippingRect,
void* pPaintData,
float* pItemsBottomOut);
15651 static bool drgui_tv_begin_at(
drgui_tree_view_item* pFirst, drgui_tree_view_iterator* pIteratorOut);
15654 static bool drgui_tv_next_visible(drgui_tree_view_iterator* pIterator);
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);
15667 static void drgui_tv_on_mouse_enter_scrollbar(
drgui_element* pSBElement);
15670 static void drgui_tv_on_scroll_v(
drgui_element* pSBElement,
int scrollPos);
15673 static void drgui_tv_on_scroll_h(
drgui_element* pSBElement,
int scrollPos);
15682 if (pTVElement ==
NULL) {
15692 if (pTV->pRootItem ==
NULL) {
15697 drgui_tree_view_scrollbar_data sbdata;
15698 sbdata.pTVElement = pTVElement;
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;
15714 pTV->onItemMouseMove =
NULL;
15715 pTV->onItemMouseLeave =
NULL;
15716 pTV->onItemPaint =
NULL;
15717 pTV->onItemMeasure =
NULL;
15718 pTV->onItemPicked =
NULL;
15720 pTV->pHoveredItem =
NULL;
15721 pTV->isMouseOverArrow =
false;
15722 pTV->isMouseOver =
false;
15723 pTV->relativeMousePosX = 0;
15724 pTV->relativeMousePosY = 0;
15726 pTV->isMultiSelectEnabled =
false;
15727 pTV->isRangeSelectEnabled =
false;
15729 pTV->extraDataSize = extraDataSize;
15730 if (pExtraData !=
NULL) {
15731 memcpy(pTV->pExtraData, pExtraData, extraDataSize);
15775 return pTV->extraDataSize;
15785 return pTV->pExtraData;
15795 return pTV->pRootItem;
15805 return pTV->pScrollbarV;
15815 return pTV->pScrollbarH;
15826 pTV->defaultBGColor = color;
15836 return pTV->defaultBGColor;
15846 pTV->hoveredBGColor = color;
15856 return pTV->hoveredBGColor;
15866 pTV->selectedBGColor = color;
15876 return pTV->selectedBGColor;
15886 pTV->childOffsetX = childOffsetX;
15896 return pTV->childOffsetX;
15907 if (pItem ==
NULL || pItem->pTVElement != pTVElement) {
15911 if (pTV->onItemMeasure)
15913 pTV->onItemMeasure(pItem, pWidthOut, pHeightOut);
15927 drgui_tv_deselect_all_items_recursive(pTV->pRootItem);
15941 pTV->isMultiSelectEnabled =
true;
15951 pTV->isMultiSelectEnabled =
false;
15961 return pTV->isMultiSelectEnabled;
15971 drgui_tree_view_iterator i;
15972 if (drgui_tv_begin_at(pTV->pRootItem->pFirstChild, &i))
15980 }
while (drgui_tv_next_visible(&i));
15993 drgui_tree_view_iterator i;
15994 if (drgui_tv_begin_at(pItem, &i))
15997 while (drgui_tv_next_visible(&i))
16016 pTV->onItemMouseMove = proc;
16026 pTV->onItemMouseLeave = proc;
16036 pTV->onItemPaint = proc;
16046 pTV->onItemMeasure = proc;
16056 pTV->onItemPicked = proc;
16071 drgui_tv_refresh_scrollbar_layouts(pTVElement);
16074 drgui_tv_refresh_scrollbar_ranges(pTVElement);
16084 pTV->isMouseOver =
false;
16086 if (pTV->pHoveredItem !=
NULL || pTV->isMouseOverArrow)
16088 if (pTV->onItemMouseLeave) {
16089 pTV->onItemMouseLeave(pTV->pHoveredItem);
16092 pTV->pHoveredItem =
NULL;
16093 pTV->isMouseOverArrow =
false;
16109 pTV->isMouseOver =
true;
16110 pTV->relativeMousePosX = relativeMousePosX;
16111 pTV->relativeMousePosY = relativeMousePosY;
16115 if (
drgui_rect_contains_point(drgui_tv_get_scrollbar_dead_space_rect(pTVElement), (float)relativeMousePosX, (
float)relativeMousePosY)) {
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);
16125 bool wasMouseOverArrow = pTV->isMouseOverArrow;
16126 pTV->isMouseOverArrow =
false;
16128 if (pNewHoveredItem !=
NULL)
16130 if (pTV->onItemMouseMove)
16132 float relativeMousePosXToItem = (float)relativeMousePosX - newHoveredItemMetrics.posX +
drgui_sb_get_scroll_position(pTV->pScrollbarH);
16133 float relativeMousePosYToItem = (float)relativeMousePosY - newHoveredItemMetrics.posY;
16135 if (relativeMousePosXToItem >= 0 && relativeMousePosXToItem < newHoveredItemMetrics.width &&
16136 relativeMousePosYToItem >= 0 && relativeMousePosYToItem < newHoveredItemMetrics.height)
16138 pTV->onItemMouseMove(pNewHoveredItem, (
int)relativeMousePosXToItem, (int)relativeMousePosYToItem, &pTV->isMouseOverArrow);
16143 if (pNewHoveredItem != pOldHoveredItem || wasMouseOverArrow != pTV->isMouseOverArrow)
16145 if (pNewHoveredItem != pOldHoveredItem && pOldHoveredItem !=
NULL)
16147 if (pTV->onItemMouseLeave) {
16148 pTV->onItemMouseLeave(pOldHoveredItem);
16153 pTV->pHoveredItem = pNewHoveredItem;
16162 (void)relativeMousePosX;
16163 (void)relativeMousePosY;
16173 if (pTV->isMouseOverArrow)
16183 if (pTV->isMultiSelectEnabled)
16205 (void)relativeMousePosX;
16206 (void)relativeMousePosY;
16219 (void)relativeMousePosX;
16220 (void)relativeMousePosY;
16230 if (!pTV->isMouseOverArrow)
16244 if (pTV->onItemPicked) {
16245 pTV->onItemPicked(pTV->pHoveredItem);
16254 (void)relativeMousePosX;
16255 (void)relativeMousePosY;
16274 drgui_draw_rect(pTVElement, drgui_tv_get_scrollbar_dead_space_rect(pTVElement), pTV->defaultBGColor, pPaintData);
16290 float itemsBottom = 0;
16291 drgui_tv_paint_items(pTVElement, innerClippingRect, pPaintData, &itemsBottom);
16303 static void drgui_tv_refresh_and_redraw(
drgui_element* pTVElement)
16312 drgui_tv_refresh_scrollbar_ranges(pTVElement);
16318 static void drgui_tv_refresh_scrollbar_layouts(
drgui_element* pTVElement)
16335 static void drgui_tv_refresh_scrollbar_ranges(
drgui_element* pTVElement)
16342 float innerWidth = 0;
16343 unsigned int totalItemCount = 0;
16344 unsigned int pageItemCount = 0;
16346 drgui_tree_view_iterator i;
16347 if (drgui_tv_begin_at(pTV->pRootItem->pFirstChild, &i))
16351 float itemRight = i.posX + i.width;
16352 if (itemRight > innerWidth) {
16353 innerWidth = itemRight;
16356 float itemBottom = i.posY + i.height;
16358 pageItemCount += 1;
16361 totalItemCount += 1;
16363 }
while (drgui_tv_next_visible(&i));
16366 if (totalItemCount == 0)
16412 static void drgui_tv_paint_items(
drgui_element* pTVElement,
drgui_rect relativeClippingRect,
void* pPaintData,
float* pItemsBottomOut)
16419 float itemsBottom = 0;
16423 drgui_tree_view_iterator i;
16424 if (drgui_tv_begin_at(drgui_tv_find_first_visible_item_on_page(pTVElement), &i))
16428 drgui_tv_paint_item(pTVElement, i.pItem, relativeClippingRect, i.posX, i.posY, i.width, i.height, pPaintData);
16433 itemsBottom = i.posY + i.height;
16435 }
while (itemsBottom < relativeClippingRect.
bottom && drgui_tv_next_visible(&i));
16439 if (pItemsBottomOut !=
NULL) {
16440 *pItemsBottomOut = itemsBottom;
16444 static bool drgui_tv_begin_at(
drgui_tree_view_item* pFirst, drgui_tree_view_iterator* pIteratorOut)
16446 if (pFirst ==
NULL || pIteratorOut ==
NULL) {
16450 if (!
drgui_tv_measure_item(pFirst->pTVElement, pFirst, &pIteratorOut->width, &pIteratorOut->height)) {
16456 pIteratorOut->pItem = pFirst;
16457 pIteratorOut->depth = depth;
16459 pIteratorOut->posY = 0;
16464 static bool drgui_tv_next_visible(drgui_tree_view_iterator* pIterator)
16466 assert(pIterator !=
NULL);
16468 if (pIterator->pItem ==
NULL) {
16479 pIterator->pItem = pIterator->pItem->pFirstChild;
16480 pIterator->depth += 1;
16488 if (pIterator->pItem ==
NULL) {
16493 pIterator->posY += pIterator->height;
16495 if (!
drgui_tv_measure_item(pIterator->pItem->pTVElement, pIterator->pItem, &pIterator->width, &pIterator->height)) {
16509 if (pTV->onItemPaint)
16517 bgcolor = pTV->selectedBGColor;
16518 }
else if (pTV->pHoveredItem == pItem) {
16519 bgcolor = pTV->hoveredBGColor;
16521 bgcolor = pTV->defaultBGColor;
16527 if (posX + innerOffsetX > 0) {
16539 pTV->onItemPaint(pTVElement, pItem, relativeClippingRect, bgcolor, posX + innerOffsetX, posY, width, height, pPaintData);
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)
16545 (void)relativePosX;
16554 drgui_tree_view_iterator i;
16555 if (drgui_tv_begin_at(drgui_tv_find_first_visible_item_on_page(pTVElement), &i))
16559 if (relativePosY >= i.posY && relativePosY < i.posY + i.height)
16561 if (pMetricsOut !=
NULL)
16563 pMetricsOut->posX = i.posX;
16564 pMetricsOut->posY = i.posY;
16565 pMetricsOut->width = i.width;
16566 pMetricsOut->height = i.height;
16580 pItem->isSelected =
false;
16584 drgui_tv_deselect_all_items_recursive(pChild);
16588 static void drgui_tv_on_mouse_enter_scrollbar(
drgui_element* pSBElement)
16599 static void drgui_tv_on_scroll_v(
drgui_element* pSBElement,
int scrollPos)
16614 if (pTV->isMouseOver) {
16622 static void drgui_tv_on_scroll_h(
drgui_element* pSBElement,
int scrollPos)
16650 drgui_tree_view_iterator i;
16651 if (drgui_tv_begin_at(pTV->pRootItem->pFirstChild, &i))
16661 }
while (drgui_tv_next_visible(&i));
16680 if (pTVElement ==
NULL) {
16684 if (pParent !=
NULL && pParent->pTVElement != pTVElement) {
16690 if (pItem ==
NULL) {
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;
16703 pItem->extraDataSize = extraDataSize;
16704 if (pExtraData !=
NULL) {
16705 memcpy(pItem->pExtraData, pExtraData, extraDataSize);
16717 if (pItem ==
NULL) {
16723 while (pItem->pFirstChild !=
NULL) {
16731 drgui_tvi_detach(pItem);
16734 drgui_tv_refresh_and_redraw(pTVElement);
16742 if (pItem ==
NULL) {
16746 return pItem->pTVElement;
16751 if (pItem ==
NULL) {
16755 return pItem->extraDataSize;
16760 if (pItem ==
NULL) {
16764 return pItem->pExtraData;
16770 if (pItem ==
NULL) {
16774 return pItem->pParent;
16779 if (pItem ==
NULL) {
16783 return pItem->pFirstChild;
16788 if (pItem ==
NULL) {
16792 return pItem->pLastChild;
16797 if (pItem ==
NULL) {
16801 return pItem->pNextSibling;
16806 if (pItem ==
NULL) {
16810 return pItem->pPrevSibling;
16815 if (pItem ==
NULL) {
16826 if (pParent ==
NULL)
16828 if (pTV->pRootItem !=
NULL) {
16834 assert(pItem->pTVElement == pParent->pTVElement);
16837 drgui_tvi_detach(pItem);
16839 pItem->pParent = pParent;
16840 assert(pItem->pParent !=
NULL);
16842 if (pItem->pParent->pLastChild !=
NULL) {
16843 pItem->pPrevSibling = pItem->pParent->pLastChild;
16844 pItem->pPrevSibling->pNextSibling = pItem;
16847 if (pItem->pParent->pFirstChild ==
NULL) {
16848 pItem->pParent->pFirstChild = pItem;
16851 pItem->pParent->pLastChild = pItem;
16855 drgui_tv_refresh_and_redraw(pItem->pTVElement);
16861 if (pItem ==
NULL) {
16872 if (pParent ==
NULL)
16874 if (pTV->pRootItem !=
NULL) {
16880 assert(pItem->pTVElement == pParent->pTVElement);
16883 drgui_tvi_detach(pItem);
16885 pItem->pParent = pParent;
16886 assert(pItem->pParent !=
NULL);
16888 if (pItem->pParent->pFirstChild !=
NULL) {
16889 pItem->pNextSibling = pItem->pParent->pFirstChild;
16890 pItem->pNextSibling->pPrevSibling = pItem;
16893 if (pItem->pParent->pLastChild ==
NULL) {
16894 pItem->pParent->pLastChild = pItem;
16897 pItem->pParent->pFirstChild = pItem;
16901 drgui_tv_refresh_and_redraw(pItem->pTVElement);
16907 if (pItemToAppend ==
NULL) {
16918 if (pItemToAppendTo ==
NULL)
16920 if (pTV->pRootItem !=
NULL) {
16926 assert(pItemToAppend->pTVElement == pItemToAppendTo->pTVElement);
16929 drgui_tvi_detach(pItemToAppend);
16932 pItemToAppend->pParent = pItemToAppendTo->pParent;
16933 assert(pItemToAppend->pParent !=
NULL);
16935 pItemToAppend->pNextSibling = pItemToAppendTo->pNextSibling;
16936 pItemToAppend->pPrevSibling = pItemToAppendTo;
16938 pItemToAppendTo->pNextSibling->pPrevSibling = pItemToAppend;
16939 pItemToAppendTo->pNextSibling = pItemToAppend;
16941 if (pItemToAppend->pParent->pLastChild == pItemToAppendTo) {
16942 pItemToAppend->pParent->pLastChild = pItemToAppend;
16947 drgui_tv_refresh_and_redraw(pItemToAppend->pTVElement);
16953 if (pItemToPrepend ==
NULL) {
16964 if (pItemToPrependTo ==
NULL)
16966 if (pTV->pRootItem !=
NULL) {
16972 assert(pItemToPrepend->pTVElement == pItemToPrependTo->pTVElement);
16975 drgui_tvi_detach(pItemToPrepend);
16978 pItemToPrepend->pParent = pItemToPrependTo->pParent;
16979 assert(pItemToPrepend->pParent !=
NULL);
16981 pItemToPrepend->pPrevSibling = pItemToPrependTo->pNextSibling;
16982 pItemToPrepend->pNextSibling = pItemToPrependTo;
16984 pItemToPrependTo->pPrevSibling->pNextSibling = pItemToPrepend;
16985 pItemToPrependTo->pNextSibling = pItemToPrepend;
16987 if (pItemToPrepend->pParent->pFirstChild == pItemToPrependTo) {
16988 pItemToPrepend->pParent->pFirstChild = pItemToPrepend;
16993 drgui_tv_refresh_and_redraw(pItemToPrepend->pTVElement);
17000 if (pItem ==
NULL) {
17004 return pItem->pFirstChild !=
NULL;
17018 if (pItem ==
NULL) {
17022 if (pItem->pNextSibling !=
NULL) {
17023 return pItem->pNextSibling;
17027 if (pDepthInOut !=
NULL) {
17037 if (pItem ==
NULL) {
17041 if (!pItem->isSelected)
17043 pItem->isSelected =
true;
17050 if (pItem ==
NULL) {
17054 if (pItem->isSelected)
17056 pItem->isSelected =
false;
17063 if (pItem ==
NULL) {
17067 return pItem->isSelected;
17072 if (pItem ==
NULL) {
17076 if (!pItem->isExpanded)
17078 pItem->isExpanded =
true;
17079 drgui_tv_refresh_and_redraw(pItem->pTVElement);
17085 if (pItem ==
NULL) {
17089 if (pItem->isExpanded)
17091 pItem->isExpanded =
false;
17092 drgui_tv_refresh_and_redraw(pItem->pTVElement);
17098 if (pItem ==
NULL) {
17102 return pItem->isExpanded;
17109 assert(pItem !=
NULL);
17111 if (pItem->pParent !=
NULL)
17113 if (pItem->pParent->pFirstChild == pItem) {
17114 pItem->pParent->pFirstChild = pItem->pNextSibling;
17117 if (pItem->pParent->pLastChild == pItem) {
17118 pItem->pParent->pLastChild = pItem->pPrevSibling;
17122 if (pItem->pPrevSibling !=
NULL) {
17123 pItem->pPrevSibling->pNextSibling = pItem->pNextSibling;
17126 if (pItem->pNextSibling !=
NULL) {
17127 pItem->pNextSibling->pPrevSibling = pItem->pPrevSibling;
17131 pItem->pParent =
NULL;
17132 pItem->pPrevSibling =
NULL;
17133 pItem->pNextSibling =
NULL;
17135 #endif //DR_GUI_IMPLEMENTATION
17138 #ifdef DR_GUI_INCLUDE_WIP
17140 #endif //DR_GUI_INCLUDE_WIP