How to create an icon

Hi all,

I have a plugin that i operate from the extension menu.
I want to create an icon for it, that I can place on the toolbar.

How can I do it?
thank you

Maybe this will help:

    ICON_PATH = File.join(PLUGIN_PATH, "icons", "TEMPLATE.png").freeze 

And:

unless @loaded
    @loaded = true
    cmd_TEMPLATE.large_icon = cmd_TEMPLATE.small_icon = ICON_PATH
    cmd_TEMPLATE.tooltip = "TEMPLATE, TEMPLATE and TEMPLATE."
    cmd_TEMPLATE.status_bar_text = "TEMPLATE."
    UI.menu("Tools").add_item(cmd_TEMPLATE)
    toolbar = UI::Toolbar.new("TEMPLATE")
    toolbar.add_item(cmd_TEMPLATE)
    toolbar.restore
end
2 Likes

Iā€™d recommend a tweak to the toolbar code.
At the end use

#toolbar.restore
toolbar.show if toolbar.get_last_state.abs == 1 # TB_VISIBLE/NEVER

Otherwise you might annoy the user who has the toolbar switched off when it reopens when SketchUp next opensā€¦

2 Likes

Hi TIG, I just did a test by disabling an extension in the Extension Manager and the extension / toolbar button did not load. Am I missing a case? I just saved what I shared as my TEMPLATE, so now would be the time to adjust it!

In my opinion and experience
toolbar.restore

does what TIGs code does. (There was a different scenarios in earlier versions but it is goneā€¦)

Edit:
Just a quick search about it:

1 Like

Because you are only posting snippets of code and not fully constructed modules etc, and we donā€™t even know if you have constructed this as an extension at allā€¦

Either give us all of you files, or read up some more and try to make a fleshed-out simple extension with a toolbar button etcā€¦

Okay, I read that but didnā€™t test. Maybe one difference was Johnā€™s Mac (as he suggested)?

What I did test was opening SU ('24), disabling in Extension Manager and then NOT closing SU (both version of code). Then opening another instance of SU (Toolbar was present in both cases). Then, same as above but closing SU and then opening another instance of SU (Toolbar not visible: as expected on my machine). So I did not see a difference.

For completeness, this is my ā€˜ruby mainā€™ template:

Ruby Main:
require 'json'

module IDKProgramming
  module TEMPLATE
    extend self

    @dialog = nil
    @loaded = false

    def show_html_dialog
      @dialog ||= create_dialog
      attach_callbacks unless @dialog.visible?
      @dialog.visible? ? @dialog.bring_to_front : @dialog.show
    end

    def create_dialog
      UI::HtmlDialog.new({
        dialog_title: "TEMPLATE",
        preferences_key: PREF_KEY,
        scrollable: true,
        resizable: true,
        width: 800,
        height: 600,
        style: UI::HtmlDialog::STYLE_DIALOG
      }).tap { |dialog| dialog.set_file(File.join(PLUGIN_PATH, 'html', 'TEMPLATE.html')) }
    end

    def attach_callbacks
      # Attach a callbacks.
    end

    unless @loaded
      @loaded = true
      cmd_TEMPLATE = UI::Command.new("TEMPLATE") { show_html_dialog }
      cmd_TEMPLATE.large_icon = cmd_TEMPLATE.small_icon = ICON_PATH
      cmd_TEMPLATE.tooltip = "TEMPLATE, TEMPLATE and TEMPLATE."
      cmd_TEMPLATE.status_bar_text = "TEMPLATE."
      UI.menu("Tools").add_item(cmd_TEMPLATE)
      toolbar = UI::Toolbar.new("TEMPLATE")
      toolbar.add_item(cmd_TEMPLATE)
      toolbar.restore
    end
  end
end
I use a Loader File:
module IDKProgramming
  module TEMPLATE

    PLUGIN_PATH = File.dirname(__FILE__).freeze

    PREF_KEY = Module.nesting[0].name.gsub('::', '_')

    ICON_PATH = File.join(PLUGIN_PATH, "icons", "TEMPLATE.png").freeze 
    
    require File.join(PLUGIN_PATH, 'TEMPLATE_main.rb')
    
  end
end

I did remove the following line of code for the html dialog because the OP had only mentioned the making the toolbar button.

Removed:
cmd_TEMPLATE = UI::Command.new("TEMPLATE") { show_html_dialog }

Because there were an old bugs with UI::Toolbar#restore and UI::Toolba#show (as discussed in the topic linked above,) ā€¦ I have been in the habit of using this:

toolbar.get_last_state == TB_NEVER_SHOWN ? toolbar.show : toolbar.restore

It always seemed to work as we expect, so Iā€™ve never broke from this ā€œhabitā€.

Thomasā€™ suggestion that UI::Toolbar#restore does what my ā€œhabitualā€ statement does, is still not declared in the documentation, now 6 years later.

1 Like

Hi Everyone,

thank you for your responses,
I want to clarify myself,

I have the extension from TIG

and I want to create an Icon for it on the toolbar.

How can I do so?
Please give me the easy way, Iā€™m newbie.

thank you.

That is a very old plugin [i.e. itā€™s not written as an extension - but it will work and is editable.
First fnd the file in the Plugins folder named ā€œTIG-Cut_to_planeā€
Edit it with a plain text editor like Notepad++.exe
Towards the end of the code is the part which sets up the menu and context-menu items.
You need to add some extra toolbar codeā€¦
Just before the closing part

end#if loaded
 file_loaded(__FILE__)

add in this extra code

### TOOLBAR
    cmd = UI::Command.new("TIG-Cut_to_plane"){Sketchup.active_model.select_tool(TIG::Cut_to_plane.new())}
    cmd.tooltip = "TIG-Cut_to_plane"
    cmd.status_bar_text = "TIG-Cut_to_plane"
    c2pfolder = File.join(File.dirname(__FILE__), "TIG-Cut_to_plane")
    cmd.small_icon = File.join(c2pfolder, 'c2p.svg')
    cmd.large_icon = File.join(c2pfolder, 'c2p.svg')
    c2ptoolbar = UI::Toolbar.new("TIG-Cut_to_plane")
    c2ptoolbar.add_item(cmd)
    c2ptoolbar.show if c2ptoolbar.get_last_state.abs == 1 # TB_VISIBLE/NEVER
    ###

You also need to add a subfolder into Plugins named ā€œTIG-Cut_to_planeā€
Inside this you need the image file to form the toolbar button icon.
Iā€™ve named it ā€œc2p.svgā€.
Youā€™ll need a tool like Inkscape to make the svgā€¦

After making the changes/additions and saving, and restarting SketchUp the new toolbar should be available and workingā€¦

Hello TIG,

Is this your plugin? it is perfect, It is my favorite plugin. You said it is old, if you have another one that works better I would like to get it.

Sorry, but I really didnā€™t understand how to do what you have kindly elaborated.

I am not a coder, and donā€™t know what does it mean closing part,
Can you please put the code in TIG cut to plane and send me, it will help me a lot.

Thank you in advance.

Eyal

This is the code TIG mentioned with the suggested code for the toolbar inserted. Look in the last ā€˜paragraphā€™ of code to see it.

Code
=begin
TIG (c) 2013
###
TIG-Cut_to_plane.rb
###
Installation: Place this file in the Plugins folder and restart Sketchup.
Usage: Plugins menu > 'TIG-Cut_to_plane'
or with a suitable selection use the context-menu 'TIG-Cut_to_plane'
Note for Pro >=v.8 only...
Preselect a 'solid' object [group or component_instance].
Activate the tool.
Follow the prompts and pick 3 points to define a plane.
Picking these points CWS  cuts to leave the part ABOVE the plane.
Picking these points CCWS cuts to leave the part BELOW the plane.
If you press <Enter> instead of picking point 3 then the plane is defined 
'vertically', the direction of the 'line' between the first 2 points will
determine what will be left behind, i.e. the part to the 'LEFT' of the 'line'.
If the points 1 and 2 are vertically aligned then no vertical plane can be 
defined, there is a warning message and you need to pick point 3, OR abort, 
say by pressing <Spacebar>.
The selected object will then be cut to the defined plane.
The new cut group made from the 'cut' operation retains the original object's 
locked/hidden-state, shadow-casting/receiving-behavior, name*, material, layer 
and any attribute-dictionaries attached to it.
*If the original was a group the 'cut' group takes its name, if it was a 
component-instance the 'cut' group takes its definition name, but if that 
instance also had a name then this is appended to the new name inside [].
The 'cutting' is one step undo-able.
Donations: PayPal.com info @ revitrev.org
Version:
1.0 20121007 First issue.
1.1 20130107 Now at Point 3 an alternative <Enter> press cuts vertically, 
			 keeping left-side parts. Context-menu if selection suitable.
=end
###
require('sketchup.rb')
###
module TIG
	class TIG::Cut_to_plane
		def initialize()
			unless Sketchup.is_pro? and Sketchup.version.to_i>=8
				UI.messagebox("TIG-Cut_to_plane.rb\n\nOnly works with Pro, >=v.8")
				return nil
			end
			@ip  = nil
			@ip1 = nil
			@ip2 = nil
			@ip3 = nil
		end
		def activate
			@state=0
			@pts = []
			@ip  = Sketchup::InputPoint.new
			@ip1 = Sketchup::InputPoint.new
			@ip2 = Sketchup::InputPoint.new
			@ip3 = Sketchup::InputPoint.new
			@model=Sketchup.active_model
			@ss=@model.selection
			@ents=@model.active_entities
			unless self.selection_solid?()
				UI.messagebox("TIG-Cut_to_plane:\n\nSelect ONE 'solid' group/component-instance !")
				return nil
			end
			@msg="TIG-Cut_to_plane: Pick Point 1 on Plane..."
			Sketchup.set_status_text(@msg)
			@lok=@gp.locked?
			@hid=@gp.hidden?
			@shc=@gp.casts_shadows?
			@shr=@gp.receives_shadows?
			if @gp.is_a?(Sketchup::ComponentInstance)
				@nam=@gp.definition.name
				unless @gp.name.empty?
					@nam=@nam+"["+@gp.name+"]"
				end
			else ### it's a Group
				@nam=@gp.name ### might be "" !
			end
			@mat=@gp.material
			@lay=@gp.layer
			@ads=@gp.attribute_dictionaries
		end
		def deactivate(view)
			view.invalidate
			@ip1 = nil
			@ip2 = nil
			@ip3 = nil
			return nil
		end
		def onCancel(flag, view)
			view.invalidate
			@ip1 = nil
			@ip2 = nil
			@ip3 = nil
			Sketchup.send_action("selectSelectionTool:")
			return nil
		end
		def resume(view=nil)
			Sketchup.set_status_text(@msg)
		end
		def getExtents
			bb=Geom::BoundingBox.new
			if @ip.valid? && @ip.display?
				bb.add(@ip.position)
			end
			@pts.each{|p|bb.add(p)}
			return bb
		end
		def onMouseMove(flags, x, y, view)
			return nil if @state>2
			case @state
				when 0 # getting the first point
					@ip.pick(view, x, y)
					if @ip.valid? && @ip != @ip1
						@ip1.copy!(@ip)
					end
					view.invalidate
					view.tooltip = @ip.tooltip if @ip.valid?
				when 1 # getting the second point
					@ip.pick(view, x, y, @ip1)
					if @ip.valid? && @ip != @ip2
						@ip2.copy!(@ip)
					end
					view.invalidate
					view.tooltip = @ip.tooltip if @ip.valid?
				when 2 # getting the third point
					@ip.pick(view, x, y, @ip2)
					if @ip.valid? && @ip != @ip3
						@ip3.copy!(@ip)
					end
					view.invalidate
					view.tooltip = @ip.tooltip if @ip.valid?
			end
		end
		def onLButtonDown(flags, x, y, view)
			return nil if @state>2
			@ip.pick(view, x, y)
			if @ip.valid?
				case @state
					when 0
						if @ip.valid?
							@pts[0] = @ip.position
							@msg="TIG-Cut_to_plane: Pick Point 2 on Plane..."
							Sketchup.set_status_text(@msg)
							@state = 1
						end
					when 1
						if @ip.valid? && @ip != @ip1
							@pts[1] = @ip2.position
							@state = 2
							@msg="TIG-Cut_to_plane: Pick Point 3 on Plane, OR Press <Enter> for a Vertical Split..."
							Sketchup.set_status_text(@msg)
						end
					when 2
						if @ip.valid? && @ip != @ip1 && @ip != @ip2
							@pts[2] = @ip3.position
							@state = 3
							view.invalidate
							@msg="TIG-Cut_to_plane: Trimming..."
							Sketchup.set_status_text(@msg)
							self.trim(view)
							view.lock_inference
							return nil
						end
				end
			end
		end
		def draw(view)
			return unless @ip
			return nil if @state>2
			if @ip.valid? && @ip.display?
				@ip.draw(view)
				@drawn = true
			end
			if @state == 1
				view.set_color_from_line(@ip1, @ip)
				view.draw(GL_LINE_STRIP, @ip1.position, @ip.position)
				@drawn = true
			elsif @state == 2
				view.drawing_color = "gray"
				view.draw(GL_LINE_STRIP, @ip1.position, @ip2.position)
				view.set_color_from_line(@ip2, @ip)
				view.draw(GL_LINE_STRIP, @ip2.position, @ip.position)
				@drawn = true
			end
		end
		def onKeyDown(key, repeat, flags, view)
			return nil if @state>2
			if key == CONSTRAIN_MODIFIER_KEY && repeat == 1
				@shift_down_time = Time.now
				if view.inference_locked?
					view.lock_inference
				elsif @ip.valid?
					view.lock_inference(@ip)
				end
			end
		end
		def onKeyUp(key, repeat, flags, view)
			return nil if @state>2
			if key == CONSTRAIN_MODIFIER_KEY && view.inference_locked? && (Time.now - @shift_down_time) > 0.5
				view.lock_inference
			end
		end
		def onReturn(view)
			return nil unless @state==2
			if @pts[0].vector_to(@pts[1]).parallel?(Z_AXIS)
				UI.messagebox("TIG-Cut_to_plane:\n\nPoints 1 and 2 are Aligned Vertically.\nA Vertical Cut is Impossible !")
				@msg="TIG-Cut_to_plane : Pick Point 3 on Plane..."
				Sketchup.set_status_text(@msg)
				return nil
			else
				@pts[2] = @pts[1].offset(Z_AXIS)
			end
			view.invalidate
			@msg="TIG-Cut_to_plane : Trimming..."
			Sketchup.set_status_text(@msg)
			self.trim(view)
			view.lock_inference
			@state = 3
			return nil
		end
		###
		def selection_solid?()
			return nil unless @ss
			return nil unless @ss[0]
			return nil if @ss[1]
			@gp=@ss[0]
			return nil unless @gp.is_a?(Sketchup::Group) || @gp.is_a?(Sketchup::ComponentInstance)
			return nil unless @gp.manifold?
			return true
		end
		def trim(view)
			begin
				@model.start_operation("Cut_to_plane")
				ct=@pts[0]
				v1=@pts[0].vector_to(@pts[1])
				v2=@pts[0].vector_to(@pts[2])
				ve=v1.cross(v2)
				ve=Z_AXIS if ve.length==0
				bb=@gp.bounds
				ra=4*ct.distance(bb.center)
				ra=2*bb.diagonal if 2*bb.diagonal>ra
				tgp=@ents.add_group()
				tents=tgp.entities
				es=tents.add_circle(ct, ve, ra, 8)
				fa=tents.add_face(es)
				fa.pushpull(ra)
				if ct.z==0 and ve.parallel?(Z_AXIS) and ve==Z_AXIS
					fa.reverse!
				end
				ob=tgp.subtract(@gp)
				if ob and ob.valid?
					ob.locked=@lok
					ob.hidden=@hid
					ob.casts_shadows=@shc
					ob.receives_shadows=@shr
					ob.name=@nam
					ob.material=@mat
					ob.layer=@lay
					@ads.each{|ad|
						na=ad.name
						ad.each_pair{|k,v|
							ob.set_attribute(na, k, v)
						}
					}if @ads
					@ss.add(ob)
				end
				@model.commit_operation
			rescue
				###
			end
			self.onCancel(0, view)
			return nil
		end
		
		
	end#class
	###
	unless file_loaded?(__FILE__)
		UI.menu("Plugins").add_item("TIG-Cut_to_plane"){Sketchup.active_model.select_tool(TIG::Cut_to_plane.new())}
		UI.add_context_menu_handler{|menu|
			ss=Sketchup.active_model.selection
			if ss.length==1 && (ss[0].is_a?(Sketchup::Group) || ss[0].is_a?(Sketchup::ComponentInstance)) && ss[0].manifold?
				menu.add_item("TIG-Cut_to_plane"){Sketchup.active_model.select_tool(TIG::Cut_to_plane.new())}
			end
		}

    ### Put the suggested code for the toolbar here.
    cmd = UI::Command.new("TIG-Cut_to_plane"){Sketchup.active_model.select_tool(TIG::Cut_to_plane.new())}
    cmd.tooltip = "TIG-Cut_to_plane"
    cmd.status_bar_text = "TIG-Cut_to_plane"
    c2pfolder = File.join(File.dirname(__FILE__), "TIG-Cut_to_plane")
    cmd.small_icon = File.join(c2pfolder, 'c2p.svg')
    cmd.large_icon = File.join(c2pfolder, 'c2p.svg')
    c2ptoolbar = UI::Toolbar.new("TIG-Cut_to_plane")
    c2ptoolbar.add_item(cmd)
    c2ptoolbar.show if c2ptoolbar.get_last_state.abs == 1 # TB_VISIBLE/NEVER
	end#if loaded
	file_loaded(__FILE__)
	###
end#module

You need to create the folder he mentioned and place it inside of your extension folder. It should be here: C:\Users\YourUserName\AppData\Roaming\SketchUp\SketchUp 2024\SketchUp\Plugins

Youā€™ll need to create the icon and name it ā€œc2p.svgā€ and put it in the folder.

1 Like

Thank you 3DxJFD,
It is very helpful.

Good day.
Eyal

1 Like