1 module main; 2 3 /** 4 * Imports. 5 */ 6 import std.algorithm; 7 import std.array; 8 import std.datetime; 9 import std.file; 10 import tkd.tkdapplication; 11 12 /** 13 * Sample application. 14 */ 15 class Application : TkdApplication 16 { 17 /** 18 * Widgets we need access to on the class level. 19 */ 20 private Entry _entry; 21 private ProgressBar _progressBar; 22 private Canvas _canvas; 23 private Entry _fontEntry; 24 private Entry _colorEntry; 25 private Entry _directoryEntry; 26 private Entry _openFileEntry; 27 private Entry _saveFileEntry; 28 private Entry _messageEntry; 29 30 /** 31 * Open the font dialog. 32 * 33 * Params: 34 * args = The callback args. 35 */ 36 private void openFontDialog(CommandArgs args) 37 { 38 auto dialog = new FontDialog("Choose a font") 39 .setCommand(delegate(CommandArgs args){ 40 this._fontEntry.setFont(args.dialog.font); 41 this._fontEntry.setValue(args.dialog.font); 42 }) 43 .show(); 44 } 45 46 /** 47 * Open the color dialog. 48 * 49 * Params: 50 * args = The callback args. 51 */ 52 private void openColorDialog(CommandArgs args) 53 { 54 auto dialog = new ColorDialog("Choose a color") 55 .setInitialColor(Color.beige) 56 .show(); 57 this._colorEntry.setForegroundColor(dialog.getResult()); 58 this._colorEntry.setValue(dialog.getResult()); 59 } 60 61 /** 62 * Open the directory dialog. 63 * 64 * Params: 65 * args = The callback args. 66 */ 67 private void openDirectoryDialog(CommandArgs args) 68 { 69 auto dialog = new DirectoryDialog("Choose a directory") 70 .setDirectoryMustExist(true) 71 .show(); 72 this._directoryEntry.setValue(dialog.getResult()); 73 } 74 75 /** 76 * Open the open file dialog. 77 * 78 * Params: 79 * args = The callback args. 80 */ 81 private void openOpenFileDialog(CommandArgs args) 82 { 83 auto dialog = new OpenFileDialog() 84 .setMultiSelection(true) 85 .setDefaultExtension(".dmo") 86 .addFileType("{{All files} {*}}") 87 .addFileType("{{D language files} {.d .di}}") 88 .addFileType("{{HTML files} {.htm .html}}") 89 .addFileType("{{Text files} {.txt}}") 90 .setInitialDirectory("~") 91 .setInitialFile("file-to-open.dmo") 92 .show(); 93 this._openFileEntry.setValue(dialog.getResults().join(" ")); 94 } 95 96 /** 97 * Open the save file dialog. 98 * 99 * Params: 100 * args = The callback args. 101 */ 102 private void openSaveFileDialog(CommandArgs args) 103 { 104 auto dialog = new SaveFileDialog() 105 .setConfirmOverwrite(true) 106 .setDefaultExtension(".dmo") 107 .addFileType("{{All files} {*}}") 108 .addFileType("{{D language files} {.d .di}}") 109 .addFileType("{{HTML files} {.htm .html}}") 110 .addFileType("{{Text files} {.txt}}") 111 .setInitialDirectory("~") 112 .setInitialFile("file-to-save.dmo") 113 .show(); 114 this._saveFileEntry.setValue(dialog.getResult()); 115 } 116 117 /** 118 * Open the message dialog. 119 * 120 * Params: 121 * args = The callback args. 122 */ 123 private void openMessageDialog(CommandArgs args) 124 { 125 auto dialog = new MessageDialog(this.mainWindow) 126 .setMessage("Lorem ipsum dolor sit amet.") 127 .setDetailMessage("Nunc at aliquam arcu. Sed eget tellus ligula.\nSed egestas est et tempus cursus.") 128 .setType(MessageDialogType.okcancel) 129 .show(); 130 this._messageEntry.setValue(dialog.getResult()); 131 } 132 133 /** 134 * Mark a point in the canvas to drag from. 135 * 136 * Params: 137 * args = The callback args. 138 */ 139 private void mark(CommandArgs args) 140 { 141 this._canvas.setScanMark(args.event.x, args.event.y); 142 } 143 144 /** 145 * Drag the canvas to reposition the contents. 146 * 147 * Params: 148 * args = The callback args. 149 */ 150 private void drag(CommandArgs args) 151 { 152 this._canvas.scanDragTo(args.event.x, args.event.y); 153 } 154 155 /** 156 * Show the about box. 157 */ 158 private void showAbout(CommandArgs args) 159 { 160 auto dialog = new MessageDialog(this.mainWindow, "About") 161 .setMessage("Tkd Showcase") 162 .setDetailMessage("A showcase Tkd application demonstrating menus, widgets and dialogs.\n\nThe possiblities are endless.") 163 .show(); 164 } 165 166 /** 167 * Create the menu. 168 */ 169 private void createMenu() 170 { 171 auto menuBar = new MenuBar(this.mainWindow); 172 173 auto checkButtonSubMenu = new Menu() 174 .addCheckButtonEntry("Option 1", delegate(CommandArgs args){}) 175 .addCheckButtonEntry("Option 2", delegate(CommandArgs args){}) 176 .addCheckButtonEntry("Option 3", delegate(CommandArgs args){}); 177 178 auto radioButtonSubMenu = new Menu() 179 .addRadioButtonEntry("Option 1", delegate(CommandArgs args){}) 180 .addRadioButtonEntry("Option 2", delegate(CommandArgs args){}) 181 .addRadioButtonEntry("Option 3", delegate(CommandArgs args){}); 182 183 auto fileMenu = new Menu(menuBar, "File", 0) 184 .addMenuEntry("Checkbutton submenu", checkButtonSubMenu) 185 .addMenuEntry("Radiobutton submenu", radioButtonSubMenu) 186 .addSeparator() 187 .addEntry(new EmbeddedPng!("cancel.png"), "Quit", &this.exitApplication, ImagePosition.left, "Ctrl-Q"); 188 189 auto helpMenu = new Menu(menuBar, "Help", 0) 190 .addEntry(new EmbeddedPng!("help.png"), "About...", &this.showAbout, ImagePosition.left, "F1"); 191 } 192 193 /** 194 * Create the widget pane for the note book. 195 * 196 * Returns: 197 * The widget pane. 198 */ 199 private Frame createWidgetPane() 200 { 201 auto widgetPane = new Frame(); 202 203 auto entryLabelFrame = new LabelFrame(widgetPane, "Text Entry") 204 .pack(10, 0, GeometrySide.top, GeometryFill.both, AnchorPosition.center, true); 205 auto entry1 = new Text(entryLabelFrame) 206 .setWidth(0) 207 .setHeight(3) 208 .appendText("import std.stdio;\n\nvoid main(string[] args)\n{\n\twriteln(\"Hello World!\");\n}") 209 .pack(5, 0, GeometrySide.bottom, GeometryFill.both, AnchorPosition.northWest, true); 210 this._entry = new Entry(entryLabelFrame) 211 .pack(5, 0, GeometrySide.left, GeometryFill.x, AnchorPosition.northWest, true); 212 auto entry3 = new SpinBox(entryLabelFrame) 213 .setData(["$foo", "[bar]", "\"baz\"", "{qux}"]) 214 .setWrap(true) 215 .setWidth(5) 216 .pack(5, 0, GeometrySide.left); 217 auto entry4 = new ComboBox(entryLabelFrame) 218 .setData(["Option 1", "Option 2", "Option 3"]) 219 .setValue("Option 1") 220 .pack(5, 0, GeometrySide.left, GeometryFill.x, AnchorPosition.northWest, true); 221 222 auto rangeLabelFrame = new LabelFrame(widgetPane, "Progress & Scale") 223 .pack(10, 0, GeometrySide.bottom, GeometryFill.both, AnchorPosition.center); 224 this._progressBar = new ProgressBar(rangeLabelFrame) 225 .setMaxValue(10) 226 .setValue(4) 227 .pack(5, 0, GeometrySide.top, GeometryFill.x, AnchorPosition.center, true); 228 auto scale = new Scale(rangeLabelFrame) 229 .setFromValue(10) 230 .setToValue(0) 231 .setCommand(delegate(CommandArgs args){ 232 auto scale = cast(Scale)args.element; 233 this._progressBar.setValue(scale.getValue()); 234 }) 235 .setValue(4) 236 .pack(5, 0, GeometrySide.top, GeometryFill.x, AnchorPosition.center, true); 237 238 auto buttonLabelFrame = new LabelFrame(widgetPane, "Buttons") 239 .pack(10, 0, GeometrySide.left, GeometryFill.both, AnchorPosition.center, true); 240 auto button1 = new Button(buttonLabelFrame, "Text button") 241 .pack(5); 242 auto button2 = new Button(buttonLabelFrame, new EmbeddedPng!("thumbnail.png"), "Image button", ImagePosition.left) 243 .pack(5); 244 auto buttonMenu = new Menu() 245 .addEntry("Option 1", delegate(CommandArgs args){}) 246 .addEntry("Option 2", delegate(CommandArgs args){}) 247 .addEntry("Option 3", delegate(CommandArgs args){}); 248 auto button3 = new MenuButton(buttonLabelFrame, "Menu button", buttonMenu) 249 .pack(5); 250 251 auto checkBoxLabelFrame = new LabelFrame(widgetPane, "Check buttons") 252 .pack(10, 0, GeometrySide.left, GeometryFill.both, AnchorPosition.center, true); 253 auto checkButton1 = new CheckButton(checkBoxLabelFrame, "Option 1") 254 .check() 255 .pack(5); 256 auto checkButton2 = new CheckButton(checkBoxLabelFrame, "Option 2") 257 .pack(5); 258 auto checkButton3 = new CheckButton(checkBoxLabelFrame, "Option 3") 259 .pack(5); 260 261 auto radioLabelFrame = new LabelFrame(widgetPane, "Radio buttons") 262 .pack(10, 0, GeometrySide.left, GeometryFill.both, AnchorPosition.center, true); 263 auto radioButton1 = new RadioButton(radioLabelFrame, "Option 1") 264 .setSelectedValue("1") 265 .select() 266 .pack(5); 267 auto radioButton2 = new RadioButton(radioLabelFrame, "Option 2") 268 .setSelectedValue("2") 269 .pack(5); 270 auto radioButton3 = new RadioButton(radioLabelFrame, "Option 3") 271 .setSelectedValue("3") 272 .pack(5); 273 274 return widgetPane; 275 } 276 277 /** 278 * Create the paned pane for the note book. 279 * 280 * Returns: 281 * The paned pane. 282 */ 283 private Frame createPanedPane() 284 { 285 auto panedPane = new Frame(); 286 287 auto panedWindow = new PanedWindow(panedPane); 288 289 auto rows = new TreeViewRow(["Computer"], true, ["computer"]); 290 rows.children ~= new TreeViewRow(["Documents"], true, ["folder"]); 291 rows.children[0].children ~= new TreeViewRow(["Important notes.txt"], true, ["file"]); 292 rows.children[0].children ~= new TreeViewRow(["The D Programming Language.pdf"], true, ["pdf"]); 293 rows.children ~= new TreeViewRow(["Pictures"], true, ["folder"]); 294 rows.children[1].children ~= new TreeViewRow(["Gary and Tessa.jpg"], true, ["image"]); 295 rows.children ~= new TreeViewRow(["Videos"], true, ["folder"]); 296 rows.children[2].children ~= new TreeViewRow(["Carlito's Way (1993).mpg"], true, ["video"]); 297 rows.children[2].children ~= new TreeViewRow(["Aliens (1986).mpg"], true, ["video"]); 298 299 auto tree1 = new TreeView(panedWindow) 300 .setHeading("Directory listing") 301 .setTag("computer", new EmbeddedPng!("computer.png")) 302 .setTag("folder", new EmbeddedPng!("folder.png")) 303 .setTag("file", new EmbeddedPng!("page.png")) 304 .setTag("pdf", new EmbeddedPng!("page_white_acrobat.png")) 305 .setTag("video", new EmbeddedPng!("film.png")) 306 .setTag("image", new EmbeddedPng!("images.png")) 307 .addRow(rows); 308 309 auto tree2 = new TreeView(panedWindow) 310 .setHeading("Film") 311 .setWidth(250) 312 .addColumn(new TreeViewColumn("Year").setWidth(20)) 313 .addColumn(new TreeViewColumn("IMDB ranking").setWidth(50)) 314 .addRow(new TreeViewRow(["The Shawshank Redemption", "1994", "1"])) 315 .addRow(new TreeViewRow(["The Godfather", "1972", "2"])) 316 .addRow(new TreeViewRow(["The Godfather: Part II", "1974", "3"])) 317 .addRow(new TreeViewRow(["The Dark Knight", "2008", "4"])) 318 .addRow(new TreeViewRow(["Pulp Fiction", "1994", "5"])) 319 .addRow(new TreeViewRow(["The Good, the Bad and the Ugly", "1966", "6"])) 320 .addRow(new TreeViewRow(["Schindler's List", "1993", "7"])) 321 .addRow(new TreeViewRow(["Angry Men", "1957", "8"])) 322 .addRow(new TreeViewRow(["The Lord of the Rings: The Return of the King", "2003", "9"])) 323 .addRow(new TreeViewRow(["Fight Club", "1999", "10"])); 324 325 panedWindow 326 .addPane(tree1).setPaneWeight(0, 1) 327 .addPane(tree2).setPaneWeight(1, 1) 328 .pack(10, 0, GeometrySide.top, GeometryFill.both, AnchorPosition.center, true); 329 330 return panedPane; 331 } 332 333 /** 334 * Create the canvas pane for the note book. 335 * 336 * Returns: 337 * The canvas pane. 338 */ 339 private Frame createCanvasPane() 340 { 341 auto canvasPane = new Frame(); 342 343 auto container = new Frame(canvasPane) 344 .pack(10, 0, GeometrySide.top, GeometryFill.both, AnchorPosition.center, true); 345 346 container.configureGeometryColumn(0, 1); 347 container.configureGeometryRow(0, 1); 348 349 this._canvas = new Canvas(container, Color.bisque) 350 .setCursor(Cursor.hand1) 351 .setScrollRegion(-300, -250, 610, 500) 352 .addItem(new CanvasRectangle([10, 10, 200, 100]).addTag("tagged")) 353 .addItem(new CanvasArc([10, 110, 110, 210], CanvasArcStyle.pie, Color.paleGreen)) 354 .addItem(new CanvasImage([210, 10], new EmbeddedPng!("thumbnail.png"))) 355 .addItem(new CanvasImage([260, 10], new EmbeddedGif!("thumbnail.gif"))) 356 .addItem(new CanvasLine([120, 110, 200, 110, 200, 160]).setArrowPosition(CanvasLineArrow.last)) 357 .addItem(new CanvasOval([10, 170, 200, 245], Color.rosyBrown3)) 358 .addItem(new CanvasPolygon([220, 80, 320, 80, 300, 120, 240, 120], Color.mediumPurple)) 359 .addItem(new CanvasText([30, 192], "Tkd Canvas", Color.white).setFont("{Times New Roman} 22 bold")) 360 .addItem(new CanvasWidget([220, 140], new Button("Embedded\nWidget", new EmbeddedPng!("error.png"))).setWidth(100).setHeight(100)) 361 .addTagConfig(new CanvasTagConfig("tagged").setFillColor(Color.salmon)) 362 .setXView(0.25) 363 .setYView(0.24) 364 .bind("<ButtonPress-1>", &this.mark) 365 .bind("<B1-Motion>", &this.drag); 366 367 auto xscroll = new XScrollBar(container) 368 .attachWidget(this._canvas) 369 .grid(0, 1, 0, 0, 1, 1, "esw"); 370 371 auto yscroll = new YScrollBar(container) 372 .attachWidget(this._canvas) 373 .grid(1, 0, 0, 0, 1, 1, "nes"); 374 375 this._canvas 376 .attachXScrollBar(xscroll) 377 .attachYScrollBar(yscroll) 378 .grid(0, 0, 0, 0, 1, 1, "nesw"); 379 380 return canvasPane; 381 } 382 383 /** 384 * Create the dialog pane for the note book. 385 * 386 * Returns: 387 * The dialog pane. 388 */ 389 private Frame createDialogPane() 390 { 391 auto dialogPane = new Frame(); 392 393 auto modalLabelFrame = new LabelFrame(dialogPane, "Modal") 394 .configureGeometryColumn(1, 1) 395 .pack(10, 0, GeometrySide.top, GeometryFill.both, AnchorPosition.center, true); 396 397 auto colorButton = new Button(modalLabelFrame, new EmbeddedPng!("color_swatch.png"), "Color", ImagePosition.left) 398 .setCommand(&this.openColorDialog) 399 .grid(0, 0, 10); 400 401 this._colorEntry = new Entry(modalLabelFrame) 402 .grid(1, 0, 10, 0, 1, 1, "ew"); 403 404 auto directoryButton = new Button(modalLabelFrame, new EmbeddedPng!("chart_organisation.png"), "Directory", ImagePosition.left) 405 .setCommand(&this.openDirectoryDialog) 406 .grid(0, 1, 10); 407 408 this._directoryEntry = new Entry(modalLabelFrame) 409 .setWidth(40) 410 .grid(1, 1, 10, 0, 1, 1, "ew"); 411 412 auto fileOpenButton = new Button(modalLabelFrame, new EmbeddedPng!("folder_page.png"), "Open File", ImagePosition.left) 413 .setCommand(&this.openOpenFileDialog) 414 .grid(0, 2, 10); 415 416 this._openFileEntry = new Entry(modalLabelFrame) 417 .setWidth(40) 418 .grid(1, 2, 10, 0, 1, 1, "ew"); 419 420 auto fileSaveButton = new Button(modalLabelFrame, new EmbeddedPng!("disk.png"), "Save File", ImagePosition.left) 421 .setCommand(&this.openSaveFileDialog) 422 .grid(0, 3, 10); 423 424 this._saveFileEntry = new Entry(modalLabelFrame) 425 .setWidth(40) 426 .grid(1, 3, 10, 0, 1, 1, "ew"); 427 428 auto messageButton = new Button(modalLabelFrame, new EmbeddedPng!("comment.png"), "Message", ImagePosition.left) 429 .setCommand(&this.openMessageDialog) 430 .grid(0, 4, 10); 431 432 this._messageEntry = new Entry(modalLabelFrame) 433 .setWidth(40) 434 .grid(1, 4, 10, 0, 1, 1, "ew"); 435 436 auto nonModalLabelFrame = new LabelFrame(dialogPane, "Non Modal") 437 .configureGeometryColumn(1, 1) 438 .pack(10, 0, GeometrySide.top, GeometryFill.both, AnchorPosition.center, true); 439 440 auto fontButton = new Button(nonModalLabelFrame, new EmbeddedPng!("style.png"), "Font", ImagePosition.left) 441 .setCommand(&this.openFontDialog) 442 .grid(0, 0, 10); 443 444 this._fontEntry = new Entry(nonModalLabelFrame) 445 .setWidth(40) 446 .grid(1, 0, 10, 0, 1, 1, "ew"); 447 448 return dialogPane; 449 } 450 451 /** 452 * Set up the key bindings for the application. 453 */ 454 private void setUpKeyBindings() 455 { 456 this.mainWindow.bind("<F1>", &this.showAbout); 457 this.mainWindow.bind("<Control-q>", &this.exitApplication); 458 } 459 460 /** 461 * Initialise the user interface. 462 */ 463 override protected void initInterface() 464 { 465 this.mainWindow.setTitle("Tkd Showcase"); 466 this.mainWindow.setMinSize(550, 560); 467 this.mainWindow.setDefaultIcon([new EmbeddedPng!("tkicon.png")]); 468 469 this.mainWindow.setProtocolCommand(WindowProtocol.deleteWindow, delegate(CommandArgs args){ 470 this.showAbout(args); 471 this.exitApplication(args); 472 }); 473 474 this.mainWindow.setIdleCommand(delegate(CommandArgs args){ 475 this._entry.setValue(Clock.currTime().toLocalTime().toSimpleString().findSplitBefore(".")[0]); 476 this.mainWindow.setIdleCommand(args.callback, 1000); 477 }); 478 479 this.createMenu(); 480 481 auto noteBook = new NoteBook(); 482 auto widgetPane = this.createWidgetPane(); 483 auto panedPane = this.createPanedPane(); 484 auto canvasPane = this.createCanvasPane(); 485 auto dialogPane = this.createDialogPane(); 486 487 noteBook 488 .addTab("Widgets", widgetPane).setTabImage(0, new EmbeddedPng!("layout_content.png"), ImagePosition.left) 489 .addTab("Panes", panedPane).setTabImage(1, new EmbeddedPng!("application_tile_horizontal.png"), ImagePosition.left) 490 .addTab("Canvas", canvasPane).setTabImage(2, new EmbeddedPng!("shape_ungroup.png"), ImagePosition.left) 491 .addTab("Dialogs", dialogPane).setTabImage(3, new EmbeddedPng!("application_double.png"), ImagePosition.left) 492 .pack(10, 0, GeometrySide.top, GeometryFill.both, AnchorPosition.center, true); 493 494 auto exitButton = new Button("Exit") 495 .setCommand(&this.exitApplication) 496 .pack(5); 497 498 auto sizeGrip = new SizeGrip(this.mainWindow) 499 .pack(0, 0, GeometrySide.bottom, GeometryFill.none, AnchorPosition.southEast); 500 501 this.setUpKeyBindings(); 502 } 503 504 /** 505 * Exit the application. 506 * 507 * Params: 508 * args = The command arguments. 509 */ 510 private void exitApplication(CommandArgs args) 511 { 512 this.exit(); 513 } 514 515 } 516 517 /** 518 * Main entry point. 519 * 520 * Params: 521 * args = An array of command line arguments passed to this program. 522 */ 523 void main(string[] args) 524 { 525 auto app = new Application(); 526 app.run(); 527 }