Trying to create an extension 'Tag Folder Visibility' to hide and show specific tag folders

I’m inspired by something I saw in other applications and the following discussion:

What I’d like to create is an extension that shows and hides layer folders in a number of ways. I’m not a coder, but thought this would be a nice exercise to learn more about coding and Ruby.

So far I came up with a basic script, based on an example of DanRathbun in the discussion above, which can hide or show all top level folders…
Basbva_FolderVisibility:003.rb (892 Bytes)

What I would like to achieve next are a number of things, but first of all to add to this script the possibility to hide all Folders except those that hold the active tag (as selected in the Tag Window).

Any clues or thoughts as to how to set about and achieve this will be greatly appreciated.

Add a condition.

module Basbva
  module FolderVisibility
    extend self

    VERSION ||= "0.0.3"
    MENU ||= "View"
    
    def toggle_off
      model = Sketchup.active_model
      layers = model.layers
      folders = layers.folders
      active_folder = model.active_layer.folder

      folders.each do |folder|
        folder.visible = false unless folder == active_folder
      end
    end
    
    def toggle_on
      model = Sketchup.active_model
      layers = model.layers

      layers.folders.each { |folder| folder.visible = true }
    end

    unless defined?(@ui_loaded)
      add_separator_to_menu(MENU)
      menu = UI.menu(MENU).add_submenu("Folder Visibility")
      
      menu.add_item("Hide Folders") { toggle_off() }
      menu.add_item("Show Folders") { toggle_on() }

      @ui_loaded = true
    end
  end
end


3 Likes

“Administrative” notes:

  • It is (more than) strongly recommended that always keep the Untagged as active tag and never hide it, and since the Untagged can not be in a folder…:wink:

  • I moved your topic to right category.

You may need to also consider this active_folder can be inside an other parent folder(s).

3 Likes

Yes. I don’t think I would build this extension out because it doesn’t work with that recommendation.

But it was a good exercise to try. ;^)

2 Likes

I’m strongly aware of the need of keeping raw elements untaged and I learned the hard way, coming from other applications. There are more ways than one though of grouping and attributing.

Another is to select a tag and start grouping, which I think can be usefull, but you certainly do need to be aware of what you’re doing. Is it really recommended by Sketchup not to do that at all?

@3DxJFD You’ve made my day already. As Dezmo suggests other child folders of the folder above the active child folder aren’t hidden. In the other thread I mentioned, DanRathbun seems to suggest that to achieve something like that, one would need to ‘walk through the tree structure’, which can be done with a recursive method. So that would be the next challenge, I guess.

Apart from that, I think you’re suggestion already can be pretty usefull.

2 Likes

I just want to mention that Layer0 (Untagged) cannot appear in a folder. This is a fact.
You can group other Tags (Layers in Ruby) and Tag Folder(s) to folder as you whish.


You can retrieve the parents folders of the layer or layer folder e.g. with this method, by calling it a and pass the the layer of layer folder as parameter:
(You will get an Array of Sketchup::LayerFolder)

def parents(l_f)
  parents = []
  while l_f.folder
   parents<<l_f.folder
   l_f = l_f.folder
  end
  parents
end

E.g. If you have structure like this:
image
Call like this to get the parent layer folders of “Tag”
parents(Sketchup.active_model.layers["Tag"])

or, to stay the example of @3DxJFD, and extend:

active_folder = model.active_layer.folder
active_folders = parents( active_folder )<<active_folder

Then you need to check if this array contains the folder that you want to change visibility to hidden…
__
An other note:

This does not return all the folders in the model, only those that are direct children of the layer manager.

To retrieve all folders in a model into an Array you can use e.g. this method:

def all_folders(folders = [], root_folder = Sketchup.active_model.layers)
  root_folder.each_folder do |folder|
    folders<<folder
    all_folders(folders, folder)
  end
  folders
end

Call it like:
all_folders_in_a_model = all_folders()

Then the toggle_off should be something like this (did not tested :blush: )

def toggle_off
  model = Sketchup.active_model
  active_folder = model.active_layer.folder
  # Edited: I guess you need to include the active_folder in a comparation...
  active_folders = parents( active_folder )<<active_folder
  all_folders_in_a_model = all_folders()
  all_folders_in_a_model.each do |folder|
    folder.visible = false unless active_folders.include?(folder)
  end
end
4 Likes

I’ll need to study this, but thanks for your effort.

For better understanding I tried to add your first suggestions to @3DxJFD’s Ruby.

And played around with it too. Sorry, if I’m ignorant. I can read and write, but I’m not a coder. The problem I guess is that there doesn’t seem to be a definition for folders that covers all of those that -need- to be hidden. Besides I don’t completely understand it :face_with_diagonal_mouth:

What I did notice is that the method active_layer in 3DxJFD’s script isn’t necessary. Only ‘layer’ will do. Which might help for a better understanding of what an active folder is :grin:

    def toggle_off
      model = Sketchup.active_model
      layers = model.layers
      folders = layers.folders
      active_folder = model.layer.folder
      #active_folder = model.active_layer.folder
      #active_folders = parents( folder )<<folder


      folders.each do |a|
        a.visible = false unless a == active_folder
      end
    end

This seems closer.

module Basbva
  module FolderVisibility
    extend self

    VERSION ||= "0.0.5"
    MENU ||= "View"

    # Method to recursively collect all folders
    def all_folders(folders = [], root_folder = Sketchup.active_model.layers)
      root_folder.each_folder do |folder|
        folders << folder
        all_folders(folders, folder)
      end
      folders
    end

    # Method to find the hierarchy path to the active folder
    def find_hierarchy_path(active_folder)
      path = []
      current_folder = active_folder
      while current_folder
        path.unshift(current_folder)
        current_folder = current_folder.folder if current_folder.respond_to?(:folder)
      end
      path
    end

    def toggle_off
      model = Sketchup.active_model
      active_folder = model.active_layer.folder
      hierarchy_path = find_hierarchy_path(active_folder)

      # Gather all folders
      all_folders = all_folders()

      # Hide all folders except those in the hierarchy path to the active folder
      all_folders.each do |folder|
        folder.visible = hierarchy_path.include?(folder)
      end
    end

    def toggle_on
      model = Sketchup.active_model
      layers = model.layers

      layers.folders.each { |folder| folder.visible = true }
    end

    unless defined?(@ui_loaded)
      add_separator_to_menu(MENU)
      menu = UI.menu(MENU).add_submenu("Folder Visibility")
      
      menu.add_item("Hide Folders") { toggle_off() }
      menu.add_item("Show Folders") { toggle_on() }

      @ui_loaded = true
    end
  end
end

1 Like

No.

  1. There is NO documented layer method for Sketchup::Model.
  2. The method you accidentally discovered returns the default layer “Layer0”.
  3. As I mentioned Layer0 (Untagged) cannot appear in a folder. Calling #folder method on it will return nil.

image
The active layer is that layer is that where the pencil is located. (marked with red rectangle)
Therefore the active folder is (are) that folders(s) that containing the active layer or congaing tag folder that containing the active layer…etc. (marked with orange rectangles)

Notes:

  • The “active folder” is an arbitrary given name in this topic, there is no such a things in Ruby API (Neither ins Sketchup)
  • The layer folders can be in several levels.
  • I intentionally left the names of the layer folders the same. You should be aware that users are given the freedom that the layer folder names can be the same. You can have hundreds of layer folders called “Tag Folders” nested together in different ways…
    Despite the common name, this hundreds of folders are different objects in Ruby.
1 Like

It seemed to work fine, when I tried.

Anyway, I’ve got plenty of stuff to study on for now, which is great.

Ahhh, :blush:
Because Sketchup basically doesn’t allow you to hide the active tag, in any way!
You do not need to check, it is done automatically. :innocent:
So your code…

…can be simoplified:

    def toggle_off
      model = Sketchup.active_model
      layers = model.layers
      folders = layers.folders
      folders.each do |a|
        a.visible = false 
    end

This still does not hide all the folders in the model - except folder(s) containing the active tag -, only those that are direct children of the layer manager.

2 Likes

This got me distracted from my work today. Very interesting. Although this hides all but the active folder, I don’t understand what this:

    # Method to find the hierarchy path to the active folder
    def find_hierarchy_path(active_folder)
      path = []
      current_folder = active_folder
      while current_folder
        path.unshift(current_folder)
        current_folder = current_folder.folder if current_folder.respond_to?(:folder)
      end
      path
    end

adds to the functionality of the code. Although it might be of value when you don’t want to hide all but the ‘active’ folders.

Also naming your def ‘Folder’ interferes with the Folder method… Sketchup freezes when you combine both methods in one def. That seems dangerous to me, so I renamed it.

Taking into account Dezmo’s last reply, suggesting to keep things simple, I’ve been editing your last script. Here all active folders are hidden and top level folders shown, just as an example. Now sure whether this is proper coding practice though…

module Basbva
  module FolderVisibility
    extend self

    VERSION ||= "0.0.6"
    MENU ||= "View"

    # Method to recursively collect all folders
    def inactive(inactive = [], root_folder = Sketchup.active_model.layers)
        root_folder.each_folder do |a|
        inactive << a
        inactive(inactive, a)
      end
      inactive
    end

    def toggle_off
      model = Sketchup.active_model
      active = model.layers.folders
      # note that model.layers.folders here only returns the 'active' folder
      
      # Gather child folders
      inactive = inactive()

      # Hide child folders
      inactive.each do |b|
        b.visible = false unless b == active
        end
      model = Sketchup.active_model
      top_level_folders = model.layers.folders
      # note that model.layers.folders here also returns top level folders
      
      top_level_folders.each do |c|
        c.visible = true
        end
      end

    def toggle_on
        inactive = inactive()

        inactive.each do |a|
          a.visible = true
        end
    end

    unless defined?(@ui_loaded)
      add_separator_to_menu(MENU)
      menu = UI.menu(MENU).add_submenu("Folder Visibility")
      
      menu.add_item("Hide Folders") { toggle_off() }
      menu.add_item("Show Folders") { toggle_on() }

      @ui_loaded = true
    end
  end
end

#folders = model.layers.folders
#returns folders that are direct children of the layer manager and of the active layer

I have no idea, why you are naming this method like this. The method recursively collect all folders, and it will return all layer folder in a model independently if it is contains the #active_layer or not.
(The active layer means that all Sketchup::Drawingelement you drawn/placed via UI or by code, will be assigned to that layer.

The default active layer in SketchUp is Layer0, and it is not recommended to change. Unless you know what the active layer is and its purpose. )

No. It will returns top level folders.
(The Model #layers method retrieves a collection of all Layers objects in the model.
The Layers #folders method returns the folders of the layer manager)
Therefore

will never be true, because you are comparing a LayerFolder object to an Array containing Sketchup::LayerFolders (or nil)
However the condition is not need at all, as I mentioned earlier, but since it is always false it’s like it’s not even there, that’s why it works.

You unnecessarily assigned it two times within a method.

Not sure why you want to keep the top level folders always visible, but it is up to you.

Wait Dezmo,

This version was a quick and dirty response with a number of mistakes in it. Because it took me some time figuring this out.

First of all I read that @3DxJFD s script implies there is a root-folder, which is equal to Sketchup.active_model.layers

Your suggestion I forgot to fully implement but if I do this is what you get:

EDIT:

      # Hide inactive folders
      inactive.each do |b|
        b.visible = false
        end

Then you’ll notice the first def in the module doesn’t represent all folders. Both the so called root folder, as the so called active folders are missing in this equation.

You unnecessarily assigned it two times within a method.

Thanks for pointing this out :slightly_smiling_face:

This version is toggling subfolder visibility back on. I disambiguated the method name (all_folders). I’m a flowchart pseudocoder, not a programmer, so my stuff is just suggestive.

module Basbva
  module FolderVisibility
    extend self

    VERSION ||= "0.0.7"
    MENU ||= "View"

    # Method to recursively collect all folders
    def collect_all_folders(folders = [], root_folder = Sketchup.active_model.layers)
      root_folder.each_folder do |folder|
        folders << folder
        all_folders(folders, folder)
      end
      folders
    end

    # Method to find the hierarchy path to the active folder
    def find_hierarchy_path(active_folder)
      path = []
      current_folder = active_folder
      while current_folder
        path.unshift(current_folder)
        current_folder = current_folder.folder if current_folder.respond_to?(:folder)
      end
      path
    end

    def toggle_off
      model = Sketchup.active_model
      active_folder = model.active_layer.folder
      hierarchy_path = find_hierarchy_path(active_folder)

      # Gather all folders
      all_folders = all_folders()

      # Hide all folders except those in the hierarchy path to the active folder
      all_folders.each do |folder|
        folder.visible = hierarchy_path.include?(folder)
      end
    end

    def toggle_on
      model = Sketchup.active_model
    
      # Gather all folders
      all_folders = all_folders()
    
      # Set all folders to visible
      all_folders.each { |folder| folder.visible = true }
    end

    unless defined?(@ui_loaded)
      add_separator_to_menu(MENU)
      menu = UI.menu(MENU).add_submenu("Folder Visibility")
      
      menu.add_item("Hide Folders") { toggle_off() }
      menu.add_item("Show Folders") { toggle_on() }

      @ui_loaded = true
    end
  end
end
1 Like

The great thing about this ‘flowchart pseudocoding’ is that it presents very interesting actions.

EDIT: so who is / are the authors of this script?

I think the question is always: what is the algorithm?

The ‘correct’ pseudocode is always the answer.

The rest is details. ;^)!

Pseudocode
Function collect_all_folders(folders, root_folder)
    For each folder in root_folder
      Add folder to folders
      Recursively call collect_all_folders with current folder
    Return folders

  Function find_hierarchy_path(active_folder)
    Initialize empty path list
    Set current_folder to active_folder
    While current_folder exists
      Add current_folder to the start of the path list
      Set current_folder to its parent folder if it exists
    Return path

  Function toggle_off
    Get the current model
    Get the active folder from the model active layer
    Find hierarchy path of the active folder
    Gather all folders
    For each folder
      Set folder visibility based on its presence in the hierarchy path

  Function toggle_on
    Get the current model
    Gather all folders
    Set all folders to visible
1 Like

SU UI does not allow you to hide the active(current) Tag even by trying to hide by its layer folder:

no_hideacivetag

If you try to do it by code, SU will react in a similar way. (Unfortunately, not that way what I like… more on that, later) Therefore the code can be simplified:

module SUForum
  module FolderVisibility
    extend self
    VERSION ||= "0.0.8"
    MENU ||= "View"

    def all_folders(folders = [], root_folder = Sketchup.active_model.layers)
      root_folder.each_folder do |folder|
        folders << folder
        all_folders(folders, folder)
      end
      folders
    end

    def toggle(state)
      all_folders.each { |f| f.visible = state }
    end

    unless defined?(@ui_loaded)
      add_separator_to_menu(MENU)
      menu = UI.menu(MENU).add_submenu("Folder Visibility")
      menu.add_item("Hide Folders") { toggle(false) }
      menu.add_item("Show Folders") { toggle(true) }
      @ui_loaded = true
    end
  end
end

FolderVisibility
_


_

What I don’t like about running Ruby code on this regards:

If you try to hide the parent folder of the active layer, e.g::
Sketchup.active_model.layers["Tag"].folder.visible = false
It will return false, but silently fail to set visibility to false.
silentfail1
.

Even worse: If you try to hide the active layer, e.g.:
Sketchup.active_model.layers["Tag"].visible = false
that will happen, however Layer0 will be silently set as the active(current) layer !! :angry:
silentset
.

My test done in SU2021 on Windows, need to check on later versions, and it is similar I will report this to SketchUp API issue tracker

1 Like

I’m testing with Su '22. I did see the reset to Default behavior.

You really boiled it down!