Set up a new, cleaner model for deriving Control's methods and applied it to the Windows backend.
This commit is contained in:
parent
5a51263adc
commit
210102fe95
|
@ -9,61 +9,21 @@ import (
|
||||||
// #include "winapi_windows.h"
|
// #include "winapi_windows.h"
|
||||||
import "C"
|
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 {
|
type button struct {
|
||||||
*widgetbase
|
*controlbase
|
||||||
clicked *event
|
clicked *event
|
||||||
}
|
}
|
||||||
|
|
||||||
var buttonclass = toUTF16("BUTTON")
|
var buttonclass = toUTF16("BUTTON")
|
||||||
|
|
||||||
func startNewButton(text string, style C.DWORD) *button {
|
func startNewButton(text string, style C.DWORD) *button {
|
||||||
w := newWidget(buttonclass,
|
c := newControl(buttonclass,
|
||||||
style | C.WS_TABSTOP,
|
style | C.WS_TABSTOP,
|
||||||
0)
|
0)
|
||||||
C.setWindowText(w.hwnd, toUTF16(text))
|
C.setWindowText(c.hwnd, toUTF16(text))
|
||||||
C.controlSetControlFont(w.hwnd)
|
C.controlSetControlFont(c.hwnd)
|
||||||
b := &button{
|
b := &button{
|
||||||
widgetbase: w,
|
controlbase: c,
|
||||||
clicked: newEvent(),
|
clicked: newEvent(),
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
|
@ -84,7 +44,7 @@ func (b *button) Text() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *button) SetText(text string) {
|
func (b *button) SetText(text string) {
|
||||||
b.settext(text)
|
b.setText(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export buttonClicked
|
//export buttonClicked
|
||||||
|
@ -94,6 +54,8 @@ func buttonClicked(data unsafe.Pointer) {
|
||||||
println("button clicked")
|
println("button clicked")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO button preferredSize
|
||||||
|
|
||||||
type checkbox struct {
|
type checkbox struct {
|
||||||
*button
|
*button
|
||||||
}
|
}
|
||||||
|
@ -131,18 +93,18 @@ func checkboxToggled(data unsafe.Pointer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type textField struct {
|
type textField struct {
|
||||||
*widgetbase
|
*controlbase
|
||||||
}
|
}
|
||||||
|
|
||||||
var editclass = toUTF16("EDIT")
|
var editclass = toUTF16("EDIT")
|
||||||
|
|
||||||
func startNewTextField(style C.DWORD) *textField {
|
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,
|
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.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{
|
return &textField{
|
||||||
widgetbase: w,
|
controlbase: c,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,28 +121,31 @@ func (t *textField) Text() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *textField) SetText(text string) {
|
func (t *textField) SetText(text string) {
|
||||||
t.settext(text)
|
t.setText(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
type label struct {
|
type label struct {
|
||||||
*widgetbase
|
*controlbase
|
||||||
standalone bool
|
standalone bool
|
||||||
|
supercommitResize func(c *allocation, d *sizing)
|
||||||
}
|
}
|
||||||
|
|
||||||
var labelclass = toUTF16("STATIC")
|
var labelclass = toUTF16("STATIC")
|
||||||
|
|
||||||
func finishNewLabel(text string, standalone bool) *label {
|
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
|
// SS_NOPREFIX avoids accelerator translation; SS_LEFTNOWORDWRAP clips text past the end
|
||||||
// controls are vertically aligned to the top by default (thanks Xeek in irc.freenode.net/#winapi)
|
// controls are vertically aligned to the top by default (thanks Xeek in irc.freenode.net/#winapi)
|
||||||
C.SS_NOPREFIX | C.SS_LEFTNOWORDWRAP,
|
C.SS_NOPREFIX | C.SS_LEFTNOWORDWRAP,
|
||||||
0)
|
0)
|
||||||
C.setWindowText(w.hwnd, toUTF16(text))
|
C.setWindowText(c.hwnd, toUTF16(text))
|
||||||
C.controlSetControlFont(w.hwnd)
|
C.controlSetControlFont(c.hwnd)
|
||||||
l := &label{
|
l := &label{
|
||||||
widgetbase: w,
|
controlbase: c,
|
||||||
standalone: standalone,
|
standalone: standalone,
|
||||||
}
|
}
|
||||||
|
l.supercommitResize = l.fcommitResize
|
||||||
|
l.fcommitResize = l.labelcommitResize
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +162,20 @@ func (l *label) Text() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *label) SetText(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)
|
||||||
|
}
|
||||||
|
|
|
@ -18,24 +18,30 @@ TODO
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type tab struct {
|
type tab struct {
|
||||||
*widgetbase
|
*controlbase
|
||||||
tabs []*container
|
tabs []*container
|
||||||
|
supersetParent func(p *controlParent)
|
||||||
|
superallocate func(x int, y int, width int, height int, d *sizing) []*allocation
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTab() Tab {
|
func newTab() Tab {
|
||||||
w := newWidget(C.xWC_TABCONTROL,
|
c := newControl(C.xWC_TABCONTROL,
|
||||||
C.TCS_TOOLTIPS | C.WS_TABSTOP,
|
C.TCS_TOOLTIPS | C.WS_TABSTOP,
|
||||||
0)
|
0)
|
||||||
t := &tab{
|
t := &tab{
|
||||||
widgetbase: w,
|
controlbase: c,
|
||||||
}
|
}
|
||||||
C.controlSetControlFont(w.hwnd)
|
t.supersetParent = t.fsetParent
|
||||||
C.setTabSubclass(w.hwnd, unsafe.Pointer(t))
|
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
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tab) setParent(p *controlParent) {
|
func (t *tab) tabsetParent(p *controlParent) {
|
||||||
t.widgetbase.setParent(p)
|
t.supersetParent(p)
|
||||||
for _, c := range t.tabs {
|
for _, c := range t.tabs {
|
||||||
c.child.setParent(p)
|
c.child.setParent(p)
|
||||||
}
|
}
|
||||||
|
@ -68,7 +74,7 @@ func tabChanged(data unsafe.Pointer, new C.LRESULT) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// a tab control contains other controls; size appropriately
|
// 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
|
var r C.RECT
|
||||||
|
|
||||||
// figure out what the rect for each child is...
|
// 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))
|
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
|
// and now allocate the tab control itself
|
||||||
return t.widgetbase.allocate(x, y, width, height, d)
|
return t.superallocate(x, y, width, height, d)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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))
|
||||||
|
}
|
|
@ -41,37 +41,6 @@ func (c *container) translateAllocationCoords(allocations []*allocation, winwidt
|
||||||
// no translation needed on windows
|
// 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.
|
// 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.
|
// 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.
|
// 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
|
// TODO
|
||||||
/*
|
/*
|
||||||
// the preferred size of an Area is its size
|
// 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
|
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
|
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():
|
// note on MulDiv():
|
||||||
// div will not be 0 in the usages above
|
// div will not be 0 in the usages above
|
||||||
|
|
|
@ -12,13 +12,13 @@ import (
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
type table struct {
|
type table struct {
|
||||||
*widgetbase
|
*controlbase
|
||||||
*tablebase
|
*tablebase
|
||||||
}
|
}
|
||||||
|
|
||||||
func finishNewTable(b *tablebase, ty reflect.Type) Table {
|
func finishNewTable(b *tablebase, ty reflect.Type) Table {
|
||||||
t := &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.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)
|
C.WS_EX_CLIENTEDGE), // WS_EX_CLIENTEDGE without WS_BORDER will show the canonical visual styles border (thanks to MindChild in irc.efnet.net/#winprog)
|
||||||
tablebase: b,
|
tablebase: b,
|
||||||
|
|
Loading…
Reference in New Issue