Bug fix & Added comments

This commit is contained in:
Ludwig Füchsl 2022-03-04 13:16:47 +01:00
parent 33395aabdb
commit a31c6c0089
3 changed files with 164 additions and 59 deletions

View File

@ -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;
}

View File

@ -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 :-)
*
*/
/*
* ==============================================================

View File

@ -6,40 +6,45 @@
#include <Windows.h>
/* 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);
}