Help to create a custom tool

Hi community,
I’ve been trying for a few days to recreate the UX of the “Components” window.
Here’s the experience I want to create:

  1. Users click on a model in my HTML extension which sends the URL of this model to Sketchup API.
  2. Ruby downloads the model (a .glb file).
  3. When the users move their mouse in the interface, an entity of the model follows the mouse.
  4. When the user clicks, an entity is created at the location of the mouse.

I’ve been able to perform steps 1 and 2 (even importing the model). But I’m struggling with steps 3 and 4.

I have the intuition that I need to create a custom tool for that.

Do you have some ideas to achieve that?

P.S.: Here’s my current code

    def self.show_dialog
      # Define dialog properties
      dialog = UI::HtmlDialog.new(
        dialog_title: "Form3d",
        # preferences_key: "form3d.extension",
        width: 525,
        height: 750 + 30, # 30px for the title bar
        resizable: false,
        scrollable: false,
        style: UI::HtmlDialog::STYLE_DIALOG
      )

      # Point to the HTML file (output from Svelte)
      html_file = File.join(__dir__, 'ui', 'build', 'index.html')
      dialog.set_file(html_file)

      dialog.add_action_callback("import_model") do |action_context, link|
        import_model(link)
      end
      dialog.show
    end

  def self.import_model(link)
    begin
      # Create a temporary file with a .glb extension
      temp_file = Tempfile.new(['model', '.glb'])
      temp_path = temp_file.path

      # Download the remote .glb file
      URI.open(link, 'rb') do |remote_file|
        File.open(temp_path, 'wb') do |local_file|
          local_file.write(remote_file.read)
        end
      end

      # Import the model into SketchUp
      model = Sketchup.active_model
      status = model.import(temp_path)

      # Cleanup: Close and remove temp file
      temp_file.close
      temp_file.unlink

      # Show success or failure message
      if status
        UI.messagebox("Model imported successfully from Form3D.")
      else
        UI.messagebox("Failed to import the model.")
      end

    rescue => e
      UI.messagebox("Error: #{e.message}")
    end
  end

Please quote code correctly on the forum:

You can check this topic: (The end of the second post might be what you are looking for…)
https://forums.sketchup.com/t/move-an-imported-dwg-component-using-ruby/248848


Edit:
Also read (credit: @DanRathbun ):

Why it is not a tool ... ( click to expand )

This is not really a tool interface; it is a command. Meaning, the code is basically sequential and does not act like a tool.

  • It does not use any tool callbacks, does not leverage the VCB (ie, Measurements toolbar) for offset input instead implementing an inputbox.
  • It does not have any tool states and does not stay in the tool and reset after each use like a tool interface should. (i.e., a tool would go back to the select edge or cline state as the initial tool state. A tool would reset when the ESC key is clicked.)
  • It does not set a cursor so it looks weird if the user uses custom system cursor scheme. Instead of the SketchUp select cursor, the system arrow cursor is shown. (I use extra large bright orange system cursor scheme that I drew many years ago. So I know immediately when a Ruby tool does not set the cursor.) This is avoided now that the code does not set the command interface as the active tool.
  • A tool generally uses interactive mouse movements and clicks or drags, often at multiple states (stages) of the task.

. :wink:

2 Likes

Thanks a lot for your reply @dezmo
I’ll try this out and make sure I follow the code standard in my following post.

You can edit your first post also.

1 Like

I’m not sure how you are trying to execute your code, but if you are pasting it into the Ruby Console, you need to then click in the model view to place the newly imported Component Instance. The place instance action of following the cursor is automatically invoked by model.import and the focus has to be in the view window for you to see it.

As noted, this is really a command, not a Tool because it is a once-and-done thing. But you still need a better way to launch it than pasting code into the Ruby Console. For that you should wrap it as a UI::Command and create a menu item and/or toolbar to fire the command.

Edit: PS I tried model#import with importing a skp file via the Ruby Console, and it worked as I described. So if you are doing that and your code is still failing, it is due to something else.

1 Like

Do not use Model#import for this.

Instead use DefinitionList#import which should return a reference to the new component definition within the model’s definitions collection.
Then, using this definition reference, make a call to Model#place_component, which creates a new instance and attaches it to the mouse cursor.
(Take note that SketchUp automatically creates an undo operation with the Model#place_component method.)


Also, the SketchUp API has non-blocking HTTP classes. See:

2 Likes

Thanks a lot everyone for your replies!
Your solution worked like a charm @DanRathbun.

1 Like

I see that @dezmo linked another topic that also suggested the same coding pattern. “Great minds think alike.”

2 Likes

Hi everyone,
There seems to be an issue happening only on macOS.
There’s an extra click that’s required to display the component.
From my understanding, this is related to this characteristic of Sketchup#active_model:

On the PC, this is the only model that one can have access to via the API, but Macintosh versions of SketchUp can have multiple models open at once, in which case the method will return the model that the user currently has focused.

Do you know how I can address this?

When is the “extra click” required in the command cycle?

I am guessing that your html dialog has the focus and the user needs to refocus one of the open model document windows?

Yes, your guess is right!
Here’s a recording that might clarify my explanation.

Okay, then perhaps add a call to Sketchup.focus()

      dialog.add_action_callback("import_model") do |action_context, link|
        Sketchup.focus
        import_model(link)
      end

… or you could add the focusing call later within the import_model() method just before importing the component defintion.

1 Like

Thanks, I’ll give it a go!

1 Like