1 // Written in the D programming language. 2 3 /** 4 DLANGUI library. 5 6 This module contains simple controls widgets implementation. 7 8 TextWidget 9 10 ImageWidget 11 12 Button 13 14 ImageButton 15 16 ScrollBar 17 18 19 Synopsis: 20 21 ---- 22 import dlangui.widgets.controls; 23 24 ---- 25 26 Copyright: Vadim Lopatin, 2014 27 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 28 Authors: $(WEB coolreader.org, Vadim Lopatin) 29 */ 30 module dlangui.widgets.controls; 31 32 import dlangui.widgets.widget; 33 import dlangui.widgets.layouts; 34 35 /// vertical spacer to fill empty space in vertical layouts 36 class VSpacer : Widget { 37 this() { 38 styleId = "VSpacer"; 39 } 40 } 41 42 /// horizontal spacer to fill empty space in horizontal layouts 43 class HSpacer : Widget { 44 this() { 45 styleId = "HSpacer"; 46 } 47 } 48 49 /// static text widget 50 class TextWidget : Widget { 51 this(string ID = null, string textResourceId = null) { 52 super(ID); 53 styleId = "TEXT"; 54 _text = textResourceId; 55 } 56 this(string ID, dstring rawText) { 57 super(ID); 58 styleId = "TEXT"; 59 _text = rawText; 60 } 61 protected UIString _text; 62 /// get widget text 63 override @property dstring text() { return _text; } 64 /// set text to show 65 override @property Widget text(dstring s) { 66 _text = s; 67 requestLayout(); 68 return this; 69 } 70 /// set text to show 71 override @property Widget text(ref UIString s) { 72 _text = s; 73 requestLayout(); 74 return this; 75 } 76 /// set text resource ID to show 77 @property Widget textResource(string s) { 78 _text = s; 79 requestLayout(); 80 return this; 81 } 82 83 override void measure(int parentWidth, int parentHeight) { 84 FontRef font = font(); 85 //auto measureStart = std.datetime.Clock.currAppTick; 86 Point sz = font.textSize(text); 87 //auto measureEnd = std.datetime.Clock.currAppTick; 88 //auto duration = measureEnd - measureStart; 89 //if (duration.length > 10) 90 // Log.d("TextWidget measureText took ", duration.length, " ticks"); 91 measuredContent(parentWidth, parentHeight, sz.x, sz.y); 92 } 93 94 bool onClick() { 95 // override it 96 Log.d("Button.onClick ", id); 97 return false; 98 } 99 100 override void onDraw(DrawBuf buf) { 101 if (visibility != Visibility.Visible) 102 return; 103 super.onDraw(buf); 104 Rect rc = _pos; 105 applyMargins(rc); 106 auto saver = ClipRectSaver(buf, rc); 107 applyPadding(rc); 108 FontRef font = font(); 109 Point sz = font.textSize(text); 110 applyAlign(rc, sz); 111 font.drawText(buf, rc.left, rc.top, text, textColor); 112 } 113 } 114 115 /// static image widget 116 class ImageWidget : Widget { 117 118 protected string _drawableId; 119 protected DrawableRef _drawable; 120 121 this(string ID = null, string drawableId = null) { 122 super(ID); 123 _drawableId = drawableId; 124 } 125 126 /// get drawable image id 127 @property string drawableId() { return _drawableId; } 128 /// set drawable image id 129 @property ImageWidget drawableId(string id) { 130 _drawableId = id; 131 _drawable.clear(); 132 requestLayout(); 133 return this; 134 } 135 /// get drawable 136 @property ref DrawableRef drawable() { 137 if (!_drawable.isNull) 138 return _drawable; 139 if (_drawableId !is null) 140 _drawable = drawableCache.get(_drawableId); 141 return _drawable; 142 } 143 /// set custom drawable (not one from resources) 144 @property ImageWidget drawable(DrawableRef img) { 145 _drawable = img; 146 _drawableId = null; 147 return this; 148 } 149 150 override void measure(int parentWidth, int parentHeight) { 151 DrawableRef img = drawable; 152 int w = 0; 153 int h = 0; 154 if (!img.isNull) { 155 w = img.width; 156 h = img.height; 157 } 158 measuredContent(parentWidth, parentHeight, w, h); 159 } 160 override void onDraw(DrawBuf buf) { 161 if (visibility != Visibility.Visible) 162 return; 163 super.onDraw(buf); 164 Rect rc = _pos; 165 applyMargins(rc); 166 auto saver = ClipRectSaver(buf, rc); 167 applyPadding(rc); 168 DrawableRef img = drawable; 169 if (!img.isNull) { 170 Point sz; 171 sz.x = img.width; 172 sz.y = img.height; 173 applyAlign(rc, sz); 174 uint st = state; 175 if (state & State.Checked) { 176 Log.d("Drawing image for checked state"); 177 } 178 img.drawTo(buf, rc, st); 179 } 180 } 181 } 182 183 /// button with image only 184 class ImageButton : ImageWidget { 185 this(string ID = null, string drawableId = null) { 186 super(ID, drawableId); 187 styleId = "BUTTON"; 188 _drawableId = drawableId; 189 clickable = true; 190 focusable = true; 191 trackHover = true; 192 } 193 } 194 195 /// button with image and text 196 class ImageTextButton : HorizontalLayout { 197 protected ImageWidget _icon; 198 protected TextWidget _label; 199 override @property dstring text() { return _label.text; } 200 override @property Widget text(dstring s) { _label.text = s; requestLayout(); return this; } 201 override @property Widget text(ref UIString s) { _label.text = s; requestLayout(); return this; } 202 this(string ID = null, string drawableId = null, string textResourceId = null) { 203 super(ID); 204 styleId = "BUTTON"; 205 _icon = new ImageWidget("icon", drawableId); 206 _label = new TextWidget("label", textResourceId); 207 _label.styleId = "BUTTON_LABEL"; 208 _icon.state = State.Parent; 209 _label.state = State.Parent; 210 addChild(_icon); 211 addChild(_label); 212 clickable = true; 213 focusable = true; 214 trackHover = true; 215 } 216 this(string ID, string drawableId, dstring rawText) { 217 super(ID); 218 styleId = "BUTTON"; 219 _icon = new ImageWidget("icon", drawableId); 220 _label = new TextWidget("label", rawText); 221 _label.styleId = "BUTTON_LABEL"; 222 _icon.styleId = "BUTTON_ICON"; 223 _icon.state = State.Parent; 224 _label.state = State.Parent; 225 addChild(_icon); 226 addChild(_label); 227 clickable = true; 228 focusable = true; 229 trackHover = true; 230 } 231 } 232 233 /// checkbox 234 class CheckBox : ImageTextButton { 235 this(string ID = null, string textResourceId = null) { 236 super(ID, "btn_check", textResourceId); 237 styleId = "TRANSPARENT_BUTTON_BACKGROUND"; 238 checkable = true; 239 } 240 this(string ID, dstring labelText) { 241 super(ID, "btn_check", labelText); 242 styleId = "TRANSPARENT_BUTTON_BACKGROUND"; 243 checkable = true; 244 } 245 // called to process click and notify listeners 246 override protected bool handleClick() { 247 checked = !checked; 248 return super.handleClick(); 249 } 250 } 251 252 /// radio button 253 class RadioButton : ImageTextButton { 254 this(string ID = null, string textResourceId = null) { 255 super(ID, "btn_radio", textResourceId); 256 styleId = "TRANSPARENT_BUTTON_BACKGROUND"; 257 checkable = true; 258 } 259 this(string ID, dstring labelText) { 260 super(ID, "btn_radio", labelText); 261 styleId = "TRANSPARENT_BUTTON_BACKGROUND"; 262 checkable = true; 263 } 264 265 void uncheckSiblings() { 266 Widget p = parent; 267 if (!p) 268 return; 269 for (int i = 0; i < p.childCount; i++) { 270 Widget child = p.child(i); 271 if (child is this) 272 continue; 273 RadioButton rb = cast(RadioButton)child; 274 if (rb) 275 rb.checked = false; 276 } 277 } 278 279 // called to process click and notify listeners 280 override protected bool handleClick() { 281 uncheckSiblings(); 282 checked = true; 283 284 return super.handleClick(); 285 } 286 287 } 288 289 /// Text only button 290 class Button : Widget { 291 protected UIString _text; 292 override @property dstring text() { return _text; } 293 override @property Widget text(dstring s) { _text = s; requestLayout(); return this; } 294 override @property Widget text(ref UIString s) { _text = s; requestLayout(); return this; } 295 @property Widget textResource(string s) { _text = s; requestLayout(); return this; } 296 this(string ID = null) { 297 super(ID); 298 styleId = "BUTTON"; 299 clickable = true; 300 focusable = true; 301 trackHover = true; 302 } 303 304 override void measure(int parentWidth, int parentHeight) { 305 FontRef font = font(); 306 Point sz = font.textSize(text); 307 measuredContent(parentWidth, parentHeight, sz.x, sz.y); 308 } 309 310 override void onDraw(DrawBuf buf) { 311 super.onDraw(buf); 312 Rect rc = _pos; 313 applyMargins(rc); 314 buf.fillRect(_pos, backgroundColor); 315 applyPadding(rc); 316 auto saver = ClipRectSaver(buf, rc); 317 FontRef font = font(); 318 Point sz = font.textSize(text); 319 applyAlign(rc, sz); 320 font.drawText(buf, rc.left, rc.top, text, textColor); 321 } 322 323 } 324 325 /// scroll event handler interface 326 interface OnScrollHandler { 327 /// handle scroll event 328 bool onScrollEvent(AbstractSlider source, ScrollEvent event); 329 } 330 331 /// base class for widgets like scrollbars and sliders 332 class AbstractSlider : WidgetGroup { 333 protected int _minValue = 0; 334 protected int _maxValue = 100; 335 protected int _pageSize = 30; 336 protected int _position = 20; 337 338 this(string ID) { 339 super(ID); 340 } 341 342 /// scroll event listeners 343 Signal!OnScrollHandler onScrollEventListener; 344 345 /// returns slider position 346 @property int position() const { return _position; } 347 /// sets new slider position 348 @property AbstractSlider position(int newPosition) { 349 if (_position != newPosition) { 350 _position = newPosition; 351 onPositionChanged(); 352 } 353 return this; 354 } 355 protected void onPositionChanged() { 356 requestLayout(); 357 } 358 /// returns slider range min value 359 @property int minValue() const { return _minValue; } 360 /// returns slider range max value 361 @property int maxValue() const { return _maxValue; } 362 /// page size (visible area size) 363 @property int pageSize() const { return _pageSize; } 364 /// set page size (visible area size) 365 @property AbstractSlider pageSize(int size) { 366 if (_pageSize != size) { 367 _pageSize = size; 368 requestLayout(); 369 } 370 return this; 371 } 372 /// set new range (min and max values for slider) 373 AbstractSlider setRange(int min, int max) { 374 if (_minValue != min || _maxValue != max) { 375 _minValue = min; 376 _maxValue = max; 377 requestLayout(); 378 } 379 return this; 380 } 381 382 protected bool sendScrollEvent(ScrollAction action, int position) { 383 if (!onScrollEventListener.assigned) 384 return false; 385 ScrollEvent event = new ScrollEvent(action, _minValue, _maxValue, _pageSize, position); 386 bool res = onScrollEventListener(this, event); 387 if (event.positionChanged) { 388 _position = event.position; 389 if (_position > _maxValue) 390 _position = _maxValue; 391 if (_position < _minValue) 392 _position = _minValue; 393 onPositionChanged(); 394 } 395 return true; 396 } 397 } 398 399 /// scroll bar - either vertical or horizontal 400 class ScrollBar : AbstractSlider, OnClickHandler { 401 protected ImageButton _btnBack; 402 protected ImageButton _btnForward; 403 protected SliderButton _indicator; 404 protected PageScrollButton _pageUp; 405 protected PageScrollButton _pageDown; 406 protected Rect _scrollArea; 407 protected int _btnSize; 408 protected int _minIndicatorSize; 409 410 411 412 class PageScrollButton : Widget { 413 this(string ID) { 414 super(ID); 415 styleId = "PAGE_SCROLL"; 416 trackHover = true; 417 clickable = true; 418 } 419 } 420 421 class SliderButton : ImageButton { 422 Point _dragStart; 423 int _dragStartPosition; 424 bool _dragging; 425 Rect _dragStartRect; 426 427 this(string resourceId) { 428 super("SLIDER", resourceId); 429 trackHover = true; 430 } 431 432 /// process mouse event; return true if event is processed by widget. 433 override bool onMouseEvent(MouseEvent event) { 434 // support onClick 435 if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) { 436 setState(State.Pressed); 437 _dragging = true; 438 _dragStart.x = event.x; 439 _dragStart.y = event.y; 440 _dragStartPosition = _position; 441 _dragStartRect = _pos; 442 sendScrollEvent(ScrollAction.SliderPressed, _position); 443 return true; 444 } 445 if (event.action == MouseAction.FocusOut && _dragging) { 446 return true; 447 } 448 if (event.action == MouseAction.Move && _dragging) { 449 int delta = _orientation == Orientation.Vertical ? event.y - _dragStart.y : event.x - _dragStart.x; 450 Rect rc = _dragStartRect; 451 int offset; 452 int space; 453 if (_orientation == Orientation.Vertical) { 454 rc.top += delta; 455 rc.bottom += delta; 456 if (rc.top < _scrollArea.top) { 457 rc.top = _scrollArea.top; 458 rc.bottom = _scrollArea.top + _dragStartRect.height; 459 } else if (rc.bottom > _scrollArea.bottom) { 460 rc.top = _scrollArea.bottom - _dragStartRect.height; 461 rc.bottom = _scrollArea.bottom; 462 } 463 offset = rc.top - _scrollArea.top; 464 space = _scrollArea.height - rc.height; 465 } else { 466 rc.left += delta; 467 rc.right += delta; 468 if (rc.left < _scrollArea.left) { 469 rc.left = _scrollArea.left; 470 rc.right = _scrollArea.left + _dragStartRect.width; 471 } else if (rc.right > _scrollArea.right) { 472 rc.left = _scrollArea.right - _dragStartRect.width; 473 rc.right = _scrollArea.right; 474 } 475 offset = rc.left - _scrollArea.left; 476 space = _scrollArea.width - rc.width; 477 } 478 layoutButtons(rc); 479 //_pos = rc; 480 int position = space > 0 ? _minValue + offset * (_maxValue - _minValue - _pageSize) / space : 0; 481 invalidate(); 482 onIndicatorDragging(_dragStartPosition, position); 483 return true; 484 } 485 if (event.action == MouseAction.ButtonUp && event.button == MouseButton.Left) { 486 resetState(State.Pressed); 487 if (_dragging) { 488 sendScrollEvent(ScrollAction.SliderReleased, _position); 489 _dragging = false; 490 } 491 return true; 492 } 493 if (event.action == MouseAction.Move && trackHover) { 494 if (!(state & State.Hovered)) { 495 Log.d("Hover ", id); 496 setState(State.Hovered); 497 } 498 return true; 499 } 500 if ((event.action == MouseAction.Leave || event.action == MouseAction.Cancel) && trackHover) { 501 Log.d("Leave ", id); 502 resetState(State.Hovered); 503 return true; 504 } 505 if (event.action == MouseAction.Cancel) { 506 Log.d("SliderButton.onMouseEvent event.action == MouseAction.Cancel"); 507 resetState(State.Pressed); 508 _dragging = false; 509 return true; 510 } 511 return false; 512 } 513 514 } 515 516 protected bool onIndicatorDragging(int initialPosition, int currentPosition) { 517 _position = currentPosition; 518 return sendScrollEvent(ScrollAction.SliderMoved, currentPosition); 519 } 520 521 private bool calcButtonSizes(int availableSize, ref int spaceBackSize, ref int spaceForwardSize, ref int indicatorSize) { 522 int dv = _maxValue - _minValue; 523 if (_pageSize >= dv) { 524 // full size 525 spaceBackSize = spaceForwardSize = 0; 526 indicatorSize = availableSize; 527 return false; 528 } 529 if (dv < 0) 530 dv = 0; 531 indicatorSize = _pageSize * availableSize / dv; 532 if (indicatorSize < _minIndicatorSize) 533 indicatorSize = _minIndicatorSize; 534 if (indicatorSize >= availableSize) { 535 // full size 536 spaceBackSize = spaceForwardSize = 0; 537 indicatorSize = availableSize; 538 return false; 539 } 540 int spaceLeft = availableSize - indicatorSize; 541 int topv = _position - _minValue; 542 int bottomv = _position + _pageSize - _minValue; 543 if (topv < 0) 544 topv = 0; 545 if (bottomv > dv) 546 bottomv = dv; 547 bottomv = dv - bottomv; 548 spaceBackSize = spaceLeft * topv / (topv + bottomv); 549 spaceForwardSize = spaceLeft - spaceBackSize; 550 return true; 551 } 552 553 protected Orientation _orientation = Orientation.Vertical; 554 /// returns scrollbar orientation (Vertical, Horizontal) 555 @property Orientation orientation() { return _orientation; } 556 /// sets scrollbar orientation 557 @property ScrollBar orientation(Orientation value) { 558 if (_orientation != value) { 559 _orientation = value; 560 _btnBack.drawableId = style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_BUTTON_UP : ATTR_SCROLLBAR_BUTTON_LEFT); 561 _btnForward.drawableId = style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_BUTTON_DOWN : ATTR_SCROLLBAR_BUTTON_RIGHT); 562 _indicator.drawableId = style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_INDICATOR_VERTICAL : ATTR_SCROLLBAR_INDICATOR_HORIZONTAL); 563 requestLayout(); 564 } 565 return this; 566 } 567 568 this(string ID = null, Orientation orient = Orientation.Vertical) { 569 super(ID); 570 styleId = "SCROLLBAR"; 571 _orientation = orient; 572 _btnBack = new ImageButton("BACK", style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_BUTTON_UP : ATTR_SCROLLBAR_BUTTON_LEFT)); 573 _btnForward = new ImageButton("FORWARD", style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_BUTTON_DOWN : ATTR_SCROLLBAR_BUTTON_RIGHT)); 574 _pageUp = new PageScrollButton("PAGE_UP"); 575 _pageDown = new PageScrollButton("PAGE_DOWN"); 576 _btnBack.styleId("SCROLLBAR_BUTTON"); 577 _btnForward.styleId("SCROLLBAR_BUTTON"); 578 _indicator = new SliderButton(style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_INDICATOR_VERTICAL : ATTR_SCROLLBAR_INDICATOR_HORIZONTAL)); 579 addChild(_btnBack); 580 addChild(_btnForward); 581 addChild(_indicator); 582 addChild(_pageUp); 583 addChild(_pageDown); 584 _btnBack.onClickListener = &onClick; 585 _btnForward.onClickListener = &onClick; 586 _pageUp.onClickListener = &onClick; 587 _pageDown.onClickListener = &onClick; 588 } 589 590 override void measure(int parentWidth, int parentHeight) { 591 Point sz; 592 _btnBack.measure(parentWidth, parentHeight); 593 _btnForward.measure(parentWidth, parentHeight); 594 _indicator.measure(parentWidth, parentHeight); 595 _pageUp.measure(parentWidth, parentHeight); 596 _pageDown.measure(parentWidth, parentHeight); 597 _btnSize = _btnBack.measuredWidth; 598 _minIndicatorSize = _orientation == Orientation.Vertical ? _indicator.measuredHeight : _indicator.measuredWidth; 599 if (_btnSize < _btnBack.measuredHeight) 600 _btnSize = _btnBack.measuredHeight; 601 if (_btnSize < 16) 602 _btnSize = 16; 603 if (_orientation == Orientation.Vertical) { 604 // vertical 605 sz.x = _btnSize; 606 sz.y = _btnSize * 5; // min height 607 } else { 608 // horizontal 609 sz.y = _btnSize; 610 sz.x = _btnSize * 5; // min height 611 } 612 measuredContent(parentWidth, parentHeight, sz.x, sz.y); 613 } 614 615 override protected void onPositionChanged() { 616 if (!needLayout) 617 layoutButtons(); 618 } 619 620 protected void layoutButtons() { 621 Rect irc = _scrollArea; 622 if (_orientation == Orientation.Vertical) { 623 // vertical 624 int spaceBackSize, spaceForwardSize, indicatorSize; 625 bool indicatorVisible = calcButtonSizes(_scrollArea.height, spaceBackSize, spaceForwardSize, indicatorSize); 626 irc.top += spaceBackSize; 627 irc.bottom -= spaceForwardSize; 628 layoutButtons(irc); 629 } else { 630 // horizontal 631 int spaceBackSize, spaceForwardSize, indicatorSize; 632 bool indicatorVisible = calcButtonSizes(_scrollArea.width, spaceBackSize, spaceForwardSize, indicatorSize); 633 irc.left += spaceBackSize; 634 irc.right -= spaceForwardSize; 635 layoutButtons(irc); 636 } 637 } 638 639 protected void layoutButtons(Rect irc) { 640 Rect r; 641 _indicator.visibility = Visibility.Visible; 642 if (_orientation == Orientation.Vertical) { 643 _indicator.layout(irc); 644 if (_scrollArea.top < irc.top) { 645 r = _scrollArea; 646 r.bottom = irc.top; 647 _pageUp.layout(r); 648 _pageUp.visibility = Visibility.Visible; 649 } else { 650 _pageUp.visibility = Visibility.Invisible; 651 } 652 if (_scrollArea.bottom > irc.bottom) { 653 r = _scrollArea; 654 r.top = irc.bottom; 655 _pageDown.layout(r); 656 _pageDown.visibility = Visibility.Visible; 657 } else { 658 _pageDown.visibility = Visibility.Invisible; 659 } 660 } else { 661 _indicator.layout(irc); 662 if (_scrollArea.left < irc.left) { 663 r = _scrollArea; 664 r.right = irc.left; 665 _pageUp.layout(r); 666 _pageUp.visibility = Visibility.Visible; 667 } else { 668 _pageUp.visibility = Visibility.Invisible; 669 } 670 if (_scrollArea.right > irc.right) { 671 r = _scrollArea; 672 r.left = irc.right; 673 _pageDown.layout(r); 674 _pageDown.visibility = Visibility.Visible; 675 } else { 676 _pageDown.visibility = Visibility.Invisible; 677 } 678 } 679 } 680 681 override void layout(Rect rc) { 682 _needLayout = false; 683 applyMargins(rc); 684 applyPadding(rc); 685 Rect r; 686 if (_orientation == Orientation.Vertical) { 687 // vertical 688 // buttons 689 int backbtnpos = rc.top + _btnSize; 690 int fwdbtnpos = rc.bottom - _btnSize; 691 r = rc; 692 r.bottom = backbtnpos; 693 _btnBack.layout(r); 694 r = rc; 695 r.top = fwdbtnpos; 696 _btnForward.layout(r); 697 // indicator 698 r = rc; 699 r.top = backbtnpos; 700 r.bottom = fwdbtnpos; 701 _scrollArea = r; 702 } else { 703 // horizontal 704 int backbtnpos = rc.left + _btnSize; 705 int fwdbtnpos = rc.right - _btnSize; 706 r = rc; 707 r.right = backbtnpos; 708 _btnBack.layout(r); 709 r = rc; 710 r.left = fwdbtnpos; 711 _btnForward.layout(r); 712 // indicator 713 r = rc; 714 r.left = backbtnpos; 715 r.right = fwdbtnpos; 716 _scrollArea = r; 717 } 718 layoutButtons(); 719 _pos = rc; 720 } 721 722 override bool onClick(Widget source) { 723 Log.d("Scrollbar.onClick ", source.id); 724 if (source.compareId("BACK")) 725 return sendScrollEvent(ScrollAction.LineUp, position); 726 if (source.compareId("FORWARD")) 727 return sendScrollEvent(ScrollAction.LineDown, position); 728 if (source.compareId("PAGE_UP")) 729 return sendScrollEvent(ScrollAction.PageUp, position); 730 if (source.compareId("PAGE_DOWN")) 731 return sendScrollEvent(ScrollAction.PageDown, position); 732 return true; 733 } 734 735 /// handle mouse wheel events 736 override bool onMouseEvent(MouseEvent event) { 737 if (visibility != Visibility.Visible) 738 return false; 739 if (event.action == MouseAction.Wheel) { 740 int delta = event.wheelDelta; 741 if (delta > 0) 742 sendScrollEvent(ScrollAction.LineUp, position); 743 else if (delta < 0) 744 sendScrollEvent(ScrollAction.LineDown, position); 745 return true; 746 } 747 return super.onMouseEvent(event); 748 } 749 750 /// Draw widget at its position to buffer 751 override void onDraw(DrawBuf buf) { 752 if (visibility != Visibility.Visible) 753 return; 754 super.onDraw(buf); 755 Rect rc = _pos; 756 applyMargins(rc); 757 applyPadding(rc); 758 auto saver = ClipRectSaver(buf, rc); 759 _btnForward.onDraw(buf); 760 _btnBack.onDraw(buf); 761 _pageUp.onDraw(buf); 762 _pageDown.onDraw(buf); 763 _indicator.onDraw(buf); 764 } 765 } 766