Push/pop tool without losing current tool state?

I have a tool that moves the camera. It works great, except it loses the state of the current tool. I want my tool to work like the orbit tool. For example if I’m drawing a line, when I select the line start point, use the orbit tool, then press escape, I’ll be back at the line tool with the first point already selected, waiting for the second point to be selected.

I tried using “Sketchup.active_model.tools.push_tool(MyTool.new)” to switch to my tool, then when escape is pressed I use “Sketchup.active_model.tools.pop_tool()”. This works and restores the last tool, but the last tool’s state is gone. For example, with the line tool example above, when the line tool is restored, I have to pick the line start point again.

Is there a way for my tool to work like the orbit or pan tools?

In my extremely simplified example:

class MyTool
  def initialize
    puts "MyTool Activated"
  def onCancel(reason, view)
    puts "MyTool was canceled for reason ##{reason} "
    Sketchup.active_model.tools.pop_tool() if reason == 0

The #pop_tool will restore the last state of native tools:
(tested on SU Pro 2019 and SU Pro 2021(21.0.339) )

Off topic:
:slight_smile: Could be the reason because you are using SketchUp Free (web) version - which is not supporting any extension -, or because you have an “interesting” version of SketchUp: :thinking:


Sorry for the delay! Seems notifications didn’t work.

Your solution works well, thank you! It is really great. You can it check out here, if you’re interested:

It allows using WSAD first person shooter type controls to fly around in SketchUp.

I have just one problem: if I’m using my tool to fly and I change to eg the orbit tool, it seems my tool doesn’t get “deactivated” so it continues flying in the same direction. When I let up the keys to stop flying, my tool doesn’t get the events and the flying continues. Is there some event I can use to know when I should stop flying because I’ve switched to the orbit/pan/etc tool?

Re: OT, my password keeper logged me in, I guess with an old free account. Good enough to post on the forum. :slight_smile:

Nevermind, I found it! “suspend” does what I want. Cheers!

I will check later…

In 2012, Google sold Sketchup to Trimble … SketchUp - Wikipedia :wink:

1 Like

Did a quick look. You are violating several cardinal rules for an extension in a shared environment.

ALL of your extensions need to be within a unique top level namespace module.
No top level classes! Only Ruby and rarely the API should define top level (ie, global classes.)
Any classes that you define should be within the specific extension submodule that uses them.
Ie, separate your extensions from one another … each to their own submodule.

Do not add separators to the main “Extensions” (aka “Plugins”) menu.
Instead create a submenu and add separators on it to your heart’s content.
(The main menus should automatically add a separator at the end of native commands so we do not have to. NOTE that the core is likely not doing this yet for us. We had a discussion on it recently.)

The name of the extension subfolder must be the same name as the extension registrar script:

I am showing "AlanChatham_FPSNav.rb" for the registrar and "AlanChatham_FPSNav/" for the folder in the example below.

What we usually recommend is that the registrar and folder name be a “_” joined string of the namespace module identifier and the extension submodule identifier. So as shown below the name could also be "AlanChatham_FPSNavigation" but not this important as it’s all within your namespace.

What I do question is the wisdom of putting all that help text in the Extension description. Does the Extension Manager even display all that much text ? I would suggest putting the help text in a multiline UI::messagebox instead.

# AlanChatham_FPSNav.rb

module AlanChatham
  module FPSNavigation

    EXTENSION = SketchupExtension.new("FPSNav", "AlanChatham_FPSNav/FPSNav.rb")
    EXTENSION.instance_eval {
      self.version   = '0.4'
      self.copyright = "2021"
      self.creator   = "Alan Chatham"
      self.description = "First-Person Shooter Style Navigation.".concat(
        "  When active, move with the arrow keys; alternately, hold 'Shift' and move with WASD, or use the numeric keypad.",
        "  Movement is restricted to the X-Y Plane.",
        "  Pressing Shift + Q will move you down in the Z direction, and Shift + E will move you up.",
        "  Alternatively, 7 and 9 on the numeric keypad do the same",
        "  Left-click the mouse and hold to look around",
        "  Double-clicking also toggles mouselook on.",
        "  Movement speed and mouse sensitivity can be found in the 'Plugins/FPSNav Options' menu."
      Sketchup.register_extension(self, true)

  end # extension submodule
end # top level namespace

Ruby Conventions

Minor, but Ruby convention for method names is all lower case with underscores between words.
CamelCase is reserved for Class and Module identifiers. (Ruby does not enforce this rule because of special scenarios where external libraries are wrapped that use the same Ruby method identifier as in for example a C or Visual Basic library.)

Ruby uses 2 space indentation. It looks like you used TAB characters and when pushed to GitHub they used the default 8 space width. In most good code editors you can set the editor to replace TAB with space characters per coding language. (ie, 4 for JavaScript and HTML, 2 for Ruby, 4 for Python, 8 for C, etc. …)
Anyway if replaced with spaces, copying and pasting snippets into the forum will be displayed correctly when needing help with code.

When defining a class, only class methods are defined using the self. prefix.
In the case of a SketchUp Ruby Tool class, the class methods you have defined should actually be methods of the extension submodule wrapping the Navigator class.

# FPSNav.rb

module AlanChatham
  module FPSNavigation

    # Extension options methods setting choices to @@opts

    class Navigator

      def initialize(opts)
        # The @@opts Hash passed into the ::new constructor
        @opts = opts

      # The tool's instance methods ...

    end # custom tool class

    if not defined?(@loaded)
      # define command object(s)
      # define menu items and toolbars
      @loaded = true

  end # extension submodule
end # top level namespace

Weirdly you are not using the UI::Command for you tool for the menu item.
It should be used for both the toolbar button and the menu item.
The command proc should also call the same command method (fpsNavTool) for debugging and testing purposes. (Ie, you cannot redefine the proc for a UI::Command or menu item by reloading the file, but a method can be redefined dynamically. So if these procs point to a method, that method can be changed, the file loaded, testing resume… without having to close and restart SketchUp.

I think the copyright date was a typo so changed it above to 2021 (from 2012) ?

Thanks for the code review! This plugin isn’t originally mine, I just hacked on it a bit to get it to do as I need. I do think it’s super useful and much better than orbit/pan/look. However, I doubt many SketchUp users actually find and use it, I’m probably the only one. Given that, it’s probably not worth rewriting from scratch or even fixing up.

1 Like

Please do NOT double post, you got an answer already on other tread.

1 Like