From d4e695420d1e613e9dd9560a0c0f5c22bcedf614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludwig=20F=C3=BCchsl?= Date: Fri, 25 Feb 2022 11:13:01 +0100 Subject: [PATCH 1/6] Added GDI full featured window code --- demo/gdi_native_nuklear/build.bat | 6 + demo/gdi_native_nuklear/main.cpp | 37 + demo/gdi_native_nuklear/nuklear_gdi.h | 932 ++++++++++++++++++++++++++ demo/gdi_native_nuklear/window.h | 377 +++++++++++ 4 files changed, 1352 insertions(+) create mode 100644 demo/gdi_native_nuklear/build.bat create mode 100644 demo/gdi_native_nuklear/main.cpp create mode 100644 demo/gdi_native_nuklear/nuklear_gdi.h create mode 100644 demo/gdi_native_nuklear/window.h diff --git a/demo/gdi_native_nuklear/build.bat b/demo/gdi_native_nuklear/build.bat new file mode 100644 index 0000000..65a8ce6 --- /dev/null +++ b/demo/gdi_native_nuklear/build.bat @@ -0,0 +1,6 @@ +@echo off + +rem This will use VS2015 for compiler +call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 + +cl /nologo /W3 /O2 /fp:fast /Gm- /D_CRT_SECURE_NO_DEPRECATE /Fedemo.exe main.cpp user32.lib gdi32.lib Msimg32.lib /link /incremental:no diff --git a/demo/gdi_native_nuklear/main.cpp b/demo/gdi_native_nuklear/main.cpp new file mode 100644 index 0000000..000ea2a --- /dev/null +++ b/demo/gdi_native_nuklear/main.cpp @@ -0,0 +1,37 @@ +#include +#include + +#pragma comment(linker,"\"/manifestdependency:type='win32' \ +name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ +processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_IMPLEMENTATION +#define NK_GDI_IMPLEMENTATION +#include "../../nuklear.h" +#include "nuklear_gdi.h" + +#define NKGDI_IMPLEMENT_WINDOW +#include "window.h" + +INT WINAPI wWinMain(HINSTANCE _In_ hInstance, HINSTANCE _In_opt_ hPrevInstance, PWSTR _In_ cmdArgs, INT _In_ cmdShow) +{ + NkGdi::Window w1(500, 500, "F1", 10, 10); + w1.AllowSizing = false; + w1.AllowMaximize = false; + w1.AllowMove = false; + w1.HasTitlebar = false; + + NkGdi::Window w2(500, 500, "F2", 520, 10); + w2.AllowSizing = true; + w2.AllowMaximize = true; + w2.AllowMove = true; + w2.HasTitlebar = true; + + while (w1.Update() && w2.Update()) Sleep(20); + + return 0; +} diff --git a/demo/gdi_native_nuklear/nuklear_gdi.h b/demo/gdi_native_nuklear/nuklear_gdi.h new file mode 100644 index 0000000..aef6452 --- /dev/null +++ b/demo/gdi_native_nuklear/nuklear_gdi.h @@ -0,0 +1,932 @@ +/* + * Nuklear - 1.32.0 - public domain + * no warrenty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ + /* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_GDI_H_ +#define NK_GDI_H_ + +#ifdef __cplusplus +extern "C"{ +#endif + +typedef struct GdiFont GdiFont; +struct _nk_gdi_ctx; +typedef struct _nk_gdi_ctx* nk_gdi_ctx; + +NK_API struct nk_context* nk_gdi_init(nk_gdi_ctx* gdi, GdiFont* font, HDC window_dc, unsigned int width, unsigned int height); +NK_API int nk_gdi_handle_event(nk_gdi_ctx gdi, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); +NK_API void nk_gdi_render(nk_gdi_ctx gdi, struct nk_color clear); +NK_API void nk_gdi_shutdown(nk_gdi_ctx gdi); + +/* font */ +NK_API GdiFont* nk_gdifont_create(const char* name, int size); +NK_API void nk_gdifont_del(GdiFont* font); +NK_API void nk_gdi_set_font(nk_gdi_ctx gdi, GdiFont* font); + +#ifdef __cplusplus +} +#endif + +#endif + +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_GDI_IMPLEMENTATION + +#include +#include + +struct GdiFont { + struct nk_user_font nk; + int height; + HFONT handle; + HDC dc; +}; + +struct _nk_gdi_ctx { + HBITMAP bitmap; + HDC window_dc; + HDC memory_dc; + unsigned int width; + unsigned int height; + struct nk_context ctx; +}; + +static void +nk_create_image(struct nk_image* image, const char* frame_buffer, const int width, const int height) +{ + if (image && frame_buffer && (width > 0) && (height > 0)) + { + const unsigned char* src = (const unsigned char*)frame_buffer; + INT row = ((width * 3 + 3) & ~3); + LPBYTE lpBuf, pb = NULL; + BITMAPINFO bi = { 0 }; + HBITMAP hbm; + int v, i; + + image->w = width; + image->h = height; + image->region[0] = 0; + image->region[1] = 0; + image->region[2] = width; + image->region[3] = height; + + bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bi.bmiHeader.biWidth = width; + bi.bmiHeader.biHeight = height; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biBitCount = 24; + bi.bmiHeader.biCompression = BI_RGB; + bi.bmiHeader.biSizeImage = row * height; + + hbm = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, (void**)&lpBuf, NULL, 0); + + pb = lpBuf + row * height; + for (v = 0; v < height; v++) + { + pb -= row; + for (i = 0; i < row; i += 3) + { + pb[i + 0] = src[0]; + pb[i + 1] = src[1]; + pb[i + 2] = src[2]; + src += 3; + } + } + SetDIBits(NULL, hbm, 0, height, lpBuf, &bi, DIB_RGB_COLORS); + image->handle.ptr = hbm; + } +} + +static void +nk_delete_image(struct nk_image* image) +{ + if (image && image->handle.id != 0) + { + HBITMAP hbm = (HBITMAP)image->handle.ptr; + DeleteObject(hbm); + memset(image, 0, sizeof(struct nk_image)); + } +} + +static void +nk_gdi_draw_image(nk_gdi_ctx gdi, short x, short y, unsigned short w, unsigned short h, + struct nk_image img, struct nk_color col) +{ + HBITMAP hbm = (HBITMAP)img.handle.ptr; + HDC hDCBits; + BITMAP bitmap; + + if (!gdi->memory_dc || !hbm) + return; + + hDCBits = CreateCompatibleDC(gdi->memory_dc); + GetObject(hbm, sizeof(BITMAP), (LPSTR)&bitmap); + SelectObject(hDCBits, hbm); + StretchBlt(gdi->memory_dc, x, y, w, h, hDCBits, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY); + DeleteDC(hDCBits); +} + +static COLORREF +convert_color(struct nk_color c) +{ + return c.r | (c.g << 8) | (c.b << 16); +} + +static void +nk_gdi_scissor(HDC dc, float x, float y, float w, float h) +{ + SelectClipRgn(dc, NULL); + IntersectClipRect(dc, (int)x, (int)y, (int)(x + w + 1), (int)(y + h + 1)); +} + +static void +nk_gdi_stroke_line(HDC dc, short x0, short y0, short x1, + short y1, unsigned int line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + + HPEN pen = NULL; + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } + else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + MoveToEx(dc, x0, y0, NULL); + LineTo(dc, x1, y1); + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_stroke_rect(HDC dc, short x, short y, unsigned short w, + unsigned short h, unsigned short r, unsigned short line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + HGDIOBJ br; + HPEN pen = NULL; + + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } + else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + br = SelectObject(dc, GetStockObject(NULL_BRUSH)); + if (r == 0) { + Rectangle(dc, x, y, x + w, y + h); + } + else { + RoundRect(dc, x, y, x + w, y + h, r, r); + } + SelectObject(dc, br); + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_fill_rect(HDC dc, short x, short y, unsigned short w, + unsigned short h, unsigned short r, struct nk_color col) +{ + COLORREF color = convert_color(col); + + if (r == 0) { + RECT rect; + SetRect(&rect, x, y, x + w, y + h); + SetBkColor(dc, color); + ExtTextOutW(dc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + } + else { + SetDCPenColor(dc, color); + SetDCBrushColor(dc, color); + RoundRect(dc, x, y, x + w, y + h, r, r); + } +} +static void +nk_gdi_set_vertexColor(PTRIVERTEX tri, struct nk_color col) +{ + tri->Red = col.r << 8; + tri->Green = col.g << 8; + tri->Blue = col.b << 8; + tri->Alpha = 0xff << 8; +} + +static void +nk_gdi_rect_multi_color(nk_gdi_ctx gdi, HDC dc, short x, short y, unsigned short w, + unsigned short h, struct nk_color left, struct nk_color top, + struct nk_color right, struct nk_color bottom) +{ + BLENDFUNCTION alphaFunction; + // GRADIENT_RECT gRect; + GRADIENT_TRIANGLE gTri[2]; + TRIVERTEX vt[4]; + alphaFunction.BlendOp = AC_SRC_OVER; + alphaFunction.BlendFlags = 0; + alphaFunction.SourceConstantAlpha = 0; + alphaFunction.AlphaFormat = AC_SRC_ALPHA; + + /* TODO: This Case Needs Repair.*/ + /* Top Left Corner */ + vt[0].x = x; + vt[0].y = y; + nk_gdi_set_vertexColor(&vt[0], left); + /* Top Right Corner */ + vt[1].x = x + w; + vt[1].y = y; + nk_gdi_set_vertexColor(&vt[1], top); + /* Bottom Left Corner */ + vt[2].x = x; + vt[2].y = y + h; + nk_gdi_set_vertexColor(&vt[2], right); + + /* Bottom Right Corner */ + vt[3].x = x + w; + vt[3].y = y + h; + nk_gdi_set_vertexColor(&vt[3], bottom); + + gTri[0].Vertex1 = 0; + gTri[0].Vertex2 = 1; + gTri[0].Vertex3 = 2; + gTri[1].Vertex1 = 2; + gTri[1].Vertex2 = 1; + gTri[1].Vertex3 = 3; + GdiGradientFill(dc, vt, 4, gTri, 2, GRADIENT_FILL_TRIANGLE); + AlphaBlend(gdi->window_dc, x, y, x + w, y + h, gdi->memory_dc, x, y, x + w, y + h, alphaFunction); + +} + +static BOOL +SetPoint(POINT* p, LONG x, LONG y) +{ + if (!p) + return FALSE; + p->x = x; + p->y = y; + return TRUE; +} + +static void +nk_gdi_fill_triangle(HDC dc, short x0, short y0, short x1, + short y1, short x2, short y2, struct nk_color col) +{ + COLORREF color = convert_color(col); + POINT points[3]; + + SetPoint(&points[0], x0, y0); + SetPoint(&points[1], x1, y1); + SetPoint(&points[2], x2, y2); + + SetDCPenColor(dc, color); + SetDCBrushColor(dc, color); + Polygon(dc, points, 3); +} + +static void +nk_gdi_stroke_triangle(HDC dc, short x0, short y0, short x1, + short y1, short x2, short y2, unsigned short line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + POINT points[4]; + HPEN pen = NULL; + + SetPoint(&points[0], x0, y0); + SetPoint(&points[1], x1, y1); + SetPoint(&points[2], x2, y2); + SetPoint(&points[3], x0, y0); + + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } + else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + Polyline(dc, points, 4); + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_fill_polygon(HDC dc, const struct nk_vec2i* pnts, int count, struct nk_color col) +{ + int i = 0; +#define MAX_POINTS 64 + POINT points[MAX_POINTS]; + COLORREF color = convert_color(col); + SetDCBrushColor(dc, color); + SetDCPenColor(dc, color); + for (i = 0; i < count && i < MAX_POINTS; ++i) { + points[i].x = pnts[i].x; + points[i].y = pnts[i].y; + } + Polygon(dc, points, i); +#undef MAX_POINTS +} + +static void +nk_gdi_stroke_polygon(HDC dc, const struct nk_vec2i* pnts, int count, + unsigned short line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + HPEN pen = NULL; + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } + else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + if (count > 0) { + int i; + MoveToEx(dc, pnts[0].x, pnts[0].y, NULL); + for (i = 1; i < count; ++i) + LineTo(dc, pnts[i].x, pnts[i].y); + LineTo(dc, pnts[0].x, pnts[0].y); + } + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_stroke_polyline(HDC dc, const struct nk_vec2i* pnts, + int count, unsigned short line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + HPEN pen = NULL; + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } + else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + if (count > 0) { + int i; + MoveToEx(dc, pnts[0].x, pnts[0].y, NULL); + for (i = 1; i < count; ++i) + LineTo(dc, pnts[i].x, pnts[i].y); + } + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_fill_circle(HDC dc, short x, short y, unsigned short w, + unsigned short h, struct nk_color col) +{ + COLORREF color = convert_color(col); + SetDCBrushColor(dc, color); + SetDCPenColor(dc, color); + Ellipse(dc, x, y, x + w, y + h); +} + +static void +nk_gdi_stroke_circle(HDC dc, short x, short y, unsigned short w, + unsigned short h, unsigned short line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + HPEN pen = NULL; + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } + else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + SetDCBrushColor(dc, OPAQUE); + Ellipse(dc, x, y, x + w, y + h); + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_stroke_curve(HDC dc, struct nk_vec2i p1, + struct nk_vec2i p2, struct nk_vec2i p3, struct nk_vec2i p4, + unsigned short line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + POINT p[4]; + HPEN pen = NULL; + + SetPoint(&p[0], p1.x, p1.y); + SetPoint(&p[1], p2.x, p2.y); + SetPoint(&p[2], p3.x, p3.y); + SetPoint(&p[3], p4.x, p4.y); + + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } + else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + SetDCBrushColor(dc, OPAQUE); + PolyBezier(dc, p, 4); + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_draw_text(HDC dc, short x, short y, unsigned short w, unsigned short h, + const char* text, int len, GdiFont* font, struct nk_color cbg, struct nk_color cfg) +{ + int wsize; + WCHAR* wstr; + + if (!text || !font || !len) return; + + wsize = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0); + wstr = (WCHAR*)_alloca(wsize * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, text, len, wstr, wsize); + + SetBkColor(dc, convert_color(cbg)); + SetTextColor(dc, convert_color(cfg)); + + SelectObject(dc, font->handle); + ExtTextOutW(dc, x, y, ETO_OPAQUE, NULL, wstr, wsize, NULL); +} + +static void +nk_gdi_clear(nk_gdi_ctx gdi, HDC dc, struct nk_color col) +{ + COLORREF color = convert_color(col); + RECT rect; + SetRect(&rect, 0, 0, gdi->width, gdi->height); + SetBkColor(dc, color); + + ExtTextOutW(dc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); +} + +static void +nk_gdi_blit(nk_gdi_ctx gdi, HDC dc) +{ + BitBlt(dc, 0, 0, gdi->width, gdi->height, gdi->memory_dc, 0, 0, SRCCOPY); + +} + +GdiFont* +nk_gdifont_create(const char* name, int size) +{ + TEXTMETRICW metric; + GdiFont* font = (GdiFont*)calloc(1, sizeof(GdiFont)); + if (!font) + return NULL; + font->dc = CreateCompatibleDC(0); + font->handle = CreateFontA(size, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, name); + SelectObject(font->dc, font->handle); + GetTextMetricsW(font->dc, &metric); + font->height = metric.tmHeight; + return font; +} + +static float +nk_gdifont_get_text_width(nk_handle handle, float height, const char* text, int len) +{ + GdiFont* font = (GdiFont*)handle.ptr; + SIZE size; + int wsize; + WCHAR* wstr; + if (!font || !text) + return 0; + + wsize = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0); + wstr = (WCHAR*)_alloca(wsize * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, text, len, wstr, wsize); + if (GetTextExtentPoint32W(font->dc, wstr, wsize, &size)) + return (float)size.cx; + return -1.0f; +} + +void +nk_gdifont_del(GdiFont* font) +{ + if (!font) return; + DeleteObject(font->handle); + DeleteDC(font->dc); + free(font); +} + +static void +nk_gdi_clipboard_paste(nk_handle usr, struct nk_text_edit* edit) +{ + (void)usr; + if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL)) + { + HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); + if (mem) + { + SIZE_T size = GlobalSize(mem) - 1; + if (size) + { + LPCWSTR wstr = (LPCWSTR)GlobalLock(mem); + if (wstr) + { + int utf8size = WideCharToMultiByte(CP_UTF8, 0, wstr, (int)(size / sizeof(wchar_t)), NULL, 0, NULL, NULL); + if (utf8size) + { + char* utf8 = (char*)malloc(utf8size); + if (utf8) + { + WideCharToMultiByte(CP_UTF8, 0, wstr, (int)(size / sizeof(wchar_t)), utf8, utf8size, NULL, NULL); + nk_textedit_paste(edit, utf8, utf8size); + free(utf8); + } + } + GlobalUnlock(mem); + } + } + } + CloseClipboard(); + } +} + +static void +nk_gdi_clipboard_copy(nk_handle usr, const char* text, int len) +{ + if (OpenClipboard(NULL)) + { + int wsize = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0); + if (wsize) + { + HGLOBAL mem = (HGLOBAL)GlobalAlloc(GMEM_MOVEABLE, (wsize + 1) * sizeof(wchar_t)); + if (mem) + { + wchar_t* wstr = (wchar_t*)GlobalLock(mem); + if (wstr) + { + MultiByteToWideChar(CP_UTF8, 0, text, len, wstr, wsize); + wstr[wsize] = 0; + GlobalUnlock(mem); + + SetClipboardData(CF_UNICODETEXT, mem); + } + } + } + CloseClipboard(); + } +} + +NK_API struct nk_context* +nk_gdi_init(nk_gdi_ctx* gdi, GdiFont* gdifont, HDC window_dc, unsigned int width, unsigned int height) +{ + *gdi = (nk_gdi_ctx)malloc(sizeof(struct _nk_gdi_ctx)); + + struct nk_user_font* font = &gdifont->nk; + font->userdata = nk_handle_ptr(gdifont); + font->height = (float)gdifont->height; + font->width = nk_gdifont_get_text_width; + + (*gdi)->bitmap = CreateCompatibleBitmap(window_dc, width, height); + (*gdi)->window_dc = window_dc; + (*gdi)->memory_dc = CreateCompatibleDC(window_dc); + (*gdi)->width = width; + (*gdi)->height = height; + SelectObject((*gdi)->memory_dc, (*gdi)->bitmap); + + nk_init_default(&(*gdi)->ctx, font); + (*gdi)->ctx.clip.copy = nk_gdi_clipboard_copy; + (*gdi)->ctx.clip.paste = nk_gdi_clipboard_paste; + return &(*gdi)->ctx; +} + +NK_API void +nk_gdi_set_font(nk_gdi_ctx gdi, GdiFont* gdifont) +{ + struct nk_user_font* font = &gdifont->nk; + font->userdata = nk_handle_ptr(gdifont); + font->height = (float)gdifont->height; + font->width = nk_gdifont_get_text_width; + nk_style_set_font(&gdi->ctx, font); +} + +NK_API int +nk_gdi_handle_event(nk_gdi_ctx gdi, HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_SIZE: + { + unsigned width = LOWORD(lparam); + unsigned height = HIWORD(lparam); + if (width != gdi->width || height != gdi->height) + { + DeleteObject(gdi->bitmap); + gdi->bitmap = CreateCompatibleBitmap(gdi->window_dc, width, height); + gdi->width = width; + gdi->height = height; + SelectObject(gdi->memory_dc, gdi->bitmap); + } + break; + } + + case WM_PAINT: + { + PAINTSTRUCT paint; + HDC dc = BeginPaint(wnd, &paint); + nk_gdi_blit(gdi, dc); + EndPaint(wnd, &paint); + return 1; + } + + case WM_KEYDOWN: + case WM_KEYUP: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + { + int down = !((lparam >> 31) & 1); + int ctrl = GetKeyState(VK_CONTROL) & (1 << 15); + + switch (wparam) + { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + nk_input_key(&gdi->ctx, NK_KEY_SHIFT, down); + return 1; + + case VK_DELETE: + nk_input_key(&gdi->ctx, NK_KEY_DEL, down); + return 1; + + case VK_RETURN: + nk_input_key(&gdi->ctx, NK_KEY_ENTER, down); + return 1; + + case VK_TAB: + nk_input_key(&gdi->ctx, NK_KEY_TAB, down); + return 1; + + case VK_LEFT: + if (ctrl) + nk_input_key(&gdi->ctx, NK_KEY_TEXT_WORD_LEFT, down); + else + nk_input_key(&gdi->ctx, NK_KEY_LEFT, down); + return 1; + + case VK_RIGHT: + if (ctrl) + nk_input_key(&gdi->ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else + nk_input_key(&gdi->ctx, NK_KEY_RIGHT, down); + return 1; + + case VK_BACK: + nk_input_key(&gdi->ctx, NK_KEY_BACKSPACE, down); + return 1; + + case VK_HOME: + nk_input_key(&gdi->ctx, NK_KEY_TEXT_START, down); + nk_input_key(&gdi->ctx, NK_KEY_SCROLL_START, down); + return 1; + + case VK_END: + nk_input_key(&gdi->ctx, NK_KEY_TEXT_END, down); + nk_input_key(&gdi->ctx, NK_KEY_SCROLL_END, down); + return 1; + + case VK_NEXT: + nk_input_key(&gdi->ctx, NK_KEY_SCROLL_DOWN, down); + return 1; + + case VK_PRIOR: + nk_input_key(&gdi->ctx, NK_KEY_SCROLL_UP, down); + return 1; + + case 'C': + if (ctrl) { + nk_input_key(&gdi->ctx, NK_KEY_COPY, down); + return 1; + } + break; + + case 'V': + if (ctrl) { + nk_input_key(&gdi->ctx, NK_KEY_PASTE, down); + return 1; + } + break; + + case 'X': + if (ctrl) { + nk_input_key(&gdi->ctx, NK_KEY_CUT, down); + return 1; + } + break; + + case 'Z': + if (ctrl) { + nk_input_key(&gdi->ctx, NK_KEY_TEXT_UNDO, down); + return 1; + } + break; + + case 'R': + if (ctrl) { + nk_input_key(&gdi->ctx, NK_KEY_TEXT_REDO, down); + return 1; + } + break; + } + return 0; + } + + case WM_CHAR: + if (wparam >= 32) + { + nk_input_unicode(&gdi->ctx, (nk_rune)wparam); + return 1; + } + break; + + case WM_LBUTTONDOWN: + nk_input_button(&gdi->ctx, NK_BUTTON_LEFT, (short)LOWORD(lparam), (short)HIWORD(lparam), 1); + SetCapture(wnd); + return 1; + + case WM_LBUTTONUP: + nk_input_button(&gdi->ctx, NK_BUTTON_DOUBLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 0); + nk_input_button(&gdi->ctx, NK_BUTTON_LEFT, (short)LOWORD(lparam), (short)HIWORD(lparam), 0); + ReleaseCapture(); + return 1; + + case WM_RBUTTONDOWN: + nk_input_button(&gdi->ctx, NK_BUTTON_RIGHT, (short)LOWORD(lparam), (short)HIWORD(lparam), 1); + SetCapture(wnd); + return 1; + + case WM_RBUTTONUP: + nk_input_button(&gdi->ctx, NK_BUTTON_RIGHT, (short)LOWORD(lparam), (short)HIWORD(lparam), 0); + ReleaseCapture(); + return 1; + + case WM_MBUTTONDOWN: + nk_input_button(&gdi->ctx, NK_BUTTON_MIDDLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 1); + SetCapture(wnd); + return 1; + + case WM_MBUTTONUP: + nk_input_button(&gdi->ctx, NK_BUTTON_MIDDLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 0); + ReleaseCapture(); + return 1; + + case WM_MOUSEWHEEL: + nk_input_scroll(&gdi->ctx, nk_vec2(0, (float)(short)HIWORD(wparam) / WHEEL_DELTA)); + return 1; + + case WM_MOUSEMOVE: + nk_input_motion(&gdi->ctx, (short)LOWORD(lparam), (short)HIWORD(lparam)); + return 1; + + case WM_LBUTTONDBLCLK: + nk_input_button(&gdi->ctx, NK_BUTTON_DOUBLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 1); + return 1; + } + + return 0; +} + +NK_API void +nk_gdi_shutdown(nk_gdi_ctx gdi) +{ + DeleteObject(gdi->memory_dc); + DeleteObject(gdi->bitmap); + nk_free(&gdi->ctx); +} + +NK_API void +nk_gdi_render(nk_gdi_ctx gdi, struct nk_color clear) +{ + const struct nk_command* cmd; + + HDC memory_dc = gdi->memory_dc; + SelectObject(memory_dc, GetStockObject(DC_PEN)); + SelectObject(memory_dc, GetStockObject(DC_BRUSH)); + nk_gdi_clear(gdi, memory_dc, clear); + + nk_foreach(cmd, &gdi->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_gdi_scissor(memory_dc, s->x, s->y, s->w, s->h); + } break; + case NK_COMMAND_LINE: { + const struct nk_command_line* l = (const struct nk_command_line*)cmd; + nk_gdi_stroke_line(memory_dc, l->begin.x, l->begin.y, l->end.x, + l->end.y, l->line_thickness, l->color); + } break; + case NK_COMMAND_RECT: { + const struct nk_command_rect* r = (const struct nk_command_rect*)cmd; + nk_gdi_stroke_rect(memory_dc, r->x, r->y, r->w, r->h, + (unsigned short)r->rounding, r->line_thickness, r->color); + } break; + case NK_COMMAND_RECT_FILLED: { + const struct nk_command_rect_filled* r = (const struct nk_command_rect_filled*)cmd; + nk_gdi_fill_rect(memory_dc, r->x, r->y, r->w, r->h, + (unsigned short)r->rounding, r->color); + } break; + case NK_COMMAND_CIRCLE: { + const struct nk_command_circle* c = (const struct nk_command_circle*)cmd; + nk_gdi_stroke_circle(memory_dc, c->x, c->y, c->w, c->h, c->line_thickness, c->color); + } break; + case NK_COMMAND_CIRCLE_FILLED: { + const struct nk_command_circle_filled* c = (const struct nk_command_circle_filled*)cmd; + nk_gdi_fill_circle(memory_dc, c->x, c->y, c->w, c->h, c->color); + } break; + case NK_COMMAND_TRIANGLE: { + const struct nk_command_triangle* t = (const struct nk_command_triangle*)cmd; + nk_gdi_stroke_triangle(memory_dc, t->a.x, t->a.y, t->b.x, t->b.y, + t->c.x, t->c.y, t->line_thickness, t->color); + } break; + case NK_COMMAND_TRIANGLE_FILLED: { + const struct nk_command_triangle_filled* t = (const struct nk_command_triangle_filled*)cmd; + nk_gdi_fill_triangle(memory_dc, t->a.x, t->a.y, t->b.x, t->b.y, + t->c.x, t->c.y, t->color); + } break; + case NK_COMMAND_POLYGON: { + const struct nk_command_polygon* p = (const struct nk_command_polygon*)cmd; + nk_gdi_stroke_polygon(memory_dc, p->points, p->point_count, p->line_thickness, p->color); + } break; + case NK_COMMAND_POLYGON_FILLED: { + const struct nk_command_polygon_filled* p = (const struct nk_command_polygon_filled*)cmd; + nk_gdi_fill_polygon(memory_dc, p->points, p->point_count, p->color); + } break; + case NK_COMMAND_POLYLINE: { + const struct nk_command_polyline* p = (const struct nk_command_polyline*)cmd; + nk_gdi_stroke_polyline(memory_dc, p->points, p->point_count, p->line_thickness, p->color); + } break; + case NK_COMMAND_TEXT: { + const struct nk_command_text* t = (const struct nk_command_text*)cmd; + nk_gdi_draw_text(memory_dc, t->x, t->y, t->w, t->h, + (const char*)t->string, t->length, + (GdiFont*)t->font->userdata.ptr, + t->background, t->foreground); + } break; + case NK_COMMAND_CURVE: { + const struct nk_command_curve* q = (const struct nk_command_curve*)cmd; + nk_gdi_stroke_curve(memory_dc, q->begin, q->ctrl[0], q->ctrl[1], + q->end, q->line_thickness, q->color); + } break; + case NK_COMMAND_RECT_MULTI_COLOR: { + const struct nk_command_rect_multi_color* r = (const struct nk_command_rect_multi_color*)cmd; + nk_gdi_rect_multi_color(gdi, memory_dc, r->x, r->y, r->w, r->h, r->left, r->top, r->right, r->bottom); + } break; + case NK_COMMAND_IMAGE: { + const struct nk_command_image* i = (const struct nk_command_image*)cmd; + nk_gdi_draw_image(gdi, i->x, i->y, i->w, i->h, i->img, i->col); + } break; + case NK_COMMAND_ARC: + case NK_COMMAND_ARC_FILLED: + default: break; + } + } + nk_gdi_blit(gdi, gdi->window_dc); + nk_clear(&gdi->ctx); +} + +#endif diff --git a/demo/gdi_native_nuklear/window.h b/demo/gdi_native_nuklear/window.h new file mode 100644 index 0000000..123e4a7 --- /dev/null +++ b/demo/gdi_native_nuklear/window.h @@ -0,0 +1,377 @@ +#pragma once + +namespace NkGdi +{ + // Container for window management + class WindowClass + { + public: + // Public exposed name + static const wchar_t* const ClassName; + + WindowClass(const WindowClass&) = delete; + private: + // Instance + static WindowClass ClassInstance; + + // Sigelton + WindowClass(); + }; + + // Window base class + class Window + { + friend class WindowClass; + + public: + // Default constructs + Window() = default; + Window(const Window&) = delete; + Window(Window&&) = default; + // Constructor + Window(unsigned int width, unsigned int height, const char* name, int posX = 100, int posY = 100); + // Destructor + ~Window(); + + // Processs window events and render the window (returns true as long as window is open) + bool Update(); + + // Properties + bool AllowSizing = true; + bool AllowMaximize = true; + bool AllowMove = true; + bool HasTitlebar = true; + + public: + // Called when the core window gets an event + virtual LRESULT OnWindowMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); + + // Called when the window is created + inline virtual void OnCreate() {}; + // Called when the window is destroyed + inline virtual void OnDestroy() {}; + // Called when the windows is beein updated (before events are served) + inline virtual void OnUpdate() {}; + // Called when the window is beeing closed by the user (return false to abort) + inline virtual bool OnClose() { return true; }; + // Called when nuklear drawcalls can be issued (return false to close the window) + inline virtual bool OnDraw(nk_context* ctx) { return true; }; + + private: + // Static window procs + static LRESULT wndProcSetup(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); + static LRESULT wndProcRun(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); + + private: + // Window handle + HWND m_window = NULL; + + // Nuklear context + nk_gdi_ctx m_nkGdiCtx = nullptr; + nk_context* m_nkCtx = nullptr; + + // Nuklear objects + GdiFont* m_gdiFont = nullptr; + HDC m_windowDc = NULL; + + // Window runtime features + bool m_isOpen = true; + bool m_isDraggin = false; + bool m_wsOverride = false; + bool m_isMaximized = false; + POINT m_dragOffset = {}; + int m_width = 0; + int m_height = 0; + }; +} + +#ifdef NKGDI_IMPLEMENT_WINDOW + +const wchar_t* const NkGdi::WindowClass::ClassName = L"WNDCLS_NkGdi"; +NkGdi::WindowClass NkGdi::WindowClass::ClassInstance; + +NkGdi::WindowClass::WindowClass() +{ + // Describe class + WNDCLASSEXW cls; + cls.cbSize = sizeof(WNDCLASSEXW); + cls.style = CS_OWNDC | CS_DBLCLKS; + cls.lpfnWndProc = &Window::wndProcSetup; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandle(NULL); + cls.hIcon = LoadIcon(NULL, IDI_APPLICATION); + cls.hCursor = LoadCursor(NULL, IDC_ARROW); + cls.hbrBackground = NULL; + cls.lpszMenuName = nullptr; + cls.lpszClassName = ClassName; + cls.hIconSm = NULL; + + // Register class + RegisterClassExW(&cls); +} + +NkGdi::Window::Window(unsigned int width, unsigned int height, const char* name, int posX, int posY) +{ + DWORD styleEx = WS_EX_WINDOWEDGE; + DWORD style = WS_POPUP; + + // Compute window size + RECT cr; + cr.left = 0; + cr.top = 0; + cr.right = width; + cr.bottom = height; + AdjustWindowRectEx(&cr, style, FALSE, styleEx); + + // Create the window + m_window = CreateWindowExW( + styleEx, + WindowClass::ClassName, + L"NkGdi", + style | WS_VISIBLE, + posX, posY, + cr.right - cr.left, cr.bottom - cr.top, + NULL, NULL, + GetModuleHandleW(nullptr), + this + ); + + // Rename window to user picked name + SetWindowTextA(m_window, name); + + // Get DC + m_windowDc = GetWindowDC(m_window); + + // Create font + m_gdiFont = nk_gdifont_create("Arial", 16); + m_nkCtx = nk_gdi_init(&m_nkGdiCtx, m_gdiFont, m_windowDc, width, height); +} + +NkGdi::Window::~Window() +{ + // Destroy GDI context + if (m_nkGdiCtx) + { + nk_gdi_shutdown(m_nkGdiCtx); + } + + // Destroy font + if (m_gdiFont) + { + nk_gdifont_del(m_gdiFont); + } + + // Close DC + if (m_windowDc) + { + ReleaseDC(m_window, m_windowDc); + } + + // Destroy window + if (m_window) + { + CloseWindow(m_window); + DestroyWindow(m_window); + } +} + +bool NkGdi::Window::Update() +{ + // Only process events while window is open + if (m_isOpen) + { + // Notify class that event processing has stated + OnUpdate(); + + // Windows event loop + MSG msg = {}; + nk_input_begin(m_nkCtx); + while (PeekMessage(&msg, m_window, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + nk_input_end(m_nkCtx); + + // Get title + char title[1024]; + GetWindowTextA(m_window, title, 1024); + + // Window body + if (m_wsOverride) + nk_window_set_bounds(m_nkCtx, title, nk_rect(0, 0, m_width, m_height)); + if (nk_begin(m_nkCtx, title, nk_rect(0, 0, m_width, m_height), + NK_WINDOW_BORDER | + (HasTitlebar ? NK_WINDOW_CLOSABLE | NK_WINDOW_TITLE : NULL) | + (m_isMaximized || !AllowSizing ? NULL : NK_WINDOW_SCALABLE) + )) + { + if(!OnDraw(m_nkCtx)) + m_isOpen = false; + + // Update window size + struct nk_rect bounds = nk_window_get_bounds(m_nkCtx); + if(bounds.w != m_width || bounds.h != m_height) + SetWindowPos(m_window, NULL, 0, 0, bounds.w, bounds.h, SWP_NOMOVE | SWP_NOOWNERZORDER); + } + else + { + // Handle window closing + if(OnClose()) + m_isOpen = false; + } + nk_end(m_nkCtx); + m_wsOverride = false; + + // Final render pass + nk_gdi_render(m_nkGdiCtx, nk_rgb(30, 30, 30)); + } + + return m_isOpen; +} + +LRESULT NkGdi::Window::OnWindowMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + // Switch on supplied message code + switch (msg) + { + // Close event + case WM_CLOSE: + if (OnClose()) + m_isOpen = false; + return 0; // Will always be handled internaly + + // While sizing + case WM_SIZING: + { + RECT cr; + GetClientRect(wnd, &cr); + m_width = cr.right - cr.left; + m_height = cr.bottom - cr.top; + } + break; + + // When sized + case WM_SIZE: + { + // Adjust maximize properties + if (wParam == SIZE_MAXIMIZED) + { + HMONITOR monitor = MonitorFromWindow(wnd, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFO); + if (GetMonitorInfoW(monitor, &monitorInfo)) + { + m_height = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; + m_width = monitorInfo.rcWork.right - monitorInfo.rcWork.left; + m_wsOverride = true; + m_isMaximized = true; + SetWindowPos(wnd, NULL, 0, 0, m_width, m_height, SWP_NOMOVE | SWP_NOZORDER); + } + } + else if (wParam == SIZE_RESTORED) + { + m_isMaximized = false; + } + + // Compute new bounds + RECT cr; + GetClientRect(wnd, &cr); + m_width = cr.right - cr.left; + m_height = cr.bottom - cr.top; + } + break; + + // When mouse start l-press (drag window) + case WM_LBUTTONDOWN: + { + if (HIWORD(lParam) <= 30 && AllowMove) + { + // Start dragging + m_isDraggin = true; + m_dragOffset.x = LOWORD(lParam); + m_dragOffset.y = HIWORD(lParam); + } + } + break; + + // When mouse stops l-press (drag window) + case WM_LBUTTONUP: + m_isDraggin = false; + break; + + // Mouse movement (dragging) + case WM_MOUSEMOVE: + { + if (m_isDraggin && !m_isMaximized) + { + // Get mouse postion and substract offset + POINT cursorPos; + GetCursorPos(&cursorPos); + cursorPos.x -= m_dragOffset.x; + cursorPos.y -= m_dragOffset.y; + // Use as new position + ShowWindow(m_window, SW_RESTORE); + SetWindowPos(wnd, NULL, cursorPos.x, cursorPos.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); + } + } + break; + + // On mouse doubble click (maximize) + case WM_LBUTTONDBLCLK: + { + if (HIWORD(lParam) <= 30 && AllowMaximize) + { + if (m_isMaximized) + { + ShowWindow(wnd, SW_RESTORE); + } + else + { + ShowWindow(wnd, SW_MAXIMIZE); + } + m_wsOverride = true; + } + } + break; + } + + // Send to nuklear + if (m_nkGdiCtx && nk_gdi_handle_event(m_nkGdiCtx, wnd, msg, wParam, lParam)) + return 0; + + // In case this is ever reached: Run default behaviour + return DefWindowProc(wnd, msg, wParam, lParam); +} + +LRESULT NkGdi::Window::wndProcSetup(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + // Wait for setup message + if (msg == WM_NCCREATE) + { + // Get creation parameters & window pointer + CREATESTRUCT* ptrCr = (CREATESTRUCT*)lParam; + Window* ptrWindow = (Window*)ptrCr->lpCreateParams; + + // Store pointer and new proc in window + SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)ptrWindow); + SetWindowLongPtr(wnd, GWLP_WNDPROC, (LONG_PTR)&wndProcRun); + + // Handled by window + return ptrWindow->OnWindowMessage(wnd, msg, wParam, lParam); + } + + // Default handler + return DefWindowProc(wnd, msg, wParam, lParam); +} + +LRESULT NkGdi::Window::wndProcRun(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + // Get window pointer + Window* ptrWindow = (Window*)GetWindowLongPtr(wnd, GWLP_USERDATA); + // Call window + return ptrWindow->OnWindowMessage(wnd, msg, wParam, lParam); +} + +#endif From 169470d2e3bfcea4956aeb5ae82a127f29f5afaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludwig=20F=C3=BCchsl?= Date: Mon, 28 Feb 2022 14:57:47 +0100 Subject: [PATCH 2/6] Converted from C++ to C WARNING: Is currently not working! Needs more debugging. --- demo/gdi_native_nuklear/build.bat | 2 +- demo/gdi_native_nuklear/{main.cpp => main.c} | 25 +- demo/gdi_native_nuklear/window.h | 356 +++++++++---------- 3 files changed, 183 insertions(+), 200 deletions(-) rename demo/gdi_native_nuklear/{main.cpp => main.c} (61%) diff --git a/demo/gdi_native_nuklear/build.bat b/demo/gdi_native_nuklear/build.bat index 65a8ce6..2f87f8e 100644 --- a/demo/gdi_native_nuklear/build.bat +++ b/demo/gdi_native_nuklear/build.bat @@ -3,4 +3,4 @@ rem This will use VS2015 for compiler call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 -cl /nologo /W3 /O2 /fp:fast /Gm- /D_CRT_SECURE_NO_DEPRECATE /Fedemo.exe main.cpp user32.lib gdi32.lib Msimg32.lib /link /incremental:no +cl /nologo /W3 /O2 /fp:fast /Gm- /D_CRT_SECURE_NO_DEPRECATE /Fedemo.exe main.c user32.lib gdi32.lib Msimg32.lib /link /incremental:no diff --git a/demo/gdi_native_nuklear/main.cpp b/demo/gdi_native_nuklear/main.c similarity index 61% rename from demo/gdi_native_nuklear/main.cpp rename to demo/gdi_native_nuklear/main.c index 000ea2a..70ba268 100644 --- a/demo/gdi_native_nuklear/main.cpp +++ b/demo/gdi_native_nuklear/main.c @@ -1,5 +1,4 @@ #include -#include #pragma comment(linker,"\"/manifestdependency:type='win32' \ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ @@ -19,19 +18,21 @@ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") INT WINAPI wWinMain(HINSTANCE _In_ hInstance, HINSTANCE _In_opt_ hPrevInstance, PWSTR _In_ cmdArgs, INT _In_ cmdShow) { - NkGdi::Window w1(500, 500, "F1", 10, 10); - w1.AllowSizing = false; - w1.AllowMaximize = false; - w1.AllowMove = false; - w1.HasTitlebar = false; + struct nkgdi_window w1, w2; - NkGdi::Window w2(500, 500, "F2", 520, 10); - w2.AllowSizing = true; - w2.AllowMaximize = true; - w2.AllowMove = true; - w2.HasTitlebar = true; + w1.allow_sizing = 0; + w1.allow_maximize = 0; + w1.allow_move = 0; + w1.has_titlebar = 0; + nkgdi_window_create(&w1, 500, 500, "F1", 10, 10); + + w2.allow_sizing = 1; + w2.allow_maximize = 1; + w2.allow_move = 1; + w2.has_titlebar = 1; + nkgdi_window_create(&w2, 500, 500, "F2", 520, 10); - while (w1.Update() && w2.Update()) Sleep(20); + while (nkgdi_window_update(&w1) && nkgdi_window_update(&w2)) Sleep(20); return 0; } diff --git a/demo/gdi_native_nuklear/window.h b/demo/gdi_native_nuklear/window.h index 123e4a7..ce60475 100644 --- a/demo/gdi_native_nuklear/window.h +++ b/demo/gdi_native_nuklear/window.h @@ -1,117 +1,93 @@ -#pragma once +#ifndef NK_GDI_WINDOW +#define NK_GDI_WINDOW -namespace NkGdi +#define NK_GDI_WINDOW_CLS L"WNDCLS_NkGdi" + +#include + +/* Functin pointer types for window callbacks */ +typedef void(*nkgdi_window_func_update)(void); +typedef int(*nkgdi_window_func_close)(void); +typedef int(*nkgdi_window_func_draw)(struct nk_context*); + +/* Window container / context */ +struct nkgdi_window { - // Container for window management - class WindowClass + /* Properties */ + int allow_sizing; + int allow_maximize; + int allow_move; + int has_titlebar; + + /* Callbacks */ + nkgdi_window_func_update cb_on_update; + nkgdi_window_func_close cb_on_close; + nkgdi_window_func_draw cb_on_draw; + + /* Internal Data */ + struct { - public: - // Public exposed name - static const wchar_t* const ClassName; + // Window handle + HWND window_handle; - WindowClass(const WindowClass&) = delete; - private: - // Instance - static WindowClass ClassInstance; + // Nuklear context + nk_gdi_ctx nk_gdi_ctx; + struct nk_context* nk_ctx; - // Sigelton - WindowClass(); - }; + // Nuklear objects + GdiFont* gdi_font; + HDC window_dc; - // Window base class - class Window - { - friend class WindowClass; + // Window runtime features + int is_open; + int is_draggin; + int ws_override; + int is_maximized; + POINT drag_offset; + int width; + int height; + }_internal; +}; - public: - // Default constructs - Window() = default; - Window(const Window&) = delete; - Window(Window&&) = default; - // Constructor - Window(unsigned int width, unsigned int height, const char* name, int posX = 100, int posY = 100); - // Destructor - ~Window(); - - // Processs window events and render the window (returns true as long as window is open) - bool Update(); - - // Properties - bool AllowSizing = true; - bool AllowMaximize = true; - bool AllowMove = true; - bool HasTitlebar = true; - - public: - // Called when the core window gets an event - virtual LRESULT OnWindowMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); - - // Called when the window is created - inline virtual void OnCreate() {}; - // Called when the window is destroyed - inline virtual void OnDestroy() {}; - // Called when the windows is beein updated (before events are served) - inline virtual void OnUpdate() {}; - // Called when the window is beeing closed by the user (return false to abort) - inline virtual bool OnClose() { return true; }; - // Called when nuklear drawcalls can be issued (return false to close the window) - inline virtual bool OnDraw(nk_context* ctx) { return true; }; - - private: - // Static window procs - static LRESULT wndProcSetup(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); - static LRESULT wndProcRun(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); - - private: - // Window handle - HWND m_window = NULL; - - // Nuklear context - nk_gdi_ctx m_nkGdiCtx = nullptr; - nk_context* m_nkCtx = nullptr; - - // Nuklear objects - GdiFont* m_gdiFont = nullptr; - HDC m_windowDc = NULL; - - // Window runtime features - bool m_isOpen = true; - bool m_isDraggin = false; - bool m_wsOverride = false; - bool m_isMaximized = false; - POINT m_dragOffset = {}; - int m_width = 0; - int m_height = 0; - }; -} +/* API */ +void nkgdi_window_init(void); +void nkgdi_window_shutdown(void); +void nkgdi_window_create(struct nkgdi_window* wnd, unsigned int width, unsigned int height, const char* name, int posX, int posY); +int nkgdi_window_update(struct nkgdi_window* wnd); +void nkgdi_window_destroy(struct nkgdi_window* wnd); #ifdef NKGDI_IMPLEMENT_WINDOW -const wchar_t* const NkGdi::WindowClass::ClassName = L"WNDCLS_NkGdi"; -NkGdi::WindowClass NkGdi::WindowClass::ClassInstance; +LRESULT nkgdi_window_proc_setup(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); +LRESULT nkgdi_window_proc_run(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); -NkGdi::WindowClass::WindowClass() +void nkgdi_window_init(void) { // Describe class WNDCLASSEXW cls; cls.cbSize = sizeof(WNDCLASSEXW); cls.style = CS_OWNDC | CS_DBLCLKS; - cls.lpfnWndProc = &Window::wndProcSetup; + cls.lpfnWndProc = &nkgdi_window_proc_setup; cls.cbClsExtra = 0; cls.cbWndExtra = 0; cls.hInstance = GetModuleHandle(NULL); cls.hIcon = LoadIcon(NULL, IDI_APPLICATION); cls.hCursor = LoadCursor(NULL, IDC_ARROW); cls.hbrBackground = NULL; - cls.lpszMenuName = nullptr; - cls.lpszClassName = ClassName; + cls.lpszMenuName = NULL; + cls.lpszClassName = NK_GDI_WINDOW_CLS; cls.hIconSm = NULL; // Register class RegisterClassExW(&cls); } -NkGdi::Window::Window(unsigned int width, unsigned int height, const char* name, int posX, int posY) +void nkgdi_window_shutdown(void) +{ + UnregisterClassW(NK_GDI_WINDOW_CLS, GetModuleHandle(NULL)); +} + +void nkgdi_window_create(struct nkgdi_window* wnd, unsigned int width, unsigned int height, const char* name, int posX, int posY) { DWORD styleEx = WS_EX_WINDOWEDGE; DWORD style = WS_POPUP; @@ -125,121 +101,155 @@ NkGdi::Window::Window(unsigned int width, unsigned int height, const char* name, AdjustWindowRectEx(&cr, style, FALSE, styleEx); // Create the window - m_window = CreateWindowExW( + wnd->_internal.window_handle = CreateWindowExW( styleEx, - WindowClass::ClassName, + NK_GDI_WINDOW_CLS, L"NkGdi", style | WS_VISIBLE, posX, posY, cr.right - cr.left, cr.bottom - cr.top, NULL, NULL, - GetModuleHandleW(nullptr), - this + GetModuleHandleW(NULL), + wnd ); // Rename window to user picked name - SetWindowTextA(m_window, name); + SetWindowTextA(wnd->_internal.window_handle, name); // Get DC - m_windowDc = GetWindowDC(m_window); + wnd->_internal.window_dc = GetWindowDC(wnd->_internal.window_handle); // Create font - m_gdiFont = nk_gdifont_create("Arial", 16); - m_nkCtx = nk_gdi_init(&m_nkGdiCtx, m_gdiFont, m_windowDc, width, height); + wnd->_internal.gdi_font = nk_gdifont_create("Arial", 16); + wnd->_internal.nk_ctx = nk_gdi_init(&wnd->_internal.nk_gdi_ctx, wnd->_internal.gdi_font, wnd->_internal.window_dc, width, height); + + // Setup internal data + wnd->_internal.is_open = 1; + wnd->_internal.is_draggin = 0; + wnd->_internal.ws_override = 0; + wnd->_internal.is_maximized = 0; + wnd->_internal.drag_offset.x = 0; + wnd->_internal.drag_offset.y = 0; + wnd->_internal.width = 0; + wnd->_internal.height = 0; } -NkGdi::Window::~Window() +void nkgdi_window_destroy(struct nkgdi_window* wnd) { // Destroy GDI context - if (m_nkGdiCtx) + if (wnd->_internal.nk_gdi_ctx) { - nk_gdi_shutdown(m_nkGdiCtx); + nk_gdi_shutdown(wnd->_internal.nk_gdi_ctx); } // Destroy font - if (m_gdiFont) + if (wnd->_internal.gdi_font) { - nk_gdifont_del(m_gdiFont); + nk_gdifont_del(wnd->_internal.gdi_font); } // Close DC - if (m_windowDc) + if (wnd->_internal.window_dc) { - ReleaseDC(m_window, m_windowDc); + ReleaseDC(wnd->_internal.window_handle, wnd->_internal.window_dc); } // Destroy window - if (m_window) + if (wnd->_internal.window_handle) { - CloseWindow(m_window); - DestroyWindow(m_window); + CloseWindow(wnd->_internal.window_handle); + DestroyWindow(wnd->_internal.window_handle); } } -bool NkGdi::Window::Update() +int nkgdi_window_update(struct nkgdi_window* wnd) { // Only process events while window is open - if (m_isOpen) + if (wnd->_internal.is_open) { - // Notify class that event processing has stated - OnUpdate(); - // Windows event loop - MSG msg = {}; - nk_input_begin(m_nkCtx); - while (PeekMessage(&msg, m_window, 0, 0, PM_REMOVE)) + MSG msg; + nk_input_begin(wnd->_internal.nk_ctx); + while (PeekMessage(&msg, wnd->_internal.window_handle, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } - nk_input_end(m_nkCtx); + nk_input_end(wnd->_internal.nk_ctx); // Get title char title[1024]; - GetWindowTextA(m_window, title, 1024); + GetWindowTextA(wnd->_internal.window_handle, title, 1024); + + // Window flags + nk_flags window_flags = NK_WINDOW_BORDER; + if(wnd->has_titlebar) + window_flags |= NK_WINDOW_CLOSABLE | NK_WINDOW_TITLE; + if(!wnd->_internal.is_maximized && wnd->allow_sizing) + window_flags |= NK_WINDOW_SCALABLE; // Window body - if (m_wsOverride) - nk_window_set_bounds(m_nkCtx, title, nk_rect(0, 0, m_width, m_height)); - if (nk_begin(m_nkCtx, title, nk_rect(0, 0, m_width, m_height), - NK_WINDOW_BORDER | - (HasTitlebar ? NK_WINDOW_CLOSABLE | NK_WINDOW_TITLE : NULL) | - (m_isMaximized || !AllowSizing ? NULL : NK_WINDOW_SCALABLE) - )) + if (wnd->_internal.ws_override) + nk_window_set_bounds(wnd->_internal.nk_ctx, title, nk_rect(0, 0, wnd->_internal.width, wnd->_internal.height)); + if (nk_begin(wnd->_internal.nk_ctx, title, nk_rect(0, 0, wnd->_internal.width, wnd->_internal.height), window_flags)) { - if(!OnDraw(m_nkCtx)) - m_isOpen = false; + if(!wnd->cb_on_draw(wnd->_internal.nk_ctx)) + wnd->_internal.is_open = 0; // Update window size - struct nk_rect bounds = nk_window_get_bounds(m_nkCtx); - if(bounds.w != m_width || bounds.h != m_height) - SetWindowPos(m_window, NULL, 0, 0, bounds.w, bounds.h, SWP_NOMOVE | SWP_NOOWNERZORDER); + struct nk_rect bounds = nk_window_get_bounds(wnd->_internal.nk_ctx); + if(bounds.w != wnd->_internal.width || bounds.h != wnd->_internal.height) + SetWindowPos(wnd->_internal.window_handle, NULL, 0, 0, bounds.w, bounds.h, SWP_NOMOVE | SWP_NOOWNERZORDER); } else { // Handle window closing - if(OnClose()) - m_isOpen = false; + if(!wnd->cb_on_close || wnd->cb_on_close()) + wnd->_internal.is_open = 0; } - nk_end(m_nkCtx); - m_wsOverride = false; + nk_end(wnd->_internal.nk_ctx); + wnd->_internal.ws_override = 0; // Final render pass - nk_gdi_render(m_nkGdiCtx, nk_rgb(30, 30, 30)); + nk_gdi_render(wnd->_internal.nk_gdi_ctx, nk_rgb(30, 30, 30)); } - return m_isOpen; + return wnd->_internal.is_open; } -LRESULT NkGdi::Window::OnWindowMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) +LRESULT nkgdi_window_proc_setup(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { + // Wait for setup message + if (msg == WM_NCCREATE) + { + // Get creation parameters & window pointer + CREATESTRUCT* ptrCr = (CREATESTRUCT*)lParam; + struct nkgdi_window* nkgdi_wnd = (struct nkgdi_window*)ptrCr->lpCreateParams; + + // Store pointer and new proc in window + SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)nkgdi_wnd); + SetWindowLongPtr(wnd, GWLP_WNDPROC, (LONG_PTR)&nkgdi_window_proc_run); + + // Handled by window + return nkgdi_window_proc_run(wnd, msg, wParam, lParam); + } + + // Default handler + return DefWindowProc(wnd, msg, wParam, lParam); +} + +LRESULT nkgdi_window_proc_run(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + // Get window pointer + struct nkgdi_window* nkwnd = (struct nkgdi_window*)GetWindowLongPtrW(wnd, GWLP_USERDATA); + // Switch on supplied message code switch (msg) { // Close event case WM_CLOSE: - if (OnClose()) - m_isOpen = false; + if(!nkwnd->cb_on_close || nkwnd->cb_on_close()) + nkwnd->_internal.is_open = 0; return 0; // Will always be handled internaly // While sizing @@ -247,8 +257,8 @@ LRESULT NkGdi::Window::OnWindowMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM { RECT cr; GetClientRect(wnd, &cr); - m_width = cr.right - cr.left; - m_height = cr.bottom - cr.top; + nkwnd->_internal.width = cr.right - cr.left; + nkwnd->_internal.height = cr.bottom - cr.top; } break; @@ -263,56 +273,56 @@ LRESULT NkGdi::Window::OnWindowMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM monitorInfo.cbSize = sizeof(MONITORINFO); if (GetMonitorInfoW(monitor, &monitorInfo)) { - m_height = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; - m_width = monitorInfo.rcWork.right - monitorInfo.rcWork.left; - m_wsOverride = true; - m_isMaximized = true; - SetWindowPos(wnd, NULL, 0, 0, m_width, m_height, SWP_NOMOVE | SWP_NOZORDER); + nkwnd->_internal.height = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; + nkwnd->_internal.width = monitorInfo.rcWork.right - monitorInfo.rcWork.left; + nkwnd->_internal.ws_override = 1; + nkwnd->_internal.is_maximized = 1; + SetWindowPos(wnd, NULL, 0, 0, nkwnd->_internal.width, nkwnd->_internal.height, SWP_NOMOVE | SWP_NOZORDER); } } else if (wParam == SIZE_RESTORED) { - m_isMaximized = false; + nkwnd->_internal.is_maximized = 0; } // Compute new bounds RECT cr; GetClientRect(wnd, &cr); - m_width = cr.right - cr.left; - m_height = cr.bottom - cr.top; + nkwnd->_internal.width = cr.right - cr.left; + nkwnd->_internal.height = cr.bottom - cr.top; } break; // When mouse start l-press (drag window) case WM_LBUTTONDOWN: { - if (HIWORD(lParam) <= 30 && AllowMove) + if (HIWORD(lParam) <= 30 && nkwnd->allow_move) { // Start dragging - m_isDraggin = true; - m_dragOffset.x = LOWORD(lParam); - m_dragOffset.y = HIWORD(lParam); + nkwnd->_internal.is_draggin = 1; + nkwnd->_internal.drag_offset.x = LOWORD(lParam); + nkwnd->_internal.drag_offset.y = HIWORD(lParam); } } break; // When mouse stops l-press (drag window) case WM_LBUTTONUP: - m_isDraggin = false; + nkwnd->_internal.is_draggin = 0; break; // Mouse movement (dragging) case WM_MOUSEMOVE: { - if (m_isDraggin && !m_isMaximized) + if (nkwnd->_internal.is_draggin && !nkwnd->_internal.is_maximized) { // Get mouse postion and substract offset POINT cursorPos; GetCursorPos(&cursorPos); - cursorPos.x -= m_dragOffset.x; - cursorPos.y -= m_dragOffset.y; + cursorPos.x -= nkwnd->_internal.drag_offset.x; + cursorPos.y -= nkwnd->_internal.drag_offset.y; // Use as new position - ShowWindow(m_window, SW_RESTORE); + ShowWindow(wnd, SW_RESTORE); SetWindowPos(wnd, NULL, cursorPos.x, cursorPos.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); } } @@ -321,9 +331,9 @@ LRESULT NkGdi::Window::OnWindowMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM // On mouse doubble click (maximize) case WM_LBUTTONDBLCLK: { - if (HIWORD(lParam) <= 30 && AllowMaximize) + if (HIWORD(lParam) <= 30 && nkwnd->allow_maximize) { - if (m_isMaximized) + if (nkwnd->_internal.is_maximized) { ShowWindow(wnd, SW_RESTORE); } @@ -331,47 +341,19 @@ LRESULT NkGdi::Window::OnWindowMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM { ShowWindow(wnd, SW_MAXIMIZE); } - m_wsOverride = true; + nkwnd->_internal.ws_override = 1; } } break; } // Send to nuklear - if (m_nkGdiCtx && nk_gdi_handle_event(m_nkGdiCtx, wnd, msg, wParam, lParam)) + if (nkwnd->_internal.nk_gdi_ctx && nk_gdi_handle_event(nkwnd->_internal.nk_gdi_ctx, wnd, msg, wParam, lParam)) return 0; // In case this is ever reached: Run default behaviour return DefWindowProc(wnd, msg, wParam, lParam); } -LRESULT NkGdi::Window::wndProcSetup(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - // Wait for setup message - if (msg == WM_NCCREATE) - { - // Get creation parameters & window pointer - CREATESTRUCT* ptrCr = (CREATESTRUCT*)lParam; - Window* ptrWindow = (Window*)ptrCr->lpCreateParams; - - // Store pointer and new proc in window - SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)ptrWindow); - SetWindowLongPtr(wnd, GWLP_WNDPROC, (LONG_PTR)&wndProcRun); - - // Handled by window - return ptrWindow->OnWindowMessage(wnd, msg, wParam, lParam); - } - - // Default handler - return DefWindowProc(wnd, msg, wParam, lParam); -} - -LRESULT NkGdi::Window::wndProcRun(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - // Get window pointer - Window* ptrWindow = (Window*)GetWindowLongPtr(wnd, GWLP_USERDATA); - // Call window - return ptrWindow->OnWindowMessage(wnd, msg, wParam, lParam); -} - +#endif #endif From 33395aabdb439ac41998f5c57982d2dfc8fc09a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludwig=20F=C3=BCchsl?= Date: Mon, 28 Feb 2022 15:53:54 +0100 Subject: [PATCH 3/6] Fixed non displayed windows. WARNING: Drawing is not done correctly --- demo/gdi_native_nuklear/main.c | 16 ++++++++++++++++ demo/gdi_native_nuklear/window.h | 6 +++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/demo/gdi_native_nuklear/main.c b/demo/gdi_native_nuklear/main.c index 70ba268..05ed83b 100644 --- a/demo/gdi_native_nuklear/main.c +++ b/demo/gdi_native_nuklear/main.c @@ -16,23 +16,39 @@ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #define NKGDI_IMPLEMENT_WINDOW #include "window.h" +int drawCallback(struct nk_context* ctx) +{ + nk_label(ctx, "Label", NK_TEXT_ALIGN_CENTERED); + nk_button_label(ctx, "Test 1234"); + return 1; +} + INT WINAPI wWinMain(HINSTANCE _In_ hInstance, HINSTANCE _In_opt_ hPrevInstance, PWSTR _In_ cmdArgs, INT _In_ cmdShow) { + nkgdi_window_init(); struct nkgdi_window w1, w2; + memset(&w1, 0x0, sizeof(struct nkgdi_window)); + memset(&w2, 0x0, sizeof(struct nkgdi_window)); w1.allow_sizing = 0; w1.allow_maximize = 0; w1.allow_move = 0; w1.has_titlebar = 0; + w1.cb_on_draw = &drawCallback; nkgdi_window_create(&w1, 500, 500, "F1", 10, 10); w2.allow_sizing = 1; w2.allow_maximize = 1; w2.allow_move = 1; w2.has_titlebar = 1; + w2.cb_on_draw = &drawCallback; nkgdi_window_create(&w2, 500, 500, "F2", 520, 10); while (nkgdi_window_update(&w1) && nkgdi_window_update(&w2)) Sleep(20); + nkgdi_window_destroy(&w1); + nkgdi_window_destroy(&w2); + + nkgdi_window_shutdown(); return 0; } diff --git a/demo/gdi_native_nuklear/window.h b/demo/gdi_native_nuklear/window.h index ce60475..ee1b5de 100644 --- a/demo/gdi_native_nuklear/window.h +++ b/demo/gdi_native_nuklear/window.h @@ -130,8 +130,8 @@ void nkgdi_window_create(struct nkgdi_window* wnd, unsigned int width, unsigned wnd->_internal.is_maximized = 0; wnd->_internal.drag_offset.x = 0; wnd->_internal.drag_offset.y = 0; - wnd->_internal.width = 0; - wnd->_internal.height = 0; + wnd->_internal.width = width; + wnd->_internal.height = height; } void nkgdi_window_destroy(struct nkgdi_window* wnd) @@ -193,7 +193,7 @@ int nkgdi_window_update(struct nkgdi_window* wnd) nk_window_set_bounds(wnd->_internal.nk_ctx, title, nk_rect(0, 0, wnd->_internal.width, wnd->_internal.height)); if (nk_begin(wnd->_internal.nk_ctx, title, nk_rect(0, 0, wnd->_internal.width, wnd->_internal.height), window_flags)) { - if(!wnd->cb_on_draw(wnd->_internal.nk_ctx)) + if(wnd->cb_on_draw && !wnd->cb_on_draw(wnd->_internal.nk_ctx)) wnd->_internal.is_open = 0; // Update window size From a31c6c0089a2e13ad6dbfc8cc29cc975cf3afcc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludwig=20F=C3=BCchsl?= Date: Fri, 4 Mar 2022 13:16:47 +0100 Subject: [PATCH 4/6] Bug fix & Added comments --- demo/gdi_native_nuklear/main.c | 76 +++++++++++++- demo/gdi_native_nuklear/nuklear_gdi.h | 5 + demo/gdi_native_nuklear/window.h | 142 +++++++++++++++----------- 3 files changed, 164 insertions(+), 59 deletions(-) diff --git a/demo/gdi_native_nuklear/main.c b/demo/gdi_native_nuklear/main.c index 05ed83b..b6cb6a9 100644 --- a/demo/gdi_native_nuklear/main.c +++ b/demo/gdi_native_nuklear/main.c @@ -4,6 +4,9 @@ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +/* Includes the default nuklear implementation + * Includes the modified GDI backend (No more global state to allow multiple hwnd's) + */ #define NK_INCLUDE_FIXED_TYPES #define NK_INCLUDE_STANDARD_IO #define NK_INCLUDE_STANDARD_VARARGS @@ -13,23 +16,88 @@ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #include "../../nuklear.h" #include "nuklear_gdi.h" +/* Include the window framework (the new fancy code of this demo) */ #define NKGDI_IMPLEMENT_WINDOW #include "window.h" +/* This callback will be called when the window is draw + * You will NOT need to call nk_begin(...) and nk_end(...) + * begin and end are handled by the parent code calling this + * callback + */ int drawCallback(struct nk_context* ctx) { - nk_label(ctx, "Label", NK_TEXT_ALIGN_CENTERED); - nk_button_label(ctx, "Test 1234"); + /* Code is from ../calculator.c */ + static int set = 0, prev = 0, op = 0; + static const char numbers[] = "789456123"; + static const char ops[] = "+-*/"; + static double a = 0, b = 0; + static double *current = &a; + + size_t i = 0; + int solve = 0; + {int len; char buffer[256]; + nk_layout_row_dynamic(ctx, 35, 1); + len = snprintf(buffer, 256, "%.2f", *current); + nk_edit_string(ctx, NK_EDIT_SIMPLE, buffer, &len, 255, nk_filter_float); + buffer[len] = 0; + *current = atof(buffer);} + + nk_layout_row_dynamic(ctx, 35, 4); + for (i = 0; i < 16; ++i) { + if (i >= 12 && i < 15) { + if (i > 12) continue; + if (nk_button_label(ctx, "C")) { + a = b = op = 0; current = &a; set = 0; + } if (nk_button_label(ctx, "0")) { + *current = *current*10.0f; set = 0; + } if (nk_button_label(ctx, "=")) { + solve = 1; prev = op; op = 0; + } + } else if (((i+1) % 4)) { + if (nk_button_text(ctx, &numbers[(i/4)*3+i%4], 1)) { + *current = *current * 10.0f + numbers[(i/4)*3+i%4] - '0'; + set = 0; + } + } else if (nk_button_text(ctx, &ops[i/4], 1)) { + if (!set) { + if (current != &b) { + current = &b; + } else { + prev = op; + solve = 1; + } + } + op = ops[i/4]; + set = 1; + } + } + if (solve) { + if (prev == '+') a = a + b; + if (prev == '-') a = a - b; + if (prev == '*') a = a * b; + if (prev == '/') a = a / b; + current = &a; + if (set) current = &b; + b = 0; set = 0; + } return 1; } +/* Main entry point (Windows wchar_t) */ INT WINAPI wWinMain(HINSTANCE _In_ hInstance, HINSTANCE _In_opt_ hPrevInstance, PWSTR _In_ cmdArgs, INT _In_ cmdShow) { + /* Call this first to setup all required prerequisites */ nkgdi_window_init(); + + /* Preparing two window contexts */ struct nkgdi_window w1, w2; memset(&w1, 0x0, sizeof(struct nkgdi_window)); memset(&w2, 0x0, sizeof(struct nkgdi_window)); + /* Configure and create window 1. + * Note: You can allways change the direct accesible parameters later as well! + */ w1.allow_sizing = 0; w1.allow_maximize = 0; w1.allow_move = 0; @@ -37,6 +105,7 @@ INT WINAPI wWinMain(HINSTANCE _In_ hInstance, HINSTANCE _In_opt_ hPrevInstance, w1.cb_on_draw = &drawCallback; nkgdi_window_create(&w1, 500, 500, "F1", 10, 10); + /* Configure and create window 2 */ w2.allow_sizing = 1; w2.allow_maximize = 1; w2.allow_move = 1; @@ -44,11 +113,14 @@ INT WINAPI wWinMain(HINSTANCE _In_ hInstance, HINSTANCE _In_opt_ hPrevInstance, w2.cb_on_draw = &drawCallback; nkgdi_window_create(&w2, 500, 500, "F2", 520, 10); + /* As long as both windows are valid (nkgdi_window_update returning 1) */ while (nkgdi_window_update(&w1) && nkgdi_window_update(&w2)) Sleep(20); + /* Destroy both windows context */ nkgdi_window_destroy(&w1); nkgdi_window_destroy(&w2); + /* Call nkgdi_window_shutdown to properly shutdown the gdi window framework */ nkgdi_window_shutdown(); return 0; } diff --git a/demo/gdi_native_nuklear/nuklear_gdi.h b/demo/gdi_native_nuklear/nuklear_gdi.h index aef6452..5c2f1f8 100644 --- a/demo/gdi_native_nuklear/nuklear_gdi.h +++ b/demo/gdi_native_nuklear/nuklear_gdi.h @@ -2,6 +2,11 @@ * Nuklear - 1.32.0 - public domain * no warrenty implied; use at your own risk. * authored from 2015-2016 by Micha Mettke + * + * Modified GDI backend 2022 + * Now based on a context that is required for each API function call. + * Removes the global state --> you can have multiple windows :-) + * */ /* * ============================================================== diff --git a/demo/gdi_native_nuklear/window.h b/demo/gdi_native_nuklear/window.h index ee1b5de..0db572f 100644 --- a/demo/gdi_native_nuklear/window.h +++ b/demo/gdi_native_nuklear/window.h @@ -6,40 +6,45 @@ #include /* Functin pointer types for window callbacks */ -typedef void(*nkgdi_window_func_update)(void); typedef int(*nkgdi_window_func_close)(void); typedef int(*nkgdi_window_func_draw)(struct nk_context*); /* Window container / context */ struct nkgdi_window { - /* Properties */ + /* The window can be sized */ int allow_sizing; + /* The window can be maximized by double clicking the titlebar */ int allow_maximize; + /* The window is allowed to be moved by the user */ int allow_move; + /* The window will render it's title bar */ int has_titlebar; /* Callbacks */ - nkgdi_window_func_update cb_on_update; + /* Called when the user or os requests a window close (return 1 to accept the reqest)*/ nkgdi_window_func_close cb_on_close; + /* Called each time the window content should be drawn. Here you will do your nuklear drawing code + * but WITHOUT nk_begin and nk_end. Return 1 to keep the window open. + */ nkgdi_window_func_draw cb_on_draw; /* Internal Data */ struct { - // Window handle + /* Window handle */ HWND window_handle; - // Nuklear context + /* Nuklear & GDI context */ nk_gdi_ctx nk_gdi_ctx; struct nk_context* nk_ctx; - // Nuklear objects + /* GDI required objects */ GdiFont* gdi_font; HDC window_dc; - // Window runtime features - int is_open; + /* Internally used state variables */ + int is_open; int is_draggin; int ws_override; int is_maximized; @@ -50,20 +55,28 @@ struct nkgdi_window }; /* API */ +/* This function will init all resources used by the implementation */ void nkgdi_window_init(void); +/* This function will free all globally used resources */ void nkgdi_window_shutdown(void); +/* Creates a new window (for the wnd context) */ void nkgdi_window_create(struct nkgdi_window* wnd, unsigned int width, unsigned int height, const char* name, int posX, int posY); +/* Updates the window (Windows message loop, nuklear loop and drawing). Returns one as long as the window is valid and open */ int nkgdi_window_update(struct nkgdi_window* wnd); +/* Destroys the window context wnd */ void nkgdi_window_destroy(struct nkgdi_window* wnd); #ifdef NKGDI_IMPLEMENT_WINDOW +/* Predeclare the windows window message procs */ +/* This proc will setup the pointer to the window context */ LRESULT nkgdi_window_proc_setup(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); +/* This proc will take the window context pointer and performs operations on it*/ LRESULT nkgdi_window_proc_run(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); void nkgdi_window_init(void) { - // Describe class + /* Describe the window class */ WNDCLASSEXW cls; cls.cbSize = sizeof(WNDCLASSEXW); cls.style = CS_OWNDC | CS_DBLCLKS; @@ -78,12 +91,13 @@ void nkgdi_window_init(void) cls.lpszClassName = NK_GDI_WINDOW_CLS; cls.hIconSm = NULL; - // Register class + /* Register the window class */ RegisterClassExW(&cls); } void nkgdi_window_shutdown(void) { + /* Windows class no longer required, unregister it */ UnregisterClassW(NK_GDI_WINDOW_CLS, GetModuleHandle(NULL)); } @@ -92,7 +106,7 @@ void nkgdi_window_create(struct nkgdi_window* wnd, unsigned int width, unsigned DWORD styleEx = WS_EX_WINDOWEDGE; DWORD style = WS_POPUP; - // Compute window size + /* Adjust window size to fit selected window styles */ RECT cr; cr.left = 0; cr.top = 0; @@ -100,7 +114,7 @@ void nkgdi_window_create(struct nkgdi_window* wnd, unsigned int width, unsigned cr.bottom = height; AdjustWindowRectEx(&cr, style, FALSE, styleEx); - // Create the window + /* Create the new window */ wnd->_internal.window_handle = CreateWindowExW( styleEx, NK_GDI_WINDOW_CLS, @@ -113,17 +127,19 @@ void nkgdi_window_create(struct nkgdi_window* wnd, unsigned int width, unsigned wnd ); - // Rename window to user picked name + /* Give the window the ascii char name */ SetWindowTextA(wnd->_internal.window_handle, name); - // Get DC + /* Extract the window dc for gdi drawing */ wnd->_internal.window_dc = GetWindowDC(wnd->_internal.window_handle); - // Create font + /* Create the gdi font required to draw text */ wnd->_internal.gdi_font = nk_gdifont_create("Arial", 16); + + /* Init the gdi backend */ wnd->_internal.nk_ctx = nk_gdi_init(&wnd->_internal.nk_gdi_ctx, wnd->_internal.gdi_font, wnd->_internal.window_dc, width, height); - // Setup internal data + /* Bring all internal variables to a defined and valid initial state */ wnd->_internal.is_open = 1; wnd->_internal.is_draggin = 0; wnd->_internal.ws_override = 0; @@ -136,25 +152,19 @@ void nkgdi_window_create(struct nkgdi_window* wnd, unsigned int width, unsigned void nkgdi_window_destroy(struct nkgdi_window* wnd) { - // Destroy GDI context + /* Destroy all objects in reverse order */ if (wnd->_internal.nk_gdi_ctx) { nk_gdi_shutdown(wnd->_internal.nk_gdi_ctx); } - - // Destroy font if (wnd->_internal.gdi_font) { nk_gdifont_del(wnd->_internal.gdi_font); } - - // Close DC if (wnd->_internal.window_dc) { ReleaseDC(wnd->_internal.window_handle, wnd->_internal.window_dc); } - - // Destroy window if (wnd->_internal.window_handle) { CloseWindow(wnd->_internal.window_handle); @@ -164,10 +174,10 @@ void nkgdi_window_destroy(struct nkgdi_window* wnd) int nkgdi_window_update(struct nkgdi_window* wnd) { - // Only process events while window is open + /* The window will only be updated when it is open / valid */ if (wnd->_internal.is_open) { - // Windows event loop + /* First all pending window events will be processed */ MSG msg; nk_input_begin(wnd->_internal.nk_ctx); while (PeekMessage(&msg, wnd->_internal.window_handle, 0, 0, PM_REMOVE)) @@ -177,41 +187,46 @@ int nkgdi_window_update(struct nkgdi_window* wnd) } nk_input_end(wnd->_internal.nk_ctx); - // Get title + /* To setup the nuklear window we need the windows title */ char title[1024]; GetWindowTextA(wnd->_internal.window_handle, title, 1024); - // Window flags + /* The nk window flags are beeing create based on the context setup */ nk_flags window_flags = NK_WINDOW_BORDER; if(wnd->has_titlebar) window_flags |= NK_WINDOW_CLOSABLE | NK_WINDOW_TITLE; if(!wnd->_internal.is_maximized && wnd->allow_sizing) window_flags |= NK_WINDOW_SCALABLE; - // Window body + /* Override the nuklear windows size when required */ if (wnd->_internal.ws_override) nk_window_set_bounds(wnd->_internal.nk_ctx, title, nk_rect(0, 0, wnd->_internal.width, wnd->_internal.height)); + + /* Start the nuklear window */ if (nk_begin(wnd->_internal.nk_ctx, title, nk_rect(0, 0, wnd->_internal.width, wnd->_internal.height), window_flags)) { + /* Call user drawing callback */ if(wnd->cb_on_draw && !wnd->cb_on_draw(wnd->_internal.nk_ctx)) wnd->_internal.is_open = 0; - // Update window size + /* Update the windows window to reflect the nuklear windows size */ struct nk_rect bounds = nk_window_get_bounds(wnd->_internal.nk_ctx); if(bounds.w != wnd->_internal.width || bounds.h != wnd->_internal.height) SetWindowPos(wnd->_internal.window_handle, NULL, 0, 0, bounds.w, bounds.h, SWP_NOMOVE | SWP_NOOWNERZORDER); } else { - // Handle window closing + /* Nuklear window was closed. Handle close internally */ if(!wnd->cb_on_close || wnd->cb_on_close()) wnd->_internal.is_open = 0; } nk_end(wnd->_internal.nk_ctx); + + /* We no longer need the window size override flag to be set */ wnd->_internal.ws_override = 0; - // Final render pass - nk_gdi_render(wnd->_internal.nk_gdi_ctx, nk_rgb(30, 30, 30)); + /* Pass context to the nuklear gdi renderer */ + nk_gdi_render(wnd->_internal.nk_gdi_ctx, nk_rgb(0, 0, 0)); } return wnd->_internal.is_open; @@ -219,42 +234,44 @@ int nkgdi_window_update(struct nkgdi_window* wnd) LRESULT nkgdi_window_proc_setup(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { - // Wait for setup message + /* Waiting to receive the NCCREATE message with the custom window data */ if (msg == WM_NCCREATE) { - // Get creation parameters & window pointer + /* Extracting the window context from message parameters */ CREATESTRUCT* ptrCr = (CREATESTRUCT*)lParam; struct nkgdi_window* nkgdi_wnd = (struct nkgdi_window*)ptrCr->lpCreateParams; - // Store pointer and new proc in window + /* Store the context in the window and redirect any further message to the run window proc*/ SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)nkgdi_wnd); SetWindowLongPtr(wnd, GWLP_WNDPROC, (LONG_PTR)&nkgdi_window_proc_run); - // Handled by window + /* Already call the run proc so that it gets the chance to handle NCCREATE as well */ return nkgdi_window_proc_run(wnd, msg, wParam, lParam); } - // Default handler + /* Until we get WM_NCCREATE windows is going to handle the messages */ return DefWindowProc(wnd, msg, wParam, lParam); } LRESULT nkgdi_window_proc_run(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { - // Get window pointer + /* The window context is extracted from the window data */ struct nkgdi_window* nkwnd = (struct nkgdi_window*)GetWindowLongPtrW(wnd, GWLP_USERDATA); - // Switch on supplied message code + /* Switch on the message code to handle all required messages */ switch (msg) { - // Close event + /* Window close event */ case WM_CLOSE: + /* Call custom close callback */ if(!nkwnd->cb_on_close || nkwnd->cb_on_close()) nkwnd->_internal.is_open = 0; - return 0; // Will always be handled internaly + return 0; /* No default behaviour. We do it our own way */ - // While sizing + /* Window sizing event (is currently beeing sized) */ case WM_SIZING: { + /* Size of the client / active are is extracted and stored */ RECT cr; GetClientRect(wnd, &cr); nkwnd->_internal.width = cr.right - cr.left; @@ -262,30 +279,33 @@ LRESULT nkgdi_window_proc_run(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) } break; - // When sized + /* Window size event (done sizing, maximize, minimize, ...) */ case WM_SIZE: { - // Adjust maximize properties + /* Window was maximized */ if (wParam == SIZE_MAXIMIZED) { + /* Get the nearest monitor and retrive its details */ HMONITOR monitor = MonitorFromWindow(wnd, MONITOR_DEFAULTTOPRIMARY); MONITORINFO monitorInfo; monitorInfo.cbSize = sizeof(MONITORINFO); if (GetMonitorInfoW(monitor, &monitorInfo)) { + /* Adjust the window size and position by the monitor working area (without taskbar) */ nkwnd->_internal.height = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; nkwnd->_internal.width = monitorInfo.rcWork.right - monitorInfo.rcWork.left; - nkwnd->_internal.ws_override = 1; + nkwnd->_internal.ws_override = 1; /* Sizing was done without nuklear beeing aware. So we need to override it */ nkwnd->_internal.is_maximized = 1; SetWindowPos(wnd, NULL, 0, 0, nkwnd->_internal.width, nkwnd->_internal.height, SWP_NOMOVE | SWP_NOZORDER); } } + /* Window was restored (no longer maximized) */ else if (wParam == SIZE_RESTORED) { nkwnd->_internal.is_maximized = 0; } - // Compute new bounds + /* Always get the new bounds of the window */ RECT cr; GetClientRect(wnd, &cr); nkwnd->_internal.width = cr.right - cr.left; @@ -293,12 +313,13 @@ LRESULT nkgdi_window_proc_run(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) } break; - // When mouse start l-press (drag window) + /* Mouse started left click */ case WM_LBUTTONDOWN: { - if (HIWORD(lParam) <= 30 && nkwnd->allow_move) + /* Handle dragging when allowed, has titlebar and mouse is in titlebar (Y <= 30) */ + if (HIWORD(lParam) <= 30 && nkwnd->allow_move && nkwnd->has_titlebar) { - // Start dragging + /* Mark dragging internally and store mouse click offset */ nkwnd->_internal.is_draggin = 1; nkwnd->_internal.drag_offset.x = LOWORD(lParam); nkwnd->_internal.drag_offset.y = HIWORD(lParam); @@ -306,52 +327,59 @@ LRESULT nkgdi_window_proc_run(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) } break; - // When mouse stops l-press (drag window) + /* Mouse stoped left click */ case WM_LBUTTONUP: + /* No longer dragging the window */ nkwnd->_internal.is_draggin = 0; break; - // Mouse movement (dragging) + /* Mouse is moving on the window */ case WM_MOUSEMOVE: { + /* When we are dragging and are not maximized process dragging */ if (nkwnd->_internal.is_draggin && !nkwnd->_internal.is_maximized) { - // Get mouse postion and substract offset + /* Get the current global position of the mouse */ POINT cursorPos; GetCursorPos(&cursorPos); + /* Substract the internal offset */ cursorPos.x -= nkwnd->_internal.drag_offset.x; cursorPos.y -= nkwnd->_internal.drag_offset.y; - // Use as new position + /* Update position of out window and make sure window is in a movable state (= Restored) */ ShowWindow(wnd, SW_RESTORE); SetWindowPos(wnd, NULL, cursorPos.x, cursorPos.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); } } break; - // On mouse doubble click (maximize) + /* Mouse double clicked */ case WM_LBUTTONDBLCLK: { - if (HIWORD(lParam) <= 30 && nkwnd->allow_maximize) + /* Will only affect window when on the titlebar */ + if (HIWORD(lParam) <= 30 && nkwnd->allow_maximize && nkwnd->has_titlebar) { + /* When the window is already maximized restore it */ if (nkwnd->_internal.is_maximized) { ShowWindow(wnd, SW_RESTORE); } + /* Else we gonna do maximize it*/ else { ShowWindow(wnd, SW_MAXIMIZE); } + /* We overrideed the window size, make sure to affect the nk window as well */ nkwnd->_internal.ws_override = 1; } } break; } - // Send to nuklear + /* Allow nuklear to handle the message as well */ if (nkwnd->_internal.nk_gdi_ctx && nk_gdi_handle_event(nkwnd->_internal.nk_gdi_ctx, wnd, msg, wParam, lParam)) return 0; - // In case this is ever reached: Run default behaviour + /* In case we reach this line our code and nuklear did not respond to the message. Allow windows to handle it s*/ return DefWindowProc(wnd, msg, wParam, lParam); } From 50277a1c39e3077aaf42bf814f5d5363a7dbc1a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludwig=20F=C3=BCchsl?= Date: Sun, 29 May 2022 01:47:00 +0200 Subject: [PATCH 5/6] Changed windows include to lowercase --- demo/gdi_native_nuklear/main.c | 2 +- demo/gdi_native_nuklear/window.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/gdi_native_nuklear/main.c b/demo/gdi_native_nuklear/main.c index b6cb6a9..349adea 100644 --- a/demo/gdi_native_nuklear/main.c +++ b/demo/gdi_native_nuklear/main.c @@ -1,4 +1,4 @@ -#include +#include #pragma comment(linker,"\"/manifestdependency:type='win32' \ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ diff --git a/demo/gdi_native_nuklear/window.h b/demo/gdi_native_nuklear/window.h index 0db572f..f78d574 100644 --- a/demo/gdi_native_nuklear/window.h +++ b/demo/gdi_native_nuklear/window.h @@ -3,7 +3,7 @@ #define NK_GDI_WINDOW_CLS L"WNDCLS_NkGdi" -#include +#include /* Functin pointer types for window callbacks */ typedef int(*nkgdi_window_func_close)(void); From adeb2a720fd4836b5e69e075e277866f3c3e80c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludwig=20F=C3=BCchsl?= Date: Mon, 30 May 2022 22:49:12 +0200 Subject: [PATCH 6/6] Added wWinMain comment --- demo/gdi_native_nuklear/main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/demo/gdi_native_nuklear/main.c b/demo/gdi_native_nuklear/main.c index 349adea..d12f972 100644 --- a/demo/gdi_native_nuklear/main.c +++ b/demo/gdi_native_nuklear/main.c @@ -84,17 +84,19 @@ int drawCallback(struct nk_context* ctx) return 1; } -/* Main entry point (Windows wchar_t) */ +/* Main entry point - wWinMain used for UNICODE + * (You can also use _tWinMain(...) to automaticaly use the ASCII or WIDE char entry point base on your build) + */ INT WINAPI wWinMain(HINSTANCE _In_ hInstance, HINSTANCE _In_opt_ hPrevInstance, PWSTR _In_ cmdArgs, INT _In_ cmdShow) { /* Call this first to setup all required prerequisites */ nkgdi_window_init(); - + /* Preparing two window contexts */ struct nkgdi_window w1, w2; memset(&w1, 0x0, sizeof(struct nkgdi_window)); memset(&w2, 0x0, sizeof(struct nkgdi_window)); - + /* Configure and create window 1. * Note: You can allways change the direct accesible parameters later as well! */