diff --git a/redo/basicctrls_windows.c b/redo/basicctrls_windows.c index 58474c1..cca3f95 100644 --- a/redo/basicctrls_windows.c +++ b/redo/basicctrls_windows.c @@ -3,7 +3,7 @@ #include "winapi_windows.h" #include "_cgo_export.h" -HWND newWidget(LPWSTR class, DWORD style, DWORD extstyle) +HWND newControl(LPWSTR class, DWORD style, DWORD extstyle) { HWND hwnd; diff --git a/redo/button_windows.go b/redo/button_windows.go index 71ed17a..8c2ebec 100644 --- a/redo/button_windows.go +++ b/redo/button_windows.go @@ -10,23 +10,24 @@ import ( import "C" type button struct { - *controlbase - clicked *event + _hwnd C.HWND + _textlen C.LONG + clicked *event } var buttonclass = toUTF16("BUTTON") func newButton(text string) *button { - c := newControl(buttonclass, + hwnd := C.newControl(buttonclass, C.BS_PUSHBUTTON | C.WS_TABSTOP, 0) - c.setText(text) - C.controlSetControlFont(c.hwnd) b := &button{ - controlbase: c, - clicked: newEvent(), + _hwnd: hwnd, + clicked: newEvent(), } - C.setButtonSubclass(b.hwnd, unsafe.Pointer(b)) + b.SetText(text) + C.controlSetControlFont(b._hwnd) + C.setButtonSubclass(b._hwnd, unsafe.Pointer(b)) return b } @@ -35,11 +36,11 @@ func (b *button) OnClicked(e func()) { } func (b *button) Text() string { - return b.text() + return baseText(b) } func (b *button) SetText(text string) { - b.setText(text) + baseSetText(b, text) } //export buttonClicked @@ -49,16 +50,28 @@ func buttonClicked(data unsafe.Pointer) { println("button clicked") } +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.controlbase, p) + basesetParent(b, p) } func (b *button) containerShow() { - basecontainerShow(b.controlbase) + basecontainerShow(b) } func (b *button) containerHide() { - basecontainerHide(b.controlbase) + basecontainerHide(b) } func (b *button) allocate(x int, y int, width int, height int, d *sizing) []*allocation { @@ -76,18 +89,18 @@ func (b *button) preferredSize(d *sizing) (width, height int) { 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) + return xmargins + int(b._textlen), fromdlgunitsY(buttonHeight, d) } func (b *button) commitResize(a *allocation, d *sizing) { - basecommitResize(b.controlbase, a, d) + basecommitResize(b, a, d) } func (b *button) getAuxResizeInfo(d *sizing) { diff --git a/redo/checkbox_windows.go b/redo/checkbox_windows.go index 5ffce28..88c82ef 100644 --- a/redo/checkbox_windows.go +++ b/redo/checkbox_windows.go @@ -10,23 +10,24 @@ import ( import "C" type checkbox struct { - *controlbase - toggled *event + _hwnd C.HWND + _textlen C.LONG + toggled *event } func newCheckbox(text string) *checkbox { // don't use BS_AUTOCHECKBOX here because it creates problems when refocusing (see http://blogs.msdn.com/b/oldnewthing/archive/2014/05/22/10527522.aspx) // we'll handle actually toggling the check state ourselves (see controls_windows.c) - cc := newControl(buttonclass, + hwnd := C.newControl(buttonclass, C.BS_CHECKBOX | C.WS_TABSTOP, 0) - cc.setText(text) - C.controlSetControlFont(cc.hwnd) c := &checkbox{ - controlbase: cc, + _hwnd: hwnd, toggled: newEvent(), } - C.setCheckboxSubclass(c.hwnd, unsafe.Pointer(c)) + c.SetText(text) + C.controlSetControlFont(c._hwnd) + C.setCheckboxSubclass(c._hwnd, unsafe.Pointer(c)) return c } @@ -35,23 +36,23 @@ func (c *checkbox) OnToggled(e func()) { } func (c *checkbox) Text() string { - return c.text() + return baseText(c) } func (c *checkbox) SetText(text string) { - c.setText(text) + baseSetText(c, 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,16 +62,28 @@ func checkboxToggled(data unsafe.Pointer) { println("checkbox toggled") } +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.controlbase, p) + basesetParent(c, p) } func (c *checkbox) containerShow() { - basecontainerShow(c.controlbase) + basecontainerShow(c) } func (c *checkbox) containerHide() { - basecontainerHide(c.controlbase) + basecontainerHide(c) } func (c *checkbox) allocate(x int, y int, width int, height int, d *sizing) []*allocation { @@ -85,12 +98,12 @@ const ( ) func (c *checkbox) preferredSize(d *sizing) (width, height int) { - return fromdlgunitsX(checkboxXFromLeftOfBoxToLeftOfLabel, d) + int(c.textlen), + return fromdlgunitsX(checkboxXFromLeftOfBoxToLeftOfLabel, d) + int(c._textlen), fromdlgunitsY(checkboxHeight, d) } func (c *checkbox) commitResize(a *allocation, d *sizing) { - basecommitResize(c.controlbase, a, d) + basecommitResize(c, a, d) } func (c *checkbox) getAuxResizeInfo(d *sizing) { diff --git a/redo/control_windows.go b/redo/control_windows.go index ac3297e..ac57527 100644 --- a/redo/control_windows.go +++ b/redo/control_windows.go @@ -6,46 +6,30 @@ package ui import "C" type controlPrivate interface { - // TODO + hwnd() C.HWND Control } -type controlbase struct { - hwnd C.HWND - parent C.HWND // for Tab and Group - textlen C.LONG -} - type controlParent struct { hwnd C.HWND } -func newControl(class C.LPWSTR, style C.DWORD, extstyle C.DWORD) *controlbase { - c := new(controlbase) - // TODO rename to newWidget - c.hwnd = C.newWidget(class, style, extstyle) - return c +func basesetParent(c controlPrivate, p *controlParent) { + C.controlSetParent(c.hwnd(), p.hwnd) } -// TODO for maximum correctness these shouldn't take controlbases... but then the amount of duplicated code would skyrocket - -func basesetParent(c *controlbase, p *controlParent) { - C.controlSetParent(c.hwnd, p.hwnd) - c.parent = p.hwnd +func basecontainerShow(c controlPrivate) { + C.ShowWindow(c.hwnd(), C.SW_SHOW) } -func basecontainerShow(c *controlbase) { - C.ShowWindow(c.hwnd, C.SW_SHOW) -} - -func basecontainerHide(c *controlbase) { - C.ShowWindow(c.hwnd, C.SW_HIDE) +func basecontainerHide(c controlPrivate) { + C.ShowWindow(c.hwnd(), C.SW_HIDE) } // don't specify basepreferredSize; it is custom on ALL controls -func basecommitResize(c *controlbase, 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 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 basegetAuxResizeInfo(c controlPrivate, d *sizing) { @@ -54,12 +38,19 @@ func basegetAuxResizeInfo(c controlPrivate, d *sizing) { // these are provided for convenience -func (c *controlbase) text() string { - return getWindowText(c.hwnd) +type textableControl interface { + controlPrivate + textlen() C.LONG + settextlen(C.LONG) } -func (c *controlbase) setText(text string) { - t := toUTF16(text) - C.setWindowText(c.hwnd, t) - c.textlen = C.controlTextLength(c.hwnd, t) +func baseText(c textableControl) string { + return getWindowText(c.hwnd()) +} + +func baseSetText(c textableControl, text string) { + hwnd := c.hwnd() + t := toUTF16(text) + C.setWindowText(hwnd, t) + c.settextlen(C.controlTextLength(hwnd, t)) } diff --git a/redo/label_windows.go b/redo/label_windows.go index d0e03eb..8e55943 100644 --- a/redo/label_windows.go +++ b/redo/label_windows.go @@ -6,24 +6,25 @@ package ui import "C" type label struct { - *controlbase + _hwnd C.HWND + _textlen C.LONG standalone bool } var labelclass = toUTF16("STATIC") func finishNewLabel(text string, standalone bool) *label { - c := newControl(labelclass, + 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, 0) - c.setText(text) - C.controlSetControlFont(c.hwnd) l := &label{ - controlbase: c, + _hwnd: hwnd, standalone: standalone, } + l.SetText(text) + C.controlSetControlFont(l._hwnd) return l } @@ -36,23 +37,35 @@ func newStandaloneLabel(text string) Label { } func (l *label) Text() string { - return l.text() + return baseText(l) } func (l *label) SetText(text string) { - l.setText(text) + baseSetText(l, text) +} + +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) { - basesetParent(l.controlbase, p) + basesetParent(l, p) } func (l *label) containerShow() { - basecontainerShow(l.controlbase) + basecontainerShow(l) } func (l *label) containerHide() { - basecontainerHide(l.controlbase) + basecontainerHide(l) } func (l *label) allocate(x int, y int, width int, height int, d *sizing) []*allocation { @@ -67,7 +80,7 @@ const ( ) func (l *label) preferredSize(d *sizing) (width, height int) { - return int(l.textlen), fromdlgunitsY(labelHeight, d) + return int(l._textlen), fromdlgunitsY(labelHeight, d) } func (l *label) commitResize(c *allocation, d *sizing) { @@ -76,7 +89,7 @@ func (l *label) commitResize(c *allocation, d *sizing) { c.y += yoff c.height -= yoff } - basecommitResize(l.controlbase, c, d) + basecommitResize(l, c, d) } func (l *label) getAuxResizeInfo(d *sizing) { diff --git a/redo/tab_windows.go b/redo/tab_windows.go index da97c2c..20e1ee0 100644 --- a/redo/tab_windows.go +++ b/redo/tab_windows.go @@ -18,19 +18,20 @@ TODO */ type tab struct { - *controlbase - tabs []*sizer + _hwnd C.HWND + tabs []*sizer + parent C.HWND } func newTab() Tab { - c := newControl(C.xWC_TABCONTROL, + hwnd := C.newControl(C.xWC_TABCONTROL, C.TCS_TOOLTIPS | C.WS_TABSTOP, 0) t := &tab{ - controlbase: c, + _hwnd: hwnd, } - C.controlSetControlFont(t.hwnd) - C.setTabSubclass(t.hwnd, unsafe.Pointer(t)) + C.controlSetControlFont(t._hwnd) + C.setTabSubclass(t._hwnd, unsafe.Pointer(t)) return t } @@ -45,7 +46,7 @@ func (t *tab) Append(name string, control Control) { if len(t.tabs) != 1 { s.child.containerHide() } - C.tabAppend(t.hwnd, toUTF16(name)) + C.tabAppend(t._hwnd, toUTF16(name)) } //export tabChanging @@ -60,21 +61,26 @@ func tabChanged(data unsafe.Pointer, new C.LRESULT) { t.tabs[int(new)].child.containerShow() } +func (t *tab) hwnd() C.HWND { + return t._hwnd +} + func (t *tab) setParent(p *controlParent) { - basesetParent(t.controlbase, p) + basesetParent(t, p) for _, c := range t.tabs { c.child.setParent(p) } + t.parent = p.hwnd } // TODO actually write this func (t *tab) containerShow() { - basecontainerShow(t.controlbase) + basecontainerShow(t) } // TODO actually write this func (t *tab) containerHide() { - basecontainerHide(t.controlbase) + basecontainerHide(t) } func (t *tab) allocate(x int, y int, width int, height int, d *sizing) []*allocation { @@ -92,7 +98,7 @@ 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 @@ -104,7 +110,7 @@ func (t *tab) commitResize(c *allocation, d *sizing) { r.top = C.LONG(c.y) r.right = C.LONG(c.x + c.width) r.bottom = C.LONG(c.y + c.height) - C.tabGetContentRect(t.hwnd, &r) + C.tabGetContentRect(t._hwnd, &r) // and resize tabs // don't resize just the current tab; resize all tabs! for _, s := range t.tabs { @@ -112,7 +118,7 @@ func (t *tab) commitResize(c *allocation, d *sizing) { s.resize(int(r.left), int(r.top), int(r.right - r.left), int(r.bottom - r.top)) } // and now resize the tab control itself - basecommitResize(t.controlbase, c, d) + basecommitResize(t, c, d) } func (t *tab) getAuxResizeInfo(d *sizing) { diff --git a/redo/table_windows.go b/redo/table_windows.go index 60b8fe0..3fef105 100644 --- a/redo/table_windows.go +++ b/redo/table_windows.go @@ -12,23 +12,23 @@ import ( import "C" type table struct { - *controlbase *tablebase + _hwnd C.HWND } func finishNewTable(b *tablebase, ty reflect.Type) Table { t := &table{ - controlbase: newControl(C.xWC_LISTVIEW, + _hwnd: C.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, } - C.setTableSubclass(t.hwnd, unsafe.Pointer(t)) + 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) 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)) } return t } @@ -39,7 +39,7 @@ func (t *table) Unlock() { // I think there's a way to set the item count without causing a refetch of data that works around this... 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())) } //export tableGetCellText @@ -53,16 +53,20 @@ func tableGetCellText(data unsafe.Pointer, row C.int, col C.int, str *C.LPWSTR) *str = toUTF16(s) } +func (t *table) hwnd() C.HWND { + return t._hwnd +} + func (t *table) setParent(p *controlParent) { - basesetParent(t.controlbase, p) + basesetParent(t, p) } func (t *table) containerShow() { - basecontainerShow(t.controlbase) + basecontainerShow(t) } func (t *table) containerHide() { - basecontainerHide(t.controlbase) + basecontainerHide(t) } func (t *table) allocate(x int, y int, width int, height int, d *sizing) []*allocation { @@ -80,7 +84,7 @@ func (t *table) preferredSize(d *sizing) (width, height int) { } func (t *table) commitResize(a *allocation, d *sizing) { - basecommitResize(t.controlbase, a, d) + basecommitResize(t, a, d) } func (t *table) getAuxResizeInfo(d *sizing) { diff --git a/redo/textfield_windows.go b/redo/textfield_windows.go index abb2780..c4e3d48 100644 --- a/redo/textfield_windows.go +++ b/redo/textfield_windows.go @@ -6,19 +6,20 @@ package ui import "C" type textField struct { - *controlbase + _hwnd C.HWND + _textlen C.LONG } var editclass = toUTF16("EDIT") func startNewTextField(style C.DWORD) *textField { - c := newControl(editclass, + hwnd := 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(c.hwnd) t := &textField{ - controlbase: c, + _hwnd: hwnd, } + C.controlSetControlFont(t._hwnd) return t } @@ -31,23 +32,35 @@ func newPasswordField() *textField { } func (t *textField) Text() string { - return t.text() + return baseText(t) } func (t *textField) SetText(text string) { - t.setText(text) + baseSetText(t, text) +} + +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.controlbase, p) + basesetParent(t, p) } func (t *textField) containerShow() { - basecontainerShow(t.controlbase) + basecontainerShow(t) } func (t *textField) containerHide() { - basecontainerHide(t.controlbase) + basecontainerHide(t) } func (t *textField) allocate(x int, y int, width int, height int, d *sizing) []*allocation { @@ -65,7 +78,7 @@ func (t *textField) preferredSize(d *sizing) (width, height int) { } func (t *textField) commitResize(a *allocation, d *sizing) { - basecommitResize(t.controlbase, a, d) + basecommitResize(t, a, d) } func (t *textField) getAuxResizeInfo(d *sizing) { diff --git a/redo/winapi_windows.h b/redo/winapi_windows.h index 9ffa019..8c7b807 100644 --- a/redo/winapi_windows.h +++ b/redo/winapi_windows.h @@ -41,8 +41,8 @@ extern BOOL (*WINAPI fv_SetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_P extern BOOL (*WINAPI fv_RemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR); extern LRESULT (*WINAPI fv_DefSubclassProc)(HWND, UINT, WPARAM, LPARAM); -/* controls_windows.c */ -extern HWND newWidget(LPWSTR, DWORD, DWORD); +/* [TODO rename] controls_windows.c */ +extern HWND newControl(LPWSTR, DWORD, DWORD); extern void controlSetParent(HWND, HWND); extern void controlSetControlFont(HWND); extern LRESULT forwardCommand(HWND, UINT, WPARAM, LPARAM);