• Login
sketchucation logo sketchucation
  • Login
🤑 SketchPlus 1.3 | 44 Tools for $15 until June 20th Buy Now

[Code] Material picker

Scheduled Pinned Locked Moved Developers' Forum
14 Posts 4 Posters 1.9k Views 4 Watching
Loading More Posts
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • E Offline
    eneroth3
    last edited by 31 May 2016, 09:50

    Hi all

    I've experimented with this material picker interface mimicking the behavior the color and pattern picker in layout. It's not entirely finished and I don't know if I'll use it myself so I thought I could just as well share it here and see if anyone has any use of it.

    Similarly to in Layout you first click a button in the dialog to start pick a material. Then the material browser opens and fetches the clicked materials back to the dialog until the dialog is closed.

    One quite mayor difference from the Layout pikers is how they bring the user's attention to the relevant panel by changing the color of its title bar. There's no such feature in SU so if the material browser is already opened it's not very clear what the user is supposed to do.

    Also I'm not 100% sure the material browser opens up at all on Mac, especially in older SU versions, since I can't test it myself. On Windows the code should work from SU 2015.

    Note: If the script is not loaded on Sketchup start a model must then be opened or a new model created for the observers to kick in.

    To test the example dialog run "MaterialSelectorTest.example".

    # UI for picking material, typically used for web dialogs.
    # Behavior inspired by Layout color or pattern selection.
    #
    # NOTE; When loading this example from console, not on Sketchup start, a model
    # must be opened or a new model created for observer to be initialized.
    #
    # To test dialog, run "MaterialSelectorTest.example".
    
    
    module MaterialSelectorTest
    
      # Interface for picking material from material browser.
      class MaterialSelector
    
        # Block to call when a material is selected.
        # TODO; store blocks as hash indexed by calling Model to function better in multi document view?
        @@callback = nil
    
        # Public; Open the material browser to let user choose a material.
        # Typically called when user clicks a button for making material input in
        # web dialog.
        #
        # old_material - Material to be selected when material browser starts
        #                or nil to not select any specific material in browser
        #                (default; nil)
        #
        # Yields Material whenever a material is selected.
        #
        # Returns nothing.
        def self.pick_material(old_material = nil, &callback)
        
          # Disable callback if any so current material first can be set to
          # old_material.
          @@callback = nil
          
          Sketchup.active_model.materials.current = old_material if old_material
          self.open_material_inspector
          @@callback = callback
          
          nil
          
        end
        
        # Public; Stop executing callback when materials are selected.
        # Typically called when the web dialog containing the material selection
        # input is closed.
        #
        # returns nothing.
        def self.done_picking_material
        
          @@callback = nil
          
          # REVIEW; If possible, maybe reset material inspector view state to what
          # it was before calling select_material.
          
          nil
          
        end
        
        # Internal; Open the material inspector.
        def self.open_material_inspector
        
          # TODO; Look up how these function on MAC.
          # TODO; If material browser is already opened it should somehow be focused to attract the user's attention.
          if Sketchup.version >= "16" # REVIEW; won't work for 1 or 3 digit mayor version numbers
            # In 2015 this toggles the material browser instead of showing it.
            UI.show_inspector "Materials"
          elsif Sketchup.platform == ;platform_win
            # Windows only for opening the inspector.
            Sketchup.send_action 21074
          else
            # Select paint bucket tool.
            # Has the drawback that the tool is selected.
            Sketchup.send_action "selectPaintTool"
          end
          
        end
        
        # Observer interface.  
        
        # Internal; Current material has changed.
        def self.onMaterialSetCurrent(clicked_material)
              
          @@callback.call(clicked_material) if @@callback
          
        end
        
        # TODO; If such an observer is ever implemented, implement this.
        # Internal; Material inspector was closed
        def self.onClosingMaterialInspector
        
          @@callback = nil
          
        end
    
      end
      
      
      # Example web dialog.
      def self.example
      
        html = <<EOF
    <!DOCTYPE HTML>
    <html>
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
        <title>TITLE</title>
        <script>
          function pickMaterial(inputId) {
            window.location= 'skp;pick_material@' + inputId;
          }
          function setPreview(inputId, colorString) {
            var input = document.getElementById(inputId);
            var span = input.getElementsByTagName('span')[0];
            span.style.backgroundColor = colorString;
          }
        </script>
        <style>
          button span {
            display; inline-block;
            width; 22px;
            height; 22px;
          }
        </style>
      </head>
      <body>
        <p>
          Example material picker. Behaviour is copied from color picking in Layout.
          Click a material button in this dialog and the material browser lets you
          select a material.
        </p>
        <label>
          Material 1;
          <button type="button" id="material1" onclick="pickMaterial(this.id)"><span></span></button>
        </label>
        <br />
        <label>
          Material 2;
          <button type="button" id="material2" onclick="pickMaterial(this.id)"><span></span></button>
        </label>
        <p>
          For this example only the material color is previewed in the button, not
          the texture.
        </p>
      </body>
    </html>
    EOF
    
        dlg = UI;;WebDialog.new(
          "Pick Material Example",
          false,
          "ene_pick_material",
          500,
          400,
          100,
          100,
          false
        )
        dlg.navigation_buttons_enabled = false
        dlg.set_background_color dlg.get_default_dialog_color
        dlg.set_html html
        Sketchup.platform == ;platform_win ? dlg.show ; dlg.show_modal
    
        # Start picking material.
        dlg.add_action_callback("pick_material") do |_, input_id|
        
          MaterialSelector.pick_material(nil) do |new_material|
          
            # Set material preview in dialog.
            # For this example only the color is used but texture could be saved to
            # file using write_thumbnail.
            if new_material
              color = new_material.color
              color_string = "rgb(#{color.red},#{color.green},#{color.blue})"
            else
              # When material is an image similar to the one inside Sketchup showing
              # front and back color could be used.
              color_string = "transparent"
            end
            js = "setPreview('#{input_id}', '#{color_string}');"
            dlg.execute_script js
            
            # Save the given material reference and the relation to the given
            # input_id.
            puts "Material #{new_material} was selected for input #{input_id}."
            
          end
        
        end
        
        # Stop picking material.
        dlg.set_on_close do
        
          MaterialSelector.done_picking_material
          
        end
        
      end
    
    
      # Observer stuff.
      class MyMaterialsObserver < Sketchup;;MaterialsObserver
    
        def onMaterialSetCurrent(_, material)
        
          MaterialSelector.onMaterialSetCurrent material
          
        end
       
      end
    
      class MyAppObserver < Sketchup;;AppObserver
    
        def expectsStartupModelNotifications
        
          true
          
        end
        
        def onNewModel(model)
        
          model.materials.add_observer MyMaterialsObserver.new
          
        end
        
        def onOpenModel(model)
        
          model.materials.add_observer MyMaterialsObserver.new
          
        end
    
      end
    
      @@app_observer ||= nil
      unless @@app_observer
        
        @@app_observer = MyAppObserver.new
        Sketchup.add_observer @@app_observer
        
      end
    
    end
    
    

    My website: http://julia-christina-eneroth.se/

    1 Reply Last reply Reply Quote 0
    • Dan RathbunD Offline
      Dan Rathbun
      last edited by 2 Jun 2016, 02:41

      if Sketchup.version >= "16"

      REVIEW: won't work for 1 or 3 digit mayor version numbers

      Use instead:
      if Sketchup.version.to_i >= 16

      The .to_i method will discard anything past the first decimal point.
      (It truncates, and does not round.)
      http://ruby-doc.org/core-2.0.0/String.html#method-i-to_i

      I'm not here much anymore.

      1 Reply Last reply Reply Quote 0
      • E Offline
        eneroth3
        last edited by 2 Jun 2016, 13:42

        This was a quite sloppy made proof of concept.

        In my current big project I use Gem::Version to check compare versions witch supports minor releases too. However to_i seems simpler when only comparing mayor versions so I might use that instead.

        Gem;;Version.new("2.1.2.4") > Gem;;Version.new("2.1.37")
        

        The restyled material buttons looks much better! The background color looks a little dark though. I usually use the values defined by the system. It has been deprecated in css3 specification but all browsers I've tried still supports it.

        Link Preview Image
        How to Use Operating System Styles in CSS — SitePoint

        Read How to Use Operating System Styles in CSS and learn with SitePoint. Our web development and design tutorials, courses, and books will teach you HTML, CSS, JavaScript, PHP, Python, and more.

        favicon

        (www.sitepoint.com)

        Also a button should have the default cursor and not the pointer.

        Regarding the pipe assignment a ||= b does not mean a = a || b. The later raises an error when used on for class variables while the former doesn't. This article explains ||= more: http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html.

        My website: http://julia-christina-eneroth.se/

        1 Reply Last reply Reply Quote 0
        • Dan RathbunD Offline
          Dan Rathbun
          last edited by 2 Jun 2016, 18:25

          @eneroth3 said:

          The background color looks a little dark though.

          Don't mind that. That is just my manual dark system settings to save my eyes.
          I did not put any background color for the page into the CSS.

          @eneroth3 said:

          I usually use the values defined by the system. It has been deprecated in css3 specification but all browsers I've tried still supports it.

          Yes, I also.

          @eneroth3 said:

          Also a button should have the default cursor and not the pointer.

          Well change the CSS, then. As long as the text select doesn't appear when your over the word "none".

          @eneroth3 said:

          Regarding the pipe assignment a ||= b does not mean a = a || b. The later raises an error when used on for class variables while the former doesn't.

          Good article. But I do not think it works on old 1.8.0 Ruby that runs on old SketchUp 7. It gave me errors so I avoided using ||=. But I always wanted it fixed. Just never noticed when they fixed it.

          (That article mentions that it added another fix for constants in Ruby 1.9.)

          Hurray! I can go ahead and use it again, because I do not write for Ruby 1.8 anymore.

          I just read somewhere a few days ago that a ||= b expanded to a = a || b. I cannot remember where it was. I was reading the latest update to the "Pick-Axe" book. It has been updated and updated again and again since Ruby 1.6, so the old behavior might still be written in the book.

          I'm not here much anymore.

          1 Reply Last reply Reply Quote 0
          • Dan RathbunD Offline
            Dan Rathbun
            last edited by 2 Jun 2016, 18:32

            Ref: @@app_observer ||= nil

            We cannot use this pattern with class / module @@vars before they are defined.

            The interpreter expands this to:
            @@app_observer = @@app_observer || nil
            ...[snip]...

            nevermind, yes we can, ... it was fixed and I didn't notice. (I'm such a stick in the mud.)

            See Christina's comment below.


            Check out the pattern I posted here:
            http://sketchucation.com/forums/viewtopic.php?f=180%26amp;t=64981#p596158

            I'm not here much anymore.

            1 Reply Last reply Reply Quote 0
            • D Offline
              driven
              last edited by 2 Jun 2016, 19:57

              an aside, I use this to replicate system dialogs...

              body {
                font-family; sans-serif;
                font; caption;
                font-size; 3.5mm;
                margin; 0;
                padding; 0;
                background-color; Window;
                line-height; 6mm;
              }
              

              john

              learn from the mistakes of others, you may not live long enough to make them all yourself...

              1 Reply Last reply Reply Quote 0
              • Dan RathbunD Offline
                Dan Rathbun
                last edited by 3 Jun 2016, 11:03

                I overhauled the CSS stylesheet in the WebDialog to mimic Windows UI.
                Added a meta tag in the head.
                Also a word "none" in the color swatch disappears after a material is chosen.

                *This image is a new screenshot made with default system colors.

                ene_PickMaterial_dialog_2.png

                [attachment=0:3svr0kkn]<!-- ia0 -->ene_pickmaterial_dialog_2.zip<!-- ia0 -->[/attachment:3svr0kkn]

                To use as a separate html file, change the code's
                dlg.set_html statement to:
                dlg.set_file("#{File.dirname(__FILE__)}/dialog.html")
                or, just paste it all over the inline HEREDOC.

                <!DOCTYPE HTML>
                <!DOCTYPE HTML>
                <html>
                    <head>
                        <meta charset="utf-8">
                        <meta http-equiv="MSThemeCompatible" content="yes"/>
                        <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
                        <title>TITLE</title>
                        <script>
                            function pickMaterial(inputId) {
                                window.location= 'skp;pick_material@' + inputId;
                            }
                            function setPreview(inputId, colorString) {
                                var input = document.getElementById(inputId);
                                var span = input.getElementsByTagName('span')[0];
                                span.innerText = ""; /* delete word "none" */
                                span.style.backgroundColor = colorString;
                            }
                        </script>
                        <style>
                            html, body {
                                font-family; "Tahoma", "Segoe UI", Sans-Serif;
                                line-height; 100%;
                                font-size;   100%;
                                font; message-box;
                                margin;  0px;
                                border;  0px;
                                padding; 5px;
                                background-color; ButtonFace;
                                color; ButtonText;
                            }
                            div.dialog {
                                margin;  0px;
                                border;  0px;
                                padding; 0px;
                            }
                            fieldset {
                              font; Status-Bar;
                            }
                            fieldset legend {
                                font; Icon;
                                font-weight; bold;
                                padding-left;  4px;
                                padding-right; 4px;
                                border-top; none;
                                border-bottom; none;
                                border-left-style;  groove;
                                border-right-style; groove;
                                border-color; ButtonHighlight;
                                border-width; thin;
                            }
                            label {
                                font; Icon;
                                font-weight; bold;
                                display; inline-block;
                                border;  0px;
                                margin;  0px;
                                padding-left;  20px;
                                margin-top;    10px;
                                margin-bottom; 0px;
                            }
                            button {
                                font; Caption;
                                color; ButtonText;
                            }
                            button.pick_material {
                                width;   66px;
                                height;  36px;
                                margin;  0px;
                                margin-left; 10px;
                                padding-left;   6px;
                                padding-top;    6px;
                                padding-right;  8px;
                                padding-bottom; 8px;
                                vertical-align; middle;
                                cursor; default;
                            }
                            span.swatch {
                                font; small-caption;
                                display; inline-block;
                                margin;  0px;
                                padding; 0px;
                                border-style; inset;
                                border-width; 1px;
                                border-color; #D0D0D0;
                                width;  100%;
                                height; 100%;
                                background-color; #E0E0E0;
                                text-align; center;
                                vertical-align; middle;
                                color; #B0B0B0;
                                /* default cursor prevents text select cursor before  */
                                /* the first button press, when hovering over "none"  */
                                cursor; default;
                            }
                        </style>
                    </head>
                    <body>
                        <div class="dialog">
                        <form>
                            <fieldset>
                                <legend> Example Material Picker </legend>
                                <p>
                                Behaviour is copied from color picking in Layout.
                                Click a material button in this dialog and the material browser lets you select a material.
                                </p>
                            <label>
                                Material 1;
                                <button type="button" id="material1" class="pick_material" onclick="pickMaterial(this.id)">
                                    <span class="swatch">none</span>
                                </button>
                            </label>
                            <br />
                            <label>
                                Material 2;
                                <button type="button" id="material2" class="pick_material" onclick="pickMaterial(this.id)">
                                    <span class="swatch">none</span>
                                </button>
                            </label>
                                <p>
                                For this example only the material color is previewed in the button, not the texture.
                                </p>
                            </fieldset>
                        </form>
                        </div><!-- dialog -->
                    </body>
                </html>
                
                

                ene_pickmaterial_dialog.zip

                I'm not here much anymore.

                1 Reply Last reply Reply Quote 0
                • Dan RathbunD Offline
                  Dan Rathbun
                  last edited by 3 Jun 2016, 11:05

                  OK, I updated the previous example for system appearance. Wrapped the dialog in a fieldset border. Changed the cursor per Julia's suggestion.

                  Also reset my own system, and made a new screenshot. (It now looks normal and not so dark.)

                  http://sketchucation.com/forums/viewtopic.php?f=180%26amp;t=65078%26amp;p=597010#p597010

                  I'm not here much anymore.

                  1 Reply Last reply Reply Quote 0
                  • sdmitchS Offline
                    sdmitch
                    last edited by 24 Jan 2017, 16:15

                    I thought I had found just what I was looking for when I found this post but, no matter what I try, I can't get this to work. Hopefully you can explain why.
                    never mind. I just realized that I wasn't starting the observer. DOH

                    Nothing is worthless, it can always be used as a bad example.

                    http://sdmitch.blogspot.com/

                    1 Reply Last reply Reply Quote 0
                    • Dan RathbunD Offline
                      Dan Rathbun
                      last edited by 24 Jan 2017, 17:55

                      Don't miss the updated styling in
                      http://sketchucation.com/forums/viewtopic.php?f=180%26amp;t=65078%26amp;view=unread#p597010

                      I'm not here much anymore.

                      1 Reply Last reply Reply Quote 0
                      • sdmitchS Offline
                        sdmitch
                        last edited by 24 Jan 2017, 21:17

                        @dan rathbun said:

                        Don't miss the updated styling in
                        http://sketchucation.com/forums/viewtopic.php?f=180%26amp;t=65078%26amp;view=unread#p597010

                        Yes, I copied that. Thanks.

                        Nothing is worthless, it can always be used as a bad example.

                        http://sdmitch.blogspot.com/

                        1 Reply Last reply Reply Quote 0
                        • sdmitchS Offline
                          sdmitch
                          last edited by 26 Jan 2017, 16:46

                          I have been testing a plugin that uses the Material Picker code and in SU2014 it works very well but, in SU1015,SU2016 and SU2017, it works ONCE! I select material1 and material2 then execute some code that replaces material1 with material2. Attempting to redefine the materials fails although there are no errors reported in the Ruby Console. Closing the WebDialog and restarting the plugin does not solve the problem. Restarting Sketchup fixes the problem but for only one execution. Anyone have an idea as to why?

                          Nothing is worthless, it can always be used as a bad example.

                          http://sdmitch.blogspot.com/

                          1 Reply Last reply Reply Quote 0
                          • Dan RathbunD Offline
                            Dan Rathbun
                            last edited by 27 Jan 2017, 11:52

                            I looked in my 2016 Plugins folder and found what I had working last June 2016.

                            It seems to work for me on SU2016.


                            ene_pickmaterial_0.2.0.zip

                            I'm not here much anymore.

                            1 Reply Last reply Reply Quote 0
                            • sdmitchS Offline
                              sdmitch
                              last edited by 27 Jan 2017, 17:21

                              @dan rathbun said:

                              I looked in my 2016 Plugins folder and found what I had working last June 2016.

                              It seems to work for me on SU2016.

                              Thanks Dan. I have come up with a different solution to what I was attempting and it works with all versions.

                              Nothing is worthless, it can always be used as a bad example.

                              http://sdmitch.blogspot.com/

                              1 Reply Last reply Reply Quote 0
                              • 1 / 1
                              • First post
                                Last post
                              Buy SketchPlus
                              Buy SUbD
                              Buy WrapR
                              Buy eBook
                              Buy Modelur
                              Buy Vertex Tools
                              Buy SketchCuisine
                              Buy FormFonts

                              Advertisement