-
Notifications
You must be signed in to change notification settings - Fork 16
/
OptionsScroller.cpp
250 lines (232 loc) · 6.46 KB
/
OptionsScroller.cpp
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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#include "OptionsScroller.h"
#include "OptionsCanvas.h"
#include "Resource.h"
#include "BoolOptionUI.h"
#include <TCFoundation/mystring.h>
#include <TCFoundation/TCLocalStrings.h>
#include <CUI/CUIWindowResizer.h>
#if defined(_MSC_VER) && _MSC_VER >= 1400 && defined(_DEBUG)
#define new DEBUG_CLIENTBLOCK
#endif // _DEBUG
OptionsScroller::OptionsScroller(HINSTANCE hInstance):
CUIDialog(hInstance),
m_canvas(NULL),
m_style(0),
m_scrolls(false),
m_scrollBarWidth(GetSystemMetrics(SM_CXVSCROLL)),
m_y(0)
{
}
OptionsScroller::~OptionsScroller(void)
{
}
void OptionsScroller::dealloc(void)
{
TCObject::release(m_canvas);
CUIDialog::dealloc();
}
void OptionsScroller::create(CUIWindow *parent)
{
::CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_OPTIONS_SCROLLER),
parent->getHWindow(), staticDialogProc, (LPARAM)this);
}
BOOL OptionsScroller::doInitDialog(HWND /*hKbControl*/)
{
m_style = GetWindowLong(hWindow, GWL_STYLE);
m_canvas = new OptionsCanvas(hInstance);
m_canvas->create(this);
ShowWindow(m_canvas->getHWindow(), SW_SHOW);
return TRUE;
}
void OptionsScroller::moveCanvas(int lwidth, int lheight, int myHeight)
{
if (m_y + myHeight > lheight && m_y > 0)
{
// Given the new width/height, m_y is too big, so adjust it.
m_y = lheight - myHeight;
if (m_y < 0)
{
m_y = 0;
}
}
MoveWindow(m_canvas->getHWindow(), 0, -m_y, lwidth, lheight, TRUE);
}
LRESULT OptionsScroller::doSize(
WPARAM /*sizeType*/,
int newWidth,
int newHeight)
{
int lwidth = newWidth;
int lheight;
bool scrollNeeded;
int optimalWidth = 0;
if (m_scrolls)
{
// If we currently have a scroll bar, newWidth will contain the width
// of our window to the left of the scroll bar. In order to see if the
// canvas fits without a scroll bar, we need to have it calulate its
// size based on the full window width, so add the width of the
// scrollbar onto width, which is used when calculating the canvas's
// needed height.
lwidth += m_scrollBarWidth;
}
// First, figure out how tall m_canvas needs to be for the given width that
// is available to it after the resize. This is done purely to decide if
// we need a scroll bar. The actual canvas layout isn't updated.
lheight = m_canvas->calcHeight(lwidth, optimalWidth);
if (lheight > newHeight)
{
// We need a vertical scroll bar. Pull the width of the scrollbar off
// of width, which will be used below as the new width of the canvas
// window.
lwidth -= m_scrollBarWidth;
scrollNeeded = true;
// Since width changed, height may have also changed, so recalculate
// the height based on the new width.
lheight = m_canvas->calcHeight(lwidth, optimalWidth);
}
else
{
// We don't need a vertical scroll bar.
scrollNeeded = false;
}
if (scrollNeeded != m_scrolls)
{
// Change in scroll bar visibility.
m_scrolls = scrollNeeded;
ShowScrollBar(hWindow, SB_VERT, m_scrolls);
}
// At this point, width and height contain the proper values for the full
// size of the canvas, so update its size. We pass newHeight in so that the
// scroll position can be updated if needed.
moveCanvas(lwidth, lheight, newHeight);
if (m_scrolls)
{
SCROLLINFO si;
// Update the position, page size, and range of the scroll bar.
memset(&si, 0, sizeof(si));
si.cbSize = sizeof(si);
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
si.nPage = newHeight;
si.nMin = 0;
si.nMax = lheight;
si.nPos = m_y;
// If you do this without showing the scroll bar first, it drops the XP
// themes temporarily. Really strange.
SetScrollInfo(hWindow, SB_VERT, &si, TRUE);
}
return 0;
}
// Scroll vertically to a new location.
void OptionsScroller::setY(int value)
{
RECT rect;
RECT canvasRect;
// Note: the client rect for a window doesn't include the area taken up by
// its scroll bars, and that's what we want.
GetClientRect(hWindow, &rect);
GetClientRect(m_canvas->getHWindow(), &canvasRect);
if (canvasRect.bottom - value < rect.bottom)
{
// Don't allow the user to scroll off the bottom.
value = canvasRect.bottom - rect.bottom;
}
if (value < 0)
{
// Don't allow the user to scroll off the top.
value = 0;
}
if (value != m_y)
{
int optimalWidth = 0;
int lheight;
// Our scroll position changed, so update it, and scroll.
m_y = value;
lheight = m_canvas->calcHeight(rect.right, optimalWidth);
// Update the canvas's vertical position.
moveCanvas(rect.right, lheight, rect.bottom);
}
// Just to play it safe, update the scroll bar even if we don't think there
// was a change.
SetScrollPos(hWindow, SB_VERT, m_y, TRUE);
}
LRESULT OptionsScroller::doVScroll(
int scrollCode,
int position,
HWND hScrollBar)
{
if (hScrollBar == NULL)
{
RECT rect;
int newY = m_y;
GetClientRect(hWindow, &rect);
switch (scrollCode)
{
case SB_THUMBTRACK:
newY = position;
break;
case SB_PAGEUP:
newY -= rect.bottom;
break;
case SB_PAGEDOWN:
newY += rect.bottom;
break;
case SB_LINEUP:
newY--;
break;
case SB_LINEDOWN:
newY++;
break;
}
setY(newY);
return 0;
}
return 1;
}
// Make it so that the given control is fully visible in the scroll region.
void OptionsScroller::scrollControlToVisible(HWND hControl)
{
RECT clientRect;
RECT controlRect;
// All controls in the canvas that might receive the keyboard focus have
// their GWLP_USERDATA setting set to point to the OptionUI they're part of.
OptionUI *optionUI = (OptionUI *)GetWindowLongPtr(hControl, GWLP_USERDATA);
GetClientRect(hWindow, &clientRect);
if (optionUI != NULL)
{
// If the control has a label above it, or a multi-line label that
// extends above and below it, we want that visible also, so let the
// OptionUI object tell us the important rectangle.
optionUI->getRect(&controlRect);
}
else
{
// We shouldn't ever get here, but if we do, it should work fine.
GetWindowRect(hControl, &controlRect);
screenToClient(GetParent(hControl), &controlRect);
}
if (controlRect.bottom > clientRect.bottom + m_y)
{
// Note that we're relying on setY to contrain things to avoid
// scrolling off the bottom;
setY(controlRect.bottom - clientRect.bottom);
}
else if (controlRect.top < clientRect.top + m_y)
{
// Note that we're relying on setY to contrain things to avoid
// scrolling off the top.
setY(controlRect.top - clientRect.top);
}
// Redraw the canvas and all its child windows.
RedrawWindow(m_canvas->getHWindow(), NULL, NULL,
RDW_INVALIDATE | RDW_ALLCHILDREN);
}
LRESULT OptionsScroller::doMouseWheel(
short /*keyFlags*/,
short zDelta,
int /*xPos*/,
int /*yPos*/)
{
setY(m_y - zDelta);
return 0;
}