emote2ss

Animated webp to spritesheets converting tool
git clone git://bsandro.tech/emote2ss
Log | Files | Refs | README | LICENSE

luigi2.h (256637B)


      1 // TODO UITextbox features - mouse input, undo, number dragging.
      2 // TODO New elements - list view, menu bar.
      3 // TODO Keyboard navigation in menus.
      4 // TODO Easier to use fonts.
      5 
      6 /////////////////////////////////////////
      7 // Header includes.
      8 /////////////////////////////////////////
      9 #pragma once
     10 
     11 #include <stdint.h>
     12 #include <stddef.h>
     13 #include <stdbool.h>
     14 #include <stdarg.h>
     15 
     16 #ifdef UI_LINUX
     17 #include <X11/Xlib.h>
     18 #include <X11/Xutil.h>
     19 #include <X11/Xatom.h>
     20 #include <X11/cursorfont.h>
     21 #endif
     22 
     23 #ifdef UI_SSE2
     24 #include <xmmintrin.h>
     25 #endif
     26 
     27 #ifdef UI_WINDOWS
     28 #undef _UNICODE
     29 #undef UNICODE
     30 #include <windows.h>
     31 
     32 #define UI_ASSERT(x) do { if (!(x)) { ui.assertionFailure = true; \
     33 	MessageBox(0, "Assertion failure on line " _UI_TO_STRING_2(__LINE__), 0, 0); \
     34 	ExitProcess(1); } } while (0)
     35 #define UI_CALLOC(x) HeapAlloc(ui.heap, HEAP_ZERO_MEMORY, (x))
     36 #define UI_FREE(x) HeapFree(ui.heap, 0, (x))
     37 #define UI_MALLOC(x) HeapAlloc(ui.heap, 0, (x))
     38 #define UI_REALLOC _UIHeapReAlloc
     39 #define UI_CLOCK GetTickCount
     40 #define UI_CLOCKS_PER_SECOND (1000)
     41 #define UI_CLOCK_T DWORD
     42 #define UI_MEMMOVE _UIMemmove
     43 #endif
     44 
     45 #ifdef UI_COCOA
     46 #import <Foundation/Foundation.h>
     47 #import <Cocoa/Cocoa.h>
     48 #import <Carbon/Carbon.h>
     49 #endif
     50 
     51 #if defined(UI_LINUX) || defined(UI_COCOA)
     52 #include <stdlib.h>
     53 #include <string.h>
     54 #include <assert.h>
     55 #include <time.h>
     56 #include <math.h>
     57 
     58 #define UI_ASSERT assert
     59 #define UI_CALLOC(x) calloc(1, (x))
     60 #define UI_FREE free
     61 #define UI_MALLOC malloc
     62 #define UI_REALLOC realloc
     63 #define UI_CLOCK _UIClock
     64 #define UI_CLOCKS_PER_SECOND 1000
     65 #define UI_CLOCK_T clock_t
     66 #define UI_MEMMOVE(d, s, n) do { size_t _n = n; if (_n) { memmove(d, s, _n); } } while (0)
     67 #endif
     68 
     69 #if defined(UI_ESSENCE)
     70 #include <essence.h>
     71 
     72 #define UI_ASSERT EsAssert
     73 #define UI_CALLOC(x) EsHeapAllocate((x), true)
     74 #define UI_FREE EsHeapFree
     75 #define UI_MALLOC(x) EsHeapAllocate((x), false)
     76 #define UI_REALLOC(x, y) EsHeapReallocate((x), (y), false)
     77 #define UI_CLOCK EsTimeStampMs
     78 #define UI_CLOCKS_PER_SECOND 1000
     79 #define UI_CLOCK_T uint64_t
     80 #define UI_MEMMOVE EsCRTmemmove
     81 
     82 // Callback to allow the application to process messages.
     83 void _UIMessageProcess(EsMessage *message);
     84 #endif
     85 
     86 #ifdef UI_DEBUG
     87 #include <stdio.h>
     88 #endif
     89 
     90 #ifdef UI_FREETYPE
     91 #include <ft2build.h>
     92 #include FT_FREETYPE_H
     93 #include <freetype/ftbitmap.h>
     94 #endif
     95 
     96 /////////////////////////////////////////
     97 // Definitions.
     98 /////////////////////////////////////////
     99 
    100 #define _UI_TO_STRING_1(x) #x
    101 #define _UI_TO_STRING_2(x) _UI_TO_STRING_1(x)
    102 
    103 #define UI_SIZE_BUTTON_MINIMUM_WIDTH (100)
    104 #define UI_SIZE_BUTTON_PADDING (16)
    105 #define UI_SIZE_BUTTON_HEIGHT (27)
    106 #define UI_SIZE_BUTTON_CHECKED_AREA (4)
    107 
    108 #define UI_SIZE_CHECKBOX_BOX (14)
    109 #define UI_SIZE_CHECKBOX_GAP (8)
    110 
    111 #define UI_SIZE_MENU_ITEM_HEIGHT (24)
    112 #define UI_SIZE_MENU_ITEM_MINIMUM_WIDTH (160)
    113 #define UI_SIZE_MENU_ITEM_MARGIN (9)
    114 
    115 #define UI_SIZE_GAUGE_WIDTH (200)
    116 #define UI_SIZE_GAUGE_HEIGHT (22)
    117 
    118 #define UI_SIZE_SLIDER_WIDTH (200)
    119 #define UI_SIZE_SLIDER_HEIGHT (25)
    120 #define UI_SIZE_SLIDER_THUMB (15)
    121 #define UI_SIZE_SLIDER_TRACK (5)
    122 
    123 #define UI_SIZE_TEXTBOX_MARGIN (3)
    124 #define UI_SIZE_TEXTBOX_WIDTH (200)
    125 #define UI_SIZE_TEXTBOX_HEIGHT (27)
    126 
    127 #define UI_SIZE_TAB_PANE_SPACE_TOP (2)
    128 #define UI_SIZE_TAB_PANE_SPACE_LEFT (4)
    129 
    130 #define UI_SIZE_SPLITTER (8)
    131 
    132 #define UI_SIZE_SCROLL_BAR (16)
    133 #define UI_SIZE_SCROLL_MINIMUM_THUMB (20)
    134 
    135 #define UI_SIZE_CODE_MARGIN (ui.activeFont->glyphWidth * 5)
    136 #define UI_SIZE_CODE_MARGIN_GAP (ui.activeFont->glyphWidth * 1)
    137 
    138 #define UI_SIZE_TABLE_HEADER (26)
    139 #define UI_SIZE_TABLE_COLUMN_GAP (20)
    140 #define UI_SIZE_TABLE_ROW (20)
    141 
    142 #define UI_SIZE_PANE_LARGE_BORDER (20)
    143 #define UI_SIZE_PANE_LARGE_GAP (10)
    144 #define UI_SIZE_PANE_MEDIUM_BORDER (5)
    145 #define UI_SIZE_PANE_MEDIUM_GAP (5)
    146 #define UI_SIZE_PANE_SMALL_BORDER (3)
    147 #define UI_SIZE_PANE_SMALL_GAP (3)
    148 
    149 #define UI_SIZE_MDI_CHILD_BORDER (6)
    150 #define UI_SIZE_MDI_CHILD_TITLE (30)
    151 #define UI_SIZE_MDI_CHILD_CORNER (12)
    152 #define UI_SIZE_MDI_CHILD_MINIMUM_WIDTH (100)
    153 #define UI_SIZE_MDI_CHILD_MINIMUM_HEIGHT (50)
    154 #define UI_SIZE_MDI_CASCADE (30)
    155 
    156 #define UI_MDI_CHILD_CALCULATE_LAYOUT(bounds, scale) \
    157 	int titleSize = UI_SIZE_MDI_CHILD_TITLE * scale; \
    158 	int borderSize = UI_SIZE_MDI_CHILD_BORDER * scale; \
    159 	UIRectangle title = UIRectangleAdd(bounds, UI_RECT_4(borderSize, -borderSize, 0, 0)); \
    160 	title.b = title.t + titleSize; \
    161 	UIRectangle content = UIRectangleAdd(bounds, UI_RECT_4(borderSize, -borderSize, titleSize, -borderSize));
    162 
    163 #define UI_UPDATE_HOVERED (1)
    164 #define UI_UPDATE_PRESSED (2)
    165 #define UI_UPDATE_FOCUSED (3)
    166 #define UI_UPDATE_DISABLED (4)
    167 
    168 typedef enum UIMessage {
    169 	// General messages.
    170 	UI_MSG_PAINT, // dp = pointer to UIPainter
    171 	UI_MSG_PAINT_FOREGROUND, // after children have painted
    172 	UI_MSG_LAYOUT,
    173 	UI_MSG_DESTROY,
    174 	UI_MSG_DEALLOCATE,
    175 	UI_MSG_UPDATE, // di = UI_UPDATE_... constant
    176 	UI_MSG_ANIMATE,
    177 	UI_MSG_SCROLLED,
    178 	UI_MSG_GET_WIDTH, // di = height (if known); return width
    179 	UI_MSG_GET_HEIGHT, // di = width (if known); return height
    180 	UI_MSG_GET_CHILD_STABILITY, // dp = child element; return stable axes, 1 (width) | 2 (height)
    181 
    182 	// Input events.
    183 	UI_MSG_INPUT_EVENTS_START, // not sent to disabled elements
    184 	UI_MSG_LEFT_DOWN,
    185 	UI_MSG_LEFT_UP,
    186 	UI_MSG_MIDDLE_DOWN,
    187 	UI_MSG_MIDDLE_UP,
    188 	UI_MSG_RIGHT_DOWN,
    189 	UI_MSG_RIGHT_UP,
    190 	UI_MSG_KEY_TYPED, // dp = pointer to UIKeyTyped; return 1 if handled
    191 	UI_MSG_KEY_RELEASED, // dp = pointer to UIKeyTyped; return 1 if handled
    192 	UI_MSG_MOUSE_MOVE,
    193 	UI_MSG_MOUSE_DRAG,
    194 	UI_MSG_MOUSE_WHEEL, // di = delta; return 1 if handled
    195 	UI_MSG_CLICKED,
    196 	UI_MSG_GET_CURSOR, // return cursor code
    197 	UI_MSG_PRESSED_DESCENDENT, // dp = pointer to child that is/contains pressed element
    198 	UI_MSG_INPUT_EVENTS_END,
    199 
    200 	// Specific elements.
    201 	UI_MSG_VALUE_CHANGED, // sent to notify that the element's value has changed
    202 	UI_MSG_TABLE_GET_ITEM, // dp = pointer to UITableGetItem; return string length
    203 	UI_MSG_CODE_GET_MARGIN_COLOR, // di = line index (starts at 1); return color
    204 	UI_MSG_CODE_DECORATE_LINE, // dp = pointer to UICodeDecorateLine
    205 	UI_MSG_TAB_SELECTED, // sent to the tab that was selected (not the tab pane itself)
    206 
    207 	// Windows.
    208 	UI_MSG_WINDOW_DROP_FILES, // di = count, dp = char ** of paths
    209 	UI_MSG_WINDOW_ACTIVATE,
    210 	UI_MSG_WINDOW_CLOSE, // return 1 to prevent default (process exit for UIWindow; close for UIMDIChild)
    211 	UI_MSG_WINDOW_UPDATE_START,
    212 	UI_MSG_WINDOW_UPDATE_BEFORE_DESTROY,
    213 	UI_MSG_WINDOW_UPDATE_BEFORE_LAYOUT,
    214 	UI_MSG_WINDOW_UPDATE_BEFORE_PAINT,
    215 	UI_MSG_WINDOW_UPDATE_END,
    216 
    217 	// User-defined messages.
    218 	UI_MSG_USER,
    219 } UIMessage;
    220 
    221 #ifdef UI_ESSENCE
    222 #define UIRectangle EsRectangle
    223 #else
    224 typedef struct UIRectangle {
    225 	int l, r, t, b;
    226 } UIRectangle;
    227 #endif
    228 
    229 typedef struct UITheme {
    230 	uint32_t panel1, panel2, selected, border;
    231 	uint32_t text, textDisabled, textSelected;
    232 	uint32_t buttonNormal, buttonHovered, buttonPressed, buttonDisabled;
    233 	uint32_t textboxNormal, textboxFocused;
    234 	uint32_t codeFocused, codeBackground, codeDefault, codeComment, codeString, codeNumber, codeOperator, codePreprocessor;
    235 	uint32_t accent1, accent2;
    236 } UITheme;
    237 
    238 typedef struct UIPainter {
    239 	UIRectangle clip;
    240 	uint32_t *bits;
    241 	int width, height;
    242 #ifdef UI_DEBUG
    243 	int fillCount;
    244 #endif
    245 } UIPainter;
    246 
    247 typedef struct UIFont {
    248 	int glyphWidth, glyphHeight;
    249 
    250 #ifdef UI_FREETYPE
    251 	bool isFreeType;
    252 	FT_Face font;
    253 #ifdef UI_UNICODE
    254 	FT_Bitmap *glyphs;
    255 	bool *glyphsRendered;
    256 	int *glyphOffsetsX, *glyphOffsetsY;
    257 #else
    258 	FT_Bitmap glyphs[128];
    259 	bool glyphsRendered[128];
    260 	int glyphOffsetsX[128], glyphOffsetsY[128];
    261 #endif
    262 #endif
    263 } UIFont;
    264 
    265 typedef struct UIShortcut {
    266 	intptr_t code;
    267 	bool ctrl, shift, alt;
    268 	void (*invoke)(void *cp);
    269 	void *cp;
    270 } UIShortcut;
    271 
    272 typedef struct UIStringSelection {
    273 	int carets[2];
    274 	uint32_t colorText, colorBackground;
    275 } UIStringSelection;
    276 
    277 typedef struct UIKeyTyped {
    278 	char *text;
    279 	int textBytes;
    280 	intptr_t code;
    281 } UIKeyTyped;
    282 
    283 typedef struct UITableGetItem {
    284 	char *buffer;
    285 	size_t bufferBytes;
    286 	int index, column;
    287 	bool isSelected;
    288 } UITableGetItem;
    289 
    290 typedef struct UICodeDecorateLine {
    291 	UIRectangle bounds;
    292 	int index; // Starting at 1!
    293 	int x, y; // Position where additional text can be drawn.
    294 	UIPainter *painter;
    295 } UICodeDecorateLine;
    296 
    297 #define UI_RECT_1(x) ((UIRectangle) { (x), (x), (x), (x) })
    298 #define UI_RECT_1I(x) ((UIRectangle) { (x), -(x), (x), -(x) })
    299 #define UI_RECT_2(x, y) ((UIRectangle) { (x), (x), (y), (y) })
    300 #define UI_RECT_2I(x, y) ((UIRectangle) { (x), -(x), (y), -(y) })
    301 #define UI_RECT_2S(x, y) ((UIRectangle) { 0, (x), 0, (y) })
    302 #define UI_RECT_4(x, y, z, w) ((UIRectangle) { (x), (y), (z), (w) })
    303 #define UI_RECT_4PD(x, y, w, h) ((UIRectangle) { (x), ((x) + (w)), (y), ((y) + (h)) })
    304 #define UI_RECT_WIDTH(_r) ((_r).r - (_r).l)
    305 #define UI_RECT_HEIGHT(_r) ((_r).b - (_r).t)
    306 #define UI_RECT_TOTAL_H(_r) ((_r).r + (_r).l)
    307 #define UI_RECT_TOTAL_V(_r) ((_r).b + (_r).t)
    308 #define UI_RECT_SIZE(_r) UI_RECT_WIDTH(_r), UI_RECT_HEIGHT(_r)
    309 #define UI_RECT_TOP_LEFT(_r) (_r).l, (_r).t
    310 #define UI_RECT_BOTTOM_LEFT(_r) (_r).l, (_r).b
    311 #define UI_RECT_BOTTOM_RIGHT(_r) (_r).r, (_r).b
    312 #define UI_RECT_ALL(_r) (_r).l, (_r).r, (_r).t, (_r).b
    313 #define UI_RECT_VALID(_r) ((_r).l < (_r).r && (_r).t < (_r).b)
    314 
    315 #define UI_COLOR_ALPHA_F(x) ((((x) >> 24) & 0xFF) / 255.0f)
    316 #define UI_COLOR_RED_F(x) ((((x) >> 16) & 0xFF) / 255.0f)
    317 #define UI_COLOR_GREEN_F(x) ((((x) >> 8) & 0xFF) / 255.0f)
    318 #define UI_COLOR_BLUE_F(x) ((((x) >> 0) & 0xFF) / 255.0f)
    319 #define UI_COLOR_ALPHA(x) ((((x) >> 24) & 0xFF))
    320 #define UI_COLOR_RED(x) ((((x) >> 16) & 0xFF))
    321 #define UI_COLOR_GREEN(x) ((((x) >> 8) & 0xFF))
    322 #define UI_COLOR_BLUE(x) ((((x) >> 0) & 0xFF))
    323 #define UI_COLOR_FROM_FLOAT(r, g, b) (((uint32_t) ((r) * 255.0f) << 16) | ((uint32_t) ((g) * 255.0f) << 8) | ((uint32_t) ((b) * 255.0f) << 0))
    324 #define UI_COLOR_FROM_RGBA_F(r, g, b, a) (((uint32_t) ((r) * 255.0f) << 16) | ((uint32_t) ((g) * 255.0f) << 8) \
    325 		| ((uint32_t) ((b) * 255.0f) << 0) | ((uint32_t) ((a) * 255.0f) << 24))
    326 
    327 #define UI_SWAP(s, a, b) do { s t = (a); (a) = (b); (b) = t; } while (0)
    328 
    329 #ifndef UI_DRAW_CONTROL_CUSTOM
    330 #define UIDrawControl UIDrawControlDefault
    331 #endif
    332 #define UI_DRAW_CONTROL_PUSH_BUTTON          (1)
    333 #define UI_DRAW_CONTROL_DROP_DOWN            (2)
    334 #define UI_DRAW_CONTROL_MENU_ITEM            (3)
    335 #define UI_DRAW_CONTROL_CHECKBOX             (4)
    336 #define UI_DRAW_CONTROL_LABEL                (5)
    337 #define UI_DRAW_CONTROL_SPLITTER             (6)
    338 #define UI_DRAW_CONTROL_SCROLL_TRACK         (7)
    339 #define UI_DRAW_CONTROL_SCROLL_UP            (8)
    340 #define UI_DRAW_CONTROL_SCROLL_DOWN          (9)
    341 #define UI_DRAW_CONTROL_SCROLL_THUMB        (10)
    342 #define UI_DRAW_CONTROL_GAUGE               (11)
    343 #define UI_DRAW_CONTROL_SLIDER              (12)
    344 #define UI_DRAW_CONTROL_TEXTBOX             (13)
    345 #define UI_DRAW_CONTROL_MODAL_POPUP         (14)
    346 #define UI_DRAW_CONTROL_MENU                (15)
    347 #define UI_DRAW_CONTROL_TABLE_ROW           (16)
    348 #define UI_DRAW_CONTROL_TABLE_CELL          (17)
    349 #define UI_DRAW_CONTROL_TABLE_BACKGROUND    (18)
    350 #define UI_DRAW_CONTROL_TABLE_HEADER        (19)
    351 #define UI_DRAW_CONTROL_MDI_CHILD           (20)
    352 #define UI_DRAW_CONTROL_TAB                 (21)
    353 #define UI_DRAW_CONTROL_TAB_BAND            (22)
    354 #define UI_DRAW_CONTROL_TYPE_MASK           (0xFF)
    355 #define UI_DRAW_CONTROL_STATE_SELECTED      (1 << 24)
    356 #define UI_DRAW_CONTROL_STATE_VERTICAL      (1 << 25)
    357 #define UI_DRAW_CONTROL_STATE_INDETERMINATE (1 << 26)
    358 #define UI_DRAW_CONTROL_STATE_CHECKED       (1 << 27)
    359 #define UI_DRAW_CONTROL_STATE_HOVERED       (1 << 28)
    360 #define UI_DRAW_CONTROL_STATE_FOCUSED       (1 << 29)
    361 #define UI_DRAW_CONTROL_STATE_PRESSED       (1 << 30)
    362 #define UI_DRAW_CONTROL_STATE_DISABLED      (1 << 31)
    363 #define UI_DRAW_CONTROL_STATE_FROM_ELEMENT(x) ((((x)->flags & UI_ELEMENT_DISABLED) ? UI_DRAW_CONTROL_STATE_DISABLED : 0) \
    364 		| (((x)->window->hovered == (x)) ? UI_DRAW_CONTROL_STATE_HOVERED : 0) \
    365 		| (((x)->window->focused == (x)) ? UI_DRAW_CONTROL_STATE_FOCUSED : 0) \
    366 		| (((x)->window->pressed == (x)) ? UI_DRAW_CONTROL_STATE_PRESSED : 0))
    367 
    368 #define UI_CURSOR_ARROW (0)
    369 #define UI_CURSOR_TEXT (1)
    370 #define UI_CURSOR_SPLIT_V (2)
    371 #define UI_CURSOR_SPLIT_H (3)
    372 #define UI_CURSOR_FLIPPED_ARROW (4)
    373 #define UI_CURSOR_CROSS_HAIR (5)
    374 #define UI_CURSOR_HAND (6)
    375 #define UI_CURSOR_RESIZE_UP (7)
    376 #define UI_CURSOR_RESIZE_LEFT (8)
    377 #define UI_CURSOR_RESIZE_UP_RIGHT (9)
    378 #define UI_CURSOR_RESIZE_UP_LEFT (10)
    379 #define UI_CURSOR_RESIZE_DOWN (11)
    380 #define UI_CURSOR_RESIZE_RIGHT (12)
    381 #define UI_CURSOR_RESIZE_DOWN_RIGHT (13)
    382 #define UI_CURSOR_RESIZE_DOWN_LEFT (14)
    383 #define UI_CURSOR_COUNT (15)
    384 
    385 #define UI_ALIGN_LEFT (1)
    386 #define UI_ALIGN_RIGHT (2)
    387 #define UI_ALIGN_CENTER (3)
    388 
    389 extern const int UI_KEYCODE_A;
    390 extern const int UI_KEYCODE_BACKSPACE;
    391 extern const int UI_KEYCODE_DELETE;
    392 extern const int UI_KEYCODE_DOWN;
    393 extern const int UI_KEYCODE_END;
    394 extern const int UI_KEYCODE_ENTER;
    395 extern const int UI_KEYCODE_ESCAPE;
    396 extern const int UI_KEYCODE_F1;
    397 extern const int UI_KEYCODE_HOME;
    398 extern const int UI_KEYCODE_LEFT;
    399 extern const int UI_KEYCODE_RIGHT;
    400 extern const int UI_KEYCODE_SPACE;
    401 extern const int UI_KEYCODE_TAB;
    402 extern const int UI_KEYCODE_UP;
    403 extern const int UI_KEYCODE_INSERT;
    404 extern const int UI_KEYCODE_0;
    405 extern const int UI_KEYCODE_BACKTICK;
    406 extern const int UI_KEYCODE_PAGE_UP;
    407 extern const int UI_KEYCODE_PAGE_DOWN;
    408 
    409 #define UI_KEYCODE_LETTER(x) (UI_KEYCODE_A + (x) - 'A')
    410 #define UI_KEYCODE_DIGIT(x) (UI_KEYCODE_0 + (x) - '0')
    411 #define UI_KEYCODE_FKEY(x) (UI_KEYCODE_F1 + (x) - 1)
    412 
    413 #define UI_ELEMENT_FILL (UI_ELEMENT_V_FILL | UI_ELEMENT_H_FILL)
    414 
    415 typedef struct UIElement {
    416 #define UI_ELEMENT_V_FILL (1 << 16)
    417 #define UI_ELEMENT_H_FILL (1 << 17)
    418 #define UI_ELEMENT_WINDOW (1 << 18)
    419 #define UI_ELEMENT_PARENT_PUSH (1 << 19)
    420 #define UI_ELEMENT_TAB_STOP (1 << 20)
    421 #define UI_ELEMENT_NON_CLIENT (1 << 21) // Don't destroy in UIElementDestroyDescendents, like scroll bars.
    422 #define UI_ELEMENT_DISABLED (1 << 22) // Don't receive input events.
    423 #define UI_ELEMENT_BORDER (1 << 23)
    424 
    425 #define UI_ELEMENT_HIDE (1 << 27)
    426 #define UI_ELEMENT_RELAYOUT (1 << 28)
    427 #define UI_ELEMENT_RELAYOUT_DESCENDENT (1 << 29)
    428 #define UI_ELEMENT_DESTROY (1 << 30)
    429 #define UI_ELEMENT_DESTROY_DESCENDENT (1 << 31)
    430 
    431 	uint32_t flags; // First 16 bits are element specific.
    432 	uint32_t id;
    433 	uint32_t childCount;
    434 	uint32_t _unused0;
    435 
    436 	struct UIElement *parent;
    437 	struct UIElement **children;
    438 	struct UIWindow *window;
    439 
    440 	UIRectangle bounds, clip;
    441 
    442 	void *cp; // Context pointer (for user).
    443 
    444 	int (*messageClass)(struct UIElement *element, UIMessage message, int di /* data integer */, void *dp /* data pointer */);
    445 	int (*messageUser)(struct UIElement *element, UIMessage message, int di, void *dp);
    446 
    447 	const char *cClassName;
    448 } UIElement;
    449 
    450 #define UI_SHORTCUT(code, ctrl, shift, alt, invoke, cp) ((UIShortcut) { (code), (ctrl), (shift), (alt), (invoke), (cp) })
    451 
    452 typedef struct UIWindow {
    453 #define UI_WINDOW_MENU (1 << 0)
    454 #define UI_WINDOW_INSPECTOR (1 << 1)
    455 #define UI_WINDOW_CENTER_IN_OWNER (1 << 2)
    456 #define UI_WINDOW_MAXIMIZE (1 << 3)
    457 #define UI_WINDOW_DIALOG (1 << 4)
    458 
    459 	UIElement e;
    460 
    461 	UIElement *dialog;
    462 
    463 	UIShortcut *shortcuts;
    464 	size_t shortcutCount, shortcutAllocated;
    465 
    466 	float scale;
    467 
    468 	uint32_t *bits;
    469 	int width, height;
    470 	struct UIWindow *next;
    471 
    472 	UIElement *hovered, *pressed, *focused, *dialogOldFocus;
    473 	int pressedButton;
    474 
    475 	int cursorX, cursorY;
    476 	int cursorStyle;
    477 
    478 	// Set when a textbox is modified.
    479 	// Useful for tracking whether changes to the loaded document have been saved.
    480 	bool textboxModifiedFlag;
    481 
    482 	bool ctrl, shift, alt;
    483 
    484 	UIRectangle updateRegion;
    485 
    486 #ifdef UI_DEBUG
    487 	float lastFullFillCount;
    488 #endif
    489 
    490 #ifdef UI_LINUX
    491 	Window window;
    492 	XImage *image;
    493 	XIC xic;
    494 	unsigned ctrlCode, shiftCode, altCode;
    495 	Window dragSource;
    496 #endif
    497 
    498 #ifdef UI_WINDOWS
    499 	HWND hwnd;
    500 	bool trackingLeave;
    501 #endif
    502 
    503 #ifdef UI_ESSENCE
    504 	EsWindow *window;
    505 	EsElement *canvas;
    506 	int cursor;
    507 #endif
    508 
    509 #ifdef UI_COCOA
    510 	NSWindow *window;
    511 	void *view;
    512 #endif
    513 } UIWindow;
    514 
    515 typedef struct UIPanel {
    516 #define UI_PANEL_HORIZONTAL (1 << 0)
    517 #define UI_PANEL_COLOR_1 (1 << 2)
    518 #define UI_PANEL_COLOR_2 (1 << 3)
    519 #define UI_PANEL_SMALL_SPACING (1 << 5)
    520 #define UI_PANEL_MEDIUM_SPACING (1 << 6)
    521 #define UI_PANEL_LARGE_SPACING (1 << 7)
    522 #define UI_PANEL_SCROLL (1 << 8)
    523 #define UI_PANEL_EXPAND (1 << 9)
    524 	UIElement e;
    525 	struct UIScrollBar *scrollBar;
    526 	UIRectangle border;
    527 	int gap;
    528 } UIPanel;
    529 
    530 typedef struct UIButton {
    531 #define UI_BUTTON_SMALL (1 << 0)
    532 #define UI_BUTTON_MENU_ITEM (1 << 1)
    533 #define UI_BUTTON_CAN_FOCUS (1 << 2)
    534 #define UI_BUTTON_DROP_DOWN (1 << 3)
    535 #define UI_BUTTON_CHECKED (1 << 15)
    536 	UIElement e;
    537 	char *label;
    538 	ptrdiff_t labelBytes;
    539 	void (*invoke)(void *cp);
    540 } UIButton;
    541 
    542 typedef struct UICheckbox {
    543 #define UI_CHECKBOX_ALLOW_INDETERMINATE (1 << 0)
    544 	UIElement e;
    545 #define UI_CHECK_UNCHECKED (0)
    546 #define UI_CHECK_CHECKED (1)
    547 #define UI_CHECK_INDETERMINATE (2)
    548 	uint8_t check;
    549 	char *label;
    550 	ptrdiff_t labelBytes;
    551 	void (*invoke)(void *cp);
    552 } UICheckbox;
    553 
    554 typedef struct UILabel {
    555 	UIElement e;
    556 	char *label;
    557 	ptrdiff_t labelBytes;
    558 } UILabel;
    559 
    560 typedef struct UISpacer {
    561 	UIElement e;
    562 	int width, height;
    563 } UISpacer;
    564 
    565 typedef struct UISplitPane {
    566 #define UI_SPLIT_PANE_VERTICAL (1 << 0)
    567 	UIElement e;
    568 	float weight;
    569 } UISplitPane;
    570 
    571 typedef struct UITabPane {
    572 	UIElement e;
    573 	char *tabs;
    574 	uint32_t active;
    575 } UITabPane;
    576 
    577 typedef struct UIScrollBar {
    578 #define UI_SCROLL_BAR_HORIZONTAL (1 << 0)
    579 	UIElement e;
    580 	int64_t maximum, page;
    581 	int64_t dragOffset;
    582 	double position;
    583 	UI_CLOCK_T lastAnimateTime;
    584 	bool inDrag, horizontal;
    585 } UIScrollBar;
    586 
    587 #define _UI_LAYOUT_SCROLL_BAR_PAIR(element) \
    588 		element->vScroll->page = vSpace - (element->hScroll->page < element->hScroll->maximum ? scrollBarSize : 0); \
    589 		element->hScroll->page = hSpace - (element->vScroll->page < element->vScroll->maximum ? scrollBarSize : 0); \
    590 		element->vScroll->page = vSpace - (element->hScroll->page < element->hScroll->maximum ? scrollBarSize : 0); \
    591 		UIRectangle vScrollBarBounds = element->e.bounds, hScrollBarBounds = element->e.bounds; \
    592 		hScrollBarBounds.r = vScrollBarBounds.l = vScrollBarBounds.r - (element->vScroll->page < element->vScroll->maximum ? scrollBarSize : 0); \
    593 		vScrollBarBounds.b = hScrollBarBounds.t = hScrollBarBounds.b - (element->hScroll->page < element->hScroll->maximum ? scrollBarSize : 0); \
    594 		UIElementMove(&element->vScroll->e, vScrollBarBounds, true); \
    595 		UIElementMove(&element->hScroll->e, hScrollBarBounds, true);
    596 #define _UI_KEY_INPUT_VSCROLL(element, rowHeight, pageHeight) \
    597 		if (m->code == UI_KEYCODE_UP) element->vScroll->position -= (rowHeight); \
    598 		else if (m->code == UI_KEYCODE_DOWN) element->vScroll->position += (rowHeight); \
    599 		else if (m->code == UI_KEYCODE_PAGE_UP) element->vScroll->position += (pageHeight); \
    600 		else if (m->code == UI_KEYCODE_PAGE_DOWN) element->vScroll->position -= (pageHeight); \
    601 		else if (m->code == UI_KEYCODE_HOME) element->vScroll->position = 0; \
    602 		else if (m->code == UI_KEYCODE_END) element->vScroll->position = element->vScroll->maximum; \
    603 		UIElementRefresh(&element->e);
    604 
    605 typedef struct UICodeLine {
    606 	int offset, bytes;
    607 } UICodeLine;
    608 
    609 typedef struct UICode {
    610 #define UI_CODE_NO_MARGIN (1 << 0)
    611 #define UI_CODE_SELECTABLE (1 << 1)
    612 	UIElement e;
    613 	UIScrollBar *vScroll, *hScroll;
    614 	UICodeLine *lines;
    615 	UIFont *font;
    616 	int lineCount, focused;
    617 	bool moveScrollToFocusNextLayout;
    618 	bool leftDownInMargin;
    619 	char *content;
    620 	size_t contentBytes;
    621 	int tabSize;
    622 	int columns;
    623 	UI_CLOCK_T lastAnimateTime;
    624 	struct { int line, offset; } selection[4 /* start, end, anchor, caret */];
    625 	int verticalMotionColumn;
    626 	bool useVerticalMotionColumn;
    627 	bool moveScrollToCaretNextLayout;
    628 	bool centerExecutionPointer;
    629 } UICode;
    630 
    631 typedef struct UIGauge {
    632 	UIElement e;
    633 	double position;
    634 } UIGauge;
    635 
    636 typedef struct UITable {
    637 	UIElement e;
    638 	UIScrollBar *vScroll, *hScroll;
    639 	int itemCount;
    640 	char *columns;
    641 	int *columnWidths, columnCount, columnHighlight;
    642 } UITable;
    643 
    644 typedef struct UITextbox {
    645 	UIElement e;
    646 	char *string;
    647 	ptrdiff_t bytes;
    648 	int carets[2];
    649 	int scroll;
    650 	bool rejectNextKey;
    651 } UITextbox;
    652 
    653 #define UI_MENU_PLACE_ABOVE (1 << 0)
    654 #define UI_MENU_NO_SCROLL (1 << 1)
    655 #if defined(UI_COCOA)
    656 typedef NSMenu UIMenu;
    657 #elif defined(UI_ESSENCE)
    658 typedef EsMenu UIMenu;
    659 #else
    660 typedef struct UIMenu {
    661 	UIElement e;
    662 	int pointX, pointY;
    663 	UIScrollBar *vScroll;
    664 	UIWindow *parentWindow;
    665 } UIMenu;
    666 #endif
    667 
    668 typedef struct UISlider {
    669 	UIElement e;
    670 	double position;
    671 	int steps;
    672 } UISlider;
    673 
    674 typedef struct UIMDIClient {
    675 #define UI_MDI_CLIENT_TRANSPARENT (1 << 0)
    676 	UIElement e;
    677 	struct UIMDIChild *active;
    678 	int cascade;
    679 } UIMDIClient;
    680 
    681 typedef struct UIMDIChild {
    682 #define UI_MDI_CHILD_CLOSE_BUTTON (1 << 0)
    683 	UIElement e;
    684 	UIRectangle bounds;
    685 	char *title;
    686 	ptrdiff_t titleBytes;
    687 	int dragHitTest;
    688 	UIRectangle dragOffset;
    689 } UIMDIChild;
    690 
    691 typedef struct UIExpandPane {
    692 	UIElement e;
    693 	UIButton *button;
    694 	UIPanel *panel;
    695 	bool expanded;
    696 } UIExpandPane;
    697 
    698 typedef struct UIImageDisplay {
    699 #define UI_IMAGE_DISPLAY_INTERACTIVE (1 << 0)
    700 #define _UI_IMAGE_DISPLAY_ZOOM_FIT (1 << 1)
    701 
    702 	UIElement e;
    703 	uint32_t *bits;
    704 	int width, height;
    705 	float panX, panY, zoom;
    706 
    707 	// Internals:
    708 	int previousWidth, previousHeight;
    709 	int previousPanPointX, previousPanPointY;
    710 } UIImageDisplay;
    711 
    712 typedef struct UIWrapPanel {
    713 	UIElement e;
    714 } UIWrapPanel;
    715 
    716 typedef struct UISwitcher {
    717 	UIElement e;
    718 	UIElement *active;
    719 } UISwitcher;
    720 
    721 void UIInitialise();
    722 int UIMessageLoop();
    723 
    724 UIElement *UIElementCreate(size_t bytes, UIElement *parent, uint32_t flags,
    725 	int (*messageClass)(UIElement *, UIMessage, int, void *), const char *cClassName);
    726 
    727 UICheckbox *UICheckboxCreate(UIElement *parent, uint32_t flags, const char *label, ptrdiff_t labelBytes);
    728 UIExpandPane *UIExpandPaneCreate(UIElement *parent, uint32_t flags, const char *label, ptrdiff_t labelBytes, uint32_t panelFlags);
    729 UIMDIClient *UIMDIClientCreate(UIElement *parent, uint32_t flags);
    730 UIMDIChild *UIMDIChildCreate(UIElement *parent, uint32_t flags, UIRectangle initialBounds, const char *title, ptrdiff_t titleBytes);
    731 UIPanel *UIPanelCreate(UIElement *parent, uint32_t flags);
    732 UIScrollBar *UIScrollBarCreate(UIElement *parent, uint32_t flags);
    733 UISlider *UISliderCreate(UIElement *parent, uint32_t flags);
    734 UISpacer *UISpacerCreate(UIElement *parent, uint32_t flags, int width, int height);
    735 UISplitPane *UISplitPaneCreate(UIElement *parent, uint32_t flags, float weight);
    736 UITabPane *UITabPaneCreate(UIElement *parent, uint32_t flags, const char *tabs /* separate with \t, terminate with \0 */);
    737 UIWrapPanel *UIWrapPanelCreate(UIElement *parent, uint32_t flags);
    738 
    739 UIGauge *UIGaugeCreate(UIElement *parent, uint32_t flags);
    740 void UIGaugeSetPosition(UIGauge *gauge, float value);
    741 
    742 UIButton *UIButtonCreate(UIElement *parent, uint32_t flags, const char *label, ptrdiff_t labelBytes);
    743 void UIButtonSetLabel(UIButton *button, const char *string, ptrdiff_t stringBytes);
    744 UILabel *UILabelCreate(UIElement *parent, uint32_t flags, const char *label, ptrdiff_t labelBytes);
    745 void UILabelSetContent(UILabel *code, const char *content, ptrdiff_t byteCount);
    746 
    747 UIImageDisplay *UIImageDisplayCreate(UIElement *parent, uint32_t flags, uint32_t *bits, size_t width, size_t height, size_t stride);
    748 void UIImageDisplaySetContent(UIImageDisplay *display, uint32_t *bits, size_t width, size_t height, size_t stride);
    749 
    750 UISwitcher *UISwitcherCreate(UIElement *parent, uint32_t flags);
    751 void UISwitcherSwitchTo(UISwitcher *switcher, UIElement *child);
    752 
    753 UIWindow *UIWindowCreate(UIWindow *owner, uint32_t flags, const char *cTitle, int width, int height);
    754 void UIWindowRegisterShortcut(UIWindow *window, UIShortcut shortcut);
    755 void UIWindowPostMessage(UIWindow *window, UIMessage message, void *dp); // Thread-safe.
    756 void UIWindowPack(UIWindow *window, int width); // Change the size of the window to best match its contents.
    757 
    758 typedef void (*UIDialogUserCallback)(UIElement *);
    759 const char *UIDialogShow(UIWindow *window, uint32_t flags, const char *format, ...);
    760 
    761 UIMenu *UIMenuCreate(UIElement *parent, uint32_t flags);
    762 void UIMenuAddItem(UIMenu *menu, uint32_t flags, const char *label, ptrdiff_t labelBytes, void (*invoke)(void *cp), void *cp);
    763 void UIMenuShow(UIMenu *menu);
    764 bool UIMenusOpen();
    765 
    766 UITextbox *UITextboxCreate(UIElement *parent, uint32_t flags);
    767 void UITextboxReplace(UITextbox *textbox, const char *text, ptrdiff_t bytes, bool sendChangedMessage);
    768 void UITextboxClear(UITextbox *textbox, bool sendChangedMessage);
    769 void UITextboxMoveCaret(UITextbox *textbox, bool backward, bool word);
    770 char *UITextboxToCString(UITextbox *textbox); // Free with UI_FREE.
    771 
    772 UITable *UITableCreate(UIElement *parent, uint32_t flags, const char *columns /* separate with \t, terminate with \0 */);
    773 int UITableHitTest(UITable *table, int x, int y); // Returns item index. Returns -1 if not on an item.
    774 int UITableHeaderHitTest(UITable *table, int x, int y); // Returns column index or -1.
    775 bool UITableEnsureVisible(UITable *table, int index); // Returns false if the item was already visible.
    776 void UITableResizeColumns(UITable *table);
    777 
    778 UICode *UICodeCreate(UIElement *parent, uint32_t flags);
    779 void UICodeFocusLine(UICode *code, int index); // Line numbers are 1-indexed!!
    780 int UICodeHitTest(UICode *code, int x, int y); // Returns line number; negates if in margin. Returns 0 if not on a line.
    781 void UICodePositionToByte(UICode *code, int x, int y, int *line, int *byte);
    782 void UICodeInsertContent(UICode *code, const char *content, ptrdiff_t byteCount, bool replace);
    783 void UICodeMoveCaret(UICode *code, bool backward, bool word);
    784 
    785 void UIDrawBlock(UIPainter *painter, UIRectangle rectangle, uint32_t color);
    786 void UIDrawCircle(UIPainter *painter, int centerX, int centerY, int radius, uint32_t fillColor, uint32_t outlineColor, bool hollow);
    787 void UIDrawControl(UIPainter *painter, UIRectangle bounds, uint32_t mode /* UI_DRAW_CONTROL_* */, const char *label, ptrdiff_t labelBytes, double position, float scale);
    788 void UIDrawControlDefault(UIPainter *painter, UIRectangle bounds, uint32_t mode, const char *label, ptrdiff_t labelBytes, double position, float scale);
    789 void UIDrawInvert(UIPainter *painter, UIRectangle rectangle);
    790 bool UIDrawLine(UIPainter *painter, int x0, int y0, int x1, int y1, uint32_t color); // Returns false if the line was not visible.
    791 void UIDrawTriangle(UIPainter *painter, int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color);
    792 void UIDrawTriangleOutline(UIPainter *painter, int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color);
    793 void UIDrawGlyph(UIPainter *painter, int x, int y, int c, uint32_t color);
    794 void UIDrawRectangle(UIPainter *painter, UIRectangle r, uint32_t mainColor, uint32_t borderColor, UIRectangle borderSize);
    795 void UIDrawBorder(UIPainter *painter, UIRectangle r, uint32_t borderColor, UIRectangle borderSize);
    796 void UIDrawString(UIPainter *painter, UIRectangle r, const char *string, ptrdiff_t bytes, uint32_t color, int align, UIStringSelection *selection);
    797 int  UIDrawStringHighlighted(UIPainter *painter, UIRectangle r, const char *string, ptrdiff_t bytes, int tabSize, UIStringSelection *selection); // Returns final x position.
    798 
    799 int UIMeasureStringWidth(const char *string, ptrdiff_t bytes);
    800 int UIMeasureStringHeight();
    801 
    802 uint64_t UIAnimateClock(); // In ms.
    803 
    804 bool UIElementAnimate(UIElement *element, bool stop);
    805 void UIElementDestroy(UIElement *element);
    806 void UIElementDestroyDescendents(UIElement *element);
    807 UIElement *UIElementFindByPoint(UIElement *element, int x, int y);
    808 void UIElementFocus(UIElement *element);
    809 UIRectangle UIElementScreenBounds(UIElement *element); // Returns bounds of element in same coordinate system as used by UIWindowCreate.
    810 void UIElementRefresh(UIElement *element);
    811 void UIElementRelayout(UIElement *element);
    812 void UIElementRepaint(UIElement *element, UIRectangle *region);
    813 void UIElementMeasurementsChanged(UIElement *element, int which);
    814 void UIElementMove(UIElement *element, UIRectangle bounds, bool alwaysLayout);
    815 int UIElementMessage(UIElement *element, UIMessage message, int di, void *dp);
    816 UIElement *UIElementChangeParent(UIElement *element, UIElement *newParent, UIElement *insertBefore); // Set insertBefore to null to insert at the end. Returns the element it was before in its previous parent, or NULL.
    817 
    818 UIElement *UIParentPush(UIElement *element);
    819 UIElement *UIParentPop();
    820 
    821 UIRectangle UIRectangleIntersection(UIRectangle a, UIRectangle b);
    822 UIRectangle UIRectangleBounding(UIRectangle a, UIRectangle b);
    823 UIRectangle UIRectangleAdd(UIRectangle a, UIRectangle b);
    824 UIRectangle UIRectangleTranslate(UIRectangle a, UIRectangle b);
    825 UIRectangle UIRectangleCenter(UIRectangle parent, UIRectangle child);
    826 UIRectangle UIRectangleFit(UIRectangle parent, UIRectangle child, bool allowScalingUp);
    827 bool UIRectangleEquals(UIRectangle a, UIRectangle b);
    828 bool UIRectangleContains(UIRectangle a, int x, int y);
    829 
    830 bool UIColorToHSV(uint32_t rgb, float *hue, float *saturation, float *value);
    831 void UIColorToRGB(float hue, float saturation, float value, uint32_t *rgb);
    832 
    833 char *UIStringCopy(const char *in, ptrdiff_t inBytes);
    834 
    835 UIFont *UIFontCreate(const char *cPath, uint32_t size);
    836 UIFont *UIFontActivate(UIFont *font); // Returns the previously active font.
    837 
    838 #ifdef UI_DEBUG
    839 void UIInspectorLog(const char *cFormat, ...);
    840 #endif
    841 
    842 static ptrdiff_t _UIStringLength(const char *cString) {
    843 	if (!cString) return 0;
    844 	ptrdiff_t length;
    845 	for (length = 0; cString[length]; length++);
    846 	return length;
    847 }
    848 
    849 #ifdef UI_UNICODE
    850 
    851 #ifndef UI_FREETYPE
    852 #error "Unicode support requires Freetype"
    853 #endif
    854 
    855 #define _UNICODE_MAX_CODEPOINT 0x10FFFF
    856 
    857 int Utf8GetCodePoint(const char *cString, ptrdiff_t bytesLength, ptrdiff_t *bytesConsumed) {
    858 	UI_ASSERT(bytesLength > 0 && "Attempted to get UTF-8 code point from an empty string");
    859 
    860 	if (bytesConsumed == NULL) {
    861 		ptrdiff_t bytesConsumed;
    862 		return Utf8GetCodePoint(cString, bytesLength, &bytesConsumed);
    863 	}
    864 
    865 	ptrdiff_t numExtraBytes;
    866 	uint8_t first = cString[0];
    867 
    868 	*bytesConsumed = 1;
    869 	if ((first & 0xF0) == 0xF0) {
    870 		numExtraBytes = 3;
    871 	} else if ((first & 0xE0) == 0xE0) {
    872 		numExtraBytes = 2;
    873 	} else if ((first & 0xC0) == 0xC0) {
    874 		numExtraBytes = 1;
    875 	} else if (first & 0x7F) {
    876 		return first & 0x80 ? -1 : first;
    877 	} else {
    878 		return -1;
    879 	}
    880 
    881 	if (bytesLength < numExtraBytes + 1) {
    882 		return -1;
    883 	}
    884 
    885 	int codePoint = ((int)first & (0x3F >> numExtraBytes)) << (6 * numExtraBytes);
    886 	for (ptrdiff_t idx = 1; idx < numExtraBytes + 1; idx++) {
    887 		char byte = cString[idx];
    888 		if ((byte & 0xC0) != 0x80) {
    889 			return -1;
    890 		}
    891 
    892 		codePoint |= (byte & 0x3F) << (6 * (numExtraBytes - idx));
    893 		(*bytesConsumed)++;
    894 	}
    895 
    896 	return codePoint > _UNICODE_MAX_CODEPOINT ? -1 : codePoint;
    897 }
    898 
    899 char * Utf8GetPreviousChar(char *string, char *offset) {
    900 	if (string == offset) {
    901 		return string;
    902 	}
    903 
    904 	char *prev = offset - 1;
    905 	while (prev > string) {
    906 		if ((*prev & 0xC0) == 0x80) prev--;
    907 		else break;
    908 	}
    909 
    910 	return prev;
    911 }
    912 
    913 ptrdiff_t Utf8GetCharBytes(const char *cString, ptrdiff_t bytes) {
    914 	if (!cString) {
    915 		return 0;
    916 	}
    917 	if (bytes == -1) {
    918 		bytes = _UIStringLength(cString);
    919 	}
    920 
    921 	ptrdiff_t bytesConsumed;
    922 	Utf8GetCodePoint(cString, bytes, &bytesConsumed);
    923 	return bytesConsumed;
    924 }
    925 
    926 ptrdiff_t Utf8StringLength(const char *cString, ptrdiff_t bytes) {
    927 	if (!cString) {
    928 		return 0;
    929 	}
    930 	if (bytes == -1) {
    931 		bytes = _UIStringLength(cString);
    932 	}
    933 
    934 	ptrdiff_t length = 0;
    935 	ptrdiff_t byteIndex = 0;
    936 	while (byteIndex < bytes) {
    937 		ptrdiff_t bytesConsumed;
    938 		Utf8GetCodePoint(cString+ byteIndex, bytes - byteIndex, &bytesConsumed);
    939 		byteIndex += bytesConsumed;
    940 		length++;
    941 
    942 		UI_ASSERT(byteIndex <= bytes && "Overran the end of the string while counting the number of UTF-8 code points");
    943 	}
    944 
    945 	return length;
    946 }
    947 
    948 #define _UI_ADVANCE_CHAR(index, text, count) \
    949 	index += Utf8GetCharBytes(text, count - index)
    950 
    951 #define _UI_SKIP_TAB(ti, text, bytesLeft, tabSize) do { \
    952 	int c = Utf8GetCodePoint(text, bytesLeft, NULL); \
    953 	if (c == '\t') while (ti % tabSize) ti++; \
    954 } while (0)
    955 
    956 #define _UI_MOVE_CARET_BACKWARD(caret, text, offset, offset2) do { \
    957 	char *prev = Utf8GetPreviousChar(text, text + offset); \
    958 	caret = prev - text - offset2; \
    959 } while (0)
    960 
    961 #define _UI_MOVE_CARET_FORWARD(caret, text, bytes, offset) do { \
    962 	caret += Utf8GetCharBytes(text + caret, bytes - offset); \
    963 } while (0)
    964 
    965 #define _UI_MOVE_CARET_BY_WORD(text, bytes, offset) { \
    966 	char *prev = Utf8GetPreviousChar(text, text + offset); \
    967 	int c1 = Utf8GetCodePoint(prev, bytes - (prev - text), NULL); \
    968 	int c2 = Utf8GetCodePoint(text + offset, bytes - offset, NULL); \
    969 	if (_UICharIsAlphaOrDigitOrUnderscore(c1) != _UICharIsAlphaOrDigitOrUnderscore(c2)) break; \
    970 }
    971 
    972 #else
    973 
    974 #define _UI_ADVANCE_CHAR(index, code, count) index++
    975 
    976 #define _UI_SKIP_TAB(ti, text, bytesLeft, tabSize) \
    977 	if (*(text) == '\t') while (ti % tabSize) ti++
    978 
    979 #define _UI_MOVE_CARET_BACKWARD(caret, text, offset, offset2) caret--
    980 #define _UI_MOVE_CARET_FORWARD(caret, text, bytes, offset) caret++
    981 
    982 #define _UI_MOVE_CARET_BY_WORD(text, bytes, offset) { \
    983 	char c1 = (text)[offset - 1]; \
    984 	char c2 = (text)[offset]; \
    985 	if (_UICharIsAlphaOrDigitOrUnderscore(c1) != _UICharIsAlphaOrDigitOrUnderscore(c2)) break; \
    986 }
    987 
    988 #endif // UI_UNICODE
    989 
    990 #ifdef UI_IMPLEMENTATION
    991 
    992 /////////////////////////////////////////
    993 // Global variables.
    994 /////////////////////////////////////////
    995 
    996 struct {
    997 	UIWindow *windows;
    998 	UITheme theme;
    999 
   1000 	UIElement **animating;
   1001 	uint32_t animatingCount;
   1002 
   1003 	UIElement *parentStack[16];
   1004 	int parentStackCount;
   1005 
   1006 	bool quit;
   1007 	const char *dialogResult;
   1008 	UIElement *dialogOldFocus;
   1009 	bool dialogCanExit;
   1010 
   1011 	UIFont *activeFont;
   1012 
   1013 #ifdef UI_DEBUG
   1014 	UIWindow *inspector;
   1015 	UITable *inspectorTable;
   1016 	UIWindow *inspectorTarget;
   1017 	UICode *inspectorLog;
   1018 #endif
   1019 
   1020 #ifdef UI_LINUX
   1021 	Display *display;
   1022 	Visual *visual;
   1023 	XIM xim;
   1024 	Atom windowClosedID, primaryID, uriListID, plainTextID;
   1025 	Atom dndEnterID, dndPositionID, dndStatusID, dndActionCopyID, dndDropID, dndSelectionID, dndFinishedID, dndAwareID;
   1026 	Atom clipboardID, xSelectionDataID, textID, targetID, incrID;
   1027 	Cursor cursors[UI_CURSOR_COUNT];
   1028 	char *pasteText;
   1029 	XEvent copyEvent;
   1030 #endif
   1031 
   1032 #ifdef UI_WINDOWS
   1033 	HCURSOR cursors[UI_CURSOR_COUNT];
   1034 	HANDLE heap;
   1035 	bool assertionFailure;
   1036 #endif
   1037 
   1038 #ifdef UI_ESSENCE
   1039 	EsInstance *instance;
   1040 #endif
   1041 
   1042 #if defined(UI_ESSENCE) || defined(UI_COCOA)
   1043 	void *menuData[256]; // HACK This limits the number of menu items to 128.
   1044 	uintptr_t menuIndex;
   1045 #endif
   1046 
   1047 #ifdef UI_COCOA
   1048 	int menuX, menuY;
   1049 	UIWindow *menuWindow;
   1050 #endif
   1051 
   1052 #ifdef UI_FREETYPE
   1053 	FT_Library ft;
   1054 #endif
   1055 } ui;
   1056 
   1057 /////////////////////////////////////////
   1058 // Themes.
   1059 /////////////////////////////////////////
   1060 
   1061 UITheme uiThemeClassic = {
   1062 	.panel1 = 0xFFF0F0F0,
   1063 	.panel2 = 0xFFFFFFFF,
   1064 	.selected = 0xFF94BEFE,
   1065 	.border = 0xFF404040,
   1066 
   1067 	.text = 0xFF000000,
   1068 	.textDisabled = 0xFF404040,
   1069 	.textSelected = 0xFF000000,
   1070 
   1071 	.buttonNormal = 0xFFE0E0E0,
   1072 	.buttonHovered = 0xFFF0F0F0,
   1073 	.buttonPressed = 0xFFA0A0A0,
   1074 	.buttonDisabled = 0xFFF0F0F0,
   1075 
   1076 	.textboxNormal = 0xFFF8F8F8,
   1077 	.textboxFocused = 0xFFFFFFFF,
   1078 
   1079 	.codeFocused = 0xFFE0E0E0,
   1080 	.codeBackground = 0xFFFFFFFF,
   1081 	.codeDefault = 0xFF000000,
   1082 	.codeComment = 0xFFA11F20,
   1083 	.codeString = 0xFF037E01,
   1084 	.codeNumber = 0xFF213EF1,
   1085 	.codeOperator = 0xFF7F0480,
   1086 	.codePreprocessor = 0xFF545D70,
   1087 
   1088 	.accent1 = 0xFF0000,
   1089 	.accent2 = 0x00FF00,
   1090 };
   1091 
   1092 UITheme uiThemeDark = {
   1093 	.panel1 = 0xFF252B31,
   1094 	.panel2 = 0xFF14181E,
   1095 	.selected = 0xFF94BEFE,
   1096 	.border = 0xFF000000,
   1097 
   1098 	.text = 0xFFFFFFFF,
   1099 	.textDisabled = 0xFF787D81,
   1100 	.textSelected = 0xFF000000,
   1101 
   1102 	.buttonNormal = 0xFF383D41,
   1103 	.buttonHovered = 0xFF4B5874,
   1104 	.buttonPressed = 0xFF0D0D0F,
   1105 	.buttonDisabled = 0xFF1B1F23,
   1106 
   1107 	.textboxNormal = 0xFF31353C,
   1108 	.textboxFocused = 0xFF4D4D59,
   1109 
   1110 	.codeFocused = 0xFF505055,
   1111 	.codeBackground = 0xFF212126,
   1112 	.codeDefault = 0xFFFFFFFF,
   1113 	.codeComment = 0xFFB4B4B4,
   1114 	.codeString = 0xFFF5DDD1,
   1115 	.codeNumber = 0xFFC3F5D3,
   1116 	.codeOperator = 0xFFF5D499,
   1117 	.codePreprocessor = 0xFFF5F3D1,
   1118 
   1119 	.accent1 = 0xF01231,
   1120 	.accent2 = 0x45F94E,
   1121 };
   1122 
   1123 /////////////////////////////////////////
   1124 // Forward declarations.
   1125 /////////////////////////////////////////
   1126 
   1127 void _UIWindowEndPaint(UIWindow *window, UIPainter *painter);
   1128 void _UIWindowSetCursor(UIWindow *window, int cursor);
   1129 void _UIWindowGetScreenPosition(UIWindow *window, int *x, int *y);
   1130 void _UIWindowSetPressed(UIWindow *window, UIElement *element, int button);
   1131 void _UIClipboardWriteText(UIWindow *window, char *text);
   1132 char *_UIClipboardReadTextStart(UIWindow *window, size_t *bytes);
   1133 void _UIClipboardReadTextEnd(UIWindow *window, char *text);
   1134 bool _UIMessageLoopSingle(int *result);
   1135 void _UIInspectorRefresh();
   1136 void _UIUpdate();
   1137 
   1138 #if defined(UI_LINUX) || defined(UI_COCOA)
   1139 UI_CLOCK_T _UIClock() {
   1140 	struct timespec spec;
   1141 	clock_gettime(CLOCK_REALTIME, &spec);
   1142 	return spec.tv_sec * 1000 + spec.tv_nsec / 1000000;
   1143 }
   1144 #endif
   1145 
   1146 #ifdef UI_WINDOWS
   1147 void *_UIHeapReAlloc(void *pointer, size_t size);
   1148 void *_UIMemmove(void *dest, const void *src, size_t n);
   1149 #endif
   1150 
   1151 /////////////////////////////////////////
   1152 // Helper functions.
   1153 /////////////////////////////////////////
   1154 
   1155 UIRectangle UIRectangleIntersection(UIRectangle a, UIRectangle b) {
   1156 	if (a.l < b.l) a.l = b.l;
   1157 	if (a.t < b.t) a.t = b.t;
   1158 	if (a.r > b.r) a.r = b.r;
   1159 	if (a.b > b.b) a.b = b.b;
   1160 	return a;
   1161 }
   1162 
   1163 UIRectangle UIRectangleBounding(UIRectangle a, UIRectangle b) {
   1164 	if (a.l > b.l) a.l = b.l;
   1165 	if (a.t > b.t) a.t = b.t;
   1166 	if (a.r < b.r) a.r = b.r;
   1167 	if (a.b < b.b) a.b = b.b;
   1168 	return a;
   1169 }
   1170 
   1171 UIRectangle UIRectangleAdd(UIRectangle a, UIRectangle b) {
   1172 	a.l += b.l;
   1173 	a.t += b.t;
   1174 	a.r += b.r;
   1175 	a.b += b.b;
   1176 	return a;
   1177 }
   1178 
   1179 UIRectangle UIRectangleTranslate(UIRectangle a, UIRectangle b) {
   1180 	a.l += b.l;
   1181 	a.t += b.t;
   1182 	a.r += b.l;
   1183 	a.b += b.t;
   1184 	return a;
   1185 }
   1186 
   1187 UIRectangle UIRectangleCenter(UIRectangle parent, UIRectangle child) {
   1188 	int childWidth = UI_RECT_WIDTH(child), childHeight = UI_RECT_HEIGHT(child);
   1189 	int parentWidth = UI_RECT_WIDTH(parent), parentHeight = UI_RECT_HEIGHT(parent);
   1190 	child.l = parentWidth / 2 - childWidth / 2 + parent.l, child.r = child.l + childWidth;
   1191 	child.t = parentHeight / 2 - childHeight / 2 + parent.t, child.b = child.t + childHeight;
   1192 	return child;
   1193 }
   1194 
   1195 UIRectangle UIRectangleFit(UIRectangle parent, UIRectangle child, bool allowScalingUp) {
   1196 	int childWidth = UI_RECT_WIDTH(child), childHeight = UI_RECT_HEIGHT(child);
   1197 	int parentWidth = UI_RECT_WIDTH(parent), parentHeight = UI_RECT_HEIGHT(parent);
   1198 
   1199 	if (childWidth < parentWidth && childHeight < parentHeight && !allowScalingUp) {
   1200 		return UIRectangleCenter(parent, child);
   1201 	}
   1202 
   1203 	float childAspectRatio = (float) childWidth / childHeight;
   1204 	int childMaximumWidth = parentHeight * childAspectRatio;
   1205 	int childMaximumHeight = parentWidth / childAspectRatio;
   1206 
   1207 	if (childMaximumWidth > parentWidth) {
   1208 		return UIRectangleCenter(parent, UI_RECT_2S(parentWidth, childMaximumHeight));
   1209 	} else {
   1210 		return UIRectangleCenter(parent, UI_RECT_2S(childMaximumWidth, parentHeight));
   1211 	}
   1212 }
   1213 
   1214 bool UIRectangleEquals(UIRectangle a, UIRectangle b) {
   1215 	return a.l == b.l && a.r == b.r && a.t == b.t && a.b == b.b;
   1216 }
   1217 
   1218 bool UIRectangleContains(UIRectangle a, int x, int y) {
   1219 	return a.l <= x && a.r > x && a.t <= y && a.b > y;
   1220 }
   1221 
   1222 typedef union _UIConvertFloatInteger {
   1223 	float f;
   1224 	uint32_t i;
   1225 } _UIConvertFloatInteger;
   1226 
   1227 float _UIFloorFloat(float x) {
   1228 	_UIConvertFloatInteger convert = {x};
   1229 	uint32_t sign = convert.i & 0x80000000;
   1230 	int exponent = (int) ((convert.i >> 23) & 0xFF) - 0x7F;
   1231 
   1232 	if (exponent >= 23) {
   1233 		// There aren't any bits representing a fractional part.
   1234 	} else if (exponent >= 0) {
   1235 		// Positive exponent.
   1236 		uint32_t mask = 0x7FFFFF >> exponent;
   1237 		if (!(mask & convert.i)) return x; // Already an integer.
   1238 		if (sign) convert.i += mask;
   1239 		convert.i &= ~mask; // Mask out the fractional bits.
   1240 	} else if (exponent < 0) {
   1241 		// Negative exponent.
   1242 		return sign ? -1.0 : 0.0;
   1243 	}
   1244 
   1245 	return convert.f;
   1246 }
   1247 
   1248 float _UILinearMap(float value, float inFrom, float inTo, float outFrom, float outTo) {
   1249 	float inRange = inTo - inFrom, outRange = outTo - outFrom;
   1250 	float normalisedValue = (value - inFrom) / inRange;
   1251 	return normalisedValue * outRange + outFrom;
   1252 }
   1253 
   1254 bool UIColorToHSV(uint32_t rgb, float *hue, float *saturation, float *value) {
   1255 	float r = UI_COLOR_RED_F(rgb);
   1256 	float g = UI_COLOR_GREEN_F(rgb);
   1257 	float b = UI_COLOR_BLUE_F(rgb);
   1258 
   1259 	float maximum = (r > g && r > b) ? r : (g > b ? g : b),
   1260 	      minimum = (r < g && r < b) ? r : (g < b ? g : b),
   1261 	      difference = maximum - minimum;
   1262 	*value = maximum;
   1263 
   1264 	if (!difference) {
   1265 		*saturation = 0;
   1266 		return false;
   1267 	} else {
   1268 		if (r == maximum) *hue = (g - b) / difference + 0;
   1269 		if (g == maximum) *hue = (b - r) / difference + 2;
   1270 		if (b == maximum) *hue = (r - g) / difference + 4;
   1271 		if (*hue < 0) *hue += 6;
   1272 		*saturation = difference / maximum;
   1273 		return true;
   1274 	}
   1275 }
   1276 
   1277 void UIColorToRGB(float h, float s, float v, uint32_t *rgb) {
   1278 	float r, g, b;
   1279 
   1280 	if (!s) {
   1281 		r = g = b = v;
   1282 	} else {
   1283 		int h0 = ((int) h) % 6;
   1284 		float f = h - _UIFloorFloat(h);
   1285 		float x = v * (1 - s), y = v * (1 - s * f), z = v * (1 - s * (1 - f));
   1286 
   1287 		switch (h0) {
   1288 			case 0:  r = v, g = z, b = x; break;
   1289 			case 1:  r = y, g = v, b = x; break;
   1290 			case 2:  r = x, g = v, b = z; break;
   1291 			case 3:  r = x, g = y, b = v; break;
   1292 			case 4:  r = z, g = x, b = v; break;
   1293 			default: r = v, g = x, b = y; break;
   1294 		}
   1295 	}
   1296 
   1297 	*rgb = UI_COLOR_FROM_FLOAT(r, g, b);
   1298 }
   1299 
   1300 char *UIStringCopy(const char *in, ptrdiff_t inBytes) {
   1301 	if (inBytes == -1) {
   1302 		inBytes = _UIStringLength(in);
   1303 	}
   1304 
   1305 	char *buffer = (char *) UI_MALLOC(inBytes + 1);
   1306 
   1307 	for (intptr_t i = 0; i < inBytes; i++) {
   1308 		buffer[i] = in[i];
   1309 	}
   1310 
   1311 	buffer[inBytes] = 0;
   1312 	return buffer;
   1313 }
   1314 
   1315 int _UIByteToColumn(const char *string, int byte, int bytes, int tabSize) {
   1316 	int ti = 0, i = 0;
   1317 
   1318 	while (i < byte && i < bytes) {
   1319 		ti++;
   1320 		_UI_SKIP_TAB(ti, string + i, bytes - i, tabSize);
   1321 		_UI_ADVANCE_CHAR(i, string + i, byte);
   1322 	}
   1323 
   1324 	return ti;
   1325 }
   1326 
   1327 int _UIColumnToByte(const char *string, int column, int bytes, int tabSize) {
   1328 	int byte = 0, ti = 0;
   1329 
   1330 	while (byte < bytes) {
   1331 		ti++;
   1332 		_UI_SKIP_TAB(ti, string + byte, bytes - byte, tabSize);
   1333 		if (column < ti) break;
   1334 
   1335 		_UI_ADVANCE_CHAR(byte, string + byte, bytes);
   1336 	}
   1337 
   1338 	return byte;
   1339 }
   1340 
   1341 /////////////////////////////////////////
   1342 // Animations.
   1343 /////////////////////////////////////////
   1344 
   1345 bool UIElementAnimate(UIElement *element, bool stop) {
   1346 	if (stop) {
   1347 		for (uint32_t i = 0; i < ui.animatingCount; i++) {
   1348 			if (ui.animating[i] == element) {
   1349 				ui.animating[i] = ui.animating[ui.animatingCount - 1];
   1350 				ui.animatingCount--;
   1351 				return true;
   1352 			}
   1353 		}
   1354 
   1355 		return false;
   1356 	} else {
   1357 		for (uint32_t i = 0; i < ui.animatingCount; i++) {
   1358 			if (ui.animating[i] == element) {
   1359 				return true;
   1360 			}
   1361 		}
   1362 
   1363 		ui.animating = (UIElement **) UI_REALLOC(ui.animating, sizeof(UIElement *) * (ui.animatingCount + 1));
   1364 		ui.animating[ui.animatingCount] = element;
   1365 		ui.animatingCount++;
   1366 		UI_ASSERT(~element->flags & UI_ELEMENT_DESTROY);
   1367 		return true;
   1368 	}
   1369 }
   1370 
   1371 uint64_t UIAnimateClock() {
   1372 	return (uint64_t) UI_CLOCK() * 1000 / UI_CLOCKS_PER_SECOND;
   1373 }
   1374 
   1375 void _UIProcessAnimations() {
   1376 	bool update = ui.animatingCount;
   1377 
   1378 	for (uint32_t i = 0; i < ui.animatingCount; i++) {
   1379 		UIElementMessage(ui.animating[i], UI_MSG_ANIMATE, 0, 0);
   1380 	}
   1381 
   1382 	if (update) {
   1383 		_UIUpdate();
   1384 	}
   1385 }
   1386 
   1387 /////////////////////////////////////////
   1388 // Rendering.
   1389 /////////////////////////////////////////
   1390 
   1391 void UIDrawBlock(UIPainter *painter, UIRectangle rectangle, uint32_t color) {
   1392 	rectangle = UIRectangleIntersection(painter->clip, rectangle);
   1393 
   1394 	if (!UI_RECT_VALID(rectangle)) {
   1395 		return;
   1396 	}
   1397 
   1398 #ifdef UI_SSE2
   1399 	__m128i color4 = _mm_set_epi32(color, color, color, color);
   1400 #endif
   1401 
   1402 	for (int line = rectangle.t; line < rectangle.b; line++) {
   1403 		uint32_t *bits = painter->bits + line * painter->width + rectangle.l;
   1404 		int count = UI_RECT_WIDTH(rectangle);
   1405 
   1406 #ifdef UI_SSE2
   1407 		while (count >= 4) {
   1408 			_mm_storeu_si128((__m128i *) bits, color4);
   1409 			bits += 4;
   1410 			count -= 4;
   1411 		}
   1412 #endif
   1413 
   1414 		while (count--) {
   1415 			*bits++ = color;
   1416 		}
   1417 	}
   1418 
   1419 #ifdef UI_DEBUG
   1420 	painter->fillCount += UI_RECT_WIDTH(rectangle) * UI_RECT_HEIGHT(rectangle);
   1421 #endif
   1422 }
   1423 
   1424 bool UIDrawLine(UIPainter *painter, int x0, int y0, int x1, int y1, uint32_t color) {
   1425 	// Apply the clip.
   1426 
   1427 	UIRectangle c = painter->clip;
   1428 	if (!UI_RECT_VALID(c)) return false;
   1429 	int dx = x1 - x0, dy = y1 - y0;
   1430 	const int p[4] = { -dx, dx, -dy, dy };
   1431 	const int q[4] = { x0 - c.l, c.r - 1 - x0, y0 - c.t, c.b - 1 - y0 };
   1432 	float t0 = 0.0f, t1 = 1.0f; // How far along the line the points end up.
   1433 
   1434 	for (int i = 0; i < 4; i++) {
   1435 		if (!p[i] && q[i] < 0) return false;
   1436 		float r = (float) q[i] / p[i];
   1437 		if (p[i] < 0 && r > t1) return false;
   1438 		if (p[i] > 0 && r < t0) return false;
   1439 		if (p[i] < 0 && r > t0) t0 = r;
   1440 		if (p[i] > 0 && r < t1) t1 = r;
   1441 	}
   1442 
   1443 	x1 = x0 + t1 * dx, y1 = y0 + t1 * dy;
   1444 	x0 += t0 * dx, y0 += t0 * dy;
   1445 
   1446 	// Calculate the delta X and delta Y.
   1447 
   1448 	if (y1 < y0) {
   1449 		int t;
   1450 		t = x0, x0 = x1, x1 = t;
   1451 		t = y0, y0 = y1, y1 = t;
   1452 	}
   1453 
   1454 	dx = x1 - x0, dy = y1 - y0;
   1455 	int dxs = dx < 0 ? -1 : 1;
   1456 	if (dx < 0) dx = -dx;
   1457 
   1458 	// Draw the line using Bresenham's line algorithm.
   1459 
   1460 	uint32_t *bits = painter->bits + y0 * painter->width + x0;
   1461 
   1462 	if (dy * dy < dx * dx) {
   1463 		int m = 2 * dy - dx;
   1464 
   1465 		for (int i = 0; i < dx; i++, bits += dxs) {
   1466 			*bits = color;
   1467 			if (m > 0) bits += painter->width, m -= 2 * dx;
   1468 			m += 2 * dy;
   1469 		}
   1470 	} else {
   1471 		int m = 2 * dx - dy;
   1472 
   1473 		for (int i = 0; i < dy; i++, bits += painter->width) {
   1474 			*bits = color;
   1475 			if (m > 0) bits += dxs, m -= 2 * dy;
   1476 			m += 2 * dx;
   1477 		}
   1478 	}
   1479 
   1480 	return true;
   1481 }
   1482 
   1483 void UIDrawCircle(UIPainter *painter, int cx, int cy, int radius, uint32_t fillColor, uint32_t outlineColor, bool hollow) {
   1484 	// TODO There's a hole missing at the bottom of the circle!
   1485 	// TODO This looks bad at small radii (< 20).
   1486 
   1487 	float x = 0, y = -radius;
   1488 	float dx = radius, dy = 0;
   1489 	float step = 0.2f / radius;
   1490 	int px = 0, py = cy + y;
   1491 
   1492 	while (x >= 0) {
   1493 		x  += dx * step;
   1494 		y  += dy * step;
   1495 		dx += -x * step;
   1496 		dy += -y * step;
   1497 
   1498 		int ix = x, iy = cy + y;
   1499 
   1500 		while (py <= iy) {
   1501 			if (py >= painter->clip.t && py < painter->clip.b) {
   1502 				for (int s = 0; s <= ix || s <= px; s++) {
   1503 					bool inOutline = ((s <= ix) != (s <= px)) || ((ix == px) && (s == ix));
   1504 					if (hollow && !inOutline) continue;
   1505 					bool clip0 = cx + s >= painter->clip.l && cx + s < painter->clip.r;
   1506 					bool clip1 = cx - s >= painter->clip.l && cx - s < painter->clip.r;
   1507 					if (clip0) painter->bits[painter->width * py + cx + s] = inOutline ? outlineColor : fillColor;
   1508 					if (clip1) painter->bits[painter->width * py + cx - s] = inOutline ? outlineColor : fillColor;
   1509 				}
   1510 			}
   1511 
   1512 			px = ix, py++;
   1513 		}
   1514 	}
   1515 }
   1516 
   1517 void UIDrawTriangle(UIPainter *painter, int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color) {
   1518 	// Step 1: Sort the points by their y-coordinate.
   1519 	if (y1 < y0) { int xt = x0; x0 = x1, x1 = xt; int yt = y0; y0 = y1, y1 = yt; }
   1520 	if (y2 < y1) { int xt = x1; x1 = x2, x2 = xt; int yt = y1; y1 = y2, y2 = yt; }
   1521 	if (y1 < y0) { int xt = x0; x0 = x1, x1 = xt; int yt = y0; y0 = y1, y1 = yt; }
   1522 	if (y2 == y0) return;
   1523 
   1524 	// Step 2: Clip the triangle.
   1525 	if (x0 < painter->clip.l && x1 < painter->clip.l && x2 < painter->clip.l) return;
   1526 	if (x0 >= painter->clip.r && x1 >= painter->clip.r && x2 >= painter->clip.r) return;
   1527 	if (y2 < painter->clip.t || y0 >= painter->clip.b) return;
   1528 	bool needsXClip = x0 < painter->clip.l + 1 || x0 >= painter->clip.r - 1
   1529 		|| x1 < painter->clip.l + 1 || x1 >= painter->clip.r - 1
   1530 		|| x2 < painter->clip.l + 1 || x2 >= painter->clip.r - 1;
   1531 	bool needsYClip = y0 < painter->clip.t + 1 || y2 >= painter->clip.b - 1;
   1532 #define _UI_DRAW_TRIANGLE_APPLY_CLIP(xo, yo) \
   1533 	if (needsYClip && (yi + yo < painter->clip.t || yi + yo >= painter->clip.b)) continue; \
   1534 	if (needsXClip && xf + xo < painter->clip.l) xf = painter->clip.l - xo; \
   1535 	if (needsXClip && xt + xo > painter->clip.r) xt = painter->clip.r - xo;
   1536 
   1537 	// Step 3: Split into 2 triangles with bases aligned with the x-axis.
   1538 	float xm0 = (x2 - x0) * (y1 - y0) / (y2 - y0), xm1 = x1 - x0;
   1539 	if (xm1 < xm0) { float xmt = xm0; xm0 = xm1, xm1 = xmt; }
   1540 	float xe0 = xm0 + x0 - x2, xe1 = xm1 + x0 - x2;
   1541 	int ym = y1 - y0, ye = y2 - y1;
   1542 	float ymr = 1.0f / ym, yer = 1.0f / ye;
   1543 
   1544 	// Step 4: Draw the top part.
   1545 	for (float y = 0; y < ym; y++) {
   1546 		int xf = xm0 * y * ymr, xt = xm1 * y * ymr, yi = (int) y;
   1547 		_UI_DRAW_TRIANGLE_APPLY_CLIP(x0, y0);
   1548 		uint32_t *b = &painter->bits[(yi + y0) * painter->width + x0];
   1549 		for (int x = xf; x < xt; x++) b[x] = color;
   1550 	}
   1551 
   1552 	// Step 5: Draw the bottom part.
   1553 	for (float y = 0; y < ye; y++) {
   1554 		int xf = xe0 * (ye - y) * yer, xt = xe1 * (ye - y) * yer, yi = (int) y;
   1555 		_UI_DRAW_TRIANGLE_APPLY_CLIP(x2, y1);
   1556 		uint32_t *b = &painter->bits[(yi + y1) * painter->width + x2];
   1557 		for (int x = xf; x < xt; x++) b[x] = color;
   1558 	}
   1559 }
   1560 
   1561 void UIDrawTriangleOutline(UIPainter *painter, int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color) {
   1562 	UIDrawLine(painter, x0, y0, x1, y1, color);
   1563 	UIDrawLine(painter, x1, y1, x2, y2, color);
   1564 	UIDrawLine(painter, x2, y2, x0, y0, color);
   1565 }
   1566 
   1567 void UIDrawInvert(UIPainter *painter, UIRectangle rectangle) {
   1568 	rectangle = UIRectangleIntersection(painter->clip, rectangle);
   1569 
   1570 	if (!UI_RECT_VALID(rectangle)) {
   1571 		return;
   1572 	}
   1573 
   1574 	for (int line = rectangle.t; line < rectangle.b; line++) {
   1575 		uint32_t *bits = painter->bits + line * painter->width + rectangle.l;
   1576 		int count = UI_RECT_WIDTH(rectangle);
   1577 
   1578 		while (count--) {
   1579 			uint32_t in = *bits;
   1580 			*bits = in ^ 0xFFFFFF;
   1581 			bits++;
   1582 		}
   1583 	}
   1584 }
   1585 
   1586 int UIMeasureStringWidth(const char *string, ptrdiff_t bytes) {
   1587 #ifdef UI_UNICODE
   1588 	return Utf8StringLength(string, bytes) * ui.activeFont->glyphWidth;
   1589 #else
   1590 	if (bytes == -1) {
   1591 		bytes = _UIStringLength(string);
   1592 	}
   1593 
   1594 	return bytes * ui.activeFont->glyphWidth;
   1595 #endif
   1596 }
   1597 
   1598 int UIMeasureStringHeight() {
   1599 	return ui.activeFont->glyphHeight;
   1600 }
   1601 
   1602 void UIDrawString(UIPainter *painter, UIRectangle r, const char *string, ptrdiff_t bytes, uint32_t color, int align, UIStringSelection *selection) {
   1603 	UIRectangle oldClip = painter->clip;
   1604 	painter->clip = UIRectangleIntersection(r, oldClip);
   1605 
   1606 	if (!UI_RECT_VALID(painter->clip)) {
   1607 		painter->clip = oldClip;
   1608 		return;
   1609 	}
   1610 
   1611 	if (bytes == -1) {
   1612 		bytes = _UIStringLength(string);
   1613 	}
   1614 
   1615 	int width = UIMeasureStringWidth(string, bytes);
   1616 	int height = UIMeasureStringHeight();
   1617 	int x = align == UI_ALIGN_CENTER ? ((r.l + r.r - width) / 2) : align == UI_ALIGN_RIGHT ? (r.r - width) : r.l;
   1618 	int y = (r.t + r.b - height) / 2;
   1619 	int i = 0, j = 0;
   1620 
   1621 	int selectFrom = -1, selectTo = -1;
   1622 
   1623 	if (selection) {
   1624 		selectFrom = selection->carets[0];
   1625 		selectTo = selection->carets[1];
   1626 
   1627 		if (selectFrom > selectTo) {
   1628 			UI_SWAP(int, selectFrom, selectTo);
   1629 		}
   1630 	}
   1631 
   1632 	while (j < bytes) {
   1633 		ptrdiff_t bytesConsumed = 1;
   1634 #ifdef UI_UNICODE
   1635 		int c = Utf8GetCodePoint(string, bytes - j, &bytesConsumed);
   1636 		UI_ASSERT(bytesConsumed > 0);
   1637 		string += bytesConsumed;
   1638 #else
   1639 		char c = *string++;
   1640 #endif
   1641 		uint32_t colorText = color;
   1642 
   1643 		if (i >= selectFrom && i < selectTo) {
   1644 			int w = ui.activeFont->glyphWidth;
   1645 			if (c == '\t') {
   1646 				int ii = i;
   1647 				while (++ii & 3) w += ui.activeFont->glyphWidth;
   1648 			}
   1649 			UIDrawBlock(painter, UI_RECT_4(x, x + w, y, y + height), selection->colorBackground);
   1650 			colorText = selection->colorText;
   1651 		}
   1652 
   1653 		if (c != '\t') {
   1654 			UIDrawGlyph(painter, x, y, c, colorText);
   1655 		}
   1656 
   1657 		if (selection && selection->carets[0] == i) {
   1658 			UIDrawInvert(painter, UI_RECT_4(x, x + 1, y, y + height));
   1659 		}
   1660 
   1661 		x += ui.activeFont->glyphWidth, i++;
   1662 
   1663 		if (c == '\t') {
   1664 			while (i & 3) x += ui.activeFont->glyphWidth, i++;
   1665 		}
   1666 
   1667 		j += bytesConsumed;
   1668 	}
   1669 
   1670 	if (selection && selection->carets[0] == i) {
   1671 		UIDrawInvert(painter, UI_RECT_4(x, x + 1, y, y + height));
   1672 	}
   1673 
   1674 	painter->clip = oldClip;
   1675 }
   1676 
   1677 void UIDrawBorder(UIPainter *painter, UIRectangle r, uint32_t borderColor, UIRectangle borderSize) {
   1678 	UIDrawBlock(painter, UI_RECT_4(r.l, r.r, r.t, r.t + borderSize.t), borderColor);
   1679 	UIDrawBlock(painter, UI_RECT_4(r.l, r.l + borderSize.l, r.t + borderSize.t, r.b - borderSize.b), borderColor);
   1680 	UIDrawBlock(painter, UI_RECT_4(r.r - borderSize.r, r.r, r.t + borderSize.t, r.b - borderSize.b), borderColor);
   1681 	UIDrawBlock(painter, UI_RECT_4(r.l, r.r, r.b - borderSize.b, r.b), borderColor);
   1682 }
   1683 
   1684 void UIDrawRectangle(UIPainter *painter, UIRectangle r, uint32_t mainColor, uint32_t borderColor, UIRectangle borderSize) {
   1685 	UIDrawBorder(painter, r, borderColor, borderSize);
   1686 	UIDrawBlock(painter, UI_RECT_4(r.l + borderSize.l, r.r - borderSize.r, r.t + borderSize.t, r.b - borderSize.b), mainColor);
   1687 }
   1688 
   1689 void UIDrawControlDefault(UIPainter *painter, UIRectangle bounds, uint32_t mode, const char *label, ptrdiff_t labelBytes, double position, float scale) {
   1690 	bool checked       = mode & UI_DRAW_CONTROL_STATE_CHECKED;
   1691 	bool disabled      = mode & UI_DRAW_CONTROL_STATE_DISABLED;
   1692 	bool focused       = mode & UI_DRAW_CONTROL_STATE_FOCUSED;
   1693 	bool hovered       = mode & UI_DRAW_CONTROL_STATE_HOVERED;
   1694 	bool indeterminate = mode & UI_DRAW_CONTROL_STATE_INDETERMINATE;
   1695 	bool pressed       = mode & UI_DRAW_CONTROL_STATE_PRESSED;
   1696 	bool selected      = mode & UI_DRAW_CONTROL_STATE_SELECTED;
   1697 	uint32_t which     = mode & UI_DRAW_CONTROL_TYPE_MASK;
   1698 
   1699 	uint32_t buttonColor = disabled ? ui.theme.buttonDisabled
   1700 		: (pressed && hovered) ? ui.theme.buttonPressed
   1701 		: (pressed || hovered) ? ui.theme.buttonHovered
   1702 		: focused ? ui.theme.selected : ui.theme.buttonNormal;
   1703 	uint32_t buttonTextColor = disabled ? ui.theme.textDisabled
   1704 		: buttonColor == ui.theme.selected ? ui.theme.textSelected : ui.theme.text;
   1705 
   1706 	if (which == UI_DRAW_CONTROL_CHECKBOX) {
   1707 		uint32_t color = buttonColor, textColor = buttonTextColor;
   1708 		int midY = (bounds.t + bounds.b) / 2;
   1709 		UIRectangle boxBounds = UI_RECT_4(bounds.l, bounds.l + UI_SIZE_CHECKBOX_BOX,
   1710 				midY - UI_SIZE_CHECKBOX_BOX / 2, midY + UI_SIZE_CHECKBOX_BOX / 2);
   1711 		UIDrawRectangle(painter, boxBounds, color, ui.theme.border, UI_RECT_1(1));
   1712 		UIDrawString(painter, UIRectangleAdd(boxBounds, UI_RECT_4(1, 0, 0, 0)),
   1713 				checked ? "*" : indeterminate ? "-" : " ", -1,
   1714 				textColor, UI_ALIGN_CENTER, NULL);
   1715 		UIDrawString(painter, UIRectangleAdd(bounds, UI_RECT_4(UI_SIZE_CHECKBOX_BOX + UI_SIZE_CHECKBOX_GAP, 0, 0, 0)),
   1716 				label, labelBytes, disabled ? ui.theme.textDisabled : ui.theme.text, UI_ALIGN_LEFT, NULL);
   1717 	} else if (which == UI_DRAW_CONTROL_MENU_ITEM || which == UI_DRAW_CONTROL_DROP_DOWN || which == UI_DRAW_CONTROL_PUSH_BUTTON) {
   1718 		uint32_t color = buttonColor, textColor = buttonTextColor;
   1719 		int borderSize = which == UI_DRAW_CONTROL_MENU_ITEM ? 0 : scale;
   1720 		UIDrawRectangle(painter, bounds, color, ui.theme.border, UI_RECT_1(borderSize));
   1721 
   1722 		if (checked && !focused) {
   1723 			UIDrawBlock(painter, UIRectangleAdd(bounds, UI_RECT_1I((int) (UI_SIZE_BUTTON_CHECKED_AREA * scale))), ui.theme.buttonPressed);
   1724 		}
   1725 
   1726 		UIRectangle innerBounds = UIRectangleAdd(bounds, UI_RECT_2I((int) (UI_SIZE_MENU_ITEM_MARGIN * scale), 0));
   1727 
   1728 		if (which == UI_DRAW_CONTROL_MENU_ITEM) {
   1729 			if (labelBytes == -1) {
   1730 				labelBytes = _UIStringLength(label);
   1731 			}
   1732 
   1733 			int tab = 0;
   1734 			for (; tab < labelBytes && label[tab] != '\t'; tab++);
   1735 
   1736 			UIDrawString(painter, innerBounds, label, tab, textColor, UI_ALIGN_LEFT, NULL);
   1737 
   1738 			if (labelBytes > tab) {
   1739 				UIDrawString(painter, innerBounds, label + tab + 1, labelBytes - tab - 1, textColor, UI_ALIGN_RIGHT, NULL);
   1740 			}
   1741 		} else if (which == UI_DRAW_CONTROL_DROP_DOWN) {
   1742 			UIDrawString(painter, innerBounds, label, labelBytes, textColor, UI_ALIGN_LEFT, NULL);
   1743 			UIDrawString(painter, innerBounds, "\x19", 1, textColor, UI_ALIGN_RIGHT, NULL);
   1744 		} else {
   1745 			UIDrawString(painter, bounds, label, labelBytes, textColor, UI_ALIGN_CENTER, NULL);
   1746 		}
   1747 	} else if (which == UI_DRAW_CONTROL_LABEL) {
   1748 		UIDrawString(painter, bounds, label, labelBytes, ui.theme.text, UI_ALIGN_LEFT, NULL);
   1749 	} else if (which == UI_DRAW_CONTROL_SPLITTER) {
   1750 		UIRectangle borders = (mode & UI_DRAW_CONTROL_STATE_VERTICAL) ? UI_RECT_2(0, 1) : UI_RECT_2(1, 0);
   1751 		UIDrawRectangle(painter, bounds, ui.theme.buttonNormal, ui.theme.border, borders);
   1752 	} else if (which == UI_DRAW_CONTROL_SCROLL_TRACK) {
   1753 		if (disabled) UIDrawBlock(painter, bounds, ui.theme.panel1);
   1754 	} else if (which == UI_DRAW_CONTROL_SCROLL_DOWN || which == UI_DRAW_CONTROL_SCROLL_UP) {
   1755 		bool isDown = which == UI_DRAW_CONTROL_SCROLL_DOWN;
   1756 		uint32_t color = pressed ? ui.theme.buttonPressed : hovered ? ui.theme.buttonHovered : ui.theme.panel2;
   1757 		UIDrawRectangle(painter, bounds, color, ui.theme.border, UI_RECT_1(0));
   1758 
   1759 		if (mode & UI_DRAW_CONTROL_STATE_VERTICAL) {
   1760 			UIDrawGlyph(painter, (bounds.l + bounds.r - ui.activeFont->glyphWidth) / 2 + 1,
   1761 					isDown ? (bounds.b - ui.activeFont->glyphHeight - 2 * scale)
   1762 					: (bounds.t + 2 * scale),
   1763 					isDown ? 25 : 24, ui.theme.text);
   1764 		} else {
   1765 			UIDrawGlyph(painter, isDown ? (bounds.r - ui.activeFont->glyphWidth - 2 * scale)
   1766 					: (bounds.l + 2 * scale),
   1767 					(bounds.t + bounds.b - ui.activeFont->glyphHeight) / 2,
   1768 					isDown ? 26 : 27, ui.theme.text);
   1769 		}
   1770 	} else if (which == UI_DRAW_CONTROL_SCROLL_THUMB) {
   1771 		uint32_t color = pressed ? ui.theme.buttonPressed : hovered ? ui.theme.buttonHovered : ui.theme.buttonNormal;
   1772 		UIDrawRectangle(painter, bounds, color, ui.theme.border, UI_RECT_1(2));
   1773 	} else if (which == UI_DRAW_CONTROL_GAUGE) {
   1774 		UIDrawRectangle(painter, bounds, ui.theme.buttonNormal, ui.theme.border, UI_RECT_1(1));
   1775 		UIRectangle filled = UIRectangleAdd(bounds, UI_RECT_1I(1));
   1776 		filled.r = filled.l + UI_RECT_WIDTH(filled) * position;
   1777 		UIDrawBlock(painter, filled, ui.theme.selected);
   1778 	} else if (which == UI_DRAW_CONTROL_SLIDER) {
   1779 		int centerY = (bounds.t + bounds.b) / 2;
   1780 		int trackSize = UI_SIZE_SLIDER_TRACK * scale;
   1781 		int thumbSize = UI_SIZE_SLIDER_THUMB * scale;
   1782 		int thumbPosition = (UI_RECT_WIDTH(bounds) - thumbSize) * position;
   1783 		UIRectangle track = UI_RECT_4(bounds.l, bounds.r, centerY - (trackSize + 1) / 2, centerY + trackSize / 2);
   1784 		UIDrawRectangle(painter, track, disabled ? ui.theme.buttonDisabled : ui.theme.buttonNormal, ui.theme.border, UI_RECT_1(1));
   1785 		uint32_t color = disabled ? ui.theme.buttonDisabled : pressed ? ui.theme.buttonPressed : hovered ? ui.theme.buttonHovered : ui.theme.buttonNormal;
   1786 		UIRectangle thumb = UI_RECT_4(bounds.l + thumbPosition, bounds.l + thumbPosition + thumbSize, centerY - (thumbSize + 1) / 2, centerY + thumbSize / 2);
   1787 		UIDrawRectangle(painter, thumb, color, ui.theme.border, UI_RECT_1(1));
   1788 	} else if (which == UI_DRAW_CONTROL_TEXTBOX) {
   1789 		UIDrawRectangle(painter, bounds,
   1790 				disabled ? ui.theme.buttonDisabled : focused ? ui.theme.textboxFocused : ui.theme.textboxNormal,
   1791 				ui.theme.border, UI_RECT_1(1));
   1792 	} else if (which == UI_DRAW_CONTROL_MODAL_POPUP) {
   1793 		UIRectangle bounds2 = UIRectangleAdd(bounds, UI_RECT_1I(-1));
   1794 		UIDrawBorder(painter, bounds2, ui.theme.border, UI_RECT_1(1));
   1795 		UIDrawBorder(painter, UIRectangleAdd(bounds2, UI_RECT_1(1)), ui.theme.border, UI_RECT_1(1));
   1796 	} else if (which == UI_DRAW_CONTROL_MENU) {
   1797 		UIDrawBlock(painter, bounds, ui.theme.border);
   1798 	} else if (which == UI_DRAW_CONTROL_TABLE_ROW) {
   1799 		if (selected) UIDrawBlock(painter, bounds, ui.theme.selected);
   1800 		else if (hovered) UIDrawBlock(painter, bounds, ui.theme.buttonHovered);
   1801 	} else if (which == UI_DRAW_CONTROL_TABLE_CELL) {
   1802 		uint32_t textColor = selected ? ui.theme.textSelected : ui.theme.text;
   1803 		UIDrawString(painter, bounds, label, labelBytes, textColor, UI_ALIGN_LEFT, NULL);
   1804 	} else if (which == UI_DRAW_CONTROL_TABLE_BACKGROUND) {
   1805 		UIDrawBlock(painter, bounds, ui.theme.panel2);
   1806 		UIDrawRectangle(painter, UI_RECT_4(bounds.l, bounds.r, bounds.t, bounds.t + (int) (UI_SIZE_TABLE_HEADER * scale)),
   1807 				ui.theme.panel1, ui.theme.border, UI_RECT_4(0, 0, 0, 1));
   1808 	} else if (which == UI_DRAW_CONTROL_TABLE_HEADER) {
   1809 		UIDrawString(painter, bounds, label, labelBytes, ui.theme.text, UI_ALIGN_LEFT, NULL);
   1810 		if (selected) UIDrawInvert(painter, bounds);
   1811 	} else if (which == UI_DRAW_CONTROL_MDI_CHILD) {
   1812 		UI_MDI_CHILD_CALCULATE_LAYOUT(bounds, scale);
   1813 		UIRectangle borders = UI_RECT_4(borderSize, borderSize, titleSize, borderSize);
   1814 		UIDrawBorder(painter, bounds, ui.theme.buttonNormal, borders);
   1815 		UIDrawBorder(painter, bounds, ui.theme.border, UI_RECT_1((int) scale));
   1816 		UIDrawBorder(painter, UIRectangleAdd(content, UI_RECT_1I(-1)), ui.theme.border, UI_RECT_1((int) scale));
   1817 		UIDrawString(painter, title, label, labelBytes, ui.theme.text, UI_ALIGN_LEFT, NULL);
   1818 	} else if (which == UI_DRAW_CONTROL_TAB) {
   1819 		uint32_t color = selected ? ui.theme.buttonPressed : ui.theme.buttonNormal;
   1820 		UIRectangle t = bounds;
   1821 		if (selected) t.b++, t.t--;
   1822 		else t.t++;
   1823 		UIDrawRectangle(painter, t, color, ui.theme.border, UI_RECT_1(1));
   1824 		UIDrawString(painter, bounds, label, labelBytes, ui.theme.text, UI_ALIGN_CENTER, NULL);
   1825 	} else if (which == UI_DRAW_CONTROL_TAB_BAND) {
   1826 		UIDrawRectangle(painter, bounds, ui.theme.panel1, ui.theme.border, UI_RECT_4(0, 0, 0, 1));
   1827 	}
   1828 }
   1829 
   1830 /////////////////////////////////////////
   1831 // Element hierarchy.
   1832 /////////////////////////////////////////
   1833 
   1834 void _UIElementDestroyDescendents(UIElement *element, bool topLevel) {
   1835 	for (uint32_t i = 0; i < element->childCount; i++) {
   1836 		UIElement *child = element->children[i];
   1837 
   1838 		if (!topLevel || (~child->flags & UI_ELEMENT_NON_CLIENT)) {
   1839 			UIElementDestroy(child);
   1840 		}
   1841 	}
   1842 
   1843 #ifdef UI_DEBUG
   1844 	_UIInspectorRefresh();
   1845 #endif
   1846 }
   1847 
   1848 void UIElementDestroyDescendents(UIElement *element) {
   1849 	_UIElementDestroyDescendents(element, true);
   1850 }
   1851 
   1852 void UIElementDestroy(UIElement *element) {
   1853 	if (element->flags & UI_ELEMENT_DESTROY) {
   1854 		return;
   1855 	}
   1856 
   1857 	UIElementMessage(element, UI_MSG_DESTROY, 0, 0);
   1858 	element->flags |= UI_ELEMENT_DESTROY | UI_ELEMENT_HIDE;
   1859 
   1860 	UIElement *ancestor = element->parent;
   1861 
   1862 	while (ancestor) {
   1863 		if (ancestor->flags & UI_ELEMENT_DESTROY_DESCENDENT) break;
   1864 		ancestor->flags |= UI_ELEMENT_DESTROY_DESCENDENT;
   1865 		ancestor = ancestor->parent;
   1866 	}
   1867 
   1868 	_UIElementDestroyDescendents(element, false);
   1869 
   1870 	if (element->parent) {
   1871 		UIElementRelayout(element->parent);
   1872 		UIElementRepaint(element->parent, &element->bounds);
   1873 		UIElementMeasurementsChanged(element->parent, 3);
   1874 	}
   1875 }
   1876 
   1877 int UIElementMessage(UIElement *element, UIMessage message, int di, void *dp) {
   1878 	if (message != UI_MSG_DEALLOCATE && (element->flags & UI_ELEMENT_DESTROY)) {
   1879 		return 0;
   1880 	}
   1881 
   1882 	if (message >= UI_MSG_INPUT_EVENTS_START && message <= UI_MSG_INPUT_EVENTS_END && (element->flags & UI_ELEMENT_DISABLED)) {
   1883 		return 0;
   1884 	}
   1885 
   1886 	if (element->messageUser) {
   1887 		int result = element->messageUser(element, message, di, dp);
   1888 
   1889 		if (result) {
   1890 			return result;
   1891 		}
   1892 	}
   1893 
   1894 	if (element->messageClass) {
   1895 		return element->messageClass(element, message, di, dp);
   1896 	} else {
   1897 		return 0;
   1898 	}
   1899 }
   1900 
   1901 UIElement *UIElementChangeParent(UIElement *element, UIElement *newParent, UIElement *insertBefore) {
   1902 	bool found = false;
   1903 	UIElement *oldBefore = NULL;
   1904 
   1905 	for (uint32_t i = 0; i < element->parent->childCount; i++) {
   1906 		if (element->parent->children[i] == element) {
   1907 			UI_MEMMOVE(&element->parent->children[i], &element->parent->children[i + 1], sizeof(UIElement *) * (element->parent->childCount - i - 1));
   1908 			element->parent->childCount--;
   1909 			oldBefore = i == element->parent->childCount ? NULL : element->parent->children[i];
   1910 			found = true;
   1911 			break;
   1912 		}
   1913 	}
   1914 
   1915 	UI_ASSERT(found && (~element->flags & UI_ELEMENT_DESTROY));
   1916 
   1917 	for (uint32_t i = 0; i <= newParent->childCount; i++) {
   1918 		if (i == newParent->childCount || newParent->children[i] == insertBefore) {
   1919 			newParent->children = (UIElement **) UI_REALLOC(newParent->children, sizeof(UIElement *) * (newParent->childCount + 1));
   1920 			UI_MEMMOVE(&newParent->children[i + 1], &newParent->children[i], sizeof(UIElement *) * (newParent->childCount - i));
   1921 			newParent->childCount++;
   1922 			newParent->children[i] = element;
   1923 			found = true;
   1924 			break;
   1925 		}
   1926 	}
   1927 
   1928 	UIElement *oldParent = element->parent;
   1929 	element->parent = newParent;
   1930 	element->window = newParent->window;
   1931 
   1932 	UIElementMeasurementsChanged(oldParent, 3);
   1933 	UIElementMeasurementsChanged(newParent, 3);
   1934 
   1935 	return oldBefore;
   1936 }
   1937 
   1938 UIElement *UIElementCreate(size_t bytes, UIElement *parent, uint32_t flags, int (*message)(UIElement *, UIMessage, int, void *), const char *cClassName) {
   1939 	UI_ASSERT(bytes >= sizeof(UIElement));
   1940 	UIElement *element = (UIElement *) UI_CALLOC(bytes);
   1941 	element->flags = flags;
   1942 	element->messageClass = message;
   1943 
   1944 	if (!parent && (~flags & UI_ELEMENT_WINDOW)) {
   1945 		UI_ASSERT(ui.parentStackCount);
   1946 		parent = ui.parentStack[ui.parentStackCount - 1];
   1947 	}
   1948 
   1949 	if (parent) {
   1950 		UI_ASSERT(~parent->flags & UI_ELEMENT_DESTROY);
   1951 		element->window = parent->window;
   1952 		element->parent = parent;
   1953 		parent->children = (UIElement **) UI_REALLOC(parent->children, sizeof(UIElement *) * (parent->childCount + 1));
   1954 		parent->children[parent->childCount] = element;
   1955 		parent->childCount++;
   1956 		UIElementRelayout(parent);
   1957 		UIElementMeasurementsChanged(parent, 3);
   1958 	}
   1959 
   1960 	element->cClassName = cClassName;
   1961 	static uint32_t id = 0;
   1962 	element->id = ++id;
   1963 
   1964 #ifdef UI_DEBUG
   1965 	_UIInspectorRefresh();
   1966 #endif
   1967 
   1968 	if (flags & UI_ELEMENT_PARENT_PUSH) {
   1969 		UIParentPush(element);
   1970 	}
   1971 
   1972 	return element;
   1973 }
   1974 
   1975 UIElement *UIParentPush(UIElement *element) {
   1976 	UI_ASSERT(ui.parentStackCount != sizeof(ui.parentStack) / sizeof(ui.parentStack[0]));
   1977 	ui.parentStack[ui.parentStackCount++] = element;
   1978 	return element;
   1979 }
   1980 
   1981 UIElement *UIParentPop() {
   1982 	UI_ASSERT(ui.parentStackCount);
   1983 	ui.parentStackCount--;
   1984 	return ui.parentStack[ui.parentStackCount];
   1985 }
   1986 
   1987 /////////////////////////////////////////
   1988 // Panels.
   1989 /////////////////////////////////////////
   1990 
   1991 int _UIPanelCalculatePerFill(UIPanel *panel, int *_count, int hSpace, int vSpace, float scale) {
   1992 	bool horizontal = panel->e.flags & UI_PANEL_HORIZONTAL;
   1993 	int available = horizontal ? hSpace : vSpace;
   1994 	int count = 0, fill = 0, perFill = 0;
   1995 
   1996 	for (uint32_t i = 0; i < panel->e.childCount; i++) {
   1997 		UIElement *child = panel->e.children[i];
   1998 
   1999 		if (child->flags & (UI_ELEMENT_HIDE | UI_ELEMENT_NON_CLIENT)) {
   2000 			continue;
   2001 		}
   2002 
   2003 		count++;
   2004 
   2005 		if (horizontal) {
   2006 			if (child->flags & UI_ELEMENT_H_FILL) {
   2007 				fill++;
   2008 			} else if (available > 0) {
   2009 				available -= UIElementMessage(child, UI_MSG_GET_WIDTH, vSpace, 0);
   2010 			}
   2011 		} else {
   2012 			if (child->flags & UI_ELEMENT_V_FILL) {
   2013 				fill++;
   2014 			} else if (available > 0) {
   2015 				available -= UIElementMessage(child, UI_MSG_GET_HEIGHT, hSpace, 0);
   2016 			}
   2017 		}
   2018 	}
   2019 
   2020 	if (count) {
   2021 		available -= (count - 1) * (int) (panel->gap * scale);
   2022 	}
   2023 
   2024 	if (available > 0 && fill) {
   2025 		perFill = available / fill;
   2026 	}
   2027 
   2028 	if (_count) {
   2029 		*_count = count;
   2030 	}
   2031 
   2032 	return perFill;
   2033 }
   2034 
   2035 int _UIPanelMeasure(UIPanel *panel, int di) {
   2036 	bool horizontal = panel->e.flags & UI_PANEL_HORIZONTAL;
   2037 	int perFill = _UIPanelCalculatePerFill(panel, NULL, horizontal ? di : 0, horizontal ? 0 : di, panel->e.window->scale);
   2038 	int size = 0;
   2039 
   2040 	for (uint32_t i = 0; i < panel->e.childCount; i++) {
   2041 		UIElement *child = panel->e.children[i];
   2042 		if (child->flags & (UI_ELEMENT_HIDE | UI_ELEMENT_NON_CLIENT)) continue;
   2043 		int childSize = UIElementMessage(child, horizontal ? UI_MSG_GET_HEIGHT : UI_MSG_GET_WIDTH,
   2044 				(child->flags & (horizontal ? UI_ELEMENT_H_FILL : UI_ELEMENT_V_FILL)) ? perFill : 0, 0);
   2045 		if (childSize > size) size = childSize;
   2046 	}
   2047 
   2048 	int border = horizontal ? (panel->border.t + panel->border.b) : (panel->border.l + panel->border.r);
   2049 	return size + border * panel->e.window->scale;
   2050 }
   2051 
   2052 int _UIPanelLayout(UIPanel *panel, UIRectangle bounds, bool measure) {
   2053 	bool horizontal = panel->e.flags & UI_PANEL_HORIZONTAL;
   2054 	float scale = panel->e.window->scale;
   2055 	int position = (horizontal ? panel->border.l : panel->border.t) * scale;
   2056 	if (panel->scrollBar && !measure) position -= panel->scrollBar->position;
   2057 	int hSpace = UI_RECT_WIDTH(bounds) - UI_RECT_TOTAL_H(panel->border) * scale;
   2058 	int vSpace = UI_RECT_HEIGHT(bounds) - UI_RECT_TOTAL_V(panel->border) * scale;
   2059 	int count = 0;
   2060 	int perFill = _UIPanelCalculatePerFill(panel, &count, hSpace, vSpace, scale);
   2061 	int scaledBorder2 = (horizontal ? panel->border.t : panel->border.l) * panel->e.window->scale;
   2062 	bool expand = panel->e.flags & UI_PANEL_EXPAND;
   2063 
   2064 	for (uint32_t i = 0; i < panel->e.childCount; i++) {
   2065 		UIElement *child = panel->e.children[i];
   2066 
   2067 		if (child->flags & (UI_ELEMENT_HIDE | UI_ELEMENT_NON_CLIENT)) {
   2068 			continue;
   2069 		}
   2070 
   2071 		if (horizontal) {
   2072 			int height = ((child->flags & UI_ELEMENT_V_FILL) || expand) ? vSpace
   2073 				: UIElementMessage(child, UI_MSG_GET_HEIGHT, (child->flags & UI_ELEMENT_H_FILL) ? perFill : 0, 0);
   2074 			int width = (child->flags & UI_ELEMENT_H_FILL) ? perFill : UIElementMessage(child, UI_MSG_GET_WIDTH, height, 0);
   2075 			UIRectangle relative = UI_RECT_4(position, position + width,
   2076 					scaledBorder2 + (vSpace - height) / 2,
   2077 					scaledBorder2 + (vSpace + height) / 2);
   2078 			if (!measure) UIElementMove(child, UIRectangleTranslate(relative, bounds), false);
   2079 			position += width + panel->gap * scale;
   2080 		} else {
   2081 			int width = ((child->flags & UI_ELEMENT_H_FILL) || expand) ? hSpace
   2082 				: UIElementMessage(child, UI_MSG_GET_WIDTH, (child->flags & UI_ELEMENT_V_FILL) ? perFill : 0, 0);
   2083 			int height = (child->flags & UI_ELEMENT_V_FILL) ? perFill : UIElementMessage(child, UI_MSG_GET_HEIGHT, width, 0);
   2084 			UIRectangle relative = UI_RECT_4(scaledBorder2 + (hSpace - width) / 2,
   2085 					scaledBorder2 + (hSpace + width) / 2, position, position + height);
   2086 			if (!measure) UIElementMove(child, UIRectangleTranslate(relative, bounds), false);
   2087 			position += height + panel->gap * scale;
   2088 		}
   2089 	}
   2090 
   2091 	return position - (count ? panel->gap : 0) * scale + (horizontal ? panel->border.r : panel->border.b) * scale;
   2092 }
   2093 
   2094 int _UIPanelMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2095 	UIPanel *panel = (UIPanel *) element;
   2096 	bool horizontal = element->flags & UI_PANEL_HORIZONTAL;
   2097 
   2098 	if (message == UI_MSG_LAYOUT) {
   2099 		int scrollBarWidth = panel->scrollBar ? (UI_SIZE_SCROLL_BAR * element->window->scale) : 0;
   2100 		UIRectangle bounds = element->bounds;
   2101 		bounds.r -= scrollBarWidth;
   2102 
   2103 		if (panel->scrollBar) {
   2104 			UIRectangle scrollBarBounds = element->bounds;
   2105 			scrollBarBounds.l = scrollBarBounds.r - scrollBarWidth;
   2106 			panel->scrollBar->maximum = _UIPanelLayout(panel, bounds, true);
   2107 			panel->scrollBar->page = UI_RECT_HEIGHT(element->bounds);
   2108 			UIElementMove(&panel->scrollBar->e, scrollBarBounds, true);
   2109 		}
   2110 
   2111 		_UIPanelLayout(panel, bounds, false);
   2112 	} else if (message == UI_MSG_GET_WIDTH) {
   2113 		if (horizontal) {
   2114 			return _UIPanelLayout(panel, UI_RECT_4(0, 0, 0, di), true);
   2115 		} else {
   2116 			return _UIPanelMeasure(panel, di);
   2117 		}
   2118 	} else if (message == UI_MSG_GET_HEIGHT) {
   2119 		if (horizontal) {
   2120 			return _UIPanelMeasure(panel, di);
   2121 		} else {
   2122 			int width = di && panel->scrollBar ? (di - UI_SIZE_SCROLL_BAR * element->window->scale) : di;
   2123 			return _UIPanelLayout(panel, UI_RECT_4(0, width, 0, 0), true);
   2124 		}
   2125 	} else if (message == UI_MSG_PAINT) {
   2126 		if (element->flags & UI_PANEL_COLOR_1) {
   2127 			UIDrawBlock((UIPainter *) dp, element->bounds, ui.theme.panel1);
   2128 		} else if (element->flags & UI_PANEL_COLOR_2) {
   2129 			UIDrawBlock((UIPainter *) dp, element->bounds, ui.theme.panel2);
   2130 		}
   2131 	} else if (message == UI_MSG_MOUSE_WHEEL && panel->scrollBar) {
   2132 		return UIElementMessage(&panel->scrollBar->e, message, di, dp);
   2133 	} else if (message == UI_MSG_SCROLLED) {
   2134 		UIElementRefresh(element);
   2135 	} else if (message == UI_MSG_GET_CHILD_STABILITY) {
   2136 		UIElement *child = (UIElement *) dp;
   2137 		return ((element->flags & UI_PANEL_EXPAND) ? (horizontal ? 2 : 1) : 0)
   2138 			| ((child->flags & UI_ELEMENT_H_FILL) ? 1 : 0) | ((child->flags & UI_ELEMENT_V_FILL) ? 2 : 0);
   2139 	}
   2140 
   2141 	return 0;
   2142 }
   2143 
   2144 UIPanel *UIPanelCreate(UIElement *parent, uint32_t flags) {
   2145 	UIPanel *panel = (UIPanel *) UIElementCreate(sizeof(UIPanel), parent, flags, _UIPanelMessage, "Panel");
   2146 
   2147 	if (flags & UI_PANEL_LARGE_SPACING) {
   2148 		panel->border = UI_RECT_1(UI_SIZE_PANE_LARGE_BORDER);
   2149 		panel->gap = UI_SIZE_PANE_LARGE_GAP;
   2150 	} else if (flags & UI_PANEL_MEDIUM_SPACING) {
   2151 		panel->border = UI_RECT_1(UI_SIZE_PANE_MEDIUM_BORDER);
   2152 		panel->gap = UI_SIZE_PANE_MEDIUM_GAP;
   2153 	} else if (flags & UI_PANEL_SMALL_SPACING) {
   2154 		panel->border = UI_RECT_1(UI_SIZE_PANE_SMALL_BORDER);
   2155 		panel->gap = UI_SIZE_PANE_SMALL_GAP;
   2156 	}
   2157 
   2158 	if (flags & UI_PANEL_SCROLL) {
   2159 		panel->scrollBar = UIScrollBarCreate(&panel->e, UI_ELEMENT_NON_CLIENT);
   2160 	}
   2161 
   2162 	return panel;
   2163 }
   2164 
   2165 void _UIWrapPanelLayoutRow(UIWrapPanel *panel, uint32_t rowStart, uint32_t rowEnd, int rowY, int rowHeight) {
   2166 	int rowPosition = 0;
   2167 
   2168 	for (uint32_t i = rowStart; i < rowEnd; i++) {
   2169 		UIElement *child = panel->e.children[i];
   2170 		if (child->flags & UI_ELEMENT_HIDE) continue;
   2171 		int height = UIElementMessage(child, UI_MSG_GET_HEIGHT, 0, 0);
   2172 		int width = UIElementMessage(child, UI_MSG_GET_WIDTH, 0, 0);
   2173 		UIRectangle relative = UI_RECT_4(rowPosition, rowPosition + width, rowY + rowHeight / 2 - height / 2, rowY + rowHeight / 2 + height / 2);
   2174 		UIElementMove(child, UIRectangleTranslate(relative, panel->e.bounds), false);
   2175 		rowPosition += width;
   2176 	}
   2177 }
   2178 
   2179 int _UIWrapPanelMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2180 	UIWrapPanel *panel = (UIWrapPanel *) element;
   2181 
   2182 	if (message == UI_MSG_LAYOUT || message == UI_MSG_GET_HEIGHT) {
   2183 		int totalHeight = 0;
   2184 		int rowPosition = 0;
   2185 		int rowHeight = 0;
   2186 		int rowLimit = message == UI_MSG_LAYOUT ? UI_RECT_WIDTH(element->bounds) : di;
   2187 
   2188 		uint32_t rowStart = 0;
   2189 
   2190 		for (uint32_t i = 0; i < panel->e.childCount; i++) {
   2191 			UIElement *child = panel->e.children[i];
   2192 			if (child->flags & UI_ELEMENT_HIDE) continue;
   2193 
   2194 			int height = UIElementMessage(child, UI_MSG_GET_HEIGHT, 0, 0);
   2195 			int width = UIElementMessage(child, UI_MSG_GET_WIDTH, 0, 0);
   2196 
   2197 			if (rowLimit && rowPosition + width > rowLimit) {
   2198 				_UIWrapPanelLayoutRow(panel, rowStart, i, totalHeight, rowHeight);
   2199 				totalHeight += rowHeight;
   2200 				rowPosition = rowHeight = 0;
   2201 				rowStart = i;
   2202 			}
   2203 
   2204 			if (height > rowHeight) {
   2205 				rowHeight = height;
   2206 			}
   2207 
   2208 			rowPosition += width;
   2209 		}
   2210 
   2211 		if (message == UI_MSG_GET_HEIGHT) {
   2212 			return totalHeight + rowHeight;
   2213 		} else {
   2214 			_UIWrapPanelLayoutRow(panel, rowStart, panel->e.childCount, totalHeight, rowHeight);
   2215 		}
   2216 	}
   2217 
   2218 	return 0;
   2219 }
   2220 
   2221 UIWrapPanel *UIWrapPanelCreate(UIElement *parent, uint32_t flags) {
   2222 	return (UIWrapPanel *) UIElementCreate(sizeof(UIWrapPanel), parent, flags, _UIWrapPanelMessage, "Wrap Panel");
   2223 }
   2224 
   2225 int _UISwitcherMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2226 	UISwitcher *switcher = (UISwitcher *) element;
   2227 
   2228 	if (!switcher->active) {
   2229 	} else if (message == UI_MSG_GET_WIDTH || message == UI_MSG_GET_HEIGHT) {
   2230 		return UIElementMessage(switcher->active, message, di, dp);
   2231 	} else if (message == UI_MSG_LAYOUT) {
   2232 		UIElementMove(switcher->active, element->bounds, false);
   2233 	}
   2234 
   2235 	return 0;
   2236 }
   2237 
   2238 void UISwitcherSwitchTo(UISwitcher *switcher, UIElement *child) {
   2239 	for (uint32_t i = 0; i < switcher->e.childCount; i++) {
   2240 		switcher->e.children[i]->flags |= UI_ELEMENT_HIDE;
   2241 	}
   2242 
   2243 	UI_ASSERT(child->parent == &switcher->e);
   2244 	child->flags &= ~UI_ELEMENT_HIDE;
   2245 	switcher->active = child;
   2246 	UIElementMeasurementsChanged(&switcher->e, 3);
   2247 	UIElementRefresh(&switcher->e);
   2248 }
   2249 
   2250 UISwitcher *UISwitcherCreate(UIElement *parent, uint32_t flags) {
   2251 	return (UISwitcher *) UIElementCreate(sizeof(UISwitcher), parent, flags, _UISwitcherMessage, "Switcher");
   2252 }
   2253 
   2254 /////////////////////////////////////////
   2255 // Checkboxes and buttons.
   2256 /////////////////////////////////////////
   2257 
   2258 int _UIButtonMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2259 	UIButton *button = (UIButton *) element;
   2260 	bool isMenuItem = element->flags & UI_BUTTON_MENU_ITEM;
   2261 	bool isDropDown = element->flags & UI_BUTTON_DROP_DOWN;
   2262 
   2263 	if (message == UI_MSG_GET_HEIGHT) {
   2264 		if (isMenuItem) {
   2265 			return UI_SIZE_MENU_ITEM_HEIGHT * element->window->scale;
   2266 		} else {
   2267 			return UI_SIZE_BUTTON_HEIGHT * element->window->scale;
   2268 		}
   2269 	} else if (message == UI_MSG_GET_WIDTH) {
   2270 		int labelSize = UIMeasureStringWidth(button->label, button->labelBytes);
   2271 		int paddedSize = labelSize + UI_SIZE_BUTTON_PADDING * element->window->scale;
   2272 		if (isDropDown) paddedSize += ui.activeFont->glyphWidth * 2;
   2273 		int minimumSize = ((element->flags & UI_BUTTON_SMALL) ? 0
   2274 				: isMenuItem ? UI_SIZE_MENU_ITEM_MINIMUM_WIDTH
   2275 				: UI_SIZE_BUTTON_MINIMUM_WIDTH)
   2276 			* element->window->scale;
   2277 		return paddedSize > minimumSize ? paddedSize : minimumSize;
   2278 	} else if (message == UI_MSG_PAINT) {
   2279 		UIDrawControl((UIPainter *) dp, element->bounds,
   2280 				(isMenuItem ? UI_DRAW_CONTROL_MENU_ITEM : isDropDown ? UI_DRAW_CONTROL_DROP_DOWN : UI_DRAW_CONTROL_PUSH_BUTTON)
   2281 				| ((element->flags & UI_BUTTON_CHECKED) ? UI_DRAW_CONTROL_STATE_CHECKED : 0) | UI_DRAW_CONTROL_STATE_FROM_ELEMENT(element),
   2282 				button->label, button->labelBytes, 0, element->window->scale);
   2283 	} else if (message == UI_MSG_UPDATE) {
   2284 		UIElementRepaint(element, NULL);
   2285 	} else if (message == UI_MSG_DEALLOCATE) {
   2286 		UI_FREE(button->label);
   2287 	} else if (message == UI_MSG_LEFT_DOWN) {
   2288 		if (element->flags & UI_BUTTON_CAN_FOCUS) {
   2289 			UIElementFocus(element);
   2290 		}
   2291 	} else if (message == UI_MSG_KEY_TYPED) {
   2292 		UIKeyTyped *m = (UIKeyTyped *) dp;
   2293 
   2294 		if ((m->textBytes == 1 && m->text[0] == ' ') || m->code == UI_KEYCODE_ENTER) {
   2295 			UIElementMessage(element, UI_MSG_CLICKED, 0, 0);
   2296 			UIElementRepaint(element, NULL);
   2297 			return 1;
   2298 		}
   2299 	} else if (message == UI_MSG_CLICKED) {
   2300 		if (button->invoke) {
   2301 			button->invoke(element->cp);
   2302 		}
   2303 	}
   2304 
   2305 	return 0;
   2306 }
   2307 
   2308 void UIButtonSetLabel(UIButton *button, const char *string, ptrdiff_t stringBytes) {
   2309 	UI_FREE(button->label);
   2310 	button->label = UIStringCopy(string, (button->labelBytes = stringBytes));
   2311 	UIElementMeasurementsChanged(&button->e, 1);
   2312 	UIElementRepaint(&button->e, NULL);
   2313 }
   2314 
   2315 UIButton *UIButtonCreate(UIElement *parent, uint32_t flags, const char *label, ptrdiff_t labelBytes) {
   2316 	UIButton *button = (UIButton *) UIElementCreate(sizeof(UIButton), parent, flags | UI_ELEMENT_TAB_STOP, _UIButtonMessage, "Button");
   2317 	button->label = UIStringCopy(label, (button->labelBytes = labelBytes));
   2318 	return button;
   2319 }
   2320 
   2321 int _UICheckboxMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2322 	UICheckbox *box = (UICheckbox *) element;
   2323 
   2324 	if (message == UI_MSG_GET_HEIGHT) {
   2325 		return UI_SIZE_BUTTON_HEIGHT * element->window->scale;
   2326 	} else if (message == UI_MSG_GET_WIDTH) {
   2327 		int labelSize = UIMeasureStringWidth(box->label, box->labelBytes);
   2328 		return (labelSize + UI_SIZE_CHECKBOX_BOX + UI_SIZE_CHECKBOX_GAP) * element->window->scale;
   2329 	} else if (message == UI_MSG_PAINT) {
   2330 		UIDrawControl((UIPainter *) dp, element->bounds,
   2331 				UI_DRAW_CONTROL_CHECKBOX | (box->check == UI_CHECK_INDETERMINATE ? UI_DRAW_CONTROL_STATE_INDETERMINATE
   2332 					: box->check == UI_CHECK_CHECKED ? UI_DRAW_CONTROL_STATE_CHECKED : 0)
   2333 				| UI_DRAW_CONTROL_STATE_FROM_ELEMENT(element),
   2334 				box->label, box->labelBytes, 0, element->window->scale);
   2335 	} else if (message == UI_MSG_UPDATE) {
   2336 		UIElementRepaint(element, NULL);
   2337 	} else if (message == UI_MSG_DEALLOCATE) {
   2338 		UI_FREE(box->label);
   2339 	} else if (message == UI_MSG_KEY_TYPED) {
   2340 		UIKeyTyped *m = (UIKeyTyped *) dp;
   2341 
   2342 		if (m->textBytes == 1 && m->text[0] == ' ') {
   2343 			UIElementMessage(element, UI_MSG_CLICKED, 0, 0);
   2344 			UIElementRepaint(element, NULL);
   2345 		}
   2346 	} else if (message == UI_MSG_CLICKED) {
   2347 		box->check = (box->check + 1) % ((element->flags & UI_CHECKBOX_ALLOW_INDETERMINATE) ? 3 : 2);
   2348 		UIElementRepaint(element, NULL);
   2349 		if (box->invoke) box->invoke(element->cp);
   2350 	}
   2351 
   2352 	return 0;
   2353 }
   2354 
   2355 UICheckbox *UICheckboxCreate(UIElement *parent, uint32_t flags, const char *label, ptrdiff_t labelBytes) {
   2356 	UICheckbox *box = (UICheckbox *) UIElementCreate(sizeof(UICheckbox), parent, flags | UI_ELEMENT_TAB_STOP, _UICheckboxMessage, "Checkbox");
   2357 	box->label = UIStringCopy(label, (box->labelBytes = labelBytes));
   2358 	return box;
   2359 }
   2360 
   2361 /////////////////////////////////////////
   2362 // Labels.
   2363 /////////////////////////////////////////
   2364 
   2365 int _UILabelMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2366 	UILabel *label = (UILabel *) element;
   2367 
   2368 	if (message == UI_MSG_GET_HEIGHT) {
   2369 		return UIMeasureStringHeight();
   2370 	} else if (message == UI_MSG_GET_WIDTH) {
   2371 		return UIMeasureStringWidth(label->label, label->labelBytes);
   2372 	} else if (message == UI_MSG_PAINT) {
   2373 		UIDrawControl((UIPainter *) dp, element->bounds, UI_DRAW_CONTROL_LABEL | UI_DRAW_CONTROL_STATE_FROM_ELEMENT(element),
   2374 				label->label, label->labelBytes, 0, element->window->scale);
   2375 	} else if (message == UI_MSG_DEALLOCATE) {
   2376 		UI_FREE(label->label);
   2377 	}
   2378 
   2379 	return 0;
   2380 }
   2381 
   2382 void UILabelSetContent(UILabel *label, const char *string, ptrdiff_t stringBytes) {
   2383 	UI_FREE(label->label);
   2384 	label->label = UIStringCopy(string, (label->labelBytes = stringBytes));
   2385 	UIElementMeasurementsChanged(&label->e, 1);
   2386 	UIElementRepaint(&label->e, NULL);
   2387 }
   2388 
   2389 UILabel *UILabelCreate(UIElement *parent, uint32_t flags, const char *string, ptrdiff_t stringBytes) {
   2390 	UILabel *label = (UILabel *) UIElementCreate(sizeof(UILabel), parent, flags, _UILabelMessage, "Label");
   2391 	label->label = UIStringCopy(string, (label->labelBytes = stringBytes));
   2392 	return label;
   2393 }
   2394 
   2395 /////////////////////////////////////////
   2396 // Split panes.
   2397 /////////////////////////////////////////
   2398 
   2399 int _UISplitPaneMessage(UIElement *element, UIMessage message, int di, void *dp);
   2400 
   2401 int _UISplitterMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2402 	UISplitPane *splitPane = (UISplitPane *) element->parent;
   2403 	bool vertical = splitPane->e.flags & UI_SPLIT_PANE_VERTICAL;
   2404 
   2405 	if (message == UI_MSG_PAINT) {
   2406 		UIDrawControl((UIPainter *) dp, element->bounds, UI_DRAW_CONTROL_SPLITTER | (vertical ? UI_DRAW_CONTROL_STATE_VERTICAL : 0)
   2407 				| UI_DRAW_CONTROL_STATE_FROM_ELEMENT(element), NULL, 0, 0, element->window->scale);
   2408 	} else if (message == UI_MSG_GET_CURSOR) {
   2409 		return vertical ? UI_CURSOR_SPLIT_V : UI_CURSOR_SPLIT_H;
   2410 	} else if (message == UI_MSG_MOUSE_DRAG) {
   2411 		int cursor = vertical ? element->window->cursorY : element->window->cursorX;
   2412 		int splitterSize = UI_SIZE_SPLITTER * element->window->scale;
   2413 		int space = (vertical ? UI_RECT_HEIGHT(splitPane->e.bounds) : UI_RECT_WIDTH(splitPane->e.bounds)) - splitterSize;
   2414 		float oldWeight = splitPane->weight;
   2415 		splitPane->weight = (float) (cursor - splitterSize / 2 - (vertical ? splitPane->e.bounds.t : splitPane->e.bounds.l)) / space;
   2416 		if (splitPane->weight < 0.05f) splitPane->weight = 0.05f;
   2417 		if (splitPane->weight > 0.95f) splitPane->weight = 0.95f;
   2418 
   2419 		if (splitPane->e.children[2]->messageClass == _UISplitPaneMessage
   2420 				&& (splitPane->e.children[2]->flags & UI_SPLIT_PANE_VERTICAL) == (splitPane->e.flags & UI_SPLIT_PANE_VERTICAL)) {
   2421 			UISplitPane *subSplitPane = (UISplitPane *) splitPane->e.children[2];
   2422 			subSplitPane->weight = (splitPane->weight - oldWeight - subSplitPane->weight + oldWeight * subSplitPane->weight) / (-1 + splitPane->weight);
   2423 			if (subSplitPane->weight < 0.05f) subSplitPane->weight = 0.05f;
   2424 			if (subSplitPane->weight > 0.95f) subSplitPane->weight = 0.95f;
   2425 		}
   2426 
   2427 		UIElementRefresh(&splitPane->e);
   2428 	}
   2429 
   2430 	return 0;
   2431 }
   2432 
   2433 int _UISplitPaneMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2434 	UISplitPane *splitPane = (UISplitPane *) element;
   2435 	bool vertical = splitPane->e.flags & UI_SPLIT_PANE_VERTICAL;
   2436 
   2437 	if (message == UI_MSG_LAYOUT) {
   2438 		UIElement *splitter = element->children[0];
   2439 		UIElement *left = element->children[1];
   2440 		UIElement *right = element->children[2];
   2441 
   2442 		int splitterSize = UI_SIZE_SPLITTER * element->window->scale;
   2443 		int space = (vertical ? UI_RECT_HEIGHT(element->bounds) : UI_RECT_WIDTH(element->bounds)) - splitterSize;
   2444 		int leftSize = space * splitPane->weight;
   2445 		int rightSize = space - leftSize;
   2446 
   2447 		if (vertical) {
   2448 			UIElementMove(left, UI_RECT_4(element->bounds.l, element->bounds.r, element->bounds.t, element->bounds.t + leftSize), false);
   2449 			UIElementMove(splitter, UI_RECT_4(element->bounds.l, element->bounds.r, element->bounds.t + leftSize, element->bounds.t + leftSize + splitterSize), false);
   2450 			UIElementMove(right, UI_RECT_4(element->bounds.l, element->bounds.r, element->bounds.b - rightSize, element->bounds.b), false);
   2451 		} else {
   2452 			UIElementMove(left, UI_RECT_4(element->bounds.l, element->bounds.l + leftSize, element->bounds.t, element->bounds.b), false);
   2453 			UIElementMove(splitter, UI_RECT_4(element->bounds.l + leftSize, element->bounds.l + leftSize + splitterSize, element->bounds.t, element->bounds.b), false);
   2454 			UIElementMove(right, UI_RECT_4(element->bounds.r - rightSize, element->bounds.r, element->bounds.t, element->bounds.b), false);
   2455 		}
   2456 	}
   2457 
   2458 	return 0;
   2459 }
   2460 
   2461 UISplitPane *UISplitPaneCreate(UIElement *parent, uint32_t flags, float weight) {
   2462 	UISplitPane *splitPane = (UISplitPane *) UIElementCreate(sizeof(UISplitPane), parent, flags, _UISplitPaneMessage, "Split Pane");
   2463 	splitPane->weight = weight;
   2464 	UIElementCreate(sizeof(UIElement), &splitPane->e, 0, _UISplitterMessage, "Splitter");
   2465 	return splitPane;
   2466 }
   2467 
   2468 /////////////////////////////////////////
   2469 // Tab panes.
   2470 /////////////////////////////////////////
   2471 
   2472 int _UITabPaneMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2473 	UITabPane *tabPane = (UITabPane *) element;
   2474 
   2475 	if (message == UI_MSG_PAINT) {
   2476 		UIPainter *painter = (UIPainter *) dp;
   2477 		UIRectangle top = element->bounds;
   2478 		top.b = top.t + UI_SIZE_BUTTON_HEIGHT * element->window->scale;
   2479 		UIDrawControl(painter, top, UI_DRAW_CONTROL_TAB_BAND, NULL, 0, 0, element->window->scale);
   2480 
   2481 		UIRectangle tab = top;
   2482 		tab.l += UI_SIZE_TAB_PANE_SPACE_LEFT * element->window->scale;
   2483 		tab.t += UI_SIZE_TAB_PANE_SPACE_TOP * element->window->scale;
   2484 
   2485 		int position = 0;
   2486 		uint32_t index = 0;
   2487 
   2488 		while (true) {
   2489 			int end = position;
   2490 			for (; tabPane->tabs[end] != '\t' && tabPane->tabs[end]; end++);
   2491 
   2492 			int width = UIMeasureStringWidth(tabPane->tabs, end - position);
   2493 			tab.r = tab.l + width + UI_SIZE_BUTTON_PADDING;
   2494 
   2495 			UIDrawControl(painter, tab, UI_DRAW_CONTROL_TAB | (tabPane->active == index ? UI_DRAW_CONTROL_STATE_SELECTED : 0),
   2496 					tabPane->tabs + position, end - position, 0, element->window->scale);
   2497 			tab.l = tab.r - 1;
   2498 
   2499 			if (tabPane->tabs[end] == '\t') {
   2500 				position = end + 1;
   2501 				index++;
   2502 			} else {
   2503 				break;
   2504 			}
   2505 		}
   2506 	} else if (message == UI_MSG_LEFT_DOWN) {
   2507 		UIRectangle tab = element->bounds;
   2508 		tab.b = tab.t + UI_SIZE_BUTTON_HEIGHT * element->window->scale;
   2509 		tab.l += UI_SIZE_TAB_PANE_SPACE_LEFT * element->window->scale;
   2510 		tab.t += UI_SIZE_TAB_PANE_SPACE_TOP * element->window->scale;
   2511 
   2512 		int position = 0;
   2513 		int index = 0;
   2514 
   2515 		while (true) {
   2516 			int end = position;
   2517 			for (; tabPane->tabs[end] != '\t' && tabPane->tabs[end]; end++);
   2518 
   2519 			int width = UIMeasureStringWidth(tabPane->tabs, end - position);
   2520 			tab.r = tab.l + width + UI_SIZE_BUTTON_PADDING;
   2521 
   2522 			if (UIRectangleContains(tab, element->window->cursorX, element->window->cursorY)) {
   2523 				tabPane->active = index;
   2524 				UIElementRelayout(element);
   2525 				UIElementRepaint(element, NULL);
   2526 				break;
   2527 			}
   2528 
   2529 			tab.l = tab.r - 1;
   2530 
   2531 			if (tabPane->tabs[end] == '\t') {
   2532 				position = end + 1;
   2533 				index++;
   2534 			} else {
   2535 				break;
   2536 			}
   2537 		}
   2538 	} else if (message == UI_MSG_LAYOUT) {
   2539 		UIRectangle content = element->bounds;
   2540 		content.t += UI_SIZE_BUTTON_HEIGHT * element->window->scale;
   2541 
   2542 		for (uint32_t index = 0; index < element->childCount; index++) {
   2543 			UIElement *child = element->children[index];
   2544 
   2545 			if (tabPane->active == index) {
   2546 				child->flags &= ~UI_ELEMENT_HIDE;
   2547 				UIElementMove(child, content, false);
   2548 				UIElementMessage(child, UI_MSG_TAB_SELECTED, 0, 0);
   2549 			} else {
   2550 				child->flags |= UI_ELEMENT_HIDE;
   2551 			}
   2552 		}
   2553 	} else if (message == UI_MSG_GET_HEIGHT) {
   2554 		int baseHeight = UI_SIZE_BUTTON_HEIGHT * element->window->scale;
   2555 
   2556 		for (uint32_t index = 0; index < element->childCount; index++) {
   2557 			UIElement *child = element->children[index];
   2558 
   2559 			if (tabPane->active == index) {
   2560 				return baseHeight + UIElementMessage(child, UI_MSG_GET_HEIGHT, di, dp);
   2561 			}
   2562 		}
   2563 	} else if (message == UI_MSG_DEALLOCATE) {
   2564 		UI_FREE(tabPane->tabs);
   2565 	}
   2566 
   2567 	return 0;
   2568 }
   2569 
   2570 UITabPane *UITabPaneCreate(UIElement *parent, uint32_t flags, const char *tabs) {
   2571 	UITabPane *tabPane = (UITabPane *) UIElementCreate(sizeof(UITabPane), parent, flags, _UITabPaneMessage, "Tab Pane");
   2572 	tabPane->tabs = UIStringCopy(tabs, -1);
   2573 	return tabPane;
   2574 }
   2575 
   2576 /////////////////////////////////////////
   2577 // Spacers.
   2578 /////////////////////////////////////////
   2579 
   2580 int _UISpacerMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2581 	UISpacer *spacer = (UISpacer *) element;
   2582 
   2583 	if (message == UI_MSG_GET_HEIGHT) {
   2584 		return spacer->height * element->window->scale;
   2585 	} else if (message == UI_MSG_GET_WIDTH) {
   2586 		return spacer->width * element->window->scale;
   2587 	}
   2588 
   2589 	return 0;
   2590 }
   2591 
   2592 UISpacer *UISpacerCreate(UIElement *parent, uint32_t flags, int width, int height) {
   2593 	UISpacer *spacer = (UISpacer *) UIElementCreate(sizeof(UISpacer), parent, flags, _UISpacerMessage, "Spacer");
   2594 	spacer->width = width;
   2595 	spacer->height = height;
   2596 	return spacer;
   2597 }
   2598 
   2599 /////////////////////////////////////////
   2600 // Scroll bars.
   2601 /////////////////////////////////////////
   2602 
   2603 int _UIScrollBarMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2604 	UIScrollBar *scrollBar = (UIScrollBar *) element;
   2605 
   2606 	if (message == UI_MSG_GET_WIDTH || message == UI_MSG_GET_HEIGHT) {
   2607 		return UI_SIZE_SCROLL_BAR * element->window->scale;
   2608 	} else if (message == UI_MSG_LAYOUT) {
   2609 		UIElement *up = element->children[0];
   2610 		UIElement *thumb = element->children[1];
   2611 		UIElement *down = element->children[2];
   2612 
   2613 		if (scrollBar->page >= scrollBar->maximum || scrollBar->maximum <= 0 || scrollBar->page <= 0) {
   2614 			up->flags |= UI_ELEMENT_HIDE;
   2615 			thumb->flags |= UI_ELEMENT_HIDE;
   2616 			down->flags |= UI_ELEMENT_HIDE;
   2617 
   2618 			scrollBar->position = 0;
   2619 		} else {
   2620 			up->flags &= ~UI_ELEMENT_HIDE;
   2621 			thumb->flags &= ~UI_ELEMENT_HIDE;
   2622 			down->flags &= ~UI_ELEMENT_HIDE;
   2623 
   2624 			int size = scrollBar->horizontal ? UI_RECT_WIDTH(element->bounds) : UI_RECT_HEIGHT(element->bounds);
   2625 			int thumbSize = size * scrollBar->page / scrollBar->maximum;
   2626 
   2627 			if (thumbSize < UI_SIZE_SCROLL_MINIMUM_THUMB * element->window->scale) {
   2628 				thumbSize = UI_SIZE_SCROLL_MINIMUM_THUMB * element->window->scale;
   2629 			}
   2630 
   2631 			if (scrollBar->position < 0) {
   2632 				scrollBar->position = 0;
   2633 			} else if (scrollBar->position > scrollBar->maximum - scrollBar->page) {
   2634 				scrollBar->position = scrollBar->maximum - scrollBar->page;
   2635 			}
   2636 
   2637 			int thumbPosition = scrollBar->position / (scrollBar->maximum - scrollBar->page) * (size - thumbSize);
   2638 
   2639 			if (scrollBar->position == scrollBar->maximum - scrollBar->page) {
   2640 				thumbPosition = size - thumbSize;
   2641 			}
   2642 
   2643 			if (scrollBar->horizontal) {
   2644 				UIRectangle r = element->bounds;
   2645 				r.r = r.l + thumbPosition;
   2646 				UIElementMove(up, r, false);
   2647 				r.l = r.r, r.r = r.l + thumbSize;
   2648 				UIElementMove(thumb, r, false);
   2649 				r.l = r.r, r.r = element->bounds.r;
   2650 				UIElementMove(down, r, false);
   2651 			} else {
   2652 				UIRectangle r = element->bounds;
   2653 				r.b = r.t + thumbPosition;
   2654 				UIElementMove(up, r, false);
   2655 				r.t = r.b, r.b = r.t + thumbSize;
   2656 				UIElementMove(thumb, r, false);
   2657 				r.t = r.b, r.b = element->bounds.b;
   2658 				UIElementMove(down, r, false);
   2659 			}
   2660 		}
   2661 	} else if (message == UI_MSG_PAINT) {
   2662 		UIDrawControl((UIPainter *) dp, element->bounds, UI_DRAW_CONTROL_SCROLL_TRACK
   2663 				| ((scrollBar->page >= scrollBar->maximum || scrollBar->maximum <= 0 || scrollBar->page <= 0) ? UI_DRAW_CONTROL_STATE_DISABLED : 0),
   2664 				NULL, 0, 0, element->window->scale);
   2665 	} else if (message == UI_MSG_MOUSE_WHEEL) {
   2666 		scrollBar->position += di;
   2667 		UIElementRefresh(element);
   2668 		UIElementMessage(element->parent, UI_MSG_SCROLLED, 0, 0);
   2669 		return 1;
   2670 	}
   2671 
   2672 	return 0;
   2673 }
   2674 
   2675 int _UIScrollUpDownMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2676 	UIScrollBar *scrollBar = (UIScrollBar *) element->parent;
   2677 	bool isDown = element->cp;
   2678 
   2679 	if (message == UI_MSG_PAINT) {
   2680 		UIDrawControl((UIPainter *) dp, element->bounds, (isDown ? UI_DRAW_CONTROL_SCROLL_DOWN : UI_DRAW_CONTROL_SCROLL_UP)
   2681 				| (scrollBar->horizontal ? 0 : UI_DRAW_CONTROL_STATE_VERTICAL) | UI_DRAW_CONTROL_STATE_FROM_ELEMENT(element),
   2682 				NULL, 0, 0, element->window->scale);
   2683 	} else if (message == UI_MSG_UPDATE) {
   2684 		UIElementRepaint(element, NULL);
   2685 	} else if (message == UI_MSG_LEFT_DOWN) {
   2686 		UIElementAnimate(element, false);
   2687 		scrollBar->lastAnimateTime = UI_CLOCK();
   2688 	} else if (message == UI_MSG_LEFT_UP) {
   2689 		UIElementAnimate(element, true);
   2690 	} else if (message == UI_MSG_ANIMATE) {
   2691 		UI_CLOCK_T previous = scrollBar->lastAnimateTime;
   2692 		UI_CLOCK_T current = UI_CLOCK();
   2693 		UI_CLOCK_T delta = current - previous;
   2694 		double deltaSeconds = (double) delta / UI_CLOCKS_PER_SECOND;
   2695 		if (deltaSeconds > 0.1) deltaSeconds = 0.1;
   2696 		double deltaPixels = deltaSeconds * scrollBar->page * 3;
   2697 		scrollBar->lastAnimateTime = current;
   2698 		if (isDown) scrollBar->position += deltaPixels;
   2699 		else scrollBar->position -= deltaPixels;
   2700 		UIElementRefresh(&scrollBar->e);
   2701 		UIElementMessage(scrollBar->e.parent, UI_MSG_SCROLLED, 0, 0);
   2702 	}
   2703 
   2704 	return 0;
   2705 }
   2706 
   2707 int _UIScrollThumbMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2708 	UIScrollBar *scrollBar = (UIScrollBar *) element->parent;
   2709 
   2710 	if (message == UI_MSG_PAINT) {
   2711 		UIDrawControl((UIPainter *) dp, element->bounds, UI_DRAW_CONTROL_SCROLL_THUMB
   2712 				| (scrollBar->horizontal ? 0 : UI_DRAW_CONTROL_STATE_VERTICAL)
   2713 				| UI_DRAW_CONTROL_STATE_FROM_ELEMENT(element), NULL, 0, 0, element->window->scale);
   2714 	} else if (message == UI_MSG_UPDATE) {
   2715 		UIElementRepaint(element, NULL);
   2716 	} else if (message == UI_MSG_MOUSE_DRAG && element->window->pressedButton == 1) {
   2717 		if (!scrollBar->inDrag) {
   2718 			scrollBar->inDrag = true;
   2719 
   2720 			if (scrollBar->horizontal) {
   2721 				scrollBar->dragOffset = element->bounds.l - scrollBar->e.bounds.l - element->window->cursorX;
   2722 			} else {
   2723 				scrollBar->dragOffset = element->bounds.t - scrollBar->e.bounds.t - element->window->cursorY;
   2724 			}
   2725 		}
   2726 
   2727 		int thumbPosition = (scrollBar->horizontal ? element->window->cursorX : element->window->cursorY) + scrollBar->dragOffset;
   2728 		int size = scrollBar->horizontal ? (UI_RECT_WIDTH(scrollBar->e.bounds) - UI_RECT_WIDTH(element->bounds))
   2729 				: (UI_RECT_HEIGHT(scrollBar->e.bounds) - UI_RECT_HEIGHT(element->bounds));
   2730 		scrollBar->position = (double) thumbPosition / size * (scrollBar->maximum - scrollBar->page);
   2731 		UIElementRefresh(&scrollBar->e);
   2732 		UIElementMessage(scrollBar->e.parent, UI_MSG_SCROLLED, 0, 0);
   2733 	} else if (message == UI_MSG_LEFT_UP) {
   2734 		scrollBar->inDrag = false;
   2735 	}
   2736 
   2737 	return 0;
   2738 }
   2739 
   2740 UIScrollBar *UIScrollBarCreate(UIElement *parent, uint32_t flags) {
   2741 	UIScrollBar *scrollBar = (UIScrollBar *) UIElementCreate(sizeof(UIScrollBar), parent, flags, _UIScrollBarMessage, "Scroll Bar");
   2742 	bool horizontal = scrollBar->horizontal = flags & UI_SCROLL_BAR_HORIZONTAL;
   2743 	UIElementCreate(sizeof(UIElement), &scrollBar->e, flags, _UIScrollUpDownMessage, !horizontal ? "Scroll Up" : "Scroll Left")->cp = (void *) (uintptr_t) 0;
   2744 	UIElementCreate(sizeof(UIElement), &scrollBar->e, flags, _UIScrollThumbMessage, "Scroll Thumb");
   2745 	UIElementCreate(sizeof(UIElement), &scrollBar->e, flags, _UIScrollUpDownMessage, !horizontal ? "Scroll Down" : "Scroll Right")->cp = (void *) (uintptr_t) 1;
   2746 	return scrollBar;
   2747 }
   2748 
   2749 /////////////////////////////////////////
   2750 // Code views.
   2751 /////////////////////////////////////////
   2752 
   2753 bool _UICharIsDigit(int c) {
   2754 	return c >= '0' && c <= '9';
   2755 }
   2756 
   2757 bool _UICharIsAlpha(int c) {
   2758 	return (
   2759 		('A' <= c && c <= 'Z') ||
   2760 		('a' <= c && c <= 'z') ||
   2761 		c > 127
   2762 	);
   2763 }
   2764 
   2765 bool _UICharIsAlphaOrDigitOrUnderscore(int c) {
   2766 	return _UICharIsAlpha(c) || _UICharIsDigit(c) || c == '_';
   2767 }
   2768 
   2769 int _UICodeByteToColumn(UICode *code, int line, int byte) {
   2770 	return _UIByteToColumn(&code->content[code->lines[line].offset], byte, code->lines[line].bytes, code->tabSize);
   2771 }
   2772 
   2773 int _UICodeColumnToByte(UICode *code, int line, int column) {
   2774 	return _UIColumnToByte(&code->content[code->lines[line].offset], column, code->lines[line].bytes, code->tabSize);
   2775 }
   2776 
   2777 void UICodePositionToByte(UICode *code, int x, int y, int *line, int *byte) {
   2778 	UIFont *previousFont = UIFontActivate(code->font);
   2779 	int lineHeight = UIMeasureStringHeight();
   2780 	*line = (y - code->e.bounds.t + code->vScroll->position) / lineHeight;
   2781 	if (*line < 0) *line = 0;
   2782 	else if (*line >= code->lineCount) *line = code->lineCount - 1;
   2783 	int column = (x - code->e.bounds.l + code->hScroll->position + ui.activeFont->glyphWidth / 2) / ui.activeFont->glyphWidth;
   2784 	if (~code->e.flags & UI_CODE_NO_MARGIN) column -= (UI_SIZE_CODE_MARGIN + UI_SIZE_CODE_MARGIN_GAP) / ui.activeFont->glyphWidth;
   2785 	UIFontActivate(previousFont);
   2786 	*byte = _UICodeColumnToByte(code, *line, column);
   2787 }
   2788 
   2789 int UICodeHitTest(UICode *code, int x, int y) {
   2790 	x -= code->e.bounds.l;
   2791 
   2792 	if (x < 0 || x >= code->vScroll->e.bounds.l) {
   2793 		return 0;
   2794 	}
   2795 
   2796 	y -= code->e.bounds.t - code->vScroll->position;
   2797 
   2798 	UIFont *previousFont = UIFontActivate(code->font);
   2799 	int lineHeight = UIMeasureStringHeight();
   2800 	bool inMargin = x < UI_SIZE_CODE_MARGIN + UI_SIZE_CODE_MARGIN_GAP / 2 && (~code->e.flags & UI_CODE_NO_MARGIN);
   2801 	UIFontActivate(previousFont);
   2802 
   2803 	if (y < 0 || y >= lineHeight * code->lineCount) {
   2804 		return 0;
   2805 	}
   2806 
   2807 	int line = y / lineHeight + 1;
   2808 	return inMargin ? -line : line;
   2809 }
   2810 
   2811 int UIDrawStringHighlighted(UIPainter *painter, UIRectangle lineBounds, const char *string, ptrdiff_t bytes, int tabSize, UIStringSelection *selection) {
   2812 	if (bytes == -1) bytes = _UIStringLength(string);
   2813 	if (bytes > 10000) bytes = 10000;
   2814 
   2815 	typedef enum _UICodeTokenType {
   2816 		UI_CODE_TOKEN_TYPE_DEFAULT,
   2817 		UI_CODE_TOKEN_TYPE_COMMENT,
   2818 		UI_CODE_TOKEN_TYPE_STRING,
   2819 		UI_CODE_TOKEN_TYPE_NUMBER,
   2820 		UI_CODE_TOKEN_TYPE_OPERATOR,
   2821 		UI_CODE_TOKEN_TYPE_PREPROCESSOR,
   2822 	} _UICodeTokenType;
   2823 
   2824 	uint32_t colors[] = {
   2825 		ui.theme.codeDefault,
   2826 		ui.theme.codeComment,
   2827 		ui.theme.codeString,
   2828 		ui.theme.codeNumber,
   2829 		ui.theme.codeOperator,
   2830 		ui.theme.codePreprocessor,
   2831 	};
   2832 
   2833 	int lineHeight = UIMeasureStringHeight();
   2834 	int x = lineBounds.l;
   2835 	int y = (lineBounds.t + lineBounds.b - lineHeight) / 2;
   2836 	int ti = 0;
   2837 	_UICodeTokenType tokenType = UI_CODE_TOKEN_TYPE_DEFAULT;
   2838 	bool inComment = false, inIdentifier = false, inChar = false, startedString = false, startedPreprocessor = false;
   2839 	uint32_t last = 0;
   2840 	int j = 0;
   2841 
   2842 	while (bytes) {
   2843 #ifdef UI_UNICODE
   2844 		ptrdiff_t bytesConsumed;
   2845 		int c = Utf8GetCodePoint(string, bytes, &bytesConsumed);
   2846 		UI_ASSERT(bytesConsumed > 0);
   2847 		string += bytesConsumed;
   2848 		bytes -= bytesConsumed;
   2849 #else
   2850 		char c = *string++;
   2851 		bytes--;
   2852 #endif
   2853 
   2854 		last <<= 8;
   2855 		last |= c & 0xFF;
   2856 
   2857 		if (tokenType == UI_CODE_TOKEN_TYPE_PREPROCESSOR) {
   2858 			if (bytes && c == '/' && (*string == '/' || *string == '*')) {
   2859 				tokenType = UI_CODE_TOKEN_TYPE_DEFAULT;
   2860 			}
   2861 		} else if (tokenType == UI_CODE_TOKEN_TYPE_OPERATOR) {
   2862 			tokenType = UI_CODE_TOKEN_TYPE_DEFAULT;
   2863 		} else if (tokenType == UI_CODE_TOKEN_TYPE_COMMENT) {
   2864 			if ((last & 0xFF0000) == ('*' << 16) && (last & 0xFF00) == ('/' << 8) && inComment) {
   2865 				tokenType = startedPreprocessor ? UI_CODE_TOKEN_TYPE_PREPROCESSOR : UI_CODE_TOKEN_TYPE_DEFAULT;
   2866 				inComment = false;
   2867 			}
   2868 		} else if (tokenType == UI_CODE_TOKEN_TYPE_NUMBER) {
   2869 			if (!_UICharIsAlpha(c) && !_UICharIsDigit(c)) {
   2870 				tokenType = UI_CODE_TOKEN_TYPE_DEFAULT;
   2871 			}
   2872 		} else if (tokenType == UI_CODE_TOKEN_TYPE_STRING) {
   2873 			if (!startedString) {
   2874 				if (!inChar && ((last >> 8) & 0xFF) == '"' && ((last >> 16) & 0xFF) != '\\') {
   2875 					tokenType = UI_CODE_TOKEN_TYPE_DEFAULT;
   2876 				} else if (inChar && ((last >> 8) & 0xFF) == '\'' && ((last >> 16) & 0xFF) != '\\') {
   2877 					tokenType = UI_CODE_TOKEN_TYPE_DEFAULT;
   2878 				}
   2879 			}
   2880 
   2881 			startedString = false;
   2882 		}
   2883 
   2884 		if (tokenType == UI_CODE_TOKEN_TYPE_DEFAULT) {
   2885 			if (c == '#') {
   2886 				tokenType = UI_CODE_TOKEN_TYPE_PREPROCESSOR;
   2887 				startedPreprocessor = true;
   2888 			} else if (bytes && c == '/' && *string == '/') {
   2889 				tokenType = UI_CODE_TOKEN_TYPE_COMMENT;
   2890 			} else if (bytes && c == '/' && *string == '*') {
   2891 				tokenType = UI_CODE_TOKEN_TYPE_COMMENT, inComment = true;
   2892 			} else if (c == '"') {
   2893 				tokenType = UI_CODE_TOKEN_TYPE_STRING;
   2894 				inChar = false;
   2895 				startedString = true;
   2896 			} else if (c == '\'') {
   2897 				tokenType = UI_CODE_TOKEN_TYPE_STRING;
   2898 				inChar = true;
   2899 				startedString = true;
   2900 			} else if (_UICharIsDigit(c) && !inIdentifier) {
   2901 				tokenType = UI_CODE_TOKEN_TYPE_NUMBER;
   2902 			} else if (!_UICharIsAlpha(c) && !_UICharIsDigit(c)) {
   2903 				tokenType = UI_CODE_TOKEN_TYPE_OPERATOR;
   2904 				inIdentifier = false;
   2905 			} else {
   2906 				inIdentifier = true;
   2907 			}
   2908 		}
   2909 
   2910 		int oldX = x;
   2911 
   2912 		if (c == '\t') {
   2913 			x += ui.activeFont->glyphWidth, ti++;
   2914 			while (ti % tabSize) x += ui.activeFont->glyphWidth, ti++, j++;
   2915 		} else {
   2916 			UIDrawGlyph(painter, x, y, c, colors[tokenType]);
   2917 			x += ui.activeFont->glyphWidth, ti++;
   2918 		}
   2919 
   2920 		if (selection && j >= selection->carets[0] && j < selection->carets[1]) {
   2921 			UIDrawBlock(painter, UI_RECT_4(oldX, x, y, y + lineHeight), selection->colorBackground);
   2922 			if (c != '\t') UIDrawGlyph(painter, oldX, y, c, selection->colorText);
   2923 		}
   2924 
   2925 		if (selection && selection->carets[0] == j) {
   2926 			UIDrawInvert(painter, UI_RECT_4(oldX, oldX + 1, y, y + lineHeight));
   2927 		}
   2928 
   2929 		j++;
   2930 	}
   2931 
   2932 	if (selection && selection->carets[0] == j) {
   2933 		UIDrawInvert(painter, UI_RECT_4(x, x + 1, y, y + lineHeight));
   2934 	}
   2935 
   2936 	return x;
   2937 }
   2938 
   2939 void _UICodeUpdateSelection(UICode *code) {
   2940 	bool swap = code->selection[3].line < code->selection[2].line
   2941 		|| (code->selection[3].line == code->selection[2].line && code->selection[3].offset < code->selection[2].offset);
   2942 	code->selection[1 - swap] = code->selection[3];
   2943 	code->selection[0 + swap] = code->selection[2];
   2944 	code->moveScrollToCaretNextLayout = true;
   2945 	UIElementRefresh(&code->e);
   2946 }
   2947 
   2948 void _UICodeSetVerticalMotionColumn(UICode *code, bool restore) {
   2949 	if (restore) {
   2950 		code->selection[3].offset = _UICodeColumnToByte(code, code->selection[3].line, code->verticalMotionColumn);
   2951 	} else if (!code->useVerticalMotionColumn) {
   2952 		code->useVerticalMotionColumn = true;
   2953 		code->verticalMotionColumn = _UICodeByteToColumn(code, code->selection[3].line, code->selection[3].offset);
   2954 	}
   2955 }
   2956 
   2957 void _UICodeCopyText(void *cp) {
   2958 	UICode *code = (UICode *) cp;
   2959 
   2960 	int from = code->lines[code->selection[0].line].offset + code->selection[0].offset;
   2961 	int to = code->lines[code->selection[1].line].offset + code->selection[1].offset;
   2962 
   2963 	if (from != to) {
   2964 		char *pasteText = (char *) UI_CALLOC(to - from + 2);
   2965 		for (int i = from; i < to; i++) pasteText[i - from] = code->content[i];
   2966 		_UIClipboardWriteText(code->e.window, pasteText);
   2967 	}
   2968 }
   2969 
   2970 int _UICodeMessage(UIElement *element, UIMessage message, int di, void *dp) {
   2971 	UICode *code = (UICode *) element;
   2972 
   2973 	if (message == UI_MSG_LAYOUT) {
   2974 		UIFont *previousFont = UIFontActivate(code->font);
   2975 		int scrollBarSize = UI_SIZE_SCROLL_BAR * code->e.window->scale;
   2976 		code->vScroll->maximum = code->lineCount * UIMeasureStringHeight();
   2977 		code->hScroll->maximum = code->columns * code->font->glyphWidth; // TODO This doesn't take into account tab sizes!
   2978 		int vSpace = code->vScroll->page = UI_RECT_HEIGHT(element->bounds);
   2979 		int hSpace = code->hScroll->page = UI_RECT_WIDTH(element->bounds);
   2980 
   2981 		if (code->moveScrollToCaretNextLayout) {
   2982 			int top = code->selection[3].line * UIMeasureStringHeight();
   2983 			int bottom = top + UIMeasureStringHeight();
   2984 			int context = UIMeasureStringHeight() * 2;
   2985 			if (bottom > code->vScroll->position + vSpace - context) code->vScroll->position = bottom - vSpace + context;
   2986 			if (top < code->vScroll->position + context) code->vScroll->position = top - context;
   2987 			code->moveScrollToCaretNextLayout = code->moveScrollToFocusNextLayout = false;
   2988 			// TODO Horizontal scrolling.
   2989 		} else if (code->moveScrollToFocusNextLayout) {
   2990 			int lineHeight = UIMeasureStringHeight();
   2991 			int viewHeight = UI_RECT_HEIGHT(code->e.bounds);
   2992 
   2993 			int padding = lineHeight*5;
   2994 			int prevPos = code->vScroll->position;
   2995 			int newPos = (code->focused + 0.5) * lineHeight - viewHeight / 2;
   2996 
   2997 			if (!code->centerExecutionPointer) {
   2998 				if (newPos-prevPos > viewHeight/2 - padding) {
   2999 					newPos = newPos - (viewHeight/2 - padding);
   3000 				} else if (newPos-prevPos < -(viewHeight/2 - padding)) {
   3001 					newPos = newPos +(viewHeight/2 - padding);
   3002 				} else {
   3003 					newPos = prevPos;
   3004 				}
   3005 			}
   3006 
   3007 			code->vScroll->position = newPos;
   3008 		}
   3009 
   3010 		if (!(code->e.flags & UI_CODE_NO_MARGIN)) hSpace -= UI_SIZE_CODE_MARGIN + UI_SIZE_CODE_MARGIN_GAP;
   3011 		_UI_LAYOUT_SCROLL_BAR_PAIR(code);
   3012 
   3013 		UIFontActivate(previousFont);
   3014 	} else if (message == UI_MSG_PAINT) {
   3015 		UIFont *previousFont = UIFontActivate(code->font);
   3016 
   3017 		UIPainter *painter = (UIPainter *) dp;
   3018 		UIRectangle lineBounds = element->bounds;
   3019 
   3020 		lineBounds.r = code->vScroll->e.bounds.l;
   3021 
   3022 		if (~code->e.flags & UI_CODE_NO_MARGIN) {
   3023 			lineBounds.l += UI_SIZE_CODE_MARGIN + UI_SIZE_CODE_MARGIN_GAP;
   3024 		}
   3025 
   3026 		int lineHeight = UIMeasureStringHeight();
   3027 		lineBounds.t -= (int64_t) code->vScroll->position % lineHeight;
   3028 
   3029 		UIDrawBlock(painter, element->bounds, ui.theme.codeBackground);
   3030 
   3031 #ifdef __cplusplus
   3032 		UIStringSelection selection = {};
   3033 #else
   3034 		UIStringSelection selection = { 0 };
   3035 #endif
   3036 		selection.colorBackground = ui.theme.selected;
   3037 		selection.colorText = ui.theme.textSelected;
   3038 
   3039 		for (int i = code->vScroll->position / lineHeight; i < code->lineCount; i++) {
   3040 			if (lineBounds.t > element->clip.b) {
   3041 				break;
   3042 			}
   3043 
   3044 			lineBounds.b = lineBounds.t + lineHeight;
   3045 
   3046 			if (~code->e.flags & UI_CODE_NO_MARGIN) {
   3047 				char string[16];
   3048 				int p = 16;
   3049 				int lineNumber = i + 1;
   3050 
   3051 				while (lineNumber) {
   3052 					string[--p] = (lineNumber % 10) + '0';
   3053 					lineNumber /= 10;
   3054 				}
   3055 
   3056 				UIRectangle marginBounds = lineBounds;
   3057 				marginBounds.r = marginBounds.l - UI_SIZE_CODE_MARGIN_GAP;
   3058 				marginBounds.l -= UI_SIZE_CODE_MARGIN + UI_SIZE_CODE_MARGIN_GAP;
   3059 
   3060 				uint32_t marginColor = UIElementMessage(element, UI_MSG_CODE_GET_MARGIN_COLOR, i + 1, 0);
   3061 
   3062 				if (marginColor) {
   3063 					UIDrawBlock(painter, marginBounds, marginColor);
   3064 				}
   3065 
   3066 				UIDrawString(painter, marginBounds, string + p, 16 - p,
   3067 						marginColor ? ui.theme.codeDefault : ui.theme.codeComment, UI_ALIGN_RIGHT, NULL);
   3068 			}
   3069 
   3070 			if (code->focused == i) {
   3071 				UIDrawBlock(painter, lineBounds, ui.theme.codeFocused);
   3072 			}
   3073 
   3074 			UIRectangle oldClip = painter->clip;
   3075 			painter->clip = UIRectangleIntersection(oldClip, lineBounds);
   3076 			if (code->hScroll) lineBounds.l -= (int64_t) code->hScroll->position;
   3077 			selection.carets[0] = i == code->selection[0].line ? _UICodeByteToColumn(code, i, code->selection[0].offset) : 0;
   3078 			selection.carets[1] = i == code->selection[1].line ? _UICodeByteToColumn(code, i, code->selection[1].offset) : code->lines[i].bytes;
   3079 			int x = UIDrawStringHighlighted(painter, lineBounds, code->content + code->lines[i].offset, code->lines[i].bytes, code->tabSize,
   3080 					element->window->focused == element && i >= code->selection[0].line && i <= code->selection[1].line ? &selection : NULL);
   3081 			int y = (lineBounds.t + lineBounds.b - UIMeasureStringHeight()) / 2;
   3082 
   3083 			if (element->window->focused == element && i >= code->selection[0].line && i < code->selection[1].line) {
   3084 				UIDrawBlock(painter, UI_RECT_4PD(x, y, code->font->glyphWidth, code->font->glyphHeight), selection.colorBackground);
   3085 			}
   3086 
   3087 			if (code->hScroll) lineBounds.l += (int64_t) code->hScroll->position;
   3088 			painter->clip = oldClip;
   3089 
   3090 			UICodeDecorateLine m;
   3091 			m.x = x, m.y = y, m.bounds = lineBounds, m.index = i + 1, m.painter = painter;
   3092 			UIElementMessage(element, UI_MSG_CODE_DECORATE_LINE, 0, &m);
   3093 
   3094 			lineBounds.t += lineHeight;
   3095 		}
   3096 
   3097 		UIFontActivate(previousFont);
   3098 	} else if (message == UI_MSG_SCROLLED) {
   3099 		code->moveScrollToFocusNextLayout = false;
   3100 		UIElementRefresh(element);
   3101 	} else if (message == UI_MSG_MOUSE_WHEEL) {
   3102 		return UIElementMessage(&code->vScroll->e, message, di, dp);
   3103 	} else if (message == UI_MSG_GET_CURSOR) {
   3104 		if (UICodeHitTest(code, element->window->cursorX, element->window->cursorY) < 0) {
   3105 			return UI_CURSOR_FLIPPED_ARROW;
   3106 		}
   3107 
   3108 		if (element->flags & UI_CODE_SELECTABLE) {
   3109 			return UI_CURSOR_TEXT;
   3110 		}
   3111 	} else if (message == UI_MSG_LEFT_UP) {
   3112 		UIElementAnimate(element, true);
   3113 	} else if (message == UI_MSG_LEFT_DOWN && code->lineCount) {
   3114 		int hitTest = UICodeHitTest(code, element->window->cursorX, element->window->cursorY);
   3115 		code->leftDownInMargin = hitTest < 0;
   3116 
   3117 		if (hitTest > 0 && (element->flags & UI_CODE_SELECTABLE)) {
   3118 			UICodePositionToByte(code, element->window->cursorX, element->window->cursorY, &code->selection[2].line, &code->selection[2].offset);
   3119 			_UICodeMessage(element, UI_MSG_MOUSE_DRAG, di, dp);
   3120 			UIElementFocus(element);
   3121 			UIElementAnimate(element, false);
   3122 			code->lastAnimateTime = UI_CLOCK();
   3123 		}
   3124 	} else if (message == UI_MSG_ANIMATE) {
   3125 		if (element->window->pressed == element && element->window->pressedButton == 1 && code->lineCount && !code->leftDownInMargin) {
   3126 			UI_CLOCK_T previous = code->lastAnimateTime;
   3127 			UI_CLOCK_T current = UI_CLOCK();
   3128 			UI_CLOCK_T deltaTicks = current - previous;
   3129 			double deltaSeconds = (double) deltaTicks / UI_CLOCKS_PER_SECOND;
   3130 			if (deltaSeconds > 0.1) deltaSeconds = 0.1;
   3131 			int delta = deltaSeconds * 800;
   3132 			if (!delta) { return 0; }
   3133 			code->lastAnimateTime = current;
   3134 
   3135 			UIFont *previousFont = UIFontActivate(code->font);
   3136 
   3137 			if (element->window->cursorX < element->bounds.l + ((element->flags & UI_CODE_NO_MARGIN)
   3138 						? UI_SIZE_CODE_MARGIN_GAP : (UI_SIZE_CODE_MARGIN + UI_SIZE_CODE_MARGIN_GAP * 2))) {
   3139 				code->hScroll->position -= delta;
   3140 			} else if (element->window->cursorX >= code->vScroll->e.bounds.l - UI_SIZE_CODE_MARGIN_GAP) {
   3141 				code->hScroll->position += delta;
   3142 			}
   3143 
   3144 			if (element->window->cursorY < element->bounds.t + UI_SIZE_CODE_MARGIN_GAP) {
   3145 				code->vScroll->position -= delta;
   3146 			} else if (element->window->cursorY >= code->hScroll->e.bounds.t - UI_SIZE_CODE_MARGIN_GAP) {
   3147 				code->vScroll->position += delta;
   3148 			}
   3149 
   3150 			code->moveScrollToFocusNextLayout = false;
   3151 			UIFontActivate(previousFont);
   3152 			_UICodeMessage(element, UI_MSG_MOUSE_DRAG, di, dp);
   3153 			UIElementRefresh(element);
   3154 		}
   3155 	} else if (message == UI_MSG_MOUSE_DRAG && element->window->pressedButton == 1 && code->lineCount && !code->leftDownInMargin) {
   3156 		// TODO Double-click and triple-click dragging for word and line granularity respectively.
   3157 		UICodePositionToByte(code, element->window->cursorX, element->window->cursorY, &code->selection[3].line, &code->selection[3].offset);
   3158 		_UICodeUpdateSelection(code);
   3159 		code->moveScrollToFocusNextLayout = code->moveScrollToCaretNextLayout = false;
   3160 		code->useVerticalMotionColumn = false;
   3161 	} else if (message == UI_MSG_KEY_TYPED && code->lineCount) {
   3162 		UIKeyTyped *m = (UIKeyTyped *) dp;
   3163 
   3164 		if ((m->code == UI_KEYCODE_LETTER('C') || m->code == UI_KEYCODE_LETTER('X') || m->code == UI_KEYCODE_INSERT)
   3165 				&& element->window->ctrl && !element->window->alt && !element->window->shift) {
   3166 			_UICodeCopyText(code);
   3167 		} else if ((m->code == UI_KEYCODE_UP || m->code == UI_KEYCODE_DOWN || m->code == UI_KEYCODE_PAGE_UP || m->code == UI_KEYCODE_PAGE_DOWN)
   3168 				&& !element->window->ctrl && !element->window->alt) {
   3169 			UIFont *previousFont = UIFontActivate(code->font);
   3170 			int lineHeight = UIMeasureStringHeight();
   3171 
   3172 			if (element->window->shift) {
   3173 				if (m->code == UI_KEYCODE_UP) {
   3174 					if (code->selection[3].line - 1 >= 0) {
   3175 						_UICodeSetVerticalMotionColumn(code, false);
   3176 						code->selection[3].line--;
   3177 						_UICodeSetVerticalMotionColumn(code, true);
   3178 					}
   3179 				} else if (m->code == UI_KEYCODE_DOWN) {
   3180 					if (code->selection[3].line + 1 < code->lineCount) {
   3181 						_UICodeSetVerticalMotionColumn(code, false);
   3182 						code->selection[3].line++;
   3183 						_UICodeSetVerticalMotionColumn(code, true);
   3184 					}
   3185 				} else if (m->code == UI_KEYCODE_PAGE_UP || m->code == UI_KEYCODE_PAGE_DOWN) {
   3186 					_UICodeSetVerticalMotionColumn(code, false);
   3187 					int pageHeight = (element->bounds.t - code->hScroll->e.bounds.t) / lineHeight * 4 / 5;
   3188 					code->selection[3].line += m->code == UI_KEYCODE_PAGE_UP ? pageHeight : -pageHeight;
   3189 					if (code->selection[3].line < 0) code->selection[3].line = 0;
   3190 					if (code->selection[3].line >= code->lineCount) code->selection[3].line = code->lineCount - 1;
   3191 					_UICodeSetVerticalMotionColumn(code, true);
   3192 				}
   3193 
   3194 				_UICodeUpdateSelection(code);
   3195 			} else {
   3196 				code->moveScrollToFocusNextLayout = false;
   3197 				_UI_KEY_INPUT_VSCROLL(code, lineHeight, (element->bounds.t - code->hScroll->e.bounds.t) * 4 / 5 /* leave a few lines for context */);
   3198 			}
   3199 
   3200 			UIFontActivate(previousFont);
   3201 		} else if ((m->code == UI_KEYCODE_HOME || m->code == UI_KEYCODE_END) && !element->window->alt) {
   3202 			if (element->window->shift) {
   3203 				if (m->code == UI_KEYCODE_HOME) {
   3204 					if (element->window->ctrl) code->selection[3].line = 0;
   3205 					code->selection[3].offset = 0;
   3206 					code->useVerticalMotionColumn = false;
   3207 				} else {
   3208 					if (element->window->ctrl) code->selection[3].line = code->lineCount - 1;
   3209 					code->selection[3].offset = code->lines[code->selection[3].line].bytes;
   3210 					code->useVerticalMotionColumn = false;
   3211 				}
   3212 
   3213 				_UICodeUpdateSelection(code);
   3214 			} else {
   3215 				code->vScroll->position = m->code == UI_KEYCODE_HOME ? 0 : code->vScroll->maximum;
   3216 				code->moveScrollToFocusNextLayout = false;
   3217 				UIElementRefresh(&code->e);
   3218 			}
   3219 		} else if ((m->code == UI_KEYCODE_LEFT || m->code == UI_KEYCODE_RIGHT) && !element->window->alt) {
   3220 			if (element->window->shift) {
   3221 				UICodeMoveCaret(code, m->code == UI_KEYCODE_LEFT, element->window->ctrl);
   3222 			} else if (!element->window->ctrl) {
   3223 				code->hScroll->position += m->code == UI_KEYCODE_LEFT ? -ui.activeFont->glyphWidth : ui.activeFont->glyphWidth;
   3224 				UIElementRefresh(&code->e);
   3225 			} else {
   3226 				return 0;
   3227 			}
   3228 		} else {
   3229 			return 0;
   3230 		}
   3231 
   3232 		return 1;
   3233 	} else if (message == UI_MSG_RIGHT_DOWN) {
   3234 		int hitTest = UICodeHitTest(code, element->window->cursorX, element->window->cursorY);
   3235 
   3236 		if (hitTest > 0 && (element->flags & UI_CODE_SELECTABLE)) {
   3237 			UIElementFocus(element);
   3238 			UIMenu *menu = UIMenuCreate(&element->window->e, UI_MENU_NO_SCROLL);
   3239 			UIMenuAddItem(menu, (code->selection[0].line == code->selection[1].line
   3240 						&& code->selection[0].offset == code->selection[1].offset) ? UI_ELEMENT_DISABLED : 0, "Copy", -1, _UICodeCopyText, code);
   3241 			UIMenuShow(menu);
   3242 		}
   3243 	} else if (message == UI_MSG_UPDATE) {
   3244 		UIElementRepaint(element, NULL);
   3245 	} else if (message == UI_MSG_DEALLOCATE) {
   3246 		UI_FREE(code->content);
   3247 		UI_FREE(code->lines);
   3248 	}
   3249 
   3250 	return 0;
   3251 }
   3252 
   3253 void UICodeMoveCaret(UICode *code, bool backward, bool word) {
   3254 	while (true) {
   3255 		if (backward) {
   3256 			if (code->selection[3].offset - 1 < 0) {
   3257 				if (code->selection[3].line > 0) {
   3258 					code->selection[3].line--;
   3259 					code->selection[3].offset = code->lines[code->selection[3].line].bytes;
   3260 				} else break;
   3261 			} else _UI_MOVE_CARET_BACKWARD(code->selection[3].offset, code->content, code->lines[code->selection[3].line].offset + code->selection[3].offset, code->lines[code->selection[3].line].offset);
   3262 		} else {
   3263 			if (code->selection[3].offset + 1 > code->lines[code->selection[3].line].bytes) {
   3264 				if (code->selection[3].line + 1 < code->lineCount) {
   3265 					code->selection[3].line++;
   3266 					code->selection[3].offset = 0;
   3267 				} else break;
   3268 			} else _UI_MOVE_CARET_FORWARD(code->selection[3].offset, code->content, code->contentBytes, code->lines[code->selection[3].line].offset + code->selection[3].offset);
   3269 		}
   3270 
   3271 		if (!word) break;
   3272 
   3273 		if (code->selection[3].offset != 0 && code->selection[3].offset != code->lines[code->selection[3].line].bytes) {
   3274 			_UI_MOVE_CARET_BY_WORD(code->content, code->contentBytes, code->lines[code->selection[3].line].offset + code->selection[3].offset);
   3275 		}
   3276 	}
   3277 
   3278 	code->useVerticalMotionColumn = false;
   3279 	_UICodeUpdateSelection(code);
   3280 }
   3281 
   3282 void UICodeFocusLine(UICode *code, int index) {
   3283 	code->focused = index - 1;
   3284 	code->moveScrollToFocusNextLayout = true;
   3285 	UIElementRefresh(&code->e);
   3286 }
   3287 
   3288 void UICodeInsertContent(UICode *code, const char *content, ptrdiff_t byteCount, bool replace) {
   3289 	code->useVerticalMotionColumn = false;
   3290 
   3291 	UIFont *previousFont = UIFontActivate(code->font);
   3292 
   3293 	if (byteCount == -1) {
   3294 		byteCount = _UIStringLength(content);
   3295 	}
   3296 
   3297 	if (byteCount > 1000000000) {
   3298 		byteCount = 1000000000;
   3299 	}
   3300 
   3301 	if (replace) {
   3302 		UI_FREE(code->content);
   3303 		UI_FREE(code->lines);
   3304 		code->content = NULL;
   3305 		code->lines = NULL;
   3306 		code->contentBytes = 0;
   3307 		code->lineCount = 0;
   3308 		code->columns = 0;
   3309 		code->selection[0].line = code->selection[1].line = 0;
   3310 		code->selection[0].offset = code->selection[1].offset = 0;
   3311 	}
   3312 
   3313 	code->content = (char *) UI_REALLOC(code->content, code->contentBytes + byteCount);
   3314 
   3315 	if (!byteCount) {
   3316 		return;
   3317 	}
   3318 
   3319 	int lineCount = content[byteCount - 1] != '\n';
   3320 
   3321 	for (int i = 0; i < byteCount; i++) {
   3322 		code->content[i + code->contentBytes] = content[i];
   3323 
   3324 		if (content[i] == '\n') {
   3325 			lineCount++;
   3326 		}
   3327 	}
   3328 
   3329 	code->lines = (UICodeLine *) UI_REALLOC(code->lines, sizeof(UICodeLine) * (code->lineCount + lineCount));
   3330 	int offset = 0, lineIndex = 0;
   3331 
   3332 	for (intptr_t i = 0; i <= byteCount && lineIndex < lineCount; i++) {
   3333 		if (content[i] == '\n' || i == byteCount) {
   3334 			UICodeLine line = { 0 };
   3335 			line.offset = offset + code->contentBytes;
   3336 			line.bytes = i - offset;
   3337 			if (line.bytes > code->columns) code->columns = line.bytes;
   3338 			code->lines[code->lineCount + lineIndex] = line;
   3339 			lineIndex++;
   3340 			offset = i + 1;
   3341 		}
   3342 	}
   3343 
   3344 	code->lineCount += lineCount;
   3345 	code->contentBytes += byteCount;
   3346 
   3347 	if (!replace) {
   3348 		code->vScroll->position = code->lineCount * UIMeasureStringHeight();
   3349 	}
   3350 
   3351 	UIFontActivate(previousFont);
   3352 	UIElementRepaint(&code->e, NULL);
   3353 }
   3354 
   3355 UICode *UICodeCreate(UIElement *parent, uint32_t flags) {
   3356 	UICode *code = (UICode *) UIElementCreate(sizeof(UICode), parent, flags, _UICodeMessage, "Code");
   3357 	code->font = ui.activeFont;
   3358 	code->vScroll = UIScrollBarCreate(&code->e, 0);
   3359 	code->hScroll = UIScrollBarCreate(&code->e, UI_SCROLL_BAR_HORIZONTAL);
   3360 	code->focused = -1;
   3361 	code->tabSize = 4;
   3362 	return code;
   3363 }
   3364 
   3365 /////////////////////////////////////////
   3366 // Gauges.
   3367 /////////////////////////////////////////
   3368 
   3369 int _UIGaugeMessage(UIElement *element, UIMessage message, int di, void *dp) {
   3370 	UIGauge *gauge = (UIGauge *) element;
   3371 
   3372 	if (message == UI_MSG_GET_HEIGHT) {
   3373 		return UI_SIZE_GAUGE_HEIGHT * element->window->scale;
   3374 	} else if (message == UI_MSG_GET_WIDTH) {
   3375 		return UI_SIZE_GAUGE_WIDTH * element->window->scale;
   3376 	} else if (message == UI_MSG_PAINT) {
   3377 		UIDrawControl((UIPainter *) dp, element->bounds, UI_DRAW_CONTROL_GAUGE | UI_DRAW_CONTROL_STATE_FROM_ELEMENT(element),
   3378 				NULL, 0, gauge->position, element->window->scale);
   3379 	}
   3380 
   3381 	return 0;
   3382 }
   3383 
   3384 void UIGaugeSetPosition(UIGauge *gauge, float position) {
   3385 	if (position == gauge->position) return;
   3386 	gauge->position = position;
   3387 	UIElementRepaint(&gauge->e, NULL);
   3388 }
   3389 
   3390 UIGauge *UIGaugeCreate(UIElement *parent, uint32_t flags) {
   3391 	return (UIGauge *) UIElementCreate(sizeof(UIGauge), parent, flags, _UIGaugeMessage, "Gauge");
   3392 }
   3393 
   3394 /////////////////////////////////////////
   3395 // Sliders.
   3396 /////////////////////////////////////////
   3397 
   3398 int _UISliderMessage(UIElement *element, UIMessage message, int di, void *dp) {
   3399 	UISlider *slider = (UISlider *) element;
   3400 
   3401 	if (message == UI_MSG_GET_HEIGHT) {
   3402 		return UI_SIZE_SLIDER_HEIGHT * element->window->scale;
   3403 	} else if (message == UI_MSG_GET_WIDTH) {
   3404 		return UI_SIZE_SLIDER_WIDTH * element->window->scale;
   3405 	} else if (message == UI_MSG_PAINT) {
   3406 		UIDrawControl((UIPainter *) dp, element->bounds, UI_DRAW_CONTROL_SLIDER | UI_DRAW_CONTROL_STATE_FROM_ELEMENT(element),
   3407 				NULL, 0, slider->position, element->window->scale);
   3408 	} else if (message == UI_MSG_LEFT_DOWN || (message == UI_MSG_MOUSE_DRAG && element->window->pressedButton == 1)) {
   3409 		UIRectangle bounds = element->bounds;
   3410 		int thumbSize = UI_SIZE_SLIDER_THUMB * element->window->scale;
   3411 		slider->position = (double) (element->window->cursorX - thumbSize / 2 - bounds.l) / (UI_RECT_WIDTH(bounds) - thumbSize);
   3412 		if (slider->steps > 1) slider->position = (int) (slider->position * (slider->steps - 1) + 0.5f) / (double) (slider->steps - 1);
   3413 		if (slider->position < 0) slider->position = 0;
   3414 		if (slider->position > 1) slider->position = 1;
   3415 		UIElementMessage(element, UI_MSG_VALUE_CHANGED, 0, 0);
   3416 		UIElementRepaint(element, NULL);
   3417 	} else if (message == UI_MSG_UPDATE) {
   3418 		UIElementRepaint(element, NULL);
   3419 	}
   3420 
   3421 	return 0;
   3422 }
   3423 
   3424 UISlider *UISliderCreate(UIElement *parent, uint32_t flags) {
   3425 	return (UISlider *) UIElementCreate(sizeof(UISlider), parent, flags, _UISliderMessage, "Slider");
   3426 }
   3427 
   3428 /////////////////////////////////////////
   3429 // Tables.
   3430 /////////////////////////////////////////
   3431 
   3432 int UITableHitTest(UITable *table, int x, int y) {
   3433 	x -= table->e.bounds.l;
   3434 
   3435 	if (x < 0 || x >= table->vScroll->e.bounds.l) {
   3436 		return -1;
   3437 	}
   3438 
   3439 	y -= (table->e.bounds.t + UI_SIZE_TABLE_HEADER * table->e.window->scale) - table->vScroll->position;
   3440 
   3441 	int rowHeight = UI_SIZE_TABLE_ROW * table->e.window->scale;
   3442 
   3443 	if (y < 0 || y >= rowHeight * table->itemCount) {
   3444 		return -1;
   3445 	}
   3446 
   3447 	return y / rowHeight;
   3448 }
   3449 
   3450 int UITableHeaderHitTest(UITable *table, int x, int y) {
   3451 	if (!table->columnCount) return -1;
   3452 	UIRectangle header = table->e.bounds;
   3453 	header.b = header.t + UI_SIZE_TABLE_HEADER * table->e.window->scale;
   3454 	header.l += UI_SIZE_TABLE_COLUMN_GAP * table->e.window->scale;
   3455 	int position = 0, index = 0;
   3456 
   3457 	while (true) {
   3458 		int end = position;
   3459 		for (; table->columns[end] != '\t' && table->columns[end]; end++);
   3460 		header.r = header.l + table->columnWidths[index];
   3461 		if (UIRectangleContains(header, x, y)) return index;
   3462 		header.l += table->columnWidths[index] + UI_SIZE_TABLE_COLUMN_GAP * table->e.window->scale;
   3463 		if (table->columns[end] != '\t') break;
   3464 		position = end + 1, index++;
   3465 	}
   3466 
   3467 	return -1;
   3468 }
   3469 
   3470 bool UITableEnsureVisible(UITable *table, int index) {
   3471 	int rowHeight = UI_SIZE_TABLE_ROW * table->e.window->scale;
   3472 	int y = index * rowHeight;
   3473 	y -= table->vScroll->position;
   3474 	int height = UI_RECT_HEIGHT(table->e.bounds) - UI_SIZE_TABLE_HEADER * table->e.window->scale - rowHeight;
   3475 
   3476 	if (y < 0) {
   3477 		table->vScroll->position += y;
   3478 		UIElementRefresh(&table->e);
   3479 		return true;
   3480 	} else if (y > height) {
   3481 		table->vScroll->position -= height - y;
   3482 		UIElementRefresh(&table->e);
   3483 		return true;
   3484 	} else {
   3485 		return false;
   3486 	}
   3487 }
   3488 
   3489 void UITableResizeColumns(UITable *table) {
   3490 	int position = 0;
   3491 	int count = 0;
   3492 
   3493 	while (true) {
   3494 		int end = position;
   3495 		for (; table->columns[end] != '\t' && table->columns[end]; end++);
   3496 		count++;
   3497 		if (table->columns[end] == '\t') position = end + 1;
   3498 		else break;
   3499 	}
   3500 
   3501 	UI_FREE(table->columnWidths);
   3502 	table->columnWidths = (int *) UI_MALLOC(count * sizeof(int));
   3503 	table->columnCount = count;
   3504 
   3505 	position = 0;
   3506 
   3507 	char buffer[256];
   3508 	UITableGetItem m = { 0 };
   3509 	m.buffer = buffer;
   3510 	m.bufferBytes = sizeof(buffer);
   3511 
   3512 	while (true) {
   3513 		int end = position;
   3514 		for (; table->columns[end] != '\t' && table->columns[end]; end++);
   3515 
   3516 		int longest = UIMeasureStringWidth(table->columns + position, end - position);
   3517 
   3518 		for (int i = 0; i < table->itemCount; i++) {
   3519 			m.index = i;
   3520 			int bytes = UIElementMessage(&table->e, UI_MSG_TABLE_GET_ITEM, 0, &m);
   3521 			int width = UIMeasureStringWidth(buffer, bytes);
   3522 
   3523 			if (width > longest) {
   3524 				longest = width;
   3525 			}
   3526 		}
   3527 
   3528 		table->columnWidths[m.column] = longest;
   3529 		m.column++;
   3530 		if (table->columns[end] == '\t') position = end + 1;
   3531 		else break;
   3532 	}
   3533 
   3534 	UIElementRepaint(&table->e, NULL);
   3535 }
   3536 
   3537 int _UITableMessage(UIElement *element, UIMessage message, int di, void *dp) {
   3538 	UITable *table = (UITable *) element;
   3539 
   3540 	if (message == UI_MSG_PAINT) {
   3541 		UIPainter *painter = (UIPainter *) dp;
   3542 		UIRectangle bounds = element->bounds;
   3543 		bounds.r = table->vScroll->e.bounds.l;
   3544 		UIDrawControl(painter, element->bounds, UI_DRAW_CONTROL_TABLE_BACKGROUND | UI_DRAW_CONTROL_STATE_FROM_ELEMENT(element), NULL, 0, 0, element->window->scale);
   3545 		char buffer[256];
   3546 		UIRectangle row = bounds;
   3547 		int rowHeight = UI_SIZE_TABLE_ROW * element->window->scale;
   3548 		UITableGetItem m = { 0 };
   3549 		m.buffer = buffer;
   3550 		m.bufferBytes = sizeof(buffer);
   3551 		row.t += UI_SIZE_TABLE_HEADER * table->e.window->scale;
   3552 		row.t -= (int64_t) table->vScroll->position % rowHeight;
   3553 		int hovered = UITableHitTest(table, element->window->cursorX, element->window->cursorY);
   3554 		UIRectangle oldClip = painter->clip;
   3555 		painter->clip = UIRectangleIntersection(oldClip, UI_RECT_4(bounds.l, bounds.r,
   3556 					bounds.t + (int) (UI_SIZE_TABLE_HEADER * element->window->scale), bounds.b));
   3557 
   3558 		for (int i = table->vScroll->position / rowHeight; i < table->itemCount; i++) {
   3559 			if (row.t > painter->clip.b) {
   3560 				break;
   3561 			}
   3562 
   3563 			row.b = row.t + rowHeight;
   3564 			m.index = i;
   3565 			m.isSelected = false;
   3566 			m.column = 0;
   3567 			int bytes = UIElementMessage(element, UI_MSG_TABLE_GET_ITEM, 0, &m);
   3568 
   3569 			uint32_t rowFlags = (m.isSelected ? UI_DRAW_CONTROL_STATE_SELECTED : 0) | (hovered == i ? UI_DRAW_CONTROL_STATE_HOVERED : 0);
   3570 			UIDrawControl(painter, row, UI_DRAW_CONTROL_TABLE_ROW | rowFlags, NULL, 0, 0, element->window->scale);
   3571 
   3572 			UIRectangle cell = row;
   3573 			cell.l += UI_SIZE_TABLE_COLUMN_GAP * table->e.window->scale - (int64_t) table->hScroll->position;
   3574 
   3575 			for (int j = 0; j < table->columnCount; j++) {
   3576 				if (j) {
   3577 					m.column = j;
   3578 					bytes = UIElementMessage(element, UI_MSG_TABLE_GET_ITEM, 0, &m);
   3579 				}
   3580 
   3581 				cell.r = cell.l + table->columnWidths[j];
   3582 				if ((size_t) bytes > m.bufferBytes && bytes > 0) bytes = m.bufferBytes;
   3583 				UIDrawControl(painter, cell, UI_DRAW_CONTROL_TABLE_CELL | rowFlags, buffer, bytes, 0, element->window->scale);
   3584 				cell.l += table->columnWidths[j] + UI_SIZE_TABLE_COLUMN_GAP * table->e.window->scale;
   3585 			}
   3586 
   3587 			row.t += rowHeight;
   3588 		}
   3589 
   3590 		bounds = element->bounds;
   3591 		painter->clip = UIRectangleIntersection(oldClip, bounds);
   3592 		if (table->hScroll) bounds.l -= (int64_t) table->hScroll->position;
   3593 
   3594 		UIRectangle header = bounds;
   3595 		header.b = header.t + UI_SIZE_TABLE_HEADER * table->e.window->scale;
   3596 		header.l += UI_SIZE_TABLE_COLUMN_GAP * table->e.window->scale;
   3597 
   3598 		int position = 0;
   3599 		int index = 0;
   3600 
   3601 		if (table->columnCount) {
   3602 			while (true) {
   3603 				int end = position;
   3604 				for (; table->columns[end] != '\t' && table->columns[end]; end++);
   3605 
   3606 				header.r = header.l + table->columnWidths[index];
   3607 				UIDrawControl(painter, header, UI_DRAW_CONTROL_TABLE_HEADER | (index == table->columnHighlight ? UI_DRAW_CONTROL_STATE_SELECTED : 0),
   3608 						table->columns + position, end - position, 0, element->window->scale);
   3609 				header.l += table->columnWidths[index] + UI_SIZE_TABLE_COLUMN_GAP * table->e.window->scale;
   3610 
   3611 				if (table->columns[end] == '\t') {
   3612 					position = end + 1;
   3613 					index++;
   3614 				} else {
   3615 					break;
   3616 				}
   3617 			}
   3618 		}
   3619 	} else if (message == UI_MSG_LAYOUT) {
   3620 		int scrollBarSize = UI_SIZE_SCROLL_BAR * table->e.window->scale;
   3621 		int columnGap = UI_SIZE_TABLE_COLUMN_GAP * table->e.window->scale;
   3622 
   3623 		table->vScroll->maximum = table->itemCount * UI_SIZE_TABLE_ROW * element->window->scale;
   3624 		table->hScroll->maximum = columnGap;
   3625 		for (int i = 0; i < table->columnCount; i++) { table->hScroll->maximum += table->columnWidths[i] + columnGap; }
   3626 
   3627 		int vSpace = table->vScroll->page = UI_RECT_HEIGHT(element->bounds) - UI_SIZE_TABLE_HEADER * element->window->scale;
   3628 		int hSpace = table->hScroll->page = UI_RECT_WIDTH(element->bounds);
   3629 		_UI_LAYOUT_SCROLL_BAR_PAIR(table);
   3630 	} else if (message == UI_MSG_MOUSE_MOVE || message == UI_MSG_UPDATE) {
   3631 		UIElementRepaint(element, NULL);
   3632 	} else if (message == UI_MSG_SCROLLED) {
   3633 		UIElementRefresh(element);
   3634 	} else if (message == UI_MSG_MOUSE_WHEEL) {
   3635 		return UIElementMessage(&table->vScroll->e, message, di, dp);
   3636 	} else if (message == UI_MSG_LEFT_DOWN) {
   3637 		UIElementFocus(element);
   3638 	} else if (message == UI_MSG_KEY_TYPED) {
   3639 		UIKeyTyped *m = (UIKeyTyped *) dp;
   3640 
   3641 		if ((m->code == UI_KEYCODE_UP || m->code == UI_KEYCODE_DOWN || m->code == UI_KEYCODE_PAGE_UP || m->code == UI_KEYCODE_PAGE_DOWN
   3642 				|| m->code == UI_KEYCODE_HOME || m->code == UI_KEYCODE_END)
   3643 				&& !element->window->ctrl && !element->window->alt && !element->window->shift) {
   3644 			_UI_KEY_INPUT_VSCROLL(table, UI_SIZE_TABLE_ROW * element->window->scale,
   3645 					(element->bounds.t - table->hScroll->e.bounds.t + UI_SIZE_TABLE_HEADER) * 4 / 5);
   3646 			return 1;
   3647 		} else if ((m->code == UI_KEYCODE_LEFT || m->code == UI_KEYCODE_RIGHT)
   3648 				&& !element->window->ctrl && !element->window->alt && !element->window->shift) {
   3649 			table->hScroll->position += m->code == UI_KEYCODE_LEFT ? -ui.activeFont->glyphWidth : ui.activeFont->glyphWidth;
   3650 			UIElementRefresh(&table->e);
   3651 			return 1;
   3652 		}
   3653 	} else if (message == UI_MSG_DEALLOCATE) {
   3654 		UI_FREE(table->columns);
   3655 		UI_FREE(table->columnWidths);
   3656 	}
   3657 
   3658 	return 0;
   3659 }
   3660 
   3661 UITable *UITableCreate(UIElement *parent, uint32_t flags, const char *columns) {
   3662 	UITable *table = (UITable *) UIElementCreate(sizeof(UITable), parent, flags, _UITableMessage, "Table");
   3663 	table->vScroll = UIScrollBarCreate(&table->e, 0);
   3664 	table->hScroll = UIScrollBarCreate(&table->e, UI_SCROLL_BAR_HORIZONTAL);
   3665 	table->columns = UIStringCopy(columns, -1);
   3666 	table->columnHighlight = -1;
   3667 	return table;
   3668 }
   3669 
   3670 /////////////////////////////////////////
   3671 // Textboxes.
   3672 /////////////////////////////////////////
   3673 
   3674 int _UITextboxByteToColumn(const char *string, int byte, ptrdiff_t bytes) {
   3675 	return _UIByteToColumn(string, byte, bytes, 4);
   3676 }
   3677 
   3678 int _UITextboxColumnToByte(const char *string, int column, ptrdiff_t bytes) {
   3679 	return _UIColumnToByte(string, column, bytes, 4);
   3680 }
   3681 
   3682 char *UITextboxToCString(UITextbox *textbox) {
   3683 	char *buffer = (char *) UI_MALLOC(textbox->bytes + 1);
   3684 
   3685 	for (intptr_t i = 0; i < textbox->bytes; i++) {
   3686 		buffer[i] = textbox->string[i];
   3687 	}
   3688 
   3689 	buffer[textbox->bytes] = 0;
   3690 	return buffer;
   3691 }
   3692 
   3693 void UITextboxReplace(UITextbox *textbox, const char *text, ptrdiff_t bytes, bool sendChangedMessage) {
   3694 	if (bytes == -1) bytes = _UIStringLength(text);
   3695 	int deleteFrom = textbox->carets[0], deleteTo = textbox->carets[1];
   3696 	if (deleteFrom > deleteTo) UI_SWAP(int, deleteFrom, deleteTo);
   3697 
   3698 	UI_MEMMOVE(&textbox->string[deleteFrom], &textbox->string[deleteTo], textbox->bytes - deleteTo);
   3699 	textbox->bytes -= deleteTo - deleteFrom;
   3700 	textbox->string = (char *) UI_REALLOC(textbox->string, textbox->bytes + bytes);
   3701 	UI_MEMMOVE(&textbox->string[deleteFrom + bytes], &textbox->string[deleteFrom], textbox->bytes - deleteFrom);
   3702 	UI_MEMMOVE(&textbox->string[deleteFrom], &text[0], bytes);
   3703 	textbox->bytes += bytes;
   3704 	textbox->carets[0] = deleteFrom + bytes;
   3705 	textbox->carets[1] = textbox->carets[0];
   3706 
   3707 	if (sendChangedMessage) UIElementMessage(&textbox->e, UI_MSG_VALUE_CHANGED, 0, 0);
   3708 	textbox->e.window->textboxModifiedFlag = true;
   3709 	UIElementRepaint(&textbox->e, NULL);
   3710 }
   3711 
   3712 void UITextboxClear(UITextbox *textbox, bool sendChangedMessage) {
   3713 	textbox->carets[1] = 0;
   3714 	textbox->carets[0] = textbox->bytes;
   3715 	UITextboxReplace(textbox, "", 0, sendChangedMessage);
   3716 }
   3717 
   3718 void UITextboxMoveCaret(UITextbox *textbox, bool backward, bool word) {
   3719 	while (true) {
   3720 		if (textbox->carets[0] > 0 && backward) {
   3721 			_UI_MOVE_CARET_BACKWARD(textbox->carets[0], textbox->string, textbox->carets[0], 0);
   3722 		} else if (textbox->carets[0] < textbox->bytes && !backward) {
   3723 			_UI_MOVE_CARET_FORWARD(textbox->carets[0], textbox->string, textbox->bytes, textbox->carets[0]);
   3724 		} else {
   3725 			return;
   3726 		}
   3727 
   3728 		if (!word) {
   3729 			return;
   3730 		} else if (textbox->carets[0] != textbox->bytes && textbox->carets[0] != 0) {
   3731 			_UI_MOVE_CARET_BY_WORD(textbox->string, textbox->bytes, textbox->carets[0]);
   3732 		}
   3733 	}
   3734 
   3735 	UIElementRepaint(&textbox->e, NULL);
   3736 }
   3737 
   3738 void _UITextboxCopyText(void *cp) {
   3739 	UITextbox *textbox = (UITextbox *) cp;
   3740 
   3741 	int   to = textbox->carets[0] > textbox->carets[1] ? textbox->carets[0] : textbox->carets[1];
   3742 	int from = textbox->carets[0] < textbox->carets[1] ? textbox->carets[0] : textbox->carets[1];
   3743 
   3744 	if (from != to) {
   3745 		char *pasteText = (char *) UI_CALLOC(to - from + 1);
   3746 		for (int i = from; i < to; i++) pasteText[i - from] = textbox->string[i];
   3747 		_UIClipboardWriteText(textbox->e.window, pasteText);
   3748 	}
   3749 }
   3750 
   3751 void _UITextboxPasteText(void *cp) {
   3752 	UITextbox *textbox = (UITextbox *) cp;
   3753 	size_t bytes;
   3754 	char *text = _UIClipboardReadTextStart(textbox->e.window, &bytes);
   3755 
   3756 	if (text) {
   3757 		for (size_t i = 0; i < bytes; i++) {
   3758 			if (text[i] == '\n') text[i] = ' ';
   3759 		}
   3760 
   3761 		UITextboxReplace(textbox, text, bytes, true);
   3762 	}
   3763 
   3764 	_UIClipboardReadTextEnd(textbox->e.window, text);
   3765 }
   3766 
   3767 int _UITextboxMessage(UIElement *element, UIMessage message, int di, void *dp) {
   3768 	UITextbox *textbox = (UITextbox *) element;
   3769 
   3770 	if (message == UI_MSG_GET_HEIGHT) {
   3771 		return UI_SIZE_TEXTBOX_HEIGHT * element->window->scale;
   3772 	} else if (message == UI_MSG_GET_WIDTH) {
   3773 		return UI_SIZE_TEXTBOX_WIDTH * element->window->scale;
   3774 	} else if (message == UI_MSG_PAINT) {
   3775 		UIDrawControl((UIPainter *) dp, element->bounds, UI_DRAW_CONTROL_TEXTBOX | UI_DRAW_CONTROL_STATE_FROM_ELEMENT(element),
   3776 				NULL, 0, 0, element->window->scale);
   3777 
   3778 		int scaledMargin = UI_SIZE_TEXTBOX_MARGIN * element->window->scale;
   3779 		int totalWidth = UIMeasureStringWidth(textbox->string, textbox->bytes) + scaledMargin * 2;
   3780 		UIRectangle textBounds = UIRectangleAdd(element->bounds, UI_RECT_1I(scaledMargin));
   3781 
   3782 		if (textbox->scroll > totalWidth - UI_RECT_WIDTH(textBounds)) {
   3783 			textbox->scroll = totalWidth - UI_RECT_WIDTH(textBounds);
   3784 		}
   3785 
   3786 		if (textbox->scroll < 0) {
   3787 			textbox->scroll = 0;
   3788 		}
   3789 
   3790 		int caretX = UIMeasureStringWidth(textbox->string, textbox->carets[0]) - textbox->scroll;
   3791 
   3792 		if (caretX < 0) {
   3793 			textbox->scroll = caretX + textbox->scroll;
   3794 		} else if (caretX > UI_RECT_WIDTH(textBounds)) {
   3795 			textbox->scroll = caretX - UI_RECT_WIDTH(textBounds) + textbox->scroll + 1;
   3796 		}
   3797 
   3798 #ifdef __cplusplus
   3799 		UIStringSelection selection = {};
   3800 #else
   3801 		UIStringSelection selection = { 0 };
   3802 #endif
   3803 		selection.carets[0] = _UITextboxByteToColumn(textbox->string, textbox->carets[0], textbox->bytes);
   3804 		selection.carets[1] = _UITextboxByteToColumn(textbox->string, textbox->carets[1], textbox->bytes);
   3805 		selection.colorBackground = ui.theme.selected;
   3806 		selection.colorText = ui.theme.textSelected;
   3807 		textBounds.l -= textbox->scroll;
   3808 
   3809 		UIDrawString((UIPainter *) dp, textBounds, textbox->string, textbox->bytes,
   3810 			(element->flags & UI_ELEMENT_DISABLED) ? ui.theme.textDisabled : ui.theme.text, UI_ALIGN_LEFT,
   3811 			element->window->focused == element ? &selection : NULL);
   3812 	} else if (message == UI_MSG_GET_CURSOR) {
   3813 		return UI_CURSOR_TEXT;
   3814 	} else if (message == UI_MSG_LEFT_DOWN) {
   3815 		int column = (element->window->cursorX - element->bounds.l + textbox->scroll - UI_SIZE_TEXTBOX_MARGIN * element->window->scale
   3816 				+ ui.activeFont->glyphWidth / 2) / ui.activeFont->glyphWidth;
   3817 		textbox->carets[0] = textbox->carets[1] = column <= 0 ? 0 : _UITextboxColumnToByte(textbox->string, column, textbox->bytes);
   3818 		UIElementFocus(element);
   3819 	} else if (message == UI_MSG_UPDATE) {
   3820 		UIElementRepaint(element, NULL);
   3821 	} else if (message == UI_MSG_DEALLOCATE) {
   3822 		UI_FREE(textbox->string);
   3823 	} else if (message == UI_MSG_KEY_TYPED) {
   3824 		UIKeyTyped *m = (UIKeyTyped *) dp;
   3825 		bool handled = true;
   3826 
   3827 		if (textbox->rejectNextKey) {
   3828 			textbox->rejectNextKey = false;
   3829 			handled = false;
   3830 		} else if (m->code == UI_KEYCODE_BACKSPACE || m->code == UI_KEYCODE_DELETE) {
   3831 			if (textbox->carets[0] == textbox->carets[1]) {
   3832 				UITextboxMoveCaret(textbox, m->code == UI_KEYCODE_BACKSPACE, element->window->ctrl);
   3833 			}
   3834 
   3835 			UITextboxReplace(textbox, NULL, 0, true);
   3836 		} else if (m->code == UI_KEYCODE_LEFT || m->code == UI_KEYCODE_RIGHT) {
   3837 			if (textbox->carets[0] == textbox->carets[1] || element->window->shift) {
   3838 				UITextboxMoveCaret(textbox, m->code == UI_KEYCODE_LEFT, element->window->ctrl);
   3839 				if (!element->window->shift) textbox->carets[1] = textbox->carets[0];
   3840 			} else {
   3841 				textbox->carets[1 - element->window->shift] = textbox->carets[element->window->shift];
   3842 			}
   3843 		} else if (m->code == UI_KEYCODE_HOME || m->code == UI_KEYCODE_END) {
   3844 			if (m->code == UI_KEYCODE_HOME) {
   3845 				textbox->carets[0] = 0;
   3846 			} else {
   3847 				textbox->carets[0] = textbox->bytes;
   3848 			}
   3849 
   3850 			if (!element->window->shift) {
   3851 				textbox->carets[1] = textbox->carets[0];
   3852 			}
   3853 		} else if (m->code == UI_KEYCODE_LETTER('A') && element->window->ctrl) {
   3854 			textbox->carets[1] = 0;
   3855 			textbox->carets[0] = textbox->bytes;
   3856 		} else if (m->textBytes && !element->window->alt && !element->window->ctrl && m->text[0] >= 0x20) {
   3857 			UITextboxReplace(textbox, m->text, m->textBytes, true);
   3858 		} else if ((m->code == UI_KEYCODE_LETTER('C') || m->code == UI_KEYCODE_LETTER('X') || m->code == UI_KEYCODE_INSERT)
   3859 				&& element->window->ctrl && !element->window->alt && !element->window->shift) {
   3860 			_UITextboxCopyText(textbox);
   3861 
   3862 			if (m->code == UI_KEYCODE_LETTER('X')) {
   3863 				UITextboxReplace(textbox, NULL, 0, true);
   3864 			}
   3865 		} else if ((m->code == UI_KEYCODE_LETTER('V') && element->window->ctrl && !element->window->alt && !element->window->shift)
   3866 				|| (m->code == UI_KEYCODE_INSERT && !element->window->ctrl && !element->window->alt && element->window->shift)) {
   3867 			_UITextboxPasteText(textbox);
   3868 		} else {
   3869 			handled = false;
   3870 		}
   3871 
   3872 		if (handled) {
   3873 			UIElementRepaint(element, NULL);
   3874 			return 1;
   3875 		}
   3876 	} else if (message == UI_MSG_RIGHT_DOWN) {
   3877 		int c0 = textbox->carets[0], c1 = textbox->carets[1];
   3878 		_UITextboxMessage(element, UI_MSG_LEFT_DOWN, di, dp);
   3879 
   3880 		if (c0 < c1 ? (textbox->carets[0] >= c0 && textbox->carets[0] < c1) : (textbox->carets[0] >= c1 && textbox->carets[0] < c0)) {
   3881 			textbox->carets[0] = c0, textbox->carets[1] = c1; // Only move caret if clicking outside the existing selection.
   3882 		}
   3883 
   3884 		UIMenu *menu = UIMenuCreate(&element->window->e, UI_MENU_NO_SCROLL);
   3885 		UIMenuAddItem(menu, textbox->carets[0] == textbox->carets[1] ? UI_ELEMENT_DISABLED : 0, "Copy", -1, _UITextboxCopyText, textbox);
   3886 		size_t pasteBytes;
   3887 		char *paste = _UIClipboardReadTextStart(textbox->e.window, &pasteBytes);
   3888 		UIMenuAddItem(menu, !paste || !pasteBytes ? UI_ELEMENT_DISABLED : 0, "Paste", -1, _UITextboxPasteText, textbox);
   3889 		_UIClipboardReadTextEnd(textbox->e.window, paste);
   3890 		UIMenuShow(menu);
   3891 	}
   3892 
   3893 	return 0;
   3894 }
   3895 
   3896 UITextbox *UITextboxCreate(UIElement *parent, uint32_t flags) {
   3897 	return (UITextbox *) UIElementCreate(sizeof(UITextbox), parent, flags | UI_ELEMENT_TAB_STOP, _UITextboxMessage, "Textbox");
   3898 }
   3899 
   3900 /////////////////////////////////////////
   3901 // MDI clients.
   3902 /////////////////////////////////////////
   3903 
   3904 int _UIMDIChildHitTest(UIMDIChild *mdiChild, int x, int y) {
   3905 	UIElement *element = &mdiChild->e;
   3906 	UI_MDI_CHILD_CALCULATE_LAYOUT(element->bounds, element->window->scale);
   3907 	int cornerSize = UI_SIZE_MDI_CHILD_CORNER * element->window->scale;
   3908 	if (!UIRectangleContains(element->bounds, x, y) || UIRectangleContains(content, x, y)) return -1;
   3909 	else if (x < element->bounds.l + cornerSize && y < element->bounds.t + cornerSize) return 0b1010;
   3910 	else if (x > element->bounds.r - cornerSize && y < element->bounds.t + cornerSize) return 0b0110;
   3911 	else if (x < element->bounds.l + cornerSize && y > element->bounds.b - cornerSize) return 0b1001;
   3912 	else if (x > element->bounds.r - cornerSize && y > element->bounds.b - cornerSize) return 0b0101;
   3913 	else if (x < element->bounds.l + borderSize) return 0b1000;
   3914 	else if (x > element->bounds.r - borderSize) return 0b0100;
   3915 	else if (y < element->bounds.t + borderSize) return 0b0010;
   3916 	else if (y > element->bounds.b - borderSize) return 0b0001;
   3917 	else if (UIRectangleContains(title, x, y)) return 0b1111;
   3918 	else return -1;
   3919 }
   3920 
   3921 void _UIMDIChildCloseButton(void *_child) {
   3922 	UIElement *child = (UIElement *) _child;
   3923 
   3924 	if (!UIElementMessage(child, UI_MSG_WINDOW_CLOSE, 0, 0)) {
   3925 		UIElementDestroy(child);
   3926 		UIElementRefresh(child->parent);
   3927 	}
   3928 }
   3929 
   3930 int _UIMDIChildMessage(UIElement *element, UIMessage message, int di, void *dp) {
   3931 	UIMDIChild *mdiChild = (UIMDIChild *) element;
   3932 
   3933 	if (message == UI_MSG_PAINT) {
   3934 		UIDrawControl((UIPainter *) dp, element->bounds, UI_DRAW_CONTROL_MDI_CHILD, mdiChild->title, mdiChild->titleBytes, 0, element->window->scale);
   3935 	} else if (message == UI_MSG_GET_WIDTH) {
   3936 		UIElement *child = element->childCount ? element->children[element->childCount - 1] : NULL;
   3937 		int width = 2 * UI_SIZE_MDI_CHILD_BORDER;
   3938 		width += (child ? UIElementMessage(child, message, di ? (di - UI_SIZE_MDI_CHILD_TITLE + UI_SIZE_MDI_CHILD_BORDER) : 0, dp) : 0);
   3939 		if (width < UI_SIZE_MDI_CHILD_MINIMUM_WIDTH) width = UI_SIZE_MDI_CHILD_MINIMUM_WIDTH;
   3940 		return width;
   3941 	} else if (message == UI_MSG_GET_HEIGHT) {
   3942 		UIElement *child = element->childCount ? element->children[element->childCount - 1] : NULL;
   3943 		int height = UI_SIZE_MDI_CHILD_TITLE + UI_SIZE_MDI_CHILD_BORDER;
   3944 		height += (child ? UIElementMessage(child, message, di ? (di - 2 * UI_SIZE_MDI_CHILD_BORDER) : 0, dp) : 0);
   3945 		if (height < UI_SIZE_MDI_CHILD_MINIMUM_HEIGHT) height = UI_SIZE_MDI_CHILD_MINIMUM_HEIGHT;
   3946 		return height;
   3947 	} else if (message == UI_MSG_LAYOUT) {
   3948 		UI_MDI_CHILD_CALCULATE_LAYOUT(element->bounds, element->window->scale);
   3949 
   3950 		int position = title.r;
   3951 
   3952 		for (uint32_t i = 0; i < element->childCount - 1; i++) {
   3953 			UIElement *child = element->children[i];
   3954 			int width = UIElementMessage(child, UI_MSG_GET_WIDTH, 0, 0);
   3955 			UIElementMove(child, UI_RECT_4(position - width, position, title.t, title.b), false);
   3956 			position -= width;
   3957 		}
   3958 
   3959 		UIElement *child = element->childCount ? element->children[element->childCount - 1] : NULL;
   3960 
   3961 		if (child) {
   3962 			UIElementMove(child, content, false);
   3963 		}
   3964 	} else if (message == UI_MSG_GET_CURSOR) {
   3965 		int hitTest = _UIMDIChildHitTest(mdiChild, element->window->cursorX, element->window->cursorY);
   3966 		if (hitTest == 0b1000) return UI_CURSOR_RESIZE_LEFT;
   3967 		if (hitTest == 0b0010) return UI_CURSOR_RESIZE_UP;
   3968 		if (hitTest == 0b0110) return UI_CURSOR_RESIZE_UP_RIGHT;
   3969 		if (hitTest == 0b1010) return UI_CURSOR_RESIZE_UP_LEFT;
   3970 		if (hitTest == 0b0100) return UI_CURSOR_RESIZE_RIGHT;
   3971 		if (hitTest == 0b0001) return UI_CURSOR_RESIZE_DOWN;
   3972 		if (hitTest == 0b1001) return UI_CURSOR_RESIZE_DOWN_LEFT;
   3973 		if (hitTest == 0b0101) return UI_CURSOR_RESIZE_DOWN_RIGHT;
   3974 		return UI_CURSOR_ARROW;
   3975 	} else if (message == UI_MSG_LEFT_DOWN) {
   3976 		mdiChild->dragHitTest = _UIMDIChildHitTest(mdiChild, element->window->cursorX, element->window->cursorY);
   3977 		mdiChild->dragOffset = UIRectangleAdd(element->bounds, UI_RECT_2(-element->window->cursorX, -element->window->cursorY));
   3978 	} else if (message == UI_MSG_LEFT_UP) {
   3979 		if (mdiChild->bounds.l < 0) mdiChild->bounds.r -= mdiChild->bounds.l, mdiChild->bounds.l = 0;
   3980 		if (mdiChild->bounds.t < 0) mdiChild->bounds.b -= mdiChild->bounds.t, mdiChild->bounds.t = 0;
   3981 		UIElementRefresh(element->parent);
   3982 	} else if (message == UI_MSG_MOUSE_DRAG) {
   3983 		if (mdiChild->dragHitTest > 0) {
   3984 #define _UI_MDI_CHILD_MOVE_EDGE(bit, edge, cursor, size, opposite, negate, minimum, offset) \
   3985 	if (mdiChild->dragHitTest & bit) mdiChild->bounds.edge = mdiChild->dragOffset.edge + element->window->cursor - element->parent->bounds.offset; \
   3986 	if ((mdiChild->dragHitTest & bit) && size(mdiChild->bounds) < minimum) mdiChild->bounds.edge = mdiChild->bounds.opposite negate minimum;
   3987 			_UI_MDI_CHILD_MOVE_EDGE(0b1000, l, cursorX, UI_RECT_WIDTH, r, -, UI_SIZE_MDI_CHILD_MINIMUM_WIDTH, l);
   3988 			_UI_MDI_CHILD_MOVE_EDGE(0b0100, r, cursorX, UI_RECT_WIDTH, l, +, UI_SIZE_MDI_CHILD_MINIMUM_WIDTH, l);
   3989 			_UI_MDI_CHILD_MOVE_EDGE(0b0010, t, cursorY, UI_RECT_HEIGHT, b, -, UI_SIZE_MDI_CHILD_MINIMUM_HEIGHT, t);
   3990 			_UI_MDI_CHILD_MOVE_EDGE(0b0001, b, cursorY, UI_RECT_HEIGHT, t, +, UI_SIZE_MDI_CHILD_MINIMUM_HEIGHT, t);
   3991 			UIElementRefresh(element->parent);
   3992 		}
   3993 	} else if (message == UI_MSG_DESTROY) {
   3994 		UIMDIClient *client = (UIMDIClient *) element->parent;
   3995 
   3996 		if (client->active == mdiChild) {
   3997 			client->active = (UIMDIChild *) (client->e.childCount == 1 ? NULL : client->e.children[client->e.childCount - 2]);
   3998 		}
   3999 	} else if (message == UI_MSG_DEALLOCATE) {
   4000 		UI_FREE(mdiChild->title);
   4001 	}
   4002 
   4003 	return 0;
   4004 }
   4005 
   4006 int _UIMDIClientMessage(UIElement *element, UIMessage message, int di, void *dp) {
   4007 	UIMDIClient *client = (UIMDIClient *) element;
   4008 
   4009 	if (message == UI_MSG_PAINT) {
   4010 		if (~element->flags & UI_MDI_CLIENT_TRANSPARENT) {
   4011 			UIDrawBlock((UIPainter *) dp, element->bounds, ui.theme.panel2);
   4012 		}
   4013 	} else if (message == UI_MSG_LAYOUT) {
   4014 		for (uint32_t i = 0; i < element->childCount; i++) {
   4015 			UIMDIChild *mdiChild = (UIMDIChild *) element->children[i];
   4016 			UI_ASSERT(mdiChild->e.messageClass == _UIMDIChildMessage);
   4017 
   4018 			if (UIRectangleEquals(mdiChild->bounds, UI_RECT_1(0))) {
   4019 				int width = UIElementMessage(&mdiChild->e, UI_MSG_GET_WIDTH, 0, 0);
   4020 				int height = UIElementMessage(&mdiChild->e, UI_MSG_GET_HEIGHT, width, 0);
   4021 				if (client->cascade + width > element->bounds.r || client->cascade + height > element->bounds.b) client->cascade = 0;
   4022 				mdiChild->bounds = UI_RECT_4(client->cascade, client->cascade + width, client->cascade, client->cascade + height);
   4023 				client->cascade += UI_SIZE_MDI_CASCADE * element->window->scale;
   4024 			}
   4025 
   4026 			UIRectangle bounds = UIRectangleAdd(mdiChild->bounds, UI_RECT_2(element->bounds.l, element->bounds.t));
   4027 			UIElementMove(&mdiChild->e, bounds, false);
   4028 		}
   4029 	} else if (message == UI_MSG_PRESSED_DESCENDENT) {
   4030 		UIMDIChild *child = (UIMDIChild *) dp;
   4031 
   4032 		if (child && child != client->active) {
   4033 			for (uint32_t i = 0; i < element->childCount; i++) {
   4034 				if (element->children[i] == &child->e) {
   4035 					UI_MEMMOVE(&element->children[i], &element->children[i + 1], sizeof(UIElement *) * (element->childCount - i - 1));
   4036 					element->children[element->childCount - 1] = &child->e;
   4037 					break;
   4038 				}
   4039 			}
   4040 
   4041 			client->active = child;
   4042 			UIElementRefresh(element);
   4043 		}
   4044 	}
   4045 
   4046 	return 0;
   4047 }
   4048 
   4049 UIMDIChild *UIMDIChildCreate(UIElement *parent, uint32_t flags, UIRectangle initialBounds, const char *title, ptrdiff_t titleBytes) {
   4050 	UI_ASSERT(parent->messageClass == _UIMDIClientMessage);
   4051 
   4052 	UIMDIChild *mdiChild = (UIMDIChild *) UIElementCreate(sizeof(UIMDIChild), parent, flags, _UIMDIChildMessage, "MDIChild");
   4053 	UIMDIClient *mdiClient = (UIMDIClient *) parent;
   4054 
   4055 	mdiChild->bounds = initialBounds;
   4056 	mdiChild->title = UIStringCopy(title, (mdiChild->titleBytes = titleBytes));
   4057 	mdiClient->active = mdiChild;
   4058 
   4059 	if (flags & UI_MDI_CHILD_CLOSE_BUTTON) {
   4060 		UIButton *closeButton = UIButtonCreate(&mdiChild->e, UI_BUTTON_SMALL | UI_ELEMENT_NON_CLIENT, "X", 1);
   4061 		closeButton->invoke = _UIMDIChildCloseButton;
   4062 		closeButton->e.cp = mdiChild;
   4063 	}
   4064 
   4065 	return mdiChild;
   4066 }
   4067 
   4068 UIMDIClient *UIMDIClientCreate(UIElement *parent, uint32_t flags) {
   4069 	return (UIMDIClient *) UIElementCreate(sizeof(UIMDIClient), parent, flags, _UIMDIClientMessage, "MDIClient");
   4070 }
   4071 
   4072 /////////////////////////////////////////
   4073 // Image displays.
   4074 /////////////////////////////////////////
   4075 
   4076 void _UIImageDisplayUpdateViewport(UIImageDisplay *display) {
   4077 	UIRectangle bounds = display->e.bounds;
   4078 	bounds.r -= bounds.l, bounds.b -= bounds.t;
   4079 
   4080 	float minimumZoomX = 1, minimumZoomY = 1;
   4081 	if (display->width  > bounds.r) minimumZoomX = (float) bounds.r / display->width;
   4082 	if (display->height > bounds.b) minimumZoomY = (float) bounds.b / display->height;
   4083 	float minimumZoom = minimumZoomX < minimumZoomY ? minimumZoomX : minimumZoomY;
   4084 
   4085 	if (display->zoom < minimumZoom || (display->e.flags & _UI_IMAGE_DISPLAY_ZOOM_FIT)) {
   4086 		display->zoom = minimumZoom;
   4087 		display->e.flags |= _UI_IMAGE_DISPLAY_ZOOM_FIT;
   4088 	}
   4089 
   4090 	if (display->panX < 0) display->panX = 0;
   4091 	if (display->panY < 0) display->panY = 0;
   4092 	if (display->panX > display->width  - bounds.r / display->zoom) display->panX = display->width  - bounds.r / display->zoom;
   4093 	if (display->panY > display->height - bounds.b / display->zoom) display->panY = display->height - bounds.b / display->zoom;
   4094 
   4095 	if (bounds.r && display->width  * display->zoom <= bounds.r) display->panX = display->width  / 2 - bounds.r / display->zoom / 2;
   4096 	if (bounds.b && display->height * display->zoom <= bounds.b) display->panY = display->height / 2 - bounds.b / display->zoom / 2;
   4097 }
   4098 
   4099 int _UIImageDisplayMessage(UIElement *element, UIMessage message, int di, void *dp) {
   4100 	UIImageDisplay *display = (UIImageDisplay *) element;
   4101 
   4102 	if (message == UI_MSG_GET_HEIGHT) {
   4103 		return display->height;
   4104 	} else if (message == UI_MSG_GET_WIDTH) {
   4105 		return display->width;
   4106 	} else if (message == UI_MSG_DEALLOCATE) {
   4107 		UI_FREE(display->bits);
   4108 	} else if (message == UI_MSG_PAINT) {
   4109 		UIPainter *painter = (UIPainter *) dp;
   4110 
   4111 		int w = UI_RECT_WIDTH(element->bounds), h = UI_RECT_HEIGHT(element->bounds);
   4112 		int x = _UILinearMap(0, display->panX, display->panX + w / display->zoom, 0, w) + element->bounds.l;
   4113 		int y = _UILinearMap(0, display->panY, display->panY + h / display->zoom, 0, h) + element->bounds.t;
   4114 
   4115 		UIRectangle image = UI_RECT_4(x, x + (int) (display->width * display->zoom), y, (int) (y + display->height * display->zoom));
   4116 		UIRectangle bounds = UIRectangleIntersection(painter->clip, UIRectangleIntersection(display->e.bounds, image));
   4117 		if (!UI_RECT_VALID(bounds)) return 0;
   4118 
   4119 		if (display->zoom == 1) {
   4120 			uint32_t *lineStart = (uint32_t *) painter->bits + bounds.t * painter->width + bounds.l;
   4121 			uint32_t *sourceLineStart = display->bits + (bounds.l - image.l) + display->width * (bounds.t - image.t);
   4122 
   4123 			for (int i = 0; i < bounds.b - bounds.t; i++, lineStart += painter->width, sourceLineStart += display->width) {
   4124 				uint32_t *destination = lineStart;
   4125 				uint32_t *source = sourceLineStart;
   4126 				int j = bounds.r - bounds.l;
   4127 
   4128 				do {
   4129 					*destination = *source;
   4130 					destination++;
   4131 					source++;
   4132 				} while (--j);
   4133 			}
   4134 		} else {
   4135 			float zr = 1.0f / display->zoom;
   4136 			uint32_t *destination = (uint32_t *) painter->bits;
   4137 
   4138 			for (int i = bounds.t; i < bounds.b; i++) {
   4139 				int ty = (i - image.t) * zr;
   4140 
   4141 				for (int j = bounds.l; j < bounds.r; j++) {
   4142 					int tx = (j - image.l) * zr;
   4143 					destination[i * painter->width + j] = display->bits[ty * display->width + tx];
   4144 				}
   4145 			}
   4146 		}
   4147 	} else if (message == UI_MSG_MOUSE_WHEEL && (element->flags & UI_IMAGE_DISPLAY_INTERACTIVE)) {
   4148 		display->e.flags &= ~_UI_IMAGE_DISPLAY_ZOOM_FIT;
   4149 		int divisions = -di / 72;
   4150 		float factor = 1;
   4151 		float perDivision = element->window->ctrl ? 2.0f : element->window->alt ? 1.01f : 1.2f;
   4152 		while (divisions > 0) factor *= perDivision, divisions--;
   4153 		while (divisions < 0) factor /= perDivision, divisions++;
   4154 		if (display->zoom * factor > 64) factor = 64 / display->zoom;
   4155 		int mx = element->window->cursorX - element->bounds.l;
   4156 		int my = element->window->cursorY - element->bounds.t;
   4157 		display->zoom *= factor;
   4158 		display->panX -= mx / display->zoom * (1 - factor);
   4159 		display->panY -= my / display->zoom * (1 - factor);
   4160 		_UIImageDisplayUpdateViewport(display);
   4161 		UIElementRepaint(&display->e, NULL);
   4162 	} else if (message == UI_MSG_LAYOUT && (element->flags & UI_IMAGE_DISPLAY_INTERACTIVE)) {
   4163 		UIRectangle bounds = display->e.bounds;
   4164 		bounds.r -= bounds.l, bounds.b -= bounds.t;
   4165 		display->panX -= (bounds.r - display->previousWidth ) / 2 / display->zoom;
   4166 		display->panY -= (bounds.b - display->previousHeight) / 2 / display->zoom;
   4167 		display->previousWidth = bounds.r, display->previousHeight = bounds.b;
   4168 		_UIImageDisplayUpdateViewport(display);
   4169 	} else if (message == UI_MSG_GET_CURSOR && (element->flags & UI_IMAGE_DISPLAY_INTERACTIVE)
   4170 			&& (UI_RECT_WIDTH(element->bounds) < display->width * display->zoom
   4171 				|| UI_RECT_HEIGHT(element->bounds) < display->height * display->zoom)) {
   4172 		return UI_CURSOR_HAND;
   4173 	} else if (message == UI_MSG_MOUSE_DRAG) {
   4174 		display->panX -= (element->window->cursorX - display->previousPanPointX) / display->zoom;
   4175 		display->panY -= (element->window->cursorY - display->previousPanPointY) / display->zoom;
   4176 		_UIImageDisplayUpdateViewport(display);
   4177 		display->previousPanPointX = element->window->cursorX;
   4178 		display->previousPanPointY = element->window->cursorY;
   4179 		UIElementRepaint(element, NULL);
   4180 	} else if (message == UI_MSG_LEFT_DOWN) {
   4181 		display->e.flags &= ~_UI_IMAGE_DISPLAY_ZOOM_FIT;
   4182 		display->previousPanPointX = element->window->cursorX;
   4183 		display->previousPanPointY = element->window->cursorY;
   4184 	}
   4185 
   4186 	return 0;
   4187 }
   4188 
   4189 void UIImageDisplaySetContent(UIImageDisplay *display, uint32_t *bits, size_t width, size_t height, size_t stride) {
   4190 	UI_FREE(display->bits);
   4191 
   4192 	display->bits = (uint32_t *) UI_MALLOC(width * height * 4);
   4193 	display->width = width;
   4194 	display->height = height;
   4195 
   4196 	uint32_t *destination = display->bits;
   4197 	uint32_t *source = bits;
   4198 
   4199 	for (uintptr_t row = 0; row < height; row++, source += stride / 4) {
   4200 		for (uintptr_t i = 0; i < width; i++) {
   4201 			*destination++ = source[i];
   4202 		}
   4203 	}
   4204 
   4205 	UIElementMeasurementsChanged(&display->e, 3);
   4206 	UIElementRepaint(&display->e, NULL);
   4207 }
   4208 
   4209 UIImageDisplay *UIImageDisplayCreate(UIElement *parent, uint32_t flags, uint32_t *bits, size_t width, size_t height, size_t stride) {
   4210 	UIImageDisplay *display = (UIImageDisplay *) UIElementCreate(sizeof(UIImageDisplay), parent, flags, _UIImageDisplayMessage, "ImageDisplay");
   4211 	display->zoom = 1.0f;
   4212 	UIImageDisplaySetContent(display, bits, width, height, stride);
   4213 	return display;
   4214 }
   4215 
   4216 /////////////////////////////////////////
   4217 // Modal dialogs.
   4218 /////////////////////////////////////////
   4219 
   4220 int _UIDialogWrapperMessage(UIElement *element, UIMessage message, int di, void *dp) {
   4221 	if (message == UI_MSG_LAYOUT) {
   4222 		int width = UIElementMessage(element->children[0], UI_MSG_GET_WIDTH, 0, 0);
   4223 		int height = UIElementMessage(element->children[0], UI_MSG_GET_HEIGHT, width, 0);
   4224 		int cx = (element->bounds.l + element->bounds.r) / 2;
   4225 		int cy = (element->bounds.t + element->bounds.b) / 2;
   4226 		UIRectangle bounds = UI_RECT_4(cx - (width + 1) / 2, cx + width / 2, cy - (height + 1) / 2, cy + height / 2);
   4227 		UIElementMove(element->children[0], bounds, false);
   4228 		UIElementRepaint(element, NULL);
   4229 	} else if (message == UI_MSG_PAINT) {
   4230 		UIDrawControl((UIPainter *) dp, element->children[0]->bounds, UI_DRAW_CONTROL_MODAL_POPUP, NULL, 0, 0, element->window->scale);
   4231 	} else if (message == UI_MSG_KEY_TYPED) {
   4232 		UIKeyTyped *typed = (UIKeyTyped *) dp;
   4233 
   4234 		if (element->window->ctrl) return 0;
   4235 		if (element->window->shift) return 0;
   4236 
   4237 		if (!ui.dialogCanExit) {
   4238 		} else if (!element->window->alt && typed->code == UI_KEYCODE_ESCAPE) {
   4239 			ui.dialogResult = "__C";
   4240 			return 1;
   4241 		} else if (!element->window->alt && typed->code == UI_KEYCODE_ENTER) {
   4242 			ui.dialogResult = "__D";
   4243 			return 1;
   4244 		}
   4245 
   4246 		char c0 = 0, c1 = 0;
   4247 
   4248 		if (typed->textBytes == 1 && typed->text[0] >= 'a' && typed->text[0] <= 'z') {
   4249 			c0 = typed->text[0], c1 = typed->text[0] - 'a' + 'A';
   4250 		} else {
   4251 			return 0;
   4252 		}
   4253 
   4254 		UIElement *rowContainer = element->children[0];
   4255 		UIElement *target = NULL;
   4256 		bool duplicate = false;
   4257 
   4258 		for (uint32_t i = 0; i < rowContainer->childCount; i++) {
   4259 			for (uint32_t j = 0; j < rowContainer->children[i]->childCount; j++) {
   4260 				UIElement *item = rowContainer->children[i]->children[j];
   4261 
   4262 				if (item->messageClass == _UIButtonMessage) {
   4263 					UIButton *button = (UIButton *) item;
   4264 
   4265 					if (button->label && button->labelBytes && (button->label[0] == c0 || button->label[0] == c1)) {
   4266 						if (!target) {
   4267 							target = &button->e;
   4268 						} else {
   4269 							duplicate = true;
   4270 						}
   4271 					}
   4272 				}
   4273 			}
   4274 		}
   4275 
   4276 		if (target) {
   4277 			if (duplicate) {
   4278 				UIElementFocus(target);
   4279 			} else {
   4280 				UIElementMessage(target, UI_MSG_CLICKED, 0, 0);
   4281 			}
   4282 
   4283 			return 1;
   4284 		}
   4285 	}
   4286 
   4287 	return 0;
   4288 }
   4289 
   4290 void _UIDialogButtonInvoke(void *cp) {
   4291 	ui.dialogResult = (const char *) cp;
   4292 }
   4293 
   4294 int _UIDialogDefaultButtonMessage(UIElement *element, UIMessage message, int di, void *dp) {
   4295 	if (message == UI_MSG_PAINT && element->window->focused->messageClass != _UIButtonMessage) {
   4296 		element->flags |= UI_BUTTON_CHECKED;
   4297 		element->messageClass(element, message, di, dp);
   4298 		element->flags &= ~UI_BUTTON_CHECKED;
   4299 		return 1;
   4300 	}
   4301 
   4302 	return 0;
   4303 }
   4304 
   4305 int _UIDialogTextboxMessage(UIElement *element, UIMessage message, int di, void *dp) {
   4306 	UITextbox *textbox = (UITextbox *) element;
   4307 
   4308 	if (message == UI_MSG_VALUE_CHANGED) {
   4309 		char **buffer = (char **) element->cp;
   4310 		*buffer = (char *) UI_REALLOC(*buffer, textbox->bytes + 1);
   4311 		(*buffer)[textbox->bytes] = 0;
   4312 
   4313 		for (ptrdiff_t i = 0; i < textbox->bytes; i++) {
   4314 			(*buffer)[i] = textbox->string[i];
   4315 		}
   4316 	} else if (message == UI_MSG_UPDATE && di == UI_UPDATE_FOCUSED && element->window->focused == element) {
   4317 		textbox->carets[1] = 0;
   4318 		textbox->carets[0] = textbox->bytes;
   4319 		UIElementRepaint(element, NULL);
   4320 	}
   4321 
   4322 	return 0;
   4323 }
   4324 
   4325 const char *UIDialogShow(UIWindow *window, uint32_t flags, const char *format, ...) {
   4326 	// Create the dialog wrapper and panel.
   4327 
   4328 	UI_ASSERT(!window->dialog);
   4329 	window->dialog = UIElementCreate(sizeof(UIElement), &window->e, 0, _UIDialogWrapperMessage, "DialogWrapper");
   4330 	UIPanel *panel = UIPanelCreate(window->dialog, UI_PANEL_MEDIUM_SPACING | UI_PANEL_COLOR_1);
   4331 	panel->border = UI_RECT_1(UI_SIZE_PANE_MEDIUM_BORDER * 2);
   4332 	window->e.children[0]->flags |= UI_ELEMENT_DISABLED;
   4333 
   4334 	// Create the dialog contents.
   4335 
   4336 	va_list arguments;
   4337 	va_start(arguments, format);
   4338 	UIPanel *row = NULL;
   4339 	UIElement *focus = NULL;
   4340 	UIButton *defaultButton = NULL;
   4341 	UIButton *cancelButton = NULL;
   4342 	uint32_t buttonCount = 0;
   4343 
   4344 	for (int i = 0; format[i]; i++) {
   4345 		if (i == 0 || format[i - 1] == '\n') {
   4346 			row = UIPanelCreate(&panel->e, UI_PANEL_HORIZONTAL | UI_ELEMENT_H_FILL);
   4347 			row->gap = UI_SIZE_PANE_SMALL_GAP;
   4348 		}
   4349 
   4350 		if (format[i] == ' ' || format[i] == '\n') {
   4351 		} else if (format[i] == '%') {
   4352 			i++;
   4353 
   4354 			if (format[i] == 'b' /* button */ || format[i] == 'B' /* default button */ || format[i] == 'C' /* cancel button */) {
   4355 				const char *label = va_arg(arguments, const char *);
   4356 				UIButton *button = UIButtonCreate(&row->e, 0, label, -1);
   4357 				if (!focus) focus = &button->e;
   4358 				if (format[i] == 'B') defaultButton = button;
   4359 				if (format[i] == 'C') cancelButton = button;
   4360 				buttonCount++;
   4361 				button->invoke = _UIDialogButtonInvoke;
   4362 				if (format[i] == 'B') button->e.messageUser = _UIDialogDefaultButtonMessage;
   4363 				button->e.cp = (void *) label;
   4364 			} else if (format[i] == 's' /* label from string */) {
   4365 				const char *label = va_arg(arguments, const char *);
   4366 				UILabelCreate(&row->e, 0, label, -1);
   4367 			} else if (format[i] == 't' /* textbox */) {
   4368 				char **buffer = va_arg(arguments, char **);
   4369 				UITextbox *textbox = UITextboxCreate(&row->e, UI_ELEMENT_H_FILL);
   4370 				if (!focus) focus = &textbox->e;
   4371 				if (*buffer) UITextboxReplace(textbox, *buffer, _UIStringLength(*buffer), false);
   4372 				textbox->e.cp = buffer;
   4373 				textbox->e.messageUser = _UIDialogTextboxMessage;
   4374 			} else if (format[i] == 'f' /* horizontal fill */) {
   4375 				UISpacerCreate(&row->e, UI_ELEMENT_H_FILL, 0, 0);
   4376 			} else if (format[i] == 'l' /* horizontal line */) {
   4377 				UISpacerCreate(&row->e, UI_ELEMENT_BORDER | UI_ELEMENT_H_FILL, 0, 1);
   4378 			} else if (format[i] == 'u' /* user */) {
   4379 				UIDialogUserCallback callback = va_arg(arguments, UIDialogUserCallback);
   4380 				callback(&row->e);
   4381 			}
   4382 		} else {
   4383 			int j = i;
   4384 			while (format[j] && format[j] != '%' && format[j] != '\n') j++;
   4385 			UILabelCreate(&row->e, 0, format + i, j - i);
   4386 			i = j - 1;
   4387 		}
   4388 	}
   4389 
   4390 	va_end(arguments);
   4391 
   4392 	window->dialogOldFocus = window->focused;
   4393 	UIElementFocus(focus ? focus : window->dialog);
   4394 
   4395 	// Run the modal message loop.
   4396 
   4397 	int result;
   4398 	ui.dialogResult = NULL;
   4399 	ui.dialogCanExit = buttonCount != 0;
   4400 	for (int i = 1; i <= 3; i++) _UIWindowSetPressed(window, NULL, i);
   4401 	UIElementRefresh(&window->e);
   4402 	_UIUpdate();
   4403 	while (!ui.dialogResult && _UIMessageLoopSingle(&result));
   4404 	ui.quit = !ui.dialogResult;
   4405 
   4406 	// Check for cancel/default action.
   4407 
   4408 	if (buttonCount == 1 && defaultButton && !cancelButton) {
   4409 		cancelButton = defaultButton;
   4410 	}
   4411 
   4412 	if (!ui.dialogResult) {
   4413 	} else if (ui.dialogResult[0] == '_' && ui.dialogResult[1] == '_' && ui.dialogResult[2] == 'C' && ui.dialogResult[3] == 0 && cancelButton) {
   4414 		ui.dialogResult = (const char *) cancelButton->e.cp;
   4415 	} else if (ui.dialogResult[0] == '_' && ui.dialogResult[1] == '_' && ui.dialogResult[2] == 'D' && ui.dialogResult[3] == 0 && defaultButton) {
   4416 		ui.dialogResult = (const char *) defaultButton->e.cp;
   4417 	}
   4418 
   4419 	// Destroy the dialog.
   4420 
   4421 	window->e.children[0]->flags &= ~UI_ELEMENT_DISABLED;
   4422 	UIElementDestroy(window->dialog);
   4423 	window->dialog = NULL;
   4424 	UIElementRefresh(&window->e);
   4425 	if (window->dialogOldFocus) UIElementFocus(window->dialogOldFocus);
   4426 	return ui.dialogResult ? ui.dialogResult : "";
   4427 }
   4428 
   4429 /////////////////////////////////////////
   4430 // Menus (common).
   4431 /////////////////////////////////////////
   4432 
   4433 bool _UIMenusClose() {
   4434 	UIWindow *window = ui.windows;
   4435 	bool anyClosed = false;
   4436 
   4437 	while (window) {
   4438 		if (window->e.flags & UI_WINDOW_MENU) {
   4439 			UIElementDestroy(&window->e);
   4440 			anyClosed = true;
   4441 		}
   4442 
   4443 		window = window->next;
   4444 	}
   4445 
   4446 	return anyClosed;
   4447 }
   4448 
   4449 #if !defined(UI_ESSENCE) && !defined(UI_COCOA)
   4450 int _UIMenuItemMessage(UIElement *element, UIMessage message, int di, void *dp) {
   4451 	if (message == UI_MSG_CLICKED) {
   4452 		_UIMenusClose();
   4453 	}
   4454 
   4455 	return 0;
   4456 }
   4457 
   4458 int _UIMenuMessage(UIElement *element, UIMessage message, int di, void *dp) {
   4459 	UIMenu *menu = (UIMenu *) element;
   4460 
   4461 	if (message == UI_MSG_GET_WIDTH) {
   4462 		int width = 0;
   4463 
   4464 		for (uint32_t i = 0; i < element->childCount; i++) {
   4465 			UIElement *child = element->children[i];
   4466 
   4467 			if (~child->flags & UI_ELEMENT_NON_CLIENT) {
   4468 				int w = UIElementMessage(child, UI_MSG_GET_WIDTH, 0, 0);
   4469 				if (w > width) width = w;
   4470 			}
   4471 		}
   4472 
   4473 		return width + 4 + UI_SIZE_SCROLL_BAR;
   4474 	} else if (message == UI_MSG_GET_HEIGHT) {
   4475 		int height = 0;
   4476 
   4477 		for (uint32_t i = 0; i < element->childCount; i++) {
   4478 			UIElement *child = element->children[i];
   4479 
   4480 			if (~child->flags & UI_ELEMENT_NON_CLIENT) {
   4481 				height += UIElementMessage(child, UI_MSG_GET_HEIGHT, 0, 0);
   4482 			}
   4483 		}
   4484 
   4485 		return height + 4;
   4486 	} else if (message == UI_MSG_PAINT) {
   4487 		UIDrawControl((UIPainter *) dp, element->bounds, UI_DRAW_CONTROL_MENU, NULL, 0, 0, element->window->scale);
   4488 	} else if (message == UI_MSG_LAYOUT) {
   4489 		int position = element->bounds.t + 2 - menu->vScroll->position;
   4490 		int totalHeight = 0;
   4491 		int scrollBarSize = (menu->e.flags & UI_MENU_NO_SCROLL) ? 0 : UI_SIZE_SCROLL_BAR;
   4492 
   4493 		for (uint32_t i = 0; i < element->childCount; i++) {
   4494 			UIElement *child = element->children[i];
   4495 
   4496 			if (~child->flags & UI_ELEMENT_NON_CLIENT) {
   4497 				int height = UIElementMessage(child, UI_MSG_GET_HEIGHT, 0, 0);
   4498 				UIElementMove(child, UI_RECT_4(element->bounds.l + 2, element->bounds.r - scrollBarSize - 2,
   4499 							position, position + height), false);
   4500 				position += height;
   4501 				totalHeight += height;
   4502 			}
   4503 		}
   4504 
   4505 		UIRectangle scrollBarBounds = element->bounds;
   4506 		scrollBarBounds.l = scrollBarBounds.r - scrollBarSize * element->window->scale;
   4507 		menu->vScroll->maximum = totalHeight;
   4508 		menu->vScroll->page = UI_RECT_HEIGHT(element->bounds);
   4509 		UIElementMove(&menu->vScroll->e, scrollBarBounds, true);
   4510 	} else if (message == UI_MSG_KEY_TYPED) {
   4511 		UIKeyTyped *m = (UIKeyTyped *) dp;
   4512 
   4513 		if (m->code == UI_KEYCODE_ESCAPE) {
   4514 			_UIMenusClose();
   4515 			return 1;
   4516 		}
   4517 	} else if (message == UI_MSG_MOUSE_WHEEL) {
   4518 		return UIElementMessage(&menu->vScroll->e, message, di, dp);
   4519 	} else if (message == UI_MSG_SCROLLED) {
   4520 		UIElementRefresh(element);
   4521 	}
   4522 
   4523 	return 0;
   4524 }
   4525 
   4526 void UIMenuAddItem(UIMenu *menu, uint32_t flags, const char *label, ptrdiff_t labelBytes, void (*invoke)(void *cp), void *cp) {
   4527 	UIButton *button = UIButtonCreate(&menu->e, flags | UI_BUTTON_MENU_ITEM, label, labelBytes);
   4528 	button->invoke = invoke;
   4529 	button->e.messageUser = _UIMenuItemMessage;
   4530 	button->e.cp = cp;
   4531 }
   4532 
   4533 void _UIMenuPrepare(UIMenu *menu, int *width, int *height) {
   4534 	*width = UIElementMessage(&menu->e, UI_MSG_GET_WIDTH, 0, 0);
   4535 	*height = UIElementMessage(&menu->e, UI_MSG_GET_HEIGHT, 0, 0);
   4536 
   4537 	if (menu->e.flags & UI_MENU_PLACE_ABOVE) {
   4538 		menu->pointY -= *height;
   4539 	}
   4540 }
   4541 
   4542 UIMenu *UIMenuCreate(UIElement *parent, uint32_t flags) {
   4543 	UIWindow *window = UIWindowCreate(parent->window, UI_WINDOW_MENU, 0, 0, 0);
   4544 	UIMenu *menu = (UIMenu *) UIElementCreate(sizeof(UIMenu), &window->e, flags, _UIMenuMessage, "Menu");
   4545 	menu->vScroll = UIScrollBarCreate(&menu->e, UI_ELEMENT_NON_CLIENT);
   4546 	menu->parentWindow = parent->window;
   4547 
   4548 	if (parent->parent) {
   4549 		UIRectangle screenBounds = UIElementScreenBounds(parent);
   4550 		menu->pointX = screenBounds.l;
   4551 		menu->pointY = (flags & UI_MENU_PLACE_ABOVE) ? (screenBounds.t + 1) : (screenBounds.b - 1);
   4552 	} else {
   4553 		int x = 0, y = 0;
   4554 		_UIWindowGetScreenPosition(parent->window, &x, &y);
   4555 
   4556 		menu->pointX = parent->window->cursorX + x;
   4557 		menu->pointY = parent->window->cursorY + y;
   4558 	}
   4559 
   4560 	return menu;
   4561 }
   4562 #endif
   4563 
   4564 /////////////////////////////////////////
   4565 // Miscellaneous core functions.
   4566 /////////////////////////////////////////
   4567 
   4568 UIRectangle UIElementScreenBounds(UIElement *element) {
   4569 	int x = 0, y = 0;
   4570 	_UIWindowGetScreenPosition(element->window, &x, &y);
   4571 	return UIRectangleAdd(element->bounds, UI_RECT_2(x, y));
   4572 }
   4573 
   4574 void UIWindowRegisterShortcut(UIWindow *window, UIShortcut shortcut) {
   4575 	if (window->shortcutCount + 1 > window->shortcutAllocated) {
   4576 		window->shortcutAllocated = (window->shortcutCount + 1) * 2;
   4577 		window->shortcuts = (UIShortcut *) UI_REALLOC(window->shortcuts, window->shortcutAllocated * sizeof(UIShortcut));
   4578 	}
   4579 
   4580 	window->shortcuts[window->shortcutCount++] = shortcut;
   4581 }
   4582 
   4583 void UIElementSetDisabled(UIElement *element, bool disabled) {
   4584 	if (element->window->focused == element && disabled) {
   4585 		UIElementFocus(&element->window->e);
   4586 	}
   4587 
   4588 	if ((element->flags & UI_ELEMENT_DISABLED) && disabled) return;
   4589 	if ((~element->flags & UI_ELEMENT_DISABLED) && !disabled) return;
   4590 
   4591 	if (disabled) element->flags |= UI_ELEMENT_DISABLED;
   4592 	else element->flags &= ~UI_ELEMENT_DISABLED;
   4593 
   4594 	UIElementMessage(element, UI_MSG_UPDATE, UI_UPDATE_DISABLED, 0);
   4595 }
   4596 
   4597 void UIElementFocus(UIElement *element) {
   4598 	UIElement *previous = element->window->focused;
   4599 	if (previous == element) return;
   4600 	element->window->focused = element;
   4601 	if (previous) UIElementMessage(previous, UI_MSG_UPDATE, UI_UPDATE_FOCUSED, 0);
   4602 	if (element) UIElementMessage(element, UI_MSG_UPDATE, UI_UPDATE_FOCUSED, 0);
   4603 
   4604 #ifdef UI_DEBUG
   4605 	_UIInspectorRefresh();
   4606 #endif
   4607 }
   4608 
   4609 /////////////////////////////////////////
   4610 // Update cycles.
   4611 /////////////////////////////////////////
   4612 
   4613 void UIElementRefresh(UIElement *element) {
   4614 	UIElementRelayout(element);
   4615 	UIElementRepaint(element, NULL);
   4616 }
   4617 
   4618 void UIElementRelayout(UIElement *element) {
   4619 	if (element->flags & UI_ELEMENT_RELAYOUT) {
   4620 		return;
   4621 	}
   4622 
   4623 	element->flags |= UI_ELEMENT_RELAYOUT;
   4624 	UIElement *ancestor = element->parent;
   4625 
   4626 	while (ancestor) {
   4627 		ancestor->flags |= UI_ELEMENT_RELAYOUT_DESCENDENT;
   4628 		ancestor = ancestor->parent;
   4629 	}
   4630 }
   4631 
   4632 void UIElementMeasurementsChanged(UIElement *element, int which) {
   4633 	if (!element->parent) {
   4634 		return; // This is the window element.
   4635 	}
   4636 
   4637 	while (true) {
   4638 		if (element->parent->flags & UI_ELEMENT_DESTROY) return;
   4639 		which &= ~UIElementMessage(element->parent, UI_MSG_GET_CHILD_STABILITY, which, element);
   4640 		if (!which) break;
   4641 		element->flags |= UI_ELEMENT_RELAYOUT;
   4642 		element = element->parent;
   4643 	}
   4644 
   4645 	UIElementRelayout(element);
   4646 }
   4647 
   4648 void UIElementRepaint(UIElement *element, UIRectangle *region) {
   4649 	if (!region) {
   4650 		region = &element->bounds;
   4651 	}
   4652 
   4653 	UIRectangle r = UIRectangleIntersection(*region, element->clip);
   4654 
   4655 	if (!UI_RECT_VALID(r)) {
   4656 		return;
   4657 	}
   4658 
   4659 	if (UI_RECT_VALID(element->window->updateRegion)) {
   4660 		element->window->updateRegion = UIRectangleBounding(element->window->updateRegion, r);
   4661 	} else {
   4662 		element->window->updateRegion = r;
   4663 	}
   4664 }
   4665 
   4666 void UIElementMove(UIElement *element, UIRectangle bounds, bool layout) {
   4667 	UIRectangle clip = element->parent? UIRectangleIntersection(element->parent->clip, bounds) : bounds;
   4668 	bool moved = !UIRectangleEquals(element->bounds, bounds) || !UIRectangleEquals(element->clip, clip);
   4669 
   4670 	if (moved) {
   4671 		layout = true;
   4672 
   4673 		UIElementRepaint(&element->window->e, &element->clip);
   4674 		UIElementRepaint(&element->window->e, &clip);
   4675 
   4676 		element->bounds = bounds;
   4677 		element->clip = clip;
   4678 	}
   4679 
   4680 	if (element->flags & UI_ELEMENT_RELAYOUT) {
   4681 		layout = true;
   4682 	}
   4683 
   4684 	if (layout) {
   4685 		UIElementMessage(element, UI_MSG_LAYOUT, 0, 0);
   4686 	} else if (element->flags & UI_ELEMENT_RELAYOUT_DESCENDENT) {
   4687 		for (uint32_t i = 0; i < element->childCount; i++) {
   4688 			UIElementMove(element->children[i], element->children[i]->bounds, false);
   4689 		}
   4690 	}
   4691 
   4692 	element->flags &= ~(UI_ELEMENT_RELAYOUT_DESCENDENT | UI_ELEMENT_RELAYOUT);
   4693 }
   4694 
   4695 void _UIElementPaint(UIElement *element, UIPainter *painter) {
   4696 	if (element->flags & UI_ELEMENT_HIDE) {
   4697 		return;
   4698 	}
   4699 
   4700 	// Clip painting to the element's clip.
   4701 
   4702 	painter->clip = UIRectangleIntersection(element->clip, painter->clip);
   4703 
   4704 	if (!UI_RECT_VALID(painter->clip)) {
   4705 		return;
   4706 	}
   4707 
   4708 	// Paint the element.
   4709 
   4710 	UIElementMessage(element, UI_MSG_PAINT, 0, painter);
   4711 
   4712 	// Paint its children.
   4713 
   4714 	UIRectangle previousClip = painter->clip;
   4715 
   4716 	for (uintptr_t i = 0; i < element->childCount; i++) {
   4717 		painter->clip = previousClip;
   4718 		_UIElementPaint(element->children[i], painter);
   4719 	}
   4720 
   4721 	// Draw the foreground and border.
   4722 
   4723 	painter->clip = previousClip;
   4724 	UIElementMessage(element, UI_MSG_PAINT_FOREGROUND, 0, painter);
   4725 
   4726 	if (element->flags & UI_ELEMENT_BORDER) {
   4727 		UIDrawBorder(painter, element->bounds, ui.theme.border, UI_RECT_1((int) element->window->scale));
   4728 	}
   4729 }
   4730 
   4731 bool _UIDestroy(UIElement *element) {
   4732 	if (element->flags & UI_ELEMENT_DESTROY_DESCENDENT) {
   4733 		element->flags &= ~UI_ELEMENT_DESTROY_DESCENDENT;
   4734 
   4735 		for (uintptr_t i = 0; i < element->childCount; i++) {
   4736 			if (_UIDestroy(element->children[i])) {
   4737 				UI_MEMMOVE(&element->children[i], &element->children[i + 1], sizeof(UIElement *) * (element->childCount - i - 1));
   4738 				element->childCount--, i--;
   4739 			}
   4740 		}
   4741 	}
   4742 
   4743 	if (element->flags & UI_ELEMENT_DESTROY) {
   4744 		UIElementMessage(element, UI_MSG_DEALLOCATE, 0, 0);
   4745 
   4746 		if (element->window->pressed == element) {
   4747 			_UIWindowSetPressed(element->window, NULL, 0);
   4748 		}
   4749 
   4750 		if (element->window->hovered == element) {
   4751 			element->window->hovered = &element->window->e;
   4752 		}
   4753 
   4754 		if (element->window->focused == element) {
   4755 			element->window->focused = NULL;
   4756 		}
   4757 
   4758 		if (element->window->dialogOldFocus == element) {
   4759 			element->window->dialogOldFocus = NULL;
   4760 		}
   4761 
   4762 		UIElementAnimate(element, true);
   4763 		UI_FREE(element->children);
   4764 		UI_FREE(element);
   4765 		return true;
   4766 	} else {
   4767 		return false;
   4768 	}
   4769 }
   4770 
   4771 void _UIUpdate() {
   4772 	UIWindow *window = ui.windows;
   4773 	UIWindow **link = &ui.windows;
   4774 
   4775 	while (window) {
   4776 		UIWindow *next = window->next;
   4777 
   4778 		UIElementMessage(&window->e, UI_MSG_WINDOW_UPDATE_START, 0, 0);
   4779 		UIElementMessage(&window->e, UI_MSG_WINDOW_UPDATE_BEFORE_DESTROY, 0, 0);
   4780 
   4781 		if (_UIDestroy(&window->e)) {
   4782 			*link = next;
   4783 		} else {
   4784 			link = &window->next;
   4785 
   4786 			UIElementMessage(&window->e, UI_MSG_WINDOW_UPDATE_BEFORE_LAYOUT, 0, 0);
   4787 			UIElementMove(&window->e, window->e.bounds, false);
   4788 			UIElementMessage(&window->e, UI_MSG_WINDOW_UPDATE_BEFORE_PAINT, 0, 0);
   4789 
   4790 			if (UI_RECT_VALID(window->updateRegion)) {
   4791 #ifdef __cplusplus
   4792 				UIPainter painter = {};
   4793 #else
   4794 				UIPainter painter = { 0 };
   4795 #endif
   4796 				painter.bits = window->bits;
   4797 				painter.width = window->width;
   4798 				painter.height = window->height;
   4799 				painter.clip = UIRectangleIntersection(UI_RECT_2S(window->width, window->height), window->updateRegion);
   4800 				_UIElementPaint(&window->e, &painter);
   4801 				_UIWindowEndPaint(window, &painter);
   4802 				window->updateRegion = UI_RECT_1(0);
   4803 
   4804 #ifdef UI_DEBUG
   4805 				window->lastFullFillCount = (float) painter.fillCount / (UI_RECT_WIDTH(window->updateRegion) * UI_RECT_HEIGHT(window->updateRegion));
   4806 #endif
   4807 			}
   4808 
   4809 			UIElementMessage(&window->e, UI_MSG_WINDOW_UPDATE_END, 0, 0);
   4810 		}
   4811 
   4812 		window = next;
   4813 	}
   4814 }
   4815 
   4816 /////////////////////////////////////////
   4817 // Input event handling.
   4818 /////////////////////////////////////////
   4819 
   4820 void _UIWindowSetPressed(UIWindow *window, UIElement *element, int button) {
   4821 	UIElement *previous = window->pressed;
   4822 	window->pressed = element;
   4823 	window->pressedButton = button;
   4824 	if (previous) UIElementMessage(previous, UI_MSG_UPDATE, UI_UPDATE_PRESSED, 0);
   4825 	if (element) UIElementMessage(element, UI_MSG_UPDATE, UI_UPDATE_PRESSED, 0);
   4826 
   4827 	UIElement *ancestor = element;
   4828 	UIElement *child = NULL;
   4829 
   4830 	while (ancestor) {
   4831 		UIElementMessage(ancestor, UI_MSG_PRESSED_DESCENDENT, 0, child);
   4832 		child = ancestor;
   4833 		ancestor = ancestor->parent;
   4834 	}
   4835 }
   4836 
   4837 UIElement *UIElementFindByPoint(UIElement *element, int x, int y) {
   4838 	for (uint32_t i = element->childCount; i > 0; i--) {
   4839 		UIElement *child = element->children[i - 1];
   4840 
   4841 		if ((~child->flags & UI_ELEMENT_HIDE) && UIRectangleContains(child->clip, x, y)) {
   4842 			return UIElementFindByPoint(child, x, y);
   4843 		}
   4844 	}
   4845 
   4846 	return element;
   4847 }
   4848 
   4849 bool UIMenusOpen() {
   4850 	UIWindow *window = ui.windows;
   4851 
   4852 	while (window) {
   4853 		if (window->e.flags & UI_WINDOW_MENU) {
   4854 			return true;
   4855 		}
   4856 
   4857 		window = window->next;
   4858 	}
   4859 
   4860 	return false;
   4861 }
   4862 
   4863 UIElement *_UIElementNextOrPreviousSibling(UIElement *element, bool previous) {
   4864 	if (!element->parent) {
   4865 		return NULL;
   4866 	}
   4867 
   4868 	for (uint32_t i = 0; i < element->parent->childCount; i++) {
   4869 		if (element->parent->children[i] == element) {
   4870 			if (previous) {
   4871 				return i > 0 ? element->parent->children[i - 1] : NULL;
   4872 			} else {
   4873 				return i < element->parent->childCount - 1 ? element->parent->children[i + 1] : NULL;
   4874 			}
   4875 		}
   4876 	}
   4877 
   4878 	UI_ASSERT(false);
   4879 	return NULL;
   4880 }
   4881 
   4882 bool _UIWindowInputEvent(UIWindow *window, UIMessage message, int di, void *dp) {
   4883 	bool handled = true;
   4884 
   4885 	if (window->pressed) {
   4886 		if (message == UI_MSG_MOUSE_MOVE) {
   4887 			UIElementMessage(window->pressed, UI_MSG_MOUSE_DRAG, di, dp);
   4888 		} else if (message == UI_MSG_LEFT_UP && window->pressedButton == 1) {
   4889 			if (window->hovered == window->pressed) {
   4890 				UIElementMessage(window->pressed, UI_MSG_CLICKED, di, dp);
   4891 				if (ui.quit || ui.dialogResult) goto end;
   4892 			}
   4893 
   4894 			if (window->pressed) {
   4895 				UIElementMessage(window->pressed, UI_MSG_LEFT_UP, di, dp);
   4896 				if (ui.quit || ui.dialogResult) goto end;
   4897 				_UIWindowSetPressed(window, NULL, 1);
   4898 			}
   4899 		} else if (message == UI_MSG_MIDDLE_UP && window->pressedButton == 2) {
   4900 			UIElementMessage(window->pressed, UI_MSG_MIDDLE_UP, di, dp);
   4901 			if (ui.quit || ui.dialogResult) goto end;
   4902 			_UIWindowSetPressed(window, NULL, 2);
   4903 		} else if (message == UI_MSG_RIGHT_UP && window->pressedButton == 3) {
   4904 			UIElementMessage(window->pressed, UI_MSG_RIGHT_UP, di, dp);
   4905 			if (ui.quit || ui.dialogResult) goto end;
   4906 			_UIWindowSetPressed(window, NULL, 3);
   4907 		}
   4908 	}
   4909 
   4910 	if (window->pressed) {
   4911 		bool inside = UIRectangleContains(window->pressed->clip, window->cursorX, window->cursorY);
   4912 
   4913 		if (inside && window->hovered == &window->e) {
   4914 			window->hovered = window->pressed;
   4915 			UIElementMessage(window->pressed, UI_MSG_UPDATE, UI_UPDATE_HOVERED, 0);
   4916 		} else if (!inside && window->hovered == window->pressed) {
   4917 			window->hovered = &window->e;
   4918 			UIElementMessage(window->pressed, UI_MSG_UPDATE, UI_UPDATE_HOVERED, 0);
   4919 		}
   4920 
   4921 		if (ui.quit || ui.dialogResult) goto end;
   4922 	}
   4923 
   4924 	if (!window->pressed) {
   4925 		UIElement *hovered = UIElementFindByPoint(&window->e, window->cursorX, window->cursorY);
   4926 
   4927 		if (message == UI_MSG_MOUSE_MOVE) {
   4928 			UIElementMessage(hovered, UI_MSG_MOUSE_MOVE, di, dp);
   4929 
   4930 			int cursor = UIElementMessage(window->hovered, UI_MSG_GET_CURSOR, di, dp);
   4931 
   4932 			if (cursor != window->cursorStyle) {
   4933 				window->cursorStyle = cursor;
   4934 				_UIWindowSetCursor(window, cursor);
   4935 			}
   4936 		} else if (message == UI_MSG_LEFT_DOWN) {
   4937 			if ((window->e.flags & UI_WINDOW_MENU) || !_UIMenusClose()) {
   4938 				_UIWindowSetPressed(window, hovered, 1);
   4939 				UIElementMessage(hovered, UI_MSG_LEFT_DOWN, di, dp);
   4940 			}
   4941 		} else if (message == UI_MSG_MIDDLE_DOWN) {
   4942 			if ((window->e.flags & UI_WINDOW_MENU) || !_UIMenusClose()) {
   4943 				_UIWindowSetPressed(window, hovered, 2);
   4944 				UIElementMessage(hovered, UI_MSG_MIDDLE_DOWN, di, dp);
   4945 			}
   4946 		} else if (message == UI_MSG_RIGHT_DOWN) {
   4947 			if ((window->e.flags & UI_WINDOW_MENU) || !_UIMenusClose()) {
   4948 				_UIWindowSetPressed(window, hovered, 3);
   4949 				UIElementMessage(hovered, UI_MSG_RIGHT_DOWN, di, dp);
   4950 			}
   4951 		} else if (message == UI_MSG_MOUSE_WHEEL) {
   4952 			UIElement *element = hovered;
   4953 
   4954 			while (element) {
   4955 				if (UIElementMessage(element, UI_MSG_MOUSE_WHEEL, di, dp)) {
   4956 					break;
   4957 				}
   4958 
   4959 				element = element->parent;
   4960 			}
   4961 		} else if (message == UI_MSG_KEY_TYPED || message == UI_MSG_KEY_RELEASED) {
   4962 			handled = false;
   4963 
   4964 			if (window->focused) {
   4965 				UIElement *element = window->focused;
   4966 
   4967 				while (element) {
   4968 					if (UIElementMessage(element, message, di, dp)) {
   4969 						handled = true;
   4970 						break;
   4971 					}
   4972 
   4973 					element = element->parent;
   4974 				}
   4975 			} else {
   4976 				if (UIElementMessage(&window->e, message, di, dp)) {
   4977 					handled = true;
   4978 				}
   4979 			}
   4980 
   4981 			if (!handled && !UIMenusOpen() && message == UI_MSG_KEY_TYPED) {
   4982 				UIKeyTyped *m = (UIKeyTyped *) dp;
   4983 
   4984 				if (m->code == UI_KEYCODE_TAB && !window->ctrl && !window->alt) {
   4985 					UIElement *start = window->focused ? window->focused : &window->e;
   4986 					UIElement *element = start;
   4987 
   4988 					do {
   4989 						if (element->childCount && !(element->flags & (UI_ELEMENT_HIDE | UI_ELEMENT_DISABLED))) {
   4990 							element = window->shift ? element->children[element->childCount - 1] : element->children[0];
   4991 							continue;
   4992 						}
   4993 
   4994 						while (element) {
   4995 							UIElement *sibling = _UIElementNextOrPreviousSibling(element, window->shift);
   4996 							if (sibling) { element = sibling; break; }
   4997 							element = element->parent;
   4998 						}
   4999 
   5000 						if (!element) {
   5001 							element = &window->e;
   5002 						}
   5003 					} while (element != start && ((~element->flags & UI_ELEMENT_TAB_STOP)
   5004 						|| (element->flags & (UI_ELEMENT_HIDE | UI_ELEMENT_DISABLED))));
   5005 
   5006 					if (~element->flags & UI_ELEMENT_WINDOW) {
   5007 						UIElementFocus(element);
   5008 					}
   5009 
   5010 					handled = true;
   5011 				} else if (!window->dialog) {
   5012 					for (intptr_t i = window->shortcutCount - 1; i >= 0; i--) {
   5013 						UIShortcut *shortcut = window->shortcuts + i;
   5014 
   5015 						if (shortcut->code == m->code && shortcut->ctrl == window->ctrl
   5016 								&& shortcut->shift == window->shift && shortcut->alt == window->alt) {
   5017 							shortcut->invoke(shortcut->cp);
   5018 							handled = true;
   5019 							break;
   5020 						}
   5021 					}
   5022 				} else if (window->dialog) {
   5023 					UIElementMessage(window->dialog, message, di, dp);
   5024 				}
   5025 			}
   5026 		}
   5027 
   5028 		if (ui.quit || ui.dialogResult) goto end;
   5029 
   5030 		if (hovered != window->hovered) {
   5031 			UIElement *previous = window->hovered;
   5032 			window->hovered = hovered;
   5033 			UIElementMessage(previous, UI_MSG_UPDATE, UI_UPDATE_HOVERED, 0);
   5034 			UIElementMessage(window->hovered, UI_MSG_UPDATE, UI_UPDATE_HOVERED, 0);
   5035 		}
   5036 	}
   5037 
   5038 	end: _UIUpdate();
   5039 	return handled;
   5040 }
   5041 
   5042 /////////////////////////////////////////
   5043 // Font handling.
   5044 /////////////////////////////////////////
   5045 
   5046 // Taken from https://commons.wikimedia.org/wiki/File:Codepage-437.png
   5047 // Public domain.
   5048 
   5049 const uint64_t _uiFont[] = {
   5050 	0x0000000000000000UL, 0x0000000000000000UL, 0xBD8181A5817E0000UL, 0x000000007E818199UL, 0xC3FFFFDBFF7E0000UL, 0x000000007EFFFFE7UL, 0x7F7F7F3600000000UL, 0x00000000081C3E7FUL,
   5051 	0x7F3E1C0800000000UL, 0x0000000000081C3EUL, 0xE7E73C3C18000000UL, 0x000000003C1818E7UL, 0xFFFF7E3C18000000UL, 0x000000003C18187EUL, 0x3C18000000000000UL, 0x000000000000183CUL,
   5052 	0xC3E7FFFFFFFFFFFFUL, 0xFFFFFFFFFFFFE7C3UL, 0x42663C0000000000UL, 0x00000000003C6642UL, 0xBD99C3FFFFFFFFFFUL, 0xFFFFFFFFFFC399BDUL, 0x331E4C5870780000UL, 0x000000001E333333UL,
   5053 	0x3C666666663C0000UL, 0x0000000018187E18UL, 0x0C0C0CFCCCFC0000UL, 0x00000000070F0E0CUL, 0xC6C6C6FEC6FE0000UL, 0x0000000367E7E6C6UL, 0xE73CDB1818000000UL, 0x000000001818DB3CUL,
   5054 	0x1F7F1F0F07030100UL, 0x000000000103070FUL, 0x7C7F7C7870604000UL, 0x0000000040607078UL, 0x1818187E3C180000UL, 0x0000000000183C7EUL, 0x6666666666660000UL, 0x0000000066660066UL,
   5055 	0xD8DEDBDBDBFE0000UL, 0x00000000D8D8D8D8UL, 0x6363361C06633E00UL, 0x0000003E63301C36UL, 0x0000000000000000UL, 0x000000007F7F7F7FUL, 0x1818187E3C180000UL, 0x000000007E183C7EUL,
   5056 	0x1818187E3C180000UL, 0x0000000018181818UL, 0x1818181818180000UL, 0x00000000183C7E18UL, 0x7F30180000000000UL, 0x0000000000001830UL, 0x7F060C0000000000UL, 0x0000000000000C06UL,
   5057 	0x0303000000000000UL, 0x0000000000007F03UL, 0xFF66240000000000UL, 0x0000000000002466UL, 0x3E1C1C0800000000UL, 0x00000000007F7F3EUL, 0x3E3E7F7F00000000UL, 0x0000000000081C1CUL,
   5058 	0x0000000000000000UL, 0x0000000000000000UL, 0x18183C3C3C180000UL, 0x0000000018180018UL, 0x0000002466666600UL, 0x0000000000000000UL, 0x36367F3636000000UL, 0x0000000036367F36UL,
   5059 	0x603E0343633E1818UL, 0x000018183E636160UL, 0x1830634300000000UL, 0x000000006163060CUL, 0x3B6E1C36361C0000UL, 0x000000006E333333UL, 0x000000060C0C0C00UL, 0x0000000000000000UL,
   5060 	0x0C0C0C0C18300000UL, 0x0000000030180C0CUL, 0x30303030180C0000UL, 0x000000000C183030UL, 0xFF3C660000000000UL, 0x000000000000663CUL, 0x7E18180000000000UL, 0x0000000000001818UL,
   5061 	0x0000000000000000UL, 0x0000000C18181800UL, 0x7F00000000000000UL, 0x0000000000000000UL, 0x0000000000000000UL, 0x0000000018180000UL, 0x1830604000000000UL, 0x000000000103060CUL,
   5062 	0xDBDBC3C3663C0000UL, 0x000000003C66C3C3UL, 0x1818181E1C180000UL, 0x000000007E181818UL, 0x0C183060633E0000UL, 0x000000007F630306UL, 0x603C6060633E0000UL, 0x000000003E636060UL,
   5063 	0x7F33363C38300000UL, 0x0000000078303030UL, 0x603F0303037F0000UL, 0x000000003E636060UL, 0x633F0303061C0000UL, 0x000000003E636363UL, 0x18306060637F0000UL, 0x000000000C0C0C0CUL,
   5064 	0x633E6363633E0000UL, 0x000000003E636363UL, 0x607E6363633E0000UL, 0x000000001E306060UL, 0x0000181800000000UL, 0x0000000000181800UL, 0x0000181800000000UL, 0x000000000C181800UL,
   5065 	0x060C183060000000UL, 0x000000006030180CUL, 0x00007E0000000000UL, 0x000000000000007EUL, 0x6030180C06000000UL, 0x00000000060C1830UL, 0x18183063633E0000UL, 0x0000000018180018UL,
   5066 	0x7B7B63633E000000UL, 0x000000003E033B7BUL, 0x7F6363361C080000UL, 0x0000000063636363UL, 0x663E6666663F0000UL, 0x000000003F666666UL, 0x03030343663C0000UL, 0x000000003C664303UL,
   5067 	0x66666666361F0000UL, 0x000000001F366666UL, 0x161E1646667F0000UL, 0x000000007F664606UL, 0x161E1646667F0000UL, 0x000000000F060606UL, 0x7B030343663C0000UL, 0x000000005C666363UL,
   5068 	0x637F636363630000UL, 0x0000000063636363UL, 0x18181818183C0000UL, 0x000000003C181818UL, 0x3030303030780000UL, 0x000000001E333333UL, 0x1E1E366666670000UL, 0x0000000067666636UL,
   5069 	0x06060606060F0000UL, 0x000000007F664606UL, 0xC3DBFFFFE7C30000UL, 0x00000000C3C3C3C3UL, 0x737B7F6F67630000UL, 0x0000000063636363UL, 0x63636363633E0000UL, 0x000000003E636363UL,
   5070 	0x063E6666663F0000UL, 0x000000000F060606UL, 0x63636363633E0000UL, 0x000070303E7B6B63UL, 0x363E6666663F0000UL, 0x0000000067666666UL, 0x301C0663633E0000UL, 0x000000003E636360UL,
   5071 	0x18181899DBFF0000UL, 0x000000003C181818UL, 0x6363636363630000UL, 0x000000003E636363UL, 0xC3C3C3C3C3C30000UL, 0x00000000183C66C3UL, 0xDBC3C3C3C3C30000UL, 0x000000006666FFDBUL,
   5072 	0x18183C66C3C30000UL, 0x00000000C3C3663CUL, 0x183C66C3C3C30000UL, 0x000000003C181818UL, 0x0C183061C3FF0000UL, 0x00000000FFC38306UL, 0x0C0C0C0C0C3C0000UL, 0x000000003C0C0C0CUL,
   5073 	0x1C0E070301000000UL, 0x0000000040607038UL, 0x30303030303C0000UL, 0x000000003C303030UL, 0x0000000063361C08UL, 0x0000000000000000UL, 0x0000000000000000UL, 0x0000FF0000000000UL,
   5074 	0x0000000000180C0CUL, 0x0000000000000000UL, 0x3E301E0000000000UL, 0x000000006E333333UL, 0x66361E0606070000UL, 0x000000003E666666UL, 0x03633E0000000000UL, 0x000000003E630303UL,
   5075 	0x33363C3030380000UL, 0x000000006E333333UL, 0x7F633E0000000000UL, 0x000000003E630303UL, 0x060F0626361C0000UL, 0x000000000F060606UL, 0x33336E0000000000UL, 0x001E33303E333333UL,
   5076 	0x666E360606070000UL, 0x0000000067666666UL, 0x18181C0018180000UL, 0x000000003C181818UL, 0x6060700060600000UL, 0x003C666660606060UL, 0x1E36660606070000UL, 0x000000006766361EUL,
   5077 	0x18181818181C0000UL, 0x000000003C181818UL, 0xDBFF670000000000UL, 0x00000000DBDBDBDBUL, 0x66663B0000000000UL, 0x0000000066666666UL, 0x63633E0000000000UL, 0x000000003E636363UL,
   5078 	0x66663B0000000000UL, 0x000F06063E666666UL, 0x33336E0000000000UL, 0x007830303E333333UL, 0x666E3B0000000000UL, 0x000000000F060606UL, 0x06633E0000000000UL, 0x000000003E63301CUL,
   5079 	0x0C0C3F0C0C080000UL, 0x00000000386C0C0CUL, 0x3333330000000000UL, 0x000000006E333333UL, 0xC3C3C30000000000UL, 0x00000000183C66C3UL, 0xC3C3C30000000000UL, 0x0000000066FFDBDBUL,
   5080 	0x3C66C30000000000UL, 0x00000000C3663C18UL, 0x6363630000000000UL, 0x001F30607E636363UL, 0x18337F0000000000UL, 0x000000007F63060CUL, 0x180E181818700000UL, 0x0000000070181818UL,
   5081 	0x1800181818180000UL, 0x0000000018181818UL, 0x18701818180E0000UL, 0x000000000E181818UL, 0x000000003B6E0000UL, 0x0000000000000000UL, 0x63361C0800000000UL, 0x00000000007F6363UL,
   5082 };
   5083 
   5084 void UIDrawGlyph(UIPainter *painter, int x0, int y0, int c, uint32_t color) {
   5085 #ifdef UI_FREETYPE
   5086 	UIFont *font = ui.activeFont;
   5087 
   5088 	if (font->isFreeType) {
   5089 #ifdef UI_UNICODE
   5090 		if (c < 0) c = '?';
   5091 #else
   5092 		if (c < 0 || c > 127) c = '?';
   5093 #endif
   5094 		if (c == '\r') c = ' ';
   5095 
   5096 		if (!font->glyphsRendered[c]) {
   5097 			FT_Load_Char(font->font, c == 24 ? 0x2191 : c == 25 ? 0x2193 : c == 26 ? 0x2192 : c == 27 ? 0x2190 : c, FT_LOAD_DEFAULT);
   5098 #ifdef UI_FREETYPE_SUBPIXEL
   5099 			FT_Render_Glyph(font->font->glyph, FT_RENDER_MODE_LCD);
   5100 #else
   5101 			FT_Render_Glyph(font->font->glyph, FT_RENDER_MODE_NORMAL);
   5102 #endif
   5103 			FT_Bitmap_Copy(ui.ft, &font->font->glyph->bitmap, &font->glyphs[c]);
   5104 			font->glyphOffsetsX[c] = font->font->glyph->bitmap_left;
   5105 			font->glyphOffsetsY[c] = font->font->size->metrics.ascender / 64 - font->font->glyph->bitmap_top;
   5106 			font->glyphsRendered[c] = true;
   5107 		}
   5108 
   5109 		FT_Bitmap *bitmap = &font->glyphs[c];
   5110 		x0 += font->glyphOffsetsX[c], y0 += font->glyphOffsetsY[c];
   5111 
   5112 		for (int y = 0; y < (int) bitmap->rows; y++) {
   5113 			if (y0 + y < painter->clip.t) continue;
   5114 			if (y0 + y >= painter->clip.b) break;
   5115 
   5116 			int width = bitmap->pixel_mode == FT_PIXEL_MODE_LCD ? bitmap->width / 3 : bitmap->width;
   5117 
   5118 			for (int x = 0; x < width; x++) {
   5119 				if (x0 + x < painter->clip.l) continue;
   5120 				if (x0 + x >= painter->clip.r) break;
   5121 
   5122 				uint32_t *destination = painter->bits + (x0 + x) + (y0 + y) * painter->width;
   5123 				uint32_t original = *destination, ra, ga, ba;
   5124 
   5125 				if (bitmap->pixel_mode == FT_PIXEL_MODE_LCD) {
   5126 					ra = ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 0];
   5127 					ga = ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 1];
   5128 					ba = ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 2];
   5129 					ra += (ga - ra) / 2, ba += (ga - ba) / 2; // TODO Gamma correct blending!
   5130 				} else if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) {
   5131 					ra = (((uint8_t *) bitmap->buffer)[(x >> 3) + y * bitmap->pitch] & (0x80 >> (x & 7))) ? 0xFF : 0;
   5132 					ga = ra, ba = ra;
   5133 				} else if (bitmap->pixel_mode == FT_PIXEL_MODE_GRAY) {
   5134 					ra = ((uint8_t *) bitmap->buffer)[x + y * bitmap->pitch];
   5135 					ga = ra, ba = ra;
   5136 				} else {
   5137 					ra = ga = ba = 0;
   5138 				}
   5139 
   5140 				uint32_t r2 = (255 - ra) * ((original & 0x000000FF) >> 0);
   5141 				uint32_t g2 = (255 - ga) * ((original & 0x0000FF00) >> 8);
   5142 				uint32_t b2 = (255 - ba) * ((original & 0x00FF0000) >> 16);
   5143 				uint32_t r1 = ra * ((color & 0x000000FF) >> 0);
   5144 				uint32_t g1 = ga * ((color & 0x0000FF00) >> 8);
   5145 				uint32_t b1 = ba * ((color & 0x00FF0000) >> 16);
   5146 
   5147 				uint32_t result = 0xFF000000 | (0x00FF0000 & ((b1 + b2) << 8))
   5148 					| (0x0000FF00 & ((g1 + g2) << 0))
   5149 					| (0x000000FF & ((r1 + r2) >> 8));
   5150 				*destination = result;
   5151 			}
   5152 		}
   5153 
   5154 		return;
   5155 	}
   5156 #endif
   5157 
   5158 	if (c < 0 || c > 127) c = '?';
   5159 
   5160 	UIRectangle rectangle = UIRectangleIntersection(painter->clip, UI_RECT_4(x0, x0 + 8, y0, y0 + 16));
   5161 
   5162 	const uint8_t *data = (const uint8_t *) _uiFont + c * 16;
   5163 
   5164 	for (int i = rectangle.t; i < rectangle.b; i++) {
   5165 		uint32_t *bits = painter->bits + i * painter->width + rectangle.l;
   5166 		uint8_t byte = data[i - y0];
   5167 
   5168 		for (int j = rectangle.l; j < rectangle.r; j++) {
   5169 			if (byte & (1 << (j - x0))) {
   5170 				*bits = color;
   5171 			}
   5172 
   5173 			bits++;
   5174 		}
   5175 	}
   5176 }
   5177 
   5178 UIFont *UIFontCreate(const char *cPath, uint32_t size) {
   5179 	UIFont *font = (UIFont *) UI_CALLOC(sizeof(UIFont));
   5180 
   5181 #ifdef UI_FREETYPE
   5182 #ifdef UI_UNICODE
   5183 	font->glyphs = (FT_Bitmap *) UI_CALLOC(sizeof(FT_Bitmap) * (_UNICODE_MAX_CODEPOINT + 1));
   5184 	font->glyphsRendered = (bool *) UI_CALLOC(sizeof(bool) * (_UNICODE_MAX_CODEPOINT + 1));
   5185 	font->glyphOffsetsX = (int *) UI_CALLOC(sizeof(int) * (_UNICODE_MAX_CODEPOINT + 1));
   5186 	font->glyphOffsetsY = (int *) UI_CALLOC(sizeof(int) * (_UNICODE_MAX_CODEPOINT + 1));
   5187 #endif
   5188 	if (cPath) {
   5189 		int ret = FT_New_Face(ui.ft, cPath, 0, &font->font);
   5190 		if (ret == 0) {
   5191 			FT_Select_Charmap(font->font, FT_ENCODING_UNICODE);
   5192 			if (FT_HAS_FIXED_SIZES(font->font) && font->font->num_fixed_sizes) {
   5193 				// Look for the smallest strike that's at least `size`.
   5194 				int j = 0;
   5195 
   5196 				for (int i = 0; i < font->font->num_fixed_sizes; i++) {
   5197 					if ((uint32_t) font->font->available_sizes[i].height >= size
   5198 							&& font->font->available_sizes[i].y_ppem < font->font->available_sizes[j].y_ppem) {
   5199 						j = i;
   5200 					}
   5201 				}
   5202 
   5203 				FT_Set_Pixel_Sizes(font->font, font->font->available_sizes[j].x_ppem / 64, font->font->available_sizes[j].y_ppem / 64);
   5204 			} else {
   5205 				FT_Set_Char_Size(font->font, 0, size * 64, 100, 100);
   5206 			}
   5207 
   5208 			FT_Load_Char(font->font, 'a', FT_LOAD_DEFAULT);
   5209 			font->glyphWidth = font->font->glyph->advance.x / 64;
   5210 			font->glyphHeight = (font->font->size->metrics.ascender - font->font->size->metrics.descender) / 64;
   5211 			font->isFreeType = true;
   5212 			return font;
   5213 		} else
   5214 			printf("Cannot load font %s : %d\n", cPath, ret);
   5215 	}
   5216 #endif
   5217 
   5218 	font->glyphWidth = 9;
   5219 	font->glyphHeight = 16;
   5220 	return font;
   5221 }
   5222 
   5223 UIFont *UIFontActivate(UIFont *font) {
   5224 	UIFont *previous = ui.activeFont;
   5225 	ui.activeFont = font;
   5226 	return previous;
   5227 }
   5228 
   5229 /////////////////////////////////////////
   5230 // Debugging.
   5231 /////////////////////////////////////////
   5232 
   5233 #ifdef UI_DEBUG
   5234 
   5235 void UIInspectorLog(const char *cFormat, ...) {
   5236 	va_list arguments;
   5237 	va_start(arguments, cFormat);
   5238 	char buffer[4096];
   5239 	vsnprintf(buffer, sizeof(buffer), cFormat, arguments);
   5240 	UICodeInsertContent(ui.inspectorLog, buffer, -1, false);
   5241 	va_end(arguments);
   5242 	UIElementRefresh(&ui.inspectorLog->e);
   5243 }
   5244 
   5245 UIElement *_UIInspectorFindNthElement(UIElement *element, int *index, int *depth) {
   5246 	if (*index == 0) {
   5247 		return element;
   5248 	}
   5249 
   5250 	*index = *index - 1;
   5251 
   5252 	for (uint32_t i = 0; i < element->childCount; i++) {
   5253 		UIElement *child = element->children[i];
   5254 
   5255 		if (!(child->flags & (UI_ELEMENT_DESTROY | UI_ELEMENT_HIDE))) {
   5256 			UIElement *result = _UIInspectorFindNthElement(child, index, depth);
   5257 
   5258 			if (result) {
   5259 				if (depth) {
   5260 					*depth = *depth + 1;
   5261 				}
   5262 
   5263 				return result;
   5264 			}
   5265 		}
   5266 	}
   5267 
   5268 	return NULL;
   5269 }
   5270 
   5271 int _UIInspectorTableMessage(UIElement *element, UIMessage message, int di, void *dp) {
   5272 	if (!ui.inspectorTarget) {
   5273 		return 0;
   5274 	}
   5275 
   5276 	if (message == UI_MSG_TABLE_GET_ITEM) {
   5277 		UITableGetItem *m = (UITableGetItem *) dp;
   5278 		int index = m->index;
   5279 		int depth = 0;
   5280 		UIElement *element = _UIInspectorFindNthElement(&ui.inspectorTarget->e, &index, &depth);
   5281 		if (!element) return 0;
   5282 
   5283 		if (m->column == 0) {
   5284 			return snprintf(m->buffer, m->bufferBytes, "%.*s%s", depth * 2, "                ", element->cClassName);
   5285 		} else if (m->column == 1) {
   5286 			return snprintf(m->buffer, m->bufferBytes, "%d:%d, %d:%d", UI_RECT_ALL(element->bounds));
   5287 		} else if (m->column == 2) {
   5288 			return snprintf(m->buffer, m->bufferBytes, "%d%c", element->id, element->window->focused == element ? '*' : ' ');
   5289 		}
   5290 	} else if (message == UI_MSG_MOUSE_MOVE) {
   5291 		int index = UITableHitTest(ui.inspectorTable, element->window->cursorX, element->window->cursorY);
   5292 		UIElement *element = NULL;
   5293 		if (index >= 0) element = _UIInspectorFindNthElement(&ui.inspectorTarget->e, &index, NULL);
   5294 		UIWindow *window = ui.inspectorTarget;
   5295 		UIPainter painter = { 0 };
   5296 		window->updateRegion = window->e.bounds;
   5297 		painter.bits = window->bits;
   5298 		painter.width = window->width;
   5299 		painter.height = window->height;
   5300 		painter.clip = UI_RECT_2S(window->width, window->height);
   5301 
   5302 		for (int i = 0; i < window->width * window->height; i++) {
   5303 			window->bits[i] = 0xFF00FF;
   5304 		}
   5305 
   5306 		_UIElementPaint(&window->e, &painter);
   5307 		painter.clip = UI_RECT_2S(window->width, window->height);
   5308 
   5309 		if (element) {
   5310 			UIDrawInvert(&painter, element->bounds);
   5311 			UIDrawInvert(&painter, UIRectangleAdd(element->bounds, UI_RECT_1I(4)));
   5312 		}
   5313 
   5314 		_UIWindowEndPaint(window, &painter);
   5315 	}
   5316 
   5317 	return 0;
   5318 }
   5319 
   5320 void _UIInspectorCreate() {
   5321 	ui.inspector = UIWindowCreate(0, UI_WINDOW_INSPECTOR, "Inspector", 0, 0);
   5322 	UISplitPane *splitPane = UISplitPaneCreate(&ui.inspector->e, 0, 0.5f);
   5323 	ui.inspectorTable = UITableCreate(&splitPane->e, 0, "Class\tBounds\tID");
   5324 	ui.inspectorTable->e.messageUser = _UIInspectorTableMessage;
   5325 	ui.inspectorLog = UICodeCreate(&splitPane->e, 0);
   5326 }
   5327 
   5328 int _UIInspectorCountElements(UIElement *element) {
   5329 	int count = 1;
   5330 
   5331 	for (uint32_t i = 0; i < element->childCount; i++) {
   5332 		UIElement *child = element->children[i];
   5333 
   5334 		if (!(child->flags & (UI_ELEMENT_DESTROY | UI_ELEMENT_HIDE))) {
   5335 			count += _UIInspectorCountElements(child);
   5336 		}
   5337 	}
   5338 
   5339 	return count;
   5340 }
   5341 
   5342 void _UIInspectorRefresh() {
   5343 	if (!ui.inspectorTarget || !ui.inspector || !ui.inspectorTable) return;
   5344 	ui.inspectorTable->itemCount = _UIInspectorCountElements(&ui.inspectorTarget->e);
   5345 	UITableResizeColumns(ui.inspectorTable);
   5346 	UIElementRefresh(&ui.inspectorTable->e);
   5347 }
   5348 
   5349 void _UIInspectorSetFocusedWindow(UIWindow *window) {
   5350 	if (!ui.inspector || !ui.inspectorTable) return;
   5351 
   5352 	if (window->e.flags & UI_WINDOW_INSPECTOR) {
   5353 		return;
   5354 	}
   5355 
   5356 	if (ui.inspectorTarget != window) {
   5357 		ui.inspectorTarget = window;
   5358 		_UIInspectorRefresh();
   5359 	}
   5360 }
   5361 
   5362 #else
   5363 
   5364 void _UIInspectorCreate() {}
   5365 void _UIInspectorSetFocusedWindow(UIWindow *window) {}
   5366 void _UIInspectorRefresh() {}
   5367 
   5368 #endif
   5369 
   5370 /////////////////////////////////////////
   5371 // Automation for tests.
   5372 /////////////////////////////////////////
   5373 
   5374 #ifdef UI_AUTOMATION_TESTS
   5375 
   5376 int UIAutomationRunTests();
   5377 
   5378 void UIAutomationProcessMessage() {
   5379 	int result;
   5380 	_UIMessageLoopSingle(&result);
   5381 }
   5382 
   5383 void UIAutomationKeyboardTypeSingle(intptr_t code, bool ctrl, bool shift, bool alt) {
   5384 	UIWindow *window = ui.windows; // TODO Get the focused window.
   5385 	UIKeyTyped m = { 0 };
   5386 	m.code = code;
   5387 	window->ctrl = ctrl;
   5388 	window->alt = alt;
   5389 	window->shift = shift;
   5390 	_UIWindowInputEvent(window, UI_MSG_KEY_TYPED, 0, &m);
   5391 	window->ctrl = false;
   5392 	window->alt = false;
   5393 	window->shift = false;
   5394 }
   5395 
   5396 void UIAutomationKeyboardType(const char *string) {
   5397 	UIWindow *window = ui.windows; // TODO Get the focused window.
   5398 
   5399 	UIKeyTyped m = { 0 };
   5400 	char c[2];
   5401 	m.text = c;
   5402 	m.textBytes = 1;
   5403 	c[1] = 0;
   5404 
   5405 	for (int i = 0; string[i]; i++) {
   5406 		window->ctrl = false;
   5407 		window->alt = false;
   5408 		window->shift = (c[0] >= 'A' && c[0] <= 'Z');
   5409 		c[0] = string[i];
   5410 		m.code = (c[0] >= 'A' && c[0] <= 'Z') ? UI_KEYCODE_LETTER(c[0])
   5411 			: c[0] == '\n' ? UI_KEYCODE_ENTER
   5412 			: c[0] == '\t' ? UI_KEYCODE_TAB
   5413 			: c[0] == ' ' ? UI_KEYCODE_SPACE
   5414 			: (c[0] >= '0' && c[0] <= '9') ? UI_KEYCODE_DIGIT(c[0]) : 0;
   5415 		_UIWindowInputEvent(window, UI_MSG_KEY_TYPED, 0, &m);
   5416 	}
   5417 
   5418 	window->ctrl = false;
   5419 	window->alt = false;
   5420 	window->shift = false;
   5421 }
   5422 
   5423 bool UIAutomationCheckCodeLineMatches(UICode *code, int lineIndex, const char *input) {
   5424 	if (lineIndex < 1 || lineIndex > code->lineCount) return false;
   5425 	int bytes = 0;
   5426 	for (int i = 0; input[i]; i++) bytes++;
   5427 	if (bytes != code->lines[lineIndex - 1].bytes) return false;
   5428 	for (int i = 0; input[i]; i++) if (code->content[code->lines[lineIndex - 1].offset + i] != input[i]) return false;
   5429 	return true;
   5430 }
   5431 
   5432 bool UIAutomationCheckTableItemMatches(UITable *table, int row, int column, const char *input) {
   5433 	int bytes = 0;
   5434 	for (int i = 0; input[i]; i++) bytes++;
   5435 	if (row < 0 || row >= table->itemCount) return false;
   5436 	if (column < 0 || column >= table->columnCount) return false;
   5437 	char *buffer = (char *) UI_MALLOC(bytes + 1);
   5438 	UITableGetItem m = { 0 };
   5439 	m.buffer = buffer;
   5440 	m.bufferBytes = bytes + 1;
   5441 	m.column = column;
   5442 	m.index = row;
   5443 	int length = UIElementMessage(&table->e, UI_MSG_TABLE_GET_ITEM, 0, &m);
   5444 	if (length != bytes) return false;
   5445 	for (int i = 0; input[i]; i++) if (buffer[i] != input[i]) return false;
   5446 	return true;
   5447 }
   5448 
   5449 #endif
   5450 
   5451 /////////////////////////////////////////
   5452 // Common platform layer functionality.
   5453 /////////////////////////////////////////
   5454 
   5455 void _UIWindowDestroyCommon(UIWindow *window) {
   5456 	UI_FREE(window->bits);
   5457 	UI_FREE(window->shortcuts);
   5458 }
   5459 
   5460 void _UIInitialiseCommon() {
   5461 	ui.theme = uiThemeClassic;
   5462 
   5463 #ifdef UI_FREETYPE
   5464 	FT_Init_FreeType(&ui.ft);
   5465 #else
   5466 	UIFontActivate(UIFontCreate(0, 0));
   5467 #endif
   5468 }
   5469 
   5470 void _UIWindowAdd(UIWindow *window) {
   5471 	window->scale = 1.0f;
   5472 	window->e.window = window;
   5473 	window->hovered = &window->e;
   5474 	window->next = ui.windows;
   5475 	ui.windows = window;
   5476 }
   5477 
   5478 int _UIWindowMessageCommon(UIElement *element, UIMessage message, int di, void *dp) {
   5479 	if (message == UI_MSG_LAYOUT && element->childCount) {
   5480 		UIElementMove(element->children[0], element->bounds, false);
   5481 		if (element->window->dialog) UIElementMove(element->window->dialog, element->bounds, false);
   5482 		UIElementRepaint(element, NULL);
   5483 	} else if (message == UI_MSG_GET_CHILD_STABILITY) {
   5484 		return 3; // Both width and height of the child element are ignored.
   5485 	}
   5486 
   5487 	return 0;
   5488 }
   5489 
   5490 int UIMessageLoop() {
   5491 	_UIInspectorCreate();
   5492 	_UIUpdate();
   5493 #ifdef UI_AUTOMATION_TESTS
   5494 	return UIAutomationRunTests();
   5495 #else
   5496 	int result = 0;
   5497 	while (!ui.quit && _UIMessageLoopSingle(&result)) ui.dialogResult = NULL;
   5498 	return result;
   5499 #endif
   5500 }
   5501 
   5502 /////////////////////////////////////////
   5503 // Platform layers.
   5504 /////////////////////////////////////////
   5505 
   5506 #ifdef UI_LINUX
   5507 
   5508 const int UI_KEYCODE_A = XK_a;
   5509 const int UI_KEYCODE_BACKSPACE = XK_BackSpace;
   5510 const int UI_KEYCODE_DELETE = XK_Delete;
   5511 const int UI_KEYCODE_DOWN = XK_Down;
   5512 const int UI_KEYCODE_END = XK_End;
   5513 const int UI_KEYCODE_ENTER = XK_Return;
   5514 const int UI_KEYCODE_ESCAPE = XK_Escape;
   5515 const int UI_KEYCODE_F1 = XK_F1;
   5516 const int UI_KEYCODE_HOME = XK_Home;
   5517 const int UI_KEYCODE_LEFT = XK_Left;
   5518 const int UI_KEYCODE_RIGHT = XK_Right;
   5519 const int UI_KEYCODE_SPACE = XK_space;
   5520 const int UI_KEYCODE_TAB = XK_Tab;
   5521 const int UI_KEYCODE_UP = XK_Up;
   5522 const int UI_KEYCODE_INSERT = XK_Insert;
   5523 const int UI_KEYCODE_0 = XK_0;
   5524 const int UI_KEYCODE_BACKTICK = XK_grave;
   5525 const int UI_KEYCODE_PAGE_DOWN = XK_Page_Down;
   5526 const int UI_KEYCODE_PAGE_UP = XK_Page_Up;
   5527 
   5528 int _UIWindowMessage(UIElement *element, UIMessage message, int di, void *dp) {
   5529 	if (message == UI_MSG_DEALLOCATE) {
   5530 		UIWindow *window = (UIWindow *) element;
   5531 		_UIWindowDestroyCommon(window);
   5532 		window->image->data = NULL;
   5533 		XDestroyImage(window->image);
   5534 		XDestroyIC(window->xic);
   5535 		XDestroyWindow(ui.display, ((UIWindow *) element)->window);
   5536 	}
   5537 
   5538 	return _UIWindowMessageCommon(element, message, di, dp);
   5539 }
   5540 
   5541 UIWindow *UIWindowCreate(UIWindow *owner, uint32_t flags, const char *cTitle, int _width, int _height) {
   5542 	_UIMenusClose();
   5543 
   5544 	UIWindow *window = (UIWindow *) UIElementCreate(sizeof(UIWindow), NULL, flags | UI_ELEMENT_WINDOW, _UIWindowMessage, "Window");
   5545 	_UIWindowAdd(window);
   5546 	if (owner) window->scale = owner->scale;
   5547 
   5548 	int width = (flags & UI_WINDOW_MENU) ? 1 : _width ? _width : 800;
   5549 	int height = (flags & UI_WINDOW_MENU) ? 1 : _height ? _height : 600;
   5550 
   5551 	XSetWindowAttributes attributes = {};
   5552 	attributes.override_redirect = flags & UI_WINDOW_MENU;
   5553 
   5554 	char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
   5555 	if (xdg_current_desktop && strcmp(xdg_current_desktop, "Hyprland") == 0) {
   5556 		window->window = XCreateWindow(ui.display, DefaultRootWindow(ui.display), 0, 0, width, height, 1, 0,
   5557 			InputOutput, CopyFromParent, CWOverrideRedirect, &attributes);
   5558 		XSetWindowBorderWidth(ui.display, window->window, 0);
   5559 	} else {
   5560 		window->window = XCreateWindow(ui.display, DefaultRootWindow(ui.display), 0, 0, width, height, 0, 0,
   5561 			InputOutput, CopyFromParent, CWOverrideRedirect, &attributes);
   5562 	}
   5563 	if (cTitle) XStoreName(ui.display, window->window, cTitle);
   5564 	XSelectInput(ui.display, window->window, SubstructureNotifyMask | ExposureMask | PointerMotionMask
   5565 		| ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask
   5566 		| EnterWindowMask | LeaveWindowMask | ButtonMotionMask | KeymapStateMask | FocusChangeMask | PropertyChangeMask);
   5567 
   5568 	if (flags & UI_WINDOW_MAXIMIZE) {
   5569 		Atom atoms[2] = { XInternAtom(ui.display, "_NET_WM_STATE_MAXIMIZED_HORZ", 0), XInternAtom(ui.display, "_NET_WM_STATE_MAXIMIZED_VERT", 0) };
   5570 		XChangeProperty(ui.display, window->window, XInternAtom(ui.display, "_NET_WM_STATE", 0), XA_ATOM, 32, PropModeReplace, (unsigned char *) atoms, 2);
   5571 	}
   5572 
   5573 	if (flags & UI_WINDOW_DIALOG) {
   5574 		Atom atoms[] = {
   5575 			XInternAtom(ui.display, "_NET_WM_STATE_MODAL", 0),
   5576 			XInternAtom(ui.display, "_NET_WM_STATE_SKIP_TASKBAR", 0)
   5577 		};
   5578 		XChangeProperty(ui.display, window->window, XInternAtom(ui.display, "_NET_WM_STATE", 0), XA_ATOM, 32, PropModeAppend, (unsigned char *)atoms, sizeof(atoms)/sizeof(*atoms));
   5579 		Atom atom1 = XInternAtom(ui.display, "_NET_WM_WINDOW_TYPE_DIALOG", 0);
   5580 		XChangeProperty(ui.display, window->window, XInternAtom(ui.display, "_NET_WM_WINDOW_TYPE", 0), XA_ATOM, 32, PropModeAppend, (unsigned char *)&atom1, 1);
   5581 
   5582 		XSetTransientForHint(ui.display, window->window, DefaultRootWindow(ui.display));
   5583 	}
   5584 
   5585 	if (~flags & UI_WINDOW_MENU) {
   5586 		XMapRaised(ui.display, window->window);
   5587 	}
   5588 
   5589 	if (flags & UI_WINDOW_CENTER_IN_OWNER) {
   5590 		int x = 0, y = 0;
   5591 		_UIWindowGetScreenPosition(owner, &x, &y);
   5592 		XMoveResizeWindow(ui.display, window->window, x + owner->width / 2 - width / 2, y + owner->height / 2 - height / 2, width, height);
   5593 	}
   5594 
   5595 	XSetWMProtocols(ui.display, window->window, &ui.windowClosedID, 1);
   5596 	window->image = XCreateImage(ui.display, ui.visual, 24, ZPixmap, 0, NULL, 10, 10, 32, 0);
   5597 
   5598 	window->xic = XCreateIC(ui.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, window->window, XNFocusWindow, window->window, NULL);
   5599 
   5600 	int dndVersion = 4;
   5601 	XChangeProperty(ui.display, window->window, ui.dndAwareID, XA_ATOM, 32 /* bits */, PropModeReplace, (uint8_t *) &dndVersion, 1);
   5602 
   5603 	return window;
   5604 }
   5605 
   5606 Display *_UIX11GetDisplay() {
   5607 	return ui.display;
   5608 }
   5609 
   5610 UIWindow *_UIFindWindow(Window window) {
   5611 	UIWindow *w = ui.windows;
   5612 
   5613 	while (w) {
   5614 		if (w->window == window) {
   5615 			return w;
   5616 		}
   5617 
   5618 		w = w->next;
   5619 	}
   5620 
   5621 	return NULL;
   5622 }
   5623 
   5624 void _UIClipboardWriteText(UIWindow *window, char *text) {
   5625 	UI_FREE(ui.pasteText);
   5626 	ui.pasteText = text;
   5627 	XSetSelectionOwner(ui.display, ui.clipboardID, window->window, 0);
   5628 }
   5629 
   5630 char *_UIClipboardReadTextStart(UIWindow *window, size_t *bytes) {
   5631 	Window clipboardOwner = XGetSelectionOwner(ui.display, ui.clipboardID);
   5632 
   5633 	if (clipboardOwner == None) {
   5634 		return NULL;
   5635 	}
   5636 
   5637 	if (_UIFindWindow(clipboardOwner)) {
   5638 		*bytes = strlen(ui.pasteText);
   5639 		char *copy = (char *) UI_MALLOC(*bytes);
   5640 		memcpy(copy, ui.pasteText, *bytes);
   5641 		return copy;
   5642 	}
   5643 
   5644 	XConvertSelection(ui.display, ui.clipboardID, XA_STRING, ui.xSelectionDataID, window->window, CurrentTime);
   5645 	XSync(ui.display, 0);
   5646 	XNextEvent(ui.display, &ui.copyEvent);
   5647 
   5648 	// Hack to get around the fact that PropertyNotify arrives before SelectionNotify.
   5649 	// We need PropertyNotify for incremental transfers.
   5650 	while (ui.copyEvent.type == PropertyNotify) {
   5651 		XNextEvent(ui.display, &ui.copyEvent);
   5652 	}
   5653 
   5654 	if (ui.copyEvent.type == SelectionNotify && ui.copyEvent.xselection.selection == ui.clipboardID && ui.copyEvent.xselection.property) {
   5655 		Atom target;
   5656 		// This `itemAmount` is actually `bytes_after_return`
   5657 		unsigned long size, itemAmount;
   5658 		char *data;
   5659 		int format;
   5660 		XGetWindowProperty(ui.copyEvent.xselection.display, ui.copyEvent.xselection.requestor, ui.copyEvent.xselection.property, 0L, ~0L, 0,
   5661 				AnyPropertyType, &target, &format, &size, &itemAmount, (unsigned char **) &data);
   5662 
   5663 		// We have to allocate for incremental transfers but we don't have to allocate for non-incremental transfers.
   5664 		// I'm allocating for both here to make _UIClipboardReadTextEnd work the same for both
   5665 		if (target != ui.incrID) {
   5666 			*bytes = size;
   5667 			char *copy = (char *) UI_MALLOC(*bytes);
   5668 			memcpy(copy, data, *bytes);
   5669 			XFree(data);
   5670 			XDeleteProperty(ui.copyEvent.xselection.display, ui.copyEvent.xselection.requestor, ui.copyEvent.xselection.property);
   5671 			return copy;
   5672 		}
   5673 
   5674 		XFree(data);
   5675 		XDeleteProperty(ui.display, ui.copyEvent.xselection.requestor, ui.copyEvent.xselection.property);
   5676 		XSync(ui.display, 0);
   5677 
   5678 		*bytes = 0;
   5679 		char *fullData = NULL;
   5680 
   5681 		while (true) {
   5682 			// TODO Timeout.
   5683 			XNextEvent(ui.display, &ui.copyEvent);
   5684 
   5685 			if (ui.copyEvent.type == PropertyNotify) {
   5686 				// The other case - PropertyDelete would be caused by us and can be ignored
   5687 				if (ui.copyEvent.xproperty.state == PropertyNewValue) {
   5688 					unsigned long chunkSize;
   5689 
   5690 					// Note that this call deletes the property.
   5691 					XGetWindowProperty(ui.display, ui.copyEvent.xproperty.window, ui.copyEvent.xproperty.atom, 0L, ~0L,
   5692 						True, AnyPropertyType, &target, &format, &chunkSize, &itemAmount, (unsigned char **) &data);
   5693 
   5694 					if (chunkSize == 0) {
   5695 						return fullData;
   5696 					} else {
   5697 						ptrdiff_t currentOffset = *bytes;
   5698 						*bytes += chunkSize;
   5699 						fullData = (char *) UI_REALLOC(fullData, *bytes);
   5700 						memcpy(fullData + currentOffset, data, chunkSize);
   5701 					}
   5702 
   5703 					XFree(data);
   5704 				}
   5705 			}
   5706 		}
   5707 	} else {
   5708 		// TODO What should happen in this case? Is the next event always going to be the selection event?
   5709 		return NULL;
   5710 	}
   5711 }
   5712 
   5713 void _UIClipboardReadTextEnd(UIWindow *window, char *text) {
   5714 	if (text) {
   5715 		UI_FREE(text);
   5716 	}
   5717 }
   5718 
   5719 void UIInitialise() {
   5720 	_UIInitialiseCommon();
   5721 
   5722 	XInitThreads();
   5723 
   5724 	ui.display = XOpenDisplay(NULL);
   5725 	ui.visual = XDefaultVisual(ui.display, 0);
   5726 
   5727 	ui.windowClosedID = XInternAtom(ui.display, "WM_DELETE_WINDOW", 0);
   5728 	ui.primaryID = XInternAtom(ui.display, "PRIMARY", 0);
   5729 	ui.dndEnterID = XInternAtom(ui.display, "XdndEnter", 0);
   5730 	ui.dndPositionID = XInternAtom(ui.display, "XdndPosition", 0);
   5731 	ui.dndStatusID = XInternAtom(ui.display, "XdndStatus", 0);
   5732 	ui.dndActionCopyID = XInternAtom(ui.display, "XdndActionCopy", 0);
   5733 	ui.dndDropID = XInternAtom(ui.display, "XdndDrop", 0);
   5734 	ui.dndSelectionID = XInternAtom(ui.display, "XdndSelection", 0);
   5735 	ui.dndFinishedID = XInternAtom(ui.display, "XdndFinished", 0);
   5736 	ui.dndAwareID = XInternAtom(ui.display, "XdndAware", 0);
   5737 	ui.uriListID = XInternAtom(ui.display, "text/uri-list", 0);
   5738 	ui.plainTextID = XInternAtom(ui.display, "text/plain", 0);
   5739 	ui.clipboardID = XInternAtom(ui.display, "CLIPBOARD", 0);
   5740 	ui.xSelectionDataID = XInternAtom(ui.display, "XSEL_DATA", 0);
   5741 	ui.textID = XInternAtom(ui.display, "TEXT", 0);
   5742 	ui.targetID = XInternAtom(ui.display, "TARGETS", 0);
   5743 	ui.incrID = XInternAtom(ui.display, "INCR", 0);
   5744 
   5745 	ui.cursors[UI_CURSOR_ARROW] = XCreateFontCursor(ui.display, XC_left_ptr);
   5746 	ui.cursors[UI_CURSOR_TEXT] = XCreateFontCursor(ui.display, XC_xterm);
   5747 	ui.cursors[UI_CURSOR_SPLIT_V] = XCreateFontCursor(ui.display, XC_sb_v_double_arrow);
   5748 	ui.cursors[UI_CURSOR_SPLIT_H] = XCreateFontCursor(ui.display, XC_sb_h_double_arrow);
   5749 	ui.cursors[UI_CURSOR_FLIPPED_ARROW] = XCreateFontCursor(ui.display, XC_right_ptr);
   5750 	ui.cursors[UI_CURSOR_CROSS_HAIR] = XCreateFontCursor(ui.display, XC_crosshair);
   5751 	ui.cursors[UI_CURSOR_HAND] = XCreateFontCursor(ui.display, XC_hand1);
   5752 	ui.cursors[UI_CURSOR_RESIZE_UP] = XCreateFontCursor(ui.display, XC_top_side);
   5753 	ui.cursors[UI_CURSOR_RESIZE_LEFT] = XCreateFontCursor(ui.display, XC_left_side);
   5754 	ui.cursors[UI_CURSOR_RESIZE_UP_RIGHT] = XCreateFontCursor(ui.display, XC_top_right_corner);
   5755 	ui.cursors[UI_CURSOR_RESIZE_UP_LEFT] = XCreateFontCursor(ui.display, XC_top_left_corner);
   5756 	ui.cursors[UI_CURSOR_RESIZE_DOWN] = XCreateFontCursor(ui.display, XC_bottom_side);
   5757 	ui.cursors[UI_CURSOR_RESIZE_RIGHT] = XCreateFontCursor(ui.display, XC_right_side);
   5758 	ui.cursors[UI_CURSOR_RESIZE_DOWN_LEFT] = XCreateFontCursor(ui.display, XC_bottom_left_corner);
   5759 	ui.cursors[UI_CURSOR_RESIZE_DOWN_RIGHT] = XCreateFontCursor(ui.display, XC_bottom_right_corner);
   5760 
   5761 	XSetLocaleModifiers("");
   5762 
   5763 	ui.xim = XOpenIM(ui.display, 0, 0, 0);
   5764 
   5765 	if(!ui.xim){
   5766 		XSetLocaleModifiers("@im=none");
   5767 		ui.xim = XOpenIM(ui.display, 0, 0, 0);
   5768 	}
   5769 }
   5770 
   5771 void _UIWindowSetCursor(UIWindow *window, int cursor) {
   5772 	XDefineCursor(ui.display, window->window, ui.cursors[cursor]);
   5773 }
   5774 
   5775 void _UIX11ResetCursor(UIWindow *window) {
   5776 	XDefineCursor(ui.display, window->window, ui.cursors[UI_CURSOR_ARROW]);
   5777 }
   5778 
   5779 void _UIWindowEndPaint(UIWindow *window, UIPainter *painter) {
   5780 	(void) painter;
   5781 
   5782 	XPutImage(ui.display, window->window, DefaultGC(ui.display, 0), window->image,
   5783 		UI_RECT_TOP_LEFT(window->updateRegion), UI_RECT_TOP_LEFT(window->updateRegion),
   5784 		UI_RECT_SIZE(window->updateRegion));
   5785 }
   5786 
   5787 void _UIWindowGetScreenPosition(UIWindow *window, int *_x, int *_y) {
   5788 	Window child;
   5789 	XTranslateCoordinates(ui.display, window->window, DefaultRootWindow(ui.display), 0, 0, _x, _y, &child);
   5790 }
   5791 
   5792 void UIMenuShow(UIMenu *menu) {
   5793 	Window child;
   5794 
   5795 	// Find the screen that contains the point the menu was created at.
   5796 	Screen *menuScreen = NULL;
   5797 	int screenX, screenY;
   5798 
   5799 	for (int i = 0; i < ScreenCount(ui.display); i++) {
   5800 		Screen *screen = ScreenOfDisplay(ui.display, i);
   5801 		int x, y;
   5802 		XTranslateCoordinates(ui.display, screen->root, DefaultRootWindow(ui.display), 0, 0, &x, &y, &child);
   5803 
   5804 		if (menu->pointX >= x && menu->pointX < x + screen->width && menu->pointY >= y && menu->pointY < y + screen->height) {
   5805 			menuScreen = screen;
   5806 			screenX = x, screenY = y;
   5807 			break;
   5808 		}
   5809 	}
   5810 
   5811 	int width, height;
   5812 	_UIMenuPrepare(menu, &width, &height);
   5813 
   5814 	{
   5815 		// Clamp the menu to the bounds of the window.
   5816 		// This step shouldn't be necessary with the screen clamping below, but there are some buggy X11 drivers that report screen sizes incorrectly.
   5817 		int wx, wy;
   5818 		UIWindow *parentWindow = menu->parentWindow;
   5819 		XTranslateCoordinates(ui.display, parentWindow->window, DefaultRootWindow(ui.display), 0, 0, &wx, &wy, &child);
   5820 		if (menu->pointX + width > wx + parentWindow->width) menu->pointX = wx + parentWindow->width - width;
   5821 		if (menu->pointY + height > wy + parentWindow->height) menu->pointY = wy + parentWindow->height - height;
   5822 		if (menu->pointX < wx) menu->pointX = wx;
   5823 		if (menu->pointY < wy) menu->pointY = wy;
   5824 	}
   5825 
   5826 	if (menuScreen) {
   5827 		// Clamp to the bounds of the screen.
   5828 		if (menu->pointX + width > screenX + menuScreen->width) menu->pointX = screenX + menuScreen->width - width;
   5829 		if (menu->pointY + height > screenY + menuScreen->height) menu->pointY = screenY + menuScreen->height - height;
   5830 		if (menu->pointX < screenX) menu->pointX = screenX;
   5831 		if (menu->pointY < screenY) menu->pointY = screenY;
   5832 		if (menu->pointX + width > screenX + menuScreen->width) width = screenX + menuScreen->width - menu->pointX;
   5833 		if (menu->pointY + height > screenY + menuScreen->height) height = screenY + menuScreen->height - menu->pointY;
   5834 	}
   5835 
   5836 	Atom properties[] = {
   5837 		XInternAtom(ui.display, "_NET_WM_WINDOW_TYPE", true),
   5838 		XInternAtom(ui.display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", true),
   5839 		XInternAtom(ui.display, "_MOTIF_WM_HINTS", true),
   5840 	};
   5841 
   5842 	XChangeProperty(ui.display, menu->e.window->window, properties[0], XA_ATOM, 32, PropModeReplace, (uint8_t *) properties, 2);
   5843 	XSetTransientForHint(ui.display, menu->e.window->window, DefaultRootWindow(ui.display));
   5844 
   5845 	struct Hints {
   5846 		int flags;
   5847 		int functions;
   5848 		int decorations;
   5849 		int inputMode;
   5850 		int status;
   5851 	};
   5852 
   5853 	struct Hints hints = { 0 };
   5854 	hints.flags = 2;
   5855 	XChangeProperty(ui.display, menu->e.window->window, properties[2], properties[2], 32, PropModeReplace, (uint8_t *) &hints, 5);
   5856 
   5857 	XMapWindow(ui.display, menu->e.window->window);
   5858 	XMoveResizeWindow(ui.display, menu->e.window->window, menu->pointX, menu->pointY, width, height);
   5859 }
   5860 
   5861 void UIWindowPack(UIWindow *window, int _width) {
   5862 	int width = _width ? _width : UIElementMessage(window->e.children[0], UI_MSG_GET_WIDTH, 0, 0);
   5863 	int height = UIElementMessage(window->e.children[0], UI_MSG_GET_HEIGHT, width, 0);
   5864 	XResizeWindow(ui.display, window->window, width, height);
   5865 }
   5866 
   5867 bool _UIProcessEvent(XEvent *event) {
   5868 	if (event->type == ClientMessage && (Atom) event->xclient.data.l[0] == ui.windowClosedID) {
   5869 		UIWindow *window = _UIFindWindow(event->xclient.window);
   5870 		if (!window) return false;
   5871 		bool exit = !UIElementMessage(&window->e, UI_MSG_WINDOW_CLOSE, 0, 0);
   5872 		if (exit) return true;
   5873 		_UIUpdate();
   5874 		return false;
   5875 	} else if (event->type == Expose) {
   5876 		UIWindow *window = _UIFindWindow(event->xexpose.window);
   5877 		if (!window) return false;
   5878 		XPutImage(ui.display, window->window, DefaultGC(ui.display, 0), window->image, 0, 0, 0, 0, window->width, window->height);
   5879 	} else if (event->type == ConfigureNotify) {
   5880 		UIWindow *window = _UIFindWindow(event->xconfigure.window);
   5881 		if (!window) return false;
   5882 
   5883 		if (window->width != event->xconfigure.width || window->height != event->xconfigure.height) {
   5884 			window->width = event->xconfigure.width;
   5885 			window->height = event->xconfigure.height;
   5886 			window->bits = (uint32_t *) UI_REALLOC(window->bits, window->width * window->height * 4);
   5887 			window->image->width = window->width;
   5888 			window->image->height = window->height;
   5889 			window->image->bytes_per_line = window->width * 4;
   5890 			window->image->data = (char *) window->bits;
   5891 			window->e.bounds = UI_RECT_2S(window->width, window->height);
   5892 			window->e.clip = UI_RECT_2S(window->width, window->height);
   5893 #ifdef UI_DEBUG
   5894 			for (int i = 0; i < window->width * window->height; i++) window->bits[i] = 0xFF00FF;
   5895 #endif
   5896 			UIElementRelayout(&window->e);
   5897 			_UIUpdate();
   5898 		}
   5899 	} else if (event->type == MotionNotify) {
   5900 		UIWindow *window = _UIFindWindow(event->xmotion.window);
   5901 		if (!window) return false;
   5902 		window->cursorX = event->xmotion.x;
   5903 		window->cursorY = event->xmotion.y;
   5904 		_UIWindowInputEvent(window, UI_MSG_MOUSE_MOVE, 0, 0);
   5905 	} else if (event->type == LeaveNotify) {
   5906 		UIWindow *window = _UIFindWindow(event->xcrossing.window);
   5907 		if (!window) return false;
   5908 
   5909 		if (!window->pressed) {
   5910 			window->cursorX = -1;
   5911 			window->cursorY = -1;
   5912 		}
   5913 
   5914 		_UIWindowInputEvent(window, UI_MSG_MOUSE_MOVE, 0, 0);
   5915 	} else if (event->type == ButtonPress || event->type == ButtonRelease) {
   5916 		UIWindow *window = _UIFindWindow(event->xbutton.window);
   5917 		if (!window) return false;
   5918 		window->cursorX = event->xbutton.x;
   5919 		window->cursorY = event->xbutton.y;
   5920 
   5921 		if (event->xbutton.button >= 1 && event->xbutton.button <= 3) {
   5922 			_UIWindowInputEvent(window, (UIMessage) ((event->type == ButtonPress ? UI_MSG_LEFT_DOWN : UI_MSG_LEFT_UP)
   5923 				+ event->xbutton.button * 2 - 2), 0, 0);
   5924 		} else if (event->xbutton.button == 4) {
   5925 			_UIWindowInputEvent(window, UI_MSG_MOUSE_WHEEL, -72, 0);
   5926 		} else if (event->xbutton.button == 5) {
   5927 			_UIWindowInputEvent(window, UI_MSG_MOUSE_WHEEL, 72, 0);
   5928 		}
   5929 
   5930 		_UIInspectorSetFocusedWindow(window);
   5931 	} else if (event->type == KeyPress) {
   5932 		UIWindow *window = _UIFindWindow(event->xkey.window);
   5933 		if (!window) return false;
   5934 
   5935 		if (event->xkey.x == 0x7123 && event->xkey.y == 0x7456) {
   5936 			// HACK! See UIWindowPostMessage.
   5937 			uintptr_t p = ((uintptr_t) (event->xkey.x_root & 0xFFFF) << 0) | ((uintptr_t) (event->xkey.y_root & 0xFFFF) << 16);
   5938 #if INTPTR_MAX == INT64_MAX
   5939 			p |= (uintptr_t) (event->xkey.time & 0xFFFFFFFF) << 32;
   5940 #endif
   5941 			UIElementMessage(&window->e, (UIMessage) event->xkey.state, 0, (void *) p);
   5942 			_UIUpdate();
   5943 		} else {
   5944 			char text[32];
   5945 			KeySym symbol = NoSymbol;
   5946 			Status status;
   5947 			// printf("%ld, %s\n", symbol, text);
   5948 			UIKeyTyped m = { 0 };
   5949 			m.textBytes = Xutf8LookupString(window->xic, &event->xkey, text, sizeof(text) - 1, &symbol, &status);
   5950 			m.text = text;
   5951 			m.code = XLookupKeysym(&event->xkey, 0);
   5952 
   5953 			if (symbol == XK_Control_L || symbol == XK_Control_R) {
   5954 				window->ctrl = true;
   5955 				window->ctrlCode = event->xkey.keycode;
   5956 				_UIWindowInputEvent(window, UI_MSG_MOUSE_MOVE, 0, 0);
   5957 			} else if (symbol == XK_Shift_L || symbol == XK_Shift_R) {
   5958 				window->shift = true;
   5959 				window->shiftCode = event->xkey.keycode;
   5960 				_UIWindowInputEvent(window, UI_MSG_MOUSE_MOVE, 0, 0);
   5961 			} else if (symbol == XK_Alt_L || symbol == XK_Alt_R) {
   5962 				window->alt = true;
   5963 				window->altCode = event->xkey.keycode;
   5964 				_UIWindowInputEvent(window, UI_MSG_MOUSE_MOVE, 0, 0);
   5965 			} else if (symbol == XK_KP_Left) {
   5966 				m.code = UI_KEYCODE_LEFT;
   5967 			} else if (symbol == XK_KP_Right) {
   5968 				m.code = UI_KEYCODE_RIGHT;
   5969 			} else if (symbol == XK_KP_Up) {
   5970 				m.code = UI_KEYCODE_UP;
   5971 			} else if (symbol == XK_KP_Down) {
   5972 				m.code = UI_KEYCODE_DOWN;
   5973 			} else if (symbol == XK_KP_Home) {
   5974 				m.code = UI_KEYCODE_HOME;
   5975 			} else if (symbol == XK_KP_End) {
   5976 				m.code = UI_KEYCODE_END;
   5977 			} else if (symbol == XK_KP_Enter) {
   5978 				m.code = UI_KEYCODE_ENTER;
   5979 			} else if (symbol == XK_KP_Delete) {
   5980 				m.code = UI_KEYCODE_DELETE;
   5981 			} else if (symbol == XK_KP_Page_Up) {
   5982 				m.code = UI_KEYCODE_UP;
   5983 			} else if (symbol == XK_KP_Page_Down) {
   5984 				m.code = UI_KEYCODE_DOWN;
   5985 			}
   5986 
   5987 			_UIWindowInputEvent(window, UI_MSG_KEY_TYPED, 0, &m);
   5988 		}
   5989 	} else if (event->type == KeyRelease) {
   5990 		UIWindow *window = _UIFindWindow(event->xkey.window);
   5991 		if (!window) return false;
   5992 
   5993 		if (event->xkey.keycode == window->ctrlCode) {
   5994 			window->ctrl = false;
   5995 			_UIWindowInputEvent(window, UI_MSG_MOUSE_MOVE, 0, 0);
   5996 		} else if (event->xkey.keycode == window->shiftCode) {
   5997 			window->shift = false;
   5998 			_UIWindowInputEvent(window, UI_MSG_MOUSE_MOVE, 0, 0);
   5999 		} else if (event->xkey.keycode == window->altCode) {
   6000 			window->alt = false;
   6001 			_UIWindowInputEvent(window, UI_MSG_MOUSE_MOVE, 0, 0);
   6002 		} else {
   6003 			char text[32];
   6004 			KeySym symbol = NoSymbol;
   6005 			Status status;
   6006 			UIKeyTyped m = { 0 };
   6007 			m.textBytes = Xutf8LookupString(window->xic, &event->xkey, text, sizeof(text) - 1, &symbol, &status);
   6008 			m.text = text;
   6009 			m.code = XLookupKeysym(&event->xkey, 0);
   6010 			_UIWindowInputEvent(window, UI_MSG_KEY_RELEASED, 0, &m);
   6011 		}
   6012 	} else if (event->type == FocusIn) {
   6013 		UIWindow *window = _UIFindWindow(event->xfocus.window);
   6014 		if (!window) return false;
   6015 		window->ctrl = window->shift = window->alt = false;
   6016 		UIElementMessage(&window->e, UI_MSG_WINDOW_ACTIVATE, 0, 0);
   6017 	} else if (event->type == FocusOut || event->type == ResizeRequest) {
   6018 		_UIMenusClose();
   6019 		_UIUpdate();
   6020 	} else if (event->type == ClientMessage && event->xclient.message_type == ui.dndEnterID) {
   6021 		UIWindow *window = _UIFindWindow(event->xclient.window);
   6022 		if (!window) return false;
   6023 		window->dragSource = (Window) event->xclient.data.l[0];
   6024 	} else if (event->type == ClientMessage && event->xclient.message_type == ui.dndPositionID) {
   6025 		UIWindow *window = _UIFindWindow(event->xclient.window);
   6026 		if (!window) return false;
   6027 		XClientMessageEvent m = { 0 };
   6028 		m.type = ClientMessage;
   6029 		m.display = event->xclient.display;
   6030 		m.window = (Window) event->xclient.data.l[0];
   6031 		m.message_type = ui.dndStatusID;
   6032 		m.format = 32;
   6033 		m.data.l[0] = window->window;
   6034 		m.data.l[1] = true;
   6035 		m.data.l[4] = ui.dndActionCopyID;
   6036 		XSendEvent(ui.display, m.window, False, NoEventMask, (XEvent *) &m);
   6037 		XFlush(ui.display);
   6038 	} else if (event->type == ClientMessage && event->xclient.message_type == ui.dndDropID) {
   6039 		UIWindow *window = _UIFindWindow(event->xclient.window);
   6040 		if (!window) return false;
   6041 
   6042 		// TODO Dropping text.
   6043 
   6044 		if (!XConvertSelection(ui.display, ui.dndSelectionID, ui.uriListID, ui.primaryID, window->window, event->xclient.data.l[2])) {
   6045 			XClientMessageEvent m = { 0 };
   6046 			m.type = ClientMessage;
   6047 			m.display = ui.display;
   6048 			m.window = window->dragSource;
   6049 			m.message_type = ui.dndFinishedID;
   6050 			m.format = 32;
   6051 			m.data.l[0] = window->window;
   6052 			m.data.l[1] = 0;
   6053 			m.data.l[2] = ui.dndActionCopyID;
   6054 			XSendEvent(ui.display, m.window, False, NoEventMask, (XEvent *) &m);
   6055 			XFlush(ui.display);
   6056 		}
   6057 	} else if (event->type == SelectionNotify) {
   6058 		UIWindow *window = _UIFindWindow(event->xselection.requestor);
   6059 		if (!window) return false;
   6060 		if (!window->dragSource) return false;
   6061 
   6062 		Atom type = None;
   6063 		int format = 0;
   6064 		unsigned long count = 0, bytesLeft = 0;
   6065 		uint8_t *data = NULL;
   6066 		XGetWindowProperty(ui.display, window->window, ui.primaryID, 0, 65536, False, AnyPropertyType, &type, &format, &count, &bytesLeft, &data);
   6067 
   6068 		if (format == 8 /* bits per character */) {
   6069 			if (event->xselection.target == ui.uriListID) {
   6070 				char *copy = (char *) UI_MALLOC(count);
   6071 				int fileCount = 0;
   6072 
   6073 				for (int i = 0; i < (int) count; i++) {
   6074 					copy[i] = data[i];
   6075 
   6076 					if (i && data[i - 1] == '\r' && data[i] == '\n') {
   6077 						fileCount++;
   6078 					}
   6079 				}
   6080 
   6081 				char **files = (char **) UI_MALLOC(sizeof(char *) * fileCount);
   6082 				fileCount = 0;
   6083 
   6084 				for (int i = 0; i < (int) count; i++) {
   6085 					char *s = copy + i;
   6086 					while (!(i && data[i - 1] == '\r' && data[i] == '\n' && i < (int) count)) i++;
   6087 					copy[i - 1] = 0;
   6088 
   6089 					for (int j = 0; s[j]; j++) {
   6090 						if (s[j] == '%' && s[j + 1] && s[j + 2]) {
   6091 							char n[3];
   6092 							n[0] = s[j + 1], n[1] = s[j + 2], n[2] = 0;
   6093 							s[j] = strtol(n, NULL, 16);
   6094 							if (!s[j]) break;
   6095 							UI_MEMMOVE(s + j + 1, s + j + 3, strlen(s) - j - 2);
   6096 						}
   6097 					}
   6098 
   6099 					if (s[0] == 'f' && s[1] == 'i' && s[2] == 'l' && s[3] == 'e' && s[4] == ':' && s[5] == '/' && s[6] == '/') {
   6100 						files[fileCount++] = s + 7;
   6101 					}
   6102 				}
   6103 
   6104 				UIElementMessage(&window->e, UI_MSG_WINDOW_DROP_FILES, fileCount, files);
   6105 
   6106 				UI_FREE(files);
   6107 				UI_FREE(copy);
   6108 			} else if (event->xselection.target == ui.plainTextID) {
   6109 				// TODO.
   6110 			}
   6111 		}
   6112 
   6113 		XFree(data);
   6114 
   6115 		XClientMessageEvent m = { 0 };
   6116 		m.type = ClientMessage;
   6117 		m.display = ui.display;
   6118 		m.window = window->dragSource;
   6119 		m.message_type = ui.dndFinishedID;
   6120 		m.format = 32;
   6121 		m.data.l[0] = window->window;
   6122 		m.data.l[1] = true;
   6123 		m.data.l[2] = ui.dndActionCopyID;
   6124 		XSendEvent(ui.display, m.window, False, NoEventMask, (XEvent *) &m);
   6125 		XFlush(ui.display);
   6126 
   6127 		window->dragSource = 0; // Drag complete.
   6128 		_UIUpdate();
   6129 	} else if (event->type == SelectionRequest) {
   6130 		UIWindow *window = _UIFindWindow(event->xclient.window);
   6131 		if (!window) return false;
   6132 
   6133 		if ((XGetSelectionOwner(ui.display, ui.clipboardID) == window->window)
   6134 				&& (event->xselectionrequest.selection == ui.clipboardID)) {
   6135 			XSelectionRequestEvent requestEvent = event->xselectionrequest;
   6136 			Atom utf8ID = XInternAtom(ui.display, "UTF8_STRING", 1);
   6137 			if (utf8ID == None) utf8ID = XA_STRING;
   6138 
   6139 			Atom type = requestEvent.target;
   6140 			type = (type == ui.textID) ? XA_STRING : type;
   6141 			int changePropertyResult = 0;
   6142 
   6143 			if(requestEvent.target == XA_STRING || requestEvent.target == ui.textID || requestEvent.target == utf8ID) {
   6144 				changePropertyResult = XChangeProperty(requestEvent.display, requestEvent.requestor, requestEvent.property,
   6145 						type, 8, PropModeReplace, (const unsigned char *) ui.pasteText, strlen(ui.pasteText));
   6146 			} else if (requestEvent.target == ui.targetID) {
   6147 				changePropertyResult = XChangeProperty(requestEvent.display, requestEvent.requestor, requestEvent.property,
   6148 						XA_ATOM, 32, PropModeReplace, (unsigned char *) &utf8ID, 1);
   6149 			}
   6150 
   6151 			if(changePropertyResult == 0 || changePropertyResult == 1) {
   6152 				XSelectionEvent sendEvent = {
   6153 					.type = SelectionNotify,
   6154 					.serial = requestEvent.serial,
   6155 					.send_event = requestEvent.send_event,
   6156 					.display = requestEvent.display,
   6157 					.requestor = requestEvent.requestor,
   6158 					.selection = requestEvent.selection,
   6159 					.target = requestEvent.target,
   6160 					.property = requestEvent.property,
   6161 					.time = requestEvent.time
   6162 				};
   6163 
   6164 				XSendEvent(ui.display, requestEvent.requestor, 0, 0, (XEvent *) &sendEvent);
   6165 			}
   6166 		}
   6167 	}
   6168 
   6169 	return false;
   6170 }
   6171 
   6172 bool _UIMessageLoopSingle(int *result) {
   6173 	XEvent events[64];
   6174 
   6175 	if (ui.animatingCount) {
   6176 		if (XPending(ui.display)) {
   6177 			XNextEvent(ui.display, events + 0);
   6178 		} else {
   6179 			_UIProcessAnimations();
   6180 			return true;
   6181 		}
   6182 	} else {
   6183 		XNextEvent(ui.display, events + 0);
   6184 	}
   6185 
   6186 	int p = 1;
   6187 
   6188 	int configureIndex = -1, motionIndex = -1, exposeIndex = -1;
   6189 
   6190 	while (p < 64 && XPending(ui.display)) {
   6191 		XNextEvent(ui.display, events + p);
   6192 
   6193 #define _UI_MERGE_EVENTS(a, b) \
   6194 	if (events[p].type == a) { \
   6195 		if (b != -1) events[b].type = 0; \
   6196 		b = p; \
   6197 	}
   6198 
   6199 		_UI_MERGE_EVENTS(ConfigureNotify, configureIndex);
   6200 		_UI_MERGE_EVENTS(MotionNotify, motionIndex);
   6201 		_UI_MERGE_EVENTS(Expose, exposeIndex);
   6202 
   6203 		p++;
   6204 	}
   6205 
   6206 	for (int i = 0; i < p; i++) {
   6207 		if (!events[i].type) {
   6208 			continue;
   6209 		}
   6210 
   6211 		if (_UIProcessEvent(events + i)) {
   6212 			return false;
   6213 		}
   6214 	}
   6215 
   6216 	return true;
   6217 }
   6218 
   6219 void UIWindowPostMessage(UIWindow *window, UIMessage message, void *_dp) {
   6220 	// HACK! Xlib doesn't seem to have a nice way to do this,
   6221 	// so send a specially crafted key press event instead.
   6222 	// TODO Maybe ClientMessage is what this should use?
   6223 	uintptr_t dp = (uintptr_t) _dp;
   6224 	XKeyEvent event = { 0 };
   6225 	event.display = ui.display;
   6226 	event.window = window->window;
   6227 	event.root = DefaultRootWindow(ui.display);
   6228 	event.subwindow = None;
   6229 #if INTPTR_MAX == INT64_MAX
   6230 	event.time = dp >> 32;
   6231 #endif
   6232 	event.x = 0x7123;
   6233 	event.y = 0x7456;
   6234 	event.x_root = (dp >> 0) & 0xFFFF;
   6235 	event.y_root = (dp >> 16) & 0xFFFF;
   6236 	event.same_screen = True;
   6237 	event.keycode = 1;
   6238 	event.state = message;
   6239 	event.type = KeyPress;
   6240 	XSendEvent(ui.display, window->window, True, KeyPressMask, (XEvent *) &event);
   6241 	XFlush(ui.display);
   6242 }
   6243 
   6244 #endif
   6245 
   6246 #ifdef UI_WINDOWS
   6247 
   6248 const int UI_KEYCODE_A = 'A';
   6249 const int UI_KEYCODE_0 = '0';
   6250 const int UI_KEYCODE_BACKSPACE = VK_BACK;
   6251 const int UI_KEYCODE_DELETE = VK_DELETE;
   6252 const int UI_KEYCODE_DOWN = VK_DOWN;
   6253 const int UI_KEYCODE_END = VK_END;
   6254 const int UI_KEYCODE_ENTER = VK_RETURN;
   6255 const int UI_KEYCODE_ESCAPE = VK_ESCAPE;
   6256 const int UI_KEYCODE_F1 = VK_F1;
   6257 const int UI_KEYCODE_HOME = VK_HOME;
   6258 const int UI_KEYCODE_LEFT = VK_LEFT;
   6259 const int UI_KEYCODE_RIGHT = VK_RIGHT;
   6260 const int UI_KEYCODE_SPACE = VK_SPACE;
   6261 const int UI_KEYCODE_TAB = VK_TAB;
   6262 const int UI_KEYCODE_UP = VK_UP;
   6263 const int UI_KEYCODE_INSERT = VK_INSERT;
   6264 const int UI_KEYCODE_PAGE_UP = VK_PRIOR;
   6265 const int UI_KEYCODE_PAGE_DOWN = VK_NEXT;
   6266 
   6267 int _UIWindowMessage(UIElement *element, UIMessage message, int di, void *dp) {
   6268 	if (message == UI_MSG_DEALLOCATE) {
   6269 		UIWindow *window = (UIWindow *) element;
   6270 		_UIWindowDestroyCommon(window);
   6271 		SetWindowLongPtr(window->hwnd, GWLP_USERDATA, 0);
   6272 		DestroyWindow(window->hwnd);
   6273 	}
   6274 
   6275 	return _UIWindowMessageCommon(element, message, di, dp);
   6276 }
   6277 
   6278 LRESULT CALLBACK _UIWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
   6279 	UIWindow *window = (UIWindow *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
   6280 
   6281 	if (!window || ui.assertionFailure) {
   6282 		return DefWindowProc(hwnd, message, wParam, lParam);
   6283 	}
   6284 
   6285 	if (message == WM_CLOSE) {
   6286 		if (UIElementMessage(&window->e, UI_MSG_WINDOW_CLOSE, 0, 0)) {
   6287 			_UIUpdate();
   6288 			return 0;
   6289 		} else {
   6290 			PostQuitMessage(0);
   6291 		}
   6292 	} else if (message == WM_SIZE) {
   6293 		RECT client;
   6294 		GetClientRect(hwnd, &client);
   6295 		window->width = client.right;
   6296 		window->height = client.bottom;
   6297 		window->bits = (uint32_t *) UI_REALLOC(window->bits, window->width * window->height * 4);
   6298 		window->e.bounds = UI_RECT_2S(window->width, window->height);
   6299 		window->e.clip = UI_RECT_2S(window->width, window->height);
   6300 		UIElementRelayout(&window->e);
   6301 		_UIUpdate();
   6302 	} else if (message == WM_MOUSEMOVE) {
   6303 		if (!window->trackingLeave) {
   6304 			window->trackingLeave = true;
   6305 			TRACKMOUSEEVENT leave = { 0 };
   6306 			leave.cbSize = sizeof(TRACKMOUSEEVENT);
   6307 			leave.dwFlags = TME_LEAVE;
   6308 			leave.hwndTrack = hwnd;
   6309 			TrackMouseEvent(&leave);
   6310 		}
   6311 
   6312 		POINT cursor;
   6313 		GetCursorPos(&cursor);
   6314 		ScreenToClient(hwnd, &cursor);
   6315 		window->cursorX = cursor.x;
   6316 		window->cursorY = cursor.y;
   6317 		_UIWindowInputEvent(window, UI_MSG_MOUSE_MOVE, 0, 0);
   6318 	} else if (message == WM_MOUSELEAVE) {
   6319 		window->trackingLeave = false;
   6320 
   6321 		if (!window->pressed) {
   6322 			window->cursorX = -1;
   6323 			window->cursorY = -1;
   6324 		}
   6325 
   6326 		_UIWindowInputEvent(window, UI_MSG_MOUSE_MOVE, 0, 0);
   6327 	} else if (message == WM_LBUTTONDOWN) {
   6328 		SetCapture(hwnd);
   6329 		_UIWindowInputEvent(window, UI_MSG_LEFT_DOWN, 0, 0);
   6330 	} else if (message == WM_LBUTTONUP) {
   6331 		if (window->pressedButton == 1) ReleaseCapture();
   6332 		_UIWindowInputEvent(window, UI_MSG_LEFT_UP, 0, 0);
   6333 	} else if (message == WM_MBUTTONDOWN) {
   6334 		SetCapture(hwnd);
   6335 		_UIWindowInputEvent(window, UI_MSG_MIDDLE_DOWN, 0, 0);
   6336 	} else if (message == WM_MBUTTONUP) {
   6337 		if (window->pressedButton == 2) ReleaseCapture();
   6338 		_UIWindowInputEvent(window, UI_MSG_MIDDLE_UP, 0, 0);
   6339 	} else if (message == WM_RBUTTONDOWN) {
   6340 		SetCapture(hwnd);
   6341 		_UIWindowInputEvent(window, UI_MSG_RIGHT_DOWN, 0, 0);
   6342 	} else if (message == WM_RBUTTONUP) {
   6343 		if (window->pressedButton == 3) ReleaseCapture();
   6344 		_UIWindowInputEvent(window, UI_MSG_RIGHT_UP, 0, 0);
   6345 	} else if (message == WM_MOUSEWHEEL) {
   6346 		int delta = (int) wParam >> 16;
   6347 		_UIWindowInputEvent(window, UI_MSG_MOUSE_WHEEL, -delta, 0);
   6348 	} else if (message == WM_KEYDOWN) {
   6349 		window->ctrl = GetKeyState(VK_CONTROL) & 0x8000;
   6350 		window->shift = GetKeyState(VK_SHIFT) & 0x8000;
   6351 		window->alt = GetKeyState(VK_MENU) & 0x8000;
   6352 
   6353 		UIKeyTyped m = { 0 };
   6354 		m.code = wParam;
   6355 		_UIWindowInputEvent(window, UI_MSG_KEY_TYPED, 0, &m);
   6356 	} else if (message == WM_CHAR) {
   6357 		UIKeyTyped m = { 0 };
   6358 		char c = wParam;
   6359 		m.text = &c;
   6360 		m.textBytes = 1;
   6361 		_UIWindowInputEvent(window, UI_MSG_KEY_TYPED, 0, &m);
   6362 	} else if (message == WM_PAINT) {
   6363 		PAINTSTRUCT paint;
   6364 		HDC dc = BeginPaint(hwnd, &paint);
   6365 		BITMAPINFOHEADER info = { 0 };
   6366 		info.biSize = sizeof(info);
   6367 		info.biWidth = window->width, info.biHeight = -window->height;
   6368 		info.biPlanes = 1, info.biBitCount = 32;
   6369 		StretchDIBits(dc, 0, 0, UI_RECT_SIZE(window->e.bounds), 0, 0, UI_RECT_SIZE(window->e.bounds),
   6370 			window->bits, (BITMAPINFO *) &info, DIB_RGB_COLORS, SRCCOPY);
   6371 		EndPaint(hwnd, &paint);
   6372 	} else if (message == WM_SETCURSOR && LOWORD(lParam) == HTCLIENT) {
   6373 		SetCursor(ui.cursors[window->cursorStyle]);
   6374 		return 1;
   6375 	} else if (message == WM_SETFOCUS || message == WM_KILLFOCUS) {
   6376 		_UIMenusClose();
   6377 
   6378 		if (message == WM_SETFOCUS) {
   6379 			_UIInspectorSetFocusedWindow(window);
   6380 			UIElementMessage(&window->e, UI_MSG_WINDOW_ACTIVATE, 0, 0);
   6381 		}
   6382 	} else if (message == WM_MOUSEACTIVATE && (window->e.flags & UI_WINDOW_MENU)) {
   6383 		return MA_NOACTIVATE;
   6384 	} else if (message == WM_DROPFILES) {
   6385 		HDROP drop = (HDROP) wParam;
   6386 		int count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
   6387 		char **files = (char **) UI_MALLOC(sizeof(char *) * count);
   6388 
   6389 		for (int i = 0; i < count; i++) {
   6390 			int length = DragQueryFile(drop, i, NULL, 0);
   6391 			files[i] = (char *) UI_MALLOC(length + 1);
   6392 			files[i][length] = 0;
   6393 			DragQueryFile(drop, i, files[i], length + 1);
   6394 		}
   6395 
   6396 		UIElementMessage(&window->e, UI_MSG_WINDOW_DROP_FILES, count, files);
   6397 		for (int i = 0; i < count; i++) UI_FREE(files[i]);
   6398 		UI_FREE(files);
   6399 		DragFinish(drop);
   6400 		_UIUpdate();
   6401 	} else if (message == WM_APP + 1) {
   6402 		UIElementMessage(&window->e, (UIMessage) wParam, 0, (void *) lParam);
   6403 		_UIUpdate();
   6404 	} else {
   6405 		if (message == WM_NCLBUTTONDOWN || message == WM_NCMBUTTONDOWN || message == WM_NCRBUTTONDOWN) {
   6406 			if (~window->e.flags & UI_WINDOW_MENU) {
   6407 				_UIMenusClose();
   6408 				_UIUpdate();
   6409 			}
   6410 		}
   6411 
   6412 		return DefWindowProc(hwnd, message, wParam, lParam);
   6413 	}
   6414 
   6415 	return 0;
   6416 }
   6417 
   6418 void UIInitialise() {
   6419 	ui.heap = GetProcessHeap();
   6420 
   6421 	_UIInitialiseCommon();
   6422 
   6423 	ui.cursors[UI_CURSOR_ARROW] = LoadCursor(NULL, IDC_ARROW);
   6424 	ui.cursors[UI_CURSOR_TEXT] = LoadCursor(NULL, IDC_IBEAM);
   6425 	ui.cursors[UI_CURSOR_SPLIT_V] = LoadCursor(NULL, IDC_SIZENS);
   6426 	ui.cursors[UI_CURSOR_SPLIT_H] = LoadCursor(NULL, IDC_SIZEWE);
   6427 	ui.cursors[UI_CURSOR_FLIPPED_ARROW] = LoadCursor(NULL, IDC_ARROW);
   6428 	ui.cursors[UI_CURSOR_CROSS_HAIR] = LoadCursor(NULL, IDC_CROSS);
   6429 	ui.cursors[UI_CURSOR_HAND] = LoadCursor(NULL, IDC_HAND);
   6430 	ui.cursors[UI_CURSOR_RESIZE_UP] = LoadCursor(NULL, IDC_SIZENS);
   6431 	ui.cursors[UI_CURSOR_RESIZE_LEFT] = LoadCursor(NULL, IDC_SIZEWE);
   6432 	ui.cursors[UI_CURSOR_RESIZE_UP_RIGHT] = LoadCursor(NULL, IDC_SIZENESW);
   6433 	ui.cursors[UI_CURSOR_RESIZE_UP_LEFT] = LoadCursor(NULL, IDC_SIZENWSE);
   6434 	ui.cursors[UI_CURSOR_RESIZE_DOWN] = LoadCursor(NULL, IDC_SIZENS);
   6435 	ui.cursors[UI_CURSOR_RESIZE_RIGHT] = LoadCursor(NULL, IDC_SIZEWE);
   6436 	ui.cursors[UI_CURSOR_RESIZE_DOWN_LEFT] = LoadCursor(NULL, IDC_SIZENESW);
   6437 	ui.cursors[UI_CURSOR_RESIZE_DOWN_RIGHT] = LoadCursor(NULL, IDC_SIZENWSE);
   6438 
   6439 	WNDCLASS windowClass = { 0 };
   6440 	windowClass.lpfnWndProc = _UIWindowProcedure;
   6441 	windowClass.lpszClassName = "normal";
   6442 	RegisterClass(&windowClass);
   6443 	windowClass.style |= CS_DROPSHADOW;
   6444 	windowClass.lpszClassName = "shadow";
   6445 	RegisterClass(&windowClass);
   6446 }
   6447 
   6448 bool _UIMessageLoopSingle(int *result) {
   6449 	MSG message = { 0 };
   6450 
   6451 	if (ui.animating) {
   6452 		if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) {
   6453 			if (message.message == WM_QUIT) {
   6454 				*result = message.wParam;
   6455 				return false;
   6456 			}
   6457 
   6458 			TranslateMessage(&message);
   6459 			DispatchMessage(&message);
   6460 		} else {
   6461 			_UIProcessAnimations();
   6462 		}
   6463 	} else {
   6464 		if (!GetMessage(&message, NULL, 0, 0)) {
   6465 			*result = message.wParam;
   6466 			return false;
   6467 		}
   6468 
   6469 		TranslateMessage(&message);
   6470 		DispatchMessage(&message);
   6471 	}
   6472 
   6473 	return true;
   6474 }
   6475 
   6476 void UIMenuShow(UIMenu *menu) {
   6477 	int width, height;
   6478 	_UIMenuPrepare(menu, &width, &height);
   6479 	MoveWindow(menu->e.window->hwnd, menu->pointX, menu->pointY, width, height, FALSE);
   6480 	ShowWindow(menu->e.window->hwnd, SW_SHOWNOACTIVATE);
   6481 }
   6482 
   6483 UIWindow *UIWindowCreate(UIWindow *owner, uint32_t flags, const char *cTitle, int width, int height) {
   6484 	_UIMenusClose();
   6485 
   6486 	UIWindow *window = (UIWindow *) UIElementCreate(sizeof(UIWindow), NULL, flags | UI_ELEMENT_WINDOW, _UIWindowMessage, "Window");
   6487 	_UIWindowAdd(window);
   6488 	if (owner) window->scale = owner->scale;
   6489 
   6490 	if (flags & UI_WINDOW_MENU) {
   6491 		UI_ASSERT(owner);
   6492 
   6493 		window->hwnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_NOACTIVATE, "shadow", 0, WS_POPUP,
   6494 			0, 0, 0, 0, owner->hwnd, NULL, NULL, NULL);
   6495 	} else {
   6496 		window->hwnd = CreateWindowEx(WS_EX_ACCEPTFILES, "normal", cTitle, WS_OVERLAPPEDWINDOW,
   6497 			CW_USEDEFAULT, CW_USEDEFAULT, width ? width : CW_USEDEFAULT, height ? height : CW_USEDEFAULT,
   6498 			owner ? owner->hwnd : NULL, NULL, NULL, NULL);
   6499 	}
   6500 
   6501 	SetWindowLongPtr(window->hwnd, GWLP_USERDATA, (LONG_PTR) window);
   6502 
   6503 	if (~flags & UI_WINDOW_MENU) {
   6504 		ShowWindow(window->hwnd, SW_SHOW);
   6505 		PostMessage(window->hwnd, WM_SIZE, 0, 0);
   6506 	}
   6507 
   6508 	return window;
   6509 }
   6510 
   6511 void _UIWindowEndPaint(UIWindow *window, UIPainter *painter) {
   6512 	HDC dc = GetDC(window->hwnd);
   6513 	BITMAPINFOHEADER info = { 0 };
   6514 	info.biSize = sizeof(info);
   6515 	info.biWidth = window->width, info.biHeight = window->height;
   6516 	info.biPlanes = 1, info.biBitCount = 32;
   6517 	StretchDIBits(dc,
   6518 		UI_RECT_TOP_LEFT(window->updateRegion), UI_RECT_SIZE(window->updateRegion),
   6519 		window->updateRegion.l, window->updateRegion.b + 1,
   6520 		UI_RECT_WIDTH(window->updateRegion), -UI_RECT_HEIGHT(window->updateRegion),
   6521 		window->bits, (BITMAPINFO *) &info, DIB_RGB_COLORS, SRCCOPY);
   6522 	ReleaseDC(window->hwnd, dc);
   6523 }
   6524 
   6525 void _UIWindowSetCursor(UIWindow *window, int cursor) {
   6526 	SetCursor(ui.cursors[cursor]);
   6527 }
   6528 
   6529 void _UIWindowGetScreenPosition(UIWindow *window, int *_x, int *_y) {
   6530 	POINT p;
   6531 	p.x = 0;
   6532 	p.y = 0;
   6533 	ClientToScreen(window->hwnd, &p);
   6534 	*_x = p.x;
   6535 	*_y = p.y;
   6536 }
   6537 
   6538 void UIWindowPostMessage(UIWindow *window, UIMessage message, void *_dp) {
   6539 	PostMessage(window->hwnd, WM_APP + 1, (WPARAM) message, (LPARAM) _dp);
   6540 }
   6541 
   6542 void *_UIHeapReAlloc(void *pointer, size_t size) {
   6543 	if (pointer) {
   6544 		if (size) {
   6545 			return HeapReAlloc(ui.heap, 0, pointer, size);
   6546 		} else {
   6547 			UI_FREE(pointer);
   6548 			return NULL;
   6549 		}
   6550 	} else {
   6551 		if (size) {
   6552 			return UI_MALLOC(size);
   6553 		} else {
   6554 			return NULL;
   6555 		}
   6556 	}
   6557 }
   6558 
   6559 void _UIClipboardWriteText(UIWindow *window, char *text) {
   6560 	if (OpenClipboard(window->hwnd)) {
   6561 		EmptyClipboard();
   6562 		HGLOBAL memory = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, _UIStringLength(text) + 1);
   6563 		char *copy = (char *) GlobalLock(memory);
   6564 		for (uintptr_t i = 0; text[i]; i++) copy[i] = text[i];
   6565 		GlobalUnlock(copy);
   6566 		SetClipboardData(CF_TEXT, memory);
   6567 		CloseClipboard();
   6568 	}
   6569 }
   6570 
   6571 char *_UIClipboardReadTextStart(UIWindow *window, size_t *bytes) {
   6572 	if (!OpenClipboard(window->hwnd)) {
   6573 		return NULL;
   6574 	}
   6575 
   6576 	HANDLE memory = GetClipboardData(CF_TEXT);
   6577 
   6578 	if (!memory) {
   6579 		CloseClipboard();
   6580 		return NULL;
   6581 	}
   6582 
   6583 	char *buffer = (char *) GlobalLock(memory);
   6584 
   6585 	if (!buffer) {
   6586 		CloseClipboard();
   6587 		return NULL;
   6588 	}
   6589 
   6590 	size_t byteCount = GlobalSize(memory);
   6591 
   6592 	if (byteCount < 1) {
   6593 		GlobalUnlock(memory);
   6594 		CloseClipboard();
   6595 		return NULL;
   6596 	}
   6597 
   6598 	char *copy = (char *) UI_MALLOC(byteCount + 1);
   6599 	for (uintptr_t i = 0; i < byteCount; i++) copy[i] = buffer[i];
   6600 	copy[byteCount] = 0; // Just in case.
   6601 
   6602 	GlobalUnlock(memory);
   6603 	CloseClipboard();
   6604 
   6605 	if (bytes) *bytes = _UIStringLength(copy);
   6606 	return copy;
   6607 }
   6608 
   6609 void _UIClipboardReadTextEnd(UIWindow *window, char *text) {
   6610 	UI_FREE(text);
   6611 }
   6612 
   6613 void *_UIMemmove(void *dest, const void *src, size_t n) {
   6614 	if ((uintptr_t) dest < (uintptr_t) src) {
   6615 		uint8_t *dest8 = (uint8_t *) dest;
   6616 		const uint8_t *src8 = (const uint8_t *) src;
   6617 		for (uintptr_t i = 0; i < n; i++) {
   6618 			dest8[i] = src8[i];
   6619 		}
   6620 		return dest;
   6621 	} else {
   6622 		uint8_t *dest8 = (uint8_t *) dest;
   6623 		const uint8_t *src8 = (const uint8_t *) src;
   6624 		for (uintptr_t i = n; i; i--) {
   6625 			dest8[i - 1] = src8[i - 1];
   6626 		}
   6627 		return dest;
   6628 	}
   6629 }
   6630 
   6631 #endif
   6632 
   6633 #ifdef UI_ESSENCE
   6634 
   6635 const int UI_KEYCODE_A = ES_SCANCODE_A;
   6636 const int UI_KEYCODE_0 = ES_SCANCODE_0;
   6637 const int UI_KEYCODE_BACKSPACE = ES_SCANCODE_BACKSPACE;
   6638 const int UI_KEYCODE_DELETE = ES_SCANCODE_DELETE;
   6639 const int UI_KEYCODE_DOWN = ES_SCANCODE_DOWN_ARROW;
   6640 const int UI_KEYCODE_END = ES_SCANCODE_END;
   6641 const int UI_KEYCODE_ENTER = ES_SCANCODE_ENTER;
   6642 const int UI_KEYCODE_ESCAPE = ES_SCANCODE_ESCAPE;
   6643 const int UI_KEYCODE_F1 = ES_SCANCODE_F1;
   6644 const int UI_KEYCODE_HOME = ES_SCANCODE_HOME;
   6645 const int UI_KEYCODE_LEFT = ES_SCANCODE_LEFT_ARROW;
   6646 const int UI_KEYCODE_RIGHT = ES_SCANCODE_RIGHT_ARROW;
   6647 const int UI_KEYCODE_SPACE = ES_SCANCODE_SPACE;
   6648 const int UI_KEYCODE_TAB = ES_SCANCODE_TAB;
   6649 const int UI_KEYCODE_UP = ES_SCANCODE_UP_ARROW;
   6650 const int UI_KEYCODE_INSERT = ES_SCANCODE_INSERT;
   6651 const int UI_KEYCODE_PAGE_UP = ES_SCANCODE_PAGE_UP;
   6652 const int UI_KEYCODE_PAGE_DOWN = ES_SCANCODE_PAGE_DOWN;
   6653 
   6654 int _UIWindowMessage(UIElement *element, UIMessage message, int di, void *dp) {
   6655 	if (message == UI_MSG_DEALLOCATE) {
   6656 		// TODO Non-main windows.
   6657 		element->window = NULL;
   6658 		EsInstanceCloseReference(ui.instance);
   6659 	}
   6660 
   6661 	return _UIWindowMessageCommon(element, message, di, dp);
   6662 }
   6663 
   6664 void UIInitialise() {
   6665 	_UIInitialiseCommon();
   6666 
   6667 	while (true) {
   6668 		EsMessage *message = EsMessageReceive();
   6669 
   6670 		if (message->type == ES_MSG_INSTANCE_CREATE) {
   6671 			ui.instance = EsInstanceCreate(message, NULL, 0);
   6672 			break;
   6673 		}
   6674 	}
   6675 }
   6676 
   6677 bool _UIMessageLoopSingle(int *result) {
   6678 	if (ui.animating) {
   6679 		// TODO.
   6680 	} else {
   6681 		_UIMessageProcess(EsMessageReceive());
   6682 	}
   6683 
   6684 	return true;
   6685 }
   6686 
   6687 UIMenu *UIMenuCreate(UIElement *parent, uint32_t flags) {
   6688 	ui.menuIndex = 0;
   6689 	return EsMenuCreate(parent->window->window, ES_MENU_AT_CURSOR);
   6690 }
   6691 
   6692 void _UIMenuItemCallback(EsMenu *menu, EsGeneric context) {
   6693 	((void (*)(void *)) ui.menuData[context.u * 2 + 0])(ui.menuData[context.u * 2 + 1]);
   6694 }
   6695 
   6696 void UIMenuAddItem(UIMenu *menu, uint32_t flags, const char *label, ptrdiff_t labelBytes, void (*invoke)(void *cp), void *cp) {
   6697 	EsAssert(ui.menuIndex < 128);
   6698 	ui.menuData[ui.menuIndex * 2 + 0] = (void *) invoke;
   6699 	ui.menuData[ui.menuIndex * 2 + 1] = cp;
   6700 	EsMenuAddItem(menu, (flags & UI_BUTTON_CHECKED) ? ES_MENU_ITEM_CHECKED : ES_FLAGS_DEFAULT,
   6701 			label, labelBytes, _UIMenuItemCallback, ui.menuIndex);
   6702 	ui.menuIndex++;
   6703 }
   6704 
   6705 void UIMenuShow(UIMenu *menu) {
   6706 	EsMenuShow(menu);
   6707 }
   6708 
   6709 int _UIWindowCanvasMessage(EsElement *element, EsMessage *message) {
   6710 	UIWindow *window = (UIWindow *) element->window->userData.p;
   6711 
   6712 	if (!window) {
   6713 		return 0;
   6714 	} else if (message->type == ES_MSG_PAINT) {
   6715 		EsRectangle bounds = ES_RECT_4PD(message->painter->offsetX, message->painter->offsetY, window->width, window->height);
   6716 		EsDrawBitmap(message->painter, bounds, window->bits, window->width * 4, 0xFFFF);
   6717 	} else if (message->type == ES_MSG_LAYOUT) {
   6718 		EsElementGetSize(element, &window->width, &window->height);
   6719 		window->bits = (uint32_t *) UI_REALLOC(window->bits, window->width * window->height * 4);
   6720 		window->e.bounds = UI_RECT_2S(window->width, window->height);
   6721 		window->e.clip = UI_RECT_2S(window->width, window->height);
   6722 		UIElementRelayout(&window->e);
   6723 		_UIUpdate();
   6724 	} else if (message->type == ES_MSG_SCROLL_WHEEL) {
   6725 		_UIWindowInputEvent(window, UI_MSG_MOUSE_WHEEL, -message->scrollWheel.dy, 0);
   6726 	} else if (message->type == ES_MSG_MOUSE_MOVED || message->type == ES_MSG_HOVERED_END
   6727 			|| message->type == ES_MSG_MOUSE_LEFT_DRAG || message->type == ES_MSG_MOUSE_RIGHT_DRAG || message->type == ES_MSG_MOUSE_MIDDLE_DRAG) {
   6728 		EsPoint point = EsMouseGetPosition(element);
   6729 		window->cursorX = point.x, window->cursorY = point.y;
   6730 		_UIWindowInputEvent(window, UI_MSG_MOUSE_MOVE, 0, 0);
   6731 	} else if (message->type == ES_MSG_KEY_UP) {
   6732 		window->ctrl = EsKeyboardIsCtrlHeld();
   6733 		window->shift = EsKeyboardIsShiftHeld();
   6734 		window->alt = EsKeyboardIsAltHeld();
   6735 	} else if (message->type == ES_MSG_KEY_DOWN) {
   6736 		window->ctrl = EsKeyboardIsCtrlHeld();
   6737 		window->shift = EsKeyboardIsShiftHeld();
   6738 		window->alt = EsKeyboardIsAltHeld();
   6739 		UIKeyTyped m = { 0 };
   6740 		char c[64];
   6741 		m.text = c;
   6742 		m.textBytes = EsMessageGetInputText(message, c);
   6743 		m.code = message->keyboard.scancode;
   6744 		return _UIWindowInputEvent(window, UI_MSG_KEY_TYPED, 0, &m) ? ES_HANDLED : 0;
   6745 	} else if (message->type == ES_MSG_MOUSE_LEFT_CLICK) {
   6746 		_UIInspectorSetFocusedWindow(window);
   6747 	} else if (message->type == ES_MSG_USER_START) {
   6748 		UIElementMessage(&window->e, (UIMessage) message->user.context1.u, 0, (void *) message->user.context2.p);
   6749 		_UIUpdate();
   6750 	} else if (message->type == ES_MSG_GET_CURSOR) {
   6751 		message->cursorStyle = ES_CURSOR_NORMAL;
   6752 		if (window->cursor == UI_CURSOR_TEXT)              message->cursorStyle = ES_CURSOR_TEXT;
   6753 		if (window->cursor == UI_CURSOR_SPLIT_V)           message->cursorStyle = ES_CURSOR_SPLIT_VERTICAL;
   6754 		if (window->cursor == UI_CURSOR_SPLIT_H)           message->cursorStyle = ES_CURSOR_SPLIT_HORIZONTAL;
   6755 		if (window->cursor == UI_CURSOR_FLIPPED_ARROW)     message->cursorStyle = ES_CURSOR_SELECT_LINES;
   6756 		if (window->cursor == UI_CURSOR_CROSS_HAIR)        message->cursorStyle = ES_CURSOR_CROSS_HAIR_PICK;
   6757 		if (window->cursor == UI_CURSOR_HAND)              message->cursorStyle = ES_CURSOR_HAND_HOVER;
   6758 		if (window->cursor == UI_CURSOR_RESIZE_UP)         message->cursorStyle = ES_CURSOR_RESIZE_VERTICAL;
   6759 		if (window->cursor == UI_CURSOR_RESIZE_LEFT)       message->cursorStyle = ES_CURSOR_RESIZE_HORIZONTAL;
   6760 		if (window->cursor == UI_CURSOR_RESIZE_UP_RIGHT)   message->cursorStyle = ES_CURSOR_RESIZE_DIAGONAL_1;
   6761 		if (window->cursor == UI_CURSOR_RESIZE_UP_LEFT)    message->cursorStyle = ES_CURSOR_RESIZE_DIAGONAL_2;
   6762 		if (window->cursor == UI_CURSOR_RESIZE_DOWN)       message->cursorStyle = ES_CURSOR_RESIZE_VERTICAL;
   6763 		if (window->cursor == UI_CURSOR_RESIZE_RIGHT)      message->cursorStyle = ES_CURSOR_RESIZE_HORIZONTAL;
   6764 		if (window->cursor == UI_CURSOR_RESIZE_DOWN_RIGHT) message->cursorStyle = ES_CURSOR_RESIZE_DIAGONAL_1;
   6765 		if (window->cursor == UI_CURSOR_RESIZE_DOWN_LEFT)  message->cursorStyle = ES_CURSOR_RESIZE_DIAGONAL_2;
   6766 	}
   6767 
   6768 	else if (message->type == ES_MSG_MOUSE_LEFT_DOWN)   _UIWindowInputEvent(window, UI_MSG_LEFT_DOWN, 0, 0);
   6769 	else if (message->type == ES_MSG_MOUSE_LEFT_UP)     _UIWindowInputEvent(window, UI_MSG_LEFT_UP, 0, 0);
   6770 	else if (message->type == ES_MSG_MOUSE_MIDDLE_DOWN) _UIWindowInputEvent(window, UI_MSG_MIDDLE_DOWN, 0, 0);
   6771 	else if (message->type == ES_MSG_MOUSE_MIDDLE_UP)   _UIWindowInputEvent(window, UI_MSG_MIDDLE_UP, 0, 0);
   6772 	else if (message->type == ES_MSG_MOUSE_RIGHT_DOWN)  _UIWindowInputEvent(window, UI_MSG_RIGHT_DOWN, 0, 0);
   6773 	else if (message->type == ES_MSG_MOUSE_RIGHT_UP)    _UIWindowInputEvent(window, UI_MSG_RIGHT_UP, 0, 0);
   6774 
   6775 	else return 0;
   6776 
   6777 	return ES_HANDLED;
   6778 }
   6779 
   6780 UIWindow *UIWindowCreate(UIWindow *owner, uint32_t flags, const char *cTitle, int width, int height) {
   6781 	_UIMenusClose();
   6782 
   6783 	UIWindow *window = (UIWindow *) UIElementCreate(sizeof(UIWindow), NULL, flags | UI_ELEMENT_WINDOW, _UIWindowMessage, "Window");
   6784 	_UIWindowAdd(window);
   6785 	if (owner) window->scale = owner->scale;
   6786 
   6787 	if (flags & UI_WINDOW_MENU) {
   6788 		// TODO.
   6789 	} else {
   6790 		// TODO Non-main windows.
   6791 		window->window = ui.instance->window;
   6792 		window->window->userData = window;
   6793 		window->canvas = EsCustomElementCreate(window->window, ES_CELL_FILL | ES_ELEMENT_FOCUSABLE);
   6794 		window->canvas->messageUser = _UIWindowCanvasMessage;
   6795 		EsWindowSetTitle(window->window, cTitle, -1);
   6796 		EsElementFocus(window->canvas);
   6797 	}
   6798 
   6799 	return window;
   6800 }
   6801 
   6802 void _UIWindowEndPaint(UIWindow *window, UIPainter *painter) {
   6803 	EsElementRepaint(window->canvas, &window->updateRegion);
   6804 }
   6805 
   6806 void _UIWindowSetCursor(UIWindow *window, int cursor) {
   6807 	window->cursor = cursor;
   6808 }
   6809 
   6810 void _UIWindowGetScreenPosition(UIWindow *window, int *_x, int *_y) {
   6811 	EsRectangle r = EsElementGetScreenBounds(window->window);
   6812 	*_x = r.l, *_y = r.t;
   6813 }
   6814 
   6815 void UIWindowPostMessage(UIWindow *window, UIMessage message, void *_dp) {
   6816 	EsMessage m = {};
   6817 	m.type = ES_MSG_USER_START;
   6818 	m.user.context1.u = message;
   6819 	m.user.context2.p = _dp;
   6820 	EsMessagePost(window->canvas, &m);
   6821 }
   6822 
   6823 void _UIClipboardWriteText(UIWindow *window, char *text) {
   6824 	EsClipboardAddText(ES_CLIPBOARD_PRIMARY, text, -1);
   6825 	UI_FREE(text);
   6826 }
   6827 
   6828 char *_UIClipboardReadTextStart(UIWindow *window, size_t *bytes) {
   6829 	return EsClipboardReadText(ES_CLIPBOARD_PRIMARY, bytes, NULL);
   6830 }
   6831 
   6832 void _UIClipboardReadTextEnd(UIWindow *window, char *text) {
   6833 	EsHeapFree(text);
   6834 }
   6835 
   6836 #endif
   6837 
   6838 #ifdef UI_COCOA
   6839 
   6840 // TODO Standard keyboard shortcuts (Command+Q, Command+W).
   6841 
   6842 const int UI_KEYCODE_A = -100; // TODO Keyboard layout support.
   6843 const int UI_KEYCODE_F1 = -70;
   6844 const int UI_KEYCODE_0 = -50;
   6845 const int UI_KEYCODE_INSERT = -30;
   6846 
   6847 const int UI_KEYCODE_BACKSPACE = kVK_Delete;
   6848 const int UI_KEYCODE_DELETE = kVK_ForwardDelete;
   6849 const int UI_KEYCODE_DOWN = kVK_DownArrow;
   6850 const int UI_KEYCODE_END = kVK_End;
   6851 const int UI_KEYCODE_ENTER = kVK_Return;
   6852 const int UI_KEYCODE_ESCAPE = kVK_Escape;
   6853 const int UI_KEYCODE_HOME = kVK_Home;
   6854 const int UI_KEYCODE_LEFT = kVK_LeftArrow;
   6855 const int UI_KEYCODE_RIGHT = kVK_RightArrow;
   6856 const int UI_KEYCODE_SPACE = kVK_Space;
   6857 const int UI_KEYCODE_TAB = kVK_Tab;
   6858 const int UI_KEYCODE_UP = kVK_UpArrow;
   6859 const int UI_KEYCODE_BACKTICK = kVK_ANSI_Grave; // TODO Keyboard layout support.
   6860 const int UI_KEYCODE_PAGE_UP = kVK_PageUp;
   6861 const int UI_KEYCODE_PAGE_DOWN = kVK_PageDown;
   6862 
   6863 int (*_cocoaAppMain)(int, char **);
   6864 int _cocoaArgc;
   6865 char **_cocoaArgv;
   6866 
   6867 struct _UIPostedMessage {
   6868 	UIMessage message;
   6869 	void *dp;
   6870 };
   6871 
   6872 char *_UIUTF8StringFromNSString(NSString *string) {
   6873 	NSUInteger size = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
   6874 	char *buffer = (char *) UI_MALLOC(size + 1);
   6875 	buffer[size] = 0;
   6876 	[string getBytes:buffer maxLength:size usedLength:NULL encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, [string length]) remainingRange:NULL];
   6877 	return buffer;
   6878 }
   6879 
   6880 int _UICocoaRemapKey(int code) {
   6881 	if (code == kVK_ANSI_A) { return UI_KEYCODE_LETTER('A'); }
   6882 	if (code == kVK_ANSI_B) { return UI_KEYCODE_LETTER('B'); }
   6883 	if (code == kVK_ANSI_C) { return UI_KEYCODE_LETTER('C'); }
   6884 	if (code == kVK_ANSI_D) { return UI_KEYCODE_LETTER('D'); }
   6885 	if (code == kVK_ANSI_E) { return UI_KEYCODE_LETTER('E'); }
   6886 	if (code == kVK_ANSI_F) { return UI_KEYCODE_LETTER('F'); }
   6887 	if (code == kVK_ANSI_G) { return UI_KEYCODE_LETTER('G'); }
   6888 	if (code == kVK_ANSI_H) { return UI_KEYCODE_LETTER('H'); }
   6889 	if (code == kVK_ANSI_I) { return UI_KEYCODE_LETTER('I'); }
   6890 	if (code == kVK_ANSI_J) { return UI_KEYCODE_LETTER('J'); }
   6891 	if (code == kVK_ANSI_K) { return UI_KEYCODE_LETTER('K'); }
   6892 	if (code == kVK_ANSI_L) { return UI_KEYCODE_LETTER('L'); }
   6893 	if (code == kVK_ANSI_M) { return UI_KEYCODE_LETTER('M'); }
   6894 	if (code == kVK_ANSI_N) { return UI_KEYCODE_LETTER('N'); }
   6895 	if (code == kVK_ANSI_O) { return UI_KEYCODE_LETTER('O'); }
   6896 	if (code == kVK_ANSI_P) { return UI_KEYCODE_LETTER('P'); }
   6897 	if (code == kVK_ANSI_Q) { return UI_KEYCODE_LETTER('Q'); }
   6898 	if (code == kVK_ANSI_R) { return UI_KEYCODE_LETTER('R'); }
   6899 	if (code == kVK_ANSI_S) { return UI_KEYCODE_LETTER('S'); }
   6900 	if (code == kVK_ANSI_T) { return UI_KEYCODE_LETTER('T'); }
   6901 	if (code == kVK_ANSI_U) { return UI_KEYCODE_LETTER('U'); }
   6902 	if (code == kVK_ANSI_V) { return UI_KEYCODE_LETTER('V'); }
   6903 	if (code == kVK_ANSI_W) { return UI_KEYCODE_LETTER('W'); }
   6904 	if (code == kVK_ANSI_X) { return UI_KEYCODE_LETTER('X'); }
   6905 	if (code == kVK_ANSI_Y) { return UI_KEYCODE_LETTER('Y'); }
   6906 	if (code == kVK_ANSI_Z) { return UI_KEYCODE_LETTER('Z'); }
   6907 
   6908 	if (code == kVK_ANSI_0) { return UI_KEYCODE_DIGIT('0'); }
   6909 	if (code == kVK_ANSI_1) { return UI_KEYCODE_DIGIT('1'); }
   6910 	if (code == kVK_ANSI_2) { return UI_KEYCODE_DIGIT('2'); }
   6911 	if (code == kVK_ANSI_3) { return UI_KEYCODE_DIGIT('3'); }
   6912 	if (code == kVK_ANSI_4) { return UI_KEYCODE_DIGIT('4'); }
   6913 	if (code == kVK_ANSI_5) { return UI_KEYCODE_DIGIT('5'); }
   6914 	if (code == kVK_ANSI_6) { return UI_KEYCODE_DIGIT('6'); }
   6915 	if (code == kVK_ANSI_7) { return UI_KEYCODE_DIGIT('7'); }
   6916 	if (code == kVK_ANSI_8) { return UI_KEYCODE_DIGIT('8'); }
   6917 	if (code == kVK_ANSI_9) { return UI_KEYCODE_DIGIT('9'); }
   6918 
   6919 	if (code == kVK_F1)  { return UI_KEYCODE_FKEY( 1); }
   6920 	if (code == kVK_F2)  { return UI_KEYCODE_FKEY( 2); }
   6921 	if (code == kVK_F3)  { return UI_KEYCODE_FKEY( 3); }
   6922 	if (code == kVK_F4)  { return UI_KEYCODE_FKEY( 4); }
   6923 	if (code == kVK_F5)  { return UI_KEYCODE_FKEY( 5); }
   6924 	if (code == kVK_F6)  { return UI_KEYCODE_FKEY( 6); }
   6925 	if (code == kVK_F7)  { return UI_KEYCODE_FKEY( 7); }
   6926 	if (code == kVK_F8)  { return UI_KEYCODE_FKEY( 8); }
   6927 	if (code == kVK_F9)  { return UI_KEYCODE_FKEY( 9); }
   6928 	if (code == kVK_F10) { return UI_KEYCODE_FKEY(10); }
   6929 	if (code == kVK_F11) { return UI_KEYCODE_FKEY(11); }
   6930 	if (code == kVK_F12) { return UI_KEYCODE_FKEY(12); }
   6931 
   6932 	return code;
   6933 }
   6934 
   6935 @interface UICocoaApplicationDelegate : NSObject<NSApplicationDelegate>
   6936 @end
   6937 
   6938 @interface UICocoaWindowDelegate : NSObject<NSWindowDelegate>
   6939 @property (nonatomic) UIWindow *uiWindow;
   6940 @end
   6941 
   6942 @interface UICocoaMainView : NSView
   6943 - (void)handlePostedMessage:(id)message;
   6944 - (void)eventCommon:(NSEvent *)event;
   6945 @property (nonatomic) UIWindow *uiWindow;
   6946 @end
   6947 
   6948 @implementation UICocoaApplicationDelegate
   6949 - (void)applicationWillFinishLaunching:(NSNotification *)notification {
   6950 	int code = _cocoaAppMain(_cocoaArgc, _cocoaArgv);
   6951 	if (code) exit(code);
   6952 }
   6953 @end
   6954 
   6955 @implementation UICocoaWindowDelegate
   6956 - (void)windowDidBecomeKey:(NSNotification *)notification {
   6957 	UIElementMessage(&_uiWindow->e, UI_MSG_WINDOW_ACTIVATE, 0, 0);
   6958 	_UIUpdate();
   6959 }
   6960 
   6961 - (void)windowDidResize:(NSNotification *)notification {
   6962 	_uiWindow->width = ((UICocoaMainView *) _uiWindow->view).frame.size.width;
   6963 	_uiWindow->height = ((UICocoaMainView *) _uiWindow->view).frame.size.height;
   6964 	_uiWindow->bits = (uint32_t *) UI_REALLOC(_uiWindow->bits, _uiWindow->width * _uiWindow->height * 4);
   6965 	_uiWindow->e.bounds = UI_RECT_2S(_uiWindow->width, _uiWindow->height);
   6966 	_uiWindow->e.clip = UI_RECT_2S(_uiWindow->width, _uiWindow->height);
   6967 	UIElementRelayout(&_uiWindow->e);
   6968 	_UIUpdate();
   6969 }
   6970 @end
   6971 
   6972 @implementation UICocoaMainView
   6973 - (void)handlePostedMessage:(id)_message {
   6974 	_UIPostedMessage *message = (_UIPostedMessage *) _message;
   6975 	_UIWindowInputEvent(_uiWindow, message->message, 0, message->dp);
   6976 	UI_FREE(message);
   6977 }
   6978 
   6979 - (BOOL)acceptsFirstResponder {
   6980 	return YES;
   6981 }
   6982 
   6983 - (void)onMenuItemSelected:(NSMenuItem *)menuItem {
   6984 	((void (*)(void *)) ui.menuData[menuItem.tag * 2 + 0])(ui.menuData[menuItem.tag * 2 + 1]);
   6985 }
   6986 
   6987 - (void)drawRect:(NSRect)dirtyRect {
   6988 	const unsigned char *data = (const unsigned char *) _uiWindow->bits;
   6989 	NSDrawBitmap(NSMakeRect(0, 0, _uiWindow->width, _uiWindow->height), _uiWindow->width, _uiWindow->height,
   6990 			8 /* bits per channel */, 4 /* channels per pixel */,
   6991 			32 /* bits per pixel */, 4 * _uiWindow->width /* bytes per row */, NO /* planar */, YES /* has alpha */,
   6992 			NSDeviceRGBColorSpace /* color space */, &data /* data */);
   6993 }
   6994 
   6995 - (void)eventCommon:(NSEvent *)event {
   6996 	NSPoint cursor = [self convertPoint:[event locationInWindow] fromView:nil];
   6997 	_uiWindow->cursorX = cursor.x, _uiWindow->cursorY = _uiWindow->height - cursor.y - 1;
   6998 	_uiWindow->ctrl = event.modifierFlags & NSEventModifierFlagCommand;
   6999 	_uiWindow->shift = event.modifierFlags & NSEventModifierFlagShift;
   7000 	_uiWindow->alt = event.modifierFlags & NSEventModifierFlagOption;
   7001 }
   7002 
   7003 - (void)keyDown:(NSEvent *)event {
   7004 	[self eventCommon:event];
   7005 	char *text = _UIUTF8StringFromNSString(event.characters);
   7006 	UIKeyTyped m = { .code = _UICocoaRemapKey(event.keyCode), .text = text, .textBytes = (int) strlen(text) };
   7007 	_UIWindowInputEvent(_uiWindow, UI_MSG_KEY_TYPED, 0, &m);
   7008 	UI_FREE(text);
   7009 }
   7010 
   7011 - (void)keyUp:(NSEvent *)event {
   7012 	[self eventCommon:event];
   7013 	UIKeyTyped m = { .code = _UICocoaRemapKey(event.keyCode) };
   7014 	_UIWindowInputEvent(_uiWindow, UI_MSG_KEY_RELEASED, 0, &m);
   7015 }
   7016 
   7017 - (void)mouseMoved:(NSEvent *)event {
   7018 	[self eventCommon:event];
   7019 	_UIWindowInputEvent(_uiWindow, UI_MSG_MOUSE_MOVE, 0, 0);
   7020 }
   7021 
   7022 - (void)mouseExited:(NSEvent *)event { [self mouseMoved:event]; }
   7023 - (void)flagsChanged:(NSEvent *)event { [self mouseMoved:event]; }
   7024 - (void)mouseDragged:(NSEvent *)event { [self mouseMoved:event]; }
   7025 - (void)rightMouseDragged:(NSEvent *)event { [self mouseMoved:event]; }
   7026 - (void)otherMouseDragged:(NSEvent *)event { [self mouseMoved:event]; }
   7027 
   7028 - (void)mouseDown:(NSEvent *)event {
   7029 	[self eventCommon:event];
   7030 	_UIWindowInputEvent(_uiWindow, UI_MSG_LEFT_DOWN, 0, 0);
   7031 }
   7032 
   7033 - (void)mouseUp:(NSEvent *)event {
   7034 	[self eventCommon:event];
   7035 	_UIWindowInputEvent(_uiWindow, UI_MSG_LEFT_UP, 0, 0);
   7036 }
   7037 
   7038 - (void)rightMouseDown:(NSEvent *)event {
   7039 	[self eventCommon:event];
   7040 	_UIWindowInputEvent(_uiWindow, UI_MSG_RIGHT_DOWN, 0, 0);
   7041 }
   7042 
   7043 - (void)rightMouseUp:(NSEvent *)event {
   7044 	[self eventCommon:event];
   7045 	_UIWindowInputEvent(_uiWindow, UI_MSG_RIGHT_UP, 0, 0);
   7046 }
   7047 
   7048 - (void)otherMouseDown:(NSEvent *)event {
   7049 	[self eventCommon:event];
   7050 	_UIWindowInputEvent(_uiWindow, UI_MSG_MIDDLE_DOWN, 0, 0);
   7051 }
   7052 
   7053 - (void)otherMouseUp:(NSEvent *)event {
   7054 	[self eventCommon:event];
   7055 	_UIWindowInputEvent(_uiWindow, UI_MSG_MIDDLE_UP, 0, 0);
   7056 }
   7057 
   7058 - (void)scrollWheel:(NSEvent *)event {
   7059 	[self eventCommon:event];
   7060 	_UIWindowInputEvent(_uiWindow, UI_MSG_MOUSE_WHEEL, -3 * event.deltaY, 0);
   7061 	_UIWindowInputEvent(_uiWindow, UI_MSG_MOUSE_MOVE, 0, 0);
   7062 }
   7063 
   7064 // TODO Animations.
   7065 // TODO Drag and drop.
   7066 // TODO Reporting window close.
   7067 
   7068 @end
   7069 
   7070 int _UIWindowMessage(UIElement *element, UIMessage message, int di, void *dp) {
   7071 	if (message == UI_MSG_DEALLOCATE) {
   7072 		UIWindow *window = (UIWindow *) element;
   7073 		_UIWindowDestroyCommon(window);
   7074 		[window->window close];
   7075 	}
   7076 
   7077 	return _UIWindowMessageCommon(element, message, di, dp);
   7078 }
   7079 
   7080 UIWindow *UIWindowCreate(UIWindow *owner, uint32_t flags, const char *cTitle, int _width, int _height) {
   7081 	_UIMenusClose();
   7082 	UIWindow *window = (UIWindow *) UIElementCreate(sizeof(UIWindow), NULL, flags | UI_ELEMENT_WINDOW, _UIWindowMessage, "Window");
   7083 	_UIWindowAdd(window);
   7084 	if (owner) window->scale = owner->scale;
   7085 
   7086 	NSRect frame = NSMakeRect(0, 0, _width ?: 800, _height ?: 600);
   7087 	NSWindowStyleMask styleMask = NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable | NSWindowStyleMaskTitled;
   7088 	NSWindow *nsWindow = [[NSWindow alloc] initWithContentRect:frame styleMask:styleMask backing:NSBackingStoreBuffered defer:NO];
   7089 	[nsWindow center];
   7090 	[nsWindow setTitle:@(cTitle ?: "untitled")];
   7091 	UICocoaWindowDelegate *delegate = [UICocoaWindowDelegate alloc];
   7092 	[delegate setUiWindow:window];
   7093 	nsWindow.delegate = delegate;
   7094 	UICocoaMainView *view = [UICocoaMainView alloc];
   7095 	window->window = nsWindow;
   7096 	window->view = view;
   7097 	window->width = frame.size.width;
   7098 	window->height = frame.size.height;
   7099 	window->bits = (uint32_t *) UI_REALLOC(window->bits, window->width * window->height * 4);
   7100 	window->e.bounds = UI_RECT_2S(window->width, window->height);
   7101 	window->e.clip = UI_RECT_2S(window->width, window->height);
   7102 	[view setUiWindow:window];
   7103 	[view initWithFrame:frame];
   7104 	nsWindow.contentView = view;
   7105 	[view addTrackingArea:[[NSTrackingArea alloc] initWithRect:frame
   7106 		options:NSTrackingMouseMoved|NSTrackingActiveInKeyWindow|NSTrackingInVisibleRect owner:view userInfo:nil]];
   7107 	[nsWindow setInitialFirstResponder:view];
   7108 	[nsWindow makeKeyAndOrderFront:delegate];
   7109 
   7110 	// TODO UI_WINDOW_MAXIMIZE.
   7111 
   7112 	return window;
   7113 }
   7114 
   7115 void _UIClipboardWriteText(UIWindow *window, char *text) {
   7116 	// TODO Clipboard support.
   7117 }
   7118 
   7119 char *_UIClipboardReadTextStart(UIWindow *window, size_t *bytes) {
   7120 	// TODO Clipboard support.
   7121 	return NULL;
   7122 }
   7123 
   7124 void _UIClipboardReadTextEnd(UIWindow *window, char *text) {
   7125 	UI_FREE(text);
   7126 }
   7127 
   7128 void UIInitialise() {
   7129 	_UIInitialiseCommon();
   7130 }
   7131 
   7132 void _UIWindowSetCursor(UIWindow *window, int cursor) {
   7133 	if      (cursor == UI_CURSOR_TEXT)          [[NSCursor IBeamCursor] set];
   7134 	else if (cursor == UI_CURSOR_SPLIT_V)       [[NSCursor resizeUpDownCursor] set];
   7135 	else if (cursor == UI_CURSOR_SPLIT_H)       [[NSCursor resizeLeftRightCursor] set];
   7136 	else if (cursor == UI_CURSOR_FLIPPED_ARROW) [[NSCursor pointingHandCursor] set];
   7137 	else if (cursor == UI_CURSOR_CROSS_HAIR)    [[NSCursor crosshairCursor] set];
   7138 	else if (cursor == UI_CURSOR_HAND)          [[NSCursor pointingHandCursor] set];
   7139 	else                                        [[NSCursor arrowCursor] set];
   7140 }
   7141 
   7142 void _UIWindowEndPaint(UIWindow *window, UIPainter *painter) {
   7143 	for (int y = painter->clip.t; y < painter->clip.b; y++) {
   7144 		for (int x = painter->clip.l; x < painter->clip.r; x++) {
   7145 			uint32_t *p = &painter->bits[y * painter->width + x];
   7146 			*p = 0xFF000000 | (*p & 0xFF00) | ((*p & 0xFF0000) >> 16) | ((*p & 0xFF) << 16);
   7147 		}
   7148 	}
   7149 
   7150 	[(UICocoaMainView *)window->view setNeedsDisplayInRect:((UICocoaMainView *)window->view).frame];
   7151 }
   7152 
   7153 void _UIWindowGetScreenPosition(UIWindow *window, int *x, int *y) {
   7154 	NSPoint point = [window->window convertPointToScreen:NSMakePoint(0, 0)];
   7155 	*x = point.x, *y = point.y;
   7156 }
   7157 
   7158 UIMenu *UIMenuCreate(UIElement *parent, uint32_t flags) {
   7159 	// TODO Fix the vertical position.
   7160 
   7161 	if (parent->parent) {
   7162 		UIRectangle screenBounds = UIElementScreenBounds(parent);
   7163 		ui.menuX = screenBounds.l;
   7164 		ui.menuY = screenBounds.b;
   7165 	} else {
   7166 		_UIWindowGetScreenPosition(parent->window, &ui.menuX, &ui.menuY);
   7167 		ui.menuX += parent->window->cursorX;
   7168 		ui.menuY += parent->window->cursorY;
   7169 	}
   7170 
   7171 	ui.menuIndex = 0;
   7172 	ui.menuWindow = parent->window;
   7173 
   7174 	NSMenu *menu = [[NSMenu alloc] init];
   7175 	[menu setAutoenablesItems:NO];
   7176 	return menu;
   7177 }
   7178 
   7179 void UIMenuAddItem(UIMenu *menu, uint32_t flags, const char *label, ptrdiff_t labelBytes, void (*invoke)(void *cp), void *cp) {
   7180 	if (ui.menuIndex == 128) return;
   7181 	ui.menuData[ui.menuIndex * 2 + 0] = (void *) invoke;
   7182 	ui.menuData[ui.menuIndex * 2 + 1] = cp;
   7183 	NSString *title = [[NSString alloc] initWithBytes:label length:(labelBytes == -1 ? strlen(label) : labelBytes) encoding:NSUTF8StringEncoding];
   7184 	NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title action:@selector(onMenuItemSelected:) keyEquivalent:@""];
   7185 	item.tag = ui.menuIndex++;
   7186 	if (flags & UI_BUTTON_CHECKED) [item setState:NSControlStateValueOn];
   7187 	[item setEnabled:((flags & UI_ELEMENT_DISABLED) ? NO : YES)];
   7188 	[item setTarget:(UICocoaMainView *)ui.menuWindow->view];
   7189 	[menu addItem:item];
   7190 	[title release];
   7191 	[item release];
   7192 }
   7193 
   7194 void UIMenuShow(UIMenu *menu) {
   7195 	[menu popUpMenuPositioningItem:nil atLocation:NSMakePoint(ui.menuX, ui.menuY) inView:nil];
   7196 	[menu release];
   7197 }
   7198 
   7199 void UIWindowPack(UIWindow *window, int _width) {
   7200 	int width = _width ? _width : UIElementMessage(window->e.children[0], UI_MSG_GET_WIDTH, 0, 0);
   7201 	int height = UIElementMessage(window->e.children[0], UI_MSG_GET_HEIGHT, width, 0);
   7202 	[window->window setContentSize:NSMakeSize(width, height)];
   7203 }
   7204 
   7205 bool _UIMessageLoopSingle(int *result) {
   7206 	// TODO Modal dialog support.
   7207 	return false;
   7208 }
   7209 
   7210 void UIWindowPostMessage(UIWindow *window, UIMessage _message, void *dp) {
   7211 	_UIPostedMessage *message = (_UIPostedMessage *) UI_MALLOC(sizeof(_UIPostedMessage));
   7212 	message->message = _message;
   7213 	message->dp = dp;
   7214 	[(UICocoaMainView*)window->view performSelectorOnMainThread:@selector(handlePostedMessage:) withObject:(id)message waitUntilDone:NO];
   7215 }
   7216 
   7217 int UICocoaMain(int argc, char **argv, int (*appMain)(int, char **)) {
   7218 	_cocoaArgc = argc, _cocoaArgv = argv, _cocoaAppMain = appMain;
   7219 	NSApplication *application = [NSApplication sharedApplication];
   7220 	application.delegate = [[UICocoaApplicationDelegate alloc] init];
   7221 	return NSApplicationMain(argc, (const char **) argv);
   7222 }
   7223 
   7224 #endif
   7225 
   7226 #endif