diff --git a/sysdata_windows.go b/sysdata_windows.go index 94f719a..4a4f69f 100644 --- a/sysdata_windows.go +++ b/sysdata_windows.go @@ -4,127 +4,127 @@ package ui import ( "fmt" + "sync" "syscall" "unsafe" - "sync" ) type sysData struct { cSysData - hwnd _HWND - children map[_HMENU]*sysData - nextChildID _HMENU - childrenLock sync.Mutex - isMarquee bool // for sysData.setProgress() + hwnd _HWND + children map[_HMENU]*sysData + nextChildID _HMENU + childrenLock sync.Mutex + isMarquee bool // for sysData.setProgress() // unlike with GTK+ and Mac OS X, we're responsible for sizing Area properly ourselves - areawidth int - areaheight int - clickCounter clickCounter - lastfocus _HWND + areawidth int + areaheight int + clickCounter clickCounter + lastfocus _HWND } type classData struct { - name *uint16 - style uint32 - xstyle uint32 - altStyle uint32 - storeSysData bool - doNotLoadFont bool - appendMsg uintptr - insertBeforeMsg uintptr - deleteMsg uintptr - selectedIndexMsg uintptr - selectedIndexErr uintptr - addSpaceErr uintptr - lenMsg uintptr + name *uint16 + style uint32 + xstyle uint32 + altStyle uint32 + storeSysData bool + doNotLoadFont bool + appendMsg uintptr + insertBeforeMsg uintptr + deleteMsg uintptr + selectedIndexMsg uintptr + selectedIndexErr uintptr + addSpaceErr uintptr + lenMsg uintptr } const controlstyle = _WS_CHILD | _WS_VISIBLE | _WS_TABSTOP const controlxstyle = 0 var classTypes = [nctypes]*classData{ - c_window: &classData{ - name: stdWndClass, - style: _WS_OVERLAPPEDWINDOW, - xstyle: 0, - storeSysData: true, - doNotLoadFont: true, + c_window: &classData{ + name: stdWndClass, + style: _WS_OVERLAPPEDWINDOW, + xstyle: 0, + storeSysData: true, + doNotLoadFont: true, }, - c_button: &classData{ - name: toUTF16("BUTTON"), - style: _BS_PUSHBUTTON | controlstyle, - xstyle: 0 | controlxstyle, + c_button: &classData{ + name: toUTF16("BUTTON"), + style: _BS_PUSHBUTTON | controlstyle, + xstyle: 0 | controlxstyle, }, - c_checkbox: &classData{ - name: toUTF16("BUTTON"), + c_checkbox: &classData{ + name: toUTF16("BUTTON"), // don't use BS_AUTOCHECKBOX because http://blogs.msdn.com/b/oldnewthing/archive/2014/05/22/10527522.aspx - style: _BS_CHECKBOX | controlstyle, - xstyle: 0 | controlxstyle, + style: _BS_CHECKBOX | controlstyle, + xstyle: 0 | controlxstyle, }, - c_combobox: &classData{ - name: toUTF16("COMBOBOX"), - style: _CBS_DROPDOWNLIST | _WS_VSCROLL | controlstyle, - xstyle: 0 | controlxstyle, - altStyle: _CBS_DROPDOWN | _CBS_AUTOHSCROLL | _WS_VSCROLL | controlstyle, - appendMsg: _CB_ADDSTRING, - insertBeforeMsg: _CB_INSERTSTRING, - deleteMsg: _CB_DELETESTRING, - selectedIndexMsg: _CB_GETCURSEL, - selectedIndexErr: negConst(_CB_ERR), - addSpaceErr: negConst(_CB_ERRSPACE), - lenMsg: _CB_GETCOUNT, + c_combobox: &classData{ + name: toUTF16("COMBOBOX"), + style: _CBS_DROPDOWNLIST | _WS_VSCROLL | controlstyle, + xstyle: 0 | controlxstyle, + altStyle: _CBS_DROPDOWN | _CBS_AUTOHSCROLL | _WS_VSCROLL | controlstyle, + appendMsg: _CB_ADDSTRING, + insertBeforeMsg: _CB_INSERTSTRING, + deleteMsg: _CB_DELETESTRING, + selectedIndexMsg: _CB_GETCURSEL, + selectedIndexErr: negConst(_CB_ERR), + addSpaceErr: negConst(_CB_ERRSPACE), + lenMsg: _CB_GETCOUNT, }, - c_lineedit: &classData{ - name: toUTF16("EDIT"), + c_lineedit: &classData{ + name: toUTF16("EDIT"), // WS_EX_CLIENTEDGE without WS_BORDER will apply visual styles // thanks to MindChild in irc.efnet.net/#winprog - style: _ES_AUTOHSCROLL | controlstyle, - xstyle: _WS_EX_CLIENTEDGE | controlxstyle, - altStyle: _ES_PASSWORD | _ES_AUTOHSCROLL | controlstyle, + style: _ES_AUTOHSCROLL | controlstyle, + xstyle: _WS_EX_CLIENTEDGE | controlxstyle, + altStyle: _ES_PASSWORD | _ES_AUTOHSCROLL | controlstyle, }, - c_label: &classData{ - name: toUTF16("STATIC"), + c_label: &classData{ + name: toUTF16("STATIC"), // 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) - style: _SS_NOPREFIX | _SS_LEFTNOWORDWRAP | controlstyle, - xstyle: 0 | controlxstyle, + style: _SS_NOPREFIX | _SS_LEFTNOWORDWRAP | controlstyle, + xstyle: 0 | controlxstyle, }, - c_listbox: &classData{ - name: toUTF16("LISTBOX"), + c_listbox: &classData{ + name: toUTF16("LISTBOX"), // we don't use LBS_STANDARD because it sorts (and has WS_BORDER; see above) // LBS_NOINTEGRALHEIGHT gives us exactly the size we want // LBS_MULTISEL sounds like it does what we want but it actually doesn't; instead, it toggles item selection regardless of modifier state, which doesn't work like anything else (see http://msdn.microsoft.com/en-us/library/windows/desktop/bb775149%28v=vs.85%29.aspx and http://msdn.microsoft.com/en-us/library/windows/desktop/aa511485.aspx) - style: _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_VSCROLL | controlstyle, - xstyle: _WS_EX_CLIENTEDGE | controlxstyle, - altStyle: _LBS_EXTENDEDSEL | _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_VSCROLL | controlstyle, - appendMsg: _LB_ADDSTRING, - insertBeforeMsg: _LB_INSERTSTRING, - deleteMsg: _LB_DELETESTRING, - selectedIndexMsg: _LB_GETCURSEL, - selectedIndexErr: negConst(_LB_ERR), - addSpaceErr: negConst(_LB_ERRSPACE), - lenMsg: _LB_GETCOUNT, + style: _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_VSCROLL | controlstyle, + xstyle: _WS_EX_CLIENTEDGE | controlxstyle, + altStyle: _LBS_EXTENDEDSEL | _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_VSCROLL | controlstyle, + appendMsg: _LB_ADDSTRING, + insertBeforeMsg: _LB_INSERTSTRING, + deleteMsg: _LB_DELETESTRING, + selectedIndexMsg: _LB_GETCURSEL, + selectedIndexErr: negConst(_LB_ERR), + addSpaceErr: negConst(_LB_ERRSPACE), + lenMsg: _LB_GETCOUNT, }, - c_progressbar: &classData{ - name: toUTF16(x_PROGRESS_CLASS), - style: _PBS_SMOOTH | controlstyle, - xstyle: 0 | controlxstyle, - doNotLoadFont: true, + c_progressbar: &classData{ + name: toUTF16(x_PROGRESS_CLASS), + style: _PBS_SMOOTH | controlstyle, + xstyle: 0 | controlxstyle, + doNotLoadFont: true, }, - c_area: &classData{ - name: areaWndClass, - style: areastyle, - xstyle: areaxstyle, - storeSysData: true, - doNotLoadFont: true, + c_area: &classData{ + name: areaWndClass, + style: areastyle, + xstyle: areaxstyle, + storeSysData: true, + doNotLoadFont: true, }, } func (s *sysData) addChild(child *sysData) _HMENU { s.childrenLock.Lock() defer s.childrenLock.Unlock() - s.nextChildID++ // start at 1 + s.nextChildID++ // start at 1 if s.children == nil { s.children = map[_HMENU]*sysData{} } @@ -140,7 +140,7 @@ func (s *sysData) delChild(id _HMENU) { var ( _blankString = toUTF16("") - blankString = utf16ToArg(_blankString) + blankString = utf16ToArg(_blankString) ) func (s *sysData) make(window *sysData) (err error) { @@ -149,7 +149,7 @@ func (s *sysData) make(window *sysData) (err error) { ct := classTypes[s.ctype] cid := _HMENU(0) pwin := uintptr(_NULL) - if window != nil { // this is a child control + if window != nil { // this is a child control cid = window.addChild(s) pwin = uintptr(window.hwnd) } @@ -162,11 +162,11 @@ func (s *sysData) make(window *sysData) (err error) { lpParam = uintptr(unsafe.Pointer(s)) } uitask <- &uimsg{ - call: _createWindowEx, - p: []uintptr{ + call: _createWindowEx, + p: []uintptr{ uintptr(ct.xstyle), utf16ToArg(ct.name), - blankString, // we set the window text later + blankString, // we set the window text later style, negConst(_CW_USEDEFAULT), negConst(_CW_USEDEFAULT), @@ -177,30 +177,30 @@ func (s *sysData) make(window *sysData) (err error) { uintptr(hInstance), lpParam, }, - ret: ret, + ret: ret, } r := <-ret - if r.ret == 0 { // failure + if r.ret == 0 { // failure if window != nil { window.delChild(cid) } return fmt.Errorf("error actually creating window/control: %v", r.err) } - if !ct.storeSysData { // regular control; store s.hwnd ourselves + if !ct.storeSysData { // regular control; store s.hwnd ourselves s.hwnd = _HWND(r.ret) - } else if s.hwnd != _HWND(r.ret) { // we store sysData in storeSysData(); sanity check + } else if s.hwnd != _HWND(r.ret) { // we store sysData in storeSysData(); sanity check panic(fmt.Errorf("hwnd mismatch creating window/control: storeSysData() stored 0x%X but CreateWindowEx() returned 0x%X", s.hwnd, ret)) } if !ct.doNotLoadFont { uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(_WM_SETFONT), uintptr(_WPARAM(controlFont)), uintptr(_LPARAM(_TRUE)), }, - ret: ret, + ret: ret, } <-ret } @@ -218,21 +218,21 @@ func (s *sysData) firstShow() error { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _showWindow, - p: []uintptr{ + call: _showWindow, + p: []uintptr{ uintptr(s.hwnd), uintptr(nCmdShow), }, - ret: ret, + ret: ret, } <-ret uitask <- &uimsg{ - call: _updateWindow, - p: []uintptr{uintptr(s.hwnd)}, - ret: ret, + call: _updateWindow, + p: []uintptr{uintptr(s.hwnd)}, + ret: ret, } r := <-ret - if r.ret == 0 { // failure + if r.ret == 0 { // failure return fmt.Errorf("error updating window for the first time: %v", r.err) } return nil @@ -242,12 +242,12 @@ func (s *sysData) show() { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _showWindow, - p: []uintptr{ + call: _showWindow, + p: []uintptr{ uintptr(s.hwnd), uintptr(_SW_SHOW), }, - ret: ret, + ret: ret, } <-ret } @@ -256,12 +256,12 @@ func (s *sysData) hide() { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _showWindow, - p: []uintptr{ + call: _showWindow, + p: []uintptr{ uintptr(s.hwnd), uintptr(_SW_HIDE), }, - ret: ret, + ret: ret, } <-ret } @@ -271,15 +271,15 @@ func (s *sysData) setText(text string) { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _setWindowText, - p: []uintptr{ + call: _setWindowText, + p: []uintptr{ uintptr(s.hwnd), utf16ToArg(ptext), }, - ret: ret, + ret: ret, } r := <-ret - if r.ret == 0 { // failure + if r.ret == 0 { // failure panic(fmt.Errorf("error setting window/control text: %v", r.err)) } } @@ -292,7 +292,7 @@ func (s *sysData) setRect(x int, y int, width int, height int, winheight int) er uintptr(width), uintptr(height), uintptr(_TRUE)) - if r1 == 0 { // failure + if r1 == 0 { // failure return fmt.Errorf("error setting window/control rect: %v", err) } return nil @@ -302,14 +302,14 @@ func (s *sysData) isChecked() bool { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(_BM_GETCHECK), uintptr(0), uintptr(0), }, - ret: ret, + ret: ret, } r := <-ret return r.ret == _BST_CHECKED @@ -321,27 +321,27 @@ func (s *sysData) text() (str string) { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(_WM_GETTEXTLENGTH), uintptr(0), uintptr(0), }, - ret: ret, + ret: ret, } r := <-ret - length := r.ret + 1 // terminating null + length := r.ret + 1 // terminating null tc = make([]uint16, length) uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(_WM_GETTEXT), uintptr(_WPARAM(length)), uintptr(_LPARAM(unsafe.Pointer(&tc[0]))), }, - ret: ret, + ret: ret, } <-ret return syscall.UTF16ToString(tc) @@ -352,14 +352,14 @@ func (s *sysData) append(what string) { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(classTypes[s.ctype].appendMsg), uintptr(_WPARAM(0)), utf16ToLPARAM(pwhat), }, - ret: ret, + ret: ret, } r := <-ret if r.ret == uintptr(classTypes[s.ctype].addSpaceErr) { @@ -374,14 +374,14 @@ func (s *sysData) insertBefore(what string, index int) { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(classTypes[s.ctype].insertBeforeMsg), uintptr(_WPARAM(index)), utf16ToLPARAM(pwhat), }, - ret: ret, + ret: ret, } r := <-ret if r.ret == uintptr(classTypes[s.ctype].addSpaceErr) { @@ -395,24 +395,24 @@ func (s *sysData) selectedIndex() int { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(classTypes[s.ctype].selectedIndexMsg), uintptr(_WPARAM(0)), uintptr(_LPARAM(0)), }, - ret: ret, + ret: ret, } r := <-ret - if r.ret == uintptr(classTypes[s.ctype].selectedIndexErr) { // no selection or manually entered text (apparently, for the latter) + if r.ret == uintptr(classTypes[s.ctype].selectedIndexErr) { // no selection or manually entered text (apparently, for the latter) return -1 } return int(r.ret) } func (s *sysData) selectedIndices() []int { - if !s.alternate { // single-selection list box; use single-selection method + if !s.alternate { // single-selection list box; use single-selection method index := s.selectedIndex() if index == -1 { return nil @@ -423,32 +423,32 @@ func (s *sysData) selectedIndices() []int { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(_LB_GETSELCOUNT), uintptr(0), uintptr(0), }, - ret: ret, + ret: ret, } r := <-ret if r.ret == negConst(_LB_ERR) { panic("UI library internal error: LB_ERR from LB_GETSELCOUNT in what we know is a multi-selection listbox") } - if r.ret == 0 { // nothing selected + if r.ret == 0 { // nothing selected return nil } indices := make([]int, r.ret) uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(_LB_GETSELITEMS), uintptr(_WPARAM(r.ret)), uintptr(_LPARAM(unsafe.Pointer(&indices[0]))), }, - ret: ret, + ret: ret, } r = <-ret if r.ret == negConst(_LB_ERR) { @@ -464,14 +464,14 @@ func (s *sysData) selectedTexts() []string { strings := make([]string, len(indices)) for i, v := range indices { uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(_LB_GETTEXTLEN), uintptr(_WPARAM(v)), uintptr(0), }, - ret: ret, + ret: ret, } r := <-ret if r.ret == negConst(_LB_ERR) { @@ -479,14 +479,14 @@ func (s *sysData) selectedTexts() []string { } str := make([]uint16, r.ret) uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(_LB_GETTEXT), uintptr(_WPARAM(v)), uintptr(_LPARAM(unsafe.Pointer(&str[0]))), }, - ret: ret, + ret: ret, } r = <-ret if r.ret == negConst(_LB_ERR) { @@ -503,12 +503,12 @@ func (s *sysData) setWindowSize(width int, height int) error { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _getClientRect, - p: []uintptr{ + call: _getClientRect, + p: []uintptr{ uintptr(s.hwnd), uintptr(unsafe.Pointer(&rect)), }, - ret: ret, + ret: ret, } r := <-ret if r.ret == 0 { @@ -526,14 +526,14 @@ func (s *sysData) delete(index int) { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(classTypes[s.ctype].deleteMsg), uintptr(_WPARAM(index)), uintptr(0), }, - ret: ret, + ret: ret, } r := <-ret if r.ret == uintptr(classTypes[s.ctype].selectedIndexErr) { @@ -545,27 +545,27 @@ func (s *sysData) setIndeterminate() { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _setWindowLongPtr, - p: []uintptr{ + call: _setWindowLongPtr, + p: []uintptr{ uintptr(s.hwnd), negConst(_GWL_STYLE), uintptr(classTypes[s.ctype].style | _PBS_MARQUEE), }, - ret: ret, + ret: ret, } r := <-ret if r.ret == 0 { panic(fmt.Errorf("error setting progress bar style to enter indeterminate mode: %v", r.err)) } uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(_PBM_SETMARQUEE), uintptr(_WPARAM(_TRUE)), uintptr(0), }, - ret: ret, + ret: ret, } <-ret s.isMarquee = true @@ -581,24 +581,24 @@ func (s *sysData) setProgress(percent int) { if s.isMarquee { // turn off marquee before switching back uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(_PBM_SETMARQUEE), uintptr(_WPARAM(_FALSE)), uintptr(0), }, - ret: ret, + ret: ret, } <-ret uitask <- &uimsg{ - call: _setWindowLongPtr, - p: []uintptr{ + call: _setWindowLongPtr, + p: []uintptr{ uintptr(s.hwnd), negConst(_GWL_STYLE), uintptr(classTypes[s.ctype].style), }, - ret: ret, + ret: ret, } r := <-ret if r.ret == 0 { @@ -608,14 +608,14 @@ func (s *sysData) setProgress(percent int) { } send := func(msg uintptr, n int, l _LPARAM) { uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), msg, uintptr(_WPARAM(n)), uintptr(l), }, - ret: ret, + ret: ret, } <-ret } @@ -627,7 +627,7 @@ func (s *sysData) setProgress(percent int) { if percent == 100 { send(_PBM_SETRANGE32, 0, 101) } - send(_PBM_SETPOS, percent + 1, 0) + send(_PBM_SETPOS, percent+1, 0) send(_PBM_SETPOS, percent, 0) if percent == 100 { send(_PBM_SETRANGE32, 0, 100) @@ -638,14 +638,14 @@ func (s *sysData) len() int { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(classTypes[s.ctype].lenMsg), uintptr(_WPARAM(0)), uintptr(_LPARAM(0)), }, - ret: ret, + ret: ret, } r := <-ret if r.ret == uintptr(classTypes[s.ctype].selectedIndexErr) { @@ -658,14 +658,14 @@ func (s *sysData) setAreaSize(width int, height int) { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(msgSetAreaSize), - uintptr(width), // WPARAM is UINT_PTR on Windows XP and newer at least, so we're good with this + uintptr(width), // WPARAM is UINT_PTR on Windows XP and newer at least, so we're good with this uintptr(height), }, - ret: ret, + ret: ret, } <-ret } @@ -674,14 +674,14 @@ func (s *sysData) repaintAll() { ret := make(chan uiret) defer close(ret) uitask <- &uimsg{ - call: _sendMessage, - p: []uintptr{ + call: _sendMessage, + p: []uintptr{ uintptr(s.hwnd), uintptr(msgRepaintAll), uintptr(0), uintptr(0), }, - ret: ret, + ret: ret, } <-ret }