Files
renderdoc/util/test/demos/apple/nuklear_appkit.h
T
baldurk 856c838def Update copyright years to 2026 and fix copyright ranges
* In a previous update in 2021 many copyright ranges were truncated
  accidentally, and some files have been copy-pasted with wrong years. These
  dates have been fixed based on git history and original copyright messages.
2026-01-05 14:17:28 +00:00

1171 lines
37 KiB
Objective-C

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2022-2026 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
// This is the minimum required to get the RenderDoc demo UI to work
// The keyboard handling for delete is not perfect
/*
* ==============================================================
*
* API
*
* ===============================================================
*/
#ifndef NK_APPKIT_H_
#define NK_APPKIT_H_
struct AppkitFont;
struct nk_appkit_window;
NK_API struct nk_context *nk_appkit_create(nk_appkit_window *win);
NK_API void nk_appkit_init(AppkitFont *font);
NK_API void nk_appkit_shutdown(void);
NK_API void nk_appkit_new_frame(void);
NK_API void nk_appkit_render(struct nk_color clear);
NK_API AppkitFont *nk_appkit_create_font(const char *name, int size);
NK_API void nk_appkit_delete_font(AppkitFont *font);
#endif // #ifndef NK_APPKIT_H_
#if defined(NK_APPKIT_IMPLEMENTATION) || defined(NK_APPKIT_OBJC_IMPLEMENTATION)
#define nk_appkit_release 0
#define nk_appkit_press 1
#define nk_appkit_key_unknown -1
#define nk_appkit_key_0 48
#define nk_appkit_key_1 49
#define nk_appkit_key_2 50
#define nk_appkit_key_3 51
#define nk_appkit_key_4 52
#define nk_appkit_key_5 53
#define nk_appkit_key_6 54
#define nk_appkit_key_7 55
#define nk_appkit_key_8 56
#define nk_appkit_key_9 57
#define nk_appkit_key_A 65
#define nk_appkit_key_B 66
#define nk_appkit_key_C 67
#define nk_appkit_key_D 68
#define nk_appkit_key_E 69
#define nk_appkit_key_F 70
#define nk_appkit_key_G 71
#define nk_appkit_key_H 72
#define nk_appkit_key_I 73
#define nk_appkit_key_J 74
#define nk_appkit_key_K 75
#define nk_appkit_key_L 76
#define nk_appkit_key_M 77
#define nk_appkit_key_N 78
#define nk_appkit_key_O 79
#define nk_appkit_key_P 80
#define nk_appkit_key_Q 81
#define nk_appkit_key_R 82
#define nk_appkit_key_S 83
#define nk_appkit_key_T 84
#define nk_appkit_key_U 85
#define nk_appkit_key_V 86
#define nk_appkit_key_W 87
#define nk_appkit_key_X 88
#define nk_appkit_key_Y 89
#define nk_appkit_key_Z 90
#define nk_appkit_key_enter 301
#define nk_appkit_key_tab 302
#define nk_appkit_key_backspace 303
#define nk_appkit_key_delete 305
#define nk_appkit_key_right 306
#define nk_appkit_key_left 307
#define nk_appkit_key_down 308
#define nk_appkit_key_up 309
#define nk_appkit_key_page_up 310
#define nk_appkit_key_page_down 311
#define nk_appkit_key_home 312
#define nk_appkit_key_end 313
#define nk_appkit_key_left_shift 400
#define nk_appkit_key_left_control 401
#define nk_appkit_key_right_shift 402
#define nk_appkit_key_right_control 403
#define nk_appkit_key_first nk_appkit_key_0
#define nk_appkit_key_last nk_appkit_key_right_control
#define nk_appkit_mouse_button_left 0
#define nk_appkit_mouse_button_right 1
#define nk_appkit_mouse_button_middle 2
struct nk_appkit_window;
typedef void (*nk_appkit_mouse_button_cb)(nk_appkit_window *, int, int, int);
typedef void (*nk_appkit_character_cb)(nk_appkit_window *, unsigned int);
typedef void (*nk_appkit_key_cb)(nk_appkit_window *, int, int);
typedef void (*nk_appkit_scroll_cb)(nk_appkit_window *, double, double);
#endif // #if defined(NK_APPKIT_IMPLEMENTATION) || defined(NK_APPKIT_OBJC_IMPLEMENTATION)
/*
* ==============================================================
*
* IMPLEMENTATION
*
* ===============================================================
*/
#ifdef NK_APPKIT_IMPLEMENTATION
#include <mach/mach_time.h>
#ifndef NK_APPKIT_TEXT_MAX
#define NK_APPKIT_TEXT_MAX 256
#endif
#ifndef NK_APPKIT_DOUBLE_CLICK_LO
#define NK_APPKIT_DOUBLE_CLICK_LO 0.02
#endif
#ifndef NK_APPKIT_DOUBLE_CLICK_HI
#define NK_APPKIT_DOUBLE_CLICK_HI 0.2
#endif
struct AppkitFont
{
struct nk_user_font nk;
float height;
};
static struct nk_appkit
{
nk_appkit_window *win;
struct nk_context ctx;
unsigned int text[NK_APPKIT_TEXT_MAX];
int text_len;
struct nk_vec2 scroll;
double last_button_click;
int is_double_click_down;
struct nk_vec2 double_click_pos;
uint64_t timerFrequency;
} nk_appkit;
bool nk_appkit_core_initialize(void);
void nk_appkit_core_shutdown(void);
nk_appkit_window *nk_appkit_window_create(int width, int height, const char *title);
void nk_appkit_window_delete(nk_appkit_window *window);
bool nk_appkit_window_is_closed(nk_appkit_window *window);
void nk_appkit_window_get_mouse_position(nk_appkit_window *window, double *xpos, double *ypos);
int nk_appkit_window_get_mouse_button_state(nk_appkit_window *window, int button);
int nk_appkit_window_get_key_state(nk_appkit_window *window, int key);
void nk_appkit_window_set_mouse_button_callback(nk_appkit_window *window,
nk_appkit_mouse_button_cb callback);
void nk_appkit_window_set_scroll_callback(nk_appkit_window *window, nk_appkit_scroll_cb callback);
void nk_appkit_window_set_character_callback(nk_appkit_window *window,
nk_appkit_character_cb callback);
void nk_appkit_window_set_key_callback(nk_appkit_window *window, nk_appkit_key_cb callback);
void nk_appkit_drawing_begin(nk_appkit_window *window, u_int8_t r, u_int8_t g, u_int8_t b,
u_int8_t a);
void nk_appkit_drawing_end(nk_appkit_window *window);
void nk_appkit_drawing_filled_rect(nk_appkit_window *window, short x, short y, u_int16_t w,
u_int16_t h, u_int8_t r, u_int8_t g, u_int8_t b, u_int8_t a,
int rounding);
void nk_appkit_drawing_rect(nk_appkit_window *window, short x, short y, u_int16_t w, u_int16_t h,
u_int8_t r, u_int8_t g, u_int8_t b, u_int8_t a, int rounding,
int lineThickness);
void nk_appkit_drawing_scissor(nk_appkit_window *window, short x, short y, u_int16_t w, u_int16_t h);
void nk_appkit_drawing_text(nk_appkit_window *window, short x, short y, u_int16_t w, u_int16_t h,
const char *text, int len, void *font, u_int8_t bgR, u_int8_t bgG,
u_int8_t bgB, u_int8_t bgA, u_int8_t fgR, u_int8_t fgG, u_int8_t fgB,
u_int8_t fgA);
float nk_appkit_drawing_set_font(nk_appkit_window *window, const char *name, float size);
float nk_appkit_drawing_get_text_width(nk_appkit_window *window, const char *text, int len);
static float nk_appkit_font_get_text_width(nk_handle handle, float height, const char *text, int len)
{
AppkitFont *font = (AppkitFont *)handle.ptr;
if(!font || !text)
return 0.0f;
return nk_appkit_drawing_get_text_width(nk_appkit.win, text, len);
}
static void nk_appkit_char_callback(nk_appkit_window *window, unsigned int codepoint)
{
if(nk_appkit.text_len < NK_APPKIT_TEXT_MAX)
nk_appkit.text[nk_appkit.text_len++] = codepoint;
}
static void nk_appkit_scroll_callback(nk_appkit_window *window, double xoff, double yoff)
{
nk_appkit.scroll.x += (float)xoff;
nk_appkit.scroll.y += (float)yoff;
}
static void nk_appkit_mouse_button_callback(nk_appkit_window *window, int button, int action, int mods)
{
double x, y;
if(button != nk_appkit_mouse_button_left)
return;
nk_appkit_window_get_mouse_position(window, &x, &y);
if(action == nk_appkit_press)
{
double now = (double)mach_absolute_time() / nk_appkit.timerFrequency;
double dt = now - nk_appkit.last_button_click;
if(dt > NK_APPKIT_DOUBLE_CLICK_LO && dt < NK_APPKIT_DOUBLE_CLICK_HI)
{
nk_appkit.is_double_click_down = nk_true;
nk_appkit.double_click_pos = nk_vec2((float)x, (float)y);
}
nk_appkit.last_button_click = now;
}
else
nk_appkit.is_double_click_down = nk_false;
}
NK_API struct nk_context *nk_appkit_create(nk_appkit_window *win)
{
nk_appkit.win = win;
nk_appkit_window_set_character_callback(win, nk_appkit_char_callback);
nk_appkit_window_set_scroll_callback(win, nk_appkit_scroll_callback);
nk_appkit_window_set_mouse_button_callback(win, nk_appkit_mouse_button_callback);
mach_timebase_info_data_t info;
mach_timebase_info(&info);
nk_appkit.timerFrequency = (info.denom * 1e9) / info.numer;
nk_appkit.is_double_click_down = nk_false;
nk_appkit.double_click_pos = nk_vec2(0, 0);
nk_appkit.last_button_click = 0;
return &nk_appkit.ctx;
}
NK_API void nk_appkit_init(AppkitFont *font)
{
struct nk_user_font *nkFont = &font->nk;
nkFont->userdata = nk_handle_ptr(font);
nkFont->height = font->height;
nkFont->width = nk_appkit_font_get_text_width;
nk_init_default(&nk_appkit.ctx, nkFont);
nk_appkit.ctx.clip.userdata = nk_handle_ptr(0);
}
NK_API void nk_appkit_shutdown(void)
{
nk_free(&nk_appkit.ctx);
memset(&nk_appkit, 0, sizeof(nk_appkit));
}
NK_API AppkitFont *nk_appkit_create_font(const char *name, int size)
{
AppkitFont *font = (AppkitFont *)calloc(1, sizeof(AppkitFont));
if(!font)
return NULL;
font->height = nk_appkit_drawing_set_font(nk_appkit.win, name, size);
return font;
}
NK_API void nk_appkit_delete_font(AppkitFont *font)
{
if(!font)
return;
free(font);
}
NK_API void nk_appkit_render(struct nk_color clear)
{
const struct nk_command *cmd;
struct nk_context *ctx = &nk_appkit.ctx;
nk_appkit_drawing_begin(nk_appkit.win, clear.r, clear.g, clear.b, clear.a);
nk_foreach(cmd, &nk_appkit.ctx)
{
switch(cmd->type)
{
case NK_COMMAND_NOP: break;
case NK_COMMAND_SCISSOR:
{
const struct nk_command_scissor *s = (const struct nk_command_scissor *)cmd;
nk_appkit_drawing_scissor(nk_appkit.win, s->x, s->y, s->w, s->h);
break;
}
case NK_COMMAND_LINE: assert(false); break;
case NK_COMMAND_CURVE: assert(false); break;
case NK_COMMAND_RECT:
{
const struct nk_command_rect *r = (const struct nk_command_rect *)cmd;
nk_appkit_drawing_rect(nk_appkit.win, r->x, r->y, r->w, r->h, r->color.r, r->color.g,
r->color.b, r->color.a, r->rounding, r->line_thickness);
break;
}
case NK_COMMAND_RECT_FILLED:
{
const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled *)cmd;
nk_appkit_drawing_filled_rect(nk_appkit.win, r->x, r->y, r->w, r->h, r->color.r, r->color.g,
r->color.b, r->color.a, r->rounding);
break;
}
case NK_COMMAND_RECT_MULTI_COLOR: assert(false); break;
case NK_COMMAND_CIRCLE: assert(false); break;
case NK_COMMAND_CIRCLE_FILLED: assert(false); break;
case NK_COMMAND_ARC: assert(false); break;
case NK_COMMAND_ARC_FILLED: assert(false); break;
case NK_COMMAND_TRIANGLE: assert(false); break;
case NK_COMMAND_TRIANGLE_FILLED: assert(false); break;
case NK_COMMAND_POLYGON: assert(false); break;
case NK_COMMAND_POLYGON_FILLED: assert(false); break;
case NK_COMMAND_POLYLINE: assert(false); break;
case NK_COMMAND_TEXT:
{
const struct nk_command_text *t = (const struct nk_command_text *)cmd;
nk_appkit_drawing_text(nk_appkit.win, t->x, t->y, t->w, t->h, (const char *)t->string,
t->length, (AppkitFont *)t->font->userdata.ptr, t->background.r,
t->background.g, t->background.b, t->background.a, t->foreground.r,
t->foreground.g, t->foreground.b, t->foreground.a);
break;
}
case NK_COMMAND_IMAGE: assert(false); break;
case NK_COMMAND_CUSTOM: assert(false); break;
}
}
nk_appkit_drawing_end(nk_appkit.win);
nk_clear(ctx);
}
NK_API void nk_appkit_new_frame(void)
{
int i;
double x, y;
struct nk_context *ctx = &nk_appkit.ctx;
nk_appkit_window *win = nk_appkit.win;
nk_input_begin(ctx);
for(i = 0; i < nk_appkit.text_len; ++i)
nk_input_unicode(ctx, nk_appkit.text[i]);
nk_input_key(ctx, NK_KEY_DEL,
nk_appkit_window_get_key_state(win, nk_appkit_key_delete) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_ENTER,
nk_appkit_window_get_key_state(win, nk_appkit_key_enter) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_TAB,
nk_appkit_window_get_key_state(win, nk_appkit_key_tab) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_BACKSPACE,
nk_appkit_window_get_key_state(win, nk_appkit_key_backspace) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_UP,
nk_appkit_window_get_key_state(win, nk_appkit_key_up) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_DOWN,
nk_appkit_window_get_key_state(win, nk_appkit_key_down) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_TEXT_START,
nk_appkit_window_get_key_state(win, nk_appkit_key_home) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_TEXT_END,
nk_appkit_window_get_key_state(win, nk_appkit_key_end) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_SCROLL_START,
nk_appkit_window_get_key_state(win, nk_appkit_key_home) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_SCROLL_END,
nk_appkit_window_get_key_state(win, nk_appkit_key_end) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_SCROLL_DOWN,
nk_appkit_window_get_key_state(win, nk_appkit_key_page_down) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_SCROLL_UP,
nk_appkit_window_get_key_state(win, nk_appkit_key_page_up) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_SHIFT,
nk_appkit_window_get_key_state(win, nk_appkit_key_left_shift) == nk_appkit_press ||
nk_appkit_window_get_key_state(win, nk_appkit_key_right_shift) == nk_appkit_press);
if(nk_appkit_window_get_key_state(win, nk_appkit_key_left_control) == nk_appkit_press ||
nk_appkit_window_get_key_state(win, nk_appkit_key_right_control) == nk_appkit_press)
{
nk_input_key(ctx, NK_KEY_COPY,
nk_appkit_window_get_key_state(win, nk_appkit_key_C) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_PASTE,
nk_appkit_window_get_key_state(win, nk_appkit_key_V) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_CUT,
nk_appkit_window_get_key_state(win, nk_appkit_key_X) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_TEXT_UNDO,
nk_appkit_window_get_key_state(win, nk_appkit_key_Z) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_TEXT_REDO,
nk_appkit_window_get_key_state(win, nk_appkit_key_R) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT,
nk_appkit_window_get_key_state(win, nk_appkit_key_left) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT,
nk_appkit_window_get_key_state(win, nk_appkit_key_right) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_TEXT_LINE_START,
nk_appkit_window_get_key_state(win, nk_appkit_key_B) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_TEXT_LINE_END,
nk_appkit_window_get_key_state(win, nk_appkit_key_E) == nk_appkit_press);
}
else
{
nk_input_key(ctx, NK_KEY_LEFT,
nk_appkit_window_get_key_state(win, nk_appkit_key_left) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_RIGHT,
nk_appkit_window_get_key_state(win, nk_appkit_key_right) == nk_appkit_press);
nk_input_key(ctx, NK_KEY_COPY, 0);
nk_input_key(ctx, NK_KEY_PASTE, 0);
nk_input_key(ctx, NK_KEY_CUT, 0);
nk_input_key(ctx, NK_KEY_SHIFT, 0);
}
nk_appkit_window_get_mouse_position(win, &x, &y);
nk_input_motion(ctx, (int)x, (int)y);
nk_input_button(
ctx, NK_BUTTON_LEFT, (int)x, (int)y,
nk_appkit_window_get_mouse_button_state(win, nk_appkit_mouse_button_left) == nk_appkit_press);
nk_input_button(ctx, NK_BUTTON_MIDDLE, (int)x, (int)y,
nk_appkit_window_get_mouse_button_state(win, nk_appkit_mouse_button_middle) ==
nk_appkit_press);
nk_input_button(ctx, NK_BUTTON_RIGHT, (int)x, (int)y,
nk_appkit_window_get_mouse_button_state(win, nk_appkit_mouse_button_right) ==
nk_appkit_press);
nk_input_button(ctx, NK_BUTTON_DOUBLE, (int)nk_appkit.double_click_pos.x,
(int)nk_appkit.double_click_pos.y, nk_appkit.is_double_click_down);
nk_input_scroll(ctx, nk_appkit.scroll);
nk_input_end(&nk_appkit.ctx);
nk_appkit.text_len = 0;
nk_appkit.scroll = nk_vec2(0, 0);
}
#endif // #ifdef NK_APPKIT_IMPLEMENTATION
#ifdef NK_APPKIT_OBJC_IMPLEMENTATION
#import <AppKit/AppKit.h>
typedef struct nk_appkit_window
{
NSWindow *nsWindow;
id delegate;
NSView *nsView;
NSImage *nsImage;
NSAutoreleasePool *nsPool;
NSSize size;
bool shouldClose;
char mouseButtons[3];
char keys[nk_appkit_key_last + 1];
nk_appkit_character_cb characterCallback;
nk_appkit_key_cb keyCallback;
nk_appkit_mouse_button_cb mouseButtonCallback;
nk_appkit_scroll_cb scrollCallback;
} nk_appkit_window;
typedef struct nk_appkit_state
{
short int keycodes[256];
nk_appkit_window *window;
NSFont *nsFont;
float fontHeight;
CGEventSourceRef eventSource;
id nsAppDelegate;
id keyUpMonitor;
} nk_appkit_state;
static nk_appkit_state s_state = {0};
static void CreateKeyTables(void)
{
memset(s_state.keycodes, -1, sizeof(s_state.keycodes));
s_state.keycodes[0x1D] = nk_appkit_key_0;
s_state.keycodes[0x12] = nk_appkit_key_1;
s_state.keycodes[0x13] = nk_appkit_key_2;
s_state.keycodes[0x14] = nk_appkit_key_3;
s_state.keycodes[0x15] = nk_appkit_key_4;
s_state.keycodes[0x17] = nk_appkit_key_5;
s_state.keycodes[0x16] = nk_appkit_key_6;
s_state.keycodes[0x1A] = nk_appkit_key_7;
s_state.keycodes[0x1C] = nk_appkit_key_8;
s_state.keycodes[0x19] = nk_appkit_key_9;
s_state.keycodes[0x00] = nk_appkit_key_A;
s_state.keycodes[0x0B] = nk_appkit_key_B;
s_state.keycodes[0x08] = nk_appkit_key_C;
s_state.keycodes[0x02] = nk_appkit_key_D;
s_state.keycodes[0x0E] = nk_appkit_key_E;
s_state.keycodes[0x03] = nk_appkit_key_F;
s_state.keycodes[0x05] = nk_appkit_key_G;
s_state.keycodes[0x04] = nk_appkit_key_H;
s_state.keycodes[0x22] = nk_appkit_key_I;
s_state.keycodes[0x26] = nk_appkit_key_J;
s_state.keycodes[0x28] = nk_appkit_key_K;
s_state.keycodes[0x25] = nk_appkit_key_L;
s_state.keycodes[0x2E] = nk_appkit_key_M;
s_state.keycodes[0x2D] = nk_appkit_key_N;
s_state.keycodes[0x1F] = nk_appkit_key_O;
s_state.keycodes[0x23] = nk_appkit_key_P;
s_state.keycodes[0x0C] = nk_appkit_key_Q;
s_state.keycodes[0x0F] = nk_appkit_key_R;
s_state.keycodes[0x01] = nk_appkit_key_S;
s_state.keycodes[0x11] = nk_appkit_key_T;
s_state.keycodes[0x20] = nk_appkit_key_U;
s_state.keycodes[0x09] = nk_appkit_key_V;
s_state.keycodes[0x0D] = nk_appkit_key_W;
s_state.keycodes[0x07] = nk_appkit_key_X;
s_state.keycodes[0x10] = nk_appkit_key_Y;
s_state.keycodes[0x06] = nk_appkit_key_Z;
s_state.keycodes[0x33] = nk_appkit_key_backspace;
s_state.keycodes[0x75] = nk_appkit_key_delete;
s_state.keycodes[0x7D] = nk_appkit_key_down;
s_state.keycodes[0x77] = nk_appkit_key_end;
s_state.keycodes[0x24] = nk_appkit_key_enter;
s_state.keycodes[0x73] = nk_appkit_key_home;
s_state.keycodes[0x7B] = nk_appkit_key_left;
s_state.keycodes[0x79] = nk_appkit_key_page_down;
s_state.keycodes[0x74] = nk_appkit_key_page_up;
s_state.keycodes[0x7C] = nk_appkit_key_right;
s_state.keycodes[0x30] = nk_appkit_key_tab;
s_state.keycodes[0x7E] = nk_appkit_key_up;
s_state.keycodes[0x33] = nk_appkit_key_backspace;
s_state.keycodes[0x75] = nk_appkit_key_delete;
s_state.keycodes[0x7D] = nk_appkit_key_down;
s_state.keycodes[0x77] = nk_appkit_key_end;
s_state.keycodes[0x24] = nk_appkit_key_enter;
s_state.keycodes[0x73] = nk_appkit_key_home;
s_state.keycodes[0x7B] = nk_appkit_key_left;
s_state.keycodes[0x3B] = nk_appkit_key_left_control;
s_state.keycodes[0x38] = nk_appkit_key_left_shift;
s_state.keycodes[0x79] = nk_appkit_key_page_down;
s_state.keycodes[0x74] = nk_appkit_key_page_up;
s_state.keycodes[0x7C] = nk_appkit_key_right;
s_state.keycodes[0x3E] = nk_appkit_key_right_control;
s_state.keycodes[0x3C] = nk_appkit_key_right_shift;
s_state.keycodes[0x30] = nk_appkit_key_tab;
s_state.keycodes[0x7E] = nk_appkit_key_up;
}
static int TranslateKey(unsigned int key)
{
if(key >= sizeof(s_state.keycodes) / sizeof(s_state.keycodes[0]))
return nk_appkit_key_unknown;
return s_state.keycodes[key];
}
static void InputMouseClick(nk_appkit_window *window, int button, int action)
{
if(button < 0 || button > 2)
return;
window->mouseButtons[button] = (char)action;
if(window->mouseButtonCallback)
window->mouseButtonCallback(window, button, action, 0);
}
static void InputKey(nk_appkit_window *window, int key, int action)
{
if(key >= 0 && key <= nk_appkit_key_last)
{
if(action == nk_appkit_release && window->keys[key] == nk_appkit_release)
return;
window->keys[key] = (char)action;
}
}
@interface nk_appkit_windowDelegate : NSObject
{
nk_appkit_window *window;
}
- (instancetype)init_with_window:(nk_appkit_window *)initWindow;
@end
@implementation nk_appkit_windowDelegate
- (instancetype)init_with_window:(nk_appkit_window *)initWindow
{
self = [super init];
window = initWindow;
return self;
}
- (BOOL)windowShouldClose:(id)sender
{
window->shouldClose = true;
return NO;
}
@end
@interface nk_appkit_windowView : NSView<NSTextInputClient>
{
nk_appkit_window *window;
}
- (instancetype)init_with_window:(nk_appkit_window *)initWindow;
@end
@implementation nk_appkit_windowView
- (instancetype)init_with_window:(nk_appkit_window *)initWindow
{
self = [super init];
window = initWindow;
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
if(window->nsImage != nil)
[window->nsImage drawInRect:dirtyRect];
}
- (void)mouseDown:(NSEvent *)event
{
InputMouseClick(window, nk_appkit_mouse_button_left, nk_appkit_press);
}
- (void)mouseUp:(NSEvent *)event
{
InputMouseClick(window, nk_appkit_mouse_button_left, nk_appkit_release);
}
- (void)rightMouseDown:(NSEvent *)event
{
InputMouseClick(window, nk_appkit_mouse_button_right, nk_appkit_press);
}
- (void)rightMouseUp:(NSEvent *)event
{
InputMouseClick(window, nk_appkit_mouse_button_right, nk_appkit_release);
}
- (void)otherMouseDown:(NSEvent *)event
{
InputMouseClick(window, (int)[event buttonNumber], nk_appkit_press);
}
- (void)otherMouseUp:(NSEvent *)event
{
InputMouseClick(window, (int)[event buttonNumber], nk_appkit_release);
}
- (void)keyDown:(NSEvent *)event
{
const int key = TranslateKey([event keyCode]);
InputKey(window, key, nk_appkit_press);
[self interpretKeyEvents:@[ event ]];
if(window->keyCallback)
window->keyCallback(window, key, nk_appkit_press);
}
- (void)flagsChanged:(NSEvent *)event
{
int action;
const unsigned int modifierFlags =
[event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
const int key = TranslateKey([event keyCode]);
NSUInteger keyFlag = 0;
switch(key)
{
case nk_appkit_key_left_shift:
case nk_appkit_key_right_shift: keyFlag = NSEventModifierFlagShift; break;
case nk_appkit_key_left_control:
case nk_appkit_key_right_control: keyFlag = NSEventModifierFlagControl; break;
}
if(keyFlag & modifierFlags)
{
if(window->keys[key] == nk_appkit_press)
action = nk_appkit_release;
else
action = nk_appkit_press;
}
else
action = nk_appkit_release;
InputKey(window, key, action);
}
- (void)keyUp:(NSEvent *)event
{
const int key = TranslateKey([event keyCode]);
InputKey(window, key, nk_appkit_release);
if(window->keyCallback)
window->keyCallback(window, key, nk_appkit_release);
}
- (void)scrollWheel:(NSEvent *)event
{
double deltaX = [event scrollingDeltaX];
double deltaY = [event scrollingDeltaY];
if([event hasPreciseScrollingDeltas])
{
deltaX *= 0.1;
deltaY *= 0.1;
}
if(fabs(deltaX) > 0.0 || fabs(deltaY) > 0.0)
{
if(window->scrollCallback)
window->scrollCallback(window, deltaX, deltaY);
}
}
static const NSRange s_NSRange_Empty = {NSNotFound, 0};
- (BOOL)hasMarkedText
{
return NO;
}
- (NSRange)markedRange
{
return s_NSRange_Empty;
}
- (NSRange)selectedRange
{
return s_NSRange_Empty;
}
- (void)setMarkedText:(id)string
selectedRange:(NSRange)selectedRange
replacementRange:(NSRange)replacementRange
{
}
- (void)unmarkText
{
}
- (NSArray *)validAttributesForMarkedText
{
return [NSArray array];
}
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range
actualRange:(NSRangePointer)actualRange
{
return nil;
}
- (NSUInteger)characterIndexForPoint:(NSPoint)point
{
return 0;
}
- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange
{
const NSRect frame = [window->nsView frame];
return NSMakeRect(frame.origin.x, frame.origin.y, 0.0, 0.0);
}
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
{
if(!window->characterCallback)
return;
NSString *characters;
NSEvent *event = [NSApp currentEvent];
if([string isKindOfClass:[NSAttributedString class]])
characters = [string string];
else
characters = (NSString *)string;
NSRange range = NSMakeRange(0, [characters length]);
while(range.length)
{
uint32_t codepoint = 0;
if([characters getBytes:&codepoint
maxLength:sizeof(codepoint)
usedLength:nil
encoding:NSUTF32StringEncoding
options:0
range:range
remainingRange:&range])
{
if(codepoint >= 0xf700 && codepoint <= 0xf7ff)
continue;
if(window->characterCallback)
window->characterCallback(window, codepoint);
}
}
}
@end
void nk_appkit_window_get_mouse_position(nk_appkit_window *window, double *xpos, double *ypos)
{
@autoreleasepool
{
const NSRect contentRect = [window->nsView frame];
const NSPoint pos = [window->nsWindow mouseLocationOutsideOfEventStream];
if(xpos)
*xpos = pos.x;
if(ypos)
*ypos = contentRect.size.height - pos.y;
}
}
@interface nk_appkit_ApplicationDelegate : NSObject<NSApplicationDelegate>
@end
@implementation nk_appkit_ApplicationDelegate
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
if(s_state.window)
s_state.window->shouldClose = true;
return NSTerminateCancel;
}
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
@autoreleasepool
{
NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
location:NSMakePoint(0, 0)
modifierFlags:0
timestamp:0
windowNumber:0
context:nil
subtype:0
data1:0
data2:0];
[NSApp postEvent:event atStart:YES];
[NSApp stop:nil];
}
}
@end // nk_appkit_ApplicationDelegate
bool nk_appkit_window_is_closed(nk_appkit_window *window)
{
return window->shouldClose;
}
void nk_appkit_window_set_character_callback(nk_appkit_window *window, nk_appkit_character_cb callback)
{
window->characterCallback = callback;
}
void nk_appkit_window_set_key_callback(nk_appkit_window *window, nk_appkit_key_cb callback)
{
window->keyCallback = callback;
}
void nk_appkit_window_set_scroll_callback(nk_appkit_window *window, nk_appkit_scroll_cb callback)
{
window->scrollCallback = callback;
}
void nk_appkit_window_set_mouse_button_callback(nk_appkit_window *window,
nk_appkit_mouse_button_cb callback)
{
window->mouseButtonCallback = callback;
}
int nk_appkit_window_get_key_state(nk_appkit_window *window, int key)
{
assert(key >= nk_appkit_key_first && key <= nk_appkit_key_last);
return (int)window->keys[key];
}
int nk_appkit_window_get_mouse_button_state(nk_appkit_window *window, int button)
{
assert(button >= 0 && button <= 2);
return (int)window->mouseButtons[button];
}
void nk_appkit_window_delete(nk_appkit_window *window)
{
assert(window == s_state.window);
window->characterCallback = nil;
window->keyCallback = nil;
window->mouseButtonCallback = nil;
window->scrollCallback = nil;
@autoreleasepool
{
[window->nsWindow orderOut:nil];
[window->nsWindow setDelegate:nil];
[window->delegate release];
window->delegate = nil;
[window->nsImage release];
window->nsImage = nil;
[window->nsView release];
window->nsView = nil;
[window->nsWindow close];
window->nsWindow = nil;
}
window->nsPool = nil;
free(window);
s_state.window = NULL;
}
void nk_appkit_core_shutdown(void)
{
if(s_state.window)
nk_appkit_window_delete(s_state.window);
@autoreleasepool
{
if(s_state.eventSource)
{
CFRelease(s_state.eventSource);
s_state.eventSource = nil;
}
if(s_state.nsAppDelegate)
{
[NSApp setDelegate:nil];
[s_state.nsAppDelegate release];
s_state.nsAppDelegate = nil;
}
if(s_state.keyUpMonitor)
[NSEvent removeMonitor:s_state.keyUpMonitor];
if(s_state.nsFont)
{
[s_state.nsFont release];
s_state.nsFont = nil;
}
}
memset(&s_state, 0, sizeof(s_state));
}
bool nk_appkit_core_initialize(void)
{
memset(&s_state, 0, sizeof(s_state));
@autoreleasepool
{
[NSApplication sharedApplication];
s_state.nsAppDelegate = [nk_appkit_ApplicationDelegate new];
[NSApp setDelegate:s_state.nsAppDelegate];
NSEvent * (^block)(NSEvent *) = ^NSEvent *(NSEvent *event)
{
if([event modifierFlags] & NSEventModifierFlagCommand)
[[NSApp keyWindow] sendEvent:event];
return event;
};
s_state.keyUpMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp
handler:block];
CreateKeyTables();
s_state.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
if(!s_state.eventSource)
{
nk_appkit_core_shutdown();
return false;
}
CGEventSourceSetLocalEventsSuppressionInterval(s_state.eventSource, 0.0);
if(![[NSRunningApplication currentApplication] isFinishedLaunching])
[NSApp run];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
}
return true;
}
nk_appkit_window *nk_appkit_window_create(int width, int height, const char *title)
{
nk_appkit_window *window = (nk_appkit_window *)calloc(1, sizeof(nk_appkit_window));
s_state.window = window;
window->delegate = [[nk_appkit_windowDelegate alloc] init_with_window:window];
window->nsWindow = [[NSWindow alloc]
initWithContentRect:NSMakeRect(0, 0, width, height)
styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable
backing:NSBackingStoreBuffered
defer:NO];
[(NSWindow *)window->nsWindow center];
window->nsView = [[nk_appkit_windowView alloc] init_with_window:window];
[window->nsWindow setContentView:window->nsView];
[window->nsWindow makeFirstResponder:window->nsView];
[window->nsWindow setTitle:@(title)];
[window->nsWindow setDelegate:window->delegate];
[window->nsWindow setAcceptsMouseMovedEvents:YES];
[window->nsWindow setRestorable:NO];
[window->nsWindow orderFront:nil];
[NSApp activateIgnoringOtherApps:YES];
[window->nsWindow makeKeyAndOrderFront:nil];
window->size = NSMakeSize(width, height);
window->nsImage = [[NSImage alloc] initWithSize:window->size];
return (nk_appkit_window *)window;
}
void nk_appkit_drawing_begin(nk_appkit_window *window, u_int8_t r, u_int8_t g, u_int8_t b, u_int8_t a)
{
window->nsPool = [[NSAutoreleasePool alloc] init];
[NSGraphicsContext saveGraphicsState];
[window->nsImage lockFocus];
[[NSColor colorWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:a / 255.0f] setFill];
NSRectFill(NSMakeRect(0, 0, window->size.width, window->size.height));
}
void nk_appkit_drawing_end(nk_appkit_window *window)
{
[window->nsImage unlockFocus];
[window->nsView setNeedsDisplay:TRUE];
[NSGraphicsContext restoreGraphicsState];
[window->nsPool drain];
}
float nk_appkit_drawing_set_font(nk_appkit_window *window, const char *name, float size)
{
@autoreleasepool
{
s_state.nsFont = [NSFont fontWithName:[NSString stringWithUTF8String:name] size:size];
[s_state.nsFont retain];
NSLayoutManager *nsLayoutManager = [NSLayoutManager new];
float height = [nsLayoutManager defaultLineHeightForFont:s_state.nsFont];
s_state.fontHeight = height + 1;
[nsLayoutManager autorelease];
return s_state.fontHeight;
}
}
float nk_appkit_drawing_get_text_width(nk_appkit_window *window, const char *text, int len)
{
@autoreleasepool
{
NSString *nsString = [[NSString alloc] initWithBytes:(void *)text
length:len
encoding:NSASCIIStringEncoding];
NSMutableDictionary *atts = [NSMutableDictionary new];
[atts setObject:s_state.nsFont forKey:NSFontAttributeName];
NSSize bounds = [nsString sizeWithAttributes:atts];
float width = floor(bounds.width);
[atts autorelease];
[nsString autorelease];
return width;
}
}
void nk_appkit_drawing_filled_rect(nk_appkit_window *window, short x, short y, u_int16_t w,
u_int16_t h, u_int8_t r, u_int8_t g, u_int8_t b, u_int8_t a,
int rounding)
{
@autoreleasepool
{
[[NSColor colorWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:a / 255.0f] setFill];
float H = window->size.height;
float y0 = H - y - h;
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:NSMakeRect(x, y0, w, h)
xRadius:rounding
yRadius:rounding];
[path fill];
}
}
void nk_appkit_drawing_rect(nk_appkit_window *window, short x, short y, u_int16_t w, u_int16_t h,
u_int8_t r, u_int8_t g, u_int8_t b, u_int8_t a, int rounding,
int lineThickness)
{
@autoreleasepool
{
[[NSColor colorWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:a / 255.0f] setStroke];
float H = window->size.height;
float y0 = H - y - h;
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:NSMakeRect(x, y0, w, h)
xRadius:rounding
yRadius:rounding];
path.lineWidth = lineThickness;
[path stroke];
}
}
void nk_appkit_drawing_scissor(nk_appkit_window *window, short x, short y, u_int16_t w, u_int16_t h)
{
@autoreleasepool
{
[NSGraphicsContext restoreGraphicsState];
[NSGraphicsContext saveGraphicsState];
float H = window->size.height;
float y0 = H - y - h;
NSRectClip(NSMakeRect(x, y0, w, h));
}
}
void nk_appkit_drawing_text(nk_appkit_window *window, short x, short y, u_int16_t w, u_int16_t h,
const char *text, int len, void *font, u_int8_t bgR, u_int8_t bgG,
u_int8_t bgB, u_int8_t bgA, u_int8_t fgR, u_int8_t fgG, u_int8_t fgB,
u_int8_t fgA)
{
@autoreleasepool
{
float H = window->size.height;
float y0 = H - y - s_state.fontHeight;
NSString *nsString = [[NSString alloc] initWithBytes:(void *)text
length:len
encoding:NSASCIIStringEncoding];
NSMutableDictionary *atts = [NSMutableDictionary new];
[atts setObject:s_state.nsFont forKey:NSFontAttributeName];
[atts setObject:[NSColor colorWithRed:bgR / 255.0f
green:bgG / 255.0f
blue:bgB / 255.0f
alpha:bgA / 255.0f]
forKey:NSBackgroundColorAttributeName];
[atts setObject:[NSColor colorWithRed:fgR / 255.0f
green:fgG / 255.0f
blue:fgB / 255.0f
alpha:fgA / 255.0f]
forKey:NSForegroundColorAttributeName];
[nsString drawAtPoint:NSMakePoint(x, y0) withAttributes:atts];
[atts autorelease];
[nsString autorelease];
}
}
#endif // #ifdef NK_APPKIT_OBJC_IMPLEMENTATION