green_shoes 0.129.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/LICENSE +21 -0
  2. data/README.md +104 -0
  3. data/README.rdoc +23 -0
  4. data/README_old.md +132 -0
  5. data/lib/ext/bloops.rb +1 -0
  6. data/lib/ext/bloops/bloops.so +0 -0
  7. data/lib/ext/bloops/libportaudio-2.dll +0 -0
  8. data/lib/ext/bloops/songs/1901_by_Aanand_Prasad.rb +478 -0
  9. data/lib/ext/bloops/songs/bloopsaphone_theme_song_by_why.rb +31 -0
  10. data/lib/ext/bloops/songs/feepogram.rb +67 -0
  11. data/lib/ext/bloops/songs/simpsons_theme_song_by_why.rb +14 -0
  12. data/lib/ext/chipmunk.rb +34 -0
  13. data/lib/ext/chipmunk/chipmunk.so +0 -0
  14. data/lib/ext/projector.rb +1 -0
  15. data/lib/ext/projector/matrix3d.rb +73 -0
  16. data/lib/ext/projector/projector.rb +306 -0
  17. data/lib/green_shoes.rb +45 -0
  18. data/lib/shoes/anim.rb +19 -0
  19. data/lib/shoes/app.rb +591 -0
  20. data/lib/shoes/basic.rb +242 -0
  21. data/lib/shoes/colors.rb +150 -0
  22. data/lib/shoes/download.rb +26 -0
  23. data/lib/shoes/help.rb +171 -0
  24. data/lib/shoes/helper_methods.rb +308 -0
  25. data/lib/shoes/main.rb +99 -0
  26. data/lib/shoes/manual.rb +6 -0
  27. data/lib/shoes/mask.rb +29 -0
  28. data/lib/shoes/projector.rb +42 -0
  29. data/lib/shoes/ruby.rb +73 -0
  30. data/lib/shoes/slot.rb +68 -0
  31. data/lib/shoes/text.rb +44 -0
  32. data/lib/shoes/url.rb +14 -0
  33. data/lib/shoes/widget.rb +18 -0
  34. data/static/Coolvetica.ttf +0 -0
  35. data/static/Lacuna.ttf +0 -0
  36. data/static/downloading.png +0 -0
  37. data/static/gshoes-heading-icon.png +0 -0
  38. data/static/gshoes-icon.png +0 -0
  39. data/static/man-app.png +0 -0
  40. data/static/man-builds.png +0 -0
  41. data/static/man-builds1.png +0 -0
  42. data/static/man-editor-notepad.png +0 -0
  43. data/static/man-editor-osx.png +0 -0
  44. data/static/man-ele-background.png +0 -0
  45. data/static/man-ele-border.png +0 -0
  46. data/static/man-ele-button.png +0 -0
  47. data/static/man-ele-check.png +0 -0
  48. data/static/man-ele-editbox.png +0 -0
  49. data/static/man-ele-editline.png +0 -0
  50. data/static/man-ele-image.png +0 -0
  51. data/static/man-ele-listbox.png +0 -0
  52. data/static/man-ele-progress.png +0 -0
  53. data/static/man-ele-radio.png +0 -0
  54. data/static/man-ele-shape.png +0 -0
  55. data/static/man-ele-textblock.png +0 -0
  56. data/static/man-ele-video.png +0 -0
  57. data/static/man-intro-dmg.png +0 -0
  58. data/static/man-intro-exe.png +0 -0
  59. data/static/man-look-tiger.png +0 -0
  60. data/static/man-look-ubuntu.png +0 -0
  61. data/static/man-look-vista.png +0 -0
  62. data/static/man-run-osx.png +0 -0
  63. data/static/man-run-vista.png +0 -0
  64. data/static/man-run-xp.png +0 -0
  65. data/static/man-shot1.png +0 -0
  66. data/static/manual-en.txt +3523 -0
  67. data/static/manual-ja.txt +2825 -0
  68. data/static/shoes-manual-apps.png +0 -0
  69. metadata +146 -0
@@ -0,0 +1,45 @@
1
+ require 'tmpdir'
2
+ require 'pathname'
3
+ require 'cairo'
4
+ require 'pango'
5
+ require 'gdk_pixbuf2'
6
+ require 'gtk2'
7
+
8
+ STDOUT.sync = true
9
+
10
+ Types = module Shoes; self end
11
+
12
+ module Shoes
13
+ DIR = Pathname.new(__FILE__).realpath.dirname.to_s
14
+ TMP_PNG_FILE = File.join(Dir.tmpdir, '__green_shoes_temporary_file__')
15
+ HAND = Gdk::Cursor.new(Gdk::Cursor::HAND1)
16
+ ARROW = Gdk::Cursor.new(Gdk::Cursor::ARROW)
17
+ FONTS = Gtk::Invisible.new.pango_context.families.map(&:name).sort
18
+ LINK_DEFAULT = "<span underline='single' underline_color='#06E' foreground='#06E' weight='normal'>"
19
+ LINKHOVER_DEFAULT = "<span underline='single' underline_color='#039' foreground='#039' weight='normal'>"
20
+ ROTATE = [Gdk::Pixbuf::ROTATE_NONE, Gdk::Pixbuf::ROTATE_CLOCKWISE, Gdk::Pixbuf::ROTATE_UPSIDEDOWN, Gdk::Pixbuf::ROTATE_COUNTERCLOCKWISE]
21
+ end
22
+
23
+ class Object
24
+ remove_const :Shoes
25
+ end
26
+
27
+ require_relative 'shoes/ruby'
28
+ require_relative 'shoes/helper_methods'
29
+ require_relative 'shoes/colors'
30
+ require_relative 'shoes/basic'
31
+ require_relative 'shoes/main'
32
+ require_relative 'shoes/app'
33
+ require_relative 'shoes/anim'
34
+ require_relative 'shoes/slot'
35
+ require_relative 'shoes/text'
36
+ require_relative 'shoes/mask'
37
+ require_relative 'shoes/widget'
38
+ require_relative 'shoes/url'
39
+ require_relative 'shoes/projector'
40
+ require_relative 'shoes/download'
41
+ require_relative 'shoes/manual'
42
+
43
+ autoload :ChipMunk, File.join(Shoes::DIR, 'ext/chipmunk')
44
+ autoload :Bloops, File.join(Shoes::DIR, 'ext/bloops')
45
+ autoload :Projector, File.join(Shoes::DIR, 'ext/projector')
@@ -0,0 +1,19 @@
1
+ class Shoes
2
+ class Anim
3
+ def stop
4
+ @stop = true
5
+ end
6
+
7
+ def continue?
8
+ !@stop
9
+ end
10
+
11
+ def pause
12
+ @pause = !@pause
13
+ end
14
+
15
+ def pause?
16
+ @pause
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,591 @@
1
+ class Shoes
2
+ class App
3
+ include Types
4
+ include Mod2
5
+
6
+ def initialize args={}
7
+ args.each do |k, v|
8
+ instance_variable_set "@#{k}", v
9
+ end
10
+
11
+ App.class_eval do
12
+ attr_accessor *(args.keys - [:width, :height, :title])
13
+ end
14
+
15
+ init_app_vars
16
+ @canvas, @win, @swin, @top_slot = nil
17
+ @cslot = (@app ||= self)
18
+ @width_pre, @height_pre = @width, @height
19
+ @link_style, @linkhover_style = LINK_DEFAULT, LINKHOVER_DEFAULT
20
+ @context_angle = @pixbuf_rotate = 0
21
+ end
22
+
23
+ attr_accessor :cslot, :cmask, :top_slot, :contents, :canvas, :app, :mccs, :mrcs, :mmcs,
24
+ :mhcs, :mlcs, :shcs, :mcs, :win, :swin, :width_pre, :height_pre, :order, :dics
25
+ attr_writer :mouse_button, :mouse_pos
26
+ attr_reader :link_style, :linkhover_style
27
+
28
+ def visit url
29
+ $urls.each do |k, v|
30
+ clear{init_app_vars; v.call self, $1} if k =~ url
31
+ end
32
+ end
33
+
34
+ def stack args={}, &blk
35
+ args[:app] = self
36
+ Stack.new slot_attributes(args), &blk
37
+ end
38
+
39
+ def flow args={}, &blk
40
+ args[:app] = self
41
+ Flow.new slot_attributes(args), &blk
42
+ end
43
+
44
+ def mask &blk
45
+ Mask.new(self, &blk).tap{|m| @mcs << m}
46
+ end
47
+
48
+ def clear &blk
49
+ mcs.each &:clear
50
+ @top_slot.clear &blk
51
+ end
52
+
53
+ def style klass, args={}
54
+ if klass == Shoes::Link
55
+ @link_style = LINK_DEFAULT
56
+ @link_style.sub!('single', 'none') if args[:underline] == false
57
+ @link_style.sub!("foreground='#06E'", "foreground='#{args[:stroke]}'") if args[:stroke]
58
+ @link_style.sub!('>', " background='#{args[:fill]}'>") if args[:fill]
59
+ @link_style.sub!('normal', "#{args[:weight]}") if args[:weight]
60
+ elsif klass == Shoes::LinkHover
61
+ @linkhover_style = LINKHOVER_DEFAULT
62
+ @linkhover_style.sub!('single', 'none') if args[:underline] == false
63
+ @linkhover_style.sub!("foreground='#039'", "foreground='#{args[:stroke]}'") if args[:stroke]
64
+ @linkhover_style.sub!('>', " background='#{args[:fill]}'>") if args[:fill]
65
+ @linkhover_style.sub!('normal', "#{args[:weight]}") if args[:weight]
66
+ end
67
+ end
68
+
69
+ def textblock klass, font_size, *msg
70
+ args = msg.last.class == Hash ? msg.pop : {}
71
+ args = basic_attributes args
72
+ msg = msg.map{|s| s == self ? s.to_s.gsub('<', '[').gsub('>', ']') : s}
73
+ args[:markup] = msg.map(&:to_s).join
74
+ attr_list, dummy_text = Pango.parse_markup args[:markup].gsub('\u0026', '@')
75
+ dummy_attr_list, text = Pango.parse_markup args[:markup]
76
+ text = text.gsub('\u0026', '&')
77
+ args[:size] ||= font_size
78
+ args[:font] ||= (@font_family or 'sans')
79
+ args[:align] ||= 'left'
80
+ line_height = args[:size] * 2
81
+
82
+ args[:links] = make_link_index(msg) unless args[:links]
83
+
84
+ if !(args[:left].zero? and args[:top].zero?) and (args[:width].zero? or args[:height].zero?)
85
+ args[:nocontrol], args[:width], args[:height] = true, self.width, self.height
86
+ layout_control = false
87
+ else
88
+ layout_control = true
89
+ end
90
+
91
+ if args[:create_real] or !layout_control
92
+ surface = Cairo::ImageSurface.new Cairo::FORMAT_ARGB32, args[:width], args[:height]
93
+ context = Cairo::Context.new surface
94
+ layout = context.create_pango_layout
95
+ layout.width = args[:width] * Pango::SCALE
96
+ layout.wrap = Pango::WRAP_WORD
97
+ layout.spacing = 5 * Pango::SCALE
98
+ layout.text = text
99
+ layout.alignment = eval "Pango::ALIGN_#{args[:align].upcase}"
100
+ fd = Pango::FontDescription.new
101
+ fd.family = args[:font]
102
+ fd.size = args[:size] * Pango::SCALE
103
+ layout.font_description = fd
104
+ layout.attributes = attr_list
105
+ context.show_pango_layout layout
106
+ context.show_page
107
+
108
+ make_link_pos args[:links], layout, line_height
109
+
110
+ args[:height] = layout.line_count * line_height
111
+ img = create_tmp_png surface
112
+ @canvas.put img, args[:left], args[:top]
113
+ img.show_now
114
+ args[:real], args[:noorder] = img, layout_control
115
+ else
116
+ args[:real] = false
117
+ end
118
+
119
+ args[:app] = self
120
+ klass.new args
121
+ end
122
+
123
+ def banner *msg; textblock Banner, 48, *msg; end
124
+ def title *msg; textblock Title, 34, *msg; end
125
+ def subtitle *msg; textblock Subtitle, 26, *msg; end
126
+ def tagline *msg; textblock Tagline, 18, *msg; end
127
+ def caption *msg; textblock Caption, 14, *msg; end
128
+ def para *msg; textblock Para, 12, *msg; end
129
+ def inscription *msg; textblock Para, 10, *msg; end
130
+
131
+ def image name, args={}
132
+ args = basic_attributes args
133
+ if name =~ /^(http|https):\/\//
134
+ tmpname = File.join(Dir.tmpdir, "__green_shoes_#{Time.now.to_f}.png")
135
+ d = download name, save: tmpname
136
+ img = Gtk::Image.new File.join(DIR, '../static/downloading.png')
137
+ downloading = true
138
+ else
139
+ img = Gtk::Image.new name
140
+ downloading = false
141
+ end
142
+ if (!args[:width].zero? or !args[:height].zero?) and !downloading
143
+ w, h = imagesize(name)
144
+ args[:width] = w if args[:width].zero?
145
+ args[:height] = w if args[:height].zero?
146
+ img = Gtk::Image.new img.pixbuf.scale(args[:width], args[:height])
147
+ end
148
+ @canvas.put img, args[:left], args[:top]
149
+ img.show_now
150
+ args[:real], args[:app] = img, self
151
+ Image.new(args).tap{|s| @dics.push([s, d, tmpname]) if downloading}
152
+ end
153
+
154
+ def imagesize name
155
+ Gtk::Image.new(name).size_request
156
+ end
157
+
158
+ def button name, args={}, &blk
159
+ args = basic_attributes args
160
+ b = Gtk::Button.new name
161
+ b.set_size_request args[:width], args[:height] if args[:width] > 0 and args[:height] > 0
162
+ b.signal_connect "clicked", &blk if blk
163
+ @canvas.put b, args[:left], args[:top]
164
+ b.show_now
165
+ args[:real], args[:text], args[:app] = b, name, self
166
+ Button.new args
167
+ end
168
+
169
+ def edit_line args={}
170
+ args = basic_attributes args
171
+ args[:width] = 200 if args[:width].zero?
172
+ el = Gtk::Entry.new
173
+ el.text = args[:text].to_s
174
+ el.width_chars = args[:width] / 6
175
+ el.signal_connect "changed" do
176
+ yield el
177
+ end if block_given?
178
+ @canvas.put el, args[:left], args[:top]
179
+ el.show_now
180
+ args[:real], args[:app] = el, self
181
+ EditLine.new args
182
+ end
183
+
184
+ def edit_box args={}
185
+ args = basic_attributes args
186
+ args[:width] = 200 if args[:width].zero?
187
+ args[:height] = 200 if args[:height].zero?
188
+ tv = Gtk::TextView.new
189
+ tv.wrap_mode = Gtk::TextTag::WRAP_WORD
190
+ tv.buffer.text = args[:text].to_s
191
+
192
+ eb = Gtk::ScrolledWindow.new
193
+ eb.set_size_request args[:width], args[:height]
194
+ eb.set_policy Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC
195
+ eb.set_shadow_type Gtk::SHADOW_IN
196
+ eb.add tv
197
+
198
+ tv.buffer.signal_connect "changed" do
199
+ yield tv.buffer
200
+ end if block_given?
201
+
202
+ @canvas.put eb, args[:left], args[:top]
203
+ eb.show_now
204
+ args[:real], args[:app], args[:textview] = eb, self, tv
205
+ EditBox.new args
206
+ end
207
+
208
+ def list_box args={}, &blk
209
+ args = basic_attributes args
210
+ args[:width] = 200 if args[:width].zero?
211
+ cb = Gtk::ComboBox.new
212
+ args[:items] ||= []
213
+ args[:items].each{|item| cb.append_text item.to_s}
214
+ cb.active = args[:items].index(args[:choose]) if args[:choose]
215
+ cb.signal_connect("changed") do
216
+ blk.call args[:items][cb.active]
217
+ end if blk
218
+ @canvas.put cb, args[:left], args[:top]
219
+ cb.show_now
220
+ args[:real], args[:app] = cb, self
221
+ ListBox.new args
222
+ end
223
+
224
+ def animate n=10, repaint=true, &blk
225
+ n, i = 1000 / n, 0
226
+ a = Anim.new
227
+ GLib::Timeout.add n do
228
+ blk[i = a.pause? ? i : i+1]
229
+ Shoes.repaint_all_by_order self if repaint
230
+ a.continue?
231
+ end
232
+ a
233
+ end
234
+
235
+ def every n=1, &blk
236
+ animate 1.0/n, &blk
237
+ end
238
+
239
+ def timer n=1, &blk
240
+ GLib::Timeout.add 1000*n do
241
+ blk.call
242
+ Shoes.repaint_all_by_order self
243
+ false
244
+ end
245
+ end
246
+
247
+ def motion &blk
248
+ @mmcs << blk
249
+ end
250
+
251
+ def keypress &blk
252
+ win.set_events Gdk::Event::BUTTON_PRESS_MASK | Gdk::Event::BUTTON_RELEASE_MASK | Gdk::Event::POINTER_MOTION_MASK | Gdk::Event::KEY_PRESS_MASK
253
+ win.signal_connect("key_press_event") do |w, e|
254
+ blk[Gdk::Keyval.to_name(e.keyval)]
255
+ end
256
+ end
257
+
258
+ def mouse
259
+ [@mouse_button, @mouse_pos[0], @mouse_pos[1]]
260
+ end
261
+
262
+ def oval *attrs
263
+ args = attrs.last.class == Hash ? attrs.pop : {}
264
+ case attrs.length
265
+ when 0, 1
266
+ when 2; args[:left], args[:top] = attrs
267
+ when 3; args[:left], args[:top], args[:radius] = attrs
268
+ else args[:left], args[:top], args[:width], args[:height] = attrs
269
+ end
270
+ args = basic_attributes args
271
+ args[:width].zero? ? (args[:width] = args[:radius] * 2) : (args[:radius] = args[:width]/2.0)
272
+ args[:height] = args[:width] if args[:height].zero?
273
+ args[:strokewidth] = ( args[:strokewidth] or strokewidth or 1 )
274
+
275
+ w, h, mx, my = set_rotate_angle(args)
276
+ my *= args[:width]/args[:height].to_f
277
+
278
+ surface = Cairo::ImageSurface.new Cairo::FORMAT_ARGB32, w, h
279
+ context = Cairo::Context.new surface
280
+ context.rotate @context_angle
281
+ context.scale(1, args[:height]/args[:width].to_f)
282
+
283
+ if pat = (args[:fill] or fill)
284
+ gp = gradient pat, args[:width], args[:height], args[:angle]
285
+ context.set_source gp
286
+ context.arc args[:radius]+mx, args[:radius]-my, args[:radius], 0, 2*Math::PI
287
+ context.fill
288
+ end
289
+
290
+ pat = (args[:stroke] or stroke)
291
+ gp = gradient pat, args[:width], args[:height], args[:angle]
292
+ context.set_source gp
293
+ context.set_line_width args[:strokewidth]
294
+ context.arc args[:radius]+mx, args[:radius]-my, args[:radius]-args[:strokewidth]/2.0, 0, 2*Math::PI
295
+ context.stroke
296
+
297
+ img = create_tmp_png surface
298
+ img = Gtk::Image.new img.pixbuf.rotate(ROTATE[@pixbuf_rotate])
299
+ @canvas.put img, args[:left], args[:top]
300
+ img.show_now
301
+ args[:real], args[:app] = img, self
302
+ Oval.new args
303
+ end
304
+
305
+ def rect *attrs
306
+ args = attrs.last.class == Hash ? attrs.pop : {}
307
+ case attrs.length
308
+ when 0, 1
309
+ when 2; args[:left], args[:top] = attrs
310
+ when 3; args[:left], args[:top], args[:width] = attrs
311
+ else args[:left], args[:top], args[:width], args[:height] = attrs
312
+ end
313
+ args[:height] = args[:width] unless args[:height]
314
+ sw = args[:strokewidth] = ( args[:strokewidth] or strokewidth or 1 )
315
+
316
+ w, h, mx, my = set_rotate_angle(args)
317
+
318
+ args = basic_attributes args
319
+ surface = Cairo::ImageSurface.new Cairo::FORMAT_ARGB32, w, h
320
+ context = Cairo::Context.new surface
321
+
322
+ context.rotate @context_angle
323
+
324
+ if pat = (args[:fill] or fill)
325
+ gp = gradient pat, args[:width], args[:height], args[:angle]
326
+ context.set_source gp
327
+ context.rounded_rectangle mx, -my, args[:width], args[:height], args[:curve]
328
+ context.fill
329
+ end
330
+
331
+ pat = (args[:stroke] or stroke)
332
+ gp = gradient pat, args[:width], args[:height], args[:angle]
333
+ context.set_source gp
334
+ context.set_line_width sw
335
+ context.rounded_rectangle sw/2.0+mx, sw/2.0-my, args[:width]-sw, args[:height]-sw, args[:curve]
336
+ context.stroke
337
+
338
+ img = create_tmp_png surface
339
+ img = Gtk::Image.new img.pixbuf.rotate(ROTATE[@pixbuf_rotate])
340
+ @canvas.put img, args[:left], args[:top]
341
+ img.show_now
342
+ args[:real], args[:app] = img, self
343
+ Rect.new args
344
+ end
345
+
346
+ def line *attrs
347
+ args = attrs.last.class == Hash ? attrs.pop : {}
348
+ case attrs.length
349
+ when 0, 1, 2
350
+ when 3; args[:sx], args[:sy], args[:ex] = attrs; args[:ey] = args[:ex]
351
+ else args[:sx], args[:sy], args[:ex], args[:ey] = attrs
352
+ end
353
+ sx, sy, ex, ey = args[:sx], args[:sy], args[:ex], args[:ey]
354
+ sw = args[:strokewidth] = ( args[:strokewidth] or strokewidth or 1 )
355
+ hsw = sw*0.5
356
+ args[:width], args[:height] = (sx - ex).abs, (sy - ey).abs
357
+
358
+ args = basic_attributes args
359
+ surface = Cairo::ImageSurface.new Cairo::FORMAT_ARGB32, args[:width]+sw, args[:height]+sw
360
+ context = Cairo::Context.new surface
361
+
362
+ pat = (args[:stroke] or stroke)
363
+ gp = gradient pat, args[:width], args[:height], args[:angle]
364
+ context.set_source gp
365
+ context.set_line_width args[:strokewidth]
366
+
367
+ if ((sx - ex) < 0 and (sy - ey) < 0) or ((sx - ex) > 0 and (sy - ey) > 0)
368
+ context.move_to hsw, hsw
369
+ context.line_to args[:width]+hsw, args[:height]+hsw
370
+ args[:left] = (sx - ex) < 0 ? sx - hsw : ex - hsw
371
+ args[:top] = (sy - ey) < 0 ? sy - hsw : ey - hsw
372
+ elsif ((sx - ex) < 0 and (sy - ey) > 0) or ((sx - ex) > 0 and (sy - ey) < 0)
373
+ context.move_to hsw, args[:height] + hsw
374
+ context.line_to args[:width]+hsw, hsw
375
+ args[:left] = (sx - ex) < 0 ? sx - hsw : ex - hsw
376
+ args[:top] = (sy - ey) < 0 ? sy - hsw : ey - hsw
377
+ elsif !(sx - ex).zero? and (sy - ey).zero?
378
+ context.move_to 0, hsw
379
+ context.line_to args[:width], hsw
380
+ args[:left] = (sx - ex) < 0 ? sx : ex
381
+ args[:top] = (sy - ey) < 0 ? sy - hsw : ey - hsw
382
+ elsif (sx - ex).zero? and !(sy - ey).zero?
383
+ context.move_to hsw, 0
384
+ context.line_to hsw, args[:height]
385
+ args[:left] = (sx - ex) < 0 ? sx - hsw : ex - hsw
386
+ args[:top] = (sy - ey) < 0 ? sy : ey
387
+ else
388
+ context.move_to 0, 0
389
+ context.line_to 0, 0
390
+ args[:left] = sw
391
+ args[:top] = sy
392
+ end
393
+
394
+ context.stroke
395
+ img = create_tmp_png surface
396
+ @canvas.put img, args[:left], args[:top]
397
+ img.show_now
398
+ args[:real], args[:app] = img, self
399
+ Line.new args
400
+ end
401
+
402
+ def shapebase klass, args
403
+ blk = args[:block]
404
+ args[:width] ||= 300
405
+ args[:height] ||= 300
406
+
407
+ w, h, mx, my = set_rotate_angle(args)
408
+
409
+ args = basic_attributes args
410
+ surface = Cairo::ImageSurface.new Cairo::FORMAT_ARGB32, w, h
411
+ context = Cairo::Context.new surface
412
+ args[:strokewidth] = ( args[:strokewidth] or strokewidth or 1 )
413
+ context.set_line_width args[:strokewidth]
414
+
415
+ context.rotate @context_angle
416
+
417
+ mk_path = proc do |pat|
418
+ gp = gradient pat, args[:width], args[:height], args[:angle]
419
+ context.set_source gp
420
+ context.move_to 0, 0
421
+ klass == Shoes::Star ? context.instance_eval{blk[self, mx, -my]} : context.instance_eval(&blk)
422
+ end
423
+
424
+ if pat = (args[:fill] or fill)
425
+ mk_path.call pat
426
+ context.fill
427
+ end
428
+
429
+ mk_path.call (args[:stroke] or stroke)
430
+ context.stroke
431
+
432
+ img = create_tmp_png surface
433
+ img = Gtk::Image.new img.pixbuf.rotate(ROTATE[@pixbuf_rotate])
434
+ @canvas.put img, args[:left], args[:top]
435
+ img.show_now
436
+ args[:real], args[:app] = img, self
437
+ klass.new args
438
+ end
439
+
440
+ def shape args, &blk
441
+ args[:block] = blk
442
+ shapebase Shape, args
443
+ end
444
+
445
+ def star *attrs
446
+ args = attrs.last.class == Hash ? attrs.pop : {}
447
+ case attrs.length
448
+ when 2; args[:left], args[:top] = attrs
449
+ when 5; args[:left], args[:top], args[:points], args[:outer], args[:inner] = attrs
450
+ else
451
+ end
452
+ args[:points] ||= 10; args[:outer] ||= 100.0; args[:inner] ||= 50.0
453
+ args[:width] = args[:height] = args[:outer]*2.0
454
+ x = y = outer = args[:outer]
455
+ points, inner = args[:points], args[:inner]
456
+
457
+ args[:block] = proc do |s, mx, my|
458
+ x += mx; y += my
459
+ s.move_to x, y + outer
460
+ (1..points*2).each do |i|
461
+ angle = i * Math::PI / points
462
+ r = (i % 2 == 0) ? outer : inner
463
+ s.line_to x + r * Math.sin(angle), y + r * Math.cos(angle)
464
+ end
465
+ end
466
+ shapebase Star, args
467
+ end
468
+
469
+ def rotate angle
470
+ @pixbuf_rotate, angle = angle.divmod(90)
471
+ @pixbuf_rotate %= 4
472
+ @context_angle = Math::PI * angle / 180
473
+ end
474
+
475
+ def rgb r, g, b, l=1.0
476
+ (r < 1 and g < 1 and b < 1) ? [r, g, b, l] : [r/255.0, g/255.0, b/255.0, l]
477
+ end
478
+
479
+ %w[fill stroke strokewidth].each do |name|
480
+ eval "def #{name} #{name}=nil; #{name} ? @#{name}=#{name} : @#{name} end"
481
+ end
482
+
483
+ def nostroke
484
+ strokewidth 0
485
+ end
486
+
487
+ def nofill
488
+ @fill = false
489
+ end
490
+
491
+ def gradient pat, w, h, angle=0
492
+ pat = tr_color pat
493
+ color = case pat
494
+ when Range; [tr_color(pat.first), tr_color(pat.last)]
495
+ when Array; [pat, pat]
496
+ when String
497
+ sp = Cairo::SurfacePattern.new Cairo::ImageSurface.from_png(pat)
498
+ return sp.set_extend(Cairo::Extend::REPEAT)
499
+ else
500
+ [black, black]
501
+ end
502
+ dx, dy = w*angle/180.0, h*angle/180.0
503
+ lp = Cairo::LinearPattern.new w*0.5-dx, dy, w*0.5+dx, h-dy
504
+ lp.add_color_stop_rgba 0, *color[0]
505
+ lp.add_color_stop_rgba 1, *color[1]
506
+ lp
507
+ end
508
+
509
+ def tr_color pat
510
+ if pat.is_a?(String) and pat[0] == '#'
511
+ color = pat[1..-1]
512
+ color = color.gsub(/(.)/){$1 + '0'} if color.length == 3
513
+ rgb *color.gsub(/(..)/).map{$1.hex}
514
+ else
515
+ pat
516
+ end
517
+ end
518
+
519
+ def background pat, args={}
520
+ args[:pattern] = pat
521
+ args = basic_attributes args
522
+
523
+ if args[:create_real] and !args[:height].zero?
524
+ surface = Cairo::ImageSurface.new Cairo::FORMAT_ARGB32, args[:width], args[:height]
525
+ context = Cairo::Context.new surface
526
+ context.rounded_rectangle 0, 0, args[:width], args[:height], args[:curve]
527
+ gp = gradient pat, args[:width], args[:height], args[:angle]
528
+ context.set_source gp
529
+ context.fill
530
+ img = create_tmp_png surface
531
+ @canvas.put img, args[:left], args[:top]
532
+ img.show_now
533
+ args[:real] = img
534
+ else
535
+ args[:real] = false
536
+ end
537
+
538
+ args[:app] = self
539
+ Background.new args
540
+ end
541
+
542
+ def border pat, args={}
543
+ args[:pattern] = pat
544
+ args = basic_attributes args
545
+ sw = args[:strokewidth] = ( args[:strokewidth] or strokewidth or 1 )
546
+
547
+ if args[:create_real] and !args[:height].zero?
548
+ surface = Cairo::ImageSurface.new Cairo::FORMAT_ARGB32, args[:width], args[:height]
549
+ context = Cairo::Context.new surface
550
+ gp = gradient pat, args[:width], args[:height], args[:angle]
551
+ context.set_source gp
552
+ context.set_line_width sw
553
+ context.rounded_rectangle sw/2.0, sw/2.0, args[:width]-sw, args[:height]-sw, args[:curve]
554
+ context.stroke
555
+
556
+ img = create_tmp_png surface
557
+ @canvas.put img, args[:left], args[:top]
558
+ img.show_now
559
+ args[:real] = img
560
+ else
561
+ args[:real] = false
562
+ end
563
+
564
+ args[:app] = self
565
+ Border.new args
566
+ end
567
+
568
+ def progress args={}
569
+ args = basic_attributes args
570
+ args[:width] = 150 if args[:width] < 150
571
+ pb = Gtk::ProgressBar.new
572
+ pb.text = ' ' * (args[:width] / 4 - 2)
573
+ @canvas.put pb, args[:left], args[:top]
574
+ pb.show_now
575
+ args[:real], args[:app], args[:noorder], args[:nocontrol] = pb, self, true, true
576
+ Progress.new args
577
+ end
578
+
579
+ def download name, args={}, &blk
580
+ Download.new name, args, &blk
581
+ end
582
+
583
+ def nolayout
584
+ @nolayout = true
585
+ end
586
+
587
+ def scroll_top
588
+ @swin.vadjustment.value
589
+ end
590
+ end
591
+ end