Added uiDarwinControlHandle(), and added it to uiWindow, and added uiWindow handle tests (including underlying string tests) to the test suite. Oof. Also I just remembered we're missing tests of these new functions in noinitwrongthread.c.

This commit is contained in:
Pietro Gagliardi 2020-05-25 22:13:48 -04:00
parent 16d2380adc
commit 0dd4bec2af
12 changed files with 179 additions and 8 deletions

View File

@ -75,7 +75,7 @@ uint32_t uiRegisterControlType(const char *name, const uiControlVtable *vtable,
uiprivProgrammerErrorNullPointer("uiControlOSVtable", uiprivFunc); uiprivProgrammerErrorNullPointer("uiControlOSVtable", uiprivFunc);
return 0; return 0;
} }
if (!uiprivOSVtableValid(osVtable, uiprivFunc)) if (!uiprivOSVtableValid(name, osVtable, uiprivFunc))
return 0; return 0;
ct = (struct controlType *) uiprivArrayAppend(&controlTypes, 1); ct = (struct controlType *) uiprivArrayAppend(&controlTypes, 1);
@ -259,6 +259,11 @@ void *uiControlImplData(uiControl *c)
return c->implData; return c->implData;
} }
uiControlOSVtable *uiprivControlOSVtable(uiControl *c)
{
return c->type->osVtable;
}
static uiControl testHookControlWithInvalidControlMarker = { static uiControl testHookControlWithInvalidControlMarker = {
// use something other than 0 here to make it look like accidental real data // use something other than 0 here to make it look like accidental real data
.controlID = UINT32_C(0x5A5A5A5A), .controlID = UINT32_C(0x5A5A5A5A),

View File

@ -73,8 +73,9 @@ uiprivPrintfFunc(
extern void uiprivReportError(const char *prefix, const char *msg, const char *suffix, bool internal); extern void uiprivReportError(const char *prefix, const char *msg, const char *suffix, bool internal);
// controls.c // controls.c
extern bool uiprivOSVtableValid(const uiControlOSVtable *osVtable, const char *func); extern bool uiprivOSVtableValid(const char *name, const uiControlOSVtable *osVtable, const char *func);
extern uiControlOSVtable *uiprivCloneOSVtable(const uiControlOSVtable *osVtable); extern uiControlOSVtable *uiprivCloneOSVtable(const uiControlOSVtable *osVtable);
extern uiControlOSVtable *uiprivControlOSVtable(uiControl *c);
// utf8.c // utf8.c
extern char *uiprivSanitizeUTF8(const char *str); extern char *uiprivSanitizeUTF8(const char *str);

View File

@ -1,12 +1,18 @@
// 8 june 2019 // 8 june 2019
#import "uipriv_darwin.h" #import "uipriv_darwin.h"
bool uiprivOSVtableValid(const uiControlOSVtable *osVtable, const char *func) bool uiprivOSVtableValid(const char *name, const uiControlOSVtable *osVtable, const char *func)
{ {
if (osVtable->Size != sizeof (uiControlOSVtable)) { if (osVtable->Size != sizeof (uiControlOSVtable)) {
uiprivProgrammerErrorWrongStructSize(osVtable->Size, "uiControlOSVtable", func); uiprivProgrammerErrorWrongStructSize(osVtable->Size, "uiControlOSVtable", func);
return false; return false;
} }
#define checkMethod(method) \
if (osVtable->method == NULL) { \
uiprivProgrammerErrorRequiredControlMethodMissing(name, "uiControlOSVtable", #method, func); \
return 0; \
}
checkMethod(Handle)
return true; return true;
} }
@ -18,3 +24,19 @@ uiControlOSVtable *uiprivCloneOSVtable(const uiControlOSVtable *osVtable)
*v2 = *osVtable; *v2 = *osVtable;
return v2; return v2;
} }
#define callVtable(method, ...) ((*(method))(__VA_ARGS__))
id uiDarwinControlHandle(uiControl *c)
{
uiControlOSVtable *osVtable;
if (!uiprivCheckInitializedAndThread())
return nil;
if (c == NULL) {
uiprivProgrammerErrorNullPointer("uiControl", uiprivFunc);
return nil;
}
osVtable = uiprivControlOSVtable(c);
return callVtable(osVtable->Handle, c, uiControlImplData(c));
}

View File

@ -415,6 +415,13 @@ static void windowParentChanged(uiControl *c, void *implData, uiControl *newPare
uiprivProgrammerErrorCannotHaveWindowsAsChildren(); uiprivProgrammerErrorCannotHaveWindowsAsChildren();
} }
static id windowHandle(uiControl *c, void *implData)
{
struct windowImplData *wi = (struct windowImplData *) implData;
return wi->window;
}
static const uiControlVtable windowVtable = { static const uiControlVtable windowVtable = {
.Size = sizeof (uiControlVtable), .Size = sizeof (uiControlVtable),
.Init = windowInit, .Init = windowInit,
@ -425,6 +432,7 @@ static const uiControlVtable windowVtable = {
static const uiControlOSVtable windowOSVtable = { static const uiControlOSVtable windowOSVtable = {
.Size = sizeof (uiControlOSVtable), .Size = sizeof (uiControlOSVtable),
.Handle = windowHandle,
}; };
static uint32_t windowType = 0; static uint32_t windowType = 0;

View File

@ -10,10 +10,11 @@ TODO
### `uiControlOSVtable` ### `uiControlOSVtable`
```c ```objective-c
typedef struct uiControlOSVtable uiControlOSVtable; typedef struct uiControlOSVtable uiControlOSVtable;
struct uiControlOSVtable { struct uiControlOSVtable {
size_t Size; size_t Size;
id (*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 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 `uiDarwinControl` 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 `uiDarwinControl` 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 `uiDarwinControlHandle()`.
### `uiDarwinControlHandle()`
```objective-c
uiprivExtern NSView *uiDarwinControlHandle(uiControl *c);
```
`uiDarwinControlHandle()` returns the Objective-C object that underpins `c`, or `nil` 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 string 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.
`uiWindow`s have a single handle of type `NSWindow` 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.
For all other `uiControl`s defined by libui, the returned object is of the appropriate `NSView` subclass:
* TODO
TODO invalid value for c

View File

@ -1,8 +1,14 @@
// 10 june 2019 // 10 june 2019
#import "test_darwin.h" #import "test_darwin.h"
static id osVtableNopHandle(uiControl *c, void *implData)
{
return nil;
}
static const uiControlOSVtable osVtable = { static const uiControlOSVtable osVtable = {
.Size = sizeof (uiControlOSVtable), .Size = sizeof (uiControlOSVtable),
.Handle = osVtableNopHandle,
}; };
const uiControlOSVtable *testOSVtable(void) const uiControlOSVtable *testOSVtable(void)
@ -23,3 +29,17 @@ Test(WrongControlOSVtableSizeIsProgrammerError)
uiRegisterControlType("name", &vtable, &osvt, 0); uiRegisterControlType("name", &vtable, &osvt, 0);
endCheckProgrammerError(ctx); endCheckProgrammerError(ctx);
} }
Test(ControlOSVtableWithMissingHandleMethodIsProgrammerError)
{
uiControlVtable vtable;
uiControlOSVtable osvt;
void *ctx;
testControlLoadNopVtable(&vtable);
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlOSVtable method Handle() missing for uiControl type name");
osvt = osVtable;
osvt.Handle = NULL;
uiRegisterControlType("name", &vtable, &osvt, 0);
endCheckProgrammerError(ctx);
}

View File

@ -17,6 +17,7 @@ if libui_OS == 'windows'
elif libui_OS == 'darwin' elif libui_OS == 'darwin'
libui_test_sources += files([ libui_test_sources += files([
'controls_darwin.m', 'controls_darwin.m',
'window_darwin.m',
]) ])
elif libui_OS == 'haiku' elif libui_OS == 'haiku'
libui_test_sources += files([ libui_test_sources += files([
@ -50,6 +51,7 @@ libui_test_deps = [
dependency('threads', dependency('threads',
required: true), required: true),
] ]
# TODO deduplicate these
if libui_OS == 'windows' if libui_OS == 'windows'
# static mode already gives us these dependencies # static mode already gives us these dependencies
if libui_mode != 'static' if libui_mode != 'static'
@ -60,6 +62,17 @@ if libui_OS == 'windows'
required: true), required: true),
] ]
endif endif
elif libui_OS == 'darwin'
# static mode already gives us these dependencies
if libui_mode != 'static'
libui_test_deps += [
meson.get_compiler('objc').find_library('objc',
required: true),
dependency('appleframeworks',
modules: ['Foundation', 'AppKit'],
required: true),
]
endif
endif endif
pymod = import('python') pymod = import('python')

View File

@ -1,3 +1,5 @@
// 10 june 2019 // 10 june 2019
// TODO proper macros and headers
#import <Cocoa/Cocoa.h>
#define libuiOSHeader "../ui_darwin.h" #define libuiOSHeader "../ui_darwin.h"
#include "test.h" #import "test.h"

View File

@ -80,5 +80,3 @@ Test(SetWindowTitle_Invalid)
{ {
testSetWindowTitleImpl(testUTF8InvalidInput, testUTF8InvalidOutput); testSetWindowTitleImpl(testUTF8InvalidInput, testUTF8InvalidOutput);
} }
// TODO for all the above, check that the underlying OS-level title was also set appropriately

78
test/window_darwin.m Normal file
View File

@ -0,0 +1,78 @@
// 24 may 2020
#import "test_darwin.h"
Test(WindowHasHandleFromStart)
{
uiWindow *a;
a = uiNewWindow();
if (uiDarwinControlHandle(uiControl(a)) == nil)
TestErrorf("uiDarwinControlHandle(brand new uiWindow) is nil; should not be");
uiControlFree(uiControl(a));
}
Test(InitialWindowTitleIsEmptyString_OSLevel)
{
uiWindow *w;
NSWindow *nsw;
const char *title;
w = uiNewWindow();
nsw = (NSWindow *) uiDarwinControlHandle(uiControl(w));
title = [[nsw title] UTF8String];
if (!utf8equal(title, testUTF8Empty))
utf8diffError("brand new uiWindow has wrong title", title, testUTF8Empty);
uiControlFree(uiControl(w));
}
static void testSetWindowTitleImplFull(const char *file, long line, const char *title, const char *want)
{
uiWindow *w;
NSWindow *nsw;
const char *got;
w = uiNewWindow();
uiWindowSetTitle(w, title);
nsw = (NSWindow *) uiDarwinControlHandle(uiControl(w));
got = [[nsw title] UTF8String];
if (!utf8equal(got, want))
utf8diffErrorFull(file, line, "uiWindowTitle() reported wrong title after uiWindowSetTitle()", got, want);
uiControlFree(uiControl(w));
}
#define testSetWindowTitleImpl(title, want) testSetWindowTitleImplFull(__FILE__, __LINE__, title, want)
Test(SetWindowTitle_OSLevel_Empty)
{
testSetWindowTitleImpl(testUTF8Empty, testUTF8Empty);
}
Test(SetWindowTitle_OSLevel_ASCIIOnly)
{
testSetWindowTitleImpl(testUTF8ASCIIOnly, testUTF8ASCIIOnly);
}
Test(SetWindowTitle_OSLevel_WithTwoByte)
{
testSetWindowTitleImpl(testUTF8WithTwoByte, testUTF8WithTwoByte);
}
Test(SetWindowTitle_OSLevel_WithThreeByte)
{
testSetWindowTitleImpl(testUTF8WithThreeByte, testUTF8WithThreeByte);
}
Test(SetWindowTitle_OSLevel_WithFourByte)
{
testSetWindowTitleImpl(testUTF8WithFourByte, testUTF8WithFourByte);
}
Test(SetWindowTitle_OSLevel_Combined)
{
testSetWindowTitleImpl(testUTF8Combined, testUTF8Combined);
}
Test(SetWindowTitle_OSLevel_Invalid)
{
testSetWindowTitleImpl(testUTF8InvalidInput, testUTF8InvalidOutput);
}

1
ui.h
View File

@ -69,6 +69,7 @@ uiprivExtern void *uiControlImplData(uiControl *c);
typedef uiControl uiWindow; typedef uiControl uiWindow;
uiprivExtern uint32_t uiWindowType(void); uiprivExtern uint32_t uiWindowType(void);
#define uiWindow(obj) ((uiWindow *) uiCheckControlType((obj), uiWindowType())) #define uiWindow(obj) ((uiWindow *) uiCheckControlType((obj), uiWindowType()))
// TODO provide events for window close button clicked
uiprivExtern uiWindow *uiNewWindow(void); uiprivExtern uiWindow *uiNewWindow(void);
uiprivExtern const char *uiWindowTitle(uiWindow *w); uiprivExtern const char *uiWindowTitle(uiWindow *w);

View File

@ -13,8 +13,12 @@ extern "C" {
struct uiControlOSVtable { struct uiControlOSVtable {
size_t Size; size_t Size;
id (*Handle)(uiControl *c, void *implData);
// TODO provide events for handle creation and destruction
}; };
uiprivExtern id uiDarwinControlHandle(uiControl *c);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif