glimmer_klondike_solitaire 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +7 -0
  3. data/README.md +41 -0
  4. data/VERSION +1 -0
  5. data/app/glimmer_klondike_solitaire.rb +34 -0
  6. data/app/glimmer_klondike_solitaire/launch.rb +3 -0
  7. data/app/glimmer_klondike_solitaire/model/column_pile.rb +60 -0
  8. data/app/glimmer_klondike_solitaire/model/dealing_pile.rb +33 -0
  9. data/app/glimmer_klondike_solitaire/model/dealt_pile.rb +25 -0
  10. data/app/glimmer_klondike_solitaire/model/foundation_pile.rb +40 -0
  11. data/app/glimmer_klondike_solitaire/model/game.rb +37 -0
  12. data/app/glimmer_klondike_solitaire/model/playing_card.rb +50 -0
  13. data/app/glimmer_klondike_solitaire/view/action_panel.rb +34 -0
  14. data/app/glimmer_klondike_solitaire/view/app_view.rb +84 -0
  15. data/app/glimmer_klondike_solitaire/view/column_pile.rb +56 -0
  16. data/app/glimmer_klondike_solitaire/view/dealing_pile.rb +36 -0
  17. data/app/glimmer_klondike_solitaire/view/dealt_pile.rb +38 -0
  18. data/app/glimmer_klondike_solitaire/view/empty_playing_card.rb +45 -0
  19. data/app/glimmer_klondike_solitaire/view/foundation_pile.rb +59 -0
  20. data/app/glimmer_klondike_solitaire/view/hidden_playing_card.rb +23 -0
  21. data/app/glimmer_klondike_solitaire/view/playing_card.rb +23 -0
  22. data/app/glimmer_klondike_solitaire/view/tableau.rb +41 -0
  23. data/bin/glimmer_klondike_solitaire +13 -0
  24. data/config/warble.rb +183 -0
  25. data/images/glimmer-klondike-solitaire.png +0 -0
  26. data/images/original/10C.png +0 -0
  27. data/images/original/10D.png +0 -0
  28. data/images/original/10H.png +0 -0
  29. data/images/original/10S.png +0 -0
  30. data/images/original/11C.png +0 -0
  31. data/images/original/11D.png +0 -0
  32. data/images/original/11H.png +0 -0
  33. data/images/original/11S.png +0 -0
  34. data/images/original/12C.png +0 -0
  35. data/images/original/12D.png +0 -0
  36. data/images/original/12H.png +0 -0
  37. data/images/original/12S.png +0 -0
  38. data/images/original/13C.png +0 -0
  39. data/images/original/13D.png +0 -0
  40. data/images/original/13H.png +0 -0
  41. data/images/original/13S.png +0 -0
  42. data/images/original/1C.png +0 -0
  43. data/images/original/1D.png +0 -0
  44. data/images/original/1H.png +0 -0
  45. data/images/original/1S.png +0 -0
  46. data/images/original/2C.png +0 -0
  47. data/images/original/2D.png +0 -0
  48. data/images/original/2H.png +0 -0
  49. data/images/original/2S.png +0 -0
  50. data/images/original/3C.png +0 -0
  51. data/images/original/3D.png +0 -0
  52. data/images/original/3H.png +0 -0
  53. data/images/original/3S.png +0 -0
  54. data/images/original/4C.png +0 -0
  55. data/images/original/4D.png +0 -0
  56. data/images/original/4H.png +0 -0
  57. data/images/original/4S.png +0 -0
  58. data/images/original/5C.png +0 -0
  59. data/images/original/5D.png +0 -0
  60. data/images/original/5H.png +0 -0
  61. data/images/original/5S.png +0 -0
  62. data/images/original/6C.png +0 -0
  63. data/images/original/6D.png +0 -0
  64. data/images/original/6H.png +0 -0
  65. data/images/original/6S.png +0 -0
  66. data/images/original/7C.png +0 -0
  67. data/images/original/7D.png +0 -0
  68. data/images/original/7H.png +0 -0
  69. data/images/original/7S.png +0 -0
  70. data/images/original/8C.png +0 -0
  71. data/images/original/8D.png +0 -0
  72. data/images/original/8H.png +0 -0
  73. data/images/original/8S.png +0 -0
  74. data/images/original/9C.png +0 -0
  75. data/images/original/9D.png +0 -0
  76. data/images/original/9H.png +0 -0
  77. data/images/original/9S.png +0 -0
  78. data/images/original/BACK.png +0 -0
  79. data/images/resized/10C.png +0 -0
  80. data/images/resized/10D.png +0 -0
  81. data/images/resized/10H.png +0 -0
  82. data/images/resized/10S.png +0 -0
  83. data/images/resized/11C.png +0 -0
  84. data/images/resized/11D.png +0 -0
  85. data/images/resized/11H.png +0 -0
  86. data/images/resized/11S.png +0 -0
  87. data/images/resized/12C.png +0 -0
  88. data/images/resized/12D.png +0 -0
  89. data/images/resized/12H.png +0 -0
  90. data/images/resized/12S.png +0 -0
  91. data/images/resized/13C.png +0 -0
  92. data/images/resized/13D.png +0 -0
  93. data/images/resized/13H.png +0 -0
  94. data/images/resized/13S.png +0 -0
  95. data/images/resized/1C.png +0 -0
  96. data/images/resized/1D.png +0 -0
  97. data/images/resized/1H.png +0 -0
  98. data/images/resized/1S.png +0 -0
  99. data/images/resized/2C.png +0 -0
  100. data/images/resized/2D.png +0 -0
  101. data/images/resized/2H.png +0 -0
  102. data/images/resized/2S.png +0 -0
  103. data/images/resized/3C.png +0 -0
  104. data/images/resized/3D.png +0 -0
  105. data/images/resized/3H.png +0 -0
  106. data/images/resized/3S.png +0 -0
  107. data/images/resized/4C.png +0 -0
  108. data/images/resized/4D.png +0 -0
  109. data/images/resized/4H.png +0 -0
  110. data/images/resized/4S.png +0 -0
  111. data/images/resized/5C.png +0 -0
  112. data/images/resized/5D.png +0 -0
  113. data/images/resized/5H.png +0 -0
  114. data/images/resized/5S.png +0 -0
  115. data/images/resized/6C.png +0 -0
  116. data/images/resized/6D.png +0 -0
  117. data/images/resized/6H.png +0 -0
  118. data/images/resized/6S.png +0 -0
  119. data/images/resized/7C.png +0 -0
  120. data/images/resized/7D.png +0 -0
  121. data/images/resized/7H.png +0 -0
  122. data/images/resized/7S.png +0 -0
  123. data/images/resized/8C.png +0 -0
  124. data/images/resized/8D.png +0 -0
  125. data/images/resized/8H.png +0 -0
  126. data/images/resized/8S.png +0 -0
  127. data/images/resized/9C.png +0 -0
  128. data/images/resized/9D.png +0 -0
  129. data/images/resized/9H.png +0 -0
  130. data/images/resized/9S.png +0 -0
  131. data/images/resized/BACK.png +0 -0
  132. data/package/linux/Glimmer Klondike Solitaire.png +0 -0
  133. data/package/macosx/Glimmer Klondike Solitaire.icns +0 -0
  134. data/package/windows/Glimmer Klondike Solitaire.ico +0 -0
  135. data/vendor/jars/org/yaml/snakeyaml/1.28/snakeyaml-1.28.jar +0 -0
  136. metadata +251 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 54d996d4a509aa9da6b6600c54ba8bc464969ed0acf1b6e28f26ebf0ce534d81
4
+ data.tar.gz: 0a1617a8b1d5215a01a3945c637189f04e0a97c94634dbc5a4572fdb6cda3761
5
+ SHA512:
6
+ metadata.gz: bc27a4a541277bfc2bff4a9d54730839c8ebfc0bcc8565493836fc808b68a6fd60b5c19be583167b7368256b7b9ec3008269c195e7075065b1724a3ac1e1ae28
7
+ data.tar.gz: 6897ce772b1b1df41c8d83a89a4d4ba67b3278eada7ead56b699da14fcb8262b1e691f66a9a0b6106e82895865aab3daf7512d3c4d8d22b10ea217ee965dfafb
data/LICENSE.txt ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2021 Andy Maleh
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # <img alt="Glimmer Tetris Icon" src="https://raw.githubusercontent.com/AndyObtiva/glimmer_klondike_solitaire/master/package/linux/Glimmer%20Klondike%20Solitaire.png" height=85 /> Glimmer Klondike Solitaire
2
+
3
+ ## [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=40 /> Glimmer Application](https://github.com/AndyObtiva/glimmer-dsl-swt)
4
+
5
+ [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) External Sample.
6
+
7
+ ![Klondike Solitaire Screenshot](/images/glimmer-klondike-soliatire.png)
8
+
9
+ Note: this was built in one week only and currently suffers from performance issues, but is fully playable otherwise.
10
+
11
+ <img alt="Glimmer Klondike Solitaire Icon" src="https://raw.githubusercontent.com/AndyObtiva/glimmer_klondike_solitaire/master/package/linux/Glimmer%20Klondike%20Solitaire.png" height=40 /> [Download v1.0.1 for the Mac (x64 Big Sur and older)](https://www.dropbox.com/s/h6ntl5wma1xs6bz/Glimmer%20Klondike%20Solitaire-1.0.1.dmg?dl=1)
12
+
13
+ Contributing to glimmer_klondike_solitaire
14
+ ------------------------------------------
15
+
16
+ - Check out the latest master to make sure the feature hasn't been
17
+ implemented or the bug hasn't been fixed yet.
18
+ - Check out the issue tracker to make sure someone already hasn't
19
+ requested it and/or contributed it.
20
+ - Fork the project.
21
+ - Start a feature/bugfix branch.
22
+ - Commit and push until you are happy with your contribution.
23
+ - Make sure to add tests for it. This is important so I don't break it
24
+ in a future version unintentionally.
25
+ - Please try not to mess with the Rakefile, version, or history. If
26
+ you want to have your own version, or is otherwise necessary, that
27
+ is fine, but please isolate to its own commit so I can cherry-pick
28
+ around it.
29
+
30
+ Copyright
31
+ ---------
32
+
33
+ [MIT](LICENSE.txt)
34
+
35
+ Copyright (c) 2021 Andy Maleh
36
+
37
+ --
38
+
39
+ [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=40 />](https://github.com/AndyObtiva/glimmer) Built with [Glimmer](https://github.com/AndyObtiva/glimmer)
40
+
41
+ Logo was made by <a href="https://www.flaticon.com/authors/freepik" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon"> www.flaticon.com</a>
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.1
@@ -0,0 +1,34 @@
1
+ $LOAD_PATH.unshift(File.expand_path('..', __FILE__))
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ Bundler.require(:default)
6
+ rescue
7
+ # this runs when packaged as a gem (no bundler)
8
+ require 'glimmer-dsl-swt'
9
+ # add more gems if needed
10
+ end
11
+
12
+ require 'glimmer_klondike_solitaire/model/playing_card'
13
+
14
+ class GlimmerKlondikeSolitaire
15
+ include Glimmer
16
+
17
+ APP_ROOT = File.expand_path('../..', __FILE__)
18
+ VERSION = File.read(File.join(APP_ROOT, 'VERSION'))
19
+ LICENSE = File.read(File.join(APP_ROOT, 'LICENSE.txt'))
20
+ PLAYING_CARD_MARGIN = 10
21
+ PLAYING_CARD_WIDTH = 106
22
+ PLAYING_CARD_HEIGHT = 162
23
+ PLAYING_CARD_SPACING = 5
24
+ PLAYING_CARD_COLUMN_SPACING = 35
25
+
26
+ IMAGES = Model::PlayingCard::SUITS.reduce({}) do |hash, suit|
27
+ hash.merge(suit => 1.upto(13).reduce({}) do |suit_hash, rank|
28
+ suit_hash.merge(rank => image(File.join(APP_ROOT, 'images', 'resized', "#{rank}#{suit.to_s[0].upcase}.png")))
29
+ end)
30
+ end
31
+ IMAGE_BACK = image(File.join(APP_ROOT, 'images', 'resized', 'BACK.png'))
32
+ end
33
+
34
+ require 'glimmer_klondike_solitaire/view/app_view'
@@ -0,0 +1,3 @@
1
+ require_relative '../glimmer_klondike_solitaire'
2
+
3
+ GlimmerKlondikeSolitaire::View::AppView.launch
@@ -0,0 +1,60 @@
1
+ class GlimmerKlondikeSolitaire
2
+ module Model
3
+ class ColumnPile
4
+ include Glimmer::DataBinding::ObservableModel
5
+ attr_reader :count
6
+
7
+ def initialize(game, count)
8
+ @game = game
9
+ @count = count
10
+ reset!
11
+ end
12
+
13
+ def reset!
14
+ playing_cards.clear
15
+ populate!(count.times.map { @game.deck.pop })
16
+ notify_observers(:playing_cards)
17
+ end
18
+
19
+ # this method does not validate
20
+ def populate!(new_playing_cards)
21
+ new_playing_cards.each_with_index do |playing_card, index|
22
+ playing_card.hidden = true if index < (new_playing_cards.size - 1)
23
+ playing_cards.push(playing_card)
24
+ end
25
+ end
26
+
27
+ # this method validates that playing card fits at the bottom of the column (opposite color and one number smaller)
28
+ # throws an error if it does not fit
29
+ def add!(new_playing_card)
30
+ bottom_card = playing_cards.last
31
+ if (playing_cards.empty? && new_playing_card.rank == 13) ||
32
+ (new_playing_card.color != bottom_card.color && new_playing_card.rank == (bottom_card.rank - 1))
33
+ playing_cards.push(new_playing_card)
34
+ else
35
+ raise "Cannot add #{new_playing_card} to #{self}"
36
+ end
37
+ end
38
+
39
+ def remove!(card)
40
+ remove_cards_starting_at(playing_cards.index(card)).tap do |result|
41
+ playing_cards.last&.hidden = false
42
+ end
43
+ end
44
+
45
+ def remove_cards_starting_at(index)
46
+ removed_cards = playing_cards[index...playing_cards.size]
47
+ playing_cards[index...playing_cards.size] = []
48
+ removed_cards
49
+ end
50
+
51
+ def playing_cards
52
+ @playing_cards ||= []
53
+ end
54
+
55
+ def to_s
56
+ "Column Pile #{count} (#{playing_cards.map {|card| "#{card.rank}#{card.suit.to_s[0].upcase}"}.join(" | ")})"
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,33 @@
1
+ class GlimmerKlondikeSolitaire
2
+ module Model
3
+ class DealingPile
4
+ DEALING_INITIAL_COUNT = 24
5
+
6
+ def initialize(game)
7
+ @game = game
8
+ reset!
9
+ end
10
+
11
+ def reset!
12
+ playing_cards.clear
13
+ DEALING_INITIAL_COUNT.times { playing_cards << @game.deck.pop }
14
+ end
15
+
16
+ def deal!
17
+ playing_card = playing_cards.shift
18
+ if playing_card.nil?
19
+ @game.dealt_pile.playing_cards.each do |a_playing_card|
20
+ playing_cards << a_playing_card
21
+ end
22
+ @game.dealt_pile.playing_cards.clear
23
+ else
24
+ @game.dealt_pile.push!(playing_card)
25
+ end
26
+ end
27
+
28
+ def playing_cards
29
+ @playing_cards ||= []
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ class GlimmerKlondikeSolitaire
2
+ module Model
3
+ class DealtPile
4
+ def initialize(game)
5
+ @game = game
6
+ end
7
+
8
+ def reset!
9
+ playing_cards.clear
10
+ end
11
+
12
+ def push!(playing_card)
13
+ playing_cards.push(playing_card)
14
+ end
15
+
16
+ def remove!(card)
17
+ playing_cards.delete(card)
18
+ end
19
+
20
+ def playing_cards
21
+ @playing_cards ||= []
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,40 @@
1
+ class GlimmerKlondikeSolitaire
2
+ module Model
3
+ class FoundationPile
4
+ attr_reader :suit
5
+
6
+ def initialize(game, suit)
7
+ @game = game
8
+ @suit = suit
9
+ reset!
10
+ end
11
+
12
+ def reset!
13
+ playing_cards.clear
14
+ end
15
+
16
+ # adds a card
17
+ # validates if it is a card that belongs to the suit
18
+ def add!(playing_card)
19
+ if playing_card.suit == suit &&
20
+ (
21
+ (playing_cards.empty? && playing_card.rank == 1) ||
22
+ playing_card.rank == (playing_cards.last.rank + 1)
23
+ )
24
+ playing_cards.push(playing_card)
25
+ else
26
+ raise "Cannot add #{playing_card} to #{self}"
27
+ end
28
+ end
29
+
30
+ def playing_cards
31
+ @playing_cards ||= []
32
+ end
33
+
34
+ def to_s
35
+ "Foundation Pile #{suit} (#{playing_cards.map {|card| "#{card.rank}#{card.suit.to_s[0].upcase}"}.join(" | ")})"
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,37 @@
1
+ require_relative 'playing_card'
2
+ require_relative 'dealt_pile'
3
+ require_relative 'dealing_pile'
4
+ require_relative 'column_pile'
5
+ require_relative 'foundation_pile'
6
+
7
+ class GlimmerKlondikeSolitaire
8
+ module Model
9
+ class Game
10
+ COLUMN_PILE_COUNT = 7
11
+
12
+ attr_reader :deck, :dealing_pile, :dealt_pile, :column_piles, :foundation_piles
13
+
14
+ def initialize
15
+ @deck = new_deck
16
+ @dealt_pile = DealtPile.new(self)
17
+ @dealing_pile = DealingPile.new(self)
18
+ @column_piles = COLUMN_PILE_COUNT.times.map {|n| ColumnPile.new(self, n + 1)}
19
+ @foundation_piles = PlayingCard::SUITS.map {|suit| FoundationPile.new(self, suit)}
20
+ end
21
+
22
+ def restart!
23
+ @deck = new_deck
24
+ @dealt_pile.reset!
25
+ @dealing_pile.reset!
26
+ @column_piles.each(&:reset!)
27
+ @foundation_piles.each(&:reset!)
28
+ end
29
+
30
+ private
31
+
32
+ def new_deck
33
+ PlayingCard.deck.shuffle
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,50 @@
1
+ class GlimmerKlondikeSolitaire
2
+ module Model
3
+ class PlayingCard
4
+ SUITS = [:spades, :hearts, :clubs, :diamonds]
5
+ BLACK_SUITS = [:spades, :clubs]
6
+ RED_SUITS = [:hearts, :diamonds]
7
+ RANK_COUNT = 13
8
+
9
+ class << self
10
+ def deck
11
+ suit_decks.flatten
12
+ end
13
+
14
+ def suit_decks
15
+ SUITS.map do |suit|
16
+ suit_deck(suit)
17
+ end
18
+ end
19
+
20
+ def suit_deck(suit)
21
+ 1.upto(RANK_COUNT).map do |rank|
22
+ new(rank, suit)
23
+ end
24
+ end
25
+ end
26
+
27
+ attr_reader :rank, :suit
28
+ attr_accessor :hidden
29
+ alias hidden? hidden
30
+
31
+ def initialize(rank, suit, hidden = false)
32
+ @rank = rank
33
+ @suit = suit
34
+ @hidden = hidden
35
+ end
36
+
37
+ def color
38
+ if BLACK_SUITS.include?(suit)
39
+ :black
40
+ elsif RED_SUITS.include?(suit)
41
+ :red
42
+ end
43
+ end
44
+
45
+ def to_s
46
+ "Playing Card #{rank}#{suit.to_s[0].upcase}"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,34 @@
1
+ require 'glimmer_klondike_solitaire/model/game'
2
+
3
+ class GlimmerKlondikeSolitaire
4
+ module View
5
+ class ActionPanel
6
+ include Glimmer::UI::CustomWidget
7
+
8
+ option :game
9
+
10
+ body {
11
+ composite {
12
+ grid_layout(1, false) {
13
+ margin_width 10
14
+ margin_height 0
15
+ margin_top 10
16
+ margin_bottom 5
17
+ }
18
+
19
+ background :dark_green
20
+
21
+ button {
22
+ layout_data :center, :center, true, false
23
+
24
+ text 'Restart Game'
25
+
26
+ on_widget_selected {
27
+ game.restart!
28
+ }
29
+ }
30
+ }
31
+ }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,84 @@
1
+ require_relative 'tableau'
2
+ require_relative 'action_panel'
3
+
4
+ class GlimmerKlondikeSolitaire
5
+ module View
6
+ class AppView
7
+ include Glimmer::UI::CustomShell
8
+
9
+ before_body {
10
+ @game = Model::Game.new
11
+ Display.app_name = 'Glimmer Klondike Solitaire'
12
+ Display.app_version = VERSION
13
+ @display = display {
14
+ on_about {
15
+ display_about_dialog
16
+ }
17
+ on_preferences {
18
+ display_preferences_dialog
19
+ }
20
+ }
21
+ }
22
+
23
+ body {
24
+ shell {
25
+ row_layout(:vertical) {
26
+ fill true
27
+ center true
28
+ margin_width 0
29
+ margin_height 0
30
+ }
31
+ minimum_size 2*PLAYING_CARD_MARGIN + 7*(PLAYING_CARD_WIDTH + PLAYING_CARD_SPACING) - PLAYING_CARD_SPACING, 700 + PLAYING_CARD_MARGIN
32
+ image File.join(APP_ROOT, 'package', 'linux', "Glimmer Klondike Solitaire.png")
33
+ text "Glimmer Klondike Solitaire"
34
+ background :dark_green
35
+
36
+ action_panel(game: @game)
37
+ tableau(game: @game) {
38
+ layout_data {
39
+ width 2*PLAYING_CARD_MARGIN + 7*(PLAYING_CARD_WIDTH + PLAYING_CARD_SPACING) - PLAYING_CARD_SPACING
40
+ height 700 + PLAYING_CARD_MARGIN
41
+ }
42
+ }
43
+
44
+ menu_bar {
45
+ menu {
46
+ text '&Game'
47
+ menu_item {
48
+ text '&Restart'
49
+ accelerator OS.mac? ? :command : :ctrl, :r
50
+ on_widget_selected {
51
+ @game.restart!
52
+ }
53
+ }
54
+ menu_item {
55
+ text 'E&xit'
56
+ accelerator :alt, :f4
57
+ on_widget_selected {
58
+ exit(0)
59
+ }
60
+ }
61
+ }
62
+ menu {
63
+ text '&Help'
64
+ menu_item {
65
+ text '&About...'
66
+ on_widget_selected {
67
+ display_about_dialog
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ def display_about_dialog
76
+ message_box(body_root) {
77
+ text 'About'
78
+ message "Glimmer Klondike Solitaire #{VERSION}\n\n#{LICENSE}"
79
+ }.open
80
+ end
81
+
82
+ end
83
+ end
84
+ end