glimmer-dsl-swt 4.18.5.0 → 4.18.5.5
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 +43 -0
- data/README.md +16 -11
- data/VERSION +1 -1
- data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +179 -33
- data/docs/reference/GLIMMER_SAMPLES.md +58 -0
- data/glimmer-dsl-swt.gemspec +7 -7
- data/lib/glimmer/data_binding/widget_binding.rb +4 -1
- data/lib/glimmer/dsl/swt/dialog_expression.rb +18 -9
- data/lib/glimmer/dsl/swt/dsl.rb +1 -0
- data/lib/glimmer/dsl/swt/font_expression.rb +1 -1
- data/lib/glimmer/dsl/swt/shape_expression.rb +1 -1
- data/lib/glimmer/dsl/swt/shell_expression.rb +1 -1
- data/lib/glimmer/swt/custom/drawable.rb +10 -2
- data/lib/glimmer/swt/custom/shape.rb +458 -58
- data/lib/glimmer/swt/custom/shape/arc.rb +35 -0
- data/lib/glimmer/swt/custom/shape/focus.rb +2 -2
- data/lib/glimmer/swt/custom/shape/image.rb +35 -9
- data/lib/glimmer/swt/custom/shape/line.rb +88 -4
- data/lib/glimmer/swt/custom/shape/oval.rb +18 -0
- data/lib/glimmer/swt/custom/shape/point.rb +10 -5
- data/lib/glimmer/swt/custom/shape/polygon.rb +105 -15
- data/lib/glimmer/swt/custom/shape/polyline.rb +88 -15
- data/lib/glimmer/swt/custom/shape/rectangle.rb +19 -0
- data/lib/glimmer/swt/custom/shape/text.rb +13 -3
- data/lib/glimmer/swt/{directory_dialog_proxy.rb → dialog_proxy.rb} +36 -7
- data/lib/glimmer/swt/font_proxy.rb +12 -6
- data/lib/glimmer/swt/message_box_proxy.rb +1 -0
- data/lib/glimmer/swt/properties.rb +3 -0
- data/lib/glimmer/swt/proxy_properties.rb +145 -0
- data/lib/glimmer/swt/transform_proxy.rb +39 -35
- data/lib/glimmer/swt/widget_proxy.rb +32 -60
- data/samples/elaborate/contact_manager.rb +2 -0
- data/samples/elaborate/login.rb +2 -0
- data/samples/elaborate/mandelbrot_fractal.rb +1 -0
- data/samples/elaborate/meta_sample.rb +1 -0
- data/samples/elaborate/tetris.rb +2 -1
- data/samples/elaborate/tic_tac_toe.rb +2 -0
- data/samples/elaborate/user_profile.rb +10 -8
- data/samples/hello/hello_browser.rb +2 -0
- data/samples/hello/hello_button.rb +2 -0
- data/samples/hello/hello_canvas.rb +157 -77
- data/samples/hello/hello_canvas_animation.rb +2 -0
- data/samples/hello/hello_canvas_transform.rb +2 -0
- data/samples/hello/hello_checkbox.rb +2 -0
- data/samples/hello/hello_checkbox_group.rb +2 -0
- data/samples/hello/hello_code_text.rb +2 -0
- data/{lib/glimmer/dsl/swt/directory_dialog_expression.rb → samples/hello/hello_color_dialog.rb} +44 -24
- data/samples/hello/hello_combo.rb +2 -0
- data/samples/hello/hello_computed.rb +2 -0
- data/samples/hello/hello_cursor.rb +2 -0
- data/samples/hello/hello_custom_shell.rb +1 -0
- data/samples/hello/hello_custom_widget.rb +2 -0
- data/samples/hello/hello_date_time.rb +2 -0
- data/samples/hello/hello_dialog.rb +2 -0
- data/samples/hello/hello_directory_dialog.rb +2 -0
- data/samples/hello/hello_drag_and_drop.rb +5 -3
- data/samples/hello/hello_expand_bar.rb +2 -0
- data/samples/hello/hello_file_dialog.rb +2 -0
- data/samples/hello/hello_font_dialog.rb +84 -0
- data/samples/hello/hello_group.rb +2 -0
- data/samples/hello/hello_link.rb +2 -0
- data/samples/hello/hello_list_multi_selection.rb +2 -0
- data/samples/hello/hello_list_single_selection.rb +2 -0
- data/samples/hello/hello_menu_bar.rb +2 -0
- data/samples/hello/hello_message_box.rb +2 -0
- data/samples/hello/hello_pop_up_context_menu.rb +2 -0
- data/samples/hello/hello_progress_bar.rb +2 -0
- data/samples/hello/hello_radio.rb +2 -0
- data/samples/hello/hello_radio_group.rb +2 -0
- data/samples/hello/hello_sash_form.rb +2 -0
- data/samples/hello/hello_spinner.rb +2 -0
- data/samples/hello/hello_styled_text.rb +19 -17
- data/samples/hello/hello_tab.rb +2 -0
- data/samples/hello/hello_table.rb +2 -0
- data/samples/hello/hello_world.rb +2 -0
- metadata +6 -6
- data/lib/glimmer/dsl/swt/file_dialog_expression.rb +0 -48
- data/lib/glimmer/swt/file_dialog_proxy.rb +0 -68
| @@ -36,6 +36,8 @@ | |
| 36 36 | 
             
                - [Hello, Cursor!](#hello-cursor)
         | 
| 37 37 | 
             
                - [Hello, Progress Bar!](#hello-progress-bar)
         | 
| 38 38 | 
             
                - [Hello, Tree!](#hello-tree)
         | 
| 39 | 
            +
                - [Hello, Color Dialog!](#hello-color-dialog)
         | 
| 40 | 
            +
                - [Hello, Font Dialog!](#hello-font-dialog)
         | 
| 39 41 | 
             
              - [Elaborate Samples](#elaborate-samples)
         | 
| 40 42 | 
             
                - [User Profile](#user-profile)
         | 
| 41 43 | 
             
                - [Login](#login)
         | 
| @@ -562,6 +564,30 @@ Hello, Canvas! | |
| 562 564 |  | 
| 563 565 | 
             
            
         | 
| 564 566 |  | 
| 567 | 
            +
            Hello, Canvas! Moving Shapes and Nested Shapes via Drag'n'Drop
         | 
| 568 | 
            +
             | 
| 569 | 
            +
            
         | 
| 570 | 
            +
             | 
| 571 | 
            +
            Hello, Canvas! with Moved Shapes (via Drag'n'Drop)
         | 
| 572 | 
            +
             | 
| 573 | 
            +
            
         | 
| 574 | 
            +
             | 
| 575 | 
            +
            Hello, Canvas! Menu (for background/foreground color changes)
         | 
| 576 | 
            +
             | 
| 577 | 
            +
            
         | 
| 578 | 
            +
             | 
| 579 | 
            +
            Hello, Canvas! Color Dialog
         | 
| 580 | 
            +
             | 
| 581 | 
            +
            
         | 
| 582 | 
            +
             | 
| 583 | 
            +
            Hello, Canvas! Colors Changed
         | 
| 584 | 
            +
             | 
| 585 | 
            +
            
         | 
| 586 | 
            +
             | 
| 587 | 
            +
            Hello, Canvas! Data-Binding (changing a `text` shape `string` via data-binding changes from another thread)
         | 
| 588 | 
            +
             | 
| 589 | 
            +
            
         | 
| 590 | 
            +
             | 
| 565 591 | 
             
            #### Hello, Canvas Animation!
         | 
| 566 592 |  | 
| 567 593 | 
             
            This sample demonstrates the use of the `canvas` widget and [Animation DSL](#canvas-animation-dsl) in Glimmer.
         | 
| @@ -630,6 +656,38 @@ Hello, Tree! | |
| 630 656 |  | 
| 631 657 | 
             
            
         | 
| 632 658 |  | 
| 659 | 
            +
            #### Hello, Color Dialog!
         | 
| 660 | 
            +
             | 
| 661 | 
            +
            This sample demonstrates the use of the `color_dialog` keyword.
         | 
| 662 | 
            +
             | 
| 663 | 
            +
            Code:
         | 
| 664 | 
            +
             | 
| 665 | 
            +
            [samples/hello/hello_color_dialog.rb](/samples/hello/hello_color_dialog.rb)
         | 
| 666 | 
            +
             | 
| 667 | 
            +
            Hello, Color Dialog!
         | 
| 668 | 
            +
             | 
| 669 | 
            +
            
         | 
| 670 | 
            +
             | 
| 671 | 
            +
            
         | 
| 672 | 
            +
             | 
| 673 | 
            +
            
         | 
| 674 | 
            +
             | 
| 675 | 
            +
            #### Hello, Font Dialog!
         | 
| 676 | 
            +
             | 
| 677 | 
            +
            This sample demonstrates the use of the `font_dialog` keyword.
         | 
| 678 | 
            +
             | 
| 679 | 
            +
            Code:
         | 
| 680 | 
            +
             | 
| 681 | 
            +
            [samples/hello/hello_font_dialog.rb](/samples/hello/hello_font_dialog.rb)
         | 
| 682 | 
            +
             | 
| 683 | 
            +
            Hello, Font Dialog!
         | 
| 684 | 
            +
             | 
| 685 | 
            +
            
         | 
| 686 | 
            +
             | 
| 687 | 
            +
            
         | 
| 688 | 
            +
             | 
| 689 | 
            +
            
         | 
| 690 | 
            +
             | 
| 633 691 | 
             
            ### Elaborate Samples
         | 
| 634 692 |  | 
| 635 693 | 
             
            For more elaborate samples, check the following:
         | 
    
        data/glimmer-dsl-swt.gemspec
    CHANGED
    
    | @@ -2,16 +2,16 @@ | |
| 2 2 | 
             
            # DO NOT EDIT THIS FILE DIRECTLY
         | 
| 3 3 | 
             
            # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
         | 
| 4 4 | 
             
            # -*- encoding: utf-8 -*-
         | 
| 5 | 
            -
            # stub: glimmer-dsl-swt 4.18.5. | 
| 5 | 
            +
            # stub: glimmer-dsl-swt 4.18.5.5 ruby lib
         | 
| 6 6 |  | 
| 7 7 | 
             
            Gem::Specification.new do |s|
         | 
| 8 8 | 
             
              s.name = "glimmer-dsl-swt".freeze
         | 
| 9 | 
            -
              s.version = "4.18.5. | 
| 9 | 
            +
              s.version = "4.18.5.5"
         | 
| 10 10 |  | 
| 11 11 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
         | 
| 12 12 | 
             
              s.require_paths = ["lib".freeze]
         | 
| 13 13 | 
             
              s.authors = ["AndyMaleh".freeze]
         | 
| 14 | 
            -
              s.date = "2021-02- | 
| 14 | 
            +
              s.date = "2021-02-27"
         | 
| 15 15 | 
             
              s.description = "Glimmer DSL for SWT (JRuby Desktop Development GUI Framework) is a native-GUI cross-platform desktop development library written in JRuby, an OS-threaded faster JVM version of Ruby. Glimmer's main innovation is a declarative Ruby DSL that enables productive and efficient authoring of desktop application user-interfaces by relying on the robust Eclipse SWT library. Glimmer additionally innovates by having built-in data-binding support, which greatly facilitates synchronizing the GUI with domain models, thus achieving true decoupling of object oriented components and enabling developers to solve business problems (test-first) without worrying about GUI concerns, or alternatively drive development GUI-first, and then write clean business models (test-first) afterwards. Not only does Glimmer provide a large set of GUI widgets, but it also supports drawing Canvas Graphics like Shapes and Animations. To get started quickly, Glimmer offers scaffolding options for Apps, Gems, and Custom Widgets. Glimmer also includes native-executable packaging support, sorely lacking in other libraries, thus enabling the delivery of desktop apps written in Ruby as truly native DMG/PKG/APP files on the Mac + App Store, MSI/EXE files on Windows, and Gem Packaged Shell Scripts on Linux.".freeze
         | 
| 16 16 | 
             
              s.email = "andy.am@gmail.com".freeze
         | 
| 17 17 | 
             
              s.executables = ["glimmer".freeze, "girb".freeze]
         | 
| @@ -69,13 +69,11 @@ Gem::Specification.new do |s| | |
| 69 69 | 
             
                "lib/glimmer/dsl/swt/custom_widget_expression.rb",
         | 
| 70 70 | 
             
                "lib/glimmer/dsl/swt/data_binding_expression.rb",
         | 
| 71 71 | 
             
                "lib/glimmer/dsl/swt/dialog_expression.rb",
         | 
| 72 | 
            -
                "lib/glimmer/dsl/swt/directory_dialog_expression.rb",
         | 
| 73 72 | 
             
                "lib/glimmer/dsl/swt/display_expression.rb",
         | 
| 74 73 | 
             
                "lib/glimmer/dsl/swt/dnd_expression.rb",
         | 
| 75 74 | 
             
                "lib/glimmer/dsl/swt/dsl.rb",
         | 
| 76 75 | 
             
                "lib/glimmer/dsl/swt/exec_expression.rb",
         | 
| 77 76 | 
             
                "lib/glimmer/dsl/swt/expand_item_expression.rb",
         | 
| 78 | 
            -
                "lib/glimmer/dsl/swt/file_dialog_expression.rb",
         | 
| 79 77 | 
             
                "lib/glimmer/dsl/swt/font_expression.rb",
         | 
| 80 78 | 
             
                "lib/glimmer/dsl/swt/image_expression.rb",
         | 
| 81 79 | 
             
                "lib/glimmer/dsl/swt/layout_data_expression.rb",
         | 
| @@ -127,11 +125,10 @@ Gem::Specification.new do |s| | |
| 127 125 | 
             
                "lib/glimmer/swt/custom/shape/rectangle.rb",
         | 
| 128 126 | 
             
                "lib/glimmer/swt/custom/shape/text.rb",
         | 
| 129 127 | 
             
                "lib/glimmer/swt/date_time_proxy.rb",
         | 
| 130 | 
            -
                "lib/glimmer/swt/ | 
| 128 | 
            +
                "lib/glimmer/swt/dialog_proxy.rb",
         | 
| 131 129 | 
             
                "lib/glimmer/swt/display_proxy.rb",
         | 
| 132 130 | 
             
                "lib/glimmer/swt/dnd_proxy.rb",
         | 
| 133 131 | 
             
                "lib/glimmer/swt/expand_item_proxy.rb",
         | 
| 134 | 
            -
                "lib/glimmer/swt/file_dialog_proxy.rb",
         | 
| 135 132 | 
             
                "lib/glimmer/swt/font_proxy.rb",
         | 
| 136 133 | 
             
                "lib/glimmer/swt/image_proxy.rb",
         | 
| 137 134 | 
             
                "lib/glimmer/swt/layout_data_proxy.rb",
         | 
| @@ -140,6 +137,7 @@ Gem::Specification.new do |s| | |
| 140 137 | 
             
                "lib/glimmer/swt/message_box_proxy.rb",
         | 
| 141 138 | 
             
                "lib/glimmer/swt/packages.rb",
         | 
| 142 139 | 
             
                "lib/glimmer/swt/properties.rb",
         | 
| 140 | 
            +
                "lib/glimmer/swt/proxy_properties.rb",
         | 
| 143 141 | 
             
                "lib/glimmer/swt/sash_form_proxy.rb",
         | 
| 144 142 | 
             
                "lib/glimmer/swt/scrolled_composite_proxy.rb",
         | 
| 145 143 | 
             
                "lib/glimmer/swt/shell_proxy.rb",
         | 
| @@ -186,6 +184,7 @@ Gem::Specification.new do |s| | |
| 186 184 | 
             
                "samples/hello/hello_checkbox.rb",
         | 
| 187 185 | 
             
                "samples/hello/hello_checkbox_group.rb",
         | 
| 188 186 | 
             
                "samples/hello/hello_code_text.rb",
         | 
| 187 | 
            +
                "samples/hello/hello_color_dialog.rb",
         | 
| 189 188 | 
             
                "samples/hello/hello_combo.rb",
         | 
| 190 189 | 
             
                "samples/hello/hello_computed.rb",
         | 
| 191 190 | 
             
                "samples/hello/hello_computed/contact.rb",
         | 
| @@ -198,6 +197,7 @@ Gem::Specification.new do |s| | |
| 198 197 | 
             
                "samples/hello/hello_drag_and_drop.rb",
         | 
| 199 198 | 
             
                "samples/hello/hello_expand_bar.rb",
         | 
| 200 199 | 
             
                "samples/hello/hello_file_dialog.rb",
         | 
| 200 | 
            +
                "samples/hello/hello_font_dialog.rb",
         | 
| 201 201 | 
             
                "samples/hello/hello_group.rb",
         | 
| 202 202 | 
             
                "samples/hello/hello_link.rb",
         | 
| 203 203 | 
             
                "samples/hello/hello_list_multi_selection.rb",
         | 
| @@ -52,7 +52,10 @@ module Glimmer | |
| 52 52 | 
             
                        unregister_all_observables
         | 
| 53 53 | 
             
                        return
         | 
| 54 54 | 
             
                      end
         | 
| 55 | 
            -
                       | 
| 55 | 
            +
                      # need the rescue false for a scenario with tree items not being equal to model objects raising an exception
         | 
| 56 | 
            +
                      unless ((value == evaluate_property) rescue false) # need the rescue false for a scenario with tree items not being equal to model objects raising an exception
         | 
| 57 | 
            +
                        @widget.set_attribute(@property, value)
         | 
| 58 | 
            +
                      end
         | 
| 56 59 | 
             
                    end
         | 
| 57 60 | 
             
                  end
         | 
| 58 61 |  | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            # Copyright (c) 2007-2021 Andy Maleh
         | 
| 2 | 
            -
            # | 
| 2 | 
            +
            #
         | 
| 3 3 | 
             
            # Permission is hereby granted, free of charge, to any person obtaining
         | 
| 4 4 | 
             
            # a copy of this software and associated documentation files (the
         | 
| 5 5 | 
             
            # "Software"), to deal in the Software without restriction, including
         | 
| @@ -7,10 +7,10 @@ | |
| 7 7 | 
             
            # distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 8 8 | 
             
            # permit persons to whom the Software is furnished to do so, subject to
         | 
| 9 9 | 
             
            # the following conditions:
         | 
| 10 | 
            -
            # | 
| 10 | 
            +
            #
         | 
| 11 11 | 
             
            # The above copyright notice and this permission notice shall be
         | 
| 12 12 | 
             
            # included in all copies or substantial portions of the Software.
         | 
| 13 | 
            -
            # | 
| 13 | 
            +
            #
         | 
| 14 14 | 
             
            # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 15 15 | 
             
            # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 16 16 | 
             
            # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| @@ -23,24 +23,33 @@ require 'glimmer/dsl/static_expression' | |
| 23 23 | 
             
            require 'glimmer/dsl/parent_expression'
         | 
| 24 24 | 
             
            require 'glimmer/dsl/top_level_expression'
         | 
| 25 25 | 
             
            require 'glimmer/swt/shell_proxy'
         | 
| 26 | 
            +
            require 'glimmer/swt/dialog_proxy'
         | 
| 26 27 |  | 
| 27 28 | 
             
            module Glimmer
         | 
| 28 29 | 
             
              module DSL
         | 
| 29 30 | 
             
                module SWT
         | 
| 30 | 
            -
                  class DialogExpression <  | 
| 31 | 
            +
                  class DialogExpression < Expression
         | 
| 31 32 | 
             
                    include TopLevelExpression
         | 
| 32 33 | 
             
                    include ParentExpression
         | 
| 33 34 |  | 
| 34 35 | 
             
                    def can_interpret?(parent, keyword, *args, &block)
         | 
| 35 | 
            -
                       | 
| 36 | 
            -
                        ( | 
| 36 | 
            +
                      (
         | 
| 37 | 
            +
                        (keyword == 'dialog') or
         | 
| 38 | 
            +
                        (keyword.to_s.end_with?('dialog') and Glimmer::SWT::DialogProxy.dialog_class(keyword))
         | 
| 39 | 
            +
                      ) and
         | 
| 40 | 
            +
                        (parent.nil? or parent.is_a?(org.eclipse.swt.widgets.Shell) or parent.is_a?(Glimmer::SWT::ShellProxy))
         | 
| 37 41 | 
             
                    end
         | 
| 38 42 |  | 
| 39 43 | 
             
                    def interpret(parent, keyword, *args, &block)
         | 
| 40 44 | 
             
                      # TODO reconcile this with the actual org.eclipse.swt.widgets.Dialog widget (maybe rename this as dialog_shell)
         | 
| 41 | 
            -
                       | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 45 | 
            +
                      if keyword == 'dialog'
         | 
| 46 | 
            +
                        args = [parent] + args unless parent.nil?
         | 
| 47 | 
            +
                        args += [:dialog_trim, :application_modal]
         | 
| 48 | 
            +
                        Glimmer::SWT::ShellProxy.new(*args)
         | 
| 49 | 
            +
                      else
         | 
| 50 | 
            +
                        args = [parent] + args unless parent.nil?
         | 
| 51 | 
            +
                        Glimmer::SWT::DialogProxy.new(keyword, *args)
         | 
| 52 | 
            +
                      end
         | 
| 44 53 | 
             
                    end
         | 
| 45 54 | 
             
                  end
         | 
| 46 55 | 
             
                end
         | 
    
        data/lib/glimmer/dsl/swt/dsl.rb
    CHANGED
    
    
| @@ -37,7 +37,7 @@ module Glimmer | |
| 37 37 | 
             
                        (parent.nil? || !parent.respond_to?('font')) and
         | 
| 38 38 | 
             
                        !parent.is_a?(Glimmer::SWT::Custom::Shape) and
         | 
| 39 39 | 
             
                        args.size == 1 and
         | 
| 40 | 
            -
                        args.first.is_a?(Hash)
         | 
| 40 | 
            +
                        (args.first.is_a?(Hash) || args.first.is_a?(org.eclipse.swt.graphics.FontData))
         | 
| 41 41 | 
             
                    end
         | 
| 42 42 |  | 
| 43 43 | 
             
                    def interpret(parent, keyword, *args, &block)
         | 
| @@ -32,7 +32,7 @@ module Glimmer | |
| 32 32 | 
             
                    include ParentExpression
         | 
| 33 33 |  | 
| 34 34 | 
             
                    def can_interpret?(parent, keyword, *args, &block)
         | 
| 35 | 
            -
                      parent.is_a?(Glimmer::SWT::Custom::Drawable) and
         | 
| 35 | 
            +
                      (parent.is_a?(Glimmer::SWT::Custom::Drawable) or parent.is_a?(Glimmer::SWT::Custom::Shape)) and
         | 
| 36 36 | 
             
                        Glimmer::SWT::Custom::Shape.valid?(parent, keyword, *args, &block)
         | 
| 37 37 | 
             
                    end
         | 
| 38 38 |  | 
| @@ -34,12 +34,20 @@ module Glimmer | |
| 34 34 | 
             
                      @shapes ||= []
         | 
| 35 35 | 
             
                    end
         | 
| 36 36 |  | 
| 37 | 
            +
                    def expanded_shapes
         | 
| 38 | 
            +
                      @shapes.map do |shape|
         | 
| 39 | 
            +
                        [shape] + shape.expanded_shapes
         | 
| 40 | 
            +
                      end.flatten
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
                    
         | 
| 37 43 | 
             
                    def image_buffered_shapes
         | 
| 38 44 | 
             
                      @image_buffered_shapes ||= []
         | 
| 39 45 | 
             
                    end
         | 
| 40 46 |  | 
| 47 | 
            +
                    # TODO add a method like shapes that specifies drawable_properties to be able to adjust properties like transform in between shapes
         | 
| 48 | 
            +
                      
         | 
| 41 49 | 
             
                    def shape_at_location(x, y)
         | 
| 42 | 
            -
                       | 
| 50 | 
            +
                      expanded_shapes.reverse.detect {|shape| shape.include?(x, y)}
         | 
| 43 51 | 
             
                    end
         | 
| 44 52 |  | 
| 45 53 | 
             
                    def add_shape(shape)
         | 
| @@ -132,7 +140,7 @@ module Glimmer | |
| 132 140 | 
             
                          @paint_listener_proxy = on_swt_paint(&shape_painter)
         | 
| 133 141 | 
             
                        end
         | 
| 134 142 | 
             
                      else
         | 
| 135 | 
            -
                        redraw if @finished_add_content && !is_disposed
         | 
| 143 | 
            +
                        redraw if respond_to?(:redraw) && @finished_add_content && !is_disposed
         | 
| 136 144 | 
             
                      end
         | 
| 137 145 | 
             
                    end
         | 
| 138 146 | 
             
                    alias resetup_shape_painting setup_shape_painting
         | 
| @@ -26,6 +26,34 @@ require 'glimmer/swt/color_proxy' | |
| 26 26 | 
             
            require 'glimmer/swt/font_proxy'
         | 
| 27 27 | 
             
            require 'glimmer/swt/transform_proxy'
         | 
| 28 28 |  | 
| 29 | 
            +
            class Java::OrgEclipseSwtGraphics::GC
         | 
| 30 | 
            +
              def setLineDashOffset(value)
         | 
| 31 | 
            +
                lineMiterLimit = getLineAttributes&.miterLimit || 999_999
         | 
| 32 | 
            +
                setLineAttributes(Java::OrgEclipseSwtGraphics::LineAttributes.new(getLineWidth, getLineCap, getLineJoin, getLineStyle, getLineDash.map(&:to_f).to_java(:float), value, lineMiterLimit))
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
              alias set_line_dash_offset setLineDashOffset
         | 
| 35 | 
            +
              alias line_dash_offset= setLineDashOffset
         | 
| 36 | 
            +
              
         | 
| 37 | 
            +
              def getLineDashOffset
         | 
| 38 | 
            +
                getLineAttributes&.dashOffset
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
              alias get_line_dash_offset getLineDashOffset
         | 
| 41 | 
            +
              alias line_dash_offset getLineDashOffset
         | 
| 42 | 
            +
              
         | 
| 43 | 
            +
              def setLineMiterLimit(value)
         | 
| 44 | 
            +
                lineDashOffset = getLineAttributes&.dashOffset || 0
         | 
| 45 | 
            +
                setLineAttributes(Java::OrgEclipseSwtGraphics::LineAttributes.new(getLineWidth, getLineCap, getLineJoin, getLineStyle, getLineDash.map(&:to_f).to_java(:float), lineDashOffset, value))
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
              alias set_line_miter_limit setLineMiterLimit
         | 
| 48 | 
            +
              alias line_miter_limit= setLineMiterLimit
         | 
| 49 | 
            +
              
         | 
| 50 | 
            +
              def getLineMiterLimit
         | 
| 51 | 
            +
                getLineAttributes&.miterLimit
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
              alias get_line_miter_limit getLineMiterLimit
         | 
| 54 | 
            +
              alias line_miter_limit getLineMiterLimit
         | 
| 55 | 
            +
            end
         | 
| 56 | 
            +
             | 
| 29 57 | 
             
            module Glimmer
         | 
| 30 58 | 
             
              module SWT
         | 
| 31 59 | 
             
                module Custom
         | 
| @@ -34,8 +62,6 @@ module Glimmer | |
| 34 62 | 
             
                  class Shape
         | 
| 35 63 | 
             
                    include Packages
         | 
| 36 64 | 
             
                    include Properties
         | 
| 37 | 
            -
                    # TODO support textExtent sized shapes nested within text/string
         | 
| 38 | 
            -
                    # TODO support a Pattern DSL for methods that take Pattern arguments
         | 
| 39 65 |  | 
| 40 66 | 
             
                    class << self
         | 
| 41 67 | 
             
                      def create(parent, keyword, *args, &property_block)
         | 
| @@ -100,15 +126,18 @@ module Glimmer | |
| 100 126 | 
             
                      end
         | 
| 101 127 | 
             
                    end
         | 
| 102 128 |  | 
| 103 | 
            -
                    attr_reader :parent, :name, :args, :options
         | 
| 129 | 
            +
                    attr_reader :drawable, :parent, :name, :args, :options, :shapes
         | 
| 130 | 
            +
                    attr_accessor :extent
         | 
| 104 131 |  | 
| 105 132 | 
             
                    def initialize(parent, keyword, *args, &property_block)
         | 
| 106 133 | 
             
                      @parent = parent
         | 
| 134 | 
            +
                      @drawable = @parent.is_a?(Drawable) ? @parent : @parent.drawable
         | 
| 107 135 | 
             
                      @name = keyword
         | 
| 108 136 | 
             
                      @options = self.class.arg_options(args, extract: true)
         | 
| 109 137 | 
             
                      @method_name = self.class.method_name(keyword, @options)
         | 
| 110 138 | 
             
                      @args = args
         | 
| 111 139 | 
             
                      @properties = {}
         | 
| 140 | 
            +
                      @shapes = [] # nested shapes
         | 
| 112 141 | 
             
                      @options.reject {|key, value| %w[fill gradient round].include?(key.to_s)}.each do |property, property_args|
         | 
| 113 142 | 
             
                        @properties[property] = property_args
         | 
| 114 143 | 
             
                      end
         | 
| @@ -116,13 +145,20 @@ module Glimmer | |
| 116 145 | 
             
                      post_add_content if property_block.nil?
         | 
| 117 146 | 
             
                    end
         | 
| 118 147 |  | 
| 148 | 
            +
                    def add_shape(shape)
         | 
| 149 | 
            +
                      @shapes << shape
         | 
| 150 | 
            +
                      calculated_args_changed_for_defaults!
         | 
| 151 | 
            +
                    end
         | 
| 152 | 
            +
                    
         | 
| 119 153 | 
             
                    def draw?
         | 
| 120 154 | 
             
                      !fill?
         | 
| 121 155 | 
             
                    end
         | 
| 156 | 
            +
                    alias drawn? draw?
         | 
| 122 157 |  | 
| 123 158 | 
             
                    def fill?
         | 
| 124 159 | 
             
                      @options[:fill]
         | 
| 125 160 | 
             
                    end
         | 
| 161 | 
            +
                    alias filled? fill?
         | 
| 126 162 |  | 
| 127 163 | 
             
                    def gradient?
         | 
| 128 164 | 
             
                      @options[:gradient]
         | 
| @@ -132,26 +168,60 @@ module Glimmer | |
| 132 168 | 
             
                      @options[:round]
         | 
| 133 169 | 
             
                    end
         | 
| 134 170 |  | 
| 135 | 
            -
                    #  | 
| 136 | 
            -
                     | 
| 171 | 
            +
                    # The bounding box top-left x, y, width, height in absolute positioning
         | 
| 172 | 
            +
                    def bounds
         | 
| 173 | 
            +
                      org.eclipse.swt.graphics.Rectangle.new(absolute_x, absolute_y, calculated_width, calculated_height)
         | 
| 174 | 
            +
                    end
         | 
| 175 | 
            +
                    
         | 
| 176 | 
            +
                    # The bounding box top-left x and y
         | 
| 177 | 
            +
                    def location
         | 
| 178 | 
            +
                      org.eclipse.swt.graphics.Point.new(bounds.x, bounds.y)
         | 
| 179 | 
            +
                    end
         | 
| 180 | 
            +
                    
         | 
| 181 | 
            +
                    # The bounding box width and height (as a Point object with x being width and y being height)
         | 
| 182 | 
            +
                    def size
         | 
| 183 | 
            +
                      org.eclipse.swt.graphics.Point.new(calculated_width, calculated_height)
         | 
| 184 | 
            +
                    end
         | 
| 185 | 
            +
                    
         | 
| 186 | 
            +
                    def extent
         | 
| 187 | 
            +
                      @extent || size
         | 
| 188 | 
            +
                    end
         | 
| 189 | 
            +
                    
         | 
| 190 | 
            +
                    # Returns if shape contains a point
         | 
| 191 | 
            +
                    # Subclasses (like polygon) may override to indicate if a point x,y coordinates falls inside the shape
         | 
| 192 | 
            +
                    # some shapes may choose to provide a fuzz factor to make usage of this method for mouse clicking more user friendly
         | 
| 193 | 
            +
                    def contain?(x, y)
         | 
| 194 | 
            +
                      # assume a rectangular filled shape by default (works for several shapes like image, text, and focus)
         | 
| 195 | 
            +
                      x.between?(self.absolute_x, self.absolute_x + calculated_width) && y.between?(self.absolute_y, self.absolute_y + calculated_height)
         | 
| 196 | 
            +
                    end
         | 
| 197 | 
            +
                    
         | 
| 198 | 
            +
                    # Returns if shape includes a point. When the shape is filled, this is the same as contain. When the shape is drawn, it only returns true if the point lies on the edge (boundary/border)
         | 
| 199 | 
            +
                    # Subclasses (like polygon) may override to indicate if a point x,y coordinates falls on the edge of a drawn shape or inside a filled shape
         | 
| 200 | 
            +
                    # some shapes may choose to provide a fuzz factor to make usage of this method for mouse clicking more user friendly
         | 
| 137 201 | 
             
                    def include?(x, y)
         | 
| 138 | 
            -
                       | 
| 139 | 
            -
                       | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 | 
            -
             | 
| 145 | 
            -
                      else
         | 
| 146 | 
            -
                        false
         | 
| 147 | 
            -
                      end
         | 
| 202 | 
            +
                      # assume a rectangular shape by default
         | 
| 203 | 
            +
                      contain?(x, y)
         | 
| 204 | 
            +
                    end
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                    # Indicates if a shape's x, y, width, height differ from its bounds calculation (e.g. arc / polygon)
         | 
| 207 | 
            +
                    def irregular?
         | 
| 208 | 
            +
                      false
         | 
| 148 209 | 
             
                    end
         | 
| 149 210 |  | 
| 211 | 
            +
                    # moves by x delta and y delta. Subclasses must implement
         | 
| 212 | 
            +
                    # provdies a default implementation that assumes moving x and y is sufficient by default (not for polygons though, which must override)
         | 
| 150 213 | 
             
                    def move_by(x_delta, y_delta)
         | 
| 151 | 
            -
                       | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
                         | 
| 214 | 
            +
                      if respond_to?(:x) && respond_to?(:y) && respond_to?(:x=) && respond_to?(:y=)
         | 
| 215 | 
            +
                        if default_x?
         | 
| 216 | 
            +
                          self.default_x_delta += x_delta
         | 
| 217 | 
            +
                        else
         | 
| 218 | 
            +
                          self.x += x_delta
         | 
| 219 | 
            +
                        end
         | 
| 220 | 
            +
                        if default_y?
         | 
| 221 | 
            +
                          self.default_y_delta += y_delta
         | 
| 222 | 
            +
                        else
         | 
| 223 | 
            +
                          self.y += y_delta
         | 
| 224 | 
            +
                        end
         | 
| 155 225 | 
             
                      end
         | 
| 156 226 | 
             
                    end
         | 
| 157 227 |  | 
| @@ -166,7 +236,7 @@ module Glimmer | |
| 166 236 | 
             
                    def post_add_content
         | 
| 167 237 | 
             
                      unless @content_added
         | 
| 168 238 | 
             
                        amend_method_name_options_based_on_properties!
         | 
| 169 | 
            -
                        @ | 
| 239 | 
            +
                        @drawable.setup_shape_painting unless @drawable.is_a?(ImageProxy)
         | 
| 170 240 | 
             
                        @content_added = true
         | 
| 171 241 | 
             
                      end
         | 
| 172 242 | 
             
                    end
         | 
| @@ -174,10 +244,18 @@ module Glimmer | |
| 174 244 | 
             
                    def apply_property_arg_conversions(method_name, property, args)
         | 
| 175 245 | 
             
                      args = args.dup
         | 
| 176 246 | 
             
                      the_java_method = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.detect {|m| m.name == method_name}
         | 
| 247 | 
            +
                      return args if the_java_method.nil?
         | 
| 248 | 
            +
                      if the_java_method.parameter_types.first == Color.java_class && args.first.is_a?(RGB)
         | 
| 249 | 
            +
                        args[0] = [args[0].red, args[0].green, args[0].blue]
         | 
| 250 | 
            +
                      end
         | 
| 177 251 | 
             
                      if ['setBackground', 'setForeground'].include?(method_name.to_s) && args.first.is_a?(Array)
         | 
| 178 252 | 
             
                        args[0] = ColorProxy.new(args[0])
         | 
| 179 253 | 
             
                      end
         | 
| 180 | 
            -
                      if  | 
| 254 | 
            +
                      if method_name.to_s == 'setLineDash' && args.size > 1
         | 
| 255 | 
            +
                        args[0] = args.dup
         | 
| 256 | 
            +
                        args[1..-1] = []
         | 
| 257 | 
            +
                      end
         | 
| 258 | 
            +
                      if args.first.is_a?(Symbol) || args.first.is_a?(::String)
         | 
| 181 259 | 
             
                        if the_java_method.parameter_types.first == Color.java_class
         | 
| 182 260 | 
             
                          args[0] = ColorProxy.new(args[0])
         | 
| 183 261 | 
             
                        end
         | 
| @@ -188,7 +266,7 @@ module Glimmer | |
| 188 266 | 
             
                      if args.first.is_a?(ColorProxy)
         | 
| 189 267 | 
             
                        args[0] = args[0].swt_color
         | 
| 190 268 | 
             
                      end
         | 
| 191 | 
            -
                      if args.first.is_a?(Hash) && the_java_method.parameter_types.first == Font.java_class
         | 
| 269 | 
            +
                      if (args.first.is_a?(Hash) || args.first.is_a?(FontData)) && the_java_method.parameter_types.first == Font.java_class
         | 
| 192 270 | 
             
                        args[0] = FontProxy.new(args[0])
         | 
| 193 271 | 
             
                      end
         | 
| 194 272 | 
             
                      if args.first.is_a?(FontProxy)
         | 
| @@ -198,21 +276,21 @@ module Glimmer | |
| 198 276 | 
             
                        args[0] = args[0].swt_transform
         | 
| 199 277 | 
             
                      end
         | 
| 200 278 | 
             
                      if ['setBackgroundPattern', 'setForegroundPattern'].include?(method_name.to_s)
         | 
| 201 | 
            -
                        @ | 
| 279 | 
            +
                        @drawable.requires_shape_disposal = true
         | 
| 280 | 
            +
                        args = args.first if args.first.is_a?(Array)
         | 
| 202 281 | 
             
                        args.each_with_index do |arg, i|
         | 
| 203 | 
            -
                           | 
| 204 | 
            -
             | 
| 205 | 
            -
                           | 
| 206 | 
            -
             | 
| 207 | 
            -
                          end
         | 
| 282 | 
            +
                          arg = ColorProxy.new(arg.red, arg.green, arg.blue) if arg.is_a?(RGB)
         | 
| 283 | 
            +
                          arg = ColorProxy.new(arg) if arg.is_a?(Symbol) || arg.is_a?(::String)
         | 
| 284 | 
            +
                          arg = arg.swt_color if arg.is_a?(ColorProxy)
         | 
| 285 | 
            +
                          args[i] = arg
         | 
| 208 286 | 
             
                        end
         | 
| 209 287 | 
             
                        @pattern_args ||= {}
         | 
| 210 288 | 
             
                        pattern_type = method_name.to_s.match(/set(.+)Pattern/)[1]
         | 
| 211 289 | 
             
                        if args.first.is_a?(Pattern)
         | 
| 212 290 | 
             
                          new_args = @pattern_args[pattern_type]
         | 
| 213 291 | 
             
                        else
         | 
| 214 | 
            -
                          new_args = [DisplayProxy.instance.swt_display] + args
         | 
| 215 | 
            -
                          @pattern_args[pattern_type] = new_args
         | 
| 292 | 
            +
                          new_args = args.first.is_a?(Display) ? args : ([DisplayProxy.instance.swt_display] + args)
         | 
| 293 | 
            +
                          @pattern_args[pattern_type] = new_args.dup
         | 
| 216 294 | 
             
                        end
         | 
| 217 295 | 
             
                        args[0] = pattern(*new_args, type: pattern_type)
         | 
| 218 296 | 
             
                        args[1..-1] = []
         | 
| @@ -226,7 +304,7 @@ module Glimmer | |
| 226 304 | 
             
                        @args[1..-1] = []
         | 
| 227 305 | 
             
                      end
         | 
| 228 306 | 
             
                      if @name == 'image'
         | 
| 229 | 
            -
                        if @args.first.is_a?(String)
         | 
| 307 | 
            +
                        if @args.first.is_a?(::String)
         | 
| 230 308 | 
             
                          @args[0] = ImageProxy.new(@args[0])
         | 
| 231 309 | 
             
                        end
         | 
| 232 310 | 
             
                        if @args.first.is_a?(ImageProxy)
         | 
| @@ -237,7 +315,7 @@ module Glimmer | |
| 237 315 | 
             
                        end
         | 
| 238 316 | 
             
                      end
         | 
| 239 317 | 
             
                      if @name == 'text'
         | 
| 240 | 
            -
                        if @args[3].is_a?(Symbol) || @args[3].is_a?(String)
         | 
| 318 | 
            +
                        if @args[3].is_a?(Symbol) || @args[3].is_a?(::String)
         | 
| 241 319 | 
             
                          @args[3] = [@args[3]]
         | 
| 242 320 | 
             
                        end
         | 
| 243 321 | 
             
                        if @args[3].is_a?(Array)
         | 
| @@ -250,19 +328,21 @@ module Glimmer | |
| 250 328 | 
             
                    end
         | 
| 251 329 |  | 
| 252 330 | 
             
                    def apply_shape_arg_defaults!
         | 
| 331 | 
            +
                      self.x = :default if current_parameter_name?(:x) && x.nil?
         | 
| 332 | 
            +
                      self.y = :default if current_parameter_name?(:y) && y.nil?
         | 
| 333 | 
            +
                      self.dest_x = :default if current_parameter_name?(:dest_x) && dest_x.nil?
         | 
| 334 | 
            +
                      self.dest_y = :default if current_parameter_name?(:dest_y) && dest_y.nil?
         | 
| 335 | 
            +
                      self.width = :default if current_parameter_name?(:width) && width.nil?
         | 
| 336 | 
            +
                      self.height = :default if current_parameter_name?(:height) && height.nil?
         | 
| 253 337 | 
             
                      if @name.include?('rectangle') && round? && @args.size.between?(4, 5)
         | 
| 254 338 | 
             
                        (6 - @args.size).times {@args << 60}
         | 
| 255 339 | 
             
                      elsif @name.include?('rectangle') && gradient? && @args.size == 4
         | 
| 256 | 
            -
                         | 
| 257 | 
            -
                      elsif (@name.include?('text') || @name.include?(' | 
| 258 | 
            -
                         | 
| 340 | 
            +
                        set_attribute('vertical', true, redraw: false)
         | 
| 341 | 
            +
                      elsif (@name.include?('text') || @name.include?('string')) && !@properties.keys.map(&:to_s).include?('background') && @args.size < 4
         | 
| 342 | 
            +
                        set_attribute('is_transparent', true, redraw: false)
         | 
| 259 343 | 
             
                      end
         | 
| 260 344 | 
             
                      if @name.include?('image')
         | 
| 261 | 
            -
                        @ | 
| 262 | 
            -
                        if @args.size == 1
         | 
| 263 | 
            -
                          @args[1] = 0
         | 
| 264 | 
            -
                          @args[2] = 0
         | 
| 265 | 
            -
                        end
         | 
| 345 | 
            +
                        @drawable.requires_shape_disposal = true
         | 
| 266 346 | 
             
                      end
         | 
| 267 347 | 
             
                    end
         | 
| 268 348 |  | 
| @@ -279,7 +359,7 @@ module Glimmer | |
| 279 359 |  | 
| 280 360 | 
             
                    def amend_method_name_options_based_on_properties!
         | 
| 281 361 | 
             
                      return if @name == 'point'
         | 
| 282 | 
            -
                      if @name != 'text' && has_some_background? && !has_some_foreground?
         | 
| 362 | 
            +
                      if @name != 'text' && @name != 'string' && has_some_background? && !has_some_foreground?
         | 
| 283 363 | 
             
                        @options[:fill] = true
         | 
| 284 364 | 
             
                      elsif !has_some_background? && has_some_foreground?
         | 
| 285 365 | 
             
                        @options[:fill] = false
         | 
| @@ -298,6 +378,12 @@ module Glimmer | |
| 298 378 | 
             
                      []
         | 
| 299 379 | 
             
                    end
         | 
| 300 380 |  | 
| 381 | 
            +
                    # subclasses may override to specify location parameter names if different from x and y (e.g. all polygon points are location parameters)
         | 
| 382 | 
            +
                    # used in calculating movement changes
         | 
| 383 | 
            +
                    def location_parameter_names
         | 
| 384 | 
            +
                      [:x, :y]
         | 
| 385 | 
            +
                    end
         | 
| 386 | 
            +
                    
         | 
| 301 387 | 
             
                    def possible_parameter_names
         | 
| 302 388 | 
             
                      parameter_names
         | 
| 303 389 | 
             
                    end
         | 
| @@ -306,8 +392,12 @@ module Glimmer | |
| 306 392 | 
             
                      possible_parameter_names.map(&:to_s).include?(ruby_attribute_getter(attribute_name))
         | 
| 307 393 | 
             
                    end
         | 
| 308 394 |  | 
| 395 | 
            +
                    def current_parameter_name?(attribute_name)
         | 
| 396 | 
            +
                      parameter_names.include?(attribute_name.to_s.to_sym)
         | 
| 397 | 
            +
                    end
         | 
| 398 | 
            +
                    
         | 
| 309 399 | 
             
                    def parameter_index(attribute_name)
         | 
| 310 | 
            -
                      parameter_names. | 
| 400 | 
            +
                      parameter_names.index(attribute_name.to_s.to_sym)
         | 
| 311 401 | 
             
                    end
         | 
| 312 402 |  | 
| 313 403 | 
             
                    def set_parameter_attribute(attribute_name, *args)
         | 
| @@ -316,24 +406,44 @@ module Glimmer | |
| 316 406 |  | 
| 317 407 | 
             
                    def has_attribute?(attribute_name, *args)
         | 
| 318 408 | 
             
                      self.class.gc_instance_methods.include?(attribute_setter(attribute_name)) or
         | 
| 319 | 
            -
                        parameter_name?(attribute_name)
         | 
| 409 | 
            +
                        parameter_name?(attribute_name) or
         | 
| 410 | 
            +
                        (respond_to?(attribute_name, super: true) and respond_to?(ruby_attribute_setter(attribute_name), super: true))
         | 
| 320 411 | 
             
                    end
         | 
| 321 | 
            -
             | 
| 412 | 
            +
                    
         | 
| 322 413 | 
             
                    def set_attribute(attribute_name, *args)
         | 
| 414 | 
            +
                      options = args.last if args.last.is_a?(Hash)
         | 
| 415 | 
            +
                      args.pop if !options.nil? && !options[:redraw].nil?
         | 
| 416 | 
            +
                      perform_redraw = @perform_redraw
         | 
| 417 | 
            +
                      perform_redraw = options[:redraw] if perform_redraw.nil? && !options.nil?
         | 
| 418 | 
            +
                      perform_redraw = true if perform_redraw.nil?
         | 
| 323 419 | 
             
                      if parameter_name?(attribute_name)
         | 
| 324 420 | 
             
                        set_parameter_attribute(attribute_name, *args)
         | 
| 421 | 
            +
                      elsif (respond_to?(attribute_name, super: true) and respond_to?(ruby_attribute_setter(attribute_name), super: true))
         | 
| 422 | 
            +
                        self.send(ruby_attribute_setter(attribute_name), *args)
         | 
| 325 423 | 
             
                      else
         | 
| 326 | 
            -
                        @properties[attribute_name] = args
         | 
| 424 | 
            +
                        @properties[ruby_attribute_getter(attribute_name)] = args
         | 
| 327 425 | 
             
                      end
         | 
| 328 | 
            -
                      if @content_added &&  | 
| 426 | 
            +
                      if @content_added && perform_redraw && !drawable.is_disposed
         | 
| 329 427 | 
             
                        @calculated_paint_args = false
         | 
| 330 | 
            -
                         | 
| 428 | 
            +
                        attribute_name = ruby_attribute_getter(attribute_name)
         | 
| 429 | 
            +
                        if location_parameter_names.map(&:to_s).include?(attribute_name)
         | 
| 430 | 
            +
                          @calculated_args = nil
         | 
| 431 | 
            +
                          parent.calculated_args_changed_for_defaults! if parent.is_a?(Shape)
         | 
| 432 | 
            +
                        end
         | 
| 433 | 
            +
                        if ['width', 'height'].include?(attribute_name)
         | 
| 434 | 
            +
                          calculated_args_changed_for_defaults!
         | 
| 435 | 
            +
                        end
         | 
| 436 | 
            +
                        # TODO consider redrawing an image proxy's gc in the future
         | 
| 437 | 
            +
                        drawable.redraw unless drawable.is_a?(ImageProxy)
         | 
| 331 438 | 
             
                      end
         | 
| 332 439 | 
             
                    end
         | 
| 333 440 |  | 
| 334 441 | 
             
                    def get_attribute(attribute_name)
         | 
| 335 442 | 
             
                      if parameter_name?(attribute_name)
         | 
| 336 | 
            -
                         | 
| 443 | 
            +
                        arg_index = parameter_index(attribute_name)
         | 
| 444 | 
            +
                        @args[arg_index] if arg_index
         | 
| 445 | 
            +
                      elsif (respond_to?(attribute_name, super: true) and respond_to?(ruby_attribute_setter(attribute_name), super: true))
         | 
| 446 | 
            +
                        self.send(attribute_name)
         | 
| 337 447 | 
             
                      else
         | 
| 338 448 | 
             
                        @properties.symbolize_keys[attribute_name.to_s.to_sym]
         | 
| 339 449 | 
             
                      end
         | 
| @@ -350,7 +460,9 @@ module Glimmer | |
| 350 460 | 
             
                    end
         | 
| 351 461 |  | 
| 352 462 | 
             
                    def respond_to?(method_name, *args, &block)
         | 
| 353 | 
            -
                      if  | 
| 463 | 
            +
                      options = args.last if args.last.is_a?(Hash)
         | 
| 464 | 
            +
                      super_invocation = options && options[:super]
         | 
| 465 | 
            +
                      if !super_invocation && has_attribute?(method_name)
         | 
| 354 466 | 
             
                        true
         | 
| 355 467 | 
             
                      else
         | 
| 356 468 | 
             
                        super
         | 
| @@ -366,6 +478,18 @@ module Glimmer | |
| 366 478 | 
             
                      the_pattern
         | 
| 367 479 | 
             
                    end
         | 
| 368 480 |  | 
| 481 | 
            +
                    def pattern_args(type: nil)
         | 
| 482 | 
            +
                      @pattern_args && @pattern_args[type.to_s.capitalize]
         | 
| 483 | 
            +
                    end
         | 
| 484 | 
            +
                    
         | 
| 485 | 
            +
                    def background_pattern_args
         | 
| 486 | 
            +
                      pattern_args(type: 'background')
         | 
| 487 | 
            +
                    end
         | 
| 488 | 
            +
                    
         | 
| 489 | 
            +
                    def foreground_pattern_args
         | 
| 490 | 
            +
                      pattern_args(type: 'foreground')
         | 
| 491 | 
            +
                    end
         | 
| 492 | 
            +
                    
         | 
| 369 493 | 
             
                    def dispose(dispose_images: true, dispose_patterns: true)
         | 
| 370 494 | 
             
                      if dispose_patterns
         | 
| 371 495 | 
             
                        @background_pattern&.dispose
         | 
| @@ -379,21 +503,297 @@ module Glimmer | |
| 379 503 | 
             
                      end
         | 
| 380 504 | 
             
                      @parent.shapes.delete(self)
         | 
| 381 505 | 
             
                    end
         | 
| 382 | 
            -
             | 
| 506 | 
            +
                    
         | 
| 383 507 | 
             
                    def paint(paint_event)
         | 
| 508 | 
            +
                      # pre-paint children an extra-time first when default width/height need to be calculated for defaults
         | 
| 509 | 
            +
                      paint_children(paint_event) if default_width? || default_height?
         | 
| 510 | 
            +
                      paint_self(paint_event)
         | 
| 511 | 
            +
                      # re-paint children from scratch in the special case of pre-calculating parent width/height to re-center within new parent dimensions
         | 
| 512 | 
            +
                      shapes.each(&:calculated_args_changed!) if default_width? || default_height?
         | 
| 513 | 
            +
                      paint_children(paint_event)
         | 
| 514 | 
            +
                    rescue => e
         | 
| 515 | 
            +
                      Glimmer::Config.logger.error {"Error encountered in painting shape (#{self.inspect}) with calculated args (#{@calculated_args}) and args (#{@args})"}
         | 
| 516 | 
            +
                      Glimmer::Config.logger.error {e.full_message}
         | 
| 517 | 
            +
                    end
         | 
| 518 | 
            +
                    
         | 
| 519 | 
            +
                    def paint_self(paint_event)
         | 
| 520 | 
            +
                      @painting = true
         | 
| 384 521 | 
             
                      calculate_paint_args!
         | 
| 522 | 
            +
                      @original_properties_backup = {}
         | 
| 385 523 | 
             
                      @properties.each do |property, args|
         | 
| 386 524 | 
             
                        method_name = attribute_setter(property)
         | 
| 387 | 
            -
                         | 
| 525 | 
            +
                        @original_properties_backup[method_name] = paint_event.gc.send(method_name.sub('set', 'get')) rescue nil
         | 
| 388 526 | 
             
                        paint_event.gc.send(method_name, *args)
         | 
| 389 527 | 
             
                        if property == 'transform' && args.first.is_a?(TransformProxy)
         | 
| 390 528 | 
             
                          args.first.swt_transform.dispose
         | 
| 391 529 | 
             
                        end
         | 
| 392 530 | 
             
                      end
         | 
| 393 | 
            -
                      paint_event | 
| 531 | 
            +
                      ensure_extent(paint_event)
         | 
| 532 | 
            +
                      if !@calculated_args || parent_shape_absolute_location_changed?
         | 
| 533 | 
            +
                        @calculated_args = calculated_args
         | 
| 534 | 
            +
                      end
         | 
| 535 | 
            +
                      # 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
         | 
| 536 | 
            +
                      paint_event.gc.send(@method_name, *@calculated_args) unless parent.is_a?(Shape) && !parent.calculated_args?
         | 
| 537 | 
            +
                      @original_properties_backup.each do |method_name, value|
         | 
| 538 | 
            +
                        paint_event.gc.send(method_name, value)
         | 
| 539 | 
            +
                      end
         | 
| 540 | 
            +
                      @painting = false
         | 
| 394 541 | 
             
                    rescue => e
         | 
| 395 | 
            -
                      Glimmer::Config.logger.error {"Error encountered in painting shape | 
| 542 | 
            +
                      Glimmer::Config.logger.error {"Error encountered in painting shape (#{self.inspect}) with calculated args (#{@calculated_args}) and args (#{@args})"}
         | 
| 396 543 | 
             
                      Glimmer::Config.logger.error {e.full_message}
         | 
| 544 | 
            +
                    ensure
         | 
| 545 | 
            +
                      @painting = false
         | 
| 546 | 
            +
                    end
         | 
| 547 | 
            +
                    
         | 
| 548 | 
            +
                    def paint_children(paint_event)
         | 
| 549 | 
            +
                      shapes.to_a.each do |shape|
         | 
| 550 | 
            +
                        shape.paint(paint_event)
         | 
| 551 | 
            +
                      end
         | 
| 552 | 
            +
                    end
         | 
| 553 | 
            +
                    
         | 
| 554 | 
            +
                    def ensure_extent(paint_event)
         | 
| 555 | 
            +
                      old_extent = @extent
         | 
| 556 | 
            +
                      if ['text', 'string'].include?(@name)
         | 
| 557 | 
            +
                        extent_args = [string]
         | 
| 558 | 
            +
                        extent_flags = SWTProxy[:draw_transparent] if current_parameter_name?(:is_transparent) && is_transparent
         | 
| 559 | 
            +
                        extent_flags = flags if current_parameter_name?(:flags)
         | 
| 560 | 
            +
                        extent_args << extent_flags unless extent_flags.nil?
         | 
| 561 | 
            +
                        self.extent = paint_event.gc.send("#{@name}Extent", *extent_args)
         | 
| 562 | 
            +
                      end
         | 
| 563 | 
            +
                      if !@extent.nil? && (old_extent&.x != @extent&.x || old_extent&.y != @extent&.y)
         | 
| 564 | 
            +
                        calculated_args_changed!
         | 
| 565 | 
            +
                        parent.calculated_args_changed_for_defaults! if parent.is_a?(Shape)
         | 
| 566 | 
            +
                      end
         | 
| 567 | 
            +
                    end
         | 
| 568 | 
            +
                    
         | 
| 569 | 
            +
                    def expanded_shapes
         | 
| 570 | 
            +
                      if shapes.to_a.any?
         | 
| 571 | 
            +
                        shapes.map do |shape|
         | 
| 572 | 
            +
                          [shape] + shape.expanded_shapes
         | 
| 573 | 
            +
                        end.flatten
         | 
| 574 | 
            +
                      else
         | 
| 575 | 
            +
                        []
         | 
| 576 | 
            +
                      end
         | 
| 577 | 
            +
                    end
         | 
| 578 | 
            +
                    
         | 
| 579 | 
            +
                    def parent_shape_absolute_location_changed?
         | 
| 580 | 
            +
                      (parent.is_a?(Shape) && (parent.absolute_x != @parent_absolute_x || parent.absolute_y != @parent_absolute_y))
         | 
| 581 | 
            +
                    end
         | 
| 582 | 
            +
                    
         | 
| 583 | 
            +
                    def calculated_args_changed!(children: true)
         | 
| 584 | 
            +
                      # TODO add a children: true option to enable setting to false to avoid recalculating children args
         | 
| 585 | 
            +
                      @calculated_args = nil
         | 
| 586 | 
            +
                      shapes.each(&:calculated_args_changed!) if children
         | 
| 587 | 
            +
                    end
         | 
| 588 | 
            +
                    
         | 
| 589 | 
            +
                    def calculated_args_changed_for_defaults!
         | 
| 590 | 
            +
                      has_default_dimensions = default_width? || default_height?
         | 
| 591 | 
            +
                      parent_calculated_args_changed_for_defaults = has_default_dimensions
         | 
| 592 | 
            +
                      @calculated_args = nil if default_x? || default_y? || has_default_dimensions
         | 
| 593 | 
            +
                      if has_default_dimensions && parent.is_a?(Shape)
         | 
| 594 | 
            +
                        parent.calculated_args_changed_for_defaults!
         | 
| 595 | 
            +
                      elsif @content_added && !drawable.is_disposed
         | 
| 596 | 
            +
                        # 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
         | 
| 597 | 
            +
                        drawable.redraw if !@painting && !drawable.is_a?(ImageProxy)
         | 
| 598 | 
            +
                      end
         | 
| 599 | 
            +
                    end
         | 
| 600 | 
            +
                    
         | 
| 601 | 
            +
                    def calculated_args?
         | 
| 602 | 
            +
                      !!@calculated_args
         | 
| 603 | 
            +
                    end
         | 
| 604 | 
            +
                            
         | 
| 605 | 
            +
                    # args translated to absolute coordinates
         | 
| 606 | 
            +
                    def calculated_args
         | 
| 607 | 
            +
                      return @args if !default_x? && !default_y? && !default_width? && !default_height? && parent.is_a?(Drawable)
         | 
| 608 | 
            +
                      # 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
         | 
| 609 | 
            +
                      # TODO change that by setting a bounding box for all shapes with a calculated top-left x, y and
         | 
| 610 | 
            +
                      # a setter that does the moving inside them instead so that I could rely on absolute_x and absolute_y
         | 
| 611 | 
            +
                      # here to get the job done of calculating absolute args
         | 
| 612 | 
            +
                      @perform_redraw = false
         | 
| 613 | 
            +
                      original_x = nil
         | 
| 614 | 
            +
                      original_y = nil
         | 
| 615 | 
            +
                      original_width = nil
         | 
| 616 | 
            +
                      original_height = nil
         | 
| 617 | 
            +
                      if parent.is_a?(Shape)
         | 
| 618 | 
            +
                        @parent_absolute_x = parent.absolute_x
         | 
| 619 | 
            +
                        @parent_absolute_y = parent.absolute_y
         | 
| 620 | 
            +
                      end
         | 
| 621 | 
            +
                      if default_width?
         | 
| 622 | 
            +
                        original_width = width
         | 
| 623 | 
            +
                        self.width = default_width + default_width_delta
         | 
| 624 | 
            +
                      end
         | 
| 625 | 
            +
                      if default_height?
         | 
| 626 | 
            +
                        original_height = height
         | 
| 627 | 
            +
                        self.height = default_height + default_height_delta
         | 
| 628 | 
            +
                      end
         | 
| 629 | 
            +
                      if default_x?
         | 
| 630 | 
            +
                        original_x = x
         | 
| 631 | 
            +
                        self.x = default_x + default_x_delta
         | 
| 632 | 
            +
                      end
         | 
| 633 | 
            +
                      if default_y?
         | 
| 634 | 
            +
                        original_y = y
         | 
| 635 | 
            +
                        self.y = default_y + default_y_delta
         | 
| 636 | 
            +
                      end
         | 
| 637 | 
            +
                      if parent.is_a?(Shape)
         | 
| 638 | 
            +
                        move_by(@parent_absolute_x, @parent_absolute_y)
         | 
| 639 | 
            +
                        result_args = @args.clone
         | 
| 640 | 
            +
                        move_by(-1*@parent_absolute_x, -1*@parent_absolute_y)
         | 
| 641 | 
            +
                      else
         | 
| 642 | 
            +
                        result_args = @args.clone
         | 
| 643 | 
            +
                      end
         | 
| 644 | 
            +
                      if original_x
         | 
| 645 | 
            +
                        self.x = original_x
         | 
| 646 | 
            +
                      end
         | 
| 647 | 
            +
                      if original_y
         | 
| 648 | 
            +
                        self.y = original_y
         | 
| 649 | 
            +
                      end
         | 
| 650 | 
            +
                      if original_width
         | 
| 651 | 
            +
                        self.width = original_width
         | 
| 652 | 
            +
                      end
         | 
| 653 | 
            +
                      if original_height
         | 
| 654 | 
            +
                        self.height = original_height
         | 
| 655 | 
            +
                      end
         | 
| 656 | 
            +
                      @perform_redraw = true
         | 
| 657 | 
            +
                      result_args
         | 
| 658 | 
            +
                    end
         | 
| 659 | 
            +
                    
         | 
| 660 | 
            +
                    def default_x?
         | 
| 661 | 
            +
                      current_parameter_name?(:x) and
         | 
| 662 | 
            +
                        (x.nil? || x.to_s == 'default' || (x.is_a?(Array) && x.first.to_s == 'default'))
         | 
| 663 | 
            +
                    end
         | 
| 664 | 
            +
                    
         | 
| 665 | 
            +
                    def default_y?
         | 
| 666 | 
            +
                      current_parameter_name?(:y) and
         | 
| 667 | 
            +
                        (y.nil? || y.to_s == 'default' || (y.is_a?(Array) && y.first.to_s == 'default'))
         | 
| 668 | 
            +
                    end
         | 
| 669 | 
            +
                    
         | 
| 670 | 
            +
                    def default_width?
         | 
| 671 | 
            +
                      return false unless current_parameter_name?(:width)
         | 
| 672 | 
            +
                      width = self.width
         | 
| 673 | 
            +
                      (width.nil? || width == :default || width == 'default' || (width.is_a?(Array) && (width.first.to_s == :default || width.first.to_s == 'default')))
         | 
| 674 | 
            +
                    end
         | 
| 675 | 
            +
                    
         | 
| 676 | 
            +
                    def default_height?
         | 
| 677 | 
            +
                      return false unless current_parameter_name?(:height)
         | 
| 678 | 
            +
                      height = self.height
         | 
| 679 | 
            +
                      (height.nil? || height == :default || height == 'default' || (height.is_a?(Array) && (height.first.to_s == :default || height.first.to_s == 'default')))
         | 
| 680 | 
            +
                    end
         | 
| 681 | 
            +
                    
         | 
| 682 | 
            +
                    def default_x
         | 
| 683 | 
            +
                      result = ((parent.size.x - size.x) / 2)
         | 
| 684 | 
            +
                      result += parent.bounds.x - parent.absolute_x if parent.is_a?(Shape) && parent.irregular?
         | 
| 685 | 
            +
                      result
         | 
| 686 | 
            +
                    end
         | 
| 687 | 
            +
                    
         | 
| 688 | 
            +
                    def default_y
         | 
| 689 | 
            +
                      result = ((parent.size.y - size.y) / 2)
         | 
| 690 | 
            +
                      result += parent.bounds.y - parent.absolute_y if parent.is_a?(Shape) && parent.irregular?
         | 
| 691 | 
            +
                      result
         | 
| 692 | 
            +
                    end
         | 
| 693 | 
            +
                    
         | 
| 694 | 
            +
                    def default_width
         | 
| 695 | 
            +
                      # TODO consider caching
         | 
| 696 | 
            +
                      x_ends = shapes.map do |shape|
         | 
| 697 | 
            +
                        shape_width = shape.calculated_width.to_f
         | 
| 698 | 
            +
                        shape_x = shape.default_x? ? 0 : shape.x.to_f
         | 
| 699 | 
            +
                        shape_x + shape_width
         | 
| 700 | 
            +
                      end
         | 
| 701 | 
            +
                      x_ends.max.to_f
         | 
| 702 | 
            +
                    end
         | 
| 703 | 
            +
                    
         | 
| 704 | 
            +
                    def default_height
         | 
| 705 | 
            +
                      # TODO consider caching
         | 
| 706 | 
            +
                      y_ends = shapes.map do |shape|
         | 
| 707 | 
            +
                        shape_height = shape.calculated_height.to_f
         | 
| 708 | 
            +
                        shape_y = shape.default_y? ? 0 : shape.y.to_f
         | 
| 709 | 
            +
                        shape_y + shape_height
         | 
| 710 | 
            +
                      end
         | 
| 711 | 
            +
                      y_ends.max.to_f
         | 
| 712 | 
            +
                    end
         | 
| 713 | 
            +
                    
         | 
| 714 | 
            +
                    def calculated_width
         | 
| 715 | 
            +
                      default_width? ? (default_width + default_width_delta) : width
         | 
| 716 | 
            +
                    end
         | 
| 717 | 
            +
                    
         | 
| 718 | 
            +
                    def calculated_height
         | 
| 719 | 
            +
                      default_height? ? (default_height + default_height_delta) : height
         | 
| 720 | 
            +
                    end
         | 
| 721 | 
            +
                    
         | 
| 722 | 
            +
                    def default_x_delta
         | 
| 723 | 
            +
                      return 0 unless default_x? && x.is_a?(Array)
         | 
| 724 | 
            +
                      x[1].to_f
         | 
| 725 | 
            +
                    end
         | 
| 726 | 
            +
                    
         | 
| 727 | 
            +
                    def default_y_delta
         | 
| 728 | 
            +
                      return 0 unless default_y? && y.is_a?(Array)
         | 
| 729 | 
            +
                      y[1].to_f
         | 
| 730 | 
            +
                    end
         | 
| 731 | 
            +
                    
         | 
| 732 | 
            +
                    def default_width_delta
         | 
| 733 | 
            +
                      return 0 unless default_width? && width.is_a?(Array)
         | 
| 734 | 
            +
                      width[1].to_f
         | 
| 735 | 
            +
                    end
         | 
| 736 | 
            +
                    
         | 
| 737 | 
            +
                    def default_height_delta
         | 
| 738 | 
            +
                      return 0 unless default_height? && height.is_a?(Array)
         | 
| 739 | 
            +
                      height[1].to_f
         | 
| 740 | 
            +
                    end
         | 
| 741 | 
            +
                    
         | 
| 742 | 
            +
                    def default_x_delta=(delta)
         | 
| 743 | 
            +
                      return unless default_x?
         | 
| 744 | 
            +
                      self.x = [:default, delta]
         | 
| 745 | 
            +
                    end
         | 
| 746 | 
            +
                    
         | 
| 747 | 
            +
                    def default_y_delta=(delta)
         | 
| 748 | 
            +
                      return unless default_y?
         | 
| 749 | 
            +
                      self.y = [:default, delta]
         | 
| 750 | 
            +
                    end
         | 
| 751 | 
            +
                    
         | 
| 752 | 
            +
                    def default_width_delta=(delta)
         | 
| 753 | 
            +
                      return unless default_width?
         | 
| 754 | 
            +
                      self.width = [:default, delta]
         | 
| 755 | 
            +
                    end
         | 
| 756 | 
            +
                    
         | 
| 757 | 
            +
                    def default_height_delta=(delta)
         | 
| 758 | 
            +
                      return unless default_height?
         | 
| 759 | 
            +
                      self.height = [:default, delta]
         | 
| 760 | 
            +
                    end
         | 
| 761 | 
            +
                    
         | 
| 762 | 
            +
                    def calculated_x
         | 
| 763 | 
            +
                      result = default_x? ? default_x : self.x
         | 
| 764 | 
            +
                      result += default_x_delta
         | 
| 765 | 
            +
                      result
         | 
| 766 | 
            +
                    end
         | 
| 767 | 
            +
                    
         | 
| 768 | 
            +
                    def calculated_y
         | 
| 769 | 
            +
                      result = default_y? ? default_y : self.y
         | 
| 770 | 
            +
                      result += default_y_delta
         | 
| 771 | 
            +
                      result
         | 
| 772 | 
            +
                    end
         | 
| 773 | 
            +
                    
         | 
| 774 | 
            +
                    def absolute_x
         | 
| 775 | 
            +
                      x = calculated_x
         | 
| 776 | 
            +
                      if parent.is_a?(Shape)
         | 
| 777 | 
            +
                        parent.absolute_x + x
         | 
| 778 | 
            +
                      else
         | 
| 779 | 
            +
                        x
         | 
| 780 | 
            +
                      end
         | 
| 781 | 
            +
                    end
         | 
| 782 | 
            +
                    
         | 
| 783 | 
            +
                    def absolute_y
         | 
| 784 | 
            +
                      y = calculated_y
         | 
| 785 | 
            +
                      if parent.is_a?(Shape)
         | 
| 786 | 
            +
                        parent.absolute_y + y
         | 
| 787 | 
            +
                      else
         | 
| 788 | 
            +
                        y
         | 
| 789 | 
            +
                      end
         | 
| 790 | 
            +
                    end
         | 
| 791 | 
            +
                    
         | 
| 792 | 
            +
                    # Overriding inspect to avoid printing very long shape hierarchies
         | 
| 793 | 
            +
                    def inspect
         | 
| 794 | 
            +
                      "#<#{self.class.name}:0x#{self.hash.to_s(16)} args=#{@args.inspect}, properties=#{@properties.inspect}}>"
         | 
| 795 | 
            +
                    rescue => e
         | 
| 796 | 
            +
                      "#<#{self.class.name}:0x#{self.hash.to_s(16)}"
         | 
| 397 797 | 
             
                    end
         | 
| 398 798 |  | 
| 399 799 | 
             
                    def calculate_paint_args!
         | 
| @@ -405,7 +805,7 @@ module Glimmer | |
| 405 805 | 
             
                            if @properties[:foreground].is_a?(Array)
         | 
| 406 806 | 
             
                              @properties[:foreground] = ColorProxy.new(@properties[:foreground], ensure_bounds: false)
         | 
| 407 807 | 
             
                            end
         | 
| 408 | 
            -
                            if @properties[:foreground].is_a?(Symbol) || @properties[:foreground].is_a?(String)
         | 
| 808 | 
            +
                            if @properties[:foreground].is_a?(Symbol) || @properties[:foreground].is_a?(::String)
         | 
| 409 809 | 
             
                             @properties[:foreground] = ColorProxy.new(@properties[:foreground], ensure_bounds: false)
         | 
| 410 810 | 
             
                            end
         | 
| 411 811 | 
             
                            if @properties[:foreground].is_a?(ColorProxy)
         | 
| @@ -413,14 +813,14 @@ module Glimmer | |
| 413 813 | 
             
                            end
         | 
| 414 814 | 
             
                          end
         | 
| 415 815 | 
             
                        else
         | 
| 416 | 
            -
                          @properties['background'] = [@ | 
| 417 | 
            -
                          @properties['foreground'] = [@ | 
| 816 | 
            +
                          @properties['background'] = [@drawable.background] if fill? && !has_some_background?
         | 
| 817 | 
            +
                          @properties['foreground'] = [@drawable.foreground] if @drawable.respond_to?(:foreground) && draw? && !has_some_foreground?
         | 
| 418 818 | 
             
                          # TODO regarding alpha, make sure to reset it to parent stored alpha once we allow setting shape properties on parents directly without shapes
         | 
| 419 819 | 
             
                          @properties['alpha'] ||= [255]
         | 
| 420 | 
            -
                          @properties['font'] = [@ | 
| 820 | 
            +
                          @properties['font'] = [@drawable.font] if @drawable.respond_to?(:font) && draw? && !@properties.keys.map(&:to_s).include?('font')
         | 
| 421 821 | 
             
                          # TODO regarding transform, make sure to reset it to parent stored alpha once we allow setting shape properties on parents directly without shapes
         | 
| 422 822 | 
             
                          # Also do that with all future-added properties
         | 
| 423 | 
            -
                          @properties['transform'] = [nil] if @ | 
| 823 | 
            +
                          @properties['transform'] = [nil] if @drawable.respond_to?(:transform) && !@properties.keys.map(&:to_s).include?('transform')
         | 
| 424 824 | 
             
                          @properties.each do |property, args|
         | 
| 425 825 | 
             
                            method_name = attribute_setter(property)
         | 
| 426 826 | 
             
                            converted_args = apply_property_arg_conversions(method_name, property, args)
         |