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; }();