xiki 0.6.4 → 0.6.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 "^[A-Z]")
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
@@ -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 the browser isn\'t open. Open it?\n@app/Firefox/"
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
 
@@ -42,7 +42,7 @@ class Piano
42
42
  def self.menu
43
43
 
44
44
  %`
45
- > Pass in notes
45
+ > Pass in notes (start GarageBand first)
46
46
  | g cdefg c c
47
47
  - .setup/
48
48
  - .instrument/
@@ -1,10 +1,15 @@
1
- class Foo
1
+ class Sammiches
2
2
  def self.menu
3
3
  "
4
- - sammiches/
4
+ - meat/
5
5
  - ham/
6
6
  - .buy/
7
- - tofu/
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
- "- buying #{item} #{category}"
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
@@ -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('&nbsp;', ' ').
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/