Split the rewrite into a new repository.
This commit is contained in:
parent
de9d72299f
commit
e34c561ed5
|
@ -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:
|
||||
|
|
|
@ -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
|
27
new/TODO.md
27
new/TODO.md
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -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];
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
398
new/stack.c
398
new/stack.c
|
@ -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;
|
||||
}
|
337
new/test.c
337
new/test.c
|
@ -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;
|
||||
}
|
161
new/ui.idl
161
new/ui.idl
|
@ -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";
|
||||
|
||||
};
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
10
new/uipriv.h
10
new/uipriv.h
|
@ -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 *);
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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))
|
|
@ -1,7 +0,0 @@
|
|||
// 9 april 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
void uiFreeText(char *t)
|
||||
{
|
||||
g_free(t);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue