1 /** 2 * Menu module. 3 * 4 * License: 5 * MIT. See LICENSE for full details. 6 */ 7 module tkd.widget.menu.menu; 8 9 /** 10 * Imports. 11 */ 12 import std.array; 13 import std.conv; 14 import std.string; 15 import std.typecons; 16 import tkd.element.element; 17 import tkd.element.uielement; 18 import tkd.image.image; 19 import tkd.image.imageposition; 20 import tkd.widget.menu.menubar; 21 22 /** 23 * The cascading menu that items are selected from. 24 * 25 * Example: 26 * --- 27 * auto menu = new Menu() 28 * .addEntry("Entry 1", delegate(CommandArgs args){ ... }) 29 * .addEntry("Entry 2", delegate(CommandArgs args){ ... }) 30 * .addSeparator() 31 * .addEntry("Entry 3", delegate(CommandArgs args){ ... }); 32 * --- 33 * 34 * Additional_Events: 35 * Additional events that can also be bound to using the $(LINK2 ../../element/uielement.html#UiElement.bind, bind) method. 36 * $(P 37 * <<MenuSelect>>, 38 * <<PrevWindow>>, 39 * <Alt-Key>, 40 * <Button>, 41 * <ButtonRelease>, 42 * <Enter> 43 * <Key-Down>, 44 * <Key-Escape>, 45 * <Key-F10>, 46 * <Key-Left>, 47 * <Key-Return>, 48 * <Key-Right>, 49 * <Key-Tab>, 50 * <Key-Up>, 51 * <Key-space>, 52 * <Key>, 53 * <Leave>, 54 * <Motion>, 55 * ) 56 * 57 * See_Also: 58 * $(LINK2 ../../element/uielement.html, tkd.element.uielement) 59 */ 60 class Menu : UiElement 61 { 62 /** 63 * Array containing variables used by check button entries in the menu. 64 */ 65 private string[] _checkButtonVariables; 66 67 /** 68 * The variable used by any radio button entries in the menu. 69 */ 70 private string _radioButtonVariable; 71 72 /** 73 * Construct the widget. 74 */ 75 public this() 76 { 77 super(); 78 this._elementId = "menu"; 79 this._radioButtonVariable = format("variable-%s", this.generateHash()); 80 81 this._tk.eval("menu %s -type normal -tearoff 0", this.id); 82 } 83 84 /** 85 * Construct the widget. 86 * 87 * Params: 88 * parent = The parent of this widget. 89 * label = The label of the menu. 90 * underlineChar = The index of the character to underline. 91 * 92 * See_Also: 93 * $(LINK2 ./menubar.html, tkd.widget.menu.menubar) 94 */ 95 public this(MenuBar parent, string label, ubyte underlineChar = ubyte.max) 96 { 97 super(parent); 98 this._elementId = "menu"; 99 this._radioButtonVariable = format("variable-%s", this.generateHash()); 100 101 this._tk.eval("menu %s -type normal -tearoff 0", this.id); 102 this._tk.eval("%s add cascade -menu %s -label {%s} -underline %s", parent.id, this.id, label, underlineChar); 103 } 104 105 /** 106 * Add a cascade menu to this menu. 107 * 108 * Params: 109 * label = The label of the menu. 110 * menu = The menu to add as a cascade menu. 111 * underlineChar = The index of the character to underline. 112 * 113 * Returns: 114 * This widget to aid method chaining. 115 */ 116 public auto addMenuEntry(this T)(string label, Menu menu, ubyte underlineChar = ubyte.max) 117 { 118 string originalId = menu.id; 119 menu._parent = this; 120 121 this._tk.eval("%s clone %s", originalId, menu.id); 122 this._tk.eval("%s add cascade -menu %s -label {%s} -underline %s", this.id, menu.id, label, underlineChar); 123 124 return cast(T) this; 125 } 126 127 /** 128 * Add an item to the menu. 129 * 130 * Params: 131 * label = The label of the item. 132 * callback = The callback to execute when this item is selected in the menu. 133 * shortCutText = The keyboard shortcut text. This is for decoration only, you must also bind this keypress to an event. 134 * 135 * Returns: 136 * This widget to aid method chaining. 137 * 138 * Callback_Arguments: 139 * These are the fields within the callback's $(LINK2 140 * ../../element/element.html#CommandArgs, CommandArgs) parameter which 141 * are populated by this method when the callback is executed. 142 * $(P 143 * $(PARAM_TABLE 144 * $(PARAM_ROW CommandArgs.element, The menu that executed the callback.) 145 * $(PARAM_ROW CommandArgs.uniqueData, The label of the menu entry which was selected.) 146 * $(PARAM_ROW CommandArgs.callback, The callback which was executed.) 147 * ) 148 * ) 149 * 150 * See_Also: 151 * $(LINK2 ../../element/element.html#CommandCallback, tkd.element.element.CommandCallback) 152 */ 153 public auto addEntry(this T)(string label, CommandCallback callback, string shortCutText = null) 154 { 155 string command = this.createCommand(callback, label); 156 157 this._tk.eval("%s add command -label {%s} -command %s -accelerator {%s}", this.id, label, command, shortCutText); 158 159 return cast(T) this; 160 } 161 162 /** 163 * Add an item to the menu with an image. 164 * 165 * Params: 166 * image = The image of the entry. 167 * label = The label of the item. 168 * callback = The callback to execute when this item is selected in the menu. 169 * shortCutText = The keyboard shortcut text. This is for decoration only, you must also bind this keypress to an event. 170 * imagePosition = The position of the image in relation to the text. 171 * 172 * Returns: 173 * This widget to aid method chaining. 174 * 175 * Callback_Arguments: 176 * These are the fields within the callback's $(LINK2 177 * ../../element/element.html#CommandArgs, CommandArgs) parameter which 178 * are populated by this method when the callback is executed. 179 * $(P 180 * $(PARAM_TABLE 181 * $(PARAM_ROW CommandArgs.element, The menu that executed the callback.) 182 * $(PARAM_ROW CommandArgs.uniqueData, The label of the menu entry which was selected.) 183 * $(PARAM_ROW CommandArgs.callback, The callback which was executed.) 184 * ) 185 * ) 186 * 187 * See_Also: 188 * $(LINK2 ../../element/element.html#CommandCallback, tkd.element.element.CommandCallback) 189 * $(LINK2 ../../image/image.html, tkd.image.image) $(BR) 190 * $(LINK2 ../../image/png.html, tkd.image.png) $(BR) 191 * $(LINK2 ../../image/gif.html, tkd.image.gif) $(BR) 192 * $(LINK2 ../../image/imageposition.html, tkd.image.imageposition) $(BR) 193 */ 194 public auto addEntry(this T)(Image image, string label, CommandCallback callback, string imagePosition = ImagePosition.left, string shortCutText = null) 195 { 196 string command = this.createCommand(callback, label); 197 198 this._tk.eval("%s add command -image %s -label {%s} -command %s -compound %s -accelerator {%s}", this.id, image.id, label, command, imagePosition, shortCutText); 199 200 return cast(T) this; 201 } 202 203 /** 204 * Add an item to the menu that when selected adds a checked icon. 205 * 206 * Params: 207 * label = The label of the item. 208 * callback = The callback to execute when this item is selected in the menu. 209 * shortCutText = The keyboard shortcut text. This is for decoration only, you must also bind this keypress to an event. 210 * 211 * Returns: 212 * This widget to aid method chaining. 213 * 214 * Callback_Arguments: 215 * These are the fields within the callback's $(LINK2 216 * ../../element/element.html#CommandArgs, CommandArgs) parameter which 217 * are populated by this method when the callback is executed. 218 * $(P 219 * $(PARAM_TABLE 220 * $(PARAM_ROW CommandArgs.element, The menu that executed the callback.) 221 * $(PARAM_ROW CommandArgs.uniqueData, The label of the menu entry which was selected.) 222 * $(PARAM_ROW CommandArgs.callback, The callback which was executed.) 223 * ) 224 * ) 225 * 226 * See_Also: 227 * $(LINK2 ../../element/element.html#CommandCallback, tkd.element.element.CommandCallback) 228 */ 229 public auto addCheckButtonEntry(this T)(string label, CommandCallback callback, string shortCutText = null) 230 { 231 this._checkButtonVariables ~= format("variable-%s", this.generateHash(label)); 232 string command = this.createCommand(callback, label); 233 234 this._tk.eval("%s add checkbutton -label {%s} -command %s -accelerator {%s} -variable %s", this.id, label, command, shortCutText, this._checkButtonVariables.back()); 235 236 return cast(T) this; 237 } 238 239 /** 240 * Add an item to the menu with an image that when selected adds a checked icon. 241 * 242 * Params: 243 * image = The image of the entry. 244 * label = The label of the item. 245 * callback = The callback to execute when this item is selected in the menu. 246 * shortCutText = The keyboard shortcut text. This is for decoration only, you must also bind this keypress to an event. 247 * imagePosition = The position of the image in relation to the text. 248 * 249 * Returns: 250 * This widget to aid method chaining. 251 * 252 * Callback_Arguments: 253 * These are the fields within the callback's $(LINK2 254 * ../../element/element.html#CommandArgs, CommandArgs) parameter which 255 * are populated by this method when the callback is executed. 256 * $(P 257 * $(PARAM_TABLE 258 * $(PARAM_ROW CommandArgs.element, The menu that executed the callback.) 259 * $(PARAM_ROW CommandArgs.uniqueData, The label of the menu entry which was selected.) 260 * $(PARAM_ROW CommandArgs.callback, The callback which was executed.) 261 * ) 262 * ) 263 * 264 * See_Also: 265 * $(LINK2 ../../element/element.html#CommandCallback, tkd.element.element.CommandCallback) 266 * $(LINK2 ../../image/image.html, tkd.image.image) $(BR) 267 * $(LINK2 ../../image/png.html, tkd.image.png) $(BR) 268 * $(LINK2 ../../image/gif.html, tkd.image.gif) $(BR) 269 * $(LINK2 ../../image/imageposition.html, tkd.image.imageposition) $(BR) 270 */ 271 public auto addCheckButtonEntry(this T)(Image image, string label, CommandCallback callback, string imagePosition = ImagePosition.left, string shortCutText = null) 272 { 273 this._checkButtonVariables ~= format("variable-%s", this.generateHash(label)); 274 string command = this.createCommand(callback, label); 275 276 this._tk.eval("%s add checkbutton -image %s -label {%s} -command %s -compound %s -accelerator {%s} -variable %s", this.id, image.id, label, command, imagePosition, shortCutText, this._checkButtonVariables.back()); 277 278 return cast(T) this; 279 } 280 281 /** 282 * Get if the check box entry at the passed index is checked or not. The 283 * index only applies to check box entries in the menu not any other type 284 * of entry. If there are no check box entries in the menu this method 285 * returns false. 286 * 287 * Params: 288 * index = The index of the check box entry. 289 * 290 * Returns: 291 * True if the check box entry is selected, false if not. 292 */ 293 public bool isCheckBoxEntrySelected(int index) 294 { 295 if (index < this._checkButtonVariables.length) 296 { 297 return this._tk.getVariable(this._checkButtonVariables[index]).to!(int) == 1; 298 } 299 return false; 300 } 301 302 /** 303 * Add an item to the menu that acts as a radio button. 304 * 305 * There can only be one group of radio button entries in one menu. If more 306 * than one group is needed use cascading menus to hold each group. 307 * 308 * Params: 309 * label = The label of the item. 310 * callback = The callback to execute when this item is selected in the menu. 311 * shortCutText = The keyboard shortcut text. This is for decoration only, you must also bind this keypress to an event. 312 * 313 * Returns: 314 * This widget to aid method chaining. 315 * 316 * Callback_Arguments: 317 * These are the fields within the callback's $(LINK2 318 * ../../element/element.html#CommandArgs, CommandArgs) parameter which 319 * are populated by this method when the callback is executed. 320 * $(P 321 * $(PARAM_TABLE 322 * $(PARAM_ROW CommandArgs.element, The menu that executed the callback.) 323 * $(PARAM_ROW CommandArgs.uniqueData, The label of the menu entry which was selected.) 324 * $(PARAM_ROW CommandArgs.callback, The callback which was executed.) 325 * ) 326 * ) 327 * 328 * See_Also: 329 * $(LINK2 ../../element/element.html#CommandCallback, tkd.element.element.CommandCallback) 330 */ 331 public auto addRadioButtonEntry(this T)(string label, CommandCallback callback, string shortCutText = null) 332 { 333 string command = this.createCommand(callback, label); 334 335 this._tk.eval("%s add radiobutton -label {%s} -command %s -accelerator {%s} -variable %s", this.id, label, command, shortCutText, this._radioButtonVariable); 336 337 return cast(T) this; 338 } 339 340 /** 341 * Add an item to the menu with an image that acts as a radio button. 342 * 343 * There can only be one group of radio button entries in one menu. If more 344 * than one group is needed use cascading menus to hold each group. 345 * 346 * Params: 347 * image = The image of the entry. 348 * label = The label of the item. 349 * callback = The callback to execute when this item is selected in the menu. 350 * shortCutText = The keyboard shortcut text. This is for decoration only, you must also bind this keypress to an event. 351 * imagePosition = The position of the image in relation to the text. 352 * 353 * Returns: 354 * This widget to aid method chaining. 355 * 356 * Callback_Arguments: 357 * These are the fields within the callback's $(LINK2 358 * ../../element/element.html#CommandArgs, CommandArgs) parameter which 359 * are populated by this method when the callback is executed. 360 * $(P 361 * $(PARAM_TABLE 362 * $(PARAM_ROW CommandArgs.element, The menu that executed the callback.) 363 * $(PARAM_ROW CommandArgs.uniqueData, The label of the menu entry which was selected.) 364 * $(PARAM_ROW CommandArgs.callback, The callback which was executed.) 365 * ) 366 * ) 367 * 368 * See_Also: 369 * $(LINK2 ../../element/element.html#CommandCallback, tkd.element.element.CommandCallback) 370 * $(LINK2 ../../image/image.html, tkd.image.image) $(BR) 371 * $(LINK2 ../../image/png.html, tkd.image.png) $(BR) 372 * $(LINK2 ../../image/gif.html, tkd.image.gif) $(BR) 373 * $(LINK2 ../../image/imageposition.html, tkd.image.imageposition) $(BR) 374 */ 375 public auto addRadioButtonEntry(this T)(Image image, string label, CommandCallback callback, string imagePosition = ImagePosition.left, string shortCutText = null) 376 { 377 string command = this.createCommand(callback, label); 378 379 this._tk.eval("%s add radiobutton -image %s -label {%s} -command %s -compound %s -accelerator {%s} -variable %s", this.id, image.id, label, command, imagePosition, shortCutText, this._radioButtonVariable); 380 381 return cast(T) this; 382 } 383 384 /** 385 * Get the value of the selected radio button entry. This value will be the 386 * same as the entry's label. This method will return an empty string if no 387 * radio button entry exists or none are selected. 388 * 389 * Returns: 390 * The value of the selected radio button entry. 391 */ 392 public string getSelectedRadioEntryValue() 393 { 394 return this._tk.getVariable(this._radioButtonVariable).to!(string); 395 } 396 397 /** 398 * Add a separator to the menu. 399 * 400 * Returns: 401 * This widget to aid method chaining. 402 */ 403 public auto addSeparator(this T)() 404 { 405 this._tk.eval("%s add separator", this.id); 406 407 return cast(T) this; 408 } 409 410 /** 411 * Disable a menu item. The item indexes start at zero for the top-most 412 * entry and increase as you go down. Index refers to all menu items 413 * including separators. 414 * 415 * Params: 416 * index = The index of the item to disable. 417 * 418 * Returns: 419 * This widget to aid method chaining. 420 */ 421 public auto disableEntry(this T)(int index) 422 { 423 this._tk.eval("%s entryconfigure %s -state disable", this.id, index); 424 425 return cast(T) this; 426 } 427 428 /** 429 * Enable a menu item. The item indexes start at zero for the top-most 430 * entry and increase as you go down. Index refers to all menu items 431 * including separators. 432 * 433 * Params: 434 * index = The index of the item to enable. 435 * 436 * Returns: 437 * This widget to aid method chaining. 438 */ 439 public auto enableEntry(this T)(int index) 440 { 441 this._tk.eval("%s entryconfigure %s -state normal", this.id, index); 442 443 return cast(T) this; 444 } 445 446 /** 447 * Invoke a menu item by its index. The item indexes start at zero for the 448 * top-most entry and increase as you go down. Index refers to all menu 449 * items including separators. 450 * 451 * Params: 452 * index = The index of the check box entry. 453 * 454 * Returns: 455 * This widget to aid method chaining. 456 */ 457 public auto invoke(this T)(int index) 458 { 459 this._tk.eval("%s invoke %s", this.id, index); 460 461 return cast(T) this; 462 } 463 464 /** 465 * Show the menu. 466 * 467 * Params: 468 * xPos = The horizontal position of the menu on the screen. 469 * yPos = The vertical position of the menu on the screen. 470 * 471 * Returns: 472 * This widget to aid method chaining. 473 */ 474 public auto popup(this T)(int xPos, int yPos) 475 { 476 this._tk.eval("tk_popup %s %s %s", this.id, xPos, yPos); 477 478 return cast(T) this; 479 } 480 }