Split the rewrite into a new repository.

This commit is contained in:
Pietro Gagliardi 2015-04-16 20:33:28 -04:00
parent de9d72299f
commit e34c561ed5
58 changed files with 4 additions and 5298 deletions

View File

@ -1,5 +1,9 @@
# ui: platform-native GUI library for Go
## NOTE
ui is currently being rewritten for stability. The guts of the package will now be in C. For progress updates, see [the new repo for the C backend](https://github.com/andlabs/libui/).
## Feature requests wanted! (Really; IDK what to add next!)
This is a library that aims to provide simple GUI software development in Go. It runs on/requires:

View File

@ -1,62 +0,0 @@
# 15 april 2015
OBJDIR = .obj
# 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 = \
stack.c \
test.c
HFILES = \
ui.h \
uipriv.h \
ui_$(OS).h \
$(OS)/uipriv_$(OS).h
xCFLAGS = \
-g \
-Wall -Wextra \
-Wno-unused-parameter \
--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

View File

@ -1,27 +0,0 @@
- change all private names to uipXxxx
- make it so Windows API calls that do logLastError(), etc. abort whatever they're doing and not try to continue, just like wintable
- figure out what to cleanup in darwin terminate:
- delegate
- deleted objects view
- assign control IDs on windows
- GWL(P)_ID
- related? [12:25] <ZeroOne> And the blue outline on those buttons [ALL clicked buttons on Windows 7] won't go away
- I get this too
- make sure all terminology is consistent
- 32-bit Mac OS X support (requires lots of code changes)
- add a test for hidden controls when a window is shown
- SWP_NOCOPYBITS (or was it WS_CLIPCHILDREN?)
- buttons not in tab get drawover issues
- buttons in tab without transparent drawing code get copied into the label when stack shown and rehidden
- see if we can clean up the backends
- rename all method implementations to typeMethod
- especially clean up the Darwin backend
ultimately:
- make everything vtable-based
- provide macros for the vtables
- figure out where updateParent() plays into this
- figure out what to do about custom containers
- rename container to parent?
- make the code flow of all platforms fully symmetrical
- add some sort of runtime type checking

View File

@ -1,19 +0,0 @@
OSMFILES = \
alloc.m \
button.m \
checkbox.m \
entry.m \
init.m \
label.m \
main.m \
newcontrol.m \
parent.m \
tab.m \
text.m \
util.m \
window.m
xCFLAGS += -mmacosx-version-min=10.7 -DMACOSX_DEPLOYMENT_TARGET=10.7
xLDFLAGS += -mmacosx-version-min=10.7 -lobjc -framework Foundation -framework AppKit
OUT = new

View File

@ -1,44 +0,0 @@
// 4 december 2014
#import <stdio.h>
#import "uipriv_darwin.h"
void *uiAlloc(size_t size, const char *type)
{
void *out;
out = malloc(size);
if (out == NULL) {
fprintf(stderr, "memory exhausted in uiAlloc() allocating %s\n", type);
abort();
}
memset(out, 0, size);
if (options.debugLogAllocations)
fprintf(stderr, "%p alloc %s\n", out, type);
return out;
}
void *uiRealloc(void *p, size_t size, const char *type)
{
void *out;
if (p == NULL)
return uiAlloc(size, type);
out = realloc(p, size);
if (out == NULL) {
fprintf(stderr, "memory exhausted in uiRealloc() reallocating %s\n", type);
abort();
}
// TODO zero the extra memory
if (options.debugLogAllocations)
fprintf(stderr, "%p realloc %p\n", p, out);
return out;
}
void uiFree(void *p)
{
if (p == NULL)
return;
free(p);
if (options.debugLogAllocations)
fprintf(stderr, "%p free\n", p);
}

View File

@ -1,86 +0,0 @@
// 7 april 2015
#import "uipriv_darwin.h"
@interface uiNSButton : NSButton
@property uiButton *uiB;
@property void (*uiOnClicked)(uiButton *, void *);
@property void *uiOnClickedData;
@end
@implementation uiNSButton
- (void)viewDidMoveToSuperview
{
if (uiDarwinControlFreeWhenAppropriate(uiControl(self.uiB), [self superview])) {
[self setTarget:nil];
self.uiB = NULL;
}
[super viewDidMoveToSuperview];
}
- (IBAction)uiButtonClicked:(id)sender
{
(*(self.uiOnClicked))(self.uiB, self.uiOnClickedData);
}
@end
static void defaultOnClicked(uiButton *c, void *data)
{
// do nothing
}
static char *buttonText(uiButton *bb)
{
uiNSButton *b;
b = (uiNSButton *) uiControlHandle(uiControl(bb));
return uiDarwinNSStringToText([b title]);
}
static void buttonSetText(uiButton *bb, const char *text)
{
uiNSButton *b;
b = (uiNSButton *) uiControlHandle(uiControl(bb));
[b setTitle:toNSString(text)];
}
static void buttonOnClicked(uiButton *bb, void (*f)(uiButton *, void *), void *data)
{
uiNSButton *b;
b = (uiNSButton *) uiControlHandle(uiControl(bb));
b.uiOnClicked = f;
b.uiOnClickedData = data;
}
uiButton *uiNewButton(const char *text)
{
uiButton *b;
uiNSButton *bb;
b = uiNew(uiButton);
uiDarwinNewControl(uiControl(b), [uiNSButton class], NO, NO);
bb = (uiNSButton *) uiControlHandle(uiControl(b));
[bb setTitle:toNSString(text)];
[bb setButtonType:NSMomentaryPushInButton];
[bb setBordered:YES];
[bb setBezelStyle:NSRoundedBezelStyle];
setStandardControlFont((NSControl *) bb);
[bb setTarget:bb];
[bb setAction:@selector(uiButtonClicked:)];
bb.uiOnClicked = defaultOnClicked;
uiButton(b)->Text = buttonText;
uiButton(b)->SetText = buttonSetText;
uiButton(b)->OnClicked = buttonOnClicked;
bb.uiB = b;
return bb.uiB;
}

View File

@ -1,107 +0,0 @@
// 7 april 2015
#import "uipriv_darwin.h"
@interface uiCheckboxNSButton : NSButton
@property uiCheckbox *uiC;
@property void (*uiOnToggled)(uiCheckbox *, void *);
@property void *uiOnToggledData;
@end
@implementation uiCheckboxNSButton
- (void)viewDidMoveToSuperview
{
if (uiDarwinControlFreeWhenAppropriate(uiControl(self.uiC), [self superview])) {
[self setTarget:nil];
self.uiC = NULL;
}
[super viewDidMoveToSuperview];
}
- (IBAction)uiCheckboxToggled:(id)sender
{
(*(self.uiOnToggled))(self.uiC, self.uiOnToggledData);
}
@end
static void defaultOnToggled(uiCheckbox *c, void *data)
{
// do nothing
}
static char *checkboxText(uiCheckbox *c)
{
uiCheckboxNSButton *cc;
cc = (uiCheckboxNSButton *) uiControlHandle(uiControl(c));
return uiDarwinNSStringToText([cc title]);
}
static void checkboxSetText(uiCheckbox *c, const char *text)
{
uiCheckboxNSButton *cc;
cc = (uiCheckboxNSButton *) uiControlHandle(uiControl(c));
[cc setTitle:toNSString(text)];
}
static void checkboxOnToggled(uiCheckbox *c, void (*f)(uiCheckbox *, void *), void *data)
{
uiCheckboxNSButton *cc;
cc = (uiCheckboxNSButton *) uiControlHandle(uiControl(c));
cc.uiOnToggled = f;
cc.uiOnToggledData = data;
}
static int checkboxChecked(uiCheckbox *c)
{
uiCheckboxNSButton *cc;
cc = (uiCheckboxNSButton *) uiControlHandle(uiControl(c));
return [cc state] == NSOnState;
}
static void checkboxSetChecked(uiCheckbox *c, int checked)
{
uiCheckboxNSButton *cc;
NSInteger state;
cc = (uiCheckboxNSButton *) uiControlHandle(uiControl(c));
state = NSOnState;
if (!checked)
state = NSOffState;
[cc setState:state];
}
uiCheckbox *uiNewCheckbox(const char *text)
{
uiCheckbox *c;
uiCheckboxNSButton *cc;
c = uiNew(uiCheckbox);
uiDarwinNewControl(uiControl(c), [uiCheckboxNSButton class], NO, NO);
cc = (uiCheckboxNSButton *) uiControlHandle(uiControl(c));
[cc setTitle:toNSString(text)];
[cc setButtonType:NSSwitchButton];
[cc setBordered:NO];
setStandardControlFont((NSControl *) cc);
[cc setTarget:cc];
[cc setAction:@selector(uiCheckboxToggled:)];
cc.uiOnToggled = defaultOnToggled;
uiCheckbox(c)->Text = checkboxText;
uiCheckbox(c)->SetText = checkboxSetText;
uiCheckbox(c)->OnToggled = checkboxOnToggled;
uiCheckbox(c)->Checked = checkboxChecked;
uiCheckbox(c)->SetChecked = checkboxSetChecked;
cc.uiC = c;
return cc.uiC;
}

View File

@ -1,73 +0,0 @@
// 9 april 2015
#import "uipriv_darwin.h"
@interface uiNSTextField : NSTextField
@property uiEntry *uiE;
@end
@implementation uiNSTextField
- (void)viewDidMoveToSuperview
{
if (uiDarwinControlFreeWhenAppropriate(uiControl(self.uiE), [self superview])) {
[self setTarget:nil];
self.uiE = NULL;
}
[super viewDidMoveToSuperview];
}
@end
static char *entryText(uiEntry *e)
{
uiNSTextField *t;
t = (uiNSTextField *) uiControlHandle(uiControl(e));
return uiDarwinNSStringToText([t stringValue]);
}
static void entrySetText(uiEntry *e, const char *text)
{
uiNSTextField *t;
t = (uiNSTextField *) uiControlHandle(uiControl(e));
[t setStringValue:toNSString(text)];
}
// TOOD move elsewhere
// these are based on interface builder defaults; my comments in the old code weren't very good so I don't really know what talked about what, sorry :/
void finishNewTextField(NSTextField *t, BOOL isEntry)
{
setStandardControlFont((id) t);
// THE ORDER OF THESE CALLS IS IMPORTANT; CHANGE IT AND THE BORDERS WILL DISAPPEAR
[t setBordered:NO];
[t setBezelStyle:NSTextFieldSquareBezel];
[t setBezeled:isEntry];
// we don't need to worry about substitutions/autocorrect here; see window_darwin.m for details
[[t cell] setLineBreakMode:NSLineBreakByClipping];
[[t cell] setScrollable:YES];
}
uiEntry *uiNewEntry(void)
{
uiEntry *e;
uiNSTextField *t;
e = uiNew(uiEntry);
uiDarwinNewControl(uiControl(e), [uiNSTextField class], NO, NO);
t = (uiNSTextField *) uiControlHandle(uiControl(e));
[t setSelectable:YES]; // otherwise the setting is masked by the editable default of YES
finishNewTextField((NSTextField *) t, YES);
uiEntry(e)->Text = entryText;
uiEntry(e)->SetText = entrySetText;
t.uiE = e;
return t.uiE;
}

View File

@ -1,67 +0,0 @@
// 6 april 2015
#import "uipriv_darwin.h"
@interface uiApplication : NSApplication
@end
@implementation uiApplication
// hey look! we're overriding terminate:!
// we're going to make sure we can go back to main() whether Cocoa likes it or not!
// and just how are we going to do that, hm?
// (note: this is called after applicationShouldTerminate:)
- (void)terminate:(id)sender
{
// yes that's right folks: DO ABSOLUTELY NOTHING.
// the magic is [NSApp run] will just... stop.
// for debugging
NSLog(@"in terminate:");
}
@end
@interface uiAppDelegate : NSObject <NSApplicationDelegate>
@end
@implementation uiAppDelegate
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app
{
// for debugging
NSLog(@"in applicationShouldTerminate:");
return NSTerminateNow;
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app
{
return NO;
}
@end
// we are not in control of the actual lifetimes and refcounts of NSViews (see http://stackoverflow.com/a/29523141/3408572)
// when we're done with a view, it'll be added as a subview of this one, and this one will be released on application shutdown
// we need this separate view because it's possible for controls to have no parent but still be alive
NSView *destroyedControlsView;
uiInitOptions options;
const char *uiInit(uiInitOptions *o)
{
options = *o;
[uiApplication sharedApplication];
// don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy!
// see https://github.com/andlabs/ui/issues/6
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp setDelegate:[uiAppDelegate new]];
// we can use a stock NSView for this
destroyedControlsView = [[NSView alloc] initWithFrame:NSZeroRect];
return NULL;
}
void uiFreeInitError(const char *err)
{
}

View File

@ -1,59 +0,0 @@
// 9 april 2015
#import "uipriv_darwin.h"
@interface uiLabelNSTextField : NSTextField
@property uiLabel *uiL;
@end
@implementation uiLabelNSTextField
- (void)viewDidMoveToSuperview
{
if (uiDarwinControlFreeWhenAppropriate(uiControl(self.uiL), [self superview])) {
[self setTarget:nil];
self.uiL = NULL;
}
[super viewDidMoveToSuperview];
}
@end
static char *labelText(uiLabel *l)
{
uiLabelNSTextField *t;
t = (uiLabelNSTextField *) uiControlHandle(uiControl(l));
return uiDarwinNSStringToText([t stringValue]);
}
static void labelSetText(uiLabel *l, const char *text)
{
uiLabelNSTextField *t;
t = (uiLabelNSTextField *) uiControlHandle(uiControl(l));
[t setStringValue:toNSString(text)];
}
uiLabel *uiNewLabel(const char *text)
{
uiLabel *l;
uiLabelNSTextField *t;
l = uiNew(uiLabel);
uiDarwinNewControl(uiControl(l), [uiLabelNSTextField class], NO, NO);
t = (uiLabelNSTextField *) uiControlHandle(uiControl(l));
[t setStringValue:toNSString(text)];
[t setEditable:NO];
[t setSelectable:NO];
[t setDrawsBackground:NO];
finishNewTextField((NSTextField *) t, NO);
uiLabel(l)->Text = labelText;
uiLabel(l)->SetText = labelSetText;
t.uiL = l;
return t.uiL;
}

View File

@ -1,27 +0,0 @@
// 6 april 2015
#import "uipriv_darwin.h"
// #qo LDFLAGS: -lobjc -framework Foundation -framework AppKit
void uiMain(void)
{
[NSApp run];
}
void uiQuit(void)
{
NSEvent *e;
[NSApp stop:NSApp];
// stop: won't register until another event has passed; let's synthesize one
e = [NSEvent otherEventWithType:NSApplicationDefined
location:NSZeroPoint
modifierFlags:0
timestamp:[[NSProcessInfo processInfo] systemUptime]
windowNumber:0
context:[NSGraphicsContext currentContext]
subtype:0
data1:0
data2:0];
[NSApp postEvent:e atStart:NO]; // let pending events take priority (this is what PostQuitMessage() on Windows does so we have to do it here too for parity; thanks to mikeash in irc.freenode.net/#macdev for confirming that this parameter should indeed be NO)
}

View File

@ -1,231 +0,0 @@
// 7 april 2015
#include "uipriv_darwin.h"
typedef struct singleView singleView;
struct singleView {
NSView *view;
NSScrollView *scrollView;
NSView *immediate; // the control that is added to the parent container; either view or scrollView
uiParent *parent;
BOOL userHid;
BOOL containerHid;
BOOL userDisabled;
BOOL containerDisabled;
};
static void singleDestroy(uiControl *c)
{
singleView *s = (singleView *) (c->Internal);
[destroyedControlsView addSubview:s->immediate];
}
static uintptr_t singleHandle(uiControl *c)
{
singleView *s = (singleView *) (c->Internal);
return (uintptr_t) (s->view);
}
static void singleSetParent(uiControl *c, uiParent *parent)
{
singleView *s = (singleView *) (c->Internal);
NSView *parentView;
uiParent *oldparent;
oldparent = s->parent;
s->parent = parent;
if (oldparent != NULL) {
[s->immediate removeFromSuperview];
uiParentUpdate(oldparent);
}
if (s->parent != NULL) {
// TODO uiControlView(), uiParentView()
parentView = (NSView *) uiParentHandle(s->parent);
[parentView addSubview:s->immediate];
uiParentUpdate(s->parent);
}
}
// also good for NSBox and NSProgressIndicator
static void singlePreferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
{
singleView *s = (singleView *) (c->Internal);
NSControl *control;
NSRect r;
control = (NSControl *) (s->view);
[control sizeToFit];
// use alignmentRect here instead of frame because we'll be resizing based on that
r = [control alignmentRectForFrame:[control frame]];
*width = (intmax_t) r.size.width;
*height = (intmax_t) r.size.height;
}
static void singleResize(uiControl *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height, uiSizing *d)
{
singleView *s = (singleView *) (c->Internal);
NSRect frame;
frame.origin.x = x;
// mac os x coordinate system has (0,0) in the lower-left
frame.origin.y = ([[s->immediate superview] bounds].size.height - height) - y;
frame.size.width = width;
frame.size.height = height;
frame = [s->immediate frameForAlignmentRect:frame];
[s->immediate setFrame:frame];
}
static int singleVisible(uiControl *c)
{
singleView *s = (singleView *) (c->Internal);
if (s->userHid)
return 0;
return 1;
}
static void singleShow(uiControl *c)
{
singleView *s = (singleView *) (c->Internal);
s->userHid = NO;
if (!s->containerHid) {
[s->immediate setHidden:NO];
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
}
static void singleHide(uiControl *c)
{
singleView *s = (singleView *) (c->Internal);
s->userHid = YES;
[s->immediate setHidden:YES];
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
static void singleContainerShow(uiControl *c)
{
singleView *s = (singleView *) (c->Internal);
s->containerHid = NO;
if (!s->userHid) {
[s->immediate setHidden:NO];
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
}
static void singleContainerHide(uiControl *c)
{
singleView *s = (singleView *) (c->Internal);
s->containerHid = YES;
[s->immediate setHidden:YES];
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
static void enable(singleView *s)
{
if ([s->view respondsToSelector:@selector(setEnabled:)])
[((NSControl *) (s->view)) setEnabled:YES];
}
static void disable(singleView *s)
{
if ([s->view respondsToSelector:@selector(setEnabled:)])
[((NSControl *) (s->view)) setEnabled:NO];
}
static void singleEnable(uiControl *c)
{
singleView *s = (singleView *) (c->Internal);
s->userDisabled = NO;
if (!s->containerDisabled)
enable(s);
}
static void singleDisable(uiControl *c)
{
singleView *s = (singleView *) (c->Internal);
s->userDisabled = YES;
disable(s);
}
static void singleContainerEnable(uiControl *c)
{
singleView *s = (singleView *) (c->Internal);
s->containerDisabled = NO;
if (!s->userDisabled)
enable(s);
}
static void singleContainerDisable(uiControl *c)
{
singleView *s = (singleView *) (c->Internal);
s->containerDisabled = YES;
disable(s);
}
void uiDarwinNewControl(uiControl *c, Class class, BOOL inScrollView, BOOL scrollViewHasBorder)
{
singleView *s;
s = uiNew(singleView);
// thanks to autoxr and arwyn in irc.freenode.net/#macdev
s->view = (NSView *) [[class alloc] initWithFrame:NSZeroRect];
s->immediate = s->view;
if (inScrollView) {
s->scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect];
[s->scrollView setDocumentView:s->view];
[s->scrollView setHasHorizontalScroller:YES];
[s->scrollView setHasVerticalScroller:YES];
[s->scrollView setAutohidesScrollers:YES];
if (scrollViewHasBorder)
[s->scrollView setBorderType:NSBezelBorder];
else
[s->scrollView setBorderType:NSNoBorder];
s->immediate = (NSView *) (s->scrollView);
}
// and keep a reference to s->immediate for when we remove the control from its parent
[s->immediate retain];
c->Internal = s;
c->Destroy = singleDestroy;
c->Handle = singleHandle;
c->SetParent = singleSetParent;
c->PreferredSize = singlePreferredSize;
c->Resize = singleResize;
c->Visible = singleVisible;
c->Show = singleShow;
c->Hide = singleHide;
c->ContainerShow = singleContainerShow;
c->ContainerHide = singleContainerHide;
c->Enable = singleEnable;
c->Disable = singleDisable;
c->ContainerEnable = singleContainerEnable;
c->ContainerDisable = singleContainerDisable;
}
BOOL uiDarwinControlFreeWhenAppropriate(uiControl *c, NSView *newSuperview)
{
singleView *s = (singleView *) (c->Internal);
if (newSuperview == destroyedControlsView) {
[s->immediate release]; // we don't need the reference anymore
uiFree(s);
return YES;
}
return NO;
}

View File

@ -1,118 +0,0 @@
// 4 august 2014
#import "uipriv_darwin.h"
// calling -[className] on the content views of NSWindow, NSTabItem, and NSBox all return NSView, so I'm assuming I just need to override these
// fornunately:
// - NSWindow resizing calls -[setFrameSize:] (but not -[setFrame:])
// - NSTabView resizing calls both -[setFrame:] and -[setFrameSIze:] on the current tab
// - NSTabView switching tabs calls both -[setFrame:] and -[setFrameSize:] on the new tab
// so we just override setFrameSize:
// thanks to mikeash and JtRip in irc.freenode.net/#macdev
@interface uipParent : NSView {
// TODO
@public
uiControl *child;
intmax_t marginLeft;
intmax_t marginTop;
intmax_t marginRight;
intmax_t marginBottom;
}
- (void)uiUpdateNow;
@end
@implementation uipParent
uiLogObjCClassAllocations
- (void)viewDidMoveToSuperview
{
// we can't just use nil because NSTabView will set page views to nil when they're tabbed away
// this means that we have to explicitly move them to the destroyed controls view when we're done with them, and likewise in NSWindow
if ([self superview] == destroyedControlsView)
if (self->child != NULL) {
uiControlDestroy(self->child);
self->child = NULL;
[self release];
}
[super viewDidMoveToSuperview];
}
- (void)setFrameSize:(NSSize)s
{
[super setFrameSize:s];
[self uiUpdateNow];
}
// These are based on measurements from Interface Builder.
// These seem to be based on Auto Layout constants, but I don't see an API that exposes these...
// This one is 8 for most pairs of controls that I've tried; the only difference is between two pushbuttons, where it's 12...
#define macXPadding 8
// Likewise, this one appears to be 12 for pairs of push buttons...
#define macYPadding 8
- (void)uiUpdateNow
{
uiSizing d;
intmax_t x, y, width, height;
if (self->child == NULL)
return;
x = [self bounds].origin.x + self->marginLeft;
y = [self bounds].origin.y + self->marginTop;
width = [self bounds].size.width - (self->marginLeft + self->marginRight);
height = [self bounds].size.height - (self->marginTop + self->marginBottom);
d.xPadding = macXPadding;
d.yPadding = macYPadding;
uiControlResize(self->child, x, y, width, height, &d);
}
@end
static uintptr_t parentHandle(uiParent *p)
{
uipParent *pp = (uipParent *) (p->Internal);
return (uintptr_t) pp;
}
static void parentSetChild(uiParent *p, uiControl *child)
{
uipParent *pp = (uipParent *) (p->Internal);
pp->child = child;
if (pp->child != NULL)
uiControlSetParent(child, p);
}
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);
[pp uiUpdateNow];
}
uiParent *uiNewParent(uintptr_t osParent)
{
uiParent *p;
p = uiNew(uiParent);
p->Internal = [[uipParent alloc] initWithFrame:NSZeroRect];
p->Handle = parentHandle;
p->SetChild = parentSetChild;
p->SetMargins = parentSetMargins;
p->Update = parentUpdate;
// don't use osParent; we'll need to call specific selectors to set the parent view
// and keep the view alive so we can release it properly later
[((uipParent *) (p->Internal)) retain];
return p;
}

View File

@ -1,73 +0,0 @@
// 12 april 2015
#import "uipriv_darwin.h"
// TODO
// - verify margins against extra space around the tab
// - free child containers properly
@interface uiNSTabView : NSTabView
@property uiTab *uiT;
@end
@implementation uiNSTabView
- (void)viewDidMoveToSuperview
{
// TODO free all tabs explicitly
if (uiDarwinControlFreeWhenAppropriate(uiControl(self.uiT), [self superview]))
self.uiT = NULL;
[super viewDidMoveToSuperview];
}
@end
// the default new control implementation uses -sizeToFit, which we don't have with NSTabView
// fortunately, we do have -minimumSize
static void preferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
{
uiNSTabView *tv;
NSSize s;
tv = (uiNSTabView *) uiControlHandle(c);
s = [tv minimumSize];
*width = (intmax_t) (s.width);
*height = (intmax_t) (s.height);
}
static void tabAddPage(uiTab *t, const char *name, uiControl *child)
{
uiNSTabView *tv;
uiParent *content;
NSTabViewItem *i;
content = uiNewParent(0);
uiParentSetChild(content, child);
i = [[NSTabViewItem alloc] initWithIdentifier:nil];
[i setLabel:toNSString(name)];
[i setView:((NSView *) uiParentHandle(content))];
tv = (uiNSTabView *) uiControlHandle(uiControl(t));
[tv addTabViewItem:i];
}
uiTab *uiNewTab(void)
{
uiTab *t;
uiNSTabView *tv;
t = uiNew(uiTab);
uiDarwinNewControl(uiControl(t), [uiNSTabView class], NO, NO);
tv = (uiNSTabView *) uiControlHandle(uiControl(t));
// also good for NSTabView (same selector and everything)
setStandardControlFont((NSControl *) tv);
uiControl(t)->PreferredSize = preferredSize;
uiTab(t)->AddPage = tabAddPage;
tv.uiT = t;
return tv.uiT;
}

View File

@ -1,19 +0,0 @@
// 10 april 2015
#import "uipriv_darwin.h"
char *uiDarwinNSStringToText(NSString *s)
{
char *out;
out = strdup([s UTF8String]);
if (out == NULL) {
fprintf(stderr, "memory exhausted in uiDarwinNSStringToText()\n");
abort();
}
return out;
}
void uiFreeText(char *s)
{
free(s);
}

View File

@ -1,40 +0,0 @@
// 6 january 2015
#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_7
#define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_7
#import <Cocoa/Cocoa.h>
#import "../uipriv.h"
#import "../ui_darwin.h"
#define toNSString(str) [NSString stringWithUTF8String:(str)]
#define fromNSString(str) [(str) UTF8String]
#define uiLogObjCClassAllocations \
+ (id)alloc \
{ \
id thing; \
thing = [super alloc]; \
if (options.debugLogAllocations) \
fprintf(stderr, "%p alloc %s\n", thing, [[self className] UTF8String]); \
return thing; \
} \
- (void)dealloc \
{ \
[super dealloc]; \
if (options.debugLogAllocations) \
fprintf(stderr, "%p free\n", self); \
}
// init_darwin.m
extern NSView *destroyedControlsView;
// util_darwin.m
extern void setStandardControlFont(NSControl *);
extern void disableAutocorrect(NSTextView *);
// These are based on measurements from Interface Builder.
// These seem to be based on Auto Layout constants, but I don't see an API that exposes these...
#define macXMargin 20
#define macYMargin 20
// entry_darwin.m
extern void finishNewTextField(NSTextField *, BOOL);

View File

@ -1,20 +0,0 @@
// 7 april 2015
#import "uipriv_darwin.h"
// also fine for NSCells and NSTexts (NSTextViews)
void setStandardControlFont(NSControl *control)
{
[control setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]];
}
void disableAutocorrect(NSTextView *tv)
{
[tv setEnabledTextCheckingTypes:0];
[tv setAutomaticDashSubstitutionEnabled:NO];
// don't worry about automatic data detection; it won't change stringValue (thanks pretty_function in irc.freenode.net/#macdev)
[tv setAutomaticSpellingCorrectionEnabled:NO];
[tv setAutomaticTextReplacementEnabled:NO];
[tv setAutomaticQuoteSubstitutionEnabled:NO];
[tv setAutomaticLinkDetectionEnabled:NO];
[tv setSmartInsertDeleteEnabled:NO];
}

View File

@ -1,162 +0,0 @@
// 6 april 2015
#import "uipriv_darwin.h"
// TODO
// - free chilld containers properly
@interface uiWindowDelegate : NSObject <NSWindowDelegate>
@property (assign) NSWindow *w;
@property uiParent *content;
@property int (*onClosing)(uiWindow *, void *);
@property void *onClosingData;
@property struct window *uiw;
@end
@implementation uiWindowDelegate
uiLogObjCClassAllocations
- (BOOL)windowShouldClose:(id)win
{
// return exact constants to be safe
if ((*(self.onClosing))(uiWindow(self.uiw), self.onClosingData))
return YES;
return NO;
}
// after this method returns we assume the window will be released (see below), so we can go too
- (void)windowWillClose:(NSNotification *)note
{
[self.w setDelegate:nil]; // see http://stackoverflow.com/a/29523141/3408572
// when we reach this point, we need to ensure that all the window's children are destroyed (for OS parity)
// because we need to set the content view's superview to the destroyed controls view to trigger deletion, we need to do this manually
// first, replace the current content view...
[self.w setContentView:[[NSView alloc] initWithFrame:NSZeroRect]];
// ...then, trigger the deletion
[destroyedControlsView addSubview:((NSView *) uiParentHandle(self.content))];
uiFree(self.uiw);
[self release];
}
@end
struct window {
uiWindow w;
uiWindowDelegate *d;
int margined;
};
static int defaultOnClosing(uiWindow *w, void *data)
{
return 1;
}
#define D (((struct window *) w)->d)
static void windowDestroy(uiWindow *w)
{
[D.w close];
}
static uintptr_t windowHandle(uiWindow *w)
{
return (uintptr_t) (D.w);
}
static char *windowTitle(uiWindow *w)
{
return uiDarwinNSStringToText([D.w title]);
}
static void windowSetTitle(uiWindow *w, const char *title)
{
[D.w setTitle:toNSString(title)];
}
static void windowShow(uiWindow *w)
{
[D.w makeKeyAndOrderFront:D.w];
}
static void windowHide(uiWindow *w)
{
[D.w orderOut:D.w];
}
static void windowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data)
{
D.onClosing = f;
D.onClosingData = data;
}
static void windowSetChild(uiWindow *w, uiControl *c)
{
uiParentSetChild(D.content, c);
}
static int windowMargined(uiWindow *ww)
{
struct window *w = (struct window *) ww;
return w->margined;
}
static void windowSetMargined(uiWindow *ww, int margined)
{
struct window *w = (struct window *) ww;
w->margined = margined;
if (w->margined)
uiParentSetMargins(D.content, macXMargin, macYMargin, macXMargin, macYMargin);
else
uiParentSetMargins(D.content, 0, 0, 0, 0);
uiParentUpdate(D.content);
}
uiWindow *uiNewWindow(const char *title, int width, int height)
{
uiWindowDelegate *d;
d = [uiWindowDelegate new];
d.w = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height)
styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
backing:NSBackingStoreBuffered
defer:YES];
[d.w setTitle:toNSString(title)];
// we do not want substitutions
// text fields, labels, etc. take their smart quotes and other autocorrect settings from their parent window, which provides a shared "field editor"
// so we have to turn them off here
// thanks akempgen in irc.freenode.net/#macdev
// for some reason, this selector returns NSText but is documented to return NSTextView...
// NOTE: if you disagree with me about disabling substitutions, start a github issue with why and I'll be happy to consider it
disableAutocorrect((NSTextView *) [d.w fieldEditor:YES forObject:nil]);
// this is what will destroy the window on close
[d.w setReleasedWhenClosed:YES];
d.content = uiNewParent(0);
[d.w setContentView:((NSView *) uiParentHandle(d.content))];
d.onClosing = defaultOnClosing;
[d.w setDelegate:d];
d.uiw = uiNew(struct window);
d.uiw->d = d;
uiWindow(d.uiw)->Destroy = windowDestroy;
uiWindow(d.uiw)->Handle = windowHandle;
uiWindow(d.uiw)->Title = windowTitle;
uiWindow(d.uiw)->SetTitle = windowSetTitle;
uiWindow(d.uiw)->Show = windowShow;
uiWindow(d.uiw)->Hide = windowHide;
uiWindow(d.uiw)->OnClosing = windowOnClosing;
uiWindow(d.uiw)->SetChild = windowSetChild;
uiWindow(d.uiw)->Margined = windowMargined;
uiWindow(d.uiw)->SetMargined = windowSetMargined;
return uiWindow(d.uiw);
}

View File

@ -1,56 +0,0 @@
# 7 april 2015
$2 == "alloc" {
if ($1 in A) {
problem($1 " already allocated (" A[$1] "); allocated at " NR)
next
}
A[$1] = type()
next
}
$2 == "realloc" {
if (!($1 in A)) {
problem($1 " not yet allocated; reallocated at " NR)
next
}
if ($3 in A) {
problem($3 " already allocated (" A[$3] "); reallocated at " NR)
next
}
t = A[$1]
delete A[$1]
A[$3] = t
next
}
$2 == "free" {
if (!($1 in A)) {
problem($1 " not yet allocated; freed at " NR)
next
}
delete A[$1]
next
}
{ problem("unrecognized line " $0 " at " NR) }
END {
for (i in A)
problem("leaked " A[i] " at " i)
close("/dev/stderr")
if (hasProblems)
exit 1
}
function problem(s) {
print s > "/dev/stderr"
hasProblems = 1
}
function type( s, i) {
s = $3
for (i = 4; i <= NF; i++)
s = s " " $i
return s
}

View File

@ -1,57 +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.SetChild(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.SetChild(i)
c.SetParent(NULL)
d.SetParent(NULL)
...
i.SetParent(p)
...
p.Update()
i.Resize()
w.SetChild(NULL)
p.SetChild(NULL)
i.SetParent(NULL)
p.Update()
w.SetChild(i)
(again)
w.Destroy()
p.Destroy()
i.Destroy()
TODO
- rename these methods
- p.DeferUpdate()/p.EndDeferUpdate()

View File

@ -1,398 +0,0 @@
// 7 april 2015
#include "uipriv.h"
// TODO
// - rename to uiBox
typedef struct stack stack;
typedef struct stackControl stackControl;
struct stack {
uiStack s;
stackControl *controls;
uintmax_t len;
uintmax_t cap;
int vertical;
uiParent *parent;
int padded;
int userHid;
int containerHid;
int userDisabled;
int containerDisabled;
};
struct stackControl {
uiControl *c;
int stretchy;
intmax_t width; // both used by resize(); preallocated to save time and reduce risk of failure
intmax_t height;
};
static void stackDestroy(uiControl *c)
{
stack *s = (stack *) c;
uintmax_t i;
for (i = 0; i < s->len; i++)
uiControlDestroy(s->controls[i].c);
uiFree(s->controls);
uiFree(s);
}
static uintptr_t stackHandle(uiControl *c)
{
return 0;
}
static void stackSetParent(uiControl *c, uiParent *parent)
{
stack *s = (stack *) c;
uintmax_t i;
uiParent *oldparent;
oldparent = s->parent;
s->parent = parent;
for (i = 0; i < s->len; i++)
uiControlSetParent(s->controls[i].c, s->parent);
if (oldparent != NULL)
uiParentUpdate(oldparent);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
static void stackPreferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
{
stack *s = (stack *) c;
int xpadding, ypadding;
uintmax_t nStretchy;
// these two contain the largest preferred width and height of all stretchy controls in the stack
// all stretchy controls will use this value to determine the final preferred size
intmax_t maxStretchyWidth, maxStretchyHeight;
uintmax_t i;
intmax_t preferredWidth, preferredHeight;
*width = 0;
*height = 0;
if (s->len == 0)
return;
// 0) get this Stack's padding
xpadding = 0;
ypadding = 0;
if (s->padded) {
xpadding = d->xPadding;
ypadding = d->yPadding;
}
// 1) initialize the desired rect with the needed padding
if (s->vertical)
*height = (s->len - 1) * ypadding;
else
*width = (s->len - 1) * xpadding;
// 2) add in the size of non-stretchy controls and get (but not add in) the largest widths and heights of stretchy controls
// we still add in like direction of stretchy controls
nStretchy = 0;
maxStretchyWidth = 0;
maxStretchyHeight = 0;
for (i = 0; i < s->len; i++) {
if (!uiControlVisible(s->controls[i].c))
continue;
uiControlPreferredSize(s->controls[i].c, d, &preferredWidth, &preferredHeight);
if (s->controls[i].stretchy) {
nStretchy++;
if (maxStretchyWidth < preferredWidth)
maxStretchyWidth = preferredWidth;
if (maxStretchyHeight < preferredHeight)
maxStretchyHeight = preferredHeight;
}
if (s->vertical) {
if (*width < preferredWidth)
*width = preferredWidth;
if (!s->controls[i].stretchy)
*height += preferredHeight;
} else {
if (!s->controls[i].stretchy)
*width += preferredWidth;
if (*height < preferredHeight)
*height = preferredHeight;
}
}
// 3) and now we can add in stretchy controls
if (s->vertical)
*height += nStretchy * maxStretchyHeight;
else
*width += nStretchy * maxStretchyWidth;
}
static void stackResize(uiControl *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height, uiSizing *d)
{
stack *s = (stack *) c;
int xpadding, ypadding;
uintmax_t nStretchy;
intmax_t stretchywid, stretchyht;
uintmax_t i;
intmax_t preferredWidth, preferredHeight;
if (s->len == 0)
return;
// -1) get this Stack's padding
xpadding = 0;
ypadding = 0;
if (s->padded) {
xpadding = d->xPadding;
ypadding = d->yPadding;
}
// 0) inset the available rect by the needed padding
if (s->vertical)
height -= (s->len - 1) * ypadding;
else
width -= (s->len - 1) * xpadding;
// 1) get width and height of non-stretchy controls
// this will tell us how much space will be left for stretchy controls
stretchywid = width;
stretchyht = height;
nStretchy = 0;
for (i = 0; i < s->len; i++) {
if (!uiControlVisible(s->controls[i].c))
continue;
if (s->controls[i].stretchy) {
nStretchy++;
continue;
}
uiControlPreferredSize(s->controls[i].c, d, &preferredWidth, &preferredHeight);
if (s->vertical) { // all controls have same width
s->controls[i].width = width;
s->controls[i].height = preferredHeight;
stretchyht -= preferredHeight;
} else { // all controls have same height
s->controls[i].width = preferredWidth;
s->controls[i].height = height;
stretchywid -= preferredWidth;
}
}
// 2) now get the size of stretchy controls
if (nStretchy != 0)
if (s->vertical)
stretchyht /= nStretchy;
else
stretchywid /= nStretchy;
for (i = 0; i < s->len; i++) {
if (!uiControlVisible(s->controls[i].c))
continue;
if (s->controls[i].stretchy) {
s->controls[i].width = stretchywid;
s->controls[i].height = stretchyht;
}
}
// 3) now we can position controls
for (i = 0; i < s->len; i++) {
if (!uiControlVisible(s->controls[i].c))
continue;
uiControlResize(s->controls[i].c, x, y, s->controls[i].width, s->controls[i].height, d);
if (s->vertical)
y += s->controls[i].height + ypadding;
else
x += s->controls[i].width + xpadding;
}
}
static int stackVisible(uiControl *c)
{
stack *s = (stack *) c;
return !(s->userHid);
}
static void stackShow(uiControl *c)
{
stack *s = (stack *) c;
uintmax_t i;
s->userHid = 0;
if (!s->containerHid) {
for (i = 0; i < s->len; i++)
uiControlContainerShow(s->controls[i].c);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
}
static void stackHide(uiControl *c)
{
stack *s = (stack *) c;
uintmax_t i;
s->userHid = 1;
for (i = 0; i < s->len; i++)
uiControlContainerHide(s->controls[i].c);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
static void stackContainerShow(uiControl *c)
{
stack *s = (stack *) c;
uintmax_t i;
s->containerHid = 0;
if (!s->userHid) {
for (i = 0; i < s->len; i++)
uiControlContainerShow(s->controls[i].c);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
}
static void stackContainerHide(uiControl *c)
{
stack *s = (stack *) c;
uintmax_t i;
s->containerHid = 1;
for (i = 0; i < s->len; i++)
uiControlContainerHide(s->controls[i].c);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
static void stackEnable(uiControl *c)
{
stack *s = (stack *) c;
uintmax_t i;
s->userDisabled = 0;
if (!s->containerDisabled)
for (i = 0; i < s->len; i++)
uiControlContainerEnable(s->controls[i].c);
}
static void stackDisable(uiControl *c)
{
stack *s = (stack *) c;
uintmax_t i;
s->userDisabled = 1;
for (i = 0; i < s->len; i++)
uiControlContainerDisable(s->controls[i].c);
}
static void stackContainerEnable(uiControl *c)
{
stack *s = (stack *) c;
uintmax_t i;
s->containerDisabled = 0;
if (!s->userDisabled)
for (i = 0; i < s->len; i++)
uiControlContainerEnable(s->controls[i].c);
}
static void stackContainerDisable(uiControl *c)
{
stack *s = (stack *) c;
uintmax_t i;
s->containerDisabled = 1;
for (i = 0; i < s->len; i++)
uiControlContainerDisable(s->controls[i].c);
}
#define stackCapGrow 32
static void stackAppend(uiStack *ss, uiControl *c, int stretchy)
{
stack *s = (stack *) ss;
if (s->len >= s->cap) {
s->cap += stackCapGrow;
s->controls = (stackControl *) uiRealloc(s->controls, s->cap * sizeof (stackControl), "stackControl[]");
}
s->controls[s->len].c = c;
s->controls[s->len].stretchy = stretchy;
s->len++; // must be here for parent updates to work
if (s->parent != NULL) {
uiControlSetParent(s->controls[s->len - 1].c, s->parent);
uiParentUpdate(s->parent);
}
}
static void stackDelete(uiStack *ss, uintmax_t index)
{
stack *s = (stack *) ss;
uiControl *removed;
uintmax_t i;
removed = s->controls[index].c;
// TODO switch to memmove?
for (i = index; i < s->len - 1; i++)
s->controls[i] = s->controls[i + 1];
// TODO memset the last one to NULL
s->len--;
if (s->parent != NULL) {
uiControlSetParent(removed, NULL);
uiParentUpdate(s->parent);
}
}
static int stackPadded(uiStack *ss)
{
stack *s = (stack *) ss;
return s->padded;
}
static void stackSetPadded(uiStack *ss, int padded)
{
stack *s = (stack *) ss;
s->padded = padded;
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
uiStack *uiNewHorizontalStack(void)
{
stack *s;
s = uiNew(stack);
uiControl(s)->Destroy = stackDestroy;
uiControl(s)->Handle = stackHandle;
uiControl(s)->SetParent = stackSetParent;
uiControl(s)->PreferredSize = stackPreferredSize;
uiControl(s)->Resize = stackResize;
uiControl(s)->Visible = stackVisible;
uiControl(s)->Show = stackShow;
uiControl(s)->Hide = stackHide;
uiControl(s)->ContainerShow = stackContainerShow;
uiControl(s)->ContainerHide = stackContainerHide;
uiControl(s)->Enable = stackEnable;
uiControl(s)->Disable = stackDisable;
uiControl(s)->ContainerEnable = stackContainerEnable;
uiControl(s)->ContainerDisable = stackContainerDisable;
uiStack(s)->Append = stackAppend;
uiStack(s)->Delete = stackDelete;
uiStack(s)->Padded = stackPadded;
uiStack(s)->SetPadded = stackSetPadded;
return uiStack(s);
}
uiStack *uiNewVerticalStack(void)
{
uiStack *ss;
stack *s;
ss = uiNewHorizontalStack();
s = (stack *) ss;
s->vertical = 1;
return ss;
}

View File

@ -1,337 +0,0 @@
// 6 april 2015
#include "ui.h"
#include <stdio.h>
#include <string.h>
// TODO convert to using the new conversion macros
int onClosing(uiWindow *w, void *data)
{
printf("in closing!\n");
uiQuit();
return 1;
}
uiEntry *e;
static void getWindowText(uiButton *b, void *data)
{
char *text;
text = uiWindowTitle(uiWindow(data));
uiEntrySetText(e, text);
uiFreeText(text);
}
static void setWindowText(uiButton *b, void *data)
{
char *text;
text = uiEntryText(e);
uiWindowSetTitle(uiWindow(data), text);
uiFreeText(text);
}
static void getButtonText(uiButton *b, void *data)
{
char *text;
text = uiButtonText(uiButton(data));
uiEntrySetText(e, text);
uiFreeText(text);
}
static void setButtonText(uiButton *b, void *data)
{
char *text;
text = uiEntryText(e);
uiButtonSetText(uiButton(data), text);
uiFreeText(text);
}
static void getCheckboxText(uiButton *b, void *data)
{
char *text;
text = uiCheckboxText(uiCheckbox(data));
uiEntrySetText(e, text);
uiFreeText(text);
}
static void setCheckboxText(uiButton *b, void *data)
{
char *text;
text = uiEntryText(e);
uiCheckboxSetText(uiCheckbox(data), text);
uiFreeText(text);
}
uiWindow *w;
#define nStacks 11
uiStack *stacks[nStacks];
uiCheckbox *spaced;
static void setSpaced(int spaced)
{
int i;
uiWindowSetMargined(w, spaced);
for (i = 0; i < nStacks; i++)
uiStackSetPadded(stacks[i], spaced);
}
static void toggleSpaced(uiCheckbox *c, void *data)
{
int s;
s = uiCheckboxChecked(spaced);
printf("toggled %d\n", s);
setSpaced(s);
}
// these will also be used to test if setting checks will trigger events
static void forceSpacedOn(uiButton *b, void *data)
{
uiCheckboxSetChecked(spaced, 1);
}
static void forceSpacedOff(uiButton *b, void *data)
{
uiCheckboxSetChecked(spaced, 0);
}
static void showSpaced(uiButton *b, void *data)
{
char msg[] = { 'm', ' ', '0', ' ', 'p', ' ', '0', '\0' };
if (uiWindowMargined(w))
msg[2] = '1';
if (uiStackPadded(stacks[0]))
msg[6] = '1';
uiEntrySetText(e, msg);
}
static void showControl(uiButton *b, void *data)
{
uiControlShow(uiControl(data));
}
static void hideControl(uiButton *b, void *data)
{
uiControlHide(uiControl(data));
}
static void enableControl(uiButton *b, void *data)
{
uiControlEnable(uiControl(data));
}
static void disableControl(uiButton *b, void *data)
{
uiControlDisable(uiControl(data));
}
static void getLabelText(uiButton *b, void *data)
{
char *text;
text = uiLabelText(uiLabel(data));
uiEntrySetText(e, text);
uiFreeText(text);
}
static void setLabelText(uiButton *b, void *data)
{
char *text;
text = uiEntryText(e);
uiLabelSetText(uiLabel(data), text);
uiFreeText(text);
}
uiStack *firstStack;
uiStack *secondStack;
uiLabel *movingLabel;
static void moveToFirst(uiButton *b, void *data)
{
uiStackDelete(secondStack, 1);
uiStackAppend(firstStack, uiControl(movingLabel), 1);
}
static void moveToSecond(uiButton *b, void *data)
{
uiStackDelete(firstStack, 1);
uiStackAppend(secondStack, uiControl(movingLabel), 1);
}
int main(int argc, char *argv[])
{
uiInitOptions o;
int i;
const char *err;
uiButton *getButton, *setButton;
uiLabel *label;
uiTab *tab;
int page2stack;
memset(&o, 0, sizeof (uiInitOptions));
for (i = 1; i < argc; i++)
if (strcmp(argv[i], "leaks") == 0)
o.debugLogAllocations = 1;
else {
fprintf(stderr, "%s: unrecognized option %s\n", argv[0], argv[i]);
return 1;
}
err = uiInit(&o);
if (err != NULL) {
fprintf(stderr, "error initializing ui: %s\n", err);
uiFreeInitError(err);
return 1;
}
w = uiNewWindow("Hello", 320, 240);
uiWindowOnClosing(w, onClosing, NULL);
stacks[0] = uiNewVerticalStack();
e = uiNewEntry();
uiStackAppend(stacks[0], uiControl(e), 0);
i = 1;
stacks[i] = uiNewHorizontalStack();
getButton = uiNewButton("Get Window Text");
uiButtonOnClicked(getButton, getWindowText, w);
setButton = uiNewButton("Set Window Text");
uiButtonOnClicked(setButton, setWindowText, w);
uiStackAppend(stacks[i], uiControl(getButton), 1);
uiStackAppend(stacks[i], uiControl(setButton), 1);
uiStackAppend(stacks[0], uiControl(stacks[i]), 0);
i++;
stacks[i] = uiNewHorizontalStack();
getButton = uiNewButton("Get Button Text");
uiButtonOnClicked(getButton, getButtonText, getButton);
setButton = uiNewButton("Set Button Text");
uiButtonOnClicked(setButton, setButtonText, getButton);
uiStackAppend(stacks[i], uiControl(getButton), 1);
uiStackAppend(stacks[i], uiControl(setButton), 1);
uiStackAppend(stacks[0], uiControl(stacks[i]), 0);
i++;
// this will also be used to make sure tab stops work properly when inserted out of creation order, especially on Windows
spaced = uiNewCheckbox("Spaced");
uiCheckboxOnToggled(spaced, toggleSpaced, NULL);
stacks[i] = uiNewHorizontalStack();
getButton = uiNewButton("Get Checkbox Text");
uiButtonOnClicked(getButton, getCheckboxText, spaced);
setButton = uiNewButton("Set Checkbox Text");
uiButtonOnClicked(setButton, setCheckboxText, spaced);
uiStackAppend(stacks[i], uiControl(getButton), 1);
uiStackAppend(stacks[i], uiControl(setButton), 1);
uiStackAppend(stacks[0], uiControl(stacks[i]), 0);
i++;
label = uiNewLabel("Label");
stacks[i] = uiNewHorizontalStack();
getButton = uiNewButton("Get Label Text");
uiButtonOnClicked(getButton, getLabelText, label);
setButton = uiNewButton("Set Label Text");
uiButtonOnClicked(setButton, setLabelText, label);
uiStackAppend(stacks[i], uiControl(getButton), 1);
uiStackAppend(stacks[i], uiControl(setButton), 1);
uiStackAppend(stacks[0], uiControl(stacks[i]), 0);
i++;
stacks[i] = uiNewHorizontalStack();
uiStackAppend(stacks[i], uiControl(spaced), 1);
getButton = uiNewButton("On");
uiButtonOnClicked(getButton, forceSpacedOn, NULL);
setButton = uiNewButton("Off");
uiButtonOnClicked(setButton, forceSpacedOff, NULL);
uiStackAppend(stacks[i], uiControl(getButton), 0);
uiStackAppend(stacks[i], uiControl(setButton), 0);
setButton = uiNewButton("Show");
uiButtonOnClicked(setButton, showSpaced, NULL);
uiStackAppend(stacks[i], uiControl(setButton), 0);
uiStackAppend(stacks[0], uiControl(stacks[i]), 0);
i++;
stacks[i] = uiNewHorizontalStack();
getButton = uiNewButton("Button");
uiStackAppend(stacks[i], uiControl(getButton), 1);
setButton = uiNewButton("Show");
uiButtonOnClicked(setButton, showControl, getButton);
uiStackAppend(stacks[i], uiControl(setButton), 0);
setButton = uiNewButton("Hide");
uiButtonOnClicked(setButton, hideControl, getButton);
uiStackAppend(stacks[i], uiControl(setButton), 0);
setButton = uiNewButton("Enable");
uiButtonOnClicked(setButton, enableControl, getButton);
uiStackAppend(stacks[i], uiControl(setButton), 0);
setButton = uiNewButton("Disable");
uiButtonOnClicked(setButton, disableControl, getButton);
uiStackAppend(stacks[i], uiControl(setButton), 0);
uiStackAppend(stacks[0], uiControl(stacks[i]), 0);
i++;
stacks[i] = uiNewHorizontalStack();
setButton = uiNewButton("Show Stack");
uiButtonOnClicked(setButton, showControl, stacks[i - 1]);
uiStackAppend(stacks[i], uiControl(setButton), 1);
setButton = uiNewButton("Hide Stack");
uiButtonOnClicked(setButton, hideControl, stacks[i - 1]);
uiStackAppend(stacks[i], uiControl(setButton), 1);
setButton = uiNewButton("Enable Stack");
uiButtonOnClicked(setButton, enableControl, stacks[i - 1]);
uiStackAppend(stacks[i], uiControl(setButton), 1);
setButton = uiNewButton("Disable Stack");
uiButtonOnClicked(setButton, disableControl, stacks[i - 1]);
uiStackAppend(stacks[i], uiControl(setButton), 1);
uiStackAppend(stacks[0], uiControl(stacks[i]), 0);
i++;
uiStackAppend(stacks[0], uiControl(label), 0);
tab = uiNewTab();
uiWindowSetChild(w, uiControl(tab));
uiTabAddPage(tab, "Page 1", uiControl(stacks[0]));
page2stack = i;
stacks[i] = uiNewVerticalStack();
uiTabAddPage(tab, "Page 2", uiControl(stacks[i]));
i++;
stacks[i] = uiNewHorizontalStack();
firstStack = stacks[i];
getButton = uiNewButton("Move Here");
uiButtonOnClicked(getButton, moveToFirst, NULL);
uiStackAppend(stacks[i], uiControl(getButton), 0);
movingLabel = uiNewLabel("This label moves!");
uiStackAppend(stacks[i], uiControl(movingLabel), 1);
uiStackAppend(stacks[page2stack], uiControl(stacks[i]), 0);
i++;
stacks[i] = uiNewHorizontalStack();
secondStack = stacks[i];
getButton = uiNewButton("Move Here");
uiButtonOnClicked(getButton, moveToSecond, NULL);
uiStackAppend(stacks[i], uiControl(getButton), 0);
uiStackAppend(stacks[page2stack], uiControl(stacks[i]), 0);
i++;
if (i != nStacks) {
fprintf(stderr, "forgot to update nStacks (expected %d)\n", i);
return 1;
}
uiWindowShow(w);
uiMain();
printf("after uiMain()\n");
return 0;
}

View File

@ -1,161 +0,0 @@
// 6 april 2015
// This is not an IDL file for the conventional RPC or Microsoft IDLs.
// Instead, this is for a custom IDL of my own creation.
// You can find it at github.com/andlabs/pgidl
package ui {
// TODO autogenerate this somehow
// TODO alternatively, move AFTER typedefs
raw "#ifndef __UI_UI_H__";
raw "#define __UI_UI_H__";
raw "#include <stdint.h>";
// TODO note that should be initialized to zero
struct InitOptions {
// TODO cbSize
// If nonzero, allocations will be logged to stderr.
// See leaks.awk.
field debugLogAllocations int;
};
// TODO const char
raw "const char *uiInit(uiInitOptions *);";
raw "void uiFreeInitError(const char *);";
func Main(void);
func Quit(void);
func FreeText(text *char);
raw "typedef struct uiSizingSys uiSizingSys;";
struct Sizing {
field xPadding intmax_t;
field yPadding intmax_t;
field sys *uiSizingSys;
};
interface Control {
field Internal *void; // for use by ui only
func Destroy(void);
func Handle(void) uintptr_t;
func SetParent(p *Parent);
func PreferredSize(d *Sizing, width *intmax_t, height *intmax_t);
func Resize(x intmax_t, y intmax_t, width intmax_t, height intmax_t, d *Sizing);
func Visible(void) int;
func Show(void);
func Hide(void);
func ContainerShow(void);
func ContainerHide(void);
func Enable(void);
func Disable(void);
func ContainerEnable(void);
func ContainerDisable(void);
};
// Parent represents an OS control that hosts other OS controls.
// It is used internally by package ui and by implementations.
// Window, Tab, and Group all use uiParents to store their controls.
interface Parent {
// Internal points to internal data.
// Do not access or alter this field.
field Internal *void;
// TODO destroy
// TODO object destruction debug handler
// Handle returns the window handle of the uiParent.
// On Windows, this is a HWND.
// On GTK+, this is a GtkContainer.
// On Mac OS X, this is a NSView.
func Handle(void) uintptr_t;
// TODO rename and clean this up
// SetChild sets the uiControl that this uiParent relegates.
// It calls uiControl.SetParent() which should, in turn, call uiParent.Update().
// The uiParent should already not have a child and the uiControl should already not have a parent.
//
// child can be NULL, in which case the uiParent has no children.
// This version should also call uiControl.SetParent(), passing NULL.
//
// If this uiParent has a child already, then the current child is replaced with the new one.
func SetChild(c *Control);
// SetMargins sets the margins of the uiParent to the given margins.
// It does not call uiParent.Update(); its caller must.
// The units of the margins are backend-defined.
// The initial margins are all 0.
func SetMargins(left intmax_t, top intmax_t, right intmax_t, bottom intmax_t);
// TODO Resize?
// Update tells the uiParent to re-layout its children immediately.
// It is called when a widget is shown or hidden or when a control is added or removed from a container such as uiStack.
func Update(void);
};
func NewParent(osParent uintptr_t) *Parent;
interface Window {
field Internal *void;
func Destroy(void);
func Handle(void) uintptr_t;
func Title(void) *char;
func SetTitle(title *const char);
func Show(void);
func Hide(void);
func OnClosing(f *func(w *Window, data *void) int, data *void);
func SetChild(c *Control);
func Margined(void) int;
func SetMargined(margined int);
};
func NewWindow(title *const char, width int, height int) *Window;
interface Button from Control {
func Text(void) *char;
func SetText(text *const char);
func OnClicked(f *func(b *Button, data *void), data *void);
};
func NewButton(text *const char) *Button;
interface Stack from Control {
func Append(c *Control, stretchy int);
func Delete(index uintmax_t);
func Padded(void) int;
func SetPadded(padded int);
};
func NewHorizontalStack(void) *Stack;
func NewVerticalStack(void) *Stack;
interface Entry from Control {
func Text(void) *char;
func SetText(text *const char);
};
func NewEntry(void) *Entry;
interface Checkbox from Control {
func Text(void) *char;
func SetText(text *const char);
func OnToggled(f *func(c *Checkbox, data *void), data *void);
func Checked(void) int;
func SetChecked(checked int);
};
func NewCheckbox(text *const char) *Checkbox;
interface Label from Control {
func Text(void) *char;
func SetText(text *const char);
};
func NewLabel(text *const char) *Label;
interface Tab from Control {
func AddPage(name *const char, c *Control);
};
func NewTab(void) *Tab;
raw "#endif";
};

View File

@ -1,25 +0,0 @@
// 7 april 2015
/*
This file assumes that you have imported <Cocoa/Cocoa.h> and "ui.h" beforehand. It provides API-specific functions for interfacing with foreign controls on Mac OS X.
*/
#ifndef __UI_UI_DARWIN_H__
#define __UI_UI_DARWIN_H__
// uiDarwinNewControl() initializes the given uiControl with the given Cocoa control inside.
// The second parameter should come from [RealControlType class].
// The two scrollView parameters allow placing scrollbars on the new control.
// Your control must call uiDarwinControlFreeWhenAppropriate() on the returned uiControl in its -[viewDidMoveToSuperview] method.
// If it returns a value other than NO, then the uiControl has been freed and you should set references to it to NULL.
extern void uiDarwinNewControl(uiControl *c, Class class, BOOL inScrollView, BOOL scrollViewHasBorder);
extern BOOL uiDarwinControlFreeWhenAppropriate(uiControl *c, NSView *newSuperview);
// You can use this function from within your control implementations to return text strings that can be freed with uiTextFree().
extern char *uiDarwinNSStringToText(NSString *);
struct uiSizingSys {
// this structure currently left blank
};
#endif

View File

@ -1,20 +0,0 @@
// 7 april 2015
/*
This file assumes that you have included <gtk/gtk.h> and "ui.h" beforehand. It provides API-specific functions for interfacing with foreign controls on Unix systems that use GTK+ to provide their UI (currently all except Mac OS X).
*/
#ifndef __UI_UI_UNIX_H__
#define __UI_UI_UNIX_H__
// uiUnixNewControl() creates a new uiControl with the given GTK+ control inside, storing it in the uiControl at c.
// The second parameter is the type of the control, as passed to the first argument of g_object_new().
// The two scrolledWindow parameters allow placing scrollbars on the new control.
// The firstProperty parameter and beyond allow passing construct properties to the new control, as with g_object_new(); end this list with NULL.
extern void uiUnixNewControl(uiControl *c, GType type, gboolean inScrolledWindow, gboolean scrolledWindowHasBorder, const char *firstProperty, ...);
struct uiSizingSys {
// this structure currently left blank
};
#endif

View File

@ -1,59 +0,0 @@
// 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__
// Correctness macros.
#define uiControlHWND(c) ((HWND) uiControlHandle(c))
#define uiParentHWND(p) ((HWND) uiParentHandle(p))
// 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);
BOOL (*onWM_NOTIFY)(uiControl *c, NMHDR *nm, LRESULT *lResult);
// This is called in WM_DESTROY.
void (*onWM_DESTROY)(uiControl *c);
};
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

View File

@ -1,10 +0,0 @@
// 6 april 2015
#include <stdlib.h>
#include "ui.h"
extern uiInitOptions options;
extern void *uiAlloc(size_t, const char *);
#define uiNew(T) ((T *) uiAlloc(sizeof (T), #T ))
extern void *uiRealloc(void *, size_t, const char *);
extern void uiFree(void *);

View File

@ -1,18 +0,0 @@
OSCFILES = \
alloc.c \
button.c \
checkbox.c \
entry.c \
init.c \
label.c \
main.c \
newcontrol.c \
parent.c \
tab.c \
util.c \
window.c
xCFLAGS += `pkg-config --cflags gtk+-3.0`
xLDFLAGS += `pkg-config --libs gtk+-3.0`
OUT = new

View File

@ -1,33 +0,0 @@
// 7 april 2015
#include <stdio.h>
#include "uipriv_unix.h"
void *uiAlloc(size_t size, const char *type)
{
void *out;
out = g_malloc0(size);
if (options.debugLogAllocations)
fprintf(stderr, "%p alloc %s\n", out, type);
return out;
}
void *uiRealloc(void *p, size_t size, const char *type)
{
void *out;
if (p == NULL)
return uiAlloc(size, type);
// TODO fill with 0s
out = g_realloc(p, size);
if (options.debugLogAllocations)
fprintf(stderr, "%p realloc %p\n", p, out);
return out;
}
void uiFree(void *p)
{
g_free(p);
if (options.debugLogAllocations)
fprintf(stderr, "%p free\n", p);
}

View File

@ -1,71 +0,0 @@
// 7 april 2015
#include "uipriv_unix.h"
struct button {
uiButton b;
void (*onClicked)(uiButton *, void *);
void *onClickedData;
};
static void clicked(GtkButton *button, gpointer data)
{
struct button *b = (struct button *) data;
(*(b->onClicked))(uiButton(b), b->onClickedData);
}
static void defaultOnClicked(uiButton *b, void *data)
{
// do nothing
}
static void destroy(GtkWidget *widget, gpointer data)
{
struct button *b = (struct button *) data;
uiFree(b);
}
#define BUTTON(b) GTK_BUTTON(widget(b))
static char *buttonText(uiButton *bb)
{
return g_strdup(gtk_button_get_label(BUTTON(bb)));
}
static void buttonSetText(uiButton *bb, const char *text)
{
gtk_button_set_label(BUTTON(bb), text);
}
static void buttonOnClicked(uiButton *bb, void (*f)(uiButton *, void *), void *data)
{
struct button *b = (struct button *) bb;
b->onClicked = f;
b->onClickedData = data;
}
uiButton *uiNewButton(const char *text)
{
struct button *b;
GtkWidget *widget;
b = uiNew(struct button);
uiUnixNewControl(uiControl(b), GTK_TYPE_BUTTON,
FALSE, FALSE,
"label", text,
NULL);
widget = WIDGET(b);
g_signal_connect(widget, "clicked", G_CALLBACK(clicked), b);
g_signal_connect(widget, "destroy", G_CALLBACK(destroy), b);
b->onClicked = defaultOnClicked;
uiButton(b)->Text = buttonText;
uiButton(b)->SetText = buttonSetText;
uiButton(b)->OnClicked = buttonOnClicked;
return uiButton(b);
}

View File

@ -1,95 +0,0 @@
// 7 april 2015
#include "uipriv_unix.h"
struct checkbox {
uiCheckbox c;
void (*onToggled)(uiCheckbox *, void *);
void *onToggledData;
gulong onToggledSignal;
};
static void onToggled(GtkToggleButton *b, gpointer data)
{
struct checkbox *c = (struct checkbox *) data;
(*(c->onToggled))(uiCheckbox(c), c->onToggledData);
}
static void defaultOnToggled(uiCheckbox *c, void *data)
{
// do nothing
}
static void onDestroy(GtkWidget *widget, gpointer data)
{
struct checkbox *c = (struct checkbox *) data;
uiFree(c);
}
#define CHECKBOX(c) GTK_CHECK_BUTTON(uiControlHandle(uiControl(c)))
static char *getText(uiCheckbox *c)
{
return g_strdup(gtk_button_get_label(GTK_BUTTON(CHECKBOX(c))));
}
static void setText(uiCheckbox *c, const char *text)
{
gtk_button_set_label(GTK_BUTTON(CHECKBOX(c)), text);
}
static void setOnToggled(uiCheckbox *cc, void (*f)(uiCheckbox *, void *), void *data)
{
struct checkbox *c = (struct checkbox *) cc;
c->onToggled = f;
c->onToggledData = data;
}
static int getChecked(uiCheckbox *c)
{
return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CHECKBOX(c))) != FALSE;
}
static void setChecked(uiCheckbox *cc, int checked)
{
struct checkbox *c = (struct checkbox *) cc;
GtkToggleButton *button;
gboolean active;
active = FALSE;
if (checked)
active = TRUE;
// we need to inhibit sending of ::toggled because this WILL send a ::toggled otherwise
button = GTK_TOGGLE_BUTTON(CHECKBOX(c));
g_signal_handler_block(button, c->onToggledSignal);
gtk_toggle_button_set_active(button, active);
g_signal_handler_unblock(button, c->onToggledSignal);
}
uiCheckbox *uiNewCheckbox(const char *text)
{
struct checkbox *c;
GtkWidget *widget;
c = uiNew(struct checkbox);
uiUnixNewControl(uiControl(c), GTK_TYPE_CHECK_BUTTON,
FALSE, FALSE,
"label", text,
NULL);
widget = GTK_WIDGET(CHECKBOX(c));
g_signal_connect(widget, "destroy", G_CALLBACK(onDestroy), c);
c->onToggledSignal = g_signal_connect(widget, "toggled", G_CALLBACK(onToggled), c);
c->onToggled = defaultOnToggled;
uiCheckbox(c)->Text = getText;
uiCheckbox(c)->SetText = setText;
uiCheckbox(c)->OnToggled = setOnToggled;
uiCheckbox(c)->Checked = getChecked;
uiCheckbox(c)->SetChecked = setChecked;
return uiCheckbox(c);
}

View File

@ -1,45 +0,0 @@
// 8 april 2015
#include "uipriv_unix.h"
struct entry {
uiEntry e;
};
static void onDestroy(GtkWidget *widget, gpointer data)
{
struct entry *e = (struct entry *) data;
uiFree(e);
}
#define ENTRY(e) GTK_ENTRY(uiControlHandle(uiControl(e)))
static char *getText(uiEntry *e)
{
return g_strdup(gtk_entry_get_text(ENTRY(e)));
}
static void setText(uiEntry *e, const char *text)
{
gtk_entry_set_text(ENTRY(e), text);
}
uiEntry *uiNewEntry(void)
{
struct entry *e;
GtkWidget *widget;
e = uiNew(struct entry);
uiUnixNewControl(uiControl(e), GTK_TYPE_ENTRY,
FALSE, FALSE,
NULL);
widget = GTK_WIDGET(ENTRY(e));
g_signal_connect(widget, "destroy", G_CALLBACK(onDestroy), e);
uiEntry(e)->Text = getText;
uiEntry(e)->SetText = setText;
return uiEntry(e);
}

View File

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

View File

@ -1,49 +0,0 @@
// 11 april 2015
#include "uipriv_unix.h"
struct label {
uiLabel l;
};
static void onDestroy(GtkWidget *widget, gpointer data)
{
struct label *l = (struct label *) data;
uiFree(l);
}
#define LABEL(l) GTK_LABEL(uiControlHandle(uiControl(l)))
static char *getText(uiLabel *l)
{
// TODO change g_strdup() to a wrapper function for export in ui_unix.h
return g_strdup(gtk_label_get_text(LABEL(l)));
}
static void setText(uiLabel *l, const char *text)
{
gtk_label_set_text(LABEL(l), text);
}
uiLabel *uiNewLabel(const char *text)
{
struct label *l;
GtkWidget *widget;
l = uiNew(struct label);
uiUnixNewControl(uiControl(l), GTK_TYPE_LABEL,
FALSE, FALSE,
"label", text,
"xalign", 0.0, // note: must be a float constant, otherwise the ... will turn it into an int and we get segfaults on some platforms (thanks ebassi in irc.gimp.net/#gtk+)
// TODO yalign 0?
NULL);
widget = GTK_WIDGET(LABEL(l));
g_signal_connect(widget, "destroy", G_CALLBACK(onDestroy), l);
uiLabel(l)->Text = getText;
uiLabel(l)->SetText = setText;
return uiLabel(l);
}

View File

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

View File

@ -1,225 +0,0 @@
// 7 april 2015
#include "uipriv_unix.h"
typedef struct singleWidget singleWidget;
struct singleWidget {
GtkWidget *widget;
GtkWidget *scrolledWindow;
GtkWidget *immediate; // the widget that is added to the parent container; either widget or scrolledWindow
uiParent *parent;
gboolean userHid;
gboolean containerHid;
gboolean userDisabled;
gboolean containerDisabled;
};
static void singleDestroy(uiControl *c)
{
singleWidget *s = (singleWidget *) (c->Internal);
gtk_widget_destroy(s->immediate);
}
static uintptr_t singleHandle(uiControl *c)
{
singleWidget *s = (singleWidget *) (c->Internal);
return (uintptr_t) (s->widget);
}
static void singleSetParent(uiControl *c, uiParent *parent)
{
singleWidget *s = (singleWidget *) (c->Internal);
uiParent *oldparent;
oldparent = s->parent;
s->parent = parent;
if (oldparent != NULL) {
gtk_container_remove(GTK_CONTAINER(uiParentHandle(oldparent)), s->immediate);
uiParentUpdate(oldparent);
}
if (s->parent != NULL) {
gtk_container_add(GTK_CONTAINER(uiParentHandle(s->parent)), s->immediate);
uiParentUpdate(s->parent);
}
}
static void singlePreferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
{
singleWidget *s = (singleWidget *) (c->Internal);
GtkRequisition natural;
// use the natural size as the minimum size is an *absolute* minimum
// for example, if a label has ellipsizing on, it can be the width of the ellipses, not the text
// there is a warning about height-for-width sizing, but in my tests this isn't an issue
gtk_widget_get_preferred_size(s->widget, NULL, &natural);
*width = natural.width;
*height = natural.height;
}
static void singleResize(uiControl *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height, uiSizing *d)
{
singleWidget *s = (singleWidget *) (c->Internal);
GtkAllocation a;
a.x = x;
a.y = y;
a.width = width;
a.height = height;
gtk_widget_size_allocate(s->immediate, &a);
}
static int singleVisible(uiControl *c)
{
singleWidget *s = (singleWidget *) (c->Internal);
if (s->userHid)
return 0;
return 1;
}
static void singleShow(uiControl *c)
{
singleWidget *s = (singleWidget *) (c->Internal);
s->userHid = FALSE;
if (!s->containerHid) {
gtk_widget_show_all(s->immediate);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
}
static void singleHide(uiControl *c)
{
singleWidget *s = (singleWidget *) (c->Internal);
s->userHid = TRUE;
gtk_widget_hide(s->immediate);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
static void singleContainerShow(uiControl *c)
{
singleWidget *s = (singleWidget *) (c->Internal);
s->containerHid = FALSE;
if (!s->userHid) {
gtk_widget_show_all(s->immediate);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
}
static void singleContainerHide(uiControl *c)
{
singleWidget *s = (singleWidget *) (c->Internal);
s->containerHid = TRUE;
gtk_widget_hide(s->immediate);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
static void singleEnable(uiControl *c)
{
singleWidget *s = (singleWidget *) (c->Internal);
s->userDisabled = FALSE;
if (!s->containerDisabled)
gtk_widget_set_sensitive(s->immediate, TRUE);
}
static void singleDisable(uiControl *c)
{
singleWidget *s = (singleWidget *) (c->Internal);
s->userDisabled = TRUE;
gtk_widget_set_sensitive(s->immediate, FALSE);
}
static void singleContainerEnable(uiControl *c)
{
singleWidget *s = (singleWidget *) (c->Internal);
s->containerDisabled = FALSE;
if (!s->userDisabled)
gtk_widget_set_sensitive(s->immediate, TRUE);
}
static void singleContainerDisable(uiControl *c)
{
singleWidget *s = (singleWidget *) (c->Internal);
s->containerDisabled = TRUE;
gtk_widget_set_sensitive(s->immediate, FALSE);
}
static void onDestroy(GtkWidget *widget, gpointer data)
{
singleWidget *s = (singleWidget *) data;
uiFree(s);
}
void uiUnixNewControl(uiControl *c, GType type, gboolean inScrolledWindow, gboolean scrolledWindowHasBorder, const char *firstProperty, ...)
{
singleWidget *s;
va_list ap;
s = uiNew(singleWidget);
va_start(ap, firstProperty);
s->widget = GTK_WIDGET(g_object_new_valist(type, firstProperty, ap));
va_end(ap);
s->immediate = s->widget;
if (inScrolledWindow) {
s->scrolledWindow = gtk_scrolled_window_new(NULL, NULL);
if (!GTK_IS_SCROLLABLE(s->widget))
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(s->scrolledWindow), s->widget);
else
gtk_container_add(GTK_CONTAINER(s->scrolledWindow), s->widget);
if (scrolledWindowHasBorder)
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(s->scrolledWindow), GTK_SHADOW_IN);
s->immediate = s->scrolledWindow;
}
// we need to keep an extra reference on the immediate widget
// this is so uiControlDestroy() can work regardless of when it is called and who calls it
// without this:
// - end user call works (only one ref)
// - call in uiContainer destructor fails (uiContainer ref freed)
// with this:
// - end user call works (shoudn't be in any container)
// - call in uiContainer works (both refs freed)
// this also ensures singleRemoveParent() works properly
g_object_ref_sink(s->immediate);
// assign s later; we still need it for one more thing
c->Destroy = singleDestroy;
c->Handle = singleHandle;
c->SetParent = singleSetParent;
c->PreferredSize = singlePreferredSize;
c->Resize = singleResize;
c->Visible = singleVisible;
c->Show = singleShow;
c->Hide = singleHide;
c->ContainerShow = singleContainerShow;
c->ContainerHide = singleContainerHide;
c->Enable = singleEnable;
c->Disable = singleDisable;
c->ContainerEnable = singleContainerEnable;
c->ContainerDisable = singleContainerDisable;
// and let's free everything with the immediate widget
// we send s as data instead of c just in case c is gone by then
g_signal_connect(s->immediate, "destroy", G_CALLBACK(onDestroy), s);
// finally, call gtk_widget_show_all() here to set the initial visibility of the widget
gtk_widget_show_all(s->immediate);
c->Internal = s;
}

View File

@ -1,183 +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;
// this is what triggers the resizing of all the children
uiControl *child;
// these are the actual children widgets of the container as far as GTK+ is concerned
GPtrArray *children; // for forall()
intmax_t marginLeft;
intmax_t marginTop;
intmax_t marginRight;
intmax_t marginBottom;
};
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);
if (p->children != NULL) {
g_ptr_array_unref(p->children);
p->children = NULL;
}
if (p->child != NULL) {
uiControlDestroy(p->child);
p->child = NULL;
}
G_OBJECT_CLASS(uipParent_parent_class)->dispose(obj);
}
static void uipParent_finalize(GObject *obj)
{
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)
g_ptr_array_remove(p->children, 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->child == 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->child, 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;
}
static uintptr_t parentHandle(uiParent *p)
{
uipParent *pp = uipParent(p->Internal);
return (uintptr_t) pp;
}
static void parentSetChild(uiParent *p, uiControl *child)
{
uipParent *pp = uipParent(p->Internal);
pp->child = child;
if (pp->child != NULL)
uiControlSetParent(child, p);
}
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->Handle = parentHandle;
p->SetChild = parentSetChild;
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;
}

View File

@ -1,61 +0,0 @@
// 12 april 2015
#include "uipriv_unix.h"
struct tab {
uiTab t;
uiParent **pages;
uintmax_t len;
uintmax_t cap;
};
static void onDestroy(GtkWidget *widget, gpointer data)
{
struct tab *t = (struct tab *) data;
uiFree(t->pages);
uiFree(t);
}
#define TAB(t) GTK_NOTEBOOK(uiControlHandle(uiControl(t)))
#define tabCapGrow 32
static void addPage(uiTab *tt, const char *name, uiControl *child)
{
struct tab *t = (struct tab *) tt;
GtkWidget *notebook;
uiParent *content;
if (t->len >= t->cap) {
t->cap += tabCapGrow;
t->pages = (uiParent **) uiRealloc(t->pages, t->cap * sizeof (uiParent *), "uiParent *[]");
}
notebook = GTK_WIDGET(TAB(t));
content = uiNewParent((uintptr_t) notebook);
uiParentSetChild(content, child);
uiParentUpdate(content);
gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(notebook), GTK_WIDGET(uiParentHandle(content)), name);
t->pages[t->len] = content;
t->len++;
}
uiTab *uiNewTab(void)
{
struct tab *t;
GtkWidget *widget;
t = uiNew(struct tab);
uiUnixNewControl(uiControl(t), GTK_TYPE_NOTEBOOK,
FALSE, FALSE,
NULL);
widget = GTK_WIDGET(TAB(t));
g_signal_connect(widget, "destroy", G_CALLBACK(onDestroy), t);
uiTab(t)->AddPage = addPage;
return uiTab(t);
}

View File

@ -1,14 +0,0 @@
// 6 april 2015
#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_32
#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_32
#define GDK_VERSION_MIN_REQUIRED GDK_VERSION_3_4
#define GDK_VERSION_MAX_ALLOWED GDK_VERSION_3_4
#include <gtk/gtk.h>
#include "../uipriv.h"
#include "../ui_unix.h"
#define gtkXMargin 12
#define gtkYMargin 12
#define widget(c) uiControlHandle(uiControl(c))
#define WIDGET(c) GTK_WIDGET(widget(c))

View File

@ -1,7 +0,0 @@
// 9 april 2015
#include "uipriv_unix.h"
void uiFreeText(char *t)
{
g_free(t);
}

View File

@ -1,137 +0,0 @@
// 6 april 2015
#include "uipriv_unix.h"
struct window {
uiWindow w;
GtkWidget *widget;
uiParent *content;
int (*onClosing)(uiWindow *, void *);
void *onClosingData;
int margined;
};
static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data)
{
struct window *w = (struct window *) data;
// return exact values just in case
if ((*(w->onClosing))(uiWindow(w), w->onClosingData))
return FALSE;
return TRUE;
}
static int defaultOnClosing(uiWindow *w, void *data)
{
return 1;
}
static void onDestroy(GtkWidget *widget, gpointer data)
{
struct window *w = (struct window *) data;
uiFree(w);
}
static void windowDestroy(uiWindow *ww)
{
struct window *w = (struct window *) ww;
gtk_widget_destroy(w->widget);
}
static uintptr_t handle(uiWindow *ww)
{
struct window *w = (struct window *) ww;
return (uintptr_t) (w->widget);
}
static char *getTitle(uiWindow *ww)
{
struct window *w = (struct window *) ww;
return g_strdup(gtk_window_get_title(GTK_WINDOW(w->widget)));
}
static void setTitle(uiWindow *ww, const char *title)
{
struct window *w = (struct window *) ww;
gtk_window_set_title(GTK_WINDOW(w->widget), title);
}
static void show(uiWindow *ww)
{
struct window *w = (struct window *) ww;
// don't use gtk_widget_show_all(); that will override user hidden settings
gtk_widget_show(w->widget);
}
static void hide(uiWindow *ww)
{
struct window *w = (struct window *) ww;
gtk_widget_hide(w->widget);
}
static void setOnClosing(uiWindow *ww, int (*f)(uiWindow *, void *), void *data)
{
struct window *w = (struct window *) ww;
w->onClosing = f;
w->onClosingData = data;
}
static void setChild(uiWindow *ww, uiControl *c)
{
struct window *w = (struct window *) ww;
uiParentSetChild(w->content, c);
uiParentUpdate(w->content);
}
static int margined(uiWindow *ww)
{
struct window *w = (struct window *) ww;
return w->margined;
}
static void setMargined(uiWindow *ww, int margined)
{
struct window *w = (struct window *) ww;
w->margined = margined;
if (w->margined)
uiParentSetMargins(w->content, gtkXMargin, gtkYMargin, gtkXMargin, gtkYMargin);
else
uiParentSetMargins(w->content, 0, 0, 0, 0);
uiParentUpdate(w->content);
}
uiWindow *uiNewWindow(const char *title, int width, int height)
{
struct window *w;
w = uiNew(struct window);
w->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(w->widget), title);
gtk_window_resize(GTK_WINDOW(w->widget), width, height);
g_signal_connect(w->widget, "delete-event", G_CALLBACK(onClosing), w);
g_signal_connect(w->widget, "destroy", G_CALLBACK(onDestroy), w);
w->content = uiNewParent((uintptr_t) (w->widget));
w->onClosing = defaultOnClosing;
uiWindow(w)->Destroy = windowDestroy;
uiWindow(w)->Handle = handle;
uiWindow(w)->Title = getTitle;
uiWindow(w)->SetTitle = setTitle;
uiWindow(w)->Show = show;
uiWindow(w)->Hide = hide;
uiWindow(w)->OnClosing = setOnClosing;
uiWindow(w)->SetChild = setChild;
uiWindow(w)->Margined = margined;
uiWindow(w)->SetMargined = setMargined;
return uiWindow(w);
}

View File

@ -1,33 +0,0 @@
OSCFILES = \
alloc.c \
button.c \
checkbox.c \
comctl32.c \
debug.c \
entry.c \
init.c \
label.c \
main.c \
newcontrol.c \
parent.c \
tab.c \
text.c \
util.c \
window.c
xLDFLAGS += \
-luser32 -lkernel32 -lgdi32 -luxtheme -lmsimg32 -lcomdlg32 -lole32 -loleaut32 -loleacc -luuid
OUT = new.exe
ifeq ($(ARCH),64)
CC = x86_64-w64-mingw32-gcc
RC = x86_64-w64-mingw32-windres
xCFLAGS += -m64
xLDFLAGS += -m64
else
CC = i686-w64-mingw32-gcc
RC = i686-w64-mingw32-windres
xCFLAGS += -m32
xLDFLAGS += -m32
endif

View File

@ -1,49 +0,0 @@
// 4 december 2014
#include "uipriv_windows.h"
// wrappers for allocator of choice
// panics on memory exhausted, undefined on heap corruption or other unreliably-detected malady (see http://stackoverflow.com/questions/28761680/is-there-a-windows-api-memory-allocator-deallocator-i-can-use-that-will-just-giv)
// new memory is set to zero
// passing NULL to tableRealloc() acts like tableAlloc()
// passing NULL to tableFree() is a no-op
void *uiAlloc(size_t size, const char *type)
{
void *out;
out = malloc(size);
if (out == NULL) {
fprintf(stderr, "memory exhausted in uiAlloc() allocating %s\n", type);
abort();
}
ZeroMemory(out, size);
if (options.debugLogAllocations)
fprintf(stderr, "%p alloc %s\n", out, type);
return out;
}
void *uiRealloc(void *p, size_t size, const char *type)
{
void *out;
if (p == NULL)
return uiAlloc(size, type);
out = realloc(p, size);
if (out == NULL) {
fprintf(stderr, "memory exhausted in uiRealloc() reallocating %s\n", type);
abort();
}
// TODO zero the extra memory
if (options.debugLogAllocations)
fprintf(stderr, "%p realloc %p\n", p, out);
return out;
}
void uiFree(void *p)
{
if (p == NULL)
return;
free(p);
if (options.debugLogAllocations)
fprintf(stderr, "%p free\n", p);
}

View File

@ -1,112 +0,0 @@
// 7 april 2015
#include "uipriv_windows.h"
struct button {
uiButton b;
void (*onClicked)(uiButton *, void *);
void *onClickedData;
};
static BOOL onWM_COMMAND(uiControl *c, WORD code, LRESULT *lResult)
{
struct button *b = (struct button *) c;
if (code != BN_CLICKED)
return FALSE;
(*(b->onClicked))(uiButton(b), b->onClickedData);
*lResult = 0;
return TRUE;
}
static BOOL onWM_NOTIFY(uiControl *c, NMHDR *nm, LRESULT *lResult)
{
return FALSE;
}
static void onWM_DESTROY(uiControl *c)
{
struct button *b = (struct button *) c;
uiFree(b);
}
// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
#define buttonHeight 14
static void preferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
{
HWND hwnd;
SIZE size;
hwnd = uiControlHWND(c);
// try the comctl32 version 6 way
size.cx = 0; // explicitly ask for ideal size
size.cy = 0;
if (SendMessageW(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM) (&size)) != FALSE) {
*width = size.cx;
*height = size.cy;
return;
}
// that didn't work; fall back to using Microsoft's metrics
// Microsoft says to use a fixed width for all buttons; this isn't good enough
// use the text width instead, with some edge padding
*width = uiWindowsWindowTextWidth(hwnd) + (2 * GetSystemMetrics(SM_CXEDGE));
*height = uiDlgUnitsToY(buttonHeight, d->sys->baseY);
}
static void defaultOnClicked(uiButton *b, void *data)
{
// do nothing
}
static char *getText(uiButton *b)
{
return uiWindowsControlText(uiControl(b));
}
static void setText(uiButton *b, const char *text)
{
uiWindowsControlSetText(uiControl(b), text);
}
static void setOnClicked(uiButton *bb, void (*f)(uiButton *, void *), void *data)
{
struct button *b = (struct button *) bb;
b->onClicked = f;
b->onClickedData = data;
}
uiButton *uiNewButton(const char *text)
{
struct button *b;
uiWindowsNewControlParams p;
WCHAR *wtext;
b = uiNew(struct button);
p.dwExStyle = 0;
p.lpClassName = L"button";
wtext = toUTF16(text);
p.lpWindowName = wtext;
p.dwStyle = BS_PUSHBUTTON | WS_TABSTOP;
p.hInstance = hInstance;
p.useStandardControlFont = TRUE;
p.onWM_COMMAND = onWM_COMMAND;
p.onWM_NOTIFY = onWM_NOTIFY;
p.onWM_DESTROY = onWM_DESTROY;
uiWindowsNewControl(uiControl(b), &p);
uiFree(wtext);
b->onClicked = defaultOnClicked;
uiControl(b)->PreferredSize = preferredSize;
uiButton(b)->Text = getText;
uiButton(b)->SetText = setText;
uiButton(b)->OnClicked = setOnClicked;
return uiButton(b);
}

View File

@ -1,129 +0,0 @@
// 7 april 2015
#include "uipriv_windows.h"
struct checkbox {
uiCheckbox c;
void (*onToggled)(uiCheckbox *, void *);
void *onToggledData;
};
static BOOL onWM_COMMAND(uiControl *cc, WORD code, LRESULT *lResult)
{
struct checkbox *c = (struct checkbox *) cc;
HWND hwnd;
WPARAM check;
if (code != BN_CLICKED)
return FALSE;
// we didn't use BS_AUTOCHECKBOX (see controls_windows.go) so we have to manage the check state ourselves
hwnd = uiControlHWND(uiControl(c));
check = BST_CHECKED;
if (SendMessage(hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED)
check = BST_UNCHECKED;
SendMessage(hwnd, BM_SETCHECK, check, 0);
(*(c->onToggled))(uiCheckbox(c), c->onToggledData);
*lResult = 0;
return TRUE;
}
static BOOL onWM_NOTIFY(uiControl *c, NMHDR *nm, LRESULT *lResult)
{
return FALSE;
}
static void onWM_DESTROY(uiControl *cc)
{
struct checkbox *c = (struct checkbox *) cc;
uiFree(c);
}
// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
#define checkboxHeight 10
// from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx
#define checkboxXFromLeftOfBoxToLeftOfLabel 12
static void preferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
{
*width = uiDlgUnitsToX(checkboxXFromLeftOfBoxToLeftOfLabel, d->sys->baseX) + uiWindowsWindowTextWidth(uiControlHWND(c));
*height = uiDlgUnitsToY(checkboxHeight, d->sys->baseY);
}
static void defaultOnToggled(uiCheckbox *c, void *data)
{
// do nothing
}
static char *getText(uiCheckbox *c)
{
return uiWindowsControlText(uiControl(c));
}
static void setText(uiCheckbox *c, const char *text)
{
uiWindowsControlSetText(uiControl(c), text);
}
static void setOnToggled(uiCheckbox *cc, void (*f)(uiCheckbox *, void *), void *data)
{
struct checkbox *c = (struct checkbox *) cc;
c->onToggled = f;
c->onToggledData = data;
}
static int getChecked(uiCheckbox *c)
{
HWND hwnd;
hwnd = uiControlHWND(uiControl(c));
return SendMessage(hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED;
}
static void setChecked(uiCheckbox *c, int checked)
{
HWND hwnd;
WPARAM check;
hwnd = uiControlHWND(uiControl(c));
check = BST_CHECKED;
if (!checked)
check = BST_UNCHECKED;
SendMessage(hwnd, BM_SETCHECK, check, 0);
}
uiCheckbox *uiNewCheckbox(const char *text)
{
struct checkbox *c;
uiWindowsNewControlParams p;
WCHAR *wtext;
c = uiNew(struct checkbox);
p.dwExStyle = 0;
p.lpClassName = L"button";
wtext = toUTF16(text);
p.lpWindowName = wtext;
p.dwStyle = BS_CHECKBOX | WS_TABSTOP;
p.hInstance = hInstance;
p.useStandardControlFont = TRUE;
p.onWM_COMMAND = onWM_COMMAND;
p.onWM_NOTIFY = onWM_NOTIFY;
p.onWM_DESTROY = onWM_DESTROY;
uiWindowsNewControl(uiControl(c), &p);
uiFree(wtext);
c->onToggled = defaultOnToggled;
uiControl(c)->PreferredSize = preferredSize;
uiCheckbox(c)->Text = getText;
uiCheckbox(c)->SetText = setText;
uiCheckbox(c)->OnToggled = setOnToggled;
uiCheckbox(c)->Checked = getChecked;
uiCheckbox(c)->SetChecked = setChecked;
return uiCheckbox(c);
}

View File

@ -1,105 +0,0 @@
// 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;
}

View File

@ -1,111 +0,0 @@
// 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

View File

@ -1,69 +0,0 @@
// 8 april 2015
#include "uipriv_windows.h"
struct entry {
uiEntry e;
};
static BOOL onWM_COMMAND(uiControl *c, WORD code, LRESULT *lResult)
{
return FALSE;
}
static BOOL onWM_NOTIFY(uiControl *c, NMHDR *nm, LRESULT *lResult)
{
return FALSE;
}
static void onWM_DESTROY(uiControl *c)
{
struct entry *e = (struct entry *) c;
uiFree(e);
}
// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
#define entryWidth 107 /* this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary */
#define entryHeight 14
static void preferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
{
*width = uiDlgUnitsToX(entryWidth, d->sys->baseX);
*height = uiDlgUnitsToY(entryHeight, d->sys->baseY);
}
static char *getText(uiEntry *e)
{
return uiWindowsControlText(uiControl(e));
}
static void setText(uiEntry *e, const char *text)
{
uiWindowsControlSetText(uiControl(e), text);
}
uiEntry *uiNewEntry(void)
{
struct entry *e;
uiWindowsNewControlParams p;
e = uiNew(struct entry);
p.dwExStyle = WS_EX_CLIENTEDGE;
p.lpClassName = L"edit";
p.lpWindowName = L"";
p.dwStyle = ES_AUTOHSCROLL | ES_LEFT | ES_NOHIDESEL | WS_TABSTOP;
p.hInstance = hInstance;
p.useStandardControlFont = TRUE;
p.onWM_COMMAND = onWM_COMMAND;
p.onWM_NOTIFY = onWM_NOTIFY;
p.onWM_DESTROY = onWM_DESTROY;
uiWindowsNewControl(uiControl(e), &p);
uiControl(e)->PreferredSize = preferredSize;
uiEntry(e)->Text = getText;
uiEntry(e)->SetText = setText;
return uiEntry(e);
}

View File

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

View File

@ -1,73 +0,0 @@
// 11 april 2015
#include "uipriv_windows.h"
struct label {
uiLabel l;
};
static BOOL onWM_COMMAND(uiControl *c, WORD code, LRESULT *lResult)
{
return FALSE;
}
static BOOL onWM_NOTIFY(uiControl *c, NMHDR *nm, LRESULT *lResult)
{
return FALSE;
}
static void onWM_DESTROY(uiControl *c)
{
struct label *l = (struct label *) c;
uiFree(l);
}
// via http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
#define labelHeight 8
static void preferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
{
*width = uiWindowsWindowTextWidth(uiControlHWND(c));
*height = uiDlgUnitsToY(labelHeight, d->sys->baseY);
}
static char *getText(uiLabel *l)
{
return uiWindowsControlText(uiControl(l));
}
static void setText(uiLabel *l, const char *text)
{
uiWindowsControlSetText(uiControl(l), text);
}
uiLabel *uiNewLabel(const char *text)
{
struct label *l;
uiWindowsNewControlParams p;
WCHAR *wtext;
l = uiNew(struct label);
p.dwExStyle = 0;
p.lpClassName = L"static";
wtext = toUTF16(text);
p.lpWindowName = wtext;
// SS_LEFTNOWORDWRAP clips text past the end; SS_NOPREFIX avoids accelerator translation
// controls are vertically aligned to the top by default (thanks Xeek in irc.freenode.net/#winapi)
p.dwStyle = SS_LEFTNOWORDWRAP | SS_NOPREFIX;
p.hInstance = hInstance;
p.useStandardControlFont = TRUE;
p.onWM_COMMAND = onWM_COMMAND;
p.onWM_NOTIFY = onWM_NOTIFY;
p.onWM_DESTROY = onWM_DESTROY;
uiWindowsNewControl(uiControl(l), &p);
uiFree(wtext);
uiControl(l)->PreferredSize = preferredSize;
uiLabel(l)->Text = getText;
uiLabel(l)->SetText = setText;
return uiLabel(l);
}

View File

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

View File

@ -1,237 +0,0 @@
// 6 april 2015
#include "uipriv_windows.h"
typedef struct singleHWND singleHWND;
struct singleHWND {
HWND hwnd;
BOOL (*onWM_COMMAND)(uiControl *, WORD, LRESULT *);
BOOL (*onWM_NOTIFY)(uiControl *, NMHDR *, LRESULT *);
void (*onWM_DESTROY)(uiControl *);
uiParent *parent;
BOOL userHid;
BOOL containerHid;
BOOL userDisabled;
BOOL containerDisabled;
};
static void singleDestroy(uiControl *c)
{
singleHWND *s = (singleHWND *) (c->Internal);
if (DestroyWindow(s->hwnd) == 0)
logLastError("error destroying control in singleDestroy()");
// the data structures are destroyed in the subclass procedure
}
static uintptr_t singleHandle(uiControl *c)
{
singleHWND *s = (singleHWND *) (c->Internal);
return (uintptr_t) (s->hwnd);
}
static void singleSetParent(uiControl *c, uiParent *parent)
{
singleHWND *s = (singleHWND *) (c->Internal);
uiParent *oldparent;
HWND newParentHWND;
oldparent = s->parent;
s->parent = parent;
newParentHWND = initialParent;
if (s->parent != NULL)
newParentHWND = uiParentHWND(s->parent);
if (SetParent(s->hwnd, newParentHWND) == NULL)
logLastError("error setting control parent in singleSetParent()");
if (oldparent != NULL)
uiParentUpdate(oldparent);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
static void singleResize(uiControl *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height, uiSizing *d)
{
singleHWND *s = (singleHWND *) (c->Internal);
if (MoveWindow(s->hwnd, x, y, width, height, TRUE) == 0)
logLastError("error moving control in singleResize()");
}
static int singleVisible(uiControl *c)
{
singleHWND *s = (singleHWND *) (c->Internal);
if (s->userHid)
return 0;
return 1;
}
static void singleShow(uiControl *c)
{
singleHWND *s = (singleHWND *) (c->Internal);
s->userHid = FALSE;
if (!s->containerHid) {
ShowWindow(s->hwnd, SW_SHOW);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
}
static void singleHide(uiControl *c)
{
singleHWND *s = (singleHWND *) (c->Internal);
s->userHid = TRUE;
ShowWindow(s->hwnd, SW_HIDE);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
static void singleContainerShow(uiControl *c)
{
singleHWND *s = (singleHWND *) (c->Internal);
s->containerHid = FALSE;
if (!s->userHid) {
ShowWindow(s->hwnd, SW_SHOW);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
}
static void singleContainerHide(uiControl *c)
{
singleHWND *s = (singleHWND *) (c->Internal);
s->containerHid = TRUE;
ShowWindow(s->hwnd, SW_HIDE);
if (s->parent != NULL)
uiParentUpdate(s->parent);
}
static void singleEnable(uiControl *c)
{
singleHWND *s = (singleHWND *) (c->Internal);
s->userDisabled = FALSE;
if (!s->containerDisabled)
EnableWindow(s->hwnd, TRUE);
}
static void singleDisable(uiControl *c)
{
singleHWND *s = (singleHWND *) (c->Internal);
s->userDisabled = TRUE;
EnableWindow(s->hwnd, FALSE);
}
static void singleContainerEnable(uiControl *c)
{
singleHWND *s = (singleHWND *) (c->Internal);
s->containerDisabled = FALSE;
if (!s->userDisabled)
EnableWindow(s->hwnd, TRUE);
}
static void singleContainerDisable(uiControl *c)
{
singleHWND *s = (singleHWND *) (c->Internal);
s->containerDisabled = TRUE;
EnableWindow(s->hwnd, FALSE);
}
static LRESULT CALLBACK singleSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
uiControl *c = (uiControl *) dwRefData;
singleHWND *s = (singleHWND *) (c->Internal);
LRESULT lResult;
switch (uMsg) {
case msgCOMMAND:
if ((*(s->onWM_COMMAND))(c, HIWORD(wParam), &lResult) != FALSE)
return lResult;
break;
case msgNOTIFY:
if ((*(s->onWM_NOTIFY))(c, (NMHDR *) lParam, &lResult) != FALSE)
return lResult;
break;
case WM_DESTROY:
(*(s->onWM_DESTROY))(c);
uiFree(s);
break;
case WM_NCDESTROY:
if ((*fv_RemoveWindowSubclass)(hwnd, singleSubclassProc, uIdSubclass) == FALSE)
logLastError("error removing Windows control subclass in singleSubclassProc()");
break;
}
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
}
void uiWindowsNewControl(uiControl *c, uiWindowsNewControlParams *p)
{
singleHWND *s;
s = uiNew(singleHWND);
s->hwnd = CreateWindowExW(p->dwExStyle,
p->lpClassName, p->lpWindowName,
p->dwStyle | WS_CHILD | WS_VISIBLE,
0, 0,
// use a nonzero initial size just in case some control breaks with a zero initial size
100, 100,
initialParent, NULL, p->hInstance, NULL);
if (s->hwnd == NULL)
logLastError("error creating control in uiWindowsNewControl()");
s->onWM_COMMAND = p->onWM_COMMAND;
s->onWM_NOTIFY = p->onWM_NOTIFY;
s->onWM_DESTROY = p->onWM_DESTROY;
c->Destroy = singleDestroy;
c->Handle = singleHandle;
c->SetParent = singleSetParent;
c->Resize = singleResize;
c->Visible = singleVisible;
c->Show = singleShow;
c->Hide = singleHide;
c->ContainerShow = singleContainerShow;
c->ContainerHide = singleContainerHide;
c->Enable = singleEnable;
c->Disable = singleDisable;
c->ContainerEnable = singleContainerEnable;
c->ContainerDisable = singleContainerDisable;
if (p->useStandardControlFont)
SendMessageW(s->hwnd, WM_SETFONT, (WPARAM) hMessageFont, (LPARAM) TRUE);
if ((*fv_SetWindowSubclass)(s->hwnd, singleSubclassProc, 0, (DWORD_PTR) c) == FALSE)
logLastError("error subclassing Windows control in uiWindowsNewControl()");
c->Internal = s;
}
char *uiWindowsControlText(uiControl *c)
{
singleHWND *s = (singleHWND *) (c->Internal);
WCHAR *wtext;
char *text;
wtext = windowText(s->hwnd);
text = toUTF8(wtext);
uiFree(wtext);
return text;
}
void uiWindowsControlSetText(uiControl *c, const char *text)
{
singleHWND *s = (singleHWND *) (c->Internal);
WCHAR *wtext;
wtext = toUTF16(text);
if (SetWindowTextW(s->hwnd, wtext) == 0)
logLastError("error setting control text in uiWindowsControlSetText()");
uiFree(wtext);
}

View File

@ -1,268 +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] <ZeroOne> There's flickering between tabs
// - with CTLCOLOR handler: [12:24] <ZeroOne> And setting the button text blanked out the entire GUI until I ran my mouse over the elements / [12:25] <ZeroOne> https://dl.dropboxusercontent.com/u/15144168/GUI%20stuff.png / [12:41] <ZeroOne> 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;
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
if (windowClassOf(parent, L"button", NULL) != 0)
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);
}
struct parent {
HWND hwnd;
uiControl *child;
intmax_t marginLeft;
intmax_t marginTop;
intmax_t marginRight;
intmax_t marginBottom;
};
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;
// 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:
// no need to explicitly destroy children; they're already gone by this point (and so are their data structures; they clean up after themselves)
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->child == 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->child, pp->hwnd, r, margin);
return 0;
}
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 uintptr_t parentHandle(uiParent *p)
{
struct parent *pp = (struct parent *) (p->Internal);
return (uintptr_t) (pp->hwnd);
}
static void parentSetChild(uiParent *p, uiControl *child)
{
struct parent *pp = (struct parent *) (p->Internal);
pp->child = child;
if (pp->child != NULL)
uiControlSetParent(child, p);
}
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->Handle = parentHandle;
p->SetChild = parentSetChild;
p->SetMargins = parentSetMargins;
p->Update = parentUpdate;
return p;
}

View File

@ -1,190 +0,0 @@
// 12 april 2015
#include "uipriv_windows.h"
// TODO
// - tab change notifications aren't being sent on wine (anymore...? TODO)
// - tell wine developers that tab controls do respond to parent changes on real windows (at least comctl6 tab controls do)
struct tab {
uiTab t;
uiParent **pages;
uintmax_t len;
uintmax_t cap;
};
static BOOL onWM_COMMAND(uiControl *c, WORD code, LRESULT *lResult)
{
return FALSE;
}
// we have to handle hiding and showing of tab pages ourselves
static BOOL onWM_NOTIFY(uiControl *c, NMHDR *nm, LRESULT *lResult)
{
struct tab *t = (struct tab *) c;
LRESULT n;
switch (nm->code) {
case TCN_SELCHANGING:
n = SendMessageW(uiControlHWND(c), TCM_GETCURSEL, 0, 0);
if (n != (LRESULT) (-1)) // if we're changing to a real tab
ShowWindow(uiParentHWND(t->pages[n]), SW_HIDE);
*lResult = FALSE; // and allow the change
return TRUE;
case TCN_SELCHANGE:
n = SendMessageW(uiControlHWND(c), TCM_GETCURSEL, 0, 0);
if (n != (LRESULT) (-1)) { // if we're changing to a real tab
ShowWindow(uiParentHWND(t->pages[n]), SW_SHOW);
// because we only resize the current child on resize, we'll need to trigger an update here
// don't call uiParentUpdate(); doing that won't size the content area (so we'll still have a 0x0 content area, for instance)
SendMessageW(uiControlHWND(c), msgUpdateChild, 0, 0);
}
*lResult = 0;
return TRUE;
}
return FALSE;
}
static void onWM_DESTROY(uiControl *c)
{
struct tab *t = (struct tab *) c;
// no need to worry about freeing the pages themselves; they'll destroy themselves after we return
uiFree(t->pages);
uiFree(t);
}
static void preferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
{
// TODO
}
// common code for resizes
static void resizeTab(uiControl *c, LONG width, LONG height)
{
struct tab *t = (struct tab *) c;
HWND hwnd;
LRESULT n;
RECT r;
hwnd = uiControlHWND(c);
n = SendMessageW(hwnd, TCM_GETCURSEL, 0, 0);
if (n == (LRESULT) (-1)) // no child selected; do nothing
return;
// make a rect at (0, 0) of the given window size
// this should give us the correct client coordinates
r.left = 0;
r.top = 0;
r.right = width;
r.bottom = height;
// convert to the display rectangle
SendMessageW(hwnd, TCM_ADJUSTRECT, FALSE, (LPARAM) (&r));
if (MoveWindow(uiParentHWND(t->pages[n]), r.left, r.top, r.right - r.left, r.bottom - r.top, TRUE) == 0)
logLastError("error resizing current tab page in resizeTab()");
}
// and finally, because we have to resize parents, we have to handle resizes and updates
static LRESULT CALLBACK tabSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
uiControl *c = (uiControl *) dwRefData;
WINDOWPOS *wp = (WINDOWPOS *) lParam;
LRESULT lResult;
RECT r;
switch (uMsg) {
case WM_WINDOWPOSCHANGED:
if ((wp->flags & SWP_NOSIZE) != 0)
break;
// first, let the tab control handle it
lResult = (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
// we have the window rect width as part of the WINDOWPOS; resize
resizeTab(c, wp->cx, wp->cy);
return lResult;
case msgUpdateChild:
if (GetWindowRect(uiControlHWND(c), &r) == 0)
logLastError("error getting Tab window rect for synthesized resize message in tabSubProc()");
// these are in screen coordinates, which match what WM_WINDOWPOSCHANGED gave us (thanks TODOTODOTODOTODOTODOTODOTODO)
resizeTab(c, r.right - r.left, r.bottom - r.top);
return 0;
case WM_NCDESTROY:
if ((*fv_RemoveWindowSubclass)(hwnd, tabSubProc, uIdSubclass) == FALSE)
logLastError("error removing Tab resize handling subclass in tabSubProc()");
break;
}
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
}
#define tabCapGrow 32
void addPage(uiTab *tt, const char *name, uiControl *child)
{
struct tab *t = (struct tab *) tt;
HWND hwnd;
TCITEMW item;
LRESULT n;
uiParent *parent;
WCHAR *wname;
if (t->len >= t->cap) {
t->cap += tabCapGrow;
t->pages = (uiParent **) uiRealloc(t->pages, t->cap * sizeof (uiParent *), "uiParent *[]");
}
hwnd = uiControlHWND(uiControl(t));
n = SendMessageW(hwnd, TCM_GETITEMCOUNT, 0, 0);
parent = uiNewParent((uintptr_t) hwnd);
uiParentSetChild(parent, child);
uiParentUpdate(parent);
if (n != 0) // if this isn't the first page, we have to hide the other controls
ShowWindow(uiParentHWND(parent), SW_HIDE);
t->pages[t->len] = parent;
t->len++;
ZeroMemory(&item, sizeof (TCITEMW));
item.mask = TCIF_TEXT;
wname = toUTF16(name);
item.pszText = wname;
// MSDN's example code uses the first invalid index directly for this
if (SendMessageW(hwnd, TCM_INSERTITEM, (WPARAM) n, (LPARAM) (&item)) == (LRESULT) -1)
logLastError("error adding tab to Tab in uiTabAddPage()");
uiFree(wname);
// if this is the first tab, Windows will automatically show it /without/ sending a TCN_SELCHANGE notification
// (TODO verify that)
// so we need to manually resize the tab ourselves
// don't use uiUpdateParent() for the same reason as in the TCN_SELCHANGE handler
SendMessageW(hwnd, msgUpdateChild, 0, 0);
}
uiTab *uiNewTab(void)
{
struct tab *t;
uiWindowsNewControlParams p;
HWND hwnd;
t = uiNew(struct tab);
p.dwExStyle = 0; // don't set WS_EX_CONTROLPARENT yet; we do that dynamically in the message loop (see main_windows.c)
p.lpClassName = WC_TABCONTROLW;
p.lpWindowName = L"";
p.dwStyle = TCS_TOOLTIPS | WS_TABSTOP;
p.hInstance = hInstance;
p.useStandardControlFont = TRUE;
p.onWM_COMMAND = onWM_COMMAND;
p.onWM_NOTIFY = onWM_NOTIFY;
p.onWM_DESTROY = onWM_DESTROY;
uiWindowsNewControl(uiControl(t), &p);
hwnd = uiControlHWND(uiControl(t));
if ((*fv_SetWindowSubclass)(hwnd, tabSubProc, 0, (DWORD_PTR) t) == FALSE)
logLastError("error subclassing Tab to give it its own resize handler in uiNewTab()");
uiControl(t)->PreferredSize = preferredSize;
uiTab(t)->AddPage = addPage;
return uiTab(t);
}

View File

@ -1,55 +0,0 @@
// 9 april 2015
#include "uipriv_windows.h"
// see http://stackoverflow.com/a/29556509/3408572
#define MBTWC(str, wstr, bufsiz) MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, bufsiz)
WCHAR *toUTF16(const char *str)
{
WCHAR *wstr;
int n;
n = MBTWC(str, NULL, 0);
if (n == 0)
logLastError("error figuring out number of characters to convert to in toUTF16()");
wstr = (WCHAR *) uiAlloc(n * sizeof (WCHAR), "WCHAR[]");
if (MBTWC(str, wstr, n) != n)
logLastError("error converting from UTF-8 to UTF-16 in toUTF16()");
return wstr;
}
#define WCTMB(wstr, str, bufsiz) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, bufsiz, NULL, NULL)
char *toUTF8(const WCHAR *wstr)
{
char *str;
int n;
n = WCTMB(wstr, NULL, 0);
if (n == 0)
logLastError("error figuring out number of characters to convert to in toUTF8()");
str = (char *) uiAlloc(n * sizeof (char), "char[]");
if (WCTMB(wstr, str, n) != n)
logLastError("error converting from UTF-16 to UTF-8 in toUTFF8()");
return str;
}
WCHAR *windowText(HWND hwnd)
{
LRESULT n;
WCHAR *text;
n = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0);
// WM_GETTEXTLENGTH does not include the null terminator
text = (WCHAR *) uiAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]");
// note the comparison: the size includes the null terminator, but the return does not
if (GetWindowTextW(hwnd, text, n + 1) != n)
logLastError("error getting window text in windowText()");
return text;
}
void uiFreeText(char *text)
{
uiFree(text);
}

View File

@ -1,69 +0,0 @@
// 6 january 2015
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
#define CINTERFACE
#define COBJMACROS
// see https://github.com/golang/go/issues/9916#issuecomment-74812211
#define INITGUID
// get Windows version right; right now Windows XP
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_WINDOWS 0x0501 /* according to Microsoft's winperf.h */
#define _WIN32_IE 0x0600 /* according to Microsoft's sdkddkver.h */
#define NTDDI_VERSION 0x05010000 /* according to Microsoft's sdkddkver.h */
#include <windows.h>
#include <commctrl.h>
#include <stdint.h>
#include <uxtheme.h>
#include <string.h>
#include <wchar.h>
#include <windowsx.h>
#include <vsstyle.h>
#include <vssym32.h>
#include <stdarg.h>
#include <oleacc.h>
#include <stdio.h>
#include "../uipriv.h"
#include "../ui_windows.h"
// ui internal window messages
enum {
// redirected WM_COMMAND and WM_NOTIFY
msgCOMMAND = WM_APP + 0x40, // start offset just to be safe
msgNOTIFY,
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)
};
// debug_windows.c
extern HRESULT logLastError(const char *);
extern HRESULT logHRESULT(const char *, HRESULT);
extern HRESULT logMemoryExhausted(const char *);
// init_windows.c
extern HINSTANCE hInstance;
extern int nCmdShow;
extern HFONT hMessageFont;
extern HBRUSH hollowBrush;
// util_windows.c
extern int windowClassOf(HWND, ...);
// text_windows.c
extern WCHAR *toUTF16(const char *);
extern char *toUTF8(const WCHAR *);
extern WCHAR *windowText(HWND);
// comctl32_windows.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_windows.c
extern ATOM registerWindowClass(HICON, HCURSOR);
// parent_windows.c
extern HWND initialParent;
extern const char *initParent(HICON, HCURSOR);

View File

@ -1,73 +0,0 @@
// 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;
}

View File

@ -1,216 +0,0 @@
// 6 april 2015
#include "uipriv_windows.h"
struct window {
uiWindow w;
HWND hwnd;
uiParent *content;
BOOL shownOnce;
int (*onClosing)(uiWindow *, void *);
void *onClosingData;
int margined;
};
#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;
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_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 = uiParentHWND(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))
return 0;
break; // fall through to DefWindowProcW()
case WM_DESTROY:
// no need to free the child ourselves; it'll destroy itself after we leave this handler
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;
DestroyWindow(w->hwnd);
}
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;
uiParentSetChild(w->content, c);
// don't call uiParentUpdate(); 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)
uiParentSetMargins(w->content, windowMargin, windowMargin, windowMargin, windowMargin);
else
uiParentSetMargins(w->content, 0, 0, 0, 0);
uiParentUpdate(w->content);
}
uiWindow *uiNewWindow(const char *title, int width, int height)
{
struct window *w;
RECT adjust;
WCHAR *wtitle;
w = uiNew(struct window);
w->onClosing = defaultOnClosing;
adjust.left = 0;
adjust.top = 0;
adjust.right = width;
adjust.bottom = height;
if (AdjustWindowRectEx(&adjust, style, FALSE, 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 = uiNewParent((uintptr_t) (w->hwnd));
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);
}