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 }