Merged new container/sizing stuff.

This commit is contained in:
Pietro Gagliardi 2014-10-18 17:03:07 -04:00
parent 8c8b642adb
commit 62048303a3
51 changed files with 928 additions and 1227 deletions

View File

@ -15,8 +15,7 @@ import "C"
type area struct { type area struct {
*areabase *areabase
_id C.id *scroller
scroller *scroller
textfield C.id textfield C.id
textfielddone *event textfielddone *event
} }
@ -26,11 +25,12 @@ func newArea(ab *areabase) Area {
areabase: ab, areabase: ab,
textfielddone: newEvent(), textfielddone: newEvent(),
} }
a._id = C.newArea(unsafe.Pointer(a)) id := C.newArea(unsafe.Pointer(a))
a.scroller = newScroller(a._id, false) // no border on Area a.scroller = newScroller(id, false) // no border on Area
a.fpreferredSize = a.xpreferredSize
a.SetSize(a.width, a.height) a.SetSize(a.width, a.height)
a.textfield = C.newTextField() a.textfield = C.newTextField()
C.areaSetTextField(a._id, a.textfield) C.areaSetTextField(a.id, a.textfield)
return a return a
} }
@ -38,7 +38,7 @@ func (a *area) SetSize(width, height int) {
a.width = width a.width = width
a.height = height a.height = height
// set the frame size to set the area's effective size on the Cocoa side // 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) { 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.y = C.intptr_t(r.Min.Y)
s.width = C.intptr_t(r.Dx()) s.width = C.intptr_t(r.Dx())
s.height = C.intptr_t(r.Dy()) s.height = C.intptr_t(r.Dy())
C.areaRepaint(a._id, s) C.areaRepaint(a.id, s)
} }
func (a *area) RepaintAll() { func (a *area) RepaintAll() {
C.areaRepaintAll(a._id) C.areaRepaintAll(a.id)
} }
func (a *area) OpenTextFieldAt(x, y int) { func (a *area) OpenTextFieldAt(x, y int) {
if x < 0 || x >= a.width || y < 0 || y >= a.height { if x < 0 || x >= a.width || y < 0 || y >= a.height {
panic(fmt.Errorf("point (%d,%d) outside Area in Area.OpenTextFieldAt()", x, y)) 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 { 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) return sendKeyEvent(self, ke, data)
} }
func (a *area) id() C.id { func (a *area) xpreferredSize(d *sizing) (width, height int) {
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) {
// the preferred size of an Area is its size // the preferred size of an Area is its size
return a.width, a.height 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)
}

View File

@ -34,9 +34,8 @@ import "C"
type area struct { type area struct {
*areabase *areabase
_widget *C.GtkWidget *scroller
drawingarea *C.GtkDrawingArea drawingarea *C.GtkDrawingArea
scroller *scroller
clickCounter *clickCounter clickCounter *clickCounter
@ -59,7 +58,6 @@ func newArea(ab *areabase) Area {
textfieldw := C.gtk_entry_new() textfieldw := C.gtk_entry_new()
a := &area{ a := &area{
areabase: ab, areabase: ab,
_widget: widget,
drawingarea: (*C.GtkDrawingArea)(unsafe.Pointer(widget)), drawingarea: (*C.GtkDrawingArea)(unsafe.Pointer(widget)),
scroller: newScroller(widget, false, false, true), // not natively scrollable; no border; have an overlay for OpenTextFieldAt() scroller: newScroller(widget, false, false, true), // not natively scrollable; no border; have an overlay for OpenTextFieldAt()
clickCounter: new(clickCounter), clickCounter: new(clickCounter),
@ -67,6 +65,7 @@ func newArea(ab *areabase) Area {
textfield: (*C.GtkEntry)(unsafe.Pointer(textfieldw)), textfield: (*C.GtkEntry)(unsafe.Pointer(textfieldw)),
textfielddone: newEvent(), textfielddone: newEvent(),
} }
a.fpreferredSize = a.xpreferredSize
for _, c := range areaCallbacks { for _, c := range areaCallbacks {
g_signal_connect( g_signal_connect(
C.gpointer(unsafe.Pointer(a.drawingarea)), C.gpointer(unsafe.Pointer(a.drawingarea)),
@ -75,9 +74,9 @@ func newArea(ab *areabase) Area {
C.gpointer(unsafe.Pointer(a))) C.gpointer(unsafe.Pointer(a)))
} }
a.SetSize(a.width, a.height) 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( g_signal_connect(
C.gpointer(unsafe.Pointer(a.scroller.overlay)), C.gpointer(unsafe.Pointer(a.scroller.overlayoverlay)),
"get-child-position", "get-child-position",
area_get_child_position_callback, area_get_child_position_callback,
C.gpointer(unsafe.Pointer(a))) C.gpointer(unsafe.Pointer(a)))
@ -103,7 +102,7 @@ func newArea(ab *areabase) Area {
func (a *area) SetSize(width, height int) { func (a *area) SetSize(width, height int) {
a.width = width a.width = width
a.height = height 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) { func (a *area) Repaint(r image.Rectangle) {
@ -111,11 +110,11 @@ func (a *area) Repaint(r image.Rectangle) {
if r.Empty() { if r.Empty() {
return 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() { 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) { func (a *area) OpenTextFieldAt(x, y int) {
@ -492,28 +491,7 @@ var modonlykeys = map[C.guint]Modifiers{
C.GDK_KEY_Super_R: Super, C.GDK_KEY_Super_R: Super,
} }
func (a *area) widget() *C.GtkWidget { func (a *area) xpreferredSize(d *sizing) (width, height int) {
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) {
// the preferred size of an Area is its size // the preferred size of an Area is its size
return a.width, a.height 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
}

View File

@ -321,7 +321,7 @@ static LRESULT CALLBACK areaWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM
uintptr_t heldButtons = (uintptr_t) wParam; uintptr_t heldButtons = (uintptr_t) wParam;
LRESULT lResult; LRESULT lResult;
data = getWindowData(hwnd, uMsg, wParam, lParam, &lResult, storeAreaHWND); data = getWindowData(hwnd, uMsg, wParam, lParam, &lResult);
if (data == NULL) if (data == NULL)
return lResult; return lResult;
switch (uMsg) { switch (uMsg) {

View File

@ -15,7 +15,7 @@ import "C"
type area struct { type area struct {
*areabase *areabase
_hwnd C.HWND *controlSingleHWND
clickCounter *clickCounter clickCounter *clickCounter
@ -39,9 +39,10 @@ func newArea(ab *areabase) Area {
clickCounter: new(clickCounter), clickCounter: new(clickCounter),
textfielddone: newEvent(), 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.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) C.controlSetControlFont(a.textfield)
return a return a
} }
@ -49,14 +50,14 @@ func newArea(ab *areabase) Area {
func (a *area) SetSize(width, height int) { func (a *area) SetSize(width, height int) {
a.width = width a.width = width
a.height = height 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) { func (a *area) Repaint(r image.Rectangle) {
var hscroll, vscroll C.int var hscroll, vscroll C.int
var rect C.RECT 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 = r.Add(image.Pt(int(hscroll), int(vscroll))) // adjust by scroll position
r = image.Rect(0, 0, a.width, a.height).Intersect(r) r = image.Rect(0, 0, a.width, a.height).Intersect(r)
if r.Empty() { if r.Empty() {
@ -66,18 +67,18 @@ func (a *area) Repaint(r image.Rectangle) {
rect.top = C.LONG(r.Min.Y) rect.top = C.LONG(r.Min.Y)
rect.right = C.LONG(r.Max.X) rect.right = C.LONG(r.Max.X)
rect.bottom = C.LONG(r.Max.Y) 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() { 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) { func (a *area) OpenTextFieldAt(x, y int) {
if x < 0 || x >= a.width || y < 0 || y >= a.height { if x < 0 || x >= a.width || y < 0 || y >= a.height {
panic(fmt.Errorf("point (%d,%d) outside Area in Area.OpenTextFieldAt()", x, y)) 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 { func (a *area) TextFieldText() string {
@ -96,7 +97,7 @@ func (a *area) OnTextFieldDismissed(f func()) {
//export areaTextFieldDone //export areaTextFieldDone
func areaTextFieldDone(data unsafe.Pointer) { func areaTextFieldDone(data unsafe.Pointer) {
a := (*area)(data) a := (*area)(data)
C.areaMarkTextFieldDone(a._hwnd) C.areaMarkTextFieldDone(a.hwnd)
a.textfielddone.fire() a.textfielddone.fire()
} }
@ -323,39 +324,13 @@ var modonlykeys = map[C.WPARAM]Modifiers{
C.VK_RWIN: Super, C.VK_RWIN: Super,
} }
//export storeAreaHWND
func storeAreaHWND(data unsafe.Pointer, hwnd C.HWND) {
a := (*area)(data)
a._hwnd = hwnd
}
//export areaResetClickCounter //export areaResetClickCounter
func areaResetClickCounter(data unsafe.Pointer) { func areaResetClickCounter(data unsafe.Pointer) {
a := (*area)(data) a := (*area)(data)
a.clickCounter.reset() a.clickCounter.reset()
} }
func (a *area) hwnd() C.HWND { func (a *area) xpreferredSize(d *sizing) (width, height int) {
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) {
// the preferred size of an Area is its size // the preferred size of an Area is its size
return a.width, a.height 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)
}

View File

@ -87,31 +87,20 @@ func NewTab() Tab {
// Label is a Control that shows a static line of text. // 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. // 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). // Labels are left-aligned. [FUTURE PLANS: For platform-specific horizontal alignment rules, use a Form.]
// 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.]
type Label interface { type Label interface {
Control Control
// Text and SetText get and set the Label's text. // Text and SetText get and set the Label's text.
Text() string Text() string
SetText(text string) SetText(text string)
isStandalone() bool
} }
// NewLabel creates a new Label with the given text. // 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 { func NewLabel(text string) Label {
return newLabel(text) 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. // 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. // 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. // 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 and SetText get and set the Group's label text.
Text() string Text() string
SetText(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. // NewGroup creates a new Group with the given text label and child Control.

View File

@ -10,7 +10,7 @@ import (
import "C" import "C"
type button struct { type button struct {
_id C.id *controlSingleObject
clicked *event clicked *event
} }
@ -18,11 +18,11 @@ func newButton(text string) *button {
ctext := C.CString(text) ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext)) defer C.free(unsafe.Pointer(ctext))
b := &button{ b := &button{
_id: C.newButton(), controlSingleObject: newControlSingleObject(C.newButton()),
clicked: newEvent(), clicked: newEvent(),
} }
C.buttonSetText(b._id, ctext) C.buttonSetText(b.id, ctext)
C.buttonSetDelegate(b._id, unsafe.Pointer(b)) C.buttonSetDelegate(b.id, unsafe.Pointer(b))
return b return b
} }
@ -31,13 +31,13 @@ func (b *button) OnClicked(e func()) {
} }
func (b *button) Text() string { 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) { func (b *button) SetText(text string) {
ctext := C.CString(text) ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext)) defer C.free(unsafe.Pointer(ctext))
C.buttonSetText(b._id, ctext) C.buttonSetText(b.id, ctext)
} }
//export buttonClicked //export buttonClicked
@ -45,27 +45,3 @@ func buttonClicked(xb unsafe.Pointer) {
b := (*button)(unsafe.Pointer(xb)) b := (*button)(unsafe.Pointer(xb))
b.clicked.fire() 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)
}

View File

@ -13,7 +13,7 @@ import (
import "C" import "C"
type button struct { type button struct {
_widget *C.GtkWidget *controlSingleWidget
button *C.GtkButton button *C.GtkButton
clicked *event clicked *event
} }
@ -24,7 +24,7 @@ func newButton(text string) *button {
defer freegstr(ctext) defer freegstr(ctext)
widget := C.gtk_button_new_with_label(ctext) widget := C.gtk_button_new_with_label(ctext)
b := &button{ b := &button{
_widget: widget, controlSingleWidget: newControlSingleWidget(widget),
button: (*C.GtkButton)(unsafe.Pointer(widget)), button: (*C.GtkButton)(unsafe.Pointer(widget)),
clicked: newEvent(), clicked: newEvent(),
} }
@ -55,27 +55,3 @@ func buttonClicked(bwid *C.GtkButton, data C.gpointer) {
b := (*button)(unsafe.Pointer(data)) b := (*button)(unsafe.Pointer(data))
b.clicked.fire() 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)
}

View File

@ -10,8 +10,7 @@ import (
import "C" import "C"
type button struct { type button struct {
_hwnd C.HWND *controlSingleHWNDWithText
_textlen C.LONG
clicked *event clicked *event
} }
@ -22,12 +21,13 @@ func newButton(text string) *button {
C.BS_PUSHBUTTON|C.WS_TABSTOP, C.BS_PUSHBUTTON|C.WS_TABSTOP,
0) 0)
b := &button{ b := &button{
_hwnd: hwnd, controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
clicked: newEvent(), clicked: newEvent(),
} }
b.fpreferredSize = b.xpreferredSize
b.SetText(text) b.SetText(text)
C.controlSetControlFont(b._hwnd) C.controlSetControlFont(b.hwnd)
C.setButtonSubclass(b._hwnd, unsafe.Pointer(b)) C.setButtonSubclass(b.hwnd, unsafe.Pointer(b))
return b return b
} }
@ -36,11 +36,11 @@ func (b *button) OnClicked(e func()) {
} }
func (b *button) Text() string { func (b *button) Text() string {
return baseText(b) return b.text()
} }
func (b *button) SetText(text string) { func (b *button) SetText(text string) {
baseSetText(b, text) b.setText(text)
} }
//export buttonClicked //export buttonClicked
@ -49,51 +49,23 @@ func buttonClicked(data unsafe.Pointer) {
b.clicked.fire() 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 ( const (
// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing // from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
buttonHeight = 14 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... // comctl32.dll version 6 thankfully provides a method to grab this...
var size C.SIZE var size C.SIZE
size.cx = 0 // explicitly ask for ideal size size.cx = 0 // explicitly ask for ideal size
size.cy = 0 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) return int(size.cx), int(size.cy)
} }
// that failed, fall back // that failed, fall back
println("message failed; falling 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) // 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)) xmargins := 2 * int(C.GetSystemMetrics(C.SM_CXEDGE))
return xmargins + int(b._textlen), fromdlgunitsY(buttonHeight, d) 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)
} }

View File

@ -10,7 +10,7 @@ import (
import "C" import "C"
type checkbox struct { type checkbox struct {
_id C.id *controlSingleObject
toggled *event toggled *event
} }
@ -18,11 +18,11 @@ func newCheckbox(text string) *checkbox {
ctext := C.CString(text) ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext)) defer C.free(unsafe.Pointer(ctext))
c := &checkbox{ c := &checkbox{
_id: C.newCheckbox(), controlSingleObject: newControlSingleObject(C.newCheckbox()),
toggled: newEvent(), toggled: newEvent(),
} }
C.buttonSetText(c._id, ctext) C.buttonSetText(c.id, ctext)
C.checkboxSetDelegate(c._id, unsafe.Pointer(c)) C.checkboxSetDelegate(c.id, unsafe.Pointer(c))
return c return c
} }
@ -31,21 +31,21 @@ func (c *checkbox) OnToggled(e func()) {
} }
func (c *checkbox) Text() string { 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) { func (c *checkbox) SetText(text string) {
ctext := C.CString(text) ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext)) defer C.free(unsafe.Pointer(ctext))
C.buttonSetText(c._id, ctext) C.buttonSetText(c.id, ctext)
} }
func (c *checkbox) Checked() bool { func (c *checkbox) Checked() bool {
return fromBOOL(C.checkboxChecked(c._id)) return fromBOOL(C.checkboxChecked(c.id))
} }
func (c *checkbox) SetChecked(checked bool) { func (c *checkbox) SetChecked(checked bool) {
C.checkboxSetChecked(c._id, toBOOL(checked)) C.checkboxSetChecked(c.id, toBOOL(checked))
} }
//export checkboxToggled //export checkboxToggled
@ -53,27 +53,3 @@ func checkboxToggled(xc unsafe.Pointer) {
c := (*checkbox)(unsafe.Pointer(xc)) c := (*checkbox)(unsafe.Pointer(xc))
c.toggled.fire() 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)
}

View File

@ -13,7 +13,7 @@ import (
import "C" import "C"
type checkbox struct { type checkbox struct {
_widget *C.GtkWidget *controlSingleWidget
button *C.GtkButton button *C.GtkButton
toggle *C.GtkToggleButton toggle *C.GtkToggleButton
checkbox *C.GtkCheckButton checkbox *C.GtkCheckButton
@ -25,7 +25,7 @@ func newCheckbox(text string) *checkbox {
defer freegstr(ctext) defer freegstr(ctext)
widget := C.gtk_check_button_new_with_label(ctext) widget := C.gtk_check_button_new_with_label(ctext)
c := &checkbox{ c := &checkbox{
_widget: widget, controlSingleWidget: newControlSingleWidget(widget),
button: (*C.GtkButton)(unsafe.Pointer(widget)), button: (*C.GtkButton)(unsafe.Pointer(widget)),
toggle: (*C.GtkToggleButton)(unsafe.Pointer(widget)), toggle: (*C.GtkToggleButton)(unsafe.Pointer(widget)),
checkbox: (*C.GtkCheckButton)(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 := (*checkbox)(unsafe.Pointer(data))
c.toggled.fire() 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)
}

View File

@ -10,8 +10,7 @@ import (
import "C" import "C"
type checkbox struct { type checkbox struct {
_hwnd C.HWND *controlSingleHWNDWithText
_textlen C.LONG
toggled *event toggled *event
} }
@ -22,12 +21,13 @@ func newCheckbox(text string) *checkbox {
C.BS_CHECKBOX|C.WS_TABSTOP, C.BS_CHECKBOX|C.WS_TABSTOP,
0) 0)
c := &checkbox{ c := &checkbox{
_hwnd: hwnd, controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
toggled: newEvent(), toggled: newEvent(),
} }
c.fpreferredSize = c.xpreferredSize
c.SetText(text) c.SetText(text)
C.controlSetControlFont(c._hwnd) C.controlSetControlFont(c.hwnd)
C.setCheckboxSubclass(c._hwnd, unsafe.Pointer(c)) C.setCheckboxSubclass(c.hwnd, unsafe.Pointer(c))
return c return c
} }
@ -36,23 +36,23 @@ func (c *checkbox) OnToggled(e func()) {
} }
func (c *checkbox) Text() string { func (c *checkbox) Text() string {
return baseText(c) return c.text()
} }
func (c *checkbox) SetText(text string) { func (c *checkbox) SetText(text string) {
baseSetText(c, text) c.setText(text)
} }
func (c *checkbox) Checked() bool { 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) { func (c *checkbox) SetChecked(checked bool) {
if checked { if checked {
C.checkboxSetChecked(c._hwnd, C.TRUE) C.checkboxSetChecked(c.hwnd, C.TRUE)
return return
} }
C.checkboxSetChecked(c._hwnd, C.FALSE) C.checkboxSetChecked(c.hwnd, C.FALSE)
} }
//export checkboxToggled //export checkboxToggled
@ -61,26 +61,6 @@ func checkboxToggled(data unsafe.Pointer) {
c.toggled.fire() 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 ( const (
// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing // from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
checkboxHeight = 10 checkboxHeight = 10
@ -88,15 +68,7 @@ const (
checkboxXFromLeftOfBoxToLeftOfLabel = 12 checkboxXFromLeftOfBoxToLeftOfLabel = 12
) )
func (c *checkbox) preferredSize(d *sizing) (width, height int) { func (c *checkbox) xpreferredSize(d *sizing) (width, height int) {
return fromdlgunitsX(checkboxXFromLeftOfBoxToLeftOfLabel, d) + int(c._textlen), return fromdlgunitsX(checkboxXFromLeftOfBoxToLeftOfLabel, d) + int(c.textlen),
fromdlgunitsY(checkboxHeight, d) fromdlgunitsY(checkboxHeight, d)
} }
func (c *checkbox) commitResize(a *allocation, d *sizing) {
basecommitResize(c, a, d)
}
func (c *checkbox) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(c, d)
}

View File

@ -29,7 +29,7 @@ void updateWindow(HWND hwnd)
xpanic("error calling UpdateWindow()", GetLastError()); 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; CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
void *data; void *data;
@ -37,11 +37,8 @@ void *getWindowData(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT
data = (void *) GetWindowLongPtrW(hwnd, GWLP_USERDATA); data = (void *) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
if (data == NULL) { if (data == NULL) {
// the lpParam is available during WM_NCCREATE and WM_CREATE // 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)); 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) // 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); *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 WCHAR classname[128] = L""; // more than enough to avoid collisions
parent = hwnd; parent = hwnd;
do { for (;;) {
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;
parent = GetParent(parent); parent = GetParent(parent);
if (parent == NULL) if (parent == NULL)
xpanic("error getting parent control of control in paintControlBackground()", GetLastError()); xpanic("error getting parent control of control in paintControlBackground()", GetLastError());
// wine sends these messages early, yay...
if (parent == msgwin) if (parent == msgwin)
return; return;
if (GetClassNameW(parent, classname, 128) == 0) if (GetClassNameW(parent, classname, 128) == 0)
xpanic("error getting name of focused window class in paintControlBackground()", GetLastError()); 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) if (GetWindowRect(hwnd, &r) == 0)
xpanic("error getting control's window rect in paintControlBackground()", GetLastError()); xpanic("error getting control's window rect in paintControlBackground()", GetLastError());
// the above is a window rect; convert to client rect // the above is a window rect; convert to client rect

View File

@ -2,41 +2,14 @@
package ui package ui
type allocation struct {
x int
y int
width int
height int
this Control
neighbor Control
}
type sizingbase struct { type sizingbase struct {
xmargin int
ymargintop int
ymarginbottom int
xpadding int xpadding int
ypadding int ypadding int
} }
type controlSizing interface { // 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
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
/* TODO
func (c *container) resize(x, y, width, height int) { func (c *container) resize(x, y, width, height int) {
if c.child == nil { // no children; nothing to do if c.child == nil { // no children; nothing to do
return return
@ -50,3 +23,4 @@ func (c *container) resize(x, y, width, height int) {
allocations[i].this.commitResize(allocations[i], d) allocations[i].this.commitResize(allocations[i], d)
} }
} }
*/

View File

@ -10,33 +10,44 @@ import (
import "C" import "C"
type container struct { type container struct {
containerbase *controlSingleObject
id C.id
} }
type sizing struct { type sizing struct {
sizingbase sizingbase
// for size calculations // for size calculations
// nothing for mac // nothing on Mac OS X
// for the actual resizing // for the actual resizing
neighborAlign C.struct_xalignment neighborAlign C.struct_xalignment
} }
func newContainer(child Control) *container { func newContainer() *container {
c := new(container) c := new(container)
c.id = C.newContainerView(unsafe.Pointer(c)) c.controlSingleObject = newControlSingleObject(C.newContainerView(unsafe.Pointer(c)))
c.child = child
c.child.setParent(&controlParent{c.id})
return c return c
} }
//export containerResized func (c *container) parent() *controlParent {
func containerResized(data unsafe.Pointer, width C.intptr_t, height C.intptr_t) { return &controlParent{c.id}
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) 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. // These are based on measurements from Interface Builder.
@ -47,18 +58,14 @@ const (
macYPadding = 8 macYPadding = 8
) )
func (c *container) beginResize() (d *sizing) { func (w *window) beginResize() (d *sizing) {
d = new(sizing) d = new(sizing)
if spaced { d.xpadding = macXPadding
d.xmargin = macXMargin d.ypadding = macYPadding
d.ymargintop = macYMargin
d.ymarginbottom = d.ymargintop
d.xpadding = macXPadding
d.ypadding = macYPadding
}
return d return d
} }
/*TODO
func (c *container) translateAllocationCoords(allocations []*allocation, winwidth, winheight int) { func (c *container) translateAllocationCoords(allocations []*allocation, winwidth, winheight int) {
for _, a := range allocations { for _, a := range allocations {
// winheight - y because (0,0) is the bottom-left corner of the window and not the top-left corner // 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 a.y = (winheight - a.y) - a.height
} }
} }
*/

View File

@ -21,12 +21,6 @@
@implementation goContainerView @implementation goContainerView
- (void)setFrameSize:(NSSize)s
{
[super setFrameSize:s];
containerResized(self->gocontainer, (intptr_t) s.width, (intptr_t) s.height);
}
@end @end
id newContainerView(void *gocontainer) 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) 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;
} }

View File

@ -59,7 +59,6 @@ static void goContainer_remove(GtkContainer *container, GtkWidget *widget)
static void goContainer_size_allocate(GtkWidget *widget, GtkAllocation *allocation) static void goContainer_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
{ {
gtk_widget_set_allocation(widget, allocation); gtk_widget_set_allocation(widget, allocation);
containerResizing(GOCONTAINER(widget)->gocontainer, allocation);
} }
struct forall { struct forall {

View File

@ -12,9 +12,8 @@ import (
import "C" import "C"
type container struct { type container struct {
containerbase *controlSingleWidget
layoutwidget *C.GtkWidget container *C.GtkContainer
layoutcontainer *C.GtkContainer
} }
type sizing struct { type sizing struct {
@ -24,27 +23,40 @@ type sizing struct {
// gtk+ needs nothing // gtk+ needs nothing
// for the actual resizing // for the actual resizing
shouldVAlignTop bool // gtk+ needs nothing
} }
func newContainer(child Control) *container { func newContainer() *container {
c := new(container) c := new(container)
widget := C.newContainer(unsafe.Pointer(c)) c.controlSingleWidget = newControlSingleWidget(C.newContainer(unsafe.Pointer(c)))
c.layoutwidget = widget c.container = (*C.GtkContainer)(unsafe.Pointer(c.widget))
c.layoutcontainer = (*C.GtkContainer)(unsafe.Pointer(widget))
c.child = child
c.child.setParent(&controlParent{c.layoutcontainer})
return c return c
} }
func (c *container) setParent(p *controlParent) { func (c *container) parent() *controlParent {
C.gtk_container_add(p.c, c.layoutwidget) return &controlParent{c.container}
} }
//export containerResizing func (c *container) allocation(margined bool) C.GtkAllocation {
func containerResizing(data unsafe.Pointer, r *C.GtkAllocation) { var a C.GtkAllocation
c := (*container)(data)
c.resize(int(r.x), int(r.y), int(r.width), int(r.height)) 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 ( const (
@ -54,18 +66,9 @@ const (
gtkYPadding = 6 gtkYPadding = 6
) )
func (c *container) beginResize() (d *sizing) { func (w *window) beginResize() (d *sizing) {
d = new(sizing) d = new(sizing)
if spaced { d.xpadding = gtkXPadding
d.xmargin = gtkXMargin d.ypadding = gtkYPadding
d.ymargintop = gtkYMargin
d.ymarginbottom = d.ymargintop
d.xpadding = gtkXPadding
d.ypadding = gtkYPadding
}
return d return d
} }
func (c *container) translateAllocationCoords(allocations []*allocation, winwidth, winheight int) {
// no need for coordinate conversion with gtk+
}

View File

@ -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. 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) static LRESULT CALLBACK containerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{ {
void *data;
RECT r;
LRESULT lResult; LRESULT lResult;
data = getWindowData(hwnd, uMsg, wParam, lParam, &lResult, storeContainerHWND);
if (data == NULL)
return lResult;
if (sharedWndProc(hwnd, uMsg, wParam, lParam, &lResult)) if (sharedWndProc(hwnd, uMsg, wParam, lParam, &lResult))
return lResult; return lResult;
switch (uMsg) { 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: default:
return DefWindowProcW(hwnd, uMsg, wParam, lParam); return DefWindowProcW(hwnd, uMsg, wParam, lParam);
} }
@ -53,7 +41,7 @@ DWORD makeContainerWindowClass(char **errmsg)
return 0; return 0;
} }
HWND newContainer(void *data) HWND newContainer(void)
{ {
HWND hwnd; HWND hwnd;
@ -63,12 +51,21 @@ HWND newContainer(void *data)
WS_CHILD | WS_VISIBLE, WS_CHILD | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
100, 100, 100, 100,
msgwin, NULL, hInstance, data); msgwin, NULL, hInstance, NULL);
if (hwnd == NULL) if (hwnd == NULL)
xpanic("container creation failed", GetLastError()); xpanic("container creation failed", GetLastError());
return hwnd; 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) void calculateBaseUnits(HWND hwnd, int *baseX, int *baseY, LONG *internalLeading)
{ {
HDC dc; HDC dc;

View File

@ -5,17 +5,13 @@ package ui
import ( import (
"fmt" "fmt"
"syscall" "syscall"
"unsafe"
) )
// #include "winapi_windows.h" // #include "winapi_windows.h"
import "C" import "C"
type container struct { type container struct {
containerbase *controlSingleHWND
hwnd C.HWND
nchildren int
isGroup bool
} }
type sizing struct { type sizing struct {
@ -40,45 +36,30 @@ func makeContainerWindowClass() error {
return nil return nil
} }
func newContainer(control Control) *container { func newContainer() *container {
c := new(container) // don't set preferredSize(); it should never be called
hwnd := C.newContainer(unsafe.Pointer(c)) return &container{
if hwnd != c.hwnd { controlSingleHWND: newControlSingleHWND(C.newContainer()),
panic(fmt.Errorf("inconsistency: hwnd returned by CreateWindowEx() (%p) and hwnd stored in container (%p) differ", hwnd, c.hwnd))
} }
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() { func (c *container) show() {
C.ShowWindow(c.hwnd, C.SW_SHOW) C.ShowWindow(c.hwnd, C.SW_SHOW)
} }
// TODO merge with controlSingleHWND
func (c *container) hide() { func (c *container) hide() {
C.ShowWindow(c.hwnd, C.SW_HIDE) C.ShowWindow(c.hwnd, C.SW_HIDE)
} }
//export storeContainerHWND func (c *container) parent() *controlParent {
func storeContainerHWND(data unsafe.Pointer, hwnd C.HWND) { return &controlParent{c.hwnd}
c := (*container)(data)
c.hwnd = hwnd
} }
//export containerResize func (c *container) bounds(d *sizing) (int, int, int, int) {
func containerResize(data unsafe.Pointer, r *C.RECT) { r := C.containerBounds(c.hwnd)
c := (*container)(data) return int(r.left), int(r.top), int(r.right - r.left), int(r.bottom - r.top)
// 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))
} }
// 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. // 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 ( const (
// shared by multiple containers
marginDialogUnits = 7 marginDialogUnits = 7
paddingDialogUnits = 4 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 baseX, baseY C.int
var internalLeading C.LONG var internalLeading C.LONG
d = new(sizing) d = new(sizing)
C.calculateBaseUnits(c.hwnd, &baseX, &baseY, &internalLeading) C.calculateBaseUnits(w.hwnd, &baseX, &baseY, &internalLeading)
d.baseX = baseX d.baseX = baseX
d.baseY = baseY d.baseY = baseY
d.internalLeading = internalLeading d.internalLeading = internalLeading
if spaced { d.xpadding = fromdlgunitsX(paddingDialogUnits, d)
d.xmargin = fromdlgunitsX(marginDialogUnits, d) d.ypadding = fromdlgunitsY(paddingDialogUnits, 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)
}
return d return d
} }
func (c *container) translateAllocationCoords(allocations []*allocation, winwidth, winheight int) { func marginRectDLU(r *C.RECT, top int, bottom int, left int, right int, d *sizing) {
// no translation needed on windows 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))
} }

View File

@ -5,16 +5,32 @@ package ui
// Control represents a control. // Control represents a control.
type Control interface { type Control interface {
setParent(p *controlParent) // controlParent defined per-platform 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 type controlbase struct {
func baseallocate(c Control, x int, y int, width int, height int, d *sizing) []*allocation { fsetParent func(p *controlParent)
return []*allocation{&allocation{ fpreferredSize func(d *sizing) (width, height int)
x: x, fresize func(x int, y int, width int, height int, d *sizing)
y: y, fnTabStops func() int
width: width, }
height: height,
this: c, // 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()
} }

View File

@ -5,54 +5,52 @@ package ui
// #include "objc_darwin.h" // #include "objc_darwin.h"
import "C" import "C"
// all Controls that call base methods must be this
type controlPrivate interface {
id() C.id
Control
}
type controlParent struct { type controlParent struct {
id C.id id C.id
} }
func basesetParent(c controlPrivate, p *controlParent) { type controlSingleObject struct {
// redrawing the new window handled by C.parent() *controlbase
C.parent(c.id(), p.id) id C.id
} }
func basepreferredSize(c controlPrivate, d *sizing) (int, int) { func newControlSingleObject(id C.id) *controlSingleObject {
s := C.controlPreferredSize(c.id()) c := new(controlSingleObject)
c.controlbase = &controlbase{
fsetParent: c.xsetParent,
fpreferredSize: c.xpreferredSize,
fresize: c.xresize,
}
c.id = id
return c
}
func (c *controlSingleObject) xsetParent(p *controlParent) {
// redrawing the new window handled by C.parent()
C.parent(c.id, p.id)
}
func (c *controlSingleObject) xpreferredSize(d *sizing) (int, int) {
s := C.controlPreferredSize(c.id)
return int(s.width), int(s.height) return int(s.width), int(s.height)
} }
func basecommitResize(c controlPrivate, a *allocation, d *sizing) { func (c *controlSingleObject) xresize(x int, y int, width int, height int, d *sizing) {
dobasecommitResize(c.id(), a, d) C.moveControl(c.id, C.intptr_t(x), C.intptr_t(y), C.intptr_t(width), C.intptr_t(height))
}
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 basegetAuxResizeInfo(c controlPrivate, d *sizing) {
d.neighborAlign = C.alignmentInfoFrame(c.id())
} }
type scroller struct { type scroller struct {
id C.id *controlSingleObject
scroller *controlSingleObject
} }
func newScroller(child C.id, bordered bool) *scroller { func newScroller(child C.id, bordered bool) *scroller {
id := C.newScrollView(child, toBOOL(bordered)) sid := C.newScrollView(child, toBOOL(bordered))
s := &scroller{ s := &scroller{
id: id, controlSingleObject: newControlSingleObject(child),
scroller: newControlSingleObject(sid),
} }
s.fsetParent = s.scroller.fsetParent
s.fresize = s .scroller.fresize
return s 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)
}

View File

@ -11,24 +11,34 @@ import (
// #include "gtk_unix.h" // #include "gtk_unix.h"
import "C" import "C"
// all Controls that call base methods must be this
type controlPrivate interface {
widget() *C.GtkWidget
Control
}
type controlParent struct { type controlParent struct {
c *C.GtkContainer c *C.GtkContainer
} }
func basesetParent(c controlPrivate, p *controlParent) { type controlSingleWidget struct {
widget := c.widget() // avoid multiple interface lookups *controlbase
C.gtk_container_add(p.c, widget) widget *C.GtkWidget
// make sure the new widget is shown if not explicitly hidden
C.gtk_widget_show_all(widget)
} }
func basepreferredSize(c controlPrivate, d *sizing) (int, int) { 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
// TODO why did I have this again?
C.gtk_widget_show_all(c.widget)
}
func (c *controlSingleWidget) xpreferredSize(d *sizing) (int, int) {
// GTK+ 3 makes this easy: controls can tell us what their preferred size is! // 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". // ...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. // 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. // There is a warning about height-for-width controls, but in my tests this isn't an issue.
var r C.GtkRequisition 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) return int(r.width), int(r.height)
} }
func basecommitResize(c controlPrivate, a *allocation, d *sizing) { func (c *controlSingleWidget) xresize(x int, y int, width int, height int, d *sizing) {
dobasecommitResize(c.widget(), a, d)
}
func dobasecommitResize(w *C.GtkWidget, c *allocation, d *sizing) {
// as we resize on size-allocate, we have to also use size-allocate on our children // 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 fine anyway; in fact, this allows us to move without knowing what the container is!
// this is what GtkBox does anyway // this is what GtkBox does anyway
@ -53,68 +59,63 @@ func dobasecommitResize(w *C.GtkWidget, c *allocation, d *sizing) {
var r C.GtkAllocation var r C.GtkAllocation
r.x = C.int(c.x) r.x = C.int(x)
r.y = C.int(c.y) r.y = C.int(y)
r.width = C.int(c.width) r.width = C.int(width)
r.height = C.int(c.height) r.height = C.int(height)
C.gtk_widget_size_allocate(w, &r) C.gtk_widget_size_allocate(c.widget, &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
} }
type scroller struct { type scroller struct {
*controlSingleWidget
scroller *controlSingleWidget
scrollwidget *C.GtkWidget scrollwidget *C.GtkWidget
scrollcontainer *C.GtkContainer scrollcontainer *C.GtkContainer
scrollwindow *C.GtkScrolledWindow scrollwindow *C.GtkScrolledWindow
overlay *controlSingleWidget
overlaywidget *C.GtkWidget overlaywidget *C.GtkWidget
overlaycontainer *C.GtkContainer overlaycontainer *C.GtkContainer
overlay *C.GtkOverlay overlayoverlay *C.GtkOverlay
addShowWhich *C.GtkWidget
} }
func newScroller(widget *C.GtkWidget, native bool, bordered bool, overlay bool) *scroller { 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) // any actual changing operations need to be done to the GtkScrolledWindow
if overlay { // that is, everything /except/ preferredSize() are done to the GtkScrolledWindow
o = C.gtk_overlay_new() s.scroller = newControlSingleWidget(s.scrollwidget)
} s.fsetParent = s.scroller.fsetParent
s := &scroller{ s.fresize = s.scroller.fresize
scrollwidget: scrollwidget,
scrollcontainer: (*C.GtkContainer)(unsafe.Pointer(scrollwidget)), // 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
scrollwindow: (*C.GtkScrolledWindow)(unsafe.Pointer(scrollwidget)), if native {
overlaywidget: o, C.gtk_container_add(s.scrollcontainer, s.widget)
overlaycontainer: (*C.GtkContainer)(unsafe.Pointer(o)), } else {
overlay: (*C.GtkOverlay)(unsafe.Pointer(o)), 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+) // give the scrolled window a border (thanks to jlindgren in irc.gimp.net/#gtk+)
if bordered { if bordered {
C.gtk_scrolled_window_set_shadow_type(s.scrollwindow, C.GTK_SHADOW_IN) 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 { 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) C.gtk_container_add(s.overlaycontainer, s.scrollwidget)
s.addShowWhich = s.overlaywidget
} }
return s 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)
}

View File

@ -5,45 +5,59 @@ package ui
// #include "winapi_windows.h" // #include "winapi_windows.h"
import "C" import "C"
type controlPrivate interface {
hwnd() C.HWND
Control
}
type controlParent struct { type controlParent struct {
c *container hwnd C.HWND
} }
func basesetParent(c controlPrivate, p *controlParent) { // don't specify preferredSize in any of these; they're per-control
C.controlSetParent(c.hwnd(), p.c.hwnd)
p.c.nchildren++ 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)
func basecommitResize(c controlPrivate, a *allocation, d *sizing) { c.controlbase = &controlbase{
C.moveWindow(c.hwnd(), C.int(a.x), C.int(a.y), C.int(a.width), C.int(a.height)) fsetParent: c.xsetParent,
fresize: c.xresize,
fnTabStops: func() int {
// most controls count as one tab stop
return 1
},
}
c.hwnd = hwnd
return c
} }
func basegetAuxResizeInfo(c controlPrivate, d *sizing) { func (c *controlSingleHWND) xsetParent(p *controlParent) {
// do nothing C.controlSetParent(c.hwnd, p.hwnd)
}
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 // these are provided for convenience
type textableControl interface { type controlSingleHWNDWithText struct {
controlPrivate *controlSingleHWND
textlen() C.LONG textlen C.LONG
settextlen(C.LONG)
} }
func baseText(c textableControl) string { func newControlSingleHWNDWithText(h C.HWND) *controlSingleHWNDWithText {
return getWindowText(c.hwnd()) return &controlSingleHWNDWithText{
controlSingleHWND: newControlSingleHWND(h),
}
} }
func baseSetText(c textableControl, text string) { // TODO export these instead of requiring dummy declarations in each implementation
hwnd := c.hwnd() func (c *controlSingleHWNDWithText) text() string {
return getWindowText(c.hwnd)
}
func (c *controlSingleHWNDWithText) setText(text string) {
t := toUTF16(text) t := toUTF16(text)
C.setWindowText(hwnd, t) C.setWindowText(c.hwnd, t)
c.settextlen(C.controlTextLength(hwnd, t)) c.textlen = C.controlTextLength(c.hwnd, t)
} }

94
grid.go
View File

@ -27,6 +27,11 @@ type Grid interface {
// The effect of overlapping spanning Controls is also undefined. // The effect of overlapping spanning Controls is also undefined.
// Add panics if either xspan or yspan are zero or negative. // 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) 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. // Align represents the alignment of a Control in its cell of a Grid.
@ -54,7 +59,8 @@ type grid struct {
controls []gridCell controls []gridCell
indexof map[Control]int indexof map[Control]int
prev int prev int
parent *controlParent container *container
padded bool
xmax int xmax int
ymax int ymax int
@ -84,6 +90,7 @@ type gridCell struct {
func NewGrid() Grid { func NewGrid() Grid {
return &grid{ return &grid{
indexof: map[Control]int{}, 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, xspan: xspan,
yspan: yspan, yspan: yspan,
} }
if g.parent != nil { control.setParent(g.container.parent())
control.setParent(g.parent)
}
// if this is the first control, just add it in directly // if this is the first control, just add it in directly
if len(g.controls) != 0 { if len(g.controls) != 0 {
next := g.prev next := g.prev
@ -161,11 +166,16 @@ func (g *grid) Add(control Control, nextTo Control, side Side, xexpand bool, xal
g.reorigin() 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) { func (g *grid) setParent(p *controlParent) {
g.parent = p g.container.setParent(p)
for i := range g.controls {
g.controls[i].control.setParent(g.parent)
}
} }
// builds the topological cell grid; also makes colwidths and rowheights // 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) 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 { if len(g.controls) == 0 {
// nothing to do // 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 // -1) discount padding from width/height
width -= (g.xmax - 1) * d.xpadding width -= (g.xmax - 1) * xpadding
height -= (g.ymax - 1) * d.ypadding height -= (g.ymax - 1) * ypadding
// 0) build necessary data structures // 0) build necessary data structures
gg, colwidths, rowheights := g.mkgrid() 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 { if i != prev {
g.controls[i].finalx = curx g.controls[i].finalx = curx
} else { } else {
g.controls[i].finalwidth += d.xpadding g.controls[i].finalwidth += xpadding
} }
g.controls[i].finalwidth += colwidths[x] g.controls[i].finalwidth += colwidths[x]
} }
curx += colwidths[x] + d.xpadding curx += colwidths[x] + xpadding
prev = i prev = i
} }
} }
@ -334,11 +356,11 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
if i != prev { if i != prev {
g.controls[i].finaly = cury g.controls[i].finaly = cury
} else { } else {
g.controls[i].finalheight += d.ypadding g.controls[i].finalheight += ypadding
} }
g.controls[i].finalheight += rowheights[y] g.controls[i].finalheight += rowheights[y]
} }
cury += rowheights[y] + d.ypadding cury += rowheights[y] + ypadding
prev = i 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 // 8) and FINALLY we draw
var current *allocation
for _, ycol := range gg { for _, ycol := range gg {
current = nil
for _, i := range ycol { for _, i := range ycol {
if i != -1 { // treat empty cells like spaces 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].finalx+x, g.controls[i].finaly+y,
g.controls[i].finalwidth, g.controls[i].finalheight, d) 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) { 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 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 // 0) build necessary data structures
gg, colwidths, rowheights := g.mkgrid() 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 // and that's it; just account for padding
return colwidth + (g.xmax-1)*d.xpadding, return colwidth + (g.xmax-1) * xpadding,
rowheight + (g.ymax-1)*d.ypadding rowheight + (g.ymax-1) * ypadding
} }
func (g *grid) commitResize(a *allocation, d *sizing) { func (g *grid) nTabStops() int {
// do nothing; needed to satisfy Control n := 0
} for _, c := range g.controls {
n += c.control.nTabStops()
func (g *grid) getAuxResizeInfo(d *sizing) { }
// do nothing; needed to satisfy Control return n
} }

View File

@ -10,49 +10,51 @@ import (
import "C" import "C"
type group struct { 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 { func newGroup(text string, control Control) Group {
g := new(group) g := new(group)
g.container = newContainer(control) g.container = newContainer()
g._id = C.newGroup(g.container.id) g.controlSingleObject = newControlSingleObject(C.newGroup(g.container.id))
g.child = control
g.child.setParent(g.container.parent())
g.SetText(text) g.SetText(text)
g.chainresize = g.fresize
g.fresize = g.xresize
return g return g
} }
func (g *group) Text() string { 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) { func (g *group) SetText(text string) {
ctext := C.CString(text) ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext)) defer C.free(unsafe.Pointer(ctext))
C.groupSetText(g._id, ctext) C.groupSetText(g.id, ctext)
} }
func (g *group) id() C.id { func (g *group) Margined() bool {
return g._id return g.margined
} }
func (g *group) setParent(p *controlParent) { func (g *group) SetMargined(margined bool) {
basesetParent(g, p) g.margined = margined
} }
func (g *group) allocate(x int, y int, width int, height int, d *sizing) []*allocation { func (g *group) xresize(x int, y int, width int, height int, d *sizing) {
return baseallocate(g, x, y, width, height, d) // first, chain up to change the GtkFrame and its child container
} g.chainresize(x, y, width, height, d)
func (g *group) preferredSize(d *sizing) (width, height int) { // now that the container has the correct size, we can resize the child
return basepreferredSize(g, d) a := g.container.allocation(g.margined)
} g.child.resize(int(a.x), int(a.y), int(a.width), int(a.height), d)
func (g *group) commitResize(a *allocation, d *sizing) {
basecommitResize(g, a, d)
}
func (g *group) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(g, d)
} }

View File

@ -12,11 +12,16 @@ import (
import "C" import "C"
type group struct { type group struct {
_widget *C.GtkWidget *controlSingleWidget
gcontainer *C.GtkContainer gcontainer *C.GtkContainer
frame *C.GtkFrame 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 { func newGroup(text string, control Control) Group {
@ -24,9 +29,10 @@ func newGroup(text string, control Control) Group {
defer freegstr(ctext) defer freegstr(ctext)
widget := C.gtk_frame_new(ctext) widget := C.gtk_frame_new(ctext)
g := &group{ g := &group{
_widget: widget, controlSingleWidget: newControlSingleWidget(widget),
gcontainer: (*C.GtkContainer)(unsafe.Pointer(widget)), gcontainer: (*C.GtkContainer)(unsafe.Pointer(widget)),
frame: (*C.GtkFrame)(unsafe.Pointer(widget)), frame: (*C.GtkFrame)(unsafe.Pointer(widget)),
child: control,
} }
// with GTK+, groupboxes by default have frames and slightly x-offset regular text // 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.gtk_label_set_attributes(label, boldlist)
C.pango_attr_list_unref(boldlist) // thanks baedert in irc.gimp.net/#gtk+ 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.container.setParent(&controlParent{g.gcontainer})
g.chainresize = g.fresize
g.fresize = g.xresize
return g return g
} }
@ -62,26 +72,19 @@ func (g *group) SetText(text string) {
C.gtk_frame_set_label(g.frame, ctext) C.gtk_frame_set_label(g.frame, ctext)
} }
func (g *group) widget() *C.GtkWidget { func (g *group) Margined() bool {
return g._widget return g.margined
} }
func (g *group) setParent(p *controlParent) { func (g *group) SetMargined(margined bool) {
basesetParent(g, p) g.margined = margined
} }
func (g *group) allocate(x int, y int, width int, height int, d *sizing) []*allocation { func (g *group) xresize(x int, y int, width int, height int, d *sizing) {
return baseallocate(g, x, y, width, height, d) // first, chain up to change the GtkFrame and its child container
} g.chainresize(x, y, width, height, d)
func (g *group) preferredSize(d *sizing) (width, height int) { // now that the container has the correct size, we can resize the child
return basepreferredSize(g, d) a := g.container.allocation(g.margined)
} g.child.resize(int(a.x), int(a.y), int(a.width), int(a.height), d)
func (g *group) commitResize(a *allocation, d *sizing) {
basecommitResize(g, a, d)
}
func (g *group) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(g, d)
} }

View File

@ -6,10 +6,10 @@ package ui
import "C" import "C"
type group struct { type group struct {
_hwnd C.HWND *controlSingleHWNDWithText
_textlen C.LONG child Control
margined bool
*container chainresize func(x int, y int, width int, height int, d *sizing)
} }
func newGroup(text string, control Control) Group { func newGroup(text string, control Control) Group {
@ -17,66 +17,82 @@ func newGroup(text string, control Control) Group {
C.BS_GROUPBOX, C.BS_GROUPBOX,
C.WS_EX_CONTROLPARENT) C.WS_EX_CONTROLPARENT)
g := &group{ g := &group{
_hwnd: hwnd, controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
container: newContainer(control), 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) g.SetText(text)
C.controlSetControlFont(g._hwnd) C.controlSetControlFont(g.hwnd)
g.container.setParent(g._hwnd) control.setParent(&controlParent{g.hwnd})
g.container.isGroup = true
return g return g
} }
func (g *group) Text() string { func (g *group) Text() string {
return baseText(g) return g.text()
} }
func (g *group) SetText(text string) { func (g *group) SetText(text string) {
baseSetText(g, text) g.setText(text)
} }
func (g *group) hwnd() C.HWND { func (g *group) Margined() bool {
return g._hwnd return g.margined
} }
func (g *group) textlen() C.LONG { func (g *group) SetMargined(margined bool) {
return g._textlen g.margined = margined
} }
func (g *group) settextlen(len C.LONG) { const (
g._textlen = len groupXMargin = 6
} groupYMarginTop = 11 // note this value /includes the groupbox label/
groupYMarginBottom = 7
)
func (g *group) setParent(p *controlParent) { func (g *group) xpreferredSize(d *sizing) (width, height int) {
basesetParent(g, p) var r C.RECT
}
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) preferredSize(d *sizing) (width, height int) {
width, height = g.child.preferredSize(d) width, height = g.child.preferredSize(d)
if width < int(g._textlen) { // if the text is longer, try not to truncate if width < int(g.textlen) { // if the text is longer, try not to truncate
width = int(g._textlen) width = int(g.textlen)
} }
// the two margin constants come from container_windows.go r.left = 0
return width, height + fromdlgunitsY(groupYMarginTop, d) + fromdlgunitsY(groupYMarginBottom, d) 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 var r C.RECT
// pretend that the client area of the group box only includes the actual empty space // pretend that the client area of the group box only includes the actual empty space
// container will handle the necessary adjustments properly // container will handle the necessary adjustments properly
r.left = 0 r.left = 0
r.top = 0 r.top = 0
r.right = C.LONG(c.width) r.right = C.LONG(width)
r.bottom = C.LONG(c.height) r.bottom = C.LONG(height)
g.container.move(&r) if g.margined {
basecommitResize(g, c, d) // see above
} marginRectDLU(&r, groupYMarginTop, groupYMarginBottom, groupXMargin, groupXMargin, d)
} else {
func (g *group) getAuxResizeInfo(d *sizing) { marginRectDLU(&r, 1, 1, 1, 1, d)
basegetAuxResizeInfo(g, d) }
g.child.resize(int(r.left), int(r.top), int(r.right - r.left), int(r.bottom - r.top), d)
} }

View File

@ -10,57 +10,28 @@ import (
import "C" import "C"
type label struct { type label struct {
_id C.id *controlSingleObject
standalone bool
} }
func finishNewLabel(text string, standalone bool) *label { func newLabel(text string) Label {
l := &label{ l := &label{
_id: C.newLabel(), controlSingleObject: newControlSingleObject(C.newLabel()),
standalone: standalone,
} }
l.SetText(text) l.SetText(text)
return l 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 { 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) { func (l *label) SetText(text string) {
ctext := C.CString(text) ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext)) defer C.free(unsafe.Pointer(ctext))
C.textfieldSetText(l._id, 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)
} }
/*TODO
func (l *label) commitResize(c *allocation, d *sizing) { func (l *label) commitResize(c *allocation, d *sizing) {
if !l.standalone && c.neighbor != nil { if !l.standalone && c.neighbor != nil {
c.neighbor.getAuxResizeInfo(d) c.neighbor.getAuxResizeInfo(d)
@ -89,7 +60,4 @@ func (l *label) commitResize(c *allocation, d *sizing) {
} }
basecommitResize(l, c, d) basecommitResize(l, c, d)
} }
*/
func (l *label) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(l, d)
}

View File

@ -9,40 +9,34 @@ import (
) )
// #include "gtk_unix.h" // #include "gtk_unix.h"
// extern void buttonClicked(GtkButton *, gpointer);
// extern void checkboxToggled(GtkToggleButton *, gpointer);
import "C" import "C"
type label struct { type label struct {
_widget *C.GtkWidget *controlSingleWidget
misc *C.GtkMisc misc *C.GtkMisc
label *C.GtkLabel label *C.GtkLabel
standalone bool
} }
func finishNewLabel(text string, standalone bool) *label { func newLabel(text string) Label {
ctext := togstr(text) ctext := togstr(text)
defer freegstr(ctext) defer freegstr(ctext)
widget := C.gtk_label_new(ctext) widget := C.gtk_label_new(ctext)
l := &label{ l := &label{
_widget: widget, controlSingleWidget: newControlSingleWidget(widget),
misc: (*C.GtkMisc)(unsafe.Pointer(widget)), misc: (*C.GtkMisc)(unsafe.Pointer(widget)),
label: (*C.GtkLabel)(unsafe.Pointer(widget)), label: (*C.GtkLabel)(unsafe.Pointer(widget)),
standalone: standalone,
} }
return l return l
} }
func newLabel(text string) Label { /*TODO
return finishNewLabel(text, false)
}
func newStandaloneLabel(text string) Label { func newStandaloneLabel(text string) Label {
l := finishNewLabel(text, true) l := finishNewLabel(text, true)
// standalone labels are always at the top left // standalone labels are always at the top left
C.gtk_misc_set_alignment(l.misc, 0, 0) C.gtk_misc_set_alignment(l.misc, 0, 0)
return l return l
} }
*/
func (l *label) Text() string { func (l *label) Text() string {
return fromgstr(C.gtk_label_get_text(l.label)) 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) C.gtk_label_set_text(l.label, ctext)
} }
func (l *label) isStandalone() bool { /*TODO
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)
}
func (l *label) commitResize(c *allocation, d *sizing) { func (l *label) commitResize(c *allocation, d *sizing) {
if !l.standalone && c.neighbor != nil { if !l.standalone && c.neighbor != nil {
c.neighbor.getAuxResizeInfo(d) c.neighbor.getAuxResizeInfo(d)
@ -86,7 +61,4 @@ func (l *label) commitResize(c *allocation, d *sizing) {
} }
basecommitResize(l, c, d) basecommitResize(l, c, d)
} }
*/
func (l *label) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(l, d)
}

View File

@ -6,67 +6,37 @@ package ui
import "C" import "C"
type label struct { type label struct {
_hwnd C.HWND *controlSingleHWNDWithText
_textlen C.LONG
standalone bool standalone bool
} }
var labelclass = toUTF16("STATIC") var labelclass = toUTF16("STATIC")
func finishNewLabel(text string, standalone bool) *label { func newLabel(text string) Label {
hwnd := C.newControl(labelclass, hwnd := C.newControl(labelclass,
// SS_NOPREFIX avoids accelerator translation; SS_LEFTNOWORDWRAP clips text past the end // 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) // controls are vertically aligned to the top by default (thanks Xeek in irc.freenode.net/#winapi)
C.SS_NOPREFIX|C.SS_LEFTNOWORDWRAP, C.SS_NOPREFIX|C.SS_LEFTNOWORDWRAP,
C.WS_EX_TRANSPARENT) C.WS_EX_TRANSPARENT)
l := &label{ l := &label{
_hwnd: hwnd, controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
standalone: standalone, }
l.fpreferredSize = l.xpreferredSize
l.fnTabStops = func() int {
// labels are not tab stops
return 0
} }
l.SetText(text) l.SetText(text)
C.controlSetControlFont(l._hwnd) C.controlSetControlFont(l.hwnd)
return l 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 { func (l *label) Text() string {
return baseText(l) return l.text()
} }
func (l *label) SetText(text string) { func (l *label) SetText(text string) {
baseSetText(l, text) l.setText(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)
} }
const ( const (
@ -75,10 +45,11 @@ const (
labelYOffset = 3 labelYOffset = 3
) )
func (l *label) preferredSize(d *sizing) (width, height int) { func (l *label) xpreferredSize(d *sizing) (width, height int) {
return int(l._textlen), fromdlgunitsY(labelHeight, d) return int(l.textlen), fromdlgunitsY(labelHeight, d)
} }
/*TODO
func (l *label) commitResize(c *allocation, d *sizing) { func (l *label) commitResize(c *allocation, d *sizing) {
if !l.standalone { if !l.standalone {
yoff := fromdlgunitsY(labelYOffset, d) yoff := fromdlgunitsY(labelYOffset, d)
@ -93,7 +64,4 @@ func (l *label) commitResize(c *allocation, d *sizing) {
} }
basecommitResize(l, c, d) basecommitResize(l, c, d)
} }
*/
func (l *label) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(l, d)
}

View File

@ -83,6 +83,7 @@ extern void groupSetText(id, char *);
/* container_darwin.m */ /* container_darwin.m */
extern id newContainerView(void *); extern id newContainerView(void *);
extern void moveControl(id, intptr_t, intptr_t, intptr_t, intptr_t); extern void moveControl(id, intptr_t, intptr_t, intptr_t, intptr_t);
extern struct xrect containerBounds(id);
/* tab_darwin.m */ /* tab_darwin.m */
extern id newTab(void); extern id newTab(void);

View File

@ -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). // 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. // 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. // 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 { type SimpleGrid interface {
Control 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). // 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. // It panics if the given coordinate is invalid.
SetStretchy(row int, column int) 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 { type simpleGrid struct {
@ -37,6 +40,8 @@ type simpleGrid struct {
stretchyfill bool stretchyfill bool
widths, heights [][]int // caches to avoid reallocating each time widths, heights [][]int // caches to avoid reallocating each time
rowheights, colwidths []int rowheights, colwidths []int
container *container
padded bool
} }
// NewSimpleGrid creates a new SimpleGrid with the given Controls. // 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) ch[row] = make([]int, nPerRow)
for x := 0; x < nPerRow; x++ { for x := 0; x < nPerRow; x++ {
cc[row][x] = controls[i] cc[row][x] = controls[i]
if l, ok := controls[i].(Label); ok && !l.isStandalone() {
cf[row][x] = true
}
i++ i++
} }
} }
return &simpleGrid{ g := &simpleGrid{
controls: cc, controls: cc,
filling: cf, filling: cf,
stretchyrow: -1, stretchyrow: -1,
@ -79,7 +81,15 @@ func NewSimpleGrid(nPerRow int, controls ...Control) SimpleGrid {
heights: ch, heights: ch,
rowheights: make([]int, nRows), rowheights: make([]int, nRows),
colwidths: make([]int, nPerRow), 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) { 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 g.filling[g.stretchyrow][g.stretchycol] = true
} }
func (g *simpleGrid) setParent(parent *controlParent) { func (g *simpleGrid) Padded() bool {
for _, col := range g.controls { return g.padded
for _, c := range col {
c.setParent(parent)
}
}
} }
func (g *simpleGrid) allocate(x int, y int, width int, height int, d *sizing) (allocations []*allocation) { func (g *simpleGrid) SetPadded(padded bool) {
g.padded = padded
}
func (g *simpleGrid) setParent(parent *controlParent) {
g.container.setParent(parent)
}
func (g *simpleGrid) resize(x int, y int, width int, height int, d *sizing) {
max := func(a int, b int) int { max := func(a int, b int) int {
if a > b { if a > b {
return a return a
@ -118,14 +132,21 @@ func (g *simpleGrid) allocate(x int, y int, width int, height int, d *sizing) (a
return b return b
} }
var current *allocation // for neighboring g.container.resize(x, y, width, height, d)
if len(g.controls) == 0 { if len(g.controls) == 0 {
return nil return
} }
// 0) inset the available rect by the needed padding x, y, width, height = g.container.bounds(d)
width -= (len(g.colwidths) - 1) * d.xpadding // -1) get this SimpleGrid's padding
height -= (len(g.rowheights) - 1) * d.ypadding xpadding := d.xpadding
ypadding := d.ypadding
if !g.padded {
xpadding = 0
ypadding = 0
}
// 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 // 1) clear data structures
for i := range g.rowheights { for i := range g.rowheights {
g.rowheights[i] = 0 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 // 4) draw
startx := x startx := x
for row, xcol := range g.controls { for row, xcol := range g.controls {
current = nil // reset on new columns
for col, c := range xcol { for col, c := range xcol {
w := g.widths[row][col] w := g.widths[row][col]
h := g.heights[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] w = g.colwidths[col]
h = g.rowheights[row] h = g.rowheights[row]
} }
as := c.allocate(x, y, w, h, d) c.resize(x, y, w, h, d)
if current != nil { // connect first left to first right x += g.colwidths[col] + xpadding
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
} }
x = startx x = startx
y += g.rowheights[row] + d.ypadding y += g.rowheights[row] + ypadding
} }
return
} }
// filling and stretchy are ignored for preferred size calculation // 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 return b
} }
width -= (len(g.colwidths) - 1) * d.xpadding xpadding := d.xpadding
height -= (len(g.rowheights) - 1) * d.ypadding 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 // 1) clear data structures
for i := range g.rowheights { for i := range g.rowheights {
g.rowheights[i] = 0 g.rowheights[i] = 0
@ -225,10 +241,12 @@ func (g *simpleGrid) preferredSize(d *sizing) (width int, height int) {
return width, height return width, height
} }
func (g *simpleGrid) commitResize(c *allocation, d *sizing) { func (g *simpleGrid) nTabStops() int {
// this is to satisfy Control; nothing to do here n := 0
} for _, cc := range g.controls {
for _, c := range cc {
func (g *simpleGrid) getAuxResizeInfo(d *sizing) { n += c.nTabStops()
// this is to satisfy Control; nothing to do here }
} }
return n
}

View File

@ -24,6 +24,11 @@ type Stack interface {
// SetStretchy marks a control in a Stack as stretchy. // SetStretchy marks a control in a Stack as stretchy.
// It panics if index is out of range. // It panics if index is out of range.
SetStretchy(index int) 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 { type stack struct {
@ -31,16 +36,24 @@ type stack struct {
controls []Control controls []Control
stretchy []bool stretchy []bool
width, height []int // caches to avoid reallocating these each time width, height []int // caches to avoid reallocating these each time
container *container
padded bool
} }
func newStack(o orientation, controls ...Control) Stack { func newStack(o orientation, controls ...Control) Stack {
return &stack{ s := &stack{
orientation: o, orientation: o,
controls: controls, controls: controls,
stretchy: make([]bool, len(controls)), stretchy: make([]bool, len(controls)),
width: make([]int, len(controls)), width: make([]int, len(controls)),
height: 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. // 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 s.stretchy[index] = true
} }
func (s *stack) setParent(parent *controlParent) { func (s *stack) Padded() bool {
for _, c := range s.controls { return s.padded
c.setParent(parent)
}
} }
func (s *stack) allocate(x int, y int, width int, height int, d *sizing) (allocations []*allocation) { func (s *stack) SetPadded(padded bool) {
var stretchywid, stretchyht int s.padded = padded
var current *allocation // for neighboring }
func (s *stack) setParent(parent *controlParent) {
s.container.setParent(parent)
}
func (s *stack) resize(x int, y int, width int, height int, d *sizing) {
var stretchywid, stretchyht int
s.container.resize(x, y, width, height, d)
if len(s.controls) == 0 { // do nothing if there's nothing to do 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 // 0) inset the available rect by the needed padding
if s.orientation == horizontal { if s.orientation == horizontal {
width -= (len(s.controls) - 1) * d.xpadding width -= (len(s.controls) - 1) * xpadding
} else { } 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 // 1) get height and width of non-stretchy controls; figure out how much space is alloted to stretchy controls
stretchywid = width 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 // 3) now actually place controls
for i, c := range s.controls { for i, c := range s.controls {
as := c.allocate(x, y, s.width[i], s.height[i], d) c.resize(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...)
if s.orientation == horizontal { if s.orientation == horizontal {
x += s.width[i] + d.xpadding x += s.width[i] + xpadding
} else { } 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). // 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 if len(s.controls) == 0 { // no controls, so return emptiness
return 0, 0 return 0, 0
} }
xpadding := d.xpadding
ypadding := d.ypadding
if !s.padded {
xpadding = 0
ypadding = 0
}
if s.orientation == horizontal { if s.orientation == horizontal {
width = (len(s.controls) - 1) * d.xpadding width = (len(s.controls) - 1) * xpadding
} else { } else {
height = (len(s.controls) - 1) * d.ypadding height = (len(s.controls) - 1) * ypadding
} }
for i, c := range s.controls { for i, c := range s.controls {
w, h := c.preferredSize(d) w, h := c.preferredSize(d)
@ -184,13 +206,15 @@ func (s *stack) preferredSize(d *sizing) (width int, height int) {
return return
} }
func (s *stack) commitResize(c *allocation, d *sizing) { func (s *stack) nTabStops() int {
// this is to satisfy Control; nothing to do here n := 0
for _, c := range s.controls {
n += c.nTabStops()
}
return n
} }
func (s *stack) getAuxResizeInfo(d *sizing) { // TODO the below needs to be changed
// this is to satisfy Control; nothing to do here
}
// Space returns a null Control intended for padding layouts with blank space. // Space returns a null Control intended for padding layouts with blank space.
// It appears to its owner as a Control of 0x0 size. // It appears to its owner as a Control of 0x0 size.

View File

@ -10,46 +10,44 @@ import (
import "C" import "C"
type tab struct { type tab struct {
_id C.id *controlSingleObject
tabs []*container tabs []*container
children []Control
chainresize func(x int, y int, width int, height int, d *sizing)
} }
func newTab() Tab { func newTab() Tab {
return &tab{ t := &tab{
_id: C.newTab(), 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) { func (t *tab) Append(name string, control Control) {
c := newContainer(control) c := newContainer()
t.tabs = append(t.tabs, c) t.tabs = append(t.tabs, c)
control.setParent(c.parent())
t.children = append(t.children, control)
cname := C.CString(name) cname := C.CString(name)
defer C.free(unsafe.Pointer(cname)) 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 { func (t *tab) xpreferredSize(d *sizing) (width, height int) {
return t._id s := C.tabPreferredSize(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)
return int(s.width), int(s.height) 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) xresize(x int, y int, width int, height int, d *sizing) {
func (t *tab) commitResize(a *allocation, d *sizing) { // first, chain up to change the GtkFrame and its child container
basecommitResize(t, a, d) t.chainresize(x, y, width, height, d)
}
func (t *tab) getAuxResizeInfo(d *sizing) { // now that the containers have the correct size, we can resize the children
basegetAuxResizeInfo(t, d) 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)
}
} }

View File

@ -12,30 +12,37 @@ import (
import "C" import "C"
type tab struct { type tab struct {
_widget *C.GtkWidget *controlSingleWidget
container *C.GtkContainer container *C.GtkContainer
notebook *C.GtkNotebook notebook *C.GtkNotebook
tabs []*container tabs []*container
children []Control
chainresize func(x int, y int, width int, height int, d *sizing)
} }
func newTab() Tab { func newTab() Tab {
widget := C.gtk_notebook_new() widget := C.gtk_notebook_new()
t := &tab{ t := &tab{
_widget: widget, controlSingleWidget: newControlSingleWidget(widget),
container: (*C.GtkContainer)(unsafe.Pointer(widget)), container: (*C.GtkContainer)(unsafe.Pointer(widget)),
notebook: (*C.GtkNotebook)(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 // 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) C.gtk_notebook_set_scrollable(t.notebook, C.TRUE)
return t return t
} }
func (t *tab) Append(name string, control Control) { func (t *tab) Append(name string, control Control) {
c := newContainer(control) c := newContainer()
t.tabs = append(t.tabs, c) 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() // 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}) c.setParent(&controlParent{t.container})
control.setParent(c.parent())
t.children = append(t.children, control)
cname := togstr(name) cname := togstr(name)
defer freegstr(cname) defer freegstr(cname)
C.gtk_notebook_set_tab_label_text(t.notebook, C.gtk_notebook_set_tab_label_text(t.notebook,
@ -44,27 +51,13 @@ func (t *tab) Append(name string, control Control) {
cname) cname)
} }
func (t *tab) widget() *C.GtkWidget { func (t *tab) xresize(x int, y int, width int, height int, d *sizing) {
return t._widget // first, chain up to change the GtkFrame and its child container
} t.chainresize(x, y, width, height, d)
func (t *tab) setParent(p *controlParent) { // now that the containers have the correct size, we can resize the children
basesetParent(t, p) 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)
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) {
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)
} }

View File

@ -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. 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 { type tab struct {
_hwnd C.HWND *controlSingleHWND
tabs []*container tabs []*container
children []Control
chainresize func(x int, y int, width int, height int, d *sizing)
} }
func newTab() Tab { func newTab() Tab {
@ -25,22 +27,29 @@ func newTab() Tab {
C.TCS_TOOLTIPS|C.WS_TABSTOP, C.TCS_TOOLTIPS|C.WS_TABSTOP,
0) // don't set WS_EX_CONTROLPARENT here; see uitask_windows.c 0) // don't set WS_EX_CONTROLPARENT here; see uitask_windows.c
t := &tab{ t := &tab{
_hwnd: hwnd, controlSingleHWND: newControlSingleHWND(hwnd),
} }
C.controlSetControlFont(t._hwnd) t.fpreferredSize = t.xpreferredSize
C.setTabSubclass(t._hwnd, unsafe.Pointer(t)) 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 return t
} }
// TODO margined
func (t *tab) Append(name string, control Control) { func (t *tab) Append(name string, control Control) {
c := newContainer(control) c := newContainer()
c.setParent(t._hwnd) control.setParent(&controlParent{c.hwnd})
c.setParent(&controlParent{t.hwnd})
t.tabs = append(t.tabs, c) 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 // 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 { if len(t.tabs) != 1 {
t.tabs[len(t.tabs)-1].hide() t.tabs[len(t.tabs)-1].hide()
} }
C.tabAppend(t._hwnd, toUTF16(name)) C.tabAppend(t.hwnd, toUTF16(name))
} }
//export tabChanging //export tabChanging
@ -61,27 +70,15 @@ func tabTabHasChildren(data unsafe.Pointer, which C.LRESULT) C.BOOL {
if len(t.tabs) == 0 { // currently no tabs if len(t.tabs) == 0 { // currently no tabs
return C.FALSE return C.FALSE
} }
if t.tabs[int(which)].nchildren > 0 { if t.children[int(which)].nTabStops() > 0 {
return C.TRUE return C.TRUE
} }
return C.FALSE return C.FALSE
} }
func (t *tab) hwnd() C.HWND { func (t *tab) xpreferredSize(d *sizing) (width, height int) {
return t._hwnd for _, c := range t.children {
} w, h := c.preferredSize(d)
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)
if width < w { if width < w {
width = w width = w
} }
@ -89,30 +86,30 @@ func (t *tab) preferredSize(d *sizing) (width, height int) {
height = h 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 // 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 var r C.RECT
// figure out what the rect for each child is... // 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.left = C.LONG(0)
r.top = C.LONG(0) r.top = C.LONG(0)
r.right = C.LONG(c.width) r.right = C.LONG(width)
r.bottom = C.LONG(c.height) r.bottom = C.LONG(height)
C.tabGetContentRect(t._hwnd, &r) C.tabGetContentRect(t.hwnd, &r)
// and resize tabs // and resize tabs
// don't resize just the current tab; resize all 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 // 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)
} }

View File

@ -14,8 +14,7 @@ import "C"
type table struct { type table struct {
*tablebase *tablebase
_id C.id *scroller
scroller *scroller
images []C.id images []C.id
selected *event selected *event
@ -24,13 +23,13 @@ type table struct {
func finishNewTable(b *tablebase, ty reflect.Type) Table { func finishNewTable(b *tablebase, ty reflect.Type) Table {
id := C.newTable() id := C.newTable()
t := &table{ t := &table{
_id: id,
scroller: newScroller(id, true), // border on Table scroller: newScroller(id, true), // border on Table
tablebase: b, tablebase: b,
selected: newEvent(), selected: newEvent(),
} }
t.fpreferredSize = t.xpreferredSize
// also sets the delegate // 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++ { for i := 0; i < ty.NumField(); i++ {
cname := C.CString(ty.Field(i).Name) cname := C.CString(ty.Field(i).Name)
coltype := C.colTypeText coltype := C.colTypeText
@ -42,7 +41,7 @@ func finishNewTable(b *tablebase, ty reflect.Type) Table {
coltype = C.colTypeCheckbox coltype = C.colTypeCheckbox
editable = true 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 C.free(unsafe.Pointer(cname)) // free now (not deferred) to conserve memory
} }
return t return t
@ -56,7 +55,7 @@ func (t *table) Unlock() {
Do(func() { Do(func() {
t.RLock() t.RLock()
defer t.RUnlock() 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 { func (t *table) Selected() int {
t.RLock() t.RLock()
defer t.RUnlock() defer t.RUnlock()
return int(C.tableSelected(t._id)) return int(C.tableSelected(t.id))
} }
func (t *table) Select(index int) { func (t *table) Select(index int) {
t.RLock() t.RLock()
defer t.RUnlock() 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()) { func (t *table) OnSelected(f func()) {
@ -132,27 +131,7 @@ func tableSelectionChanged(data unsafe.Pointer) {
t.selected.fire() t.selected.fire()
} }
func (t *table) id() C.id { func (t *table) xpreferredSize(d *sizing) (width, height int) {
return t._id s := C.tablePreferredSize(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)
return int(s.width), int(s.height) 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)
}

View File

@ -18,9 +18,8 @@ import "C"
type table struct { type table struct {
*tablebase *tablebase
_widget *C.GtkWidget *scroller
treeview *C.GtkTreeView treeview *C.GtkTreeView
scroller *scroller
model *C.goTableModel model *C.goTableModel
modelgtk *C.GtkTreeModel modelgtk *C.GtkTreeModel
@ -48,7 +47,6 @@ func finishNewTable(b *tablebase, ty reflect.Type) Table {
t := &table{ t := &table{
scroller: newScroller(widget, true, true, false), // natively scrollable; has a border; no overlay scroller: newScroller(widget, true, true, false), // natively scrollable; has a border; no overlay
tablebase: b, tablebase: b,
_widget: widget,
treeview: (*C.GtkTreeView)(unsafe.Pointer(widget)), treeview: (*C.GtkTreeView)(unsafe.Pointer(widget)),
crtocol: make(map[*C.GtkCellRendererToggle]int), crtocol: make(map[*C.GtkCellRendererToggle]int),
selected: newEvent(), selected: newEvent(),
@ -222,28 +220,3 @@ func tableSelectionChanged(sel *C.GtkTreeSelection, data C.gpointer) {
t := (*table)(unsafe.Pointer(data)) t := (*table)(unsafe.Pointer(data))
t.selected.fire() 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
}

View File

@ -13,7 +13,7 @@ import "C"
type table struct { type table struct {
*tablebase *tablebase
_hwnd C.HWND *controlSingleHWND
noautosize bool noautosize bool
colcount C.int colcount C.int
hotrow C.int hotrow C.int
@ -21,13 +21,15 @@ type table struct {
pushedrow C.int pushedrow C.int
pushedcol C.int pushedcol C.int
selected *event selected *event
chainresize func(x int, y int, width int, height int, d *sizing)
} }
func finishNewTable(b *tablebase, ty reflect.Type) Table { 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{ t := &table{
_hwnd: C.newControl(C.xWC_LISTVIEW, controlSingleHWND: newControlSingleHWND(hwnd),
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)
tablebase: b, tablebase: b,
hotrow: -1, hotrow: -1,
hotcol: -1, hotcol: -1,
@ -35,14 +37,17 @@ func finishNewTable(b *tablebase, ty reflect.Type) Table {
pushedcol: -1, pushedcol: -1,
selected: newEvent(), 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_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 // 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 // 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++ { 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()) t.colcount = C.int(ty.NumField())
return t return t
@ -56,25 +61,25 @@ func (t *table) Unlock() {
Do(func() { Do(func() {
t.RLock() t.RLock()
defer t.RUnlock() 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) { func (t *table) LoadImageList(il ImageList) {
il.apply(t._hwnd, C.msgLoadImageList) il.apply(t.hwnd, C.msgLoadImageList)
} }
func (t *table) Selected() int { func (t *table) Selected() int {
t.RLock() t.RLock()
defer t.RUnlock() defer t.RUnlock()
return int(C.tableSelectedItem(t._hwnd)) return int(C.tableSelectedItem(t.hwnd))
} }
func (t *table) Select(index int) { func (t *table) Select(index int) {
t.RLock() t.RLock()
defer t.RUnlock() 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()) { func (t *table) OnSelected(f func()) {
@ -144,7 +149,7 @@ func (t *table) autoresize() {
t.RLock() t.RLock()
defer t.RUnlock() defer t.RUnlock()
if !t.noautosize { 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.hotrow = row
t.hotcol = col t.hotcol = col
if redraw { 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 := (*table)(data)
t.pushedrow = row t.pushedrow = row
t.pushedcol = col 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 //export tableToggled
@ -211,18 +216,6 @@ func tableSelectionChanged(data unsafe.Pointer) {
t.selected.fire() 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 ( 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 // 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... // 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 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) return fromdlgunitsX(tableWidth, d), fromdlgunitsY(tableHeight, d)
} }
func (t *table) commitResize(a *allocation, d *sizing) { func (t *table) xresize(x int, y int, width int, height int, d *sizing) {
basecommitResize(t, a, d) t.chainresize(x, y, width, height, d)
t.RLock() t.RLock()
defer t.RUnlock() defer t.RUnlock()
t.autoresize() t.autoresize()
} }
func (t *table) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(t, d)
}

View File

@ -10,17 +10,20 @@ import (
import "C" import "C"
type textfield struct { type textfield struct {
_id C.id *controlSingleObject
changed *event changed *event
invalid C.id invalid C.id
chainpreferredSize func(d *sizing) (int, int)
} }
func finishNewTextField(id C.id) *textfield { func finishNewTextField(id C.id) *textfield {
t := &textfield{ t := &textfield{
_id: id, controlSingleObject: newControlSingleObject(id),
changed: newEvent(), 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 return t
} }
@ -33,13 +36,13 @@ func newPasswordField() *textfield {
} }
func (t *textfield) Text() string { 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) { func (t *textfield) SetText(text string) {
ctext := C.CString(text) ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext)) defer C.free(unsafe.Pointer(ctext))
C.textfieldSetText(t._id, ctext) C.textfieldSetText(t.id, ctext)
} }
func (t *textfield) OnChanged(f func()) { func (t *textfield) OnChanged(f func()) {
@ -56,7 +59,7 @@ func (t *textfield) Invalid(reason string) {
} }
creason := C.CString(reason) creason := C.CString(reason)
defer C.free(unsafe.Pointer(creason)) defer C.free(unsafe.Pointer(creason))
t.invalid = C.textfieldOpenInvalidPopover(t._id, creason) t.invalid = C.textfieldOpenInvalidPopover(t.id, creason)
} }
//export textfieldChanged //export textfieldChanged
@ -65,28 +68,8 @@ func textfieldChanged(data unsafe.Pointer) {
t.changed.fire() t.changed.fire()
} }
func (t *textfield) id() C.id { func (t *textfield) xpreferredSize(d *sizing) (width, height int) {
return t._id _, height = t.chainpreferredSize(d)
}
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)
// the returned width is based on the contents; use this instead // the returned width is based on the contents; use this instead
return C.textfieldWidth, height 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)
}

View File

@ -18,7 +18,7 @@ import (
import "C" import "C"
type textfield struct { type textfield struct {
_widget *C.GtkWidget *controlSingleWidget
entry *C.GtkEntry entry *C.GtkEntry
changed *event changed *event
} }
@ -26,12 +26,12 @@ type textfield struct {
func startNewTextField() *textfield { func startNewTextField() *textfield {
widget := C.gtk_entry_new() widget := C.gtk_entry_new()
t := &textfield{ t := &textfield{
_widget: widget, controlSingleWidget: newControlSingleWidget(widget),
entry: (*C.GtkEntry)(unsafe.Pointer(widget)), entry: (*C.GtkEntry)(unsafe.Pointer(widget)),
changed: newEvent(), changed: newEvent(),
} }
g_signal_connect( g_signal_connect(
C.gpointer(unsafe.Pointer(t._widget)), C.gpointer(unsafe.Pointer(t.widget)),
"changed", "changed",
C.GCallback(C.textfieldChanged), C.GCallback(C.textfieldChanged),
C.gpointer(unsafe.Pointer(t))) C.gpointer(unsafe.Pointer(t)))
@ -71,7 +71,7 @@ func (t *textfield) Invalid(reason string) {
creason := togstr(reason) creason := togstr(reason)
defer freegstr(creason) defer freegstr(creason)
C.gtk_entry_set_icon_tooltip_text(t.entry, C.GTK_ENTRY_ICON_SECONDARY, 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 //export textfieldChanged
@ -79,27 +79,3 @@ func textfieldChanged(editable *C.GtkEditable, data C.gpointer) {
t := (*textfield)(unsafe.Pointer(data)) t := (*textfield)(unsafe.Pointer(data))
t.changed.fire() 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)
}

View File

@ -10,8 +10,7 @@ import (
import "C" import "C"
type textfield struct { type textfield struct {
_hwnd C.HWND *controlSingleHWNDWithText
_textlen C.LONG
changed *event changed *event
} }
@ -22,11 +21,12 @@ func startNewTextField(style C.DWORD) *textfield {
style|C.textfieldStyle, 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) 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{ t := &textfield{
_hwnd: hwnd, controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
changed: newEvent(), changed: newEvent(),
} }
C.controlSetControlFont(t._hwnd) t.fpreferredSize = t.xpreferredSize
C.setTextFieldSubclass(t._hwnd, unsafe.Pointer(t)) C.controlSetControlFont(t.hwnd)
C.setTextFieldSubclass(t.hwnd, unsafe.Pointer(t))
return t return t
} }
@ -39,11 +39,11 @@ func newPasswordField() *textfield {
} }
func (t *textfield) Text() string { func (t *textfield) Text() string {
return baseText(t) return t.text()
} }
func (t *textfield) SetText(text string) { func (t *textfield) SetText(text string) {
baseSetText(t, text) t.setText(text)
} }
func (t *textfield) OnChanged(f func()) { func (t *textfield) OnChanged(f func()) {
@ -52,10 +52,10 @@ func (t *textfield) OnChanged(f func()) {
func (t *textfield) Invalid(reason string) { func (t *textfield) Invalid(reason string) {
if reason == "" { if reason == "" {
C.textfieldHideInvalidBalloonTip(t._hwnd) C.textfieldHideInvalidBalloonTip(t.hwnd)
return return
} }
C.textfieldSetAndShowInvalidBalloonTip(t._hwnd, toUTF16(reason)) C.textfieldSetAndShowInvalidBalloonTip(t.hwnd, toUTF16(reason))
} }
//export textfieldChanged //export textfieldChanged
@ -64,40 +64,12 @@ func textfieldChanged(data unsafe.Pointer) {
t.changed.fire() 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 ( const (
// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing // 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 textfieldWidth = 107 // this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary
textfieldHeight = 14 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) 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)
}

View File

@ -98,7 +98,7 @@ extern LRESULT getWindowTextLen(HWND);
extern void getWindowText(HWND, WPARAM, LPWSTR); extern void getWindowText(HWND, WPARAM, LPWSTR);
extern void setWindowText(HWND, LPWSTR); extern void setWindowText(HWND, LPWSTR);
extern void updateWindow(HWND); 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 BOOL sharedWndProc(HWND, UINT, WPARAM, LPARAM, LRESULT *);
extern void paintControlBackground(HWND, HDC); extern void paintControlBackground(HWND, HDC);
@ -122,8 +122,10 @@ extern intptr_t tableSelectedItem(HWND);
extern void tableSelectItem(HWND, intptr_t); extern void tableSelectItem(HWND, intptr_t);
// container_windows.c // container_windows.c
#define containerclass L"gouicontainer"
extern DWORD makeContainerWindowClass(char **); extern DWORD makeContainerWindowClass(char **);
extern HWND newContainer(void *); extern HWND newContainer();
extern RECT containerBounds(HWND);
extern void calculateBaseUnits(HWND, int *, int *, LONG *); extern void calculateBaseUnits(HWND, int *, int *, LONG *);
// area_windows.c // area_windows.c

View File

@ -26,6 +26,11 @@ type Window interface {
// If this handler returns false, the Window is not closed. // If this handler returns false, the Window is not closed.
OnClosing(func() bool) 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 windowDialog
} }

View File

@ -14,7 +14,10 @@ type window struct {
closing *event closing *event
*container child Control
container *container
margined bool
} }
func newWindow(title string, width int, height int, control Control) *window { 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{ w := &window{
id: id, id: id,
closing: newEvent(), closing: newEvent(),
container: newContainer(control), child: control,
container: newContainer(),
} }
C.windowSetDelegate(w.id, unsafe.Pointer(w)) C.windowSetDelegate(w.id, unsafe.Pointer(w))
C.windowSetContentView(w.id, w.container.id) C.windowSetContentView(w.id, w.container.id)
w.child.setParent(w.container.parent())
// trigger an initial resize
return w return w
} }
@ -44,6 +50,9 @@ func (w *window) SetTitle(title string) {
func (w *window) Show() { func (w *window) Show() {
C.windowShow(w.id) C.windowShow(w.id)
// trigger an initial resize
// TODO fine-tune this
windowResized(unsafe.Pointer(w))
} }
func (w *window) Hide() { func (w *window) Hide() {
@ -58,6 +67,14 @@ func (w *window) OnClosing(e func() bool) {
w.closing.setbool(e) w.closing.setbool(e)
} }
func (w *window) Margined() bool {
return w.margined
}
func (w *window) SetMargined(margined bool) {
w.margined = margined
}
//export windowClosing //export windowClosing
func windowClosing(xw unsafe.Pointer) C.BOOL { func windowClosing(xw unsafe.Pointer) C.BOOL {
w := (*window)(unsafe.Pointer(xw)) w := (*window)(unsafe.Pointer(xw))
@ -67,3 +84,11 @@ func windowClosing(xw unsafe.Pointer) C.BOOL {
} }
return C.NO 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)
}

View File

@ -20,6 +20,11 @@
return windowClosing(self->gowin); return windowClosing(self->gowin);
} }
- (void)windowDidResize:(NSNotification *)note
{
windowResized(self->gowin);
}
@end @end
id newWindow(intptr_t width, intptr_t height) id newWindow(intptr_t width, intptr_t height)

View File

@ -10,6 +10,7 @@ import (
// #include "gtk_unix.h" // #include "gtk_unix.h"
// extern gboolean windowClosing(GtkWidget *, GdkEvent *, gpointer); // extern gboolean windowClosing(GtkWidget *, GdkEvent *, gpointer);
// extern void windowResized(GtkWidget *, GdkRectangle *, gpointer);
import "C" import "C"
type window struct { type window struct {
@ -22,7 +23,10 @@ type window struct {
closing *event closing *event
*container child Control
container *container
margined bool
} }
func newWindow(title string, width int, height int, control Control) *window { 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)), bin: (*C.GtkBin)(unsafe.Pointer(widget)),
window: (*C.GtkWindow)(unsafe.Pointer(widget)), window: (*C.GtkWindow)(unsafe.Pointer(widget)),
closing: newEvent(), closing: newEvent(),
child: control,
} }
C.gtk_window_set_title(w.window, ctitle) C.gtk_window_set_title(w.window, ctitle)
g_signal_connect( g_signal_connect(
@ -43,8 +48,15 @@ func newWindow(title string, width int, height int, control Control) *window {
C.GCallback(C.windowClosing), C.GCallback(C.windowClosing),
C.gpointer(unsafe.Pointer(w))) C.gpointer(unsafe.Pointer(w)))
C.gtk_window_resize(w.window, C.gint(width), C.gint(height)) 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}) 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 // for dialogs; otherwise, they will be modal to all windows, not just this one
w.group = C.gtk_window_group_new() w.group = C.gtk_window_group_new()
C.gtk_window_group_add_window(w.group, w.window) 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) w.closing.setbool(e)
} }
func (w *window) Margined() bool {
return w.margined
}
func (w *window) SetMargined(margined bool) {
w.margined = margined
}
//export windowClosing //export windowClosing
func windowClosing(wid *C.GtkWidget, e *C.GdkEvent, data C.gpointer) C.gboolean { func windowClosing(wid *C.GtkWidget, e *C.GdkEvent, data C.gpointer) C.gboolean {
w := (*window)(unsafe.Pointer(data)) 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 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)
}

View File

@ -13,7 +13,7 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA
RECT r; RECT r;
LRESULT lResult; LRESULT lResult;
data = (void *) getWindowData(hwnd, uMsg, wParam, lParam, &lResult, storeWindowHWND); data = (void *) getWindowData(hwnd, uMsg, wParam, lParam, &lResult);
if (data == NULL) if (data == NULL)
return lResult; return lResult;
if (sharedWndProc(hwnd, uMsg, wParam, lParam, &lResult)) if (sharedWndProc(hwnd, uMsg, wParam, lParam, &lResult))

View File

@ -17,7 +17,8 @@ type window struct {
closing *event closing *event
*container child Control
margined bool
} }
func makeWindowWindowClass() error { func makeWindowWindowClass() error {
@ -32,19 +33,15 @@ func makeWindowWindowClass() error {
func newWindow(title string, width int, height int, control Control) *window { func newWindow(title string, width int, height int, control Control) *window {
w := &window{ w := &window{
// hwnd set in WM_CREATE handler
closing: newEvent(), closing: newEvent(),
container: newContainer(control), child: 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))
} }
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) hresult := C.EnableThemeDialogTexture(w.hwnd, C.ETDT_ENABLE|C.ETDT_USETABTEXTURE)
if hresult != C.S_OK { if hresult != C.S_OK {
panic(fmt.Errorf("error setting tab background texture on Window; HRESULT: 0x%X", hresult)) 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 return w
} }
@ -78,17 +75,22 @@ func (w *window) OnClosing(e func() bool) {
w.closing.setbool(e) w.closing.setbool(e)
} }
//export storeWindowHWND func (w *window) Margined() bool {
func storeWindowHWND(data unsafe.Pointer, hwnd C.HWND) { return w.margined
w := (*window)(data) }
w.hwnd = hwnd
func (w *window) SetMargined(margined bool) {
w.margined = margined
} }
//export windowResize //export windowResize
func windowResize(data unsafe.Pointer, r *C.RECT) { func windowResize(data unsafe.Pointer, r *C.RECT) {
w := (*window)(data) 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 d := w.beginResize()
w.container.move(r) 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 //export windowClosing

View File

@ -52,6 +52,7 @@ func newRepainter(times int) *repainter {
grid.Add(r.repaint, nil, South, true, Fill, true, Fill, 1, 1) grid.Add(r.repaint, nil, South, true, Fill, true, Fill, 1, 1)
grid.Add(r.all, nil, South, true, Center, false, LeftTop, 1, 1) grid.Add(r.all, nil, South, true, Center, false, LeftTop, 1, 1)
r.grid = grid r.grid = grid
r.grid.SetPadded(*spaced)
return r return r
} }

View File

@ -18,6 +18,25 @@ import (
var closeOnClick = flag.Bool("close", false, "close on click") 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 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 { type dtype struct {
Name string Name string
@ -96,7 +115,7 @@ func (tw *testwin) addfe() {
tw.felabel.SetText(t.String()) tw.felabel.SetText(t.String())
}) })
}) })
tw.felabel = NewStandaloneLabel("<stopped>") tw.felabel = NewLabel("<stopped>")
tw.festop = NewButton("Stop") tw.festop = NewButton("Stop")
tw.festop.OnClicked(func() { tw.festop.OnClicked(func() {
if tw.fe != nil { if tw.fe != nil {
@ -117,8 +136,8 @@ func (tw *testwin) addfe() {
tw.openbtn.OnClicked(func() { tw.openbtn.OnClicked(func() {
OpenFile(tw.w, tw.openFile) OpenFile(tw.w, tw.openFile)
}) })
tw.fnlabel = NewStandaloneLabel("<no file selected>") tw.fnlabel = NewLabel("<no file selected>")
tw.festack = NewVerticalStack(tw.festart, tw.festack = newVerticalStack(tw.festart,
tw.felabel, tw.felabel,
tw.festop, tw.festop,
NewCheckbox("This is a checkbox test"), NewCheckbox("This is a checkbox test"),
@ -129,7 +148,7 @@ func (tw *testwin) addfe() {
tw.openbtn, tw.fnlabel) tw.openbtn, tw.fnlabel)
tw.festack.SetStretchy(4) tw.festack.SetStretchy(4)
tw.festack.SetStretchy(6) tw.festack.SetStretchy(6)
tw.festack = NewHorizontalStack(tw.festack, Space()) tw.festack = newHorizontalStack(tw.festack, Space())
tw.festack.SetStretchy(0) tw.festack.SetStretchy(0)
tw.festack.SetStretchy(1) tw.festack.SetStretchy(1)
tw.t.Append("Foreign Events", tw.festack) tw.t.Append("Foreign Events", tw.festack)
@ -138,6 +157,7 @@ func (tw *testwin) addfe() {
func (tw *testwin) make(done chan struct{}) { func (tw *testwin) make(done chan struct{}) {
tw.t = NewTab() tw.t = NewTab()
tw.w = NewWindow("Hello", 320, 240, tw.t) tw.w = NewWindow("Hello", 320, 240, tw.t)
tw.w.SetMargined(*spaced)
tw.w.OnClosing(func() bool { tw.w.OnClosing(func() bool {
if *closeOnClick { if *closeOnClick {
panic("window closed normally in close on click mode (should not happen)") 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.group2 = NewGroup("Group", NewButton("Button in Group"))
tw.t.Append("Empty Group", NewGroup("Group", Space())) tw.t.Append("Empty Group", NewGroup("Group", Space()))
tw.t.Append("Filled Group", tw.group2) 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.t.Append("Group", tw.group)
tw.simpleGrid = NewSimpleGrid(3, tw.simpleGrid = newSimpleGrid(3,
NewLabel("0,0"), NewTextField(), NewLabel("0,2"), NewLabel("0,0"), NewTextField(), NewLabel("0,2"),
NewButton("1,0"), NewButton("1,1"), NewButton("1,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(2, 1)
tw.simpleGrid.SetFilling(1, 2) tw.simpleGrid.SetFilling(1, 2)
tw.simpleGrid.SetStretchy(1, 1) tw.simpleGrid.SetStretchy(1, 1)
@ -189,33 +211,33 @@ func (tw *testwin) make(done chan struct{}) {
tw.t.Append("Space", Space()) tw.t.Append("Space", Space())
tw.a = NewArea(200, 200, &areaHandler{false}) tw.a = NewArea(200, 200, &areaHandler{false})
tw.t.Append("Area", tw.a) tw.t.Append("Area", tw.a)
tw.spw = NewHorizontalStack( tw.spw = newHorizontalStack(
NewButton("hello"), NewButton("hello"),
NewCheckbox("hello"), NewCheckbox("hello"),
NewTextField(), NewTextField(),
NewPasswordField(), NewPasswordField(),
NewTable(reflect.TypeOf(struct{ A, B, C int }{})), NewTable(reflect.TypeOf(struct{ A, B, C int }{})),
NewStandaloneLabel("hello")) NewLabel("hello"))
tw.t.Append("Pref Width", tw.spw) tw.t.Append("Pref Width", tw.spw)
tw.sph = NewVerticalStack( tw.sph = newVerticalStack(
NewButton("hello"), NewButton("hello"),
NewCheckbox("hello"), NewCheckbox("hello"),
NewTextField(), NewTextField(),
NewPasswordField(), NewPasswordField(),
NewTable(reflect.TypeOf(struct{ A, B, C int }{})), NewTable(reflect.TypeOf(struct{ A, B, C int }{})),
NewStandaloneLabel("hello ÉÀÔ")) NewLabel("hello ÉÀÔ"))
tw.t.Append("Pref Height", tw.sph) tw.t.Append("Pref Height", tw.sph)
stack1 := NewHorizontalStack(NewLabel("Test"), NewTextField()) stack1 := newHorizontalStack(NewLabel("Test"), NewTextField())
stack1.SetStretchy(1) stack1.SetStretchy(1)
stack2 := NewHorizontalStack(NewLabel("ÉÀÔ"), NewTextField()) stack2 := newHorizontalStack(NewLabel("ÉÀÔ"), NewTextField())
stack2.SetStretchy(1) stack2.SetStretchy(1)
stack3 := NewHorizontalStack(NewLabel("Test 2"), stack3 := newHorizontalStack(NewLabel("Test 2"),
NewTable(reflect.TypeOf(struct{ A, B, C int }{}))) NewTable(reflect.TypeOf(struct{ A, B, C int }{})))
stack3.SetStretchy(1) stack3.SetStretchy(1)
tw.s = NewVerticalStack(stack1, stack2, stack3) tw.s = newVerticalStack(stack1, stack2, stack3)
tw.s.SetStretchy(2) tw.s.SetStretchy(2)
tw.t.Append("Stack", tw.s) tw.t.Append("Stack", tw.s)
tw.l = NewStandaloneLabel("hello") tw.l = NewLabel("hello")
tw.t.Append("Label", tw.l) tw.t.Append("Label", tw.l)
tw.table = NewTable(reflect.TypeOf(ddata[0])) tw.table = NewTable(reflect.TypeOf(ddata[0]))
tw.table.Lock() tw.table.Lock()
@ -248,7 +270,7 @@ func (tw *testwin) make(done chan struct{}) {
tw.w.Show() tw.w.Show()
if *smallWindow { if *smallWindow {
tw.wsmall = NewWindow("Small", 80, 80, tw.wsmall = NewWindow("Small", 80, 80,
NewVerticalStack( newVerticalStack(
NewButton("Small"), NewButton("Small"),
NewButton("Small 2"), NewButton("Small 2"),
NewArea(200, 200, &areaHandler{true}))) 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 // because Cocoa hates being run off the main thread, even if it's run exclusively off the main thread
func init() { func init() {
flag.BoolVar(&spaced, "spaced", false, "enable spacing")
flag.Parse() flag.Parse()
go func() { go func() {
tw = new(testwin) tw = new(testwin)