Restore full UpCast functionality, this time w/o the runtime cost.
[girtod.git] / example_gtk.d
1 import std.string;
2 import gtk = gtk2.gtk2;
3
4 int main(string argv[]) {
5    argv = gtk.init(argv);
6
7    auto window = new AppWin("Hello World!");
8    window.show_all();
9
10    gtk.main_();
11    return 0;
12 }
13
14 struct AppWin {
15    gtk.Window* window;
16    alias window this;
17    
18    this(string name) {
19       window = gtk.Window(gtk.WindowType.TOPLEVEL);
20       
21       set_title(name);
22       set_default_size(200, 128);
23
24       this.signal_connect!"delete-event"(&delete_cb);
25
26       auto vbox = gtk.VBox(0, 0);
27       auto disp = gtk.Entry();
28       vbox.add(disp);
29       vbox.set_child_packing(disp, 0, 1, 0, gtk.PackType.START);
30       
31       auto calc = new Calculator(disp);
32       
33       auto keypad = new Keypad(calc);
34       vbox.add(keypad);
35       
36       add(vbox);
37    }
38    
39    static extern (C) int delete_cb(gtk.Widget* this_, gtk.Event* event, void* user_data) nothrow {
40       gtk.exit(0);
41       return 0;
42    }
43
44 }
45
46 // A custom widget, composited out of many std buttons:
47
48 struct Keypad {
49    gtk.Table* table;
50    alias table this;
51    
52    // D lets us "magically" inherit the GTK object, but the "parent"
53    // widget has no idea about our pseudo-derived class. We keep a
54    // mapping from gtk-widgets to instances of this object; 
55    // This also allows us to "cheat" and eg. redirect child widget
56    // callbacks directly to this "class".
57    static Keypad*[gtk.Widget*] widget2this;
58    
59    Calculator* calc;
60    
61    enum colnum=5, rownum=4;
62    static immutable string[colnum][rownum] labels = [
63       ["7", "8", "9", "/", "C"],
64       ["4", "5", "6", "*", ""],
65       ["1", "2", "3", "-", ""],
66       ["0", ".", "=", "+", ""]
67    ];
68    
69    this(Calculator* calc) {
70       this.calc = calc;
71       
72       table = gtk.Table(colnum, rownum, 0);
73       
74       set_border_width(2);
75       set_row_spacings(4);
76       set_col_spacings(4);
77       
78       foreach (gtk.c_uint y, labelrow; labels)
79          foreach (gtk.c_uint x, label; labelrow) {
80             auto button = gtk.Button.new_with_label(label);
81             button.signal_connect!"button-press-event"(&bpress_cb, cast(void*)label);
82             widget2this[&button.widget] = &this;
83             attach_defaults(button, x, x+1, y, y+1);
84          }
85    }
86    
87    static extern (C) int bpress_cb(gtk.Widget* this_, gtk.Gdk2.EventButton* event,
88                                    void* user_data) nothrow {
89       gtk._dumpObj(event);
90       if (event.type==gtk.EventType.BUTTON_PRESS && event.button==1)
91          widget2this[this_].calc.key(cast(immutable char*)user_data);
92       return 0;
93    }
94 }
95
96 // The code sitting between the keypad and the display:
97 //
98 // NOTE: The "display" could be another custom widget, just like the
99 // keypad, but to keep this example simple we'll use a gtk.Entry
100 // directly.
101
102 struct Calculator {
103    double v = 0, pv = 0;
104    string display_string;
105    char* str0;
106    bool entering, haveprev;
107    char pop;
108    
109    gtk.Entry* disp;
110    
111    import std.conv;
112    
113    nothrow:
114    
115    void update() {
116       str0 = cast(char*)toStringz(display_string);
117       disp.set_text(str0);
118    }
119    
120    this(typeof(disp) disp) {
121       this.disp = disp;
122       disp.set_alignment(1);
123       
124       clear();
125       update();
126    }
127    
128    private static DT ntto(DT, ST)(ST d) {
129       DT s;
130       try
131          s = to!DT(d);
132       catch (Exception e)
133          {}
134       return s;
135    }
136    
137    void oper(char op) {
138       if (!entering && op==pop)
139          return;
140       entering = 0;
141       if (!haveprev) {
142          pv = v;
143          display_string = null;
144          v = 0;
145          haveprev = 1;
146          pop = op;
147          return;
148       }
149       switch (pop) {
150       case '+': v = pv + v; break;
151       case '-': v = pv - v; break;
152       case '*': v = pv * v; break;
153       case '/': v = pv / v; break;
154       case '=':             break;
155       default: assert(0);
156       }
157       display_string = ntto!string(v);
158       pv = v;
159       pop = op;
160    }
161    
162    void clear() { v = 0; pv = 0; display_string = null; haveprev = 0; entering = 0; pop = 0; }
163    
164    void key(immutable char* key) {
165       if (key[1]==0) {
166          char k = key[0];
167          switch (k) {
168          case '0': .. case '9': case '.':
169             if (entering) {
170                display_string ~= k;
171                try {
172                   v = to!double(display_string);
173                }
174                catch {
175                   display_string = display_string[0..$-1];
176                   v = ntto!double(display_string);
177                }
178             }
179             else {
180                display_string = (k=='.') ? "0." : key[0..1];
181                v = ntto!double(display_string);
182                entering = 1;
183             }
184             break;
185          case '+', '-', '*', '/', '=': oper(k); break;
186          case 'C': clear();
187          }
188          update();
189       }
190    }
191    
192 }