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