2014-02-24 12:22:59 -06:00
// 24 february 2014
2014-03-12 20:55:45 -05:00
2014-02-24 12:22:59 -06:00
package ui
import (
2014-03-09 18:34:43 -05:00
"fmt"
2014-02-24 12:22:59 -06:00
"unsafe"
)
2014-06-25 22:05:29 -05:00
type sysSizeData struct {
cSysSizeData
// for size calculations
baseX int
baseY int
// for the actual resizing
// possibly the HDWP
}
const (
marginDialogUnits = 7
paddingDialogUnits = 4
2014-06-25 22:21:57 -05:00
)
2014-06-25 22:05:29 -05:00
func ( s * sysData ) beginResize ( ) ( d * sysSizeData ) {
d = new ( sysSizeData )
dc := getTextDC ( s . hwnd )
2014-06-25 22:21:57 -05:00
defer releaseTextDC ( s . hwnd , dc )
var tm _TEXTMETRICS
2014-06-25 22:05:29 -05:00
2014-06-25 22:21:57 -05:00
r1 , _ , err := _getTextMetrics . Call (
2014-06-25 22:05:29 -05:00
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 )
2014-06-26 09:51:26 -05:00
d . ypadding = muldiv ( paddingDialogUnits , d . baseY , 8 )
2014-06-25 22:05:29 -05:00
}
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
2014-06-25 22:21:57 -05:00
s . setRect ( c . x , c . y , c . width , c . height , 0 )
2014-06-25 22:05:29 -05:00
}
func ( s * sysData ) getAuxResizeInfo ( d * sysSizeData ) {
// do nothing
}
2014-02-24 12:22:59 -06:00
// 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
2014-02-24 14:02:58 -06:00
// (thanks to http://stackoverflow.com/questions/58620/default-button-size)
2014-04-03 20:00:38 -05:00
// 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.
2014-02-24 12:22:59 -06:00
// As we are left with incomplete data, an arbitrary size will be chosen
const (
2014-06-10 10:23:00 -05:00
defaultWidth = 100 // 2 * preferred width of buttons
2014-02-24 12:22:59 -06:00
)
type dlgunits struct {
2014-06-10 10:23:00 -05:00
width int
height int
longest bool // TODO actually use this
getsize uintptr
area bool // use area sizes instead
2014-06-25 11:20:59 -05:00
yoff int
yoffalt int
2014-02-24 12:22:59 -06:00
}
var stdDlgSizes = [ nctypes ] dlgunits {
2014-06-10 10:23:00 -05:00
c_button : dlgunits {
width : 50 ,
height : 14 ,
getsize : _BCM_GETIDEALSIZE ,
2014-02-24 12:22:59 -06:00
} ,
2014-06-10 10:23:00 -05:00
c_checkbox : dlgunits {
2014-02-24 12:22:59 -06:00
// widtdh is not defined here so assume longest
2014-06-10 10:23:00 -05:00
longest : true ,
height : 10 ,
2014-02-24 12:22:59 -06:00
} ,
2014-06-10 10:23:00 -05:00
c_combobox : dlgunits {
2014-04-10 11:53:23 -05:00
// 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
2014-06-10 10:23:00 -05:00
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
2014-02-24 12:22:59 -06:00
} ,
2014-06-10 10:23:00 -05:00
c_lineedit : dlgunits {
longest : true ,
height : 14 ,
2014-02-24 12:22:59 -06:00
} ,
2014-06-10 10:23:00 -05:00
c_label : dlgunits {
longest : true ,
height : 8 ,
2014-06-25 11:20:59 -05:00
yoff : 3 ,
yoffalt : 0 ,
2014-02-24 12:22:59 -06:00
} ,
2014-06-10 10:23:00 -05:00
c_listbox : dlgunits {
longest : true ,
2014-02-24 12:22:59 -06:00
// height is not clearly defined here ("an integral number of items (3 items minimum)") so just use a three-line edit control
2014-06-10 10:23:00 -05:00
height : 14 + 10 + 10 ,
2014-02-24 12:22:59 -06:00
} ,
2014-06-10 10:23:00 -05:00
c_progressbar : dlgunits {
width : 237 , // the first reference says 107 also works; TODO decide which to use
height : 8 ,
2014-02-25 00:02:16 -06:00
} ,
2014-06-10 10:23:00 -05:00
c_area : dlgunits {
area : true ,
2014-06-09 15:56:19 -05:00
} ,
2014-02-24 12:22:59 -06:00
}
var (
2014-06-10 10:23:00 -05:00
_selectObject = gdi32 . NewProc ( "SelectObject" )
_getDC = user32 . NewProc ( "GetDC" )
2014-04-13 01:57:35 -05:00
_getTextExtentPoint32 = gdi32 . NewProc ( "GetTextExtentPoint32W" )
2014-06-10 10:23:00 -05:00
_getTextMetrics = gdi32 . NewProc ( "GetTextMetricsW" )
_releaseDC = user32 . NewProc ( "ReleaseDC" )
2014-02-24 12:22:59 -06:00
)
2014-06-25 22:05:29 -05:00
func getTextDC ( hwnd _HWND ) ( dc _HANDLE ) {
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 ) {
2014-06-25 22:21:57 -05:00
r1 , _ , err := _releaseDC . Call (
2014-06-25 22:05:29 -05:00
uintptr ( hwnd ) ,
uintptr ( dc ) )
if r1 == 0 { // failure
panic ( fmt . Errorf ( "error releasing DC for preferred size calculations: %v" , err ) )
}
}
func ( s * sysData ) preferredSize ( d * sysSizeData ) ( width int , height int ) {
2014-06-06 22:03:29 -05:00
// the preferred size of an Area is its size
2014-06-09 15:56:19 -05:00
if stdDlgSizes [ s . ctype ] . area {
2014-06-25 22:21:57 -05:00
return s . areawidth , s . areaheight
2014-06-06 22:03:29 -05:00
}
2014-04-03 20:00:38 -05:00
if msg := stdDlgSizes [ s . ctype ] . getsize ; msg != 0 {
var size _SIZE
r1 , _ , _ := _sendMessage . Call (
uintptr ( s . hwnd ) ,
msg ,
uintptr ( 0 ) ,
uintptr ( unsafe . Pointer ( & size ) ) )
2014-06-10 10:23:00 -05:00
if r1 != uintptr ( _FALSE ) { // success
2014-06-25 22:21:57 -05:00
return int ( size . cx ) , int ( size . cy )
2014-04-03 20:00:38 -05:00
}
// otherwise the message approach failed, so fall back to the regular approach
println ( "message failed; falling back" )
}
2014-02-24 12:22:59 -06:00
width = stdDlgSizes [ s . ctype ] . width
if width == 0 {
width = defaultWidth
}
height = stdDlgSizes [ s . ctype ] . height
2014-06-25 22:05:29 -05:00
width = muldiv ( width , d . baseX , 4 ) // equivalent to right of rect
height = muldiv ( height , d . baseY , 8 ) // equivalent to bottom of rect
2014-06-25 11:20:59 -05:00
2014-06-25 22:05:29 -05:00
return width , height
2014-02-24 12:22:59 -06:00
}
2014-04-02 09:43:28 -05:00
var (
_mulDiv = kernel32 . NewProc ( "MulDiv" )
)
2014-02-24 12:22:59 -06:00
func muldiv ( ma int , mb int , div int ) int {
2014-04-02 09:43:28 -05:00
// 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 ) )
2014-02-24 12:22:59 -06:00
}
2014-04-03 20:00:38 -05:00
type _SIZE struct {
2014-06-10 10:23:00 -05:00
cx int32 // originally LONG
cy int32
2014-04-03 20:00:38 -05:00
}
2014-02-24 12:22:59 -06:00
type _TEXTMETRICS struct {
2014-06-10 10:23:00 -05:00
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
2014-02-24 12:22:59 -06:00
}