glimmer-dsl-libui 0.4.7 → 0.4.11
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +1043 -493
- data/VERSION +1 -1
- data/examples/basic_table_button.rb +54 -30
- data/examples/basic_table_button2.rb +34 -0
- data/examples/basic_table_color.rb +1 -1
- data/examples/button_counter.rb +2 -1
- data/examples/cpu_percentage.rb +36 -0
- data/examples/editable_table.rb +1 -1
- data/examples/form_table.rb +21 -17
- data/examples/form_table2.rb +104 -85
- data/examples/form_table3.rb +113 -0
- data/examples/form_table4.rb +110 -0
- data/examples/form_table5.rb +94 -0
- data/examples/meta_example.rb +21 -8
- data/examples/midi_player.rb +1 -1
- data/examples/snake.rb +19 -10
- data/examples/snake2.rb +97 -0
- data/examples/tetris.rb +15 -18
- data/examples/tic_tac_toe.rb +1 -0
- data/examples/tic_tac_toe2.rb +84 -0
- data/glimmer-dsl-libui.gemspec +0 -0
- data/lib/glimmer/dsl/libui/control_expression.rb +2 -1
- data/lib/glimmer/dsl/libui/shape_expression.rb +2 -2
- data/lib/glimmer/dsl/libui/string_expression.rb +2 -1
- data/lib/glimmer/libui/attributed_string.rb +3 -2
- data/lib/glimmer/libui/control_proxy/checkbox_proxy.rb +4 -0
- data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/combobox_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +4 -5
- data/lib/glimmer/libui/control_proxy/entry_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +5 -1
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +4 -0
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +16 -4
- data/lib/glimmer/libui/control_proxy/multiline_entry_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +19 -0
- data/lib/glimmer/libui/control_proxy/slider_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/spinbox_proxy.rb +1 -2
- data/lib/glimmer/libui/control_proxy/table_proxy.rb +88 -24
- data/lib/glimmer/libui/control_proxy.rb +6 -4
- data/lib/glimmer/libui/data_bindable.rb +34 -4
- data/lib/glimmer/libui/shape.rb +3 -2
- data/lib/glimmer-dsl-libui.rb +1 -0
- metadata +9 -2
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'glimmer-dsl-libui'
|
2
|
+
|
3
|
+
class FormTable
|
4
|
+
include Glimmer
|
5
|
+
|
6
|
+
attr_accessor :data, :name, :email, :phone, :city, :state, :filter_value
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@data = [
|
10
|
+
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
|
11
|
+
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
|
12
|
+
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
|
13
|
+
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
|
14
|
+
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
def launch
|
19
|
+
window('Contacts', 600, 600) { |w|
|
20
|
+
margined true
|
21
|
+
|
22
|
+
vertical_box {
|
23
|
+
form {
|
24
|
+
stretchy false
|
25
|
+
|
26
|
+
entry {
|
27
|
+
label 'Name'
|
28
|
+
text <=> [self, :name] # bidirectional data-binding between entry text and self.name
|
29
|
+
}
|
30
|
+
|
31
|
+
entry {
|
32
|
+
label 'Email'
|
33
|
+
text <=> [self, :email]
|
34
|
+
}
|
35
|
+
|
36
|
+
entry {
|
37
|
+
label 'Phone'
|
38
|
+
text <=> [self, :phone]
|
39
|
+
}
|
40
|
+
|
41
|
+
entry {
|
42
|
+
label 'City'
|
43
|
+
text <=> [self, :city]
|
44
|
+
}
|
45
|
+
|
46
|
+
entry {
|
47
|
+
label 'State'
|
48
|
+
text <=> [self, :state]
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
button('Save Contact') {
|
53
|
+
stretchy false
|
54
|
+
|
55
|
+
on_clicked do
|
56
|
+
new_row = [name, email, phone, city, state]
|
57
|
+
if new_row.include?('')
|
58
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
59
|
+
else
|
60
|
+
data << new_row # automatically inserts a row into the table due to implicit data-binding
|
61
|
+
@unfiltered_data = data.dup
|
62
|
+
self.name = '' # automatically clears name entry through explicit data-binding
|
63
|
+
self.email = ''
|
64
|
+
self.phone = ''
|
65
|
+
self.city = ''
|
66
|
+
self.state = ''
|
67
|
+
end
|
68
|
+
end
|
69
|
+
}
|
70
|
+
|
71
|
+
search_entry {
|
72
|
+
stretchy false
|
73
|
+
# bidirectional data-binding of text to self.filter_value with after_write option
|
74
|
+
text <=> [self, :filter_value,
|
75
|
+
after_write: ->(filter_value) { # execute after write to self.filter_value
|
76
|
+
@unfiltered_data ||= data.dup
|
77
|
+
# Unfilter first to remove any previous filters
|
78
|
+
data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
|
79
|
+
# Now, apply filter if entered
|
80
|
+
unless filter_value.empty?
|
81
|
+
data.filter! do |row_data| # affects table indirectly through implicit data-binding
|
82
|
+
row_data.any? do |cell|
|
83
|
+
cell.to_s.downcase.include?(filter_value.downcase)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
}
|
88
|
+
]
|
89
|
+
}
|
90
|
+
|
91
|
+
table {
|
92
|
+
text_column('Name')
|
93
|
+
text_column('Email')
|
94
|
+
text_column('Phone')
|
95
|
+
text_column('City')
|
96
|
+
text_column('State')
|
97
|
+
|
98
|
+
editable true
|
99
|
+
cell_rows <=> [self, :data] # explicit data-binding to raw data Array of Arrays
|
100
|
+
|
101
|
+
on_changed do |row, type, row_data|
|
102
|
+
puts "Row #{row} #{type}: #{row_data}"
|
103
|
+
end
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}.show
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
FormTable.new.launch
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'glimmer-dsl-libui'
|
2
|
+
|
3
|
+
include Glimmer
|
4
|
+
|
5
|
+
data = [
|
6
|
+
['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
|
7
|
+
['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
|
8
|
+
['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
|
9
|
+
['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
|
10
|
+
['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
|
11
|
+
]
|
12
|
+
|
13
|
+
window('Contacts', 600, 600) { |w|
|
14
|
+
margined true
|
15
|
+
|
16
|
+
vertical_box {
|
17
|
+
form {
|
18
|
+
stretchy false
|
19
|
+
|
20
|
+
@name_entry = entry {
|
21
|
+
label 'Name'
|
22
|
+
}
|
23
|
+
|
24
|
+
@email_entry = entry {
|
25
|
+
label 'Email'
|
26
|
+
}
|
27
|
+
|
28
|
+
@phone_entry = entry {
|
29
|
+
label 'Phone'
|
30
|
+
}
|
31
|
+
|
32
|
+
@city_entry = entry {
|
33
|
+
label 'City'
|
34
|
+
}
|
35
|
+
|
36
|
+
@state_entry = entry {
|
37
|
+
label 'State'
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
button('Save Contact') {
|
42
|
+
stretchy false
|
43
|
+
|
44
|
+
on_clicked do
|
45
|
+
new_row = [@name_entry.text, @email_entry.text, @phone_entry.text, @city_entry.text, @state_entry.text]
|
46
|
+
if new_row.include?('')
|
47
|
+
msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
|
48
|
+
else
|
49
|
+
data << new_row # automatically inserts a row into the table due to implicit data-binding
|
50
|
+
@unfiltered_data = data.dup
|
51
|
+
@name_entry.text = ''
|
52
|
+
@email_entry.text = ''
|
53
|
+
@phone_entry.text = ''
|
54
|
+
@city_entry.text = ''
|
55
|
+
@state_entry.text = ''
|
56
|
+
end
|
57
|
+
end
|
58
|
+
}
|
59
|
+
|
60
|
+
search_entry { |se|
|
61
|
+
stretchy false
|
62
|
+
|
63
|
+
on_changed do
|
64
|
+
filter_value = se.text
|
65
|
+
@unfiltered_data ||= data.dup
|
66
|
+
# Unfilter first to remove any previous filters
|
67
|
+
data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
|
68
|
+
# Now, apply filter if entered
|
69
|
+
unless filter_value.empty?
|
70
|
+
data.filter! do |row_data| # affects table indirectly through implicit data-binding
|
71
|
+
row_data.any? do |cell|
|
72
|
+
cell.to_s.downcase.include?(filter_value.downcase)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
}
|
78
|
+
|
79
|
+
table {
|
80
|
+
text_column('Name')
|
81
|
+
text_column('Email')
|
82
|
+
text_column('Phone')
|
83
|
+
text_column('City')
|
84
|
+
text_column('State')
|
85
|
+
|
86
|
+
editable true
|
87
|
+
cell_rows data # implicit data-binding to raw data Array of Arrays
|
88
|
+
|
89
|
+
on_changed do |row, type, row_data|
|
90
|
+
puts "Row #{row} #{type}: #{row_data}"
|
91
|
+
end
|
92
|
+
}
|
93
|
+
}
|
94
|
+
}.show
|
data/examples/meta_example.rb
CHANGED
@@ -24,18 +24,28 @@ class MetaExample
|
|
24
24
|
@examples
|
25
25
|
end
|
26
26
|
|
27
|
+
def basic_examples
|
28
|
+
examples.select {|example| example.start_with?('Basic') || ADDITIONAL_BASIC_EXAMPLES.include?(example) }.sort
|
29
|
+
end
|
30
|
+
|
31
|
+
def advanced_examples
|
32
|
+
(examples - basic_examples).sort
|
33
|
+
end
|
34
|
+
|
27
35
|
def examples_with_versions
|
28
|
-
examples
|
29
|
-
version_count_for(example) > 1 ? "#{example} (#{version_count_for(example)} versions)" : example
|
30
|
-
end
|
36
|
+
append_versions(examples)
|
31
37
|
end
|
32
38
|
|
33
39
|
def basic_examples_with_versions
|
34
|
-
|
40
|
+
append_versions(basic_examples)
|
35
41
|
end
|
36
42
|
|
37
43
|
def advanced_examples_with_versions
|
38
|
-
|
44
|
+
append_versions(advanced_examples)
|
45
|
+
end
|
46
|
+
|
47
|
+
def append_versions(examples)
|
48
|
+
examples.map { |example| version_count_for(example) > 1 ? "#{example} (#{version_count_for(example)} versions)" : example }
|
39
49
|
end
|
40
50
|
|
41
51
|
def file_path_for(example)
|
@@ -59,12 +69,12 @@ class MetaExample
|
|
59
69
|
command = "#{RbConfig.ruby} -r #{glimmer_dsl_libui_file} #{example} 2>&1"
|
60
70
|
result = ''
|
61
71
|
IO.popen(command) do |f|
|
62
|
-
sleep(0.
|
72
|
+
sleep(0.00001) # yield to main thread
|
63
73
|
f.each_line do |line|
|
64
74
|
result << line
|
65
75
|
puts line
|
66
76
|
$stdout.flush # for Windows
|
67
|
-
sleep(0.
|
77
|
+
sleep(0.00001) # yield to main thread
|
68
78
|
end
|
69
79
|
end
|
70
80
|
Glimmer::LibUI.queue_main { msg_box('Error Running Example', result) } if result.downcase.include?('error')
|
@@ -72,7 +82,7 @@ class MetaExample
|
|
72
82
|
end
|
73
83
|
|
74
84
|
def launch
|
75
|
-
window('Meta-Example',
|
85
|
+
window('Meta-Example', 1000, 500) {
|
76
86
|
margined true
|
77
87
|
|
78
88
|
horizontal_box {
|
@@ -94,6 +104,7 @@ class MetaExample
|
|
94
104
|
example = selected_example
|
95
105
|
self.code_text = File.read(file_path_for(example))
|
96
106
|
@version_spinbox.value = 1
|
107
|
+
@advanced_example_radio_buttons.selected = -1
|
97
108
|
end
|
98
109
|
}
|
99
110
|
|
@@ -107,12 +118,14 @@ class MetaExample
|
|
107
118
|
@advanced_example_radio_buttons = radio_buttons {
|
108
119
|
stretchy false
|
109
120
|
items advanced_examples_with_versions
|
121
|
+
selected -1
|
110
122
|
|
111
123
|
on_selected do
|
112
124
|
@selected_example_index = examples_with_versions.index(advanced_examples_with_versions[@advanced_example_radio_buttons.selected])
|
113
125
|
example = selected_example
|
114
126
|
self.code_text = File.read(file_path_for(example))
|
115
127
|
@version_spinbox.value = 1
|
128
|
+
@basic_example_radio_buttons.selected = -1
|
116
129
|
end
|
117
130
|
}
|
118
131
|
|
data/examples/midi_player.rb
CHANGED
@@ -70,7 +70,7 @@ class TinyMidiPlayer
|
|
70
70
|
}
|
71
71
|
}
|
72
72
|
|
73
|
-
combobox {
|
73
|
+
combobox {
|
74
74
|
items @midi_files.map { |path| File.basename(path) }
|
75
75
|
# data-bind selected item (String) to self.selected_file with on-read/on-write converters and after_write operation
|
76
76
|
selected_item <=> [self, :selected_file, on_read: ->(f) {File.basename(f.to_s)}, on_write: ->(f) {File.join(@music_directory, f)}, after_write: -> { play_midi if @th&.alive? }]
|
data/examples/snake.rb
CHANGED
@@ -12,6 +12,7 @@ class Snake
|
|
12
12
|
@game = Model::Game.new
|
13
13
|
@grid = Presenter::Grid.new(@game)
|
14
14
|
@game.start
|
15
|
+
@keypress_queue = []
|
15
16
|
create_gui
|
16
17
|
register_observers
|
17
18
|
end
|
@@ -31,7 +32,23 @@ class Snake
|
|
31
32
|
end
|
32
33
|
|
33
34
|
Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
|
34
|
-
|
35
|
+
unless @game.over?
|
36
|
+
process_queued_keypress
|
37
|
+
@game.snake.move
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def process_queued_keypress
|
43
|
+
# key press queue ensures one turn per snake move to avoid a double-turn resulting in instant death (due to snake illogically going back against itself)
|
44
|
+
key = @keypress_queue.shift
|
45
|
+
case [@game.snake.head.orientation, key]
|
46
|
+
in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
|
47
|
+
@game.snake.turn_right
|
48
|
+
in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
|
49
|
+
@game.snake.turn_left
|
50
|
+
else
|
51
|
+
# No Op
|
35
52
|
end
|
36
53
|
end
|
37
54
|
|
@@ -56,15 +73,7 @@ class Snake
|
|
56
73
|
}
|
57
74
|
|
58
75
|
on_key_up do |area_key_event|
|
59
|
-
|
60
|
-
case orientation_and_key
|
61
|
-
in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
|
62
|
-
@game.snake.turn_right
|
63
|
-
in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
|
64
|
-
@game.snake.turn_left
|
65
|
-
else
|
66
|
-
# No Op
|
67
|
-
end
|
76
|
+
@keypress_queue << area_key_event[:ext_key]
|
68
77
|
end
|
69
78
|
}
|
70
79
|
end
|
data/examples/snake2.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'glimmer-dsl-libui'
|
2
|
+
|
3
|
+
require_relative 'snake/presenter/grid'
|
4
|
+
|
5
|
+
class Snake
|
6
|
+
include Glimmer
|
7
|
+
|
8
|
+
CELL_SIZE = 15
|
9
|
+
SNAKE_MOVE_DELAY = 0.1
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@game = Model::Game.new
|
13
|
+
@grid = Presenter::Grid.new(@game)
|
14
|
+
@game.start
|
15
|
+
@keypress_queue = []
|
16
|
+
create_gui
|
17
|
+
register_observers
|
18
|
+
end
|
19
|
+
|
20
|
+
def launch
|
21
|
+
@main_window.show
|
22
|
+
end
|
23
|
+
|
24
|
+
def register_observers
|
25
|
+
@game.height.times do |row|
|
26
|
+
@game.width.times do |column|
|
27
|
+
observe(@grid.cells[row][column], :color) do |new_color|
|
28
|
+
@cell_grid[row][column].fill = new_color
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
observe(@game, :over) do |game_over|
|
34
|
+
Glimmer::LibUI.queue_main do
|
35
|
+
if game_over
|
36
|
+
msg_box('Game Over!', "Score: #{@game.score} | High Score: #{@game.high_score}")
|
37
|
+
@game.start
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
|
43
|
+
unless @game.over?
|
44
|
+
process_queued_keypress
|
45
|
+
@game.snake.move
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def process_queued_keypress
|
51
|
+
# key press queue ensures one turn per snake move to avoid a double-turn resulting in instant death (due to snake illogically going back against itself)
|
52
|
+
key = @keypress_queue.shift
|
53
|
+
case [@game.snake.head.orientation, key]
|
54
|
+
in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
|
55
|
+
@game.snake.turn_right
|
56
|
+
in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
|
57
|
+
@game.snake.turn_left
|
58
|
+
else
|
59
|
+
# No Op
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def create_gui
|
64
|
+
@cell_grid = []
|
65
|
+
@main_window = window {
|
66
|
+
# data-bind window title to game score, converting it to a title string on read from the model
|
67
|
+
title <= [@game, :score, on_read: -> (score) {"Snake (Score: #{@game.score})"}]
|
68
|
+
content_size @game.width * CELL_SIZE, @game.height * CELL_SIZE
|
69
|
+
resizable false
|
70
|
+
|
71
|
+
vertical_box {
|
72
|
+
padded false
|
73
|
+
|
74
|
+
@game.height.times do |row|
|
75
|
+
@cell_grid << []
|
76
|
+
horizontal_box {
|
77
|
+
padded false
|
78
|
+
|
79
|
+
@game.width.times do |column|
|
80
|
+
area {
|
81
|
+
@cell_grid.last << square(0, 0, CELL_SIZE) {
|
82
|
+
fill Presenter::Cell::COLOR_CLEAR
|
83
|
+
}
|
84
|
+
|
85
|
+
on_key_up do |area_key_event|
|
86
|
+
@keypress_queue << area_key_event[:ext_key]
|
87
|
+
end
|
88
|
+
}
|
89
|
+
end
|
90
|
+
}
|
91
|
+
end
|
92
|
+
}
|
93
|
+
}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
Snake.new.launch
|
data/examples/tetris.rb
CHANGED
@@ -109,22 +109,23 @@ class Tetris
|
|
109
109
|
menu('Game') {
|
110
110
|
@pause_menu_item = check_menu_item('Pause') {
|
111
111
|
enabled false
|
112
|
-
|
113
|
-
on_clicked do
|
114
|
-
@game.paused = @pause_menu_item.checked?
|
115
|
-
end
|
112
|
+
checked <=> [@game, :paused]
|
116
113
|
}
|
114
|
+
|
117
115
|
menu_item('Restart') {
|
118
116
|
on_clicked do
|
119
117
|
@game.restart!
|
120
118
|
end
|
121
119
|
}
|
120
|
+
|
122
121
|
separator_menu_item
|
122
|
+
|
123
123
|
menu_item('Exit') {
|
124
124
|
on_clicked do
|
125
125
|
exit(0)
|
126
126
|
end
|
127
127
|
}
|
128
|
+
|
128
129
|
quit_menu_item if OS.mac?
|
129
130
|
}
|
130
131
|
|
@@ -134,6 +135,7 @@ class Tetris
|
|
134
135
|
show_high_scores
|
135
136
|
end
|
136
137
|
}
|
138
|
+
|
137
139
|
menu_item('Clear High Scores') {
|
138
140
|
on_clicked {
|
139
141
|
@game.clear_high_scores!
|
@@ -142,22 +144,16 @@ class Tetris
|
|
142
144
|
}
|
143
145
|
|
144
146
|
menu('Options') {
|
145
|
-
radio_menu_item('Instant Down on Up Arrow') {
|
146
|
-
|
147
|
-
@game.instant_down_on_up = true
|
148
|
-
end
|
147
|
+
radio_menu_item('Instant Down on Up Arrow') { |r|
|
148
|
+
checked <=> [@game, :instant_down_on_up]
|
149
149
|
}
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
end
|
150
|
+
|
151
|
+
radio_menu_item('Rotate Right on Up Arrow') { |r|
|
152
|
+
checked <=> [@game, :rotate_right_on_up]
|
154
153
|
}
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
on_clicked do
|
159
|
-
@game.rotate_left_on_up = true
|
160
|
-
end
|
154
|
+
|
155
|
+
radio_menu_item('Rotate Left on Up Arrow') { |r|
|
156
|
+
checked <=> [@game, :rotate_left_on_up]
|
161
157
|
}
|
162
158
|
}
|
163
159
|
|
@@ -169,6 +165,7 @@ class Tetris
|
|
169
165
|
end
|
170
166
|
}
|
171
167
|
end
|
168
|
+
|
172
169
|
menu_item('About') {
|
173
170
|
on_clicked do
|
174
171
|
show_about_dialog
|
data/examples/tic_tac_toe.rb
CHANGED
@@ -41,6 +41,7 @@ class TicTacToe
|
|
41
41
|
text(23, 19) {
|
42
42
|
string {
|
43
43
|
font family: 'Arial', size: OS.mac? ? 20 : 16
|
44
|
+
# data-bind string property of area text attributed string to tic tac toe board cell sign
|
44
45
|
string <= [@tic_tac_toe_board[row + 1, column + 1], :sign] # board model is 1-based
|
45
46
|
}
|
46
47
|
}
|
@@ -0,0 +1,84 @@
|
|
1
|
+
|
2
|
+
require 'glimmer-dsl-libui'
|
3
|
+
|
4
|
+
require_relative "tic_tac_toe/board"
|
5
|
+
|
6
|
+
class TicTacToe
|
7
|
+
include Glimmer
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@tic_tac_toe_board = Board.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def launch
|
14
|
+
create_gui
|
15
|
+
register_observers
|
16
|
+
@main_window.show
|
17
|
+
end
|
18
|
+
|
19
|
+
def register_observers
|
20
|
+
observe(@tic_tac_toe_board, :game_status) do |game_status|
|
21
|
+
display_win_message if game_status == Board::WIN
|
22
|
+
display_draw_message if game_status == Board::DRAW
|
23
|
+
end
|
24
|
+
|
25
|
+
3.times.map do |row|
|
26
|
+
3.times.map do |column|
|
27
|
+
observe(@tic_tac_toe_board[row + 1, column + 1], :sign) do |sign| # board model is 1-based
|
28
|
+
@cells[row][column].string = sign
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_gui
|
35
|
+
@main_window = window('Tic-Tac-Toe', 180, 180) {
|
36
|
+
resizable false
|
37
|
+
|
38
|
+
@cells = []
|
39
|
+
vertical_box {
|
40
|
+
padded false
|
41
|
+
|
42
|
+
3.times.map do |row|
|
43
|
+
@cells << []
|
44
|
+
horizontal_box {
|
45
|
+
padded false
|
46
|
+
|
47
|
+
3.times.map do |column|
|
48
|
+
area {
|
49
|
+
square(0, 0, 60) {
|
50
|
+
stroke :black, thickness: 2
|
51
|
+
}
|
52
|
+
text(23, 19) {
|
53
|
+
@cells[row] << string('') {
|
54
|
+
font family: 'Arial', size: OS.mac? ? 20 : 16
|
55
|
+
}
|
56
|
+
}
|
57
|
+
on_mouse_up do
|
58
|
+
@tic_tac_toe_board.mark(row + 1, column + 1) # board model is 1-based
|
59
|
+
end
|
60
|
+
}
|
61
|
+
end
|
62
|
+
}
|
63
|
+
end
|
64
|
+
}
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def display_win_message
|
69
|
+
display_game_over_message("Player #{@tic_tac_toe_board.winning_sign} has won!")
|
70
|
+
end
|
71
|
+
|
72
|
+
def display_draw_message
|
73
|
+
display_game_over_message("Draw!")
|
74
|
+
end
|
75
|
+
|
76
|
+
def display_game_over_message(message_text)
|
77
|
+
Glimmer::LibUI.queue_main do
|
78
|
+
msg_box('Game Over', message_text)
|
79
|
+
@tic_tac_toe_board.reset!
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
TicTacToe.new.launch
|
data/glimmer-dsl-libui.gemspec
CHANGED
Binary file
|
@@ -39,8 +39,9 @@ module Glimmer
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def add_content(parent, keyword, *args, &block)
|
42
|
+
options = args.last.is_a?(Hash) ? args.last : {post_add_content: true}
|
42
43
|
super
|
43
|
-
parent&.post_add_content
|
44
|
+
parent&.post_add_content if options[:post_add_content]
|
44
45
|
end
|
45
46
|
end
|
46
47
|
end
|
@@ -47,10 +47,10 @@ module Glimmer
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def add_content(parent, keyword, *args, &block)
|
50
|
+
options = args.last.is_a?(Hash) ? args.last : {post_add_content: true}
|
50
51
|
super
|
51
|
-
parent
|
52
|
+
parent&.post_add_content if options[:post_add_content]
|
52
53
|
end
|
53
|
-
|
54
54
|
end
|
55
55
|
end
|
56
56
|
end
|
@@ -46,7 +46,8 @@ module Glimmer
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def add_content(parent, keyword, *args, &block)
|
49
|
-
|
49
|
+
options = args.last.is_a?(Hash) ? args.last : {post_add_content: true}
|
50
|
+
parent&.post_add_content(block) if options[:post_add_content]
|
50
51
|
end
|
51
52
|
end
|
52
53
|
end
|
@@ -30,8 +30,9 @@ module Glimmer
|
|
30
30
|
class AttributedString
|
31
31
|
include DataBindable
|
32
32
|
|
33
|
-
attr_reader :keyword, :parent_proxy, :args
|
33
|
+
attr_reader :keyword, :parent_proxy, :args, :content_added
|
34
34
|
attr_accessor :block
|
35
|
+
alias content_added? content_added
|
35
36
|
|
36
37
|
def initialize(keyword, parent_proxy, args, &block)
|
37
38
|
@keyword = keyword
|
@@ -205,7 +206,7 @@ module Glimmer
|
|
205
206
|
end
|
206
207
|
|
207
208
|
def content(&block)
|
208
|
-
Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Libui::StringExpression.new, @keyword, &block)
|
209
|
+
Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Libui::StringExpression.new, @keyword, {post_add_content: true}, &block)
|
209
210
|
end
|
210
211
|
end
|
211
212
|
end
|
@@ -29,6 +29,10 @@ module Glimmer
|
|
29
29
|
# Follows the Proxy Design Pattern
|
30
30
|
class CheckboxProxy < ControlProxy
|
31
31
|
DEFAULT_TEXT = ''
|
32
|
+
|
33
|
+
def data_bind_write(property, model_binding)
|
34
|
+
handle_listener('on_toggled') { model_binding.call(checked) } if property == 'checked'
|
35
|
+
end
|
32
36
|
|
33
37
|
private
|
34
38
|
|