Component Browser Alternative?

Remove the @ please, if you are not sure why you use it. It is for instance variables when you do object-oriented programming with classes.

1 Like

OK, I’ll get on that…

  1. Remove @ for variables… I think I could not get the script to work with the Modules and Classes that we are supposed to use to protect namespaces. In other words, no I don’t understand when to or when not to use!

  2. Top level components only… Thinking I will do a parent check saying something like if parent is not Model to get only the top level components… @enroth3 Yes, perhaps a nested toggle would be useful.

Thanks!

OK here is the latest…

require 'sketchup.rb'
module ZCode
module ZInsert

def self.z_insert()
    
    model_defs = Sketchup.active_model.definitions
    comp_list = ""
    comp_names = Array.new
    comp_defs =  Array.new
    model_defs.each_with_index{|cdef,index|
        #print model_defs[index].hidden?
        if(!model_defs[index].hidden?) && (!model_defs[index].group?)
            comp_list += "#{cdef.name}|"
            comp_names.push(cdef.name)
            comp_defs.push(cdef)
        end
    }
    
    #UI.messagebox(comp_list, MB_MULTILINE)
    
    prompts = ["Select Component"]
    defaults = [""]
    list = [comp_list]
    selected = UI.inputbox(prompts, defaults, list, "Z Insert")

    selected_name = selected[0]
    print "user selected component: #{selected_name}\n"
    selected_index = comp_names.index(selected_name)
    print "selected index: #{selected_index}\n"
    selected_def = comp_defs[selected_index]
    print "selected comp: #{selected_def}\n"
    
end
    
end # end of module ZInsert
end # end of module ZCode

Now what? how do I actually insert the selected component now that I have it?

This’ll point you in the correct direction

require 'sketchup.rb'
module ZCode
  module ZInsert

    def self.z_insert()
      model_defs = Sketchup.active_model.definitions
      comp_list = ""
      comp_names = Array.new
      comp_defs =  Array.new
      model_defs.each_with_index{|cdef,index|
          #print model_defs[index].hidden?
          if(!model_defs[index].hidden?) && (!model_defs[index].group?)
              comp_list += "#{cdef.name}|"
              comp_names.push(cdef.name)
              comp_defs.push(cdef)
          end
      }
      #UI.messagebox(comp_list, MB_MULTILINE)
      
      prompts = ["Select Component"]
      defaults = [""]
      list = [comp_list]
      selected = UI.inputbox(prompts, defaults, list, "Z Insert")

      #user may have canceled or chosen the blank item at the top of your drop down list
      if selected.is_a?(Array) && (selected[0].size != 0)
        selected_name = selected[0]
        print "user selected component: #{selected_name}\n"
        selected_index = comp_names.index(selected_name)
      
        print "selected index: #{selected_index}\n"
        selected_def = comp_defs[selected_index]
        print "selected comp: #{selected_def}\n"
      
        Sketchup.active_model.place_component(selected_def, false)
      end
    end
      
  end # end of module ZInsert
end # end of module ZCode

ZCode::ZInsert.z_insert

is equal to

comp_names = []

Model#place_component gives you an interactive positioning tool (interrupts Ruby execution, waiting until user has placed it). If you every need to automate this without user interaction, you will want to add an instance of that component definition to the entities: Entities#add_instance

It works! That is perfect. Thank you.

Also thanks Aerilius, I much prefer that array def approach.

Now my one last feature is to make the list focused so, being a keyboard shortcut user, I can either start typing the first few characters of the component name to select it OR use the up/down arrow keys to select. Obviously, you’d want pressing enter to be equivalent to hitting the OK button.

There do not seem to be focus controls for Ui.inputbox so I may have to switch to html dialog?

UI.inputbox does not give you any control. You could maybe hit the key (tab).

UI.inputbox does not give you any control. You could maybe hit the key (tab).

I’d prefer not to because it is sort of the point of all this to get down to as few keystrokes as possible.

Here is an htmldialog version that is getting closer…

require 'sketchup.rb'
module ZCode
module ZInsert

def self.z_insert()
    
    model_defs = Sketchup.active_model.definitions
    option_list = ""
    comp_defs =  []
    model_defs.each_with_index{|cdef,index|
        #print model_defs[index].hidden?
        if(!model_defs[index].hidden?) && (!model_defs[index].group?)
            option_list += "<option value='#{cdef}'>#{cdef.name}</option>"
            comp_defs.push(cdef)
        end
    }
    
    html_dialog = UI::HtmlDialog.new()
    html_dialog.center
    html = "
        <b>Select Component</b>
        <select id='sel' onchange='if(this.selectedIndex) sketchup.comp_selected(this.selectedIndex);'>
    "
    html += option_list
    html += "
    </select>
    "
    html_dialog.set_html(html)
    html_dialog.show
    
    html_dialog.add_action_callback("comp_selected") { |action_context, sel_index|
        Sketchup.active_model.place_component(comp_defs[sel_index], false)
        html_dialog.close
    }
    
end
    
end # end of module ZInsert
end # end of module ZCode

just need to figure out how to focus the select element onLoad.

I tried some of the stuff mentioned here:

https://forums.sketchup.com/t/how-to-pass-values-to-htmldialog/47968

to no avail.

See HTMLElement#focus. This is a JavaScript method that needs to be called on your <select> element, after it has been created and loaded.
Your HTML is incomplete and misses the wrapping <html><head>…</head><body>…</body></html>, but apparently the browser engine fixes it for you (so your solution is shorter). Probably the simplest solution is to append <script type="text/javascript">document.getElementById("sel").focus()</script>.

I’m not fully sure this helps, but I have a second skp file open whenever I am modeling. I have it full of components, profiles ready modeled rooms etc. I toggle back and forth between the two files when I am modeling to grab what I need. Additionally I have scenes set up in that skp file to quickly take me to what I’m looking for, as well as 3D text labeling items that may look similar but might be slightly different size ie a 2’6” door oppose to a 2’8” door.
Thought this may help. I’m not at my work station at the moment, or I would do a quick screen shot to show you. I’ll try to add on Monday.

Yes, Aerilius, that worked perfect. Adding,

    <script type='text/javascript'>
    document.getElementById('sel').focus();
    </script>

focuses the select on load so you can start typing immediately.

Currently, its a little abrupt because if you start typing the name of a component and it finds one that matches, BANG you have it and are inserting…

It would be better to have something like a HTML5 dataset element that would allow you to type, see what you are filtering down to, and either hit enter to accept or hit up/down arrows then select.

HOWEVER, I could NOT get dataset to work! The same code in the w3schools TryIt editor works fine but not in Sketchup. Have any of you had any luck with dataset in an htmldialog?

Thank you spmcgill81 but that’s a little different workaround than what I am looking for.

datalist won’t work in CEF [ as I explained ] in the other thread…

even if it did, you can have issue sending too many options to the list…

Ruby can filter what you send based on what it receives…

in my test model you can see that it only took 3 chars to reduce from 1009 definitions to 1…

that’s trivial for js to handle, but play with this in Ruby Console and get it ’ fit for your purpose’ first…

model = Sketchup.active_model
@defns = model.definitions

@defns_hash = {}
  
def build_hash
	@defns.each do |defn|
		next if defn.hidden?
		next if defn.group?
		name = defn.name
		@defns_hash[name] = defn
	end
	p "@defns_hash contains #{@defns_hash.length} definitions"
end
# called here for pupose of testing
build_hash 
# returns 
#=> "@defns_hash contains 1009 definitions"

def match_input(*args)
	targets = args[0].chars.join('.*')
	results = []
	@defns_hash.each_key{|k| results << k if k.match(/.*#{targets}/i)}
	results.empty? ? 'no matches' : results
end
# filter the hash
match_input('D').length
#=> 24
match_input('Di').length
#=>3
 match_input('Dis').length
#=>1
key = (match_input('Dis'))[0]
@defns_hash[key]
#<Sketchup::ComponentDefinition:0x00007fcc3bd03978>

when you think about it datalist it waits for the 2nd char, so the list can be empty before that…

an action_callback can be triggered by >2 char entries, in any type of input field, and you can use execute_script to populate a ‘dropdown’…

use vanilla javascript, as the non ‘V8 only js’ libraries only add bloat and complexity…

if you find a js UI library exclusivley for CEF lets us all know…

john

1 Like

First of, what a shame that the Chrome Embedded Framework is somehow behind the current full-featured browser. I have no idea how all this works but I gather that Sketchup somehow refers to the client computers applications for help with html files. Why wouldn’t it just use whatever full browser is installed on the machine? I’m sure there are reasons but too bad it SU doesn’t work more fluidly with current browser implementations.

Secondly, it sounds like you are suggesting that, rather than using a native element like datalist (that doesn’t work in SU anyway), I should work up a custom script or possibly plug-in an external library.

I immediately think JQuery but every time I see jQuery mentioned relative to htmlDialog, I see people commenting that it’s too bloated.

If I SHOULDN’T use an external library, I can muddle through but now isn’t a custom script also getting bloated too? That is what attracted me to datalist - a built-in (an thereby somewhat lean) element.

I may just go back to the UI.inputbox at the sacrifice of a few keystrokes here because this is getting more complicated than I had hoped.

it’s common for all browsers to withhold key functionality in the frameworks they supply to ‘competitors’…

UI::WebDialog.new() uses IE on PC or Safari on mac…
UI::HtmlDialog.new() uses CEF on both platforms…

without the need for all the shims the javascript is minimal…
Ruby can do the heavy lifting…

I have a test 90% complete and it has three lines of javascript…

they are there so I can test out side of SU and will not be needed if I write the Ruby bits…

I’d probably add in about 20 lines to smooth things out…

if I fed in my 1006 component definitions, it would;t be so lean…

john

1 Like

OK John, its a bit over my head at this point! I may just keep muddling away and show you guys whatever I come up with. Thanks!

I’m coming into the conversation late, when it is getting deep into ruby.
I had two comments I wanted to add at the beginning of the conversation:

  1. I wish that the component browser would show the size (in bits, or better yet number of faces) of components as I often want ones that are efficient and want to skip components that are heavy resource consumers (but not always).
  2. Also, regarding Component Finder by Flextools. I looked a little demo of it, and it seemed like the main feature was organizing components into folders. I already to that in the existing component browser, although I can do it from within the browser. Are there other advantages to Component Finder?
    Huck

I started a new topic on this,

john

Hi Hank,

We just released an update to ComponentFinder. It now searches through the In-Model components as well. We are still working on more improvements and plan to add a few more functions like ‘Replace Selected’ etc.

I hope you find it useful. If not… I’d love to know what we can do to make it better!

1 Like

Hi,
regarding the discussion about “organizing model by tags instead of layers” that happened somewhere, does Component Finder make use of model tags (external components are saved as models)?

Or, since the API makes them only readable from files instead of component definitions, maybe searching components/definitions by attributes could be helpful to users? There is only no fast access available through the API, so by whatever criterion a user wants to search, it would probably require indexing first all components in the model?

@Yoni Amazing! That is what I am talking about. After indexing, I can type the first few characters of a component name and the list is filtered to matching comps! It even does BOTH the model I have open AND my home folder of components simultaneously.

Great work. Now I HAVE to look more closely at what FlexTools is all about.

1 Like