window.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. #ifndef NK_GDI_WINDOW
  2. #define NK_GDI_WINDOW
  3. #define NK_GDI_WINDOW_CLS L"WNDCLS_NkGdi"
  4. #include <windows.h>
  5. /* Functin pointer types for window callbacks */
  6. typedef int(*nkgdi_window_func_close)(void);
  7. typedef int(*nkgdi_window_func_draw)(struct nk_context*);
  8. /* Window container / context */
  9. struct nkgdi_window
  10. {
  11. /* The window can be sized */
  12. int allow_sizing;
  13. /* The window can be maximized by double clicking the titlebar */
  14. int allow_maximize;
  15. /* The window is allowed to be moved by the user */
  16. int allow_move;
  17. /* The window will render it's title bar */
  18. int has_titlebar;
  19. /* Callbacks */
  20. /* Called when the user or os requests a window close (return 1 to accept the reqest)*/
  21. nkgdi_window_func_close cb_on_close;
  22. /* Called each time the window content should be drawn. Here you will do your nuklear drawing code
  23. * but WITHOUT nk_begin and nk_end. Return 1 to keep the window open.
  24. */
  25. nkgdi_window_func_draw cb_on_draw;
  26. /* Internal Data */
  27. struct
  28. {
  29. /* Window handle */
  30. HWND window_handle;
  31. /* Nuklear & GDI context */
  32. nk_gdi_ctx nk_gdi_ctx;
  33. struct nk_context* nk_ctx;
  34. /* GDI required objects */
  35. GdiFont* gdi_font;
  36. HDC window_dc;
  37. /* Internally used state variables */
  38. int is_open;
  39. int is_draggin;
  40. int ws_override;
  41. int is_maximized;
  42. POINT drag_offset;
  43. int width;
  44. int height;
  45. }_internal;
  46. };
  47. /* API */
  48. /* This function will init all resources used by the implementation */
  49. void nkgdi_window_init(void);
  50. /* This function will free all globally used resources */
  51. void nkgdi_window_shutdown(void);
  52. /* Creates a new window (for the wnd context) */
  53. void nkgdi_window_create(struct nkgdi_window* wnd, unsigned int width, unsigned int height, const char* name, int posX, int posY);
  54. /* Updates the window (Windows message loop, nuklear loop and drawing). Returns one as long as the window is valid and open */
  55. int nkgdi_window_update(struct nkgdi_window* wnd);
  56. /* Destroys the window context wnd */
  57. void nkgdi_window_destroy(struct nkgdi_window* wnd);
  58. #ifdef NKGDI_IMPLEMENT_WINDOW
  59. /* Predeclare the windows window message procs */
  60. /* This proc will setup the pointer to the window context */
  61. LRESULT CALLBACK nkgdi_window_proc_setup(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam);
  62. /* This proc will take the window context pointer and performs operations on it*/
  63. LRESULT CALLBACK nkgdi_window_proc_run(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam);
  64. void nkgdi_window_init(void)
  65. {
  66. /* Describe the window class */
  67. WNDCLASSEXW cls;
  68. cls.cbSize = sizeof(WNDCLASSEXW);
  69. cls.style = CS_OWNDC | CS_DBLCLKS;
  70. cls.lpfnWndProc = &nkgdi_window_proc_setup;
  71. cls.cbClsExtra = 0;
  72. cls.cbWndExtra = 0;
  73. cls.hInstance = GetModuleHandle(NULL);
  74. cls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  75. cls.hCursor = LoadCursor(NULL, IDC_ARROW);
  76. cls.hbrBackground = NULL;
  77. cls.lpszMenuName = NULL;
  78. cls.lpszClassName = NK_GDI_WINDOW_CLS;
  79. cls.hIconSm = NULL;
  80. /* Register the window class */
  81. RegisterClassExW(&cls);
  82. }
  83. void nkgdi_window_shutdown(void)
  84. {
  85. /* Windows class no longer required, unregister it */
  86. UnregisterClassW(NK_GDI_WINDOW_CLS, GetModuleHandle(NULL));
  87. }
  88. void nkgdi_window_create(struct nkgdi_window* wnd, unsigned int width, unsigned int height, const char* name, int posX, int posY)
  89. {
  90. DWORD styleEx = WS_EX_WINDOWEDGE;
  91. DWORD style = WS_POPUP;
  92. /* Adjust window size to fit selected window styles */
  93. RECT cr;
  94. cr.left = 0;
  95. cr.top = 0;
  96. cr.right = width;
  97. cr.bottom = height;
  98. AdjustWindowRectEx(&cr, style, FALSE, styleEx);
  99. /* Create the new window */
  100. wnd->_internal.window_handle = CreateWindowExW(
  101. styleEx,
  102. NK_GDI_WINDOW_CLS,
  103. L"NkGdi",
  104. style | WS_VISIBLE,
  105. posX, posY,
  106. cr.right - cr.left, cr.bottom - cr.top,
  107. NULL, NULL,
  108. GetModuleHandleW(NULL),
  109. wnd
  110. );
  111. /* Give the window the ascii char name */
  112. SetWindowTextA(wnd->_internal.window_handle, name);
  113. /* Extract the window dc for gdi drawing */
  114. wnd->_internal.window_dc = GetWindowDC(wnd->_internal.window_handle);
  115. /* Create the gdi font required to draw text */
  116. wnd->_internal.gdi_font = nk_gdifont_create("Arial", 16);
  117. /* Init the gdi backend */
  118. wnd->_internal.nk_ctx = nk_gdi_init(&wnd->_internal.nk_gdi_ctx, wnd->_internal.gdi_font, wnd->_internal.window_dc, width, height);
  119. /* Bring all internal variables to a defined and valid initial state */
  120. wnd->_internal.is_open = 1;
  121. wnd->_internal.is_draggin = 0;
  122. wnd->_internal.ws_override = 0;
  123. wnd->_internal.is_maximized = 0;
  124. wnd->_internal.drag_offset.x = 0;
  125. wnd->_internal.drag_offset.y = 0;
  126. wnd->_internal.width = width;
  127. wnd->_internal.height = height;
  128. }
  129. void nkgdi_window_destroy(struct nkgdi_window* wnd)
  130. {
  131. /* Destroy all objects in reverse order */
  132. if (wnd->_internal.nk_gdi_ctx)
  133. {
  134. nk_gdi_shutdown(wnd->_internal.nk_gdi_ctx);
  135. }
  136. if (wnd->_internal.gdi_font)
  137. {
  138. nk_gdifont_del(wnd->_internal.gdi_font);
  139. }
  140. if (wnd->_internal.window_dc)
  141. {
  142. ReleaseDC(wnd->_internal.window_handle, wnd->_internal.window_dc);
  143. }
  144. if (wnd->_internal.window_handle)
  145. {
  146. CloseWindow(wnd->_internal.window_handle);
  147. DestroyWindow(wnd->_internal.window_handle);
  148. }
  149. }
  150. int nkgdi_window_update(struct nkgdi_window* wnd)
  151. {
  152. /* The window will only be updated when it is open / valid */
  153. if (wnd->_internal.is_open)
  154. {
  155. /* First all pending window events will be processed */
  156. MSG msg;
  157. nk_input_begin(wnd->_internal.nk_ctx);
  158. while (PeekMessage(&msg, wnd->_internal.window_handle, 0, 0, PM_REMOVE))
  159. {
  160. TranslateMessage(&msg);
  161. DispatchMessageW(&msg);
  162. }
  163. nk_input_end(wnd->_internal.nk_ctx);
  164. /* To setup the nuklear window we need the windows title */
  165. char title[1024];
  166. GetWindowTextA(wnd->_internal.window_handle, title, 1024);
  167. /* The nk window flags are beeing create based on the context setup */
  168. nk_flags window_flags = NK_WINDOW_BORDER;
  169. if(wnd->has_titlebar)
  170. window_flags |= NK_WINDOW_CLOSABLE | NK_WINDOW_TITLE;
  171. if(!wnd->_internal.is_maximized && wnd->allow_sizing)
  172. window_flags |= NK_WINDOW_SCALABLE;
  173. /* Override the nuklear windows size when required */
  174. if (wnd->_internal.ws_override)
  175. nk_window_set_bounds(wnd->_internal.nk_ctx, title, nk_rect(0, 0, wnd->_internal.width, wnd->_internal.height));
  176. /* Start the nuklear window */
  177. if (nk_begin(wnd->_internal.nk_ctx, title, nk_rect(0, 0, wnd->_internal.width, wnd->_internal.height), window_flags))
  178. {
  179. /* Call user drawing callback */
  180. if(wnd->cb_on_draw && !wnd->cb_on_draw(wnd->_internal.nk_ctx))
  181. wnd->_internal.is_open = 0;
  182. /* Update the windows window to reflect the nuklear windows size */
  183. struct nk_rect bounds = nk_window_get_bounds(wnd->_internal.nk_ctx);
  184. if(bounds.w != wnd->_internal.width || bounds.h != wnd->_internal.height)
  185. SetWindowPos(wnd->_internal.window_handle, NULL, 0, 0, bounds.w, bounds.h, SWP_NOMOVE | SWP_NOOWNERZORDER);
  186. }
  187. else
  188. {
  189. /* Nuklear window was closed. Handle close internally */
  190. if(!wnd->cb_on_close || wnd->cb_on_close())
  191. wnd->_internal.is_open = 0;
  192. }
  193. nk_end(wnd->_internal.nk_ctx);
  194. /* We no longer need the window size override flag to be set */
  195. wnd->_internal.ws_override = 0;
  196. /* Pass context to the nuklear gdi renderer */
  197. nk_gdi_render(wnd->_internal.nk_gdi_ctx, nk_rgb(0, 0, 0));
  198. }
  199. return wnd->_internal.is_open;
  200. }
  201. LRESULT CALLBACK nkgdi_window_proc_setup(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
  202. {
  203. /* Waiting to receive the NCCREATE message with the custom window data */
  204. if (msg == WM_NCCREATE)
  205. {
  206. /* Extracting the window context from message parameters */
  207. CREATESTRUCT* ptrCr = (CREATESTRUCT*)lParam;
  208. struct nkgdi_window* nkgdi_wnd = (struct nkgdi_window*)ptrCr->lpCreateParams;
  209. /* Store the context in the window and redirect any further message to the run window proc*/
  210. SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)nkgdi_wnd);
  211. SetWindowLongPtr(wnd, GWLP_WNDPROC, (LONG_PTR)&nkgdi_window_proc_run);
  212. /* Already call the run proc so that it gets the chance to handle NCCREATE as well */
  213. return nkgdi_window_proc_run(wnd, msg, wParam, lParam);
  214. }
  215. /* Until we get WM_NCCREATE windows is going to handle the messages */
  216. return DefWindowProc(wnd, msg, wParam, lParam);
  217. }
  218. LRESULT CALLBACK nkgdi_window_proc_run(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
  219. {
  220. /* The window context is extracted from the window data */
  221. struct nkgdi_window* nkwnd = (struct nkgdi_window*)GetWindowLongPtrW(wnd, GWLP_USERDATA);
  222. /* Switch on the message code to handle all required messages */
  223. switch (msg)
  224. {
  225. /* Window close event */
  226. case WM_CLOSE:
  227. /* Call custom close callback */
  228. if(!nkwnd->cb_on_close || nkwnd->cb_on_close())
  229. nkwnd->_internal.is_open = 0;
  230. return 0; /* No default behaviour. We do it our own way */
  231. /* Window sizing event (is currently beeing sized) */
  232. case WM_SIZING:
  233. {
  234. /* Size of the client / active are is extracted and stored */
  235. RECT cr;
  236. GetClientRect(wnd, &cr);
  237. nkwnd->_internal.width = cr.right - cr.left;
  238. nkwnd->_internal.height = cr.bottom - cr.top;
  239. }
  240. break;
  241. /* Window size event (done sizing, maximize, minimize, ...) */
  242. case WM_SIZE:
  243. {
  244. /* Window was maximized */
  245. if (wParam == SIZE_MAXIMIZED)
  246. {
  247. /* Get the nearest monitor and retrive its details */
  248. HMONITOR monitor = MonitorFromWindow(wnd, MONITOR_DEFAULTTOPRIMARY);
  249. MONITORINFO monitorInfo;
  250. monitorInfo.cbSize = sizeof(MONITORINFO);
  251. if (GetMonitorInfoW(monitor, &monitorInfo))
  252. {
  253. /* Adjust the window size and position by the monitor working area (without taskbar) */
  254. nkwnd->_internal.height = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
  255. nkwnd->_internal.width = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
  256. nkwnd->_internal.ws_override = 1; /* Sizing was done without nuklear beeing aware. So we need to override it */
  257. nkwnd->_internal.is_maximized = 1;
  258. SetWindowPos(wnd, NULL, 0, 0, nkwnd->_internal.width, nkwnd->_internal.height, SWP_NOMOVE | SWP_NOZORDER);
  259. }
  260. }
  261. /* Window was restored (no longer maximized) */
  262. else if (wParam == SIZE_RESTORED)
  263. {
  264. nkwnd->_internal.is_maximized = 0;
  265. }
  266. /* Always get the new bounds of the window */
  267. RECT cr;
  268. GetClientRect(wnd, &cr);
  269. nkwnd->_internal.width = cr.right - cr.left;
  270. nkwnd->_internal.height = cr.bottom - cr.top;
  271. }
  272. break;
  273. /* Mouse started left click */
  274. case WM_LBUTTONDOWN:
  275. {
  276. /* Handle dragging when allowed, has titlebar and mouse is in titlebar (Y <= 30) */
  277. if (HIWORD(lParam) <= 30 && nkwnd->allow_move && nkwnd->has_titlebar)
  278. {
  279. /* Mark dragging internally and store mouse click offset */
  280. nkwnd->_internal.is_draggin = 1;
  281. nkwnd->_internal.drag_offset.x = LOWORD(lParam);
  282. nkwnd->_internal.drag_offset.y = HIWORD(lParam);
  283. }
  284. }
  285. break;
  286. /* Mouse stoped left click */
  287. case WM_LBUTTONUP:
  288. /* No longer dragging the window */
  289. nkwnd->_internal.is_draggin = 0;
  290. break;
  291. /* Mouse is moving on the window */
  292. case WM_MOUSEMOVE:
  293. {
  294. /* When we are dragging and are not maximized process dragging */
  295. if (nkwnd->_internal.is_draggin && !nkwnd->_internal.is_maximized)
  296. {
  297. /* Get the current global position of the mouse */
  298. POINT cursorPos;
  299. GetCursorPos(&cursorPos);
  300. /* Substract the internal offset */
  301. cursorPos.x -= nkwnd->_internal.drag_offset.x;
  302. cursorPos.y -= nkwnd->_internal.drag_offset.y;
  303. /* Update position of out window and make sure window is in a movable state (= Restored) */
  304. ShowWindow(wnd, SW_RESTORE);
  305. SetWindowPos(wnd, NULL, cursorPos.x, cursorPos.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
  306. }
  307. }
  308. break;
  309. /* Mouse double clicked */
  310. case WM_LBUTTONDBLCLK:
  311. {
  312. /* Will only affect window when on the titlebar */
  313. if (HIWORD(lParam) <= 30 && nkwnd->allow_maximize && nkwnd->has_titlebar)
  314. {
  315. /* When the window is already maximized restore it */
  316. if (nkwnd->_internal.is_maximized)
  317. {
  318. ShowWindow(wnd, SW_RESTORE);
  319. }
  320. /* Else we gonna do maximize it*/
  321. else
  322. {
  323. ShowWindow(wnd, SW_MAXIMIZE);
  324. }
  325. /* We overrideed the window size, make sure to affect the nk window as well */
  326. nkwnd->_internal.ws_override = 1;
  327. }
  328. }
  329. break;
  330. }
  331. /* Allow nuklear to handle the message as well */
  332. if (nkwnd->_internal.nk_gdi_ctx && nk_gdi_handle_event(nkwnd->_internal.nk_gdi_ctx, wnd, msg, wParam, lParam))
  333. return 0;
  334. /* In case we reach this line our code and nuklear did not respond to the message. Allow windows to handle it s*/
  335. return DefWindowProc(wnd, msg, wParam, lParam);
  336. }
  337. #endif
  338. #endif