diff --git a/GNUmakefile b/GNUmakefile deleted file mode 100644 index 80b22ba7..00000000 --- a/GNUmakefile +++ /dev/null @@ -1,67 +0,0 @@ -# 15 april 2015 - -OBJDIR = .obj -OUTBASE = new - -# MAME does this so :/ -ifeq ($(OS),Windows_NT) -OS = windows -endif - -ifndef OS -UNAME = $(shell uname -s) -ifeq ($(UNAME),Darwin) -OS = darwin -else -OS = unix -endif -endif - -CFILES = \ - box.c \ - test.c -HFILES = \ - ui.h \ - uipriv.h \ - ui_$(OS).h \ - $(OS)/uipriv_$(OS).h - -xCFLAGS = \ - -g \ - -Wall -Wextra \ - -Wno-unused-parameter \ - -Wno-switch \ - --std=c99 \ - $(CFLAGS) -xLDFLAGS = \ - -g \ - $(LDFLAGS) - -include $(OS)/GNUmakeinc.mk -xOSCFILES = $(OSCFILES:%=$(OS)/%) -xOSMFILES = $(OSMFILES:%=$(OS)/%) - -OFILES = $(CFILES:%.c=$(OBJDIR)/%.o) \ - $(xOSCFILES:$(OS)/%.c=$(OBJDIR)/%_$(OS).o) \ - $(xOSMFILES:$(OS)/%.m=$(OBJDIR)/%_$(OS).o) - -$(OUT): $(OFILES) - $(CC) -o $(OUT) $(OFILES) $(xLDFLAGS) - -$(OBJDIR)/%.o: %.c $(OBJDIR) $(HFILES) - $(CC) -o $@ -c $< $(xCFLAGS) - -$(OBJDIR)/%_$(OS).o: $(OS)/%.c $(OBJDIR) $(HFILES) - $(CC) -o $@ -c $< $(xCFLAGS) - -$(OBJDIR)/%_$(OS).o: $(OS)/%.m $(OBJDIR) $(HFILES) - $(CC) -o $@ -c $< $(xCFLAGS) - -$(OBJDIR): - mkdir -p $(OBJDIR) - -ui.h: ui.idl - idl2h < ui.idl > ui.h - -clean: - rm -rf $(OBJDIR) ui.h diff --git a/parentplan b/parentplan deleted file mode 100644 index 4146108c..00000000 --- a/parentplan +++ /dev/null @@ -1,56 +0,0 @@ -current situation - -let's say the control hierarchy is -w window - p parent -c stack - d stack - e button - f button - g button -h button -i entry - -w = NewWindow() - p = NewParent(w.Handle) -w.SetChild(c) - p.SetMainControl(c) - c.SetParent(p) - d.SetParent(p) - e.SetParent(p) - f.SetParent(p) - g.SetParent(p) - p.Update() - c.Resize() -c.Add(h) - h.SetParent(p) - p.Update() - c.Resize() -d.Remove(1) - f.SetParent(NULL) - p.Update() - c.Resize() -g.Hide() - p.Update() - c.Resize() -w.SetChild(i) - p.SetMainControl(i) - c.SetParent(NULL) - d.SetParent(NULL) - ... - i.SetParent(p) - ... - p.Update() - i.Resize() -w.SetChild(NULL) - p.SetMainControl(NULL) - i.SetParent(NULL) - p.Update() -w.SetChild(i) - (again) -w.Destroy() - p.Destroy() - i.Destroy() - -TODO -- p.DeferUpdate()/p.EndDeferUpdate() diff --git a/unix/init.c b/unix/init.c deleted file mode 100644 index b10c05b0..00000000 --- a/unix/init.c +++ /dev/null @@ -1,23 +0,0 @@ -// 6 april 2015 -#include "uipriv_unix.h" - -uiInitOptions options; - -const char *uiInit(uiInitOptions *o) -{ - GError *err = NULL; - const char *msg; - - options = *o; - if (gtk_init_with_args(NULL, NULL, NULL, NULL, NULL, &err) == FALSE) { - msg = g_strdup(err->message); - g_error_free(err); - return msg; - } - return NULL; -} - -void uiFreeInitError(const char *err) -{ - g_free((gpointer) err); -} diff --git a/unix/main.c b/unix/main.c deleted file mode 100644 index 10af782e..00000000 --- a/unix/main.c +++ /dev/null @@ -1,23 +0,0 @@ -// 6 april 2015 -#include "uipriv_unix.h" - -// #qo pkg-config: gtk+-3.0 - -void uiMain(void) -{ - gtk_main(); -} - -// gtk_main_quit() may run immediately, or it may wait for other pending events; "it depends" (thanks mclasen in irc.gimp.net/#gtk+) -// PostQuitMessage() on Windows always waits, so we must do so too -// we'll do it by using an idle callback -static gboolean quit(gpointer data) -{ - gtk_main_quit(); - return FALSE; -} - -void uiQuit(void) -{ - gdk_threads_add_idle(quit, NULL); -} diff --git a/unix/menu.c b/unix/menu.c deleted file mode 100644 index 3fc930ff..00000000 --- a/unix/menu.c +++ /dev/null @@ -1,80 +0,0 @@ -// 20 april 2015 -#include "uipriv_unix.h" - -static void appendSeparator(GtkMenuShell *menu) -{ - gtk_menu_shell_append(menu, gtk_separator_menu_item_new()); -} - -static void appendMenuItem(GtkMenuShell *menu, const uiMenuItem *item) -{ - GtkWidget *iw; - - switch (item->Type) { - case uiMenuItemTypeCommand: - iw = gtk_menu_item_new_with_label(item->Name); - gtk_menu_shell_append(menu, iw); - return; - case uiMenuItemTypeCheckbox: - iw = gtk_check_menu_item_new_with_label(item->Name); - gtk_menu_shell_append(menu, iw); - return; - // TODO see if there are stock items for these three - case uiMenuItemTypeQuit: - // TODO verify name - appendSeparator(menu); - iw = gtk_menu_item_new_with_label("Quit"); - gtk_menu_shell_append(menu, iw); - return; - case uiMenuItemTypePreferences: - // TODO verify name - appendSeparator(menu); - iw = gtk_menu_item_new_with_label("Preferences"); - gtk_menu_shell_append(menu, iw); - return; - case uiMenuItemTypeAbout: - // TODO verify name - appendSeparator(menu); - iw = gtk_menu_item_new_with_label("About"); - gtk_menu_shell_append(menu, iw); - return; - case uiMenuItemTypeSeparator: - // TODO verify name - appendSeparator(menu); - return; - } - // TODO complain -} - -static GtkWidget *makeMenu(const char *name, uiMenuItem *items) -{ - GtkWidget *menu; - GtkWidget *submenu; - const uiMenuItem *i; - - menu = gtk_menu_item_new_with_label(name); - submenu = gtk_menu_new(); - for (i = items; i->Type != 0; i++) - appendMenuItem(GTK_MENU_SHELL(submenu), i); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu), submenu); - return menu; -} - -GtkWidget *makeMenubar(void) -{ - GtkWidget *menubar; - const uiMenu *m; - - if (options.Menu == NULL) - complain("asked to give uiWindow a menubar but didn't specify a menu in uiInitOptions"); - - menubar = gtk_menu_bar_new(); - - for (m = options.Menu; m->Name != NULL; m++) - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), makeMenu(m->Name, m->Items)); - - gtk_widget_set_hexpand(menubar, TRUE); - gtk_widget_set_halign(menubar, GTK_ALIGN_FILL); - gtk_widget_show_all(menubar); - return menubar; -} diff --git a/unix/parent.c b/unix/parent.c deleted file mode 100644 index 6a8b8424..00000000 --- a/unix/parent.c +++ /dev/null @@ -1,212 +0,0 @@ -// 13 august 2014 -#include "uipriv_unix.h" - -#define uipParentType (uipParent_get_type()) -#define uipParent(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), uipParentType, uipParent)) -#define uipIsParent(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), uipParentType)) -#define uipParentClass(class) (G_TYPE_CHECK_CLASS_CAST((class), uipParentType, uipParentClass)) -#define uipIsParentClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), uipParent)) -#define uipGetParentClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), uipParentType, uipParentClass)) - -typedef struct uipParent uipParent; -typedef struct uipParentClass uipParentClass; - -struct uipParent { - GtkContainer parent_instance; - uiControl *mainControl; - GPtrArray *children; // for forall() - intmax_t marginLeft; - intmax_t marginTop; - intmax_t marginRight; - intmax_t marginBottom; - gboolean canDestroy; -}; - -struct uipParentClass { - GtkContainerClass parent_class; -}; - -G_DEFINE_TYPE(uipParent, uipParent, GTK_TYPE_CONTAINER) - -static void uipParent_init(uipParent *p) -{ - if (options.debugLogAllocations) - fprintf(stderr, "%p alloc uipParent\n", p); - p->children = g_ptr_array_new(); - gtk_widget_set_has_window(GTK_WIDGET(p), FALSE); -} - -// instead of having GtkContainer itself unref all our controls, we'll run our own uiControlDestroy() functions for child, which will do that and more -// we still chain up because we need to, but by that point there will be no children for GtkContainer to free -static void uipParent_dispose(GObject *obj) -{ - uipParent *p = uipParent(obj); - - // don't free mainControl here; that should have been done by uiParentDestroy() - if (!p->canDestroy) - complain("attempt to dispose uiParent with uipParent at %p before uiParentDestroy()", p); - if (p->children != NULL) { - if (p->children->len != 0) - complain("disposing uiParent with uipParent at %p while there are still children", p); - g_ptr_array_unref(p->children); - p->children = NULL; - } - G_OBJECT_CLASS(uipParent_parent_class)->dispose(obj); -} - -static void uipParent_finalize(GObject *obj) -{ - uipParent *p = uipParent(obj); - - if (!p->canDestroy) - complain("attempt to finalize uiParent with uipParent at %p before uiParentDestroy()", p); - G_OBJECT_CLASS(uipParent_parent_class)->finalize(obj); - if (options.debugLogAllocations) - fprintf(stderr, "%p free\n", obj); -} - -static void uipParent_add(GtkContainer *container, GtkWidget *widget) -{ - uipParent *p = uipParent(container); - - gtk_widget_set_parent(widget, GTK_WIDGET(p)); - if (p->children != NULL) - g_ptr_array_add(p->children, widget); -} - -static void uipParent_remove(GtkContainer *container, GtkWidget *widget) -{ - uipParent *p = uipParent(container); - - gtk_widget_unparent(widget); - if (p->children != NULL) - if (g_ptr_array_remove(p->children, widget) == FALSE) - complain("widget %p not found in uipParent gtk_container_remove()", widget); -} - -#define gtkXPadding 12 -#define gtkYPadding 6 - -static void uipParent_size_allocate(GtkWidget *widget, GtkAllocation *allocation) -{ - uipParent *p = uipParent(widget); - uiSizing d; - intmax_t x, y, width, height; - - gtk_widget_set_allocation(GTK_WIDGET(p), allocation); - if (p->mainControl == NULL) - return; - x = allocation->x + p->marginLeft; - y = allocation->y + p->marginTop; - width = allocation->width - (p->marginLeft + p->marginRight); - height = allocation->height - (p->marginTop + p->marginBottom); - d.xPadding = gtkXPadding; - d.yPadding = gtkYPadding; - uiControlResize(p->mainControl, x, y, width, height, &d); -} - -struct forall { - GtkCallback callback; - gpointer data; -}; - -static void doforall(gpointer obj, gpointer data) -{ - struct forall *s = (struct forall *) data; - - (*(s->callback))(GTK_WIDGET(obj), s->data); -} - -static void uipParent_forall(GtkContainer *container, gboolean includeInternals, GtkCallback callback, gpointer data) -{ - uipParent *p = uipParent(container); - struct forall s; - - s.callback = callback; - s.data = data; - if (p->children != NULL) - g_ptr_array_foreach(p->children, doforall, &s); -} - -static void uipParent_class_init(uipParentClass *class) -{ - G_OBJECT_CLASS(class)->dispose = uipParent_dispose; - G_OBJECT_CLASS(class)->finalize = uipParent_finalize; - GTK_WIDGET_CLASS(class)->size_allocate = uipParent_size_allocate; - GTK_CONTAINER_CLASS(class)->add = uipParent_add; - GTK_CONTAINER_CLASS(class)->remove = uipParent_remove; - GTK_CONTAINER_CLASS(class)->forall = uipParent_forall; -} - -// TODO convert other methods of this + other backends to pp arg p instance variable - -static void parentDestroy(uiParent *pp) -{ - uipParent *p = uipParent(pp->Internal); - - // first, destroy the main control - if (p->mainControl != NULL) { - // we have to do this before we can destroy controls - uiControlSetParent(p->mainControl, NULL); - uiControlDestroy(p->mainControl); - p->mainControl = NULL; - } - // now we can mark the parent as ready to be destroyed - p->canDestroy = TRUE; - // finally, destroy the parent - gtk_widget_destroy(GTK_WIDGET(p)); - // and free ourselves - uiFree(pp); -} - -static uintptr_t parentHandle(uiParent *p) -{ - uipParent *pp = uipParent(p->Internal); - - return (uintptr_t) pp; -} - -static void parentSetMainControl(uiParent *pp, uiControl *mainControl) -{ - uipParent *p = uipParent(pp->Internal); - - if (p->mainControl != NULL) - uiControlSetParent(p->mainControl, NULL); - p->mainControl = mainControl; - if (p->mainControl != NULL) - uiControlSetParent(p->mainControl, pp); -} - -static void parentSetMargins(uiParent *p, intmax_t left, intmax_t top, intmax_t right, intmax_t bottom) -{ - uipParent *pp = uipParent(p->Internal); - - pp->marginLeft = left; - pp->marginTop = top; - pp->marginRight = right; - pp->marginBottom = bottom; -} - -static void parentUpdate(uiParent *p) -{ - uipParent *pp = uipParent(p->Internal); - - gtk_widget_queue_resize(GTK_WIDGET(pp)); -} - -uiParent *uiNewParent(uintptr_t osParent) -{ - uiParent *p; - - p = uiNew(uiParent); - p->Internal = g_object_new(uipParentType, NULL); - p->Destroy = parentDestroy; - p->Handle = parentHandle; - p->SetMainControl = parentSetMainControl; - p->SetMargins = parentSetMargins; - p->Update = parentUpdate; - gtk_container_add(GTK_CONTAINER(osParent), GTK_WIDGET(p->Internal)); - // and make it visible by default - gtk_widget_show_all(GTK_WIDGET(p->Internal)); - return p; -} diff --git a/windows/init.c b/windows/init.c deleted file mode 100644 index 99039345..00000000 --- a/windows/init.c +++ /dev/null @@ -1,112 +0,0 @@ -// 6 april 2015 -#include "uipriv_windows.h" - -HINSTANCE hInstance; -int nCmdShow; - -HFONT hMessageFont; - -HBRUSH hollowBrush; - -struct uiInitError { - char *msg; - char failbuf[256]; -}; - -#define initErrorFormat L"error %s: %s%sGetLastError() == %I32u%s" -#define initErrorArgs wmessage, sysmsg, beforele, le, afterle - -static const char *loadLastError(const char *message) -{ - WCHAR *sysmsg; - BOOL hassysmsg; - WCHAR *beforele; - WCHAR *afterle; - int n; - WCHAR *wmessage; - WCHAR *wstr; - const char *str; - DWORD le; - - le = GetLastError(); - if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, le, 0, (LPWSTR) (&sysmsg), 0, NULL) != 0) { - hassysmsg = TRUE; - beforele = L" ("; - afterle = L")"; - } else { - hassysmsg = FALSE; - sysmsg = L""; - beforele = L""; - afterle = L""; - } - wmessage = toUTF16(message); - n = _scwprintf(initErrorFormat, initErrorArgs); - wstr = (WCHAR *) uiAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); - snwprintf(wstr, n + 1, initErrorFormat, initErrorArgs); - str = toUTF8(wstr); - uiFree(wstr); - if (hassysmsg) - if (LocalFree(sysmsg) != NULL) - logLastError("error freeing system message in loadLastError()"); - uiFree(wmessage); - return str; -} - -uiInitOptions options; - -const char *uiInit(uiInitOptions *o) -{ - STARTUPINFOW si; - const char *ce; - HICON hDefaultIcon; - HCURSOR hDefaultCursor; - NONCLIENTMETRICSW ncm; - - options = *o; - - hInstance = GetModuleHandle(NULL); - if (hInstance == NULL) - return loadLastError("getting program HINSTANCE"); - - nCmdShow = SW_SHOWDEFAULT; - GetStartupInfoW(&si); - if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0) - nCmdShow = si.wShowWindow; - - ce = initCommonControls(); - if (ce != NULL) - return loadLastError(ce); - - hDefaultIcon = LoadIconW(NULL, IDI_APPLICATION); - if (hDefaultIcon == NULL) - return loadLastError("loading default icon for window classes"); - hDefaultCursor = LoadCursorW(NULL, IDC_ARROW); - if (hDefaultCursor == NULL) - return loadLastError("loading default cursor for window classes"); - - if (registerWindowClass(hDefaultIcon, hDefaultCursor) == 0) - return loadLastError("registering uiWindow window class"); - - ZeroMemory(&ncm, sizeof (NONCLIENTMETRICSW)); - ncm.cbSize = sizeof (NONCLIENTMETRICSW); - if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof (NONCLIENTMETRICSW), &ncm, sizeof (NONCLIENTMETRICSW)) == 0) - return loadLastError("getting default fonts"); - hMessageFont = CreateFontIndirectW(&(ncm.lfMessageFont)); - if (hMessageFont == NULL) - return loadLastError("loading default messagebox font; this is the default UI font"); - - ce = initParent(hDefaultIcon, hDefaultCursor); - if (ce != NULL) - return loadLastError(ce); - - hollowBrush = (HBRUSH) GetStockObject(HOLLOW_BRUSH); - if (hollowBrush == NULL) - return loadLastError("getting hollow brush"); - - return NULL; -} - -void uiFreeInitError(const char *err) -{ - uiFree((void *) err); -} diff --git a/windows/main.c b/windows/main.c deleted file mode 100644 index bd973b72..00000000 --- a/windows/main.c +++ /dev/null @@ -1,56 +0,0 @@ -// 6 april 2015 -#include "uipriv_windows.h" - -// #qo LDFLAGS: -luser32 -lkernel32 -lgdi32 -luxtheme -lmsimg32 -lcomdlg32 -lole32 -loleaut32 -loleacc -luuid - -static void uimsgloop_else(MSG *msg) -{ - TranslateMessage(msg); - DispatchMessage(msg); -} - -void uiMain(void) -{ - MSG msg; - int res; - HWND active, focus; - - for (;;) { - SetLastError(0); - res = GetMessageW(&msg, NULL, 0, 0); - if (res < 0) - logLastError("error calling GetMessage() in uiMain()"); - if (res == 0) // WM_QUIT - break; - active = GetActiveWindow(); - if (active == NULL) { - uimsgloop_else(&msg); - continue; - } - - // bit of logic involved here: - // we don't want dialog messages passed into Areas, so we don't call IsDialogMessageW() there - // as for Tabs, we can't have both WS_TABSTOP and WS_EX_CONTROLPARENT set at the same time, so we hotswap the two styles to get the behavior we want - focus = GetFocus(); - if (focus != NULL) { -/*TODO switch (windowClassOf(focus, areaWindowClass, WC_TABCONTROLW, NULL)) { - case 0: // areaWindowClass - uimsgloop_area(active, focus, &msg); - continue; - case 1: // WC_TABCONTROLW - uimsgloop_tab(active, focus, &msg); - continue; - } - // else fall through -*/ } - - if (IsDialogMessage(active, &msg) != 0) - continue; - uimsgloop_else(&msg); - } -} - -void uiQuit(void) -{ - PostQuitMessage(0); -} diff --git a/windows/menu.c b/windows/menu.c deleted file mode 100644 index a7ab4f5c..00000000 --- a/windows/menu.c +++ /dev/null @@ -1,124 +0,0 @@ -// 20 april 2015 -#include "uipriv_windows.h" - -static void appendSeparator(HMENU menu) -{ - if (AppendMenuW(menu, MF_SEPARATOR, 0, NULL) == 0) - logLastError("error appending separator in appendSeparator()"); -} - -static void appendTextItem(HMENU menu, const char *text, UINT_PTR *id) -{ - WCHAR *wtext; - - wtext = toUTF16(text); - if (AppendMenuW(menu, MF_STRING, *id, wtext) == 0) - logLastError("error appending menu item in appendTextItem()"); - uiFree(wtext); - (*id)++; -} - -static void appendMenuItem(HMENU menu, const uiMenuItem *item, UINT_PTR *id) -{ - switch (item->Type) { - case uiMenuItemTypeCommand: - case uiMenuItemTypeCheckbox: - appendTextItem(menu, item->Name, id); - return; - // TODO see if there are stock items for these three - case uiMenuItemTypeQuit: - // TODO verify name - appendSeparator(menu); - appendTextItem(menu, "Quit", id); - return; - case uiMenuItemTypePreferences: - // TODO verify name - appendSeparator(menu); - appendTextItem(menu, "Preferences", id); - return; - case uiMenuItemTypeAbout: - // TODO verify name - appendSeparator(menu); - appendTextItem(menu, "About", id); - return; - case uiMenuItemTypeSeparator: - // TODO verify name - appendSeparator(menu); - return; - } - // TODO complain -} - - -static HMENU makeMenu(uiMenuItem *items, UINT_PTR *id) -{ - HMENU menu; - const uiMenuItem *i; - - menu = CreatePopupMenu(); - if (menu == NULL) - logLastError("error creating menu in makeMenu()"); - for (i = items; i->Type != 0; i++) - appendMenuItem(menu, i, id); - return menu; -} - -HMENU makeMenubar(void) -{ - HMENU menubar; - const uiMenu *m; - WCHAR *wname; - HMENU menu; - UINT_PTR id; - - if (options.Menu == NULL) - complain("asked to give uiWindow a menubar but didn't specify a menu in uiInitOptions"); - - menubar = CreateMenu(); - if (menubar == NULL) - logLastError("error creating menubar in makeMenubar()"); - - id = 100; // start at a safe number - for (m = options.Menu; m->Name != NULL; m++) { - wname = toUTF16(m->Name); - menu = makeMenu(m->Items, &id); - if (AppendMenuW(menubar, MF_POPUP | MF_STRING, (UINT_PTR) menu, wname) == 0) - logLastError("error appending menu to menubar in makeMenubar()"); - uiFree(wname); - } - - return menubar; -} - -// this is slow, but it will do for now -// TODO investigate faster options - -static const uiMenuItem *lookupID(const uiMenuItem *items, UINT_PTR *cur, UINT_PTR id) -{ - const uiMenuItem *i; - - for (i = items; i->Type != 0; i++) { - if (i->Type == uiMenuItemTypeSeparator) - continue; - if (*cur == id) - return i; - (*cur)++; - } - return NULL; -} - -const uiMenuItem *menuIDToItem(UINT_PTR id) -{ - UINT_PTR cur; - const uiMenu *m; - const uiMenuItem *item; - - cur = 100; - for (m = options.Menu; m->Name != NULL; m++) { - item = lookupID(m->Items, &cur, id); - if (item != NULL) - return item; - } - // TODO complain - return NULL; -} diff --git a/windows/parent.c b/windows/parent.c deleted file mode 100644 index 1f81777d..00000000 --- a/windows/parent.c +++ /dev/null @@ -1,309 +0,0 @@ -// 10 april 2015 -#include "uipriv_windows.h" - -// All controls in package ui are children of a window of this class. -// This keeps everything together, makes hiding controls en masse (tab page switching, for instance) easy, and makes the overall design cleaner. -// In addition, controls that are first created or don't have a parent are considered children of the "initial parent", which is also of this class. -// This parent is invisible, disabled, and should not be interacted with. - -// TODOs -// - wiith CTLCOLOR handler: [12:24] There's flickering between tabs -// - with CTLCOLOR handler: [12:24] And setting the button text blanked out the entire GUI until I ran my mouse over the elements / [12:25] https://dl.dropboxusercontent.com/u/15144168/GUI%20stuff.png / [12:41] https://dl.dropboxusercontent.com/u/15144168/stack.png here have another screenshot -// - I get this too - -#define uiParentClass L"uiParentClass" - -HWND initialParent; - -static void paintControlBackground(HWND hwnd, HDC dc) -{ - HWND parent; - RECT r; - POINT pOrig; - int class; - DWORD le; - - parent = hwnd; - for (;;) { - parent = GetParent(parent); - if (parent == NULL) - logLastError("error getting parent control of control in paintControlBackground()"); - // wine sends these messages early, yay... - if (parent == initialParent) - return; - // skip groupboxes; they're (supposed to be) transparent - // skip uiParents for reasons described below - class = windowClassOf(parent, L"button", uiParentClass, NULL); - if (class != 0 && class != 1) - break; - } - if (GetWindowRect(hwnd, &r) == 0) - logLastError("error getting control's window rect in paintControlBackground()"); - // the above is a window rect in screen coordinates; convert to parent coordinates - SetLastError(0); - if (MapWindowRect(NULL, parent, &r) == 0) { - le = GetLastError(); - SetLastError(le); // just to be safe - if (le != 0) - logLastError("error getting client origin of control in paintControlBackground()"); - } - if (SetWindowOrgEx(dc, r.left, r.top, &pOrig) == 0) - logLastError("error moving window origin in paintControlBackground()"); - SendMessageW(parent, WM_PRINTCLIENT, (WPARAM) dc, PRF_CLIENT); - if (SetWindowOrgEx(dc, pOrig.x, pOrig.y, NULL) == 0) - logLastError("error resetting window origin in paintControlBackground()"); -} - -// from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing and https://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx -// this X value is really only for buttons but I don't see a better one :/ -#define winXPadding 4 -#define winYPadding 4 - -static void resize(uiControl *control, HWND parent, RECT r, RECT margin) -{ - uiSizing d; - uiSizingSys sys; - HDC dc; - HFONT prevfont; - TEXTMETRICW tm; - SIZE size; - - size.cx = 0; - size.cy = 0; - ZeroMemory(&tm, sizeof (TEXTMETRICW)); - dc = GetDC(parent); - if (dc == NULL) - logLastError("error getting DC in resize()"); - prevfont = (HFONT) SelectObject(dc, hMessageFont); - if (prevfont == NULL) - logLastError("error loading control font into device context in resize()"); - if (GetTextMetricsW(dc, &tm) == 0) - logLastError("error getting text metrics in resize()"); - if (GetTextExtentPoint32W(dc, L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &size) == 0) - logLastError("error getting text extent point in resize()"); - sys.baseX = (int) ((size.cx / 26 + 1) / 2); - sys.baseY = (int) tm.tmHeight; - sys.internalLeading = tm.tmInternalLeading; - if (SelectObject(dc, prevfont) != hMessageFont) - logLastError("error restoring previous font into device context in resize()"); - if (ReleaseDC(parent, dc) == 0) - logLastError("error releasing DC in resize()"); - r.left += uiDlgUnitsToX(margin.left, sys.baseX); - r.top += uiDlgUnitsToY(margin.top, sys.baseY); - r.right -= uiDlgUnitsToX(margin.right, sys.baseX); - r.bottom -= uiDlgUnitsToY(margin.bottom, sys.baseY); - d.xPadding = uiDlgUnitsToX(winXPadding, sys.baseX); - d.yPadding = uiDlgUnitsToY(winYPadding, sys.baseY); - d.sys = &sys; - uiControlResize(control, r.left, r.top, r.right - r.left, r.bottom - r.top, &d); -} - -// TODO make this a uiParent directly -struct parent { - HWND hwnd; - uiControl *mainControl; - intmax_t marginLeft; - intmax_t marginTop; - intmax_t marginRight; - intmax_t marginBottom; - BOOL canDestroy; -}; - -static LRESULT CALLBACK parentWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - uiParent *p; - struct parent *pp; - CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; - HWND control; - NMHDR *nm = (NMHDR *) lParam; - WINDOWPOS *wp = (WINDOWPOS *) lParam; - RECT r, margin; - HDC dc; - PAINTSTRUCT ps; - - // these must always be executed, even on the initial parent - // why? http://blogs.msdn.com/b/oldnewthing/archive/2010/03/16/9979112.aspx - switch (uMsg) { - case WM_COMMAND: - // bounce back to the control in question - // except if to the initial parent, in which case act as if the message was ignored - control = (HWND) lParam; - if (control != NULL && IsChild(initialParent, control) == 0) - return SendMessageW(control, msgCOMMAND, wParam, lParam); - return DefWindowProcW(hwnd, uMsg, wParam, lParam); - case WM_NOTIFY: - // same as WM_COMMAND - control = nm->hwndFrom; - if (control != NULL && IsChild(initialParent, control) == 0) - return SendMessageW(control, msgNOTIFY, wParam, lParam); - return DefWindowProcW(hwnd, uMsg, wParam, lParam); - case WM_CTLCOLORSTATIC: - case WM_CTLCOLORBTN: -/*TODO // read-only TextFields and Textboxes are exempt - // this is because read-only edit controls count under WM_CTLCOLORSTATIC - if (windowClassOf((HWND) lParam, L"edit", NULL) == 0) - if (textfieldReadOnly((HWND) lParam)) - return DefWindowProcW(hwnd, uMsg, wParam, lParam); -*/ if (SetBkMode((HDC) wParam, TRANSPARENT) == 0) - logLastError("error setting transparent background mode to controls in parentWndProc()"); - paintControlBackground((HWND) lParam, (HDC) wParam); - return (LRESULT) hollowBrush; - } - - // these are only executed on actual parents - p = (uiParent *) GetWindowLongPtrW(hwnd, GWLP_USERDATA); - if (p == NULL) { - if (uMsg == WM_NCCREATE) { - p = (uiParent *) (cs->lpCreateParams); - // this will be NULL for the initial parent; that's what we want - SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) p); - // fall through to DefWindowProcW() - } - // this is the return the initial parent will always use - return DefWindowProcW(hwnd, uMsg, wParam, lParam); - } - pp = (struct parent *) (p->Internal); - switch (uMsg) { - case WM_NCDESTROY: - if (!pp->canDestroy) - complain("attempt to destroy uiParent at %p before uiParentDestroy()", p); - uiFree(p->Internal); - uiFree(p); - break; // fall through to DefWindowPocW() - case WM_WINDOWPOSCHANGED: - if ((wp->flags & SWP_NOSIZE) != 0) - break; - // fall through - case msgUpdateChild: - if (pp->mainControl == NULL) - break; - if (GetClientRect(pp->hwnd, &r) == 0) - logLastError("error getting client rect for resize in parentWndProc()"); - margin.left = pp->marginLeft; - margin.top = pp->marginTop; - margin.right = pp->marginRight; - margin.bottom = pp->marginBottom; - resize(pp->mainControl, pp->hwnd, r, margin); - return 0; - case WM_PAINT: - dc = BeginPaint(pp->hwnd, &ps); - if (dc == NULL) - logLastError("TODO write this"); - paintControlBackground(pp->hwnd, dc); - EndPaint(pp->hwnd, &ps); - return 0; - // don't implement WM_PRINTCLIENT - // if paintControlBackground() asks us to draw our background, we have to ask our parent to draw its, which causes weird origin issues - // instead, we skip uiParents in paintControlBackground() - } - - return DefWindowProcW(hwnd, uMsg, wParam, lParam); -} - -const char *initParent(HICON hDefaultIcon, HCURSOR hDefaultCursor) -{ - WNDCLASSW wc; - - ZeroMemory(&wc, sizeof (WNDCLASSW)); - wc.lpszClassName = uiParentClass; - wc.lpfnWndProc = parentWndProc; - wc.hInstance = hInstance; - wc.hIcon = hDefaultIcon; - wc.hCursor = hDefaultCursor; - wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); - if (RegisterClassW(&wc) == 0) - return "registering parent window class"; - - initialParent = CreateWindowExW(0, - uiParentClass, L"", - WS_OVERLAPPEDWINDOW, - 0, 0, - 100, 100, - NULL, NULL, hInstance, NULL); - if (initialParent == NULL) - return "creating initial parent window"; - - // just to be safe, disable the initial parent so it can't be interacted with accidentally - // if this causes issues for our controls, we can remove it - EnableWindow(initialParent, FALSE); - return NULL; -} - -static void parentDestroy(uiParent *pp) -{ - struct parent *p = (struct parent *) (pp->Internal); - - // first destroy the main control, if any - if (p->mainControl != NULL) { - // we have to do this before we can destroy controls - uiControlSetParent(p->mainControl, NULL); - uiControlDestroy(p->mainControl); - p->mainControl = NULL; - } - // then mark that we are ready to destroy - p->canDestroy = TRUE; - // and finally destroy - if (DestroyWindow(p->hwnd) == 0) - logLastError("error destroying uiParent window in parentDestroy()"); -} - -static uintptr_t parentHandle(uiParent *p) -{ - struct parent *pp = (struct parent *) (p->Internal); - - return (uintptr_t) (pp->hwnd); -} - -static void parentSetMainControl(uiParent *pp, uiControl *mainControl) -{ - struct parent *p = (struct parent *) (pp->Internal); - - if (p->mainControl != NULL) - uiControlSetParent(p->mainControl, NULL); - p->mainControl = mainControl; - if (p->mainControl != NULL) - uiControlSetParent(p->mainControl, pp); -} - -static void parentSetMargins(uiParent *p, intmax_t left, intmax_t top, intmax_t right, intmax_t bottom) -{ - struct parent *pp = (struct parent *) (p->Internal); - - pp->marginLeft = left; - pp->marginTop = top; - pp->marginRight = right; - pp->marginBottom = bottom; -} - -static void parentUpdate(uiParent *p) -{ - struct parent *pp = (struct parent *) (p->Internal); - - SendMessageW(pp->hwnd, msgUpdateChild, 0, 0); -} - -uiParent *uiNewParent(uintptr_t osParent) -{ - uiParent *p; - struct parent *pp; - - p = uiNew(uiParent); - p->Internal = uiNew(struct parent); // set now in case the parent class window procedure uses it - pp = (struct parent *) (p->Internal); - pp->hwnd = CreateWindowExW(0, - uiParentClass, L"", - WS_CHILD | WS_VISIBLE, - 0, 0, - 0, 0, - (HWND) osParent, NULL, hInstance, p); - if (pp->hwnd == NULL) - logLastError("error creating uiParent window in uiNewParent()"); - - p->Destroy = parentDestroy; - p->Handle = parentHandle; - p->SetMainControl = parentSetMainControl; - p->SetMargins = parentSetMargins; - p->Update = parentUpdate; - - return p; -}