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.
- data/LICENSE +21 -0
- data/README.md +104 -0
- data/README.rdoc +23 -0
- data/README_old.md +132 -0
- data/lib/ext/bloops.rb +1 -0
- data/lib/ext/bloops/bloops.so +0 -0
- data/lib/ext/bloops/libportaudio-2.dll +0 -0
- data/lib/ext/bloops/songs/1901_by_Aanand_Prasad.rb +478 -0
- data/lib/ext/bloops/songs/bloopsaphone_theme_song_by_why.rb +31 -0
- data/lib/ext/bloops/songs/feepogram.rb +67 -0
- data/lib/ext/bloops/songs/simpsons_theme_song_by_why.rb +14 -0
- data/lib/ext/chipmunk.rb +34 -0
- data/lib/ext/chipmunk/chipmunk.so +0 -0
- data/lib/ext/projector.rb +1 -0
- data/lib/ext/projector/matrix3d.rb +73 -0
- data/lib/ext/projector/projector.rb +306 -0
- data/lib/green_shoes.rb +45 -0
- data/lib/shoes/anim.rb +19 -0
- data/lib/shoes/app.rb +591 -0
- data/lib/shoes/basic.rb +242 -0
- data/lib/shoes/colors.rb +150 -0
- data/lib/shoes/download.rb +26 -0
- data/lib/shoes/help.rb +171 -0
- data/lib/shoes/helper_methods.rb +308 -0
- data/lib/shoes/main.rb +99 -0
- data/lib/shoes/manual.rb +6 -0
- data/lib/shoes/mask.rb +29 -0
- data/lib/shoes/projector.rb +42 -0
- data/lib/shoes/ruby.rb +73 -0
- data/lib/shoes/slot.rb +68 -0
- data/lib/shoes/text.rb +44 -0
- data/lib/shoes/url.rb +14 -0
- data/lib/shoes/widget.rb +18 -0
- data/static/Coolvetica.ttf +0 -0
- data/static/Lacuna.ttf +0 -0
- data/static/downloading.png +0 -0
- data/static/gshoes-heading-icon.png +0 -0
- data/static/gshoes-icon.png +0 -0
- data/static/man-app.png +0 -0
- data/static/man-builds.png +0 -0
- data/static/man-builds1.png +0 -0
- data/static/man-editor-notepad.png +0 -0
- data/static/man-editor-osx.png +0 -0
- data/static/man-ele-background.png +0 -0
- data/static/man-ele-border.png +0 -0
- data/static/man-ele-button.png +0 -0
- data/static/man-ele-check.png +0 -0
- data/static/man-ele-editbox.png +0 -0
- data/static/man-ele-editline.png +0 -0
- data/static/man-ele-image.png +0 -0
- data/static/man-ele-listbox.png +0 -0
- data/static/man-ele-progress.png +0 -0
- data/static/man-ele-radio.png +0 -0
- data/static/man-ele-shape.png +0 -0
- data/static/man-ele-textblock.png +0 -0
- data/static/man-ele-video.png +0 -0
- data/static/man-intro-dmg.png +0 -0
- data/static/man-intro-exe.png +0 -0
- data/static/man-look-tiger.png +0 -0
- data/static/man-look-ubuntu.png +0 -0
- data/static/man-look-vista.png +0 -0
- data/static/man-run-osx.png +0 -0
- data/static/man-run-vista.png +0 -0
- data/static/man-run-xp.png +0 -0
- data/static/man-shot1.png +0 -0
- data/static/manual-en.txt +3523 -0
- data/static/manual-ja.txt +2825 -0
- data/static/shoes-manual-apps.png +0 -0
- metadata +146 -0
data/lib/green_shoes.rb
ADDED
@@ -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')
|
data/lib/shoes/anim.rb
ADDED
data/lib/shoes/app.rb
ADDED
@@ -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
|