More Windows stuff. Ugh this is what I was trying to AVOID but oh well
This commit is contained in:
parent
3ea84f9bf5
commit
c42b9399cd
|
@ -0,0 +1,58 @@
|
|||
// 7 april 2015
|
||||
|
||||
/*
|
||||
This file assumes that you have included <windows.h> and "ui.h" beforehand. It provides API-specific functions for interfacing with foreign controls in Windows.
|
||||
*/
|
||||
|
||||
#ifndef __UI_UI_WINDOWS_H__
|
||||
#define __UI_UI_WINDOWS_H__
|
||||
|
||||
// uiWindowsNewControl() initializes the given uiControl with the given Windows API control inside.
|
||||
// You will need to provide the preferredSize() method yourself.
|
||||
typedef struct uiWindowsNewControlParams uiWindowsNewControlParams;
|
||||
struct uiWindowsNewControlParams {
|
||||
// These match the CreateWindowExW() function.
|
||||
DWORD dwExStyle;
|
||||
LPCWSTR lpClassName;
|
||||
LPCWSTR lpWindowName;
|
||||
DWORD dwStyle; // WS_CHILD and WS_VISIBLE are automatically applied.
|
||||
HINSTANCE hInstance;
|
||||
|
||||
// Set this to non-FALSE to use the standard control font used by other ui controls.
|
||||
BOOL useStandardControlFont;
|
||||
|
||||
// These are called when the control sends a WM_COMMAND or WM_NOTIFY (respectively) to its parent.
|
||||
// ui redirects the message back and calls these functions.
|
||||
// Store the result in *lResult and return any non-FALSE value (such as TRUE) to return the given result; return FALSE to pass the notification up to your window procedure.
|
||||
// Note that these are only issued if they come from the uiControl itself; notifications from children of the uiControl (such as a header control) will be received normally.
|
||||
BOOL (*onWM_COMMAND)(uiControl *c, WORD code, LRESULT *lResult);
|
||||
// TODO set idFrom to 0?
|
||||
BOOL (*onWM_NOTIFY)(uiControl *c, NMHDR *nm, LRESULT *lResult);
|
||||
|
||||
// This is called when the widget is ready to be destroyed.
|
||||
void (*onDestroy)(void *data);
|
||||
void *onDestroyData;
|
||||
};
|
||||
void uiWindowsNewControl(uiControl *c, uiWindowsNewControlParams *p);
|
||||
|
||||
// This contains the Windows-specific parts of the uiSizing structure.
|
||||
// baseX and baseY are the dialog base units.
|
||||
// internalLeading is the standard control font's internal leading; labels in uiForms use this for correct Y positioning.
|
||||
struct uiSizingSys {
|
||||
int baseX;
|
||||
int baseY;
|
||||
LONG internalLeading;
|
||||
};
|
||||
// Use these in your preferredSize() implementation with baseX and baseY.
|
||||
#define uiDlgUnitsToX(dlg, baseX) MulDiv((dlg), baseX, 4)
|
||||
#define uiDlgUnitsToY(dlg, baseY) MulDiv((dlg), baseY, 8)
|
||||
|
||||
// and use this if you need the text of the window width
|
||||
extern intmax_t uiWindowsWindowTextWidth(HWND hwnd);
|
||||
|
||||
// these functions get and set the window text for such a uiControl
|
||||
// the value returned should be freed with uiFreeText()
|
||||
extern char *uiWindowsControlText(uiControl *);
|
||||
extern void uiWindowsControlSetText(uiControl *, const char *);
|
||||
|
||||
#endif
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
osCFILES = \
|
||||
windows/alloc.c \
|
||||
windows/comctl32.c \
|
||||
windows/debug.c \
|
||||
windows/init.c \
|
||||
windows/main.c \
|
||||
windows/oscontainer.c \
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
// 17 july 2014
|
||||
#include "uipriv_windows.h"
|
||||
|
||||
static ULONG_PTR comctlManifestCookie;
|
||||
static HMODULE comctl32;
|
||||
|
||||
// these are listed as WINAPI in both Microsoft's and MinGW's headers, but not on MSDN for some reason
|
||||
BOOL (*WINAPI fv_SetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
|
||||
BOOL (*WINAPI fv_RemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);
|
||||
LRESULT (*WINAPI fv_DefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
#define wantedICCClasses ( \
|
||||
ICC_STANDARD_CLASSES | /* user32.dll controls */ \
|
||||
ICC_PROGRESS_CLASS | /* progress bars */ \
|
||||
ICC_TAB_CLASSES | /* tabs */ \
|
||||
ICC_LISTVIEW_CLASSES | /* table headers */ \
|
||||
ICC_UPDOWN_CLASS | /* spinboxes */ \
|
||||
0)
|
||||
|
||||
// note that this is an 8-bit character string we're writing; see the encoding clause
|
||||
static const char manifest[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n<assemblyIdentity\n version=\"1.0.0.0\"\n processorArchitecture=\"*\"\n name=\"CompanyName.ProductName.YourApplication\"\n type=\"win32\"\n/>\n<description>Your application description here.</description>\n<dependency>\n <dependentAssembly>\n <assemblyIdentity\n type=\"win32\"\n name=\"Microsoft.Windows.Common-Controls\"\n version=\"6.0.0.0\"\n processorArchitecture=\"*\"\n publicKeyToken=\"6595b64144ccf1df\"\n language=\"*\"\n />\n </dependentAssembly>\n</dependency>\n</assembly>\n";
|
||||
|
||||
/*
|
||||
Windows requires a manifest file to enable Common Controls version 6.
|
||||
The only way to not require an external manifest is to synthesize the manifest ourselves.
|
||||
We can use the activation context API to load it at runtime.
|
||||
References:
|
||||
- http://stackoverflow.com/questions/4308503/how-to-enable-visual-styles-without-a-manifest
|
||||
- http://support.microsoft.com/kb/830033
|
||||
Because neither Go nor MinGW have ways to compile in resources like this (as far as I know), we have to do the work ourselves.
|
||||
*/
|
||||
const char *initCommonControls(void)
|
||||
{
|
||||
WCHAR temppath[MAX_PATH + 1];
|
||||
WCHAR filename[MAX_PATH + 1];
|
||||
HANDLE file;
|
||||
DWORD nExpected, nGot;
|
||||
ACTCTX actctx;
|
||||
HANDLE ac;
|
||||
INITCOMMONCONTROLSEX icc;
|
||||
FARPROC f;
|
||||
// this is listed as WINAPI in both Microsoft's and MinGW's headers, but not on MSDN for some reason
|
||||
BOOL (*WINAPI ficc)(const LPINITCOMMONCONTROLSEX);
|
||||
|
||||
if (GetTempPathW(MAX_PATH + 1, temppath) == 0)
|
||||
return "getting temporary path for writing manifest file in initCommonControls()";
|
||||
if (GetTempFileNameW(temppath, L"manifest", 0, filename) == 0)
|
||||
return "getting temporary filename for writing manifest file in initCommonControls()";
|
||||
file = CreateFileW(filename, GENERIC_WRITE,
|
||||
0, // don't share while writing
|
||||
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (file == NULL)
|
||||
return "creating manifest file in initCommonControls()";
|
||||
nExpected = (sizeof manifest / sizeof manifest[0]) - 1; // - 1 to omit the terminating null character)
|
||||
SetLastError(0); // catch errorless short writes
|
||||
if (WriteFile(file, manifest, nExpected, &nGot, NULL) == 0)
|
||||
return "writing manifest file in initCommonControls()";
|
||||
if (nGot != nExpected) {
|
||||
DWORD lasterr;
|
||||
|
||||
lasterr = GetLastError();
|
||||
if (lasterr == 0)
|
||||
return "writing entire manifest file (short write) without error code in initCommonControls()";
|
||||
return "writing entire manifest file (short write) in initCommonControls()";
|
||||
}
|
||||
if (CloseHandle(file) == 0)
|
||||
return "closing manifest file (this IS an error here because not doing so will prevent Windows from being able to use the manifest file in an activation context) in initCommonControls()";
|
||||
|
||||
ZeroMemory(&actctx, sizeof (ACTCTX));
|
||||
actctx.cbSize = sizeof (ACTCTX);
|
||||
actctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT;
|
||||
actctx.lpSource = filename;
|
||||
ac = CreateActCtx(&actctx);
|
||||
if (ac == INVALID_HANDLE_VALUE)
|
||||
return "creating activation context for synthesized manifest file in initCommonControls()";
|
||||
if (ActivateActCtx(ac, &comctlManifestCookie) == FALSE)
|
||||
return "activating activation context for synthesized manifest file in initCommonControls()";
|
||||
|
||||
ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
|
||||
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
|
||||
icc.dwICC = wantedICCClasses;
|
||||
|
||||
comctl32 = LoadLibraryW(L"comctl32.dll");
|
||||
if (comctl32 == NULL)
|
||||
return "loading comctl32.dll in initCommonControls()";
|
||||
|
||||
// GetProcAddress() only takes a multibyte string
|
||||
#define LOAD(fn) f = GetProcAddress(comctl32, fn); \
|
||||
if (f == NULL) \
|
||||
return "loading " fn "() in initCommonControls()";
|
||||
|
||||
LOAD("InitCommonControlsEx");
|
||||
ficc = (BOOL (*WINAPI)(const LPINITCOMMONCONTROLSEX)) f;
|
||||
LOAD("SetWindowSubclass");
|
||||
fv_SetWindowSubclass = (BOOL (*WINAPI)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR)) f;
|
||||
LOAD("RemoveWindowSubclass");
|
||||
fv_RemoveWindowSubclass = (BOOL (*WINAPI)(HWND, SUBCLASSPROC, UINT_PTR)) f;
|
||||
LOAD("DefSubclassProc");
|
||||
fv_DefSubclassProc = (LRESULT (*WINAPI)(HWND, UINT, WPARAM, LPARAM)) f;
|
||||
|
||||
if ((*ficc)(&icc) == FALSE)
|
||||
return "initializing Common Controls (comctl32.dll) in initCommonControls()";
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
// 25 february 2015
|
||||
#include "uipriv_windows.h"
|
||||
|
||||
// uncomment the following line to enable debug messages
|
||||
#define tableDebug
|
||||
// uncomment the following line to halt on a debug message
|
||||
#define tableDebugStop
|
||||
|
||||
#ifdef tableDebug
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
HRESULT logLastError(const char *context)
|
||||
{
|
||||
DWORD le;
|
||||
WCHAR *msg;
|
||||
BOOL parenthesize = FALSE;
|
||||
BOOL localFreeFailed = FALSE;
|
||||
DWORD localFreeLastError;
|
||||
|
||||
le = GetLastError();
|
||||
fprintf(stderr, "%s: ", context);
|
||||
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, le, 0, (LPWSTR) (&msg), 0, NULL) != 0) {
|
||||
fprintf(stderr, "%S (", msg);
|
||||
if (LocalFree(msg) != NULL) {
|
||||
localFreeFailed = TRUE;
|
||||
localFreeLastError = GetLastError();
|
||||
}
|
||||
parenthesize = TRUE;
|
||||
}
|
||||
fprintf(stderr, "GetLastError() == %I32u", le);
|
||||
if (parenthesize)
|
||||
fprintf(stderr, ")");
|
||||
if (localFreeFailed)
|
||||
fprintf(stderr, "; local free of system message failed with last error %I32u", localFreeLastError);
|
||||
fprintf(stderr, "\n");
|
||||
#ifdef tableDebugStop
|
||||
DebugBreak();
|
||||
#endif
|
||||
SetLastError(le);
|
||||
// a function does not have to set a last error
|
||||
// if the last error we get is actually 0, then HRESULT_FROM_WIN32(0) will return S_OK (0 cast to an HRESULT, since 0 <= 0), which we don't want
|
||||
// prevent this by returning E_FAIL, so the rest of the Table code doesn't barge onward
|
||||
if (le == 0)
|
||||
return E_FAIL;
|
||||
return HRESULT_FROM_WIN32(le);
|
||||
}
|
||||
|
||||
HRESULT logHRESULT(const char *context, HRESULT hr)
|
||||
{
|
||||
WCHAR *msg;
|
||||
BOOL parenthesize = FALSE;
|
||||
BOOL localFreeFailed = FALSE;
|
||||
DWORD localFreeLastError;
|
||||
|
||||
fprintf(stderr, "%s: ", context);
|
||||
// this isn't technically documented, but everyone does it, including Microsoft (see the implementation of _com_error::ErrorMessage() in a copy of comdef.h that comes with the Windows DDK)
|
||||
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD) hr, 0, (LPWSTR) (&msg), 0, NULL) != 0) {
|
||||
fprintf(stderr, "%S (", msg);
|
||||
if (LocalFree(msg) != NULL) {
|
||||
localFreeFailed = TRUE;
|
||||
localFreeLastError = GetLastError();
|
||||
}
|
||||
parenthesize = TRUE;
|
||||
}
|
||||
fprintf(stderr, "HRESULT == 0x%I32X", hr);
|
||||
if (parenthesize)
|
||||
fprintf(stderr, ")");
|
||||
if (localFreeFailed)
|
||||
fprintf(stderr, "; local free of system message failed with last error %I32u", localFreeLastError);
|
||||
fprintf(stderr, "\n");
|
||||
#ifdef tableDebugStop
|
||||
DebugBreak();
|
||||
#endif
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT logMemoryExhausted(const char *reason)
|
||||
{
|
||||
fprintf(stderr, "memory exhausted %s\n", reason);
|
||||
#ifdef tableDebugStop
|
||||
DebugBreak();
|
||||
#endif
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
HRESULT logLastError(const char *reason)
|
||||
{
|
||||
DWORD le;
|
||||
|
||||
le = GetLastError();
|
||||
// we shouldn't need to do this, but let's do this anyway just to be safe
|
||||
SetLastError(le);
|
||||
if (le == 0)
|
||||
return E_FAIL;
|
||||
return HRESULT_FROM_WIN32(le);
|
||||
}
|
||||
|
||||
HRESULT logHRESULT(const char *reason, HRESULT hr)
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT logMemoryExhausted(const char *reason)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -25,7 +25,7 @@ static void paintControlBackground(HWND hwnd, HDC dc)
|
|||
|
||||
parent = hwnd;
|
||||
for (;;) {
|
||||
parent = GetOSContainer(parent);
|
||||
parent = GetParent(parent);
|
||||
if (parent == NULL)
|
||||
logLastError("error getting parent control of control in paintControlBackground()");
|
||||
// wine sends these messages early, yay...
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// 24 april 2015
|
||||
// 6 january 2015
|
||||
#define UNICODE
|
||||
#define _UNICODE
|
||||
#define STRICT
|
||||
|
@ -37,3 +37,42 @@ enum {
|
|||
msgUpdateChild, // fake because Windows seems to SWP_NOSIZE MoveWindow()s and SetWindowPos()s that don't change the window size (even if SWP_NOSIZE isn't specified)
|
||||
msgCanDestroyNow,
|
||||
};
|
||||
|
||||
#define HWND(c) ((HWND) uiControlHandle(uiControl(c)))
|
||||
#define uiOSContainerHWND(p) ((HWND) uiOSContainerHandle(p))
|
||||
|
||||
// debug.c
|
||||
extern HRESULT logLastError(const char *);
|
||||
extern HRESULT logHRESULT(const char *, HRESULT);
|
||||
extern HRESULT logMemoryExhausted(const char *);
|
||||
|
||||
// init.c
|
||||
extern HINSTANCE hInstance;
|
||||
extern int nCmdShow;
|
||||
extern HFONT hMessageFont;
|
||||
extern HBRUSH hollowBrush;
|
||||
|
||||
// util.c
|
||||
extern int windowClassOf(HWND, ...);
|
||||
|
||||
// text.c
|
||||
extern WCHAR *toUTF16(const char *);
|
||||
extern char *toUTF8(const WCHAR *);
|
||||
extern WCHAR *windowText(HWND);
|
||||
|
||||
// comctl32.c
|
||||
extern BOOL (*WINAPI fv_SetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
|
||||
extern BOOL (*WINAPI fv_RemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);
|
||||
extern LRESULT (*WINAPI fv_DefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
|
||||
extern const char *initCommonControls(void);
|
||||
|
||||
// window.c
|
||||
extern ATOM registerWindowClass(HICON, HCURSOR);
|
||||
|
||||
// parent.c
|
||||
extern HWND initialOSContainer;
|
||||
extern const char *initOSContainer(HICON, HCURSOR);
|
||||
|
||||
// menu.c
|
||||
extern HMENU makeMenubar(void);
|
||||
extern const uiMenuItem *menuIDToItem(UINT_PTR);
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
// 6 april 2015
|
||||
#include "uipriv_windows.h"
|
||||
|
||||
intmax_t uiWindowsWindowTextWidth(HWND hwnd)
|
||||
{
|
||||
LRESULT len;
|
||||
WCHAR *text;
|
||||
HDC dc;
|
||||
HFONT prevfont;
|
||||
SIZE size;
|
||||
|
||||
size.cx = 0;
|
||||
size.cy = 0;
|
||||
|
||||
// first we need the window text
|
||||
len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0);
|
||||
if (len == 0) // no text; nothing to do
|
||||
return 0;
|
||||
text = (WCHAR *) uiAlloc((len + 1) * sizeof (WCHAR), "WCHAR[]");
|
||||
// note the comparison: the size includes the null terminator, but the return does not
|
||||
if (GetWindowText(hwnd, text, len + 1) != len)
|
||||
logLastError("error getting window text in uiWindowsWindowTextWidth()");
|
||||
|
||||
// now we can do the calculations
|
||||
dc = GetDC(hwnd);
|
||||
if (dc == NULL)
|
||||
logLastError("error getting DC in uiWindowsWindowTextWidth()");
|
||||
prevfont = (HFONT) SelectObject(dc, hMessageFont);
|
||||
if (prevfont == NULL)
|
||||
logLastError("error loading control font into device context in uiWindowsWindowTextWidth()");
|
||||
if (GetTextExtentPoint32W(dc, text, len, &size) == 0)
|
||||
logLastError("error getting text extent point in uiWindowsWindowTextWidth()");
|
||||
if (SelectObject(dc, prevfont) != hMessageFont)
|
||||
logLastError("error restoring previous font into device context in uiWindowsWindowTextWidth()");
|
||||
if (ReleaseDC(hwnd, dc) == 0)
|
||||
logLastError("error releasing DC in uiWindowsWindowTextWidth()");
|
||||
uiFree(text);
|
||||
|
||||
return size.cx;
|
||||
}
|
||||
|
||||
// this is a helper function that takes the logic of determining window classes and puts it all in one place
|
||||
// there are a number of places where we need to know what window class an arbitrary handle has
|
||||
// theoretically we could use the class atom to avoid a _wcsicmp()
|
||||
// however, raymond chen advises against this - http://blogs.msdn.com/b/oldnewthing/archive/2004/10/11/240744.aspx (and we're not in control of the Tab class, before you say anything)
|
||||
// usage: windowClassOf(hwnd, L"class 1", L"class 2", ..., NULL)
|
||||
int windowClassOf(HWND hwnd, ...)
|
||||
{
|
||||
// MSDN says 256 is the maximum length of a class name; add a few characters just to be safe (because it doesn't say whether this includes the terminating null character)
|
||||
#define maxClassName 260
|
||||
WCHAR classname[maxClassName + 1];
|
||||
va_list ap;
|
||||
WCHAR *curname;
|
||||
int i;
|
||||
|
||||
if (GetClassNameW(hwnd, classname, maxClassName) == 0)
|
||||
logLastError("error getting name of window class in windowClassOf()");
|
||||
va_start(ap, hwnd);
|
||||
i = 0;
|
||||
for (;;) {
|
||||
curname = va_arg(ap, WCHAR *);
|
||||
if (curname == NULL)
|
||||
break;
|
||||
if (_wcsicmp(classname, curname) == 0) {
|
||||
va_end(ap);
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// no match
|
||||
va_end(ap);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void complain(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
abort();
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
// 6 april 2015
|
||||
#include "uipriv_windows.h"
|
||||
|
||||
struct window {
|
||||
uiWindow w;
|
||||
HWND hwnd;
|
||||
uiOSContainer *content;
|
||||
BOOL shownOnce;
|
||||
int (*onClosing)(uiWindow *, void *);
|
||||
void *onClosingData;
|
||||
int margined;
|
||||
BOOL canDestroy;
|
||||
};
|
||||
|
||||
#define uiWindowClass L"uiWindowClass"
|
||||
|
||||
static LRESULT CALLBACK uiWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
struct window *w;
|
||||
CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
|
||||
WINDOWPOS *wp = (WINDOWPOS *) lParam;
|
||||
RECT r;
|
||||
HWND contenthwnd;
|
||||
const uiMenuItem *item;
|
||||
|
||||
w = (struct window *) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
|
||||
if (w == NULL) {
|
||||
if (uMsg == WM_CREATE)
|
||||
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) (cs->lpCreateParams));
|
||||
// fall through to DefWindowProc() anyway
|
||||
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
switch (uMsg) {
|
||||
case WM_COMMAND:
|
||||
// not a menu
|
||||
if (lParam != 0)
|
||||
break;
|
||||
if (HIWORD(wParam) != 0)
|
||||
break;
|
||||
/*TODO item = menuIDToItem(LOWORD(wParam));
|
||||
printf("%d", item->Type);
|
||||
if (item->Type == uiMenuItemTypeCommand)
|
||||
printf(" %s", item->Name);
|
||||
printf("\n");
|
||||
*/ return 0;
|
||||
case WM_WINDOWPOSCHANGED:
|
||||
if ((wp->flags & SWP_NOSIZE) != 0)
|
||||
break;
|
||||
// fall through
|
||||
case msgUpdateChild:
|
||||
if (GetClientRect(w->hwnd, &r) == 0)
|
||||
logLastError("error getting window client rect for resize in uiWindowWndProc()");
|
||||
contenthwnd = uiOSContainerHWND(w->content);
|
||||
if (MoveWindow(contenthwnd, r.left, r.top, r.right - r.left, r.bottom - r.top, TRUE) == 0)
|
||||
logLastError("error resizing window content parent in uiWindowWndProc()");
|
||||
return 0;
|
||||
case WM_CLOSE:
|
||||
if (!(*(w->onClosing))(uiWindow(w), w->onClosingData))
|
||||
uiWindowDestroy(uiWindow(w));
|
||||
return 0; // we destroyed it already
|
||||
case WM_DESTROY:
|
||||
if (!w->canDestroy)
|
||||
complain("attempt to destroy uiWindow at %p before uiWindowDestroy()", w);
|
||||
uiFree(w);
|
||||
break; // fall through to DefWindowProcW()
|
||||
}
|
||||
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
ATOM registerWindowClass(HICON hDefaultIcon, HCURSOR hDefaultCursor)
|
||||
{
|
||||
WNDCLASSW wc;
|
||||
|
||||
ZeroMemory(&wc, sizeof (WNDCLASSW));
|
||||
wc.lpszClassName = uiWindowClass;
|
||||
wc.lpfnWndProc = uiWindowWndProc;
|
||||
wc.hInstance = hInstance;
|
||||
wc.hIcon = hDefaultIcon;
|
||||
wc.hCursor = hDefaultCursor;
|
||||
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
|
||||
return RegisterClassW(&wc);
|
||||
}
|
||||
|
||||
#define exstyle 0
|
||||
#define style WS_OVERLAPPEDWINDOW
|
||||
|
||||
static int defaultOnClosing(uiWindow *w, void *data)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void windowDestroy(uiWindow *ww)
|
||||
{
|
||||
struct window *w = (struct window *) ww;
|
||||
|
||||
// first destroy the content
|
||||
uiOSContainerDestroy(w->content);
|
||||
// then mark that we're ready to destroy
|
||||
w->canDestroy = TRUE;
|
||||
// and finally destroy
|
||||
// TODO check for errors
|
||||
DestroyWindow(w->hwnd);
|
||||
// no need to explicitly destroy the menubar, if any; that's done automatically during window destruction
|
||||
}
|
||||
|
||||
static uintptr_t windowHandle(uiWindow *ww)
|
||||
{
|
||||
struct window *w = (struct window *) ww;
|
||||
|
||||
return (uintptr_t) (w->hwnd);
|
||||
}
|
||||
|
||||
static char *windowTitle(uiWindow *ww)
|
||||
{
|
||||
struct window *w = (struct window *) ww;
|
||||
WCHAR *wtext;
|
||||
char *text;
|
||||
|
||||
wtext = windowText(w->hwnd);
|
||||
text = toUTF8(wtext);
|
||||
uiFree(wtext);
|
||||
return text;
|
||||
}
|
||||
|
||||
static void windowSetTitle(uiWindow *ww, const char *text)
|
||||
{
|
||||
struct window *w = (struct window *) ww;
|
||||
WCHAR *wtext;
|
||||
|
||||
wtext = toUTF16(text);
|
||||
if (SetWindowTextW(w->hwnd, wtext) == 0)
|
||||
logLastError("error setting window title in uiWindowSetTitle()");
|
||||
uiFree(wtext);
|
||||
}
|
||||
|
||||
static void windowShow(uiWindow *ww)
|
||||
{
|
||||
struct window *w = (struct window *) ww;
|
||||
|
||||
if (w->shownOnce) {
|
||||
ShowWindow(w->hwnd, SW_SHOW);
|
||||
return;
|
||||
}
|
||||
w->shownOnce = TRUE;
|
||||
ShowWindow(w->hwnd, nCmdShow);
|
||||
if (UpdateWindow(w->hwnd) == 0)
|
||||
logLastError("error calling UpdateWindow() after showing uiWindow for the first time");
|
||||
}
|
||||
|
||||
static void windowHide(uiWindow *ww)
|
||||
{
|
||||
struct window *w = (struct window *) ww;
|
||||
|
||||
ShowWindow(w->hwnd, SW_HIDE);
|
||||
}
|
||||
|
||||
static void windowOnClosing(uiWindow *ww, int (*f)(uiWindow *, void *), void *data)
|
||||
{
|
||||
struct window *w = (struct window *) ww;
|
||||
|
||||
w->onClosing = f;
|
||||
w->onClosingData = data;
|
||||
}
|
||||
|
||||
static void windowSetChild(uiWindow *ww, uiControl *c)
|
||||
{
|
||||
struct window *w = (struct window *) ww;
|
||||
|
||||
uiOSContainerSetMainControl(w->content, c);
|
||||
// don't call uiOSContainerUpdate(); instead, synthesize a resize
|
||||
// otherwise, we'll have a 0x0 content area at first
|
||||
SendMessageW(w->hwnd, msgUpdateChild, 0, 0);
|
||||
}
|
||||
|
||||
static int windowMargined(uiWindow *ww)
|
||||
{
|
||||
struct window *w = (struct window *) ww;
|
||||
|
||||
return w->margined;
|
||||
}
|
||||
|
||||
// from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
|
||||
#define windowMargin 7
|
||||
|
||||
static void windowSetMargined(uiWindow *ww, int margined)
|
||||
{
|
||||
struct window *w = (struct window *) ww;
|
||||
|
||||
w->margined = margined;
|
||||
if (w->margined)
|
||||
uiOSContainerSetMargins(w->content, windowMargin, windowMargin, windowMargin, windowMargin);
|
||||
else
|
||||
uiOSContainerSetMargins(w->content, 0, 0, 0, 0);
|
||||
uiOSContainerUpdate(w->content);
|
||||
}
|
||||
|
||||
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
||||
{
|
||||
struct window *w;
|
||||
RECT adjust;
|
||||
WCHAR *wtitle;
|
||||
BOOL hasMenubarBOOL;
|
||||
HMENU hmenu;
|
||||
|
||||
w = uiNew(struct window);
|
||||
w->onClosing = defaultOnClosing;
|
||||
|
||||
hasMenubarBOOL = FALSE;
|
||||
if (hasMenubar)
|
||||
hasMenubarBOOL = TRUE;
|
||||
|
||||
adjust.left = 0;
|
||||
adjust.top = 0;
|
||||
adjust.right = width;
|
||||
adjust.bottom = height;
|
||||
// TODO does not handle menu wrapping; see http://blogs.msdn.com/b/oldnewthing/archive/2003/09/11/54885.aspx
|
||||
if (AdjustWindowRectEx(&adjust, style, hasMenubarBOOL, exstyle) == 0)
|
||||
logLastError("error getting real window coordinates in uiWindow()");
|
||||
|
||||
wtitle = toUTF16(title);
|
||||
w->hwnd = CreateWindowExW(exstyle,
|
||||
uiWindowClass, wtitle,
|
||||
style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
adjust.right - adjust.left, adjust.bottom - adjust.top,
|
||||
NULL, NULL, hInstance, w);
|
||||
if (w->hwnd == NULL)
|
||||
logLastError("error creating window in uiWindow()");
|
||||
uiFree(wtitle);
|
||||
|
||||
w->content = uiNewOSContainer((uintptr_t) (w->hwnd));
|
||||
|
||||
/*TODO if (hasMenubar) {
|
||||
hmenu = makeMenubar();
|
||||
if (SetMenu(w->hwnd, hmenu) == 0)
|
||||
logLastError("error giving menu to window in uiNewWindow()");
|
||||
}*/
|
||||
|
||||
uiWindow(w)->Destroy = windowDestroy;
|
||||
uiWindow(w)->Handle = windowHandle;
|
||||
uiWindow(w)->Title = windowTitle;
|
||||
uiWindow(w)->SetTitle = windowSetTitle;
|
||||
uiWindow(w)->Show = windowShow;
|
||||
uiWindow(w)->Hide = windowHide;
|
||||
uiWindow(w)->OnClosing = windowOnClosing;
|
||||
uiWindow(w)->SetChild = windowSetChild;
|
||||
uiWindow(w)->Margined = windowMargined;
|
||||
uiWindow(w)->SetMargined = windowSetMargined;
|
||||
|
||||
return uiWindow(w);
|
||||
}
|
Loading…
Reference in New Issue