rubysketch-solitaire 0.1.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +3 -0
  3. data/.gitignore +2 -0
  4. data/.hooks/post-commit +37 -0
  5. data/.hooks/pre-commit +69 -0
  6. data/ChangeLog.md +27 -0
  7. data/Gemfile.lock +10 -10
  8. data/Podfile.lock +33 -33
  9. data/README.md +8 -0
  10. data/Rakefile +107 -10
  11. data/RubySolitaire/Ad.swift +88 -0
  12. data/RubySolitaire/Assets.xcassets/AppIcon.appiconset/AppIcon.png +0 -0
  13. data/RubySolitaire/Assets.xcassets/AppIcon.appiconset/Contents.json +3 -87
  14. data/RubySolitaire/Base.lproj/Localizable.strings +6 -0
  15. data/RubySolitaire/Extensions.swift +5 -0
  16. data/RubySolitaire/GameView.swift +75 -13
  17. data/RubySolitaire/Helper.swift +70 -0
  18. data/RubySolitaire/MenuScreen.swift +141 -0
  19. data/RubySolitaire/RubySolitaireApp.swift +188 -1
  20. data/RubySolitaire/SafariView.swift +16 -0
  21. data/RubySolitaire/Strings.swift +48 -0
  22. data/RubySolitaire/ja.lproj/InfoPlist.strings +4 -0
  23. data/RubySolitaire/ja.lproj/Localizable.strings +6 -0
  24. data/VERSION +1 -1
  25. data/data/card.png +0 -0
  26. data/data/classicPSPWave.glsl +94 -0
  27. data/data/colorfulUnderwaterBubbles2.glsl +113 -0
  28. data/data/cosmic2.glsl +121 -0
  29. data/data/reflectiveHexes.glsl +219 -0
  30. data/fastlane/Fastfile +76 -0
  31. data/fastlane/metadata/copyright.txt +1 -0
  32. data/fastlane/metadata/en-US/apple_tv_privacy_policy.txt +1 -0
  33. data/fastlane/metadata/en-US/description.txt +5 -0
  34. data/fastlane/metadata/en-US/keywords.txt +1 -0
  35. data/fastlane/metadata/en-US/marketing_url.txt +1 -0
  36. data/fastlane/metadata/en-US/name.txt +1 -0
  37. data/fastlane/metadata/en-US/privacy_url.txt +1 -0
  38. data/fastlane/metadata/en-US/promotional_text.txt +1 -0
  39. data/fastlane/metadata/en-US/release_notes.txt +1 -0
  40. data/fastlane/metadata/en-US/subtitle.txt +1 -0
  41. data/fastlane/metadata/en-US/support_url.txt +1 -0
  42. data/fastlane/metadata/ja/apple_tv_privacy_policy.txt +1 -0
  43. data/fastlane/metadata/ja/description.txt +5 -0
  44. data/fastlane/metadata/ja/keywords.txt +1 -0
  45. data/fastlane/metadata/ja/marketing_url.txt +1 -0
  46. data/fastlane/metadata/ja/name.txt +1 -0
  47. data/fastlane/metadata/ja/privacy_url.txt +1 -0
  48. data/fastlane/metadata/ja/promotional_text.txt +1 -0
  49. data/fastlane/metadata/ja/release_notes.txt +1 -0
  50. data/fastlane/metadata/ja/subtitle.txt +1 -0
  51. data/fastlane/metadata/ja/support_url.txt +1 -0
  52. data/fastlane/metadata/primary_category.txt +1 -0
  53. data/fastlane/metadata/primary_first_sub_category.txt +1 -0
  54. data/fastlane/metadata/primary_second_sub_category.txt +1 -0
  55. data/fastlane/metadata/review_information/demo_password.txt +1 -0
  56. data/fastlane/metadata/review_information/demo_user.txt +1 -0
  57. data/fastlane/metadata/review_information/email_address.txt +1 -0
  58. data/fastlane/metadata/review_information/first_name.txt +0 -0
  59. data/fastlane/metadata/review_information/last_name.txt +0 -0
  60. data/fastlane/metadata/review_information/notes.txt +1 -0
  61. data/fastlane/metadata/review_information/phone_number.txt +0 -0
  62. data/fastlane/metadata/secondary_category.txt +1 -0
  63. data/fastlane/metadata/secondary_first_sub_category.txt +1 -0
  64. data/fastlane/metadata/secondary_second_sub_category.txt +1 -0
  65. data/lib/rubysketch/solitaire/background.rb +115 -12
  66. data/lib/rubysketch/solitaire/card.rb +10 -87
  67. data/lib/rubysketch/solitaire/common/animation.rb +34 -34
  68. data/lib/rubysketch/solitaire/common/button.rb +49 -19
  69. data/lib/rubysketch/solitaire/common/dialog.rb +78 -21
  70. data/lib/rubysketch/solitaire/common/scene.rb +15 -4
  71. data/lib/rubysketch/solitaire/common/settings.rb +15 -3
  72. data/lib/rubysketch/solitaire/common/timer.rb +2 -2
  73. data/lib/rubysketch/solitaire/common/transitions.rb +12 -6
  74. data/lib/rubysketch/solitaire/common/utils.rb +15 -0
  75. data/lib/rubysketch/solitaire/klondike.rb +382 -81
  76. data/lib/rubysketch/solitaire/places.rb +12 -102
  77. data/lib/rubysketch/solitaire/skin.rb +151 -0
  78. data/lib/rubysketch/solitaire.rb +33 -9
  79. data/main.rb +1 -0
  80. data/project.yml +85 -4
  81. metadata +55 -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