1 // Written in the D programming language. 2 3 /** 4 This app is a demo for most of DlangUI library features. 5 6 Synopsis: 7 8 ---- 9 dub run dlangui:example1 10 ---- 11 12 Copyright: Vadim Lopatin, 2014 13 License: Boost License 1.0 14 Authors: Vadim Lopatin, coolreader.org@gmail.com 15 */ 16 module example1; 17 18 import dlangui; 19 import dlangui.dialogs.dialog; 20 import dlangui.dialogs.filedlg; 21 import dlangui.dialogs.msgbox; 22 import std.stdio; 23 import std.conv; 24 import std.utf; 25 import std.algorithm; 26 import std.path; 27 28 import widgets; 29 30 mixin APP_ENTRY_POINT; 31 32 class TextEditorWidget : VerticalLayout { 33 EditBox _edit; 34 this(string ID) { 35 super(ID); 36 _edit = new EditBox("editor"); 37 _edit.layoutWidth = FILL_PARENT; 38 _edit.layoutHeight = FILL_PARENT; 39 addChild(_edit); 40 } 41 } 42 43 /// Constructs items for main menu 44 auto constructMainMenu() 45 { 46 MenuItem mainMenuItems = new MenuItem(); 47 MenuItem fileItem = new MenuItem(new Action(1, "MENU_FILE"c)); 48 fileItem.add(new Action(ACTION_FILE_OPEN, "MENU_FILE_OPEN"c, "document-open", KeyCode.KEY_O, KeyFlag.Control)); 49 fileItem.add(new Action(ACTION_FILE_SAVE, "MENU_FILE_SAVE"c, "document-save", KeyCode.KEY_S, KeyFlag.Control)); 50 MenuItem openRecentItem = new MenuItem(new Action(13, "MENU_FILE_OPEN_RECENT", "document-open-recent")); 51 openRecentItem.add(new Action(100, "&1: File 1"d)); 52 openRecentItem.add(new Action(101, "&2: File 2"d)); 53 openRecentItem.add(new Action(102, "&3: File 3"d)); 54 openRecentItem.add(new Action(103, "&4: File 4"d)); 55 openRecentItem.add(new Action(104, "&5: File 5"d)); 56 fileItem.add(openRecentItem); 57 fileItem.add(new Action(ACTION_FILE_EXIT, "MENU_FILE_EXIT"c, "document-close"c, KeyCode.KEY_X, KeyFlag.Alt)); 58 59 MenuItem editItem = new MenuItem(new Action(2, "MENU_EDIT")); 60 editItem.add(new Action(EditorActions.Copy, "MENU_EDIT_COPY"c, "edit-copy", KeyCode.KEY_C, KeyFlag.Control)); 61 editItem.add(new Action(EditorActions.Paste, "MENU_EDIT_PASTE"c, "edit-paste", KeyCode.KEY_V, KeyFlag.Control)); 62 editItem.add(new Action(EditorActions.Cut, "MENU_EDIT_CUT"c, "edit-cut", KeyCode.KEY_X, KeyFlag.Control)); 63 editItem.add(new Action(EditorActions.Undo, "MENU_EDIT_UNDO"c, "edit-undo", KeyCode.KEY_Z, KeyFlag.Control)); 64 editItem.add(new Action(EditorActions.Redo, "MENU_EDIT_REDO"c, "edit-redo", KeyCode.KEY_Y, KeyFlag.Control)); 65 editItem.add(new Action(EditorActions.Indent, "MENU_EDIT_INDENT"c, "edit-indent", KeyCode.TAB, 0)); 66 editItem.add(new Action(EditorActions.Unindent, "MENU_EDIT_UNINDENT"c, "edit-unindent", KeyCode.TAB, KeyFlag.Control)); 67 editItem.add(new Action(20, "MENU_EDIT_PREFERENCES")); 68 69 MenuItem viewItem = new MenuItem(new Action(60, "MENU_VIEW")); 70 MenuItem langItem = new MenuItem(new Action(61, "MENU_VIEW_LANGUAGE")); 71 auto onLangChange = delegate (MenuItem item) { 72 if (!item.checked) 73 return false; 74 if (item.id == 611) { 75 // set interface language to english 76 platform.instance.uiLanguage = "en"; 77 } else if (item.id == 612) { 78 // set interface language to russian 79 platform.instance.uiLanguage = "ru"; 80 } 81 return true; 82 }; 83 MenuItem enLang = (new MenuItem(new Action(611, "MENU_VIEW_LANGUAGE_EN"))).type(MenuItemType.Radio).checked(true); 84 MenuItem ruLang = (new MenuItem(new Action(612, "MENU_VIEW_LANGUAGE_RU"))).type(MenuItemType.Radio); 85 enLang.menuItemClick = onLangChange; 86 ruLang.menuItemClick = onLangChange; 87 langItem.add(enLang); 88 langItem.add(ruLang); 89 viewItem.add(langItem); 90 MenuItem themeItem = new MenuItem(new Action(62, "MENU_VIEW_THEME")); 91 MenuItem theme1 = (new MenuItem(new Action(621, "MENU_VIEW_THEME_DEFAULT"))).type(MenuItemType.Radio).checked(true); 92 MenuItem theme2 = (new MenuItem(new Action(622, "MENU_VIEW_THEME_DARK"))).type(MenuItemType.Radio); 93 MenuItem theme3 = (new MenuItem(new Action(623, "MENU_VIEW_THEME_CUSTOM1"))).type(MenuItemType.Radio); 94 auto onThemeChange = delegate (MenuItem item) { 95 if (!item.checked) 96 return false; 97 if (item.id == 621) { 98 platform.instance.uiTheme = "theme_default"; 99 } else if (item.id == 622) { 100 platform.instance.uiTheme = "theme_dark"; 101 } else if (item.id == 623) { 102 platform.instance.uiTheme = "theme_custom1"; 103 } 104 return true; 105 }; 106 theme1.menuItemClick = onThemeChange; 107 theme2.menuItemClick = onThemeChange; 108 theme3.menuItemClick = onThemeChange; 109 themeItem.add(theme1); 110 themeItem.add(theme2); 111 themeItem.add(theme3); 112 viewItem.add(themeItem); 113 114 MenuItem windowItem = new MenuItem(new Action(3, "MENU_WINDOW"c)); 115 windowItem.add(new Action(30, "MENU_WINDOW_PREFERENCES")); 116 windowItem.add(new Action(31, UIString.fromId("MENU_WINDOW_MINIMIZE"))); 117 windowItem.add(new Action(32, UIString.fromId("MENU_WINDOW_MAXIMIZE"))); 118 windowItem.add(new Action(33, UIString.fromId("MENU_WINDOW_RESTORE"))); 119 MenuItem helpItem = new MenuItem(new Action(4, "MENU_HELP"c)); 120 helpItem.add(new Action(40, "MENU_HELP_VIEW_HELP")); 121 MenuItem aboutItem = new MenuItem(new Action(41, "MENU_HELP_ABOUT")); 122 helpItem.add(aboutItem); 123 mainMenuItems.add(fileItem); 124 mainMenuItems.add(editItem); 125 mainMenuItems.add(viewItem); 126 mainMenuItems.add(windowItem); 127 mainMenuItems.add(helpItem); 128 return mainMenuItems; 129 } 130 131 /// entry point for dlangui based application 132 extern (C) int UIAppMain(string[] args) 133 { 134 // always use trace, even for release builds 135 //Log.setLogLevel(LogLevel.Trace); 136 //Log.setFileLogger(new std.stdio.File("ui.log", "w")); 137 138 // resource directory search paths 139 // not required if only embedded resources are used 140 //string[] resourceDirs = [ 141 // appendPath(exePath, "../../../res/"), // for Visual D and DUB builds 142 // appendPath(exePath, "../../../res/mdpi/"), // for Visual D and DUB builds 143 // appendPath(exePath, "../../../../res/"),// for Mono-D builds 144 // appendPath(exePath, "../../../../res/mdpi/"),// for Mono-D builds 145 // appendPath(exePath, "res/"), // when res dir is located at the same directory as executable 146 // appendPath(exePath, "../res/"), // when res dir is located at project directory 147 // appendPath(exePath, "../../res/"), // when res dir is located at the same directory as executable 148 // appendPath(exePath, "res/mdpi/"), // when res dir is located at the same directory as executable 149 // appendPath(exePath, "../res/mdpi/"), // when res dir is located at project directory 150 // appendPath(exePath, "../../res/mdpi/") // when res dir is located at the same directory as executable 151 //]; 152 // setup resource directories - will use only existing directories 153 //Platform.instance.resourceDirs = resourceDirs; 154 155 // embed resources listed in views/resources.list into executable 156 embeddedResourceList.addResources(embedResourcesFromList!("resources.list")()); 157 158 //version (USE_OPENGL) { 159 // // you can turn on subpixel font rendering (ClearType) here 160 //FontManager.subpixelRenderingMode = SubpixelRenderingMode.None; // 161 //} else { 162 // you can turn on subpixel font rendering (ClearType) here 163 FontManager.subpixelRenderingMode = SubpixelRenderingMode.BGR; //SubpixelRenderingMode.None; // 164 //} 165 166 // select translation file - for english language 167 Platform.instance.uiLanguage = "en"; 168 // load theme from file "theme_default.xml" 169 Platform.instance.uiTheme = "theme_default"; 170 //Platform.instance.uiTheme = "theme_dark"; 171 172 // you can override default hinting mode here (Normal, AutoHint, Disabled) 173 FontManager.hintingMode = HintingMode.Normal; 174 // you can override antialiasing setting here (0 means antialiasing always on, some big value = always off) 175 // fonts with size less than specified value will not be antialiased 176 FontManager.minAnitialiasedFontSize = 0; // 0 means always antialiased 177 //version (USE_OPENGL) { 178 // // you can turn on subpixel font rendering (ClearType) here 179 FontManager.subpixelRenderingMode = SubpixelRenderingMode.None; // 180 //} else { 181 // you can turn on subpixel font rendering (ClearType) here 182 //FontManager.subpixelRenderingMode = SubpixelRenderingMode.BGR; //SubpixelRenderingMode.None; // 183 //} 184 185 // create window 186 //Window window = Platform.instance.createWindow("DlangUI Example 1", null, WindowFlag.Resizable, 800, 700); 187 // Expand window size if content is bigger than 800, 700 (change to above version if you want scrollbars and 800, 700 size) 188 Window window = Platform.instance.createWindow("DlangUI Example 1", null, WindowFlag.Resizable | WindowFlag.ExpandSize, 800, 700); 189 // here you can see window or content resize mode 190 //Window window = Platform.instance.createWindow("DlangUI Example 1", null, WindowFlag.Resizable, 400, 400); 191 //window.windowOrContentResizeMode = WindowOrContentResizeMode.resizeWindow; 192 //window.windowOrContentResizeMode = WindowOrContentResizeMode.scrollWindow; 193 //window.windowOrContentResizeMode = WindowOrContentResizeMode.shrinkWidgets; 194 195 VerticalLayout contentLayout = new VerticalLayout(); 196 197 TabWidget tabs = new TabWidget("TABS"); 198 tabs.tabClose = delegate(string tabId) { 199 tabs.removeTab(tabId); 200 }; 201 202 //========================================================================= 203 // create main menu 204 205 MainMenu mainMenu = new MainMenu(constructMainMenu()); 206 mainMenu.menuItemClick = delegate(MenuItem item) { 207 Log.d("mainMenu.onMenuItemListener", item.label); 208 const Action a = item.action; 209 if (a) { 210 return contentLayout.dispatchAction(a); 211 } 212 return false; 213 }; 214 contentLayout.addChild(mainMenu); 215 // to let main menu handle keyboard shortcuts 216 contentLayout.keyToAction = delegate(Widget source, uint keyCode, uint flags) { 217 return mainMenu.findKeyAction(keyCode, flags); 218 }; 219 contentLayout.onAction = delegate(Widget source, const Action a) { 220 if (a.id == ACTION_FILE_EXIT) { 221 window.close(); 222 return true; 223 } else if (a.id == 31) { 224 window.minimizeWindow(); 225 return true; 226 } else if (a.id == 32) { 227 window.maximizeWindow(); 228 return true; 229 } else if (a.id == 33) { 230 window.restoreWindow(); 231 return true; 232 } else if (a.id == 41) { 233 window.showMessageBox(UIString.fromRaw("About"d), UIString.fromRaw("DLangUI demo app\n(C) Vadim Lopatin, 2014\nhttp://github.com/buggins/dlangui"d)); 234 return true; 235 } else if (a.id == ACTION_FILE_OPEN) { 236 UIString caption; 237 caption = "Open Text File"d; 238 FileDialog dlg = new FileDialog(caption, window, null); 239 dlg.allowMultipleFiles = true; 240 dlg.addFilter(FileFilterEntry(UIString("FILTER_ALL_FILES", "All files (*)"d), "*")); 241 dlg.addFilter(FileFilterEntry(UIString("FILTER_TEXT_FILES", "Text files (*.txt)"d), "*.txt")); 242 dlg.addFilter(FileFilterEntry(UIString("FILTER_SOURCE_FILES", "Source files"d), "*.d;*.dd;*.c;*.cc;*.cpp;*.h;*.hpp")); 243 dlg.addFilter(FileFilterEntry(UIString("FILTER_EXECUTABLE_FILES", "Executable files"d), "*", true)); 244 //dlg.filterIndex = 2; 245 dlg.dialogResult = delegate(Dialog dlg, const Action result) { 246 if (result.id == ACTION_OPEN.id) { 247 string[] filenames = (cast(FileDialog)dlg).filenames; 248 foreach (filename; filenames) { 249 if (filename.endsWith(".d") || filename.endsWith(".txt") || filename.endsWith(".cpp") || filename.endsWith(".h") || filename.endsWith(".c") 250 || filename.endsWith(".json") || filename.endsWith(".dd") || filename.endsWith(".ddoc") || filename.endsWith(".xml") || filename.endsWith(".html") 251 || filename.endsWith(".html") || filename.endsWith(".css") || filename.endsWith(".log") || filename.endsWith(".hpp")) { 252 // open source file in tab 253 int index = tabs.tabIndex(filename); 254 if (index >= 0) { 255 // file is already opened in tab 256 tabs.selectTab(index, true); 257 } else { 258 SourceEdit editor = new SourceEdit(filename); 259 if (editor.load(filename)) { 260 tabs.addTab(editor, toUTF32(baseName(filename)), null, true); 261 tabs.selectTab(filename); 262 } else { 263 destroy(editor); 264 window.showMessageBox(UIString.fromRaw("File open error"d), UIString.fromRaw("Cannot open file "d ~ toUTF32(filename))); 265 } 266 } 267 } else { 268 Log.d("FileDialog.onDialogResult: ", result, " param=", result.stringParam); 269 window.showMessageBox(UIString.fromRaw("FileOpen result"d), UIString.fromRaw("Filename: "d ~ toUTF32(filename))); 270 } 271 } 272 } 273 274 }; 275 dlg.show(); 276 return true; 277 } 278 //else 279 //return contentLayout.dispatchAction(a); 280 return false; 281 }; 282 283 // Setup tab view 284 tabs.tabChanged = delegate(string newTabId, string oldTabId) 285 { 286 window.windowCaption = tabs.tab(newTabId).text.value ~ " - dlangui example 1"d; 287 }; 288 tabs.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT); 289 290 291 // Add all the example tabs 292 tabs.addTab(new BasicControls("controls"), "Controls"d); 293 tabs.addTab(new MiscExample("tab1"), "Misc"d); 294 tabs.addTab(new LongListsExample("tab2"), "TAB_LONG_LIST"c); 295 tabs.addTab(new ButtonsExample("tab3"), "TAB_BUTTONS"c); 296 tabs.addTab(new TableExample("TABLE"), "TAB_TABLE_LAYOUT"c); 297 tabs.addTab(new EditorsExample("EDITORS"), "TAB_EDITORS"c); 298 tabs.addTab(new GridExample("GRID_CONTENT"), "Grid"d); 299 tabs.addTab(new ScrollExample("SCROLL1"), "Scroll"d); 300 tabs.addTab(new TreeExample("TREE"), "Tree"d); 301 tabs.addTab(new ChartsExample("charts"), "Charts"d); 302 tabs.addTab((new SampleAnimationWidget("tab6")).layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT), "TAB_ANIMATION"c); 303 tabs.addTab(new CanvasExample("canvas"), UIString.fromId("TAB_CANVAS")); 304 tabs.addTab(new IconsExample("icons"), "Icons"d); 305 306 static if (BACKEND_GUI && ENABLE_OPENGL) 307 { 308 tabs.addTab(new OpenGLExample(), "OpenGL"d); 309 } 310 311 //========================================================================== 312 313 contentLayout.addChild(tabs); 314 window.mainWidget = contentLayout; 315 316 tabs.selectTab("controls"); 317 318 static if (BACKEND_GUI) { 319 window.windowIcon = drawableCache.getImage("dlangui-logo1"); 320 } 321 window.show(); 322 //window.windowCaption = "New Window Caption"; 323 // run message loop 324 325 Log.i("HOME path: ", homePath); 326 Log.i("APPDATA path: ", appDataPath(".dlangui")); 327 Log.i("Root paths: ", getRootPaths); 328 329 return Platform.instance.enterMessageLoop(); 330 }