tkar 0.63

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,95 @@
1
+ module Tkar
2
+ class Tkaroid
3
+ attr_accessor :shape, :id, :flags, :layer, :x, :y, :r, :params
4
+ # note r is in radians, unlike Tk
5
+ attr_accessor :newly_added
6
+ attr_reader :tag, :parts
7
+
8
+ def initialize
9
+ yield self if block_given?
10
+ @tag = Tkaroid.tag(@id)
11
+ @parts = []
12
+ end
13
+
14
+ def self.tag id
15
+ "tkar#{id}"
16
+ end
17
+
18
+ def draggable?
19
+ true ## get from flags
20
+ end
21
+
22
+ def drop_target?
23
+ true ## get from flags
24
+ end
25
+
26
+ HOVER_COLOR = 'SeaGreen1' ## user config
27
+ DRAG_COLOR = 'SeaGreen3' ## user config
28
+
29
+ module CarefulColorize
30
+ def colorize canvas, color
31
+ parts = canvas.find_withtag(tag)
32
+ parts.each do |part|
33
+ case part
34
+ when TkcImage
35
+ else
36
+ canvas.itemconfigure part, :fill => color
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ # note: call after update, or else color is lost
43
+ def colorize canvas, color
44
+ canvas.itemconfigure tag, :fill => color
45
+ rescue => ex
46
+ if ex.message =~ /unknown option "-fill"/
47
+ extend CarefulColorize
48
+ colorize canvas, color
49
+ else
50
+ raise
51
+ end
52
+ end
53
+
54
+ def drag_colorize canvas
55
+ colorize canvas, DRAG_COLOR
56
+ end
57
+
58
+ def hover_colorize canvas
59
+ colorize canvas, HOVER_COLOR
60
+ end
61
+
62
+ def decolorize canvas
63
+ colorize canvas, nil
64
+ update canvas
65
+ end
66
+
67
+ def update canvas, zoom=canvas.zoom
68
+ tags = [tag]
69
+ result = @newly_added
70
+
71
+ if @newly_added
72
+ @shape[self].each do |klass, coords, config|
73
+ coords.map! {|x| x*zoom} if zoom
74
+ coords << config
75
+ part = klass.new(canvas, *coords)
76
+ @parts << part
77
+ part.raise tag rescue nil
78
+ part.tags tags
79
+ end
80
+ @newly_added = false
81
+
82
+ else
83
+ i = 0
84
+ @shape[self].each do |klass, coords, config|
85
+ part = parts[i]; i += 1
86
+ coords.map! {|x| x*zoom} if zoom
87
+ part.coords(*coords)
88
+ part.configure(config)
89
+ end
90
+ end
91
+
92
+ return result
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,5 @@
1
+
2
+ module Tkar
3
+ VERSION = '0.63'
4
+ end
5
+
@@ -0,0 +1,383 @@
1
+ require 'tk'
2
+ require 'tkar/canvas'
3
+ require 'tkar/help-window'
4
+
5
+ module Tkar
6
+ class Window
7
+ attr_reader :canvas
8
+
9
+ HELP_ICON = TkPhotoImage.new(
10
+ :format => 'GIF',
11
+ #:file => 'sample/help.gif'
12
+ :data => %{
13
+ R0lGODlhCAAIAMIEAKK1zQAAAIQAAP//AP///////////////yH5BAEKAAcA\nLAAAAAAIAA
14
+ gAAAMQSBQcug6qON9kFtud6dNEAgA7\n
15
+ }
16
+ )
17
+
18
+ ## cmds to show grid, save image, make movie
19
+ ## preferences
20
+
21
+ def initialize root, opts
22
+ @flip = opts["flip"]
23
+
24
+ canvas_frame = TkFrame.new(root) do
25
+ pack 'fill'=>'both', 'expand'=>true
26
+ background "lightgray"
27
+ end
28
+ TkGrid.rowconfigure(canvas_frame, 0, 'weight'=>1, 'minsize'=>0)
29
+ TkGrid.columnconfigure(canvas_frame, 0, 'weight'=>1, 'minsize'=>0)
30
+
31
+ @canvas = canvas = Tkar::Canvas.new(canvas_frame) do
32
+ @bounds = [-3000, -3000, 3000, 3000]
33
+ background "gray"
34
+ relief "sunken"
35
+ width 100; height 100 ## user prefs?
36
+ configure :scrollregion => @bounds
37
+ xscrollincrement 1
38
+ yscrollincrement 1
39
+ grid(:in => canvas_frame,
40
+ :row => 0, :column => 0,
41
+ :rowspan => 1, :columnspan => 1, :sticky => :news)
42
+ @root = root
43
+ end
44
+
45
+ xscroll = TkScrollbar.new(canvas_frame) do
46
+ background 'darkgray'
47
+ width 10
48
+ relief "sunken"
49
+ command do |*args|
50
+ canvas.xview *args
51
+ end
52
+ grid(:in => canvas_frame,
53
+ :row => 1, :column => 0,
54
+ :rowspan => 1, :columnspan => 1, :sticky => :news)
55
+ orient 'horiz'
56
+ end
57
+
58
+ yscroll = TkScrollbar.new(canvas_frame) do
59
+ background 'darkgray'
60
+ width 10
61
+ relief "sunken"
62
+ command do |*args|
63
+ canvas.yview *args
64
+ end
65
+ grid(:in => canvas_frame,
66
+ :row => 0, :column => 1,
67
+ :rowspan => 1, :columnspan => 1, :sticky => :news)
68
+ orient 'vertical'
69
+ end
70
+
71
+ TkButton.new(canvas_frame) do
72
+ image HELP_ICON
73
+ compound :none
74
+ relief "flat"
75
+ command {HelpWindow.show}
76
+ grid(:in => canvas_frame,
77
+ :row => 1, :column => 1,
78
+ :rowspan => 1, :columnspan => 1, :sticky => :news)
79
+ end
80
+
81
+ canvas.xscrollcommand do |first, last|
82
+ xscroll.set(first, last)
83
+ end
84
+ canvas.yscrollcommand do |first, last|
85
+ yscroll.set(first, last)
86
+ end
87
+
88
+ root.bind "Key-Right" do
89
+ canvas.xview "scroll", 10, "units"
90
+ end
91
+
92
+ root.bind "Key-Left" do
93
+ canvas.xview "scroll", -10, "units"
94
+ end
95
+
96
+ root.bind "Key-Down" do
97
+ canvas.yview "scroll", 10, "units"
98
+ end
99
+
100
+ root.bind "Key-Up" do
101
+ canvas.yview "scroll", -10, "units"
102
+ end
103
+
104
+ root.bind "Alt-Key-Right" do
105
+ canvas.xview "scroll", 1, "units"
106
+ end
107
+
108
+ root.bind "Alt-Key-Left" do
109
+ canvas.xview "scroll", -1, "units"
110
+ end
111
+
112
+ root.bind "Alt-Key-Down" do
113
+ canvas.yview "scroll", 1, "units"
114
+ end
115
+
116
+ root.bind "Alt-Key-Up" do
117
+ canvas.yview "scroll", -1, "units"
118
+ end
119
+
120
+ root.bind "Control-Up" do
121
+ canvas.zoom_by 0.75
122
+ end
123
+
124
+ root.bind "Control-Down" do
125
+ canvas.zoom_by 1.5
126
+ end
127
+
128
+ root.bind "Key-h" do
129
+ HelpWindow.show
130
+ end
131
+
132
+ root.bind "Control-q" do
133
+ exit
134
+ end
135
+
136
+ at_exit do
137
+ message_out "quit"
138
+ end
139
+
140
+ drag_start = drag_dx = drag_dy = drag_tkaroid = click_tkaroid = nil
141
+ drop_target = nil
142
+ drag_timer = nil
143
+
144
+ drag_proc = proc do |x, y|
145
+ if drop_target
146
+ drop_target.decolorize(canvas)
147
+ drop_target = nil # we'll check below if it still is
148
+ end
149
+
150
+ tkaroid = canvas.current_tkaroid
151
+ if tkaroid and tkaroid.draggable?
152
+ drag_tkaroid = tkaroid
153
+ x0, y0 = drag_start
154
+
155
+ drag_dx = (x-x0)/canvas.zoom
156
+ drag_dy = (y-y0)/canvas.zoom
157
+ new_x, new_y = tkaroid.x + drag_dx, tkaroid.y + drag_dy
158
+ canvas.moveto(tkaroid.id, new_x, new_y)
159
+ msg_x, msg_y = new_x, new_y
160
+ if @flip
161
+ msg_y = -msg_y
162
+ end
163
+ message_out "drag #{tkaroid.id} #{msg_x} #{msg_y}"
164
+ tkaroid.update(canvas)
165
+ tkaroid.drag_colorize(canvas)
166
+ drag_start = [x, y]
167
+
168
+ cx = canvas.canvasx(x)
169
+ cy = canvas.canvasy(y)
170
+ closest = canvas.find_closest(cx, cy, 0, "all") # start at top
171
+ tags = closest.map {|prim|prim.tag}.flatten
172
+ if tags.include? "current"
173
+ closest = canvas.find_closest(cx, cy, 0, "current")
174
+ tags = closest.map {|prim|prim.tag}.flatten
175
+ end
176
+
177
+ unless tags.include? "current"
178
+ tkar_id = tags.grep(/^tkar\d+$/).first[/\d+/].to_i
179
+ tgt = canvas.get_object tkar_id
180
+ if tgt.drop_target? and tgt != drag_tkaroid
181
+ drop_target = tgt
182
+ drop_target.hover_colorize(canvas)
183
+ end
184
+ end
185
+ end
186
+ end
187
+
188
+ drag_outside = proc do |x, y|
189
+ drag_timer = TkTimer.new(50) do
190
+ # scroll canvas if out of window if needed
191
+ width = canvas.current_width
192
+ height = canvas.current_height
193
+ units = 10
194
+ xsi = canvas.cget("xscrollincrement")*units
195
+ ysi = canvas.cget("yscrollincrement")*units
196
+
197
+ if x < 0
198
+ canvas.xview "scroll", -units, "units"
199
+ x = -xsi
200
+ if drag_start
201
+ x0,y0 = drag_start
202
+ drag_start = [x0 + xsi, y0]
203
+ end
204
+ end
205
+
206
+ if x > width
207
+ canvas.xview "scroll", units, "units"
208
+ x = width + xsi
209
+ if drag_start
210
+ x0,y0 = drag_start
211
+ drag_start = [x0 - xsi, y0]
212
+ end
213
+ end
214
+
215
+ if y < 0
216
+ canvas.yview "scroll", -units, "units"
217
+ y = -ysi
218
+ if drag_start
219
+ x0,y0 = drag_start
220
+ drag_start = [x0, y0 + ysi]
221
+ end
222
+ end
223
+
224
+ if y > height
225
+ canvas.yview "scroll", units, "units"
226
+ y = height + ysi
227
+ if drag_start
228
+ x0,y0 = drag_start
229
+ drag_start = [x0, y0 - ysi]
230
+ end
231
+ end
232
+
233
+ drag_proc[x,y] if drag_start
234
+ end
235
+ drag_timer.start
236
+ end
237
+
238
+ canvas.bind('B1-Motion', '%x %y') do |x, y|
239
+ if drag_timer
240
+ drag_timer.stop
241
+ drag_timer = nil
242
+ end
243
+
244
+ if x >= 0 and x <= canvas.current_width and
245
+ y >= 0 and y <= canvas.current_height
246
+ drag_proc[x,y] if drag_start
247
+ else
248
+ drag_outside[x,y] if drag_start
249
+ end
250
+ end
251
+
252
+ canvas.bind('B1-ButtonRelease', '%x %y') do |x, y|
253
+ if drag_timer
254
+ drag_timer.stop
255
+ drag_timer = nil
256
+ end
257
+
258
+ if drag_tkaroid
259
+ drag_tkaroid.hover_colorize(canvas)
260
+
261
+ if drop_target
262
+ message_out "drop #{drag_tkaroid.id} #{drop_target.id}"
263
+ drop_target.decolorize(canvas)
264
+ else
265
+ message_out "drop #{drag_tkaroid.id}"
266
+ end
267
+
268
+ # if mouse is now over a NEW object
269
+ # leave the old object
270
+ # enter the new object
271
+ # (since Tk doesn't seem to generate leave and enter events)
272
+ cx = canvas.canvasx(x)
273
+ cy = canvas.canvasy(y)
274
+ closest = canvas.find_closest(cx, cy, 0, "all") # start at top
275
+ tags = closest.map {|prim|prim.tag}.flatten
276
+ tkar_id = tags.grep(/^tkar\d+$/).first[/\d+/].to_i
277
+ top_tkaroid = canvas.get_object tkar_id
278
+ if top_tkaroid != drag_tkaroid
279
+ drag_tkaroid.update(canvas)
280
+ top_tkaroid.hover_colorize(canvas) if top_tkaroid
281
+ end
282
+
283
+ elsif click_tkaroid
284
+ click_tkaroid.hover_colorize(canvas)
285
+ end
286
+
287
+ drag_start = drag_dx = drag_dy = drag_tkaroid = drop_target = nil
288
+ click_tkaroid = nil
289
+ end
290
+
291
+ zoom_start = nil
292
+ canvas.bind('2', '%x %y') {|x,y| zoom_start = y}
293
+ canvas.bind('B2-Motion', '%x %y') {|x,y|
294
+ canvas.zoom_by(1 + (y - zoom_start)/500.0)
295
+ zoom_start = y
296
+ }
297
+
298
+ canvas.bind('3', '%x %y') {|x,y| canvas.scan_mark(x,y)}
299
+ canvas.bind('B3-Motion', '%x %y') {|x,y| canvas.scan_dragto(x,y,1)}
300
+ canvas.bind('Control-B3-Motion', '%x %y') {|x,y|
301
+ canvas.scan_dragto(x,y,3)}
302
+
303
+ small_zoom = 0.95
304
+ large_zoom = 0.75
305
+
306
+ canvas.bind('4') {canvas.zoom_by small_zoom}
307
+ canvas.bind('5') {canvas.zoom_by 1/small_zoom}
308
+
309
+ # For X windows:
310
+ canvas.bind('Control-4') {canvas.zoom_by large_zoom}
311
+ canvas.bind('Control-5') {canvas.zoom_by 1/large_zoom}
312
+
313
+ # For MSWindows:
314
+ root.bind('MouseWheel', "%D") { |delta|
315
+ canvas.zoom_by small_zoom**(delta/120)
316
+ }
317
+ root.bind('Control-MouseWheel', "%D") { |delta|
318
+ canvas.zoom_by large_zoom**(delta/120)
319
+ }
320
+
321
+ canvas.bind('Control-1', '%x %y') do |x, y|
322
+ tkaroid = canvas.current_tkaroid
323
+ if tkaroid
324
+ # handled in itembind('Control-1')
325
+ else
326
+ canvas.follow(nil)
327
+ canvas.view_at_screen(x,y)
328
+ end
329
+ end
330
+
331
+ canvas.itembind('all', '1', '%x %y') do |x, y|
332
+ tkaroid = canvas.current_tkaroid
333
+ if tkaroid
334
+ if tkaroid.draggable?
335
+ drag_start = [x, y]
336
+ tkaroid.drag_colorize(canvas)
337
+ end
338
+ message_out "click #{tkaroid.id}"
339
+ end
340
+ click_tkaroid = tkaroid
341
+ end
342
+
343
+ canvas.itembind('all', 'Control-1', '%x %y') do |x, y|
344
+ tkaroid = canvas.current_tkaroid
345
+ if tkaroid
346
+ if tkaroid.id == canvas.follow_id
347
+ canvas.follow(nil)
348
+ else
349
+ canvas.follow(tkaroid.id)
350
+ canvas.view_followed_obj
351
+ end
352
+ end
353
+ end
354
+
355
+ canvas.itembind('all', 'Double-1') do
356
+ tkaroid = canvas.current_tkaroid
357
+ message_out "doubleclick #{tkaroid.id}" if tkaroid
358
+ end
359
+
360
+ canvas.itembind('all', 'Any-Enter') do
361
+ unless drag_start
362
+ tkaroid = canvas.current_tkaroid
363
+ if tkaroid
364
+ tkaroid.hover_colorize(canvas)
365
+ end
366
+ end
367
+ end
368
+
369
+ canvas.itembind('all', 'Any-Leave') do
370
+ tkaroid = canvas.current_tkaroid
371
+ tkaroid.decolorize(canvas) if tkaroid
372
+ end
373
+ end
374
+
375
+ def message_out msg
376
+ @message_out[msg] if @message_out
377
+ end
378
+
379
+ def def_message_out(&bl)
380
+ @message_out = bl
381
+ end
382
+ end
383
+ end