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 TimerTest : HorizontalLayout {
33     ulong timerId;
34     TextWidget _counter;
35     int _value;
36     Button _start;
37     Button _stop;
38     override bool onTimer(ulong id) {
39         _value++;
40         _counter.text = to!dstring(_value);
41         return true;
42     }
43     this() {
44         addChild(new TextWidget(null, "Timer test."d));
45         _counter = new TextWidget(null, "0"d);
46         _counter.fontSize(32);
47         _start = new Button(null, "Start timer"d);
48         _stop = new Button(null, "Stop timer"d);
49         _stop.enabled = false;
50         _start.click = delegate(Widget src) {
51             _start.enabled = false;
52             _stop.enabled = true;
53             timerId = setTimer(1000);
54             return true;
55         };
56         _stop.click = delegate(Widget src) {
57             _start.enabled = true;
58             _stop.enabled = false;
59             cancelTimer(timerId);
60             return true;
61         };
62         addChild(_start);
63         addChild(_stop);
64         addChild(_counter);
65     }
66 }
67 
68 class TextEditorWidget : VerticalLayout {
69     EditBox _edit;
70     this(string ID) {
71         super(ID);
72         _edit = new EditBox("editor");
73         _edit.layoutWidth = FILL_PARENT;
74         _edit.layoutHeight = FILL_PARENT;
75         addChild(_edit);
76     }
77 }
78 
79 Widget createEditorSettingsControl(EditWidgetBase editor) {
80     HorizontalLayout res = new HorizontalLayout("editor_options");
81     res.addChild((new CheckBox("wantTabs", "wantTabs"d)).checked(editor.wantTabs).addOnCheckChangeListener(delegate(Widget, bool checked) { editor.wantTabs = checked; return true;}));
82     res.addChild((new CheckBox("useSpacesForTabs", "useSpacesForTabs"d)).checked(editor.useSpacesForTabs).addOnCheckChangeListener(delegate(Widget, bool checked) { editor.useSpacesForTabs = checked; return true;}));
83     res.addChild((new CheckBox("readOnly", "readOnly"d)).checked(editor.readOnly).addOnCheckChangeListener(delegate(Widget, bool checked) { editor.readOnly = checked; return true;}));
84     res.addChild((new CheckBox("showLineNumbers", "showLineNumbers"d)).checked(editor.showLineNumbers).addOnCheckChangeListener(delegate(Widget, bool checked) { editor.showLineNumbers = checked; return true;}));
85     res.addChild((new CheckBox("fixedFont", "fixedFont"d)).checked(editor.fontFamily == FontFamily.MonoSpace).addOnCheckChangeListener(delegate(Widget, bool checked) {
86         if (checked)
87             editor.fontFamily(FontFamily.MonoSpace).fontFace("Courier New");
88         else
89             editor.fontFamily(FontFamily.SansSerif).fontFace("Arial");
90         return true;
91     }));
92     res.addChild((new CheckBox("tabSize", "Tab size 8"d)).checked(editor.tabSize == 8).addOnCheckChangeListener(delegate(Widget, bool checked) {
93         if (checked)
94             editor.tabSize(8);
95         else
96             editor.tabSize(4);
97         return true;
98     }));
99     return res;
100 }
101 
102 enum : int {
103     ACTION_FILE_OPEN = 5500,
104     ACTION_FILE_SAVE,
105     ACTION_FILE_CLOSE,
106     ACTION_FILE_EXIT,
107 }
108 
109 debug(SDLSettings) {
110     import dlangui.core.settings;
111     void testSDL(string fn) {
112         Log.d("Loading SDL from ", fn);
113         Setting s = new Setting();
114         if (s.load(fn)) {
115             Log.d("JSON:\n", s.toJSON(true));
116         } else {
117             Log.e("failed to read SDL from ", fn);
118         }
119     }
120     void testSDLSettings() {
121         testSDL(`C:\Users\vlopatin\AppData\Roaming\.dlangide\settings.json`);
122         testSDL("dub.json");
123         testSDL("test1.sdl");
124     }
125 }
126 
127 /// entry point for dlangui based application
128 extern (C) int UIAppMain(string[] args) {
129 
130     debug(SDLSettings) {
131         testSDLSettings();
132     }
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     static if (true) {
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         MenuItem mainMenuItems = new MenuItem();
206         MenuItem fileItem = new MenuItem(new Action(1, "MENU_FILE"c));
207         fileItem.add(new Action(ACTION_FILE_OPEN, "MENU_FILE_OPEN"c, "document-open", KeyCode.KEY_O, KeyFlag.Control));
208         fileItem.add(new Action(ACTION_FILE_SAVE, "MENU_FILE_SAVE"c, "document-save", KeyCode.KEY_S, KeyFlag.Control));
209         MenuItem openRecentItem = new MenuItem(new Action(13, "MENU_FILE_OPEN_RECENT", "document-open-recent"));
210         openRecentItem.add(new Action(100, "&1: File 1"d));
211         openRecentItem.add(new Action(101, "&2: File 2"d));
212         openRecentItem.add(new Action(102, "&3: File 3"d));
213         openRecentItem.add(new Action(103, "&4: File 4"d));
214         openRecentItem.add(new Action(104, "&5: File 5"d));
215         fileItem.add(openRecentItem);
216         fileItem.add(new Action(ACTION_FILE_EXIT, "MENU_FILE_EXIT"c, "document-close"c, KeyCode.KEY_X, KeyFlag.Alt));
217 
218         MenuItem editItem = new MenuItem(new Action(2, "MENU_EDIT"));
219         editItem.add(new Action(EditorActions.Copy, "MENU_EDIT_COPY"c, "edit-copy", KeyCode.KEY_C, KeyFlag.Control));
220         editItem.add(new Action(EditorActions.Paste, "MENU_EDIT_PASTE"c, "edit-paste", KeyCode.KEY_V, KeyFlag.Control));
221         editItem.add(new Action(EditorActions.Cut, "MENU_EDIT_CUT"c, "edit-cut", KeyCode.KEY_X, KeyFlag.Control));
222         editItem.add(new Action(EditorActions.Undo, "MENU_EDIT_UNDO"c, "edit-undo", KeyCode.KEY_Z, KeyFlag.Control));
223         editItem.add(new Action(EditorActions.Redo, "MENU_EDIT_REDO"c, "edit-redo", KeyCode.KEY_Y, KeyFlag.Control));
224         editItem.add(new Action(EditorActions.Indent, "MENU_EDIT_INDENT"c, "edit-indent", KeyCode.TAB, 0));
225         editItem.add(new Action(EditorActions.Unindent, "MENU_EDIT_UNINDENT"c, "edit-unindent", KeyCode.TAB, KeyFlag.Control));
226         editItem.add(new Action(20, "MENU_EDIT_PREFERENCES"));
227 
228         MenuItem editPopupItem = new MenuItem(null);
229         editPopupItem.add(new Action(EditorActions.Copy, "MENU_EDIT_COPY"c, "edit-copy", KeyCode.KEY_C, KeyFlag.Control));
230         editPopupItem.add(new Action(EditorActions.Paste, "MENU_EDIT_PASTE"c, "edit-paste", KeyCode.KEY_V, KeyFlag.Control));
231         editPopupItem.add(new Action(EditorActions.Cut, "MENU_EDIT_CUT"c, "edit-cut", KeyCode.KEY_X, KeyFlag.Control));
232         editPopupItem.add(new Action(EditorActions.Undo, "MENU_EDIT_UNDO"c, "edit-undo", KeyCode.KEY_Z, KeyFlag.Control));
233         editPopupItem.add(new Action(EditorActions.Redo, "MENU_EDIT_REDO"c, "edit-redo", KeyCode.KEY_Y, KeyFlag.Control));
234         editPopupItem.add(new Action(EditorActions.Indent, "MENU_EDIT_INDENT"c, "edit-indent", KeyCode.TAB, 0));
235         editPopupItem.add(new Action(EditorActions.Unindent, "MENU_EDIT_UNINDENT"c, "edit-unindent", KeyCode.TAB, KeyFlag.Control));
236 
237         MenuItem viewItem = new MenuItem(new Action(60, "MENU_VIEW"));
238         MenuItem langItem = new MenuItem(new Action(61, "MENU_VIEW_LANGUAGE"));
239         auto onLangChange = delegate (MenuItem item) {
240             if (!item.checked)
241                 return false;
242             if (item.id == 611) {
243                 // set interface language to english
244                 platform.instance.uiLanguage = "en";
245             } else if (item.id == 612) {
246                 // set interface language to russian
247                 platform.instance.uiLanguage = "ru";
248             }
249             return true;
250         };
251         MenuItem enLang = (new MenuItem(new Action(611, "MENU_VIEW_LANGUAGE_EN"))).type(MenuItemType.Radio).checked(true);
252         MenuItem ruLang = (new MenuItem(new Action(612, "MENU_VIEW_LANGUAGE_RU"))).type(MenuItemType.Radio);
253         enLang.menuItemClick = onLangChange;
254         ruLang.menuItemClick = onLangChange;
255         langItem.add(enLang);
256         langItem.add(ruLang);
257         viewItem.add(langItem);
258         MenuItem themeItem = new MenuItem(new Action(62, "MENU_VIEW_THEME"));
259         MenuItem theme1 = (new MenuItem(new Action(621, "MENU_VIEW_THEME_DEFAULT"))).type(MenuItemType.Radio).checked(true);
260         MenuItem theme2 = (new MenuItem(new Action(622, "MENU_VIEW_THEME_DARK"))).type(MenuItemType.Radio);
261         MenuItem theme3 = (new MenuItem(new Action(623, "MENU_VIEW_THEME_CUSTOM1"))).type(MenuItemType.Radio);
262         auto onThemeChange = delegate (MenuItem item) {
263             if (!item.checked)
264                 return false;
265             if (item.id == 621) {
266                 platform.instance.uiTheme = "theme_default";
267             } else if (item.id == 622) {
268                 platform.instance.uiTheme = "theme_dark";
269             } else if (item.id == 623) {
270                 platform.instance.uiTheme = "theme_custom1";
271             }
272             return true;
273         };
274         theme1.menuItemClick = onThemeChange;
275         theme2.menuItemClick = onThemeChange;
276         theme3.menuItemClick = onThemeChange;
277         themeItem.add(theme1);
278         themeItem.add(theme2);
279         themeItem.add(theme3);
280         viewItem.add(themeItem);
281 
282         MenuItem windowItem = new MenuItem(new Action(3, "MENU_WINDOW"c));
283         windowItem.add(new Action(30, "MENU_WINDOW_PREFERENCES"));
284         windowItem.add(new Action(31, UIString.fromId("MENU_WINDOW_MINIMIZE")));
285         windowItem.add(new Action(32, UIString.fromId("MENU_WINDOW_MAXIMIZE")));
286         windowItem.add(new Action(33, UIString.fromId("MENU_WINDOW_RESTORE")));
287         MenuItem helpItem = new MenuItem(new Action(4, "MENU_HELP"c));
288         helpItem.add(new Action(40, "MENU_HELP_VIEW_HELP"));
289         MenuItem aboutItem = new MenuItem(new Action(41, "MENU_HELP_ABOUT"));
290         helpItem.add(aboutItem);
291         mainMenuItems.add(fileItem);
292         mainMenuItems.add(editItem);
293         mainMenuItems.add(viewItem);
294         mainMenuItems.add(windowItem);
295         mainMenuItems.add(helpItem);
296         MainMenu mainMenu = new MainMenu(mainMenuItems);
297         contentLayout.addChild(mainMenu);
298         // to let main menu handle keyboard shortcuts
299         contentLayout.keyToAction = delegate(Widget source, uint keyCode, uint flags) {
300             return mainMenu.findKeyAction(keyCode, flags);
301         };
302         contentLayout.onAction = delegate(Widget source, const Action a) {
303             if (a.id == ACTION_FILE_EXIT) {
304                 window.close();
305                 return true;
306             } else if (a.id == 31) {
307                 window.minimizeWindow();
308                 return true;
309             } else if (a.id == 32) {
310                 window.maximizeWindow();
311                 return true;
312             } else if (a.id == 33) {
313                 window.restoreWindow();
314                 return true;
315             } else if (a.id == 41) {
316                 window.showMessageBox(UIString.fromRaw("About"d), UIString.fromRaw("DLangUI demo app\n(C) Vadim Lopatin, 2014\nhttp://github.com/buggins/dlangui"d));
317                 return true;
318             } else if (a.id == ACTION_FILE_OPEN) {
319                 UIString caption;
320                 caption = "Open Text File"d;
321                 FileDialog dlg = new FileDialog(caption, window, null);
322                 dlg.allowMultipleFiles = true;
323                 dlg.addFilter(FileFilterEntry(UIString("FILTER_ALL_FILES", "All files (*)"d), "*"));
324                 dlg.addFilter(FileFilterEntry(UIString("FILTER_TEXT_FILES", "Text files (*.txt)"d), "*.txt"));
325                 dlg.addFilter(FileFilterEntry(UIString("FILTER_SOURCE_FILES", "Source files"d), "*.d;*.dd;*.c;*.cc;*.cpp;*.h;*.hpp"));
326                 dlg.addFilter(FileFilterEntry(UIString("FILTER_EXECUTABLE_FILES", "Executable files"d), "*", true));
327                 //dlg.filterIndex = 2;
328                 dlg.dialogResult = delegate(Dialog dlg, const Action result) {
329                     if (result.id == ACTION_OPEN.id) {
330                         string[] filenames = (cast(FileDialog)dlg).filenames;
331                         foreach (filename; filenames) {
332                             if (filename.endsWith(".d") || filename.endsWith(".txt") || filename.endsWith(".cpp") || filename.endsWith(".h") || filename.endsWith(".c")
333                                 || filename.endsWith(".json") || filename.endsWith(".dd") || filename.endsWith(".ddoc") || filename.endsWith(".xml") || filename.endsWith(".html")
334                                 || filename.endsWith(".html") || filename.endsWith(".css") || filename.endsWith(".log") || filename.endsWith(".hpp")) {
335                                     // open source file in tab
336                                     int index = tabs.tabIndex(filename);
337                                     if (index >= 0) {
338                                         // file is already opened in tab
339                                         tabs.selectTab(index, true);
340                                     } else {
341                                         SourceEdit editor = new SourceEdit(filename);
342                                         if (editor.load(filename)) {
343                                             tabs.addTab(editor, toUTF32(baseName(filename)), null, true);
344                                             tabs.selectTab(filename);
345                                         } else {
346                                             destroy(editor);
347                                             window.showMessageBox(UIString.fromRaw("File open error"d), UIString.fromRaw("Cannot open file "d ~ toUTF32(filename)));
348                                         }
349                                     }
350                                 } else {
351                                     Log.d("FileDialog.onDialogResult: ", result, " param=", result.stringParam);
352                                     window.showMessageBox(UIString.fromRaw("FileOpen result"d), UIString.fromRaw("Filename: "d ~ toUTF32(filename)));
353                                 }
354                         }
355                     }
356 
357                 };
358                 dlg.show();
359                 return true;
360             }
361             //else
362             //return contentLayout.dispatchAction(a);
363             return false;
364         };
365         mainMenu.menuItemClick = delegate(MenuItem item) {
366             Log.d("mainMenu.onMenuItemListener", item.label);
367             const Action a = item.action;
368             if (a) {
369                 return contentLayout.dispatchAction(a);
370             }
371             return false;
372         };
373 
374         // ========= create tabs ===================
375 
376         tabs.tabChanged = delegate(string newTabId, string oldTabId) {
377             window.windowCaption = tabs.tab(newTabId).text.value ~ " - dlangui example 1"d;
378         };
379         tabs.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
380 
381         // most of controls example
382         {
383             LinearLayout controls = new VerticalLayout("controls");
384             controls.layoutHeight(FILL_PARENT);
385             controls.padding = Rect(12.pointsToPixels,12.pointsToPixels,12.pointsToPixels,12.pointsToPixels);
386 
387             HorizontalLayout line1 = new HorizontalLayout();
388             controls.addChild(line1);
389 
390             GroupBox gb = new GroupBox("checkboxes", "CheckBox"d);
391             gb.addChild(new CheckBox("cb1", "CheckBox 1"d));
392             gb.addChild(new CheckBox("cb2", "CheckBox 2"d).checked(true));
393             gb.addChild(new CheckBox("cb3", "CheckBox disabled"d).enabled(false));
394             gb.addChild(new CheckBox("cb4", "CheckBox disabled"d).checked(true).enabled(false));
395             line1.addChild(gb);
396 
397             GroupBox gb2 = new GroupBox("radiobuttons", "RadioButton"d);
398             gb2.addChild(new RadioButton("rb1", "RadioButton 1"d).checked(true));
399             gb2.addChild(new RadioButton("rb2", "RadioButton 2"d));
400             gb2.addChild(new RadioButton("rb3", "RadioButton disabled"d).enabled(false));
401             line1.addChild(gb2);
402 
403             VerticalLayout col1 = new VerticalLayout();
404             GroupBox gb3 = new GroupBox("textbuttons", "Button"d, Orientation.Horizontal);
405             gb3.addChild(new Button("tb1", "Button"d));
406             gb3.addChild(new Button("tb2", "Button disabled"d).enabled(false));
407             col1.addChild(gb3);
408             GroupBox gb4 = new GroupBox("imagetextbuttons", "ImageTextButton"d, Orientation.Horizontal);
409             gb4.addChild(new ImageTextButton("itb1", "document-open", "Enabled"d));
410             gb4.addChild(new ImageTextButton("itb2", "document-save", "Disabled"d).enabled(false));
411             col1.addChild(gb4);
412             GroupBox gbtext = new GroupBox("text", "TextWidget"d, Orientation.Horizontal);
413             gbtext.addChild(new TextWidget("t1", "Red text"d).fontSize(12.pointsToPixels).textColor(0xFF0000));
414             gbtext.addChild(new TextWidget("t2", "Italic text"d).fontSize(12.pointsToPixels).fontItalic(true));
415             col1.addChild(gbtext);
416             line1.addChild(col1);
417 
418             VerticalLayout col2 = new VerticalLayout();
419             GroupBox gb31 = new GroupBox("switches", "SwitchButton"d, Orientation.Vertical);
420             gb31.addChild(new SwitchButton("sb1"));
421             gb31.addChild(new SwitchButton("sb2").checked(true));
422             gb31.addChild(new SwitchButton("sb3").enabled(false));
423             gb31.addChild(new SwitchButton("sb4").enabled(false).checked(true));
424             col2.addChild(gb31);
425             line1.addChild(col2);
426 
427             VerticalLayout col3 = new VerticalLayout();
428             GroupBox gb32 = new GroupBox("switches", "ImageButton"d, Orientation.Vertical);
429             gb32.addChild(new ImageButton("ib1", "edit-copy"));
430             gb32.addChild(new ImageButton("ib3", "edit-paste").enabled(false));
431             col3.addChild(gb32);
432             GroupBox gb33 = new GroupBox("images", "ImageWidget"d, Orientation.Vertical);
433             gb33.addChild(new ImageWidget("cr3_logo", "cr3_logo"));
434             col3.addChild(gb33);
435             line1.addChild(col3);
436 
437 
438             HorizontalLayout line2 = new HorizontalLayout();
439             controls.addChild(line2);
440 
441             GroupBox gb5 = new GroupBox("scrollbar", "horizontal ScrollBar"d);
442             gb5.addChild(new ScrollBar("sb1", Orientation.Horizontal));
443             line2.addChild(gb5);
444             GroupBox gb6 = new GroupBox("slider", "horizontal SliderWidget"d);
445             gb6.addChild(new SliderWidget("sb2", Orientation.Horizontal));
446             line2.addChild(gb6);
447             GroupBox gb7 = new GroupBox("editline1", "EditLine"d);
448             gb7.addChild(new EditLine("ed1", "Some text"d).minWidth(120.pointsToPixels));
449             line2.addChild(gb7);
450             GroupBox gb8 = new GroupBox("editline2", "EditLine disabled"d);
451             gb8.addChild(new EditLine("ed2", "Some text"d).enabled(false).minWidth(120.pointsToPixels));
452             line2.addChild(gb8);
453 
454             HorizontalLayout line3 = new HorizontalLayout();
455             line3.layoutWidth(FILL_PARENT);
456             GroupBox gbeditbox = new GroupBox("editbox", "EditBox"d, Orientation.Horizontal);
457             gbeditbox.layoutWidth(FILL_PARENT);
458             EditBox ed1 = new EditBox("ed1", "Some text in EditBox\nOne more line\nYet another text line");
459             ed1.layoutHeight(FILL_PARENT);
460             gbeditbox.addChild(ed1);
461             line3.addChild(gbeditbox);
462             GroupBox gbtabs = new GroupBox(null, "TabWidget"d);
463             gbtabs.layoutWidth(FILL_PARENT);
464             TabWidget tabs1 = new TabWidget("tabs1");
465             tabs1.addTab(new TextWidget("tab1", "TextWidget on tab page\nTextWidgets can be\nMultiline"d).maxLines(3), "Tab 1"d);
466             tabs1.addTab(new ImageWidget("tab2", "dlangui-logo1"), "Tab 2"d);
467             tabs1.tabHost.backgroundColor = 0xE0E0E0;
468             tabs1.tabHost.padding = Rect(10.pointsToPixels, 10.pointsToPixels, 10.pointsToPixels, 10.pointsToPixels);
469             gbtabs.addChild(tabs1);
470             line3.addChild(gbtabs);
471             controls.addChild(line3);
472 
473             HorizontalLayout line4 = new HorizontalLayout();
474             line4.layoutWidth(FILL_PARENT);
475             line4.layoutHeight(FILL_PARENT);
476             GroupBox gbgrid = new GroupBox("grid", "StringGridWidget"d, Orientation.Horizontal);
477             StringGridWidget grid = new StringGridWidget("stringgrid");
478             grid.resize(12, 10);
479             gbgrid.layoutWidth(FILL_PARENT);
480             gbgrid.layoutHeight(FILL_PARENT);
481             grid.layoutWidth(FILL_PARENT);
482             grid.layoutHeight(FILL_PARENT);
483             foreach (index, month; ["January"d, "February"d, "March"d, "April"d, "May"d, "June"d, "July"d, "August"d, "September"d, "October"d, "November"d, "December"d])
484                 grid.setColTitle(cast(int)index, month);
485             for (int y = 0; y < 10; y++)
486                 grid.setRowTitle(y, to!dstring(y+1));
487             //grid.alignment = Align.Right;
488             grid.setColWidth(0, 30.pointsToPixels);
489             grid.autoFit();
490             import std.random;
491             import std.string;
492             for (int x = 0; x < 12; x++) {
493                 for (int y = 0; y < 10; y++) {
494                     int n = uniform(0, 10000);
495                     grid.setCellText(x, y, to!dstring("%.2f".format(n / 100.0)));
496                 }
497             }
498             //grid.autoFit();
499             gbgrid.addChild(grid);
500             line4.addChild(gbgrid);
501 
502             GroupBox gbtree = new GroupBox("tree", "TreeWidget"d, Orientation.Vertical);
503             auto tree = new TreeWidget("gbtree");
504             //tree.layoutWidth(WRAP_CONTENT).layoutHeight(FILL_PARENT);
505             tree.maxHeight(200.pointsToPixels);
506             TreeItem tree1 = tree.items.newChild("group1", "Group 1"d, "document-open");
507             tree1.newChild("g1_1", "Group 1 item 1"d);
508             tree1.newChild("g1_2", "Group 1 item 2"d);
509             tree1.newChild("g1_3", "Group 1 item 3"d);
510             TreeItem tree2 = tree.items.newChild("group2", "Group 2"d, "document-save");
511             tree2.newChild("g2_1", "Group 2 item 1"d, "edit-copy");
512             tree2.newChild("g2_2", "Group 2 item 2"d, "edit-cut");
513             tree2.newChild("g2_3", "Group 2 item 3"d, "edit-paste");
514             tree2.newChild("g2_4", "Group 2 item 4"d);
515             TreeItem tree3 = tree.items.newChild("group3", "Group 3"d);
516             tree3.newChild("g3_1", "Group 3 item 1"d);
517             tree3.newChild("g3_2", "Group 3 item 2"d);
518             TreeItem tree32 = tree3.newChild("g3_3", "Group 3 item 3"d);
519             tree3.newChild("g3_4", "Group 3 item 4"d);
520             tree32.newChild("group3_2_1", "Group 3 item 2 subitem 1"d);
521             tree32.newChild("group3_2_2", "Group 3 item 2 subitem 2"d);
522             tree32.newChild("group3_2_3", "Group 3 item 2 subitem 3"d);
523             tree32.newChild("group3_2_4", "Group 3 item 2 subitem 4"d);
524             tree32.newChild("group3_2_5", "Group 3 item 2 subitem 5"d);
525             tree3.newChild("g3_5", "Group 3 item 5"d);
526             tree3.newChild("g3_6", "Group 3 item 6"d);
527             gbtree.addChild(tree);
528             tree.items.selectItem(tree1);
529             // test adding new tree items
530             HorizontalLayout newTreeItem = new HorizontalLayout();
531             newTreeItem.layoutWidth = FILL_PARENT;
532             EditLine edNewTreeItem = new EditLine("newTreeItem", "new item"d);
533             edNewTreeItem.layoutWidth = FILL_PARENT;
534             Button btnAddItem = new Button("btnAddTreeItem", "Add"d);
535             Button btnRemoveItem = new Button("btnRemoveTreeItem", "Remove"d);
536             newTreeItem.addChild(edNewTreeItem);
537             newTreeItem.addChild(btnAddItem);
538             newTreeItem.addChild(btnRemoveItem);
539             btnAddItem.click = delegate(Widget source) {
540                 import std.random;
541                 dstring label = edNewTreeItem.text;
542                 string id = "item%d".format(uniform(1000000, 9999999, rndGen));
543                 TreeItem item = tree.items.selectedItem;
544                 if (item) {
545                     Log.d("Creating new tree item ", id, " ", label);
546                     TreeItem newItem = new TreeItem(id, label);
547                     item.addChild(newItem);
548                 }
549                 return true;
550             };
551             btnRemoveItem.click = delegate(Widget source) {
552                 TreeItem item = tree.items.selectedItem;
553                 if (item) {
554                     Log.d("Removing tree item ", item.id, " ", item.text);
555                     item.parent.removeChild(item);
556                 }
557                 return true;
558             };
559             gbtree.addChild(newTreeItem);
560             line4.addChild(gbtree);
561 
562             controls.addChild(line4);
563 
564             tabs.addTab(controls, "Controls"d);
565         }
566 
567         LinearLayout layout = new LinearLayout("tab1");
568 
569 
570         layout.addChild((new TextWidget()).textColor(0x00802000).text("Text widget 0"));
571         layout.addChild((new TextWidget()).textColor(0x40FF4000).text("Text widget"));
572         layout.addChild(new ProgressBarWidget().progress(300).animationInterval(50));
573         layout.addChild(new ProgressBarWidget().progress(-1).animationInterval(50));
574         layout.addChild((new Button("BTN1")).textResource("EXIT")); //.textColor(0x40FF4000)
575         layout.addChild(new TimerTest());
576 
577         static if (true) {
578 
579 
580         LinearLayout hlayout = new HorizontalLayout();
581         hlayout.layoutWidth(FILL_PARENT);
582         //hlayout.addChild((new Button()).text("<<")); //.textColor(0x40FF4000)
583         hlayout.addChild((new TextWidget()).text("Several").alignment(Align.Center));
584         hlayout.addChild((new ImageWidget()).drawableId("btn_radio").padding(Rect(5,5,5,5)).alignment(Align.Center));
585         hlayout.addChild((new TextWidget()).text("items").alignment(Align.Center));
586         hlayout.addChild((new ImageWidget()).drawableId("btn_check").padding(Rect(5,5,5,5)).alignment(Align.Center));
587         hlayout.addChild((new TextWidget()).text("in horizontal layout"));
588         hlayout.addChild((new ImageWidget()).drawableId("exit").padding(Rect(5,5,5,5)).alignment(Align.Center));
589         hlayout.addChild((new EditLine("editline", "Some text to edit"d)).popupMenu(editPopupItem).alignment(Align.Center).layoutWidth(FILL_PARENT));
590         hlayout.addChild((new EditLine("passwd", "Password"d)).passwordChar('*').popupMenu(editPopupItem).alignment(Align.Center).layoutWidth(FILL_PARENT));
591         //hlayout.addChild((new Button()).text(">>")); //.textColor(0x40FF4000)
592         hlayout.backgroundColor = 0x8080C0;
593         layout.addChild(hlayout);
594 
595         LinearLayout vlayoutgroup = new HorizontalLayout();
596         LinearLayout vlayout = new VerticalLayout();
597         vlayout.addChild((new TextWidget()).text("VLayout line 1").textColor(0x40FF4000)); //
598         vlayout.addChild((new TextWidget()).text("VLayout line 2").textColor(0x40FF8000));
599         vlayout.addChild((new TextWidget()).text("VLayout line 2").textColor(0x40008000));
600         vlayout.addChild(new RadioButton("rb1", "Radio button 1"d));
601         vlayout.addChild(new RadioButton("rb2", "Radio button 2"d));
602         vlayout.addChild(new RadioButton("rb3", "Radio button 3"d));
603         vlayout.layoutWidth(FILL_PARENT);
604         vlayoutgroup.addChild(vlayout);
605         vlayoutgroup.layoutWidth(FILL_PARENT);
606         ScrollBar vsb = new ScrollBar("vscroll", Orientation.Vertical);
607         vlayoutgroup.addChild(vsb);
608         layout.addChild(vlayoutgroup);
609 
610         ScrollBar sb = new ScrollBar("hscroll", Orientation.Horizontal);
611         layout.addChild(sb.layoutHeight(WRAP_CONTENT).layoutWidth(FILL_PARENT));
612 
613         layout.addChild((new CheckBox("BTN2", "Some checkbox"d)));
614         layout.addChild((new TextWidget()).textColor(0x40FF4000).text("Text widget"));
615         layout.addChild((new ImageWidget()).drawableId("exit").padding(Rect(5,5,5,5)));
616         layout.addChild((new TextWidget()).textColor(0xFF4000).text("Text widget2").padding(Rect(5,5,5,5)).margins(Rect(5,5,5,5)).backgroundColor(0xA0A0A0));
617         layout.addChild((new RadioButton("BTN3", "Some radio button"d)));
618         layout.addChild((new TextWidget(null, "Text widget3 with very long text"d)).textColor(0x004000));
619         layout.addChild(new VSpacer()); // vertical spacer to fill extra space
620 
621 
622         Widget w = parseML(q{
623             VerticalLayout {
624                 id: vlayout
625                 margins: Rect { left: 5; right: 3; top: 2; bottom: 4 }
626                 padding: Rect { 5, 4, 3, 2 } // same as Rect { left: 5; top: 4; right: 3; bottom: 2 }
627                 TextWidget {
628                     /* this widget can be accessed via id myLabel1
629                     e.g. w.childById!TextWidget("myLabel1")
630                     */
631                     id: myLabel1
632                     text: "Some text"; padding: 5
633                     enabled: false
634                 }
635                 TextWidget {
636                     id: myLabel2
637                     text: SOME_TEXT_RESOURCE_ID; margins: 5
638                     enabled: true
639                 }
640             }
641         });
642         Log.d("id=", w.id, " text=", w.text, " padding=", w.padding, " margins=", w.margins,
643               " lbl1.text=", w.childById!TextWidget("myLabel1").text,
644               " lbl1.enabled=", w.childById!TextWidget("myLabel1").enabled,
645               " lbl2.text=", w.childById!TextWidget("myLabel2").text
646               );
647         destroy(w);
648 
649         layout.childById("BTN1").click = delegate (Widget w) {
650             Log.d("onClick ", w.id);
651             //w.backgroundImageId = null;
652             //w.backgroundColor = 0xFF00FF;
653             w.textColor = 0xFF00FF;
654             w.styleId = STYLE_BUTTON_NOMARGINS;
655             return true;
656         };
657         layout.childById("BTN2").click = delegate (Widget w) { Log.d("onClick ", w.id); return true; };
658         layout.childById("BTN3").click = delegate (Widget w) { Log.d("onClick ", w.id); return true; };
659 
660         }
661 
662         layout.layoutHeight(FILL_PARENT).layoutWidth(FILL_PARENT);
663 
664         tabs.addTab(layout, "Misc"d);
665 
666         static if (true) {
667             // two long lists
668             // left one is list with widgets as items
669             // right one is list with string list adapter
670             HorizontalLayout longLists = new HorizontalLayout("tab2");
671             longLists.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
672 
673             ListWidget list = new ListWidget("list1", Orientation.Vertical);
674             list.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
675 
676             StringListAdapter stringList = new StringListAdapter();
677             WidgetListAdapter listAdapter = new WidgetListAdapter();
678             listAdapter.add((new TextWidget()).text("This is a list of widgets"d).styleId("LIST_ITEM"));
679             stringList.add("This is a list of strings from StringListAdapter"d);
680             stringList.add("If you type with your keyboard,"d);
681             stringList.add("then you can find the"d);
682             stringList.add("item in the list"d);
683             stringList.add("neat!"d);
684             for (int i = 1; i < 1000; i++) {
685                 dstring label = "List item "d ~ to!dstring(i);
686                 listAdapter.add((new TextWidget()).text("Widget list - "d ~ label).styleId("LIST_ITEM"));
687                 stringList.add("Simple string - "d ~ label);
688             }
689             list.ownAdapter = listAdapter;
690             listAdapter.resetItemState(0, State.Enabled);
691             listAdapter.resetItemState(5, State.Enabled);
692             listAdapter.resetItemState(7, State.Enabled);
693             listAdapter.resetItemState(12, State.Enabled);
694             assert(list.itemEnabled(5) == false);
695             assert(list.itemEnabled(6) == true);
696             list.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
697             list.selectItem(0);
698 
699             longLists.addChild(list);
700 
701             ListWidget list2 = new StringListWidget("list2");
702             list2.ownAdapter = stringList;
703             list2.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
704             list2.selectItem(0);
705             longLists.addChild(list2);
706 
707             VerticalLayout itemedit = new VerticalLayout();
708             itemedit.addChild(new TextWidget(null, "New item text:"d));
709             EditLine itemtext = new EditLine(null, "Text for new item"d);
710             itemedit.addChild(itemtext);
711             Button btn = new Button(null, "Add item"d);
712             itemedit.addChild(btn);
713             longLists.addChild(itemedit);
714             btn.click = delegate(Widget src)
715             {
716                 stringList.add(itemtext.text);
717                 listAdapter.add((new TextWidget()).text(itemtext.text).styleId("LIST_ITEM"));
718                 return true;
719             };
720             tabs.addTab(longLists, "TAB_LONG_LIST"c);
721         }
722 
723         {
724             LinearLayout layout3 = new VerticalLayout("tab3");
725             // 3 types of buttons: Button, ImageButton, ImageTextButton
726             layout3.addChild(new TextWidget(null, "Buttons in HorizontalLayout"d));
727             WidgetGroup buttons1 = new HorizontalLayout();
728             buttons1.addChild(new TextWidget(null, "Button widgets: "d));
729             buttons1.addChild((new Button("btn1", "Button"d)).tooltipText("Tooltip text for button"d));
730             buttons1.addChild((new Button("btn2", "Disabled Button"d)).enabled(false));
731             buttons1.addChild(new TextWidget(null, "ImageButton widgets: "d));
732             buttons1.addChild(new ImageButton("btn3", "text-plain"));
733             buttons1.addChild(new TextWidget(null, "disabled: "d));
734             buttons1.addChild((new ImageButton("btn4", "folder")).enabled(false));
735             layout3.addChild(buttons1);
736 
737             WidgetGroup buttons10 = new HorizontalLayout();
738             buttons10.addChild(new TextWidget(null, "ImageTextButton widgets: "d));
739             buttons10.addChild(new ImageTextButton("btn5", "text-plain", "Enabled"d));
740             buttons10.addChild((new ImageTextButton("btn6", "folder", "Disabled"d)).enabled(false));
741             buttons10.addChild(new TextWidget(null, "SwitchButton widgets: "d));
742             buttons10.addChild((new SwitchButton("SW1")).checked(true));
743             buttons10.addChild((new SwitchButton("SW2")).checked(false));
744             buttons10.addChild((new SwitchButton("SW3")).checked(true).enabled(false));
745             buttons10.addChild((new SwitchButton("SW4")).checked(false).enabled(false));
746             layout3.addChild(buttons10);
747 
748             WidgetGroup buttons11 = new HorizontalLayout();
749             buttons11.addChild(new TextWidget(null, "Construct buttons by action (Button, ImageButton, ImageTextButton): "d));
750             Action FILE_OPEN_ACTION = new Action(ACTION_FILE_OPEN, "MENU_FILE_OPEN"c, "document-open", KeyCode.KEY_O, KeyFlag.Control);
751             buttons11.addChild(new Button(FILE_OPEN_ACTION));
752             buttons11.addChild(new ImageButton(FILE_OPEN_ACTION));
753             buttons11.addChild(new ImageTextButton(FILE_OPEN_ACTION));
754             layout3.addChild(buttons11);
755 
756             WidgetGroup buttons12 = new HorizontalLayout();
757             buttons12.addChild(new TextWidget(null, "The same in disabled state: "d));
758             buttons12.addChild((new Button(FILE_OPEN_ACTION)).enabled(false));
759             buttons12.addChild((new ImageButton(FILE_OPEN_ACTION)).enabled(false));
760             buttons12.addChild((new ImageTextButton(FILE_OPEN_ACTION)).enabled(false));
761             layout3.addChild(buttons12);
762 
763             layout3.addChild(new VSpacer());
764             layout3.addChild(new TextWidget(null, "CheckBoxes in HorizontalLayout"d));
765             WidgetGroup buttons2 = new HorizontalLayout();
766             buttons2.addChild(new CheckBox("btn1", "CheckBox 1"d));
767             buttons2.addChild(new CheckBox("btn2", "CheckBox 2"d));
768             //buttons2.addChild(new ResizerWidget());
769             buttons2.addChild(new CheckBox("btn3", "CheckBox 3"d));
770             buttons2.addChild(new CheckBox("btn4", "CheckBox 4"d));
771             layout3.addChild(buttons2);
772 
773             layout3.addChild(new VSpacer());
774             layout3.addChild(new TextWidget(null, "RadioButtons in HorizontalLayout"d));
775             WidgetGroup buttons3 = new HorizontalLayout();
776             buttons3.addChild(new RadioButton("btn1", "RadioButton 1"d));
777             buttons3.addChild(new RadioButton("btn2", "RadioButton 2"d));
778             buttons3.addChild(new RadioButton("btn3", "RadioButton 3"d));
779             buttons3.addChild(new RadioButton("btn4", "RadioButton 4"d));
780             layout3.addChild(buttons3);
781 
782             layout3.addChild(new VSpacer());
783             layout3.addChild(new TextWidget(null, "ImageButtons HorizontalLayout"d));
784             WidgetGroup buttons4 = new HorizontalLayout();
785             buttons4.addChild(new ImageButton("btn1", "fileclose"));
786             buttons4.addChild(new ImageButton("btn2", "fileopen"));
787             buttons4.addChild(new ImageButton("btn3", "exit"));
788             layout3.addChild(buttons4);
789 
790             layout3.addChild(new VSpacer());
791             layout3.addChild(new TextWidget(null, "In vertical layouts:"d));
792             HorizontalLayout hlayout2 = new HorizontalLayout();
793             hlayout2.layoutHeight(FILL_PARENT); //layoutWidth(FILL_PARENT).
794 
795             buttons1 = new VerticalLayout();
796             buttons1.addChild(new TextWidget(null, "Buttons"d));
797             buttons1.addChild(new Button("btn1", "Button 1"d));
798             buttons1.addChild(new Button("btn2", "Button 2"d));
799             buttons1.addChild((new Button("btn3", "Button 3 - disabled"d)).enabled(false));
800             buttons1.addChild(new Button("btn4", "Button 4"d));
801             hlayout2.addChild(buttons1);
802             hlayout2.addChild(new HSpacer());
803 
804             buttons2 = new VerticalLayout();
805             buttons2.addChild(new TextWidget(null, "CheckBoxes"d));
806             buttons2.addChild(new CheckBox("btn1", "CheckBox 1"d));
807             buttons2.addChild(new CheckBox("btn2", "CheckBox 2"d));
808             buttons2.addChild(new CheckBox("btn3", "CheckBox 3"d));
809             buttons2.addChild(new CheckBox("btn4", "CheckBox 4"d));
810             hlayout2.addChild(buttons2);
811             hlayout2.addChild(new HSpacer());
812 
813             buttons3 = new VerticalLayout();
814             buttons3.addChild(new TextWidget(null, "RadioButtons"d));
815             buttons3.addChild(new RadioButton("btn1", "RadioButton 1"d));
816             buttons3.addChild(new RadioButton("btn2", "RadioButton 2"d));
817             //buttons3.addChild(new ResizerWidget());
818             buttons3.addChild(new RadioButton("btn3", "RadioButton 3"d));
819             buttons3.addChild(new RadioButton("btn4", "RadioButton 4"d));
820             hlayout2.addChild(buttons3);
821             hlayout2.addChild(new HSpacer());
822 
823             buttons4 = new VerticalLayout();
824             buttons4.addChild(new TextWidget(null, "ImageButtons"d));
825             buttons4.addChild(new ImageButton("btn1", "fileclose"));
826             buttons4.addChild(new ImageButton("btn2", "fileopen"));
827             buttons4.addChild(new ImageButton("btn3", "exit"));
828             hlayout2.addChild(buttons4);
829             hlayout2.addChild(new HSpacer());
830 
831             WidgetGroup buttons5 = new VerticalLayout();
832             buttons5.addChild(new TextWidget(null, "ImageTextButtons"d));
833             buttons5.addChild(new ImageTextButton("btn1", "fileclose", "Close"d));
834             buttons5.addChild(new ImageTextButton("btn2", "fileopen", "Open"d));
835             buttons5.addChild(new ImageTextButton("btn3", "exit", "Exit"d));
836             hlayout2.addChild(buttons5);
837 
838 
839             layout3.addChild(hlayout2);
840 
841             layout3.addChild(new VSpacer());
842             layout3.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
843             tabs.addTab(layout3, "TAB_BUTTONS"c);
844         }
845 
846         TableLayout table = new TableLayout("TABLE");
847         table.colCount = 2;
848         // headers
849         table.addChild((new TextWidget(null, "Parameter Name"d)).alignment(Align.Right | Align.VCenter));
850         table.addChild((new TextWidget(null, "Edit Box to edit parameter"d)).alignment(Align.Left | Align.VCenter));
851         // row 1
852         table.addChild((new TextWidget(null, "Parameter 1 name"d)).alignment(Align.Right | Align.VCenter));
853         table.addChild((new EditLine("edit1", "Text 1"d)).layoutWidth(FILL_PARENT));
854         // row 2
855         table.addChild((new TextWidget(null, "Parameter 2 name bla bla"d)).alignment(Align.Right | Align.VCenter));
856         table.addChild((new EditLine("edit2", "Some text for parameter 2"d)).layoutWidth(FILL_PARENT));
857         // row 3
858         table.addChild((new TextWidget(null, "Param 3 is disabled"d)).alignment(Align.Right | Align.VCenter).enabled(false));
859         table.addChild((new EditLine("edit3", "Parameter 3 value"d)).layoutWidth(FILL_PARENT).enabled(false));
860         // normal readonly combo box
861         ComboBox combo1 = new ComboBox("combo1", ["item value 1"d, "item value 2"d, "item value 3"d, "item value 4"d, "item value 5"d, "item value 6"d]);
862         table.addChild((new TextWidget(null, "Combo box param"d)).alignment(Align.Right | Align.VCenter));
863         combo1.selectedItemIndex = 3;
864         table.addChild(combo1).layoutWidth(FILL_PARENT);
865         // disabled readonly combo box
866         ComboBox combo2 = new ComboBox("combo2", ["item value 1"d, "item value 2"d, "item value 3"d]);
867         table.addChild((new TextWidget(null, "Disabled combo box"d)).alignment(Align.Right | Align.VCenter));
868         combo2.enabled = false;
869         combo2.selectedItemIndex = 0;
870         table.addChild(combo2).layoutWidth(FILL_PARENT);
871 
872         table.margins(Rect(2,2,2,2)).layoutWidth(FILL_PARENT);
873         tabs.addTab(table, "TAB_TABLE_LAYOUT"c);
874 
875         //tabs.addTab((new TextWidget()).id("tab5").textColor(0x00802000).text("Tab 5 contents"), "Tab 5"d);
876 
877         //==========================================================================
878         // create Editors test tab
879         VerticalLayout editors = new VerticalLayout("editors");
880 
881         // EditLine sample
882         editors.addChild(new TextWidget(null, "EditLine: Single line editor"d));
883         EditLine editLine = new EditLine("editline1", "Single line editor sample text");
884         editors.addChild(createEditorSettingsControl(editLine));
885         editors.addChild(editLine);
886         editLine.popupMenu = editPopupItem;
887 
888         // EditBox sample
889         editors.addChild(new TextWidget(null, "SourceEdit: multiline editor, for source code editing"d));
890 
891         SourceEdit editBox = new SourceEdit("editbox1");
892         editBox.text = q{#!/usr/bin/env rdmd
893 // Computes average line length for standard input.
894 import std.stdio;
895 
896 void main()
897 {
898     ulong lines = 0;
899     double sumLength = 0;
900     foreach (line; stdin.byLine())
901     {
902         ++lines;
903         sumLength += line.length;
904     }
905     writeln("Average line length: ",
906             lines ? sumLength / lines : 0);
907 }
908         }};
909         editors.addChild(createEditorSettingsControl(editBox));
910         editors.addChild(editBox);
911         editBox.popupMenu = editPopupItem;
912 
913         editors.addChild(new TextWidget(null, "EditBox: additional view for the same content (split view testing)"d));
914         SourceEdit editBox2 = new SourceEdit("editbox2");
915         editBox2.content = editBox.content; // view the same content as first editbox
916         editors.addChild(editBox2);
917         editors.layoutHeight(FILL_PARENT).layoutWidth(FILL_PARENT);
918 
919         tabs.addTab(editors, "TAB_EDITORS"c);
920 
921         //==========================================================================
922 
923         VerticalLayout gridContent = new VerticalLayout("GRID_CONTENT");
924         gridContent.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
925         HorizontalLayout gridSettings = new HorizontalLayout();
926         StringGridWidget grid = new StringGridWidget("GRID1");
927 
928         gridSettings.addChild((new CheckBox("fullColumnOnLeft", "fullColumnOnLeft"d)).checked(grid.fullColumnOnLeft).tooltipText("Extends scroll area to show full column at left when scrolled to rightmost column"d).addOnCheckChangeListener(delegate(Widget, bool checked) { grid.fullColumnOnLeft = checked; return true;}));
929         gridSettings.addChild((new CheckBox("fullRowOnTop", "fullRowOnTop"d)).checked(grid.fullRowOnTop).tooltipText("Extends scroll area to show full row at top when scrolled to end row"d).addOnCheckChangeListener(delegate(Widget, bool checked) { grid.fullRowOnTop = checked; return true;}));
930         gridContent.addChild(gridSettings);
931 
932         grid.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
933         grid.showColHeaders = true;
934         grid.showRowHeaders = true;
935         grid.resize(30, 50);
936         grid.fixedCols = 3;
937         grid.fixedRows = 2;
938         //grid.rowSelect = true; // testing full row selection
939         grid.multiSelect = true;
940         grid.selectCell(4, 6, false);
941         // create sample grid content
942         for (int y = 0; y < grid.rows; y++) {
943             for (int x = 0; x < grid.cols; x++) {
944                 grid.setCellText(x, y, "cell("d ~ to!dstring(x + 1) ~ ","d ~ to!dstring(y + 1) ~ ")"d);
945             }
946             grid.setRowTitle(y, to!dstring(y + 1));
947         }
948         for (int x = 0; x < grid.cols; x++) {
949             int col = x + 1;
950             dstring res;
951             int n1 = col / 26;
952             int n2 = col % 26;
953             if (n1)
954                 res ~= n1 + 'A';
955             res ~= n2 + 'A';
956             grid.setColTitle(x, res);
957         }
958         grid.autoFit();
959         gridContent.addChild(grid);
960         tabs.addTab(gridContent, "Grid"d);
961 
962         //==========================================================================
963         // Scroll view example
964         ScrollWidget scroll = new ScrollWidget("SCROLL1");
965         scroll.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
966         WidgetGroup scrollContent = new VerticalLayout("CONTENT");
967         scrollContent.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
968 
969         TableLayout table2 = new TableLayout("TABLE2");
970         table2.colCount = 2;
971         // headers
972         table2.addChild((new TextWidget(null, "Parameter Name"d)).alignment(Align.Right | Align.VCenter));
973         table2.addChild((new TextWidget(null, "Edit Box to edit parameter"d)).alignment(Align.Left | Align.VCenter));
974         // row 1
975         table2.addChild((new TextWidget(null, "Parameter 1 name"d)).alignment(Align.Right | Align.VCenter));
976         table2.addChild((new EditLine("edit1", "Text 1"d)).layoutWidth(FILL_PARENT));
977         // row 2
978         table2.addChild((new TextWidget(null, "Parameter 2 name bla bla"d)).alignment(Align.Right | Align.VCenter));
979         table2.addChild((new EditLine("edit2", "Some text for parameter 2 blah blah blah"d)).layoutWidth(FILL_PARENT));
980         // row 3
981         table2.addChild((new TextWidget(null, "Param 3"d)).alignment(Align.Right | Align.VCenter));
982         table2.addChild((new EditLine("edit3", "Parameter 3 value"d)).layoutWidth(FILL_PARENT));
983         // row 4
984         table2.addChild((new TextWidget(null, "Param 4"d)).alignment(Align.Right | Align.VCenter));
985         table2.addChild((new EditLine("edit3", "Parameter 4 value shdjksdfh hsjdfas hdjkf hdjsfk ah"d)).layoutWidth(FILL_PARENT));
986         // row 5
987         table2.addChild((new TextWidget(null, "Param 5 - edit text here - blah blah blah"d)).alignment(Align.Right | Align.VCenter));
988         table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT));
989         // row 6
990         table2.addChild((new TextWidget(null, "Param 6 - just to fill content widget (DISABLED)"d)).alignment(Align.Right | Align.VCenter).enabled(false));
991         table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT).enabled(false));
992         // row 7
993         table2.addChild((new TextWidget(null, "Param 7 - just to fill content widget (DISABLED)"d)).alignment(Align.Right | Align.VCenter).enabled(false));
994         table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT).enabled(false));
995         // row 8
996         table2.addChild((new TextWidget(null, "Param 8 - just to fill content widget"d)).alignment(Align.Right | Align.VCenter));
997         table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT));
998         table2.margins(Rect(10,10,10,10)).layoutWidth(FILL_PARENT);
999         scrollContent.addChild(table2);
1000 
1001         scrollContent.addChild(new TextWidget(null, "Now - some buttons"d));
1002         scrollContent.addChild(new ImageTextButton("btn1", "fileclose", "Close"d));
1003         scrollContent.addChild(new ImageTextButton("btn2", "fileopen", "Open"d));
1004         scrollContent.addChild(new TextWidget(null, "And checkboxes"d));
1005         scrollContent.addChild(new CheckBox("btn1", "CheckBox 1"d));
1006         scrollContent.addChild(new CheckBox("btn2", "CheckBox 2"d));
1007 
1008         scroll.contentWidget = scrollContent;
1009         tabs.addTab(scroll, "Scroll"d);
1010         //==========================================================================
1011         // tree view example
1012         TreeWidget tree = new TreeWidget("TREE1");
1013         tree.layoutWidth(WRAP_CONTENT).layoutHeight(FILL_PARENT);
1014         TreeItem tree1 = tree.items.newChild("group1", "Group 1"d, "document-open");
1015         tree1.newChild("g1_1", "Group 1 item 1"d);
1016         tree1.newChild("g1_2", "Group 1 item 2"d);
1017         tree1.newChild("g1_3", "Group 1 item 3"d);
1018         TreeItem tree2 = tree.items.newChild("group2", "Group 2"d, "document-save");
1019         tree2.newChild("g2_1", "Group 2 item 1"d, "edit-copy");
1020         tree2.newChild("g2_2", "Group 2 item 2"d, "edit-cut");
1021         tree2.newChild("g2_3", "Group 2 item 3"d, "edit-paste");
1022         tree2.newChild("g2_4", "Group 2 item 4"d);
1023         TreeItem tree3 = tree.items.newChild("group3", "Group 3"d);
1024         tree3.newChild("g3_1", "Group 3 item 1"d);
1025         tree3.newChild("g3_2", "Group 3 item 2"d);
1026         TreeItem tree32 = tree3.newChild("g3_3", "Group 3 item 3"d);
1027         tree3.newChild("g3_4", "Group 3 item 4"d);
1028         tree32.newChild("group3_2_1", "Group 3 item 2 subitem 1"d);
1029         tree32.newChild("group3_2_2", "Group 3 item 2 subitem 2"d);
1030         tree32.newChild("group3_2_3", "Group 3 item 2 subitem 3"d);
1031         tree32.newChild("group3_2_4", "Group 3 item 2 subitem 4"d);
1032         tree32.newChild("group3_2_5", "Group 3 item 2 subitem 5"d);
1033         tree3.newChild("g3_5", "Group 3 item 5"d);
1034         tree3.newChild("g3_6", "Group 3 item 6"d);
1035 
1036         LinearLayout treeLayout = new HorizontalLayout("TREE");
1037         LinearLayout treeControlledPanel = new VerticalLayout();
1038         treeLayout.layoutWidth = FILL_PARENT;
1039         treeControlledPanel.layoutWidth = FILL_PARENT;
1040         treeControlledPanel.layoutHeight = FILL_PARENT;
1041         TextWidget treeItemLabel = new TextWidget("TREE_ITEM_DESC");
1042         treeItemLabel.layoutWidth = FILL_PARENT;
1043         treeItemLabel.layoutHeight = FILL_PARENT;
1044         treeItemLabel.alignment = Align.Center;
1045         treeItemLabel.text = "Sample text"d;
1046         treeControlledPanel.addChild(treeItemLabel);
1047         treeLayout.addChild(tree);
1048         treeLayout.addChild(new ResizerWidget());
1049         treeLayout.addChild(treeControlledPanel);
1050 
1051         tree.selectionChange = delegate(TreeItems source, TreeItem selectedItem, bool activated) {
1052             dstring label = "Selected item: "d ~ toUTF32(selectedItem.id) ~ (activated ? " selected + activated"d : " selected"d);
1053             treeItemLabel.text = label;
1054         };
1055 
1056         tree.items.selectItem(tree.items.child(0));
1057 
1058         tabs.addTab(treeLayout, "Tree"d);
1059 
1060         tabs.addTab(new ChartsExample("charts"), "Charts"d);
1061         tabs.addTab((new SampleAnimationWidget("tab6")).layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT), "TAB_ANIMATION"c);
1062         tabs.addTab(new CanvasExample("canvas"), UIString.fromId("TAB_CANVAS"));
1063         tabs.addTab(new IconsExample("icons"), "Icons"d);
1064 
1065         static if (BACKEND_GUI && ENABLE_OPENGL)
1066         {
1067             tabs.addTab(new MyOpenglWidget(), "OpenGL"d);
1068         }
1069 
1070         //==========================================================================
1071 
1072         contentLayout.addChild(tabs);
1073         window.mainWidget = contentLayout;
1074 
1075         tabs.selectTab("controls");
1076     } else {
1077         window.mainWidget = (new Button()).text("sample button");
1078     }
1079     static if (BACKEND_GUI) {
1080         window.windowIcon = drawableCache.getImage("dlangui-logo1");
1081     }
1082     window.show();
1083     //window.windowCaption = "New Window Caption";
1084     // run message loop
1085 
1086     Log.i("HOME path: ", homePath);
1087     Log.i("APPDATA path: ", appDataPath(".dlangui"));
1088     Log.i("Root paths: ", getRootPaths);
1089 
1090     return Platform.instance.enterMessageLoop();
1091 }
1092 
1093 static if (ENABLE_OPENGL) {
1094 
1095     import bindbc.opengl;
1096 
1097     class MyOpenglWidget : VerticalLayout {
1098         this() {
1099             super("OpenGLView");
1100             layoutWidth = FILL_PARENT;
1101             layoutHeight = FILL_PARENT;
1102             alignment = Align.Center;
1103             // add some UI on top of OpenGL drawable
1104             Widget w = parseML(q{
1105                 VerticalLayout {
1106                     alignment: center
1107                     layoutWidth: fill; layoutHeight: fill
1108                     // background for window - tiled texture
1109                     backgroundImageId: "tx_fabric.tiled"
1110                     VerticalLayout {
1111                         // child widget - will draw using OpenGL here
1112                         id: glView
1113                         margins: 20
1114                         padding: 20
1115                         layoutWidth: fill; layoutHeight: fill
1116 
1117                         //backgroundColor: "#C0E0E070" // semitransparent yellow background
1118                         // red bold text with size = 150% of base style size and font face Arial
1119                         TextWidget { text: "Some controls to draw on top of OpenGL scene"; textColor: "red"; fontSize: 150%; fontWeight: 800; fontFace: "Arial" }
1120                         // arrange controls as form - table with two columns
1121                         TableLayout {
1122                             colCount: 2
1123                             TextWidget { text: "param 1" }
1124                             EditLine { id: edit1; text: "some text" }
1125                             TextWidget { text: "param 2" }
1126                             EditLine { id: edit2; text: "some text for param2" }
1127                             TextWidget { text: "some radio buttons" }
1128                             // arrange some radio buttons vertically
1129                             VerticalLayout {
1130                                 RadioButton { id: rb1; text: "Item 1" }
1131                                 RadioButton { id: rb2; text: "Item 2" }
1132                                 RadioButton { id: rb3; text: "Item 3" }
1133                             }
1134                             TextWidget { text: "and checkboxes" }
1135                             // arrange some checkboxes horizontally
1136                             HorizontalLayout {
1137                                 CheckBox { id: cb1; text: "checkbox 1" }
1138                                 CheckBox { id: cb2; text: "checkbox 2" }
1139                             }
1140                         }
1141                         VSpacer { layoutWeight: 10 }
1142                         HorizontalLayout {
1143                             Button { id: btnOk; text: "Ok" }
1144                             Button { id: btnCancel; text: "Cancel" }
1145                         }
1146                     }
1147                 }
1148             });
1149             // setting OpenGL background drawable for one of child widgets
1150             w.childById("glView").backgroundDrawable = DrawableRef(new OpenGLDrawable(&doDraw));
1151             addChild(w);
1152         }
1153 
1154         bool _oldApi;
1155 
1156         /// this is OpenGLDrawableDelegate implementation
1157         private void doDraw(Rect windowRect, Rect rc) {
1158             if (!openglEnabled) {
1159                 Log.v("GlGears: OpenGL is disabled");
1160                 return;
1161             }
1162             import dlangui.graphics.glsupport : glSupport;
1163             _oldApi = glSupport.legacyMode;
1164             if (_oldApi) {
1165                 drawUsingOldAPI(rc);
1166             } else {
1167                 drawUsingNewAPI(rc);
1168             }
1169         }
1170 
1171         /// Legacy API example (glBegin/glEnd)
1172         void drawUsingOldAPI(Rect rc) {
1173 	/*
1174             static bool _initCalled;
1175             if (!_initCalled) {
1176                 Log.d("GlGears: calling init()");
1177                 _initCalled = true;
1178                 glxgears_init();
1179             }
1180             glxgears_reshape(rc);
1181             glEnable(GL_LIGHTING);
1182             glEnable(GL_LIGHT0);
1183             glEnable(GL_DEPTH_TEST);
1184             glxgears_draw();
1185             glDisable(GL_LIGHTING);
1186             glDisable(GL_LIGHT0);
1187             glDisable(GL_DEPTH_TEST);
1188 	*/
1189         }
1190 
1191         /// New API example (OpenGL3+, shaders)
1192         void drawUsingNewAPI(Rect rc) {
1193             // TODO: put some sample code here
1194         }
1195         /// returns true is widget is being animated - need to call animate() and redraw
1196         @property override bool animating() { return true; }
1197         /// animates window; interval is time left from previous draw, in hnsecs (1/10000000 of second)
1198         override void animate(long interval) {
1199             if (_oldApi) {
1200                 // animate legacy API example
1201                 // rotate gears
1202                 angle += interval * 0.000002f;
1203             } else {
1204                 // TODO: animate new API example
1205             }
1206             invalidate();
1207         }
1208     }
1209 
1210     static __gshared GLfloat angle = 0.0;
1211 
1212 version (GLLegacyAPI) {
1213 
1214     // Sample project for old API: GlxGears
1215 
1216     import std.math;
1217     static __gshared GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 0.0;
1218     static __gshared GLint gear1, gear2, gear3;
1219     alias M_PI = std.math.PI;
1220 
1221     /*
1222  *
1223  *  Draw a gear wheel.  You'll probably want to call this function when
1224  *  building a display list since we do a lot of trig here.
1225  *
1226  *  Input:  inner_radius - radius of hole at center
1227  *          outer_radius - radius at center of teeth
1228  *          width - width of gear
1229  *          teeth - number of teeth
1230  *          tooth_depth - depth of tooth
1231  */
1232 
1233     static void
1234         gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width,
1235             GLint teeth, GLfloat tooth_depth)
1236     {
1237         GLint i;
1238         GLfloat r0, r1, r2;
1239         GLfloat angle, da;
1240         GLfloat u, v, len;
1241 
1242         r0 = inner_radius;
1243         r1 = outer_radius - tooth_depth / 2.0;
1244         r2 = outer_radius + tooth_depth / 2.0;
1245 
1246         da = 2.0 * M_PI / teeth / 4.0;
1247 
1248         glShadeModel(GL_FLAT);
1249 
1250         glNormal3f(0.0, 0.0, 1.0);
1251 
1252         /* draw front face */
1253         glBegin(GL_QUAD_STRIP);
1254         for (i = 0; i <= teeth; i++) {
1255             angle = i * 2.0 * M_PI / teeth;
1256             glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
1257             glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
1258             if (i < teeth) {
1259                 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
1260                 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
1261                     width * 0.5);
1262             }
1263         }
1264         glEnd();
1265 
1266         /* draw front sides of teeth */
1267         glBegin(GL_QUADS);
1268         da = 2.0 * M_PI / teeth / 4.0;
1269         for (i = 0; i < teeth; i++) {
1270             angle = i * 2.0 * M_PI / teeth;
1271 
1272             glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
1273             glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
1274             glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
1275                 width * 0.5);
1276             glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
1277                 width * 0.5);
1278         }
1279         glEnd();
1280 
1281         glNormal3f(0.0, 0.0, -1.0);
1282 
1283         /* draw back face */
1284         glBegin(GL_QUAD_STRIP);
1285         for (i = 0; i <= teeth; i++) {
1286             angle = i * 2.0 * M_PI / teeth;
1287             glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
1288             glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
1289             if (i < teeth) {
1290                 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
1291                     -width * 0.5);
1292                 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
1293             }
1294         }
1295         glEnd();
1296 
1297         /* draw back sides of teeth */
1298         glBegin(GL_QUADS);
1299         da = 2.0 * M_PI / teeth / 4.0;
1300         for (i = 0; i < teeth; i++) {
1301             angle = i * 2.0 * M_PI / teeth;
1302 
1303             glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
1304                 -width * 0.5);
1305             glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
1306                 -width * 0.5);
1307             glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
1308             glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
1309         }
1310         glEnd();
1311 
1312         /* draw outward faces of teeth */
1313         glBegin(GL_QUAD_STRIP);
1314         for (i = 0; i < teeth; i++) {
1315             angle = i * 2.0 * M_PI / teeth;
1316 
1317             glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
1318             glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
1319             u = r2 * cos(angle + da) - r1 * cos(angle);
1320             v = r2 * sin(angle + da) - r1 * sin(angle);
1321             len = sqrt(u * u + v * v);
1322             u /= len;
1323             v /= len;
1324             glNormal3f(v, -u, 0.0);
1325             glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
1326             glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
1327             glNormal3f(cos(angle), sin(angle), 0.0);
1328             glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
1329                 width * 0.5);
1330             glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
1331                 -width * 0.5);
1332             u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
1333             v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
1334             glNormal3f(v, -u, 0.0);
1335             glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
1336                 width * 0.5);
1337             glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
1338                 -width * 0.5);
1339             glNormal3f(cos(angle), sin(angle), 0.0);
1340         }
1341 
1342         glVertex3f(r1 * cos(0.0), r1 * sin(0.0), width * 0.5);
1343         glVertex3f(r1 * cos(0.0), r1 * sin(0.0), -width * 0.5);
1344 
1345         glEnd();
1346 
1347         glShadeModel(GL_SMOOTH);
1348 
1349         /* draw inside radius cylinder */
1350         glBegin(GL_QUAD_STRIP);
1351         for (i = 0; i <= teeth; i++) {
1352             angle = i * 2.0 * M_PI / teeth;
1353             glNormal3f(-cos(angle), -sin(angle), 0.0);
1354             glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
1355             glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
1356         }
1357         glEnd();
1358     }
1359 
1360 
1361     static void glxgears_draw()
1362     {
1363         glClear(/*GL_COLOR_BUFFER_BIT | */GL_DEPTH_BUFFER_BIT);
1364 
1365         glPushMatrix();
1366         glRotatef(view_rotx, 1.0, 0.0, 0.0);
1367         glRotatef(view_roty, 0.0, 1.0, 0.0);
1368         glRotatef(view_rotz, 0.0, 0.0, 1.0);
1369 
1370         glPushMatrix();
1371         glTranslatef(-3.0, -2.0, 0.0);
1372         glRotatef(angle, 0.0, 0.0, 1.0);
1373         glCallList(gear1);
1374         glPopMatrix();
1375 
1376         glPushMatrix();
1377         glTranslatef(3.1, -2.0, 0.0);
1378         glRotatef(-2.0 * angle - 9.0, 0.0, 0.0, 1.0);
1379         glCallList(gear2);
1380         glPopMatrix();
1381 
1382         glPushMatrix();
1383         glTranslatef(-3.1, 4.2, 0.0);
1384         glRotatef(-2.0 * angle - 25.0, 0.0, 0.0, 1.0);
1385         glCallList(gear3);
1386         glPopMatrix();
1387 
1388         glPopMatrix();
1389     }
1390 
1391 
1392     /* new window size or exposure */
1393     static void
1394         glxgears_reshape(Rect rc)
1395     {
1396         GLfloat h = cast(GLfloat) rc.height / cast(GLfloat) rc.width;
1397         glMatrixMode(GL_PROJECTION);
1398         glLoadIdentity();
1399         glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
1400         glMatrixMode(GL_MODELVIEW);
1401         glLoadIdentity();
1402         glTranslatef(0.0, 0.0, -40.0);
1403     }
1404 
1405 
1406     static void glxgears_init()
1407     {
1408         static GLfloat[4] pos = [ 5.0, 5.0, 10.0, 0.0 ];
1409         static GLfloat[4] red = [ 0.8, 0.1, 0.0, 1.0 ];
1410         static GLfloat[4] green = [ 0.0, 0.8, 0.2, 1.0 ];
1411         static GLfloat[4] blue = [ 0.2, 0.2, 1.0, 1.0 ];
1412 
1413         glLightfv(GL_LIGHT0, GL_POSITION, pos.ptr);
1414         glEnable(GL_CULL_FACE);
1415         glEnable(GL_LIGHTING);
1416         glEnable(GL_LIGHT0);
1417         glEnable(GL_DEPTH_TEST);
1418 
1419         /* make the gears */
1420         gear1 = glGenLists(1);
1421         glNewList(gear1, GL_COMPILE);
1422         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red.ptr);
1423         gear(1.0, 4.0, 1.0, 20, 0.7);
1424         glEndList();
1425 
1426         gear2 = glGenLists(1);
1427         glNewList(gear2, GL_COMPILE);
1428         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green.ptr);
1429         gear(0.5, 2.0, 2.0, 10, 0.7);
1430         glEndList();
1431 
1432         gear3 = glGenLists(1);
1433         glNewList(gear3, GL_COMPILE);
1434         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue.ptr);
1435         gear(1.3, 2.0, 0.5, 10, 0.7);
1436         glEndList();
1437 
1438         glEnable(GL_NORMALIZE);
1439     }
1440 
1441 }
1442 }