lib/glimmer/swt/custom/shape.rb in glimmer-dsl-swt-4.18.7.2 vs lib/glimmer/swt/custom/shape.rb in glimmer-dsl-swt-4.18.7.3

- old
+ new

@@ -169,21 +169,33 @@ @options[:round] end # The bounding box top-left x, y, width, height in absolute positioning def bounds - org.eclipse.swt.graphics.Rectangle.new(absolute_x, absolute_y, calculated_width, calculated_height) + bounds_dependencies = [absolute_x, absolute_y, calculated_width, calculated_height] + if bounds_dependencies != @bounds_dependencies + # avoid repeating calculations + absolute_x, absolute_y, calculated_width, calculated_height = @bounds_dependencies = bounds_dependencies + @bounds = org.eclipse.swt.graphics.Rectangle.new(absolute_x, absolute_y, calculated_width, calculated_height) + end + @bounds end # The bounding box top-left x and y def location org.eclipse.swt.graphics.Point.new(bounds.x, bounds.y) end # The bounding box width and height (as a Point object with x being width and y being height) def size - org.eclipse.swt.graphics.Point.new(calculated_width, calculated_height) + size_dependencies = [calculated_width, calculated_height] + if size_dependencies != @size_dependencies + # avoid repeating calculations + calculated_width, calculated_height = @size_dependencies = size_dependencies + @size = org.eclipse.swt.graphics.Point.new(calculated_width, calculated_height) + end + @size end def extent @extent || size end @@ -306,14 +318,14 @@ arg = arg.swt_color if arg.is_a?(ColorProxy) args[i] = arg end @pattern_args ||= {} pattern_type = method_name.to_s.match(/set(.+)Pattern/)[1] - if args.first.is_a?(Pattern) + if args.first.is_a?(org.eclipse.swt.graphics.Pattern) new_args = @pattern_args[pattern_type] else - new_args = args.first.is_a?(Display) ? args : ([DisplayProxy.instance.swt_display] + args) + new_args = args.first.is_a?(org.eclipse.swt.widgets.Display) ? args : ([DisplayProxy.instance.swt_display] + args) @pattern_args[pattern_type] = new_args.dup end args[0] = pattern(*new_args, type: pattern_type) args[1..-1] = [] end @@ -427,10 +439,14 @@ def parameter_index(attribute_name) parameter_names.index(attribute_name.to_s.to_sym) end + def get_parameter_attribute(attribute_name) + @args[parameter_index(ruby_attribute_getter(attribute_name))] + 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) @@ -442,46 +458,58 @@ def set_attribute(attribute_name, *args) options = args.last if args.last.is_a?(Hash) args.pop if !options.nil? && !options[:redraw].nil? perform_redraw = @perform_redraw perform_redraw = options[:redraw] if perform_redraw.nil? && !options.nil? - perform_redraw = true if perform_redraw.nil? + perform_redraw ||= true + property_change = nil + ruby_attribute_getter_name = ruby_attribute_getter(attribute_name) + ruby_attribute_setter_name = ruby_attribute_setter(attribute_name) if parameter_name?(attribute_name) - set_parameter_attribute(attribute_name, *args) - elsif (respond_to?(attribute_name, super: true) and respond_to?(ruby_attribute_setter(attribute_name), super: true)) - self.send(ruby_attribute_setter(attribute_name), *args) + return if ruby_attribute_getter_name == (args.size == 1 ? args.first : args) + set_parameter_attribute(ruby_attribute_getter_name, *args) + elsif (respond_to?(attribute_name, super: true) and respond_to?(ruby_attribute_setter_name, super: true)) + return if self.send(ruby_attribute_getter_name) == (args.size == 1 ? args.first : args) + self.send(ruby_attribute_setter_name, *args) else - @properties[ruby_attribute_getter(attribute_name)] = args + # TODO consider this optimization of preconverting args (removing conversion from other methods) to reject equal args + args = apply_property_arg_conversions(ruby_attribute_getter_name, args) + return if @properties[ruby_attribute_getter_name] == args + @properties[ruby_attribute_getter_name] = args + property_change = true end if @content_added && perform_redraw && !drawable.is_disposed - @calculated_paint_args = false - if is_a?(PathSegment) - root_path&.calculated_path_args = @calculated_path_args = false - calculated_args_changed! - root_path&.calculated_args_changed! + redrawn = false + unless property_change + @calculated_paint_args = false + if is_a?(PathSegment) + root_path&.calculated_path_args = @calculated_path_args = false + calculated_args_changed! + root_path&.calculated_args_changed! + end + if location_parameter_names.map(&:to_s).include?(ruby_attribute_getter_name) + calculated_args_changed!(children: true) + redrawn = parent.calculated_args_changed_for_defaults! if parent.is_a?(Shape) + end + if ['width', 'height'].include?(ruby_attribute_getter_name) + redrawn = calculated_args_changed_for_defaults! + end end - attribute_name = ruby_attribute_getter(attribute_name) - if location_parameter_names.map(&:to_s).include?(attribute_name) - @calculated_args = nil - parent.calculated_args_changed_for_defaults! if parent.is_a?(Shape) - end - if ['width', 'height'].include?(attribute_name) - calculated_args_changed_for_defaults! - end # TODO consider redrawing an image proxy's gc in the future - drawable.redraw unless drawable.is_a?(ImageProxy) + # TODO consider ensuring only a single redraw happens for a hierarchy of nested shapes + drawable.redraw unless redrawn || drawable.is_a?(ImageProxy) end end def get_attribute(attribute_name) if parameter_name?(attribute_name) arg_index = parameter_index(attribute_name) @args[arg_index] if arg_index elsif (respond_to?(attribute_name, super: true) and respond_to?(ruby_attribute_setter(attribute_name), super: true)) self.send(attribute_name) else - @properties.symbolize_keys[attribute_name.to_s.to_sym] + @properties[attribute_name.to_s] end end # Sets data just like SWT widgets def set_data(key=nil, value) @@ -553,42 +581,80 @@ end @parent.shapes.delete(self) drawable.redraw if redraw && !drawable.is_a?(ImageProxy) end - # Indicate if this is a shape composite (meaning a shape bag that just contains nested shapes, but doesn't render anything of its own) - def shape_composite? + # Indicate if this is a container shape (meaning a shape bag that is just there to contain nested shapes, but doesn't render anything of its own) + def container? @name == 'shape' end + # Indicate if this is a composite shape (meaning a shape that contains nested shapes like a rectangle with ovals inside it) + def composite? + !shapes.empty? + end + # ordered from closest to farthest parent def parent_shapes - current_parent = parent - the_parent_shapes = [] - until current_parent.is_a?(Drawable) - the_parent_shapes << current_parent - current_parent = current_parent.parent + if @parent_shapes.nil? + if parent.is_a?(Drawable) + @parent_shapes = [] + else + @parent_shapes = parent.parent_shapes + [parent] + end end - the_parent_shapes + @parent_shapes end # ordered from closest to farthest parent + def parent_shape_containers + if @parent_shape_containers.nil? + if parent.is_a?(Drawable) + @parent_shape_containers = [] + elsif !parent.container? + @parent_shape_containers = parent.parent_shape_containers + else + @parent_shape_containers = parent.parent_shape_containers + [parent] + end + end + @parent_shape_containers + end + + # ordered from closest to farthest parent def parent_shape_composites - parent_shapes.select(&:shape_composite?) + if @parent_shape_composites.nil? + if parent.is_a?(Drawable) + @parent_shape_composites = [] + elsif !parent.container? + @parent_shape_composites = parent.parent_shape_composites + else + @parent_shape_composites = parent.parent_shape_composites + [parent] + end + end + @parent_shape_composites end - def all_parent_properties - # TODO consider providing a converted property version of this ready for consumption - @all_parent_properties ||= parent_shape_composites.reverse.reduce({}) do |all_properties, parent_shape| - parent_properties = parent_shape.properties - parent_properties.each do |property, args| - parent_properties[property] = apply_property_arg_conversions(property, args) + def convert_properties! + if @properties != @converted_properties + @properties.each do |property, args| + @properties[property] = apply_property_arg_conversions(property, args) end - all_properties.merge(parent_properties) + @converted_properties = @properties.dup end end + def converted_properties + convert_properties! + @properties + end + + def all_parent_properties + @all_parent_properties ||= parent_shape_containers.reverse.reduce({}) do |all_properties, parent_shape| + all_properties.merge(parent_shape.converted_properties) + end + end + def paint(paint_event) paint_children(paint_event) if default_width? || default_height? paint_self(paint_event) # re-paint children from scratch in the special case of pre-calculating parent width/height to re-center within new parent dimensions shapes.each(&:calculated_args_changed!) if default_width? || default_height? @@ -598,28 +664,25 @@ Glimmer::Config.logger.error {e.full_message} end def paint_self(paint_event) @painting = true - unless shape_composite? + unless container? calculate_paint_args! @original_gc_properties = {} # this stores GC properties before making calls to updates TODO avoid using in pixel graphics - @original_properties = @properties # this stores original shape attributes like background/foreground/font - @properties.merge(all_parent_properties).each do |property, args| + @properties.each do |property, args| method_name = attribute_setter(property) @original_gc_properties[method_name] = paint_event.gc.send(method_name.sub('set', 'get')) rescue nil paint_event.gc.send(method_name, *args) if property == 'transform' && args.first.is_a?(TransformProxy) args.first.swt_transform.dispose end end ensure_extent(paint_event) end - if !@calculated_args || parent_shape_absolute_location_changed? - @calculated_args = calculated_args - end - unless shape_composite? + @calculated_args ||= calculate_args! + unless container? # paint unless parent's calculated args are not calculated yet, meaning it is about to get painted and trigger a paint on this child anyways paint_event.gc.send(@method_name, *@calculated_args) unless (parent.is_a?(Shape) && !parent.calculated_args?) @original_gc_properties.each do |method_name, value| paint_event.gc.send(method_name, value) end @@ -661,107 +724,133 @@ else [] end end - def parent_shape_absolute_location_changed? - (parent.is_a?(Shape) && (parent.absolute_x != @parent_absolute_x || parent.absolute_y != @parent_absolute_y)) - end - def calculated_args_changed!(children: true) # TODO add a children: true option to enable setting to false to avoid recalculating children args @calculated_args = nil shapes.each(&:calculated_args_changed!) if children end + # Notifies object that calculated args changed for defaults. Returns true if redrawing and false otherwise. def calculated_args_changed_for_defaults! has_default_dimensions = default_width? || default_height? parent_calculated_args_changed_for_defaults = has_default_dimensions - @calculated_args = nil if default_x? || default_y? || has_default_dimensions + calculated_args_changed!(children: false) if default_x? || default_y? || has_default_dimensions if has_default_dimensions && parent.is_a?(Shape) parent.calculated_args_changed_for_defaults! elsif @content_added && !drawable.is_disposed # TODO consider optimizing in the future if needed by ensuring one redraw for all parents in the hierarchy at the end instead of doing one per parent that needs it - drawable.redraw if !@painting && !drawable.is_a?(ImageProxy) + if !@painting && !drawable.is_a?(ImageProxy) + drawable.redraw + return true + end end + false end def calculated_args? !!@calculated_args end # args translated to absolute coordinates - def calculated_args - return @args if !default_x? && !default_y? && !default_width? && !default_height? && !max_width? && !max_height? && parent.is_a?(Drawable) - # Note: Must set x and move_by because not all shapes have a real x and some must translate all their points with move_by - # TODO change that by setting a bounding box for all shapes with a calculated top-left x, y and - # a setter that does the moving inside them instead so that I could rely on absolute_x and absolute_y - # here to get the job done of calculating absolute args - @perform_redraw = false - original_x = nil - original_y = nil - original_width = nil - original_height = nil - if parent.is_a?(Shape) - @parent_absolute_x = parent.absolute_x - @parent_absolute_y = parent.absolute_y + def calculate_args! + # TODO add conditions for parent having default width/height too + return @args if parent.is_a?(Drawable) && !default_x? && !default_y? && !default_width? && !default_height? && !max_width? && !max_height? + calculated_args_dependencies = [ + x, + y, + parent.is_a?(Shape) && parent.absolute_x, + parent.is_a?(Shape) && parent.absolute_y, + default_width? && default_width, + default_width? && width_delta, + default_height? && default_height, + default_height? && height_delta, + max_width? && max_width, + max_width? && width_delta, + max_height? && max_height, + max_height? && height_delta, + default_x? && default_x, + default_x? && x_delta, + default_y? && default_y, + default_y? && y_delta, + ] + if calculated_args_dependencies != @calculated_args_dependencies + # avoid recalculating values again + x, y, parent_absolute_x, parent_absolute_y, default_width, default_width_delta, default_height, default_height_delta, max_width, max_width_delta, max_height, max_height_delta, default_x, default_x_delta, default_y, default_y_delta = @calculated_args_dependencies = calculated_args_dependencies + # Note: Must set x and move_by because not all shapes have a real x and some must translate all their points with move_by + # TODO change that by setting a bounding box for all shapes with a calculated top-left x, y and + # a setter that does the moving inside them instead so that I could rely on absolute_x and absolute_y + # here to get the job done of calculating absolute args + @perform_redraw = false + original_x = nil + original_y = nil + original_width = nil + original_height = nil + if parent.is_a?(Shape) + @parent_absolute_x = parent_absolute_x + @parent_absolute_y = parent_absolute_y + end + if default_width? + original_width = width + self.width = default_width + default_width_delta + end + if default_height? + original_height = height + self.height = default_height + default_height_delta + end + if max_width? + original_width = width + self.width = max_width + max_width_delta + end + if max_height? + original_height = height + self.height = max_height + max_height_delta + end + if default_x? + original_x = x + self.x = default_x + default_x_delta + end + if default_y? + original_y = y + self.y = default_y + default_y_delta + end + if parent.is_a?(Shape) + move_by(@parent_absolute_x, @parent_absolute_y) + @result_calculated_args = @args.clone + move_by(-1*@parent_absolute_x, -1*@parent_absolute_y) + else + @result_calculated_args = @args.clone + end + if original_x + self.x = original_x + end + if original_y + self.y = original_y + end + if original_width + self.width = original_width + end + if original_height + self.height = original_height + end + @perform_redraw = true end - if default_width? - original_width = width - self.width = default_width + width_delta - end - if default_height? - original_height = height - self.height = default_height + height_delta - end - if max_width? - original_width = width - self.width = max_width + width_delta - end - if max_height? - original_height = height - self.height = max_height + height_delta - end - if default_x? - original_x = x - self.x = default_x + self.x_delta - end - if default_y? - original_y = y - self.y = default_y + self.y_delta - end - if parent.is_a?(Shape) - move_by(@parent_absolute_x, @parent_absolute_y) - result_args = @args.clone - move_by(-1*@parent_absolute_x, -1*@parent_absolute_y) - else - result_args = @args.clone - end - if original_x - self.x = original_x - end - if original_y - self.y = original_y - end - if original_width - self.width = original_width - end - if original_height - self.height = original_height - end - @perform_redraw = true - result_args + @result_calculated_args end def default_x? - current_parameter_name?(:x) and - (x.nil? || x.to_s == 'default' || (x.is_a?(Array) && x.first.to_s == 'default')) + return false unless current_parameter_name?(:x) + x = self.x + x.nil? || x.to_s == 'default' || (x.is_a?(Array) && x.first.to_s == 'default') end def default_y? - current_parameter_name?(:y) and - (y.nil? || y.to_s == 'default' || (y.is_a?(Array) && y.first.to_s == 'default')) + return false unless current_parameter_name?(:y) + y = self.y + y.nil? || y.to_s == 'default' || (y.is_a?(Array) && y.first.to_s == 'default') end def default_width? return false unless current_parameter_name?(:width) width = self.width @@ -773,112 +862,161 @@ height = self.height (height.nil? || height == :default || height == 'default' || (height.is_a?(Array) && (height.first.to_s == :default || height.first.to_s == 'default'))) end def max_width? - current_parameter_name?(:width) and - (width.nil? || width.to_s == 'max' || (width.is_a?(Array) && width.first.to_s == 'max')) + return false unless current_parameter_name?(:width) + width = self.width + (width.nil? || width.to_s == 'max' || (width.is_a?(Array) && width.first.to_s == 'max')) end def max_height? - current_parameter_name?(:height) and - (height.nil? || height.to_s == 'max' || (height.is_a?(Array) && height.first.to_s == 'max')) + return false unless current_parameter_name?(:height) + height = self.height + (height.nil? || height.to_s == 'max' || (height.is_a?(Array) && height.first.to_s == 'max')) end def default_x - result = ((parent.size.x - size.x) / 2) - result += parent.bounds.x - parent.absolute_x if parent.is_a?(Shape) && parent.irregular? - result + default_x_dependencies = [parent.size.x, size.x, parent.is_a?(Shape) && parent.irregular? && parent.bounds.x, parent.is_a?(Shape) && parent.irregular? && parent.absolute_x] + if default_x_dependencies != @default_x_dependencies + @default_x_dependencies = default_x_dependencies + result = ((parent.size.x - size.x) / 2) + result += parent.bounds.x - parent.absolute_x if parent.is_a?(Shape) && parent.irregular? + @default_x = result + end + @default_x end def default_y - result = ((parent.size.y - size.y) / 2) - result += parent.bounds.y - parent.absolute_y if parent.is_a?(Shape) && parent.irregular? - result + default_y_dependencies = [parent.size.y, size.y, parent.is_a?(Shape) && parent.irregular? && parent.bounds.y, parent.is_a?(Shape) && parent.irregular? && parent.absolute_y] + if default_y_dependencies != @default_y_dependencies + result = ((parent.size.y - size.y) / 2) + result += parent.bounds.y - parent.absolute_y if parent.is_a?(Shape) && parent.irregular? + @default_y = result + end + @default_y end + # right-most x coordinate in this shape (adding up its width and location) + def x_end + x_end_dependencies = [calculated_width, default_x?, !default_x? && x] + if x_end_dependencies != @x_end_dependencies + # avoid recalculation of dependencies + calculated_width, is_default_x, x = @x_end_dependencies = x_end_dependencies + shape_width = calculated_width.to_f + shape_x = is_default_x ? 0 : x.to_f + @x_end = shape_x + shape_width + end + @x_end + end + + # right-most y coordinate in this shape (adding up its height and location) + def y_end + y_end_dependencies = [calculated_height, default_y?, !default_y? && y] + if y_end_dependencies != @y_end_dependencies + # avoid recalculation of dependencies + calculated_height, is_default_y, y = @y_end_dependencies = y_end_dependencies + shape_height = calculated_height.to_f + shape_y = is_default_y ? 0 : y.to_f + @y_end = shape_y + shape_height + end + @y_end + end + def default_width - # TODO consider caching - x_ends = shapes.map do |shape| - if shape.max_width? - 0 + default_width_dependencies = [shapes.empty? && max_width, shapes.size == 1 && shapes.first.max_width? && parent.size.x, shapes.size >= 1 && !shapes.first.max_width? && shapes.map {|s| s.max_width? ? 0 : s.x_end}] + if default_width_dependencies != @default_width_dependencies + # Do not repeat calculations + max_width, parent_size_x, x_ends = @default_width_dependencies = default_width_dependencies + @default_width = if shapes.empty? + max_width + elsif shapes.size == 1 && shapes.first.max_width? + parent_size_x else - shape_width = shape.calculated_width.to_f - shape_x = shape.default_x? ? 0 : shape.x.to_f - shape_x + shape_width + x_ends.max.to_f end end - if shapes.empty? - max_width - elsif shapes.size == 1 && shapes.first.max_width? - self.parent.size.x - else - x_ends.max.to_f - end + @default_width end def default_height - # TODO consider caching - y_ends = shapes.map do |shape| - if shape.max_height? - 0 + default_height_dependencies = [shapes.empty? && max_height, shapes.size == 1 && shapes.first.max_height? && parent.size.y, shapes.size >= 1 && !shapes.first.max_height? && shapes.map {|s| s.max_height? ? 0 : s.y_end}] + if default_height_dependencies != @default_height_dependencies + # Do not repeat calculations + max_height, parent_size_y, y_ends = @default_height_dependencies = default_height_dependencies + @default_height = if shapes.empty? + max_height + elsif shapes.size == 1 && shapes.first.max_height? + parent_size_y else - shape_height = shape.calculated_height.to_f - shape_y = shape.default_y? ? 0 : shape.y.to_f - shape_y + shape_height + y_ends.max.to_f end end - if shapes.empty? - max_height - elsif shapes.size == 1 && shapes.first.max_height? - self.parent.size.y - else - y_ends.max.to_f - end + @default_height end def max_width - # consider caching - parent.is_a?(Drawable) ? parent.size.x : parent.calculated_width + max_width_dependencies = [parent.is_a?(Drawable) && parent.size.x, !parent.is_a?(Drawable) && parent.calculated_width] + if max_width_dependencies != @max_width_dependencies + # do not repeat calculations + parent_size_x, parent_calculated_width = @max_width_dependencies = max_width_dependencies + @max_width = parent.is_a?(Drawable) ? parent_size_x : parent_calculated_width + end + @max_width end def max_height - # consider caching - parent.is_a?(Drawable) ? parent.size.y : parent.calculated_height + max_height_dependencies = [parent.is_a?(Drawable) && parent.size.y, !parent.is_a?(Drawable) && parent.calculated_height] + if max_height_dependencies != @max_height_dependencies + # do not repeat calculations + parent_size_y, parent_calculated_height = @max_height_dependencies = max_height_dependencies + @max_height = parent.is_a?(Drawable) ? parent_size_y : parent_calculated_height + end + @max_height end def calculated_width - result_width = width - result_width = (default_width + width_delta) if default_width? - result_width = (max_width + width_delta) if max_width? - result_width + calculated_width_dependencies = [width, default_width? && (default_width + width_delta), max_width? && (max_width + width_delta)] + if calculated_width_dependencies != @calculated_width_dependencies + @calculated_width_dependencies = calculated_width_dependencies + result_width = width + result_width = (default_width + width_delta) if default_width? + result_width = (max_width + width_delta) if max_width? + @calculated_width = result_width + end + @calculated_width end def calculated_height - result_height = height - result_height = (default_height + height_delta) if default_height? - result_height = (max_height + height_delta) if max_height? - result_height + calculated_height_dependencies = [height, default_height? && (default_height + height_delta), max_height? && (max_height + height_delta)] + if calculated_height_dependencies != @calculated_height_dependencies + @calculated_height_dependencies = calculated_height_dependencies + result_height = height + result_height = (default_height + height_delta) if default_height? + result_height = (max_height + height_delta) if max_height? + @calculated_height = result_height + end + @calculated_height end def x_delta - return 0 unless default_x? && x.is_a?(Array) + return 0 unless x.is_a?(Array) && default_x? x[1].to_f end def y_delta - return 0 unless default_y? && y.is_a?(Array) + return 0 unless y.is_a?(Array) && default_y? y[1].to_f end def width_delta - return 0 unless (default_width? || max_width?) && width.is_a?(Array) + return 0 unless width.is_a?(Array) && (default_width? || max_width?) width[1].to_f end def height_delta - return 0 unless (default_height? || max_height?) && height.is_a?(Array) + return 0 unless height.is_a?(Array) && (default_height? || max_height?) height[1].to_f end def x_delta=(delta) return unless default_x? @@ -903,43 +1041,72 @@ symbol = height.is_a?(Array) ? height.first : height self.height = [symbol, delta] end def calculated_x - result = default_x? ? default_x : self.x - result += self.x_delta - result + calculated_x_dependencies = [default_x? && default_x, !default_x? && self.x, self.x_delta] + if calculated_x_dependencies != @calculated_x_dependencies + default_x, x, x_delta = @calculated_x_dependencies = calculated_x_dependencies + result = default_x? ? default_x : x + result += x_delta + @calculated_x = result + end + @calculated_x end def calculated_y - result = default_y? ? default_y : self.y - result += self.y_delta - result + calculated_y_dependencies = [default_y? && default_y, !default_y? && self.y, self.y_delta] + if calculated_y_dependencies != @calculated_y_dependencies + default_y, y, y_delta = @calculated_y_dependencies = calculated_y_dependencies + result = default_y? ? default_y : y + result += y_delta + @calculated_y = result + end + @calculated_y end def absolute_x - x = calculated_x - if parent.is_a?(Shape) - parent.absolute_x + x - else - x + absolute_x_dependencies = [calculated_x, parent.is_a?(Shape) && parent.absolute_x] + if absolute_x_dependencies != @absolute_x_dependencies + # do not repeat calculations + calculated_x, parent_absolute_x = @absolute_x_dependencies = absolute_x_dependencies + x = calculated_x + @absolute_x = if parent.is_a?(Shape) + parent_absolute_x + x + else + x + end end + @absolute_x end def absolute_y - y = calculated_y - if parent.is_a?(Shape) - parent.absolute_y + y - else - y + absolute_y_dependencies = [calculated_y, parent.is_a?(Shape) && parent.absolute_y] + if absolute_y_dependencies != @absolute_y_dependencies + calculated_y, parent_absolute_y = @absolute_y_dependencies = absolute_y_dependencies + y = calculated_y + @absolute_y = if parent.is_a?(Shape) + parent_absolute_y + y + else + y + end end + @absolute_y end - # Overriding inspect to avoid printing very long shape hierarchies - def inspect - "#<#{self.class.name}:0x#{self.hash.to_s(16)} args=#{@args.inspect}, properties=#{@properties.inspect}}>" + # Overriding inspect to avoid printing very long nested shape hierarchies (recurses onces only) + def inspect(recursive: 1, calculated: false, args: true, properties: true, calculated_args: false) + recurse = recursive == true || recursive.is_a?(Integer) && recursive.to_i > 0 + recursive = [recursive -= 1, 0].max if recursive.is_a?(Integer) + args_string = " args=#{@args.inspect}" if args + properties_string = " properties=#{@properties.inspect}}" if properties + calculated_args_string = " calculated_args=#{@calculated_args.inspect}" if calculated_args + calculated_string = " absolute_x=#{absolute_x} absolute_y=#{absolute_y} calculated_width=#{calculated_width} calculated_height=#{calculated_height}" if calculated + recursive_string = " shapes=#{@shapes.map {|s| s.inspect(recursive: recursive, calculated: calculated, args: args, properties: properties)}}" if recurse + "#<#{self.class.name}:0x#{self.hash.to_s(16)}#{args_string}#{properties_string}#{calculated_args_string}#{calculated_string}#{recursive_string}>" rescue => e + Glimmer::Config.logger.error { e.full_message } "#<#{self.class.name}:0x#{self.hash.to_s(16)}" end def calculate_paint_args! unless @calculated_paint_args @@ -956,20 +1123,17 @@ if @properties[:foreground].is_a?(ColorProxy) @properties[:foreground] = @properties[:foreground].swt_color end end else + @properties = all_parent_properties.merge(@properties) @properties['background'] = [@drawable.background] if fill? && !has_some_background? @properties['foreground'] = [@drawable.foreground] if @drawable.respond_to?(:foreground) && draw? && !has_some_foreground? # TODO regarding alpha, make sure to reset it to parent stored alpha once we allow setting shape properties on parents directly without shapes - @properties['alpha'] ||= [255] - @properties['font'] = [@drawable.font] if @drawable.respond_to?(:font) && draw? && !@properties.keys.map(&:to_s).include?('font') + @properties['font'] = [@drawable.font] if @drawable.respond_to?(:font) && @name == 'text' && draw? && !@properties.keys.map(&:to_s).include?('font') # TODO regarding transform, make sure to reset it to parent stored transform once we allow setting shape properties on parents directly without shapes # Also do that with all future-added properties - @properties['transform'] = [nil] if @drawable.respond_to?(:transform) && !@properties.keys.map(&:to_s).include?('transform') - @properties.each do |property, args| - @properties[property] = apply_property_arg_conversions(property, args) - end + convert_properties! apply_shape_arg_conversions! apply_shape_arg_defaults! tolerate_shape_extra_args! @calculated_paint_args = true end