glimmer-dsl-libui 0.2.22 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -20,44 +20,192 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  require 'glimmer/libui/control_proxy'
23
+ require 'glimmer/libui/control_proxy/image_part_proxy'
24
+ require 'glimmer/libui/image_path_renderer'
23
25
  require 'glimmer/data_binding/observer'
26
+ require 'glimmer/libui/control_proxy/transformable'
24
27
 
25
28
  using ArrayIncludeMethods
26
29
 
27
30
  module Glimmer
28
31
  module LibUI
29
32
  class ControlProxy
30
- # Proxy for LibUI image objects
33
+ # Proxy for LibUI image object and Glimmer custom control
31
34
  #
32
35
  # Follows the Proxy Design Pattern
33
36
  class ImageProxy < ControlProxy
37
+ include Parent
38
+ prepend Transformable
39
+
40
+ attr_reader :data, :pixels, :shapes
41
+
34
42
  def initialize(keyword, parent, args, &block)
35
43
  @keyword = keyword
36
44
  @parent_proxy = parent
37
45
  @args = args
38
46
  @block = block
39
47
  @enabled = true
40
- @children = []
41
48
  post_add_content if @block.nil?
42
49
  end
43
-
50
+
44
51
  def post_add_content
45
- build_control
46
- super
52
+ if area_image?
53
+ @shapes = nil
54
+ super
55
+ if @parent_proxy.nil? && AreaProxy.current_area_draw_params
56
+ draw(AreaProxy.current_area_draw_params)
57
+ destroy
58
+ end
59
+ @content_added = true
60
+ else # image object not control
61
+ build_control
62
+ super
63
+ end
64
+ end
65
+
66
+ def file(value = nil)
67
+ if value.nil?
68
+ @args[0]
69
+ else
70
+ @args[0] = value
71
+ if @content_added
72
+ post_add_content
73
+ request_auto_redraw
74
+ end
75
+ end
76
+ end
77
+ alias file= file
78
+ alias set_file file
79
+
80
+ def width(value = nil)
81
+ if value.nil?
82
+ @args[1]
83
+ else
84
+ @args[1] = value
85
+ if area_image? && @content_added
86
+ post_add_content
87
+ request_auto_redraw
88
+ end
89
+ end
90
+ end
91
+ alias width= width
92
+ alias set_width width
93
+
94
+ def height(value = nil)
95
+ if value.nil?
96
+ @args[2]
97
+ else
98
+ @args[2] = value
99
+ if area_image? && @content_added
100
+ post_add_content
101
+ request_auto_redraw
102
+ end
103
+ end
104
+ end
105
+ alias height= height
106
+ alias set_height height
107
+
108
+ def redraw
109
+ @parent_proxy&.redraw
110
+ end
111
+
112
+ def request_auto_redraw
113
+ @parent_proxy&.request_auto_redraw if area_image?
114
+ end
115
+
116
+ def draw(area_draw_params)
117
+ if @shapes.nil?
118
+ load_image
119
+ parse_pixels
120
+ calculate_shapes
121
+ end
122
+ ImagePathRenderer.new(@parent_proxy, @shapes).render
47
123
  end
48
-
49
- def post_initialize_child(child)
50
- @children << child
124
+
125
+ def area_image?
126
+ @parent_proxy&.is_a?(AreaProxy) or
127
+ AreaProxy.current_area_draw_params
128
+ end
129
+
130
+ def destroy
131
+ @parent_proxy&.children&.delete(self)
132
+ ControlProxy.control_proxies.delete(self)
51
133
  end
52
134
 
53
135
  private
54
136
 
55
137
  def build_control
56
- @args = [@children.first.args[1], @children.first.args[2]] if @children.size == 1 && (@args[0].nil? || @args[1].nil?)
57
- super
58
- @libui.tap do
59
- @children.each {|child| child&.send(:build_control) }
138
+ unless area_image? # image object
139
+ if file
140
+ load_image
141
+ ImagePartProxy.new('image_part', self, [@data, width, height, width * 4])
142
+ end
143
+ @args[1] = @children.first.args[1] if @children.size == 1 && @args[1].nil?
144
+ @args[2] = @children.first.args[2] if @children.size == 1 && @args[2].nil?
145
+ @libui = ControlProxy.new_control(@keyword, [width, height])
146
+ @libui.tap do
147
+ @children.each {|child| child&.send(:build_control) }
148
+ end
149
+ end
150
+ rescue => e
151
+ Glimmer::Config.logger.error {"Failed to load image file: #{file}"}
152
+ Glimmer::Config.logger.error {e.full_message}
153
+ raise e
154
+ end
155
+
156
+ def load_image
157
+ require 'chunky_png'
158
+ canvas = nil
159
+ if file.start_with?('http')
160
+ require 'net/http'
161
+ require 'open-uri'
162
+ uri = URI(file)
163
+ canvas = ChunkyPNG::Canvas.from_string(Net::HTTP.get(uri))
164
+ else
165
+ f = File.open(file)
166
+ canvas = ChunkyPNG::Canvas.from_io(f)
167
+ f.close
168
+ end
169
+ canvas.resample_nearest_neighbor!(width, height) if width && height
170
+ @data = canvas.to_rgba_stream
171
+ self.width = canvas.width
172
+ self.height = canvas.height
173
+ [@data, width, height]
174
+ end
175
+
176
+ def parse_pixels
177
+ @pixels = height.times.map do |y|
178
+ width.times.map do |x|
179
+ r = @data[(y*width + x)*4].ord
180
+ g = @data[(y*width + x)*4 + 1].ord
181
+ b = @data[(y*width + x)*4 + 2].ord
182
+ a = @data[(y*width + x)*4 + 3].ord
183
+ {x: x, y: y, color: {r: r, g: g, b: b, a: a}}
184
+ end
185
+ end.flatten
186
+ end
187
+
188
+ def calculate_shapes
189
+ @shapes = []
190
+ original_pixels = @pixels.dup
191
+ indexed_original_pixels = Hash[original_pixels.each_with_index.to_a]
192
+ @pixels.each do |pixel|
193
+ index = indexed_original_pixels[pixel]
194
+ @rectangle_start_x ||= pixel[:x]
195
+ @rectangle_width ||= 1
196
+ if pixel[:x] < width - 1 && pixel[:color] == original_pixels[index + 1][:color]
197
+ @rectangle_width += 1
198
+ else
199
+ if pixel[:x] > 0 && pixel[:color] == original_pixels[index - 1][:color]
200
+ @shapes << {x: @rectangle_start_x, y: pixel[:y], width: @rectangle_width, height: 1, color: pixel[:color]}
201
+ else
202
+ @shapes << {x: pixel[:x], y: pixel[:y], width: 1, height: 1, color: pixel[:color]}
203
+ end
204
+ @rectangle_width = 1
205
+ @rectangle_start_x = pixel[:x] == width - 1 ? 0 : pixel[:x] + 1
206
+ end
60
207
  end
208
+ @shapes
61
209
  end
62
210
  end
63
211
  end
@@ -21,6 +21,7 @@
21
21
 
22
22
  require 'glimmer/libui/control_proxy'
23
23
  require 'glimmer/libui/control_proxy/dual_column'
24
+ require 'glimmer/libui/control_proxy/triple_column'
24
25
  require 'glimmer/data_binding/observer'
25
26
  require 'glimmer/fiddle_consumer'
26
27
 
@@ -137,7 +138,7 @@ module Glimmer
137
138
 
138
139
  def build_control
139
140
  @model_handler = ::LibUI::FFI::TableModelHandler.malloc
140
- @model_handler.NumColumns = fiddle_closure_block_caller(4) { @columns.map {|c| c.is_a?(DualColumn) ? 2 : 1}.sum }
141
+ @model_handler.NumColumns = fiddle_closure_block_caller(4) { @columns.map {|c| c.is_a?(DualColumn) ? 2 : (c.is_a?(TripleColumn) ? 3 : 1)}.sum }
141
142
  @model_handler.ColumnType = fiddle_closure_block_caller(4, [1, 1, 4]) do |_, _, column|
142
143
  # TODO consider refactoring to use Glimmer::LibUI.enum_symbols(:table_value_type)
143
144
  case @columns[column]
@@ -193,15 +194,27 @@ module Glimmer
193
194
  column = @columns[column].index
194
195
  @cell_rows[row] ||= []
195
196
  @cell_rows[row][column] = ::LibUI.table_value_string(val).to_s
197
+ when Column::TextColorColumnProxy
198
+ column = @columns[column].index
199
+ @cell_rows[row] ||= []
200
+ @cell_rows[row][column] ||= []
201
+ @cell_rows[row][column][0] = ::LibUI.table_value_string(val).to_s
196
202
  when :text
197
203
  column = @columns[column - 1].index
204
+ @cell_rows[row] ||= []
205
+ @cell_rows[row][column] ||= []
198
206
  @cell_rows[row][column][1] = ::LibUI.table_value_string(val).to_s
199
207
  when Column::ButtonColumnProxy
200
208
  @columns[column].notify_listeners(:on_clicked, row)
201
- when Column::CheckboxColumnProxy, Column::CheckboxTextColumnProxy
209
+ when Column::CheckboxColumnProxy
202
210
  column = @columns[column].index
203
211
  @cell_rows[row] ||= []
204
212
  @cell_rows[row][column] = ::LibUI.table_value_int(val).to_i == 1
213
+ when Column::CheckboxTextColumnProxy
214
+ column = @columns[column].index
215
+ @cell_rows[row] ||= []
216
+ @cell_rows[row][column] ||= []
217
+ @cell_rows[row][column][0] = ::LibUI.table_value_int(val).to_i == 1
205
218
  end
206
219
  on_edited.each {|listener| listener.call(row, @cell_rows[row])}
207
220
  end
@@ -45,7 +45,7 @@ module Glimmer
45
45
 
46
46
  def destroy
47
47
  super
48
- ControlProxy.image_proxies.each { |image_proxy| ::LibUI.free_image(image_proxy.libui) }
48
+ ControlProxy.image_proxies.each { |image_proxy| ::LibUI.free_image(image_proxy.libui) unless image_proxy.area_image? }
49
49
  @on_destroy_procs&.each { |on_destroy_proc| on_destroy_proc.call(self)}
50
50
  end
51
51
 
@@ -27,8 +27,8 @@ module Glimmer
27
27
  class ControlProxy
28
28
  class << self
29
29
  def exists?(keyword)
30
- ::LibUI.respond_to?("new_#{keyword}") ||
31
- ::LibUI.respond_to?(keyword) ||
30
+ ::LibUI.respond_to?("new_#{keyword}") or
31
+ ::LibUI.respond_to?(keyword) or
32
32
  descendant_keyword_constant_map.keys.include?(keyword)
33
33
  end
34
34
 
@@ -207,11 +207,11 @@ module Glimmer
207
207
  end
208
208
 
209
209
  def respond_to_libui?(method_name, *args, &block)
210
- ::LibUI.respond_to?("control_#{method_name}") ||
211
- (::LibUI.respond_to?("control_#{method_name.to_s.sub(/\?$/, '')}") && BOOLEAN_PROPERTIES.include?(method_name.to_s.sub(/\?$/, '')) ) ||
212
- ::LibUI.respond_to?("control_set_#{method_name.to_s.sub(/=$/, '')}") ||
213
- ::LibUI.respond_to?("#{libui_api_keyword}_#{method_name}") ||
214
- (::LibUI.respond_to?("#{libui_api_keyword}_#{method_name.to_s.sub(/\?$/, '')}") && BOOLEAN_PROPERTIES.include?(method_name.to_s.sub(/\?$/, '')) ) ||
210
+ ::LibUI.respond_to?("control_#{method_name}") or
211
+ (::LibUI.respond_to?("control_#{method_name.to_s.sub(/\?$/, '')}") && BOOLEAN_PROPERTIES.include?(method_name.to_s.sub(/\?$/, '')) ) or
212
+ ::LibUI.respond_to?("control_set_#{method_name.to_s.sub(/=$/, '')}") or
213
+ ::LibUI.respond_to?("#{libui_api_keyword}_#{method_name}") or
214
+ (::LibUI.respond_to?("#{libui_api_keyword}_#{method_name.to_s.sub(/\?$/, '')}") && BOOLEAN_PROPERTIES.include?(method_name.to_s.sub(/\?$/, '')) ) or
215
215
  ::LibUI.respond_to?("#{libui_api_keyword}_set_#{method_name.to_s.sub(/=$/, '')}")
216
216
  end
217
217
 
@@ -0,0 +1,30 @@
1
+ module Glimmer
2
+ module LibUI
3
+ class ImagePathRenderer
4
+ include Glimmer
5
+
6
+ def initialize(area_proxy, shapes)
7
+ @area_proxy = area_proxy
8
+ @shapes = shapes
9
+ end
10
+
11
+ def render
12
+ work = Proc.new do
13
+ @shapes.each do |shape|
14
+ path {
15
+ rectangle(shape[:x], shape[:y], shape[:width], shape[:height])
16
+
17
+ fill shape[:color]
18
+ }
19
+ end
20
+ end
21
+ if @area_proxy.nil?
22
+ # Ensure it renders without a parent
23
+ Glimmer::DSL::Engine.add_content(nil, Glimmer::DSL::Libui::ControlExpression.new, 'image', &work)
24
+ else
25
+ work.call
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
data/lib/glimmer/libui.rb CHANGED
@@ -26,12 +26,12 @@ module Glimmer
26
26
  class << self
27
27
  include Glimmer::FiddleConsumer
28
28
 
29
- def integer_to_boolean(int, allow_nil: true)
30
- int.nil? ? (allow_nil ? nil : false) : ((int.is_a?(TrueClass) || int.is_a?(FalseClass)) ? int : (int.is_a?(Integer) ? int == 1 : (allow_nil ? nil : false)))
29
+ def integer_to_boolean(int, allow_nil: true, allow_boolean: true)
30
+ int.nil? ? (allow_nil ? nil : false) : (allow_boolean && (int.is_a?(TrueClass) || int.is_a?(FalseClass)) ? int : (int.is_a?(Integer) ? int == 1 : (allow_nil ? nil : false)))
31
31
  end
32
32
 
33
- def boolean_to_integer(bool, allow_nil: true)
34
- bool.nil? ? (allow_nil ? nil : 0) : (bool.is_a?(Integer) ? bool : (bool.is_a?(TrueClass) || bool.is_a?(FalseClass) ? (bool == true ? 1 : 0) : (allow_nil ? nil : 0)))
33
+ def boolean_to_integer(bool, allow_nil: true, allow_integer: true)
34
+ bool.nil? ? (allow_nil ? nil : 0) : (allow_integer && bool.is_a?(Integer) ? bool : (bool.is_a?(TrueClass) || bool.is_a?(FalseClass) ? (bool == true ? 1 : 0) : (allow_nil ? nil : 0)))
35
35
  end
36
36
 
37
37
  def degrees_to_radians(degrees)
@@ -83,6 +83,7 @@ module Glimmer
83
83
  value = value.chars.map {|char| [char, char]}.flatten.join if value.length == 3
84
84
  value = "0x#{value}"
85
85
  end
86
+ value = "0x#{value[1..-1]}" if value.start_with?('#')
86
87
  value = value.to_i(16)
87
88
  end
88
89
  if value.is_a?(Integer)
@@ -173,7 +174,7 @@ module Glimmer
173
174
  # If block returns true at any point, the timer continues for another repetition regardless of `repeat:` keyword arg value
174
175
  def timer(time_in_seconds = 0.1, repeat: true, &block)
175
176
  closure = fiddle_closure_block_caller(4, [0]) do
176
- result = boolean_to_integer(block.call)
177
+ result = boolean_to_integer(block.call, allow_integer: false)
177
178
  repeat -= 1 if repeat.is_a?(Integer)
178
179
  if result.nil?
179
180
  if (repeat == true || (repeat.is_a?(Integer) && repeat > 0))
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer-dsl-libui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.22
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-08 00:00:00.000000000 Z
11
+ date: 2021-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.4.0
19
+ version: 2.4.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 2.4.0
26
+ version: 2.4.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: os
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -72,6 +72,20 @@ dependencies:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
74
  version: 0.0.12
75
+ - !ruby/object:Gem::Dependency
76
+ name: chunky_png
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 1.4.0
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: 1.4.0
75
89
  - !ruby/object:Gem::Dependency
76
90
  name: juwelier
77
91
  requirement: !ruby/object:Gem::Requirement
@@ -176,20 +190,6 @@ dependencies:
176
190
  - - "~>"
177
191
  - !ruby/object:Gem::Version
178
192
  version: 0.7.0
179
- - !ruby/object:Gem::Dependency
180
- name: chunky_png
181
- requirement: !ruby/object:Gem::Requirement
182
- requirements:
183
- - - "~>"
184
- - !ruby/object:Gem::Version
185
- version: 1.4.0
186
- type: :development
187
- prerelease: false
188
- version_requirements: !ruby/object:Gem::Requirement
189
- requirements:
190
- - - "~>"
191
- - !ruby/object:Gem::Version
192
- version: 1.4.0
193
193
  description: Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI
194
194
  Library) - No need to pre-install any prerequisites. Just install the gem and have
195
195
  platform-independent native GUI that just works! Glimmer DSL for LibUI aims to provide
@@ -221,13 +221,21 @@ files:
221
221
  - examples/basic_draw_text.rb
222
222
  - examples/basic_draw_text2.rb
223
223
  - examples/basic_entry.rb
224
+ - examples/basic_image.rb
225
+ - examples/basic_image2.rb
226
+ - examples/basic_image3.rb
227
+ - examples/basic_image4.rb
228
+ - examples/basic_image5.rb
224
229
  - examples/basic_table.rb
225
230
  - examples/basic_table_button.rb
226
231
  - examples/basic_table_checkbox.rb
227
232
  - examples/basic_table_checkbox_text.rb
228
233
  - examples/basic_table_color.rb
234
+ - examples/basic_table_color2.rb
229
235
  - examples/basic_table_image.rb
236
+ - examples/basic_table_image2.rb
230
237
  - examples/basic_table_image_text.rb
238
+ - examples/basic_table_image_text2.rb
231
239
  - examples/basic_table_progress_bar.rb
232
240
  - examples/basic_transform.rb
233
241
  - examples/basic_window.rb
@@ -347,6 +355,7 @@ files:
347
355
  - lib/glimmer/libui/control_proxy/transformable.rb
348
356
  - lib/glimmer/libui/control_proxy/triple_column.rb
349
357
  - lib/glimmer/libui/control_proxy/window_proxy.rb
358
+ - lib/glimmer/libui/image_path_renderer.rb
350
359
  - lib/glimmer/libui/parent.rb
351
360
  - lib/glimmer/libui/shape.rb
352
361
  - lib/glimmer/libui/shape/arc.rb