1 /** 2 * A tab control wrapping the native Win32 `SysTabControl32` common control. 3 * 4 * Each tab page is an arbitrary `Widget` whose bounds track the tab control's 5 * display area. Selecting a tab shows its page and hides the others. 6 */ 7 module deft.controls.tabcontrol; 8 9 version (Windows): 10 11 import core.sys.windows.windows; 12 import core.sys.windows.commctrl; 13 14 import deft.controls.control; 15 import deft.events; 16 import deft.util.strings; 17 import deft.widget; 18 19 /** 20 * A native tab control hosting one `Widget` per page. 21 * 22 * Pages are positioned into the tab control's display rect (the area below the 23 * tab strip). The page widgets are expected to be children of the same parent 24 * window as the tab control. 25 */ 26 class TabControl : Control 27 { 28 private Widget[] pages_; 29 private int selected_ = -1; 30 31 /// Fired when the selected page changes, with the new page index. 32 Event!(int) onPageChanged; 33 34 /// Create a tab control as a child of `parent`. 35 this(Widget parent) 36 { 37 super(parent, "SysTabControl32", WS_TABSTOP); 38 } 39 40 /** 41 * Add a page captioned `title` showing `pageContent` when selected. Returns 42 * the index of the newly inserted page. 43 */ 44 int addPage(string title, Widget pageContent) 45 { 46 TCITEMW item; 47 item.mask = TCIF_TEXT; 48 item.pszText = cast(LPWSTR) title.toWStringz; 49 int index = cast(int) SendMessageW(handle, TCM_INSERTITEMW, pages_.length, cast(LPARAM)&item); 50 51 pages_ ~= pageContent; 52 layoutPages(); 53 54 if (pages_.length == 1) 55 setSelectedPage(0); 56 else 57 pageContent.setVisible(false); 58 59 return index; 60 } 61 62 /// Change the label of the tab at `index` (e.g. when the UI language changes). 63 void setTabTitle(int index, string title) 64 { 65 TCITEMW item; 66 item.mask = TCIF_TEXT; 67 item.pszText = cast(LPWSTR) title.toWStringz; 68 SendMessageW(handle, TCM_SETITEMW, index, cast(LPARAM)&item); 69 } 70 71 /// The index of the currently selected page, or -1 if none. 72 int getSelectedPage() 73 { 74 return cast(int) SendMessageW(handle, TCM_GETCURSEL, 0, 0); 75 } 76 77 /// Select the page at `index`, showing its content and hiding the rest. 78 void setSelectedPage(int index) 79 { 80 SendMessageW(handle, TCM_SETCURSEL, index, 0); 81 foreach (i, p; pages_) 82 p.setVisible(i == index); 83 selected_ = index; 84 layoutPages(); 85 } 86 87 /// The display rect (content area below the tab strip), in client coordinates. 88 Rect getDisplayRect() 89 { 90 RECT rc; 91 GetClientRect(handle, &rc); 92 SendMessageW(handle, TCM_ADJUSTRECT, FALSE, cast(LPARAM)&rc); 93 return Rect.fromRECT(rc); 94 } 95 96 private void layoutPages() 97 { 98 auto r = getDisplayRect(); 99 r.x += bounds.x; 100 r.y += bounds.y; 101 foreach (p; pages_) 102 p.setBounds(r); 103 } 104 105 /// Re-positions all pages into the current display rect; call on window resize. 106 void relayout() 107 { 108 layoutPages(); 109 } 110 111 /// Move/resize the tab control (bounds `r`, parent-relative), then re-lay out its pages. 112 override void setBounds(Rect r) 113 { 114 super.setBounds(r); 115 layoutPages(); 116 } 117 118 /// Translate tab-selection-change notifications into `onPageChanged`. 119 override bool processNotify(NMHDR* header) 120 { 121 if (header.code == TCN_SELCHANGE) 122 { 123 int idx = getSelectedPage(); 124 setSelectedPage(idx); 125 onPageChanged.fire(idx); 126 return true; 127 } 128 return false; 129 } 130 131 /// The preferred size of the tab control. 132 override Size getPreferredSize() 133 { 134 return Size(400, 300); 135 } 136 }