xiki 0.6.4 → 0.6.5
Sign up to get free protection for your applications and to get access to all the features.
- data/etc/command/copy_xiki_command_to.rb +15 -2
- data/etc/install/el4r_setup.sh +1 -1
- data/lib/xiki.rb +1 -1
- data/lib/xiki/console.rb +4 -2
- data/lib/xiki/files.rb +2 -0
- data/lib/xiki/launcher.rb +6 -4
- data/lib/xiki/menu.rb +177 -83
- data/lib/xiki/search.rb +1 -1
- data/lib/xiki/tree.rb +11 -1
- data/lib/xiki/trouble_shooting.rb +3 -3
- data/menu/firefox.rb +1 -1
- data/menu/piano.rb +1 -1
- data/menu/{foo.rb → sammiches.rb} +9 -12
- data/menu/shoes.menu +1482 -0
- data/menu/shoes.rb +16 -0
- data/xiki.gemspec +3 -6
- metadata +16 -46
@@ -2,7 +2,7 @@ class TroubleShooting
|
|
2
2
|
def self.keys
|
3
3
|
return if ! $el
|
4
4
|
|
5
|
-
$el.el4r_lisp_eval
|
5
|
+
$el.el4r_lisp_eval %`
|
6
6
|
(progn
|
7
7
|
(global-set-key (kbd "M-l") (lambda () (interactive)
|
8
8
|
"Load .emacs (reloading EmacsRuby)"
|
@@ -17,11 +17,11 @@ class TroubleShooting
|
|
17
17
|
(setq truncate-lines nil)
|
18
18
|
(end-of-buffer)
|
19
19
|
(re-search-backward "^ from ")
|
20
|
-
(re-search-backward "
|
20
|
+
(re-search-backward "^\\\\S-")
|
21
21
|
(recenter 0)
|
22
22
|
))
|
23
23
|
)
|
24
|
-
|
24
|
+
`
|
25
25
|
end
|
26
26
|
end
|
27
27
|
TroubleShooting.keys
|
data/menu/firefox.rb
CHANGED
@@ -244,7 +244,7 @@ class Firefox
|
|
244
244
|
|
245
245
|
# If brawser wasn't running, ask if they want to open it
|
246
246
|
|
247
|
-
raise "> Looks like
|
247
|
+
raise "> Looks like Firefox isn\'t open. Open it?\n@app/Firefox/\n\nOr, maybe the MozRepl Firefox extension isn't installed and on."
|
248
248
|
|
249
249
|
end
|
250
250
|
|
data/menu/piano.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
|
-
class
|
1
|
+
class Sammiches
|
2
2
|
def self.menu
|
3
3
|
"
|
4
|
-
-
|
4
|
+
- meat/
|
5
5
|
- ham/
|
6
6
|
- .buy/
|
7
|
-
-
|
7
|
+
- philly/
|
8
|
+
- .buy/
|
9
|
+
- veggie/
|
10
|
+
- cucumber/
|
11
|
+
- .buy/
|
12
|
+
- bark/
|
8
13
|
- .buy/
|
9
14
|
- .checkout/
|
10
15
|
- cash/
|
@@ -12,19 +17,11 @@ class Foo
|
|
12
17
|
"
|
13
18
|
end
|
14
19
|
def self.buy category, item
|
15
|
-
"-
|
20
|
+
"- adding to cart #{item} #{category}"
|
16
21
|
end
|
17
22
|
|
18
23
|
def self.checkout kind
|
19
24
|
"- checking out as #{kind}..."
|
20
25
|
end
|
21
26
|
|
22
|
-
def self.admin
|
23
|
-
"
|
24
|
-
- reports/
|
25
|
-
- .profit/
|
26
|
-
- .loss/
|
27
|
-
- .restart_server/
|
28
|
-
"
|
29
|
-
end
|
30
27
|
end
|
data/menu/shoes.menu
ADDED
@@ -0,0 +1,1482 @@
|
|
1
|
+
> Type some shoes code here
|
2
|
+
| Shoes.app { button "Hello World" }
|
3
|
+
- examples/
|
4
|
+
- button/
|
5
|
+
@shoes/
|
6
|
+
| Shoes.app :width=>104, :height=>40 do
|
7
|
+
| button("Click me!") { alert("Good job.") }
|
8
|
+
| end
|
9
|
+
- mask/
|
10
|
+
@shoes/
|
11
|
+
| Shoes.app do
|
12
|
+
| background black
|
13
|
+
|
|
14
|
+
| stack :top => 0.4, :left => 0.2 do
|
15
|
+
| @stripes = stack
|
16
|
+
|
|
17
|
+
| mask do
|
18
|
+
| title "Shoes", :weight => "bold", :size => 82
|
19
|
+
| end
|
20
|
+
| end
|
21
|
+
|
|
22
|
+
| animate 10 do
|
23
|
+
| @stripes.clear do
|
24
|
+
| 20.times do |i|
|
25
|
+
| strokewidth 4
|
26
|
+
| stroke rgb((0.0..0.5).rand, (0.0..1.0).rand, (0.0..0.3).rand)
|
27
|
+
| line 0, i * 5, 400, i * 8
|
28
|
+
| end
|
29
|
+
| end
|
30
|
+
| end
|
31
|
+
| end
|
32
|
+
- dialogs/
|
33
|
+
@shoes/
|
34
|
+
| Shoes.app :width => 300, :height => 150, :margin => 10 do
|
35
|
+
| def answer(v)
|
36
|
+
| @answer.replace v.inspect
|
37
|
+
| end
|
38
|
+
|
|
39
|
+
| button "Ask" do
|
40
|
+
| answer ask("What is your name?")
|
41
|
+
| end
|
42
|
+
| button "Confirm" do
|
43
|
+
| answer confirm("Would you like to proceed?")
|
44
|
+
| end
|
45
|
+
| button "Open File..." do
|
46
|
+
| answer ask_open_file
|
47
|
+
| end
|
48
|
+
| button "Save File..." do
|
49
|
+
| answer ask_save_file
|
50
|
+
| end
|
51
|
+
| button "Open Folder..." do
|
52
|
+
| answer ask_open_folder
|
53
|
+
| end
|
54
|
+
| button "Save Folder..." do
|
55
|
+
| answer ask_save_folder
|
56
|
+
| end
|
57
|
+
| button "Color" do
|
58
|
+
| answer ask_color("Pick a Color")
|
59
|
+
| end
|
60
|
+
|
|
61
|
+
| @answer = para "Answers appear here"
|
62
|
+
| end
|
63
|
+
- clock/
|
64
|
+
@shoes/
|
65
|
+
| #
|
66
|
+
| # Shoes Clock by Thomas Bell
|
67
|
+
| # posted to the Shoes mailing list on 04 Dec 2007
|
68
|
+
| #
|
69
|
+
| Shoes.app :height => 260, :width => 250 do
|
70
|
+
| @radius, @centerx, @centery = 90, 126, 140
|
71
|
+
| animate(8) do
|
72
|
+
| @time = Time.now
|
73
|
+
| clear do
|
74
|
+
| draw_background
|
75
|
+
| stack do
|
76
|
+
| background black
|
77
|
+
| para @time.strftime("%a"),
|
78
|
+
| span(@time.strftime(" %b %d, %Y "), :stroke => "#ccc"),
|
79
|
+
| strong(@time.strftime("%I:%M"), :stroke => white),
|
80
|
+
| @time.strftime(".%S"), :align => "center", :stroke => "#666",
|
81
|
+
| :margin => 4
|
82
|
+
| end
|
83
|
+
| clock_hand @time.sec + (@time.usec * 0.000001),2,30,red
|
84
|
+
| clock_hand @time.min + (@time.sec / 60.0),5
|
85
|
+
| clock_hand @time.hour + (@time.min / 60.0),8,6
|
86
|
+
| end
|
87
|
+
| end
|
88
|
+
| def draw_background
|
89
|
+
| background rgb(230, 240, 200)
|
90
|
+
|
|
91
|
+
| fill white
|
92
|
+
| stroke black
|
93
|
+
| strokewidth 4
|
94
|
+
| oval @centerx - 102, @centery - 102, 204, 204
|
95
|
+
|
|
96
|
+
| fill black
|
97
|
+
| nostroke
|
98
|
+
| oval @centerx - 5, @centery - 5, 10, 10
|
99
|
+
|
|
100
|
+
| stroke black
|
101
|
+
| strokewidth 1
|
102
|
+
| line(@centerx, @centery - 102, @centerx, @centery - 95)
|
103
|
+
| line(@centerx - 102, @centery, @centerx - 95, @centery)
|
104
|
+
| line(@centerx + 95, @centery, @centerx + 102, @centery)
|
105
|
+
| line(@centerx, @centery + 95, @centerx, @centery + 102)
|
106
|
+
| end
|
107
|
+
| def clock_hand(time, sw, unit=30, color=black)
|
108
|
+
| radius_local = unit == 30 ? @radius : @radius - 15
|
109
|
+
| _x = radius_local * Math.sin( time * Math::PI / unit )
|
110
|
+
| _y = radius_local * Math.cos( time * Math::PI / unit )
|
111
|
+
| stroke color
|
112
|
+
| strokewidth sw
|
113
|
+
| line(@centerx, @centery, @centerx + _x, @centery - _y)
|
114
|
+
| end
|
115
|
+
| end
|
116
|
+
- follow/
|
117
|
+
@shoes/
|
118
|
+
| trails = [[0, 0]] * 60
|
119
|
+
| Shoes.app :width => 200, :height => 200, :resizable => false do
|
120
|
+
| nostroke
|
121
|
+
| fill rgb(0x3, 0x1, 0x3, 0.6)
|
122
|
+
|
|
123
|
+
| # animation at 100 frames per second
|
124
|
+
| animate(60) do
|
125
|
+
| trails.shift
|
126
|
+
| trails << self.mouse[1, 2]
|
127
|
+
|
|
128
|
+
| clear do
|
129
|
+
| # change the background based on where the pointer is
|
130
|
+
| background rgb(
|
131
|
+
| 20 + (70 * (trails.last[0].to_f / self.width)).to_i,
|
132
|
+
| 20 + (70 * (trails.last[1].to_f / self.height)).to_i,
|
133
|
+
| 51)
|
134
|
+
|
|
135
|
+
| # draw circles progressively bigger
|
136
|
+
| trails.each_with_index do |(x, y), i|
|
137
|
+
| i += 1
|
138
|
+
| oval :left => x, :top => y, :radius => (i*0.5), :center => true
|
139
|
+
| end
|
140
|
+
| end
|
141
|
+
| end
|
142
|
+
|
|
143
|
+
| end
|
144
|
+
- tank spank/
|
145
|
+
@shoes/
|
146
|
+
| # Tankspank
|
147
|
+
| # kevin conner
|
148
|
+
| # connerk@gmail.com
|
149
|
+
| # version 3, 13 March 2008
|
150
|
+
| # this code is free, do what you like with it!
|
151
|
+
|
|
152
|
+
| $width, $height = 700, 500
|
153
|
+
| $camera_tightness = 0.1
|
154
|
+
|
|
155
|
+
| module Collisions
|
156
|
+
| def contains? x, y
|
157
|
+
| not (x < west or x > east or y < north or y > south)
|
158
|
+
| end
|
159
|
+
|
|
160
|
+
| def intersects? other
|
161
|
+
| not (other.east < west or other.west > east or
|
162
|
+
| other.south < north or other.north > south)
|
163
|
+
| end
|
164
|
+
| end
|
165
|
+
|
|
166
|
+
| class Building
|
167
|
+
| include Collisions
|
168
|
+
|
|
169
|
+
| attr_reader :west, :east, :north, :south
|
170
|
+
|
|
171
|
+
| def initialize(west, east, north, south)
|
172
|
+
| @west, @east, @north, @south = west, east, north, south
|
173
|
+
| @top, @bottom = 1.1 + rand(3) * 0.15, 1.0
|
174
|
+
|
|
175
|
+
| color = (1..3).collect { 0.2 + 0.4 * rand }
|
176
|
+
| color << 0.9
|
177
|
+
| @stroke = $app.rgb *color
|
178
|
+
| color[-1] = 0.3
|
179
|
+
| @fill = $app.rgb *color
|
180
|
+
| end
|
181
|
+
|
|
182
|
+
| def draw
|
183
|
+
| $app.stroke @stroke
|
184
|
+
| $app.fill @fill
|
185
|
+
| Opp.draw_opp_box(@west, @east, @north, @south, @top, @bottom)
|
186
|
+
| end
|
187
|
+
| end
|
188
|
+
|
|
189
|
+
| module Guidance
|
190
|
+
| def guidance_system x, y, dest_x, dest_y, angle
|
191
|
+
| vx, vy = dest_x - x, dest_y - y
|
192
|
+
| if vx.abs < 0.1 and vy.abs <= 0.1
|
193
|
+
| yield 0, 0
|
194
|
+
| else
|
195
|
+
| length = Math.sqrt(vx * vx + vy * vy)
|
196
|
+
| vx /= length
|
197
|
+
| vy /= length
|
198
|
+
| ax, ay = Math.cos(angle), Math.sin(angle)
|
199
|
+
| cos_between = vx * ax + vy * ay
|
200
|
+
| sin_between = vx * -ay + vy * ax
|
201
|
+
| yield sin_between, cos_between
|
202
|
+
| end
|
203
|
+
| end
|
204
|
+
| end
|
205
|
+
|
|
206
|
+
| module Life
|
207
|
+
| attr_reader :health
|
208
|
+
| def dead?
|
209
|
+
| @health == 0
|
210
|
+
| end
|
211
|
+
| def hurt damage
|
212
|
+
| @health = [@health - damage, 0].max
|
213
|
+
| end
|
214
|
+
| end
|
215
|
+
|
|
216
|
+
| class Tank
|
217
|
+
| include Collisions
|
218
|
+
| include Guidance
|
219
|
+
| include Life
|
220
|
+
| # ^ sounds like insurance
|
221
|
+
|
|
222
|
+
| @@collide_size = 15
|
223
|
+
| def west; @x - @@collide_size; end
|
224
|
+
| def east; @x + @@collide_size; end
|
225
|
+
| def north; @y - @@collide_size; end
|
226
|
+
| def south; @y + @@collide_size; end
|
227
|
+
|
|
228
|
+
| attr_reader :x, :y
|
229
|
+
|
|
230
|
+
| def initialize
|
231
|
+
| @x, @y = 0, -125
|
232
|
+
| @last_x, @last_y = @x, @y
|
233
|
+
| @tank_angle = 0.0
|
234
|
+
| @dest_x, @dest_y = 0, 0
|
235
|
+
| @acceleration = 0.0
|
236
|
+
| @speed = 0.0
|
237
|
+
| @moving = false
|
238
|
+
|
|
239
|
+
| @aim_angle = 0.0
|
240
|
+
| @target_x, @target_y = 0, 0
|
241
|
+
| @aimed = false
|
242
|
+
|
|
243
|
+
| @health = 100
|
244
|
+
| end
|
245
|
+
|
|
246
|
+
| def set_destination
|
247
|
+
| @dest_x, @dest_y = @target_x, @target_y
|
248
|
+
| @moving = true
|
249
|
+
| end
|
250
|
+
|
|
251
|
+
| def fire
|
252
|
+
| Opp.add_shell Shell.new(@x + 30 * Math.cos(@aim_angle),
|
253
|
+
| @y + 30 * Math.sin(@aim_angle), @aim_angle)
|
254
|
+
| end
|
255
|
+
|
|
256
|
+
| def update button, mouse_x, mouse_y
|
257
|
+
| @target_x, @target_y = mouse_x, mouse_y
|
258
|
+
|
|
259
|
+
| if @moving
|
260
|
+
| guidance_system @x, @y, @dest_x, @dest_y, @tank_angle do |direction, on_target|
|
261
|
+
| turn direction
|
262
|
+
| @acceleration = on_target * 0.25
|
263
|
+
| end
|
264
|
+
|
|
265
|
+
| distance = Math.sqrt((@dest_x - @x) ** 2 + (@dest_y - @y) ** 2)
|
266
|
+
| @moving = false if distance < 50
|
267
|
+
| else
|
268
|
+
| @acceleration = 0.0
|
269
|
+
| end
|
270
|
+
|
|
271
|
+
| guidance_system @x, @y, @target_x, @target_y, @aim_angle do |direction, on_target|
|
272
|
+
| aim direction
|
273
|
+
| @aimed = on_target > 0.98
|
274
|
+
| end
|
275
|
+
|
|
276
|
+
| integrity = @health / 100.0 # the more hurt you are, the slower you go
|
277
|
+
| @speed = [[@speed + @acceleration, 5.0 * integrity].min, -3.0 * integrity].max
|
278
|
+
| @speed *= 0.9 if !@moving
|
279
|
+
|
|
280
|
+
| @last_x, @last_y = @x, @y
|
281
|
+
| @x += @speed * Math.cos(@tank_angle)
|
282
|
+
| @y += @speed * Math.sin(@tank_angle)
|
283
|
+
| end
|
284
|
+
|
|
285
|
+
| def collide_and_stop
|
286
|
+
| @x, @y = @last_x, @last_y
|
287
|
+
| hurt @speed.abs * 3 + 5
|
288
|
+
| @speed = 0
|
289
|
+
| @moving = false
|
290
|
+
| end
|
291
|
+
|
|
292
|
+
| def turn direction
|
293
|
+
| @tank_angle += [[-0.03, direction].max, 0.03].min
|
294
|
+
| end
|
295
|
+
|
|
296
|
+
| def aim direction
|
297
|
+
| @aim_angle += [[-0.1, direction].max, 0.1].min
|
298
|
+
| end
|
299
|
+
|
|
300
|
+
| def draw
|
301
|
+
| $app.stroke $app.blue
|
302
|
+
| $app.fill $app.blue(0.4)
|
303
|
+
| Opp.draw_opp_rect @x - 20, @x + 20, @y - 15, @y + 15, 1.05, @tank_angle
|
304
|
+
| #Opp.draw_opp_box @x - 20, @x + 20, @y - 20, @y + 20, 1.03, 1.0
|
305
|
+
| Opp.draw_opp_rect @x - 10, @x + 10, @y - 7, @y + 7, 1.05, @aim_angle
|
306
|
+
| x, unused1, y, unused2 = Opp.project(@x, 0, @y, 0, 1.05)
|
307
|
+
| $app.line x, y, x + 25 * Math.cos(@aim_angle), y + 25 * Math.sin(@aim_angle)
|
308
|
+
|
|
309
|
+
| $app.stroke $app.red
|
310
|
+
| $app.fill $app.red(@aimed ? 0.4 : 0.1)
|
311
|
+
| Opp.draw_opp_oval @target_x - 10, @target_x + 10, @target_y - 10, @target_y + 10, 1.00
|
312
|
+
|
|
313
|
+
| if @moving
|
314
|
+
| $app.stroke $app.green
|
315
|
+
| $app.fill $app.green(0.2)
|
316
|
+
| Opp.draw_opp_oval @dest_x - 20, @dest_x + 20, @dest_y - 20, @dest_y + 20, 1.00
|
317
|
+
| end
|
318
|
+
| end
|
319
|
+
| end
|
320
|
+
|
|
321
|
+
| class Shell
|
322
|
+
| attr_reader :x, :y
|
323
|
+
|
|
324
|
+
| def initialize x, y, angle
|
325
|
+
| @x, @y, @angle = x, y, angle
|
326
|
+
| @speed = 10.0
|
327
|
+
| end
|
328
|
+
|
|
329
|
+
| def update
|
330
|
+
| @x += @speed * Math.cos(@angle)
|
331
|
+
| @y += @speed * Math.sin(@angle)
|
332
|
+
| end
|
333
|
+
|
|
334
|
+
| def draw
|
335
|
+
| $app.stroke $app.red
|
336
|
+
| $app.fill $app.red(0.1)
|
337
|
+
| Opp.draw_opp_box @x - 2, @x + 2, @y - 2, @y + 2, 1.05, 1.04
|
338
|
+
| end
|
339
|
+
| end
|
340
|
+
|
|
341
|
+
| class Opp
|
342
|
+
| def self.new_game
|
343
|
+
| @offset_x, @offset_y = 0, 0
|
344
|
+
| @buildings = [
|
345
|
+
| [-1000, -750, -750, -250],
|
346
|
+
| [-500, 250, -750, -250],
|
347
|
+
| [500, 1000, -750, -500],
|
348
|
+
| [750, 1250, -250, 0],
|
349
|
+
| [750, 1250, 250, 750],
|
350
|
+
| [250, 500, 0, 750],
|
351
|
+
| [-250, 0, 0, 500],
|
352
|
+
| [-500, 0, 750, 1000],
|
353
|
+
| [-1000, -500, 0, 500],
|
354
|
+
| [400, 600, -350, -150]
|
355
|
+
| ].collect { |p| Building.new *p }
|
356
|
+
| @shells = []
|
357
|
+
| @boundary = [-1250, 1500, -1250, 1250]
|
358
|
+
| @tank = Tank.new
|
359
|
+
| @center_x, @center_y = $app.width / 2, $app.height / 2
|
360
|
+
| end
|
361
|
+
|
|
362
|
+
| def self.tank
|
363
|
+
| @tank
|
364
|
+
| end
|
365
|
+
|
|
366
|
+
| def self.read_input
|
367
|
+
| @input = $app.mouse
|
368
|
+
| end
|
369
|
+
|
|
370
|
+
| def self.update_scene
|
371
|
+
| button, x, y = @input
|
372
|
+
| x += @offset_x - @center_x
|
373
|
+
| y += @offset_y - @center_y
|
374
|
+
|
|
375
|
+
| @tank.update(button, x, y) if !@tank.dead?
|
376
|
+
| @buildings.each do |b|
|
377
|
+
| @tank.collide_and_stop if b.intersects? @tank
|
378
|
+
| end
|
379
|
+
|
|
380
|
+
| @shells.each { |s| s.update }
|
381
|
+
| @buildings.each do |b|
|
382
|
+
| @shells.reject! do |s|
|
383
|
+
| b.contains?(s.x, s.y)
|
384
|
+
| end
|
385
|
+
| end
|
386
|
+
| #collide shells with tanks -- don't need this until there are enemy tanks
|
387
|
+
| #@shells.reject! do |s|
|
388
|
+
| # @tank.contains?(s.x, s.y)
|
389
|
+
| #end
|
390
|
+
|
|
391
|
+
| $app.clear do
|
392
|
+
| @offset_x += $camera_tightness * (@tank.x - @offset_x)
|
393
|
+
| @offset_y += $camera_tightness * (@tank.y - @offset_y)
|
394
|
+
|
|
395
|
+
| $app.background $app.black
|
396
|
+
| @center_x, @center_y = $app.width / 2, $app.height / 2
|
397
|
+
|
|
398
|
+
| $app.stroke $app.red(0.9)
|
399
|
+
| $app.nofill
|
400
|
+
| draw_opp_box *(@boundary + [1.1, 1.0, false])
|
401
|
+
|
|
402
|
+
| @tank.draw
|
403
|
+
| @shells.each { |s| s.draw }
|
404
|
+
| @buildings.each { |b| b.draw }
|
405
|
+
| end
|
406
|
+
| end
|
407
|
+
|
|
408
|
+
| def self.add_shell shell
|
409
|
+
| @shells << shell
|
410
|
+
| @shells.shift if @shells.size > 10
|
411
|
+
| end
|
412
|
+
|
|
413
|
+
| def self.project left, right, top, bottom, depth
|
414
|
+
| [left, right].collect { |x| @center_x + depth * (x - @offset_x) } +
|
415
|
+
| [top, bottom].collect { |y| @center_y + depth * (y - @offset_y) }
|
416
|
+
| end
|
417
|
+
|
|
418
|
+
| # here "front" and "back" push the rect into and out of the window.
|
419
|
+
| # 1.0 means your x and y units are pixels on the surface.
|
420
|
+
| # greater than that brings the box closer. less pushes it back. 0.0 => infinity.
|
421
|
+
| # the front will be filled but the rest is wireframe only.
|
422
|
+
| def self.draw_opp_box left, right, top, bottom, front, back, occlude = true
|
423
|
+
| near_left, near_right, near_top, near_bottom = project(left, right, top, bottom, front)
|
424
|
+
| far_left, far_right, far_top, far_bottom = project(left, right, top, bottom, back)
|
425
|
+
|
|
426
|
+
| # determine which sides of the box are visible
|
427
|
+
| if occlude
|
428
|
+
| draw_left = @center_x < near_left
|
429
|
+
| draw_right = near_right < @center_x
|
430
|
+
| draw_top = @center_y < near_top
|
431
|
+
| draw_bottom = near_bottom < @center_y
|
432
|
+
| else
|
433
|
+
| draw_left, draw_right, draw_top, draw_bottom = [true] * 4
|
434
|
+
| end
|
435
|
+
|
|
436
|
+
| # draw lines for the back edges
|
437
|
+
| $app.line far_left, far_top, far_right, far_top if draw_top
|
438
|
+
| $app.line far_left, far_bottom, far_right, far_bottom if draw_bottom
|
439
|
+
| $app.line far_left, far_top, far_left, far_bottom if draw_left
|
440
|
+
| $app.line far_right, far_top, far_right, far_bottom if draw_right
|
441
|
+
|
|
442
|
+
| # draw lines to connect the front and back
|
443
|
+
| $app.line near_left, near_top, far_left, far_top if draw_left or draw_top
|
444
|
+
| $app.line near_right, near_top, far_right, far_top if draw_right or draw_top
|
445
|
+
| $app.line near_left, near_bottom, far_left, far_bottom if draw_left or draw_bottom
|
446
|
+
| $app.line near_right, near_bottom, far_right, far_bottom if draw_right or draw_bottom
|
447
|
+
|
|
448
|
+
| # draw the front, filled
|
449
|
+
| $app.rect near_left, near_top, near_right - near_left, near_bottom - near_top
|
450
|
+
| end
|
451
|
+
|
|
452
|
+
| def self.draw_opp_rect left, right, top, bottom, depth, angle, with_x = false
|
453
|
+
| pl, pr, pt, pb = project(left, right, top, bottom, depth)
|
454
|
+
| cos = Math.cos(angle)
|
455
|
+
| sin = Math.sin(angle)
|
456
|
+
| cx, cy = (pr + pl) / 2.0, (pb + pt) / 2.0
|
457
|
+
| points = [[pl, pt], [pr, pt], [pr, pb], [pl, pb]].collect do |x, y|
|
458
|
+
| [cx + (x - cx) * cos - (y - cy) * sin,
|
459
|
+
| cy + (x - cx) * sin + (y - cy) * cos]
|
460
|
+
| end
|
461
|
+
|
|
462
|
+
| $app.line *(points[0] + points[1])
|
463
|
+
| $app.line *(points[1] + points[2])
|
464
|
+
| $app.line *(points[2] + points[3])
|
465
|
+
| $app.line *(points[3] + points[0])
|
466
|
+
| end
|
467
|
+
|
|
468
|
+
| def self.draw_opp_oval left, right, top, bottom, depth
|
469
|
+
| pl, pr, pt, pb = project(left, right, top, bottom, depth)
|
470
|
+
| $app.oval(pl, pt, pr - pl, pb - pt)
|
471
|
+
| end
|
472
|
+
|
|
473
|
+
| def self.draw_opp_plane x1, y1, x2, y2, front, back, stroke_color
|
474
|
+
| near_x1, near_x2, near_y1, near_y2 = project(x1, x2, y1, y2, front)
|
475
|
+
| far_x1, far_x2, far_y1, far_y2 = project(x1, x2, y1, y2, back)
|
476
|
+
|
|
477
|
+
| $app.stroke stroke_color
|
478
|
+
|
|
479
|
+
| $app.line far_x1, far_y1, far_x2, far_y2
|
480
|
+
| $app.line far_x1, far_y1, near_x1, near_y1
|
481
|
+
| $app.line far_x2, far_y2, near_x2, near_y2
|
482
|
+
| $app.line near_x1, near_y1, near_x2, near_y2
|
483
|
+
| end
|
484
|
+
| end
|
485
|
+
|
|
486
|
+
| Shoes.app :width => $width, :height => $height do
|
487
|
+
| $app = self
|
488
|
+
|
|
489
|
+
| Opp.new_game
|
490
|
+
| @playing = true
|
491
|
+
|
|
492
|
+
| keypress do |key|
|
493
|
+
| if @playing
|
494
|
+
| if key == "1" or key == "z"
|
495
|
+
| Opp.tank.set_destination
|
496
|
+
| elsif key == "2" or key == "x" or key == " "
|
497
|
+
| Opp.tank.fire
|
498
|
+
| end
|
499
|
+
| else
|
500
|
+
| if key == "n"
|
501
|
+
| Opp.new_game
|
502
|
+
| @playing = true
|
503
|
+
| end
|
504
|
+
| end
|
505
|
+
| end
|
506
|
+
|
|
507
|
+
| click do |button, x, y|
|
508
|
+
| if @playing
|
509
|
+
| if button == 1
|
510
|
+
| Opp.tank.set_destination
|
511
|
+
| else
|
512
|
+
| Opp.tank.fire
|
513
|
+
| end
|
514
|
+
| end
|
515
|
+
| end
|
516
|
+
|
|
517
|
+
| game_over_count = -1
|
518
|
+
| animate(60) do
|
519
|
+
| Opp.read_input if @playing
|
520
|
+
| Opp.update_scene
|
521
|
+
|
|
522
|
+
| @playing = false if Opp.tank.dead?
|
523
|
+
| if !@playing
|
524
|
+
| stack do
|
525
|
+
| banner "Game Over", :stroke => white, :margin => 10
|
526
|
+
| caption "learn to drive!", :stroke => white, :margin => 20
|
527
|
+
| end
|
528
|
+
| end
|
529
|
+
| end
|
530
|
+
| end
|
531
|
+
- arc/
|
532
|
+
@shoes/
|
533
|
+
| #
|
534
|
+
| # a translation from a processing example
|
535
|
+
| # http://vormplus.be/weging/an-introduction-to-processing/
|
536
|
+
| #
|
537
|
+
| Shoes.app :width => 420, :height => 420, :resizable => false do
|
538
|
+
| rotation = -(Shoes::HALF_PI / 3)
|
539
|
+
| step = 20
|
540
|
+
|
|
541
|
+
| background gray(240)
|
542
|
+
| stroke gray(127)
|
543
|
+
| cap :curve
|
544
|
+
| nofill
|
545
|
+
|
|
546
|
+
| 10.times do |i|
|
547
|
+
| strokewidth i
|
548
|
+
| size = 200 + (step * i)
|
549
|
+
| shape do
|
550
|
+
| arc self.width / 2, self.height / 2,
|
551
|
+
| size, size,
|
552
|
+
| rotation * i, rotation * i + Shoes::TWO_PI - Shoes::HALF_PI
|
553
|
+
| end
|
554
|
+
| end
|
555
|
+
| end
|
556
|
+
- draw/
|
557
|
+
@shoes/
|
558
|
+
| Shoes.app do
|
559
|
+
| background "#999"
|
560
|
+
| stroke "#000"
|
561
|
+
| x, y = nil, nil
|
562
|
+
| motion do |_x, _y|
|
563
|
+
| if x and y and (x != _x or y != _y)
|
564
|
+
| append do
|
565
|
+
| line x, y, _x, _y
|
566
|
+
| end
|
567
|
+
| end
|
568
|
+
| x, y = _x, _y
|
569
|
+
| end
|
570
|
+
| end
|
571
|
+
- reminder/
|
572
|
+
@shoes/
|
573
|
+
| require 'yaml'
|
574
|
+
|
|
575
|
+
| Shoes.app :title => "A Gentle Reminder",
|
576
|
+
| :width => 370, :height => 560, :resizable => false do
|
577
|
+
|
|
578
|
+
| background white
|
579
|
+
| background tan, :height => 40
|
580
|
+
|
|
581
|
+
| caption "A Gentle Reminder", :margin => 8, :stroke => white
|
582
|
+
|
|
583
|
+
| stack :margin => 10, :margin_top => 50 do
|
584
|
+
| para "You need to", :stroke => red, :fill => yellow
|
585
|
+
|
|
586
|
+
| stack :margin_left => 5, :margin_right => 10, :width => 1.0, :height => 200, :scroll => true do
|
587
|
+
| background white
|
588
|
+
| border white, :strokewidth => 3
|
589
|
+
| @gui_todo = para
|
590
|
+
| end
|
591
|
+
|
|
592
|
+
| flow :margin_top => 10 do
|
593
|
+
| para "Remember to"
|
594
|
+
| @add = edit_line(:margin_left => 10, :width => 180)
|
595
|
+
| button("Add", :margin_left => 5) { add_todo(@add.text); @add.text = '' }
|
596
|
+
| end
|
597
|
+
| end
|
598
|
+
|
|
599
|
+
| stack :margin_top => 10 do
|
600
|
+
| background darkgray
|
601
|
+
| para strong('Completed'), :stroke => white
|
602
|
+
| end
|
603
|
+
|
|
604
|
+
| @gui_completed = stack :width => 1.0, :height => 207, :margin_right => 20
|
605
|
+
|
|
606
|
+
|
|
607
|
+
| def data_path
|
608
|
+
| if RUBY_PLATFORM =~ /win32/
|
609
|
+
| if ENV['USERPROFILE']
|
610
|
+
| if File.exist?(File.join(File.expand_path(ENV['USERPROFILE']), "Application Data"))
|
611
|
+
| user_data_directory = File.join File.expand_path(ENV['USERPROFILE']), "Application Data", "GentleReminder"
|
612
|
+
| else
|
613
|
+
| user_data_directory = File.join File.expand_path(ENV['USERPROFILE']), "GentleReminder"
|
614
|
+
| end
|
615
|
+
| else
|
616
|
+
| user_data_directory = File.join File.expand_path(Dir.getwd), "data"
|
617
|
+
| end
|
618
|
+
| else
|
619
|
+
| user_data_directory = File.expand_path(File.join("~", ".gentlereminder"))
|
620
|
+
| end
|
621
|
+
|
|
622
|
+
| unless File.exist?(user_data_directory)
|
623
|
+
| Dir.mkdir(user_data_directory)
|
624
|
+
| end
|
625
|
+
|
|
626
|
+
| return File.join(user_data_directory, "data.yaml")
|
627
|
+
| end
|
628
|
+
|
|
629
|
+
|
|
630
|
+
| def refresh_todo
|
631
|
+
| @gui_todo.replace *(
|
632
|
+
| @todo.map { |item|
|
633
|
+
| [ item, ' ' ] + [ link('Done') { complete_todo item } ] + [ ' ' ] +
|
634
|
+
| [ link('Forget it') { forget_todo item } ] + [ "\n" ]
|
635
|
+
| }.flatten
|
636
|
+
| )
|
637
|
+
| end
|
638
|
+
|
|
639
|
+
|
|
640
|
+
| def refresh
|
641
|
+
| refresh_todo
|
642
|
+
|
|
643
|
+
| @gui_completed.clear
|
644
|
+
|
|
645
|
+
| @gui_completed.append do
|
646
|
+
| background white
|
647
|
+
|
|
648
|
+
| @completed.keys.sort.reverse.each { |day|
|
649
|
+
| stack do
|
650
|
+
| background lightgrey
|
651
|
+
| para strong(Time.at(day).strftime('%B %d, %Y')), :stroke => white
|
652
|
+
| end
|
653
|
+
|
|
654
|
+
| stack do
|
655
|
+
| inscription *(
|
656
|
+
| @completed[day].map { |item|
|
657
|
+
| [ item ] + [ ' ' ] + [ link('Not Done') { undo_todo day, item } ] +
|
658
|
+
| (@completed[day].index(item) == @completed[day].length - 1 ? [ '' ] : [ "\n" ])
|
659
|
+
| }.flatten
|
660
|
+
| )
|
661
|
+
| end
|
662
|
+
|
|
663
|
+
| }
|
664
|
+
| end
|
665
|
+
| end
|
666
|
+
|
|
667
|
+
|
|
668
|
+
| def complete_todo(item)
|
669
|
+
| day = Time.today.to_i
|
670
|
+
|
|
671
|
+
| if @completed.keys.include? day
|
672
|
+
| @completed[day] << item
|
673
|
+
| else
|
674
|
+
| @completed[day] = [ item ]
|
675
|
+
| end
|
676
|
+
|
|
677
|
+
| @todo.delete(item)
|
678
|
+
|
|
679
|
+
| save
|
680
|
+
|
|
681
|
+
| refresh
|
682
|
+
| end
|
683
|
+
|
|
684
|
+
|
|
685
|
+
| def undo_todo(day, item)
|
686
|
+
| @completed[day].delete item
|
687
|
+
|
|
688
|
+
| @completed.delete(day) if @completed[day].empty?
|
689
|
+
|
|
690
|
+
| @todo << item unless @todo.include? item
|
691
|
+
|
|
692
|
+
| save
|
693
|
+
|
|
694
|
+
| refresh
|
695
|
+
| end
|
696
|
+
|
|
697
|
+
|
|
698
|
+
| def add_todo(item)
|
699
|
+
| item = item.strip
|
700
|
+
|
|
701
|
+
| return if item == ''
|
702
|
+
|
|
703
|
+
| if @todo.include? item
|
704
|
+
| alert('You have already added that to the list!')
|
705
|
+
| return
|
706
|
+
| end
|
707
|
+
|
|
708
|
+
| @todo << item
|
709
|
+
|
|
710
|
+
| save
|
711
|
+
|
|
712
|
+
| refresh_todo
|
713
|
+
| end
|
714
|
+
|
|
715
|
+
|
|
716
|
+
| def forget_todo(item)
|
717
|
+
| @todo.delete item
|
718
|
+
|
|
719
|
+
| save
|
720
|
+
|
|
721
|
+
| refresh_todo
|
722
|
+
| end
|
723
|
+
|
|
724
|
+
|
|
725
|
+
| def load
|
726
|
+
| if File.exist?(data_path)
|
727
|
+
| @todo, @completed = YAML::load(File.open(data_path, 'r'))
|
728
|
+
| else
|
729
|
+
| @todo = []
|
730
|
+
| @completed = {}
|
731
|
+
| end
|
732
|
+
|
|
733
|
+
| refresh
|
734
|
+
| end
|
735
|
+
|
|
736
|
+
|
|
737
|
+
| def save
|
738
|
+
| File.open(data_path, 'w') { |f|
|
739
|
+
| f.write [ @todo, @completed ].to_yaml
|
740
|
+
| }
|
741
|
+
| end
|
742
|
+
|
|
743
|
+
|
|
744
|
+
| load
|
745
|
+
|
|
746
|
+
| end
|
747
|
+
- vjot/
|
748
|
+
@shoes/
|
749
|
+
| NOTES = [['Welcome to the vJot Clone', <<-'END']]
|
750
|
+
| This sample app is a notetaker, a clone of PJ Hyett's vjot.com.
|
751
|
+
|
|
752
|
+
| Creating
|
753
|
+
| ----------
|
754
|
+
| Click "Add a New Note" and the jot will be loaded into the editor for reading or editing.
|
755
|
+
|
|
756
|
+
| Editing
|
757
|
+
| ---------
|
758
|
+
| Click a jot's title to load it.
|
759
|
+
|
|
760
|
+
| Saving
|
761
|
+
| --------
|
762
|
+
| There is no save button, the jot is saved as you edit.
|
763
|
+
|
|
764
|
+
| END
|
765
|
+
|
|
766
|
+
| Shoes.app :title => "vJot",
|
767
|
+
| :width => 420, :height => 560, :resizable => false do
|
768
|
+
|
|
769
|
+
| @note = NOTES.first
|
770
|
+
| background "#C7EAFB"
|
771
|
+
| stack :width => 400, :margin => 20 do
|
772
|
+
| background "#eee", :curve => 12
|
773
|
+
| border "#00D0FF", :strokewidth => 3, :curve => 12
|
774
|
+
| stack :margin => 20 do
|
775
|
+
| caption "vJot"
|
776
|
+
| @title = edit_line @note[0], :width => 1.0 do
|
777
|
+
| @note[0] = @title.text
|
778
|
+
| load_list
|
779
|
+
| end
|
780
|
+
| stack :width => 1.0, :height => 200, :scroll => true do
|
781
|
+
| @list = para
|
782
|
+
| end
|
783
|
+
| @jot = edit_box @note[1], :width => 1.0, :height => 200, :margin_bottom => 20 do
|
784
|
+
| @note[1] = @jot.text
|
785
|
+
| end
|
786
|
+
| end
|
787
|
+
| end
|
788
|
+
|
|
789
|
+
| def load_list
|
790
|
+
| @list.replace *(NOTES.map { |note|
|
791
|
+
| [link(note.first) { @note = load_note(note); load_list }, "\n"]
|
792
|
+
| }.flatten +
|
793
|
+
| [link("+ Add a new Note") { NOTES << (@note = load_note); load_list }])
|
794
|
+
| end
|
795
|
+
|
|
796
|
+
| def load_note(note = ['New Note', ''])
|
797
|
+
| @note = note
|
798
|
+
| @title.text = note[0]
|
799
|
+
| @jot.text = note[1]
|
800
|
+
| note
|
801
|
+
| end
|
802
|
+
|
|
803
|
+
| load_list
|
804
|
+
| end
|
805
|
+
- minesweeper/
|
806
|
+
@shoes/
|
807
|
+
| #
|
808
|
+
| # Shoes Minesweeper by que/varyform
|
809
|
+
| #
|
810
|
+
| LEVELS = { :beginner => [9, 9, 10], :intermediate => [16, 16, 40], :expert => [30, 16, 99] }
|
811
|
+
|
|
812
|
+
| class Field
|
813
|
+
| CELL_SIZE = 20
|
814
|
+
| COLORS = %w(#00A #0A0 #A00 #004 #040 #400 #000)
|
815
|
+
|
|
816
|
+
| class Cell
|
817
|
+
| attr_accessor :flag
|
818
|
+
| def initialize(aflag = false)
|
819
|
+
| @flag = aflag
|
820
|
+
| end
|
821
|
+
| end
|
822
|
+
|
|
823
|
+
| class Bomb < Cell
|
824
|
+
| attr_accessor :exploded
|
825
|
+
| def initialize(exploded = false)
|
826
|
+
| @exploded = exploded
|
827
|
+
| end
|
828
|
+
| end
|
829
|
+
|
|
830
|
+
| class OpenCell < Cell
|
831
|
+
| attr_accessor :number
|
832
|
+
| def initialize(bombs_around = 0)
|
833
|
+
| @number = bombs_around
|
834
|
+
| end
|
835
|
+
| end
|
836
|
+
|
|
837
|
+
| class EmptyCell < Cell; end
|
838
|
+
|
|
839
|
+
| attr_reader :cell_size, :offset
|
840
|
+
|
|
841
|
+
| def initialize(app, level = :beginner)
|
842
|
+
| @app = app
|
843
|
+
| @field = []
|
844
|
+
| @w, @h, @bombs = LEVELS[level][0], LEVELS[level][1], LEVELS[level][2]
|
845
|
+
| @h.times { @field << Array.new(@w) { EmptyCell.new } }
|
846
|
+
| @game_over = false
|
847
|
+
| @width, @height, @cell_size = @w * CELL_SIZE, @h * CELL_SIZE, CELL_SIZE
|
848
|
+
| @offset = [(@app.width - @width.to_i) / 2, (@app.height - @height.to_i) / 2]
|
849
|
+
| plant_bombs
|
850
|
+
| @start_time = Time.now
|
851
|
+
| end
|
852
|
+
|
|
853
|
+
| def total_time
|
854
|
+
| @latest_time = Time.now - @start_time unless game_over? || all_found?
|
855
|
+
| @latest_time
|
856
|
+
| end
|
857
|
+
|
|
858
|
+
| def click!(x, y)
|
859
|
+
| return unless cell_exists?(x, y)
|
860
|
+
| return if has_flag?(x, y)
|
861
|
+
| return die!(x, y) if bomb?(x, y)
|
862
|
+
| open(x, y)
|
863
|
+
| discover(x, y) if bombs_around(x, y) == 0
|
864
|
+
| end
|
865
|
+
|
|
866
|
+
| def flag!(x, y)
|
867
|
+
| return unless cell_exists?(x, y)
|
868
|
+
| self[x, y].flag = !self[x, y].flag unless self[x, y].is_a?(OpenCell)
|
869
|
+
| end
|
870
|
+
|
|
871
|
+
| def game_over?
|
872
|
+
| @game_over
|
873
|
+
| end
|
874
|
+
|
|
875
|
+
| def render_cell(x, y, color = "#AAA", stroke = true)
|
876
|
+
| @app.stroke "#666" if stroke
|
877
|
+
| @app.fill color
|
878
|
+
| @app.rect x*cell_size, y*cell_size, cell_size-1, cell_size-1
|
879
|
+
| @app.stroke "#BBB" if stroke
|
880
|
+
| @app.line x*cell_size+1, y*cell_size+1, x*cell_size+cell_size-1, y*cell_size
|
881
|
+
| @app.line x*cell_size+1, y*cell_size+1, x*cell_size, y*cell_size+cell_size-1
|
882
|
+
| end
|
883
|
+
|
|
884
|
+
| def render_flag(x, y)
|
885
|
+
| @app.stroke "#000"
|
886
|
+
| @app.line(x*cell_size+cell_size / 4 + 1, y*cell_size + cell_size / 5, x*cell_size+cell_size / 4 + 1, y*cell_size+cell_size / 5 * 4)
|
887
|
+
| @app.fill "#A00"
|
888
|
+
| @app.rect(x*cell_size+cell_size / 4+2, y*cell_size + cell_size / 5,
|
889
|
+
| cell_size / 3, cell_size / 4)
|
890
|
+
| end
|
891
|
+
|
|
892
|
+
| def render_bomb(x, y)
|
893
|
+
| render_cell(x, y)
|
894
|
+
| if (game_over? or all_found?) then # draw bomb
|
895
|
+
| if self[x, y].exploded then
|
896
|
+
| render_cell(x, y, @app.rgb(0xFF, 0, 0, 0.5))
|
897
|
+
| end
|
898
|
+
| @app.nostroke
|
899
|
+
| @app.fill @app.rgb(0, 0, 0, 0.8)
|
900
|
+
| @app.oval(x*cell_size+3, y*cell_size+3, 13)
|
901
|
+
| @app.fill "#333"
|
902
|
+
| @app.oval(x*cell_size+5, y*cell_size+5, 7)
|
903
|
+
| @app.fill "#AAA"
|
904
|
+
| @app.oval(x*cell_size+6, y*cell_size+6, 3)
|
905
|
+
| @app.fill @app.rgb(0, 0, 0, 0.8)
|
906
|
+
| @app.stroke "#222"
|
907
|
+
| @app.strokewidth 2
|
908
|
+
| @app.oval(x*cell_size + cell_size / 2 + 2, y*cell_size + cell_size / 4 - 2, 2)
|
909
|
+
| @app.oval(x*cell_size + cell_size / 2 + 4, y*cell_size + cell_size / 4 - 2, 1)
|
910
|
+
| @app.strokewidth 1
|
911
|
+
| end
|
912
|
+
| end
|
913
|
+
|
|
914
|
+
| def render_number(x, y)
|
915
|
+
| render_cell(x, y, "#999", false)
|
916
|
+
| if self[x, y].number != 0 then
|
917
|
+
| @app.nostroke
|
918
|
+
| @app.para self[x, y].number.to_s, :left => x*cell_size + 3, :top => y*cell_size - 2,
|
919
|
+
| :font => '13px', :stroke => COLORS[self[x, y].number - 1]
|
920
|
+
| end
|
921
|
+
| end
|
922
|
+
|
|
923
|
+
| def paint
|
924
|
+
| 0.upto @h-1 do |y|
|
925
|
+
| 0.upto @w-1 do |x|
|
926
|
+
| @app.nostroke
|
927
|
+
| case self[x, y]
|
928
|
+
| when EmptyCell then render_cell(x, y)
|
929
|
+
| when Bomb then render_bomb(x, y)
|
930
|
+
| when OpenCell then render_number(x, y)
|
931
|
+
| end
|
932
|
+
| render_flag(x, y) if has_flag?(x, y) && !(game_over? && bomb?(x, y))
|
933
|
+
| end
|
934
|
+
| end
|
935
|
+
| end
|
936
|
+
|
|
937
|
+
| def bombs_left
|
938
|
+
| @bombs - @field.flatten.compact.reject {|e| !e.flag }.size
|
939
|
+
| end
|
940
|
+
|
|
941
|
+
| def all_found?
|
942
|
+
| @field.flatten.compact.reject {|e| !e.is_a?(OpenCell) }.size + @bombs == @w*@h
|
943
|
+
| end
|
944
|
+
|
|
945
|
+
| def reveal!(x, y)
|
946
|
+
| return unless cell_exists?(x, y)
|
947
|
+
| return unless self[x, y].is_a?(Field::OpenCell)
|
948
|
+
| if flags_around(x, y) >= self[x, y].number then
|
949
|
+
| (-1..1).each do |v|
|
950
|
+
| (-1..1).each { |h| click!(x+h, y+v) unless (v==0 && h==0) or has_flag?(x+h, y+v) }
|
951
|
+
| end
|
952
|
+
| end
|
953
|
+
| end
|
954
|
+
|
|
955
|
+
| private
|
956
|
+
|
|
957
|
+
| def cell_exists?(x, y)
|
958
|
+
| ((0...@w).include? x) && ((0...@h).include? y)
|
959
|
+
| end
|
960
|
+
|
|
961
|
+
| def has_flag?(x, y)
|
962
|
+
| return false unless cell_exists?(x, y)
|
963
|
+
| return self[x, y].flag
|
964
|
+
| end
|
965
|
+
|
|
966
|
+
| def bomb?(x, y)
|
967
|
+
| cell_exists?(x, y) && (self[x, y].is_a? Bomb)
|
968
|
+
| end
|
969
|
+
|
|
970
|
+
| def can_be_discovered?(x, y)
|
971
|
+
| return false unless cell_exists?(x, y)
|
972
|
+
| return false if self[x, y].flag
|
973
|
+
| cell_exists?(x, y) && (self[x, y].is_a? EmptyCell) && !bomb?(x, y) && (bombs_around(x, y) == 0)
|
974
|
+
| end
|
975
|
+
|
|
976
|
+
| def open(x, y)
|
977
|
+
| self[x, y] = OpenCell.new(bombs_around(x, y)) unless (self[x, y].is_a? OpenCell) or has_flag?(x, y)
|
978
|
+
| end
|
979
|
+
|
|
980
|
+
| def neighbors
|
981
|
+
| (-1..1).each do |col|
|
982
|
+
| (-1..1).each { |row| yield row, col unless col==0 && row == 0 }
|
983
|
+
| end
|
984
|
+
| end
|
985
|
+
|
|
986
|
+
| def discover(x, y)
|
987
|
+
| open(x, y)
|
988
|
+
| neighbors do |col, row|
|
989
|
+
| cx, cy = x+row, y+col
|
990
|
+
| next unless cell_exists?(cx, cy)
|
991
|
+
| discover(cx, cy) if can_be_discovered?(cx, cy)
|
992
|
+
| open(cx, cy)
|
993
|
+
| end
|
994
|
+
| end
|
995
|
+
|
|
996
|
+
| def count_neighbors
|
997
|
+
| return 0 unless block_given?
|
998
|
+
| count = 0
|
999
|
+
| neighbors { |h, v| count += 1 if yield(h, v) }
|
1000
|
+
| count
|
1001
|
+
| end
|
1002
|
+
|
|
1003
|
+
| def bombs_around(x, y)
|
1004
|
+
| count_neighbors { |v, h| bomb?(x+h, y+v) }
|
1005
|
+
| end
|
1006
|
+
|
|
1007
|
+
| def flags_around(x, y)
|
1008
|
+
| count_neighbors { |v, h| has_flag?(x+h, y+v) }
|
1009
|
+
| end
|
1010
|
+
|
|
1011
|
+
| def die!(x, y)
|
1012
|
+
| self[x, y].exploded = true
|
1013
|
+
| @game_over = true
|
1014
|
+
| end
|
1015
|
+
|
|
1016
|
+
| def plant_bomb(x, y)
|
1017
|
+
| self[x, y].is_a?(EmptyCell) ? self[x, y] = Bomb.new : false
|
1018
|
+
| end
|
1019
|
+
|
|
1020
|
+
| def plant_bombs
|
1021
|
+
| @bombs.times { redo unless plant_bomb(rand(@w), rand(@h)) }
|
1022
|
+
| end
|
1023
|
+
|
|
1024
|
+
| def [](*args)
|
1025
|
+
| x, y = args
|
1026
|
+
| raise "Cell #{x}:#{y} does not exists!" unless cell_exists?(x, y)
|
1027
|
+
| @field[y][x]
|
1028
|
+
| end
|
1029
|
+
|
|
1030
|
+
| def []=(*args)
|
1031
|
+
| x, y, v = args
|
1032
|
+
| cell_exists?(x, y) ? @field[y][x] = v : false
|
1033
|
+
| end
|
1034
|
+
| end
|
1035
|
+
|
|
1036
|
+
| Shoes.app :width => 730, :height => 450, :title => 'Minesweeper' do
|
1037
|
+
| def render_field
|
1038
|
+
| clear do
|
1039
|
+
| background rgb(50, 50, 90, 0.7)
|
1040
|
+
| flow :margin => 4 do
|
1041
|
+
| button("Beginner") { new_game :beginner }
|
1042
|
+
| button("Intermediate") { new_game :intermediate }
|
1043
|
+
| button("Expert") { new_game :expert }
|
1044
|
+
| end
|
1045
|
+
| stack do @status = para :stroke => white end
|
1046
|
+
| @field.paint
|
1047
|
+
| para "Left click - open cell, right click - put flag, middle click - reveal empty cells", :top => 420, :left => 0, :stroke => white, :font => "11px"
|
1048
|
+
| end
|
1049
|
+
| end
|
1050
|
+
|
|
1051
|
+
| def new_game level
|
1052
|
+
| @field = Field.new self, level
|
1053
|
+
| translate -@old_offset.first, -@old_offset.last unless @old_offset.nil?
|
1054
|
+
| translate @field.offset.first, @field.offset.last
|
1055
|
+
| @old_offset = @field.offset
|
1056
|
+
| render_field
|
1057
|
+
| end
|
1058
|
+
|
|
1059
|
+
| new_game :beginner
|
1060
|
+
| animate(5) { @status.replace "Time: #{@field.total_time.to_i} Bombs left: #{@field.bombs_left}" }
|
1061
|
+
|
|
1062
|
+
| click do |button, x, y|
|
1063
|
+
| next if @field.game_over? || @field.all_found?
|
1064
|
+
| fx, fy = ((x-@field.offset.first) / @field.cell_size).to_i, ((y-@field.offset.last) / @field.cell_size).to_i
|
1065
|
+
| @field.click!(fx, fy) if button == 1
|
1066
|
+
| @field.flag!(fx, fy) if button == 2
|
1067
|
+
| @field.reveal!(fx, fy) if button == 3
|
1068
|
+
|
|
1069
|
+
| render_field
|
1070
|
+
| alert("Winner!\nTotal time: #{@field.total_time}") if @field.all_found?
|
1071
|
+
| alert("Bang!\nYou loose.") if @field.game_over?
|
1072
|
+
| end
|
1073
|
+
| end
|
1074
|
+
- animated shapes/
|
1075
|
+
@shoes/
|
1076
|
+
| Shoes.app do
|
1077
|
+
| background rgb(0, 0, 0)
|
1078
|
+
| fill rgb(255, 255, 255)
|
1079
|
+
| rects = [
|
1080
|
+
| rect(0, 0, 50, 50),
|
1081
|
+
| rect(0, 0, 100, 100),
|
1082
|
+
| rect(0, 0, 75, 75)
|
1083
|
+
| ]
|
1084
|
+
| animate(24) do |i|
|
1085
|
+
| rects.each do |r|
|
1086
|
+
| r.move((0..400).rand, (0..400).rand)
|
1087
|
+
| end
|
1088
|
+
| end
|
1089
|
+
| button "OK", :top => 0.5, :left => 0.5 do
|
1090
|
+
| quit unless confirm "You ARE sure you're OK??"
|
1091
|
+
| end
|
1092
|
+
| end
|
1093
|
+
- animated text/
|
1094
|
+
@shoes/
|
1095
|
+
| Shoes.app do
|
1096
|
+
| stack :top => 0.5, :left => 0.5 do
|
1097
|
+
| para "Counting up:"
|
1098
|
+
| l = para "0"
|
1099
|
+
| animate(24) do |i|
|
1100
|
+
| f = ['Arial 14px', 'Serif 34px', 'Monospace 18px', 'Arial 48px'][rand(3)]
|
1101
|
+
| l.replace "#{i}", :font => f
|
1102
|
+
| end
|
1103
|
+
| motion do |x, y|
|
1104
|
+
| Shoes.p [x, y]
|
1105
|
+
| end
|
1106
|
+
| end
|
1107
|
+
| end
|
1108
|
+
- bounce/
|
1109
|
+
@shoes/
|
1110
|
+
| xspeed, yspeed = 8.4, 6.6
|
1111
|
+
| xdir, ydir = 1, 1
|
1112
|
+
|
|
1113
|
+
| Shoes.app do
|
1114
|
+
| background "#DFA"
|
1115
|
+
| border black, :strokewidth => 6
|
1116
|
+
|
|
1117
|
+
| nostroke
|
1118
|
+
| @icon = image "#{DIR}/static/shoes-icon.png", :left => 100, :top => 100 do
|
1119
|
+
| alert "You're soooo quick."
|
1120
|
+
| end
|
1121
|
+
|
|
1122
|
+
| x, y = self.width / 2, self.height / 2
|
1123
|
+
| size = @icon.size
|
1124
|
+
| animate(30) do
|
1125
|
+
| x += xspeed * xdir
|
1126
|
+
| y += yspeed * ydir
|
1127
|
+
|
|
1128
|
+
| xdir *= -1 if x > self.width - size[0] or x < 0
|
1129
|
+
| ydir *= -1 if y > self.height - size[1] or y < 0
|
1130
|
+
|
|
1131
|
+
| @icon.move x.to_i, y.to_i
|
1132
|
+
| end
|
1133
|
+
| end
|
1134
|
+
- calc/
|
1135
|
+
@shoes/
|
1136
|
+
| class Calc
|
1137
|
+
| def initialize
|
1138
|
+
| @number = 0
|
1139
|
+
| @previous = nil
|
1140
|
+
| @op = nil
|
1141
|
+
| end
|
1142
|
+
|
|
1143
|
+
| def to_s
|
1144
|
+
| @number.to_s
|
1145
|
+
| end
|
1146
|
+
|
|
1147
|
+
| (0..9).each do |n|
|
1148
|
+
| define_method "press_#{n}" do
|
1149
|
+
| @number = @number.to_i * 10 + n
|
1150
|
+
| end
|
1151
|
+
| end
|
1152
|
+
|
|
1153
|
+
| def press_clear
|
1154
|
+
| @number = 0
|
1155
|
+
| end
|
1156
|
+
|
|
1157
|
+
| {'add' => '+', 'sub' => '-', 'times' => '*', 'div' => '/'}.each do |meth, op|
|
1158
|
+
| define_method "press_#{meth}" do
|
1159
|
+
| if @op
|
1160
|
+
| press_equals
|
1161
|
+
| end
|
1162
|
+
| @op = op
|
1163
|
+
| @previous, @number = @number, nil
|
1164
|
+
| end
|
1165
|
+
| end
|
1166
|
+
|
|
1167
|
+
| def press_equals
|
1168
|
+
| @number = @previous.send(@op, @number.to_i)
|
1169
|
+
| @op = nil
|
1170
|
+
| end
|
1171
|
+
|
|
1172
|
+
| end
|
1173
|
+
|
|
1174
|
+
| number_field = nil
|
1175
|
+
| number = Calc.new
|
1176
|
+
| Shoes.app :height => 250, :width => 200, :resizable => false do
|
1177
|
+
| background "#EEC".."#996", :curve => 5, :margin => 2
|
1178
|
+
|
|
1179
|
+
| stack :margin => 2 do
|
1180
|
+
|
|
1181
|
+
| stack :margin => 8 do
|
1182
|
+
| number_field = para strong(number)
|
1183
|
+
| end
|
1184
|
+
|
|
1185
|
+
| flow :width => 218, :margin => 4 do
|
1186
|
+
| %w(7 8 9 / 4 5 6 * 1 2 3 - 0 Clr = +).each do |btn|
|
1187
|
+
| button btn, :width => 46, :height => 46 do
|
1188
|
+
| method = case btn
|
1189
|
+
| when /[0-9]/; 'press_'+btn
|
1190
|
+
| when 'Clr'; 'press_clear'
|
1191
|
+
| when '='; 'press_equals'
|
1192
|
+
| when '+'; 'press_add'
|
1193
|
+
| when '-'; 'press_sub'
|
1194
|
+
| when '*'; 'press_times'
|
1195
|
+
| when '/'; 'press_div'
|
1196
|
+
| end
|
1197
|
+
|
|
1198
|
+
| number.send(method)
|
1199
|
+
| number_field.replace strong(number)
|
1200
|
+
| end
|
1201
|
+
| end
|
1202
|
+
| end
|
1203
|
+
| end
|
1204
|
+
|
|
1205
|
+
| end
|
1206
|
+
- control sizes/
|
1207
|
+
@shoes/
|
1208
|
+
| Shoes.app :width => 360, :height => 600, :resizable => false do
|
1209
|
+
| stroke "#dde"
|
1210
|
+
| background "#f1f5ff"
|
1211
|
+
| 13.times { |x| line 20, 142 + (30 * x), 320, 142 + (30 * x) }
|
1212
|
+
| 11.times { |x| line 20 + (30 * x), 142, 20 + (30 * x), 502 }
|
1213
|
+
|
|
1214
|
+
| stack :margin => 20 do
|
1215
|
+
| title "Control Sizes", :size => 16
|
1216
|
+
| para "This app measures various controls against a grid of lines, to be sure they size appropriately despite the platform."
|
1217
|
+
| stack :top => 122, :left => 40 do
|
1218
|
+
| button "Standard"
|
1219
|
+
| button "Margin: 2, Height: 28", :margin => 2, :height => 30
|
1220
|
+
| edit_line "Standard", :margin => 1
|
1221
|
+
| edit_line "Margin: 4, Height: 30", :height => 30, :margin => 4
|
1222
|
+
| list_box :items => ["Standard"], :choose => "Standard"
|
1223
|
+
| list_box :items => ["Margin: 4, Height: 32"],
|
1224
|
+
| :choose => "Margin: 4, Height: 32",
|
1225
|
+
| :height => 32, :margin => 4
|
1226
|
+
| progress
|
1227
|
+
| progress :height => 32, :margin => 4
|
1228
|
+
| edit_box
|
1229
|
+
| end
|
1230
|
+
| end
|
1231
|
+
| end
|
1232
|
+
- dictionary/
|
1233
|
+
@shoes/
|
1234
|
+
| Shoes.app :title => "Dictionary, powered by Definr", :width => 370, :height => 320 do
|
1235
|
+
| stack do
|
1236
|
+
| background red, :height => 60
|
1237
|
+
| flow :margin => 20 do
|
1238
|
+
| caption "Define: ", :stroke => white
|
1239
|
+
| @lookup = edit_line
|
1240
|
+
| button "Go" do
|
1241
|
+
| download "http://definr.com/definr/show/#{@lookup.text}" do |dl|
|
1242
|
+
| doc = dl.response.body.gsub(' ', ' ').
|
1243
|
+
| gsub(%r!(</a>|<br />|<a href.+?>)!, '').
|
1244
|
+
| gsub(%r!\(http://.+?\)!, '').strip
|
1245
|
+
| title, doc = doc.split(/\n+/, 2)
|
1246
|
+
| @deft.replace title
|
1247
|
+
| @defn.replace doc
|
1248
|
+
| end
|
1249
|
+
| end
|
1250
|
+
| end
|
1251
|
+
| stack :margin => 20 do
|
1252
|
+
| @deft = subtitle "", :margin => 10
|
1253
|
+
| @defn = para ""
|
1254
|
+
| end
|
1255
|
+
| end
|
1256
|
+
| end
|
1257
|
+
- pong/
|
1258
|
+
@shoes/
|
1259
|
+
| #
|
1260
|
+
| # Pong in Shoes
|
1261
|
+
| # a clone of http://billmill.org/pong.html
|
1262
|
+
| # and shoes is at http://shoooes.net
|
1263
|
+
| #
|
1264
|
+
| # This is just for kicks -- I'm very fond of NodeBox as well.
|
1265
|
+
| #
|
1266
|
+
| # There's a slightly different approach in Shoes: rather than
|
1267
|
+
| # redrawing the shapes, you can move the shapes around as objects.
|
1268
|
+
| # Yeah, see, notice how @you, @comp and @ball are used.
|
1269
|
+
| #
|
1270
|
+
| Shoes.app :width => 400, :height => 400, :resizable => false do
|
1271
|
+
| paddle_size = 75
|
1272
|
+
| ball_diameter = 20
|
1273
|
+
| vx, vy = [3, 4]
|
1274
|
+
| compuspeed = 10
|
1275
|
+
| bounce = 1.2
|
1276
|
+
|
|
1277
|
+
| # set up the playing board
|
1278
|
+
| nostroke and background white
|
1279
|
+
| @ball = oval 0, 0, ball_diameter, :fill => "#9B7"
|
1280
|
+
| @you, @comp = [app.height-4, 0].map {|y| rect 0, y, paddle_size, 4, :curve => 2}
|
1281
|
+
|
|
1282
|
+
| # animates at 40 frames per second
|
1283
|
+
| @anim = animate 40 do
|
1284
|
+
|
|
1285
|
+
| # check for game over
|
1286
|
+
| if @ball.top + ball_diameter < 0 or @ball.top > app.height
|
1287
|
+
| para strong("GAME OVER", :size => 32), "\n",
|
1288
|
+
| @ball.top < 0 ? "You win!" : "Computer wins", :top => 140, :align => 'center'
|
1289
|
+
| @ball.hide and @anim.stop
|
1290
|
+
| end
|
1291
|
+
|
|
1292
|
+
| # move the @you paddle, following the mouse
|
1293
|
+
| @you.left = mouse[1] - (paddle_size / 2)
|
1294
|
+
| nx, ny = (@ball.left + vx).to_i, (@ball.top + vy).to_i
|
1295
|
+
|
|
1296
|
+
| # move the @comp paddle, speed based on `compuspeed` variable
|
1297
|
+
| @comp.left +=
|
1298
|
+
| if nx + (ball_diameter / 2) > @comp.left + paddle_size; compuspeed
|
1299
|
+
| elsif nx < @comp.left; -compuspeed
|
1300
|
+
| else 0 end
|
1301
|
+
|
|
1302
|
+
| # if the @you paddle hits the ball
|
1303
|
+
| if ny + ball_diameter > app.height and vy > 0 and
|
1304
|
+
| (0..paddle_size).include? nx + (ball_diameter / 2) - @you.left
|
1305
|
+
| vx, vy = (nx - @you.left - (paddle_size / 2)) * 0.25, -vy * bounce
|
1306
|
+
| ny = app.height - ball_diameter
|
1307
|
+
| end
|
1308
|
+
|
|
1309
|
+
| # if the @comp paddle hits the ball
|
1310
|
+
| if ny < 0 and vy < 0 and
|
1311
|
+
| (0..paddle_size).include? nx + (ball_diameter / 2) - @comp.left
|
1312
|
+
| vx, vy = (nx - @comp.left - (paddle_size / 2)) * 0.25, -vy * bounce
|
1313
|
+
| ny = 0
|
1314
|
+
| elsif nx + ball_diameter > app.width or nx < 0
|
1315
|
+
| vx = -vx
|
1316
|
+
| end
|
1317
|
+
|
|
1318
|
+
| @ball.move nx, ny
|
1319
|
+
| end
|
1320
|
+
| end
|
1321
|
+
- curve/
|
1322
|
+
@shoes/
|
1323
|
+
| #
|
1324
|
+
| # based on the cairo curve_to example
|
1325
|
+
| # http://www.cairographics.org/samples/curve_to/
|
1326
|
+
| #
|
1327
|
+
| Shoes.app do
|
1328
|
+
| x, y = 25.6, 128.0
|
1329
|
+
| x1 = 102.4; y1 = 230.4
|
1330
|
+
| x2 = 153.6; y2 = 25.6
|
1331
|
+
| x3 = 230.4; y3 = 128.0
|
1332
|
+
|
|
1333
|
+
| nofill
|
1334
|
+
| strokewidth 10.0
|
1335
|
+
| shape do
|
1336
|
+
| move_to x, y
|
1337
|
+
| curve_to x1, y1, x2, y2, x3, y3
|
1338
|
+
| end
|
1339
|
+
|
|
1340
|
+
| strokewidth 6.0
|
1341
|
+
| stroke rgb(1.0, 0.2, 0.2, 0.6)
|
1342
|
+
| shape do
|
1343
|
+
| move_to x, y
|
1344
|
+
| line_to x1, y1
|
1345
|
+
| move_to x2, y2
|
1346
|
+
| line_to x3, y3
|
1347
|
+
| end
|
1348
|
+
| end
|
1349
|
+
- downloader/
|
1350
|
+
@shoes/
|
1351
|
+
| Shoes.app do
|
1352
|
+
| background "#eee"
|
1353
|
+
| @list = stack do
|
1354
|
+
| para "Enter a URL to download:", :margin => [10, 8, 10, 0]
|
1355
|
+
| flow :margin => 10 do
|
1356
|
+
| @url = edit_line :width => -120
|
1357
|
+
| button "Download", :width => 120 do
|
1358
|
+
| @list.append do
|
1359
|
+
| stack do
|
1360
|
+
| background "#eee".."#ccd"
|
1361
|
+
| stack :margin => 10 do
|
1362
|
+
| dl = nil
|
1363
|
+
| para @url.text, " [", link("cancel") { dl.abort }, "]", :margin => 0
|
1364
|
+
| d = inscription "Beginning transfer.", :margin => 0
|
1365
|
+
| p = progress :width => 1.0, :height => 14
|
1366
|
+
| dl = download @url.text, :save => File.basename(@url.text),
|
1367
|
+
| :progress => proc { |dl|
|
1368
|
+
| d.text = "Transferred #{dl.transferred} of #{dl.length} bytes (#{dl.percent}%)"
|
1369
|
+
| p.fraction = dl.percent * 0.01 },
|
1370
|
+
| :finish => proc { |dl| d.text = "Download completed" }
|
1371
|
+
| end
|
1372
|
+
| end
|
1373
|
+
| end
|
1374
|
+
| end
|
1375
|
+
| end
|
1376
|
+
| end
|
1377
|
+
| end
|
1378
|
+
- editor/
|
1379
|
+
@shoes/
|
1380
|
+
| str, t = "", nil
|
1381
|
+
| Shoes.app :height => 500, :width => 450 do
|
1382
|
+
| background rgb(77, 77, 77)
|
1383
|
+
| stack :margin => 10 do
|
1384
|
+
| para span("TEXT EDITOR", :stroke => red, :fill => white), " * USE ALT-Q TO QUIT", :stroke => white
|
1385
|
+
| end
|
1386
|
+
| stack :margin => 10 do
|
1387
|
+
| t = para "", :font => "Monospace 12px", :stroke => white
|
1388
|
+
| t.cursor = -1
|
1389
|
+
| end
|
1390
|
+
| keypress do |k|
|
1391
|
+
| case k
|
1392
|
+
| when String
|
1393
|
+
| str += k
|
1394
|
+
| when :backspace
|
1395
|
+
| str.slice!(-1)
|
1396
|
+
| when :tab
|
1397
|
+
| str += " "
|
1398
|
+
| when :alt_q
|
1399
|
+
| quit
|
1400
|
+
| when :alt_c
|
1401
|
+
| self.clipboard = str
|
1402
|
+
| when :alt_v
|
1403
|
+
| str += self.clipboard
|
1404
|
+
| end
|
1405
|
+
| t.replace str
|
1406
|
+
| end
|
1407
|
+
| end
|
1408
|
+
- slide/
|
1409
|
+
@shoes/
|
1410
|
+
| #
|
1411
|
+
| # mimicking the mootools demo for Fx.Slide
|
1412
|
+
| # http://demos.mootools.net/Fx.Slide
|
1413
|
+
| #
|
1414
|
+
| Shoes.app do
|
1415
|
+
| def stop_anim
|
1416
|
+
| @anim.stop
|
1417
|
+
| @anim = nil
|
1418
|
+
| end
|
1419
|
+
| def slide_anim &blk
|
1420
|
+
| stop_anim if @anim
|
1421
|
+
| @anim = animate 30, &blk
|
1422
|
+
| end
|
1423
|
+
| def slide_out slot
|
1424
|
+
| slide_anim do |i|
|
1425
|
+
| slot.height = 150 - (i * 3)
|
1426
|
+
| slot.contents[0].top = -i * 3
|
1427
|
+
| if slot.height == 0
|
1428
|
+
| stop_anim
|
1429
|
+
| slot.hide
|
1430
|
+
| end
|
1431
|
+
| end
|
1432
|
+
| end
|
1433
|
+
| def slide_in slot
|
1434
|
+
| slot.show
|
1435
|
+
| slide_anim do |i|
|
1436
|
+
| slot.height = i * 6
|
1437
|
+
| slot.contents[0].top = slot.height - 150
|
1438
|
+
| stop_anim if slot.height == 150
|
1439
|
+
| end
|
1440
|
+
| end
|
1441
|
+
|
|
1442
|
+
| background white
|
1443
|
+
| stack :margin => 10 do
|
1444
|
+
| para link("slide out") { slide_out @lipsum }, " | ",
|
1445
|
+
| link("slide in") { slide_in @lipsum }
|
1446
|
+
| @lipsum = stack :width => 1.0, :height => 150 do
|
1447
|
+
| stack do
|
1448
|
+
| background "#ddd"
|
1449
|
+
| border "#eee", :strokewidth => 5
|
1450
|
+
| para "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", :margin => 10
|
1451
|
+
| end
|
1452
|
+
| end
|
1453
|
+
| end
|
1454
|
+
| end
|
1455
|
+
- timer/
|
1456
|
+
@shoes/
|
1457
|
+
| Shoes.app :height => 150, :width => 250 do
|
1458
|
+
| background rgb(240, 250, 208)
|
1459
|
+
| stack :margin => 10 do
|
1460
|
+
| button "Start" do
|
1461
|
+
| @time = Time.now
|
1462
|
+
| @label.replace "Stop watch started at #@time"
|
1463
|
+
| end
|
1464
|
+
| button "Stop" do
|
1465
|
+
| @label.replace "Stopped, ", strong("#{Time.now - @time}"), " seconds elapsed."
|
1466
|
+
| end
|
1467
|
+
| @label = para "Press ", strong("start"), " to begin timing."
|
1468
|
+
| end
|
1469
|
+
| end
|
1470
|
+
- docs/
|
1471
|
+
- install shoes/
|
1472
|
+
- 1) Go here
|
1473
|
+
@http://shoesrb.com/downloads
|
1474
|
+
|
1475
|
+
- 2) Download the .dmg
|
1476
|
+
| Currently this menu is mac-only, but it should be simple
|
1477
|
+
| to update it to support other platforms.
|
1478
|
+
|
1479
|
+
- 3) Drag Shoes.app to /Applications
|
1480
|
+
- try it out/
|
1481
|
+
Run some of the examples in this menu.
|
1482
|
+
<<< examples/
|