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