shoes 4.0.0.pre11 → 4.0.0.pre12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. checksums.yaml +4 -4
  2. data/bin/shoes-stub +8 -0
  3. data/lib/shoes/samples.rb +8 -0
  4. data/samples/README +122 -0
  5. data/samples/avatar.png +0 -0
  6. data/samples/blue-box.png +0 -0
  7. data/samples/class-book.yaml +387 -0
  8. data/samples/class_book.rb +46 -0
  9. data/samples/cy.png +0 -0
  10. data/samples/expert_definr.rb +24 -0
  11. data/samples/expert_funnies.rb +60 -0
  12. data/samples/expert_game_of_life.rb +224 -0
  13. data/samples/expert_irb.rb +109 -0
  14. data/samples/expert_minesweeper.rb +272 -0
  15. data/samples/expert_othello.rb +323 -0
  16. data/samples/expert_pong.rb +69 -0
  17. data/samples/expert_snake.rb +88 -0
  18. data/samples/expert_tankspank.rb +425 -0
  19. data/samples/expert_tetris.rb +322 -0
  20. data/samples/good_arc.rb +51 -0
  21. data/samples/good_bounce.rb +34 -0
  22. data/samples/good_clock.rb +55 -0
  23. data/samples/good_displace.rb +99 -0
  24. data/samples/good_follow.rb +27 -0
  25. data/samples/good_image_dl.rb +13 -0
  26. data/samples/good_potato_chopping.rb +23 -0
  27. data/samples/good_psychidelic_circles.rb +73 -0
  28. data/samples/good_reminder.rb +163 -0
  29. data/samples/good_vjot.rb +65 -0
  30. data/samples/lib/require_me.rb +6 -0
  31. data/samples/loogink.png +0 -0
  32. data/samples/menu-corner1.png +0 -0
  33. data/samples/menu-corner2.png +0 -0
  34. data/samples/menu-gray.png +0 -0
  35. data/samples/menu-left.png +0 -0
  36. data/samples/menu-right.png +0 -0
  37. data/samples/menu-top.png +0 -0
  38. data/samples/nks_booklist.rb +25 -0
  39. data/samples/nks_breadsticks.rb +8 -0
  40. data/samples/nks_dancing_circle.rb +14 -0
  41. data/samples/nks_dictionary.rb +24 -0
  42. data/samples/nks_edit_box.rb +5 -0
  43. data/samples/nks_edit_line.rb +7 -0
  44. data/samples/nks_notes.rb +16 -0
  45. data/samples/nks_poem.rb +23 -0
  46. data/samples/nks_self.rb +7 -0
  47. data/samples/nks_text_sizes.rb +11 -0
  48. data/samples/nks_trurl.rb +6 -0
  49. data/samples/potato_chopping/1258_s001.gif +0 -0
  50. data/samples/potato_chopping/1258_s002.gif +0 -0
  51. data/samples/potato_chopping/1258_s003.gif +0 -0
  52. data/samples/potato_chopping/1258_s004.gif +0 -0
  53. data/samples/potato_chopping/1258_s005.gif +0 -0
  54. data/samples/potato_chopping/1258_s006.gif +0 -0
  55. data/samples/potato_chopping/1258_s007.gif +0 -0
  56. data/samples/potato_chopping/1258_s008.gif +0 -0
  57. data/samples/potato_chopping/1258_s009.gif +0 -0
  58. data/samples/potato_chopping/1258_s010.gif +0 -0
  59. data/samples/potato_chopping/1258_s011.gif +0 -0
  60. data/samples/potato_chopping/1258_s012.gif +0 -0
  61. data/samples/potato_chopping/1258_s013.gif +0 -0
  62. data/samples/potato_chopping/1258_s014.gif +0 -0
  63. data/samples/potato_chopping/1258_s015.gif +0 -0
  64. data/samples/potato_chopping/1258_s016.gif +0 -0
  65. data/samples/potato_chopping/1258_s017.gif +0 -0
  66. data/samples/potato_chopping/1258_s018.gif +0 -0
  67. data/samples/potato_chopping/1258_s019.gif +0 -0
  68. data/samples/potato_chopping/1258_s020.gif +0 -0
  69. data/samples/potato_chopping/1258_s021.gif +0 -0
  70. data/samples/potato_chopping/1258_s022.gif +0 -0
  71. data/samples/potato_chopping/1258_s023.gif +0 -0
  72. data/samples/potato_chopping/1258_s024.gif +0 -0
  73. data/samples/potato_chopping/1258_s025.gif +0 -0
  74. data/samples/potato_chopping/1258_s026.gif +0 -0
  75. data/samples/potato_chopping/1258_s027.gif +0 -0
  76. data/samples/potato_chopping/1258_s028.gif +0 -0
  77. data/samples/potato_chopping/1258_s029.gif +0 -0
  78. data/samples/potato_chopping/1258_s030.gif +0 -0
  79. data/samples/potato_chopping/1258_s031.gif +0 -0
  80. data/samples/potato_chopping/1258_s032.gif +0 -0
  81. data/samples/potato_chopping/1258_s033.gif +0 -0
  82. data/samples/potato_chopping/1258_s034.gif +0 -0
  83. data/samples/potato_chopping/1258_s035.gif +0 -0
  84. data/samples/potato_chopping/1258_s036.gif +0 -0
  85. data/samples/potato_chopping/1258_s037.gif +0 -0
  86. data/samples/potato_chopping/1258_s038.gif +0 -0
  87. data/samples/potato_chopping/1258_s039.gif +0 -0
  88. data/samples/potato_chopping/1258_s040.gif +0 -0
  89. data/samples/potato_chopping/1258_s041.gif +0 -0
  90. data/samples/potato_chopping/1258_s042.gif +0 -0
  91. data/samples/potato_chopping/1258_s043.gif +0 -0
  92. data/samples/potato_chopping/1258_s044.gif +0 -0
  93. data/samples/potato_chopping/1258_s045.gif +0 -0
  94. data/samples/potato_chopping/1258_s046.gif +0 -0
  95. data/samples/potato_chopping/1258_s047.gif +0 -0
  96. data/samples/potato_chopping/1258_s048.gif +0 -0
  97. data/samples/potato_chopping/1258_s049.gif +0 -0
  98. data/samples/potato_chopping/1258_s050.gif +0 -0
  99. data/samples/potato_chopping/1258_s051.gif +0 -0
  100. data/samples/potato_chopping/1258_s052.gif +0 -0
  101. data/samples/potato_chopping/1258_s053.gif +0 -0
  102. data/samples/potato_chopping/1258_s054.gif +0 -0
  103. data/samples/potato_chopping/1258_s055.gif +0 -0
  104. data/samples/potato_chopping/1258_s056.gif +0 -0
  105. data/samples/potato_chopping/1258_s057.gif +0 -0
  106. data/samples/potato_chopping/1258_s058.gif +0 -0
  107. data/samples/potato_chopping/1258_s059.gif +0 -0
  108. data/samples/red-box.png +0 -0
  109. data/samples/shape_arc_to.rb +10 -0
  110. data/samples/simple-form.shy +0 -0
  111. data/samples/simple_accordion.rb +98 -0
  112. data/samples/simple_alert.rb +10 -0
  113. data/samples/simple_altered_para.rb +11 -0
  114. data/samples/simple_anim_shapes.rb +18 -0
  115. data/samples/simple_anim_text.rb +14 -0
  116. data/samples/simple_animate.rb +12 -0
  117. data/samples/simple_arc.rb +25 -0
  118. data/samples/simple_attach.rb +26 -0
  119. data/samples/simple_border_image.rb +9 -0
  120. data/samples/simple_borderless.rb +4 -0
  121. data/samples/simple_bounce.rb +27 -0
  122. data/samples/simple_breadsticks.rb +11 -0
  123. data/samples/simple_breadsticks2.rb +11 -0
  124. data/samples/simple_brightness_transitions.rb +16 -0
  125. data/samples/simple_button_animate.rb +17 -0
  126. data/samples/simple_buttons.rb +4 -0
  127. data/samples/simple_calc.rb +67 -0
  128. data/samples/simple_calc_2.rb +49 -0
  129. data/samples/simple_clipboard.rb +17 -0
  130. data/samples/simple_color_selector.rb +11 -0
  131. data/samples/simple_color_transitions.rb +12 -0
  132. data/samples/simple_concentric_circles.rb +9 -0
  133. data/samples/simple_console.rb +12 -0
  134. data/samples/simple_control_sizes.rb +25 -0
  135. data/samples/simple_count_and_draw.rb +19 -0
  136. data/samples/simple_curve.rb +34 -0
  137. data/samples/simple_dialogs.rb +33 -0
  138. data/samples/simple_dialogs_outside.rb +17 -0
  139. data/samples/simple_displace.rb +16 -0
  140. data/samples/simple_downloader.rb +29 -0
  141. data/samples/simple_draw.rb +16 -0
  142. data/samples/simple_editor.rb +30 -0
  143. data/samples/simple_face.rb +15 -0
  144. data/samples/simple_flashing.rb +22 -0
  145. data/samples/simple_flow_wrap.rb +12 -0
  146. data/samples/simple_font.rb +18 -0
  147. data/samples/simple_form.rb +30 -0
  148. data/samples/simple_fullscreen.rb +5 -0
  149. data/samples/simple_gradient_shapes.rb +23 -0
  150. data/samples/simple_guess_game.rb +30 -0
  151. data/samples/simple_image_as_stroke.rb +22 -0
  152. data/samples/simple_image_fill.rb +13 -0
  153. data/samples/simple_image_stroke.rb +13 -0
  154. data/samples/simple_info.rb +10 -0
  155. data/samples/simple_iterated_content.rb +8 -0
  156. data/samples/simple_keypress.rb +15 -0
  157. data/samples/simple_logo_display.rb +15 -0
  158. data/samples/simple_loogink_cy.rb +33 -0
  159. data/samples/simple_lorem_ipsum.rb +19 -0
  160. data/samples/simple_manual.rb +5 -0
  161. data/samples/simple_menu.rb +42 -0
  162. data/samples/simple_mouse_follow.rb +9 -0
  163. data/samples/simple_oval.rb +6 -0
  164. data/samples/simple_polygon_line.rb +19 -0
  165. data/samples/simple_position_as_we_go.rb +10 -0
  166. data/samples/simple_progress_bar.rb +15 -0
  167. data/samples/simple_random_bubbles.rb +15 -0
  168. data/samples/simple_require.rb +9 -0
  169. data/samples/simple_sample_executor.rb +7 -0
  170. data/samples/simple_sample_executor_all.rb +14 -0
  171. data/samples/simple_sesame_street_shoes.rb +6 -0
  172. data/samples/simple_shoes_intro.rb +22 -0
  173. data/samples/simple_slide.rb +57 -0
  174. data/samples/simple_stack_flow_buttons.rb +16 -0
  175. data/samples/simple_stripes.rb +9 -0
  176. data/samples/simple_system_background.rb +5 -0
  177. data/samples/simple_text_movement.rb +8 -0
  178. data/samples/simple_tictactoe.rb +224 -0
  179. data/samples/simple_timer.rb +16 -0
  180. data/samples/simple_translate.rb +10 -0
  181. data/samples/simple_visibility.rb +20 -0
  182. metadata +186 -7
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app title: 'Shoes 4 Logo Icon!', width: 310, height: 420 do
3
+ stack do
4
+ path = File.join(Shoes::DIR, 'static/shoes-icon.png')
5
+ image path
6
+ flow do
7
+ image path
8
+ image path
9
+ end
10
+ stack do
11
+ image path
12
+ para ' ' * 20 + 'Powered by JRuby and SWT!'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ class PhotoFrame < Shoes
3
+ url '/', :index
4
+ url '/cy', :cy
5
+ url '/loogink', :loogink
6
+
7
+ def index
8
+ send(['loogink', 'cy'].sample)
9
+ end
10
+
11
+ def loogink
12
+ display tomato, white, "Loogink", "Cy", "She is Loogink"
13
+ end
14
+
15
+ def cy
16
+ display paleturquoise, gray, "Cy", "Loogink", "He is Cy"
17
+ end
18
+
19
+ def display(bg_color, fg_color, name, other, message)
20
+ background bg_color
21
+ stack do
22
+ inscription 'Shoes 4', left: 80
23
+ image File.expand_path(File.join(__FILE__, "../#{name.downcase}.png")),
24
+ left: 75, top: 25
25
+ para fg(strong(message), fg_color),
26
+ ' ->',
27
+ link(strong(other), click: "/#{other.downcase}"),
28
+ left: 35, top: 85
29
+ end
30
+ end
31
+ end
32
+
33
+ Shoes.app width: 200, height: 120, title: 'Photo Frame'
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ TEXT = <<EOS
3
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, \
4
+ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
5
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris \
6
+ nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in \
7
+ reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
8
+ Excepteur sint occaecat cupidatat non proident, sunt in \
9
+ culpa qui officia deserunt mollit anim id est laborum.
10
+ EOS
11
+
12
+ NL = "\n\n"
13
+
14
+ Shoes.app do
15
+ lines = TEXT.split("\n")
16
+ para lines[0], NL
17
+ para lines[1], NL, justify: true
18
+ para lines[2], NL, leading: 0
19
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ require 'shoes/manual'
3
+
4
+ Shoes::Manual.run 'English'
5
+ # Shoes.show_manual 'Japanese'
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ class MenuPanel < Shoes::Widget
3
+ def self.boxes
4
+ @boxes ||= []
5
+ end
6
+
7
+ def boxes
8
+ self.class.boxes
9
+ end
10
+
11
+ def initialize_widget(color, _args)
12
+ boxes << self
13
+ background color
14
+ para link("Box #{boxes.length}", fg: white, fill: nil, click: "/"),
15
+ margin: 18, align: "center", size: 20
16
+ hover { expand }
17
+ end
18
+
19
+ def expand
20
+ if width < 170
21
+ a = animate 30 do
22
+ boxes.each do |b|
23
+ b.width -= 5 if (b != self) && b.width > 140
24
+ end
25
+ self.width += 5
26
+ a.stop if self.width >= 170
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ Shoes.app width: 600, height: 130 do
33
+ style(Shoes::Link, underline: nil)
34
+
35
+ # hover styling currently a no-op https://github.com/shoes/shoes4/issues/638
36
+ style(Shoes::LinkHover, fill: nil, underline: nil)
37
+
38
+ menu_panel green, width: 170, height: 120, margin: 4
39
+ menu_panel blue, width: 140, height: 120, margin: 4
40
+ menu_panel red, width: 140, height: 120, margin: 4
41
+ menu_panel purple, width: 140, height: 120, margin: 4
42
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app width: 200, height: 200 do
3
+ background black
4
+ fill white
5
+ @circ = oval 0, 0, 100
6
+ motion do |top, left|
7
+ @circ.move top - 50, left - 50
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app do
3
+ oval 100, 100, 100, 50
4
+ oval 200, 200, radius: 50
5
+ oval 300, 300, diameter: 100
6
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app width: 300, height: 300 do
3
+ COLORS = Shoes::COLORS
4
+ i = 45
5
+ button 'new' do
6
+ i += 5
7
+ box = rand(2).zero? ? rect(i, i, 20) : oval(i, i, 20)
8
+ box.style fill: send(COLORS.keys[rand(COLORS.keys.size)])
9
+ @flag = false
10
+
11
+ box.click do
12
+ @flag = true
13
+ @box = box
14
+ end
15
+
16
+ box.release { @flag = false }
17
+ end
18
+ motion { |left, top| @box.move(left - 10, top - 10) if @flag }
19
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app do
3
+ stack do
4
+ @p = para "Things get placed"
5
+ @b = button "as we go"
6
+
7
+ para "#{@p.element_left}, #{@p.element_top} -> #{@p.width}, #{@p.height}"
8
+ para "#{@b.element_left}, #{@b.element_top} -> #{@b.width}, #{@b.height}"
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app height: 200 do
3
+ stack margin_left: 10 do
4
+ title 'Progress Example'
5
+ @p = para
6
+ end
7
+
8
+ pg = progress width: width - 20, height: 20
9
+ pg.move 10, 100
10
+ animate do |i|
11
+ j = i % 100 + 1
12
+ pg.fraction = j / 100.0
13
+ @p.text = format("%2d%", (pg.fraction * 100))
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ class Range
3
+ def rand
4
+ Random.new.rand self
5
+ end
6
+ end
7
+
8
+ Shoes.app width: 300, height: 400 do
9
+ fill rgb(0, 0.6, 0.9, 0.1)
10
+ stroke rgb(0, 0.6, 0.9, 0.3)
11
+ strokewidth 1
12
+ 100.times do
13
+ oval left: rand(-30..width), top: rand(-30..height), diameter: rand(25..100)
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # This app tests that we are correctly setting load paths so files relative to
4
+ # the executing directory can be required withour require_relative shenanigans.
5
+ require 'lib/require_me'
6
+
7
+ Shoes.app do
8
+ para RequireMe.message
9
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app width: 650, height: 32 do
3
+ path = File.expand_path(File.join(__FILE__, ".."))
4
+ %w(simple_altered_para simple_border_image simple_breadsticks simple_buttons).each do |name|
5
+ button(name) { load "#{path}/#{name}.rb" }
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app title: 'Samples Executor', width: 540, height: 640 do
3
+ BUTTON_HEIGHT = 20
4
+
5
+ samples = File.join(File.dirname(__FILE__), "*.rb")
6
+
7
+ stack do
8
+ Dir.glob(samples).sort.each_with_index do |file, index|
9
+ button = button(File.basename(file)) { load file }
10
+
11
+ button.style top: (BUTTON_HEIGHT * index), height: BUTTON_HEIGHT
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app width: 300, height: 100 do
3
+ background forestgreen, curve: 30
4
+ border gold, strokewidth: 5, curve: 30
5
+ subtitle fg(strong('Shoes Shoes Shoes'), white), align: 'center'
6
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app width: 700, height: 600 do
3
+ title "Shoes is a ", link("tiny") { alert "Cool!" }, " graphics toolkit. "
4
+
5
+ flow width: 0.4 do
6
+ image File.join(Shoes::DIR, 'static/shoes-icon.png'),
7
+ click: ->() { alert "You're soooo quick!" }
8
+ end
9
+
10
+ flow width: 0.6 do
11
+ tagline "It's simple and straightforward. ",
12
+ link("Shoes ") { alert "Yay!" },
13
+ "was born to be easy! ",
14
+ link("Shoes ") { alert "Yay!" },
15
+ "was born to be easy! ",
16
+ link("Shoes ") { alert "Yay!" },
17
+ "was born to be easy! ",
18
+ "Really, it was made for absolute beginners. "
19
+ end
20
+
21
+ subtitle link(strong(em("There's ", fg(bg("really ", "really ", yellow), "#f00"), "nothing to it. "))) { alert "Have fun!" }
22
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # mimicking the mootools demo for Fx.Slide
4
+ # http://demos.mootools.net/Fx.Slide
5
+ #
6
+ Shoes.app do
7
+ def stop_anim
8
+ @anim.stop
9
+ @anim = nil
10
+ end
11
+
12
+ def slide_anim(&blk)
13
+ stop_anim if @anim
14
+ @anim = animate 30, &blk
15
+ end
16
+
17
+ def slide_out(slot)
18
+ slide_anim do |i|
19
+ slot.height = 150 - (i * 3)
20
+ slot.contents[0].top = -i * 3
21
+ if slot.height.zero?
22
+ stop_anim
23
+ slot.hide
24
+ end
25
+ end
26
+ end
27
+
28
+ def slide_in(slot)
29
+ slot.show
30
+ slide_anim do |i|
31
+ slot.height = i * 6
32
+ slot.contents[0].top = slot.height - 150
33
+ stop_anim if slot.height == 150
34
+ end
35
+ end
36
+
37
+ background white
38
+ stack margin: 10 do
39
+ para link("slide out") { slide_out @lipsum }, " | ",
40
+ link("slide in") { slide_in @lipsum }
41
+ @lipsum = stack width: 1.0, height: 150 do
42
+ stack do
43
+ background "#ddd"
44
+ border "#eee", strokewidth: 5
45
+ para "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed " \
46
+ "do eiusmod tempor incididunt ut labore et dolore magna " \
47
+ "aliqua. Ut enim ad minim veniam, quis nostrud exercitation " \
48
+ "ullamco laboris nisi ut aliquip ex ea commodo consequat. " \
49
+ "Duis aute irure dolor in reprehenderit in voluptate velit " \
50
+ "esse cillum dolore eu fugiat nulla pariatur. Excepteur sint " \
51
+ "occaecat cupidatat non proident, sunt in culpa qui officia " \
52
+ "deserunt mollit anim id est laborum.",
53
+ margin: 10
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app do
3
+ flow(width: 300) { 3.times { |i| button "hello#{i}" } }
4
+ stack(width: 100) { 3.times { |i| button "hello#{i}" } }
5
+ button 'bye bye1'
6
+ flow(width: 0.5) { 2.times { flow(width: 0.5) { 2.times { |j| button "Yayyyy#{j}" } } } }
7
+ flow(width: 0.3) { button 'go go go go go' }
8
+
9
+ stack(width: 0.3) do
10
+ edit_line
11
+ 3.times { para 'hello' }
12
+ end
13
+
14
+ flow(width: 0.3) { image File.join(Shoes::DIR, 'static/shoes-icon.png') }
15
+ button 'bye bye2'
16
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app do
3
+ spacing = 20
4
+
5
+ 50.times do |i|
6
+ line(0, spacing * i, 1000, 1000 + (spacing * i))
7
+ line(spacing * (i + 1), 0, 1000 + (spacing * (i + 1)), 1000)
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app do
3
+ fill system_background
4
+ title "This should be in the default system background color for your OS", align: 'center'
5
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ Shoes.app width: 400, height: 300 do
3
+ el = edit_line text: 'Hello Shoes 4' do |s|
4
+ @msg.text = s.text
5
+ end
6
+ button('move at random') { @msg.move rand(200), 50 + rand(200) }
7
+ @msg = para el.text
8
+ end
@@ -0,0 +1,224 @@
1
+ # frozen_string_literal: true
2
+ # Size module
3
+ # Change these constants to change the size of the board.
4
+ module SIZE
5
+ PADDING = 10
6
+ TIC_SIZE = 60
7
+ B_RADIUS = 5
8
+ WIDTH = HEIGHT = (PADDING * 4) + (TIC_SIZE * 5)
9
+ end
10
+
11
+ class Tic
12
+ attr_reader :x, :y, :checked, :rect, :player
13
+
14
+ def initialize(tic)
15
+ @rect = tic[0]
16
+ @x = tic[1]
17
+ @y = tic[2]
18
+ @player = false
19
+ @checked = false
20
+ end
21
+
22
+ def check
23
+ @checked = true
24
+ end
25
+
26
+ def reset
27
+ @player = false
28
+ @checked = false
29
+ @rect.style fill: '#000000'
30
+ end
31
+
32
+ def player=(symbol)
33
+ @player = symbol
34
+ if symbol == 'X'
35
+ @rect.style fill: '#00FFFF'
36
+ else
37
+ @rect.style fill: '#FFFF44'
38
+ end
39
+ end
40
+ end
41
+
42
+ # Mostly keeps track of the Player.
43
+ # 'class Player' would imply two.
44
+ class Game
45
+ def initialize(player_label)
46
+ @player = false
47
+ @label = player_label
48
+ end
49
+
50
+ def restart
51
+ (@player = true) && toggle_player # English syntax!
52
+ end
53
+
54
+ def toggle_player
55
+ @player = !@player
56
+ @label.text = player_turn
57
+ end
58
+
59
+ def player_symbol
60
+ @player ? 'X' : 'O'
61
+ end
62
+
63
+ def player_color
64
+ @player ? "Yellow" : "Blue"
65
+ end
66
+
67
+ def player_turn
68
+ "#{player_color}'s Turn"
69
+ end
70
+
71
+ def player_win
72
+ @player = !@player
73
+ "#{player_color} won!"
74
+ end
75
+ end
76
+
77
+ # Organizes the Tics and
78
+ # checks for winning configurations
79
+ class Board
80
+ attr_reader :game
81
+
82
+ def initialize(tics, player_label)
83
+ @tics = []
84
+ @over = false
85
+ @game = Game.new player_label
86
+
87
+ tics.each do |tic| # Each tic in this array is of the format: [rect, x, y]
88
+ @tics << Tic.new(tic)
89
+ end
90
+ end
91
+
92
+ # Actions for each of the tic's rectangle
93
+ def set_tic_actions
94
+ @tics.each do |tic|
95
+ tic.rect.click do
96
+ unless tic.checked || @over
97
+ @game.toggle_player
98
+ tic.player = @game.player_symbol
99
+ tic.check
100
+ if check_if_over
101
+ show_screen(game.player_win)
102
+ elsif full?
103
+ show_screen("Cat's Game!")
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ def check_if_over
111
+ (1..3).any? { |i| all_checked?(row(i)) || all_checked?(column(i)) } ||
112
+ all_checked?(diagonal1) ||
113
+ all_checked?(diagonal2)
114
+ end
115
+
116
+ # Takes an array and tells if each Tic has been checked by the same player
117
+ def all_checked?(array)
118
+ if array[0].checked
119
+ array[0].player == array[1].player && array[1].player == array[2].player
120
+ else
121
+ false
122
+ end
123
+ end
124
+
125
+ # In this case, the screen is the
126
+ # end screen, including the transparent
127
+ # black panel and its buttons.
128
+ def screen=(screen)
129
+ @screen = screen
130
+ end
131
+
132
+ def clear_screen
133
+ @screen.each(&:hide)
134
+ end
135
+
136
+ def show_screen(title)
137
+ @over = true
138
+ @screen.each(&:show)
139
+ @screen[0].text = title
140
+ end
141
+
142
+ def restart
143
+ clear_screen
144
+ @over = false
145
+ @tics.each(&:reset)
146
+ game.restart
147
+ end
148
+
149
+ def row(x)
150
+ @tics.select { |tic| tic.x == x }
151
+ end
152
+
153
+ def column(y)
154
+ @tics.select { |tic| tic.y == y }
155
+ end
156
+
157
+ def diagonal1
158
+ tics = []
159
+ (1..3).each do |i|
160
+ tics.concat(@tics.select { |tic| tic.x == i && tic.y == i })
161
+ end
162
+ tics
163
+ end
164
+
165
+ def diagonal2
166
+ tics = []
167
+ (1..3).each do |i|
168
+ tics.concat(@tics.select { |tic| tic.x == i && tic.y == (4 - i) })
169
+ end
170
+ tics
171
+ end
172
+
173
+ def full?
174
+ @tics.select(&:checked).length == 9
175
+ end
176
+ end
177
+
178
+ # Main GUI App
179
+ Shoes.app width: SIZE::WIDTH, height: SIZE::HEIGHT, title: 'Tic Tac Toe' do
180
+ background gradient(slategray, slateblue)
181
+
182
+ stroke white
183
+
184
+ title 'Tic Tac Toe', align: 'center', family: 'Lacuna', top: 10
185
+
186
+ flow bottom: 10 do
187
+ @player_label = para "Blue's Turn", size: 20, align: 'center'
188
+ end
189
+
190
+ stroke black
191
+
192
+ # The making of the Tic rectangles, along with a record of their coordinates.
193
+ tics = []
194
+ (1..3).each do |i|
195
+ (1..3).each do |j|
196
+ rectangle = rect((SIZE::PADDING + SIZE::TIC_SIZE) * i, (SIZE::PADDING + SIZE::TIC_SIZE) * j, SIZE::TIC_SIZE, SIZE::TIC_SIZE, SIZE::B_RADIUS)
197
+ tics << [rectangle, i, j]
198
+ end
199
+ end
200
+
201
+ stroke black
202
+
203
+ board = Board.new(tics, @player_label) # player_label is passed on to the game.
204
+
205
+ board.set_tic_actions
206
+
207
+ # End Screen
208
+ black_screen = rect 0, 0, SIZE::WIDTH, SIZE::WIDTH, fill: app.rgb(0, 0, 0, 0.5)
209
+ flow align: 'center' do
210
+ # Positioned oddly because 'align: "center"' appears not to work on buttons
211
+ @quit_button = button 'Quit', top: 174, left: (SIZE::WIDTH / 2 - 34) do
212
+ exit
213
+ end
214
+ # Positioned oddly because 'align: "center"' appears not to work on buttons
215
+ @restart_button = button 'Restart', top: 140, align: 'center', left: (SIZE::WIDTH / 2 - 43) do
216
+ board.restart
217
+ end
218
+ end
219
+ alert_title = title "Cat's Game!", stroke: white, top: 90, align: 'center', size: 34
220
+ exit_screen = [alert_title, black_screen, @quit_button, @restart_button]
221
+
222
+ board.screen = exit_screen # Passes pre-made gui on for later use
223
+ board.clear_screen # Clears it to begin with
224
+ end