SketchUp Plug-in Development Hole Tool: Better Tool Management

Keywords: Ruby

In our tool management class ToolManager, there is a lot of duplication in the code for creating toolbars. Each toolbar button is created through the following steps

  1. Loading tool class
  2. Creating Tool Objects
  3. Create Tool Activation Command Objects
  4. Add Tool Activation Command to Toolbar

Creating tools to activate command objects can be complex, such as setting icons, prompt text, status bar text, etc.

If these operations are categorized simply, the creation tool activation command object can be extracted and put into each tool to do, including creating command object, setting icons and so on. Because each tool should know what buttons it should represent, which belongs to the "individual" content. Tool management objects don't need to pay attention to these. They only deal with the toolbars with what names they create and where they should be added to the toolbars, which belongs to the "group" content. Therefore, we can modify the tool class and tool management class to make it more convenient and flexible to add tools.

Tool class

The transformation of tool class mainly involves two parts:

  1. Create Tool Activation Command Objects
  2. Personalized icons and prompt text

Tool management class should only focus on the command objects created by tools. In order to deal with them uniformly, we should add a common interface to the tools to obtain the command objects of tools. This can be achieved by ruby's blending module or class inheritance. For flexibility, we use the way of blending module here. First, you need to define a module for tool commands, in which you implement a method to create tool activation command objects.

# Tool Command Module
module ToolCommand
  # Get the SketchUp command
  def command
    cmd = UI::Command.new('Tool name') do
      Sketchup.active_model.select_tool(self)
    end
  end
end

Then mix this module into each tool so that each tool has a command method to create commands that activate itself.

require 'free_wall/tool/tool_command'
class DrawWallTool
  include ToolCommand
...

For a complete plug-in, tools should have their own icons, prompt text and other personalized information, so we should set up these information when creating commands, and these information should be defined in each tool.

class DrawWallTool
  include ToolCommand

  def initialize
    @menu_text = 'Wall Painting Tool'
    @large_icon = File.join(File.dirname(__dir__), 'resource/img/draw_wall_tool.jpg')
    @small_icon = File.join(File.dirname(__dir__), 'resource/img/draw_wall_tool.jpg')
    @status_bar_text = 'Wall Painting Tool'
    @tooltip = 'Draw lines to create a wall'
  end
...

This information is then applied uniformly when creating commands

def command
  menu_text = @menu_text ? @menu_text : 'Unnamed Tools'
  cmd = UI::Command.new(menu_text) do
    Sketchup.active_model.select_tool(self)
  end
  cmd.large_icon = @large_icon if @large_icon
  cmd.small_icon = @small_icon if @small_icon
  cmd.status_bar_text = @status_bar_text if @status_bar_text
  cmd.tooltip = @tooltip if @tooltip
  cmd
end

In this way, we can create personalized commands for tools, and when added to the toolbar, we can see that different tools have different display effects.

Tool Management Class

Load definition

The first step in the tool management class is to load the definition of the tool class. Since our tools are all defined in the tool directory, we can actually traverse the directory to load all ruby files.

tool_dir = File.join(__dir__, 'tool')
Dir.entries(tool_dir).each { |file|
  file_path = File.join(tool_dir, file)
  next unless File.file?(file_path)
  require(file_path)
}

After that, we should define the name of the toolbar we created and how the toolbar should lay out the specific tools, which can actually be defined by a hash table.

TOOLBAR_LIST = { 'Wall toolbar' => [DrawWallTool, CreateHoleTool] }.freeze

key is the name of the toolbar and value is the list of tools. This allows you to create all the tools through a single traversal.
Special elements can also be added to the list of tools to mark them as separators, such as adding a nil element between the two tools and creating a separator if nil elements are encountered while traversing the create tool button.

# Create toolbars
def create_toolbar
  TOOLBAR_LIST.each { |name, tools_klass|
    tool_bar = UI::Toolbar.new(name)
    tools_klass.each { |tool_klass|
      if tool_klass
        tool = tool_klass.new
        tool_bar.add_item(tool.command)
      else
        tool_bar.add_separator
      end
    }
  }
end

If you want to control the action of clicking the button when creating the toolbar, you can also replace the command of activating the tool when creating the toolbar with the execution of the block, and add the specific behavior when creating the toolbar to obtain the tool command.

...
cmd = UI::Command.new(menu_text) do
  yield
end
...

...
cmd = tool.command {
  Sketchup.active_model.select_tool(tool)
}
...

After such modification, when adding a new tool, you only need to add the class definition of the new tool to the specific location of the toolbar in TOOLBAR_LIST. And even with the same tool, we can add to different toolbars, and the behavior in each toolbar can be controlled.

Posted by s_dhumal on Thu, 29 Aug 2019 06:54:14 -0700