Merged new container/sizing stuff.
This commit is contained in:
parent
8c8b642adb
commit
62048303a3
|
@ -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)
|
||||
}
|
||||
|
|
38
area_unix.go
38
area_unix.go
|
@ -34,9 +34,8 @@ import "C"
|
|||
type area struct {
|
||||
*areabase
|
||||
|
||||
_widget *C.GtkWidget
|
||||
*scroller
|
||||
drawingarea *C.GtkDrawingArea
|
||||
scroller *scroller
|
||||
|
||||
clickCounter *clickCounter
|
||||
|
||||
|
@ -59,7 +58,6 @@ func newArea(ab *areabase) Area {
|
|||
textfieldw := C.gtk_entry_new()
|
||||
a := &area{
|
||||
areabase: ab,
|
||||
_widget: widget,
|
||||
drawingarea: (*C.GtkDrawingArea)(unsafe.Pointer(widget)),
|
||||
scroller: newScroller(widget, false, false, true), // not natively scrollable; no border; have an overlay for OpenTextFieldAt()
|
||||
clickCounter: new(clickCounter),
|
||||
|
@ -67,6 +65,7 @@ func newArea(ab *areabase) Area {
|
|||
textfield: (*C.GtkEntry)(unsafe.Pointer(textfieldw)),
|
||||
textfielddone: newEvent(),
|
||||
}
|
||||
a.fpreferredSize = a.xpreferredSize
|
||||
for _, c := range areaCallbacks {
|
||||
g_signal_connect(
|
||||
C.gpointer(unsafe.Pointer(a.drawingarea)),
|
||||
|
@ -75,9 +74,9 @@ func newArea(ab *areabase) Area {
|
|||
C.gpointer(unsafe.Pointer(a)))
|
||||
}
|
||||
a.SetSize(a.width, a.height)
|
||||
C.gtk_overlay_add_overlay(a.scroller.overlay, a.textfieldw)
|
||||
C.gtk_overlay_add_overlay(a.scroller.overlayoverlay, a.textfieldw)
|
||||
g_signal_connect(
|
||||
C.gpointer(unsafe.Pointer(a.scroller.overlay)),
|
||||
C.gpointer(unsafe.Pointer(a.scroller.overlayoverlay)),
|
||||
"get-child-position",
|
||||
area_get_child_position_callback,
|
||||
C.gpointer(unsafe.Pointer(a)))
|
||||
|
@ -103,7 +102,7 @@ func newArea(ab *areabase) Area {
|
|||
func (a *area) SetSize(width, height int) {
|
||||
a.width = width
|
||||
a.height = height
|
||||
C.gtk_widget_set_size_request(a._widget, C.gint(a.width), C.gint(a.height))
|
||||
C.gtk_widget_set_size_request(a.widget, C.gint(a.width), C.gint(a.height))
|
||||
}
|
||||
|
||||
func (a *area) Repaint(r image.Rectangle) {
|
||||
|
@ -111,11 +110,11 @@ func (a *area) Repaint(r image.Rectangle) {
|
|||
if r.Empty() {
|
||||
return
|
||||
}
|
||||
C.gtk_widget_queue_draw_area(a._widget, C.gint(r.Min.X), C.gint(r.Min.Y), C.gint(r.Dx()), C.gint(r.Dy()))
|
||||
C.gtk_widget_queue_draw_area(a.widget, C.gint(r.Min.X), C.gint(r.Min.Y), C.gint(r.Dx()), C.gint(r.Dy()))
|
||||
}
|
||||
|
||||
func (a *area) RepaintAll() {
|
||||
C.gtk_widget_queue_draw(a._widget)
|
||||
C.gtk_widget_queue_draw(a.widget)
|
||||
}
|
||||
|
||||
func (a *area) OpenTextFieldAt(x, y int) {
|
||||
|
@ -492,28 +491,7 @@ var modonlykeys = map[C.guint]Modifiers{
|
|||
C.GDK_KEY_Super_R: Super,
|
||||
}
|
||||
|
||||
func (a *area) widget() *C.GtkWidget {
|
||||
return a._widget
|
||||
}
|
||||
|
||||
func (a *area) setParent(p *controlParent) {
|
||||
a.scroller.setParent(p)
|
||||
}
|
||||
|
||||
func (a *area) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
|
||||
return baseallocate(a, x, y, width, height, d)
|
||||
}
|
||||
|
||||
func (a *area) preferredSize(d *sizing) (width, height int) {
|
||||
func (a *area) xpreferredSize(d *sizing) (width, height int) {
|
||||
// the preferred size of an Area is its size
|
||||
return a.width, a.height
|
||||
}
|
||||
|
||||
func (a *area) commitResize(c *allocation, d *sizing) {
|
||||
a.scroller.commitResize(c, d)
|
||||
}
|
||||
|
||||
func (a *area) getAuxResizeInfo(d *sizing) {
|
||||
// a Label to the left of an Area should be vertically aligned to the top
|
||||
d.shouldVAlignTop = true
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
32
container.go
32
container.go
|
@ -2,41 +2,14 @@
|
|||
|
||||
package ui
|
||||
|
||||
type allocation struct {
|
||||
x int
|
||||
y int
|
||||
width int
|
||||
height int
|
||||
this Control
|
||||
neighbor Control
|
||||
}
|
||||
|
||||
type sizingbase struct {
|
||||
xmargin int
|
||||
ymargintop int
|
||||
ymarginbottom int
|
||||
xpadding int
|
||||
ypadding int
|
||||
}
|
||||
|
||||
type controlSizing interface {
|
||||
allocate(x int, y int, width int, height int, d *sizing) []*allocation
|
||||
preferredSize(*sizing) (int, int)
|
||||
commitResize(*allocation, *sizing)
|
||||
getAuxResizeInfo(*sizing)
|
||||
}
|
||||
|
||||
// A container hosts a Control and resizes that Control based on changes in size to the parent Window.
|
||||
// container is used by Window, Tab, and Group to contain and control their respective Controls.
|
||||
// Tab and Group use containers for their content; as such, their commitResize() functions should only change the size of the Tab and Group themselves, and have their containers do the real work.
|
||||
// All containers must embed containerbase.
|
||||
type containerbase struct {
|
||||
child Control
|
||||
}
|
||||
|
||||
// set to true to apply spacing to all windows
|
||||
var spaced bool = false
|
||||
// The container type, which is defined per-platform, is an internal Control that is only used to house other Controls from the underlying UI toolkit's point of view
|
||||
|
||||
/* TODO
|
||||
func (c *container) resize(x, y, width, height int) {
|
||||
if c.child == nil { // no children; nothing to do
|
||||
return
|
||||
|
@ -50,3 +23,4 @@ func (c *container) resize(x, y, width, height int) {
|
|||
allocations[i].this.commitResize(allocations[i], d)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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+
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
36
control.go
36
control.go
|
@ -5,16 +5,32 @@ package ui
|
|||
// Control represents a control.
|
||||
type Control interface {
|
||||
setParent(p *controlParent) // controlParent defined per-platform
|
||||
controlSizing
|
||||
preferredSize(d *sizing) (width, height int)
|
||||
resize(x int, y int, width int, height int, d *sizing)
|
||||
nTabStops() int // used by the Windows backend
|
||||
}
|
||||
|
||||
// this is the same across all platforms
|
||||
func baseallocate(c Control, x int, y int, width int, height int, d *sizing) []*allocation {
|
||||
return []*allocation{&allocation{
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height,
|
||||
this: c,
|
||||
}}
|
||||
type controlbase struct {
|
||||
fsetParent func(p *controlParent)
|
||||
fpreferredSize func(d *sizing) (width, height int)
|
||||
fresize func(x int, y int, width int, height int, d *sizing)
|
||||
fnTabStops func() int
|
||||
}
|
||||
|
||||
// children should not use the same name as these, otherwise weird things will happen
|
||||
|
||||
func (c *controlbase) setParent(p *controlParent) {
|
||||
c.fsetParent(p)
|
||||
}
|
||||
|
||||
func (c *controlbase) preferredSize(d *sizing) (width, height int) {
|
||||
return c.fpreferredSize(d)
|
||||
}
|
||||
|
||||
func (c *controlbase) resize(x int, y int, width int, height int, d *sizing) {
|
||||
c.fresize(x, y, width, height, d)
|
||||
}
|
||||
|
||||
func (c *controlbase) nTabStops() int {
|
||||
return c.fnTabStops()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
121
control_unix.go
121
control_unix.go
|
@ -11,24 +11,34 @@ import (
|
|||
// #include "gtk_unix.h"
|
||||
import "C"
|
||||
|
||||
// all Controls that call base methods must be this
|
||||
type controlPrivate interface {
|
||||
widget() *C.GtkWidget
|
||||
Control
|
||||
}
|
||||
|
||||
type controlParent struct {
|
||||
c *C.GtkContainer
|
||||
}
|
||||
|
||||
func basesetParent(c controlPrivate, p *controlParent) {
|
||||
widget := c.widget() // avoid multiple interface lookups
|
||||
C.gtk_container_add(p.c, widget)
|
||||
// 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)
|
||||
}
|
||||
|
|
|
@ -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
94
grid.go
|
@ -27,6 +27,11 @@ type Grid interface {
|
|||
// The effect of overlapping spanning Controls is also undefined.
|
||||
// Add panics if either xspan or yspan are zero or negative.
|
||||
Add(control Control, nextTo Control, side Side, xexpand bool, xalign Align, yexpand bool, yalign Align, xspan int, yspan int)
|
||||
|
||||
// Padded and SetPadded get and set whether the controls of the Grid have padding between them.
|
||||
// The size of the padding is platform-dependent.
|
||||
Padded() bool
|
||||
SetPadded(padded bool)
|
||||
}
|
||||
|
||||
// Align represents the alignment of a Control in its cell of a Grid.
|
||||
|
@ -54,7 +59,8 @@ type grid struct {
|
|||
controls []gridCell
|
||||
indexof map[Control]int
|
||||
prev int
|
||||
parent *controlParent
|
||||
container *container
|
||||
padded bool
|
||||
|
||||
xmax int
|
||||
ymax int
|
||||
|
@ -84,6 +90,7 @@ type gridCell struct {
|
|||
func NewGrid() Grid {
|
||||
return &grid{
|
||||
indexof: map[Control]int{},
|
||||
container: newContainer(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,9 +136,7 @@ func (g *grid) Add(control Control, nextTo Control, side Side, xexpand bool, xal
|
|||
xspan: xspan,
|
||||
yspan: yspan,
|
||||
}
|
||||
if g.parent != nil {
|
||||
control.setParent(g.parent)
|
||||
}
|
||||
control.setParent(g.container.parent())
|
||||
// if this is the first control, just add it in directly
|
||||
if len(g.controls) != 0 {
|
||||
next := g.prev
|
||||
|
@ -161,11 +166,16 @@ func (g *grid) Add(control Control, nextTo Control, side Side, xexpand bool, xal
|
|||
g.reorigin()
|
||||
}
|
||||
|
||||
func (g *grid) Padded() bool {
|
||||
return g.padded
|
||||
}
|
||||
|
||||
func (g *grid) SetPadded(padded bool) {
|
||||
g.padded = padded
|
||||
}
|
||||
|
||||
func (g *grid) setParent(p *controlParent) {
|
||||
g.parent = p
|
||||
for i := range g.controls {
|
||||
g.controls[i].control.setParent(g.parent)
|
||||
}
|
||||
g.container.setParent(p)
|
||||
}
|
||||
|
||||
// builds the topological cell grid; also makes colwidths and rowheights
|
||||
|
@ -187,15 +197,27 @@ func (g *grid) mkgrid() (gg [][]int, colwidths []int, rowheights []int) {
|
|||
return gg, make([]int, g.xmax), make([]int, g.ymax)
|
||||
}
|
||||
|
||||
func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocations []*allocation) {
|
||||
func (g *grid) resize(x int, y int, width int, height int, d *sizing) {
|
||||
g.container.resize(x, y, width, height, d)
|
||||
|
||||
if len(g.controls) == 0 {
|
||||
// nothing to do
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
x, y, width, height = g.container.bounds(d)
|
||||
|
||||
// -2) get this Grid's padding
|
||||
xpadding := d.xpadding
|
||||
ypadding := d.ypadding
|
||||
if !g.padded {
|
||||
xpadding = 0
|
||||
ypadding = 0
|
||||
}
|
||||
|
||||
// -1) discount padding from width/height
|
||||
width -= (g.xmax - 1) * d.xpadding
|
||||
height -= (g.ymax - 1) * d.ypadding
|
||||
width -= (g.xmax - 1) * xpadding
|
||||
height -= (g.ymax - 1) * ypadding
|
||||
|
||||
// 0) build necessary data structures
|
||||
gg, colwidths, rowheights := g.mkgrid()
|
||||
|
@ -317,11 +339,11 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
|
|||
if i != prev {
|
||||
g.controls[i].finalx = curx
|
||||
} else {
|
||||
g.controls[i].finalwidth += d.xpadding
|
||||
g.controls[i].finalwidth += xpadding
|
||||
}
|
||||
g.controls[i].finalwidth += colwidths[x]
|
||||
}
|
||||
curx += colwidths[x] + d.xpadding
|
||||
curx += colwidths[x] + xpadding
|
||||
prev = i
|
||||
}
|
||||
}
|
||||
|
@ -334,11 +356,11 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
|
|||
if i != prev {
|
||||
g.controls[i].finaly = cury
|
||||
} else {
|
||||
g.controls[i].finalheight += d.ypadding
|
||||
g.controls[i].finalheight += ypadding
|
||||
}
|
||||
g.controls[i].finalheight += rowheights[y]
|
||||
}
|
||||
cury += rowheights[y] + d.ypadding
|
||||
cury += rowheights[y] + ypadding
|
||||
prev = i
|
||||
}
|
||||
}
|
||||
|
@ -367,29 +389,17 @@ func (g *grid) allocate(x int, y int, width int, height int, d *sizing) (allocat
|
|||
}
|
||||
|
||||
// 8) and FINALLY we draw
|
||||
var current *allocation
|
||||
|
||||
for _, ycol := range gg {
|
||||
current = nil
|
||||
for _, i := range ycol {
|
||||
if i != -1 { // treat empty cells like spaces
|
||||
as := g.controls[i].control.allocate(
|
||||
g.controls[i].control.resize(
|
||||
g.controls[i].finalx+x, g.controls[i].finaly+y,
|
||||
g.controls[i].finalwidth, g.controls[i].finalheight, d)
|
||||
if current != nil { // connect first left to first right
|
||||
current.neighbor = g.controls[i].control
|
||||
}
|
||||
if len(as) != 0 {
|
||||
current = as[0] // next left is first subwidget
|
||||
} else {
|
||||
current = nil // spaces don't have allocation data
|
||||
}
|
||||
allocations = append(allocations, as...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allocations
|
||||
return
|
||||
}
|
||||
|
||||
func (g *grid) preferredSize(d *sizing) (width, height int) {
|
||||
|
@ -398,6 +408,14 @@ func (g *grid) preferredSize(d *sizing) (width, height int) {
|
|||
return 0, 0
|
||||
}
|
||||
|
||||
// -1) get this Grid's padding
|
||||
xpadding := d.xpadding
|
||||
ypadding := d.ypadding
|
||||
if !g.padded {
|
||||
xpadding = 0
|
||||
ypadding = 0
|
||||
}
|
||||
|
||||
// 0) build necessary data structures
|
||||
gg, colwidths, rowheights := g.mkgrid()
|
||||
|
||||
|
@ -434,14 +452,14 @@ func (g *grid) preferredSize(d *sizing) (width, height int) {
|
|||
}
|
||||
|
||||
// and that's it; just account for padding
|
||||
return colwidth + (g.xmax-1)*d.xpadding,
|
||||
rowheight + (g.ymax-1)*d.ypadding
|
||||
return colwidth + (g.xmax-1) * xpadding,
|
||||
rowheight + (g.ymax-1) * ypadding
|
||||
}
|
||||
|
||||
func (g *grid) commitResize(a *allocation, d *sizing) {
|
||||
// do nothing; needed to satisfy Control
|
||||
}
|
||||
|
||||
func (g *grid) getAuxResizeInfo(d *sizing) {
|
||||
// do nothing; needed to satisfy Control
|
||||
func (g *grid) nTabStops() int {
|
||||
n := 0
|
||||
for _, c := range g.controls {
|
||||
n += c.control.nTabStops()
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
102
simplegrid.go
102
simplegrid.go
|
@ -14,8 +14,6 @@ import (
|
|||
// One Control can be marked as "stretchy": when the Window containing the SimpleGrid is resized, the cell containing that Control resizes to take any remaining space; its row and column are adjusted accordingly (so other filling controls in the same row and column will fill to the new height and width, respectively).
|
||||
// A stretchy Control implicitly fills its cell.
|
||||
// All cooridnates in a SimpleGrid are given in (row,column) form with (0,0) being the top-left cell.
|
||||
//
|
||||
// As a special rule, to ensure proper appearance, non-standalone Labels are automatically made filling.
|
||||
type SimpleGrid interface {
|
||||
Control
|
||||
|
||||
|
@ -28,6 +26,11 @@ type SimpleGrid interface {
|
|||
// Only one control can be stretchy per SimpleGrid; calling SetStretchy multiple times merely changes which control is stretchy (preserving the previous filling value).
|
||||
// It panics if the given coordinate is invalid.
|
||||
SetStretchy(row int, column int)
|
||||
|
||||
// Padded and SetPadded get and set whether the controls of the SimpleGrid have padding between them.
|
||||
// The size of the padding is platform-dependent.
|
||||
Padded() bool
|
||||
SetPadded(padded bool)
|
||||
}
|
||||
|
||||
type simpleGrid struct {
|
||||
|
@ -37,6 +40,8 @@ type simpleGrid struct {
|
|||
stretchyfill bool
|
||||
widths, heights [][]int // caches to avoid reallocating each time
|
||||
rowheights, colwidths []int
|
||||
container *container
|
||||
padded bool
|
||||
}
|
||||
|
||||
// NewSimpleGrid creates a new SimpleGrid with the given Controls.
|
||||
|
@ -64,13 +69,10 @@ func NewSimpleGrid(nPerRow int, controls ...Control) SimpleGrid {
|
|||
ch[row] = make([]int, nPerRow)
|
||||
for x := 0; x < nPerRow; x++ {
|
||||
cc[row][x] = controls[i]
|
||||
if l, ok := controls[i].(Label); ok && !l.isStandalone() {
|
||||
cf[row][x] = true
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
return &simpleGrid{
|
||||
g := &simpleGrid{
|
||||
controls: cc,
|
||||
filling: cf,
|
||||
stretchyrow: -1,
|
||||
|
@ -79,7 +81,15 @@ func NewSimpleGrid(nPerRow int, controls ...Control) SimpleGrid {
|
|||
heights: ch,
|
||||
rowheights: make([]int, nRows),
|
||||
colwidths: make([]int, nPerRow),
|
||||
container: newContainer(),
|
||||
}
|
||||
p := g.container.parent()
|
||||
for _, cc := range g.controls {
|
||||
for _, c := range cc {
|
||||
c.setParent(p)
|
||||
}
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *simpleGrid) SetFilling(row int, column int) {
|
||||
|
@ -102,15 +112,19 @@ func (g *simpleGrid) SetStretchy(row int, column int) {
|
|||
g.filling[g.stretchyrow][g.stretchycol] = true
|
||||
}
|
||||
|
||||
func (g *simpleGrid) 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
|
||||
}
|
90
stack.go
90
stack.go
|
@ -24,6 +24,11 @@ type Stack interface {
|
|||
// SetStretchy marks a control in a Stack as stretchy.
|
||||
// It panics if index is out of range.
|
||||
SetStretchy(index int)
|
||||
|
||||
// Padded and SetPadded get and set whether the controls of the Stack have padding between them.
|
||||
// The size of the padding is platform-dependent.
|
||||
Padded() bool
|
||||
SetPadded(padded bool)
|
||||
}
|
||||
|
||||
type stack struct {
|
||||
|
@ -31,16 +36,24 @@ type stack struct {
|
|||
controls []Control
|
||||
stretchy []bool
|
||||
width, height []int // caches to avoid reallocating these each time
|
||||
container *container
|
||||
padded bool
|
||||
}
|
||||
|
||||
func newStack(o orientation, controls ...Control) Stack {
|
||||
return &stack{
|
||||
s := &stack{
|
||||
orientation: o,
|
||||
controls: controls,
|
||||
stretchy: make([]bool, len(controls)),
|
||||
width: make([]int, len(controls)),
|
||||
height: make([]int, len(controls)),
|
||||
container: newContainer(),
|
||||
}
|
||||
p := s.container.parent()
|
||||
for _, c := range s.controls {
|
||||
c.setParent(p)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// NewHorizontalStack creates a new Stack that arranges the given Controls horizontally.
|
||||
|
@ -60,24 +73,38 @@ func (s *stack) SetStretchy(index int) {
|
|||
s.stretchy[index] = true
|
||||
}
|
||||
|
||||
func (s *stack) 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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
43
tab_unix.go
43
tab_unix.go
|
@ -12,30 +12,37 @@ import (
|
|||
import "C"
|
||||
|
||||
type tab struct {
|
||||
_widget *C.GtkWidget
|
||||
*controlSingleWidget
|
||||
container *C.GtkContainer
|
||||
notebook *C.GtkNotebook
|
||||
|
||||
tabs []*container
|
||||
children []Control
|
||||
|
||||
chainresize func(x int, y int, width int, height int, d *sizing)
|
||||
}
|
||||
|
||||
func newTab() Tab {
|
||||
widget := C.gtk_notebook_new()
|
||||
t := &tab{
|
||||
_widget: widget,
|
||||
controlSingleWidget: newControlSingleWidget(widget),
|
||||
container: (*C.GtkContainer)(unsafe.Pointer(widget)),
|
||||
notebook: (*C.GtkNotebook)(unsafe.Pointer(widget)),
|
||||
}
|
||||
t.chainresize = t.fresize
|
||||
t.fresize = t.xresize
|
||||
// there are no scrolling arrows by default; add them in case there are too many tabs
|
||||
C.gtk_notebook_set_scrollable(t.notebook, C.TRUE)
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *tab) Append(name string, control Control) {
|
||||
c := newContainer(control)
|
||||
c := newContainer()
|
||||
t.tabs = append(t.tabs, c)
|
||||
// this calls gtk_container_add(), which, according to gregier in irc.gimp.net/#gtk+, acts just like gtk_notebook_append_page()
|
||||
c.setParent(&controlParent{t.container})
|
||||
control.setParent(c.parent())
|
||||
t.children = append(t.children, control)
|
||||
cname := togstr(name)
|
||||
defer freegstr(cname)
|
||||
C.gtk_notebook_set_tab_label_text(t.notebook,
|
||||
|
@ -44,27 +51,13 @@ func (t *tab) Append(name string, control Control) {
|
|||
cname)
|
||||
}
|
||||
|
||||
func (t *tab) widget() *C.GtkWidget {
|
||||
return t._widget
|
||||
}
|
||||
func (t *tab) 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
return windowClosing(self->gowin);
|
||||
}
|
||||
|
||||
- (void)windowDidResize:(NSNotification *)note
|
||||
{
|
||||
windowResized(self->gowin);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
id newWindow(intptr_t width, intptr_t height)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
57
zz_test.go
57
zz_test.go
|
@ -18,6 +18,25 @@ import (
|
|||
|
||||
var closeOnClick = flag.Bool("close", false, "close on click")
|
||||
var smallWindow = flag.Bool("small", false, "open a small window (test Mac OS X initial control sizing)")
|
||||
var spaced = flag.Bool("spaced", false, "enable spacing")
|
||||
|
||||
func newHorizontalStack(c ...Control) Stack {
|
||||
s := NewHorizontalStack(c...)
|
||||
s.SetPadded(*spaced)
|
||||
return s
|
||||
}
|
||||
|
||||
func newVerticalStack(c ...Control) Stack {
|
||||
s := NewVerticalStack(c...)
|
||||
s.SetPadded(*spaced)
|
||||
return s
|
||||
}
|
||||
|
||||
func newSimpleGrid(n int, c ...Control) SimpleGrid {
|
||||
g := NewSimpleGrid(n, c...)
|
||||
g.SetPadded(*spaced)
|
||||
return g
|
||||
}
|
||||
|
||||
type dtype struct {
|
||||
Name string
|
||||
|
@ -96,7 +115,7 @@ func (tw *testwin) addfe() {
|
|||
tw.felabel.SetText(t.String())
|
||||
})
|
||||
})
|
||||
tw.felabel = NewStandaloneLabel("<stopped>")
|
||||
tw.felabel = NewLabel("<stopped>")
|
||||
tw.festop = NewButton("Stop")
|
||||
tw.festop.OnClicked(func() {
|
||||
if tw.fe != nil {
|
||||
|
@ -117,8 +136,8 @@ func (tw *testwin) addfe() {
|
|||
tw.openbtn.OnClicked(func() {
|
||||
OpenFile(tw.w, tw.openFile)
|
||||
})
|
||||
tw.fnlabel = NewStandaloneLabel("<no file selected>")
|
||||
tw.festack = NewVerticalStack(tw.festart,
|
||||
tw.fnlabel = NewLabel("<no file selected>")
|
||||
tw.festack = newVerticalStack(tw.festart,
|
||||
tw.felabel,
|
||||
tw.festop,
|
||||
NewCheckbox("This is a checkbox test"),
|
||||
|
@ -129,7 +148,7 @@ func (tw *testwin) addfe() {
|
|||
tw.openbtn, tw.fnlabel)
|
||||
tw.festack.SetStretchy(4)
|
||||
tw.festack.SetStretchy(6)
|
||||
tw.festack = NewHorizontalStack(tw.festack, Space())
|
||||
tw.festack = newHorizontalStack(tw.festack, Space())
|
||||
tw.festack.SetStretchy(0)
|
||||
tw.festack.SetStretchy(1)
|
||||
tw.t.Append("Foreign Events", tw.festack)
|
||||
|
@ -138,6 +157,7 @@ func (tw *testwin) addfe() {
|
|||
func (tw *testwin) make(done chan struct{}) {
|
||||
tw.t = NewTab()
|
||||
tw.w = NewWindow("Hello", 320, 240, tw.t)
|
||||
tw.w.SetMargined(*spaced)
|
||||
tw.w.OnClosing(func() bool {
|
||||
if *closeOnClick {
|
||||
panic("window closed normally in close on click mode (should not happen)")
|
||||
|
@ -171,12 +191,14 @@ func (tw *testwin) make(done chan struct{}) {
|
|||
tw.group2 = NewGroup("Group", NewButton("Button in Group"))
|
||||
tw.t.Append("Empty Group", NewGroup("Group", Space()))
|
||||
tw.t.Append("Filled Group", tw.group2)
|
||||
tw.group = NewGroup("Group", NewVerticalStack(NewCheckbox("Checkbox in Group")))
|
||||
tw.group2.SetMargined(*spaced)
|
||||
tw.group = NewGroup("Group", newVerticalStack(NewCheckbox("Checkbox in Group")))
|
||||
tw.group.SetMargined(*spaced)
|
||||
tw.t.Append("Group", tw.group)
|
||||
tw.simpleGrid = NewSimpleGrid(3,
|
||||
tw.simpleGrid = newSimpleGrid(3,
|
||||
NewLabel("0,0"), NewTextField(), NewLabel("0,2"),
|
||||
NewButton("1,0"), NewButton("1,1"), NewButton("1,2"),
|
||||
NewLabel("2,0"), NewTextField(), NewStandaloneLabel("2,2"))
|
||||
NewLabel("2,0"), NewTextField(), NewLabel("2,2"))
|
||||
tw.simpleGrid.SetFilling(2, 1)
|
||||
tw.simpleGrid.SetFilling(1, 2)
|
||||
tw.simpleGrid.SetStretchy(1, 1)
|
||||
|
@ -189,33 +211,33 @@ func (tw *testwin) make(done chan struct{}) {
|
|||
tw.t.Append("Space", Space())
|
||||
tw.a = NewArea(200, 200, &areaHandler{false})
|
||||
tw.t.Append("Area", tw.a)
|
||||
tw.spw = NewHorizontalStack(
|
||||
tw.spw = newHorizontalStack(
|
||||
NewButton("hello"),
|
||||
NewCheckbox("hello"),
|
||||
NewTextField(),
|
||||
NewPasswordField(),
|
||||
NewTable(reflect.TypeOf(struct{ A, B, C int }{})),
|
||||
NewStandaloneLabel("hello"))
|
||||
NewLabel("hello"))
|
||||
tw.t.Append("Pref Width", tw.spw)
|
||||
tw.sph = NewVerticalStack(
|
||||
tw.sph = newVerticalStack(
|
||||
NewButton("hello"),
|
||||
NewCheckbox("hello"),
|
||||
NewTextField(),
|
||||
NewPasswordField(),
|
||||
NewTable(reflect.TypeOf(struct{ A, B, C int }{})),
|
||||
NewStandaloneLabel("hello ÉÀÔ"))
|
||||
NewLabel("hello ÉÀÔ"))
|
||||
tw.t.Append("Pref Height", tw.sph)
|
||||
stack1 := NewHorizontalStack(NewLabel("Test"), NewTextField())
|
||||
stack1 := newHorizontalStack(NewLabel("Test"), NewTextField())
|
||||
stack1.SetStretchy(1)
|
||||
stack2 := NewHorizontalStack(NewLabel("ÉÀÔ"), NewTextField())
|
||||
stack2 := newHorizontalStack(NewLabel("ÉÀÔ"), NewTextField())
|
||||
stack2.SetStretchy(1)
|
||||
stack3 := NewHorizontalStack(NewLabel("Test 2"),
|
||||
stack3 := newHorizontalStack(NewLabel("Test 2"),
|
||||
NewTable(reflect.TypeOf(struct{ A, B, C int }{})))
|
||||
stack3.SetStretchy(1)
|
||||
tw.s = NewVerticalStack(stack1, stack2, stack3)
|
||||
tw.s = newVerticalStack(stack1, stack2, stack3)
|
||||
tw.s.SetStretchy(2)
|
||||
tw.t.Append("Stack", tw.s)
|
||||
tw.l = NewStandaloneLabel("hello")
|
||||
tw.l = NewLabel("hello")
|
||||
tw.t.Append("Label", tw.l)
|
||||
tw.table = NewTable(reflect.TypeOf(ddata[0]))
|
||||
tw.table.Lock()
|
||||
|
@ -248,7 +270,7 @@ func (tw *testwin) make(done chan struct{}) {
|
|||
tw.w.Show()
|
||||
if *smallWindow {
|
||||
tw.wsmall = NewWindow("Small", 80, 80,
|
||||
NewVerticalStack(
|
||||
newVerticalStack(
|
||||
NewButton("Small"),
|
||||
NewButton("Small 2"),
|
||||
NewArea(200, 200, &areaHandler{true})))
|
||||
|
@ -262,7 +284,6 @@ var tw *testwin
|
|||
|
||||
// because Cocoa hates being run off the main thread, even if it's run exclusively off the main thread
|
||||
func init() {
|
||||
flag.BoolVar(&spaced, "spaced", false, "enable spacing")
|
||||
flag.Parse()
|
||||
go func() {
|
||||
tw = new(testwin)
|
||||
|
|
Loading…
Reference in New Issue