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
|
|
|
// "syscall"
|
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
|
|
|
// 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-02-24 12:22:59 -06:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
var stdDlgSizes = [nctypes]dlgunits{
|
|
|
|
c_button: dlgunits{
|
|
|
|
width: 50,
|
|
|
|
height: 14,
|
|
|
|
},
|
|
|
|
c_checkbox: dlgunits{
|
|
|
|
// widtdh is not defined here so assume longest
|
|
|
|
longest: true,
|
|
|
|
height: 10,
|
|
|
|
},
|
|
|
|
c_combobox: dlgunits{
|
|
|
|
longest: true,
|
|
|
|
height: 14,
|
|
|
|
},
|
|
|
|
c_lineedit: dlgunits{
|
|
|
|
longest: true,
|
|
|
|
height: 14,
|
|
|
|
},
|
|
|
|
c_label: dlgunits{
|
|
|
|
longest: true,
|
|
|
|
height: 8,
|
|
|
|
},
|
|
|
|
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,
|
|
|
|
},
|
2014-02-25 00:02:16 -06:00
|
|
|
c_progressbar: dlgunits{
|
|
|
|
width: 237, // the first reference says 107 also works; TODO decide which to use
|
|
|
|
height: 8,
|
|
|
|
},
|
2014-02-24 12:22:59 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2014-04-01 20:17:27 -05:00
|
|
|
_selectObject = gdi32.NewProc("SelectObject")
|
2014-02-24 13:16:05 -06:00
|
|
|
_getTextMetrics = gdi32.NewProc("GetTextMetricsW")
|
2014-02-24 12:22:59 -06:00
|
|
|
_getWindowDC = user32.NewProc("GetWindowDC")
|
|
|
|
_releaseDC = user32.NewProc("ReleaseDC")
|
|
|
|
)
|
|
|
|
|
2014-02-24 13:17:37 -06:00
|
|
|
// This function runs on uitask; call the functions directly.
|
2014-02-24 12:22:59 -06:00
|
|
|
func (s *sysData) preferredSize() (width int, height int) {
|
|
|
|
var dc _HANDLE
|
|
|
|
var tm _TEXTMETRICS
|
|
|
|
var baseX, baseY int
|
|
|
|
|
2014-02-24 13:17:37 -06:00
|
|
|
// TODO use GetDC() and not GetWindowDC()?
|
2014-02-24 13:12:02 -06:00
|
|
|
r1, _, err := _getWindowDC.Call(uintptr(s.hwnd))
|
|
|
|
if r1 == 0 { // failure
|
2014-03-09 18:34:43 -05:00
|
|
|
panic(fmt.Errorf("error getting DC for preferred size calculations: %v", err))
|
2014-02-24 12:22:59 -06:00
|
|
|
}
|
2014-02-24 13:12:02 -06:00
|
|
|
dc = _HANDLE(r1)
|
2014-04-01 20:17:27 -05:00
|
|
|
r1, _, err = _selectObject.Call(
|
|
|
|
uintptr(dc),
|
2014-04-01 20:24:20 -05:00
|
|
|
uintptr(controlFont))
|
2014-04-01 20:17:27 -05:00
|
|
|
if r1 == 0 { // failure
|
|
|
|
panic(fmt.Errorf("error loading control font into device context for preferred size calculation: %v", err))
|
|
|
|
}
|
2014-02-24 13:12:02 -06:00
|
|
|
r1, _, err = _getTextMetrics.Call(
|
|
|
|
uintptr(dc),
|
|
|
|
uintptr(unsafe.Pointer(&tm)))
|
|
|
|
if r1 == 0 { // failure
|
2014-03-09 18:34:43 -05:00
|
|
|
panic(fmt.Errorf("error getting text metrics for preferred size calculations: %v", err))
|
2014-02-24 12:22:59 -06:00
|
|
|
}
|
|
|
|
baseX = int(tm.tmAveCharWidth) // TODO not optimal; third reference has better way
|
|
|
|
baseY = int(tm.tmHeight)
|
2014-02-24 13:12:02 -06:00
|
|
|
r1, _, err = _releaseDC.Call(
|
|
|
|
uintptr(s.hwnd),
|
|
|
|
uintptr(dc))
|
|
|
|
if r1 == 0 { // failure
|
2014-03-09 18:34:43 -05:00
|
|
|
panic(fmt.Errorf("error releasing DC for preferred size calculations: %v", err))
|
2014-02-24 12:22:59 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// now that we have the conversion factors...
|
|
|
|
width = stdDlgSizes[s.ctype].width
|
|
|
|
if width == 0 {
|
|
|
|
width = defaultWidth
|
|
|
|
}
|
|
|
|
height = stdDlgSizes[s.ctype].height
|
|
|
|
width = muldiv(width, baseX, 4) // equivalent to right of rect
|
|
|
|
height = muldiv(height, baseY, 8) // equivalent to bottom of rect
|
|
|
|
return width, height
|
|
|
|
}
|
|
|
|
|
|
|
|
// attempts to mimic the behavior of kernel32.MulDiv()
|
|
|
|
// caling it directly would be better (TODO)
|
|
|
|
// alternatively TODO make sure the rounding is correct
|
|
|
|
func muldiv(ma int, mb int, div int) int {
|
|
|
|
xa := int64(ma) * int64(mb)
|
|
|
|
xa /= int64(div)
|
|
|
|
return int(xa)
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|