From 7cdd6ee38c1e3bb82489f0faac211e9899327b91 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 7 Jun 2020 14:10:16 -0400 Subject: [PATCH] Finished all the necessary bits to implement what little of uiWindow we have now on Windows. Now to fill in the tests. --- doc/controls.md | 1 + doc/haiku-controls.md | 4 ++-- doc/windows-controls.md | 21 ++++++++++++++++++++- test/test_windows.h | 2 ++ ui_windows.h | 3 +++ windows/controls.cpp | 24 +++++++++++++++++++++++- windows/utf16.cpp | 2 ++ windows/window.cpp | 28 ++++++++++------------------ windows/winhresult.cpp | 8 ++++++++ windows/winhresult.hpp | 1 + 10 files changed, 72 insertions(+), 22 deletions(-) diff --git a/doc/controls.md b/doc/controls.md index 493580e4..bf79fad1 100644 --- a/doc/controls.md +++ b/doc/controls.md @@ -86,6 +86,7 @@ This function is meant for control implementations to use in the implementation It is a programmer error to pass an invalid value for either `type` or `initData`. **For control implementations**: This function allocates both the `uiControl` and the memory for the implementation data, and then passes both of these allocations as well as the value of `initData` into your `Init()` method. Before calling `Init()`, libui will clear the `implData` memory, as with `memset(0)`. Return `false` from the `Init()` method if `initData` is invalid; if it is valid, initialize the control and return `true`. To discourage direct use of `uiNewControl()`, you should generally not allow `initData` to be `NULL`, even if there are no parameters. Do **not** return `false` for any other reason, including other forms of initialization failures; see [Error handling](error-handling.md) for details on what to do instead. +TODO is this whole spiel about the return value even necessary? shouldn't the outer library be responsible for handling errors instead? ### `uiControlFree()` diff --git a/doc/haiku-controls.md b/doc/haiku-controls.md index 24992b40..967f1e95 100644 --- a/doc/haiku-controls.md +++ b/doc/haiku-controls.md @@ -28,13 +28,13 @@ Each method is named for the `uiHaikuControl` function that it implements. As su ### `uiHaikuControlHandle()` -```objective-c +```c uiprivExtern void *uiHaikuControlHandle(uiControl *c); ``` `uiHaikuControlHandle()` returns the Objective-C object that underpins `c`, or `NULL` if `c` does not have any underlying object associated with it when called. -The object returned by `uiDarwinControlHandle()` is owned by `c`; you do not receive a reference to it at all. The object is valid until either `c` is destroyed or until `c` decides to destroy the object; you can handle the latter by catching TODO. In general, you should not store the returned object pointer directly for later use, nor should you attempt to retain the returned object. Instead, use the returned handle immediately if you have to, or follow TODO if you need to adjust properties of the handle that should persist across handle destruction/creation. +The object returned by `uiHaikuControlHandle()` is owned by `c`; you do not receive a reference to it at all. The object is valid until either `c` is destroyed or until `c` decides to destroy the object; you can handle the latter by catching TODO. In general, you should not store the returned object pointer directly for later use. Instead, use the returned handle immediately if you have to, or follow TODO if you need to adjust properties of the handle that should persist across handle destruction/creation. `uiWindow`s have a single handle of type `BWindow` that is created when the `uiWindow` is created and destroyed when the `uiWindow` is destroyed. Despite this, you should still follow the best practices described above. diff --git a/doc/windows-controls.md b/doc/windows-controls.md index 6eef6eab..a6cc7f64 100644 --- a/doc/windows-controls.md +++ b/doc/windows-controls.md @@ -14,6 +14,7 @@ TODO typedef struct uiControlOSVtable uiControlOSVtable; struct uiControlOSVtable { size_t Size; + HWND (*Handle)(uiControl *c, void *implData); }; ``` @@ -23,4 +24,22 @@ You are responsible for allocating and initializing this struct. To do so, you s Each method takes at least two parameters. The first, `c`, is the `uiControl` itself. The second, `implData`, is the implementation data pointer; it is the same as the pointer returned by `uiControlImplData(c)`, and is provided here as a convenience. -Each method is named for the `uiWindowsControl` function that it implements. As such, details on how to implement these methods are documented alongside those functions. For instance, instructions on implementing `TODO()` are given under the documentation for `TODO()`. +Each method is named for the `uiWindowsControl` function that it implements. As such, details on how to implement these methods are documented alongside those functions. For instance, instructions on implementing `Handle()` are given under the documentation for `uiWindowsControlHandle()`. + +### `uiWindowsControlHandle()` + +```c +uiprivExtern HWND uiWindowsControlHandle(uiControl *c); +``` + +`uiWindowsControlHandle()` returns the window handle that underpins `c`, or `NULL` if `c` does not have any underlying window handle associated with it when called. + +The window returned by `uiWindowsControlHandle()` is owned by `c`; you do not receive a reference to it at all. The window is valid until either `c` is destroyed or until `c` decides to destroy the window; you can handle the latter by catching TODO. In general, you should not store the returned window handle directly for later use. Instead, use the returned handle immediately if you have to, or follow TODO if you need to adjust properties of the handle that should persist across handle destruction/creation. + +`uiWindow`s have a single window that is created when the `uiWindow` is created and destroyed when the `uiWindow` is destroyed. Despite this, you should still follow the best practices described above. The window is of a special libui-internal window class. + +For all other `uiControl`s defined by libui, the returned window is of the appropriate window class: + +* TODO + +It is a programmer error to pass `NULL` for `c`. TODO a non-`uiControl`? diff --git a/test/test_windows.h b/test/test_windows.h index 2396dff5..1049ac9e 100644 --- a/test/test_windows.h +++ b/test/test_windows.h @@ -1,3 +1,5 @@ // 10 june 2019 +// TODO proper macros and headers +#include "../windows/winapi.hpp" #define libuiOSHeader "../ui_windows.h" #include "test.h" diff --git a/ui_windows.h b/ui_windows.h index 1bd916ff..31235c8b 100644 --- a/ui_windows.h +++ b/ui_windows.h @@ -13,8 +13,11 @@ extern "C" { struct uiControlOSVtable { size_t Size; + HWND (*Handle)(uiControl *c, void *implData); }; +uiprivExtern HWND uiWindowsControlHandle(uiControl *c); + #ifdef __cplusplus } #endif diff --git a/windows/controls.cpp b/windows/controls.cpp index 8dde1477..57688c1b 100644 --- a/windows/controls.cpp +++ b/windows/controls.cpp @@ -1,12 +1,18 @@ // 8 june 2019 #include "uipriv_windows.hpp" -bool uiprivOSVtableValid(const uiControlOSVtable *osVtable, const char *func) +bool uiprivOSVtableValid(const char *name, const uiControlOSVtable *osVtable, const char *func) { if (osVtable->Size != sizeof (uiControlOSVtable)) { uiprivProgrammerErrorWrongStructSize(osVtable->Size, "uiControlOSVtable", func); return false; } +#define checkMethod(method) \ + if (osVtable->method == NULL) { \ + uiprivProgrammerErrorRequiredControlMethodMissing(name, "uiControlOSVtable", #method, func); \ + return 0; \ + } + checkMethod(Handle) return true; } @@ -18,3 +24,19 @@ uiControlOSVtable *uiprivCloneOSVtable(const uiControlOSVtable *osVtable) *v2 = *osVtable; return v2; } + +#define callVtable(method, ...) ((*(method))(__VA_ARGS__)) + +HWND uiWindowsControlHandle(uiControl *c) +{ + uiControlOSVtable *osVtable; + + if (!uiprivCheckInitializedAndThread()) + return NULL; + if (c == NULL) { + uiprivProgrammerErrorNullPointer("uiControl", uiprivFunc); + return NULL; + } + osVtable = uiprivControlOSVtable(c); + return callVtable(osVtable->Handle, c, uiControlImplData(c)); +} diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 3319b617..acb24f41 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -1,7 +1,9 @@ // 21 april 2016 #include "uipriv_windows.hpp" +#include "../common/third_party/utf.h" // TODO clean this up +#define emptyUTF8() ((char *) uiprivAlloc(1 * sizeof (char), "char[]")) #define emptyUTF16() ((WCHAR *) uiprivAlloc(1 * sizeof (WCHAR), "WCHAR[]")) WCHAR *uiprivToUTF16(const char *str) diff --git a/windows/window.cpp b/windows/window.cpp index e1068b9c..e4e36ec3 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -5,7 +5,7 @@ struct windowImplData { HWND hwnd; - const char *title; + char *title; #if 0 HMENU menubar; uiControl *child; @@ -156,18 +156,6 @@ static void uiWindowDestroy(uiControl *c) uiFreeControl(uiControl(w)); } -uiWindowsControlDefaultHandle(uiWindow) - -uiControl *uiWindowParent(uiControl *c) -{ - return NULL; -} - -void uiWindowSetParent(uiControl *c, uiControl *parent) -{ - uiUserBugCannotSetParentOnToplevel("uiWindow"); -} - static int uiWindowToplevel(uiControl *c) { return 1; @@ -511,7 +499,8 @@ static bool windowInit(uiControl *c, void *implData, void *initData) NULL, NULL, uipriv_hInstance, c, &(wi->hwnd)); if (hr != S_OK) { - // TODO + uiprivInternalError("CreateWindowExW() failed in windowInit(): 0x%08I32X", hr); + return true; } return true; @@ -520,12 +509,15 @@ static bool windowInit(uiControl *c, void *implData, void *initData) static void windowFree(uiControl *c, void *implData) { struct windowImplData *wi = (struct windowImplData *) implData; + HRESULT hr; if (wi->title != NULL) { uiprivFreeUTF8(wi->title); wi->title = NULL; } - xxxx + hr = uiprivHrDestroyWindow(wi->hwnd); + if (hr != S_OK) + uiprivInternalError("DestroyWindow() failed in windowFree(): 0x%08I32X", hr); } static void windowParentChanging(uiControl *c, void *implData, uiControl *oldParent) @@ -596,6 +588,7 @@ void uiprivSysWindowSetTitle(uiWindow *w, const char *title) { struct windowImplData *wi = (struct windowImplData *) uiControlImplData(uiControl(w)); WCHAR *wtitle; + HRESULT hr; if (wi->title != NULL) uiprivFreeUTF8(wi->title); @@ -603,9 +596,8 @@ void uiprivSysWindowSetTitle(uiWindow *w, const char *title) wtitle = uiprivToUTF16(wi->title); hr = uiprivHrSetWindowTextW(wi->hwnd, wtitle); uiprivFree(wtitle); - if (hr != S_OK) { - // TODO - } + if (hr != S_OK) + uiprivInternalError("SetWindowTextW() failed in uiWindowSetTitle(): 0x%08I32X", hr); // don't queue resize; the caption isn't part of what affects layout and sizing of the client area (it'll be ellipsized if too long) } diff --git a/windows/winhresult.cpp b/windows/winhresult.cpp index 43209254..70e95f4f 100644 --- a/windows/winhresult.cpp +++ b/windows/winhresult.cpp @@ -90,3 +90,11 @@ HRESULT WINAPI uiprivHrSetWindowTextW(HWND hwnd, LPCWSTR text) return lastErrorToHRESULT(); return S_OK; } + +HRESULT WINAPI uiprivHrDestroyWindow(HWND hwnd) +{ + SetLastError(0); + if (DestroyWindow(hwnd) == 0) + return lastErrorToHRESULT(); + return S_OK; +} diff --git a/windows/winhresult.hpp b/windows/winhresult.hpp index 6973b1c0..2511a960 100644 --- a/windows/winhresult.hpp +++ b/windows/winhresult.hpp @@ -8,3 +8,4 @@ extern HRESULT WINAPI uiprivHrPostMessageW(HWND hwnd, UINT uMsg, WPARAM wParam, extern HRESULT WINAPI uiprivHrLoadIconW(HINSTANCE hInstance, LPCWSTR name, HICON *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);