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 }