1 /** DGui project file. 2 3 Copyright: Trogu Antonio Davide 2011-2013 4 5 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). 6 7 Authors: Trogu Antonio Davide 8 */ 9 module dguihub.tabcontrol; 10 11 import std.utf : toUTFz; 12 import dguihub.core.controls.subclassedcontrol; 13 import dguihub.core.interfaces.ilayoutcontrol; 14 import dguihub.layout.panel; 15 import dguihub.imagelist; 16 17 private struct TCItem { 18 TCITEMHEADERW header; 19 TabPage page; 20 } 21 22 enum TabAlignment { 23 top = 0, 24 left = TCS_VERTICAL, 25 right = TCS_VERTICAL | TCS_RIGHT, 26 bottom = TCS_BOTTOM, 27 } 28 29 class TabPage : Panel { 30 private int _imgIndex; 31 private TabControl _owner; 32 33 protected void initTabPage() { 34 //Does Nothing 35 } 36 37 @property public final int index() { 38 if (this._owner && this._owner.created && this._owner.tabPages) { 39 int i = 0; 40 41 foreach (TabPage tp; this._owner.tabPages) { 42 if (tp is this) { 43 return i; 44 } 45 46 i++; 47 } 48 } 49 50 return -1; 51 } 52 53 @property package void tabControl(TabControl tc) { 54 this._owner = tc; 55 } 56 57 @property public final TabControl tabControl() { 58 return this._owner; 59 } 60 61 alias @property Control.text text; 62 63 @property public override void text(string txt) { 64 super.text = txt; 65 66 if (this._owner && this._owner.created) { 67 TCItem tci = void; 68 69 tci.header.mask = TCIF_TEXT; 70 tci.header.pszText = toUTFz!(wchar*)(txt); 71 72 this._owner.sendMessage(TCM_SETITEMW, this.index, cast(LPARAM)&tci); 73 this.redraw(); 74 } 75 } 76 77 @property public final int imageIndex() { 78 return this._imgIndex; 79 } 80 81 @property public final void imageIndex(int idx) { 82 this._imgIndex = idx; 83 84 if (this._owner && this._owner.created) { 85 TCItem tci = void; 86 87 tci.header.mask = TCIF_IMAGE; 88 tci.header.iImage = idx; 89 90 this._owner.sendMessage(TCM_SETITEMW, this.index, cast(LPARAM)&tci); 91 } 92 } 93 94 protected override void createControlParams(ref CreateControlParams ccp) { 95 this.setExStyle(WS_EX_STATICEDGE, true); 96 97 super.createControlParams(ccp); 98 } 99 100 protected override void onHandleCreated(EventArgs e) { 101 this.initTabPage(); 102 103 super.onHandleCreated(e); 104 } 105 } 106 107 alias CancelEventArgs!(TabPage) CancelTabPageEventArgs; 108 109 class TabControl : SubclassedControl, ILayoutControl { 110 public Event!(Control, CancelTabPageEventArgs) tabPageChanging; 111 public Event!(Control, EventArgs) tagPageChanged; 112 113 private Collection!(TabPage) _tabPages; 114 private TabAlignment _ta = TabAlignment.top; 115 private ImageList _imgList; 116 private int _selIndex = 0; //By Default: select the first TagPage (if exists) 117 118 public final T addPage(T : TabPage = TabPage)(string t, int imgIndex = -1) { 119 if (!this._tabPages) { 120 this._tabPages = new Collection!(TabPage); 121 } 122 123 T tp = new T(); 124 tp.text = t; 125 tp.imageIndex = imgIndex; 126 tp.visible = false; 127 tp.tabControl = this; 128 tp.parent = this; 129 130 this._tabPages.add(tp); 131 132 if (this.created) { 133 this.createTabPage(tp); 134 } 135 136 return tp; 137 } 138 139 public final void removePage(int idx) { 140 if (this.created) { 141 this.removeTabPage(idx); 142 } 143 144 this._tabPages.removeAt(idx); 145 } 146 147 @property public final TabPage[] tabPages() { 148 if (this._tabPages) { 149 return this._tabPages.get(); 150 } 151 152 return null; 153 } 154 155 @property public final TabPage selectedPage() { 156 if (this._tabPages) { 157 return this._tabPages[this._selIndex]; 158 } 159 160 return null; 161 } 162 163 @property public final void selectedPage(TabPage stp) { 164 this.selectedIndex = stp.index; 165 } 166 167 @property public final int selectedIndex() { 168 return this._selIndex; 169 } 170 171 @property public final void selectedIndex(int idx) { 172 if (this._tabPages) { 173 TabPage sp = this.selectedPage; //Old TabPage 174 TabPage tp = this._tabPages[idx]; //New TabPage 175 176 if (sp && sp !is tp) { 177 this._selIndex = idx; 178 tp.visible = true; //Show new TabPage 179 sp.visible = false; //Hide old TabPage 180 } else if (sp is tp) // Same TabPage, make visibile 181 { 182 /* 183 * By default, TabPages are created not visible 184 */ 185 186 tp.visible = true; 187 } 188 189 if (this.created) { 190 this.updateLayout(); 191 } 192 } 193 } 194 195 @property public final ImageList imageList() { 196 return this._imgList; 197 } 198 199 @property public final void imageList(ImageList imgList) { 200 this._imgList = imgList; 201 202 if (this.created) { 203 this.sendMessage(TCM_SETIMAGELIST, 0, cast(LPARAM)this._imgList.handle); 204 } 205 } 206 207 @property public final TabAlignment alignment() { 208 return this._ta; 209 } 210 211 @property public final void alignment(TabAlignment ta) { 212 this.setStyle(this._ta, false); 213 this.setStyle(ta, true); 214 215 this._ta = ta; 216 } 217 218 private void doTabPages() { 219 if (this._tabPages) { 220 foreach (int i, TabPage tp; this._tabPages) { 221 this.createTabPage(tp, false); 222 223 if (i == this._selIndex) { 224 tp.visible = true; 225 this.updateLayout(); 226 } 227 } 228 229 this.selectedIndex = this._selIndex; 230 } 231 } 232 233 public void updateLayout() { 234 TabPage selPage = this.selectedPage; 235 236 if (selPage) { 237 TabControl tc = selPage.tabControl; 238 Rect adjRect, r = Rect(nullPoint, tc.clientSize); 239 240 tc.sendMessage(TCM_ADJUSTRECT, false, cast(LPARAM)&adjRect.rect); 241 242 r.left += adjRect.left; 243 r.top += adjRect.top; 244 r.right += r.left + adjRect.width; 245 r.bottom += r.top + adjRect.height; 246 247 selPage.bounds = r; //selPage docks its child componentsS 248 } 249 } 250 251 private void createTabPage(TabPage tp, bool adding = true) { 252 TCItem tci; 253 tci.header.mask = TCIF_IMAGE | TCIF_TEXT | TCIF_PARAM; 254 tci.header.iImage = tp.imageIndex; 255 tci.header.pszText = toUTFz!(wchar*)(tp.text); 256 tci.page = tp; 257 258 tp.sendMessage(DGUI_CREATEONLY, 0, 0); //Calls Control.create() 259 260 int idx = tp.index; 261 this.sendMessage(TCM_INSERTITEMW, idx, cast(LPARAM)&tci); 262 263 if (adding) //Adding mode: select the last TabPage 264 { 265 this.sendMessage(TCM_SETCURSEL, idx, 0); 266 this.selectedIndex = idx; 267 } 268 } 269 270 private void removeTabPage(int idx) { 271 if (this._tabPages) { 272 if (idx == this._selIndex) { 273 this.selectedIndex = idx > 0 ? idx - 1 : 0; 274 } 275 276 if (this.created) { 277 this.sendMessage(TCM_DELETEITEM, idx, 0); 278 this.sendMessage(TCM_SETCURSEL, this._selIndex, 0); //Set the new tab's index 279 } 280 281 TabPage tp = this._tabPages[idx]; 282 tp.dispose(); 283 } 284 } 285 286 protected override void createControlParams(ref CreateControlParams ccp) { 287 this.setStyle(WS_CLIPCHILDREN | WS_CLIPSIBLINGS, true); 288 this.setExStyle(WS_EX_CONTROLPARENT, true); 289 290 ccp.superclassName = WC_TABCONTROL; 291 ccp.className = WC_DTABCONTROL; 292 293 super.createControlParams(ccp); 294 } 295 296 protected override void onHandleCreated(EventArgs e) { 297 if (this._imgList) { 298 this.sendMessage(TCM_SETIMAGELIST, 0, cast(LPARAM)this._imgList.handle); 299 } 300 301 this.doTabPages(); 302 super.onHandleCreated(e); 303 } 304 305 protected override void onReflectedMessage(ref Message m) { 306 if (m.msg == WM_NOTIFY) { 307 NMHDR* pNotify = cast(NMHDR*)m.lParam; 308 309 switch (pNotify.code) { 310 case TCN_SELCHANGING: 311 scope CancelTabPageEventArgs e = new CancelTabPageEventArgs(this.selectedPage); 312 this.onTabPageChanging(e); 313 m.result = e.cancel; 314 break; 315 316 case TCN_SELCHANGE: 317 this.selectedIndex = this.sendMessage(TCM_GETCURSEL, 0, 0); 318 this.onTabPageChanged(EventArgs.empty); 319 break; 320 321 default: 322 break; 323 } 324 } 325 326 super.onReflectedMessage(m); 327 } 328 329 protected override void show() { 330 super.show(); 331 this.updateLayout(); 332 } 333 334 protected override void onResize(EventArgs e) { 335 this.updateLayout(); 336 super.onResize(e); 337 } 338 339 protected void onTabPageChanging(CancelTabPageEventArgs e) { 340 this.tabPageChanging(this, e); 341 } 342 343 protected void onTabPageChanged(EventArgs e) { 344 this.tagPageChanged(this, e); 345 } 346 }