lib/glimmer/swt/custom/shape.rb in glimmer-dsl-swt-4.18.4.11 vs lib/glimmer/swt/custom/shape.rb in glimmer-dsl-swt-4.18.5.0

- old
+ new

@@ -36,10 +36,19 @@ include Properties # TODO support textExtent sized shapes nested within text/string # TODO support a Pattern DSL for methods that take Pattern arguments class << self + def create(parent, keyword, *args, &property_block) + potential_shape_class_name = keyword.to_s.camelcase(:upper).to_sym + if constants.include?(potential_shape_class_name) + const_get(potential_shape_class_name).new(parent, keyword, *args, &property_block) + else + new(parent, keyword, *args, &property_block) + end + end + def valid?(parent, keyword, *args, &block) gc_instance_methods.include?(method_name(keyword, arg_options(args))) end def gc_instance_methods @@ -77,19 +86,19 @@ def flyweight_method_names @flyweight_method_names ||= {} end def pattern(*args) - found_pattern = flyweigh_patterns[args] + found_pattern = flyweight_patterns[args] if found_pattern.nil? || found_pattern.is_disposed - found_pattern = flyweigh_patterns[args] = org.eclipse.swt.graphics.Pattern.new(*args) + found_pattern = flyweight_patterns[args] = org.eclipse.swt.graphics.Pattern.new(*args) end found_pattern end - def flyweigh_patterns - @flyweigh_patterns ||= {} + def flyweight_patterns + @flyweight_patterns ||= {} end end attr_reader :parent, :name, :args, :options @@ -121,10 +130,33 @@ def round? @options[:round] end + # subclasses (like polygon) may override to indicate if a point x,y coordinates fall inside the shape + # has a default implementation for rectangle and oval + def include?(x, y) + case @name + when 'rectangle', 'oval', 'arc' + self_x = self.x + self_y = self.y + width = self.width + height = self.height + x.between?(self_x, self_x + width) && y.between?(self_y, self_y + height) + else + false + end + end + + def move_by(x_delta, y_delta) + case @name + when 'rectangle', 'oval', 'arc' + self.x += x_delta + self.y += y_delta + end + end + def has_some_background? @properties.keys.map(&:to_s).include?('background') || @properties.keys.map(&:to_s).include?('background_pattern') end def has_some_foreground? @@ -172,22 +204,51 @@ args[i] = ColorProxy.new(arg).swt_color elsif arg.is_a?(ColorProxy) args[i] = arg.swt_color end end - new_args = [DisplayProxy.instance.swt_display] + args - args[0] = pattern(*new_args, type: method_name.to_s.match(/set(.+)Pattern/)[1]) + @pattern_args ||= {} + pattern_type = method_name.to_s.match(/set(.+)Pattern/)[1] + if args.first.is_a?(Pattern) + new_args = @pattern_args[pattern_type] + else + new_args = [DisplayProxy.instance.swt_display] + args + @pattern_args[pattern_type] = new_args + end + args[0] = pattern(*new_args, type: pattern_type) args[1..-1] = [] end args end def apply_shape_arg_conversions! if @args.size > 1 && (['polygon', 'polyline'].include?(@name)) @args[0] = @args.dup @args[1..-1] = [] end + if @name == 'image' + if @args.first.is_a?(String) + @args[0] = ImageProxy.new(@args[0]) + end + if @args.first.is_a?(ImageProxy) + @image = @args[0] = @args[0].swt_image + end + if @args.first.nil? + @image = nil + end + end + if @name == 'text' + if @args[3].is_a?(Symbol) || @args[3].is_a?(String) + @args[3] = [@args[3]] + end + if @args[3].is_a?(Array) + if @args[3].size == 1 && @args[3].first.is_a?(Array) + @args[3] = @args[3].first + end + @args[3] = SWTProxy[*@args[3]] + end + end end def apply_shape_arg_defaults! if @name.include?('rectangle') && round? && @args.size.between?(4, 5) (6 - @args.size).times {@args << 60} @@ -200,33 +261,27 @@ @parent.requires_shape_disposal = true if @args.size == 1 @args[1] = 0 @args[2] = 0 end - if @args.first.is_a?(String) - @args[0] = ImageProxy.new(@args[0]) - end - if @args.first.is_a?(ImageProxy) - @image = @args[0] = @args[0].swt_image - end end end # Tolerates shape extra args added by user by mistake # (e.g. happens when switching from round rectangle to a standard one without removing all extra args) def tolerate_shape_extra_args! the_java_method_arg_count = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.select do |m| m.name == @method_name.camelcase(:lower) end.map(&:parameter_types).map(&:size).max - if @args.size > the_java_method_arg_count + if the_java_method_arg_count && @args.to_a.size > the_java_method_arg_count @args[the_java_method_arg_count..-1] = [] end end def amend_method_name_options_based_on_properties! return if @name == 'point' - if has_some_background? && !has_some_foreground? + if @name != 'text' && has_some_background? && !has_some_foreground? @options[:fill] = true elsif !has_some_background? && has_some_foreground? @options[:fill] = false elsif @name == 'rectangle' && has_some_background? && has_some_foreground? @options[:fill] = true @@ -235,31 +290,79 @@ if @name == 'rectangle' && @args.size > 4 && @args.last.is_a?(Numeric) @options[:round] = true end @method_name = self.class.method_name(@name, @options) end - + + # parameter names for arguments to pass to SWT GC.xyz method for rendering shape (e.g. draw_image(image, x, y) yields :image, :x, :y parameter names) + def parameter_names + [] + end + + def possible_parameter_names + parameter_names + end + + def parameter_name?(attribute_name) + possible_parameter_names.map(&:to_s).include?(ruby_attribute_getter(attribute_name)) + end + + def parameter_index(attribute_name) + parameter_names.map(&:to_s).index(attribute_name.to_s) + end + + def set_parameter_attribute(attribute_name, *args) + @args[parameter_index(ruby_attribute_getter(attribute_name))] = args.size == 1 ? args.first : args + end + def has_attribute?(attribute_name, *args) - self.class.gc_instance_methods.include?(attribute_setter(attribute_name)) + self.class.gc_instance_methods.include?(attribute_setter(attribute_name)) or + parameter_name?(attribute_name) end def set_attribute(attribute_name, *args) - @properties[attribute_name] = args + if parameter_name?(attribute_name) + set_parameter_attribute(attribute_name, *args) + else + @properties[attribute_name] = args + end if @content_added && !@parent.is_disposed @calculated_paint_args = false @parent.redraw end end def get_attribute(attribute_name) - @properties.symbolize_keys[attribute_name.to_s.to_sym] + if parameter_name?(attribute_name) + @args[parameter_index(attribute_name)] + else + @properties.symbolize_keys[attribute_name.to_s.to_sym] + end end + def method_missing(method_name, *args, &block) + if method_name.to_s.end_with?('=') + set_attribute(method_name, *args) + elsif has_attribute?(method_name) + get_attribute(method_name) + else + super + end + end + + def respond_to?(method_name, *args, &block) + if has_attribute?(method_name) + true + else + super + end + end + def pattern(*args, type: nil) instance_variable_name = "@#{type}_pattern" the_pattern = instance_variable_get(instance_variable_name) - if the_pattern.nil? + if the_pattern.nil? || the_pattern.is_disposed the_pattern = self.class.pattern(*args) end the_pattern end @@ -286,15 +389,19 @@ if property == 'transform' && args.first.is_a?(TransformProxy) args.first.swt_transform.dispose end end paint_event.gc.send(@method_name, *@args) + rescue => e + Glimmer::Config.logger.error {"Error encountered in painting shape: #{self.inspect}"} + Glimmer::Config.logger.error {e.full_message} end def calculate_paint_args! unless @calculated_paint_args - if @name == 'point' + if @name == 'pixel' + @name = 'point' # optimized performance calculation for pixel points if !@properties[:foreground].is_a?(Color) if @properties[:foreground].is_a?(Array) @properties[:foreground] = ColorProxy.new(@properties[:foreground], ensure_bounds: false) end @@ -332,5 +439,7 @@ end end end + +Dir[File.expand_path(File.join(__dir__, 'shape', '**', '*.rb'))].each {|shape_file| require(shape_file)}