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 {
*areabase
_id C.id
scroller *scroller
*scroller
textfield C.id
textfielddone *event
}
@ -26,11 +25,12 @@ func newArea(ab *areabase) Area {
areabase: ab,
textfielddone: newEvent(),
}
a._id = C.newArea(unsafe.Pointer(a))
a.scroller = newScroller(a._id, false) // no border on Area
id := C.newArea(unsafe.Pointer(a))
a.scroller = newScroller(id, false) // no border on Area
a.fpreferredSize = a.xpreferredSize
a.SetSize(a.width, a.height)
a.textfield = C.newTextField()
C.areaSetTextField(a._id, a.textfield)
C.areaSetTextField(a.id, a.textfield)
return a
}
@ -38,7 +38,7 @@ func (a *area) SetSize(width, height int) {
a.width = width
a.height = height
// set the frame size to set the area's effective size on the Cocoa side
C.moveControl(a._id, 0, 0, C.intptr_t(a.width), C.intptr_t(a.height))
C.moveControl(a.id, 0, 0, C.intptr_t(a.width), C.intptr_t(a.height))
}
func (a *area) Repaint(r image.Rectangle) {
@ -52,18 +52,18 @@ func (a *area) Repaint(r image.Rectangle) {
s.y = C.intptr_t(r.Min.Y)
s.width = C.intptr_t(r.Dx())
s.height = C.intptr_t(r.Dy())
C.areaRepaint(a._id, s)
C.areaRepaint(a.id, s)
}
func (a *area) RepaintAll() {
C.areaRepaintAll(a._id)
C.areaRepaintAll(a.id)
}
func (a *area) OpenTextFieldAt(x, y int) {
if x < 0 || x >= a.width || y < 0 || y >= a.height {
panic(fmt.Errorf("point (%d,%d) outside Area in Area.OpenTextFieldAt()", x, y))
}
C.areaTextFieldOpen(a._id, a.textfield, C.intptr_t(x), C.intptr_t(y))
C.areaTextFieldOpen(a.id, a.textfield, C.intptr_t(x), C.intptr_t(y))
}
func (a *area) TextFieldText() string {
@ -238,27 +238,7 @@ func areaView_flagsChanged(self C.id, e C.id, data unsafe.Pointer) C.BOOL {
return sendKeyEvent(self, ke, data)
}
func (a *area) id() C.id {
return a._id
}
func (a *area) setParent(p *controlParent) {
a.scroller.setParent(p)
}
func (a *area) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(a, x, y, width, height, d)
}
func (a *area) preferredSize(d *sizing) (width, height int) {
func (a *area) xpreferredSize(d *sizing) (width, height int) {
// the preferred size of an Area is its size
return a.width, a.height
}
func (a *area) commitResize(c *allocation, d *sizing) {
a.scroller.commitResize(c, d)
}
func (a *area) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(a, d)
}

View File

@ -34,9 +34,8 @@ import "C"
type area struct {
*areabase
_widget *C.GtkWidget
*scroller
drawingarea *C.GtkDrawingArea
scroller *scroller
clickCounter *clickCounter
@ -59,7 +58,6 @@ func newArea(ab *areabase) Area {
textfieldw := C.gtk_entry_new()
a := &area{
areabase: ab,
_widget: widget,
drawingarea: (*C.GtkDrawingArea)(unsafe.Pointer(widget)),
scroller: newScroller(widget, false, false, true), // not natively scrollable; no border; have an overlay for OpenTextFieldAt()
clickCounter: new(clickCounter),
@ -67,6 +65,7 @@ func newArea(ab *areabase) Area {
textfield: (*C.GtkEntry)(unsafe.Pointer(textfieldw)),
textfielddone: newEvent(),
}
a.fpreferredSize = a.xpreferredSize
for _, c := range areaCallbacks {
g_signal_connect(
C.gpointer(unsafe.Pointer(a.drawingarea)),
@ -75,9 +74,9 @@ func newArea(ab *areabase) Area {
C.gpointer(unsafe.Pointer(a)))
}
a.SetSize(a.width, a.height)
C.gtk_overlay_add_overlay(a.scroller.overlay, a.textfieldw)
C.gtk_overlay_add_overlay(a.scroller.overlayoverlay, a.textfieldw)
g_signal_connect(
C.gpointer(unsafe.Pointer(a.scroller.overlay)),
C.gpointer(unsafe.Pointer(a.scroller.overlayoverlay)),
"get-child-position",
area_get_child_position_callback,
C.gpointer(unsafe.Pointer(a)))
@ -103,7 +102,7 @@ func newArea(ab *areabase) Area {
func (a *area) SetSize(width, height int) {
a.width = width
a.height = height
C.gtk_widget_set_size_request(a._widget, C.gint(a.width), C.gint(a.height))
C.gtk_widget_set_size_request(a.widget, C.gint(a.width), C.gint(a.height))
}
func (a *area) Repaint(r image.Rectangle) {
@ -111,11 +110,11 @@ func (a *area) Repaint(r image.Rectangle) {
if r.Empty() {
return
}
C.gtk_widget_queue_draw_area(a._widget, C.gint(r.Min.X), C.gint(r.Min.Y), C.gint(r.Dx()), C.gint(r.Dy()))
C.gtk_widget_queue_draw_area(a.widget, C.gint(r.Min.X), C.gint(r.Min.Y), C.gint(r.Dx()), C.gint(r.Dy()))
}
func (a *area) RepaintAll() {
C.gtk_widget_queue_draw(a._widget)
C.gtk_widget_queue_draw(a.widget)
}
func (a *area) OpenTextFieldAt(x, y int) {
@ -492,28 +491,7 @@ var modonlykeys = map[C.guint]Modifiers{
C.GDK_KEY_Super_R: Super,
}
func (a *area) widget() *C.GtkWidget {
return a._widget
}
func (a *area) setParent(p *controlParent) {
a.scroller.setParent(p)
}
func (a *area) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(a, x, y, width, height, d)
}
func (a *area) preferredSize(d *sizing) (width, height int) {
func (a *area) xpreferredSize(d *sizing) (width, height int) {
// the preferred size of an Area is its size
return a.width, a.height
}
func (a *area) commitResize(c *allocation, d *sizing) {
a.scroller.commitResize(c, d)
}
func (a *area) getAuxResizeInfo(d *sizing) {
// a Label to the left of an Area should be vertically aligned to the top
d.shouldVAlignTop = true
}

View File

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

View File

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

View File

@ -87,31 +87,20 @@ func NewTab() Tab {
// Label is a Control that shows a static line of text.
// Label shows one line of text; any text that does not fit is truncated.
// A Label can either have smart vertical alignment relative to the control to its right or just be vertically aligned to the top (standalone).
// The effect of placing a non-standalone Label in any context other than to the immediate left of a Control is undefined.
// Both types of labels are left-aligned. [FUTURE PLANS: For platform-specific horizontal alignment rules, use a Form.]
// Labels are left-aligned. [FUTURE PLANS: For platform-specific horizontal alignment rules, use a Form.]
type Label interface {
Control
// Text and SetText get and set the Label's text.
Text() string
SetText(text string)
isStandalone() bool
}
// NewLabel creates a new Label with the given text.
// The Label will smartly vertically position itself relative to the control to its immediate right.
func NewLabel(text string) Label {
return newLabel(text)
}
// NewStandaloneLabel creates a new Label with the given text.
// The Label will be vertically positioned at the top of its allocated space.
func NewStandaloneLabel(text string) Label {
return newStandaloneLabel(text)
}
// Group is a Control that holds a single Control; if that Control also contains other Controls, then the Controls will appear visually grouped together.
// The appearance of a Group varies from system to system; for the most part a Group consists of a thin frame.
// All Groups have a text label indicating what the Group is for.
@ -121,6 +110,11 @@ type Group interface {
// Text and SetText get and set the Group's label text.
Text() string
SetText(text string)
// Margined and SetMargined get and set whether the contents of the Group have a margin around them.
// The size of the margin is platform-dependent.
Margined() bool
SetMargined(margined bool)
}
// NewGroup creates a new Group with the given text label and child Control.

View File

@ -10,7 +10,7 @@ import (
import "C"
type button struct {
_id C.id
*controlSingleObject
clicked *event
}
@ -18,11 +18,11 @@ func newButton(text string) *button {
ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext))
b := &button{
_id: C.newButton(),
controlSingleObject: newControlSingleObject(C.newButton()),
clicked: newEvent(),
}
C.buttonSetText(b._id, ctext)
C.buttonSetDelegate(b._id, unsafe.Pointer(b))
C.buttonSetText(b.id, ctext)
C.buttonSetDelegate(b.id, unsafe.Pointer(b))
return b
}
@ -31,13 +31,13 @@ func (b *button) OnClicked(e func()) {
}
func (b *button) Text() string {
return C.GoString(C.buttonText(b._id))
return C.GoString(C.buttonText(b.id))
}
func (b *button) SetText(text string) {
ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext))
C.buttonSetText(b._id, ctext)
C.buttonSetText(b.id, ctext)
}
//export buttonClicked
@ -45,27 +45,3 @@ func buttonClicked(xb unsafe.Pointer) {
b := (*button)(unsafe.Pointer(xb))
b.clicked.fire()
}
func (b *button) id() C.id {
return b._id
}
func (b *button) setParent(p *controlParent) {
basesetParent(b, p)
}
func (b *button) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(b, x, y, width, height, d)
}
func (b *button) preferredSize(d *sizing) (width, height int) {
return basepreferredSize(b, d)
}
func (b *button) commitResize(a *allocation, d *sizing) {
basecommitResize(b, a, d)
}
func (b *button) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(b, d)
}

View File

@ -13,7 +13,7 @@ import (
import "C"
type button struct {
_widget *C.GtkWidget
*controlSingleWidget
button *C.GtkButton
clicked *event
}
@ -24,7 +24,7 @@ func newButton(text string) *button {
defer freegstr(ctext)
widget := C.gtk_button_new_with_label(ctext)
b := &button{
_widget: widget,
controlSingleWidget: newControlSingleWidget(widget),
button: (*C.GtkButton)(unsafe.Pointer(widget)),
clicked: newEvent(),
}
@ -55,27 +55,3 @@ func buttonClicked(bwid *C.GtkButton, data C.gpointer) {
b := (*button)(unsafe.Pointer(data))
b.clicked.fire()
}
func (b *button) widget() *C.GtkWidget {
return b._widget
}
func (b *button) setParent(p *controlParent) {
basesetParent(b, p)
}
func (b *button) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(b, x, y, width, height, d)
}
func (b *button) preferredSize(d *sizing) (width, height int) {
return basepreferredSize(b, d)
}
func (b *button) commitResize(a *allocation, d *sizing) {
basecommitResize(b, a, d)
}
func (b *button) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(b, d)
}

View File

@ -10,8 +10,7 @@ import (
import "C"
type button struct {
_hwnd C.HWND
_textlen C.LONG
*controlSingleHWNDWithText
clicked *event
}
@ -22,12 +21,13 @@ func newButton(text string) *button {
C.BS_PUSHBUTTON|C.WS_TABSTOP,
0)
b := &button{
_hwnd: hwnd,
controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
clicked: newEvent(),
}
b.fpreferredSize = b.xpreferredSize
b.SetText(text)
C.controlSetControlFont(b._hwnd)
C.setButtonSubclass(b._hwnd, unsafe.Pointer(b))
C.controlSetControlFont(b.hwnd)
C.setButtonSubclass(b.hwnd, unsafe.Pointer(b))
return b
}
@ -36,11 +36,11 @@ func (b *button) OnClicked(e func()) {
}
func (b *button) Text() string {
return baseText(b)
return b.text()
}
func (b *button) SetText(text string) {
baseSetText(b, text)
b.setText(text)
}
//export buttonClicked
@ -49,51 +49,23 @@ func buttonClicked(data unsafe.Pointer) {
b.clicked.fire()
}
func (b *button) hwnd() C.HWND {
return b._hwnd
}
func (b *button) textlen() C.LONG {
return b._textlen
}
func (b *button) settextlen(len C.LONG) {
b._textlen = len
}
func (b *button) setParent(p *controlParent) {
basesetParent(b, p)
}
func (b *button) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(b, x, y, width, height, d)
}
const (
// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
buttonHeight = 14
)
func (b *button) preferredSize(d *sizing) (width, height int) {
func (b *button) xpreferredSize(d *sizing) (width, height int) {
// comctl32.dll version 6 thankfully provides a method to grab this...
var size C.SIZE
size.cx = 0 // explicitly ask for ideal size
size.cy = 0
if C.SendMessageW(b._hwnd, C.BCM_GETIDEALSIZE, 0, C.LPARAM(uintptr(unsafe.Pointer(&size)))) != C.FALSE {
if C.SendMessageW(b.hwnd, C.BCM_GETIDEALSIZE, 0, C.LPARAM(uintptr(unsafe.Pointer(&size)))) != C.FALSE {
return int(size.cx), int(size.cy)
}
// that failed, fall back
println("message failed; falling back")
// don't worry about the error return from GetSystemMetrics(); there's no way to tell (explicitly documented as such)
xmargins := 2 * int(C.GetSystemMetrics(C.SM_CXEDGE))
return xmargins + int(b._textlen), fromdlgunitsY(buttonHeight, d)
}
func (b *button) commitResize(a *allocation, d *sizing) {
basecommitResize(b, a, d)
}
func (b *button) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(b, d)
return xmargins + int(b.textlen), fromdlgunitsY(buttonHeight, d)
}

View File

@ -10,7 +10,7 @@ import (
import "C"
type checkbox struct {
_id C.id
*controlSingleObject
toggled *event
}
@ -18,11 +18,11 @@ func newCheckbox(text string) *checkbox {
ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext))
c := &checkbox{
_id: C.newCheckbox(),
controlSingleObject: newControlSingleObject(C.newCheckbox()),
toggled: newEvent(),
}
C.buttonSetText(c._id, ctext)
C.checkboxSetDelegate(c._id, unsafe.Pointer(c))
C.buttonSetText(c.id, ctext)
C.checkboxSetDelegate(c.id, unsafe.Pointer(c))
return c
}
@ -31,21 +31,21 @@ func (c *checkbox) OnToggled(e func()) {
}
func (c *checkbox) Text() string {
return C.GoString(C.buttonText(c._id))
return C.GoString(C.buttonText(c.id))
}
func (c *checkbox) SetText(text string) {
ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext))
C.buttonSetText(c._id, ctext)
C.buttonSetText(c.id, ctext)
}
func (c *checkbox) Checked() bool {
return fromBOOL(C.checkboxChecked(c._id))
return fromBOOL(C.checkboxChecked(c.id))
}
func (c *checkbox) SetChecked(checked bool) {
C.checkboxSetChecked(c._id, toBOOL(checked))
C.checkboxSetChecked(c.id, toBOOL(checked))
}
//export checkboxToggled
@ -53,27 +53,3 @@ func checkboxToggled(xc unsafe.Pointer) {
c := (*checkbox)(unsafe.Pointer(xc))
c.toggled.fire()
}
func (c *checkbox) id() C.id {
return c._id
}
func (c *checkbox) setParent(p *controlParent) {
basesetParent(c, p)
}
func (c *checkbox) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(c, x, y, width, height, d)
}
func (c *checkbox) preferredSize(d *sizing) (width, height int) {
return basepreferredSize(c, d)
}
func (c *checkbox) commitResize(a *allocation, d *sizing) {
basecommitResize(c, a, d)
}
func (c *checkbox) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(c, d)
}

View File

@ -13,7 +13,7 @@ import (
import "C"
type checkbox struct {
_widget *C.GtkWidget
*controlSingleWidget
button *C.GtkButton
toggle *C.GtkToggleButton
checkbox *C.GtkCheckButton
@ -25,7 +25,7 @@ func newCheckbox(text string) *checkbox {
defer freegstr(ctext)
widget := C.gtk_check_button_new_with_label(ctext)
c := &checkbox{
_widget: widget,
controlSingleWidget: newControlSingleWidget(widget),
button: (*C.GtkButton)(unsafe.Pointer(widget)),
toggle: (*C.GtkToggleButton)(unsafe.Pointer(widget)),
checkbox: (*C.GtkCheckButton)(unsafe.Pointer(widget)),
@ -66,27 +66,3 @@ func checkboxToggled(bwid *C.GtkToggleButton, data C.gpointer) {
c := (*checkbox)(unsafe.Pointer(data))
c.toggled.fire()
}
func (c *checkbox) widget() *C.GtkWidget {
return c._widget
}
func (c *checkbox) setParent(p *controlParent) {
basesetParent(c, p)
}
func (c *checkbox) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(c, x, y, width, height, d)
}
func (c *checkbox) preferredSize(d *sizing) (width, height int) {
return basepreferredSize(c, d)
}
func (c *checkbox) commitResize(a *allocation, d *sizing) {
basecommitResize(c, a, d)
}
func (c *checkbox) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(c, d)
}

View File

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

View File

@ -29,7 +29,7 @@ void updateWindow(HWND hwnd)
xpanic("error calling UpdateWindow()", GetLastError());
}
void *getWindowData(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *lResult, void (*storeHWND)(void *, HWND))
void *getWindowData(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
{
CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
void *data;
@ -37,11 +37,8 @@ void *getWindowData(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT
data = (void *) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
if (data == NULL) {
// the lpParam is available during WM_NCCREATE and WM_CREATE
if (uMsg == WM_NCCREATE) {
if (uMsg == WM_NCCREATE)
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) (cs->lpCreateParams));
data = (void *) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
(*storeHWND)(data, hwnd);
}
// act as if we're not ready yet, even during WM_NCCREATE (nothing important to the switch statement below happens here anyway)
*lResult = DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
@ -106,21 +103,20 @@ void paintControlBackground(HWND hwnd, HDC dc)
WCHAR classname[128] = L""; // more than enough to avoid collisions
parent = hwnd;
do {
parent = GetParent(parent);
if (parent == NULL)
xpanic("error getting parent container of control in paintControlBackground()", GetLastError());
// wine sends these messages early, yay...
if (parent == msgwin)
return;
for (;;) {
parent = GetParent(parent);
if (parent == NULL)
xpanic("error getting parent control of control in paintControlBackground()", GetLastError());
// wine sends these messages early, yay...
if (parent == msgwin)
return;
if (GetClassNameW(parent, classname, 128) == 0)
xpanic("error getting name of focused window class in paintControlBackground()", GetLastError());
} while (_wcsicmp(classname, L"button") == 0); // skip groupboxes
// skip container and groupboxes
if (_wcsicmp(classname, containerclass) != 0) // container
if (_wcsicmp(classname, L"button") != 0) // groupbox
break;
}
if (GetWindowRect(hwnd, &r) == 0)
xpanic("error getting control's window rect in paintControlBackground()", GetLastError());
// the above is a window rect; convert to client rect

View File

@ -2,41 +2,14 @@
package ui
type allocation struct {
x int
y int
width int
height int
this Control
neighbor Control
}
type sizingbase struct {
xmargin int
ymargintop int
ymarginbottom int
xpadding int
ypadding int
}
type controlSizing interface {
allocate(x int, y int, width int, height int, d *sizing) []*allocation
preferredSize(*sizing) (int, int)
commitResize(*allocation, *sizing)
getAuxResizeInfo(*sizing)
}
// A container hosts a Control and resizes that Control based on changes in size to the parent Window.
// container is used by Window, Tab, and Group to contain and control their respective Controls.
// Tab and Group use containers for their content; as such, their commitResize() functions should only change the size of the Tab and Group themselves, and have their containers do the real work.
// All containers must embed containerbase.
type containerbase struct {
child Control
}
// set to true to apply spacing to all windows
var spaced bool = false
// The container type, which is defined per-platform, is an internal Control that is only used to house other Controls from the underlying UI toolkit's point of view
/* TODO
func (c *container) resize(x, y, width, height int) {
if c.child == nil { // no children; nothing to do
return
@ -50,3 +23,4 @@ func (c *container) resize(x, y, width, height int) {
allocations[i].this.commitResize(allocations[i], d)
}
}
*/

View File

@ -10,33 +10,44 @@ import (
import "C"
type container struct {
containerbase
id C.id
*controlSingleObject
}
type sizing struct {
sizingbase
// for size calculations
// nothing for mac
// nothing on Mac OS X
// for the actual resizing
neighborAlign C.struct_xalignment
}
func newContainer(child Control) *container {
func newContainer() *container {
c := new(container)
c.id = C.newContainerView(unsafe.Pointer(c))
c.child = child
c.child.setParent(&controlParent{c.id})
c.controlSingleObject = newControlSingleObject(C.newContainerView(unsafe.Pointer(c)))
return c
}
//export containerResized
func containerResized(data unsafe.Pointer, width C.intptr_t, height C.intptr_t) {
c := (*container)(unsafe.Pointer(data))
// the origin of a view's content area is always (0, 0)
c.resize(0, 0, int(width), int(height))
func (c *container) parent() *controlParent {
return &controlParent{c.id}
}
func (c *container) allocation(margined bool) C.struct_xrect {
b := C.containerBounds(c.id)
if margined {
b.x += C.intptr_t(macXMargin)
b.y += C.intptr_t(macYMargin)
b.width -= C.intptr_t(macXMargin) * 2
b.height -= C.intptr_t(macYMargin) * 2
}
return b
}
// we can just return these values as is
func (c *container) bounds(d *sizing) (int, int, int, int) {
b := C.containerBounds(c.id)
return int(b.x), int(b.y), int(b.width), int(b.height)
}
// These are based on measurements from Interface Builder.
@ -47,18 +58,14 @@ const (
macYPadding = 8
)
func (c *container) beginResize() (d *sizing) {
func (w *window) beginResize() (d *sizing) {
d = new(sizing)
if spaced {
d.xmargin = macXMargin
d.ymargintop = macYMargin
d.ymarginbottom = d.ymargintop
d.xpadding = macXPadding
d.ypadding = macYPadding
}
d.xpadding = macXPadding
d.ypadding = macYPadding
return d
}
/*TODO
func (c *container) translateAllocationCoords(allocations []*allocation, winwidth, winheight int) {
for _, a := range allocations {
// winheight - y because (0,0) is the bottom-left corner of the window and not the top-left corner
@ -66,3 +73,4 @@ func (c *container) translateAllocationCoords(allocations []*allocation, winwidt
a.y = (winheight - a.y) - a.height
}
}
*/

View File

@ -21,12 +21,6 @@
@implementation goContainerView
- (void)setFrameSize:(NSSize)s
{
[super setFrameSize:s];
containerResized(self->gocontainer, (intptr_t) s.width, (intptr_t) s.height);
}
@end
id newContainerView(void *gocontainer)
@ -40,5 +34,25 @@ id newContainerView(void *gocontainer)
void moveControl(id c, intptr_t x, intptr_t y, intptr_t width, intptr_t height)
{
[toNSView(c) setFrame:NSMakeRect((CGFloat) x, (CGFloat) y, (CGFloat) width, (CGFloat) height)];
NSView *v;
NSRect frame;
frame = NSMakeRect((CGFloat) x, (CGFloat) y, (CGFloat) width, (CGFloat) height);
// mac os x coordinate system has (0,0) in the lower-left
v = toNSView(c);
frame.origin.y = ([[v superview] bounds].size.height - frame.size.height) - frame.origin.y;
[v setFrame:frame];
}
struct xrect containerBounds(id view)
{
NSRect b;
struct xrect r;
b = [toNSView(view) bounds];
r.x = (intptr_t) b.origin.x;
r.y = (intptr_t) b.origin.y;
r.width = (intptr_t) b.size.width;
r.height = (intptr_t) b.size.height;
return r;
}

View File

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

View File

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

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

View File

@ -5,17 +5,13 @@ package ui
import (
"fmt"
"syscall"
"unsafe"
)
// #include "winapi_windows.h"
import "C"
type container struct {
containerbase
hwnd C.HWND
nchildren int
isGroup bool
*controlSingleHWND
}
type sizing struct {
@ -40,45 +36,30 @@ func makeContainerWindowClass() error {
return nil
}
func newContainer(control Control) *container {
c := new(container)
hwnd := C.newContainer(unsafe.Pointer(c))
if hwnd != c.hwnd {
panic(fmt.Errorf("inconsistency: hwnd returned by CreateWindowEx() (%p) and hwnd stored in container (%p) differ", hwnd, c.hwnd))
func newContainer() *container {
// don't set preferredSize(); it should never be called
return &container{
controlSingleHWND: newControlSingleHWND(C.newContainer()),
}
c.child = control
c.child.setParent(&controlParent{c})
return c
}
func (c *container) setParent(hwnd C.HWND) {
C.controlSetParent(c.hwnd, hwnd)
}
// this is needed because Windows won't move/resize a child window for us
func (c *container) move(r *C.RECT) {
C.moveWindow(c.hwnd, C.int(r.left), C.int(r.top), C.int(r.right-r.left), C.int(r.bottom-r.top))
}
// TODO merge with controlSingleHWND
func (c *container) show() {
C.ShowWindow(c.hwnd, C.SW_SHOW)
}
// TODO merge with controlSingleHWND
func (c *container) hide() {
C.ShowWindow(c.hwnd, C.SW_HIDE)
}
//export storeContainerHWND
func storeContainerHWND(data unsafe.Pointer, hwnd C.HWND) {
c := (*container)(data)
c.hwnd = hwnd
func (c *container) parent() *controlParent {
return &controlParent{c.hwnd}
}
//export containerResize
func containerResize(data unsafe.Pointer, r *C.RECT) {
c := (*container)(data)
// the origin of any window's content area is always (0, 0), but let's use the values from the RECT just to be safe
c.resize(int(r.left), int(r.top), int(r.right-r.left), int(r.bottom-r.top))
func (c *container) bounds(d *sizing) (int, int, int, int) {
r := C.containerBounds(c.hwnd)
return int(r.left), int(r.top), int(r.right - r.left), int(r.bottom - r.top)
}
// For Windows, Microsoft just hands you a list of preferred control sizes as part of the MSDN documentation and tells you to roll with it.
@ -103,45 +84,31 @@ func fromdlgunitsY(du int, d *sizing) int {
}
const (
// shared by multiple containers
marginDialogUnits = 7
paddingDialogUnits = 4
groupXMargin = 6
groupYMarginTop = 11 // note this value /includes the groupbox label/
groupYMarginBottom = 7
)
func (c *container) beginResize() (d *sizing) {
func (w *window) beginResize() (d *sizing) {
var baseX, baseY C.int
var internalLeading C.LONG
d = new(sizing)
C.calculateBaseUnits(c.hwnd, &baseX, &baseY, &internalLeading)
C.calculateBaseUnits(w.hwnd, &baseX, &baseY, &internalLeading)
d.baseX = baseX
d.baseY = baseY
d.internalLeading = internalLeading
if spaced {
d.xmargin = fromdlgunitsX(marginDialogUnits, d)
d.ymargintop = fromdlgunitsY(marginDialogUnits, d)
d.ymarginbottom = d.ymargintop
d.xpadding = fromdlgunitsX(paddingDialogUnits, d)
d.ypadding = fromdlgunitsY(paddingDialogUnits, d)
}
if c.isGroup {
// note that these values apply regardless of whether or not spaced is set
// this is because Windows groupboxes have the client rect spanning the entire size of the control, not just the active work area
// the measurements Microsoft give us are for spaced margining; let's just use them
d.xmargin = fromdlgunitsX(groupXMargin, d)
d.ymargintop = fromdlgunitsY(groupYMarginTop, d)
d.ymarginbottom = fromdlgunitsY(groupYMarginBottom, d)
}
d.xpadding = fromdlgunitsX(paddingDialogUnits, d)
d.ypadding = fromdlgunitsY(paddingDialogUnits, d)
return d
}
func (c *container) translateAllocationCoords(allocations []*allocation, winwidth, winheight int) {
// no translation needed on windows
func marginRectDLU(r *C.RECT, top int, bottom int, left int, right int, d *sizing) {
r.left += C.LONG(fromdlgunitsX(left, d))
r.top += C.LONG(fromdlgunitsY(top, d))
r.right -= C.LONG(fromdlgunitsX(right, d))
r.bottom -= C.LONG(fromdlgunitsY(bottom, d))
}

View File

@ -5,16 +5,32 @@ package ui
// Control represents a control.
type Control interface {
setParent(p *controlParent) // controlParent defined per-platform
controlSizing
preferredSize(d *sizing) (width, height int)
resize(x int, y int, width int, height int, d *sizing)
nTabStops() int // used by the Windows backend
}
// this is the same across all platforms
func baseallocate(c Control, x int, y int, width int, height int, d *sizing) []*allocation {
return []*allocation{&allocation{
x: x,
y: y,
width: width,
height: height,
this: c,
}}
type controlbase struct {
fsetParent func(p *controlParent)
fpreferredSize func(d *sizing) (width, height int)
fresize func(x int, y int, width int, height int, d *sizing)
fnTabStops func() int
}
// children should not use the same name as these, otherwise weird things will happen
func (c *controlbase) setParent(p *controlParent) {
c.fsetParent(p)
}
func (c *controlbase) preferredSize(d *sizing) (width, height int) {
return c.fpreferredSize(d)
}
func (c *controlbase) resize(x int, y int, width int, height int, d *sizing) {
c.fresize(x, y, width, height, d)
}
func (c *controlbase) nTabStops() int {
return c.fnTabStops()
}

View File

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

View File

@ -11,24 +11,34 @@ import (
// #include "gtk_unix.h"
import "C"
// all Controls that call base methods must be this
type controlPrivate interface {
widget() *C.GtkWidget
Control
}
type controlParent struct {
c *C.GtkContainer
}
func basesetParent(c controlPrivate, p *controlParent) {
widget := c.widget() // avoid multiple interface lookups
C.gtk_container_add(p.c, widget)
// make sure the new widget is shown if not explicitly hidden
C.gtk_widget_show_all(widget)
type controlSingleWidget struct {
*controlbase
widget *C.GtkWidget
}
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!
// ...actually, it tells us two things: the "minimum size" and the "natural size".
// The "minimum size" is the smallest size we /can/ display /anything/. The "natural size" is the smallest size we would /prefer/ to display.
@ -37,15 +47,11 @@ func basepreferredSize(c controlPrivate, d *sizing) (int, int) {
// There is a warning about height-for-width controls, but in my tests this isn't an issue.
var r C.GtkRequisition
C.gtk_widget_get_preferred_size(c.widget(), nil, &r)
C.gtk_widget_get_preferred_size(c.widget, nil, &r)
return int(r.width), int(r.height)
}
func basecommitResize(c controlPrivate, a *allocation, d *sizing) {
dobasecommitResize(c.widget(), a, d)
}
func dobasecommitResize(w *C.GtkWidget, c *allocation, d *sizing) {
func (c *controlSingleWidget) xresize(x int, y int, width int, height int, d *sizing) {
// as we resize on size-allocate, we have to also use size-allocate on our children
// this is fine anyway; in fact, this allows us to move without knowing what the container is!
// this is what GtkBox does anyway
@ -53,68 +59,63 @@ func dobasecommitResize(w *C.GtkWidget, c *allocation, d *sizing) {
var r C.GtkAllocation
r.x = C.int(c.x)
r.y = C.int(c.y)
r.width = C.int(c.width)
r.height = C.int(c.height)
C.gtk_widget_size_allocate(w, &r)
}
func basegetAuxResizeInfo(c Control, d *sizing) {
// controls set this to true if a Label to its left should be vertically aligned to the control's top
d.shouldVAlignTop = false
r.x = C.int(x)
r.y = C.int(y)
r.width = C.int(width)
r.height = C.int(height)
C.gtk_widget_size_allocate(c.widget, &r)
}
type scroller struct {
*controlSingleWidget
scroller *controlSingleWidget
scrollwidget *C.GtkWidget
scrollcontainer *C.GtkContainer
scrollwindow *C.GtkScrolledWindow
overlay *controlSingleWidget
overlaywidget *C.GtkWidget
overlaycontainer *C.GtkContainer
overlay *C.GtkOverlay
addShowWhich *C.GtkWidget
overlayoverlay *C.GtkOverlay
}
func newScroller(widget *C.GtkWidget, native bool, bordered bool, overlay bool) *scroller {
var o *C.GtkWidget
s := new(scroller)
s.controlSingleWidget = newControlSingleWidget(widget)
s.scrollwidget = C.gtk_scrolled_window_new(nil, nil)
s.scrollcontainer = (*C.GtkContainer)(unsafe.Pointer(s.scrollwidget))
s.scrollwindow = (*C.GtkScrolledWindow)(unsafe.Pointer(s.scrollwidget))
scrollwidget := C.gtk_scrolled_window_new(nil, nil)
if overlay {
o = C.gtk_overlay_new()
}
s := &scroller{
scrollwidget: scrollwidget,
scrollcontainer: (*C.GtkContainer)(unsafe.Pointer(scrollwidget)),
scrollwindow: (*C.GtkScrolledWindow)(unsafe.Pointer(scrollwidget)),
overlaywidget: o,
overlaycontainer: (*C.GtkContainer)(unsafe.Pointer(o)),
overlay: (*C.GtkOverlay)(unsafe.Pointer(o)),
// any actual changing operations need to be done to the GtkScrolledWindow
// that is, everything /except/ preferredSize() are done to the GtkScrolledWindow
s.scroller = newControlSingleWidget(s.scrollwidget)
s.fsetParent = s.scroller.fsetParent
s.fresize = s.scroller.fresize
// in GTK+ 3.4 we still technically need to use the separate gtk_scrolled_window_add_with_viewpoint()/gtk_container_add() spiel for adding the widget to the scrolled window
if native {
C.gtk_container_add(s.scrollcontainer, s.widget)
} else {
C.gtk_scrolled_window_add_with_viewport(s.scrollwindow, s.widget)
}
// give the scrolled window a border (thanks to jlindgren in irc.gimp.net/#gtk+)
if bordered {
C.gtk_scrolled_window_set_shadow_type(s.scrollwindow, C.GTK_SHADOW_IN)
}
if native {
C.gtk_container_add(s.scrollcontainer, widget)
} else {
C.gtk_scrolled_window_add_with_viewport(s.scrollwindow, widget)
}
s.addShowWhich = s.scrollwidget
if overlay {
// ok things get REALLY fun now
// we now have to do all of the above again
s.overlaywidget = C.gtk_overlay_new()
s.overlaycontainer = (*C.GtkContainer)(unsafe.Pointer(s.overlaywidget))
s.overlayoverlay = (*C.GtkOverlay)(unsafe.Pointer(s.overlaywidget))
s.overlay = newControlSingleWidget(s.overlaywidget)
s.fsetParent = s.overlay.fsetParent
s.fresize = s.overlay.fresize
C.gtk_container_add(s.overlaycontainer, s.scrollwidget)
s.addShowWhich = s.overlaywidget
}
return s
}
func (s *scroller) setParent(p *controlParent) {
C.gtk_container_add(p.c, s.addShowWhich)
// see basesetParent() above for why we call gtk_widget_show_all()
C.gtk_widget_show_all(s.addShowWhich)
}
func (s *scroller) commitResize(c *allocation, d *sizing) {
dobasecommitResize(s.addShowWhich, c, d)
}

View File

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

94
grid.go
View File

@ -27,6 +27,11 @@ type Grid interface {
// The effect of overlapping spanning Controls is also undefined.
// Add panics if either xspan or yspan are zero or negative.
Add(control Control, nextTo Control, side Side, xexpand bool, xalign Align, yexpand bool, yalign Align, xspan int, yspan int)
// Padded and SetPadded get and set whether the controls of the Grid have padding between them.
// The size of the padding is platform-dependent.
Padded() bool
SetPadded(padded bool)
}
// Align represents the alignment of a Control in its cell of a Grid.
@ -54,7 +59,8 @@ type grid struct {
controls []gridCell
indexof map[Control]int
prev int
parent *controlParent
container *container
padded bool
xmax int
ymax int
@ -84,6 +90,7 @@ type gridCell struct {
func NewGrid() Grid {
return &grid{
indexof: map[Control]int{},
container: newContainer(),
}
}
@ -129,9 +136,7 @@ func (g *grid) Add(control Control, nextTo Control, side Side, xexpand bool, xal
xspan: xspan,
yspan: yspan,
}
if g.parent != nil {
control.setParent(g.parent)
}
control.setParent(g.container.parent())
// if this is the first control, just add it in directly
if len(g.controls) != 0 {
next := g.prev
@ -161,11 +166,16 @@ func (g *grid) Add(control Control, nextTo Control, side Side, xexpand bool, xal
g.reorigin()
}
func (g *grid) Padded() bool {
return g.padded
}
func (g *grid) SetPadded(padded bool) {
g.padded = padded
}
func (g *grid) setParent(p *controlParent) {
g.parent = p
for i := range g.controls {
g.controls[i].control.setParent(g.parent)
}
g.container.setParent(p)
}
// builds the topological cell grid; also makes colwidths and rowheights
@ -187,15 +197,27 @@ func (g *grid) mkgrid() (gg [][]int, colwidths []int, rowheights []int) {
return gg, make([]int, g.xmax), make([]int, g.ymax)
}
func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocations []*allocation) {
func (g *grid) resize(x int, y int, width int, height int, d *sizing) {
g.container.resize(x, y, width, height, d)
if len(g.controls) == 0 {
// nothing to do
return nil
return
}
x, y, width, height = g.container.bounds(d)
// -2) get this Grid's padding
xpadding := d.xpadding
ypadding := d.ypadding
if !g.padded {
xpadding = 0
ypadding = 0
}
// -1) discount padding from width/height
width -= (g.xmax - 1) * d.xpadding
height -= (g.ymax - 1) * d.ypadding
width -= (g.xmax - 1) * xpadding
height -= (g.ymax - 1) * ypadding
// 0) build necessary data structures
gg, colwidths, rowheights := g.mkgrid()
@ -317,11 +339,11 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
if i != prev {
g.controls[i].finalx = curx
} else {
g.controls[i].finalwidth += d.xpadding
g.controls[i].finalwidth += xpadding
}
g.controls[i].finalwidth += colwidths[x]
}
curx += colwidths[x] + d.xpadding
curx += colwidths[x] + xpadding
prev = i
}
}
@ -334,11 +356,11 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
if i != prev {
g.controls[i].finaly = cury
} else {
g.controls[i].finalheight += d.ypadding
g.controls[i].finalheight += ypadding
}
g.controls[i].finalheight += rowheights[y]
}
cury += rowheights[y] + d.ypadding
cury += rowheights[y] + ypadding
prev = i
}
}
@ -367,29 +389,17 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
}
// 8) and FINALLY we draw
var current *allocation
for _, ycol := range gg {
current = nil
for _, i := range ycol {
if i != -1 { // treat empty cells like spaces
as := g.controls[i].control.allocate(
g.controls[i].control.resize(
g.controls[i].finalx+x, g.controls[i].finaly+y,
g.controls[i].finalwidth, g.controls[i].finalheight, d)
if current != nil { // connect first left to first right
current.neighbor = g.controls[i].control
}
if len(as) != 0 {
current = as[0] // next left is first subwidget
} else {
current = nil // spaces don't have allocation data
}
allocations = append(allocations, as...)
}
}
}
return allocations
return
}
func (g *grid) preferredSize(d *sizing) (width, height int) {
@ -398,6 +408,14 @@ func (g *grid) preferredSize(d *sizing) (width, height int) {
return 0, 0
}
// -1) get this Grid's padding
xpadding := d.xpadding
ypadding := d.ypadding
if !g.padded {
xpadding = 0
ypadding = 0
}
// 0) build necessary data structures
gg, colwidths, rowheights := g.mkgrid()
@ -434,14 +452,14 @@ func (g *grid) preferredSize(d *sizing) (width, height int) {
}
// and that's it; just account for padding
return colwidth + (g.xmax-1)*d.xpadding,
rowheight + (g.ymax-1)*d.ypadding
return colwidth + (g.xmax-1) * xpadding,
rowheight + (g.ymax-1) * ypadding
}
func (g *grid) commitResize(a *allocation, d *sizing) {
// do nothing; needed to satisfy Control
}
func (g *grid) getAuxResizeInfo(d *sizing) {
// do nothing; needed to satisfy Control
func (g *grid) nTabStops() int {
n := 0
for _, c := range g.controls {
n += c.control.nTabStops()
}
return n
}

View File

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

View File

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

View File

@ -6,10 +6,10 @@ package ui
import "C"
type group struct {
_hwnd C.HWND
_textlen C.LONG
*container
*controlSingleHWNDWithText
child Control
margined bool
chainresize func(x int, y int, width int, height int, d *sizing)
}
func newGroup(text string, control Control) Group {
@ -17,66 +17,82 @@ func newGroup(text string, control Control) Group {
C.BS_GROUPBOX,
C.WS_EX_CONTROLPARENT)
g := &group{
_hwnd: hwnd,
container: newContainer(control),
controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
child: control,
}
g.fpreferredSize = g.xpreferredSize
g.chainresize = g.fresize
g.fresize = g.xresize
g.fnTabStops = control.nTabStops // groupbox itself is not tabbable but the contents might be
g.SetText(text)
C.controlSetControlFont(g._hwnd)
g.container.setParent(g._hwnd)
g.container.isGroup = true
C.controlSetControlFont(g.hwnd)
control.setParent(&controlParent{g.hwnd})
return g
}
func (g *group) Text() string {
return baseText(g)
return g.text()
}
func (g *group) SetText(text string) {
baseSetText(g, text)
g.setText(text)
}
func (g *group) hwnd() C.HWND {
return g._hwnd
func (g *group) Margined() bool {
return g.margined
}
func (g *group) textlen() C.LONG {
return g._textlen
func (g *group) SetMargined(margined bool) {
g.margined = margined
}
func (g *group) settextlen(len C.LONG) {
g._textlen = len
}
const (
groupXMargin = 6
groupYMarginTop = 11 // note this value /includes the groupbox label/
groupYMarginBottom = 7
)
func (g *group) setParent(p *controlParent) {
basesetParent(g, p)
}
func (g *group) xpreferredSize(d *sizing) (width, height int) {
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)
if width < int(g._textlen) { // if the text is longer, try not to truncate
width = int(g._textlen)
if width < int(g.textlen) { // if the text is longer, try not to truncate
width = int(g.textlen)
}
// the two margin constants come from container_windows.go
return width, height + fromdlgunitsY(groupYMarginTop, d) + fromdlgunitsY(groupYMarginBottom, d)
r.left = 0
r.top = 0
r.right = C.LONG(width)
r.bottom = C.LONG(height)
// use negative numbers to increase the size of the rectangle
if g.margined {
marginRectDLU(&r, -groupYMarginTop, -groupYMarginBottom, -groupXMargin, -groupXMargin, d)
} else {
// unforutnately, as mentioned above, the size of a groupbox includes the label and border
// 1DLU on each side should be enough to make up for that; TODO is not, we can change it
// TODO make these named constants
marginRectDLU(&r, -1, -1, -1, -1, d)
}
return int(r.right - r.left), int(r.bottom - r.top)
}
func (g *group) commitResize(c *allocation, d *sizing) {
func (g *group) xresize(x int, y int, width int, height int, d *sizing) {
// first, chain up to the container base to keep the Z-order correct
g.chainresize(x, y, width, height, d)
// now resize the child container
var r C.RECT
// pretend that the client area of the group box only includes the actual empty space
// container will handle the necessary adjustments properly
r.left = 0
r.top = 0
r.right = C.LONG(c.width)
r.bottom = C.LONG(c.height)
g.container.move(&r)
basecommitResize(g, c, d)
}
func (g *group) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(g, d)
r.right = C.LONG(width)
r.bottom = C.LONG(height)
if g.margined {
// see above
marginRectDLU(&r, groupYMarginTop, groupYMarginBottom, groupXMargin, groupXMargin, d)
} else {
marginRectDLU(&r, 1, 1, 1, 1, d)
}
g.child.resize(int(r.left), int(r.top), int(r.right - r.left), int(r.bottom - r.top), d)
}

View File

@ -10,57 +10,28 @@ import (
import "C"
type label struct {
_id C.id
standalone bool
*controlSingleObject
}
func finishNewLabel(text string, standalone bool) *label {
func newLabel(text string) Label {
l := &label{
_id: C.newLabel(),
standalone: standalone,
controlSingleObject: newControlSingleObject(C.newLabel()),
}
l.SetText(text)
return l
}
func newLabel(text string) Label {
return finishNewLabel(text, false)
}
func newStandaloneLabel(text string) Label {
return finishNewLabel(text, true)
}
func (l *label) Text() string {
return C.GoString(C.textfieldText(l._id))
return C.GoString(C.textfieldText(l.id))
}
func (l *label) SetText(text string) {
ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext))
C.textfieldSetText(l._id, ctext)
}
func (l *label) isStandalone() bool {
return l.standalone
}
func (l *label) id() C.id {
return l._id
}
func (l *label) setParent(p *controlParent) {
basesetParent(l, p)
}
func (l *label) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(l, x, y, width, height, d)
}
func (l *label) preferredSize(d *sizing) (width, height int) {
return basepreferredSize(l, d)
C.textfieldSetText(l.id, ctext)
}
/*TODO
func (l *label) commitResize(c *allocation, d *sizing) {
if !l.standalone && c.neighbor != nil {
c.neighbor.getAuxResizeInfo(d)
@ -89,7 +60,4 @@ func (l *label) commitResize(c *allocation, d *sizing) {
}
basecommitResize(l, c, d)
}
func (l *label) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(l, d)
}
*/

View File

@ -9,40 +9,34 @@ import (
)
// #include "gtk_unix.h"
// extern void buttonClicked(GtkButton *, gpointer);
// extern void checkboxToggled(GtkToggleButton *, gpointer);
import "C"
type label struct {
_widget *C.GtkWidget
*controlSingleWidget
misc *C.GtkMisc
label *C.GtkLabel
standalone bool
}
func finishNewLabel(text string, standalone bool) *label {
func newLabel(text string) Label {
ctext := togstr(text)
defer freegstr(ctext)
widget := C.gtk_label_new(ctext)
l := &label{
_widget: widget,
controlSingleWidget: newControlSingleWidget(widget),
misc: (*C.GtkMisc)(unsafe.Pointer(widget)),
label: (*C.GtkLabel)(unsafe.Pointer(widget)),
standalone: standalone,
}
return l
}
func newLabel(text string) Label {
return finishNewLabel(text, false)
}
/*TODO
func newStandaloneLabel(text string) Label {
l := finishNewLabel(text, true)
// standalone labels are always at the top left
C.gtk_misc_set_alignment(l.misc, 0, 0)
return l
}
*/
func (l *label) Text() string {
return fromgstr(C.gtk_label_get_text(l.label))
@ -54,26 +48,7 @@ func (l *label) SetText(text string) {
C.gtk_label_set_text(l.label, ctext)
}
func (l *label) isStandalone() bool {
return l.standalone
}
func (l *label) widget() *C.GtkWidget {
return l._widget
}
func (l *label) setParent(p *controlParent) {
basesetParent(l, p)
}
func (l *label) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(l, x, y, width, height, d)
}
func (l *label) preferredSize(d *sizing) (width, height int) {
return basepreferredSize(l, d)
}
/*TODO
func (l *label) commitResize(c *allocation, d *sizing) {
if !l.standalone && c.neighbor != nil {
c.neighbor.getAuxResizeInfo(d)
@ -86,7 +61,4 @@ func (l *label) commitResize(c *allocation, d *sizing) {
}
basecommitResize(l, c, d)
}
func (l *label) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(l, d)
}
*/

View File

@ -6,67 +6,37 @@ package ui
import "C"
type label struct {
_hwnd C.HWND
_textlen C.LONG
*controlSingleHWNDWithText
standalone bool
}
var labelclass = toUTF16("STATIC")
func finishNewLabel(text string, standalone bool) *label {
func newLabel(text string) Label {
hwnd := C.newControl(labelclass,
// SS_NOPREFIX avoids accelerator translation; SS_LEFTNOWORDWRAP clips text past the end
// controls are vertically aligned to the top by default (thanks Xeek in irc.freenode.net/#winapi)
C.SS_NOPREFIX|C.SS_LEFTNOWORDWRAP,
C.WS_EX_TRANSPARENT)
l := &label{
_hwnd: hwnd,
standalone: standalone,
controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
}
l.fpreferredSize = l.xpreferredSize
l.fnTabStops = func() int {
// labels are not tab stops
return 0
}
l.SetText(text)
C.controlSetControlFont(l._hwnd)
C.controlSetControlFont(l.hwnd)
return l
}
func newLabel(text string) Label {
return finishNewLabel(text, false)
}
func newStandaloneLabel(text string) Label {
return finishNewLabel(text, true)
}
func (l *label) Text() string {
return baseText(l)
return l.text()
}
func (l *label) SetText(text string) {
baseSetText(l, text)
}
func (l *label) isStandalone() bool {
return l.standalone
}
func (l *label) hwnd() C.HWND {
return l._hwnd
}
func (l *label) textlen() C.LONG {
return l._textlen
}
func (l *label) settextlen(len C.LONG) {
l._textlen = len
}
func (l *label) setParent(p *controlParent) {
C.controlSetParent(l.hwnd(), p.c.hwnd)
// don't increment p.c.nchildren here because Labels aren't tab stops
}
func (l *label) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(l, x, y, width, height, d)
l.setText(text)
}
const (
@ -75,10 +45,11 @@ const (
labelYOffset = 3
)
func (l *label) preferredSize(d *sizing) (width, height int) {
return int(l._textlen), fromdlgunitsY(labelHeight, d)
func (l *label) xpreferredSize(d *sizing) (width, height int) {
return int(l.textlen), fromdlgunitsY(labelHeight, d)
}
/*TODO
func (l *label) commitResize(c *allocation, d *sizing) {
if !l.standalone {
yoff := fromdlgunitsY(labelYOffset, d)
@ -93,7 +64,4 @@ func (l *label) commitResize(c *allocation, d *sizing) {
}
basecommitResize(l, c, d)
}
func (l *label) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(l, d)
}
*/

View File

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

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).
// A stretchy Control implicitly fills its cell.
// All cooridnates in a SimpleGrid are given in (row,column) form with (0,0) being the top-left cell.
//
// As a special rule, to ensure proper appearance, non-standalone Labels are automatically made filling.
type SimpleGrid interface {
Control
@ -28,6 +26,11 @@ type SimpleGrid interface {
// Only one control can be stretchy per SimpleGrid; calling SetStretchy multiple times merely changes which control is stretchy (preserving the previous filling value).
// It panics if the given coordinate is invalid.
SetStretchy(row int, column int)
// Padded and SetPadded get and set whether the controls of the SimpleGrid have padding between them.
// The size of the padding is platform-dependent.
Padded() bool
SetPadded(padded bool)
}
type simpleGrid struct {
@ -37,6 +40,8 @@ type simpleGrid struct {
stretchyfill bool
widths, heights [][]int // caches to avoid reallocating each time
rowheights, colwidths []int
container *container
padded bool
}
// NewSimpleGrid creates a new SimpleGrid with the given Controls.
@ -64,13 +69,10 @@ func NewSimpleGrid(nPerRow int, controls ...Control) SimpleGrid {
ch[row] = make([]int, nPerRow)
for x := 0; x < nPerRow; x++ {
cc[row][x] = controls[i]
if l, ok := controls[i].(Label); ok && !l.isStandalone() {
cf[row][x] = true
}
i++
}
}
return &simpleGrid{
g := &simpleGrid{
controls: cc,
filling: cf,
stretchyrow: -1,
@ -79,7 +81,15 @@ func NewSimpleGrid(nPerRow int, controls ...Control) SimpleGrid {
heights: ch,
rowheights: make([]int, nRows),
colwidths: make([]int, nPerRow),
container: newContainer(),
}
p := g.container.parent()
for _, cc := range g.controls {
for _, c := range cc {
c.setParent(p)
}
}
return g
}
func (g *simpleGrid) SetFilling(row int, column int) {
@ -102,15 +112,19 @@ func (g *simpleGrid) SetStretchy(row int, column int) {
g.filling[g.stretchyrow][g.stretchycol] = true
}
func (g *simpleGrid) setParent(parent *controlParent) {
for _, col := range g.controls {
for _, c := range col {
c.setParent(parent)
}
}
func (g *simpleGrid) Padded() bool {
return g.padded
}
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 {
if a > b {
return a
@ -118,14 +132,21 @@ func (g *simpleGrid) allocate(x int, y int, width int, height int, d *sizing) (a
return b
}
var current *allocation // for neighboring
g.container.resize(x, y, width, height, d)
if len(g.controls) == 0 {
return nil
return
}
// 0) inset the available rect by the needed padding
width -= (len(g.colwidths) - 1) * d.xpadding
height -= (len(g.rowheights) - 1) * d.ypadding
x, y, width, height = g.container.bounds(d)
// -1) get this SimpleGrid's padding
xpadding := d.xpadding
ypadding := d.ypadding
if !g.padded {
xpadding = 0
ypadding = 0
}
// 0) inset the available rect by the needed padding and reset x/y for children
width -= (len(g.colwidths) - 1) * xpadding
height -= (len(g.rowheights) - 1) * ypadding
// 1) clear data structures
for i := range g.rowheights {
g.rowheights[i] = 0
@ -161,7 +182,6 @@ func (g *simpleGrid) allocate(x int, y int, width int, height int, d *sizing) (a
// 4) draw
startx := x
for row, xcol := range g.controls {
current = nil // reset on new columns
for col, c := range xcol {
w := g.widths[row][col]
h := g.heights[row][col]
@ -169,22 +189,12 @@ func (g *simpleGrid) allocate(x int, y int, width int, height int, d *sizing) (a
w = g.colwidths[col]
h = g.rowheights[row]
}
as := c.allocate(x, y, w, h, d)
if current != nil { // connect first left to first right
current.neighbor = c
}
if len(as) != 0 {
current = as[0] // next left is first subwidget
} else {
current = nil // spaces don't have allocation data
}
allocations = append(allocations, as...)
x += g.colwidths[col] + d.xpadding
c.resize(x, y, w, h, d)
x += g.colwidths[col] + xpadding
}
x = startx
y += g.rowheights[row] + d.ypadding
y += g.rowheights[row] + ypadding
}
return
}
// filling and stretchy are ignored for preferred size calculation
@ -196,8 +206,14 @@ func (g *simpleGrid) preferredSize(d *sizing) (width int, height int) {
return b
}
width -= (len(g.colwidths) - 1) * d.xpadding
height -= (len(g.rowheights) - 1) * d.ypadding
xpadding := d.xpadding
ypadding := d.ypadding
if !g.padded {
xpadding = 0
ypadding = 0
}
width -= (len(g.colwidths) - 1) * xpadding
height -= (len(g.rowheights) - 1) * ypadding
// 1) clear data structures
for i := range g.rowheights {
g.rowheights[i] = 0
@ -225,10 +241,12 @@ func (g *simpleGrid) preferredSize(d *sizing) (width int, height int) {
return width, height
}
func (g *simpleGrid) commitResize(c *allocation, d *sizing) {
// this is to satisfy Control; nothing to do here
}
func (g *simpleGrid) getAuxResizeInfo(d *sizing) {
// this is to satisfy Control; nothing to do here
}
func (g *simpleGrid) nTabStops() int {
n := 0
for _, cc := range g.controls {
for _, c := range cc {
n += c.nTabStops()
}
}
return n
}

View File

@ -24,6 +24,11 @@ type Stack interface {
// SetStretchy marks a control in a Stack as stretchy.
// It panics if index is out of range.
SetStretchy(index int)
// Padded and SetPadded get and set whether the controls of the Stack have padding between them.
// The size of the padding is platform-dependent.
Padded() bool
SetPadded(padded bool)
}
type stack struct {
@ -31,16 +36,24 @@ type stack struct {
controls []Control
stretchy []bool
width, height []int // caches to avoid reallocating these each time
container *container
padded bool
}
func newStack(o orientation, controls ...Control) Stack {
return &stack{
s := &stack{
orientation: o,
controls: controls,
stretchy: make([]bool, len(controls)),
width: make([]int, len(controls)),
height: make([]int, len(controls)),
container: newContainer(),
}
p := s.container.parent()
for _, c := range s.controls {
c.setParent(p)
}
return s
}
// NewHorizontalStack creates a new Stack that arranges the given Controls horizontally.
@ -60,24 +73,38 @@ func (s *stack) SetStretchy(index int) {
s.stretchy[index] = true
}
func (s *stack) setParent(parent *controlParent) {
for _, c := range s.controls {
c.setParent(parent)
}
func (s *stack) Padded() bool {
return s.padded
}
func (s *stack) allocate(x int, y int, width int, height int, d *sizing) (allocations []*allocation) {
var stretchywid, stretchyht int
var current *allocation // for neighboring
func (s *stack) SetPadded(padded bool) {
s.padded = padded
}
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
return nil
return
}
x, y, width, height = s.container.bounds(d)
// -1) get this Stack's padding
xpadding := d.xpadding
ypadding := d.ypadding
if !s.padded {
xpadding = 0
ypadding = 0
}
// 0) inset the available rect by the needed padding
if s.orientation == horizontal {
width -= (len(s.controls) - 1) * d.xpadding
width -= (len(s.controls) - 1) * xpadding
} else {
height -= (len(s.controls) - 1) * d.ypadding
height -= (len(s.controls) - 1) * ypadding
}
// 1) get height and width of non-stretchy controls; figure out how much space is alloted to stretchy controls
stretchywid = width
@ -116,25 +143,14 @@ func (s *stack) allocate(x int, y int, width int, height int, d *sizing) (alloca
}
// 3) now actually place controls
for i, c := range s.controls {
as := c.allocate(x, y, s.width[i], s.height[i], d)
if s.orientation == horizontal { // no vertical neighbors
if current != nil { // connect first left to first right
current.neighbor = c
}
if len(as) != 0 {
current = as[0] // next left is first subwidget
} else {
current = nil // spaces don't have allocation data
}
}
allocations = append(allocations, as...)
c.resize(x, y, s.width[i], s.height[i], d)
if s.orientation == horizontal {
x += s.width[i] + d.xpadding
x += s.width[i] + xpadding
} else {
y += s.height[i] + d.ypadding
y += s.height[i] + ypadding
}
}
return allocations
return
}
// The preferred size of a Stack is the sum of the preferred sizes of non-stretchy controls + (the number of stretchy controls * the largest preferred size among all stretchy controls).
@ -152,10 +168,16 @@ func (s *stack) preferredSize(d *sizing) (width int, height int) {
if len(s.controls) == 0 { // no controls, so return emptiness
return 0, 0
}
xpadding := d.xpadding
ypadding := d.ypadding
if !s.padded {
xpadding = 0
ypadding = 0
}
if s.orientation == horizontal {
width = (len(s.controls) - 1) * d.xpadding
width = (len(s.controls) - 1) * xpadding
} else {
height = (len(s.controls) - 1) * d.ypadding
height = (len(s.controls) - 1) * ypadding
}
for i, c := range s.controls {
w, h := c.preferredSize(d)
@ -184,13 +206,15 @@ func (s *stack) preferredSize(d *sizing) (width int, height int) {
return
}
func (s *stack) commitResize(c *allocation, d *sizing) {
// this is to satisfy Control; nothing to do here
func (s *stack) nTabStops() int {
n := 0
for _, c := range s.controls {
n += c.nTabStops()
}
return n
}
func (s *stack) getAuxResizeInfo(d *sizing) {
// this is to satisfy Control; nothing to do here
}
// TODO the below needs to be changed
// Space returns a null Control intended for padding layouts with blank space.
// It appears to its owner as a Control of 0x0 size.

View File

@ -10,46 +10,44 @@ import (
import "C"
type tab struct {
_id C.id
tabs []*container
*controlSingleObject
tabs []*container
children []Control
chainresize func(x int, y int, width int, height int, d *sizing)
}
func newTab() Tab {
return &tab{
_id: C.newTab(),
t := &tab{
controlSingleObject: newControlSingleObject(C.newTab()),
}
t.fpreferredSize = t.xpreferredSize
t.chainresize = t.fresize
t.fresize = t.xresize
return t
}
func (t *tab) Append(name string, control Control) {
c := newContainer(control)
c := newContainer()
t.tabs = append(t.tabs, c)
control.setParent(c.parent())
t.children = append(t.children, control)
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
C.tabAppend(t._id, cname, c.id)
C.tabAppend(t.id, cname, c.id)
}
func (t *tab) id() C.id {
return t._id
}
func (t *tab) setParent(p *controlParent) {
basesetParent(t, p)
}
func (t *tab) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(t, x, y, width, height, d)
}
func (t *tab) preferredSize(d *sizing) (width, height int) {
s := C.tabPreferredSize(t._id)
func (t *tab) xpreferredSize(d *sizing) (width, height int) {
s := C.tabPreferredSize(t.id)
return int(s.width), int(s.height)
}
// no need to override Control.commitResize() as only prepared the tabbed control; its children will be resized when that one is resized (and NSTabView itself will call setFrame: for us)
func (t *tab) commitResize(a *allocation, d *sizing) {
basecommitResize(t, a, d)
}
func (t *tab) xresize(x int, y int, width int, height int, d *sizing) {
// first, chain up to change the GtkFrame and its child container
t.chainresize(x, y, width, height, d)
func (t *tab) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(t, d)
// now that the containers have the correct size, we can resize the children
for i, _ := range t.tabs {
a := t.tabs[i].allocation(false/*TODO*/)
t.children[i].resize(int(a.x), int(a.y), int(a.width), int(a.height), d)
}
}

View File

@ -12,30 +12,37 @@ import (
import "C"
type tab struct {
_widget *C.GtkWidget
*controlSingleWidget
container *C.GtkContainer
notebook *C.GtkNotebook
tabs []*container
children []Control
chainresize func(x int, y int, width int, height int, d *sizing)
}
func newTab() Tab {
widget := C.gtk_notebook_new()
t := &tab{
_widget: widget,
controlSingleWidget: newControlSingleWidget(widget),
container: (*C.GtkContainer)(unsafe.Pointer(widget)),
notebook: (*C.GtkNotebook)(unsafe.Pointer(widget)),
}
t.chainresize = t.fresize
t.fresize = t.xresize
// there are no scrolling arrows by default; add them in case there are too many tabs
C.gtk_notebook_set_scrollable(t.notebook, C.TRUE)
return t
}
func (t *tab) Append(name string, control Control) {
c := newContainer(control)
c := newContainer()
t.tabs = append(t.tabs, c)
// this calls gtk_container_add(), which, according to gregier in irc.gimp.net/#gtk+, acts just like gtk_notebook_append_page()
c.setParent(&controlParent{t.container})
control.setParent(c.parent())
t.children = append(t.children, control)
cname := togstr(name)
defer freegstr(cname)
C.gtk_notebook_set_tab_label_text(t.notebook,
@ -44,27 +51,13 @@ func (t *tab) Append(name string, control Control) {
cname)
}
func (t *tab) widget() *C.GtkWidget {
return t._widget
}
func (t *tab) xresize(x int, y int, width int, height int, d *sizing) {
// first, chain up to change the GtkFrame and its child container
t.chainresize(x, y, width, height, d)
func (t *tab) 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) {
return basepreferredSize(t, d)
}
// no need to override Control.commitResize() as only prepared the tabbed control; its children will be reallocated when that one is resized
func (t *tab) commitResize(a *allocation, d *sizing) {
basecommitResize(t, a, d)
}
func (t *tab) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(t, d)
// now that the containers have the correct size, we can resize the children
for i, _ := range t.tabs {
a := t.tabs[i].allocation(false/*TODO*/)
t.children[i].resize(int(a.x), int(a.y), int(a.width), int(a.height), d)
}
}

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.
We'll create a dummy window using the pre-existing Window window class for each tab page. This makes showing and hiding tabs a matter of showing and hiding one control.
We'll create a dummy window using the container window class for each tab page. This makes showing and hiding tabs a matter of showing and hiding one control.
*/
type tab struct {
_hwnd C.HWND
tabs []*container
*controlSingleHWND
tabs []*container
children []Control
chainresize func(x int, y int, width int, height int, d *sizing)
}
func newTab() Tab {
@ -25,22 +27,29 @@ func newTab() Tab {
C.TCS_TOOLTIPS|C.WS_TABSTOP,
0) // don't set WS_EX_CONTROLPARENT here; see uitask_windows.c
t := &tab{
_hwnd: hwnd,
controlSingleHWND: newControlSingleHWND(hwnd),
}
C.controlSetControlFont(t._hwnd)
C.setTabSubclass(t._hwnd, unsafe.Pointer(t))
t.fpreferredSize = t.xpreferredSize
t.chainresize = t.fresize
t.fresize = t.xresize
// count tabs as 1 tab stop; the actual number of tab stops varies
C.controlSetControlFont(t.hwnd)
C.setTabSubclass(t.hwnd, unsafe.Pointer(t))
return t
}
// TODO margined
func (t *tab) Append(name string, control Control) {
c := newContainer(control)
c.setParent(t._hwnd)
c := newContainer()
control.setParent(&controlParent{c.hwnd})
c.setParent(&controlParent{t.hwnd})
t.tabs = append(t.tabs, c)
t.children = append(t.children, control)
// initially hide tab 1..n controls; if we don't, they'll appear over other tabs, resulting in weird behavior
if len(t.tabs) != 1 {
t.tabs[len(t.tabs)-1].hide()
}
C.tabAppend(t._hwnd, toUTF16(name))
C.tabAppend(t.hwnd, toUTF16(name))
}
//export tabChanging
@ -61,27 +70,15 @@ func tabTabHasChildren(data unsafe.Pointer, which C.LRESULT) C.BOOL {
if len(t.tabs) == 0 { // currently no tabs
return C.FALSE
}
if t.tabs[int(which)].nchildren > 0 {
if t.children[int(which)].nTabStops() > 0 {
return C.TRUE
}
return C.FALSE
}
func (t *tab) hwnd() C.HWND {
return t._hwnd
}
func (t *tab) setParent(p *controlParent) {
basesetParent(t, p)
}
func (t *tab) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(t, x, y, width, height, d)
}
func (t *tab) preferredSize(d *sizing) (width, height int) {
for _, s := range t.tabs {
w, h := s.child.preferredSize(d)
func (t *tab) xpreferredSize(d *sizing) (width, height int) {
for _, c := range t.children {
w, h := c.preferredSize(d)
if width < w {
width = w
}
@ -89,30 +86,30 @@ func (t *tab) preferredSize(d *sizing) (width, height int) {
height = h
}
}
return width, height + int(C.tabGetTabHeight(t._hwnd))
return width, height + int(C.tabGetTabHeight(t.hwnd))
}
// a tab control contains other controls; size appropriately
func (t *tab) commitResize(c *allocation, d *sizing) {
func (t *tab) xresize(x int, y int, width int, height int, d *sizing) {
// first, chain up to the container base to keep the Z-order correct
t.chainresize(x, y, width, height, d)
// now resize the children
var r C.RECT
// figure out what the rect for each child is...
// the tab contents are children of the tab itself, so ignore c.x and c.y, which are relative to the window!
// the tab contents are children of the tab itself, so ignore x and y, which are relative to the window!
r.left = C.LONG(0)
r.top = C.LONG(0)
r.right = C.LONG(c.width)
r.bottom = C.LONG(c.height)
C.tabGetContentRect(t._hwnd, &r)
r.right = C.LONG(width)
r.bottom = C.LONG(height)
C.tabGetContentRect(t.hwnd, &r)
// and resize tabs
// don't resize just the current tab; resize all tabs!
for _, c := range t.tabs {
for i, _ := range t.tabs {
// because each widget is actually a child of the Window, the origin is the one we calculated above
c.move(&r)
t.tabs[i].resize(int(r.left), int(r.top), int(r.right - r.left), int(r.bottom - r.top), d)
// TODO get the actual client rect
t.children[i].resize(int(0), int(0), int(r.right - r.left), int(r.bottom - r.top), d)
}
// and now resize the tab control itself
basecommitResize(t, c, d)
}
func (t *tab) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(t, d)
}

View File

@ -14,8 +14,7 @@ import "C"
type table struct {
*tablebase
_id C.id
scroller *scroller
*scroller
images []C.id
selected *event
@ -24,13 +23,13 @@ type table struct {
func finishNewTable(b *tablebase, ty reflect.Type) Table {
id := C.newTable()
t := &table{
_id: id,
scroller: newScroller(id, true), // border on Table
tablebase: b,
selected: newEvent(),
}
t.fpreferredSize = t.xpreferredSize
// also sets the delegate
C.tableMakeDataSource(t._id, unsafe.Pointer(t))
C.tableMakeDataSource(t.id, unsafe.Pointer(t))
for i := 0; i < ty.NumField(); i++ {
cname := C.CString(ty.Field(i).Name)
coltype := C.colTypeText
@ -42,7 +41,7 @@ func finishNewTable(b *tablebase, ty reflect.Type) Table {
coltype = C.colTypeCheckbox
editable = true
}
C.tableAppendColumn(t._id, C.intptr_t(i), cname, C.int(coltype), toBOOL(editable))
C.tableAppendColumn(t.id, C.intptr_t(i), cname, C.int(coltype), toBOOL(editable))
C.free(unsafe.Pointer(cname)) // free now (not deferred) to conserve memory
}
return t
@ -56,7 +55,7 @@ func (t *table) Unlock() {
Do(func() {
t.RLock()
defer t.RUnlock()
C.tableUpdate(t._id)
C.tableUpdate(t.id)
})
}()
}
@ -68,13 +67,13 @@ func (t *table) LoadImageList(i ImageList) {
func (t *table) Selected() int {
t.RLock()
defer t.RUnlock()
return int(C.tableSelected(t._id))
return int(C.tableSelected(t.id))
}
func (t *table) Select(index int) {
t.RLock()
defer t.RUnlock()
C.tableSelect(t._id, C.intptr_t(index))
C.tableSelect(t.id, C.intptr_t(index))
}
func (t *table) OnSelected(f func()) {
@ -132,27 +131,7 @@ func tableSelectionChanged(data unsafe.Pointer) {
t.selected.fire()
}
func (t *table) id() C.id {
return t._id
}
func (t *table) setParent(p *controlParent) {
t.scroller.setParent(p)
}
func (t *table) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(t, x, y, width, height, d)
}
func (t *table) preferredSize(d *sizing) (width, height int) {
s := C.tablePreferredSize(t._id)
func (t *table) xpreferredSize(d *sizing) (width, height int) {
s := C.tablePreferredSize(t.id)
return int(s.width), int(s.height)
}
func (t *table) commitResize(c *allocation, d *sizing) {
t.scroller.commitResize(c, d)
}
func (t *table) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(t, d)
}

View File

@ -18,9 +18,8 @@ import "C"
type table struct {
*tablebase
_widget *C.GtkWidget
*scroller
treeview *C.GtkTreeView
scroller *scroller
model *C.goTableModel
modelgtk *C.GtkTreeModel
@ -48,7 +47,6 @@ func finishNewTable(b *tablebase, ty reflect.Type) Table {
t := &table{
scroller: newScroller(widget, true, true, false), // natively scrollable; has a border; no overlay
tablebase: b,
_widget: widget,
treeview: (*C.GtkTreeView)(unsafe.Pointer(widget)),
crtocol: make(map[*C.GtkCellRendererToggle]int),
selected: newEvent(),
@ -222,28 +220,3 @@ func tableSelectionChanged(sel *C.GtkTreeSelection, data C.gpointer) {
t := (*table)(unsafe.Pointer(data))
t.selected.fire()
}
func (t *table) widget() *C.GtkWidget {
return t._widget
}
func (t *table) setParent(p *controlParent) {
t.scroller.setParent(p)
}
func (t *table) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(t, x, y, width, height, d)
}
func (t *table) preferredSize(d *sizing) (width, height int) {
return basepreferredSize(t, d)
}
func (t *table) commitResize(c *allocation, d *sizing) {
t.scroller.commitResize(c, d)
}
func (t *table) getAuxResizeInfo(d *sizing) {
// a Label to the left of a Table should be vertically aligned to the top
d.shouldVAlignTop = true
}

View File

@ -13,7 +13,7 @@ import "C"
type table struct {
*tablebase
_hwnd C.HWND
*controlSingleHWND
noautosize bool
colcount C.int
hotrow C.int
@ -21,13 +21,15 @@ type table struct {
pushedrow C.int
pushedcol C.int
selected *event
chainresize func(x int, y int, width int, height int, d *sizing)
}
func finishNewTable(b *tablebase, ty reflect.Type) Table {
hwnd := C.newControl(C.xWC_LISTVIEW,
C.LVS_REPORT|C.LVS_OWNERDATA|C.LVS_NOSORTHEADER|C.LVS_SHOWSELALWAYS|C.LVS_SINGLESEL|C.WS_HSCROLL|C.WS_VSCROLL|C.WS_TABSTOP,
C.WS_EX_CLIENTEDGE) // WS_EX_CLIENTEDGE without WS_BORDER will show the canonical visual styles border (thanks to MindChild in irc.efnet.net/#winprog)
t := &table{
_hwnd: C.newControl(C.xWC_LISTVIEW,
C.LVS_REPORT|C.LVS_OWNERDATA|C.LVS_NOSORTHEADER|C.LVS_SHOWSELALWAYS|C.LVS_SINGLESEL|C.WS_HSCROLL|C.WS_VSCROLL|C.WS_TABSTOP,
C.WS_EX_CLIENTEDGE), // WS_EX_CLIENTEDGE without WS_BORDER will show the canonical visual styles border (thanks to MindChild in irc.efnet.net/#winprog)
controlSingleHWND: newControlSingleHWND(hwnd),
tablebase: b,
hotrow: -1,
hotcol: -1,
@ -35,14 +37,17 @@ func finishNewTable(b *tablebase, ty reflect.Type) Table {
pushedcol: -1,
selected: newEvent(),
}
C.setTableSubclass(t._hwnd, unsafe.Pointer(t))
t.fpreferredSize = t.xpreferredSize
t.chainresize = t.fresize
t.fresize = t.xresize
C.setTableSubclass(t.hwnd, unsafe.Pointer(t))
// LVS_EX_FULLROWSELECT gives us selection across the whole row, not just the leftmost column; this makes the list view work like on other platforms
// LVS_EX_SUBITEMIMAGES gives us images in subitems, which will be important when both images and checkboxes are added
C.tableAddExtendedStyles(t._hwnd, C.LVS_EX_FULLROWSELECT|C.LVS_EX_SUBITEMIMAGES)
C.tableAddExtendedStyles(t.hwnd, C.LVS_EX_FULLROWSELECT|C.LVS_EX_SUBITEMIMAGES)
// this must come after the subclass because it uses one of our private messages
C.SendMessageW(t._hwnd, C.msgTableMakeInitialCheckboxImageList, 0, 0)
C.SendMessageW(t.hwnd, C.msgTableMakeInitialCheckboxImageList, 0, 0)
for i := 0; i < ty.NumField(); i++ {
C.tableAppendColumn(t._hwnd, C.int(i), toUTF16(ty.Field(i).Name))
C.tableAppendColumn(t.hwnd, C.int(i), toUTF16(ty.Field(i).Name))
}
t.colcount = C.int(ty.NumField())
return t
@ -56,25 +61,25 @@ func (t *table) Unlock() {
Do(func() {
t.RLock()
defer t.RUnlock()
C.tableUpdate(t._hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
C.tableUpdate(t.hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
})
}()
}
func (t *table) LoadImageList(il ImageList) {
il.apply(t._hwnd, C.msgLoadImageList)
il.apply(t.hwnd, C.msgLoadImageList)
}
func (t *table) Selected() int {
t.RLock()
defer t.RUnlock()
return int(C.tableSelectedItem(t._hwnd))
return int(C.tableSelectedItem(t.hwnd))
}
func (t *table) Select(index int) {
t.RLock()
defer t.RUnlock()
C.tableSelectItem(t._hwnd, C.intptr_t(index))
C.tableSelectItem(t.hwnd, C.intptr_t(index))
}
func (t *table) OnSelected(f func()) {
@ -144,7 +149,7 @@ func (t *table) autoresize() {
t.RLock()
defer t.RUnlock()
if !t.noautosize {
C.tableAutosizeColumns(t._hwnd, t.colcount)
C.tableAutosizeColumns(t.hwnd, t.colcount)
}
}
@ -167,7 +172,7 @@ func tableSetHot(data unsafe.Pointer, row C.int, col C.int) {
t.hotrow = row
t.hotcol = col
if redraw {
C.tableUpdate(t._hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
C.tableUpdate(t.hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
}
}
@ -176,7 +181,7 @@ func tablePushed(data unsafe.Pointer, row C.int, col C.int) {
t := (*table)(data)
t.pushedrow = row
t.pushedcol = col
C.tableUpdate(t._hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
C.tableUpdate(t.hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
}
//export tableToggled
@ -211,18 +216,6 @@ func tableSelectionChanged(data unsafe.Pointer) {
t.selected.fire()
}
func (t *table) hwnd() C.HWND {
return t._hwnd
}
func (t *table) setParent(p *controlParent) {
basesetParent(t, p)
}
func (t *table) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(t, x, y, width, height, d)
}
const (
// from C++ Template 05 in http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx as this is the best I can do for now
// there IS a message LVM_APPROXIMATEVIEWRECT that can do calculations, but it doesn't seem to work right when asked to base its calculations on the current width/height on Windows and wine...
@ -230,17 +223,13 @@ const (
tableHeight = 50
)
func (t *table) preferredSize(d *sizing) (width, height int) {
func (t *table) xpreferredSize(d *sizing) (width, height int) {
return fromdlgunitsX(tableWidth, d), fromdlgunitsY(tableHeight, d)
}
func (t *table) commitResize(a *allocation, d *sizing) {
basecommitResize(t, a, d)
func (t *table) xresize(x int, y int, width int, height int, d *sizing) {
t.chainresize(x, y, width, height, d)
t.RLock()
defer t.RUnlock()
t.autoresize()
}
func (t *table) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(t, d)
}

View File

@ -10,17 +10,20 @@ import (
import "C"
type textfield struct {
_id C.id
*controlSingleObject
changed *event
invalid C.id
chainpreferredSize func(d *sizing) (int, int)
}
func finishNewTextField(id C.id) *textfield {
t := &textfield{
_id: id,
controlSingleObject: newControlSingleObject(id),
changed: newEvent(),
}
C.textfieldSetDelegate(t._id, unsafe.Pointer(t))
C.textfieldSetDelegate(t.id, unsafe.Pointer(t))
t.chainpreferredSize = t.fpreferredSize
t.fpreferredSize = t.xpreferredSize
return t
}
@ -33,13 +36,13 @@ func newPasswordField() *textfield {
}
func (t *textfield) Text() string {
return C.GoString(C.textfieldText(t._id))
return C.GoString(C.textfieldText(t.id))
}
func (t *textfield) SetText(text string) {
ctext := C.CString(text)
defer C.free(unsafe.Pointer(ctext))
C.textfieldSetText(t._id, ctext)
C.textfieldSetText(t.id, ctext)
}
func (t *textfield) OnChanged(f func()) {
@ -56,7 +59,7 @@ func (t *textfield) Invalid(reason string) {
}
creason := C.CString(reason)
defer C.free(unsafe.Pointer(creason))
t.invalid = C.textfieldOpenInvalidPopover(t._id, creason)
t.invalid = C.textfieldOpenInvalidPopover(t.id, creason)
}
//export textfieldChanged
@ -65,28 +68,8 @@ func textfieldChanged(data unsafe.Pointer) {
t.changed.fire()
}
func (t *textfield) id() C.id {
return t._id
}
func (t *textfield) setParent(p *controlParent) {
basesetParent(t, p)
}
func (t *textfield) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(t, x, y, width, height, d)
}
func (t *textfield) preferredSize(d *sizing) (width, height int) {
_, height = basepreferredSize(t, d)
func (t *textfield) xpreferredSize(d *sizing) (width, height int) {
_, height = t.chainpreferredSize(d)
// the returned width is based on the contents; use this instead
return C.textfieldWidth, height
}
func (t *textfield) commitResize(a *allocation, d *sizing) {
basecommitResize(t, a, d)
}
func (t *textfield) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(t, d)
}

View File

@ -18,7 +18,7 @@ import (
import "C"
type textfield struct {
_widget *C.GtkWidget
*controlSingleWidget
entry *C.GtkEntry
changed *event
}
@ -26,12 +26,12 @@ type textfield struct {
func startNewTextField() *textfield {
widget := C.gtk_entry_new()
t := &textfield{
_widget: widget,
controlSingleWidget: newControlSingleWidget(widget),
entry: (*C.GtkEntry)(unsafe.Pointer(widget)),
changed: newEvent(),
}
g_signal_connect(
C.gpointer(unsafe.Pointer(t._widget)),
C.gpointer(unsafe.Pointer(t.widget)),
"changed",
C.GCallback(C.textfieldChanged),
C.gpointer(unsafe.Pointer(t)))
@ -71,7 +71,7 @@ func (t *textfield) Invalid(reason string) {
creason := togstr(reason)
defer freegstr(creason)
C.gtk_entry_set_icon_tooltip_text(t.entry, C.GTK_ENTRY_ICON_SECONDARY, creason)
C.gtk_widget_error_bell(t._widget)
C.gtk_widget_error_bell(t.widget)
}
//export textfieldChanged
@ -79,27 +79,3 @@ func textfieldChanged(editable *C.GtkEditable, data C.gpointer) {
t := (*textfield)(unsafe.Pointer(data))
t.changed.fire()
}
func (t *textfield) widget() *C.GtkWidget {
return t._widget
}
func (t *textfield) setParent(p *controlParent) {
basesetParent(t, p)
}
func (t *textfield) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(t, x, y, width, height, d)
}
func (t *textfield) preferredSize(d *sizing) (width, height int) {
return basepreferredSize(t, d)
}
func (t *textfield) commitResize(a *allocation, d *sizing) {
basecommitResize(t, a, d)
}
func (t *textfield) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(t, d)
}

View File

@ -10,8 +10,7 @@ import (
import "C"
type textfield struct {
_hwnd C.HWND
_textlen C.LONG
*controlSingleHWNDWithText
changed *event
}
@ -22,11 +21,12 @@ func startNewTextField(style C.DWORD) *textfield {
style|C.textfieldStyle,
C.textfieldExtStyle) // WS_EX_CLIENTEDGE without WS_BORDER will show the canonical visual styles border (thanks to MindChild in irc.efnet.net/#winprog)
t := &textfield{
_hwnd: hwnd,
controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd),
changed: newEvent(),
}
C.controlSetControlFont(t._hwnd)
C.setTextFieldSubclass(t._hwnd, unsafe.Pointer(t))
t.fpreferredSize = t.xpreferredSize
C.controlSetControlFont(t.hwnd)
C.setTextFieldSubclass(t.hwnd, unsafe.Pointer(t))
return t
}
@ -39,11 +39,11 @@ func newPasswordField() *textfield {
}
func (t *textfield) Text() string {
return baseText(t)
return t.text()
}
func (t *textfield) SetText(text string) {
baseSetText(t, text)
t.setText(text)
}
func (t *textfield) OnChanged(f func()) {
@ -52,10 +52,10 @@ func (t *textfield) OnChanged(f func()) {
func (t *textfield) Invalid(reason string) {
if reason == "" {
C.textfieldHideInvalidBalloonTip(t._hwnd)
C.textfieldHideInvalidBalloonTip(t.hwnd)
return
}
C.textfieldSetAndShowInvalidBalloonTip(t._hwnd, toUTF16(reason))
C.textfieldSetAndShowInvalidBalloonTip(t.hwnd, toUTF16(reason))
}
//export textfieldChanged
@ -64,40 +64,12 @@ func textfieldChanged(data unsafe.Pointer) {
t.changed.fire()
}
func (t *textfield) hwnd() C.HWND {
return t._hwnd
}
func (t *textfield) textlen() C.LONG {
return t._textlen
}
func (t *textfield) settextlen(len C.LONG) {
t._textlen = len
}
func (t *textfield) setParent(p *controlParent) {
basesetParent(t, p)
}
func (t *textfield) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
return baseallocate(t, x, y, width, height, d)
}
const (
// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
textfieldWidth = 107 // this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary
textfieldHeight = 14
)
func (t *textfield) preferredSize(d *sizing) (width, height int) {
func (t *textfield) xpreferredSize(d *sizing) (width, height int) {
return fromdlgunitsX(textfieldWidth, d), fromdlgunitsY(textfieldHeight, d)
}
func (t *textfield) commitResize(a *allocation, d *sizing) {
basecommitResize(t, a, d)
}
func (t *textfield) getAuxResizeInfo(d *sizing) {
basegetAuxResizeInfo(t, d)
}

View File

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

View File

@ -26,6 +26,11 @@ type Window interface {
// If this handler returns false, the Window is not closed.
OnClosing(func() bool)
// Margined and SetMargined get and set whether the contents of the Window have a margin around them.
// The size of the margin is platform-dependent.
Margined() bool
SetMargined(margined bool)
windowDialog
}

View File

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

View File

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

View File

@ -10,6 +10,7 @@ import (
// #include "gtk_unix.h"
// extern gboolean windowClosing(GtkWidget *, GdkEvent *, gpointer);
// extern void windowResized(GtkWidget *, GdkRectangle *, gpointer);
import "C"
type window struct {
@ -22,7 +23,10 @@ type window struct {
closing *event
*container
child Control
container *container
margined bool
}
func newWindow(title string, width int, height int, control Control) *window {
@ -35,6 +39,7 @@ func newWindow(title string, width int, height int, control Control) *window {
bin: (*C.GtkBin)(unsafe.Pointer(widget)),
window: (*C.GtkWindow)(unsafe.Pointer(widget)),
closing: newEvent(),
child: control,
}
C.gtk_window_set_title(w.window, ctitle)
g_signal_connect(
@ -43,8 +48,15 @@ func newWindow(title string, width int, height int, control Control) *window {
C.GCallback(C.windowClosing),
C.gpointer(unsafe.Pointer(w)))
C.gtk_window_resize(w.window, C.gint(width), C.gint(height))
w.container = newContainer(control)
w.container = newContainer()
w.child.setParent(w.container.parent())
w.container.setParent(&controlParent{w.wc})
// notice that we connect this to the container
g_signal_connect_after( // so we get it after the child container has been allocated
C.gpointer(unsafe.Pointer(w.container.widget)),
"size-allocate",
C.GCallback(C.windowResized),
C.gpointer(unsafe.Pointer(w)))
// for dialogs; otherwise, they will be modal to all windows, not just this one
w.group = C.gtk_window_group_new()
C.gtk_window_group_add_window(w.group, w.window)
@ -77,6 +89,14 @@ func (w *window) OnClosing(e func() bool) {
w.closing.setbool(e)
}
func (w *window) Margined() bool {
return w.margined
}
func (w *window) SetMargined(margined bool) {
w.margined = margined
}
//export windowClosing
func windowClosing(wid *C.GtkWidget, e *C.GdkEvent, data C.gpointer) C.gboolean {
w := (*window)(unsafe.Pointer(data))
@ -86,3 +106,11 @@ func windowClosing(wid *C.GtkWidget, e *C.GdkEvent, data C.gpointer) C.gboolean
}
return C.GDK_EVENT_STOP // keeps window alive
}
//export windowResized
func windowResized(wid *C.GtkWidget, r *C.GdkRectangle, data C.gpointer) {
w := (*window)(unsafe.Pointer(data))
a := w.container.allocation(w.margined)
d := w.beginResize()
w.child.resize(int(a.x), int(a.y), int(a.width), int(a.height), d)
}

View File

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

View File

@ -17,7 +17,8 @@ type window struct {
closing *event
*container
child Control
margined bool
}
func makeWindowWindowClass() error {
@ -32,19 +33,15 @@ func makeWindowWindowClass() error {
func newWindow(title string, width int, height int, control Control) *window {
w := &window{
// hwnd set in WM_CREATE handler
closing: newEvent(),
container: newContainer(control),
}
hwnd := C.newWindow(toUTF16(title), C.int(width), C.int(height), unsafe.Pointer(w))
if hwnd != w.hwnd {
panic(fmt.Errorf("inconsistency: hwnd returned by CreateWindowEx() (%p) and hwnd stored in Window (%p) differ", hwnd, w.hwnd))
child: control,
}
w.hwnd = C.newWindow(toUTF16(title), C.int(width), C.int(height), unsafe.Pointer(w))
hresult := C.EnableThemeDialogTexture(w.hwnd, C.ETDT_ENABLE|C.ETDT_USETABTEXTURE)
if hresult != C.S_OK {
panic(fmt.Errorf("error setting tab background texture on Window; HRESULT: 0x%X", hresult))
}
w.container.setParent(w.hwnd)
w.child.setParent(&controlParent{w.hwnd})
return w
}
@ -78,17 +75,22 @@ func (w *window) OnClosing(e func() bool) {
w.closing.setbool(e)
}
//export storeWindowHWND
func storeWindowHWND(data unsafe.Pointer, hwnd C.HWND) {
w := (*window)(data)
w.hwnd = hwnd
func (w *window) Margined() bool {
return w.margined
}
func (w *window) SetMargined(margined bool) {
w.margined = margined
}
//export windowResize
func windowResize(data unsafe.Pointer, r *C.RECT) {
w := (*window)(data)
// the origin of the window's content area is always (0, 0), but let's use the values from the RECT just to be safe
w.container.move(r)
d := w.beginResize()
if w.margined {
marginRectDLU(r, marginDialogUnits, marginDialogUnits, marginDialogUnits, marginDialogUnits, d)
}
w.child.resize(int(r.left), int (r.top), int(r.right - r.left), int(r.bottom - r.top), d)
}
//export windowClosing

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.all, nil, South, true, Center, false, LeftTop, 1, 1)
r.grid = grid
r.grid.SetPadded(*spaced)
return r
}

View File

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