From f3820ac4b0ed67fd55a6ea66d5b76edf2b7fca4a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 27 Jul 2022 21:43:15 -0400 Subject: [PATCH] Okay new plan: we're going to start with the Windows implementation instead, since that's the one that's going to have to be more complicated. I'll then decide if we should copy this strategy on the other platforms or not. --- doc/windows-controls.md | 35 +++++++++++++++++++++++++++++++++++ test/allcalls.h | 2 ++ test/allcalls_windows.h | 1 + test/controls_windows.c | 29 +++++++++++++++++++++++++++++ ui_windows.h | 2 ++ windows/controls.cpp | 19 +++++++++++++++++++ windows/window.cpp | 9 +++++++++ 7 files changed, 97 insertions(+) diff --git a/doc/windows-controls.md b/doc/windows-controls.md index a6cc7f64..7e28131c 100644 --- a/doc/windows-controls.md +++ b/doc/windows-controls.md @@ -15,6 +15,8 @@ typedef struct uiControlOSVtable uiControlOSVtable; struct uiControlOSVtable { size_t Size; HWND (*Handle)(uiControl *c, void *implData); + HWND (*ParentHandleForChild)(uiControl *c, void *implData, uiControl *child); +}; }; ``` @@ -43,3 +45,36 @@ For all other `uiControl`s defined by libui, the returned window is of the appro * TODO It is a programmer error to pass `NULL` for `c`. TODO a non-`uiControl`? + +**For control implementations**: This function does the above programmer error checks and then calls your `Handle()` method. You do not need to repeat the check yourself. + +### `uiWindowsControlParentHandle()` + +```c +uiprivExtern HWND uiWindowsControlParentHandle(uiControl *c); +``` + +`uiWindowsControlParentHandle()` returns the parent handle for `c`, or `NULL` if there currently is no such handle (either because `c` has no parent control or because none of its parent controls have a handle). + +This is the parent from the point of view of the Windows API. When creating the window handle for a uiControl, this is the handle to use as the `hwndParent`. + +The value returned by this function TODO should not be stored TODO refer to the top of this page for the control model + +It is a programmer error to pass `NULL` for `c`. TODO a non-`uiControl`? + +**For control implementations**: This function does the above programmer error checks; you do not need to repeat the checks yourself. + +Unlike the other functions that operate on a `uiControl`, this function actually calls the `ParentHandleForChild()` method of the *parent control* of `c`, passing `c` as the `child` argument. If your parent control is a container that has window handles to use as the parent handles of its children, you should return the approprpiate window handle. (As an example of a case where knowing the child is important, `uiTab` has a separate parent handle for each of its tab pages.) + +If your parent control is a container that does not have window handles of its own, you should call `uiWindowsControlParentHandle()` on the parent control itself, which will cause libui to chain up until it has reached the top level: + +```c +static HWND controlParentHandleForChild(uiControl *c, void *implData, uiControl *child) +{ + return uiWindowsControlParentHandle(c); +} +``` + +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. diff --git a/test/allcalls.h b/test/allcalls.h index c249084c..218040e6 100644 --- a/test/allcalls.h +++ b/test/allcalls.h @@ -21,3 +21,5 @@ allcallsCase(uiWindowType, /* no arguments */) allcallsCase(uiNewWindow, /* no arguments */) allcallsCase(uiWindowTitle, NULL) allcallsCase(uiWindowSetTitle, NULL, NULL) +allcallsCase(uiWindowChild, NULL) +allcallsCase(uiWindowSetChild, NULL, NULL) diff --git a/test/allcalls_windows.h b/test/allcalls_windows.h index 7b05913d..b35d4ad9 100644 --- a/test/allcalls_windows.h +++ b/test/allcalls_windows.h @@ -3,3 +3,4 @@ // This file should NOT have include guards as it is intended to be included more than once; see noinitwrongthread_windows.c for details. allcallsCase(uiWindowsControlHandle, NULL) +allcallsCase(uiWindowsControlParentHandle, NULL) diff --git a/test/controls_windows.c b/test/controls_windows.c index 845589d7..73170300 100644 --- a/test/controls_windows.c +++ b/test/controls_windows.c @@ -6,9 +6,15 @@ static HWND osVtableNopHandle(uiControl *c, void *implData) return NULL; } +static HWND osVtableNopParentHandleForChild(uiControl *c, void *implData, uiControl *child) +{ + return NULL; +} + static const uiControlOSVtable osVtable = { .Size = sizeof (uiControlOSVtable), .Handle = osVtableNopHandle, + .ParentHandleForChild = osVtableNopParentHandleForChild, }; const uiControlOSVtable *testOSVtable(void) @@ -44,6 +50,20 @@ Test(ControlOSVtableWithMissingHandleMethodIsProgrammerError) endCheckProgrammerError(ctx); } +Test(ControlOSVtableWithMissingParentHandleForChildMethodIsProgrammerError) +{ + uiControlVtable vtable; + uiControlOSVtable osvt; + void *ctx; + + testControlLoadNopVtable(&vtable); + ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlOSVtable method ParentHandleForChild() missing for uiControl type name"); + osvt = osVtable; + osvt.ParentHandleForChild = NULL; + uiRegisterControlType("name", &vtable, &osvt, 0); + endCheckProgrammerError(ctx); +} + Test(GettingWindowsHandleOfNullControlIsProgrammerError) { void *ctx; @@ -52,3 +72,12 @@ Test(GettingWindowsHandleOfNullControlIsProgrammerError) uiWindowsControlHandle(NULL); endCheckProgrammerError(ctx); } + +Test(GettingWindowsParentHandleOfNullControlIsProgrammerError) +{ + void *ctx; + + ctx = beginCheckProgrammerError("uiWindowsControlHandle(): invalid null pointer for uiControl"); + uiWindowsControlParentHandle(NULL); + endCheckProgrammerError(ctx); +} diff --git a/ui_windows.h b/ui_windows.h index 31235c8b..3348d24e 100644 --- a/ui_windows.h +++ b/ui_windows.h @@ -14,9 +14,11 @@ extern "C" { struct uiControlOSVtable { size_t Size; HWND (*Handle)(uiControl *c, void *implData); + HWND (*ParentHandleForChild)(uiControl *c, void *implData, uiControl *child); }; uiprivExtern HWND uiWindowsControlHandle(uiControl *c); +uiprivExtern HWND uiWindowsControlParentHandle(uiControl *c); #ifdef __cplusplus } diff --git a/windows/controls.cpp b/windows/controls.cpp index 57688c1b..ec99b2a5 100644 --- a/windows/controls.cpp +++ b/windows/controls.cpp @@ -13,6 +13,7 @@ bool uiprivOSVtableValid(const char *name, const uiControlOSVtable *osVtable, co return 0; \ } checkMethod(Handle) + checkMethod(ParentHandleForChild) return true; } @@ -40,3 +41,21 @@ HWND uiWindowsControlHandle(uiControl *c) osVtable = uiprivControlOSVtable(c); return callVtable(osVtable->Handle, c, uiControlImplData(c)); } + +HWND uiWindowsControlParentHandle(uiControl *c) +{ + uiControl *parent; + uiControlOSVtable *parentVtable; + + if (!uiprivCheckInitializedAndThread()) + return NULL; + if (c == NULL) { + uiprivProgrammerErrorNullPointer("uiControl", uiprivFunc); + return NULL; + } + parent = uiControlParent(c); + if (parent == NULL) + return NULL; + parentVtable = uiprivControlOSVtable(parent); + return callVtable(parentVtable->ParentHandleForChild, parent, uiControlImplData(parent), c); +} diff --git a/windows/window.cpp b/windows/window.cpp index e4e36ec3..c20aa565 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -537,6 +537,14 @@ static HWND windowHandle(uiControl *c, void *implData) return wi->hwnd; } +static HWND windowParentHandleForChild(uiControl *c, void *implData, uiControl *child) +{ + struct windowImplData *wi = (struct windowImplData *) implData; + + // In this case, we have a fixed handle for the entire lifetime of the uiWindow that should be used as the parent. + return wi->hwnd; +} + // 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) { @@ -557,6 +565,7 @@ static const uiControlOSVtable windowOSVtable = [](void) { memset(&vt, 0, sizeof (uiControlOSVtable)); vt.Size = sizeof (uiControlOSVtable); vt.Handle = windowHandle; + vt.ParentHandleForChild = windowParentHandleForChild; return vt; }();