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