summaryrefslogtreecommitdiff
path: root/newctrl/container_windows.go
blob: fe40f01b613b7c5ca0eab0b030e9e10fc6896c93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// 4 august 2014

package ui

import (
	"fmt"
	"syscall"
	"unsafe"
)

// #include "winapi_windows.h"
import "C"

type container struct {
	*controlSingleHWND
}

type sizing struct {
	sizingbase

	// for size calculations
	baseX           C.int
	baseY           C.int
	internalLeading C.LONG // for Label; see Label.commitResize() for details

	// for the actual resizing
	// possibly the HDWP
}

func makeContainerWindowClass() error {
	var errmsg *C.char

	err := C.makeContainerWindowClass(&errmsg)
	if err != 0 || errmsg != nil {
		return fmt.Errorf("%s: %v", C.GoString(errmsg), syscall.Errno(err))
	}
	return nil
}

func newContainer() *container {
	c := new(container)
	hwnd := C.newContainer(unsafe.Pointer(c))
	if hwnd != c.hwnd {
		panic(fmt.Errorf("inconsistency: hwnd returned by CreateWindowEx() (%p) and hwnd stored in container (%p) differ", hwnd, c.hwnd))
	}
	// don't set preferredSize(); it should never be called
	return c
}

// TODO merge with controlSingleHWND
func (c *container) show() {
	C.ShowWindow(c.hwnd, C.SW_SHOW)
}

// TODO merge with controlSingleHWND
func (c *container) hide() {
	C.ShowWindow(c.hwnd, C.SW_HIDE)
}

func (c *container) parent() *controlParent {
	return &controlParent{c.hwnd}
}

//export storeContainerHWND
func storeContainerHWND(data unsafe.Pointer, hwnd C.HWND) {
	c := (*container)(data)
	c.hwnd = hwnd
}

// 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/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)
// In my tests (see https://github.com/andlabs/windlgunits), the GetTextExtentPoint32() option for getting the base X produces much more accurate results than the tmAveCharWidth option when tested against the sample values given in http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing, but can be off by a pixel in either direction (probably due to rounding errors).

// note on MulDiv():
// div will not be 0 in the usages below
// we also ignore overflow; that isn't likely to happen for our use case anytime soon

func fromdlgunitsX(du int, d *sizing) int {
	return int(C.MulDiv(C.int(du), d.baseX, 4))
}

func fromdlgunitsY(du int, d *sizing) int {
	return int(C.MulDiv(C.int(du), d.baseY, 8))
}

const (
	// shared by multiple containers
	marginDialogUnits  = 7
	paddingDialogUnits = 4
)

func (w *window) beginResize() (d *sizing) {
	var baseX, baseY C.int
	var internalLeading C.LONG

	d = new(sizing)

	C.calculateBaseUnits(w.hwnd, &baseX, &baseY, &internalLeading)
	d.baseX = baseX
	d.baseY = baseY
	d.internalLeading = internalLeading

	d.xpadding = fromdlgunitsX(paddingDialogUnits, d)
	d.ypadding = fromdlgunitsY(paddingDialogUnits, d)

/*TODO
	if c.isGroup {
		// note that these values apply regardless of whether or not spaced is set
		// this is because Windows groupboxes have the client rect spanning the entire size of the control, not just the active work area
		// the measurements Microsoft give us are for spaced margining; let's just use them
		d.xmargin = fromdlgunitsX(groupXMargin, d)
		d.ymargintop = fromdlgunitsY(groupYMarginTop, d)
		d.ymarginbottom = fromdlgunitsY(groupYMarginBottom, d)

	}
*/

	return d
}

func marginRectDLU(r *C.RECT, top int, bottom int, left int, right int, d *sizing) {
	r.left += C.LONG(fromdlgunitsX(left, d))
	r.top += C.LONG(fromdlgunitsY(top, d))
	r.right -= C.LONG(fromdlgunitsX(right, d))
	r.bottom -= C.LONG(fromdlgunitsY(bottom, d))
}