SR Technology WTK Repo
Check-in [1cf1e20494]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merging in changes from trunk
Timelines: family | ancestors | descendants | both | hypnotoad
Files: files | file ages | folders
SHA1:1cf1e204941eadabe22968d62c878523f9dddbcf
User & Date: seandeelywoods 2013-02-12 14:57:03
Context
2013-02-12
15:11
Moved wtk widgets back to the global namespace Leaf check-in: c4c1ad43fd user: seandeelywoods tags: hypnotoad
14:57
Merging in changes from trunk check-in: 1cf1e20494 user: seandeelywoods tags: hypnotoad
2013-02-05
02:15
Add logging methods. check-in: 04e9c7f911 user: gerald tags: trunk
2013-01-29
20:35
Re-implemented wtk in TclOO Thusfar the demo works check-in: f950e5cf61 user: seandeelywoods tags: hypnotoad
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to lib/wtk-base.tcl.

5
6
7
8
9
10
11

12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
40
41
42
43
44
45
46



47
48





49
50
51
52
53
54
55
# callbacks or other event bindings.
#
# Communication with the client is solely via the "fromclient" and "toclient" routines
# (the latter of which is setup in the ::wtk::init call).

package require TclOO
package require snit


source odie/index.tcl

###
# Add "option"
###

namespace eval ::wtk {
    variable widgets
    variable wobj
    variable _nextid -1
    variable _sender ""

    # Initialization and communication
................................................................................
        ::wtk::GridState _reset
        init $_sender
        return ""
    }

    proc toclient {cmd} {uplevel #0 $::wtk::_sender [list $cmd]}




    proc fromclient {cmd} {if {[lindex $cmd 0]=="EVENT"} {[getwidget [lindex $cmd 1]] wtk_event {*}[lrange $cmd 2 end]}}







    # 'Generic' widget object, which handles routines common to all widgets like
    # assigning it an id, keeping track of whether or not its been created, etc.
    # Purely for convenience, we also include some code here that manages widgets
    # that use -text or -textvariable, though not every widget will do so.

    ::odie::class ::wtk::Widget {







>






<







 







>
>
>
|
|
>
>
>
>
>







5
6
7
8
9
10
11
12
13
14
15
16
17
18

19
20
21
22
23
24
25
..
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# callbacks or other event bindings.
#
# Communication with the client is solely via the "fromclient" and "toclient" routines
# (the latter of which is setup in the ::wtk::init call).

package require TclOO
package require snit
package require log

source odie/index.tcl

###
# Add "option"
###

namespace eval ::wtk {
    variable widgets
    variable wobj
    variable _nextid -1
    variable _sender ""

    # Initialization and communication
................................................................................
        ::wtk::GridState _reset
        init $_sender
        return ""
    }

    proc toclient {cmd} {uplevel #0 $::wtk::_sender [list $cmd]}

    proc fromclient {cmd} {
        switch -exact -- [lindex $cmd 0] {
            "EVENT" {
                [getwidget [lindex $cmd 1]] wtk_event {*}[lrange $cmd 2 end]
            }
            "LOG" {
                ::log::log [lindex $cmd 1] [lrange $cmd 2 end]
            }
        }
    }

    # 'Generic' widget object, which handles routines common to all widgets like
    # assigning it an id, keeping track of whether or not its been created, etc.
    # Purely for convenience, we also include some code here that manages widgets
    # that use -text or -textvariable, though not every widget will do so.

    ::odie::class ::wtk::Widget {

Changes to widgets/wtk.js.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
..
63
64
65
66
67
68
69



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
..
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
...
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
var wtk = {
    
    widgets : new Array(),
    objs : new Array(),

		pause : function(ms) {
			ms += new Date().getTime();
			while (new Date() < ms){}
		},
  
    /*
     *   Initialize, and manage two AJAX connections to the server; one is used to send
     *   messages, and the other polling connection is used to receive messages.  These
     *   correspond to the routines in server.tcl, and could be equally well replaced by
     *   a different and/or more reliable communications channel.
     */
    init : function(sessionid) {
        wtk.sessionid = sessionid;
				wtk.conntype = "init";
        wtk.widgets['obj0'] = document.getElementById('obj0');
        

				if ('WebSocket' in window){
			 		/* WebSocket is supported. Proceed with server connection*/

					//create the uri to the ws controller on server
					var loc = window.location, ws_uri;
					if (loc.protocol === "https:") {
................................................................................
					console.log('Browser doesnt support WebSockets, using AJAX instead');
					wtk.poller;
				};


    },






    poller : function() {$.ajax({type:'GET', url:'wtkpoll.html?sessionid='+wtk.sessionid, dataType:'script', 
                                complete: function() {setTimeout(wtk.poller,100);},
                                error: function(jqXHR, textStatus, errorThrown) {
																	//alert("AJAX server connection interrupted\nPress OK to reconnect.");
																	console.log('AJAX server connecton interrupted '+textStatus+' '+errorThrown);
																	setTimeout(location.reload(1),200);}
                               });
												 },

    sendto : function(msg) { 
      if (wtk.conntype != "websocket") {
				$.get('wtkcb.html?sessionid='+wtk.sessionid, {cmd : msg});
			} else {
				wtk.connection.send(msg);
			}
		},

................................................................................
    CreateWidget : function(id,type,txt,attr) {
        var w = document.createElement(type);
        w.id = id;
        if(txt!='') {if (attr=='innerHTML') {w.innerHTML=txt;} else {w.value=txt;};}
        wtk.widgets[id] = w;
        return w;
    },
    
    /*
     * Buttons, labels and entries, oh my!
     */

  
		createMisc   : function(id, type, txt, attr) { 
			wtk.CreateWidget(id, type, txt, attr).onclick = function() {wtk.miscClicked(id);}; 
			},
		miscClicked : function(id) { wtk.sendto('EVENT '+id+' pressed'+' value '+wtk.widgets[id].value); },

    createButton  : function(id,txt) { wtk.CreateWidget(id,'button',txt,'innerHTML').onclick = function() {wtk.buttonClicked(id);}; },
    buttonClicked : function(id) { wtk.sendto('EVENT '+id+' pressed'); },

    createCombobox  : function(id,txt) { 
    	  wtk.CreateWidget(id,'select',txt,'value').onchange = function() {wtk.comboboxClicked(id);}; 
    },
    comboboxClicked : function(id) { wtk.sendto('EVENT '+id+' value '+wtk.widgets[id].value); },

    createLabel   : function(id, txt) { wtk.CreateWidget(id, 'span', txt,'innerHTML'); },

    createEntry   : function(id, txt) { wtk.CreateWidget(id, 'input', txt,'value').onkeyup = function() {wtk.entryChanged(id);}; },
    entryChanged  : function(id) { wtk.sendto('EVENT '+id+' value '+wtk.widgets[id].value); },

    createText   : function(id, txt) { wtk.CreateWidget(id, 'textarea', txt,'value').onkeyup = function() {wtk.textChanged(id);}; },
    textChanged  : function(id) { wtk.sendto('EVENT '+id+' value '+wtk.widgets[id].value); },
        
    createFrame   : function(id) { wtk.CreateWidget(id, 'div', '', '');},
    


    createCheckButton : function(id,txt) {
        var w = wtk.CreateWidget(id,'span', '', ''); 
        var c = w.appendChild(document.createElement('input'));
        var l = w.appendChild(document.createElement('span'));
        c.type = 'checkbox';
        c.onclick = function() {wtk.checkButtonClicked(id);};
        l.innerHTML = txt;
    },
    checkButtonClicked : function(id) { var ev; if (wtk.widgets[id].childNodes[0].checked==true) {ev='checked';} else {ev='unchecked';}; wtk.sendto('EVENT ' + id + ' ' + ev);},
    
    /*
     * Grid .
     */
    
    newGrid : function(parent,id) {
        var w = document.createElement('table');
        w.id = id;
        wtk.widgets[parent].appendChild(w);
    },
    
    /*
     * Canvas
     */
    
    Canvas : function(w,id) {
        var self = this;
        this.w = w;
        this.id = id;
        this.ctx = null;
        this.items = [];
        this.context = w.getContext("2d");
................................................................................
        this.drawtimer = null;
        this.ghostcanvas = null;
        this.gctx = null;
        w.width = 100; w.height = 100; w.style.width = '100px'; w.style.height = '100px';
        w.style.background = '#ffffff';
        w.style.position = 'relative';
        w.style.cursor = 'default';
        
        w.onmousedown = function(ev) {self.handleMouse(ev, 'mousedown');}
        w.onmousemove = function(ev) {self.handleMouse(ev, 'mousemove');}
        w.onmouseup = function(ev) {self.handleMouse(ev, 'mouseup');}
        w.ondrag = function(ev) {self.handleMouse(ev, 'drag');}
        
        this.createItem = function(cid, type, coords, opts) {
            var o = {'cid':cid,'type':type,'coords':coords,'opts':opts};
            this.items.push(o);
            this.scheduleDraw();
        }
        
        this.scheduleDraw = function() {if (this.drawtimer==null) {var self=this;this.drawtimer = setTimeout(function() {self.draw()}, 100)}}
        
        this.draw = function() {
            var self = this;
            this.drawtimer = null;
            var ctx = this.context;
            ctx.clearRect(0,0,this.w.width,this.w.height);
            ctx.beginPath();
            $.each(this.items, function(idx,i) {self.drawItem(ctx,i)});
        }
        
        this.drawItem = function(ctx,i,color) {
            ctx.beginPath();
            ctx.strokeStyle='#000000'; if ('strokeStyle' in i.opts && color!='black') {ctx.strokeStyle = i.opts['strokeStyle'];}
            ctx.fillStyle='#000000'; if ('fillStyle' in i.opts && color!='black') {ctx.fillStyle = i.opts['fillStyle'];}
            ctx.lineWidth = 3; if ('lineWidth' in i.opts) {ctx.lineWidth = i.opts['lineWidth'];}
            ctx.lineCap = 'round';
            if (i.type=="line") {ctx.moveTo(i.coords[0],i.coords[1]); for (var j=2;j<i.coords.length;j+=2) {ctx.lineTo(i.coords[j],i.coords[j+1]);};ctx.stroke();} 
            if (i.type=="rectangle") {ctx.fillRect(i.coords[0],i.coords[1],i.coords[2]-i.coords[0],i.coords[3]-i.coords[1]);}
        }
      
        this.itemAt = function(x,y) {
            /* use a 'ghost canvas' - see http://simonsarris.com/blog/140-canvas-moving-selectable-shapes */
            if (this.ghostcanvas==null) {this.ghostcanvas = document.createElement('canvas');this.gctx = null;}
            if (this.ghostcanvas.width!=this.w.width || this.ghostcanvas.height!=this.w.height) {
                this.ghostcanvas.width = this.w.width; this.ghostcanvas.height = this.w.height; this.gctx = null;
            }
            if (this.gctx==null) {this.gctx = this.ghostcanvas.getContext("2d");}
................................................................................
                var imageData = this.gctx.getImageData(x,y,1,1);
                if (imageData.data[3]>0) {
                    return this.items[i].cid;
                }
            }
            return '';
        }
      
        this.handleMouse = function(ev, action) {
            var itemhit = '';
            var x = ev.pageX-this.w.offsetLeft;
            var y = ev.pageY-this.w.offsetTop;
            if (action=="mousedown") {itemhit = this.itemAt(x,y);}
            wtk.sendto('EVENT '+this.id+' '+action+' '+x+' '+y+' '+ev.button+' '+itemhit);
        }
        
    },
        
    createCanvas : function(id) {
        var w = wtk.CreateWidget(id,'canvas', '', '');
        wtk.objs[id] = new wtk.Canvas(w,id);
    },
};


|







|










|







 







>
>
>

<
|








|







 







|




|
|
|






|
|










|

|



|







|



|





|



|







 







|




|





|

|








|






|


|







 







|







|

|





<
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
..
63
64
65
66
67
68
69
70
71
72
73

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
..
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
...
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236

var wtk = {

    widgets : new Array(),
    objs : new Array(),

		pause : function(ms) {
			ms += new Date().getTime();
			while (new Date() < ms){}
		},

    /*
     *   Initialize, and manage two AJAX connections to the server; one is used to send
     *   messages, and the other polling connection is used to receive messages.  These
     *   correspond to the routines in server.tcl, and could be equally well replaced by
     *   a different and/or more reliable communications channel.
     */
    init : function(sessionid) {
        wtk.sessionid = sessionid;
				wtk.conntype = "init";
        wtk.widgets['obj0'] = document.getElementById('obj0');


				if ('WebSocket' in window){
			 		/* WebSocket is supported. Proceed with server connection*/

					//create the uri to the ws controller on server
					var loc = window.location, ws_uri;
					if (loc.protocol === "https:") {
................................................................................
					console.log('Browser doesnt support WebSockets, using AJAX instead');
					wtk.poller;
				};


    },

    server_log :function(logLevel,msg) {
        wtk.sendto('LOG '+logLevel+' '+wtk.sessionid+' '+msg);
    },


    poller : function() {$.ajax({type:'GET', url:'wtkpoll.html?sessionid='+wtk.sessionid, dataType:'script',
                                complete: function() {setTimeout(wtk.poller,100);},
                                error: function(jqXHR, textStatus, errorThrown) {
																	//alert("AJAX server connection interrupted\nPress OK to reconnect.");
																	console.log('AJAX server connecton interrupted '+textStatus+' '+errorThrown);
																	setTimeout(location.reload(1),200);}
                               });
												 },

    sendto : function(msg) {
      if (wtk.conntype != "websocket") {
				$.get('wtkcb.html?sessionid='+wtk.sessionid, {cmd : msg});
			} else {
				wtk.connection.send(msg);
			}
		},

................................................................................
    CreateWidget : function(id,type,txt,attr) {
        var w = document.createElement(type);
        w.id = id;
        if(txt!='') {if (attr=='innerHTML') {w.innerHTML=txt;} else {w.value=txt;};}
        wtk.widgets[id] = w;
        return w;
    },

    /*
     * Buttons, labels and entries, oh my!
     */


		createMisc   : function(id, type, txt, attr) {
			wtk.CreateWidget(id, type, txt, attr).onclick = function() {wtk.miscClicked(id);};
			},
		miscClicked : function(id) { wtk.sendto('EVENT '+id+' pressed'+' value '+wtk.widgets[id].value); },

    createButton  : function(id,txt) { wtk.CreateWidget(id,'button',txt,'innerHTML').onclick = function() {wtk.buttonClicked(id);}; },
    buttonClicked : function(id) { wtk.sendto('EVENT '+id+' pressed'); },

    createCombobox  : function(id,txt) {
    	  wtk.CreateWidget(id,'select',txt,'value').onchange = function() {wtk.comboboxClicked(id);};
    },
    comboboxClicked : function(id) { wtk.sendto('EVENT '+id+' value '+wtk.widgets[id].value); },

    createLabel   : function(id, txt) { wtk.CreateWidget(id, 'span', txt,'innerHTML'); },

    createEntry   : function(id, txt) { wtk.CreateWidget(id, 'input', txt,'value').onkeyup = function() {wtk.entryChanged(id);}; },
    entryChanged  : function(id) { wtk.sendto('EVENT '+id+' value '+wtk.widgets[id].value); },

    createText   : function(id, txt) { wtk.CreateWidget(id, 'textarea', txt,'value').onkeyup = function() {wtk.textChanged(id);}; },
    textChanged  : function(id) { wtk.sendto('EVENT '+id+' value '+wtk.widgets[id].value); },

    createFrame   : function(id) { wtk.CreateWidget(id, 'div', '', '');},



    createCheckButton : function(id,txt) {
        var w = wtk.CreateWidget(id,'span', '', '');
        var c = w.appendChild(document.createElement('input'));
        var l = w.appendChild(document.createElement('span'));
        c.type = 'checkbox';
        c.onclick = function() {wtk.checkButtonClicked(id);};
        l.innerHTML = txt;
    },
    checkButtonClicked : function(id) { var ev; if (wtk.widgets[id].childNodes[0].checked==true) {ev='checked';} else {ev='unchecked';}; wtk.sendto('EVENT ' + id + ' ' + ev);},

    /*
     * Grid .
     */

    newGrid : function(parent,id) {
        var w = document.createElement('table');
        w.id = id;
        wtk.widgets[parent].appendChild(w);
    },

    /*
     * Canvas
     */

    Canvas : function(w,id) {
        var self = this;
        this.w = w;
        this.id = id;
        this.ctx = null;
        this.items = [];
        this.context = w.getContext("2d");
................................................................................
        this.drawtimer = null;
        this.ghostcanvas = null;
        this.gctx = null;
        w.width = 100; w.height = 100; w.style.width = '100px'; w.style.height = '100px';
        w.style.background = '#ffffff';
        w.style.position = 'relative';
        w.style.cursor = 'default';

        w.onmousedown = function(ev) {self.handleMouse(ev, 'mousedown');}
        w.onmousemove = function(ev) {self.handleMouse(ev, 'mousemove');}
        w.onmouseup = function(ev) {self.handleMouse(ev, 'mouseup');}
        w.ondrag = function(ev) {self.handleMouse(ev, 'drag');}

        this.createItem = function(cid, type, coords, opts) {
            var o = {'cid':cid,'type':type,'coords':coords,'opts':opts};
            this.items.push(o);
            this.scheduleDraw();
        }

        this.scheduleDraw = function() {if (this.drawtimer==null) {var self=this;this.drawtimer = setTimeout(function() {self.draw()}, 100)}}

        this.draw = function() {
            var self = this;
            this.drawtimer = null;
            var ctx = this.context;
            ctx.clearRect(0,0,this.w.width,this.w.height);
            ctx.beginPath();
            $.each(this.items, function(idx,i) {self.drawItem(ctx,i)});
        }

        this.drawItem = function(ctx,i,color) {
            ctx.beginPath();
            ctx.strokeStyle='#000000'; if ('strokeStyle' in i.opts && color!='black') {ctx.strokeStyle = i.opts['strokeStyle'];}
            ctx.fillStyle='#000000'; if ('fillStyle' in i.opts && color!='black') {ctx.fillStyle = i.opts['fillStyle'];}
            ctx.lineWidth = 3; if ('lineWidth' in i.opts) {ctx.lineWidth = i.opts['lineWidth'];}
            ctx.lineCap = 'round';
            if (i.type=="line") {ctx.moveTo(i.coords[0],i.coords[1]); for (var j=2;j<i.coords.length;j+=2) {ctx.lineTo(i.coords[j],i.coords[j+1]);};ctx.stroke();}
            if (i.type=="rectangle") {ctx.fillRect(i.coords[0],i.coords[1],i.coords[2]-i.coords[0],i.coords[3]-i.coords[1]);}
        }

        this.itemAt = function(x,y) {
            /* use a 'ghost canvas' - see http://simonsarris.com/blog/140-canvas-moving-selectable-shapes */
            if (this.ghostcanvas==null) {this.ghostcanvas = document.createElement('canvas');this.gctx = null;}
            if (this.ghostcanvas.width!=this.w.width || this.ghostcanvas.height!=this.w.height) {
                this.ghostcanvas.width = this.w.width; this.ghostcanvas.height = this.w.height; this.gctx = null;
            }
            if (this.gctx==null) {this.gctx = this.ghostcanvas.getContext("2d");}
................................................................................
                var imageData = this.gctx.getImageData(x,y,1,1);
                if (imageData.data[3]>0) {
                    return this.items[i].cid;
                }
            }
            return '';
        }

        this.handleMouse = function(ev, action) {
            var itemhit = '';
            var x = ev.pageX-this.w.offsetLeft;
            var y = ev.pageY-this.w.offsetTop;
            if (action=="mousedown") {itemhit = this.itemAt(x,y);}
            wtk.sendto('EVENT '+this.id+' '+action+' '+x+' '+y+' '+ev.button+' '+itemhit);
        }

    },

    createCanvas : function(id) {
        var w = wtk.CreateWidget(id,'canvas', '', '');
        wtk.objs[id] = new wtk.Canvas(w,id);
    },
};