From 1d7c530c32a7444ea44e75bfb1c5b0b65859d98f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 30 Jul 2022 04:14:16 -0400 Subject: [PATCH] More implementation of the new uiWindows control parenting and sizing system. --- common/programmererrors.h | 4 ++++ doc/windows-controls.md | 8 +++---- test/controls_windows.c | 44 +++++++++++++++++++++++++++++++++++++++ test/window_windows.c | 19 +++++++++++++++++ ui_windows.h | 2 +- windows/window.cpp | 28 ++++++++++++++++++++++++- windows/winhresult.cpp | 18 ++++++++++++++++ windows/winhresult.hpp | 2 ++ 8 files changed, 119 insertions(+), 6 deletions(-) diff --git a/common/programmererrors.h b/common/programmererrors.h index 4c341706..6c86a507 100644 --- a/common/programmererrors.h +++ b/common/programmererrors.h @@ -86,4 +86,8 @@ #define uiprivProgrammerErrorCannotHaveWindowsAsChildren() \ uiprivProgrammerError("cannot set a uiWindow as the child of another uiControl") +// for Windows only +#define uiprivProgrammerErrorCannotCallSetControlPosOnWindow() \ + uiprivProgrammerError("cannot call uiWindowsControlSetControlPos() on a uiWindow") + // } diff --git a/doc/windows-controls.md b/doc/windows-controls.md index 82bf4861..d1429714 100644 --- a/doc/windows-controls.md +++ b/doc/windows-controls.md @@ -79,19 +79,19 @@ If your parent control is not a container, return `NULL`. TODO programmer error? As libui ensures that the arguments to `ParentHandleForChild()` are actually related, you do not need to check that `child` is actually your child yourself. -### `uiWindowsSetControlPos()` +### `uiWindowsControlSetControlPos()` ```c -uiprivExtern HRESULT uiWindowsSetControlPos(uiControl *c, const RECT *r); +uiprivExtern HRESULT uiWindowsControlSetControlPos(uiControl *c, const RECT *r); ``` -`uiWindowsSetControlPos()` causes `c` to be moved and resized to fill `r`. `r` must be in the *client* coordinates of `c`'s parent handle. +`uiWindowsControlSetControlPos()` causes `c` to be moved and resized to fill `r`. `r` must be in the *client* coordinates of `c`'s parent handle. This function should be called by container implementations to reposition its children, either in response to a window being resized or when children need to be laid out due to some change (such as visibility). Users should not call this function directly. It returns `S_OK` if the resize succeeded or some error if the resize failed *from the perspective of the OS*. It will not return an error in the event of a libui-specific programmer or internal error of some other sort. This error return is only intended for libui-internal use; see the control implementation details below. -It is a programmer error to pass `NULL` for `c` or `r`. +It is a programmer error to pass `NULL` for `c` or `r`. It is also a programmer error to call `uiWindowsControlSetControlPos()` on a `uiWindow`. **For control implementations**: This function calls your `SetControlPos()` method. For a simple control with a single window handle, the method should do nothing but call `uiWindowsSetControlHandlePos()` and return its return value: diff --git a/test/controls_windows.c b/test/controls_windows.c index 73170300..b4b5e214 100644 --- a/test/controls_windows.c +++ b/test/controls_windows.c @@ -11,10 +11,16 @@ static HWND osVtableNopParentHandleForChild(uiControl *c, void *implData, uiCont return NULL; } +HRESULT osVtableNopSetControlPos(uiControl *c, void *implData, const RECT *r) +{ + return S_OK; +} + static const uiControlOSVtable osVtable = { .Size = sizeof (uiControlOSVtable), .Handle = osVtableNopHandle, .ParentHandleForChild = osVtableNopParentHandleForChild, + .SetControlPos = osVtableNopSetControlPos, }; const uiControlOSVtable *testOSVtable(void) @@ -64,6 +70,20 @@ Test(ControlOSVtableWithMissingParentHandleForChildMethodIsProgrammerError) endCheckProgrammerError(ctx); } +Test(ControlOSVtableWithMissingSetControlPosMethodIsProgrammerError) +{ + uiControlVtable vtable; + uiControlOSVtable osvt; + void *ctx; + + testControlLoadNopVtable(&vtable); + ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlOSVtable method SetControlPos() missing for uiControl type name"); + osvt = osVtable; + osvt.SetControlPos = NULL; + uiRegisterControlType("name", &vtable, &osvt, 0); + endCheckProgrammerError(ctx); +} + Test(GettingWindowsHandleOfNullControlIsProgrammerError) { void *ctx; @@ -81,3 +101,27 @@ Test(GettingWindowsParentHandleOfNullControlIsProgrammerError) uiWindowsControlParentHandle(NULL); endCheckProgrammerError(ctx); } + +Test(SettingWindowsControlPosOfNullControlIsProgrammerError) +{ + void *ctx; + + ctx = beginCheckProgrammerError("uiWindowsControlSetControlPos(): invalid null pointer for uiControl"); + uiWindowsControlSetControlPos(NULL, NULL); + endCheckProgrammerError(ctx); +} + +Test(SettingWindowsControlPosOfNullControlIsProgrammerError) +{ +#if 0 + // TODO + uiControl *c; + void *ctx; + + ctx = beginCheckProgrammerError("uiWindowsControlSetControlPos(): invalid null pointer for RECT"); + uiWindowsControlSetControlPos(c, NULL); + endCheckProgrammerError(ctx); +#endif +} + +// TODO uiWindowsSetControlHandlePos errors diff --git a/test/window_windows.c b/test/window_windows.c index 0b4f1435..8e9aae2f 100644 --- a/test/window_windows.c +++ b/test/window_windows.c @@ -78,3 +78,22 @@ Test(SetWindowTitle_OSLevel_Invalid) { testSetWindowTitleImpl(testUTF8InvalidInput, testUTF16InvalidOutput); } + +Test(WindowsCannotSetWindowControlPos) +{ + uiWindow *w; + RECT r; + void *ctx; + + w = uiNewWindow(); + + ctx = beginCheckProgrammerError("cannot set a uiWindow as the child of another uiControl"); + r.left = 0; + r.top = 0; + r.right = 640; + r.bottom = 480; + uiWindowsControlSetControlPos(uiControl(w), &r); + endCheckProgrammerError(ctx); + + uiControlFree(uiControl(w)); +} diff --git a/ui_windows.h b/ui_windows.h index b42a3c02..65ca0660 100644 --- a/ui_windows.h +++ b/ui_windows.h @@ -20,7 +20,7 @@ struct uiControlOSVtable { uiprivExtern HWND uiWindowsControlHandle(uiControl *c); uiprivExtern HWND uiWindowsControlParentHandle(uiControl *c); -uiprivExtern HRESULT uiWindowsSetControlPos(uiControl *c, const RECT *r); +uiprivExtern HRESULT uiWindowsControlSetControlPos(uiControl *c, const RECT *r); uiprivExtern HRESULT uiWindowsSetControlHandlePos(HWND hwnd, const RECT *r); diff --git a/windows/window.cpp b/windows/window.cpp index 9539bc5a..90b21e3a 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -439,6 +439,23 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) // } TODO #endif +static void windowRelayout(struct windowImplData *wi) +{ + RECT r; + HRESULT hr; + + if (wi->child == NULL) + return; + hr = uiprivHrGetClientRect(wi->hwnd, &r); + if (hr != S_OK) { + uiprivInternalError("GetClientRect() failed in windowRelayout(): 0x%08I32X", hr); + return; + } + hr = uiWindowsControlSetControlPos(wi->child, &r); + if (hr != S_OK) + uiprivInternalError("uiWindowsSetControlHandlePos() failed in windowRelayout(): 0x%08I32X", hr); +} + static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LONG_PTR ww; @@ -543,6 +560,12 @@ static HWND windowParentHandleForChild(uiControl *c, void *implData, uiControl * return wi->hwnd; } +static HRESULT windowSetControlPos(uiControl *c, void *implData, const RECT *r) +{ + uiprivProgrammerErrorCannotCallSetControlPosOnWindow(); + return E_FAIL; +} + // gotta do this because of lack of C99-style initializers in C++11 // see also https://stackoverflow.com/questions/11516657/c-structure-initialization static const uiControlVtable windowVtable = [](void) { @@ -564,6 +587,7 @@ static const uiControlOSVtable windowOSVtable = [](void) { vt.Size = sizeof (uiControlOSVtable); vt.Handle = windowHandle; vt.ParentHandleForChild = windowParentHandleForChild; + vt.SetControlPos = windowSetControlPos; return vt; }(); @@ -622,8 +646,10 @@ void uiprivSysWindowSetChild(uiWindow *w, uiControl *child) if (wi->child != NULL) uiControlSetParent(wi->child, NULL); wi->child = child; - if (wi->child != NULL) + if (wi->child != NULL) { uiControlSetParent(wi->child, uiControl(w)); + windowRelayout(wi); + } } #if 0 diff --git a/windows/winhresult.cpp b/windows/winhresult.cpp index 70e95f4f..322dea57 100644 --- a/windows/winhresult.cpp +++ b/windows/winhresult.cpp @@ -98,3 +98,21 @@ HRESULT WINAPI uiprivHrDestroyWindow(HWND hwnd) return lastErrorToHRESULT(); return S_OK; } + +HRESULT WINAPI uiprivHrGetWindowRect(HWND hwnd, LPRECT r) +{ + SetLastError(0); + if (GetWindowRect(hwnd, r) == 0) + // TODO set r to a zero rect? + return lastErrorToHRESULT(); + return S_OK; +} + +HRESULT WINAPI uiprivHrGetClientRect(HWND hwnd, LPRECT r) +{ + SetLastError(0); + if (GetClientRect(hwnd, r) == 0) + // TODO set r to a zero rect? + return lastErrorToHRESULT(); + return S_OK; +} diff --git a/windows/winhresult.hpp b/windows/winhresult.hpp index 2511a960..81f1ce9f 100644 --- a/windows/winhresult.hpp +++ b/windows/winhresult.hpp @@ -9,3 +9,5 @@ extern HRESULT WINAPI uiprivHrLoadIconW(HINSTANCE hInstance, LPCWSTR name, HICON extern HRESULT WINAPI uiprivHrLoadCursorW(HINSTANCE hInstance, LPCWSTR name, HCURSOR *hCursor); extern HRESULT WINAPI uiprivHrSetWindowTextW(HWND hwnd, LPCWSTR text); extern HRESULT WINAPI uiprivHrDestroyWindow(HWND hwnd); +extern HRESULT WINAPI uiprivHrGetWindowRect(HWND hwnd, LPRECT r); +extern HRESULT WINAPI uiprivHrGetClientRect(HWND hwnd, LPRECT r);