green_shoes 0.129.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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