rubysketch-solitaire 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +3 -0
  3. data/.gitignore +2 -0
  4. data/.hooks/pre-commit +69 -0
  5. data/ChangeLog.md +21 -0
  6. data/Gemfile.lock +10 -10
  7. data/Podfile.lock +33 -33
  8. data/Rakefile +107 -10
  9. data/RubySolitaire/Ad.swift +88 -0
  10. data/RubySolitaire/Assets.xcassets/AppIcon.appiconset/AppIcon.png +0 -0
  11. data/RubySolitaire/Assets.xcassets/AppIcon.appiconset/Contents.json +3 -87
  12. data/RubySolitaire/Base.lproj/Localizable.strings +6 -0
  13. data/RubySolitaire/Extensions.swift +5 -0
  14. data/RubySolitaire/GameView.swift +75 -13
  15. data/RubySolitaire/Helper.swift +70 -0
  16. data/RubySolitaire/MenuScreen.swift +140 -0
  17. data/RubySolitaire/RubySolitaireApp.swift +188 -1
  18. data/RubySolitaire/SafariView.swift +16 -0
  19. data/RubySolitaire/Strings.swift +48 -0
  20. data/RubySolitaire/ja.lproj/InfoPlist.strings +4 -0
  21. data/RubySolitaire/ja.lproj/Localizable.strings +6 -0
  22. data/VERSION +1 -1
  23. data/data/card.png +0 -0
  24. data/data/classicPSPWave.glsl +94 -0
  25. data/data/colorfulUnderwaterBubbles2.glsl +113 -0
  26. data/data/cosmic2.glsl +121 -0
  27. data/data/reflectiveHexes.glsl +219 -0
  28. data/fastlane/Fastfile +76 -0
  29. data/fastlane/metadata/copyright.txt +1 -0
  30. data/fastlane/metadata/en-US/apple_tv_privacy_policy.txt +1 -0
  31. data/fastlane/metadata/en-US/description.txt +5 -0
  32. data/fastlane/metadata/en-US/keywords.txt +1 -0
  33. data/fastlane/metadata/en-US/marketing_url.txt +1 -0
  34. data/fastlane/metadata/en-US/name.txt +1 -0
  35. data/fastlane/metadata/en-US/privacy_url.txt +1 -0
  36. data/fastlane/metadata/en-US/promotional_text.txt +1 -0
  37. data/fastlane/metadata/en-US/release_notes.txt +1 -0
  38. data/fastlane/metadata/en-US/subtitle.txt +1 -0
  39. data/fastlane/metadata/en-US/support_url.txt +1 -0
  40. data/fastlane/metadata/ja/apple_tv_privacy_policy.txt +1 -0
  41. data/fastlane/metadata/ja/description.txt +5 -0
  42. data/fastlane/metadata/ja/keywords.txt +1 -0
  43. data/fastlane/metadata/ja/marketing_url.txt +1 -0
  44. data/fastlane/metadata/ja/name.txt +1 -0
  45. data/fastlane/metadata/ja/privacy_url.txt +1 -0
  46. data/fastlane/metadata/ja/promotional_text.txt +1 -0
  47. data/fastlane/metadata/ja/release_notes.txt +1 -0
  48. data/fastlane/metadata/ja/subtitle.txt +1 -0
  49. data/fastlane/metadata/ja/support_url.txt +1 -0
  50. data/fastlane/metadata/primary_category.txt +1 -0
  51. data/fastlane/metadata/primary_first_sub_category.txt +1 -0
  52. data/fastlane/metadata/primary_second_sub_category.txt +1 -0
  53. data/fastlane/metadata/review_information/demo_password.txt +1 -0
  54. data/fastlane/metadata/review_information/demo_user.txt +1 -0
  55. data/fastlane/metadata/review_information/email_address.txt +1 -0
  56. data/fastlane/metadata/review_information/first_name.txt +0 -0
  57. data/fastlane/metadata/review_information/last_name.txt +0 -0
  58. data/fastlane/metadata/review_information/notes.txt +1 -0
  59. data/fastlane/metadata/review_information/phone_number.txt +0 -0
  60. data/fastlane/metadata/secondary_category.txt +1 -0
  61. data/fastlane/metadata/secondary_first_sub_category.txt +1 -0
  62. data/fastlane/metadata/secondary_second_sub_category.txt +1 -0
  63. data/lib/rubysketch/solitaire/background.rb +115 -12
  64. data/lib/rubysketch/solitaire/card.rb +10 -87
  65. data/lib/rubysketch/solitaire/common/animation.rb +34 -34
  66. data/lib/rubysketch/solitaire/common/button.rb +49 -19
  67. data/lib/rubysketch/solitaire/common/dialog.rb +78 -21
  68. data/lib/rubysketch/solitaire/common/scene.rb +15 -4
  69. data/lib/rubysketch/solitaire/common/settings.rb +10 -2
  70. data/lib/rubysketch/solitaire/common/timer.rb +2 -2
  71. data/lib/rubysketch/solitaire/common/transitions.rb +12 -6
  72. data/lib/rubysketch/solitaire/common/utils.rb +15 -0
  73. data/lib/rubysketch/solitaire/klondike.rb +378 -81
  74. data/lib/rubysketch/solitaire/places.rb +12 -102
  75. data/lib/rubysketch/solitaire/skin.rb +151 -0
  76. data/lib/rubysketch/solitaire.rb +33 -9
  77. data/main.rb +1 -0
  78. data/project.yml +85 -4
  79. metadata +54 -2
@@ -0,0 +1 @@
1
+ 「宇宙ソリティア」で最高のソリティア体験をお楽しみください!鮮やかなグラフィック、シンプルな操作性、そして中毒性の高いゲームプレイがあなたを待っています。時間を忘れて、ストレスを解消しながらカードを並べましょう。ソリティア愛好家のための最適な選択です。
@@ -0,0 +1 @@
1
+ - 軽微な不具合をいくつか修正
@@ -0,0 +1 @@
1
+ 選べるカードデザイン、選べるゲーム背景
@@ -0,0 +1 @@
1
+ https://xord.org/rubysolitaire/
@@ -0,0 +1 @@
1
+ GAMES
@@ -0,0 +1 @@
1
+ xordog@gmail.com
@@ -0,0 +1 @@
1
+ GAMES_CARD
@@ -1,34 +1,137 @@
1
+ # -*- coding: utf-8 -*-
1
2
  using RubySketch
2
3
 
3
4
 
4
5
  class Background < Scene
5
6
 
6
- def initialize()
7
+ TYPES = {
8
+ checker: {
9
+ name: 'Checker'
10
+ },
11
+ checker2: {
12
+ name: 'Checker (No Scroll)'
13
+ },
14
+ cosmic2: {
15
+ name: 'Cosmic 2',
16
+ author: 'huwb',
17
+ url: 'https://www.shadertoy.com/view/XllGzN'
18
+ },
19
+ classicPSPWave: {
20
+ name: 'Classic PSP Wave',
21
+ author: 'ParkingLotGames',
22
+ url: 'https://www.shadertoy.com/view/ddV3DK'
23
+ },
24
+ reflectiveHexes: {
25
+ name: 'Reflective hexes',
26
+ author: 'mrange',
27
+ url: 'https://www.shadertoy.com/view/ds2XRt'
28
+ },
29
+ colorfulUnderwaterBubbles2: {
30
+ name: 'Colorful underwater bubbles II',
31
+ author: 'mrange',
32
+ url: 'https://www.shadertoy.com/view/mlBSWc'
33
+ },
34
+ }
35
+
36
+ def initialize(type = nil)
7
37
  super
8
- @start = now
38
+ @start = now
39
+ @canvas = createGraphics width, height
40
+ set type || settings['background']&.intern|| types.first
41
+ end
42
+
43
+ attr_reader :type
44
+
45
+ def types()
46
+ TYPES.keys
47
+ end
48
+
49
+ def name()
50
+ TYPES[type][:name]
51
+ end
52
+
53
+ def author()
54
+ TYPES[type][:author]
55
+ end
56
+
57
+ def url()
58
+ TYPES[type][:url]
59
+ end
60
+
61
+ def nextType()
62
+ index = types.index(@type) || 0
63
+ types[(index + 1) % types.size]
64
+ end
65
+
66
+ def set(type)
67
+ type = types.first unless types.include?(type)
68
+ case type
69
+ when :checker, :checker2
70
+ @shader = createShader nil, checker
71
+ when :cosmic2
72
+ @shader = createShader nil, cosmic2
73
+ when :classicPSPWave
74
+ @shader = createShader nil, classicPSPWave
75
+ when :reflectiveHexes
76
+ @shader = createShader nil, reflectiveHexes
77
+ when :colorfulUnderwaterBubbles2
78
+ @shader = createShader nil, colorfulUnderwaterBubbles2
79
+ end
80
+ settings['background'] = @type = type
9
81
  end
10
82
 
11
83
  def draw()
12
- pushStyle do
13
- checker.set :time, now - @start
14
- shader checker
15
- rect 0, 0, width, height
84
+ @canvas.beginDraw do |g|
85
+ sh = @shader
86
+ case type
87
+ when :checker, :checker2
88
+ colors = skin.backgroundCheckerColors
89
+ sh.set :iTime, (type == :checker2 ? 0.0 : now - @start)
90
+ sh.set :color1, *colors[0].map {|n| n / 255.0}
91
+ sh.set :color2, *colors[1].map {|n| n / 255.0}
92
+ else
93
+ sh.set :iTime, now - @start
94
+ sh.set :iResolution, width, height
95
+ end
96
+ g.shader sh
97
+ g.translate 0, g.height
98
+ g.scale 1, -1
99
+ g.rect 0, 0, g.width, g.height
16
100
  end
101
+ copy @canvas, 0, 0, @canvas.width, @canvas.height, 0, 0, width, height
17
102
  end
18
103
 
19
104
  private
20
105
 
21
106
  def checker()
22
- @checker ||= createShader nil, <<~END
107
+ <<~END
23
108
  varying vec4 vertTexCoord;
24
- uniform float time;
109
+ uniform float iTime;
110
+ uniform vec4 color1;
111
+ uniform vec4 color2;
25
112
  void main() {
26
- float t = mod(time, 32.0) * 16.0;
27
- float x = mod(vertTexCoord.x + t, 32.0) < 16.0 ? 1.0 : 0.0;
28
- float y = mod(vertTexCoord.y + t, 32.0) < 16.0 ? 1.0 : 0.0;
29
- gl_FragColor = x != y ? vec4(0.6, 0.9, 0.7, 1) : vec4(0.7, 0.9, 0.6, 1);
113
+ float t = mod(iTime, 32.) * 8.;
114
+ float x = mod( vertTexCoord.x + t, 32.) < 16. ? 1. : 0.;
115
+ float y = mod(-vertTexCoord.y + t, 32.) < 16. ? 1. : 0.;
116
+ gl_FragColor = x != y ? color1 : color2;
30
117
  }
31
118
  END
32
119
  end
33
120
 
121
+ def cosmic2()
122
+ File.read(dataPath 'cosmic2.glsl').gsub('iMouse', 'vec2(0.)')
123
+ end
124
+
125
+ def classicPSPWave()
126
+ File.read(dataPath 'classicPSPWave.glsl')
127
+ end
128
+
129
+ def reflectiveHexes()
130
+ File.read(dataPath 'reflectiveHexes.glsl')
131
+ end
132
+
133
+ def colorfulUnderwaterBubbles2()
134
+ File.read(dataPath 'colorfulUnderwaterBubbles2.glsl')
135
+ end
136
+
34
137
  end# Background
@@ -90,7 +90,7 @@ class Card
90
90
  end
91
91
 
92
92
  def color()
93
- self.class.markColor mark
93
+ skin.markColor mark
94
94
  end
95
95
 
96
96
  def count()
@@ -111,11 +111,13 @@ class Card
111
111
  end
112
112
 
113
113
  def sprite()
114
- @sprite ||= Sprite.new(0, 0, *spriteSize, image: closedImage).tap do |sp|
114
+ @sprite ||= Sprite.new(
115
+ 0, 0, *skin.cardSpriteSize, image: skin.closedImage
116
+ ).tap do |sp|
115
117
  sp.pivot = [rand, rand]
116
118
  sp.angle = rand -5.0..5.0
117
119
  sp.update do
118
- sp.image = @open > 90 ? openedImage : closedImage
120
+ sp.image = @open > 90 ? skin.openedImage(mark, number) : skin.closedImage
119
121
  end
120
122
  sp.draw do |&draw|
121
123
  push do
@@ -134,12 +136,17 @@ class Card
134
136
  image sp.image, 0, 0, w, h
135
137
  end
136
138
  sp.mousePressed do
139
+ next if $dragging
140
+ $dragging = self
137
141
  mousePressed sp.mouseX, sp.mouseY
138
142
  end
139
143
  sp.mouseReleased do
144
+ next unless $dragging.object_id == self.object_id
140
145
  mouseReleased sp.mouseX, sp.mouseY, sp.clickCount
146
+ $dragging = nil
141
147
  end
142
148
  sp.mouseDragged do
149
+ next unless $dragging.object_id == self.object_id
143
150
  x, y = sp.mouseX, sp.mouseY
144
151
  mouseDragged x, y, x - sp.pmouseX, y - sp.pmouseY
145
152
  end
@@ -169,88 +176,4 @@ class Card
169
176
  self.pos += createVector x - @startPos.x, y - @startPos.y if @startPos
170
177
  end
171
178
 
172
- def openedImage()
173
- @openedImage ||= createGraphics(*self.class.cardSize).tap do |g|
174
- c, w, h, m = self.class, g.width, g.height, 16# margin
175
- image = c.cardImage
176
- nx, ny, nw, nh = c.numberRect number
177
- mx, my, mw, mh = c.markRect mark
178
- mnh = m + nh
179
- mxx, myy = (w - mw) / 2, mnh + ((h - mnh) - mh) / 2
180
- g.beginDraw
181
- g.angleMode DEGREES
182
- g.translate w / 2, h / 2
183
- g.rotate 180
184
- g.translate -w / 2, -h / 2
185
- g.copy image, 896, 0, w, h, 0, 0, w, h
186
- g.tint *c.markColor(mark)
187
- g.copy image, nx, ny, nw, nh, m, m, nw, nh
188
- g.copy image, mx, my, mw, mh, mxx, myy, mw, mh
189
- g.endDraw
190
- end
191
- end
192
-
193
- def closedImage()
194
- self.class.closedImages[3]
195
- end
196
-
197
- def spriteSize()
198
- self.class.spriteSize
199
- end
200
-
201
- def self.closedImages()
202
- @closedImages ||= (0..3).map {|n| n * 256}.map do |x|
203
- createGraphics(*cardSize).tap do |g|
204
- w, h = g.width, g.height
205
- g.beginDraw
206
- g.copy cardImage, x, 256, w, h, 0, 0, w, h
207
- g.endDraw
208
- end
209
- end
210
- end
211
-
212
- def self.cardImage()
213
- @cardImage ||= loadImage dataPath 'card.png'
214
- end
215
-
216
- def self.cardSize()
217
- [164, 252]
218
- end
219
-
220
- def self.spriteSize()
221
- @spriteSize ||= cardSize.then do |cw, ch|
222
- ncolumns = 7
223
- size = [width, height].min
224
- cardWidth = (size - margin * (ncolumns + 1)) / ncolumns
225
- [cardWidth, cardWidth * (ch.to_f / cw.to_f)]
226
- end
227
- end
228
-
229
- def self.margin()
230
- @marin ||= [width, height].min * 0.02
231
- end
232
-
233
- def self.markRect(mark)
234
- w = h = 128
235
- [MARKS.index(mark) * w, 0, w, h]
236
- end
237
-
238
- def self.numberRect(number)
239
- w = h = 64
240
- [(number - 1) * w, 128, w, h]
241
- end
242
-
243
- def self.markColor(mark)
244
- MARKS[0, 2].include?(mark) ? [255, 111, 61] : [62, 79, 60]
245
- end
246
-
247
- def __find_or_last_and_prev(card = nil)
248
- prev, it = nil, self
249
- while it.next
250
- break if it == card
251
- prev, it = it, it.next
252
- end
253
- return it, prev
254
- end
255
-
256
179
  end# Card
@@ -2,40 +2,40 @@ using RubySketch
2
2
 
3
3
 
4
4
  EASINGS = {
5
- linear: lambda { |x| x },
6
- sineIn: lambda { |x| 1.0 - Math.cos(x * Math::PI / 2) },
7
- sineOut: lambda { |x| Math.sin(x * Math::PI / 2) },
8
-
9
- quadIn: lambda { |x| quadIn x },
10
- cubicIn: lambda { |x| cubicIn x },
11
- quartIn: lambda { |x| quartIn x },
12
- quintIn: lambda { |x| quintIn x },
13
- circIn: lambda { |x| circIn x },
14
- backIn: lambda { |x| backIn x },
15
- expoIn: lambda { |x| expoIn x },
16
- elasticIn: lambda { |x| elasticIn x },
17
- bounceIn: lambda { |x| 1.0 - bounceOut(1.0 - x) },
18
-
19
- quadOut: lambda { |x| 1.0 - quadIn(1.0 - x) },
20
- cubicOut: lambda { |x| 1.0 - cubicIn(1.0 - x) },
21
- quartOut: lambda { |x| 1.0 - quartIn(1.0 - x) },
22
- quintOut: lambda { |x| 1.0 - quintIn(1.0 - x) },
23
- circOut: lambda { |x| 1.0 - curcIn(1.0 - x) },
24
- backOut: lambda { |x| 1.0 - backIn(1.0 - x) },
25
- expoOut: lambda { |x| 1.0 - expoIn(1.0 - x) },
26
- elasticOut: lambda { |x| 1.0 - elasticIn(1.0 - x) },
27
- bounceOut: lambda { |x| bounceOut x },
28
-
29
- sineInOut: lambda { |x| x < 0.5 ? sineIn(x) : sineOut(x) },
30
- quadInOut: lambda { |x| x < 0.5 ? quadIn(x) : quadOut(x) },
31
- cubicInOut: lambda { |x| x < 0.5 ? cubicIn(x) : cubicOut(x) },
32
- quartInOut: lambda { |x| x < 0.5 ? quartIn(x) : quartOut(x) },
33
- quintInOut: lambda { |x| x < 0.5 ? quintIn(x) : quintOut(x) },
34
- circInOut: lambda { |x| x < 0.5 ? circIn(x) : circOut(x) },
35
- backInOut: lambda { |x| x < 0.5 ? backIn(x) : backOut(x) },
36
- expoInOut: lambda { |x| x < 0.5 ? expoIn(x) : expoOut(x) },
37
- elasticInOut: lambda { |x| x < 0.5 ? elasticIn(x) : elasticOut(x) },
38
- bounceInOut: lambda { |x| x < 0.5 ? bounceIn(x) : bounceOut(x) }
5
+ linear: lambda {|x| x},
6
+ sineIn: lambda {|x| 1.0 - Math.cos(x * Math::PI / 2)},
7
+ sineOut: lambda {|x| Math.sin(x * Math::PI / 2)},
8
+
9
+ quadIn: lambda {|x| quadIn x},
10
+ cubicIn: lambda {|x| cubicIn x},
11
+ quartIn: lambda {|x| quartIn x},
12
+ quintIn: lambda {|x| quintIn x},
13
+ circIn: lambda {|x| circIn x},
14
+ backIn: lambda {|x| backIn x},
15
+ expoIn: lambda {|x| expoIn x},
16
+ elasticIn: lambda {|x| elasticIn x},
17
+ bounceIn: lambda {|x| 1.0 - bounceOut(1.0 - x)},
18
+
19
+ quadOut: lambda {|x| 1.0 - quadIn(1.0 - x)},
20
+ cubicOut: lambda {|x| 1.0 - cubicIn(1.0 - x)},
21
+ quartOut: lambda {|x| 1.0 - quartIn(1.0 - x)},
22
+ quintOut: lambda {|x| 1.0 - quintIn(1.0 - x)},
23
+ circOut: lambda {|x| 1.0 - curcIn(1.0 - x)},
24
+ backOut: lambda {|x| 1.0 - backIn(1.0 - x)},
25
+ expoOut: lambda {|x| 1.0 - expoIn(1.0 - x)},
26
+ elasticOut: lambda {|x| 1.0 - elasticIn(1.0 - x)},
27
+ bounceOut: lambda {|x| bounceOut x},
28
+
29
+ sineInOut: lambda {|x| x < 0.5 ? sineIn(x) : sineOut(x)},
30
+ quadInOut: lambda {|x| x < 0.5 ? quadIn(x) : quadOut(x)},
31
+ cubicInOut: lambda {|x| x < 0.5 ? cubicIn(x) : cubicOut(x)},
32
+ quartInOut: lambda {|x| x < 0.5 ? quartIn(x) : quartOut(x)},
33
+ quintInOut: lambda {|x| x < 0.5 ? quintIn(x) : quintOut(x)},
34
+ circInOut: lambda {|x| x < 0.5 ? circIn(x) : circOut(x)},
35
+ backInOut: lambda {|x| x < 0.5 ? backIn(x) : backOut(x)},
36
+ expoInOut: lambda {|x| x < 0.5 ? expoIn(x) : expoOut(x)},
37
+ elasticInOut: lambda {|x| x < 0.5 ? elasticIn(x) : elasticOut(x)},
38
+ bounceInOut: lambda {|x| x < 0.5 ? bounceIn(x) : bounceOut(x)}
39
39
  }
40
40
 
41
41
  def quadIn(x)
@@ -6,10 +6,14 @@ class Button < Sprite
6
6
  include CanDisable
7
7
 
8
8
  def initialize(
9
- label, *args, rgb: 200, width: 1, fontSize: 24, round: 5, **kwargs, &block)
9
+ label = nil, *args,
10
+ icon: nil, rgb: nil, width: 1, fontSize: 24, round: 5, **kwargs,
11
+ &block)
12
+
13
+ raise if !label && !icon
10
14
 
11
15
  super 0, 0, 44 * width, 44, *args, **kwargs, &block
12
- @label, @rgb, @fontSize, @round = label, [rgb].flatten, fontSize, round
16
+ @label, @icon, @rgb, @fontSize, @round = label, icon, rgb, fontSize, round
13
17
  @click = nil
14
18
  setup
15
19
  end
@@ -23,31 +27,53 @@ class Button < Sprite
23
27
 
24
28
  def setup()
25
29
  pressing = false
26
- mousePressed {pressing = true}
27
- mouseReleased {pressing = false}
30
+
31
+ mousePressed do
32
+ next if $dragging
33
+ $dragging = self
34
+ pressing = true
35
+ end
36
+
37
+ mouseReleased do
38
+ next unless $dragging.object_id == self.object_id
39
+ pressing = false
40
+ if includeMouse?
41
+ if enabled?
42
+ @click&.call
43
+ else
44
+ shake
45
+ end
46
+ sound.play gain: 0.5
47
+ end
48
+ $dragging = nil
49
+ end
50
+
51
+ mouseDragged do
52
+ next unless $dragging.object_id == self.object_id
53
+ pressing = includeMouse?
54
+ end
28
55
 
29
56
  draw do
30
57
  offset = 5
58
+ light = [@rgb || skin.buttonColor].flatten
59
+ dark = light.map {|n| n - 32}
31
60
  y = pressing ? (offset - (enabled? ? 2 : 3.5)) : 0
32
61
  h = self.h - y
33
62
  offset -= y
34
- fill *@rgb.map {|n| n - 20}
63
+ fill *dark
35
64
  rect 0, y, w, h, *@round
36
- fill *@rgb
37
- rect 0, y, w, h - offset, *@round
38
- fill enabled? ? 255 : 180
39
- textAlign CENTER, CENTER
40
- textSize @fontSize
41
- text @label, 0, y, w, h - offset
42
- end
43
-
44
- mouseClicked do
45
- if enabled?
46
- @click.call if @click
47
- else
48
- shake
65
+ fill *light
66
+ h -= offset
67
+ rect 0, y, w, h, *@round
68
+ fill *(enabled? ? [255] : dark)
69
+ if @label
70
+ textAlign CENTER, CENTER
71
+ textSize @fontSize
72
+ text @label, 0, y, w, h
73
+ elsif @icon
74
+ imageMode CENTER
75
+ drawImage @icon, w / 2, y + h / 2, @icon.width / 2, @icon.height / 2
49
76
  end
50
- sound.play gain: 0.5
51
77
  end
52
78
  end
53
79
 
@@ -64,4 +90,8 @@ class Button < Sprite
64
90
  @sound ||= loadSound dataPath 'button.mp3'
65
91
  end
66
92
 
93
+ def includeMouse?()
94
+ (0...width).include?(mouseX) && (0...height).include?(mouseY)
95
+ end
96
+
67
97
  end# Button
@@ -3,42 +3,66 @@ using RubySketch
3
3
 
4
4
  class Dialog < Scene
5
5
 
6
- def initialize(background: 0, alpha: 100, z: 1000)
7
- super
6
+ class Label < Sprite
7
+ attr_accessor :label
8
+ end
9
+
10
+ def initialize(background: 0, alpha: 100, z: 1000, &block)
8
11
  @background, @alpha = background, 0
12
+ @elements = []
13
+ super
9
14
  overlay.z = z
15
+ group :vertical, &block if block
10
16
  animate 0.2 do |t|
11
17
  @alpha = alpha * t
12
18
  end
13
19
  end
14
20
 
21
+ def group(flow = :horizontal, space: nil, &block)
22
+ old, @group = @group, []
23
+ block.call self
24
+ ensure
25
+ (old || @elements).push({
26
+ elements: @group,
27
+ flow: flow,
28
+ space: space || MARGIN
29
+ })
30
+ @group = old
31
+ updateLayout
32
+ end
33
+
15
34
  def addElement(sprite)
16
- elements.push sprite
35
+ (@group || @elements).push sprite
36
+ sprite.z = overlay.z
17
37
  addSprite sprite if active?
18
38
  updateLayout
39
+ sprite
19
40
  end
20
41
 
21
- def addLabel(label, rgb: [255], fontSize: 24, align: CENTER)
42
+ def addLabel(label, rgb: [255], alpha: nil, fontSize: 24, align: CENTER, &block)
22
43
  bounds = textFont.textBounds label, 0, 0, fontSize
23
- addElement Sprite.new(0, 0, bounds.w, bounds.h).tap {|sp|
24
- sp.z = overlay.z
44
+ addElement Label.new(0, 0, width - MARGIN * 2, bounds.h).tap {|sp|
45
+ sp.label = label
25
46
  sp.draw do
47
+ r, g, b, a = skin.translucentBackgroundColor
48
+ fill r, g, b, alpha || (a * 3)
49
+ rect 0, -MARGIN / 2, sp.w, sp.h + MARGIN
26
50
  textAlign align, CENTER
27
51
  textSize fontSize
28
52
  fill *rgb
29
- text label, 0, 0, sp.w, sp.h
53
+ text sp.label, 0, 0, sp.w, sp.h
30
54
  end
55
+ sp.mouseClicked &block
31
56
  }
32
57
  end
33
58
 
34
- def addButton(label, *args, **kwargs, &block)
35
- addElement Button.new(label, *args, **kwargs).tap {|b|
36
- b.z = overlay.z
59
+ def addButton(*args, **kwargs, &block)
60
+ addElement Button.new(*args, **kwargs).tap {|b|
37
61
  b.clicked &block
38
62
  }
39
63
  end
40
64
 
41
- def addSpace(height)
65
+ def addSpace(height = 0)
42
66
  addElement Sprite.new(0, 0, 1, height).tap {|sp|
43
67
  sp.draw {}
44
68
  }
@@ -53,18 +77,30 @@ class Dialog < Scene
53
77
  end
54
78
 
55
79
  def elements()
56
- @elements ||= []
80
+ f = -> es {es.map {|e| ((e in {elements:})) ? f[elements] : e}}
81
+ f.call(@elements).flatten
57
82
  end
58
83
 
59
84
  def draw()
60
- sprite overlay, *elements
85
+ sprite overlay
61
86
  super
87
+ sprite *elements
62
88
  end
63
89
 
64
90
  def resized(w, h)
65
91
  updateLayout
66
92
  end
67
93
 
94
+ def activated()
95
+ super
96
+ parent.pause
97
+ end
98
+
99
+ def deactivated()
100
+ parent.resume
101
+ super
102
+ end
103
+
68
104
  private
69
105
 
70
106
  MARGIN = 10
@@ -84,19 +120,40 @@ class Dialog < Scene
84
120
 
85
121
  def cancelButton()
86
122
  @cancelButton ||= Button.new('CLOSE', width: 3, fontSize: 28).tap do |sp|
87
- sp.z = overlay.z
88
123
  sp.clicked {close}
89
124
  end
90
125
  end
91
126
 
92
127
  def updateLayout()
93
- w, h = width, height
94
- allHeight = elements.map(&:height).reduce {|a, b| a + MARGIN + b} || 0
95
- y = (h - allHeight) / 2
96
- elements.each do |e|
97
- e.x = (w - e.w) / 2
98
- e.y = y
99
- y += e.h + MARGIN
128
+ element = {elements: @elements, flow: :vertical, space: MARGIN}
129
+ w, h = getSize element
130
+ setPosition element, (width - w) / 2, (height - h) / 2, w, h
131
+ end
132
+
133
+ def getSize(element)
134
+ if element in {elements:, flow:, space:}
135
+ v = flow == :vertical
136
+ sizes = elements.map {|e| getSize e}
137
+ sum = sizes.map {|size| size[v ? 1 : 0]}.reduce {|a, b| a + space + b} || 0
138
+ max = sizes.map {|size| size[v ? 0 : 1]}.max || 0
139
+ v ? [max, sum] : [sum, max]
140
+ else
141
+ [element.w, element.h]
142
+ end
143
+ end
144
+
145
+ def setPosition(element, x, y, w, h)
146
+ if element in {elements:, flow:, space:}
147
+ v = flow == :vertical
148
+ elements.each do |e|
149
+ ew, eh = getSize e
150
+ ex, ey = v ? [x + (w - ew) / 2, y] : [x, y + (h - eh) / 2]
151
+ setPosition e, ex, ey, ew, eh
152
+ x += ew + space if !v
153
+ y += eh + space if v
154
+ end
155
+ else
156
+ element.x, element.y = x, y
100
157
  end
101
158
  end
102
159