glimmer-dsl-libui 0.1.9 → 0.2.1

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +624 -29
  4. data/VERSION +1 -1
  5. data/examples/area_gallery.rb +7 -1
  6. data/examples/area_gallery2.rb +14 -4
  7. data/examples/area_gallery3.rb +8 -2
  8. data/examples/area_gallery4.rb +15 -5
  9. data/examples/color_the_circles.rb +223 -0
  10. data/examples/control_gallery.rb +1 -1
  11. data/examples/form_table.rb +20 -0
  12. data/examples/grid.rb +38 -2
  13. data/examples/login.rb +45 -0
  14. data/examples/midi_player.rb +1 -1
  15. data/examples/timer.rb +135 -0
  16. data/glimmer-dsl-libui.gemspec +0 -0
  17. data/lib/glimmer/libui/control_proxy/area_proxy/scrolling_area_proxy.rb +40 -0
  18. data/lib/glimmer/libui/control_proxy/area_proxy.rb +9 -5
  19. data/lib/glimmer/libui/control_proxy/entry_proxy/password_entry_proxy.rb +36 -0
  20. data/lib/glimmer/libui/control_proxy/entry_proxy/search_entry_proxy.rb +36 -0
  21. data/lib/glimmer/libui/control_proxy/entry_proxy.rb +39 -0
  22. data/lib/glimmer/libui/control_proxy/grid_proxy.rb +12 -1
  23. data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +69 -0
  24. data/lib/glimmer/libui/control_proxy/menu_proxy.rb +3 -0
  25. data/lib/glimmer/libui/control_proxy/path_proxy.rb +3 -30
  26. data/lib/glimmer/libui/control_proxy.rb +8 -1
  27. data/lib/glimmer/libui/shape/arc.rb +6 -3
  28. data/lib/glimmer/libui/shape/circle.rb +50 -0
  29. data/lib/glimmer/libui/shape/rectangle.rb +4 -0
  30. data/lib/glimmer/libui/shape/square.rb +4 -0
  31. data/lib/glimmer/libui.rb +76 -2
  32. data/sounds/AlanWalker-Faded.mid +0 -0
  33. data/sounds/AlanWalker-SingMeToSleep.mid +0 -0
  34. data/sounds/CalvinHarris-Blame.mid +0 -0
  35. data/sounds/CalvinHarris-MyWay.mid +0 -0
  36. data/sounds/deadmau5-2448.mid +0 -0
  37. data/sounds/deadmau5-SoThereIWas.mid +0 -0
  38. metadata +19 -4
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.9
1
+ 0.2.1
@@ -41,11 +41,17 @@ window('Area Gallery', 400, 400) {
41
41
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
42
42
  }
43
43
  path { # declarative stable path
44
- arc(200, 200, 90, 0, 360, false)
44
+ circle(200, 200, 90)
45
45
 
46
46
  fill r: 202, g: 102, b: 204, a: 0.5
47
47
  stroke r: 0, g: 0, b: 0, thickness: 2
48
48
  }
49
+ path { # declarative stable path
50
+ arc(400, 220, 180, 90, 90, false)
51
+
52
+ fill r: 204, g: 102, b: 204, a: 0.5
53
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
54
+ }
49
55
 
50
56
  on_mouse_event do |area_mouse_event|
51
57
  p area_mouse_event
@@ -95,18 +95,28 @@ window('Area Gallery', 400, 400) {
95
95
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
96
96
  }
97
97
  path { # declarative stable path
98
- arc {
98
+ circle {
99
99
  x_center 200
100
100
  y_center 200
101
101
  radius 90
102
- start_angle 0
103
- sweep 360
104
- is_negative false
105
102
  }
106
103
 
107
104
  fill r: 202, g: 102, b: 204, a: 0.5
108
105
  stroke r: 0, g: 0, b: 0, thickness: 2
109
106
  }
107
+ path { # declarative stable path
108
+ arc {
109
+ x_center 400
110
+ y_center 220
111
+ radius 180
112
+ start_angle 90
113
+ sweep 90
114
+ is_negative false
115
+ }
116
+
117
+ fill r: 204, g: 102, b: 204, a: 0.5
118
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
119
+ }
110
120
 
111
121
  on_mouse_event do |area_mouse_event|
112
122
  p area_mouse_event
@@ -42,11 +42,17 @@ window('Area Gallery', 400, 400) {
42
42
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
43
43
  }
44
44
  path { # a dynamic path is added semi-declaratively inside on_draw block
45
- arc(200, 200, 90, 0, 360, false)
46
-
45
+ circle(200, 200, 90)
46
+
47
47
  fill r: 202, g: 102, b: 204, a: 0.5
48
48
  stroke r: 0, g: 0, b: 0, thickness: 2
49
49
  }
50
+ path { # a dynamic path is added semi-declaratively inside on_draw block
51
+ arc(400, 220, 180, 90, 90, false)
52
+
53
+ fill r: 204, g: 102, b: 204, a: 0.5
54
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
55
+ }
50
56
  end
51
57
 
52
58
  on_mouse_event do |area_mouse_event|
@@ -96,18 +96,28 @@ window('Area Gallery', 400, 400) {
96
96
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
97
97
  }
98
98
  path { # a dynamic path is added semi-declaratively inside on_draw block
99
- arc {
99
+ circle {
100
100
  x_center 200
101
101
  y_center 200
102
102
  radius 90
103
- start_angle 0
104
- sweep 360
105
- is_negative false
106
103
  }
107
-
104
+
108
105
  fill r: 202, g: 102, b: 204, a: 0.5
109
106
  stroke r: 0, g: 0, b: 0, thickness: 2
110
107
  }
108
+ path { # a dynamic path is added semi-declaratively inside on_draw block
109
+ arc {
110
+ x_center 400
111
+ y_center 220
112
+ radius 180
113
+ start_angle 90
114
+ sweep 90
115
+ is_negative false
116
+ }
117
+
118
+ fill r: 204, g: 102, b: 204, a: 0.5
119
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
120
+ }
111
121
  end
112
122
 
113
123
  on_mouse_event do |area_mouse_event|
@@ -0,0 +1,223 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ class ColorTheCircles
4
+ include Glimmer
5
+
6
+ WINDOW_WIDTH = 800
7
+ WINDOW_HEIGHT = 600
8
+ CIRCLE_MIN_RADIUS = 15
9
+ CIRCLE_MAX_RADIUS = 50
10
+ MARGIN_WIDTH = 55
11
+ MARGIN_HEIGHT = 155
12
+ TIME_MAX_EASY = 4
13
+ TIME_MAX_MEDIUM = 3
14
+ TIME_MAX_HARD = 2
15
+ TIME_MAX_INSANE = 1
16
+
17
+ attr_accessor :score
18
+
19
+ def initialize
20
+ @circles_data = []
21
+ @score = 0
22
+ @time_max = TIME_MAX_HARD
23
+ @game_over = false
24
+ register_observers
25
+ setup_circle_factory
26
+ end
27
+
28
+ def register_observers
29
+ observer = Glimmer::DataBinding::Observer.proc do |new_score|
30
+ @score_label.text = new_score.to_s
31
+ if new_score == -20
32
+ @game_over = true
33
+ msg_box('You Lost!', 'Sorry! Your score reached -20')
34
+ restart_game
35
+ elsif new_score == 0
36
+ @game_over = true
37
+ msg_box('You Won!', 'Congratulations! Your score reached 0')
38
+ restart_game
39
+ end
40
+ end
41
+ observer.observe(self, :score) # automatically enhances self to become Glimmer::DataBinding::ObservableModel and notify observer on score attribute changes
42
+ end
43
+
44
+ def setup_circle_factory
45
+ consumer = Proc.new do
46
+ unless @game_over
47
+ if @circles_data.empty?
48
+ # start with 3 circles to make more challenging
49
+ add_circle until @circles_data.size > 3
50
+ else
51
+ add_circle
52
+ end
53
+ end
54
+ delay = rand * @time_max
55
+ Glimmer::LibUI.timer(delay, repeat: false, &consumer)
56
+ end
57
+ Glimmer::LibUI.queue_main(&consumer)
58
+ end
59
+
60
+ def add_circle
61
+ circle_x_center = rand * (WINDOW_WIDTH - MARGIN_WIDTH - CIRCLE_MAX_RADIUS) + CIRCLE_MAX_RADIUS
62
+ circle_y_center = rand * (WINDOW_HEIGHT - MARGIN_HEIGHT - CIRCLE_MAX_RADIUS) + CIRCLE_MAX_RADIUS
63
+ circle_radius = rand * (CIRCLE_MAX_RADIUS - CIRCLE_MIN_RADIUS) + CIRCLE_MIN_RADIUS
64
+ stroke_color = Glimmer::LibUI.x11_colors.sample
65
+ @circles_data << {
66
+ args: [circle_x_center, circle_y_center, circle_radius],
67
+ fill: nil,
68
+ stroke: stroke_color
69
+ }
70
+ @area.queue_redraw_all
71
+ self.score -= 1 # notifies score observers automatically of change
72
+ end
73
+
74
+ def restart_game
75
+ @score = 0 # update variable directly to avoid notifying observers
76
+ @circles_data.clear
77
+ @game_over = false
78
+ end
79
+
80
+ def color_circle(x, y)
81
+ clicked_circle_data = @circles_data.find do |circle_data|
82
+ circle_data[:fill].nil? && circle_data[:circle].include?(x, y)
83
+ end
84
+ if clicked_circle_data
85
+ clicked_circle_data[:fill] = clicked_circle_data[:stroke]
86
+ push_colored_circle_behind_uncolored_circles(clicked_circle_data)
87
+ @area.queue_redraw_all
88
+ self.score += 1 # notifies score observers automatically of change
89
+ end
90
+ end
91
+
92
+ def push_colored_circle_behind_uncolored_circles(colored_circle_data)
93
+ removed_colored_circle_data = @circles_data.delete(colored_circle_data)
94
+ last_colored_circle_data = @circles_data.select {|cd| cd[:fill]}.last
95
+ last_colored_circle_data_index = @circles_data.index(last_colored_circle_data) || -1
96
+ @circles_data.insert(last_colored_circle_data_index + 1, removed_colored_circle_data)
97
+ end
98
+
99
+ def launch
100
+ menu('Actions') {
101
+ menu_item('Restart') {
102
+ on_clicked do
103
+ restart_game
104
+ end
105
+ }
106
+
107
+ quit_menu_item
108
+ }
109
+
110
+ menu('Difficulty') {
111
+ radio_menu_item('Easy') {
112
+ on_clicked do
113
+ @time_max = TIME_MAX_EASY
114
+ end
115
+ }
116
+
117
+ radio_menu_item('Medium') {
118
+ on_clicked do
119
+ @time_max = TIME_MAX_MEDIUM
120
+ end
121
+ }
122
+
123
+ radio_menu_item('Hard') {
124
+ checked true
125
+
126
+ on_clicked do
127
+ @time_max = TIME_MAX_HARD
128
+ end
129
+ }
130
+
131
+ radio_menu_item('Insane') {
132
+ on_clicked do
133
+ @time_max = TIME_MAX_INSANE
134
+ end
135
+ }
136
+ }
137
+
138
+ menu('Help') {
139
+ menu_item('Instructions') {
140
+ on_clicked do
141
+ msg_box('Instructions', "Score goes down as circles are added.\nIf it reaches -20, you lose!\n\nClick circles to color and score!\nOnce score reaches 0, you win!\n\nBeware of concealed light-colored circles!\nThey are revealed once darker circles intersect them.\n\nThere are four levels of difficulty.\nChange via difficulty menu if the game gets too tough.")
142
+ end
143
+ }
144
+ }
145
+
146
+ window('Color The Circles', WINDOW_WIDTH, WINDOW_HEIGHT) {
147
+ margined true
148
+
149
+ grid {
150
+ button('Restart') {
151
+ left 0
152
+ top 0
153
+ halign :center
154
+
155
+ on_clicked do
156
+ restart_game
157
+ end
158
+ }
159
+
160
+ label('Score goes down as circles are added. If it reaches -20, you lose!') {
161
+ left 0
162
+ top 1
163
+ halign :center
164
+ }
165
+
166
+ label('Click circles to color and score! Once score reaches 0, you win!') {
167
+ left 0
168
+ top 2
169
+ halign :center
170
+ }
171
+
172
+ horizontal_box {
173
+ left 0
174
+ top 3
175
+ halign :center
176
+
177
+ label('Score:') {
178
+ stretchy false
179
+ }
180
+
181
+ @score_label = label(@score.to_s) {
182
+ stretchy false
183
+ }
184
+ }
185
+
186
+ vertical_box {
187
+ left 0
188
+ top 4
189
+ hexpand true
190
+ vexpand true
191
+ halign :fill
192
+ valign :fill
193
+
194
+ @area = area {
195
+ on_draw do |area_draw_params|
196
+ path {
197
+ rectangle(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT)
198
+
199
+ fill :white
200
+ }
201
+
202
+ @circles_data.each do |circle_data|
203
+ path {
204
+ circle_data[:circle] = circle(*circle_data[:args])
205
+
206
+ fill circle_data[:fill]
207
+ stroke circle_data[:stroke]
208
+ }
209
+ end
210
+ end
211
+
212
+ on_mouse_down do |area_mouse_event|
213
+ color_circle(area_mouse_event[:x], area_mouse_event[:y])
214
+ end
215
+ }
216
+ }
217
+
218
+ }
219
+ }.show
220
+ end
221
+ end
222
+
223
+ ColorTheCircles.new.launch
@@ -78,7 +78,7 @@ MAIN_WINDOW = window('Control Gallery', 600, 500) {
78
78
  date_picker { stretchy false }
79
79
 
80
80
  time_picker { stretchy false }
81
-
81
+
82
82
  date_time_picker { stretchy false }
83
83
 
84
84
  font_button { stretchy false }
@@ -45,6 +45,7 @@ window('Contacts', 600, 600) { |w|
45
45
  msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
46
46
  else
47
47
  data << new_row # automatically inserts a row into the table due to implicit data-binding
48
+ @unfiltered_data = data.dup
48
49
  @name_entry.text = ''
49
50
  @email_entry.text = ''
50
51
  @phone_entry.text = ''
@@ -54,6 +55,25 @@ window('Contacts', 600, 600) { |w|
54
55
  end
55
56
  }
56
57
 
58
+ search_entry { |se|
59
+ stretchy false
60
+
61
+ on_changed do
62
+ filter_value = se.text
63
+ @unfiltered_data ||= data.dup
64
+ # Unfilter first to remove any previous filters
65
+ data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
66
+ # Now, apply filter if entered
67
+ unless filter_value.empty?
68
+ data.filter! do |row_data| # affects table indirectly through implicit data-binding
69
+ row_data.any? do |cell|
70
+ cell.to_s.downcase.include?(filter_value.downcase)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ }
76
+
57
77
  table {
58
78
  text_column('Name')
59
79
  text_column('Email')
data/examples/grid.rb CHANGED
@@ -6,7 +6,7 @@ include Glimmer
6
6
 
7
7
  window('Grid') {
8
8
  tab {
9
- tab_item('Spanning') {
9
+ tab_item('Span') {
10
10
  grid {
11
11
  4.times { |top_value|
12
12
  4.times { |left_value|
@@ -46,7 +46,7 @@ window('Grid') {
46
46
  }
47
47
  }
48
48
  }
49
- tab_item('Expanding') {
49
+ tab_item('Expand') {
50
50
  grid {
51
51
  label("(0, 0) hexpand/vexpand\nall available horizontal space is taken\nand\nall\navailable\nvertical\nspace\nis\ntaken") {
52
52
  left 0
@@ -68,5 +68,41 @@ window('Grid') {
68
68
  }
69
69
  }
70
70
  }
71
+ tab_item('Align') {
72
+ grid {
73
+ label("(0, 0) halign/valign fill\nall available horizontal space is taken\nand\nall\navailable\nvertical\nspace\nis\ntaken") {
74
+ left 0
75
+ top 0
76
+ hexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
77
+ vexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
78
+ halign :fill
79
+ valign :fill
80
+ }
81
+ label("(1, 0) halign/valign start") {
82
+ left 1
83
+ top 0
84
+ hexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
85
+ vexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
86
+ halign :start
87
+ valign :start
88
+ }
89
+ label("(0, 1) halign/valign center") {
90
+ left 0
91
+ top 1
92
+ hexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
93
+ vexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
94
+ halign :center
95
+ valign :center
96
+ }
97
+ label("(1, 1) halign/valign end") {
98
+ left 1
99
+ top 1
100
+ hexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
101
+ vexpand true unless OS.mac? # on Mac, only the first label is given all space, so avoid expanding
102
+ halign :end
103
+ valign :end
104
+ }
105
+ }
106
+ }
71
107
  }
72
108
  }.show
data/examples/login.rb ADDED
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'glimmer-dsl-libui'
4
+
5
+ include Glimmer
6
+
7
+ window('Login') {
8
+ margined true
9
+
10
+ vertical_box {
11
+ form {
12
+ @username_entry = entry {
13
+ label 'Username:'
14
+ }
15
+
16
+ @password_entry = password_entry {
17
+ label 'Password:'
18
+ }
19
+ }
20
+
21
+ horizontal_box {
22
+ @login_button = button('Login') {
23
+ on_clicked do
24
+ @username_entry.enabled = false
25
+ @password_entry.enabled = false
26
+ @login_button.enabled = false
27
+ @logout_button.enabled = true
28
+ end
29
+ }
30
+
31
+ @logout_button = button('Logout') {
32
+ enabled false
33
+
34
+ on_clicked do
35
+ @username_entry.text = ''
36
+ @password_entry.text = ''
37
+ @username_entry.enabled = true
38
+ @password_entry.enabled = true
39
+ @login_button.enabled = true
40
+ @logout_button.enabled = false
41
+ end
42
+ }
43
+ }
44
+ }
45
+ }.show
@@ -48,7 +48,7 @@ class TinyMidiPlayer
48
48
  end
49
49
 
50
50
  def create_gui
51
- menu('Help') { |m|
51
+ menu('Help') {
52
52
  menu_item('Version') {
53
53
  on_clicked do
54
54
  show_version
data/examples/timer.rb ADDED
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'glimmer-dsl-libui'
4
+
5
+ class Timer
6
+ include Glimmer
7
+
8
+ SECOND_MAX = 59
9
+ MINUTE_MAX = 59
10
+ HOUR_MAX = 23
11
+
12
+ def initialize
13
+ @pid = nil
14
+ @alarm_file = File.expand_path('../sounds/AlanWalker-Faded.mid', __dir__)
15
+ at_exit { stop_alarm }
16
+ setup_timer
17
+ create_gui
18
+ end
19
+
20
+ def stop_alarm
21
+ if @pid
22
+ if @th.alive?
23
+ Process.kill(:SIGKILL, @pid)
24
+ @pid = nil
25
+ else
26
+ @pid = nil
27
+ end
28
+ end
29
+ end
30
+
31
+ def play_alarm
32
+ stop_alarm
33
+ if @pid.nil?
34
+ begin
35
+ @pid = spawn "timidity -G 0.0-10.0 #{@alarm_file}"
36
+ @th = Process.detach @pid
37
+ rescue Errno::ENOENT
38
+ warn 'Timidty++ not found. Please install Timidity++.'
39
+ warn 'https://sourceforge.net/projects/timidity/'
40
+ end
41
+ end
42
+ end
43
+
44
+ def setup_timer
45
+ unless @setup_timer
46
+ Glimmer::LibUI.timer(1) do
47
+ if @started
48
+ seconds = @sec_spinbox.value
49
+ minutes = @min_spinbox.value
50
+ hours = @hour_spinbox.value
51
+ if seconds > 0
52
+ @sec_spinbox.value = seconds -= 1
53
+ end
54
+ if seconds == 0
55
+ if minutes > 0
56
+ @min_spinbox.value = minutes -= 1
57
+ @sec_spinbox.value = seconds = SECOND_MAX
58
+ end
59
+ if minutes == 0
60
+ if hours > 0
61
+ @hour_spinbox.value = hours -= 1
62
+ @min_spinbox.value = minutes = MINUTE_MAX
63
+ @sec_spinbox.value = seconds = SECOND_MAX
64
+ end
65
+ if hours == 0 && minutes == 0 && seconds == 0
66
+ @start_button.enabled = true
67
+ @stop_button.enabled = false
68
+ @started = false
69
+ unless @played
70
+ play_alarm
71
+ msg_box('Alarm', 'Countdown Is Finished!')
72
+ @played = true
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ @setup_timer = true
80
+ end
81
+ end
82
+
83
+ def create_gui
84
+ window('Timer') {
85
+ margined true
86
+
87
+ group('Countdown') {
88
+ vertical_box {
89
+ horizontal_box {
90
+ @hour_spinbox = spinbox(0, HOUR_MAX) {
91
+ stretchy false
92
+ value 0
93
+ }
94
+ label(':') {
95
+ stretchy false
96
+ }
97
+ @min_spinbox = spinbox(0, MINUTE_MAX) {
98
+ stretchy false
99
+ value 0
100
+ }
101
+ label(':') {
102
+ stretchy false
103
+ }
104
+ @sec_spinbox = spinbox(0, SECOND_MAX) {
105
+ stretchy false
106
+ value 0
107
+ }
108
+ }
109
+ horizontal_box {
110
+ @start_button = button('Start') {
111
+ on_clicked do
112
+ @start_button.enabled = false
113
+ @stop_button.enabled = true
114
+ @started = true
115
+ @played = false
116
+ end
117
+ }
118
+
119
+ @stop_button = button('Stop') {
120
+ enabled false
121
+
122
+ on_clicked do
123
+ @start_button.enabled = true
124
+ @stop_button.enabled = false
125
+ @started = false
126
+ end
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }.show
132
+ end
133
+ end
134
+
135
+ Timer.new
Binary file
@@ -0,0 +1,40 @@
1
+ # Copyright (c) 2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/libui/control_proxy/area_proxy'
23
+
24
+ module Glimmer
25
+ module LibUI
26
+ class ControlProxy
27
+ class AreaProxy < ControlProxy
28
+ # Proxy for LibUI scrolling area objects
29
+ #
30
+ # Follows the Proxy Design Pattern
31
+ class ScrollingAreaProxy < AreaProxy
32
+ def build_control
33
+ @area_handler = ::LibUI::FFI::AreaHandler.malloc
34
+ @libui = ::LibUI.new_scrolling_area(@area_handler, *@args)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end