1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 5 6 module dfl.tabcontrol; 7 8 private import dfl.internal.dlib; 9 10 private import dfl.control, dfl.panel, dfl.internal.winapi, dfl.drawing; 11 private import dfl.application, dfl.event, dfl.base, dfl.collections; 12 13 14 private extern(Windows) void _initTabcontrol(); 15 16 17 18 class TabPage: Panel { 19 20 this(Dstring tabText) { 21 this(); 22 23 this.text = tabText; 24 } 25 26 /+ 27 /// ditto 28 this(Object v) { // package 29 this(getObjectString(v)); 30 } 31 +/ 32 33 /// ditto 34 this() { 35 Application.ppin(cast(void*)this); 36 37 ctrlStyle |= ControlStyles.CONTAINER_CONTROL; 38 39 wstyle &= ~WS_VISIBLE; 40 cbits &= ~CBits.VISIBLE; 41 } 42 43 44 override Dstring toString() { 45 return text; 46 } 47 48 49 alias Control.opEquals opEquals; 50 51 52 override Dequ opEquals(Object o) { 53 return text == getObjectString(o); 54 } 55 56 57 Dequ opEquals(Dstring val) { 58 return text == val; 59 } 60 61 62 alias Control.opCmp opCmp; 63 64 65 override int opCmp(Object o) { 66 return stringICmp(text, getObjectString(o)); 67 } 68 69 70 int opCmp(Dstring val) { 71 return stringICmp(text, val); 72 } 73 74 75 // imageIndex 76 77 78 override @property void text(Dstring newText) { // setter 79 // Note: this probably causes toStringz() to be called twice, 80 // allocating 2 of the same string. 81 82 super.text = newText; 83 84 if(created) { 85 TabControl tc; 86 tc = cast(TabControl)parent; 87 if(tc) { 88 tc.updateTabText(this, newText); 89 } 90 } 91 } 92 93 alias Panel.text text; // Overload with Panel.text. 94 95 96 /+ 97 final @property void toolTipText(Dstring ttt) { // setter 98 // TODO: ... 99 } 100 101 102 final @property Dstring toolTipText() { // getter 103 // TODO: ... 104 return null; 105 } 106 +/ 107 108 109 /+ package +/ /+ protected +/ override int _rtype() { // package 110 return 4; 111 } 112 113 114 protected override void setBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { 115 assert(0); // Cannot set bounds of TabPage; it is done automatically. 116 } 117 118 119 package final @property void realBounds(Rect r) { // setter 120 // DMD 0.124: if I don't put this here, super.setBoundsCore ends up calling setBoundsCore instead of super.setBoundsCore. 121 void delegate(int, int, int, int, BoundsSpecified) _foo = &setBoundsCore; 122 123 super.setBoundsCore(r.x, r.y, r.width, r.height, BoundsSpecified.ALL); 124 } 125 126 127 protected override void setVisibleCore(bool byes) { 128 assert(0); // Cannot set visibility of TabPage; it is done automatically. 129 } 130 131 132 package final @property void realVisible(bool byes) { // setter 133 // DMD 0.124: if I don't put this here, super.setVisibleCore ends up calling setVisibleCore instead of super.setVisibleCore. 134 void delegate(bool byes) _foo = &setVisibleCore; 135 136 super.setVisibleCore(byes); 137 } 138 } 139 140 141 package union TcItem { 142 TC_ITEMW tciw; 143 TC_ITEMA tcia; 144 struct { 145 UINT mask; 146 UINT lpReserved1; 147 UINT lpReserved2; 148 private void* pszText; 149 int cchTextMax; 150 int iImage; 151 LPARAM lParam; 152 } 153 } 154 155 156 157 class TabPageCollection { 158 protected this(TabControl owner) 159 in { 160 assert(owner.tchildren is null); 161 } 162 body { 163 tc = owner; 164 } 165 166 167 private: 168 169 TabControl tc; 170 TabPage[] _pages = null; 171 172 173 void doPages() 174 in { 175 assert(created); 176 } 177 body { 178 Rect area; 179 area = tc.displayRectangle; 180 181 Message m; 182 m.hWnd = tc.handle; 183 184 // Note: duplicate code. 185 //TC_ITEMA tci; 186 TcItem tci; 187 if(dfl.internal.utf.useUnicode) { 188 m.msg = TCM_INSERTITEMW; // <-- 189 foreach(int i, TabPage page; _pages) { 190 // TODO: TCIF_RTLREADING flag based on rightToLeft property. 191 tci.mask = TCIF_TEXT | TCIF_PARAM; 192 tci.tciw.pszText = cast(typeof(tci.tciw.pszText))dfl.internal.utf.toUnicodez(page.text); // <-- 193 static assert(tci.lParam.sizeof >= (void*).sizeof); 194 tci.lParam = cast(LPARAM)cast(void*)page; 195 196 m.wParam = i; 197 m.lParam = cast(LPARAM)&tci.tciw; 198 tc.prevWndProc(m); 199 assert(cast(int)m.result != -1); 200 } 201 } else 202 { 203 m.msg = TCM_INSERTITEMA; // <-- 204 foreach(int i, TabPage page; _pages) { 205 // TODO: TCIF_RTLREADING flag based on rightToLeft property. 206 tci.mask = TCIF_TEXT | TCIF_PARAM; 207 tci.tcia.pszText = cast(typeof(tci.tcia.pszText))dfl.internal.utf.toAnsiz(page.text); // <-- 208 static assert(tci.lParam.sizeof >= (void*).sizeof); 209 tci.lParam = cast(LPARAM)cast(void*)page; 210 211 m.wParam = i; 212 m.lParam = cast(LPARAM)&tci.tcia; 213 tc.prevWndProc(m); 214 assert(cast(int)m.result != -1); 215 } 216 } 217 } 218 219 220 package final @property bool created() { // getter 221 return tc && tc.created(); 222 } 223 224 225 void _added(size_t idx, TabPage val) { 226 if(val.parent) { 227 TabControl tc; 228 tc = cast(TabControl)val.parent; 229 if(tc && tc.tabPages.indexOf(val) != -1) { 230 throw new DflException("TabPage already has a parent"); 231 } 232 } 233 234 //val.realVisible = false; 235 assert(val.visible == false); 236 assert(!(tc is null)); 237 val.parent = tc; 238 239 if(created) { 240 Message m; 241 //TC_ITEMA tci; 242 TcItem tci; 243 // TODO: TCIF_RTLREADING flag based on rightToLeft property. 244 tci.mask = TCIF_TEXT | TCIF_PARAM; 245 static assert(tci.lParam.sizeof >= (void*).sizeof); 246 tci.lParam = cast(LPARAM)cast(void*)val; 247 if(dfl.internal.utf.useUnicode) { 248 tci.tciw.pszText = cast(typeof(tci.tciw.pszText))dfl.internal.utf.toUnicodez(val.text); 249 m = Message(tc.handle, TCM_INSERTITEMW, idx, cast(LPARAM)&tci.tciw); 250 } else { 251 tci.tcia.pszText = cast(typeof(tci.tcia.pszText))dfl.internal.utf.toAnsiz(val.text); 252 m = Message(tc.handle, TCM_INSERTITEMA, idx, cast(LPARAM)&tci.tcia); 253 } 254 tc.prevWndProc(m); 255 assert(cast(int)m.result != -1); 256 257 if(tc.selectedTab is val) { 258 //val.realVisible = true; 259 tc.tabToFront(val); 260 } 261 } 262 } 263 264 265 void _removed(size_t idx, TabPage val) { 266 if(size_t.max == idx) { // Clear all. 267 if(created) { 268 Message m; 269 m = Message(tc.handle, TCM_DELETEALLITEMS, 0, 0); 270 tc.prevWndProc(m); 271 } 272 } else { 273 //val.parent = null; // Can't do that. 274 275 if(created) { 276 Message m; 277 m = Message(tc.handle, TCM_DELETEITEM, idx, 0); 278 tc.prevWndProc(m); 279 280 // Hide this one. 281 val.realVisible = false; 282 283 // Show next visible. 284 val = tc.selectedTab; 285 if(val) { 286 tc.tabToFront(val); 287 } 288 } 289 } 290 } 291 292 293 public: 294 295 mixin ListWrapArray!(TabPage, _pages, 296 _blankListCallback!(TabPage), _added, 297 _blankListCallback!(TabPage), _removed, 298 true, false, false, 299 true); // CLEAR_EACH 300 } 301 302 303 304 enum TabAlignment: ubyte { 305 TOP, /// 306 BOTTOM, /// ditto 307 LEFT, /// ditto 308 RIGHT, /// ditto 309 } 310 311 312 313 enum TabAppearance: ubyte { 314 NORMAL, /// 315 BUTTONS, /// ditto 316 FLAT_BUTTONS, /// ditto 317 } 318 319 320 321 enum TabDrawMode: ubyte { 322 NORMAL, /// 323 OWNER_DRAW_FIXED, /// ditto 324 } 325 326 327 328 class TabControlBase: ControlSuperClass { 329 this() { 330 _initTabcontrol(); 331 332 wstyle |= WS_TABSTOP; 333 ctrlStyle |= ControlStyles.SELECTABLE | ControlStyles.CONTAINER_CONTROL; 334 wclassStyle = tabcontrolClassStyle; 335 } 336 337 338 339 final @property void drawMode(TabDrawMode dm) { // setter 340 switch(dm) { 341 case TabDrawMode.OWNER_DRAW_FIXED: 342 _style(wstyle | TCS_OWNERDRAWFIXED); 343 break; 344 345 case TabDrawMode.NORMAL: 346 _style(wstyle & ~TCS_OWNERDRAWFIXED); 347 break; 348 349 default: 350 assert(0); 351 } 352 353 _crecreate(); 354 } 355 356 /// ditto 357 final @property TabDrawMode drawMode() { // getter 358 if(wstyle & TCS_OWNERDRAWFIXED) { 359 return TabDrawMode.OWNER_DRAW_FIXED; 360 } 361 return TabDrawMode.NORMAL; 362 } 363 364 365 override @property Rect displayRectangle() { // getter 366 if(!created) { 367 return super.displayRectangle(); // Hack? 368 } else { 369 RECT drr; 370 Message m; 371 drr.left = 0; 372 drr.top = 0; 373 drr.right = clientSize.width; 374 drr.bottom = clientSize.height; 375 m = Message(hwnd, TCM_ADJUSTRECT, FALSE, cast(LPARAM)&drr); 376 prevWndProc(m); 377 return Rect(&drr); 378 } 379 } 380 381 382 protected override @property Size defaultSize() { // getter 383 return Size(200, 200); // ? 384 } 385 386 387 388 final Rect getTabRect(int i) { 389 Rect result; 390 391 if(created) { 392 RECT rt; 393 Message m; 394 m = Message(hwnd, TCM_GETITEMRECT, cast(WPARAM)i, cast(LPARAM)&rt); 395 prevWndProc(m); 396 if(!m.result) { 397 goto rtfail; 398 } 399 result = Rect(&rt); 400 } else { 401 rtfail: 402 with(result) { 403 x = 0; 404 y = 0; 405 width = 0; 406 height = 0; 407 } 408 } 409 410 return result; 411 } 412 413 414 // drawItem event. 415 //EventHandler selectedIndexChanged; 416 Event!(TabControlBase, EventArgs) selectedIndexChanged; /// 417 //CancelEventHandler selectedIndexChanging; 418 Event!(TabControlBase, CancelEventArgs) selectedIndexChanging; /// 419 420 421 protected override void createParams(ref CreateParams cp) { 422 super.createParams(cp); 423 424 cp.className = TABCONTROL_CLASSNAME; 425 } 426 427 428 429 protected void onSelectedIndexChanged(EventArgs ea) { 430 selectedIndexChanged(this, ea); 431 } 432 433 434 435 protected void onSelectedIndexChanging(CancelEventArgs ea) { 436 selectedIndexChanging(this, ea); 437 } 438 439 440 protected override void prevWndProc(ref Message msg) { 441 //msg.result = CallWindowProcA(tabcontrolPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam); 442 msg.result = dfl.internal.utf.callWindowProc(tabcontrolPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam); 443 } 444 445 446 protected override void wndProc(ref Message m) { 447 // TODO: support the tab control messages. 448 449 switch(m.msg) { 450 /+ 451 case WM_SETFOCUS: 452 _exStyle(_exStyle() | WS_EX_CONTROLPARENT); 453 break; 454 455 case WM_KILLFOCUS: 456 _exStyle(_exStyle() & ~WS_EX_CONTROLPARENT); 457 break; 458 +/ 459 460 case TCM_DELETEALLITEMS: 461 m.result = FALSE; 462 return; 463 464 case TCM_DELETEITEM: 465 m.result = FALSE; 466 return; 467 468 case TCM_INSERTITEMA: 469 case TCM_INSERTITEMW: 470 m.result = -1; 471 return; 472 473 //case TCM_REMOVEIMAGE: 474 // return; 475 476 //case TCM_SETIMAGELIST: 477 // m.result = cast(LRESULT)null; 478 // return; 479 480 case TCM_SETITEMA: 481 case TCM_SETITEMW: 482 m.result = FALSE; 483 return; 484 485 case TCM_SETITEMEXTRA: 486 m.result = FALSE; 487 return; 488 489 case TCM_SETITEMSIZE: 490 m.result = 0; 491 return; 492 493 case TCM_SETPADDING: 494 return; 495 496 case TCM_SETTOOLTIPS: 497 return; 498 499 default: 500 } 501 502 super.wndProc(m); 503 } 504 505 506 protected override void onReflectedMessage(ref Message m) { 507 super.onReflectedMessage(m); 508 509 TabPage page; 510 NMHDR* nmh; 511 nmh = cast(NMHDR*)m.lParam; 512 513 switch(nmh.code) { 514 case TCN_SELCHANGE: 515 onSelectedIndexChanged(EventArgs.empty); 516 break; 517 518 case TCN_SELCHANGING: { 519 scope CancelEventArgs ea = new CancelEventArgs; 520 onSelectedIndexChanging(ea); 521 if(ea.cancel) { 522 m.result = TRUE; // Prevent change. 523 return; 524 } 525 } 526 m.result = FALSE; // Allow change. 527 return; 528 529 default: 530 } 531 } 532 } 533 534 535 536 class TabControl: TabControlBase { // docmain 537 this() { 538 tchildren = new TabPageCollection(this); 539 _pad = Point(6, 3); 540 } 541 542 543 544 final @property void alignment(TabAlignment talign) { // setter 545 switch(talign) { 546 case TabAlignment.TOP: 547 _style(wstyle & ~(TCS_VERTICAL | TCS_RIGHT | TCS_BOTTOM)); 548 break; 549 550 case TabAlignment.BOTTOM: 551 _style((wstyle & ~(TCS_VERTICAL | TCS_RIGHT)) | TCS_BOTTOM); 552 break; 553 554 case TabAlignment.LEFT: 555 _style((wstyle & ~(TCS_BOTTOM | TCS_RIGHT)) | TCS_VERTICAL); 556 break; 557 558 case TabAlignment.RIGHT: 559 _style((wstyle & ~TCS_BOTTOM) | TCS_VERTICAL | TCS_RIGHT); 560 break; 561 562 default: 563 assert(0); 564 } 565 566 // Display rectangle changed. 567 568 if(created && visible) { 569 invalidate(true); // Update children too ? 570 571 TabPage page; 572 page = selectedTab; 573 if(page) { 574 page.realBounds = displayRectangle; 575 } 576 } 577 } 578 579 /// ditto 580 final @property TabAlignment alignment() { // getter 581 // Note: TCS_RIGHT and TCS_BOTTOM are the same flag. 582 583 if(wstyle & TCS_VERTICAL) { 584 if(wstyle & TCS_RIGHT) { 585 return TabAlignment.RIGHT; 586 } 587 return TabAlignment.LEFT; 588 } else { 589 if(wstyle & TCS_BOTTOM) { 590 return TabAlignment.BOTTOM; 591 } 592 return TabAlignment.TOP; 593 } 594 } 595 596 597 598 final @property void appearance(TabAppearance tappear) { // setter 599 switch(tappear) { 600 case TabAppearance.NORMAL: 601 _style(wstyle & ~(TCS_BUTTONS | TCS_FLATBUTTONS)); 602 break; 603 604 case TabAppearance.BUTTONS: 605 _style((wstyle & ~TCS_FLATBUTTONS) | TCS_BUTTONS); 606 break; 607 608 case TabAppearance.FLAT_BUTTONS: 609 _style(wstyle | TCS_BUTTONS | TCS_FLATBUTTONS); 610 break; 611 612 default: 613 assert(0); 614 } 615 616 if(created && visible) { 617 invalidate(false); 618 619 TabPage page; 620 page = selectedTab; 621 if(page) { 622 page.realBounds = displayRectangle; 623 } 624 } 625 } 626 627 /// ditto 628 final @property TabAppearance appearance() { // getter 629 if(wstyle & TCS_FLATBUTTONS) { 630 return TabAppearance.FLAT_BUTTONS; 631 } 632 if(wstyle & TCS_BUTTONS) { 633 return TabAppearance.BUTTONS; 634 } 635 return TabAppearance.NORMAL; 636 } 637 638 639 640 final @property void padding(Point pad) { // setter 641 if(created) { 642 SendMessageA(hwnd, TCM_SETPADDING, 0, MAKELPARAM(pad.x, pad.y)); 643 644 TabPage page; 645 page = selectedTab; 646 if(page) { 647 page.realBounds = displayRectangle; 648 } 649 } 650 651 _pad = pad; 652 } 653 654 /// ditto 655 final @property Point padding() { // getter 656 return _pad; 657 } 658 659 660 661 final @property TabPageCollection tabPages() { // getter 662 return tchildren; 663 } 664 665 666 667 final @property void multiline(bool byes) { // setter 668 if(byes) { 669 _style(_style() | TCS_MULTILINE); 670 } else { 671 _style(_style() & ~TCS_MULTILINE); 672 } 673 674 TabPage page; 675 page = selectedTab; 676 if(page) { 677 page.realBounds = displayRectangle; 678 } 679 } 680 681 /// ditto 682 final @property bool multiline() { // getter 683 return (_style() & TCS_MULTILINE) != 0; 684 } 685 686 687 688 final @property int rowCount() { // getter 689 if(!created || !multiline) { 690 return 0; 691 } 692 Message m; 693 m = Message(hwnd, TCM_GETROWCOUNT, 0, 0); 694 prevWndProc(m); 695 return cast(int)m.result; 696 } 697 698 699 700 final @property int tabCount() { // getter 701 return tchildren._pages.length; 702 } 703 704 705 706 final @property void selectedIndex(int i) { // setter 707 if(!created || !tchildren._pages.length) { 708 return; 709 } 710 711 TabPage curpage; 712 curpage = selectedTab; 713 if(curpage is tchildren._pages[i]) { 714 return; // Already selected. 715 } 716 curpage.realVisible = false; 717 718 SendMessageA(hwnd, TCM_SETCURSEL, cast(WPARAM)i, 0); 719 tabToFront(tchildren._pages[i]); 720 } 721 722 /// ditto 723 // Returns -1 if there are no tabs selected. 724 final @property int selectedIndex() { // getter 725 if(!created || !tchildren._pages.length) { 726 return -1; 727 } 728 Message m; 729 m = Message(hwnd, TCM_GETCURSEL, 0, 0); 730 prevWndProc(m); 731 return cast(int)m.result; 732 } 733 734 735 736 final @property void selectedTab(TabPage page) { // setter 737 int i; 738 i = tabPages.indexOf(page); 739 if(-1 != i) { 740 selectedIndex = i; 741 } 742 } 743 744 /// ditto 745 final @property TabPage selectedTab() { // getter 746 int i; 747 i = selectedIndex; 748 if(-1 == i) { 749 return null; 750 } 751 return tchildren._pages[i]; 752 } 753 754 755 /+ 756 757 final @property void showToolTips(bool byes) { // setter 758 if(byes) { 759 _style(_style() | TCS_TOOLTIPS); 760 } else { 761 _style(_style() & ~TCS_TOOLTIPS); 762 } 763 } 764 765 /// ditto 766 final @property bool showToolTips() { // getter 767 return (_style() & TCS_TOOLTIPS) != 0; 768 } 769 +/ 770 771 772 protected override void onHandleCreated(EventArgs ea) { 773 super.onHandleCreated(ea); 774 775 SendMessageA(hwnd, TCM_SETPADDING, 0, MAKELPARAM(_pad.x, _pad.y)); 776 777 tchildren.doPages(); 778 779 // Bring selected tab to front. 780 if(tchildren._pages.length) { 781 int i; 782 i = selectedIndex; 783 if(-1 != i) { 784 tabToFront(tchildren._pages[i]); 785 } 786 } 787 } 788 789 790 protected override void onLayout(LayoutEventArgs ea) { 791 if(tchildren._pages.length) { 792 int i; 793 i = selectedIndex; 794 if(-1 != i) { 795 tchildren._pages[i].realBounds = displayRectangle; 796 //assert(tchildren._pages[i].bounds == displayRectangle); 797 } 798 } 799 800 //super.onLayout(ea); // Tab control shouldn't even have other controls on it. 801 super.onLayout(ea); // Should call it for consistency. Ideally it just checks handlers.length == 0 and does nothing. 802 } 803 804 805 /+ 806 protected override void wndProc(ref Message m) { 807 // TODO: support the tab control messages. 808 809 switch(m.msg) { 810 /+ // Now handled in onLayout(). 811 case WM_WINDOWPOSCHANGED: { 812 WINDOWPOS* wp; 813 wp = cast(WINDOWPOS*)m.lParam; 814 815 if(!(wp.flags & SWP_NOSIZE) || (wp.flags & SWP_FRAMECHANGED)) { 816 if(tchildren._pages.length) { 817 int i; 818 i = selectedIndex; 819 if(-1 != i) { 820 tchildren._pages[i].realBounds = displayRectangle; 821 //assert(tchildren._pages[i].bounds == displayRectangle); 822 } 823 } 824 } 825 } 826 break; 827 +/ 828 829 default: 830 } 831 832 super.wndProc(m); 833 } 834 +/ 835 836 837 protected override void onReflectedMessage(ref Message m) { 838 TabPage page; 839 NMHDR* nmh; 840 nmh = cast(NMHDR*)m.lParam; 841 842 switch(nmh.code) { 843 case TCN_SELCHANGE: 844 page = selectedTab; 845 if(page) { 846 tabToFront(page); 847 } 848 super.onReflectedMessage(m); 849 break; 850 851 case TCN_SELCHANGING: 852 super.onReflectedMessage(m); 853 if(!m.result) { // Allowed. 854 page = selectedTab; 855 if(page) { 856 page.realVisible = false; 857 } 858 } 859 return; 860 861 default: 862 super.onReflectedMessage(m); 863 } 864 } 865 866 867 /+ 868 /+ package +/ /+ protected +/ override int _rtype() { // package 869 return 0x20; 870 } 871 +/ 872 873 874 private: 875 Point _pad; 876 TabPageCollection tchildren; 877 878 879 void tabToFront(TabPage page) { 880 page.realBounds = displayRectangle; 881 //page.realVisible = true; 882 SetWindowPos(page.handle, HWND_TOP, 0, 0, 0, 0, /+ SWP_NOACTIVATE | +/ SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); 883 assert(page.visible == true); 884 885 /+ 886 // Make sure the previous tab isn't still focused. 887 // Will "steal" focus if done programatically. 888 SetFocus(handle); 889 //SetFocus(page.handle); 890 +/ 891 } 892 893 894 void updateTabText(TabPage page, Dstring newText) 895 in { 896 assert(created); 897 } 898 body { 899 int i; 900 i = tabPages.indexOf(page); 901 assert(-1 != i); 902 903 //TC_ITEMA tci; 904 TcItem tci; 905 tci.mask = TCIF_TEXT; 906 Message m; 907 if(dfl.internal.utf.useUnicode) { 908 tci.tciw.pszText = cast(typeof(tci.tciw.pszText))dfl.internal.utf.toUnicodez(newText); 909 m = Message(hwnd, TCM_SETITEMW, cast(WPARAM)i, cast(LPARAM)&tci.tciw); 910 } else 911 { 912 tci.tcia.pszText = cast(typeof(tci.tcia.pszText))dfl.internal.utf.toAnsiz(newText); 913 m = Message(hwnd, TCM_SETITEMA, cast(WPARAM)i, cast(LPARAM)&tci.tcia); 914 } 915 prevWndProc(m); 916 917 // Updating a tab's text could cause tab rows to be adjusted, 918 // so update the selected tab's area. 919 page = selectedTab; 920 if(page) { 921 page.realBounds = displayRectangle; 922 } 923 } 924 } 925