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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
|
// 8 april 2015
#include "uipriv_windows.hpp"
struct uiSpinbox {
uiWindowsControl c;
HWND hwnd;
HWND edit;
HWND updown;
void (*onChanged)(uiSpinbox *, void *);
void *onChangedData;
BOOL inhibitChanged;
};
// utility functions
static int value(uiSpinbox *s)
{
BOOL neededCap = FALSE;
LRESULT val;
// This verifies the value put in, capping it automatically.
// We don't need to worry about checking for an error; that flag should really be called "did we have to cap?".
// We DO need to set the value in case of a cap though.
val = SendMessageW(s->updown, UDM_GETPOS32, 0, (LPARAM) (&neededCap));
if (neededCap) {
s->inhibitChanged = TRUE;
SendMessageW(s->updown, UDM_SETPOS32, 0, (LPARAM) val);
s->inhibitChanged = FALSE;
}
return val;
}
// control implementation
static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult)
{
uiSpinbox *s = (uiSpinbox *) c;
WCHAR *wtext;
if (code != EN_CHANGE)
return FALSE;
if (s->inhibitChanged)
return FALSE;
// We want to allow typing negative numbers; the natural way to do so is to start with a -.
// However, if we just have the code below, the up-down will catch the bare - and reject it.
// Let's fix that.
// This won't handle leading spaces, but spaces aren't allowed *anyway*.
wtext = windowText(s->edit);
if (wcscmp(wtext, L"-") == 0) {
uiFree(wtext);
return TRUE;
}
uiFree(wtext);
// value() does the work for us
value(s);
(*(s->onChanged))(s, s->onChangedData);
return TRUE;
}
static void uiSpinboxDestroy(uiControl *c)
{
uiSpinbox *s = uiSpinbox(c);
uiWindowsUnregisterWM_COMMANDHandler(s->edit);
uiWindowsEnsureDestroyWindow(s->updown);
uiWindowsEnsureDestroyWindow(s->edit);
uiWindowsEnsureDestroyWindow(s->hwnd);
uiFreeControl(uiControl(s));
}
// TODO SyncEnableState
uiWindowsControlAllDefaultsExceptDestroy(uiSpinbox)
// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
// TODO reduce this?
#define entryWidth 107 /* this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary */
#define entryHeight 14
static void uiSpinboxMinimumSize(uiWindowsControl *c, int *width, int *height)
{
uiSpinbox *s = uiSpinbox(c);
uiWindowsSizing sizing;
int x, y;
x = entryWidth;
y = entryHeight;
// note that we go by the edit here
uiWindowsGetSizing(s->edit, &sizing);
uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y);
*width = x;
*height = y;
}
static void spinboxArrangeChildren(uiSpinbox *s)
{
LONG_PTR controlID;
HWND insertAfter;
controlID = 100;
insertAfter = NULL;
uiWindowsEnsureAssignControlIDZOrder(s->edit, &controlID, &insertAfter);
uiWindowsEnsureAssignControlIDZOrder(s->updown, &controlID, &insertAfter);
}
// 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 :(
static void recreateUpDown(uiSpinbox *s)
{
BOOL preserve = FALSE;
int current;
// Microsoft's commctrl.h says to use this type
INT min, max;
if (s->updown != NULL) {
preserve = TRUE;
current = value(s);
SendMessageW(s->updown, UDM_GETRANGE32, (WPARAM) (&min), (LPARAM) (&max));
uiWindowsEnsureDestroyWindow(s->updown);
}
s->inhibitChanged = TRUE;
s->updown = CreateWindowExW(0,
UPDOWN_CLASSW, L"",
// no WS_VISIBLE; we set visibility ourselves
// up-down control should not be a tab stop
WS_CHILD | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_HOTTRACK | UDS_NOTHOUSANDS | UDS_SETBUDDYINT,
// this is important; it's necessary for autosizing to work
0, 0, 0, 0,
s->hwnd, NULL, hInstance, NULL);
if (s->updown == NULL)
logLastError(L"error creating updown");
SendMessageW(s->updown, UDM_SETBUDDY, (WPARAM) (s->edit), 0);
if (preserve) {
SendMessageW(s->updown, UDM_SETRANGE32, (WPARAM) min, (LPARAM) max);
SendMessageW(s->updown, UDM_SETPOS32, 0, (LPARAM) current);
}
// preserve the Z-order
spinboxArrangeChildren(s);
// TODO properly show/enable
ShowWindow(s->updown, SW_SHOW);
s->inhibitChanged = FALSE;
}
static void spinboxRelayout(uiSpinbox *s)
{
RECT r;
// make the edit fill the container first; the new updown will resize it
uiWindowsEnsureGetClientRect(s->hwnd, &r);
uiWindowsEnsureMoveWindowDuringResize(s->edit, r.left, r.top, r.right - r.left, r.bottom - r.top);
recreateUpDown(s);
}
static void defaultOnChanged(uiSpinbox *s, void *data)
{
// do nothing
}
int uiSpinboxValue(uiSpinbox *s)
{
return value(s);
}
void uiSpinboxSetValue(uiSpinbox *s, int value)
{
s->inhibitChanged = TRUE;
SendMessageW(s->updown, UDM_SETPOS32, 0, (LPARAM) value);
s->inhibitChanged = FALSE;
}
void uiSpinboxOnChanged(uiSpinbox *s, void (*f)(uiSpinbox *, void *), void *data)
{
s->onChanged = f;
s->onChangedData = data;
}
static void onResize(uiWindowsControl *c)
{
spinboxRelayout(uiSpinbox(c));
}
uiSpinbox *uiNewSpinbox(int min, int max)
{
uiSpinbox *s;
int temp;
if (min >= max) {
temp = min;
min = max;
max = temp;
}
uiWindowsNewControl(uiSpinbox, s);
s->hwnd = uiWindowsMakeContainer(uiWindowsControl(s), onResize);
s->edit = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE,
L"edit", L"",
// don't use ES_NUMBER; it doesn't allow typing in a leading -
ES_AUTOHSCROLL | ES_LEFT | ES_NOHIDESEL | WS_TABSTOP,
hInstance, NULL,
TRUE);
uiWindowsEnsureSetParentHWND(s->edit, s->hwnd);
uiWindowsRegisterWM_COMMANDHandler(s->edit, onWM_COMMAND, uiControl(s));
uiSpinboxOnChanged(s, defaultOnChanged, NULL);
recreateUpDown(s);
s->inhibitChanged = TRUE;
SendMessageW(s->updown, UDM_SETRANGE32, (WPARAM) min, (LPARAM) max);
SendMessageW(s->updown, UDM_SETPOS32, 0, (LPARAM) min);
s->inhibitChanged = FALSE;
return s;
}
|