forked from andlabs/ui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
spinbox_windows.go
148 lines (129 loc) · 4.34 KB
/
spinbox_windows.go
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// 28 october 2014
package ui
import (
"strconv"
"unsafe"
)
// #include "winapi_windows.h"
import "C"
// TODO do we have to manually monitor user changes to the edit control?
// TODO WS_EX_CLIENTEDGE on the updown?
type spinbox struct {
hwndEdit C.HWND
hwndUpDown C.HWND
changed *event
// updown state
updownVisible bool
// keep these here to avoid having to get them out
value int
min int
max int
}
func newSpinbox(min int, max int) Spinbox {
s := new(spinbox)
s.hwndEdit = C.newControl(editclass,
C.textfieldStyle | C.ES_NUMBER,
C.textfieldExtStyle)
s.changed = newEvent()
s.updownVisible = true // initially shown
s.min = min
s.max = max
s.value = s.min
s.remakeUpDown()
C.controlSetControlFont(s.hwndEdit)
C.setSpinboxEditSubclass(s.hwndEdit, unsafe.Pointer(s))
return s
}
func (s *spinbox) cap() {
if s.value < s.min {
s.value = s.min
}
if s.value > s.max {
s.value = s.max
}
}
func (s *spinbox) Value() int {
return s.value
}
func (s *spinbox) SetValue(value int) {
// UDM_SETPOS32 is documented to do what we want, but since we're keeping a copy of value we need to do it anyway
s.value = value
s.cap()
C.SendMessageW(s.hwndUpDown, C.UDM_SETPOS32, 0, C.LPARAM(s.value))
}
func (s *spinbox) OnChanged(e func()) {
s.changed.set(e)
}
//export spinboxUpDownClicked
func spinboxUpDownClicked(data unsafe.Pointer, nud *C.NMUPDOWN) {
// this is where we do custom increments
s := (*spinbox)(data)
s.value = int(nud.iPos + nud.iDelta)
// this can go above or below the bounds (the spinbox only rejects invalid values after the UDN_DELTAPOS notification is processed)
// because we have a copy of the value, we need to fix that here
s.cap()
s.changed.fire()
}
//export spinboxEditChanged
func spinboxEditChanged(data unsafe.Pointer) {
// we're basically on our own here
s := (*spinbox)(unsafe.Pointer(data))
// this basically does what OS X does: values too low get clamped to the minimum, values too high get clamped to the maximum, and deleting everything clamps to the minimum
value, err := strconv.Atoi(getWindowText(s.hwndEdit))
if err != nil {
// best we can do fo rnow in this case :S
// a partial atoi() like in C would be more optimal
// it handles the deleting everything case just fine
value = s.min
}
s.value = value
s.cap()
C.SendMessageW(s.hwndUpDown, C.UDM_SETPOS32, 0, C.LPARAM(s.value))
// TODO position the insertion caret at the end (or wherever is appropriate)
s.changed.fire()
}
func (s *spinbox) setParent(p *controlParent) {
C.controlSetParent(s.hwndEdit, p.hwnd)
C.controlSetParent(s.hwndUpDown, p.hwnd)
}
// an up-down control will only properly position itself the first time
// stupidly, there are no messages to force a size calculation, nor can I seem to reset the buddy window to force a new position
// alas, we have to make a new up/down control each time :(
// TODO we'll need to store a copy of the current position and range for this
func (s *spinbox) remakeUpDown() {
// destroying the previous one, setting the parent properly, and subclassing are handled here
s.hwndUpDown = C.newUpDown(s.hwndUpDown, unsafe.Pointer(s))
// for this to work, hwndUpDown needs to have rect [0 0 0 0]
C.moveWindow(s.hwndUpDown, 0, 0, 0, 0)
C.SendMessageW(s.hwndUpDown, C.UDM_SETBUDDY, C.WPARAM(uintptr(unsafe.Pointer(s.hwndEdit))), 0)
C.SendMessageW(s.hwndUpDown, C.UDM_SETRANGE32, C.WPARAM(s.min), C.LPARAM(s.max))
C.SendMessageW(s.hwndUpDown, C.UDM_SETPOS32, 0, C.LPARAM(s.value))
if s.updownVisible {
C.ShowWindow(s.hwndUpDown, C.SW_SHOW)
}
}
// use the same height as normal text fields
// TODO constrain the width somehow
func (s *spinbox) preferredSize(d *sizing) (width, height int) {
return fromdlgunitsX(textfieldWidth, d), fromdlgunitsY(textfieldHeight, d)
}
func (s *spinbox) resize(x int, y int, width int, height int, d *sizing) {
C.moveWindow(s.hwndEdit, C.int(x), C.int(y), C.int(width), C.int(height))
s.remakeUpDown()
}
func (s *spinbox) nTabStops() int {
// TODO does the up-down control count?
return 1
}
// TODO be sure to modify this when we add Show()/Hide()
func (s *spinbox) containerShow() {
C.ShowWindow(s.hwndEdit, C.SW_SHOW)
C.ShowWindow(s.hwndUpDown, C.SW_SHOW)
s.updownVisible = true
}
// TODO be sure to modify this when we add Show()/Hide()
func (s *spinbox) containerHide() {
C.ShowWindow(s.hwndEdit, C.SW_HIDE)
C.ShowWindow(s.hwndUpDown, C.SW_HIDE)
s.updownVisible = false
}