glimmer-dsl-libui 0.3.5 → 0.4.3

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -0
  3. data/README.md +4364 -3406
  4. data/VERSION +1 -1
  5. data/examples/basic_entry.rb +27 -24
  6. data/examples/basic_entry2.rb +31 -0
  7. data/examples/button_counter.rb +27 -0
  8. data/examples/color_the_circles.rb +2 -2
  9. data/examples/form.rb +42 -30
  10. data/examples/form2.rb +37 -0
  11. data/examples/form_table.rb +100 -85
  12. data/examples/form_table2.rb +95 -0
  13. data/examples/login.rb +45 -39
  14. data/examples/login2.rb +55 -0
  15. data/examples/login3.rb +65 -0
  16. data/examples/login4.rb +61 -0
  17. data/examples/login5.rb +43 -0
  18. data/examples/meta_example.rb +10 -7
  19. data/examples/method_based_custom_keyword.rb +8 -15
  20. data/examples/method_based_custom_keyword2.rb +97 -0
  21. data/examples/midi_player.rb +2 -6
  22. data/examples/snake/model/game.rb +2 -2
  23. data/examples/snake/presenter/grid.rb +5 -3
  24. data/examples/snake.rb +11 -21
  25. data/examples/tetris.rb +12 -12
  26. data/examples/tic_tac_toe.rb +3 -12
  27. data/examples/timer.rb +2 -6
  28. data/glimmer-dsl-libui.gemspec +0 -0
  29. data/lib/glimmer/dsl/libui/bind_expression.rb +36 -0
  30. data/lib/glimmer/dsl/libui/data_binding_expression.rb +45 -0
  31. data/lib/glimmer/dsl/libui/dsl.rb +3 -0
  32. data/lib/glimmer/dsl/libui/observe_expression.rb +35 -0
  33. data/lib/glimmer/dsl/libui/shine_data_binding_expression.rb +42 -0
  34. data/lib/glimmer/dsl/libui/string_expression.rb +4 -5
  35. data/lib/glimmer/libui/attributed_string.rb +19 -6
  36. data/lib/glimmer/libui/control_proxy/area_proxy.rb +52 -46
  37. data/lib/glimmer/libui/control_proxy/entry_proxy.rb +5 -0
  38. data/lib/glimmer/libui/control_proxy/image_proxy.rb +4 -5
  39. data/lib/glimmer/libui/control_proxy/multiline_entry_proxy.rb +5 -0
  40. data/lib/glimmer/libui/control_proxy/table_proxy.rb +1 -1
  41. data/lib/glimmer/libui/control_proxy.rb +16 -1
  42. data/lib/glimmer/libui/shape.rb +6 -3
  43. metadata +17 -4
@@ -0,0 +1,55 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ class Login
4
+ include Glimmer
5
+
6
+ attr_accessor :username, :password, :logged_in
7
+
8
+ def logged_out
9
+ !logged_in
10
+ end
11
+
12
+ def launch
13
+ window('Login') {
14
+ margined true
15
+
16
+ vertical_box {
17
+ form {
18
+ entry {
19
+ label 'Username:'
20
+ text <=> [self, :username]
21
+ enabled <= [self, :logged_out, computed_by: :logged_in] # computed_by option ensures being notified of changes to logged_in
22
+ }
23
+
24
+ password_entry {
25
+ label 'Password:'
26
+ text <=> [self, :password]
27
+ enabled <= [self, :logged_out, computed_by: :logged_in]
28
+ }
29
+ }
30
+
31
+ horizontal_box {
32
+ button('Login') {
33
+ enabled <= [self, :logged_out, computed_by: :logged_in]
34
+
35
+ on_clicked do
36
+ self.logged_in = true
37
+ end
38
+ }
39
+
40
+ button('Logout') {
41
+ enabled <= [self, :logged_in]
42
+
43
+ on_clicked do
44
+ self.logged_in = false
45
+ self.username = ''
46
+ self.password = ''
47
+ end
48
+ }
49
+ }
50
+ }
51
+ }.show
52
+ end
53
+ end
54
+
55
+ Login.new.launch
@@ -0,0 +1,65 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ class Login
4
+ include Glimmer
5
+
6
+ attr_accessor :username, :password
7
+ attr_reader :logged_in
8
+
9
+ def logged_in=(value)
10
+ @logged_in = value
11
+ self.logged_out = !value # calling logged_out= method notifies logged_out observers
12
+ end
13
+
14
+ def logged_out=(value)
15
+ self.logged_in = !value unless logged_in == !value
16
+ end
17
+
18
+ def logged_out
19
+ !logged_in
20
+ end
21
+
22
+ def launch
23
+ window('Login') {
24
+ margined true
25
+
26
+ vertical_box {
27
+ form {
28
+ entry {
29
+ label 'Username:'
30
+ text <=> [self, :username]
31
+ enabled <= [self, :logged_out]
32
+ }
33
+
34
+ password_entry {
35
+ label 'Password:'
36
+ text <=> [self, :password]
37
+ enabled <= [self, :logged_out]
38
+ }
39
+ }
40
+
41
+ horizontal_box {
42
+ button('Login') {
43
+ enabled <= [self, :logged_out]
44
+
45
+ on_clicked do
46
+ self.logged_in = true
47
+ end
48
+ }
49
+
50
+ button('Logout') {
51
+ enabled <= [self, :logged_in]
52
+
53
+ on_clicked do
54
+ self.logged_in = false
55
+ self.username = ''
56
+ self.password = ''
57
+ end
58
+ }
59
+ }
60
+ }
61
+ }.show
62
+ end
63
+ end
64
+
65
+ Login.new.launch
@@ -0,0 +1,61 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ class Login
4
+ include Glimmer
5
+
6
+ attr_accessor :username, :password
7
+ attr_reader :logged_in
8
+
9
+ def logged_in=(value)
10
+ @logged_in = value
11
+ notify_observers(:logged_out) # manually notify observers of logged_out upon logged_in changes; this method comes automatically from enhancement as Glimmer::DataBinding::ObservableModel via data-binding
12
+ end
13
+
14
+ def logged_out
15
+ !logged_in
16
+ end
17
+
18
+ def launch
19
+ window('Login') {
20
+ margined true
21
+
22
+ vertical_box {
23
+ form {
24
+ entry {
25
+ label 'Username:'
26
+ text <=> [self, :username]
27
+ enabled <= [self, :logged_out]
28
+ }
29
+
30
+ password_entry {
31
+ label 'Password:'
32
+ text <=> [self, :password]
33
+ enabled <= [self, :logged_out]
34
+ }
35
+ }
36
+
37
+ horizontal_box {
38
+ button('Login') {
39
+ enabled <= [self, :logged_out]
40
+
41
+ on_clicked do
42
+ self.logged_in = true
43
+ end
44
+ }
45
+
46
+ button('Logout') {
47
+ enabled <= [self, :logged_in]
48
+
49
+ on_clicked do
50
+ self.logged_in = false
51
+ self.username = ''
52
+ self.password = ''
53
+ end
54
+ }
55
+ }
56
+ }
57
+ }.show
58
+ end
59
+ end
60
+
61
+ Login.new.launch
@@ -0,0 +1,43 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ include Glimmer
4
+
5
+ window('Login') {
6
+ margined true
7
+
8
+ vertical_box {
9
+ form {
10
+ @username_entry = entry {
11
+ label 'Username:'
12
+ }
13
+
14
+ @password_entry = password_entry {
15
+ label 'Password:'
16
+ }
17
+ }
18
+
19
+ horizontal_box {
20
+ @login_button = button('Login') {
21
+ on_clicked do
22
+ @username_entry.enabled = false
23
+ @password_entry.enabled = false
24
+ @login_button.enabled = false
25
+ @logout_button.enabled = true
26
+ end
27
+ }
28
+
29
+ @logout_button = button('Logout') {
30
+ enabled false
31
+
32
+ on_clicked do
33
+ @username_entry.text = ''
34
+ @password_entry.text = ''
35
+ @username_entry.enabled = true
36
+ @password_entry.enabled = true
37
+ @login_button.enabled = true
38
+ @logout_button.enabled = false
39
+ end
40
+ }
41
+ }
42
+ }
43
+ }.show
@@ -7,8 +7,11 @@ class MetaExample
7
7
 
8
8
  ADDITIONAL_BASIC_EXAMPLES = ['Color Button', 'Font Button', 'Form', 'Date Time Picker', 'Simple Notepad']
9
9
 
10
+ attr_accessor :code_text
11
+
10
12
  def initialize
11
13
  @selected_example_index = examples_with_versions.index(basic_examples_with_versions.first)
14
+ @code_text = File.read(file_path_for(selected_example))
12
15
  end
13
16
 
14
17
  def examples
@@ -40,7 +43,7 @@ class MetaExample
40
43
  end
41
44
 
42
45
  def version_count_for(example)
43
- Dir.glob(File.join(File.expand_path('.', __dir__), "#{example.underscore}*.rb")).select {|file| file.match(/\d\.rb$/)}.count + 1
46
+ Dir.glob(File.join(File.expand_path('.', __dir__), "#{example.underscore}*.rb")).select {|file| file.match(/#{example.underscore}\d\.rb$/)}.count + 1
44
47
  end
45
48
 
46
49
  def glimmer_dsl_libui_file
@@ -89,7 +92,7 @@ class MetaExample
89
92
  on_selected do
90
93
  @selected_example_index = examples_with_versions.index(basic_examples_with_versions[@basic_example_radio_buttons.selected])
91
94
  example = selected_example
92
- @code_entry.text = File.read(file_path_for(example))
95
+ self.code_text = File.read(file_path_for(example))
93
96
  @version_spinbox.value = 1
94
97
  end
95
98
  }
@@ -108,7 +111,7 @@ class MetaExample
108
111
  on_selected do
109
112
  @selected_example_index = examples_with_versions.index(advanced_examples_with_versions[@advanced_example_radio_buttons.selected])
110
113
  example = selected_example
111
- @code_entry.text = File.read(file_path_for(example))
114
+ self.code_text = File.read(file_path_for(example))
112
115
  @version_spinbox.value = 1
113
116
  end
114
117
  }
@@ -134,7 +137,7 @@ class MetaExample
134
137
  else
135
138
  version_number = @version_spinbox.value == 1 ? '' : @version_spinbox.value
136
139
  example = "#{selected_example}#{version_number}"
137
- @code_entry.text = File.read(file_path_for(example))
140
+ self.code_text = File.read(file_path_for(example))
138
141
  end
139
142
  end
140
143
  }
@@ -149,7 +152,7 @@ class MetaExample
149
152
  parent_dir = File.join(Dir.home, '.glimmer-dsl-libui', 'examples')
150
153
  FileUtils.mkdir_p(parent_dir)
151
154
  example_file = File.join(parent_dir, "#{selected_example.underscore}.rb")
152
- File.write(example_file, @code_entry.text)
155
+ File.write(example_file, code_text)
153
156
  example_supporting_directory = File.expand_path(selected_example.underscore, __dir__)
154
157
  FileUtils.cp_r(example_supporting_directory, parent_dir) if Dir.exist?(example_supporting_directory)
155
158
  FileUtils.cp_r(File.expand_path('../icons', __dir__), File.dirname(parent_dir))
@@ -164,14 +167,14 @@ class MetaExample
164
167
  }
165
168
  button('Reset') {
166
169
  on_clicked do
167
- @code_entry.text = File.read(file_path_for(selected_example))
170
+ self.code_text = File.read(file_path_for(selected_example))
168
171
  end
169
172
  }
170
173
  }
171
174
  }
172
175
 
173
176
  @code_entry = non_wrapping_multiline_entry {
174
- text File.read(file_path_for(selected_example))
177
+ text <=> [self, :code_text]
175
178
  }
176
179
  }
177
180
  }.show
@@ -5,15 +5,11 @@ include Glimmer
5
5
 
6
6
  Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
7
7
 
8
- def form_field(model, property)
9
- property = property.to_s
8
+ def form_field(model, attribute)
9
+ attribute = attribute.to_s
10
10
  entry { |e|
11
- label property.underscore.split('_').map(&:capitalize).join(' ')
12
- text model.send(property).to_s
13
-
14
- on_changed do
15
- model.send("#{property}=", e.text)
16
- end
11
+ label attribute.underscore.split('_').map(&:capitalize).join(' ')
12
+ text <=> [model, attribute]
17
13
  }
18
14
  end
19
15
 
@@ -28,15 +24,12 @@ def address_form(address)
28
24
  end
29
25
 
30
26
  def label_pair(model, attribute, value)
31
- name_label = nil
32
- value_label = nil
33
27
  horizontal_box {
34
- name_label = label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
35
- value_label = label(value.to_s)
28
+ label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
29
+ label(value.to_s) {
30
+ text <= [model, attribute]
31
+ }
36
32
  }
37
- Glimmer::DataBinding::Observer.proc do
38
- value_label.text = model.send(attribute)
39
- end.observe(model, attribute)
40
33
  end
41
34
 
42
35
  def address(address)
@@ -0,0 +1,97 @@
1
+ require 'glimmer-dsl-libui'
2
+ require 'facets'
3
+
4
+ include Glimmer
5
+
6
+ Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
7
+
8
+ def form_field(model, property)
9
+ property = property.to_s
10
+ entry { |e|
11
+ label property.underscore.split('_').map(&:capitalize).join(' ')
12
+ text model.send(property).to_s
13
+
14
+ on_changed do
15
+ model.send("#{property}=", e.text)
16
+ end
17
+ }
18
+ end
19
+
20
+ def address_form(address)
21
+ form {
22
+ form_field(address, :street)
23
+ form_field(address, :p_o_box)
24
+ form_field(address, :city)
25
+ form_field(address, :state)
26
+ form_field(address, :zip_code)
27
+ }
28
+ end
29
+
30
+ def label_pair(model, attribute, value)
31
+ name_label = nil
32
+ value_label = nil
33
+ horizontal_box {
34
+ name_label = label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
35
+ value_label = label(value.to_s)
36
+ }
37
+ observe(model, attribute) do
38
+ value_label.text = model.send(attribute)
39
+ end
40
+ end
41
+
42
+ def address(address)
43
+ vertical_box {
44
+ address.each_pair do |attribute, value|
45
+ label_pair(address, attribute, value)
46
+ end
47
+ }
48
+ end
49
+
50
+ address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
51
+ address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
52
+
53
+ window('Method-Based Custom Keyword') {
54
+ margined true
55
+
56
+ horizontal_box {
57
+ vertical_box {
58
+ label('Address 1') {
59
+ stretchy false
60
+ }
61
+
62
+ address_form(address1)
63
+
64
+ horizontal_separator {
65
+ stretchy false
66
+ }
67
+
68
+ label('Address 1 (Saved)') {
69
+ stretchy false
70
+ }
71
+
72
+ address(address1)
73
+ }
74
+
75
+ vertical_separator {
76
+ stretchy false
77
+ }
78
+
79
+ vertical_box {
80
+ label('Address 2') {
81
+ stretchy false
82
+ }
83
+
84
+ address_form(address2)
85
+
86
+ horizontal_separator {
87
+ stretchy false
88
+ }
89
+
90
+ label('Address 2 (Saved)') {
91
+ stretchy false
92
+ }
93
+
94
+ address(address2)
95
+ }
96
+ }
97
+ }.show
@@ -18,12 +18,8 @@ class TinyMidiPlayer
18
18
 
19
19
  def stop_midi
20
20
  if @pid
21
- if @th.alive?
22
- Process.kill(:SIGKILL, @pid)
23
- @pid = nil
24
- else
25
- @pid = nil
26
- end
21
+ Process.kill(:SIGKILL, @pid) if @th.alive?
22
+ @pid = nil
27
23
  end
28
24
  end
29
25
 
@@ -6,8 +6,8 @@ require_relative 'apple'
6
6
  class Snake
7
7
  module Model
8
8
  class Game
9
- WIDTH_DEFAULT = 40
10
- HEIGHT_DEFAULT = 40
9
+ WIDTH_DEFAULT = 20
10
+ HEIGHT_DEFAULT = 20
11
11
  FILE_HIGH_SCORE = File.expand_path(File.join(Dir.home, '.glimmer-snake'))
12
12
 
13
13
  attr_reader :width, :height
@@ -1,10 +1,12 @@
1
- require 'glimmer/data_binding/observer'
1
+ require 'glimmer'
2
2
  require_relative '../model/game'
3
3
  require_relative 'cell'
4
4
 
5
5
  class Snake
6
6
  module Presenter
7
7
  class Grid
8
+ include Glimmer
9
+
8
10
  attr_reader :game, :cells
9
11
 
10
12
  def initialize(game = Model::Game.new)
@@ -14,7 +16,7 @@ class Snake
14
16
  Cell.new(grid: self, row: row, column: column)
15
17
  end
16
18
  end
17
- Glimmer::DataBinding::Observer.proc do |new_vertebrae|
19
+ observe(@game.snake, :vertebrae) do |new_vertebrae|
18
20
  occupied_snake_positions = @game.snake.vertebrae.map {|v| [v.row, v.column]}
19
21
  @cells.each_with_index do |row_cells, row|
20
22
  row_cells.each_with_index do |cell, column|
@@ -27,7 +29,7 @@ class Snake
27
29
  end
28
30
  end
29
31
  end
30
- end.observe(@game.snake, :vertebrae)
32
+ end
31
33
  end
32
34
 
33
35
  def clear
data/examples/snake.rb CHANGED
@@ -1,12 +1,12 @@
1
1
  require 'glimmer-dsl-libui'
2
- require 'glimmer/data_binding/observer'
3
2
 
4
3
  require_relative 'snake/presenter/grid'
5
4
 
6
5
  class Snake
6
+ include Glimmer
7
+
7
8
  CELL_SIZE = 15
8
9
  SNAKE_MOVE_DELAY = 0.1
9
- include Glimmer
10
10
 
11
11
  def initialize
12
12
  @game = Model::Game.new
@@ -21,48 +21,38 @@ class Snake
21
21
  end
22
22
 
23
23
  def register_observers
24
- @game.height.times do |row|
25
- @game.width.times do |column|
26
- Glimmer::DataBinding::Observer.proc do |new_color|
27
- @cell_grid[row][column].fill = new_color
28
- end.observe(@grid.cells[row][column], :color)
29
- end
30
- end
31
-
32
- Glimmer::DataBinding::Observer.proc do |game_over|
24
+ observe(@game, :over) do |game_over|
33
25
  Glimmer::LibUI.queue_main do
34
26
  if game_over
35
27
  msg_box('Game Over!', "Score: #{@game.score} | High Score: #{@game.high_score}")
36
28
  @game.start
37
29
  end
38
30
  end
39
- end.observe(@game, :over)
31
+ end
40
32
 
41
33
  Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
42
- unless @game.over?
43
- @game.snake.move
44
- @main_window.title = "Glimmer Snake (Score: #{@game.score} | High Score: #{@game.high_score})"
45
- end
34
+ @game.snake.move unless @game.over?
46
35
  end
47
36
  end
48
37
 
49
38
  def create_gui
50
- @cell_grid = []
51
- @main_window = window('Glimmer Snake', @game.width * CELL_SIZE, @game.height * CELL_SIZE) {
39
+ @main_window = window {
40
+ # data-bind window title to game score, converting it to a title string on read from the model
41
+ title <= [@game, :score, on_read: -> (score) {"Snake (Score: #{@game.score})"}]
42
+ content_size @game.width * CELL_SIZE, @game.height * CELL_SIZE
52
43
  resizable false
53
44
 
54
45
  vertical_box {
55
46
  padded false
56
47
 
57
48
  @game.height.times do |row|
58
- @cell_grid << []
59
49
  horizontal_box {
60
50
  padded false
61
51
 
62
52
  @game.width.times do |column|
63
53
  area {
64
- @cell_grid.last << square(0, 0, CELL_SIZE) {
65
- fill Presenter::Cell::COLOR_CLEAR
54
+ square(0, 0, CELL_SIZE) {
55
+ fill <= [@grid.cells[row][column], :color] # data-bind square fill to grid cell color
66
56
  }
67
57
 
68
58
  on_key_up do |area_key_event|
data/examples/tetris.rb CHANGED
@@ -42,7 +42,7 @@ class Tetris
42
42
  end
43
43
 
44
44
  def register_observers
45
- Glimmer::DataBinding::Observer.proc do |game_over|
45
+ observe(@game, :game_over) do |game_over|
46
46
  if game_over
47
47
  @pause_menu_item.enabled = false
48
48
  show_game_over_dialog
@@ -50,11 +50,11 @@ class Tetris
50
50
  @pause_menu_item.enabled = true
51
51
  start_moving_tetrominos_down
52
52
  end
53
- end.observe(@game, :game_over)
53
+ end
54
54
 
55
55
  Model::Game::PLAYFIELD_HEIGHT.times do |row|
56
56
  Model::Game::PLAYFIELD_WIDTH.times do |column|
57
- Glimmer::DataBinding::Observer.proc do |new_color|
57
+ observe(@game.playfield[row][column], :color) do |new_color|
58
58
  Glimmer::LibUI.queue_main do
59
59
  color = Glimmer::LibUI.interpret_color(new_color)
60
60
  block = @playfield_blocks[row][column]
@@ -65,13 +65,13 @@ class Tetris
65
65
  block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
66
66
  block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
67
67
  end
68
- end.observe(@game.playfield[row][column], :color)
68
+ end
69
69
  end
70
70
  end
71
71
 
72
72
  Model::Game::PREVIEW_PLAYFIELD_HEIGHT.times do |row|
73
73
  Model::Game::PREVIEW_PLAYFIELD_WIDTH.times do |column|
74
- Glimmer::DataBinding::Observer.proc do |new_color|
74
+ observe(@game.preview_playfield[row][column], :color) do |new_color|
75
75
  Glimmer::LibUI.queue_main do
76
76
  color = Glimmer::LibUI.interpret_color(new_color)
77
77
  block = @preview_playfield_blocks[row][column]
@@ -82,27 +82,27 @@ class Tetris
82
82
  block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
83
83
  block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
84
84
  end
85
- end.observe(@game.preview_playfield[row][column], :color)
85
+ end
86
86
  end
87
87
  end
88
88
 
89
- Glimmer::DataBinding::Observer.proc do |new_score|
89
+ observe(@game, :score) do |new_score|
90
90
  Glimmer::LibUI.queue_main do
91
91
  @score_label.text = new_score.to_s
92
92
  end
93
- end.observe(@game, :score)
93
+ end
94
94
 
95
- Glimmer::DataBinding::Observer.proc do |new_lines|
95
+ observe(@game, :lines) do |new_lines|
96
96
  Glimmer::LibUI.queue_main do
97
97
  @lines_label.text = new_lines.to_s
98
98
  end
99
- end.observe(@game, :lines)
99
+ end
100
100
 
101
- Glimmer::DataBinding::Observer.proc do |new_level|
101
+ observe(@game, :level) do |new_level|
102
102
  Glimmer::LibUI.queue_main do
103
103
  @level_label.text = new_level.to_s
104
104
  end
105
- end.observe(@game, :level)
105
+ end
106
106
  end
107
107
 
108
108
  def menu_bar
@@ -16,17 +16,9 @@ class TicTacToe
16
16
  end
17
17
 
18
18
  def register_observers
19
- Glimmer::DataBinding::Observer.proc do |game_status|
19
+ observe(@tic_tac_toe_board, :game_status) do |game_status|
20
20
  display_win_message if game_status == Board::WIN
21
21
  display_draw_message if game_status == Board::DRAW
22
- end.observe(@tic_tac_toe_board, :game_status)
23
-
24
- 3.times.map do |row|
25
- 3.times.map do |column|
26
- Glimmer::DataBinding::Observer.proc do |sign|
27
- @cells[row][column].string = sign
28
- end.observe(@tic_tac_toe_board[row + 1, column + 1], :sign) # board model is 1-based
29
- end
30
22
  end
31
23
  end
32
24
 
@@ -34,12 +26,10 @@ class TicTacToe
34
26
  @main_window = window('Tic-Tac-Toe', 180, 180) {
35
27
  resizable false
36
28
 
37
- @cells = []
38
29
  vertical_box {
39
30
  padded false
40
31
 
41
32
  3.times.map do |row|
42
- @cells << []
43
33
  horizontal_box {
44
34
  padded false
45
35
 
@@ -49,8 +39,9 @@ class TicTacToe
49
39
  stroke :black, thickness: 2
50
40
  }
51
41
  text(23, 19) {
52
- @cells[row] << string('') {
42
+ string {
53
43
  font family: 'Arial', size: OS.mac? ? 20 : 16
44
+ string <= [@tic_tac_toe_board[row + 1, column + 1], :sign] # board model is 1-based
54
45
  }
55
46
  }
56
47
  on_mouse_up do