1 /** 2 * Widget module. 3 * 4 * License: 5 * MIT. See LICENSE for full details. 6 */ 7 module tkd.widget.treeview; 8 9 /** 10 * Imports. 11 */ 12 import std.algorithm; 13 import std.array; 14 import std.conv; 15 import std.regex; 16 import std..string; 17 import tkd.element.color; 18 import tkd.element.element; 19 import tkd.element.uielement; 20 import tkd.image.image; 21 import tkd.widget.anchorposition; 22 import tkd.widget.common.height; 23 import tkd.widget.common.padding; 24 import tkd.widget.common.xscrollcommand; 25 import tkd.widget.common.yscrollcommand; 26 import tkd.widget.widget; 27 28 /** 29 * The treeview widget displays a hierarchical collection of items. Each item 30 * has a textual label, an optional image, and an optional list of data values. 31 * 32 * There are two varieties of columns. The first is the main tree view column 33 * that is present all the time. The second are data columns that can be added 34 * when needed. 35 * 36 * Each tree item has a list of tags, which can be used to associate event 37 * bindings and control their appearance. Treeview widgets support horizontal 38 * and vertical scrolling with the standard scroll commands. 39 * 40 * Example: 41 * --- 42 * auto treeView = new TreeView() 43 * .setHeading("Text") 44 * .addRow(new TreeViewRow(["row1"])) 45 * .addRow(new TreeViewRow(["row2"])) 46 * .pack(); 47 * --- 48 * 49 * Common_Commands: 50 * These are injected common commands that can also be used with this widget. 51 * $(P 52 * $(LINK2 ./common/height.html, Height) $(BR) 53 * $(LINK2 ./common/padding.html, Padding) $(BR) 54 * $(LINK2 ./common/xscrollcommand.html, XScrollCommand) $(BR) 55 * $(LINK2 ./common/yscrollcommand.html, YScrollCommand) $(BR) 56 * ) 57 * 58 * Additional_Events: 59 * Additional events that can also be bound to using the $(LINK2 ../element/uielement.html#UiElement.bind, bind) method. 60 * $(P 61 * <<PrevWindow>>, 62 * <<TreeviewClose>> 63 * <<TreeviewOpen>> 64 * <<TreeviewSelect>> 65 * <Alt-Key>, 66 * <B1-Leave>, 67 * <B1-Motion>, 68 * <Button-1>, 69 * <Button-4>, 70 * <Button-5>, 71 * <ButtonRelease-1>, 72 * <Control-Button-1>, 73 * <Double-Button-1>, 74 * <Key-Down>, 75 * <Key-F10>, 76 * <Key-Left>, 77 * <Key-Next>, 78 * <Key-Prior>, 79 * <Key-Return>, 80 * <Key-Right>, 81 * <Key-Tab>, 82 * <Key-Up>, 83 * <Key-space>, 84 * <Leave>, 85 * <Motion>, 86 * <Shift-Button-1>, 87 * <Shift-Button-4>, 88 * <Shift-Button-5>, 89 * ) 90 * 91 * See_Also: 92 * $(LINK2 ./widget.html, tkd.widget.widget) 93 */ 94 class TreeView : Widget, IXScrollable!(TreeView), IYScrollable!(TreeView) 95 { 96 /** 97 * An array containing all the columns. 98 */ 99 private TreeViewColumn[] _columns; 100 101 /** 102 * Construct the widget. 103 * 104 * Params: 105 * parent = The parent of this widget. 106 * 107 * See_Also: 108 * $(LINK2 ../element/uielement.html, tkd.element.uielement) $(BR) 109 */ 110 this(UiElement parent = null) 111 { 112 super(parent); 113 this._elementId = "treeview"; 114 115 this._tk.eval("ttk::treeview %s -selectmode browse", this.id); 116 117 // Add the treeview column to the column collection. 118 this._columns ~= new TreeViewColumn(); 119 this._columns[0].init(this); 120 } 121 122 /** 123 * Get the column identifiers of the passed data column indexes. 124 * 125 * Params: 126 * indexex = The indexes of the data columns. 127 * 128 * Returns: 129 * A string array containing the columns relating to the indexes. 130 */ 131 private string[] getDataColumnIdentifiers(int[] indexes) 132 { 133 string[] columns; 134 135 for (int x = 1; x < this._columns.length; x++) 136 { 137 if (indexes.canFind(x)) 138 { 139 columns ~= this._columns[x].id; 140 } 141 } 142 143 return columns; 144 } 145 146 /** 147 * Get all column identifiers. 148 * 149 * Returns: 150 * A string array containing all column identifiers. 151 */ 152 private string[] getDataColumnIdentifiers() 153 { 154 string[] columns; 155 156 for (int x = 1; x < this._columns.length; x++) 157 { 158 columns ~= this._columns[x].id; 159 } 160 161 return columns; 162 } 163 164 /** 165 * Get the tree view elements that are currently being shown. 166 * 167 * Returns: 168 * An array cotaining all shown elements. 169 */ 170 private string[] getShownElements() 171 { 172 this._tk.eval("%s cget -show", this.id); 173 return this._tk.getResult!(string).split(); 174 } 175 176 /** 177 * Build the columns found in the column array. This is needed because the 178 * data columns always seem to forget setting if configured piece-meal. 179 */ 180 private void buildColumns() 181 { 182 // String concatenation is used to build the script here instead of 183 // using format specifiers to enable supporting input which includes 184 // Tcl/Tk reserved characters and elements that could be construed as 185 // format specifiers. 186 string columns = format(`[list "%s"]`, this.getDataColumnIdentifiers().join(`" "`)); 187 string script = std.conv.text(this.id, ` configure -columns `, columns); 188 this._tk.eval(script); 189 190 for (int x = 1; x < this._columns.length; x++) 191 { 192 this.columns[x].init(this); 193 } 194 } 195 196 /** 197 * Convenience method to set the tree column heading text. 198 * 199 * Params: 200 * title = The title of the column. 201 * anchor = The anchor position of the text. 202 * 203 * Returns: 204 * This widget to aid method chaining. 205 * 206 * See_Also: 207 * $(LINK2 ./anchorposition.html, tkd.widget.anchorposition) $(BR) 208 */ 209 public auto setHeading(this T)(string title, string anchor = AnchorPosition.west) 210 { 211 this._columns[0].setHeading(title, anchor); 212 213 return cast(T) this; 214 } 215 216 /** 217 * Convenience method to set the tree column heading image. 218 * 219 * Params: 220 * image = The image to use. 221 * 222 * Returns: 223 * This widget to aid method chaining. 224 */ 225 public auto setHeadingImage(this T)(Image image) 226 { 227 this._columns[0].setHeadingImage(image); 228 229 return cast(T) this; 230 } 231 232 /** 233 * Convenience method to set the tree column command to be executed when 234 * clicking on the heading. 235 * 236 * Params: 237 * callback = The delegate callback to execute when invoking the command. 238 * 239 * Returns: 240 * This widget to aid method chaining. 241 * 242 * Callback_Arguments: 243 * These are the fields within the callback's $(LINK2 244 * ../element/element.html#CommandArgs, CommandArgs) parameter which 245 * are populated by this method when the callback is executed. 246 * $(P 247 * $(PARAM_TABLE 248 * $(PARAM_ROW CommandArgs.element, The tree column.) 249 * $(PARAM_ROW CommandArgs.uniqueData, The internal id of the tree view (An implementation detail that's not very useful).) 250 * $(PARAM_ROW CommandArgs.callback, The callback which was executed.) 251 * ) 252 * ) 253 * 254 * See_Also: 255 * $(LINK2 ../element/element.html#CommandCallback, tkd.element.element.CommandCallback) 256 */ 257 public auto setHeadingCommand(this T)(CommandCallback callback) 258 { 259 this._columns[0].setHeadingCommand(callback); 260 261 return cast(T) this; 262 } 263 264 /** 265 * Convenience method to remove the tree view column command. 266 * 267 * Returns: 268 * This widget to aid method chaining. 269 */ 270 public auto removeHeadingCommand(this T)() 271 { 272 this._columns[0].removeHeadingCommand(); 273 274 return cast(T) this; 275 } 276 277 /** 278 * Convenience method to set the minium width of the tree column. 279 * 280 * Params: 281 * minWidth = The minimum width in pixels. 282 * 283 * Returns: 284 * This widget to aid method chaining. 285 */ 286 public auto setMinWidth(this T)(int minWidth) 287 { 288 this._columns[0].setMinWidth(minWidth); 289 290 return cast(T) this; 291 } 292 293 /** 294 * Convenience method to enable or disable stretching for the tree column. 295 * This controls how this column react when other columns or the parent 296 * widget is resized. 297 * 298 * Params: 299 * stretch = true for enabling stretching, false to disable. 300 * 301 * Returns: 302 * This widget to aid method chaining. 303 */ 304 public auto setStretch(this T)(bool stretch) 305 { 306 this._columns[0].setStretch(stretch); 307 308 return cast(T) this; 309 } 310 311 /** 312 * Convenience method to set the width of the tree column. 313 * 314 * Params: 315 * width = The width in pixels. 316 * 317 * Returns: 318 * This widget to aid method chaining. 319 */ 320 public auto setWidth(this T)(int width) 321 { 322 this._columns[0].setWidth(width); 323 324 return cast(T) this; 325 } 326 327 /** 328 * Add a new column to the tree view. 329 * 330 * Params: 331 * column = The new column to add. 332 * 333 * Returns: 334 * This widget to aid method chaining. 335 */ 336 public auto addColumn(this T)(TreeViewColumn column) 337 { 338 this._columns ~= column; 339 this.buildColumns(); 340 341 return cast(T) this; 342 } 343 344 /** 345 * Add a row to the tree view. 346 * 347 * Params: 348 * row = The row to add. 349 * 350 * Returns: 351 * This widget to aid method chaining. 352 */ 353 public auto addRow(this T)(TreeViewRow row) 354 { 355 this.appendRows("", [row]); 356 357 return cast(T) this; 358 } 359 360 /** 361 * Add an array of rowr to the tree view. 362 * 363 * Params: 364 * rows = The rows to add. 365 * 366 * Returns: 367 * This widget to aid method chaining. 368 */ 369 public auto addRows(this T)(TreeViewRow[] rows) 370 { 371 this.appendRows("", rows); 372 373 return cast(T) this; 374 } 375 376 /** 377 * This method does the actualy work of adding rows to the tree view. 378 * All children are recursed and added too. 379 * 380 * Params: 381 * parentRow = The id of the parent row. Use '{}' for the top level. 382 * rows = The rows to add to the tree view. 383 */ 384 private void appendRows(string parentRow, TreeViewRow[] rows) 385 { 386 string values; 387 string tags; 388 389 foreach (row; rows) 390 { 391 row._values = this._tk.escape(row._values); 392 393 if (row.values.length > 1) 394 { 395 // Build the data column list. 396 values = " -values " ~ format(`[list "%s"]`, row.values[1 .. $].join(`" "`)); 397 } 398 399 row._tags = this._tk.escape(row._tags); 400 401 if (row.tags.length) 402 { 403 // Build the tags list. 404 tags = " -tags " ~ format(`[list "%s"]`, row.tags.join(`" "`)); 405 } 406 407 // String concatenation is used to build the script here instead of 408 // using format specifiers to enable supporting input which includes 409 // Tcl/Tk reserved characters and elements that could be construed as 410 // format specifiers. 411 string script = std.conv.text(this.id, ` insert {`, parentRow, `} end -text "`, row.values[0], `"`, values, ` -open `, row.isOpen, tags); 412 this._tk.eval(script); 413 414 row._rowId = this._tk.getResult!(string); 415 416 if (row.children.length) 417 { 418 this.appendRows(row.id, row.children); 419 } 420 } 421 } 422 423 /** 424 * Set the value of a data column, given a row id. Row id's are populated 425 * within a tree view row object once that row has been inserted into the 426 * tree view. 427 * 428 * Params: 429 * rowId = The row id. 430 * columnIndex = The 0-based data column index. 431 * value = The new value. 432 * 433 * Returns: 434 * This widget to aid method chaining. 435 */ 436 public auto updateDataColumn(this T)(string rowId, uint columnIndex, string value) 437 { 438 // String concatenation is used to build the script here instead of 439 // using format specifiers to enable supporting input which includes 440 // Tcl/Tk reserved characters and elements that could be construed as 441 // format specifiers. 442 string script = std.conv.text(this.id, ` set `, rowId, ` `, columnIndex, ` "`, this._tk.escape(value), `"`); 443 this._tk.eval(script); 444 445 return cast(T) this; 446 } 447 448 /** 449 * Set image and colors for a specific tag. 450 * Use colors from the preset color $(LINK2 ../element/color.html, list) or a web style hex color. 451 * 452 * Params: 453 * name = The name of the tag. 454 * image = The image to associate to the tag. 455 * foreground = The forground color. 456 * background = The background color. 457 * 458 * Returns: 459 * This widget to aid method chaining. 460 * 461 * See_Also: 462 * $(LINK2 ../element/color.html, tkd.widget.color) $(BR) 463 */ 464 public auto setTag(this T)(string name, Image image, string foreground = Color.default_, string background = Color.default_) 465 { 466 this._tk.eval("%s tag configure %s -image %s -foreground {%s} -background {%s}", this.id, name, image.id, foreground, background); 467 468 return cast(T) this; 469 } 470 471 /** 472 * Get the columns. 473 * 474 * Returns: 475 * An array containing all the data columns. 476 */ 477 public @property TreeViewColumn[] columns() 478 { 479 return this._columns; 480 } 481 482 /** 483 * Show all data columns in the event some or all are hidden. 484 * 485 * Returns: 486 * This widget to aid method chaining. 487 */ 488 public auto displayAllDataColumns(this T)() 489 { 490 this._tk.eval("%s configure -displaycolumns #all", this.id); 491 492 return cast(T) this; 493 } 494 495 /** 496 * Show the data columns that relate to the indexes passed. 497 * 498 * Params: 499 * indexes = The indexes of the data columns to show. 500 * 501 * Returns: 502 * This widget to aid method chaining. 503 */ 504 public auto displayDataColumns(this T)(int[] indexes) 505 { 506 string columns = format(`[list "%s"]`, this.getDataColumnIdentifiers(indexes).join(`" "`)); 507 508 // String concatenation is used to build the script here instead of 509 // using format specifiers to enable supporting input which includes 510 // Tcl/Tk reserved characters and elements that could be construed as 511 // format specifiers. 512 string script = std.conv.text(this.id, ` configure -displaycolumns `, columns); 513 this._tk.eval(script); 514 515 return cast(T) this; 516 } 517 518 /** 519 * Set the selection mode. 520 * 521 * Params: 522 * mode = The mode of the selection. 523 * 524 * Returns: 525 * This widget to aid method chaining. 526 * 527 * See_Also: 528 * $(LINK2 ./treeview.html#TreeViewSelectionMode, tkd.widget.treeview.TreeViewSelectionMode) $(BR) 529 */ 530 public auto setSelectionMode(this T)(string mode) 531 { 532 this._tk.eval("%s configure -selectmode %s", this.id, mode); 533 534 return cast(T) this; 535 } 536 537 /** 538 * Hide the headings from all columns. 539 * 540 * Returns: 541 * This widget to aid method chaining. 542 */ 543 public auto hideHeadings(this T)() 544 { 545 this._tk.eval("%s configure -show { %s }", this.id, this.getShownElements() 546 .remove!(x => x == "headings") 547 .join(" ") 548 ); 549 550 return cast(T) this; 551 } 552 553 /** 554 * Show the headings for all columns. 555 * 556 * Returns: 557 * This widget to aid method chaining. 558 */ 559 public auto showHeadings(this T)() 560 { 561 string[] elements = this.getShownElements(); 562 elements ~= "headings"; 563 564 this._tk.eval("%s configure -show { %s }", this.id, elements.join(" ")); 565 566 return cast(T) this; 567 } 568 569 /** 570 * Hide the tree view column. 571 * 572 * Returns: 573 * This widget to aid method chaining. 574 */ 575 public auto hideTreeColumn(this T)() 576 { 577 this._tk.eval("%s configure -show { %s }", this.id, this.getShownElements() 578 .remove!(x => x == "tree") 579 .join(" ") 580 ); 581 582 return cast(T) this; 583 } 584 585 /** 586 * Show the tree view column. 587 * 588 * Returns: 589 * This widget to aid method chaining. 590 */ 591 public auto showTreeColumn(this T)() 592 { 593 string[] elements = this.getShownElements(); 594 elements ~= "tree"; 595 596 this._tk.eval("%s configure -show { %s }", this.id, elements.join(" ")); 597 598 return cast(T) this; 599 } 600 601 /** 602 * Construct a row object from a row id. 603 * 604 * Params: 605 * rowId = The id of the row to construct. 606 * 607 * Returns: 608 * A tree view row. 609 */ 610 private TreeViewRow getRowFromId(string rowId) 611 { 612 auto row = new TreeViewRow(); 613 614 this._tk.eval("%s item %s -text", this.id, rowId); 615 row._values ~= this._tk.getResult!(string); 616 617 this._tk.eval("%s item %s -values", this.id, rowId); 618 auto results = matchAll(this._tk.getResult!(string), "\"(.*?)\""); 619 foreach (result; results) 620 { 621 row._values ~= result.captures[1]; 622 } 623 624 this._tk.eval("%s item %s -open", this.id, rowId); 625 row._isOpen = this._tk.getResult!(bool); 626 627 this._tk.eval("%s item %s -tags", this.id, rowId); 628 results = matchAll(this._tk.getResult!(string), "\"(.*?)\""); 629 foreach (result; results) 630 { 631 row._tags ~= result.captures[1]; 632 } 633 634 return row; 635 } 636 637 /** 638 * Populate row objects and return them. 639 * 640 * Params: 641 * rows = An array to append the rows to. 642 * includeChildren = Specifies whether or not to include the children. 643 * 644 * Returns: 645 * AN array of tree view rows. 646 */ 647 private TreeViewRow[] populateRows(string[] rowIds, bool includeChildren) 648 { 649 TreeViewRow[] rows; 650 TreeViewRow currentRow; 651 652 foreach (rowId; rowIds) 653 { 654 currentRow = this.getRowFromId(rowId); 655 656 if (includeChildren) 657 { 658 this._tk.eval("%s children %s", this.id, rowId); 659 currentRow.children = this.populateRows(this._tk.getResult!(string).split(), includeChildren); 660 } 661 662 rows ~= currentRow; 663 } 664 665 return rows; 666 } 667 668 /** 669 * Get the row(s) selected in the tree view. 670 * 671 * Params: 672 * includeChildren = Specifies whether or not to include the children. 673 * 674 * Returns: 675 * An array containing the selected rows. 676 */ 677 public TreeViewRow[] getSelectedRows(bool includeChildren = false) 678 { 679 this._tk.eval("%s selection", this.id); 680 string[] rowIds = this._tk.getResult!(string).split(); 681 682 return this.populateRows(rowIds, includeChildren); 683 } 684 685 /** 686 * Delete all rows in the widget. 687 * 688 * Returns: 689 * This widget to aid method chaining. 690 */ 691 public auto deleteRows() 692 { 693 this._tk.eval("%s children {}", this.id); 694 this._tk.eval("%s delete {%s}", this.id, this._tk.getResult!(string)); 695 } 696 697 /** 698 * Mixin common commands. 699 */ 700 mixin Height; 701 mixin Padding; 702 mixin XScrollCommand!(TreeView); 703 mixin YScrollCommand!(TreeView); 704 } 705 706 /** 707 * A class representing a column in the tree view. 708 */ 709 class TreeViewColumn : Element 710 { 711 /** 712 * The parent of this column. 713 */ 714 private TreeView _parent; 715 716 /** 717 * The title of the heading. 718 */ 719 private string _title; 720 721 /** 722 * The anchor position of the heading title. 723 */ 724 private string _anchor = AnchorPosition.west; 725 726 /** 727 * The image of the heading. 728 */ 729 private Image _image; 730 731 /** 732 * The minimum width of the column. 733 */ 734 private int _minWidth = 20; 735 736 /** 737 * Whether to alter the size of the column when the widget is resized. 738 */ 739 private bool _stretch = true; 740 741 /** 742 * Width of the column. 743 */ 744 private int _width = 200; 745 746 /** 747 * The command associated with the heading. 748 */ 749 private CommandCallback _commandCallback; 750 751 /** 752 * Construct a new column to add the treeview column to the column 753 * collection. 754 * 755 * '#0' is the built-in Tcl/Tk display id of the tree view column. 756 * This allows access to this column even if it wasn't created by us. 757 */ 758 private this() 759 { 760 super(); 761 this.overrideGeneratedId("#0"); 762 } 763 764 /** 765 * Construct a new column. 766 * 767 * Params: 768 * title = The optional title of the heading. 769 * anchor = The anchor position of the heading title. 770 */ 771 public this(string title = null, string anchor = AnchorPosition.west) 772 { 773 this._elementId = "column"; 774 this.setHeading(title, anchor); 775 } 776 777 /** 778 * Initialise the column and attach to a parent. 779 * 780 * Params: 781 * parent = The parent tree view. 782 */ 783 private void init(TreeView parent) 784 { 785 this._parent = parent; 786 787 this.setHeading(this._title, this._anchor); 788 this.setHeadingImage(this._image); 789 this.setHeadingCommand(this._commandCallback); 790 this.setMinWidth(this._minWidth); 791 this.setStretch(this._stretch); 792 this.setWidth(this._width); 793 } 794 795 /** 796 * Set the heading title. 797 * 798 * Params: 799 * title = The title of the column. 800 * anchor = The anchor position of the text. 801 * 802 * Returns: 803 * This column to aid method chaining. 804 * 805 * See_Also: 806 * $(LINK2 ./anchorposition.html, tkd.widget.anchorposition) $(BR) 807 */ 808 public auto setHeading(this T)(string title, string anchor = AnchorPosition.west) 809 { 810 this._title = title; 811 this._anchor = anchor; 812 813 if (this._parent) 814 { 815 // String concatenation is used to build the script here instead of 816 // using format specifiers to enable supporting input which includes 817 // Tcl/Tk reserved characters and elements that could be construed as 818 // format specifiers. 819 string script = std.conv.text(this._parent.id, ` heading `, this.id, ` -text "`, this._tk.escape(this._title), `" -anchor `, this._anchor); 820 this._tk.eval(script); 821 822 } 823 824 return cast(T) this; 825 } 826 827 /** 828 * Set the heading image. 829 * 830 * Params: 831 * image = The image to use. 832 * 833 * Returns: 834 * This column to aid method chaining. 835 */ 836 public auto setHeadingImage(this T)(Image image) 837 { 838 this._image = image; 839 840 if (this._parent && this._image) 841 { 842 // String concatenation is used to build the script here instead of 843 // using format specifiers to enable supporting input which includes 844 // Tcl/Tk reserved characters and elements that could be construed as 845 // format specifiers. 846 string script = std.conv.text(this._parent.id, ` heading `, this.id, ` -text "`, this._tk.escape(this._title), `" -anchor `, this._anchor, ` -image `, image.id); 847 this._tk.eval(script); 848 } 849 850 return cast(T) this; 851 } 852 853 /** 854 * Set the column command to be executed when clicking on the heading. 855 * 856 * Params: 857 * callback = The delegate callback to execute when invoking the command. 858 * 859 * Returns: 860 * This widget to aid method chaining. 861 * 862 * Callback_Arguments: 863 * These are the fields within the callback's $(LINK2 864 * ../element/element.html#CommandArgs, CommandArgs) parameter which 865 * are populated by this method when the callback is executed. 866 * $(P 867 * $(PARAM_TABLE 868 * $(PARAM_ROW CommandArgs.element, The column that executed the callback.) 869 * $(PARAM_ROW CommandArgs.uniqueData, The internal id of the tree view (An implementation detail that's not very useful).) 870 * $(PARAM_ROW CommandArgs.callback, The callback which was executed.) 871 * ) 872 * ) 873 * 874 * See_Also: 875 * $(LINK2 ../element/element.html#CommandCallback, tkd.element.element.CommandCallback) 876 */ 877 public auto setHeadingCommand(this T)(CommandCallback callback) 878 { 879 this._commandCallback = callback; 880 881 if (this._parent && this._commandCallback) 882 { 883 string command = this.createCommand(callback, this._parent.id); 884 this._tk.eval("%s heading %s -command %s", this._parent.id, this.id, command); 885 } 886 887 return cast(T) this; 888 } 889 890 /** 891 * Remove the column command. 892 * 893 * Returns: 894 * This widget to aid method chaining. 895 */ 896 public auto removeHeadingCommand(this T)() 897 { 898 if (this._parent && this._commandCallback) 899 { 900 this._tk.deleteCommand(this.getCommandName(this._parent.id)); 901 this._tk.eval("%s heading %s -command {}", this._parent.id, this.id); 902 } 903 904 return cast(T) this; 905 } 906 907 /** 908 * Set the minium width of the column. 909 * 910 * Params: 911 * minWidth = The minimum width in pixels. 912 * 913 * Returns: 914 * This widget to aid method chaining. 915 */ 916 public auto setMinWidth(this T)(int minWidth) 917 { 918 this._minWidth = minWidth; 919 920 if (this._parent) 921 { 922 this._tk.eval("%s column %s -minwidth %s", this._parent.id, this.id, this._minWidth); 923 } 924 925 return cast(T) this; 926 } 927 928 /** 929 * Enable or disable stretching for the column. This controls how this 930 * column react when other columns or the parent widget is resized. 931 * 932 * Params: 933 * stretch = true for enabling stretching, false to disable. 934 * 935 * Returns: 936 * This widget to aid method chaining. 937 */ 938 public auto setStretch(this T)(bool stretch) 939 { 940 this._stretch = stretch; 941 942 if (this._parent) 943 { 944 this._tk.eval("%s column %s -stretch %s", this._parent.id, this.id, this._stretch); 945 } 946 947 return cast(T) this; 948 } 949 950 /** 951 * Set the width of the column. 952 * 953 * Params: 954 * width = The width in pixels. 955 * 956 * Returns: 957 * This widget to aid method chaining. 958 */ 959 public auto setWidth(this T)(int width) 960 { 961 this._width = width; 962 963 if (this._parent) 964 { 965 this._tk.eval("%s column %s -width %s", this._parent.id, this.id, this._width); 966 } 967 968 return cast(T) this; 969 } 970 } 971 972 /** 973 * A class representing a row in the tree view. 974 */ 975 class TreeViewRow 976 { 977 /** 978 * The row id. This is populated by the treeview once the row has been 979 * inserted. 980 */ 981 private string _rowId; 982 983 /** 984 * An array containing the column values. 985 */ 986 private string[] _values; 987 988 /** 989 * Boolean representing if the row was set to be open when created. 990 */ 991 private bool _isOpen; 992 993 /** 994 * An array containing the tags. 995 */ 996 private string[] _tags; 997 998 /** 999 * An array containing the child rows. 1000 */ 1001 public TreeViewRow[] children; 1002 1003 /** 1004 * Constructor. 1005 */ 1006 private this() 1007 { 1008 } 1009 1010 /** 1011 * Constructor. 1012 * 1013 * Params: 1014 * values = The values of the columns. 1015 * isOpen = Whether or not to display the row open. 1016 * tags = The tags to associate to this row. 1017 */ 1018 public this(string[] values, bool isOpen = false, string[] tags = []) 1019 { 1020 assert(values.length, "There must be at least 1 value in the row."); 1021 1022 this._values = values; 1023 this._isOpen = isOpen; 1024 this._tags = tags; 1025 } 1026 1027 /** 1028 * Get the row id. This is populated by the treeview once the row has been 1029 * inserted. 1030 * 1031 * Returns: 1032 * A string containing the row id. 1033 */ 1034 public @property string id() 1035 { 1036 return this._rowId; 1037 } 1038 1039 /** 1040 * Get the data column values. 1041 * 1042 * Returns: 1043 * An array containing the data values. 1044 */ 1045 public @property string[] values() 1046 { 1047 return this._values; 1048 } 1049 1050 /** 1051 * Get if the row was open. 1052 * 1053 * Returns: 1054 * true if the row was set to be open, false if not. 1055 */ 1056 public @property bool isOpen() 1057 { 1058 return this._isOpen; 1059 } 1060 1061 /** 1062 * Get the tags. 1063 * 1064 * Returns: 1065 * An array of tags assocaited to this row. 1066 */ 1067 public @property string[] tags() 1068 { 1069 return this._tags; 1070 } 1071 1072 /** 1073 * String representation. 1074 */ 1075 debug override public string toString() 1076 { 1077 return format("Row Id: %s, Values: %s, isOpen: %s, Tags: %s, Children: %s", this.id, this.values, this.isOpen, this.tags, this.children); 1078 } 1079 } 1080 1081 /** 1082 * Tree view selection modes. 1083 */ 1084 enum TreeViewSelectionMode : string 1085 { 1086 browse = "browse", /// The default mode, allows one selection only. 1087 extended = "extended", /// Allows multiple selections to be made. 1088 none = "none", /// Disabled all selection. 1089 }