1
2
3
4
5
6 using System;
7 using System.Collections;
8 using System.Windows.API;
9
10 namespace System.Windows
11 {
12 public ControlCreateParams& ScrollableControlControlCreateParams(ControlCreateParams& controlCreateParams, Control* child)
13 {
14 return controlCreateParams.SetWindowClassName("System.Windows.ScrollableControl").SetBackgroundColor(child->BackgroundColor());
15 }
16
17 public class ScrollableControlCreateParams
18 {
19 public ScrollableControlCreateParams(ControlCreateParams& controlCreateParams_, Control* child_) :
20 controlCreateParams(controlCreateParams_), child(child_)
21 {
22 }
23 public ScrollableControlCreateParams& Defaults()
24 {
25 return *this;
26 }
27 public ControlCreateParams& controlCreateParams;
28 public Control* child;
29 }
30
31 public class ScrollableControl : Control
32 {
33 public ScrollableControl(Control* child_, const Point& location, const Size& size, Dock dock, Anchors anchors) :
34 base("System.Windows.ScrollableControl", DefaultWindowClassStyle(), DefaultChildWindowStyle(), DefaultExtendedWindowStyle(),
35 child_->BackgroundColor(), "scrollableControl", location, size, dock, anchors), container(this), child(child_),
36 verticalScrollUnit(0), horizontalScrollUnit(0), vpage(0u), vpos(0), vmin(0), vmax(0), hpage(0u), hpos(0), hmin(0), hmax(0),
37 verticalScrollBarShown(false), horizontalScrollBarShown(false)
38 {
39 auto result = container.AddChild(child);
40 if (result.Error())
41 {
42 SetErrorId(result.GetErrorId());
43 }
44 }
45 public ScrollableControl(ScrollableControlCreateParams& createParams) :
46 base(createParams.controlCreateParams), container(this), child(createParams.child),
47 verticalScrollUnit(0), horizontalScrollUnit(0), vpage(0u), vpos(0), vmin(0), vmax(0), hpage(0u), hpos(0), hmin(0), hmax(0),
48 verticalScrollBarShown(false), horizontalScrollBarShown(false)
49 {
50 auto result = container.AddChild(child);
51 if (result.Error())
52 {
53 SetErrorId(result.GetErrorId());
54 }
55 }
56 protected override bool IsDecoratorControl() const
57 {
58 return true;
59 }
60 [nodiscard]
61 protected override Result<bool> OnLocationChanged()
62 {
63 auto result = base->OnLocationChanged();
64 if (result.Error()) return result;
65 result = child->SetLocation(Point());
66 if (result.Error()) return result;
67 result = child->SetSize(GetSize());
68 if (result.Error()) return result;
69 return Result<bool>(true);
70 }
71 [nodiscard]
72 protected override Result<bool> OnSizeChanged(SizeChangedEventArgs& args)
73 {
74 auto result = base->OnSizeChanged(args);
75 if (result.Error()) return result;
76 result = child->SetLocation(Point());
77 if (result.Error()) return result;
78 result = child->SetSize(GetSize());
79 if (result.Error()) return result;
80 return Result<bool>(true);
81 }
82 [nodiscard]
83 protected override Result<bool> OnChildSizeChanged(ControlEventArgs& args)
84 {
85 auto result = base->OnChildSizeChanged(args);
86 if (result.Error()) return result;
87 result = ChildSizeOrContentSizeChanged(args);
88 if (result.Error()) return result;
89 return Result<bool>(true);
90 }
91 protected override void OnChildContentChanged(ControlEventArgs& args)
92 {
93 base->OnChildContentChanged(args);
94 hpos = 0;
95 vpos = 0;
96 }
97 protected override void OnChildContentLocationChanged(ControlEventArgs& args)
98 {
99 base->OnChildContentLocationChanged(args);
100 scrolledChild = args.control;
101 Point childContentLocation = scrolledChild->ContentLocation();
102 Pair<int, int> scrollUnits = scrolledChild->GetScrollUnits();
103 int verticalScrollUnit = scrollUnits.first;
104 int horizontalScrollUnit = scrollUnits.second;
105 vpos = childContentLocation.y / verticalScrollUnit;
106 SetScrollInfo(Handle(), ScrollBar.SB_VERT, ScrollInfoMask.SIF_POS, true, 0u, vpos, 0, 0);
107 hpos = childContentLocation.x / horizontalScrollUnit;
108 SetScrollInfo(Handle(), ScrollBar.SB_HORZ, ScrollInfoMask.SIF_POS, true, 0u, hpos, 0, 0);
109 }
110 [nodiscard]
111 private Result<bool> ChildSizeOrContentSizeChanged(ControlEventArgs& args)
112 {
113 scrolledChild = args.control;
114 Pair<int, int> scrollUnits = scrolledChild->GetScrollUnits();
115 verticalScrollUnit = scrollUnits.first;
116 horizontalScrollUnit = scrollUnits.second;
117 Size scrolledChildClientSize = scrolledChild->GetSize();
118 Size scrolledChildContentSize = scrolledChild->ContentSize();
119 if (scrolledChildContentSize.h > scrolledChildClientSize.h)
120 {
121 vmin = 0;
122 vmax = 1;
123 vpage = 1u;
124 if (verticalScrollUnit > 0)
125 {
126 vmax = cast<int>(scrolledChildContentSize.h / verticalScrollUnit);
127 vpage = cast<uint>(scrolledChildClientSize.h / verticalScrollUnit);
128 }
129 SetScrollInfo(Handle(), ScrollBar.SB_VERT, cast<ScrollInfoMask>(ScrollInfoMask.SIF_POS | ScrollInfoMask.SIF_PAGE | ScrollInfoMask.SIF_RANGE), true,
130 vpage, vpos, vmin, vmax);
131 auto result = ShowScrollBar(Handle(), ScrollBar.SB_VERT, true);
132 if (result.Error()) return result;
133 verticalScrollBarShown = true;
134 }
135 else
136 {
137 auto result = ShowScrollBar(Handle(), ScrollBar.SB_VERT, false);
138 if (result.Error()) return result;
139 verticalScrollBarShown = false;
140 }
141 if (scrolledChildContentSize.w > scrolledChildClientSize.w)
142 {
143 hmin = 0;
144 hmax = 1;
145 hpage = 1u;
146 if (horizontalScrollUnit > 0)
147 {
148 hmax = cast<int>(scrolledChildContentSize.w / horizontalScrollUnit);
149 hpage = cast<uint>(scrolledChildClientSize.w / horizontalScrollUnit);
150 }
151 SetScrollInfo(Handle(), ScrollBar.SB_HORZ, cast<ScrollInfoMask>(ScrollInfoMask.SIF_POS | ScrollInfoMask.SIF_PAGE | ScrollInfoMask.SIF_RANGE), true,
152 hpage, hpos, hmin, hmax);
153 auto result = ShowScrollBar(Handle(), ScrollBar.SB_HORZ, true);
154 if (result.Error()) return result;
155 horizontalScrollBarShown = true;
156 }
157 else
158 {
159 auto result = ShowScrollBar(Handle(), ScrollBar.SB_HORZ, false);
160 if (result.Error()) return result;
161 horizontalScrollBarShown = false;
162 }
163 return Result<bool>(true);
164 }
165 [nodiscard]
166 protected override Result<bool> OnChildContentSizeChanged(ControlEventArgs& args)
167 {
168 auto result = base->OnChildContentSizeChanged(args);
169 if (result.Error()) return result;
170 result = ChildSizeOrContentSizeChanged(args);
171 if (result.Error()) return result;
172 return Result<bool>(true);
173 }
174 [nodiscard]
175 protected override Result<bool> OnChildGotFocus(ControlEventArgs& args)
176 {
177 auto result = base->OnChildGotFocus(args);
178 if (result.Error()) return result;
179 Control* parentControl = ParentControl();
180 if (parentControl != null)
181 {
182 result = parentControl->OnChildGotFocus(args);
183 if (result.Error()) return result;
184 }
185 return Result<bool>(true);
186 }
187 [nodiscard]
188 protected override Result<bool> OnChildLostFocus(ControlEventArgs& args)
189 {
190 auto result = base->OnChildLostFocus(args);
191 if (result.Error()) return result;
192 Control* parentControl = ParentControl();
193 if (parentControl != null)
194 {
195 result = parentControl->OnChildLostFocus(args);
196 if (result.Error()) return result;
197 }
198 return Result<bool>(true);
199 }
200 [nodiscard]
201 protected override Result<bool> OnHScroll(ScrollEventArgs& args)
202 {
203 auto result = base->OnHScroll(args);
204 if (result.Error()) return result;
205 if (args.errorId != 0) return Result<bool>(ErrorId(args.errorId));
206 int trackPos;
207 result = GetScrollInfo(Handle(), ScrollBar.SB_HORZ, hpage, hpos, hmin, hmax, trackPos);
208 if (result.Error()) return result;
209 int prevHPos = hpos;
210 switch (args.request)
211 {
212 case SB_LINELEFT:
213 {
214 --hpos;
215 break;
216 }
217 case SB_LINERIGHT:
218 {
219 ++hpos;
220 break;
221 }
222 case SB_PAGELEFT:
223 {
224 hpos = hpos - cast<int>(hpage);
225 break;
226 }
227 case SB_PAGERIGHT:
228 {
229 hpos = hpos + cast<int>(hpage);
230 break;
231 }
232 case SB_THUMBTRACK:
233 {
234 hpos = trackPos;
235 break;
236 }
237 }
238 SetScrollInfo(Handle(), ScrollBar.SB_HORZ, ScrollInfoMask.SIF_POS, true, 0u, hpos, 0, 0);
239 result = GetScrollInfo(Handle(), ScrollBar.SB_HORZ, hpage, hpos, hmin, hmax, trackPos);
240 if (result.Error()) return result;
241 if (prevHPos != hpos)
242 {
243 scrolledChild->SetContentLocationInternal(Point(horizontalScrollUnit * hpos, verticalScrollUnit * vpos));
244 int xAmount = horizontalScrollUnit * (prevHPos - hpos);
245 Rect updateRect = MakeUpdateRect(xAmount, 0);
246 auto result = ScrollWindowEx(scrolledChild->Handle(), xAmount, 0, null, null, updateRect);
247 if (result.Error()) return result;
248 if (scrolledChild->IsDoubleBuffered())
249 {
250 result = scrolledChild->Invalidate();
251 if (result.Error()) return result;
252 }
253 }
254 return Result<bool>(true);
255 }
256 [nodiscard]
257 public override Result<bool> ScrollLineDown()
258 {
259 int trackPos;
260 auto result = GetScrollInfo(Handle(), ScrollBar.SB_VERT, vpage, vpos, vmin, vmax, trackPos);
261 if (result.Error()) return result;
262 int prevVPos = vpos;
263 if (vpos != vmax)
264 {
265 ++vpos;
266 SetScrollInfo(Handle(), ScrollBar.SB_VERT, ScrollInfoMask.SIF_POS, true, 0u, vpos, 0, 0);
267 result = GetScrollInfo(Handle(), ScrollBar.SB_VERT, vpage, vpos, vmin, vmax, trackPos);
268 if (result.Error()) return result;
269 if (prevVPos != vpos)
270 {
271 scrolledChild->SetContentLocationInternal(Point(horizontalScrollUnit * hpos, verticalScrollUnit * vpos));
272 int yAmount = verticalScrollUnit * (prevVPos - vpos);
273 Rect updateRect = MakeUpdateRect(0, yAmount);
274 auto result = ScrollWindowEx(scrolledChild->Handle(), 0, yAmount, null, null, updateRect);
275 if (result.Error()) return result;
276 if (scrolledChild->IsDoubleBuffered())
277 {
278 result = scrolledChild->Invalidate();
279 if (result.Error()) return result;
280 }
281 }
282 }
283 return Result<bool>(true);
284 }
285 [nodiscard]
286 public override Result<bool> ScrollLineUp()
287 {
288 int trackPos;
289 auto result = GetScrollInfo(Handle(), ScrollBar.SB_VERT, vpage, vpos, vmin, vmax, trackPos);
290 if (result.Error()) return result;
291 int prevVPos = vpos;
292 if (vpos != vmin)
293 {
294 --vpos;
295 SetScrollInfo(Handle(), ScrollBar.SB_VERT, ScrollInfoMask.SIF_POS, true, 0u, vpos, 0, 0);
296 result = GetScrollInfo(Handle(), ScrollBar.SB_VERT, vpage, vpos, vmin, vmax, trackPos);
297 if (result.Error()) return result;
298 if (prevVPos != vpos)
299 {
300 scrolledChild->SetContentLocationInternal(Point(horizontalScrollUnit * hpos, verticalScrollUnit * vpos));
301 int yAmount = verticalScrollUnit * (prevVPos - vpos);
302 Rect updateRect = MakeUpdateRect(0, yAmount);
303 result = ScrollWindowEx(scrolledChild->Handle(), 0, yAmount, null, null, updateRect);
304 if (result.Error()) return result;
305 if (scrolledChild->IsDoubleBuffered())
306 {
307 result = scrolledChild->Invalidate();
308 if (result.Error()) return result;
309 }
310 }
311 }
312 return Result<bool>(true);
313 }
314 protected override Result<bool> OnVScroll(ScrollEventArgs& args)
315 {
316 auto result = base->OnVScroll(args);
317 if (result.Error()) return result;
318 int trackPos;
319 result = GetScrollInfo(Handle(), ScrollBar.SB_VERT, vpage, vpos, vmin, vmax, trackPos);
320 if (result.Error()) return result;
321 int prevVPos = vpos;
322 switch (args.request)
323 {
324 case SB_TOP:
325 {
326 vpos = vmin;
327 break;
328 }
329 case SB_BOTTOM:
330 {
331 vpos = vmax;
332 break;
333 }
334 case SB_LINEUP:
335 {
336 --vpos;
337 break;
338 }
339 case SB_LINEDOWN:
340 {
341 ++vpos;
342 break;
343 }
344 case SB_PAGEUP:
345 {
346 vpos = vpos - cast<int>(vpage);
347 break;
348 }
349 case SB_PAGEDOWN:
350 {
351 vpos = vpos + cast<int>(vpage);
352 break;
353 }
354 case SB_THUMBTRACK:
355 {
356 vpos = trackPos;
357 break;
358 }
359 }
360 SetScrollInfo(Handle(), ScrollBar.SB_VERT, ScrollInfoMask.SIF_POS, true, 0u, vpos, 0, 0);
361 result = GetScrollInfo(Handle(), ScrollBar.SB_VERT, vpage, vpos, vmin, vmax, trackPos);
362 if (result.Error()) return result;
363 if (prevVPos != vpos)
364 {
365 scrolledChild->SetContentLocationInternal(Point(horizontalScrollUnit * hpos, verticalScrollUnit * vpos));
366 int yAmount = verticalScrollUnit * (prevVPos - vpos);
367 Rect updateRect = MakeUpdateRect(0, yAmount);
368 result = ScrollWindowEx(scrolledChild->Handle(), 0, yAmount, null, null, updateRect);
369 if (result.Error()) return result;
370 if (scrolledChild->IsDoubleBuffered())
371 {
372 result = scrolledChild->Invalidate();
373 if (result.Error()) return result;
374 }
375 }
376 return Result<bool>(true);
377 }
378 [nodiscard]
379 protected override Result<bool> OnMouseWheel(MouseWheelEventArgs& args)
380 {
381 auto result = base->OnMouseWheel(args);
382 if (result.Error()) return result;
383 if (!args.handled)
384 {
385 if (verticalScrollBarShown)
386 {
387 int trackPos;
388 result = GetScrollInfo(Handle(), ScrollBar.SB_VERT, vpage, vpos, vmin, vmax, trackPos);
389 if (result.Error()) return result;
390 int prevVPos = vpos;
391 vpos = cast<int>(vpos - args.distance / (2.000000 * verticalScrollUnit));
392 SetScrollInfo(Handle(), ScrollBar.SB_VERT, ScrollInfoMask.SIF_POS, true, 0u, vpos, 0, 0);
393 result = GetScrollInfo(Handle(), ScrollBar.SB_VERT, vpage, vpos, vmin, vmax, trackPos);
394 if (result.Error()) return result;
395 if (prevVPos != vpos)
396 {
397 Point contentLocation(horizontalScrollUnit * hpos, verticalScrollUnit * vpos);
398 scrolledChild->SetContentLocationInternal(contentLocation);
399 int yAmount = verticalScrollUnit * (prevVPos - vpos);
400 Rect updateRect = MakeUpdateRect(0, yAmount);
401 result = ScrollWindowEx(scrolledChild->Handle(), 0, yAmount, null, null, updateRect);
402 if (result.Error()) return result;
403 if (scrolledChild->IsDoubleBuffered())
404 {
405 result = scrolledChild->Invalidate();
406 if (result.Error()) return result;
407 }
408 }
409 args.handled = true;
410 }
411 }
412 return Result<bool>(true);
413 }
414 protected override Result<bool> TranslateChildGraphics(Graphics& graphics)
415 {
416 int dx = -hpos * horizontalScrollUnit;
417 int dy = -vpos * verticalScrollUnit;
418 if (dx != 0 || dy != 0)
419 {
420 auto result = graphics.TranslateTransform(dx, dy);
421 if (result.Error())
422 {
423 return Result<bool>(ErrorId(result.GetErrorId()));
424 }
425 }
426 return Result<bool>(true);
427 }
428 protected override void TranslateMousePos(Point& location)
429 {
430 int dx = hpos * horizontalScrollUnit;
431 int dy = vpos * verticalScrollUnit;
432 location.x = location.x + dx;
433 location.y = location.y + dy;
434 }
435 protected override void TranslateContentLocation(Point& location)
436 {
437 int dx = hpos * horizontalScrollUnit;
438 int dy = vpos * verticalScrollUnit;
439 location.x = location.x - dx;
440 location.y = location.y - dy;
441 }
442 internal override Control* GetFirstEnabledTabStopControl() const
443 {
444 return child->GetFirstEnabledTabStopControl();
445 }
446 internal override Control* GetLastEnabledTabStopControl() const
447 {
448 return child->GetLastEnabledTabStopControl();
449 }
450 private Rect MakeUpdateRect(int xAmount, int yAmount)
451 {
452 Point loc(0, 0);
453 Size size = scrolledChild->GetSize();
454 if (xAmount < 0)
455 {
456 loc.x = size.w + xAmount;
457 }
458 if (xAmount != 0)
459 {
460 size.w = Abs(xAmount);
461 }
462 if (yAmount < 0)
463 {
464 loc.y = size.h + yAmount;
465 }
466 if (yAmount != 0)
467 {
468 size.h = Abs(yAmount);
469 }
470 Rect updateRect(loc, size);
471 updateRect.Inflate(horizontalScrollUnit, verticalScrollUnit);
472 return updateRect;
473 }
474 private ComponentContainer container;
475 private Control* child;
476 private Control* scrolledChild;
477 private int verticalScrollUnit;
478 private int horizontalScrollUnit;
479 private uint vpage;
480 private int vpos;
481 private int vmin;
482 private int vmax;
483 private uint hpage;
484 private int hpos;
485 private int hmin;
486 private int hmax;
487 private bool verticalScrollBarShown;
488 private bool horizontalScrollBarShown;
489 }