1 /** 2 * Widget module. 3 * 4 * License: 5 * MIT. See LICENSE for full details. 6 */ 7 module tkd.widget.notebook; 8 9 /** 10 * Imports. 11 */ 12 import std.array; 13 import std.conv; 14 import tkd.element.uielement; 15 import tkd.image.image; 16 import tkd.image.imageposition; 17 import tkd.widget.common.height; 18 import tkd.widget.common.padding; 19 import tkd.widget.common.width; 20 import tkd.widget.widget; 21 22 /** 23 * A notebook widget manages a collection of panes and displays a single one at 24 * a time. Each pane is associated with a tab, which the user may select to 25 * change the currently-displayed pane. 26 * 27 * Example: 28 * --- 29 * // The notebook must be created first. 30 * // See the constructor notes in the documentation. 31 * auto noteBook = new NoteBook(); 32 * 33 * // The pane's widgets are contained within the frame. 34 * auto pane = new Frame(noteBook); 35 * 36 * noteBook.addTab("Text", pane) 37 * .pack(); 38 * --- 39 * 40 * Common_Commands: 41 * These are injected common commands that can also be used with this widget. 42 * $(P 43 * $(LINK2 ./common/height.html, Height) $(BR) 44 * $(LINK2 ./common/padding.html, Padding) $(BR) 45 * $(LINK2 ./common/width.html, Width) $(BR) 46 * ) 47 * 48 * Additional_Events: 49 * Additional events that can also be bound to using the $(LINK2 ../element/uielement.html#UiElement.bind, bind) method. 50 * $(P 51 * <<NotebookTabChanged>>, 52 * <<PrevWindow>>, 53 * <Alt-Key>, 54 * <Button-1>, 55 * <Control-Key-ISO_Left_Tab>, 56 * <Control-Key-Tab>, 57 * <Control-Shift-Key-Tab>, 58 * <Destroy>, 59 * <Key-F10>, 60 * <Key-Left>, 61 * <Key-Right>, 62 * <Key-Tab>, 63 * ) 64 * 65 * See_Also: 66 * $(LINK2 ./widget.html, tkd.widget.widget) 67 */ 68 class NoteBook : Widget 69 { 70 /** 71 * Construct the widget. 72 * 73 * Params: 74 * parent = The parent of this widget. 75 * 76 * Bugs: 77 * Because this widget contains and handles other widget's geometry, it 78 * must be created before the child panes and not chained with methods 79 * that add new tabs. If it is chained, tabs will not be handled 80 * correctly and might not show at all. This seems to be a limitation 81 * with Tcl/Tk. 82 * 83 * See_Also: 84 * $(LINK2 ../element/uielement.html, tkd.element.UiElement) $(BR) 85 */ 86 public this(UiElement parent = null) 87 { 88 super(parent); 89 this._elementId = "notebook"; 90 91 this._tk.eval("ttk::notebook %s", this.id); 92 } 93 94 /** 95 * Add a tab to the notebook. When adding a tab to the notebook the tab 96 * gains an id that is equal to the passed widget's id and can be used 97 * later to refer to the new tab. 98 * 99 * Params: 100 * text = The text of the tab. 101 * widget = The widget to add as the tab pane. 102 * 103 * Returns: 104 * This widget to aid method chaining. 105 */ 106 public auto addTab(this T)(string text, Widget widget) 107 { 108 this.insertTab("end", text, widget); 109 110 return cast(T) this; 111 } 112 113 /** 114 * Insert a tab into the notebook at a specified zero based index. When 115 * adding a tab to the notebook the tab gains an id that is equal to the 116 * passed widget's id and can be used later to refer to the new tab. If the 117 * id of the widget passed is already used as a tab id then that existing 118 * one will be moved to the new position. 119 * 120 * Params: 121 * tabIdentifier = The zero based index or string id of the tab. 122 * text = The text of the tab. 123 * widget = The widget to add as the tab pane. 124 * 125 * Returns: 126 * This widget to aid method chaining. 127 */ 128 public auto insertTab(this T, I)(I tabIdentifier, string text, Widget widget) if (is(I == int) || is(I == string)) 129 { 130 // String concatenation is used to build the script here instead of 131 // using format specifiers to enable supporting input which includes 132 // Tcl/Tk reserved characters and elements that could be construed as 133 // format specifiers. 134 string script = std.conv.text(this.id, ` insert `, tabIdentifier, ` `, widget.id, ` -text "`, this._tk.escape(text), `"`); 135 this._tk.eval(script); 136 137 return cast(T) this; 138 } 139 140 /** 141 * Select a tab in the notebook. 142 * 143 * Params: 144 * tabIdentifier = The zero based index or string id of the tab. 145 * 146 * Returns: 147 * This widget to aid method chaining. 148 */ 149 public auto selectTab(this T, I)(I tabIdentifier) if (is(I == int) || is(I == string)) 150 { 151 this._tk.eval("%s select %s", this.id, tabIdentifier); 152 153 return cast(T) this; 154 } 155 156 /** 157 * Remove a tab from the notbook. 158 * 159 * Params: 160 * tabIdentifier = The zero based index or string id of the tab. 161 * 162 * Returns: 163 * This widget to aid method chaining. 164 */ 165 public auto removeTab(this T, I)(I tabIdentifier) if (is(I == int) || is(I == string)) 166 { 167 this._tk.eval("%s forget %s", this.id, tabIdentifier); 168 169 return cast(T) this; 170 } 171 172 /** 173 * Hide a tab from the notbook. 174 * 175 * Params: 176 * tabIdentifier = The zero based index or string id of the tab. 177 * 178 * Returns: 179 * This widget to aid method chaining. 180 */ 181 public auto hideTab(this T, I)(I tabIdentifier) if (is(I == int) || is(I == string)) 182 { 183 this._tk.eval("%s hide %s", this.id, tabIdentifier); 184 185 return cast(T) this; 186 } 187 188 /** 189 * Set a tab's state. 190 * 191 * Params: 192 * tabIdentifier = The zero based index or string id of the tab. 193 * state = A widget state. 194 * 195 * Returns: 196 * This widget to aid method chaining. 197 * 198 * See_Also: 199 * $(LINK2 ./state.html, tkd.widget.state) for states. 200 */ 201 public auto setTabState(this T, I)(I tabIdentifier, string state) if (is(I == int) || is(I == string)) 202 { 203 this._tk.eval("%s tab %s -state %s", this.id, tabIdentifier, state); 204 205 return cast(T) this; 206 } 207 208 /** 209 * Set a tab pane's sticky state. Specifies how the slave widget is 210 * positioned within the pane area. Sticky state is a string containing 211 * zero or more of the characters n, s, e, or w. Each letter refers to a 212 * side (north, south, east, or west) that the slave window will "stick" 213 * to, as per the grid geometry manager. 214 * 215 * Params: 216 * tabIdentifier = The zero based index or string id of the tab. 217 * stickyState = A widget state. 218 * 219 * Returns: 220 * This widget to aid method chaining. 221 */ 222 public auto setPaneStickyState(this T, I)(I tabIdentifier, string stickyState) if (is(I == int) || is(I == string)) 223 { 224 this._tk.eval("%s tab %s -sticky %s", this.id, tabIdentifier, stickyState); 225 226 return cast(T) this; 227 } 228 229 /** 230 * Set a tab pane's padding. 231 * 232 * Params: 233 * tabIdentifier = The zero based index or string id of the tab. 234 * padding = The desired widget padding. 235 * 236 * Returns: 237 * This widget to aid method chaining. 238 */ 239 public auto setPanePadding(this T, I)(I tabIdentifier, int padding) if (is(I == int) || is(I == string)) 240 { 241 this._tk.eval("%s tab %s -padding %s", this.id, tabIdentifier, padding); 242 243 return cast(T) this; 244 } 245 246 /** 247 * Set a tab's text. 248 * 249 * Params: 250 * tabIdentifier = The zero based index or string id of the tab. 251 * text = The tab text. 252 * 253 * Returns: 254 * This widget to aid method chaining. 255 */ 256 public auto setTabText(this T, I)(I tabIdentifier, string text) if (is(I == int) || is(I == string)) 257 { 258 // String concatenation is used to build the script here instead of 259 // using format specifiers to enable supporting input which includes 260 // Tcl/Tk reserved characters and elements that could be construed as 261 // format specifiers. 262 string script = std.conv.text(this.id, ` tab `, tabIdentifier, ` -text "`, this._tk.escape(text), `"`); 263 this._tk.eval(script); 264 265 return cast(T) this; 266 } 267 268 /** 269 * Set a tab's image. 270 * 271 * Params: 272 * tabIdentifier = The zero based index or string id of the tab. 273 * image = The image to set on the widget. 274 * imagePosition = The position of the image relative to the text. 275 * 276 * Returns: 277 * This widget to aid method chaining. 278 * 279 * See_Also: 280 * $(LINK2 ../image/image.html, tkd.image.image) $(BR) 281 * $(LINK2 ../image/png.html, tkd.image.png) $(BR) 282 * $(LINK2 ../image/gif.html, tkd.image.gif) $(BR) 283 * $(LINK2 ../image/imageposition.html, tkd.image.imageposition) $(BR) 284 */ 285 public auto setTabImage(this T, I)(I tabIdentifier, Image image, string imagePosition = ImagePosition.image) if (is(I == int) || is(I == string)) 286 { 287 this._tk.eval("%s tab %s -image %s", this.id, tabIdentifier, image.id); 288 this.setTabImagePosition(tabIdentifier, imagePosition); 289 290 return cast(T) this; 291 } 292 293 /** 294 * Change the position of the tab image in relation to the text. 295 * 296 * Params: 297 * tabIdentifier = The zero based index or string id of the tab. 298 * imagePosition = The position of the image relative to the text. 299 * 300 * Returns: 301 * This widget to aid method chaining. 302 * 303 * See_Also: 304 * $(LINK2 ../image/imageposition.html, tkd.image.imageposition) 305 */ 306 public auto setTabImagePosition(this T, I)(I tabIdentifier, string imagePosition) if (is(I == int) || is(I == string)) 307 { 308 this._tk.eval("%s tab %s -compound %s", this.id, tabIdentifier, imagePosition); 309 310 return cast(T) this; 311 } 312 313 /** 314 * Underline a character in the tab text. The underlined character is used 315 * for mnemonic activation if keyboard traversal is enabled. 316 * 317 * Params: 318 * tabIdentifier = The zero based index or string id of the tab. 319 * index = The index of the character to underline. 320 * 321 * Returns: 322 * This widget to aid method chaining. 323 * 324 * See_Also: 325 * $(LINK2 ../image/imageposition.html, tkd.image.imageposition) $(BR) 326 * $(LINK2 ./notebook.html#NoteBook.enableKeyboardTraversal, enableKeyboardTraversal) $(BR) 327 */ 328 public auto underlineTabChar(this T, I)(I tabIdentifier, int index) if (is(I == int) || is(I == string)) 329 { 330 this._tk.eval("%s tab %s -underline %s", this.id, tabIdentifier, index); 331 332 return cast(T) this; 333 } 334 335 /** 336 * Call to enable keyboard traversal of the tabs. 337 * 338 * This will extend the bindings for the toplevel window containing the notebook as follows: 339 * $(UL 340 * $(LI Control-Tab selects the tab following the currently selected one.) 341 * $(LI Control-Shift-Tab selects the tab preceding the currently selected one.) 342 * $(LI Alt-c, where c is the mnemonic (underlined) character of any tab, will select that tab.) 343 * ) 344 * Multiple notebooks in a single window may be enabled for traversal, 345 * including nested notebooks. However, notebook traversal only works 346 * properly if all widget panes are direct children of the notebook. 347 * 348 * Returns: 349 * This widget to aid method chaining. 350 * 351 * See_Also: 352 * $(LINK2 ./notebook.html#NoteBook.underlineTabChar, underlineTabChar) $(BR) 353 */ 354 public auto enableKeyboardTraversal(this T)() 355 { 356 this._tk.eval("ttk::notebook::enableTraversal %s", this.id); 357 358 return cast(T) this; 359 } 360 361 /** 362 * Get an array of all the current tab id's. 363 * 364 * Returns: 365 * An array containing all the tab id's. 366 */ 367 public string[] getTabIds() 368 { 369 this._tk.eval("%s tabs", this.id); 370 return this._tk.getResult!(string).split(); 371 } 372 373 /** 374 * Get the tab index from its id. The id is the widget id that was added as 375 * the tab. 376 * 377 * Params: 378 * tabId = The tab id of the tab. 379 */ 380 public int getTabIndexById(string tabId) 381 { 382 this._tk.eval("%s index %s", this.id, tabId); 383 return this._tk.getResult!(int); 384 } 385 386 /** 387 * Get the number of tabs in the notebook. 388 * 389 * Returns: 390 * The number of tabs. 391 */ 392 public int getNumberOfTabs() 393 { 394 return this.getTabIndexById("end"); 395 } 396 397 /** 398 * Mixin common commands. 399 */ 400 mixin Height; 401 mixin Padding; 402 mixin Width; 403 }