From 210102fe95a72f32f9149fd674bb0c2190f14171 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 30 Jul 2014 02:06:01 -0400 Subject: [PATCH] Set up a new, cleaner model for deriving Control's methods and applied it to the Windows backend. --- redo/basicctrls_windows.go | 96 +++++++++++++--------------------- redo/containerctrls_windows.go | 26 +++++---- redo/control.go | 45 ++++++++++++++++ redo/control_windows.go | 63 ++++++++++++++++++++++ redo/sizing_windows.go | 37 ++----------- redo/table_windows.go | 4 +- 6 files changed, 166 insertions(+), 105 deletions(-) create mode 100644 redo/control.go create mode 100644 redo/control_windows.go diff --git a/redo/basicctrls_windows.go b/redo/basicctrls_windows.go index ecc1494..138c6a8 100644 --- a/redo/basicctrls_windows.go +++ b/redo/basicctrls_windows.go @@ -9,61 +9,21 @@ import ( // #include "winapi_windows.h" import "C" -type widgetbase struct { - hwnd C.HWND - parent C.HWND -} - -func newWidget(class C.LPCWSTR, style C.DWORD, extstyle C.DWORD) *widgetbase { - return &widgetbase{ - hwnd: C.newWidget(class, style, extstyle), - } -} - -// these few methods are embedded by all the various Controls since they all will do the same thing - -type controlParent struct { - hwnd C.HWND -} - -func (w *widgetbase) setParent(win *controlParent) { - C.controlSetParent(w.hwnd, win.hwnd) - w.parent = win.hwnd -} - -func (w *widgetbase) containerShow() { - C.ShowWindow(w.hwnd, C.SW_SHOW) -} - -func (w *widgetbase) containerHide() { - C.ShowWindow(w.hwnd, C.SW_HIDE) -} - -// don't embed these as exported; let each Control decide if it should - -func (w *widgetbase) text() string { - return getWindowText(w.hwnd) -} - -func (w *widgetbase) settext(text string) { - C.setWindowText(w.hwnd, toUTF16(text)) -} - type button struct { - *widgetbase + *controlbase clicked *event } var buttonclass = toUTF16("BUTTON") func startNewButton(text string, style C.DWORD) *button { - w := newWidget(buttonclass, + c := newControl(buttonclass, style | C.WS_TABSTOP, 0) - C.setWindowText(w.hwnd, toUTF16(text)) - C.controlSetControlFont(w.hwnd) + C.setWindowText(c.hwnd, toUTF16(text)) + C.controlSetControlFont(c.hwnd) b := &button{ - widgetbase: w, + controlbase: c, clicked: newEvent(), } return b @@ -84,7 +44,7 @@ func (b *button) Text() string { } func (b *button) SetText(text string) { - b.settext(text) + b.setText(text) } //export buttonClicked @@ -94,6 +54,8 @@ func buttonClicked(data unsafe.Pointer) { println("button clicked") } +// TODO button preferredSize + type checkbox struct { *button } @@ -131,18 +93,18 @@ func checkboxToggled(data unsafe.Pointer) { } type textField struct { - *widgetbase + *controlbase } var editclass = toUTF16("EDIT") func startNewTextField(style C.DWORD) *textField { - w := newWidget(editclass, + c := newControl(editclass, style | C.ES_AUTOHSCROLL | C.ES_LEFT | C.ES_NOHIDESEL | 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) - C.controlSetControlFont(w.hwnd) + C.controlSetControlFont(c.hwnd) return &textField{ - widgetbase: w, + controlbase: c, } } @@ -159,28 +121,31 @@ func (t *textField) Text() string { } func (t *textField) SetText(text string) { - t.settext(text) + t.setText(text) } type label struct { - *widgetbase - standalone bool + *controlbase + standalone bool + supercommitResize func(c *allocation, d *sizing) } var labelclass = toUTF16("STATIC") func finishNewLabel(text string, standalone bool) *label { - w := newWidget(labelclass, + 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, 0) - C.setWindowText(w.hwnd, toUTF16(text)) - C.controlSetControlFont(w.hwnd) + C.setWindowText(c.hwnd, toUTF16(text)) + C.controlSetControlFont(c.hwnd) l := &label{ - widgetbase: w, + controlbase: c, standalone: standalone, } + l.supercommitResize = l.fcommitResize + l.fcommitResize = l.labelcommitResize return l } @@ -197,7 +162,20 @@ func (l *label) Text() string { } func (l *label) SetText(text string) { - l.settext(text) + l.setText(text) } -// TODO label commitResize +func (l *label) labelcommitResize(c *allocation, d *sizing) { +// TODO +/* + yoff := stdDlgSizes[s.ctype].yoff + if s.standalone { + yoff = stdDlgSizes[s.ctype].yoffalt + } + if yoff != 0 { + yoff = int(C.MulDiv(C.int(yoff), C.int(d.baseY), 8)) + } + c.y += yoff +*/ + l.supercommitResize(c, d) +} diff --git a/redo/containerctrls_windows.go b/redo/containerctrls_windows.go index cc881e7..e81c340 100644 --- a/redo/containerctrls_windows.go +++ b/redo/containerctrls_windows.go @@ -18,24 +18,30 @@ TODO */ type tab struct { - *widgetbase - tabs []*container + *controlbase + tabs []*container + supersetParent func(p *controlParent) + superallocate func(x int, y int, width int, height int, d *sizing) []*allocation } func newTab() Tab { - w := newWidget(C.xWC_TABCONTROL, + c := newControl(C.xWC_TABCONTROL, C.TCS_TOOLTIPS | C.WS_TABSTOP, 0) t := &tab{ - widgetbase: w, + controlbase: c, } - C.controlSetControlFont(w.hwnd) - C.setTabSubclass(w.hwnd, unsafe.Pointer(t)) + t.supersetParent = t.fsetParent + t.fsetParent = t.tabsetParent + t.superallocate = t.fallocate + t.fallocate = t.taballocate + C.controlSetControlFont(t.hwnd) + C.setTabSubclass(t.hwnd, unsafe.Pointer(t)) return t } -func (t *tab) setParent(p *controlParent) { - t.widgetbase.setParent(p) +func (t *tab) tabsetParent(p *controlParent) { + t.supersetParent(p) for _, c := range t.tabs { c.child.setParent(p) } @@ -68,7 +74,7 @@ func tabChanged(data unsafe.Pointer, new C.LRESULT) { } // a tab control contains other controls; size appropriately -func (t *tab) allocate(x int, y int, width int, height int, d *sizing) []*allocation { +func (t *tab) taballocate(x int, y int, width int, height int, d *sizing) []*allocation { var r C.RECT // figure out what the rect for each child is... @@ -84,5 +90,5 @@ func (t *tab) allocate(x int, y int, width int, height int, d *sizing) []*alloca c.resize(int(r.left), int(r.top), int(r.right - r.left), int(r.bottom - r.top)) } // and now allocate the tab control itself - return t.widgetbase.allocate(x, y, width, height, d) + return t.superallocate(x, y, width, height, d) } diff --git a/redo/control.go b/redo/control.go new file mode 100644 index 0000000..bd4ef00 --- /dev/null +++ b/redo/control.go @@ -0,0 +1,45 @@ +// 30 july 2014 + +package ui + +// All Controls embed this structure, which provides the Control interface methods. +// If a Control needs to override one of these functions, it assigns to the function variables. +type controldefs struct { + fsetParent func(p *controlParent) + fcontainerShow func() + fcontainerHide func() + fallocate func(x int, y int, width int, height int, d *sizing) []*allocation + fpreferredSize func(*sizing) (int, int) + fcommitResize func(*allocation, *sizing) + fgetAuxResizeInfo func(*sizing) +} + +// There's no newcontroldefs() function; all defaults are set by controlbase. + +func (w *controldefs) setParent(p *controlParent) { + w.fsetParent(p) +} + +func (w *controldefs) containerShow() { + w.fcontainerShow() +} + +func (w *controldefs) containerHide() { + w.fcontainerHide() +} + +func (w *controldefs) allocate(x int, y int, width int, height int, d *sizing) []*allocation { + return w.fallocate(x, y, width, height, d) +} + +func (w *controldefs) preferredSize(d *sizing) (int, int) { + return w.fpreferredSize(d) +} + +func (w *controldefs) commitResize(c *allocation, d *sizing) { + w.fcommitResize(c, d) +} + +func (w *controldefs) getAuxResizeInfo(d *sizing) { + w.fgetAuxResizeInfo(d) +} diff --git a/redo/control_windows.go b/redo/control_windows.go new file mode 100644 index 0000000..36f3d4a --- /dev/null +++ b/redo/control_windows.go @@ -0,0 +1,63 @@ +// 30 july 2014 + +package ui + +// #include "winapi_windows.h" +import "C" + +type controlbase struct { + *controldefs + hwnd C.HWND + parent C.HWND // for Tab and Group +} + +type controlParent struct { + hwnd C.HWND +} + +func newControl(class C.LPCWSTR, style C.DWORD, extstyle C.DWORD) *controlbase { + c := new(controlbase) + c.hwnd = C.newWidget(class, style, extstyle) + c.controldefs = new(controldefs) + c.fsetParent = func(p *controlParent) { + C.controlSetParent(c.hwnd, p.hwnd) + c.parent = p.hwnd + } + c.fcontainerShow = func() { + C.ShowWindow(c.hwnd, C.SW_SHOW) + } + c.fcontainerHide = func() { + C.ShowWindow(c.hwnd, C.SW_HIDE) + } + c.fallocate = func(x int, y int, width int, height int, d *sizing) []*allocation { + // TODO split into its own function + return []*allocation{&allocation{ + x: x, + y: y, + width: width, + height: height, + this: c, + }} + } + c.fpreferredSize = func(d *sizing) (int, int) { + // TODO + return 75, 23 + } + c.fcommitResize = func(a *allocation, d *sizing) { + C.moveWindow(c.hwnd, C.int(a.x), C.int(a.y), C.int(a.width), C.int(a.height)) + } + c.fgetAuxResizeInfo = func(d *sizing) { + // do nothing + } + return c +} + +// these are provided for convenience + +func (c *controlbase) text() string { + return getWindowText(c.hwnd) +} + +func (c *controlbase) setText(text string) { + C.setWindowText(c.hwnd, toUTF16(text)) +} diff --git a/redo/sizing_windows.go b/redo/sizing_windows.go index d8f3246..2404aea 100644 --- a/redo/sizing_windows.go +++ b/redo/sizing_windows.go @@ -41,37 +41,6 @@ func (c *container) translateAllocationCoords(allocations []*allocation, winwidt // no translation needed on windows } -// TODO move this to sizing.go -// TODO when doing so, account for margins -func (w *widgetbase) allocate(x int, y int, width int, height int, d *sizing) []*allocation { - return []*allocation{&allocation{ - x: x, - y: y, - width: width, - height: height, - this: w, - }} -} - -func (w *widgetbase) commitResize(c *allocation, d *sizing) { -// TODO -/* - yoff := stdDlgSizes[s.ctype].yoff - if s.alternate { - yoff = stdDlgSizes[s.ctype].yoffalt - } - if yoff != 0 { - yoff = int(C.MulDiv(C.int(yoff), C.int(d.baseY), 8)) - } - c.y += yoff -*/ - C.moveWindow(w.hwnd, C.int(c.x), C.int(c.y), C.int(c.width), C.int(c.height)) -} - -func (w *widgetbase) getAuxResizeInfo(d *sizing) { - // do nothing -} - // 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. // These sizes are given in "dialog units", which are independent of the font in use. // We need to convert these into standard pixels, which requires we get the device context of the OS window. @@ -143,7 +112,7 @@ var stdDlgSizes = [nctypes]dlgunits{ } */ -func (w *widgetbase) preferredSize(d *sizing) (width int, height int) { +//func (w *widgetbase) preferredSize(d *sizing) (width int, height int) { // TODO /* // the preferred size of an Area is its size @@ -174,8 +143,8 @@ func (w *widgetbase) preferredSize(d *sizing) (width int, height int) { width = int(C.MulDiv(C.int(width), C.int(d.baseX), 4)) // equivalent to right of rect height = int(C.MulDiv(C.int(height), C.int(d.baseY), 8)) // equivalent to bottom of rect */ - return width, height -} +// return width, height +//} // note on MulDiv(): // div will not be 0 in the usages above diff --git a/redo/table_windows.go b/redo/table_windows.go index c3e0453..1453407 100644 --- a/redo/table_windows.go +++ b/redo/table_windows.go @@ -12,13 +12,13 @@ import ( import "C" type table struct { - *widgetbase + *controlbase *tablebase } func finishNewTable(b *tablebase, ty reflect.Type) Table { t := &table{ - widgetbase: newWidget(C.xWC_LISTVIEW, + controlbase: newControl(C.xWC_LISTVIEW, C.LVS_REPORT | C.LVS_OWNERDATA | C.LVS_NOSORTHEADER | C.LVS_SHOWSELALWAYS | C.WS_HSCROLL | C.WS_VSCROLL, C.WS_EX_CLIENTEDGE), // WS_EX_CLIENTEDGE without WS_BORDER will show the canonical visual styles border (thanks to MindChild in irc.efnet.net/#winprog) tablebase: b,