Merge pull request #414 from Ohjurot/gdi_nuklear_native_window
Windows (GDI): Native-like nuklear windows
This commit is contained in:
commit
07bd7a0925
6
demo/gdi_native_nuklear/build.bat
Normal file
6
demo/gdi_native_nuklear/build.bat
Normal file
@ -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.c user32.lib gdi32.lib Msimg32.lib /link /incremental:no
|
128
demo/gdi_native_nuklear/main.c
Normal file
128
demo/gdi_native_nuklear/main.c
Normal file
@ -0,0 +1,128 @@
|
||||
#include <windows.h>
|
||||
|
||||
#pragma comment(linker,"\"/manifestdependency:type='win32' \
|
||||
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
|
||||
#define NK_INCLUDE_DEFAULT_ALLOCATOR
|
||||
#define NK_IMPLEMENTATION
|
||||
#define NK_GDI_IMPLEMENTATION
|
||||
#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)
|
||||
{
|
||||
/* 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 - 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!
|
||||
*/
|
||||
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);
|
||||
|
||||
/* Configure and create window 2 */
|
||||
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);
|
||||
|
||||
/* 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;
|
||||
}
|
937
demo/gdi_native_nuklear/nuklear_gdi.h
Normal file
937
demo/gdi_native_nuklear/nuklear_gdi.h
Normal file
@ -0,0 +1,937 @@
|
||||
/*
|
||||
* 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 :-)
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* ==============================================================
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
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
|
387
demo/gdi_native_nuklear/window.h
Normal file
387
demo/gdi_native_nuklear/window.h
Normal file
@ -0,0 +1,387 @@
|
||||
#ifndef NK_GDI_WINDOW
|
||||
#define NK_GDI_WINDOW
|
||||
|
||||
#define NK_GDI_WINDOW_CLS L"WNDCLS_NkGdi"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
/* Functin pointer types for window callbacks */
|
||||
typedef int(*nkgdi_window_func_close)(void);
|
||||
typedef int(*nkgdi_window_func_draw)(struct nk_context*);
|
||||
|
||||
/* Window container / context */
|
||||
struct nkgdi_window
|
||||
{
|
||||
/* 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 */
|
||||
/* 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 */
|
||||
HWND window_handle;
|
||||
|
||||
/* Nuklear & GDI context */
|
||||
nk_gdi_ctx nk_gdi_ctx;
|
||||
struct nk_context* nk_ctx;
|
||||
|
||||
/* GDI required objects */
|
||||
GdiFont* gdi_font;
|
||||
HDC window_dc;
|
||||
|
||||
/* Internally used state variables */
|
||||
int is_open;
|
||||
int is_draggin;
|
||||
int ws_override;
|
||||
int is_maximized;
|
||||
POINT drag_offset;
|
||||
int width;
|
||||
int height;
|
||||
}_internal;
|
||||
};
|
||||
|
||||
/* 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 the window class */
|
||||
WNDCLASSEXW cls;
|
||||
cls.cbSize = sizeof(WNDCLASSEXW);
|
||||
cls.style = CS_OWNDC | CS_DBLCLKS;
|
||||
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 = NULL;
|
||||
cls.lpszClassName = NK_GDI_WINDOW_CLS;
|
||||
cls.hIconSm = NULL;
|
||||
|
||||
/* 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));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* Adjust window size to fit selected window styles */
|
||||
RECT cr;
|
||||
cr.left = 0;
|
||||
cr.top = 0;
|
||||
cr.right = width;
|
||||
cr.bottom = height;
|
||||
AdjustWindowRectEx(&cr, style, FALSE, styleEx);
|
||||
|
||||
/* Create the new window */
|
||||
wnd->_internal.window_handle = CreateWindowExW(
|
||||
styleEx,
|
||||
NK_GDI_WINDOW_CLS,
|
||||
L"NkGdi",
|
||||
style | WS_VISIBLE,
|
||||
posX, posY,
|
||||
cr.right - cr.left, cr.bottom - cr.top,
|
||||
NULL, NULL,
|
||||
GetModuleHandleW(NULL),
|
||||
wnd
|
||||
);
|
||||
|
||||
/* Give the window the ascii char name */
|
||||
SetWindowTextA(wnd->_internal.window_handle, name);
|
||||
|
||||
/* Extract the window dc for gdi drawing */
|
||||
wnd->_internal.window_dc = GetWindowDC(wnd->_internal.window_handle);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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;
|
||||
wnd->_internal.is_maximized = 0;
|
||||
wnd->_internal.drag_offset.x = 0;
|
||||
wnd->_internal.drag_offset.y = 0;
|
||||
wnd->_internal.width = width;
|
||||
wnd->_internal.height = height;
|
||||
}
|
||||
|
||||
void nkgdi_window_destroy(struct nkgdi_window* wnd)
|
||||
{
|
||||
/* Destroy all objects in reverse order */
|
||||
if (wnd->_internal.nk_gdi_ctx)
|
||||
{
|
||||
nk_gdi_shutdown(wnd->_internal.nk_gdi_ctx);
|
||||
}
|
||||
if (wnd->_internal.gdi_font)
|
||||
{
|
||||
nk_gdifont_del(wnd->_internal.gdi_font);
|
||||
}
|
||||
if (wnd->_internal.window_dc)
|
||||
{
|
||||
ReleaseDC(wnd->_internal.window_handle, wnd->_internal.window_dc);
|
||||
}
|
||||
if (wnd->_internal.window_handle)
|
||||
{
|
||||
CloseWindow(wnd->_internal.window_handle);
|
||||
DestroyWindow(wnd->_internal.window_handle);
|
||||
}
|
||||
}
|
||||
|
||||
int nkgdi_window_update(struct nkgdi_window* wnd)
|
||||
{
|
||||
/* The window will only be updated when it is open / valid */
|
||||
if (wnd->_internal.is_open)
|
||||
{
|
||||
/* 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))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
nk_input_end(wnd->_internal.nk_ctx);
|
||||
|
||||
/* To setup the nuklear window we need the windows title */
|
||||
char title[1024];
|
||||
GetWindowTextA(wnd->_internal.window_handle, title, 1024);
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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 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
|
||||
{
|
||||
/* 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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
LRESULT nkgdi_window_proc_setup(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
/* Waiting to receive the NCCREATE message with the custom window data */
|
||||
if (msg == WM_NCCREATE)
|
||||
{
|
||||
/* Extracting the window context from message parameters */
|
||||
CREATESTRUCT* ptrCr = (CREATESTRUCT*)lParam;
|
||||
struct nkgdi_window* nkgdi_wnd = (struct nkgdi_window*)ptrCr->lpCreateParams;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
/* The window context is extracted from the window data */
|
||||
struct nkgdi_window* nkwnd = (struct nkgdi_window*)GetWindowLongPtrW(wnd, GWLP_USERDATA);
|
||||
|
||||
/* Switch on the message code to handle all required messages */
|
||||
switch (msg)
|
||||
{
|
||||
/* 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; /* No default behaviour. We do it our own way */
|
||||
|
||||
/* 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;
|
||||
nkwnd->_internal.height = cr.bottom - cr.top;
|
||||
}
|
||||
break;
|
||||
|
||||
/* Window size event (done sizing, maximize, minimize, ...) */
|
||||
case WM_SIZE:
|
||||
{
|
||||
/* 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; /* 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;
|
||||
}
|
||||
|
||||
/* Always get the new bounds of the window */
|
||||
RECT cr;
|
||||
GetClientRect(wnd, &cr);
|
||||
nkwnd->_internal.width = cr.right - cr.left;
|
||||
nkwnd->_internal.height = cr.bottom - cr.top;
|
||||
}
|
||||
break;
|
||||
|
||||
/* Mouse started left click */
|
||||
case WM_LBUTTONDOWN:
|
||||
{
|
||||
/* Handle dragging when allowed, has titlebar and mouse is in titlebar (Y <= 30) */
|
||||
if (HIWORD(lParam) <= 30 && nkwnd->allow_move && nkwnd->has_titlebar)
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* Mouse stoped left click */
|
||||
case WM_LBUTTONUP:
|
||||
/* No longer dragging the window */
|
||||
nkwnd->_internal.is_draggin = 0;
|
||||
break;
|
||||
|
||||
/* 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 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;
|
||||
/* 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;
|
||||
|
||||
/* Mouse double clicked */
|
||||
case WM_LBUTTONDBLCLK:
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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 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);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user