diff options
| author | Pietro Gagliardi <[email protected]> | 2014-06-25 23:05:29 -0400 |
|---|---|---|
| committer | Pietro Gagliardi <[email protected]> | 2014-06-25 23:05:29 -0400 |
| commit | 9a3e73b46001219177bb13a381420007217dfecf (patch) | |
| tree | aada49b967ea0bcd9ca220011e9854a54cd4149a /controlsize_windows.go | |
| parent | 92afc9b9447b4ce8961a0c9a02802c5300f886b5 (diff) | |
Finished the Windows conversion to the new sizing system. Untested.
Diffstat (limited to 'controlsize_windows.go')
| -rw-r--r-- | controlsize_windows.go | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/controlsize_windows.go b/controlsize_windows.go new file mode 100644 index 0000000..3bb3ecb --- /dev/null +++ b/controlsize_windows.go @@ -0,0 +1,251 @@ +// 24 february 2014 + +package ui + +import ( + "fmt" + "unsafe" +) + +type sysSizeData struct { + cSysSizeData + + // for size calculations + baseX int + baseY int + + // for the actual resizing + // possibly the HDWP +} + +const ( + marginDialogUnits = 7 + paddingDialogUnits = 4 +} + +func (s *sysData) beginResize() (d *sysSizeData) { + d = new(sysSizeData) + + dc := getTextDC(s.hwnd) + defer releaseTextDC(dc) + + r1, _, err = _getTextMetrics.Call( + uintptr(dc), + uintptr(unsafe.Pointer(&tm))) + if r1 == 0 { // failure + panic(fmt.Errorf("error getting text metrics for preferred size calculations: %v", err)) + } + d.baseX = int(tm.tmAveCharWidth) // TODO not optimal; third reference has better way + d.baseY = int(tm.tmHeight) + + if s.spaced { + d.xmargin = muldiv(marginDialogUnits, d.baseX, 4) + d.ymargin = muldiv(marginDialogUnits, d.baseY, 8) + d.xpadding = muldiv(paddingDialogUnits, d.baseX, 4) + d.xpadding = muldiv(paddingDialogUnits, d.baseY, 8) + } + + return d +} + +func (s *sysData) endResize(d *sysSizeData) { + // redraw +} + +func (s *sysData) translateAllocationCoords(allocations []*allocation, winwidth, winheight int) { + // no translation needed on windows +} + +func (s *sysData) commitResize(c *allocation, d *sysSizeData) { + yoff := stdDlgSizes[s.ctype].yoff + if s.alternate { + yoff = stdDlgSizes[s.ctype].yoffalt + } + if yoff != 0 { + yoff = muldiv(yoff, d.baseY, 8) + } + c.y += yoff + // TODO move this here + s.setRect(c.x, c.y, c.width, c.height) +} + +func (s *sysData) getAuxResizeInfo(d *sysSizeData) { + // 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. +// References: +// - http://msdn.microsoft.com/en-us/library/windows/desktop/aa511279.aspx#controlsizing for control sizes +// - http://msdn.microsoft.com/en-us/library/ms645502%28VS.85%29.aspx - the calculation needed +// - http://support.microsoft.com/kb/125681 - to get the base X and Y +// (thanks to http://stackoverflow.com/questions/58620/default-button-size) +// For push buttons, date/time pickers, links (which we don't use), toolbars, and rebars (another type of toolbar), Common Controls version 6 provides convenient methods to use instead, falling back to the old way if it fails. + +// As we are left with incomplete data, an arbitrary size will be chosen +const ( + defaultWidth = 100 // 2 * preferred width of buttons +) + +type dlgunits struct { + width int + height int + longest bool // TODO actually use this + getsize uintptr + area bool // use area sizes instead + yoff int + yoffalt int +} + +var stdDlgSizes = [nctypes]dlgunits{ + c_button: dlgunits{ + width: 50, + height: 14, + getsize: _BCM_GETIDEALSIZE, + }, + c_checkbox: dlgunits{ + // widtdh is not defined here so assume longest + longest: true, + height: 10, + }, + c_combobox: dlgunits{ + // technically the height of a combobox has to include the drop-down list (this is a historical accident: originally comboboxes weren't drop-down) + // but since we're forcing Common Controls version 6, we can take advantage of one of its mechanisms to automatically fix this mistake (bad practice but whatever) + // see also: http://blogs.msdn.com/b/oldnewthing/archive/2006/03/10/548537.aspx + // note that the Microsoft guidelines pages don't take the list size into account + longest: true, + height: 12, // from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx; the page linked above says 14 + }, + c_lineedit: dlgunits{ + longest: true, + height: 14, + }, + c_label: dlgunits{ + longest: true, + height: 8, + yoff: 3, + yoffalt: 0, + }, + c_listbox: dlgunits{ + longest: true, + // height is not clearly defined here ("an integral number of items (3 items minimum)") so just use a three-line edit control + height: 14 + 10 + 10, + }, + c_progressbar: dlgunits{ + width: 237, // the first reference says 107 also works; TODO decide which to use + height: 8, + }, + c_area: dlgunits{ + area: true, + }, +} + +var ( + _selectObject = gdi32.NewProc("SelectObject") + _getDC = user32.NewProc("GetDC") + _getTextExtentPoint32 = gdi32.NewProc("GetTextExtentPoint32W") + _getTextMetrics = gdi32.NewProc("GetTextMetricsW") + _releaseDC = user32.NewProc("ReleaseDC") +) + +func getTextDC(hwnd _HWND) (dc _HANDLE) { + var tm _TEXTMETRICS + + r1, _, err := _getDC.Call(uintptr(hwnd)) + if r1 == 0 { // failure + panic(fmt.Errorf("error getting DC for preferred size calculations: %v", err)) + } + dc = _HANDLE(r1) + r1, _, err = _selectObject.Call( + uintptr(dc), + uintptr(controlFont)) + if r1 == 0 { // failure + panic(fmt.Errorf("error loading control font into device context for preferred size calculation: %v", err)) + } + return dc +} + +func releaseTextDC(hwnd _HWND, dc _HANDLE) { + r1, _, err = _releaseDC.Call( + uintptr(hwnd), + uintptr(dc)) + if r1 == 0 { // failure + panic(fmt.Errorf("error releasing DC for preferred size calculations: %v", err)) + } +} + +// This function runs on uitask; call the functions directly. +func (s *sysData) preferredSize(d *sysSizeData) (width int, height int) { + // the preferred size of an Area is its size + if stdDlgSizes[s.ctype].area { + return s.areawidth, s.areaheight, 0 // no yoff for areas + } + + if msg := stdDlgSizes[s.ctype].getsize; msg != 0 { + var size _SIZE + + r1, _, _ := _sendMessage.Call( + uintptr(s.hwnd), + msg, + uintptr(0), + uintptr(unsafe.Pointer(&size))) + if r1 != uintptr(_FALSE) { // success + return int(size.cx), int(size.cy), 0 // TODO + } + // otherwise the message approach failed, so fall back to the regular approach + println("message failed; falling back") + } + + width = stdDlgSizes[s.ctype].width + if width == 0 { + width = defaultWidth + } + height = stdDlgSizes[s.ctype].height + width = muldiv(width, d.baseX, 4) // equivalent to right of rect + height = muldiv(height, d.baseY, 8) // equivalent to bottom of rect + + return width, height +} + +var ( + _mulDiv = kernel32.NewProc("MulDiv") +) + +func muldiv(ma int, mb int, div int) int { + // div will not be 0 in the usages above + // we also ignore overflow; that isn't likely to happen for our use case anytime soon + r1, _, _ := _mulDiv.Call( + uintptr(int32(ma)), + uintptr(int32(mb)), + uintptr(int32(div))) + return int(int32(r1)) +} + +type _SIZE struct { + cx int32 // originally LONG + cy int32 +} + +type _TEXTMETRICS struct { + tmHeight int32 + tmAscent int32 + tmDescent int32 + tmInternalLeading int32 + tmExternalLeading int32 + tmAveCharWidth int32 + tmMaxCharWidth int32 + tmWeight int32 + tmOverhang int32 + tmDigitizedAspectX int32 + tmDigitizedAspectY int32 + tmFirstChar uint16 + tmLastChar uint16 + tmDefaultChar uint16 + tmBreakChar uint16 + tmItalic byte + tmUnderlined byte + tmStruckOut byte + tmPitchAndFamily byte + tmCharSet byte +} |
