summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--area_darwin.go40
-rw-r--r--area_unix.go38
-rw-r--r--area_windows.c2
-rw-r--r--area_windows.go47
-rw-r--r--basicctrls.go18
-rw-r--r--button_darwin.go36
-rw-r--r--button_unix.go28
-rw-r--r--button_windows.go48
-rw-r--r--checkbox_darwin.go40
-rw-r--r--checkbox_unix.go28
-rw-r--r--checkbox_windows.go52
-rw-r--r--common_windows.c22
-rw-r--r--container.go32
-rw-r--r--container_darwin.go48
-rw-r--r--container_darwin.m28
-rw-r--r--container_unix.c1
-rw-r--r--container_unix.go59
-rw-r--r--container_windows.c25
-rw-r--r--container_windows.go77
-rw-r--r--control.go36
-rw-r--r--control_darwin.go56
-rw-r--r--control_unix.go119
-rw-r--r--control_windows.go62
-rw-r--r--grid.go94
-rw-r--r--group_darwin.go48
-rw-r--r--group_unix.go45
-rw-r--r--group_windows.go94
-rw-r--r--label_darwin.go46
-rw-r--r--label_unix.go42
-rw-r--r--label_windows.go62
-rw-r--r--objc_darwin.h1
-rw-r--r--simplegrid.go100
-rw-r--r--stack.go86
-rw-r--r--tab_darwin.go50
-rw-r--r--tab_unix.go43
-rw-r--r--tab_windows.go75
-rw-r--r--table_darwin.go39
-rw-r--r--table_unix.go29
-rw-r--r--table_windows.go57
-rw-r--r--textfield_darwin.go39
-rw-r--r--textfield_unix.go32
-rw-r--r--textfield_windows.go48
-rw-r--r--winapi_windows.h6
-rw-r--r--window.go5
-rw-r--r--window_darwin.go29
-rw-r--r--window_darwin.m5
-rw-r--r--window_unix.go32
-rw-r--r--window_windows.c2
-rw-r--r--window_windows.go30
-rw-r--r--yz_repaint_test.go1
-rw-r--r--zz_test.go57
51 files changed, 920 insertions, 1219 deletions
diff --git a/area_darwin.go b/area_darwin.go
index 1b2dd92..6288ac9 100644
--- a/area_darwin.go
+++ b/area_darwin.go
@@ -15,8 +15,7 @@ import "C"
type area struct {
*areabase
- _id C.id
- scroller *scroller
+ *scroller
textfield C.id
textfielddone *event
}
@@ -26,11 +25,12 @@ func newArea(ab *areabase) Area {
areabase: ab,
textfielddone: newEvent(),
}
- a._id = C.newArea(unsafe.Pointer(a))
- a.scroller = newScroller(a._id, false) // no border on Area
+ id := C.newArea(unsafe.Pointer(a))
+ a.scroller = newScroller(id, false) // no border on Area
+ a.fpreferredSize = a.xpreferredSize
a.SetSize(a.width, a.height)
a.textfield = C.newTextField()
- C.areaSetTextField(a._id, a.textfield)
+ C.areaSetTextField(a.id, a.textfield)
return a
}
@@ -38,7 +38,7 @@ func (a *area) SetSize(width, height int) {
a.width = width
a.height = height
// set the frame size to set the area's effective size on the Cocoa side
- C.moveControl(a._id, 0, 0, C.intptr_t(a.width), C.intptr_t(a.height))
+ C.moveControl(a.id, 0, 0, C.intptr_t(a.width), C.intptr_t(a.height))
}
func (a *area) Repaint(r image.Rectangle) {
@@ -52,18 +52,18 @@ func (a *area) Repaint(r image.Rectangle) {
s.y = C.intptr_t(r.Min.Y)
s.width = C.intptr_t(r.Dx())
s.height = C.intptr_t(r.Dy())
- C.areaRepaint(a._id, s)
+ C.areaRepaint(a.id, s)
}
func (a *area) RepaintAll() {
- C.areaRepaintAll(a._id)
+ C.areaRepaintAll(a.id)
}
func (a *area) OpenTextFieldAt(x, y int) {
if x < 0 || x >= a.width || y < 0 || y >= a.height {
panic(fmt.Errorf("point (%d,%d) outside Area in Area.OpenTextFieldAt()", x, y))
}
- C.areaTextFieldOpen(a._id, a.textfield, C.intptr_t(x), C.intptr_t(y))
+ C.areaTextFieldOpen(a.id, a.textfield, C.intptr_t(x), C.intptr_t(y))
}
func (a *area) TextFieldText() string {
@@ -238,27 +238,7 @@ func areaView_flagsChanged(self C.id, e C.id, data unsafe.Pointer) C.BOOL {
return sendKeyEvent(self, ke, data)
}
-func (a *area) id() C.id {
- return a._id
-}
-
-func (a *area) setParent(p *controlParent) {
- a.scroller.setParent(p)
-}
-
-func (a *area) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(a, x, y, width, height, d)
-}
-
-func (a *area) preferredSize(d *sizing) (width, height int) {
+func (a *area) xpreferredSize(d *sizing) (width, height int) {
// the preferred size of an Area is its size
return a.width, a.height
}
-
-func (a *area) commitResize(c *allocation, d *sizing) {
- a.scroller.commitResize(c, d)
-}
-
-func (a *area) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(a, d)
-}
diff --git a/area_unix.go b/area_unix.go
index 2e36628..e434d50 100644
--- a/area_unix.go
+++ b/area_unix.go
@@ -34,9 +34,8 @@ import "C"
type area struct {
*areabase
- _widget *C.GtkWidget
+ *scroller
drawingarea *C.GtkDrawingArea
- scroller *scroller
clickCounter *clickCounter
@@ -59,7 +58,6 @@ func newArea(ab *areabase) Area {
textfieldw := C.gtk_entry_new()
a := &area{
areabase: ab,
- _widget: widget,
drawingarea: (*C.GtkDrawingArea)(unsafe.Pointer(widget)),
scroller: newScroller(widget, false, false, true), // not natively scrollable; no border; have an overlay for OpenTextFieldAt()
clickCounter: new(clickCounter),
@@ -67,6 +65,7 @@ func newArea(ab *areabase) Area {
textfield: (*C.GtkEntry)(unsafe.Pointer(textfieldw)),
textfielddone: newEvent(),
}
+ a.fpreferredSize = a.xpreferredSize
for _, c := range areaCallbacks {
g_signal_connect(
C.gpointer(unsafe.Pointer(a.drawingarea)),
@@ -75,9 +74,9 @@ func newArea(ab *areabase) Area {
C.gpointer(unsafe.Pointer(a)))
}
a.SetSize(a.width, a.height)
- C.gtk_overlay_add_overlay(a.scroller.overlay, a.textfieldw)
+ C.gtk_overlay_add_overlay(a.scroller.overlayoverlay, a.textfieldw)
g_signal_connect(
- C.gpointer(unsafe.Pointer(a.scroller.overlay)),
+ C.gpointer(unsafe.Pointer(a.scroller.overlayoverlay)),
"get-child-position",
area_get_child_position_callback,
C.gpointer(unsafe.Pointer(a)))
@@ -103,7 +102,7 @@ func newArea(ab *areabase) Area {
func (a *area) SetSize(width, height int) {
a.width = width
a.height = height
- C.gtk_widget_set_size_request(a._widget, C.gint(a.width), C.gint(a.height))
+ C.gtk_widget_set_size_request(a.widget, C.gint(a.width), C.gint(a.height))
}
func (a *area) Repaint(r image.Rectangle) {
@@ -111,11 +110,11 @@ func (a *area) Repaint(r image.Rectangle) {
if r.Empty() {
return
}
- C.gtk_widget_queue_draw_area(a._widget, C.gint(r.Min.X), C.gint(r.Min.Y), C.gint(r.Dx()), C.gint(r.Dy()))
+ C.gtk_widget_queue_draw_area(a.widget, C.gint(r.Min.X), C.gint(r.Min.Y), C.gint(r.Dx()), C.gint(r.Dy()))
}
func (a *area) RepaintAll() {
- C.gtk_widget_queue_draw(a._widget)
+ C.gtk_widget_queue_draw(a.widget)
}
func (a *area) OpenTextFieldAt(x, y int) {
@@ -492,28 +491,7 @@ var modonlykeys = map[C.guint]Modifiers{
C.GDK_KEY_Super_R: Super,
}
-func (a *area) widget() *C.GtkWidget {
- return a._widget
-}
-
-func (a *area) setParent(p *controlParent) {
- a.scroller.setParent(p)
-}
-
-func (a *area) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(a, x, y, width, height, d)
-}
-
-func (a *area) preferredSize(d *sizing) (width, height int) {
+func (a *area) xpreferredSize(d *sizing) (width, height int) {
// the preferred size of an Area is its size
return a.width, a.height
}
-
-func (a *area) commitResize(c *allocation, d *sizing) {
- a.scroller.commitResize(c, d)
-}
-
-func (a *area) getAuxResizeInfo(d *sizing) {
- // a Label to the left of an Area should be vertically aligned to the top
- d.shouldVAlignTop = true
-}
diff --git a/area_windows.c b/area_windows.c
index 115997b..31f705c 100644
--- a/area_windows.c
+++ b/area_windows.c
@@ -321,7 +321,7 @@ static LRESULT CALLBACK areaWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM
uintptr_t heldButtons = (uintptr_t) wParam;
LRESULT lResult;
- data = getWindowData(hwnd, uMsg, wParam, lParam, &lResult, storeAreaHWND);
+ data = getWindowData(hwnd, uMsg, wParam, lParam, &lResult);
if (data == NULL)
return lResult;
switch (uMsg) {
diff --git a/area_windows.go b/area_windows.go
index d95beb1..bf4b9e9 100644
--- a/area_windows.go
+++ b/area_windows.go
@@ -15,7 +15,7 @@ import "C"
type area struct {
*areabase
- _hwnd C.HWND
+ *controlSingleHWND
clickCounter *clickCounter
@@ -39,9 +39,10 @@ func newArea(ab *areabase) Area {
clickCounter: new(clickCounter),
textfielddone: newEvent(),
}
- a._hwnd = C.newArea(unsafe.Pointer(a))
+ a.controlSingleHWND = newControlSingleHWND(C.newArea(unsafe.Pointer(a)))
+ a.fpreferredSize = a.xpreferredSize
a.SetSize(a.width, a.height)
- a.textfield = C.newAreaTextField(a._hwnd, unsafe.Pointer(a))
+ a.textfield = C.newAreaTextField(a.hwnd, unsafe.Pointer(a))
C.controlSetControlFont(a.textfield)
return a
}
@@ -49,14 +50,14 @@ func newArea(ab *areabase) Area {
func (a *area) SetSize(width, height int) {
a.width = width
a.height = height
- C.SendMessageW(a._hwnd, C.msgAreaSizeChanged, 0, 0)
+ C.SendMessageW(a.hwnd, C.msgAreaSizeChanged, 0, 0)
}
func (a *area) Repaint(r image.Rectangle) {
var hscroll, vscroll C.int
var rect C.RECT
- C.SendMessageW(a._hwnd, C.msgAreaGetScroll, C.WPARAM(uintptr(unsafe.Pointer(&hscroll))), C.LPARAM(uintptr(unsafe.Pointer(&vscroll))))
+ C.SendMessageW(a.hwnd, C.msgAreaGetScroll, C.WPARAM(uintptr(unsafe.Pointer(&hscroll))), C.LPARAM(uintptr(unsafe.Pointer(&vscroll))))
r = r.Add(image.Pt(int(hscroll), int(vscroll))) // adjust by scroll position
r = image.Rect(0, 0, a.width, a.height).Intersect(r)
if r.Empty() {
@@ -66,18 +67,18 @@ func (a *area) Repaint(r image.Rectangle) {
rect.top = C.LONG(r.Min.Y)
rect.right = C.LONG(r.Max.X)
rect.bottom = C.LONG(r.Max.Y)
- C.SendMessageW(a._hwnd, C.msgAreaRepaint, 0, C.LPARAM(uintptr(unsafe.Pointer(&rect))))
+ C.SendMessageW(a.hwnd, C.msgAreaRepaint, 0, C.LPARAM(uintptr(unsafe.Pointer(&rect))))
}
func (a *area) RepaintAll() {
- C.SendMessageW(a._hwnd, C.msgAreaRepaintAll, 0, 0)
+ C.SendMessageW(a.hwnd, C.msgAreaRepaintAll, 0, 0)
}
func (a *area) OpenTextFieldAt(x, y int) {
if x < 0 || x >= a.width || y < 0 || y >= a.height {
panic(fmt.Errorf("point (%d,%d) outside Area in Area.OpenTextFieldAt()", x, y))
}
- C.areaOpenTextField(a._hwnd, a.textfield, C.int(x), C.int(y), textfieldWidth, textfieldHeight)
+ C.areaOpenTextField(a.hwnd, a.textfield, C.int(x), C.int(y), textfieldWidth, textfieldHeight)
}
func (a *area) TextFieldText() string {
@@ -96,7 +97,7 @@ func (a *area) OnTextFieldDismissed(f func()) {
//export areaTextFieldDone
func areaTextFieldDone(data unsafe.Pointer) {
a := (*area)(data)
- C.areaMarkTextFieldDone(a._hwnd)
+ C.areaMarkTextFieldDone(a.hwnd)
a.textfielddone.fire()
}
@@ -323,39 +324,13 @@ var modonlykeys = map[C.WPARAM]Modifiers{
C.VK_RWIN: Super,
}
-//export storeAreaHWND
-func storeAreaHWND(data unsafe.Pointer, hwnd C.HWND) {
- a := (*area)(data)
- a._hwnd = hwnd
-}
-
//export areaResetClickCounter
func areaResetClickCounter(data unsafe.Pointer) {
a := (*area)(data)
a.clickCounter.reset()
}
-func (a *area) hwnd() C.HWND {
- return a._hwnd
-}
-
-func (a *area) setParent(p *controlParent) {
- basesetParent(a, p)
-}
-
-func (a *area) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(a, x, y, width, height, d)
-}
-
-func (a *area) preferredSize(d *sizing) (width, height int) {
+func (a *area) xpreferredSize(d *sizing) (width, height int) {
// the preferred size of an Area is its size
return a.width, a.height
}
-
-func (a *area) commitResize(c *allocation, d *sizing) {
- basecommitResize(a, c, d)
-}
-
-func (a *area) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(a, d)
-}
diff --git a/basicctrls.go b/basicctrls.go
index cee60f2..1ef720b 100644
--- a/basicctrls.go
+++ b/basicctrls.go
@@ -87,31 +87,20 @@ func NewTab() Tab {
// Label is a Control that shows a static line of text.
// Label shows one line of text; any text that does not fit is truncated.
-// A Label can either have smart vertical alignment relative to the control to its right or just be vertically aligned to the top (standalone).
-// The effect of placing a non-standalone Label in any context other than to the immediate left of a Control is undefined.
-// Both types of labels are left-aligned. [FUTURE PLANS: For platform-specific horizontal alignment rules, use a Form.]
+// Labels are left-aligned. [FUTURE PLANS: For platform-specific horizontal alignment rules, use a Form.]
type Label interface {
Control
// Text and SetText get and set the Label's text.
Text() string
SetText(text string)
-
- isStandalone() bool
}
// NewLabel creates a new Label with the given text.
-// The Label will smartly vertically position itself relative to the control to its immediate right.
func NewLabel(text string) Label {
return newLabel(text)
}
-// NewStandaloneLabel creates a new Label with the given text.
-// The Label will be vertically positioned at the top of its allocated space.
-func NewStandaloneLabel(text string) Label {
- return newStandaloneLabel(text)
-}
-
// Group is a Control that holds a single Control; if that Control also contains other Controls, then the Controls will appear visually grouped together.
// The appearance of a Group varies from system to system; for the most part a Group consists of a thin frame.
// All Groups have a text label indicating what the Group is for.
@@ -121,6 +110,11 @@ type Group interface {
// Text and SetText get and set the Group's label text.
Text() string
SetText(text string)
+
+ // Margined and SetMargined get and set whether the contents of the Group have a margin around them.
+ // The size of the margin is platform-dependent.
+ Margined() bool
+ SetMargined(margined bool)
}
// NewGroup creates a new Group with the given text label and child Control.
diff --git a/button_darwin.go b/button_darwin.go
index 06fac20..1a0cd1b 100644
--- a/button_darwin.go
+++ b/button_darwin.go
@@ -10,7 +10,7 @@ import (
import "C"
type button struct {
- _id C.id
+ *controlSingleObject
clicked *event
}
@@ -18,11 +18,11 @@ func newButton(text string) *button {
ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext))
b := &button{
- _id: C.newButton(),
+ controlSingleObject: newControlSingleObject(C.newButton()),
clicked: newEvent(),
}
- C.buttonSetText(b._id, ctext)
- C.buttonSetDelegate(b._id, unsafe.Pointer(b))
+ C.buttonSetText(b.id, ctext)
+ C.buttonSetDelegate(b.id, unsafe.Pointer(b))
return b
}
@@ -31,13 +31,13 @@ func (b *button) OnClicked(e func()) {
}
func (b *button) Text() string {
- return C.GoString(C.buttonText(b._id))
+ return C.GoString(C.buttonText(b.id))
}
func (b *button) SetText(text string) {
ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext))
- C.buttonSetText(b._id, ctext)
+ C.buttonSetText(b.id, ctext)
}
//export buttonClicked
@@ -45,27 +45,3 @@ func buttonClicked(xb unsafe.Pointer) {
b := (*button)(unsafe.Pointer(xb))
b.clicked.fire()
}
-
-func (b *button) id() C.id {
- return b._id
-}
-
-func (b *button) setParent(p *controlParent) {
- basesetParent(b, p)
-}
-
-func (b *button) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(b, x, y, width, height, d)
-}
-
-func (b *button) preferredSize(d *sizing) (width, height int) {
- return basepreferredSize(b, d)
-}
-
-func (b *button) commitResize(a *allocation, d *sizing) {
- basecommitResize(b, a, d)
-}
-
-func (b *button) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(b, d)
-}
diff --git a/button_unix.go b/button_unix.go
index a880b8c..a1da527 100644
--- a/button_unix.go
+++ b/button_unix.go
@@ -13,7 +13,7 @@ import (
import "C"
type button struct {
- _widget *C.GtkWidget
+ *controlSingleWidget
button *C.GtkButton
clicked *event
}
@@ -24,7 +24,7 @@ func newButton(text string) *button {
defer freegstr(ctext)
widget := C.gtk_button_new_with_label(ctext)
b := &button{
- _widget: widget,
+ controlSingleWidget: newControlSingleWidget(widget),
button: (*C.GtkButton)(unsafe.Pointer(widget)),
clicked: newEvent(),
}
@@ -55,27 +55,3 @@ func buttonClicked(bwid *C.GtkButton, data C.gpointer) {
b := (*button)(unsafe.Pointer(data))
b.clicked.fire()
}
-
-func (b *button) widget() *C.GtkWidget {
- return b._widget
-}
-
-func (b *button) setParent(p *controlParent) {
- basesetParent(b, p)
-}
-
-func (b *button) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(b, x, y, width, height, d)
-}
-
-func (b *button) preferredSize(d *sizing) (width, height int) {
- return basepreferredSize(b, d)
-}
-
-func (b *button) commitResize(a *allocation, d *sizing) {
- basecommitResize(b, a, d)
-}
-
-func (b *button) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(b, d)
-}
diff --git a/button_windows.go b/button_windows.go
index 38e43d5..ff5a251 100644
--- a/button_windows.go
+++ b/button_windows.go
@@ -10,8 +10,7 @@ import (
import "C"
type button struct {
- _hwnd C.HWND
- _textlen C.LONG
+ *controlSingleHWNDWithText
clicked *event
}
@@ -22,12 +21,13 @@ func newButton(text string) *button {
C.BS_PUSHBUTTON|C.WS_TABSTOP,
0)
b := &button{
- _hwnd: hwnd,
+ controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
clicked: newEvent(),
}
+ b.fpreferredSize = b.xpreferredSize
b.SetText(text)
- C.controlSetControlFont(b._hwnd)
- C.setButtonSubclass(b._hwnd, unsafe.Pointer(b))
+ C.controlSetControlFont(b.hwnd)
+ C.setButtonSubclass(b.hwnd, unsafe.Pointer(b))
return b
}
@@ -36,11 +36,11 @@ func (b *button) OnClicked(e func()) {
}
func (b *button) Text() string {
- return baseText(b)
+ return b.text()
}
func (b *button) SetText(text string) {
- baseSetText(b, text)
+ b.setText(text)
}
//export buttonClicked
@@ -49,51 +49,23 @@ func buttonClicked(data unsafe.Pointer) {
b.clicked.fire()
}
-func (b *button) hwnd() C.HWND {
- return b._hwnd
-}
-
-func (b *button) textlen() C.LONG {
- return b._textlen
-}
-
-func (b *button) settextlen(len C.LONG) {
- b._textlen = len
-}
-
-func (b *button) setParent(p *controlParent) {
- basesetParent(b, p)
-}
-
-func (b *button) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(b, x, y, width, height, d)
-}
-
const (
// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
buttonHeight = 14
)
-func (b *button) preferredSize(d *sizing) (width, height int) {
+func (b *button) xpreferredSize(d *sizing) (width, height int) {
// comctl32.dll version 6 thankfully provides a method to grab this...
var size C.SIZE
size.cx = 0 // explicitly ask for ideal size
size.cy = 0
- if C.SendMessageW(b._hwnd, C.BCM_GETIDEALSIZE, 0, C.LPARAM(uintptr(unsafe.Pointer(&size)))) != C.FALSE {
+ if C.SendMessageW(b.hwnd, C.BCM_GETIDEALSIZE, 0, C.LPARAM(uintptr(unsafe.Pointer(&size)))) != C.FALSE {
return int(size.cx), int(size.cy)
}
// that failed, fall back
println("message failed; falling back")
// don't worry about the error return from GetSystemMetrics(); there's no way to tell (explicitly documented as such)
xmargins := 2 * int(C.GetSystemMetrics(C.SM_CXEDGE))
- return xmargins + int(b._textlen), fromdlgunitsY(buttonHeight, d)
-}
-
-func (b *button) commitResize(a *allocation, d *sizing) {
- basecommitResize(b, a, d)
-}
-
-func (b *button) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(b, d)
+ return xmargins + int(b.textlen), fromdlgunitsY(buttonHeight, d)
}
diff --git a/checkbox_darwin.go b/checkbox_darwin.go
index 126662c..1578add 100644
--- a/checkbox_darwin.go
+++ b/checkbox_darwin.go
@@ -10,7 +10,7 @@ import (
import "C"
type checkbox struct {
- _id C.id
+ *controlSingleObject
toggled *event
}
@@ -18,11 +18,11 @@ func newCheckbox(text string) *checkbox {
ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext))
c := &checkbox{
- _id: C.newCheckbox(),
+ controlSingleObject: newControlSingleObject(C.newCheckbox()),
toggled: newEvent(),
}
- C.buttonSetText(c._id, ctext)
- C.checkboxSetDelegate(c._id, unsafe.Pointer(c))
+ C.buttonSetText(c.id, ctext)
+ C.checkboxSetDelegate(c.id, unsafe.Pointer(c))
return c
}
@@ -31,21 +31,21 @@ func (c *checkbox) OnToggled(e func()) {
}
func (c *checkbox) Text() string {
- return C.GoString(C.buttonText(c._id))
+ return C.GoString(C.buttonText(c.id))
}
func (c *checkbox) SetText(text string) {
ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext))
- C.buttonSetText(c._id, ctext)
+ C.buttonSetText(c.id, ctext)
}
func (c *checkbox) Checked() bool {
- return fromBOOL(C.checkboxChecked(c._id))
+ return fromBOOL(C.checkboxChecked(c.id))
}
func (c *checkbox) SetChecked(checked bool) {
- C.checkboxSetChecked(c._id, toBOOL(checked))
+ C.checkboxSetChecked(c.id, toBOOL(checked))
}
//export checkboxToggled
@@ -53,27 +53,3 @@ func checkboxToggled(xc unsafe.Pointer) {
c := (*checkbox)(unsafe.Pointer(xc))
c.toggled.fire()
}
-
-func (c *checkbox) id() C.id {
- return c._id
-}
-
-func (c *checkbox) setParent(p *controlParent) {
- basesetParent(c, p)
-}
-
-func (c *checkbox) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(c, x, y, width, height, d)
-}
-
-func (c *checkbox) preferredSize(d *sizing) (width, height int) {
- return basepreferredSize(c, d)
-}
-
-func (c *checkbox) commitResize(a *allocation, d *sizing) {
- basecommitResize(c, a, d)
-}
-
-func (c *checkbox) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(c, d)
-}
diff --git a/checkbox_unix.go b/checkbox_unix.go
index 7d872a5..fc27bee 100644
--- a/checkbox_unix.go
+++ b/checkbox_unix.go
@@ -13,7 +13,7 @@ import (
import "C"
type checkbox struct {
- _widget *C.GtkWidget
+ *controlSingleWidget
button *C.GtkButton
toggle *C.GtkToggleButton
checkbox *C.GtkCheckButton
@@ -25,7 +25,7 @@ func newCheckbox(text string) *checkbox {
defer freegstr(ctext)
widget := C.gtk_check_button_new_with_label(ctext)
c := &checkbox{
- _widget: widget,
+ controlSingleWidget: newControlSingleWidget(widget),
button: (*C.GtkButton)(unsafe.Pointer(widget)),
toggle: (*C.GtkToggleButton)(unsafe.Pointer(widget)),
checkbox: (*C.GtkCheckButton)(unsafe.Pointer(widget)),
@@ -66,27 +66,3 @@ func checkboxToggled(bwid *C.GtkToggleButton, data C.gpointer) {
c := (*checkbox)(unsafe.Pointer(data))
c.toggled.fire()
}
-
-func (c *checkbox) widget() *C.GtkWidget {
- return c._widget
-}
-
-func (c *checkbox) setParent(p *controlParent) {
- basesetParent(c, p)
-}
-
-func (c *checkbox) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(c, x, y, width, height, d)
-}
-
-func (c *checkbox) preferredSize(d *sizing) (width, height int) {
- return basepreferredSize(c, d)
-}
-
-func (c *checkbox) commitResize(a *allocation, d *sizing) {
- basecommitResize(c, a, d)
-}
-
-func (c *checkbox) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(c, d)
-}
diff --git a/checkbox_windows.go b/checkbox_windows.go
index 7f8ce85..4ccd5e2 100644
--- a/checkbox_windows.go
+++ b/checkbox_windows.go
@@ -10,8 +10,7 @@ import (
import "C"
type checkbox struct {
- _hwnd C.HWND
- _textlen C.LONG
+ *controlSingleHWNDWithText
toggled *event
}
@@ -22,12 +21,13 @@ func newCheckbox(text string) *checkbox {
C.BS_CHECKBOX|C.WS_TABSTOP,
0)
c := &checkbox{
- _hwnd: hwnd,
+ controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
toggled: newEvent(),
}
+ c.fpreferredSize = c.xpreferredSize
c.SetText(text)
- C.controlSetControlFont(c._hwnd)
- C.setCheckboxSubclass(c._hwnd, unsafe.Pointer(c))
+ C.controlSetControlFont(c.hwnd)
+ C.setCheckboxSubclass(c.hwnd, unsafe.Pointer(c))
return c
}
@@ -36,23 +36,23 @@ func (c *checkbox) OnToggled(e func()) {
}
func (c *checkbox) Text() string {
- return baseText(c)
+ return c.text()
}
func (c *checkbox) SetText(text string) {
- baseSetText(c, text)
+ c.setText(text)
}
func (c *checkbox) Checked() bool {
- return C.checkboxChecked(c._hwnd) != C.FALSE
+ return C.checkboxChecked(c.hwnd) != C.FALSE
}
func (c *checkbox) SetChecked(checked bool) {
if checked {
- C.checkboxSetChecked(c._hwnd, C.TRUE)
+ C.checkboxSetChecked(c.hwnd, C.TRUE)
return
}
- C.checkboxSetChecked(c._hwnd, C.FALSE)
+ C.checkboxSetChecked(c.hwnd, C.FALSE)
}
//export checkboxToggled
@@ -61,26 +61,6 @@ func checkboxToggled(data unsafe.Pointer) {
c.toggled.fire()
}
-func (c *checkbox) hwnd() C.HWND {
- return c._hwnd
-}
-
-func (c *checkbox) textlen() C.LONG {
- return c._textlen
-}
-
-func (c *checkbox) settextlen(len C.LONG) {
- c._textlen = len
-}
-
-func (c *checkbox) setParent(p *controlParent) {
- basesetParent(c, p)
-}
-
-func (c *checkbox) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(c, x, y, width, height, d)
-}
-
const (
// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
checkboxHeight = 10
@@ -88,15 +68,7 @@ const (
checkboxXFromLeftOfBoxToLeftOfLabel = 12
)
-func (c *checkbox) preferredSize(d *sizing) (width, height int) {
- return fromdlgunitsX(checkboxXFromLeftOfBoxToLeftOfLabel, d) + int(c._textlen),
+func (c *checkbox) xpreferredSize(d *sizing) (width, height int) {
+ return fromdlgunitsX(checkboxXFromLeftOfBoxToLeftOfLabel, d) + int(c.textlen),
fromdlgunitsY(checkboxHeight, d)
}
-
-func (c *checkbox) commitResize(a *allocation, d *sizing) {
- basecommitResize(c, a, d)
-}
-
-func (c *checkbox) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(c, d)
-}
diff --git a/common_windows.c b/common_windows.c
index 327e707..d4db842 100644
--- a/common_windows.c
+++ b/common_windows.c
@@ -29,7 +29,7 @@ void updateWindow(HWND hwnd)
xpanic("error calling UpdateWindow()", GetLastError());
}
-void *getWindowData(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *lResult, void (*storeHWND)(void *, HWND))
+void *getWindowData(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
{
CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
void *data;
@@ -37,11 +37,8 @@ void *getWindowData(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT
data = (void *) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
if (data == NULL) {
// the lpParam is available during WM_NCCREATE and WM_CREATE
- if (uMsg == WM_NCCREATE) {
+ if (uMsg == WM_NCCREATE)
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) (cs->lpCreateParams));
- data = (void *) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
- (*storeHWND)(data, hwnd);
- }
// act as if we're not ready yet, even during WM_NCCREATE (nothing important to the switch statement below happens here anyway)
*lResult = DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
@@ -106,21 +103,20 @@ void paintControlBackground(HWND hwnd, HDC dc)
WCHAR classname[128] = L""; // more than enough to avoid collisions
parent = hwnd;
- do {
- parent = GetParent(parent);
- if (parent == NULL)
- xpanic("error getting parent container of control in paintControlBackground()", GetLastError());
- // wine sends these messages early, yay...
- if (parent == msgwin)
- return;
+ for (;;) {
parent = GetParent(parent);
if (parent == NULL)
xpanic("error getting parent control of control in paintControlBackground()", GetLastError());
+ // wine sends these messages early, yay...
if (parent == msgwin)
return;
if (GetClassNameW(parent, classname, 128) == 0)
xpanic("error getting name of focused window class in paintControlBackground()", GetLastError());
- } while (_wcsicmp(classname, L"button") == 0); // skip groupboxes
+ // skip container and groupboxes
+ if (_wcsicmp(classname, containerclass) != 0) // container
+ if (_wcsicmp(classname, L"button") != 0) // groupbox
+ break;
+ }
if (GetWindowRect(hwnd, &r) == 0)
xpanic("error getting control's window rect in paintControlBackground()", GetLastError());
// the above is a window rect; convert to client rect
diff --git a/container.go b/container.go
index 143985c..27674d0 100644
--- a/container.go
+++ b/container.go
@@ -2,41 +2,14 @@
package ui
-type allocation struct {
- x int
- y int
- width int
- height int
- this Control
- neighbor Control
-}
-
type sizingbase struct {
- xmargin int
- ymargintop int
- ymarginbottom int
xpadding int
ypadding int
}
-type controlSizing interface {
- allocate(x int, y int, width int, height int, d *sizing) []*allocation
- preferredSize(*sizing) (int, int)
- commitResize(*allocation, *sizing)
- getAuxResizeInfo(*sizing)
-}
-
-// A container hosts a Control and resizes that Control based on changes in size to the parent Window.
-// container is used by Window, Tab, and Group to contain and control their respective Controls.
-// Tab and Group use containers for their content; as such, their commitResize() functions should only change the size of the Tab and Group themselves, and have their containers do the real work.
-// All containers must embed containerbase.
-type containerbase struct {
- child Control
-}
-
-// set to true to apply spacing to all windows
-var spaced bool = false
+// The container type, which is defined per-platform, is an internal Control that is only used to house other Controls from the underlying UI toolkit's point of view
+/* TODO
func (c *container) resize(x, y, width, height int) {
if c.child == nil { // no children; nothing to do
return
@@ -50,3 +23,4 @@ func (c *container) resize(x, y, width, height int) {
allocations[i].this.commitResize(allocations[i], d)
}
}
+*/
diff --git a/container_darwin.go b/container_darwin.go
index b2086c4..58b7777 100644
--- a/container_darwin.go
+++ b/container_darwin.go
@@ -10,33 +10,44 @@ import (
import "C"
type container struct {
- containerbase
- id C.id
+ *controlSingleObject
}
type sizing struct {
sizingbase
// for size calculations
- // nothing for mac
+ // nothing on Mac OS X
// for the actual resizing
neighborAlign C.struct_xalignment
}
-func newContainer(child Control) *container {
+func newContainer() *container {
c := new(container)
- c.id = C.newContainerView(unsafe.Pointer(c))
- c.child = child
- c.child.setParent(&controlParent{c.id})
+ c.controlSingleObject = newControlSingleObject(C.newContainerView(unsafe.Pointer(c)))
return c
}
-//export containerResized
-func containerResized(data unsafe.Pointer, width C.intptr_t, height C.intptr_t) {
- c := (*container)(unsafe.Pointer(data))
- // the origin of a view's content area is always (0, 0)
- c.resize(0, 0, int(width), int(height))
+func (c *container) parent() *controlParent {
+ return &controlParent{c.id}
+}
+
+func (c *container) allocation(margined bool) C.struct_xrect {
+ b := C.containerBounds(c.id)
+ if margined {
+ b.x += C.intptr_t(macXMargin)
+ b.y += C.intptr_t(macYMargin)
+ b.width -= C.intptr_t(macXMargin) * 2
+ b.height -= C.intptr_t(macYMargin) * 2
+ }
+ return b
+}
+
+// we can just return these values as is
+func (c *container) bounds(d *sizing) (int, int, int, int) {
+ b := C.containerBounds(c.id)
+ return int(b.x), int(b.y), int(b.width), int(b.height)
}
// These are based on measurements from Interface Builder.
@@ -47,18 +58,14 @@ const (
macYPadding = 8
)
-func (c *container) beginResize() (d *sizing) {
+func (w *window) beginResize() (d *sizing) {
d = new(sizing)
- if spaced {
- d.xmargin = macXMargin
- d.ymargintop = macYMargin
- d.ymarginbottom = d.ymargintop
- d.xpadding = macXPadding
- d.ypadding = macYPadding
- }
+ d.xpadding = macXPadding
+ d.ypadding = macYPadding
return d
}
+/*TODO
func (c *container) translateAllocationCoords(allocations []*allocation, winwidth, winheight int) {
for _, a := range allocations {
// winheight - y because (0,0) is the bottom-left corner of the window and not the top-left corner
@@ -66,3 +73,4 @@ func (c *container) translateAllocationCoords(allocations []*allocation, winwidt
a.y = (winheight - a.y) - a.height
}
}
+*/
diff --git a/container_darwin.m b/container_darwin.m
index 0a598c6..1dfa7cb 100644
--- a/container_darwin.m
+++ b/container_darwin.m
@@ -21,12 +21,6 @@
@implementation goContainerView
-- (void)setFrameSize:(NSSize)s
-{
- [super setFrameSize:s];
- containerResized(self->gocontainer, (intptr_t) s.width, (intptr_t) s.height);
-}
-
@end
id newContainerView(void *gocontainer)
@@ -40,5 +34,25 @@ id newContainerView(void *gocontainer)
void moveControl(id c, intptr_t x, intptr_t y, intptr_t width, intptr_t height)
{
- [toNSView(c) setFrame:NSMakeRect((CGFloat) x, (CGFloat) y, (CGFloat) width, (CGFloat) height)];
+ NSView *v;
+ NSRect frame;
+
+ frame = NSMakeRect((CGFloat) x, (CGFloat) y, (CGFloat) width, (CGFloat) height);
+ // mac os x coordinate system has (0,0) in the lower-left
+ v = toNSView(c);
+ frame.origin.y = ([[v superview] bounds].size.height - frame.size.height) - frame.origin.y;
+ [v setFrame:frame];
+}
+
+struct xrect containerBounds(id view)
+{
+ NSRect b;
+ struct xrect r;
+
+ b = [toNSView(view) bounds];
+ r.x = (intptr_t) b.origin.x;
+ r.y = (intptr_t) b.origin.y;
+ r.width = (intptr_t) b.size.width;
+ r.height = (intptr_t) b.size.height;
+ return r;
}
diff --git a/container_unix.c b/container_unix.c
index 9802383..8de3cb5 100644
--- a/container_unix.c
+++ b/container_unix.c
@@ -59,7 +59,6 @@ static void goContainer_remove(GtkContainer *container, GtkWidget *widget)
static void goContainer_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
{
gtk_widget_set_allocation(widget, allocation);
- containerResizing(GOCONTAINER(widget)->gocontainer, allocation);
}
struct forall {
diff --git a/container_unix.go b/container_unix.go
index ffb1ffa..90bc84b 100644
--- a/container_unix.go
+++ b/container_unix.go
@@ -12,9 +12,8 @@ import (
import "C"
type container struct {
- containerbase
- layoutwidget *C.GtkWidget
- layoutcontainer *C.GtkContainer
+ *controlSingleWidget
+ container *C.GtkContainer
}
type sizing struct {
@@ -24,27 +23,40 @@ type sizing struct {
// gtk+ needs nothing
// for the actual resizing
- shouldVAlignTop bool
+ // gtk+ needs nothing
}
-func newContainer(child Control) *container {
+func newContainer() *container {
c := new(container)
- widget := C.newContainer(unsafe.Pointer(c))
- c.layoutwidget = widget
- c.layoutcontainer = (*C.GtkContainer)(unsafe.Pointer(widget))
- c.child = child
- c.child.setParent(&controlParent{c.layoutcontainer})
+ c.controlSingleWidget = newControlSingleWidget(C.newContainer(unsafe.Pointer(c)))
+ c.container = (*C.GtkContainer)(unsafe.Pointer(c.widget))
return c
}
-func (c *container) setParent(p *controlParent) {
- C.gtk_container_add(p.c, c.layoutwidget)
+func (c *container) parent() *controlParent {
+ return &controlParent{c.container}
}
-//export containerResizing
-func containerResizing(data unsafe.Pointer, r *C.GtkAllocation) {
- c := (*container)(data)
- c.resize(int(r.x), int(r.y), int(r.width), int(r.height))
+func (c *container) allocation(margined bool) C.GtkAllocation {
+ var a C.GtkAllocation
+
+ C.gtk_widget_get_allocation(c.widget, &a)
+ if margined {
+ a.x += C.int(gtkXMargin)
+ a.y += C.int(gtkYMargin)
+ a.width -= C.int(gtkXMargin) * 2
+ a.height -= C.int(gtkYMargin) * 2
+ }
+ return a
+}
+
+// we can just return these values as is
+// note that allocations of a widget on GTK+ have their origin in the /window/ origin
+func (c *container) bounds(d *sizing) (int, int, int, int) {
+ var a C.GtkAllocation
+
+ C.gtk_widget_get_allocation(c.widget, &a)
+ return int(a.x), int(a.y), int(a.width), int(a.height)
}
const (
@@ -54,18 +66,9 @@ const (
gtkYPadding = 6
)
-func (c *container) beginResize() (d *sizing) {
+func (w *window) beginResize() (d *sizing) {
d = new(sizing)
- if spaced {
- d.xmargin = gtkXMargin
- d.ymargintop = gtkYMargin
- d.ymarginbottom = d.ymargintop
- d.xpadding = gtkXPadding
- d.ypadding = gtkYPadding
- }
+ d.xpadding = gtkXPadding
+ d.ypadding = gtkYPadding
return d
}
-
-func (c *container) translateAllocationCoords(allocations []*allocation, winwidth, winheight int) {
- // no need for coordinate conversion with gtk+
-}
diff --git a/container_windows.c b/container_windows.c
index a95818d..7910b92 100644
--- a/container_windows.c
+++ b/container_windows.c
@@ -9,25 +9,13 @@ In this case, I chose to waste a window handle rather than keep things super com
If this is seriously an issue in the future, I can roll it back.
*/
-#define containerclass L"gouicontainer"
-
static LRESULT CALLBACK containerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
- void *data;
- RECT r;
LRESULT lResult;
- data = getWindowData(hwnd, uMsg, wParam, lParam, &lResult, storeContainerHWND);
- if (data == NULL)
- return lResult;
if (sharedWndProc(hwnd, uMsg, wParam, lParam, &lResult))
return lResult;
switch (uMsg) {
- case WM_SIZE:
- if (GetClientRect(hwnd, &r) == 0)
- xpanic("error getting client rect for Window in WM_SIZE", GetLastError());
- containerResize(data, &r);
- return 0;
default:
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
@@ -53,7 +41,7 @@ DWORD makeContainerWindowClass(char **errmsg)
return 0;
}
-HWND newContainer(void *data)
+HWND newContainer(void)
{
HWND hwnd;
@@ -63,12 +51,21 @@ HWND newContainer(void *data)
WS_CHILD | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
100, 100,
- msgwin, NULL, hInstance, data);
+ msgwin, NULL, hInstance, NULL);
if (hwnd == NULL)
xpanic("container creation failed", GetLastError());
return hwnd;
}
+RECT containerBounds(HWND hwnd)
+{
+ RECT r;
+
+ if (GetClientRect(hwnd, &r) == 0)
+ xpanic("error getting container client rect for container.bounds()", GetLastError());
+ return r;
+}
+
void calculateBaseUnits(HWND hwnd, int *baseX, int *baseY, LONG *internalLeading)
{
HDC dc;
diff --git a/container_windows.go b/container_windows.go
index 99be1c2..31963e0 100644
--- a/container_windows.go
+++ b/container_windows.go
@@ -5,17 +5,13 @@ package ui
import (
"fmt"
"syscall"
- "unsafe"
)
// #include "winapi_windows.h"
import "C"
type container struct {
- containerbase
- hwnd C.HWND
- nchildren int
- isGroup bool
+ *controlSingleHWND
}
type sizing struct {
@@ -40,45 +36,30 @@ func makeContainerWindowClass() error {
return nil
}
-func newContainer(control Control) *container {
- c := new(container)
- hwnd := C.newContainer(unsafe.Pointer(c))
- if hwnd != c.hwnd {
- panic(fmt.Errorf("inconsistency: hwnd returned by CreateWindowEx() (%p) and hwnd stored in container (%p) differ", hwnd, c.hwnd))
+func newContainer() *container {
+ // don't set preferredSize(); it should never be called
+ return &container{
+ controlSingleHWND: newControlSingleHWND(C.newContainer()),
}
- c.child = control
- c.child.setParent(&controlParent{c})
- return c
-}
-
-func (c *container) setParent(hwnd C.HWND) {
- C.controlSetParent(c.hwnd, hwnd)
-}
-
-// this is needed because Windows won't move/resize a child window for us
-func (c *container) move(r *C.RECT) {
- C.moveWindow(c.hwnd, C.int(r.left), C.int(r.top), C.int(r.right-r.left), C.int(r.bottom-r.top))
}
+// TODO merge with controlSingleHWND
func (c *container) show() {
C.ShowWindow(c.hwnd, C.SW_SHOW)
}
+// TODO merge with controlSingleHWND
func (c *container) hide() {
C.ShowWindow(c.hwnd, C.SW_HIDE)
}
-//export storeContainerHWND
-func storeContainerHWND(data unsafe.Pointer, hwnd C.HWND) {
- c := (*container)(data)
- c.hwnd = hwnd
+func (c *container) parent() *controlParent {
+ return &controlParent{c.hwnd}
}
-//export containerResize
-func containerResize(data unsafe.Pointer, r *C.RECT) {
- c := (*container)(data)
- // the origin of any window's content area is always (0, 0), but let's use the values from the RECT just to be safe
- c.resize(int(r.left), int(r.top), int(r.right-r.left), int(r.bottom-r.top))
+func (c *container) bounds(d *sizing) (int, int, int, int) {
+ r := C.containerBounds(c.hwnd)
+ return int(r.left), int(r.top), int(r.right - r.left), int(r.bottom - r.top)
}
// For Windows, Microsoft just hands you a list of preferred control sizes as part of the MSDN documentation and tells you to roll with it.
@@ -103,45 +84,31 @@ func fromdlgunitsY(du int, d *sizing) int {
}
const (
+ // shared by multiple containers
marginDialogUnits = 7
paddingDialogUnits = 4
-
- groupXMargin = 6
- groupYMarginTop = 11 // note this value /includes the groupbox label/
- groupYMarginBottom = 7
)
-func (c *container) beginResize() (d *sizing) {
+func (w *window) beginResize() (d *sizing) {
var baseX, baseY C.int
var internalLeading C.LONG
d = new(sizing)
- C.calculateBaseUnits(c.hwnd, &baseX, &baseY, &internalLeading)
+ C.calculateBaseUnits(w.hwnd, &baseX, &baseY, &internalLeading)
d.baseX = baseX
d.baseY = baseY
d.internalLeading = internalLeading
- if spaced {
- d.xmargin = fromdlgunitsX(marginDialogUnits, d)
- d.ymargintop = fromdlgunitsY(marginDialogUnits, d)
- d.ymarginbottom = d.ymargintop
- d.xpadding = fromdlgunitsX(paddingDialogUnits, d)
- d.ypadding = fromdlgunitsY(paddingDialogUnits, d)
- }
- if c.isGroup {
- // note that these values apply regardless of whether or not spaced is set
- // this is because Windows groupboxes have the client rect spanning the entire size of the control, not just the active work area
- // the measurements Microsoft give us are for spaced margining; let's just use them
- d.xmargin = fromdlgunitsX(groupXMargin, d)
- d.ymargintop = fromdlgunitsY(groupYMarginTop, d)
- d.ymarginbottom = fromdlgunitsY(groupYMarginBottom, d)
-
- }
+ d.xpadding = fromdlgunitsX(paddingDialogUnits, d)
+ d.ypadding = fromdlgunitsY(paddingDialogUnits, d)
return d
}
-func (c *container) translateAllocationCoords(allocations []*allocation, winwidth, winheight int) {
- // no translation needed on windows
+func marginRectDLU(r *C.RECT, top int, bottom int, left int, right int, d *sizing) {
+ r.left += C.LONG(fromdlgunitsX(left, d))
+ r.top += C.LONG(fromdlgunitsY(top, d))
+ r.right -= C.LONG(fromdlgunitsX(right, d))
+ r.bottom -= C.LONG(fromdlgunitsY(bottom, d))
}
diff --git a/control.go b/control.go
index a8c3ecd..34e5c84 100644
--- a/control.go
+++ b/control.go
@@ -5,16 +5,32 @@ package ui
// Control represents a control.
type Control interface {
setParent(p *controlParent) // controlParent defined per-platform
- controlSizing
+ preferredSize(d *sizing) (width, height int)
+ resize(x int, y int, width int, height int, d *sizing)
+ nTabStops() int // used by the Windows backend
}
-// this is the same across all platforms
-func baseallocate(c Control, x int, y int, width int, height int, d *sizing) []*allocation {
- return []*allocation{&allocation{
- x: x,
- y: y,
- width: width,
- height: height,
- this: c,
- }}
+type controlbase struct {
+ fsetParent func(p *controlParent)
+ fpreferredSize func(d *sizing) (width, height int)
+ fresize func(x int, y int, width int, height int, d *sizing)
+ fnTabStops func() int
+}
+
+// children should not use the same name as these, otherwise weird things will happen
+
+func (c *controlbase) setParent(p *controlParent) {
+ c.fsetParent(p)
+}
+
+func (c *controlbase) preferredSize(d *sizing) (width, height int) {
+ return c.fpreferredSize(d)
+}
+
+func (c *controlbase) resize(x int, y int, width int, height int, d *sizing) {
+ c.fresize(x, y, width, height, d)
+}
+
+func (c *controlbase) nTabStops() int {
+ return c.fnTabStops()
}
diff --git a/control_darwin.go b/control_darwin.go
index cc77ed2..330d50c 100644
--- a/control_darwin.go
+++ b/control_darwin.go
@@ -5,54 +5,52 @@ package ui
// #include "objc_darwin.h"
import "C"
-// all Controls that call base methods must be this
-type controlPrivate interface {
- id() C.id
- Control
-}
-
type controlParent struct {
id C.id
}
-func basesetParent(c controlPrivate, p *controlParent) {
- // redrawing the new window handled by C.parent()
- C.parent(c.id(), p.id)
+type controlSingleObject struct {
+ *controlbase
+ id C.id
}
-func basepreferredSize(c controlPrivate, d *sizing) (int, int) {
- s := C.controlPreferredSize(c.id())
- return int(s.width), int(s.height)
+func newControlSingleObject(id C.id) *controlSingleObject {
+ c := new(controlSingleObject)
+ c.controlbase = &controlbase{
+ fsetParent: c.xsetParent,
+ fpreferredSize: c.xpreferredSize,
+ fresize: c.xresize,
+ }
+ c.id = id
+ return c
}
-func basecommitResize(c controlPrivate, a *allocation, d *sizing) {
- dobasecommitResize(c.id(), a, d)
+func (c *controlSingleObject) xsetParent(p *controlParent) {
+ // redrawing the new window handled by C.parent()
+ C.parent(c.id, p.id)
}
-func dobasecommitResize(id C.id, c *allocation, d *sizing) {
- C.moveControl(id, C.intptr_t(c.x), C.intptr_t(c.y), C.intptr_t(c.width), C.intptr_t(c.height))
+func (c *controlSingleObject) xpreferredSize(d *sizing) (int, int) {
+ s := C.controlPreferredSize(c.id)
+ return int(s.width), int(s.height)
}
-func basegetAuxResizeInfo(c controlPrivate, d *sizing) {
- d.neighborAlign = C.alignmentInfoFrame(c.id())
+func (c *controlSingleObject) xresize(x int, y int, width int, height int, d *sizing) {
+ C.moveControl(c.id, C.intptr_t(x), C.intptr_t(y), C.intptr_t(width), C.intptr_t(height))
}
type scroller struct {
- id C.id
+ *controlSingleObject
+ scroller *controlSingleObject
}
func newScroller(child C.id, bordered bool) *scroller {
- id := C.newScrollView(child, toBOOL(bordered))
+ sid := C.newScrollView(child, toBOOL(bordered))
s := &scroller{
- id: id,
+ controlSingleObject: newControlSingleObject(child),
+ scroller: newControlSingleObject(sid),
}
+ s.fsetParent = s.scroller.fsetParent
+ s.fresize = s .scroller.fresize
return s
}
-
-func (s *scroller) setParent(p *controlParent) {
- C.parent(s.id, p.id)
-}
-
-func (s *scroller) commitResize(c *allocation, d *sizing) {
- dobasecommitResize(s.id, c, d)
-}
diff --git a/control_unix.go b/control_unix.go
index e8200c4..6aa2278 100644
--- a/control_unix.go
+++ b/control_unix.go
@@ -11,24 +11,34 @@ import (
// #include "gtk_unix.h"
import "C"
-// all Controls that call base methods must be this
-type controlPrivate interface {
- widget() *C.GtkWidget
- Control
-}
-
type controlParent struct {
c *C.GtkContainer
}
-func basesetParent(c controlPrivate, p *controlParent) {
- widget := c.widget() // avoid multiple interface lookups
- C.gtk_container_add(p.c, widget)
+type controlSingleWidget struct {
+ *controlbase
+ widget *C.GtkWidget
+}
+
+func newControlSingleWidget(widget *C.GtkWidget) *controlSingleWidget {
+ c := new(controlSingleWidget)
+ c.controlbase = &controlbase{
+ fsetParent: c.xsetParent,
+ fpreferredSize: c.xpreferredSize,
+ fresize: c.xresize,
+ }
+ c.widget = widget
+ return c
+}
+
+func (c *controlSingleWidget) xsetParent(p *controlParent) {
+ C.gtk_container_add(p.c, c.widget)
// make sure the new widget is shown if not explicitly hidden
- C.gtk_widget_show_all(widget)
+ // TODO why did I have this again?
+ C.gtk_widget_show_all(c.widget)
}
-func basepreferredSize(c controlPrivate, d *sizing) (int, int) {
+func (c *controlSingleWidget) xpreferredSize(d *sizing) (int, int) {
// GTK+ 3 makes this easy: controls can tell us what their preferred size is!
// ...actually, it tells us two things: the "minimum size" and the "natural size".
// The "minimum size" is the smallest size we /can/ display /anything/. The "natural size" is the smallest size we would /prefer/ to display.
@@ -37,15 +47,11 @@ func basepreferredSize(c controlPrivate, d *sizing) (int, int) {
// There is a warning about height-for-width controls, but in my tests this isn't an issue.
var r C.GtkRequisition
- C.gtk_widget_get_preferred_size(c.widget(), nil, &r)
+ C.gtk_widget_get_preferred_size(c.widget, nil, &r)
return int(r.width), int(r.height)
}
-func basecommitResize(c controlPrivate, a *allocation, d *sizing) {
- dobasecommitResize(c.widget(), a, d)
-}
-
-func dobasecommitResize(w *C.GtkWidget, c *allocation, d *sizing) {
+func (c *controlSingleWidget) xresize(x int, y int, width int, height int, d *sizing) {
// as we resize on size-allocate, we have to also use size-allocate on our children
// this is fine anyway; in fact, this allows us to move without knowing what the container is!
// this is what GtkBox does anyway
@@ -53,68 +59,63 @@ func dobasecommitResize(w *C.GtkWidget, c *allocation, d *sizing) {
var r C.GtkAllocation
- r.x = C.int(c.x)
- r.y = C.int(c.y)
- r.width = C.int(c.width)
- r.height = C.int(c.height)
- C.gtk_widget_size_allocate(w, &r)
-}
-
-func basegetAuxResizeInfo(c Control, d *sizing) {
- // controls set this to true if a Label to its left should be vertically aligned to the control's top
- d.shouldVAlignTop = false
+ r.x = C.int(x)
+ r.y = C.int(y)
+ r.width = C.int(width)
+ r.height = C.int(height)
+ C.gtk_widget_size_allocate(c.widget, &r)
}
type scroller struct {
+ *controlSingleWidget
+
+ scroller *controlSingleWidget
scrollwidget *C.GtkWidget
scrollcontainer *C.GtkContainer
scrollwindow *C.GtkScrolledWindow
+ overlay *controlSingleWidget
overlaywidget *C.GtkWidget
overlaycontainer *C.GtkContainer
- overlay *C.GtkOverlay
-
- addShowWhich *C.GtkWidget
+ overlayoverlay *C.GtkOverlay
}
func newScroller(widget *C.GtkWidget, native bool, bordered bool, overlay bool) *scroller {
- var o *C.GtkWidget
+ s := new(scroller)
+ s.controlSingleWidget = newControlSingleWidget(widget)
+ s.scrollwidget = C.gtk_scrolled_window_new(nil, nil)
+ s.scrollcontainer = (*C.GtkContainer)(unsafe.Pointer(s.scrollwidget))
+ s.scrollwindow = (*C.GtkScrolledWindow)(unsafe.Pointer(s.scrollwidget))
- scrollwidget := C.gtk_scrolled_window_new(nil, nil)
- if overlay {
- o = C.gtk_overlay_new()
- }
- s := &scroller{
- scrollwidget: scrollwidget,
- scrollcontainer: (*C.GtkContainer)(unsafe.Pointer(scrollwidget)),
- scrollwindow: (*C.GtkScrolledWindow)(unsafe.Pointer(scrollwidget)),
- overlaywidget: o,
- overlaycontainer: (*C.GtkContainer)(unsafe.Pointer(o)),
- overlay: (*C.GtkOverlay)(unsafe.Pointer(o)),
+ // any actual changing operations need to be done to the GtkScrolledWindow
+ // that is, everything /except/ preferredSize() are done to the GtkScrolledWindow
+ s.scroller = newControlSingleWidget(s.scrollwidget)
+ s.fsetParent = s.scroller.fsetParent
+ s.fresize = s.scroller.fresize
+
+ // in GTK+ 3.4 we still technically need to use the separate gtk_scrolled_window_add_with_viewpoint()/gtk_container_add() spiel for adding the widget to the scrolled window
+ if native {
+ C.gtk_container_add(s.scrollcontainer, s.widget)
+ } else {
+ C.gtk_scrolled_window_add_with_viewport(s.scrollwindow, s.widget)
}
+
// give the scrolled window a border (thanks to jlindgren in irc.gimp.net/#gtk+)
if bordered {
C.gtk_scrolled_window_set_shadow_type(s.scrollwindow, C.GTK_SHADOW_IN)
}
- if native {
- C.gtk_container_add(s.scrollcontainer, widget)
- } else {
- C.gtk_scrolled_window_add_with_viewport(s.scrollwindow, widget)
- }
- s.addShowWhich = s.scrollwidget
+
if overlay {
+ // ok things get REALLY fun now
+ // we now have to do all of the above again
+ s.overlaywidget = C.gtk_overlay_new()
+ s.overlaycontainer = (*C.GtkContainer)(unsafe.Pointer(s.overlaywidget))
+ s.overlayoverlay = (*C.GtkOverlay)(unsafe.Pointer(s.overlaywidget))
+ s.overlay = newControlSingleWidget(s.overlaywidget)
+ s.fsetParent = s.overlay.fsetParent
+ s.fresize = s.overlay.fresize
C.gtk_container_add(s.overlaycontainer, s.scrollwidget)
- s.addShowWhich = s.overlaywidget
}
- return s
-}
-func (s *scroller) setParent(p *controlParent) {
- C.gtk_container_add(p.c, s.addShowWhich)
- // see basesetParent() above for why we call gtk_widget_show_all()
- C.gtk_widget_show_all(s.addShowWhich)
-}
-
-func (s *scroller) commitResize(c *allocation, d *sizing) {
- dobasecommitResize(s.addShowWhich, c, d)
+ return s
}
diff --git a/control_windows.go b/control_windows.go
index d48fa54..ffe0222 100644
--- a/control_windows.go
+++ b/control_windows.go
@@ -5,45 +5,59 @@ package ui
// #include "winapi_windows.h"
import "C"
-type controlPrivate interface {
- hwnd() C.HWND
- Control
-}
-
type controlParent struct {
- c *container
+ hwnd C.HWND
}
-func basesetParent(c controlPrivate, p *controlParent) {
- C.controlSetParent(c.hwnd(), p.c.hwnd)
- p.c.nchildren++
+// don't specify preferredSize in any of these; they're per-control
+
+type controlSingleHWND struct {
+ *controlbase
+ hwnd C.HWND
}
-// don't specify basepreferredSize; it is custom on ALL controls
+func newControlSingleHWND(hwnd C.HWND) *controlSingleHWND {
+ c := new(controlSingleHWND)
+ c.controlbase = &controlbase{
+ fsetParent: c.xsetParent,
+ fresize: c.xresize,
+ fnTabStops: func() int {
+ // most controls count as one tab stop
+ return 1
+ },
+ }
+ c.hwnd = hwnd
+ return c
+}
-func basecommitResize(c controlPrivate, a *allocation, d *sizing) {
- C.moveWindow(c.hwnd(), C.int(a.x), C.int(a.y), C.int(a.width), C.int(a.height))
+func (c *controlSingleHWND) xsetParent(p *controlParent) {
+ C.controlSetParent(c.hwnd, p.hwnd)
}
-func basegetAuxResizeInfo(c controlPrivate, d *sizing) {
- // do nothing
+func (c *controlSingleHWND) xresize(x int, y int, width int, height int, d *sizing) {
+ C.moveWindow(c.hwnd, C.int(x), C.int(y), C.int(width), C.int(height))
}
// these are provided for convenience
-type textableControl interface {
- controlPrivate
- textlen() C.LONG
- settextlen(C.LONG)
+type controlSingleHWNDWithText struct {
+ *controlSingleHWND
+ textlen C.LONG
+}
+
+func newControlSingleHWNDWithText(h C.HWND) *controlSingleHWNDWithText {
+ return &controlSingleHWNDWithText{
+ controlSingleHWND: newControlSingleHWND(h),
+ }
}
-func baseText(c textableControl) string {
- return getWindowText(c.hwnd())
+// TODO export these instead of requiring dummy declarations in each implementation
+func (c *controlSingleHWNDWithText) text() string {
+ return getWindowText(c.hwnd)
}
-func baseSetText(c textableControl, text string) {
- hwnd := c.hwnd()
+func (c *controlSingleHWNDWithText) setText(text string) {
t := toUTF16(text)
- C.setWindowText(hwnd, t)
- c.settextlen(C.controlTextLength(hwnd, t))
+ C.setWindowText(c.hwnd, t)
+ c.textlen = C.controlTextLength(c.hwnd, t)
}
diff --git a/grid.go b/grid.go
index 6142a4f..9e68e66 100644
--- a/grid.go
+++ b/grid.go
@@ -27,6 +27,11 @@ type Grid interface {
// The effect of overlapping spanning Controls is also undefined.
// Add panics if either xspan or yspan are zero or negative.
Add(control Control, nextTo Control, side Side, xexpand bool, xalign Align, yexpand bool, yalign Align, xspan int, yspan int)
+
+ // Padded and SetPadded get and set whether the controls of the Grid have padding between them.
+ // The size of the padding is platform-dependent.
+ Padded() bool
+ SetPadded(padded bool)
}
// Align represents the alignment of a Control in its cell of a Grid.
@@ -54,7 +59,8 @@ type grid struct {
controls []gridCell
indexof map[Control]int
prev int
- parent *controlParent
+ container *container
+ padded bool
xmax int
ymax int
@@ -84,6 +90,7 @@ type gridCell struct {
func NewGrid() Grid {
return &grid{
indexof: map[Control]int{},
+ container: newContainer(),
}
}
@@ -129,9 +136,7 @@ func (g *grid) Add(control Control, nextTo Control, side Side, xexpand bool, xal
xspan: xspan,
yspan: yspan,
}
- if g.parent != nil {
- control.setParent(g.parent)
- }
+ control.setParent(g.container.parent())
// if this is the first control, just add it in directly
if len(g.controls) != 0 {
next := g.prev
@@ -161,11 +166,16 @@ func (g *grid) Add(control Control, nextTo Control, side Side, xexpand bool, xal
g.reorigin()
}
+func (g *grid) Padded() bool {
+ return g.padded
+}
+
+func (g *grid) SetPadded(padded bool) {
+ g.padded = padded
+}
+
func (g *grid) setParent(p *controlParent) {
- g.parent = p
- for i := range g.controls {
- g.controls[i].control.setParent(g.parent)
- }
+ g.container.setParent(p)
}
// builds the topological cell grid; also makes colwidths and rowheights
@@ -187,15 +197,27 @@ func (g *grid) mkgrid() (gg [][]int, colwidths []int, rowheights []int) {
return gg, make([]int, g.xmax), make([]int, g.ymax)
}
-func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocations []*allocation) {
+func (g *grid) resize(x int, y int, width int, height int, d *sizing) {
+ g.container.resize(x, y, width, height, d)
+
if len(g.controls) == 0 {
// nothing to do
- return nil
+ return
+ }
+
+ x, y, width, height = g.container.bounds(d)
+
+ // -2) get this Grid's padding
+ xpadding := d.xpadding
+ ypadding := d.ypadding
+ if !g.padded {
+ xpadding = 0
+ ypadding = 0
}
// -1) discount padding from width/height
- width -= (g.xmax - 1) * d.xpadding
- height -= (g.ymax - 1) * d.ypadding
+ width -= (g.xmax - 1) * xpadding
+ height -= (g.ymax - 1) * ypadding
// 0) build necessary data structures
gg, colwidths, rowheights := g.mkgrid()
@@ -317,11 +339,11 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
if i != prev {
g.controls[i].finalx = curx
} else {
- g.controls[i].finalwidth += d.xpadding
+ g.controls[i].finalwidth += xpadding
}
g.controls[i].finalwidth += colwidths[x]
}
- curx += colwidths[x] + d.xpadding
+ curx += colwidths[x] + xpadding
prev = i
}
}
@@ -334,11 +356,11 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
if i != prev {
g.controls[i].finaly = cury
} else {
- g.controls[i].finalheight += d.ypadding
+ g.controls[i].finalheight += ypadding
}
g.controls[i].finalheight += rowheights[y]
}
- cury += rowheights[y] + d.ypadding
+ cury += rowheights[y] + ypadding
prev = i
}
}
@@ -367,29 +389,17 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
}
// 8) and FINALLY we draw
- var current *allocation
-
for _, ycol := range gg {
- current = nil
for _, i := range ycol {
if i != -1 { // treat empty cells like spaces
- as := g.controls[i].control.allocate(
+ g.controls[i].control.resize(
g.controls[i].finalx+x, g.controls[i].finaly+y,
g.controls[i].finalwidth, g.controls[i].finalheight, d)
- if current != nil { // connect first left to first right
- current.neighbor = g.controls[i].control
- }
- if len(as) != 0 {
- current = as[0] // next left is first subwidget
- } else {
- current = nil // spaces don't have allocation data
- }
- allocations = append(allocations, as...)
}
}
}
- return allocations
+ return
}
func (g *grid) preferredSize(d *sizing) (width, height int) {
@@ -398,6 +408,14 @@ func (g *grid) preferredSize(d *sizing) (width, height int) {
return 0, 0
}
+ // -1) get this Grid's padding
+ xpadding := d.xpadding
+ ypadding := d.ypadding
+ if !g.padded {
+ xpadding = 0
+ ypadding = 0
+ }
+
// 0) build necessary data structures
gg, colwidths, rowheights := g.mkgrid()
@@ -434,14 +452,14 @@ func (g *grid) preferredSize(d *sizing) (width, height int) {
}
// and that's it; just account for padding
- return colwidth + (g.xmax-1)*d.xpadding,
- rowheight + (g.ymax-1)*d.ypadding
+ return colwidth + (g.xmax-1) * xpadding,
+ rowheight + (g.ymax-1) * ypadding
}
-func (g *grid) commitResize(a *allocation, d *sizing) {
- // do nothing; needed to satisfy Control
-}
-
-func (g *grid) getAuxResizeInfo(d *sizing) {
- // do nothing; needed to satisfy Control
+func (g *grid) nTabStops() int {
+ n := 0
+ for _, c := range g.controls {
+ n += c.control.nTabStops()
+ }
+ return n
}
diff --git a/group_darwin.go b/group_darwin.go
index ad3610a..23dd563 100644
--- a/group_darwin.go
+++ b/group_darwin.go
@@ -10,49 +10,51 @@ import (
import "C"
type group struct {
- _id C.id
+ *controlSingleObject
- *container
+ child Control
+ container *container
+
+ margined bool
+
+ chainresize func(x int, y int, width int, height int, d *sizing)
}
func newGroup(text string, control Control) Group {
g := new(group)
- g.container = newContainer(control)
- g._id = C.newGroup(g.container.id)
+ g.container = newContainer()
+ g.controlSingleObject = newControlSingleObject(C.newGroup(g.container.id))
+ g.child = control
+ g.child.setParent(g.container.parent())
g.SetText(text)
+ g.chainresize = g.fresize
+ g.fresize = g.xresize
return g
}
func (g *group) Text() string {
- return C.GoString(C.groupText(g._id))
+ return C.GoString(C.groupText(g.id))
}
func (g *group) SetText(text string) {
ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext))
- C.groupSetText(g._id, ctext)
-}
-
-func (g *group) id() C.id {
- return g._id
+ C.groupSetText(g.id, ctext)
}
-func (g *group) setParent(p *controlParent) {
- basesetParent(g, p)
+func (g *group) Margined() bool {
+ return g.margined
}
-func (g *group) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(g, x, y, width, height, d)
+func (g *group) SetMargined(margined bool) {
+ g.margined = margined
}
-func (g *group) preferredSize(d *sizing) (width, height int) {
- return basepreferredSize(g, d)
-}
-
-func (g *group) commitResize(a *allocation, d *sizing) {
- basecommitResize(g, a, d)
-}
+func (g *group) xresize(x int, y int, width int, height int, d *sizing) {
+ // first, chain up to change the GtkFrame and its child container
+ g.chainresize(x, y, width, height, d)
-func (g *group) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(g, d)
+ // now that the container has the correct size, we can resize the child
+ a := g.container.allocation(g.margined)
+ g.child.resize(int(a.x), int(a.y), int(a.width), int(a.height), d)
}
diff --git a/group_unix.go b/group_unix.go
index b59b158..681ec75 100644
--- a/group_unix.go
+++ b/group_unix.go
@@ -12,11 +12,16 @@ import (
import "C"
type group struct {
- _widget *C.GtkWidget
+ *controlSingleWidget
gcontainer *C.GtkContainer
frame *C.GtkFrame
- *container
+ child Control
+ container *container
+
+ margined bool
+
+ chainresize func(x int, y int, width int, height int, d *sizing)
}
func newGroup(text string, control Control) Group {
@@ -24,9 +29,10 @@ func newGroup(text string, control Control) Group {
defer freegstr(ctext)
widget := C.gtk_frame_new(ctext)
g := &group{
- _widget: widget,
+ controlSingleWidget: newControlSingleWidget(widget),
gcontainer: (*C.GtkContainer)(unsafe.Pointer(widget)),
frame: (*C.GtkFrame)(unsafe.Pointer(widget)),
+ child: control,
}
// with GTK+, groupboxes by default have frames and slightly x-offset regular text
@@ -46,9 +52,13 @@ func newGroup(text string, control Control) Group {
C.gtk_label_set_attributes(label, boldlist)
C.pango_attr_list_unref(boldlist) // thanks baedert in irc.gimp.net/#gtk+
- g.container = newContainer(control)
+ g.container = newContainer()
+ g.child.setParent(g.container.parent())
g.container.setParent(&controlParent{g.gcontainer})
+ g.chainresize = g.fresize
+ g.fresize = g.xresize
+
return g
}
@@ -62,26 +72,19 @@ func (g *group) SetText(text string) {
C.gtk_frame_set_label(g.frame, ctext)
}
-func (g *group) widget() *C.GtkWidget {
- return g._widget
-}
-
-func (g *group) setParent(p *controlParent) {
- basesetParent(g, p)
-}
-
-func (g *group) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(g, x, y, width, height, d)
+func (g *group) Margined() bool {
+ return g.margined
}
-func (g *group) preferredSize(d *sizing) (width, height int) {
- return basepreferredSize(g, d)
+func (g *group) SetMargined(margined bool) {
+ g.margined = margined
}
-func (g *group) commitResize(a *allocation, d *sizing) {
- basecommitResize(g, a, d)
-}
+func (g *group) xresize(x int, y int, width int, height int, d *sizing) {
+ // first, chain up to change the GtkFrame and its child container
+ g.chainresize(x, y, width, height, d)
-func (g *group) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(g, d)
+ // now that the container has the correct size, we can resize the child
+ a := g.container.allocation(g.margined)
+ g.child.resize(int(a.x), int(a.y), int(a.width), int(a.height), d)
}
diff --git a/group_windows.go b/group_windows.go
index 05956b0..d4acdda 100644
--- a/group_windows.go
+++ b/group_windows.go
@@ -6,10 +6,10 @@ package ui
import "C"
type group struct {
- _hwnd C.HWND
- _textlen C.LONG
-
- *container
+ *controlSingleHWNDWithText
+ child Control
+ margined bool
+ chainresize func(x int, y int, width int, height int, d *sizing)
}
func newGroup(text string, control Control) Group {
@@ -17,66 +17,82 @@ func newGroup(text string, control Control) Group {
C.BS_GROUPBOX,
C.WS_EX_CONTROLPARENT)
g := &group{
- _hwnd: hwnd,
- container: newContainer(control),
+ controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
+ child: control,
}
+ g.fpreferredSize = g.xpreferredSize
+ g.chainresize = g.fresize
+ g.fresize = g.xresize
+ g.fnTabStops = control.nTabStops // groupbox itself is not tabbable but the contents might be
g.SetText(text)
- C.controlSetControlFont(g._hwnd)
- g.container.setParent(g._hwnd)
- g.container.isGroup = true
+ C.controlSetControlFont(g.hwnd)
+ control.setParent(&controlParent{g.hwnd})
return g
}
func (g *group) Text() string {
- return baseText(g)
+ return g.text()
}
func (g *group) SetText(text string) {
- baseSetText(g, text)
-}
-
-func (g *group) hwnd() C.HWND {
- return g._hwnd
+ g.setText(text)
}
-func (g *group) textlen() C.LONG {
- return g._textlen
+func (g *group) Margined() bool {
+ return g.margined
}
-func (g *group) settextlen(len C.LONG) {
- g._textlen = len
+func (g *group) SetMargined(margined bool) {
+ g.margined = margined
}
-func (g *group) setParent(p *controlParent) {
- basesetParent(g, p)
-}
+const (
+ groupXMargin = 6
+ groupYMarginTop = 11 // note this value /includes the groupbox label/
+ groupYMarginBottom = 7
+)
-func (g *group) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(g, x, y, width, height, d)
-}
+func (g *group) xpreferredSize(d *sizing) (width, height int) {
+ var r C.RECT
-func (g *group) preferredSize(d *sizing) (width, height int) {
width, height = g.child.preferredSize(d)
- if width < int(g._textlen) { // if the text is longer, try not to truncate
- width = int(g._textlen)
+ if width < int(g.textlen) { // if the text is longer, try not to truncate
+ width = int(g.textlen)
}
- // the two margin constants come from container_windows.go
- return width, height + fromdlgunitsY(groupYMarginTop, d) + fromdlgunitsY(groupYMarginBottom, d)
+ r.left = 0
+ r.top = 0
+ r.right = C.LONG(width)
+ r.bottom = C.LONG(height)
+ // use negative numbers to increase the size of the rectangle
+ if g.margined {
+ marginRectDLU(&r, -groupYMarginTop, -groupYMarginBottom, -groupXMargin, -groupXMargin, d)
+ } else {
+ // unforutnately, as mentioned above, the size of a groupbox includes the label and border
+ // 1DLU on each side should be enough to make up for that; TODO is not, we can change it
+ // TODO make these named constants
+ marginRectDLU(&r, -1, -1, -1, -1, d)
+ }
+ return int(r.right - r.left), int(r.bottom - r.top)
}
-func (g *group) commitResize(c *allocation, d *sizing) {
+func (g *group) xresize(x int, y int, width int, height int, d *sizing) {
+ // first, chain up to the container base to keep the Z-order correct
+ g.chainresize(x, y, width, height, d)
+
+ // now resize the child container
var r C.RECT
// pretend that the client area of the group box only includes the actual empty space
// container will handle the necessary adjustments properly
r.left = 0
r.top = 0
- r.right = C.LONG(c.width)
- r.bottom = C.LONG(c.height)
- g.container.move(&r)
- basecommitResize(g, c, d)
-}
-
-func (g *group) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(g, d)
+ r.right = C.LONG(width)
+ r.bottom = C.LONG(height)
+ if g.margined {
+ // see above
+ marginRectDLU(&r, groupYMarginTop, groupYMarginBottom, groupXMargin, groupXMargin, d)
+ } else {
+ marginRectDLU(&r, 1, 1, 1, 1, d)
+ }
+ g.child.resize(int(r.left), int(r.top), int(r.right - r.left), int(r.bottom - r.top), d)
}
diff --git a/label_darwin.go b/label_darwin.go
index 0ad73e7..64a15b1 100644
--- a/label_darwin.go
+++ b/label_darwin.go
@@ -10,57 +10,28 @@ import (
import "C"
type label struct {
- _id C.id
- standalone bool
+ *controlSingleObject
}
-func finishNewLabel(text string, standalone bool) *label {
+func newLabel(text string) Label {
l := &label{
- _id: C.newLabel(),
- standalone: standalone,
+ controlSingleObject: newControlSingleObject(C.newLabel()),
}
l.SetText(text)
return l
}
-func newLabel(text string) Label {
- return finishNewLabel(text, false)
-}
-
-func newStandaloneLabel(text string) Label {
- return finishNewLabel(text, true)
-}
-
func (l *label) Text() string {
- return C.GoString(C.textfieldText(l._id))
+ return C.GoString(C.textfieldText(l.id))
}
func (l *label) SetText(text string) {
ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext))
- C.textfieldSetText(l._id, ctext)
-}
-
-func (l *label) isStandalone() bool {
- return l.standalone
-}
-
-func (l *label) id() C.id {
- return l._id
-}
-
-func (l *label) setParent(p *controlParent) {
- basesetParent(l, p)
-}
-
-func (l *label) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(l, x, y, width, height, d)
-}
-
-func (l *label) preferredSize(d *sizing) (width, height int) {
- return basepreferredSize(l, d)
+ C.textfieldSetText(l.id, ctext)
}
+/*TODO
func (l *label) commitResize(c *allocation, d *sizing) {
if !l.standalone && c.neighbor != nil {
c.neighbor.getAuxResizeInfo(d)
@@ -89,7 +60,4 @@ func (l *label) commitResize(c *allocation, d *sizing) {
}
basecommitResize(l, c, d)
}
-
-func (l *label) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(l, d)
-}
+*/
diff --git a/label_unix.go b/label_unix.go
index 750a7f8..819c0e4 100644
--- a/label_unix.go
+++ b/label_unix.go
@@ -9,40 +9,34 @@ import (
)
// #include "gtk_unix.h"
-// extern void buttonClicked(GtkButton *, gpointer);
-// extern void checkboxToggled(GtkToggleButton *, gpointer);
import "C"
type label struct {
- _widget *C.GtkWidget
+ *controlSingleWidget
misc *C.GtkMisc
label *C.GtkLabel
- standalone bool
}
-func finishNewLabel(text string, standalone bool) *label {
+func newLabel(text string) Label {
ctext := togstr(text)
defer freegstr(ctext)
widget := C.gtk_label_new(ctext)
l := &label{
- _widget: widget,
+ controlSingleWidget: newControlSingleWidget(widget),
misc: (*C.GtkMisc)(unsafe.Pointer(widget)),
label: (*C.GtkLabel)(unsafe.Pointer(widget)),
- standalone: standalone,
}
return l
}
-func newLabel(text string) Label {
- return finishNewLabel(text, false)
-}
-
+/*TODO
func newStandaloneLabel(text string) Label {
l := finishNewLabel(text, true)
// standalone labels are always at the top left
C.gtk_misc_set_alignment(l.misc, 0, 0)
return l
}
+*/
func (l *label) Text() string {
return fromgstr(C.gtk_label_get_text(l.label))
@@ -54,26 +48,7 @@ func (l *label) SetText(text string) {
C.gtk_label_set_text(l.label, ctext)
}
-func (l *label) isStandalone() bool {
- return l.standalone
-}
-
-func (l *label) widget() *C.GtkWidget {
- return l._widget
-}
-
-func (l *label) setParent(p *controlParent) {
- basesetParent(l, p)
-}
-
-func (l *label) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(l, x, y, width, height, d)
-}
-
-func (l *label) preferredSize(d *sizing) (width, height int) {
- return basepreferredSize(l, d)
-}
-
+/*TODO
func (l *label) commitResize(c *allocation, d *sizing) {
if !l.standalone && c.neighbor != nil {
c.neighbor.getAuxResizeInfo(d)
@@ -86,7 +61,4 @@ func (l *label) commitResize(c *allocation, d *sizing) {
}
basecommitResize(l, c, d)
}
-
-func (l *label) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(l, d)
-}
+*/
diff --git a/label_windows.go b/label_windows.go
index df39e51..8f8f120 100644
--- a/label_windows.go
+++ b/label_windows.go
@@ -6,67 +6,37 @@ package ui
import "C"
type label struct {
- _hwnd C.HWND
- _textlen C.LONG
+ *controlSingleHWNDWithText
standalone bool
}
var labelclass = toUTF16("STATIC")
-func finishNewLabel(text string, standalone bool) *label {
+func newLabel(text string) Label {
hwnd := C.newControl(labelclass,
// SS_NOPREFIX avoids accelerator translation; SS_LEFTNOWORDWRAP clips text past the end
// controls are vertically aligned to the top by default (thanks Xeek in irc.freenode.net/#winapi)
C.SS_NOPREFIX|C.SS_LEFTNOWORDWRAP,
C.WS_EX_TRANSPARENT)
l := &label{
- _hwnd: hwnd,
- standalone: standalone,
+ controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
+ }
+ l.fpreferredSize = l.xpreferredSize
+ l.fnTabStops = func() int {
+ // labels are not tab stops
+ return 0
}
l.SetText(text)
- C.controlSetControlFont(l._hwnd)
+ C.controlSetControlFont(l.hwnd)
return l
}
-func newLabel(text string) Label {
- return finishNewLabel(text, false)
-}
-
-func newStandaloneLabel(text string) Label {
- return finishNewLabel(text, true)
-}
-
func (l *label) Text() string {
- return baseText(l)
+ return l.text()
}
func (l *label) SetText(text string) {
- baseSetText(l, text)
-}
-
-func (l *label) isStandalone() bool {
- return l.standalone
-}
-
-func (l *label) hwnd() C.HWND {
- return l._hwnd
-}
-
-func (l *label) textlen() C.LONG {
- return l._textlen
-}
-
-func (l *label) settextlen(len C.LONG) {
- l._textlen = len
-}
-
-func (l *label) setParent(p *controlParent) {
- C.controlSetParent(l.hwnd(), p.c.hwnd)
- // don't increment p.c.nchildren here because Labels aren't tab stops
-}
-
-func (l *label) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(l, x, y, width, height, d)
+ l.setText(text)
}
const (
@@ -75,10 +45,11 @@ const (
labelYOffset = 3
)
-func (l *label) preferredSize(d *sizing) (width, height int) {
- return int(l._textlen), fromdlgunitsY(labelHeight, d)
+func (l *label) xpreferredSize(d *sizing) (width, height int) {
+ return int(l.textlen), fromdlgunitsY(labelHeight, d)
}
+/*TODO
func (l *label) commitResize(c *allocation, d *sizing) {
if !l.standalone {
yoff := fromdlgunitsY(labelYOffset, d)
@@ -93,7 +64,4 @@ func (l *label) commitResize(c *allocation, d *sizing) {
}
basecommitResize(l, c, d)
}
-
-func (l *label) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(l, d)
-}
+*/
diff --git a/objc_darwin.h b/objc_darwin.h
index 5fa1557..9f51b8e 100644
--- a/objc_darwin.h
+++ b/objc_darwin.h
@@ -83,6 +83,7 @@ extern void groupSetText(id, char *);
/* container_darwin.m */
extern id newContainerView(void *);
extern void moveControl(id, intptr_t, intptr_t, intptr_t, intptr_t);
+extern struct xrect containerBounds(id);
/* tab_darwin.m */
extern id newTab(void);
diff --git a/simplegrid.go b/simplegrid.go
index 7e8527f..aac2586 100644
--- a/simplegrid.go
+++ b/simplegrid.go
@@ -14,8 +14,6 @@ import (
// One Control can be marked as "stretchy": when the Window containing the SimpleGrid is resized, the cell containing that Control resizes to take any remaining space; its row and column are adjusted accordingly (so other filling controls in the same row and column will fill to the new height and width, respectively).
// A stretchy Control implicitly fills its cell.
// All cooridnates in a SimpleGrid are given in (row,column) form with (0,0) being the top-left cell.
-//
-// As a special rule, to ensure proper appearance, non-standalone Labels are automatically made filling.
type SimpleGrid interface {
Control
@@ -28,6 +26,11 @@ type SimpleGrid interface {
// Only one control can be stretchy per SimpleGrid; calling SetStretchy multiple times merely changes which control is stretchy (preserving the previous filling value).
// It panics if the given coordinate is invalid.
SetStretchy(row int, column int)
+
+ // Padded and SetPadded get and set whether the controls of the SimpleGrid have padding between them.
+ // The size of the padding is platform-dependent.
+ Padded() bool
+ SetPadded(padded bool)
}
type simpleGrid struct {
@@ -37,6 +40,8 @@ type simpleGrid struct {
stretchyfill bool
widths, heights [][]int // caches to avoid reallocating each time
rowheights, colwidths []int
+ container *container
+ padded bool
}
// NewSimpleGrid creates a new SimpleGrid with the given Controls.
@@ -64,13 +69,10 @@ func NewSimpleGrid(nPerRow int, controls ...Control) SimpleGrid {
ch[row] = make([]int, nPerRow)
for x := 0; x < nPerRow; x++ {
cc[row][x] = controls[i]
- if l, ok := controls[i].(Label); ok && !l.isStandalone() {
- cf[row][x] = true
- }
i++
}
}
- return &simpleGrid{
+ g := &simpleGrid{
controls: cc,
filling: cf,
stretchyrow: -1,
@@ -79,7 +81,15 @@ func NewSimpleGrid(nPerRow int, controls ...Control) SimpleGrid {
heights: ch,
rowheights: make([]int, nRows),
colwidths: make([]int, nPerRow),
+ container: newContainer(),
}
+ p := g.container.parent()
+ for _, cc := range g.controls {
+ for _, c := range cc {
+ c.setParent(p)
+ }
+ }
+ return g
}
func (g *simpleGrid) SetFilling(row int, column int) {
@@ -102,15 +112,19 @@ func (g *simpleGrid) SetStretchy(row int, column int) {
g.filling[g.stretchyrow][g.stretchycol] = true
}
+func (g *simpleGrid) Padded() bool {
+ return g.padded
+}
+
+func (g *simpleGrid) SetPadded(padded bool) {
+ g.padded = padded
+}
+
func (g *simpleGrid) setParent(parent *controlParent) {
- for _, col := range g.controls {
- for _, c := range col {
- c.setParent(parent)
- }
- }
+ g.container.setParent(parent)
}
-func (g *simpleGrid) allocate(x int, y int, width int, height int, d *sizing) (allocations []*allocation) {
+func (g *simpleGrid) resize(x int, y int, width int, height int, d *sizing) {
max := func(a int, b int) int {
if a > b {
return a
@@ -118,14 +132,21 @@ func (g *simpleGrid) allocate(x int, y int, width int, height int, d *sizing) (a
return b
}
- var current *allocation // for neighboring
-
+ g.container.resize(x, y, width, height, d)
if len(g.controls) == 0 {
- return nil
+ return
+ }
+ x, y, width, height = g.container.bounds(d)
+ // -1) get this SimpleGrid's padding
+ xpadding := d.xpadding
+ ypadding := d.ypadding
+ if !g.padded {
+ xpadding = 0
+ ypadding = 0
}
- // 0) inset the available rect by the needed padding
- width -= (len(g.colwidths) - 1) * d.xpadding
- height -= (len(g.rowheights) - 1) * d.ypadding
+ // 0) inset the available rect by the needed padding and reset x/y for children
+ width -= (len(g.colwidths) - 1) * xpadding
+ height -= (len(g.rowheights) - 1) * ypadding
// 1) clear data structures
for i := range g.rowheights {
g.rowheights[i] = 0
@@ -161,7 +182,6 @@ func (g *simpleGrid) allocate(x int, y int, width int, height int, d *sizing) (a
// 4) draw
startx := x
for row, xcol := range g.controls {
- current = nil // reset on new columns
for col, c := range xcol {
w := g.widths[row][col]
h := g.heights[row][col]
@@ -169,22 +189,12 @@ func (g *simpleGrid) allocate(x int, y int, width int, height int, d *sizing) (a
w = g.colwidths[col]
h = g.rowheights[row]
}
- as := c.allocate(x, y, w, h, d)
- if current != nil { // connect first left to first right
- current.neighbor = c
- }
- if len(as) != 0 {
- current = as[0] // next left is first subwidget
- } else {
- current = nil // spaces don't have allocation data
- }
- allocations = append(allocations, as...)
- x += g.colwidths[col] + d.xpadding
+ c.resize(x, y, w, h, d)
+ x += g.colwidths[col] + xpadding
}
x = startx
- y += g.rowheights[row] + d.ypadding
+ y += g.rowheights[row] + ypadding
}
- return
}
// filling and stretchy are ignored for preferred size calculation
@@ -196,8 +206,14 @@ func (g *simpleGrid) preferredSize(d *sizing) (width int, height int) {
return b
}
- width -= (len(g.colwidths) - 1) * d.xpadding
- height -= (len(g.rowheights) - 1) * d.ypadding
+ xpadding := d.xpadding
+ ypadding := d.ypadding
+ if !g.padded {
+ xpadding = 0
+ ypadding = 0
+ }
+ width -= (len(g.colwidths) - 1) * xpadding
+ height -= (len(g.rowheights) - 1) * ypadding
// 1) clear data structures
for i := range g.rowheights {
g.rowheights[i] = 0
@@ -225,10 +241,12 @@ func (g *simpleGrid) preferredSize(d *sizing) (width int, height int) {
return width, height
}
-func (g *simpleGrid) commitResize(c *allocation, d *sizing) {
- // this is to satisfy Control; nothing to do here
-}
-
-func (g *simpleGrid) getAuxResizeInfo(d *sizing) {
- // this is to satisfy Control; nothing to do here
-}
+func (g *simpleGrid) nTabStops() int {
+ n := 0
+ for _, cc := range g.controls {
+ for _, c := range cc {
+ n += c.nTabStops()
+ }
+ }
+ return n
+} \ No newline at end of file
diff --git a/stack.go b/stack.go
index 12fc08f..f210ec8 100644
--- a/stack.go
+++ b/stack.go
@@ -24,6 +24,11 @@ type Stack interface {
// SetStretchy marks a control in a Stack as stretchy.
// It panics if index is out of range.
SetStretchy(index int)
+
+ // Padded and SetPadded get and set whether the controls of the Stack have padding between them.
+ // The size of the padding is platform-dependent.
+ Padded() bool
+ SetPadded(padded bool)
}
type stack struct {
@@ -31,16 +36,24 @@ type stack struct {
controls []Control
stretchy []bool
width, height []int // caches to avoid reallocating these each time
+ container *container
+ padded bool
}
func newStack(o orientation, controls ...Control) Stack {
- return &stack{
+ s := &stack{
orientation: o,
controls: controls,
stretchy: make([]bool, len(controls)),
width: make([]int, len(controls)),
height: make([]int, len(controls)),
+ container: newContainer(),
+ }
+ p := s.container.parent()
+ for _, c := range s.controls {
+ c.setParent(p)
}
+ return s
}
// NewHorizontalStack creates a new Stack that arranges the given Controls horizontally.
@@ -60,24 +73,38 @@ func (s *stack) SetStretchy(index int) {
s.stretchy[index] = true
}
+func (s *stack) Padded() bool {
+ return s.padded
+}
+
+func (s *stack) SetPadded(padded bool) {
+ s.padded = padded
+}
+
func (s *stack) setParent(parent *controlParent) {
- for _, c := range s.controls {
- c.setParent(parent)
- }
+ s.container.setParent(parent)
}
-func (s *stack) allocate(x int, y int, width int, height int, d *sizing) (allocations []*allocation) {
+func (s *stack) resize(x int, y int, width int, height int, d *sizing) {
var stretchywid, stretchyht int
- var current *allocation // for neighboring
+ s.container.resize(x, y, width, height, d)
if len(s.controls) == 0 { // do nothing if there's nothing to do
- return nil
+ return
+ }
+ x, y, width, height = s.container.bounds(d)
+ // -1) get this Stack's padding
+ xpadding := d.xpadding
+ ypadding := d.ypadding
+ if !s.padded {
+ xpadding = 0
+ ypadding = 0
}
// 0) inset the available rect by the needed padding
if s.orientation == horizontal {
- width -= (len(s.controls) - 1) * d.xpadding
+ width -= (len(s.controls) - 1) * xpadding
} else {
- height -= (len(s.controls) - 1) * d.ypadding
+ height -= (len(s.controls) - 1) * ypadding
}
// 1) get height and width of non-stretchy controls; figure out how much space is alloted to stretchy controls
stretchywid = width
@@ -116,25 +143,14 @@ func (s *stack) allocate(x int, y int, width int, height int, d *sizing) (alloca
}
// 3) now actually place controls
for i, c := range s.controls {
- as := c.allocate(x, y, s.width[i], s.height[i], d)
- if s.orientation == horizontal { // no vertical neighbors
- if current != nil { // connect first left to first right
- current.neighbor = c
- }
- if len(as) != 0 {
- current = as[0] // next left is first subwidget
- } else {
- current = nil // spaces don't have allocation data
- }
- }
- allocations = append(allocations, as...)
+ c.resize(x, y, s.width[i], s.height[i], d)
if s.orientation == horizontal {
- x += s.width[i] + d.xpadding
+ x += s.width[i] + xpadding
} else {
- y += s.height[i] + d.ypadding
+ y += s.height[i] + ypadding
}
}
- return allocations
+ return
}
// The preferred size of a Stack is the sum of the preferred sizes of non-stretchy controls + (the number of stretchy controls * the largest preferred size among all stretchy controls).
@@ -152,10 +168,16 @@ func (s *stack) preferredSize(d *sizing) (width int, height int) {
if len(s.controls) == 0 { // no controls, so return emptiness
return 0, 0
}
+ xpadding := d.xpadding
+ ypadding := d.ypadding
+ if !s.padded {
+ xpadding = 0
+ ypadding = 0
+ }
if s.orientation == horizontal {
- width = (len(s.controls) - 1) * d.xpadding
+ width = (len(s.controls) - 1) * xpadding
} else {
- height = (len(s.controls) - 1) * d.ypadding
+ height = (len(s.controls) - 1) * ypadding
}
for i, c := range s.controls {
w, h := c.preferredSize(d)
@@ -184,13 +206,15 @@ func (s *stack) preferredSize(d *sizing) (width int, height int) {
return
}
-func (s *stack) commitResize(c *allocation, d *sizing) {
- // this is to satisfy Control; nothing to do here
+func (s *stack) nTabStops() int {
+ n := 0
+ for _, c := range s.controls {
+ n += c.nTabStops()
+ }
+ return n
}
-func (s *stack) getAuxResizeInfo(d *sizing) {
- // this is to satisfy Control; nothing to do here
-}
+// TODO the below needs to be changed
// Space returns a null Control intended for padding layouts with blank space.
// It appears to its owner as a Control of 0x0 size.
diff --git a/tab_darwin.go b/tab_darwin.go
index b33827d..112b81e 100644
--- a/tab_darwin.go
+++ b/tab_darwin.go
@@ -10,46 +10,44 @@ import (
import "C"
type tab struct {
- _id C.id
- tabs []*container
+ *controlSingleObject
+ tabs []*container
+ children []Control
+ chainresize func(x int, y int, width int, height int, d *sizing)
}
func newTab() Tab {
- return &tab{
- _id: C.newTab(),
+ t := &tab{
+ controlSingleObject: newControlSingleObject(C.newTab()),
}
+ t.fpreferredSize = t.xpreferredSize
+ t.chainresize = t.fresize
+ t.fresize = t.xresize
+ return t
}
func (t *tab) Append(name string, control Control) {
- c := newContainer(control)
+ c := newContainer()
t.tabs = append(t.tabs, c)
+ control.setParent(c.parent())
+ t.children = append(t.children, control)
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
- C.tabAppend(t._id, cname, c.id)
+ C.tabAppend(t.id, cname, c.id)
}
-func (t *tab) id() C.id {
- return t._id
-}
-
-func (t *tab) setParent(p *controlParent) {
- basesetParent(t, p)
-}
-
-func (t *tab) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(t, x, y, width, height, d)
-}
-
-func (t *tab) preferredSize(d *sizing) (width, height int) {
- s := C.tabPreferredSize(t._id)
+func (t *tab) xpreferredSize(d *sizing) (width, height int) {
+ s := C.tabPreferredSize(t.id)
return int(s.width), int(s.height)
}
-// no need to override Control.commitResize() as only prepared the tabbed control; its children will be resized when that one is resized (and NSTabView itself will call setFrame: for us)
-func (t *tab) commitResize(a *allocation, d *sizing) {
- basecommitResize(t, a, d)
-}
+func (t *tab) xresize(x int, y int, width int, height int, d *sizing) {
+ // first, chain up to change the GtkFrame and its child container
+ t.chainresize(x, y, width, height, d)
-func (t *tab) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(t, d)
+ // now that the containers have the correct size, we can resize the children
+ for i, _ := range t.tabs {
+ a := t.tabs[i].allocation(false/*TODO*/)
+ t.children[i].resize(int(a.x), int(a.y), int(a.width), int(a.height), d)
+ }
}
diff --git a/tab_unix.go b/tab_unix.go
index 79f7844..064f17f 100644
--- a/tab_unix.go
+++ b/tab_unix.go
@@ -12,30 +12,37 @@ import (
import "C"
type tab struct {
- _widget *C.GtkWidget
+ *controlSingleWidget
container *C.GtkContainer
notebook *C.GtkNotebook
tabs []*container
+ children []Control
+
+ chainresize func(x int, y int, width int, height int, d *sizing)
}
func newTab() Tab {
widget := C.gtk_notebook_new()
t := &tab{
- _widget: widget,
+ controlSingleWidget: newControlSingleWidget(widget),
container: (*C.GtkContainer)(unsafe.Pointer(widget)),
notebook: (*C.GtkNotebook)(unsafe.Pointer(widget)),
}
+ t.chainresize = t.fresize
+ t.fresize = t.xresize
// there are no scrolling arrows by default; add them in case there are too many tabs
C.gtk_notebook_set_scrollable(t.notebook, C.TRUE)
return t
}
func (t *tab) Append(name string, control Control) {
- c := newContainer(control)
+ c := newContainer()
t.tabs = append(t.tabs, c)
// this calls gtk_container_add(), which, according to gregier in irc.gimp.net/#gtk+, acts just like gtk_notebook_append_page()
c.setParent(&controlParent{t.container})
+ control.setParent(c.parent())
+ t.children = append(t.children, control)
cname := togstr(name)
defer freegstr(cname)
C.gtk_notebook_set_tab_label_text(t.notebook,
@@ -44,27 +51,13 @@ func (t *tab) Append(name string, control Control) {
cname)
}
-func (t *tab) widget() *C.GtkWidget {
- return t._widget
-}
-
-func (t *tab) setParent(p *controlParent) {
- basesetParent(t, p)
-}
-
-func (t *tab) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(t, x, y, width, height, d)
-}
+func (t *tab) xresize(x int, y int, width int, height int, d *sizing) {
+ // first, chain up to change the GtkFrame and its child container
+ t.chainresize(x, y, width, height, d)
-func (t *tab) preferredSize(d *sizing) (width, height int) {
- return basepreferredSize(t, d)
-}
-
-// no need to override Control.commitResize() as only prepared the tabbed control; its children will be reallocated when that one is resized
-func (t *tab) commitResize(a *allocation, d *sizing) {
- basecommitResize(t, a, d)
-}
-
-func (t *tab) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(t, d)
+ // now that the containers have the correct size, we can resize the children
+ for i, _ := range t.tabs {
+ a := t.tabs[i].allocation(false/*TODO*/)
+ t.children[i].resize(int(a.x), int(a.y), int(a.width), int(a.height), d)
+ }
}
diff --git a/tab_windows.go b/tab_windows.go
index 05d75d0..7203166 100644
--- a/tab_windows.go
+++ b/tab_windows.go
@@ -12,12 +12,14 @@ import "C"
/*
On Windows, container controls are just regular controls that notify their parent when the user wants to do things; changing the contents of a switching container (such as a tab control) must be done manually.
-We'll create a dummy window using the pre-existing Window window class for each tab page. This makes showing and hiding tabs a matter of showing and hiding one control.
+We'll create a dummy window using the container window class for each tab page. This makes showing and hiding tabs a matter of showing and hiding one control.
*/
type tab struct {
- _hwnd C.HWND
- tabs []*container
+ *controlSingleHWND
+ tabs []*container
+ children []Control
+ chainresize func(x int, y int, width int, height int, d *sizing)
}
func newTab() Tab {
@@ -25,22 +27,29 @@ func newTab() Tab {
C.TCS_TOOLTIPS|C.WS_TABSTOP,
0) // don't set WS_EX_CONTROLPARENT here; see uitask_windows.c
t := &tab{
- _hwnd: hwnd,
+ controlSingleHWND: newControlSingleHWND(hwnd),
}
- C.controlSetControlFont(t._hwnd)
- C.setTabSubclass(t._hwnd, unsafe.Pointer(t))
+ t.fpreferredSize = t.xpreferredSize
+ t.chainresize = t.fresize
+ t.fresize = t.xresize
+ // count tabs as 1 tab stop; the actual number of tab stops varies
+ C.controlSetControlFont(t.hwnd)
+ C.setTabSubclass(t.hwnd, unsafe.Pointer(t))
return t
}
+// TODO margined
func (t *tab) Append(name string, control Control) {
- c := newContainer(control)
- c.setParent(t._hwnd)
+ c := newContainer()
+ control.setParent(&controlParent{c.hwnd})
+ c.setParent(&controlParent{t.hwnd})
t.tabs = append(t.tabs, c)
+ t.children = append(t.children, control)
// initially hide tab 1..n controls; if we don't, they'll appear over other tabs, resulting in weird behavior
if len(t.tabs) != 1 {
t.tabs[len(t.tabs)-1].hide()
}
- C.tabAppend(t._hwnd, toUTF16(name))
+ C.tabAppend(t.hwnd, toUTF16(name))
}
//export tabChanging
@@ -61,27 +70,15 @@ func tabTabHasChildren(data unsafe.Pointer, which C.LRESULT) C.BOOL {
if len(t.tabs) == 0 { // currently no tabs
return C.FALSE
}
- if t.tabs[int(which)].nchildren > 0 {
+ if t.children[int(which)].nTabStops() > 0 {
return C.TRUE
}
return C.FALSE
}
-func (t *tab) hwnd() C.HWND {
- return t._hwnd
-}
-
-func (t *tab) setParent(p *controlParent) {
- basesetParent(t, p)
-}
-
-func (t *tab) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(t, x, y, width, height, d)
-}
-
-func (t *tab) preferredSize(d *sizing) (width, height int) {
- for _, s := range t.tabs {
- w, h := s.child.preferredSize(d)
+func (t *tab) xpreferredSize(d *sizing) (width, height int) {
+ for _, c := range t.children {
+ w, h := c.preferredSize(d)
if width < w {
width = w
}
@@ -89,30 +86,30 @@ func (t *tab) preferredSize(d *sizing) (width, height int) {
height = h
}
}
- return width, height + int(C.tabGetTabHeight(t._hwnd))
+ return width, height + int(C.tabGetTabHeight(t.hwnd))
}
// a tab control contains other controls; size appropriately
-func (t *tab) commitResize(c *allocation, d *sizing) {
+func (t *tab) xresize(x int, y int, width int, height int, d *sizing) {
+ // first, chain up to the container base to keep the Z-order correct
+ t.chainresize(x, y, width, height, d)
+
+ // now resize the children
var r C.RECT
// figure out what the rect for each child is...
- // the tab contents are children of the tab itself, so ignore c.x and c.y, which are relative to the window!
+ // the tab contents are children of the tab itself, so ignore x and y, which are relative to the window!
r.left = C.LONG(0)
r.top = C.LONG(0)
- r.right = C.LONG(c.width)
- r.bottom = C.LONG(c.height)
- C.tabGetContentRect(t._hwnd, &r)
+ r.right = C.LONG(width)
+ r.bottom = C.LONG(height)
+ C.tabGetContentRect(t.hwnd, &r)
// and resize tabs
// don't resize just the current tab; resize all tabs!
- for _, c := range t.tabs {
+ for i, _ := range t.tabs {
// because each widget is actually a child of the Window, the origin is the one we calculated above
- c.move(&r)
+ t.tabs[i].resize(int(r.left), int(r.top), int(r.right - r.left), int(r.bottom - r.top), d)
+ // TODO get the actual client rect
+ t.children[i].resize(int(0), int(0), int(r.right - r.left), int(r.bottom - r.top), d)
}
- // and now resize the tab control itself
- basecommitResize(t, c, d)
-}
-
-func (t *tab) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(t, d)
}
diff --git a/table_darwin.go b/table_darwin.go
index 9caf0fe..60a3614 100644
--- a/table_darwin.go
+++ b/table_darwin.go
@@ -14,8 +14,7 @@ import "C"
type table struct {
*tablebase
- _id C.id
- scroller *scroller
+ *scroller
images []C.id
selected *event
@@ -24,13 +23,13 @@ type table struct {
func finishNewTable(b *tablebase, ty reflect.Type) Table {
id := C.newTable()
t := &table{
- _id: id,
scroller: newScroller(id, true), // border on Table
tablebase: b,
selected: newEvent(),
}
+ t.fpreferredSize = t.xpreferredSize
// also sets the delegate
- C.tableMakeDataSource(t._id, unsafe.Pointer(t))
+ C.tableMakeDataSource(t.id, unsafe.Pointer(t))
for i := 0; i < ty.NumField(); i++ {
cname := C.CString(ty.Field(i).Name)
coltype := C.colTypeText
@@ -42,7 +41,7 @@ func finishNewTable(b *tablebase, ty reflect.Type) Table {
coltype = C.colTypeCheckbox
editable = true
}
- C.tableAppendColumn(t._id, C.intptr_t(i), cname, C.int(coltype), toBOOL(editable))
+ C.tableAppendColumn(t.id, C.intptr_t(i), cname, C.int(coltype), toBOOL(editable))
C.free(unsafe.Pointer(cname)) // free now (not deferred) to conserve memory
}
return t
@@ -56,7 +55,7 @@ func (t *table) Unlock() {
Do(func() {
t.RLock()
defer t.RUnlock()
- C.tableUpdate(t._id)
+ C.tableUpdate(t.id)
})
}()
}
@@ -68,13 +67,13 @@ func (t *table) LoadImageList(i ImageList) {
func (t *table) Selected() int {
t.RLock()
defer t.RUnlock()
- return int(C.tableSelected(t._id))
+ return int(C.tableSelected(t.id))
}
func (t *table) Select(index int) {
t.RLock()
defer t.RUnlock()
- C.tableSelect(t._id, C.intptr_t(index))
+ C.tableSelect(t.id, C.intptr_t(index))
}
func (t *table) OnSelected(f func()) {
@@ -132,27 +131,7 @@ func tableSelectionChanged(data unsafe.Pointer) {
t.selected.fire()
}
-func (t *table) id() C.id {
- return t._id
-}
-
-func (t *table) setParent(p *controlParent) {
- t.scroller.setParent(p)
-}
-
-func (t *table) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(t, x, y, width, height, d)
-}
-
-func (t *table) preferredSize(d *sizing) (width, height int) {
- s := C.tablePreferredSize(t._id)
+func (t *table) xpreferredSize(d *sizing) (width, height int) {
+ s := C.tablePreferredSize(t.id)
return int(s.width), int(s.height)
}
-
-func (t *table) commitResize(c *allocation, d *sizing) {
- t.scroller.commitResize(c, d)
-}
-
-func (t *table) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(t, d)
-}
diff --git a/table_unix.go b/table_unix.go
index 62fb7e2..931a28e 100644
--- a/table_unix.go
+++ b/table_unix.go
@@ -18,9 +18,8 @@ import "C"
type table struct {
*tablebase
- _widget *C.GtkWidget
+ *scroller
treeview *C.GtkTreeView
- scroller *scroller
model *C.goTableModel
modelgtk *C.GtkTreeModel
@@ -48,7 +47,6 @@ func finishNewTable(b *tablebase, ty reflect.Type) Table {
t := &table{
scroller: newScroller(widget, true, true, false), // natively scrollable; has a border; no overlay
tablebase: b,
- _widget: widget,
treeview: (*C.GtkTreeView)(unsafe.Pointer(widget)),
crtocol: make(map[*C.GtkCellRendererToggle]int),
selected: newEvent(),
@@ -222,28 +220,3 @@ func tableSelectionChanged(sel *C.GtkTreeSelection, data C.gpointer) {
t := (*table)(unsafe.Pointer(data))
t.selected.fire()
}
-
-func (t *table) widget() *C.GtkWidget {
- return t._widget
-}
-
-func (t *table) setParent(p *controlParent) {
- t.scroller.setParent(p)
-}
-
-func (t *table) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(t, x, y, width, height, d)
-}
-
-func (t *table) preferredSize(d *sizing) (width, height int) {
- return basepreferredSize(t, d)
-}
-
-func (t *table) commitResize(c *allocation, d *sizing) {
- t.scroller.commitResize(c, d)
-}
-
-func (t *table) getAuxResizeInfo(d *sizing) {
- // a Label to the left of a Table should be vertically aligned to the top
- d.shouldVAlignTop = true
-}
diff --git a/table_windows.go b/table_windows.go
index c606654..ceeb504 100644
--- a/table_windows.go
+++ b/table_windows.go
@@ -13,7 +13,7 @@ import "C"
type table struct {
*tablebase
- _hwnd C.HWND
+ *controlSingleHWND
noautosize bool
colcount C.int
hotrow C.int
@@ -21,13 +21,15 @@ type table struct {
pushedrow C.int
pushedcol C.int
selected *event
+ chainresize func(x int, y int, width int, height int, d *sizing)
}
func finishNewTable(b *tablebase, ty reflect.Type) Table {
+ hwnd := C.newControl(C.xWC_LISTVIEW,
+ C.LVS_REPORT|C.LVS_OWNERDATA|C.LVS_NOSORTHEADER|C.LVS_SHOWSELALWAYS|C.LVS_SINGLESEL|C.WS_HSCROLL|C.WS_VSCROLL|C.WS_TABSTOP,
+ C.WS_EX_CLIENTEDGE) // WS_EX_CLIENTEDGE without WS_BORDER will show the canonical visual styles border (thanks to MindChild in irc.efnet.net/#winprog)
t := &table{
- _hwnd: C.newControl(C.xWC_LISTVIEW,
- C.LVS_REPORT|C.LVS_OWNERDATA|C.LVS_NOSORTHEADER|C.LVS_SHOWSELALWAYS|C.LVS_SINGLESEL|C.WS_HSCROLL|C.WS_VSCROLL|C.WS_TABSTOP,
- C.WS_EX_CLIENTEDGE), // WS_EX_CLIENTEDGE without WS_BORDER will show the canonical visual styles border (thanks to MindChild in irc.efnet.net/#winprog)
+ controlSingleHWND: newControlSingleHWND(hwnd),
tablebase: b,
hotrow: -1,
hotcol: -1,
@@ -35,14 +37,17 @@ func finishNewTable(b *tablebase, ty reflect.Type) Table {
pushedcol: -1,
selected: newEvent(),
}
- C.setTableSubclass(t._hwnd, unsafe.Pointer(t))
+ t.fpreferredSize = t.xpreferredSize
+ t.chainresize = t.fresize
+ t.fresize = t.xresize
+ C.setTableSubclass(t.hwnd, unsafe.Pointer(t))
// LVS_EX_FULLROWSELECT gives us selection across the whole row, not just the leftmost column; this makes the list view work like on other platforms
// LVS_EX_SUBITEMIMAGES gives us images in subitems, which will be important when both images and checkboxes are added
- C.tableAddExtendedStyles(t._hwnd, C.LVS_EX_FULLROWSELECT|C.LVS_EX_SUBITEMIMAGES)
+ C.tableAddExtendedStyles(t.hwnd, C.LVS_EX_FULLROWSELECT|C.LVS_EX_SUBITEMIMAGES)
// this must come after the subclass because it uses one of our private messages
- C.SendMessageW(t._hwnd, C.msgTableMakeInitialCheckboxImageList, 0, 0)
+ C.SendMessageW(t.hwnd, C.msgTableMakeInitialCheckboxImageList, 0, 0)
for i := 0; i < ty.NumField(); i++ {
- C.tableAppendColumn(t._hwnd, C.int(i), toUTF16(ty.Field(i).Name))
+ C.tableAppendColumn(t.hwnd, C.int(i), toUTF16(ty.Field(i).Name))
}
t.colcount = C.int(ty.NumField())
return t
@@ -56,25 +61,25 @@ func (t *table) Unlock() {
Do(func() {
t.RLock()
defer t.RUnlock()
- C.tableUpdate(t._hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
+ C.tableUpdate(t.hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
})
}()
}
func (t *table) LoadImageList(il ImageList) {
- il.apply(t._hwnd, C.msgLoadImageList)
+ il.apply(t.hwnd, C.msgLoadImageList)
}
func (t *table) Selected() int {
t.RLock()
defer t.RUnlock()
- return int(C.tableSelectedItem(t._hwnd))
+ return int(C.tableSelectedItem(t.hwnd))
}
func (t *table) Select(index int) {
t.RLock()
defer t.RUnlock()
- C.tableSelectItem(t._hwnd, C.intptr_t(index))
+ C.tableSelectItem(t.hwnd, C.intptr_t(index))
}
func (t *table) OnSelected(f func()) {
@@ -144,7 +149,7 @@ func (t *table) autoresize() {
t.RLock()
defer t.RUnlock()
if !t.noautosize {
- C.tableAutosizeColumns(t._hwnd, t.colcount)
+ C.tableAutosizeColumns(t.hwnd, t.colcount)
}
}
@@ -167,7 +172,7 @@ func tableSetHot(data unsafe.Pointer, row C.int, col C.int) {
t.hotrow = row
t.hotcol = col
if redraw {
- C.tableUpdate(t._hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
+ C.tableUpdate(t.hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
}
}
@@ -176,7 +181,7 @@ func tablePushed(data unsafe.Pointer, row C.int, col C.int) {
t := (*table)(data)
t.pushedrow = row
t.pushedcol = col
- C.tableUpdate(t._hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
+ C.tableUpdate(t.hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
}
//export tableToggled
@@ -211,18 +216,6 @@ func tableSelectionChanged(data unsafe.Pointer) {
t.selected.fire()
}
-func (t *table) hwnd() C.HWND {
- return t._hwnd
-}
-
-func (t *table) setParent(p *controlParent) {
- basesetParent(t, p)
-}
-
-func (t *table) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(t, x, y, width, height, d)
-}
-
const (
// from C++ Template 05 in http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx as this is the best I can do for now
// there IS a message LVM_APPROXIMATEVIEWRECT that can do calculations, but it doesn't seem to work right when asked to base its calculations on the current width/height on Windows and wine...
@@ -230,17 +223,13 @@ const (
tableHeight = 50
)
-func (t *table) preferredSize(d *sizing) (width, height int) {
+func (t *table) xpreferredSize(d *sizing) (width, height int) {
return fromdlgunitsX(tableWidth, d), fromdlgunitsY(tableHeight, d)
}
-func (t *table) commitResize(a *allocation, d *sizing) {
- basecommitResize(t, a, d)
+func (t *table) xresize(x int, y int, width int, height int, d *sizing) {
+ t.chainresize(x, y, width, height, d)
t.RLock()
defer t.RUnlock()
t.autoresize()
}
-
-func (t *table) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(t, d)
-}
diff --git a/textfield_darwin.go b/textfield_darwin.go
index 3270cde..bf63073 100644
--- a/textfield_darwin.go
+++ b/textfield_darwin.go
@@ -10,17 +10,20 @@ import (
import "C"
type textfield struct {
- _id C.id
+ *controlSingleObject
changed *event
invalid C.id
+ chainpreferredSize func(d *sizing) (int, int)
}
func finishNewTextField(id C.id) *textfield {
t := &textfield{
- _id: id,
+ controlSingleObject: newControlSingleObject(id),
changed: newEvent(),
}
- C.textfieldSetDelegate(t._id, unsafe.Pointer(t))
+ C.textfieldSetDelegate(t.id, unsafe.Pointer(t))
+ t.chainpreferredSize = t.fpreferredSize
+ t.fpreferredSize = t.xpreferredSize
return t
}
@@ -33,13 +36,13 @@ func newPasswordField() *textfield {
}
func (t *textfield) Text() string {
- return C.GoString(C.textfieldText(t._id))
+ return C.GoString(C.textfieldText(t.id))
}
func (t *textfield) SetText(text string) {
ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext))
- C.textfieldSetText(t._id, ctext)
+ C.textfieldSetText(t.id, ctext)
}
func (t *textfield) OnChanged(f func()) {
@@ -56,7 +59,7 @@ func (t *textfield) Invalid(reason string) {
}
creason := C.CString(reason)
defer C.free(unsafe.Pointer(creason))
- t.invalid = C.textfieldOpenInvalidPopover(t._id, creason)
+ t.invalid = C.textfieldOpenInvalidPopover(t.id, creason)
}
//export textfieldChanged
@@ -65,28 +68,8 @@ func textfieldChanged(data unsafe.Pointer) {
t.changed.fire()
}
-func (t *textfield) id() C.id {
- return t._id
-}
-
-func (t *textfield) setParent(p *controlParent) {
- basesetParent(t, p)
-}
-
-func (t *textfield) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(t, x, y, width, height, d)
-}
-
-func (t *textfield) preferredSize(d *sizing) (width, height int) {
- _, height = basepreferredSize(t, d)
+func (t *textfield) xpreferredSize(d *sizing) (width, height int) {
+ _, height = t.chainpreferredSize(d)
// the returned width is based on the contents; use this instead
return C.textfieldWidth, height
}
-
-func (t *textfield) commitResize(a *allocation, d *sizing) {
- basecommitResize(t, a, d)
-}
-
-func (t *textfield) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(t, d)
-}
diff --git a/textfield_unix.go b/textfield_unix.go
index 33898d3..6535d39 100644
--- a/textfield_unix.go
+++ b/textfield_unix.go
@@ -18,7 +18,7 @@ import (
import "C"
type textfield struct {
- _widget *C.GtkWidget
+ *controlSingleWidget
entry *C.GtkEntry
changed *event
}
@@ -26,12 +26,12 @@ type textfield struct {
func startNewTextField() *textfield {
widget := C.gtk_entry_new()
t := &textfield{
- _widget: widget,
+ controlSingleWidget: newControlSingleWidget(widget),
entry: (*C.GtkEntry)(unsafe.Pointer(widget)),
changed: newEvent(),
}
g_signal_connect(
- C.gpointer(unsafe.Pointer(t._widget)),
+ C.gpointer(unsafe.Pointer(t.widget)),
"changed",
C.GCallback(C.textfieldChanged),
C.gpointer(unsafe.Pointer(t)))
@@ -71,7 +71,7 @@ func (t *textfield) Invalid(reason string) {
creason := togstr(reason)
defer freegstr(creason)
C.gtk_entry_set_icon_tooltip_text(t.entry, C.GTK_ENTRY_ICON_SECONDARY, creason)
- C.gtk_widget_error_bell(t._widget)
+ C.gtk_widget_error_bell(t.widget)
}
//export textfieldChanged
@@ -79,27 +79,3 @@ func textfieldChanged(editable *C.GtkEditable, data C.gpointer) {
t := (*textfield)(unsafe.Pointer(data))
t.changed.fire()
}
-
-func (t *textfield) widget() *C.GtkWidget {
- return t._widget
-}
-
-func (t *textfield) setParent(p *controlParent) {
- basesetParent(t, p)
-}
-
-func (t *textfield) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(t, x, y, width, height, d)
-}
-
-func (t *textfield) preferredSize(d *sizing) (width, height int) {
- return basepreferredSize(t, d)
-}
-
-func (t *textfield) commitResize(a *allocation, d *sizing) {
- basecommitResize(t, a, d)
-}
-
-func (t *textfield) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(t, d)
-}
diff --git a/textfield_windows.go b/textfield_windows.go
index 16ce9d2..76ec882 100644
--- a/textfield_windows.go
+++ b/textfield_windows.go
@@ -10,8 +10,7 @@ import (
import "C"
type textfield struct {
- _hwnd C.HWND
- _textlen C.LONG
+ *controlSingleHWNDWithText
changed *event
}
@@ -22,11 +21,12 @@ func startNewTextField(style C.DWORD) *textfield {
style|C.textfieldStyle,
C.textfieldExtStyle) // WS_EX_CLIENTEDGE without WS_BORDER will show the canonical visual styles border (thanks to MindChild in irc.efnet.net/#winprog)
t := &textfield{
- _hwnd: hwnd,
+ controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
changed: newEvent(),
}
- C.controlSetControlFont(t._hwnd)
- C.setTextFieldSubclass(t._hwnd, unsafe.Pointer(t))
+ t.fpreferredSize = t.xpreferredSize
+ C.controlSetControlFont(t.hwnd)
+ C.setTextFieldSubclass(t.hwnd, unsafe.Pointer(t))
return t
}
@@ -39,11 +39,11 @@ func newPasswordField() *textfield {
}
func (t *textfield) Text() string {
- return baseText(t)
+ return t.text()
}
func (t *textfield) SetText(text string) {
- baseSetText(t, text)
+ t.setText(text)
}
func (t *textfield) OnChanged(f func()) {
@@ -52,10 +52,10 @@ func (t *textfield) OnChanged(f func()) {
func (t *textfield) Invalid(reason string) {
if reason == "" {
- C.textfieldHideInvalidBalloonTip(t._hwnd)
+ C.textfieldHideInvalidBalloonTip(t.hwnd)
return
}
- C.textfieldSetAndShowInvalidBalloonTip(t._hwnd, toUTF16(reason))
+ C.textfieldSetAndShowInvalidBalloonTip(t.hwnd, toUTF16(reason))
}
//export textfieldChanged
@@ -64,40 +64,12 @@ func textfieldChanged(data unsafe.Pointer) {
t.changed.fire()
}
-func (t *textfield) hwnd() C.HWND {
- return t._hwnd
-}
-
-func (t *textfield) textlen() C.LONG {
- return t._textlen
-}
-
-func (t *textfield) settextlen(len C.LONG) {
- t._textlen = len
-}
-
-func (t *textfield) setParent(p *controlParent) {
- basesetParent(t, p)
-}
-
-func (t *textfield) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
- return baseallocate(t, x, y, width, height, d)
-}
-
const (
// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
textfieldWidth = 107 // this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary
textfieldHeight = 14
)
-func (t *textfield) preferredSize(d *sizing) (width, height int) {
+func (t *textfield) xpreferredSize(d *sizing) (width, height int) {
return fromdlgunitsX(textfieldWidth, d), fromdlgunitsY(textfieldHeight, d)
}
-
-func (t *textfield) commitResize(a *allocation, d *sizing) {
- basecommitResize(t, a, d)
-}
-
-func (t *textfield) getAuxResizeInfo(d *sizing) {
- basegetAuxResizeInfo(t, d)
-}
diff --git a/winapi_windows.h b/winapi_windows.h
index 83f7822..e6858e5 100644
--- a/winapi_windows.h
+++ b/winapi_windows.h
@@ -98,7 +98,7 @@ extern LRESULT getWindowTextLen(HWND);
extern void getWindowText(HWND, WPARAM, LPWSTR);
extern void setWindowText(HWND, LPWSTR);
extern void updateWindow(HWND);
-extern void *getWindowData(HWND, UINT, WPARAM, LPARAM, LRESULT *, void (*)(void *, HWND));
+extern void *getWindowData(HWND, UINT, WPARAM, LPARAM, LRESULT *);
extern BOOL sharedWndProc(HWND, UINT, WPARAM, LPARAM, LRESULT *);
extern void paintControlBackground(HWND, HDC);
@@ -122,8 +122,10 @@ extern intptr_t tableSelectedItem(HWND);
extern void tableSelectItem(HWND, intptr_t);
// container_windows.c
+#define containerclass L"gouicontainer"
extern DWORD makeContainerWindowClass(char **);
-extern HWND newContainer(void *);
+extern HWND newContainer();
+extern RECT containerBounds(HWND);
extern void calculateBaseUnits(HWND, int *, int *, LONG *);
// area_windows.c
diff --git a/window.go b/window.go
index dc98c1f..92b7080 100644
--- a/window.go
+++ b/window.go
@@ -26,6 +26,11 @@ type Window interface {
// If this handler returns false, the Window is not closed.
OnClosing(func() bool)
+ // Margined and SetMargined get and set whether the contents of the Window have a margin around them.
+ // The size of the margin is platform-dependent.
+ Margined() bool
+ SetMargined(margined bool)
+
windowDialog
}
diff --git a/window_darwin.go b/window_darwin.go
index bb3214f..e0804be 100644
--- a/window_darwin.go
+++ b/window_darwin.go
@@ -14,7 +14,10 @@ type window struct {
closing *event
- *container
+ child Control
+ container *container
+
+ margined bool
}
func newWindow(title string, width int, height int, control Control) *window {
@@ -25,10 +28,13 @@ func newWindow(title string, width int, height int, control Control) *window {
w := &window{
id: id,
closing: newEvent(),
- container: newContainer(control),
+ child: control,
+ container: newContainer(),
}
C.windowSetDelegate(w.id, unsafe.Pointer(w))
C.windowSetContentView(w.id, w.container.id)
+ w.child.setParent(w.container.parent())
+ // trigger an initial resize
return w
}
@@ -44,6 +50,9 @@ func (w *window) SetTitle(title string) {
func (w *window) Show() {
C.windowShow(w.id)
+ // trigger an initial resize
+ // TODO fine-tune this
+ windowResized(unsafe.Pointer(w))
}
func (w *window) Hide() {
@@ -58,6 +67,14 @@ func (w *window) OnClosing(e func() bool) {
w.closing.setbool(e)
}
+func (w *window) Margined() bool {
+ return w.margined
+}
+
+func (w *window) SetMargined(margined bool) {
+ w.margined = margined
+}
+
//export windowClosing
func windowClosing(xw unsafe.Pointer) C.BOOL {
w := (*window)(unsafe.Pointer(xw))
@@ -67,3 +84,11 @@ func windowClosing(xw unsafe.Pointer) C.BOOL {
}
return C.NO
}
+
+//export windowResized
+func windowResized(data unsafe.Pointer) {
+ w := (*window)(data)
+ a := w.container.allocation(w.margined)
+ d := w.beginResize()
+ w.child.resize(int(a.x), int(a.y), int(a.width), int(a.height), d)
+}
diff --git a/window_darwin.m b/window_darwin.m
index 21287cf..b156392 100644
--- a/window_darwin.m
+++ b/window_darwin.m
@@ -20,6 +20,11 @@
return windowClosing(self->gowin);
}
+- (void)windowDidResize:(NSNotification *)note
+{
+ windowResized(self->gowin);
+}
+
@end
id newWindow(intptr_t width, intptr_t height)
diff --git a/window_unix.go b/window_unix.go
index a220fb0..6b15bc1 100644
--- a/window_unix.go
+++ b/window_unix.go
@@ -10,6 +10,7 @@ import (
// #include "gtk_unix.h"
// extern gboolean windowClosing(GtkWidget *, GdkEvent *, gpointer);
+// extern void windowResized(GtkWidget *, GdkRectangle *, gpointer);
import "C"
type window struct {
@@ -22,7 +23,10 @@ type window struct {
closing *event
- *container
+ child Control
+ container *container
+
+ margined bool
}
func newWindow(title string, width int, height int, control Control) *window {
@@ -35,6 +39,7 @@ func newWindow(title string, width int, height int, control Control) *window {
bin: (*C.GtkBin)(unsafe.Pointer(widget)),
window: (*C.GtkWindow)(unsafe.Pointer(widget)),
closing: newEvent(),
+ child: control,
}
C.gtk_window_set_title(w.window, ctitle)
g_signal_connect(
@@ -43,8 +48,15 @@ func newWindow(title string, width int, height int, control Control) *window {
C.GCallback(C.windowClosing),
C.gpointer(unsafe.Pointer(w)))
C.gtk_window_resize(w.window, C.gint(width), C.gint(height))
- w.container = newContainer(control)
+ w.container = newContainer()
+ w.child.setParent(w.container.parent())
w.container.setParent(&controlParent{w.wc})
+ // notice that we connect this to the container
+ g_signal_connect_after( // so we get it after the child container has been allocated
+ C.gpointer(unsafe.Pointer(w.container.widget)),
+ "size-allocate",
+ C.GCallback(C.windowResized),
+ C.gpointer(unsafe.Pointer(w)))
// for dialogs; otherwise, they will be modal to all windows, not just this one
w.group = C.gtk_window_group_new()
C.gtk_window_group_add_window(w.group, w.window)
@@ -77,6 +89,14 @@ func (w *window) OnClosing(e func() bool) {
w.closing.setbool(e)
}
+func (w *window) Margined() bool {
+ return w.margined
+}
+
+func (w *window) SetMargined(margined bool) {
+ w.margined = margined
+}
+
//export windowClosing
func windowClosing(wid *C.GtkWidget, e *C.GdkEvent, data C.gpointer) C.gboolean {
w := (*window)(unsafe.Pointer(data))
@@ -86,3 +106,11 @@ func windowClosing(wid *C.GtkWidget, e *C.GdkEvent, data C.gpointer) C.gboolean
}
return C.GDK_EVENT_STOP // keeps window alive
}
+
+//export windowResized
+func windowResized(wid *C.GtkWidget, r *C.GdkRectangle, data C.gpointer) {
+ w := (*window)(unsafe.Pointer(data))
+ a := w.container.allocation(w.margined)
+ d := w.beginResize()
+ w.child.resize(int(a.x), int(a.y), int(a.width), int(a.height), d)
+}
diff --git a/window_windows.c b/window_windows.c
index 2d047b0..a4eefb3 100644
--- a/window_windows.c
+++ b/window_windows.c
@@ -13,7 +13,7 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA
RECT r;
LRESULT lResult;
- data = (void *) getWindowData(hwnd, uMsg, wParam, lParam, &lResult, storeWindowHWND);
+ data = (void *) getWindowData(hwnd, uMsg, wParam, lParam, &lResult);
if (data == NULL)
return lResult;
if (sharedWndProc(hwnd, uMsg, wParam, lParam, &lResult))
diff --git a/window_windows.go b/window_windows.go
index e20206e..8537be8 100644
--- a/window_windows.go
+++ b/window_windows.go
@@ -17,7 +17,8 @@ type window struct {
closing *event
- *container
+ child Control
+ margined bool
}
func makeWindowWindowClass() error {
@@ -32,19 +33,15 @@ func makeWindowWindowClass() error {
func newWindow(title string, width int, height int, control Control) *window {
w := &window{
- // hwnd set in WM_CREATE handler
closing: newEvent(),
- container: newContainer(control),
- }
- hwnd := C.newWindow(toUTF16(title), C.int(width), C.int(height), unsafe.Pointer(w))
- if hwnd != w.hwnd {
- panic(fmt.Errorf("inconsistency: hwnd returned by CreateWindowEx() (%p) and hwnd stored in Window (%p) differ", hwnd, w.hwnd))
+ child: control,
}
+ w.hwnd = C.newWindow(toUTF16(title), C.int(width), C.int(height), unsafe.Pointer(w))
hresult := C.EnableThemeDialogTexture(w.hwnd, C.ETDT_ENABLE|C.ETDT_USETABTEXTURE)
if hresult != C.S_OK {
panic(fmt.Errorf("error setting tab background texture on Window; HRESULT: 0x%X", hresult))
}
- w.container.setParent(w.hwnd)
+ w.child.setParent(&controlParent{w.hwnd})
return w
}
@@ -78,17 +75,22 @@ func (w *window) OnClosing(e func() bool) {
w.closing.setbool(e)
}
-//export storeWindowHWND
-func storeWindowHWND(data unsafe.Pointer, hwnd C.HWND) {
- w := (*window)(data)
- w.hwnd = hwnd
+func (w *window) Margined() bool {
+ return w.margined
+}
+
+func (w *window) SetMargined(margined bool) {
+ w.margined = margined
}
//export windowResize
func windowResize(data unsafe.Pointer, r *C.RECT) {
w := (*window)(data)
- // the origin of the window's content area is always (0, 0), but let's use the values from the RECT just to be safe
- w.container.move(r)
+ d := w.beginResize()
+ if w.margined {
+ marginRectDLU(r, marginDialogUnits, marginDialogUnits, marginDialogUnits, marginDialogUnits, d)
+ }
+ w.child.resize(int(r.left), int (r.top), int(r.right - r.left), int(r.bottom - r.top), d)
}
//export windowClosing
diff --git a/yz_repaint_test.go b/yz_repaint_test.go
index 8fca64b..8ea3cde 100644
--- a/yz_repaint_test.go
+++ b/yz_repaint_test.go
@@ -52,6 +52,7 @@ func newRepainter(times int) *repainter {
grid.Add(r.repaint, nil, South, true, Fill, true, Fill, 1, 1)
grid.Add(r.all, nil, South, true, Center, false, LeftTop, 1, 1)
r.grid = grid
+ r.grid.SetPadded(*spaced)
return r
}
diff --git a/zz_test.go b/zz_test.go
index a7b0670..3f2f079 100644
--- a/zz_test.go
+++ b/zz_test.go
@@ -18,6 +18,25 @@ import (
var closeOnClick = flag.Bool("close", false, "close on click")
var smallWindow = flag.Bool("small", false, "open a small window (test Mac OS X initial control sizing)")
+var spaced = flag.Bool("spaced", false, "enable spacing")
+
+func newHorizontalStack(c ...Control) Stack {
+ s := NewHorizontalStack(c...)
+ s.SetPadded(*spaced)
+ return s
+}
+
+func newVerticalStack(c ...Control) Stack {
+ s := NewVerticalStack(c...)
+ s.SetPadded(*spaced)
+ return s
+}
+
+func newSimpleGrid(n int, c ...Control) SimpleGrid {
+ g := NewSimpleGrid(n, c...)
+ g.SetPadded(*spaced)
+ return g
+}
type dtype struct {
Name string
@@ -96,7 +115,7 @@ func (tw *testwin) addfe() {
tw.felabel.SetText(t.String())
})
})
- tw.felabel = NewStandaloneLabel("<stopped>")
+ tw.felabel = NewLabel("<stopped>")
tw.festop = NewButton("Stop")
tw.festop.OnClicked(func() {
if tw.fe != nil {
@@ -117,8 +136,8 @@ func (tw *testwin) addfe() {
tw.openbtn.OnClicked(func() {
OpenFile(tw.w, tw.openFile)
})
- tw.fnlabel = NewStandaloneLabel("<no file selected>")
- tw.festack = NewVerticalStack(tw.festart,
+ tw.fnlabel = NewLabel("<no file selected>")
+ tw.festack = newVerticalStack(tw.festart,
tw.felabel,
tw.festop,
NewCheckbox("This is a checkbox test"),
@@ -129,7 +148,7 @@ func (tw *testwin) addfe() {
tw.openbtn, tw.fnlabel)
tw.festack.SetStretchy(4)
tw.festack.SetStretchy(6)
- tw.festack = NewHorizontalStack(tw.festack, Space())
+ tw.festack = newHorizontalStack(tw.festack, Space())
tw.festack.SetStretchy(0)
tw.festack.SetStretchy(1)
tw.t.Append("Foreign Events", tw.festack)
@@ -138,6 +157,7 @@ func (tw *testwin) addfe() {
func (tw *testwin) make(done chan struct{}) {
tw.t = NewTab()
tw.w = NewWindow("Hello", 320, 240, tw.t)
+ tw.w.SetMargined(*spaced)
tw.w.OnClosing(func() bool {
if *closeOnClick {
panic("window closed normally in close on click mode (should not happen)")
@@ -171,12 +191,14 @@ func (tw *testwin) make(done chan struct{}) {
tw.group2 = NewGroup("Group", NewButton("Button in Group"))
tw.t.Append("Empty Group", NewGroup("Group", Space()))
tw.t.Append("Filled Group", tw.group2)
- tw.group = NewGroup("Group", NewVerticalStack(NewCheckbox("Checkbox in Group")))
+ tw.group2.SetMargined(*spaced)
+ tw.group = NewGroup("Group", newVerticalStack(NewCheckbox("Checkbox in Group")))
+ tw.group.SetMargined(*spaced)
tw.t.Append("Group", tw.group)
- tw.simpleGrid = NewSimpleGrid(3,
+ tw.simpleGrid = newSimpleGrid(3,
NewLabel("0,0"), NewTextField(), NewLabel("0,2"),
NewButton("1,0"), NewButton("1,1"), NewButton("1,2"),
- NewLabel("2,0"), NewTextField(), NewStandaloneLabel("2,2"))
+ NewLabel("2,0"), NewTextField(), NewLabel("2,2"))
tw.simpleGrid.SetFilling(2, 1)
tw.simpleGrid.SetFilling(1, 2)
tw.simpleGrid.SetStretchy(1, 1)
@@ -189,33 +211,33 @@ func (tw *testwin) make(done chan struct{}) {
tw.t.Append("Space", Space())
tw.a = NewArea(200, 200, &areaHandler{false})
tw.t.Append("Area", tw.a)
- tw.spw = NewHorizontalStack(
+ tw.spw = newHorizontalStack(
NewButton("hello"),
NewCheckbox("hello"),
NewTextField(),
NewPasswordField(),
NewTable(reflect.TypeOf(struct{ A, B, C int }{})),
- NewStandaloneLabel("hello"))
+ NewLabel("hello"))
tw.t.Append("Pref Width", tw.spw)
- tw.sph = NewVerticalStack(
+ tw.sph = newVerticalStack(
NewButton("hello"),
NewCheckbox("hello"),
NewTextField(),
NewPasswordField(),
NewTable(reflect.TypeOf(struct{ A, B, C int }{})),
- NewStandaloneLabel("hello ÉÀÔ"))
+ NewLabel("hello ÉÀÔ"))
tw.t.Append("Pref Height", tw.sph)
- stack1 := NewHorizontalStack(NewLabel("Test"), NewTextField())
+ stack1 := newHorizontalStack(NewLabel("Test"), NewTextField())
stack1.SetStretchy(1)
- stack2 := NewHorizontalStack(NewLabel("ÉÀÔ"), NewTextField())
+ stack2 := newHorizontalStack(NewLabel("ÉÀÔ"), NewTextField())
stack2.SetStretchy(1)
- stack3 := NewHorizontalStack(NewLabel("Test 2"),
+ stack3 := newHorizontalStack(NewLabel("Test 2"),
NewTable(reflect.TypeOf(struct{ A, B, C int }{})))
stack3.SetStretchy(1)
- tw.s = NewVerticalStack(stack1, stack2, stack3)
+ tw.s = newVerticalStack(stack1, stack2, stack3)
tw.s.SetStretchy(2)
tw.t.Append("Stack", tw.s)
- tw.l = NewStandaloneLabel("hello")
+ tw.l = NewLabel("hello")
tw.t.Append("Label", tw.l)
tw.table = NewTable(reflect.TypeOf(ddata[0]))
tw.table.Lock()
@@ -248,7 +270,7 @@ func (tw *testwin) make(done chan struct{}) {
tw.w.Show()
if *smallWindow {
tw.wsmall = NewWindow("Small", 80, 80,
- NewVerticalStack(
+ newVerticalStack(
NewButton("Small"),
NewButton("Small 2"),
NewArea(200, 200, &areaHandler{true})))
@@ -262,7 +284,6 @@ var tw *testwin
// because Cocoa hates being run off the main thread, even if it's run exclusively off the main thread
func init() {
- flag.BoolVar(&spaced, "spaced", false, "enable spacing")
flag.Parse()
go func() {
tw = new(testwin)