1 // Written in the D programming language.
2
3 /**
4
5 This module contains base implementation of scrolling capabilities for widgets
6
7
8 ScrollWidgetBase - abstract scrollable widget (used as a base for other widgets with scrolling)
9
10 ScrollWidget - widget which can scroll its content (directly usable class)
11
12
13 Synopsis:
14
15 ----
16 import dlangui.widgets.scroll;
17
18 // Scroll view example
19 ScrollWidget scroll = new ScrollWidget("SCROLL1");
20 scroll.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
21 WidgetGroup scrollContent = new VerticalLayout("CONTENT");
22 scrollContent.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
23
24 TableLayout table2 = new TableLayout("TABLE2");
25 table2.colCount = 2;
26 // headers
27 table2.addChild((new TextWidget(null, "Parameter Name"d)).alignment(Align.Right | Align.VCenter));
28 table2.addChild((new TextWidget(null, "Edit Box to edit parameter"d)).alignment(Align.Left | Align.VCenter));
29 // row 1
30 table2.addChild((new TextWidget(null, "Parameter 1 name"d)).alignment(Align.Right | Align.VCenter));
31 table2.addChild((new EditLine("edit1", "Text 1"d)).layoutWidth(FILL_PARENT));
32 // row 2
33 table2.addChild((new TextWidget(null, "Parameter 2 name bla bla"d)).alignment(Align.Right | Align.VCenter));
34 table2.addChild((new EditLine("edit2", "Some text for parameter 2 blah blah blah"d)).layoutWidth(FILL_PARENT));
35 // row 3
36 table2.addChild((new TextWidget(null, "Param 3"d)).alignment(Align.Right | Align.VCenter));
37 table2.addChild((new EditLine("edit3", "Parameter 3 value"d)).layoutWidth(FILL_PARENT));
38 // row 4
39 table2.addChild((new TextWidget(null, "Param 4"d)).alignment(Align.Right | Align.VCenter));
40 table2.addChild((new EditLine("edit3", "Parameter 4 value shdjksdfh hsjdfas hdjkf hdjsfk ah"d)).layoutWidth(FILL_PARENT));
41 // row 5
42 table2.addChild((new TextWidget(null, "Param 5 - edit text here - blah blah blah"d)).alignment(Align.Right | Align.VCenter));
43 table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT));
44 // row 6
45 table2.addChild((new TextWidget(null, "Param 6 - just to fill content widget"d)).alignment(Align.Right | Align.VCenter));
46 table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT));
47 // row 7
48 table2.addChild((new TextWidget(null, "Param 7 - just to fill content widget"d)).alignment(Align.Right | Align.VCenter));
49 table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT));
50 // row 8
51 table2.addChild((new TextWidget(null, "Param 8 - just to fill content widget"d)).alignment(Align.Right | Align.VCenter));
52 table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT));
53 table2.margins(Rect(10,10,10,10)).layoutWidth(FILL_PARENT);
54 scrollContent.addChild(table2);
55
56 scrollContent.addChild(new TextWidget(null, "Now - some buttons"d));
57 scrollContent.addChild(new ImageTextButton("btn1", "fileclose", "Close"d));
58 scrollContent.addChild(new ImageTextButton("btn2", "fileopen", "Open"d));
59 scrollContent.addChild(new TextWidget(null, "And checkboxes"d));
60 scrollContent.addChild(new CheckBox("btn1", "CheckBox 1"d));
61 scrollContent.addChild(new CheckBox("btn2", "CheckBox 2"d));
62
63 scroll.contentWidget = scrollContent;
64
65 ----
66
67 Copyright: Vadim Lopatin, 2014
68 License: Boost License 1.0
69 Authors: Vadim Lopatin, coolreader.org@gmail.com
70 */
71 module dlangui.widgets.scroll;
72
73 import dlangui.widgets.widget;
74 import dlangui.widgets.controls;
75 import dlangui.widgets.scrollbar;
76 import std.conv;
77
78 /** Scroll bar visibility mode. */
79 enum ScrollBarMode {
80 /** always invisible */
81 Invisible,
82 /** always visible */
83 Visible,
84 /** automatically show/hide scrollbar depending on content size */
85 Auto,
86 /** Scrollbar is provided by external control outside this widget */
87 External,
88 }
89
90 /**
91 Abstract scrollable widget
92
93 Provides scroll bars and basic scrolling functionality.
94
95 */
96 class ScrollWidgetBase : WidgetGroup, OnScrollHandler {
97 protected ScrollBarMode _vscrollbarMode;
98 protected ScrollBarMode _hscrollbarMode;
99 /// vertical scrollbar control
100 protected ScrollBar _vscrollbar;
101 /// horizontal scrollbar control
102 protected ScrollBar _hscrollbar;
103 /// inner area, excluding additional controls like scrollbars
104 protected Rect _clientRect;
105
106 protected Rect _fullScrollableArea;
107 protected Rect _visibleScrollableArea;
108
109 this(string ID = null, ScrollBarMode hscrollbarMode = ScrollBarMode.Visible, ScrollBarMode vscrollbarMode = ScrollBarMode.Visible) {
110 super(ID);
111 _hscrollbarMode = hscrollbarMode;
112 _vscrollbarMode = vscrollbarMode;
113 if (_vscrollbarMode != ScrollBarMode.Invisible) {
114 _vscrollbar = new ScrollBar("vscrollbar", Orientation.Vertical);
115 _vscrollbar.scrollEvent = this;
116 addChild(_vscrollbar);
117 }
118 if (_hscrollbarMode != ScrollBarMode.Invisible) {
119 _hscrollbar = new ScrollBar("hscrollbar", Orientation.Horizontal);
120 _hscrollbar.scrollEvent = this;
121 addChild(_hscrollbar);
122 }
123 }
124
125 /// vertical scrollbar mode
126 @property ScrollBarMode vscrollbarMode() { return _vscrollbarMode; }
127 @property void vscrollbarMode(ScrollBarMode m) { _vscrollbarMode = m; }
128 /// horizontal scrollbar mode
129 @property ScrollBarMode hscrollbarMode() { return _hscrollbarMode; }
130 @property void hscrollbarMode(ScrollBarMode m) { _hscrollbarMode = m; }
131
132 /// returns client area rectangle
133 @property Rect clientRect() { return _clientRect; }
134
135 /// process horizontal scrollbar event
136 bool onHScroll(ScrollEvent event) {
137 return true;
138 }
139
140 /// process vertical scrollbar event
141 bool onVScroll(ScrollEvent event) {
142 return true;
143 }
144
145 /// process mouse event; return true if event is processed by widget.
146 override bool onMouseEvent(MouseEvent event) {
147 if (event.action == MouseAction.Wheel) {
148 if (event.flags == MouseFlag.Shift) {
149 if (_hscrollbar) {
150 _hscrollbar.sendScrollEvent(event.wheelDelta > 0 ? ScrollAction.LineUp : ScrollAction.LineDown);
151 return true;
152 }
153 } else if (event.flags == 0) {
154 if (_vscrollbar) {
155 _vscrollbar.sendScrollEvent(event.wheelDelta > 0 ? ScrollAction.LineUp : ScrollAction.LineDown);
156 return true;
157 }
158 }
159 }
160 return super.onMouseEvent(event);
161 }
162
163 /// handle scroll event
164 override bool onScrollEvent(AbstractSlider source, ScrollEvent event) {
165 if (source.orientation == Orientation.Horizontal) {
166 return onHScroll(event);
167 } else {
168 return onVScroll(event);
169 }
170 }
171
172 protected bool _insideChangeScrollbarVisibility;
173 protected void checkIfNeededToChangeScrollbarVisibility() {
174 if (_insideChangeScrollbarVisibility)
175 return;
176 bool needHScroll = false;
177 bool needVScroll = false;
178 checkIfScrollbarsNeeded(needHScroll, needVScroll);
179 bool hscrollVisible = _hscrollbar && _hscrollbar.visibility == Visibility.Visible;
180 bool vscrollVisible = _vscrollbar && _vscrollbar.visibility == Visibility.Visible;
181 bool needChange = false;
182 if (_hscrollbar && hscrollVisible != needHScroll)
183 needChange = true;
184 if (_vscrollbar && vscrollVisible != needVScroll)
185 needChange = true;
186 if (needChange) {
187 _insideChangeScrollbarVisibility = true;
188 layout(_pos);
189 _insideChangeScrollbarVisibility = false;
190 }
191 }
192
193 /// update scrollbar positions
194 protected void updateScrollBars() {
195 if (_hscrollbar) {
196 updateHScrollBar();
197 }
198 if (_vscrollbar) {
199 updateVScrollBar();
200 }
201 checkIfNeededToChangeScrollbarVisibility();
202 }
203
204 public @property ScrollBar hscrollbar() { return _hscrollbar; }
205 public @property ScrollBar vscrollbar() { return _vscrollbar; }
206
207 public @property void hscrollbar(ScrollBar hscroll) {
208 if (_hscrollbar) {
209 removeChild(_hscrollbar);
210 destroy(_hscrollbar);
211 _hscrollbar = null;
212 _hscrollbarMode = ScrollBarMode.Invisible;
213 }
214 if (hscroll) {
215 _hscrollbar = hscroll;
216 _hscrollbarMode = ScrollBarMode.External;
217 }
218 }
219
220 public @property void vscrollbar(ScrollBar vscroll) {
221 if (_vscrollbar) {
222 removeChild(_vscrollbar);
223 destroy(_vscrollbar);
224 _vscrollbar = null;
225 _vscrollbarMode = ScrollBarMode.Invisible;
226 }
227 if (vscroll) {
228 _vscrollbar = vscroll;
229 _vscrollbarMode = ScrollBarMode.External;
230 }
231 }
232
233 /// update horizontal scrollbar widget position
234 protected void updateHScrollBar() {
235 // default implementation: use _fullScrollableArea, _visibleScrollableArea: override it if necessary
236 _hscrollbar.setRange(0, _fullScrollableArea.width);
237 _hscrollbar.pageSize(_visibleScrollableArea.width);
238 _hscrollbar.position(_visibleScrollableArea.left - _fullScrollableArea.left);
239 }
240
241 /// update verticat scrollbar widget position
242 protected void updateVScrollBar() {
243 // default implementation: use _fullScrollableArea, _visibleScrollableArea: override it if necessary
244 _vscrollbar.setRange(0, _fullScrollableArea.height);
245 _vscrollbar.pageSize(_visibleScrollableArea.height);
246 _vscrollbar.position(_visibleScrollableArea.top - _fullScrollableArea.top);
247 }
248
249 protected void drawClient(DrawBuf buf) {
250 // override it
251 }
252
253 protected void drawExtendedArea(DrawBuf buf) {
254 }
255
256 /// Draw widget at its position to buffer
257 override void onDraw(DrawBuf buf) {
258 if (visibility != Visibility.Visible)
259 return;
260 super.onDraw(buf);
261 Rect rc = _pos;
262 applyMargins(rc);
263 {
264 auto saver = ClipRectSaver(buf, rc, alpha);
265 DrawableRef bg = backgroundDrawable;
266 if (!bg.isNull) {
267 bg.drawTo(buf, rc, state);
268 }
269 applyPadding(rc);
270 if (_hscrollbar)
271 _hscrollbar.onDraw(buf);
272 if (_vscrollbar)
273 _vscrollbar.onDraw(buf);
274 // apply clipping
275 {
276 auto saver2 = ClipRectSaver(buf, _clientRect, alpha);
277 drawClient(buf);
278 }
279 {
280 // no clipping for drawing of extended area
281 Rect clipr = rc;
282 clipr.bottom = _clientRect.bottom;
283 auto saver3 = ClipRectSaver(buf, clipr, alpha);
284 drawExtendedArea(buf);
285 }
286 }
287
288 _needDraw = false;
289 }
290
291 /// calculate full content size in pixels
292 Point fullContentSize() {
293 // override it
294 Point sz;
295 return sz;
296 }
297
298 /// calculate full content size in pixels including widget borders / margins
299 Point fullContentSizeWithBorders() {
300 Point sz = fullContentSize;
301 Rect paddingrc = padding;
302 Rect marginsrc = margins;
303 sz.x += paddingrc.left + paddingrc.right + marginsrc.left + marginsrc.right;
304 sz.y += paddingrc.top + paddingrc.bottom + marginsrc.top + marginsrc.bottom;
305 return sz;
306 }
307
308 // override to set minimum scrollwidget size - default 100x100
309 Point minimumVisibleContentSize() {
310 return Point(100,100);
311 }
312
313 /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
314 override void measure(int parentWidth, int parentHeight) {
315 if (visibility == Visibility.Gone) {
316 return;
317 }
318 Rect m = margins;
319 Rect p = padding;
320
321 // calc size constraints for children
322 int pwidth = parentWidth;
323 int pheight = parentHeight;
324 if (parentWidth != SIZE_UNSPECIFIED)
325 pwidth -= m.left + m.right + p.left + p.right;
326 if (parentHeight != SIZE_UNSPECIFIED)
327 pheight -= m.top + m.bottom + p.top + p.bottom;
328 int vsbw = 0;
329 int hsbh = 0;
330 if (_hscrollbar && (_hscrollbarMode == ScrollBarMode.Visible || _hscrollbarMode == ScrollBarMode.Auto)) {
331 Visibility oldVisibility = _hscrollbar.visibility;
332 _hscrollbar.visibility = Visibility.Visible;
333 _hscrollbar.measure(pwidth, pheight);
334 hsbh = _hscrollbar.measuredHeight;
335 _hscrollbar.visibility = oldVisibility;
336 }
337 if (_vscrollbar && (_vscrollbarMode == ScrollBarMode.Visible || _vscrollbarMode == ScrollBarMode.Auto)) {
338 Visibility oldVisibility = _vscrollbar.visibility;
339 _vscrollbar.visibility = Visibility.Visible;
340 _vscrollbar.measure(pwidth, pheight);
341 vsbw = _vscrollbar.measuredWidth;
342 _vscrollbar.visibility = oldVisibility;
343 }
344 Point sz = minimumVisibleContentSize();
345
346 //if (_hscrollbar && _hscrollbarMode == ScrollBarMode.Visible) {
347 sz.y += hsbh;
348 //}
349 //if (_vscrollbar && _vscrollbarMode == ScrollBarMode.Visible) {
350 sz.x += vsbw;
351 //}
352
353 measuredContent(parentWidth, parentHeight, sz.x, sz.y);
354 }
355
356 /// override to support modification of client rect after change, e.g. apply offset
357 protected void handleClientRectLayout(ref Rect rc) {
358 }
359
360 /// override to determine if scrollbars are needed or not
361 protected void checkIfScrollbarsNeeded(ref bool needHScroll, ref bool needVScroll) {
362 needHScroll = _hscrollbar && (_hscrollbarMode == ScrollBarMode.Visible || _hscrollbarMode == ScrollBarMode.Auto);
363 needVScroll = _vscrollbar && (_vscrollbarMode == ScrollBarMode.Visible || _vscrollbarMode == ScrollBarMode.Auto);
364 if (!needHScroll && !needVScroll)
365 return; // not needed
366 if (_hscrollbarMode != ScrollBarMode.Auto && _vscrollbarMode != ScrollBarMode.Auto)
367 return; // no auto scrollbars
368 // either h or v scrollbar is in auto mode
369 Point contentSize = fullContentSize();
370 int contentWidth = contentSize.x;
371 int contentHeight = contentSize.y;
372 int clientWidth = _clientRect.width;
373 int clientHeight = _clientRect.height;
374
375 int hsbHeight = _hscrollbar.measuredHeight;
376 int vsbWidth = _hscrollbar.measuredWidth;
377
378 int clientWidthWithScrollbar = clientWidth - vsbWidth;
379 int clientHeightWithScrollbar = clientHeight - hsbHeight;
380
381 if (_hscrollbarMode == ScrollBarMode.Auto && _vscrollbarMode == ScrollBarMode.Auto) {
382 // both scrollbars in auto mode
383 bool xFits = contentWidth <= clientWidth;
384 bool yFits = contentHeight <= clientHeight;
385 if (!xFits && !yFits) {
386 // none fits, need both scrollbars
387 } else if (xFits && yFits) {
388 // everything fits!
389 needHScroll = false;
390 needVScroll = false;
391 } else if (xFits) {
392 // only X fits
393 if (contentWidth <= clientWidthWithScrollbar)
394 needHScroll = false; // disable hscroll
395 } else { // yFits
396 // only Y fits
397 if (contentHeight <= clientHeightWithScrollbar)
398 needVScroll = false; // disable vscroll
399 }
400 } else if (_hscrollbarMode == ScrollBarMode.Auto) {
401 // only hscroll is in auto mode
402 if (needVScroll)
403 clientWidth = clientWidthWithScrollbar;
404 needHScroll = contentWidth > clientWidth;
405 } else {
406 // only vscroll is in auto mode
407 if (needHScroll)
408 clientHeight = clientHeightWithScrollbar;
409 needVScroll = contentHeight > clientHeight;
410 }
411 }
412
413 /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
414 override void layout(Rect rc) {
415 if (visibility == Visibility.Gone) {
416 return;
417 }
418 _pos = rc;
419 _needLayout = false;
420 applyMargins(rc);
421 applyPadding(rc);
422
423 // client area - initial setup w/o scrollbars
424 _clientRect = rc;
425 handleClientRectLayout(_clientRect);
426
427 bool needHscroll;
428 bool needVscroll;
429
430 checkIfScrollbarsNeeded(needHscroll, needVscroll);
431
432 // scrollbars
433 Rect vsbrc = rc;
434 vsbrc.left = vsbrc.right - (needVscroll ? _vscrollbar.measuredWidth : 0);
435 vsbrc.bottom = vsbrc.bottom - (needHscroll ? _hscrollbar.measuredHeight : 0);
436 Rect hsbrc = rc;
437 hsbrc.right = hsbrc.right - (needVscroll ? _vscrollbar.measuredWidth : 0);
438 hsbrc.top = hsbrc.bottom - (needHscroll ? _hscrollbar.measuredHeight : 0);
439 if (_vscrollbar && _vscrollbarMode != ScrollBarMode.External) {
440 _vscrollbar.visibility = needVscroll ? Visibility.Visible : Visibility.Gone;
441 _vscrollbar.layout(vsbrc);
442 }
443 if (_hscrollbar && _hscrollbarMode != ScrollBarMode.External) {
444 _hscrollbar.visibility = needHscroll ? Visibility.Visible : Visibility.Gone;
445 _hscrollbar.layout(hsbrc);
446 }
447
448 _clientRect = rc;
449 if (needVscroll)
450 _clientRect.right = vsbrc.left;
451 if (needHscroll)
452 _clientRect.bottom = hsbrc.top;
453 handleClientRectLayout(_clientRect);
454 updateScrollBars();
455 }
456
457 void makeRectVisible(Rect rc, bool alignHorizontally = true, bool alignVertically = true) {
458 if (rc.isInsideOf(_visibleScrollableArea))
459 return;
460 Rect oldRect = _visibleScrollableArea;
461 if (alignHorizontally && rc.right > _visibleScrollableArea.right)
462 _visibleScrollableArea.offset(rc.right - _visibleScrollableArea.right, 0);
463 if (alignVertically && rc.bottom > _visibleScrollableArea.bottom)
464 _visibleScrollableArea.offset(0, rc.bottom - _visibleScrollableArea.bottom);
465 if (alignHorizontally && rc.left < _visibleScrollableArea.left)
466 _visibleScrollableArea.offset(rc.left - _visibleScrollableArea.left, 0);
467 if (alignVertically && rc.top < _visibleScrollableArea.top)
468 _visibleScrollableArea.offset(0, rc.top - _visibleScrollableArea.top);
469 if (_visibleScrollableArea != oldRect)
470 requestLayout();
471 }
472 }
473
474 /**
475 Widget which can show content of widget group with optional scrolling
476
477 If size of content widget exceeds available space, allows to scroll it.
478 */
479 class ScrollWidget : ScrollWidgetBase {
480 protected Widget _contentWidget;
481 @property Widget contentWidget() { return _contentWidget; }
482 @property ScrollWidget contentWidget(Widget newContent) {
483 if (_contentWidget) {
484 removeChild(childIndex(_contentWidget));
485 destroy(_contentWidget);
486 }
487 _contentWidget = newContent;
488 addChild(_contentWidget);
489 requestLayout();
490 return this;
491 }
492 /// empty parameter list constructor - for usage by factory
493 this() {
494 this(null);
495 }
496 /// create with ID parameter
497 this(string ID, ScrollBarMode hscrollbarMode = ScrollBarMode.Visible, ScrollBarMode vscrollbarMode = ScrollBarMode.Visible) {
498 super(ID, hscrollbarMode, vscrollbarMode);
499 }
500
501 /// calculate full content size in pixels
502 override Point fullContentSize() {
503 // override it
504 Point sz;
505 if (_contentWidget) {
506 _contentWidget.measure(SIZE_UNSPECIFIED, SIZE_UNSPECIFIED);
507 sz.x = _contentWidget.measuredWidth;
508 sz.y = _contentWidget.measuredHeight;
509 }
510 _fullScrollableArea.right = sz.x;
511 _fullScrollableArea.bottom = sz.y;
512 return sz;
513 }
514
515 /// update scrollbar positions
516 override protected void updateScrollBars() {
517 Point sz = fullContentSize();
518 _visibleScrollableArea.right = _visibleScrollableArea.left + _clientRect.width;
519 _visibleScrollableArea.bottom = _visibleScrollableArea.top + _clientRect.height;
520 // move back if scroll is too big after window resize
521 int extrax = _visibleScrollableArea.right - _fullScrollableArea.right;
522 int extray = _visibleScrollableArea.bottom - _fullScrollableArea.bottom;
523 if (extrax > _visibleScrollableArea.left)
524 extrax = _visibleScrollableArea.left;
525 if (extray > _visibleScrollableArea.top)
526 extray = _visibleScrollableArea.top;
527 if (extrax < 0)
528 extrax = 0;
529 if (extray < 0)
530 extray = 0;
531 _visibleScrollableArea.offset(-extrax, -extray);
532 super.updateScrollBars();
533 }
534
535 override protected void drawClient(DrawBuf buf) {
536 if (_contentWidget) {
537 Point sz = fullContentSize();
538 Point p = scrollPos;
539 _contentWidget.layout(Rect(_clientRect.left - p.x, _clientRect.top - p.y, _clientRect.left + sz.x - p.x, _clientRect.top + sz.y - p.y));
540 _contentWidget.onDraw(buf);
541 }
542 }
543
544
545 @property Point scrollPos() {
546 return Point(_visibleScrollableArea.left - _fullScrollableArea.left, _visibleScrollableArea.top - _fullScrollableArea.top);
547 }
548
549 protected void scrollTo(int x, int y) {
550 if (x > _fullScrollableArea.right - _visibleScrollableArea.width)
551 x = _fullScrollableArea.right - _visibleScrollableArea.width;
552 if (y > _fullScrollableArea.bottom - _visibleScrollableArea.height)
553 y = _fullScrollableArea.bottom - _visibleScrollableArea.height;
554 if (x < 0)
555 x = 0;
556 if (y < 0)
557 y = 0;
558 _visibleScrollableArea.left = x;
559 _visibleScrollableArea.top = y;
560 updateScrollBars();
561 invalidate();
562 }
563
564 /// process horizontal scrollbar event
565 override bool onHScroll(ScrollEvent event) {
566 if (event.action == ScrollAction.SliderMoved || event.action == ScrollAction.SliderReleased) {
567 scrollTo(event.position, scrollPos.y);
568 } else if (event.action == ScrollAction.PageUp) {
569 scrollTo(scrollPos.x - _clientRect.width * 3 / 4, scrollPos.y);
570 } else if (event.action == ScrollAction.PageDown) {
571 scrollTo(scrollPos.x + _clientRect.width * 3 / 4, scrollPos.y);
572 } else if (event.action == ScrollAction.LineUp) {
573 scrollTo(scrollPos.x - _clientRect.width / 10, scrollPos.y);
574 } else if (event.action == ScrollAction.LineDown) {
575 scrollTo(scrollPos.x + _clientRect.width / 10, scrollPos.y);
576 }
577 return true;
578 }
579
580 /// process vertical scrollbar event
581 override bool onVScroll(ScrollEvent event) {
582 if (event.action == ScrollAction.SliderMoved || event.action == ScrollAction.SliderReleased) {
583 scrollTo(scrollPos.x, event.position);
584 } else if (event.action == ScrollAction.PageUp) {
585 scrollTo(scrollPos.x, scrollPos.y - _clientRect.height * 3 / 4);
586 } else if (event.action == ScrollAction.PageDown) {
587 scrollTo(scrollPos.x, scrollPos.y + _clientRect.height * 3 / 4);
588 } else if (event.action == ScrollAction.LineUp) {
589 scrollTo(scrollPos.x, scrollPos.y - _clientRect.height / 10);
590 } else if (event.action == ScrollAction.LineDown) {
591 scrollTo(scrollPos.x, scrollPos.y + _clientRect.height / 10); }
592 return true;
593 }
594
595 void makeWidgetVisible(Widget widget, bool alignHorizontally = true, bool alignVertically = true) {
596 if (!widget || !widget.visibility == Visibility.Gone)
597 return;
598 if (!_contentWidget || !_contentWidget.isChild(widget))
599 return;
600 Rect wpos = widget.pos;
601 Rect cpos = _contentWidget.pos;
602 wpos.offset(-cpos.left, -cpos.top);
603 makeRectVisible(wpos, alignHorizontally, alignVertically);
604 }
605 }