Outliner GSoC Follow-up Feedback

Not only would the preferences become cluttered, but the number of possible test cases and bugs increase exponentially the more options there are.

If you have 2 options, there are 4 possible situations that you have support. If you have 8 options, 256 situations to support, etc. Obviously it’s not that simple, but you have to consider that there is a real cost to adding preferences that isn’t always visible at first.

(Personally I hope we can support shortcuts like shift-D in the outliner eventually too)

3 Likes

Some small things I’ve noticed. On a DK keyboard doesn’t the Del key for delete work and when having more entries than the height and deleting everything but one, it doesn’t scroll up to show the entry still left, so it is hidden. It is a bit tedious to use the menu for accessing ex. hide one level - maybe some View settings could be added to the header(like the File Browser)?

Duplicate in the outliner should be performed with a modifier key (shift or ctrl) + click and drag…
That’s pretty much the standard way of doing this, also very intuitive…

What can be more intuitive than this? :wink:

10 Likes

Found this behavior with ALT holding, I hope you know about it, but I leave it here.
ALT work only for hide in viewport and it can’t switch back

I hope Alt+D (for instancing) will also be possible

2 Likes

I really enjoy this new behavior. It makes it easier to access properties using the Outliner. The change of the icons under the hover state makes it really easy to learn the new concept and I didn’t have issues with miss clicks yet. I haven’t tested it much yet but this new behavior works really well for me. It feels like it makes me more efficient in Blender.

1 Like

I have an idea on clicking collection Icon.

For now it do nothing, but what if clicking on it will turn collection visibility on off?
With this we can get rid of collection checkbox icon col_ch
Dragging by name only and by click and hold on icon and move up/down do the same if click on col_ch and move up/down with multiple collections in column.

I would like to test this feature with custom blender build if some one can do this.

Or you have other plans for collection icon?

In fact, that is a consequence of using Alt for multi-object editing.
Visibility in one viewport is property of Viewlayer.
Visibility, Renderability or Selectability in all viewports are properties of Object.
They are affected by multi-object editing using alt key.

Problem is that selection driving that behavior is selection in Viewport.
So, if you hide objects in viewport, you deselect them. As a consequence, multi-object editing does not work anymore in outliner.
Internal rules should change. Selection in Outliner should become the reference.
Outliner should drive not just Properties Editors, but also Animation Editors.

I have an idea on clicking collection Icon.

For now it do nothing, but what if clicking on it will turn collection visibility on off?
With this we can get rid of collection checkbox icon

That is not true. It does not do nothing.
Collection icon is used to indicate active collection.
Active collection is the one where you add objects using Add menu in viewport.
By default, in default blend, that is the collection named Collection.
If you click on Scene collection icon, when you add an object : it is added outside of collection Collection.
And if you create more collections, you can add directly objects to them or nest collections by clicking on collection icon.

It is also used for color tagging collections.

Icon for collection visibility is same icon than the same toggle as for object visibility.
Icon for collection selectability is same icon than the same toggle as for object selectability.
Icon for collection renderability is same icon than the same toggle as for object renderability.
Etc…
If you shift click on it, you change the state for all objects inside collection.
If you ctrl click on it, you change the state for all other collections.

The checkbox is not about visibility. It is about evaluation of collection.
When it is deactivated, objects inside of it are not just hidden. They are deactivated of all depsgraph operations like they did not exist inside blend file. But that effect is restricted to View Layer.

It is frequent for physics to set a collection of invisible obstacles.
Smoke, particles, cloth can collide with those invisible obstacles if you just disable collection visibility / renderability.
If you deactivate checkbox, they are no more invisible obstacles. They are no more obstacles.
Smoke, particles, cloth are passing through those colliders like if they do not exist.

But it do the same if you operate(click) with Collection Name instead of icon.
Or I miss something?

I meant this icon col_icon.
By clicking on it, it do nothing, the same if we click on name

You are proposing to limit designation of active collection to clicking on name of collection.
Sorry, I missed that in your post.

People can change shortcut to rename collection to a single click instead of F2 or double click and use only icon for the rest.
Or they may like clicking on icon more than on name for drag and drop actions related to collection.
If that is the case, that could be problematic to set something as important as deactivation of collection to a single click.
You could often deactivate a collection by mistake although your intention was just to move it.
I can understand preference for moving checkbox icon next to the name to quickly activate/deactivate them.
That was the case in early state of GSOC.
But I like current state because it improves readability of collection lines by breaking the flow of toggles.
If you remove the checkbox icon, those lines are less noticeable.

2 Likes

That make sense. Let’s drop that idea)

1 Like

I don’t like the idea. One icon less might be a good thing, but I have doubts concerning discoverability and consistency: why clicking on an item (that usually is perceived as a selection action) should turn into an enable/disable operator? Not so logic imho

1 Like

:heart:


I failed to mention this a couple weeks ago, but the popover for controlling properties sync is in current builds of 2.92

image

Thanks for the ideas and feedback everyone! Remember that this thread is for feedback on the changes made in 2.91 and 2.92, and any feature requests should go somewhere else like https://blender.community/c/rightclickselect/

5 Likes

outliner-wtf

Same goes if I just select the nodes in the tree.

I don’t know why it’s not implemented yet.
Maybe because performance issue but it can be easily done with simple script
(even without area.type = 'OUTLINER')

1 Like

Can someone test it. Should I report this as a bug?
Short description to reproduce:

  1. create new file
  2. select cube and add one more material
  3. expand cube in outliner to the materials list
  4. minimize cube list
  5. then click on any other green icon of any object
  6. cube become highlights only by hovering on object icon, and if click on cube name you get material list popup
    Only in 2.91.0.
    2.90 and 2.92 is ok, so no worry about it…
1 Like

Hi @jamez, I am replying here to keep related discussions together.

The custom object sorting worked in most cases during GSoC, and it would be technically possible to commit it. However, we decided to delay it with no timeline at the moment. We would prefer to do sorting on a UI level, rather than on a data level. The collections and modifiers you mention are a good example of the difference.

Modifiers are sorted on a data level, meaning that reordering the modifiers in the outliner actually rearranges the list in Blender’s internal database. This is good because the order matters during modifier evaluation. In the current implementation, collections are also reordered in the internal database lists, but we would prefer not to do this, primarily because there isn’t any reason to.

The preferred solution would be to custom sort objects, collections, (and other data) on an interface level. This means that the internal databases are untouched. The downside is this (UI level sorting) would be a new concept in Blender so it’s a bigger project. I would eventually like to work on this, but at the moment I don’t have the time.

Another problem with the GSoC implementation was it would be very difficult to do custom sorting of bones and object children in the outliner by actually reordering the internal lists.

6 Likes

@natecraddock thanks a lot for the detailed reply, it is appreciated! I really hope that the Blender Foundation can hire you in some capacity to finish the great work you did on the outliner. Maybe you are also planning on doing the GSOC again this year for further UI development ?

2 Likes

With new changes to 2.92 it’s possible now have a list what we selected in Outliner (can be multiple Collections selected also)
https://wiki.blender.org/wiki/Reference/Release_Notes/2.92/Python_API
and with python I was able to add new entries for Outliner context for Objects and for Collections selected (with my basic knowledge) such as:
For objects:

  • Duplicate’ for multiple selected objects
  • Duplicate Hierarchy’ for multiple selected objects without selecting parented elements
  • Duplicate Instance’ for multiple selected objects
  • ’Duplicate Instance Hierarchy’ for multiple selected objects without selecting parented elements
  • Select Hierarchy Multi’ for multiple selected objects
    (for this no needs in new ‘selected_ids’ btw)

For collections (possibile with new ‘selected_ids’ feature):

  • Select Objects’ for multiple selected collections
  • ’Duplicate Collections’ for multiple selected collections (thanks for people from blender.stackexchange)
  • Duplicate Linked’ for multiple selected collections

Maybe we can expect some rewriting for Outliner context in 2.93 now?

My little investigation in the form of addon for Outliner if someone want to test it and maybe improve it, you are welcome

Code here, since I can't upload .py file
# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

#https://blender.stackexchange.com/questions/64951/add-menu-entry-to-generic-right-click-menu
#https://stackoverflow.com/questions/27265915/get-list-of-selected-objects-as-string-blender-python
#https://blender.stackexchange.com/questions/132825/python-selecting-object-by-name-in-2-8/132829
#https://blenderartists.org/t/python-scripted-button-select-hierarchy-parent-and-children-at-same-time/1132109/4

# Add-on info
bl_info = {
    "name": "Outliner Menu Entries",
    "author": "APEC",
    "version": (0, 1, 0),
    "blender": (2, 92, 0),
    "location": "Outliner > Objects RMB or Collections RMB",
    "description": "Adds menu entries for objects and collections in Outliner", 
    "doc_url": "",
    "tracker_url": "",      
    "category": "Outliner"
}

import bpy
from bpy.types import Operator
from collections import  defaultdict

###########################################################################################
################################### Functions #############################################
###########################################################################################

class OUTLINER_OT_duplicate(Operator):    
    bl_idname = "outliner.duplicate"
    bl_label = "Duplicate"
    bl_description = '''Duplicate selected objects'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):
        bpy.ops.object.mode_set(mode = 'OBJECT')    
        bpy.ops.object.duplicate()
        #dupli_obj = bpy.context.object
                
        return { 'FINISHED' }   

class OUTLINER_OT_duplicate_hierarchy(Operator):    
    bl_idname = "outliner.duplicate_hierarchy"
    bl_label = "Duplicate Hierarchy"
    bl_description = '''Duplicate selected objects with respecting the hierarchy'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):   
        bpy.ops.object.mode_set(mode = 'OBJECT')

        selection_names = [obj.name for obj in bpy.context.selected_objects]

        for o in selection_names:
            obj = bpy.context.scene.objects.get(o)
            bpy.context.view_layer.objects.active = obj
            obj.select_set(True)
            bpy.ops.object.select_grouped(extend=True, type='CHILDREN_RECURSIVE')

        bpy.ops.object.duplicate()
        
        return { 'FINISHED' } 

class OUTLINER_OT_duplicate_instance(Operator):    
    bl_idname = "outliner.duplicate_instance"
    bl_label = "Duplicate Instance"
    bl_description = '''Duplicate selected objects with instances'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):   
        bpy.ops.object.mode_set(mode = 'OBJECT')
        bpy.ops.object.duplicate(linked=True)
        #dupli_obj = bpy.context.object
                
        return { 'FINISHED' } 

class OUTLINER_OT_duplicate_instance_hierarchy(Operator):    
    bl_idname = "outliner.duplicate_instance_hierarchy"
    bl_label = "Duplicate Instance Hierarchy"
    bl_description = '''Duplicate selected objects with instances respecting the hierarchy'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):
        bpy.ops.object.mode_set(mode = 'OBJECT')

        selection_names = [obj.name for obj in bpy.context.selected_objects]

        for o in selection_names:
            obj = bpy.context.scene.objects.get(o)
            bpy.context.view_layer.objects.active = obj
            obj.select_set(True)
            bpy.ops.object.select_grouped(extend=True, type='CHILDREN_RECURSIVE')

        bpy.ops.object.duplicate(linked=True)

        return { 'FINISHED' } 

class OUTLINER_OT_select_hierarchy(Operator):    
    bl_idname = "outliner.select_hierarchy"
    bl_label = "Select Hierarchy Multi"
    bl_description = '''Selects hierarchy and parent for multiple objects'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):   
        bpy.ops.object.mode_set(mode = 'OBJECT')

        selection_names = [obj.name for obj in bpy.context.selected_objects]

        for o in selection_names:
            obj = bpy.context.scene.objects.get(o)
            bpy.context.view_layer.objects.active = obj
            obj.select_set(True)
            bpy.ops.object.select_grouped(extend=True, type='CHILDREN_RECURSIVE')
    
        return { 'FINISHED' } 

#https://blenderartists.org/t/how-to-select-all-objects-of-a-known-collection-with-python/1195742/3
class OUTLINER_OT_select_collection_objects(Operator):    
    bl_idname = "outliner.select_collection_objects"
    bl_label = "Select Objects Multi"
    bl_description = '''Selects all objects for multiple collections'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context): 

        collection_names = [col.name for col in bpy.context.selected_ids]

        # use only collections whose names are in collection_names
        collections = [col for col in bpy.data.collections if col.name in collection_names]

        for col in collections:
            for obj in col.all_objects:
                obj.select_set(True)
    
        return { 'FINISHED' } 


def copy_objects(from_col, to_col, linked, dupe_lut):
    for o in from_col.objects:
        dupe = o.copy()
        if not linked and o.data:
            dupe.data = dupe.data.copy()
        to_col.objects.link(dupe)
        dupe_lut[o] = dupe

def copy(parent, collection, linked=False):
    dupe_lut = defaultdict(lambda : None)
    def _copy(parent, collection, linked=False):
        cc = bpy.data.collections.new(collection.name)
        copy_objects(collection, cc, linked, dupe_lut)

        for c in collection.children:
            _copy(cc, c, linked)

        parent.children.link(cc)
    
    _copy(parent, collection, linked)
    print(dupe_lut)
    for o, dupe in tuple(dupe_lut.items()):
        parent = dupe_lut[o.parent]
        if parent:
            dupe.parent = parent

#https://blender.stackexchange.com/questions/157828/python-collection-duplicate-help
class OUTLINER_OT_duplicate_collections(Operator):    
    bl_idname = "outliner.duplicate_collections"
    bl_label = "Duplicate Collection Multi"
    bl_description = '''Duplicate multiple selected collections'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context): 

        context = bpy.context
        scene = context.scene

        collection_names = [col.name for col in bpy.context.selected_ids]

        for col_name in collection_names:
            col = bpy.data.collections.get(col_name)
            print(col, scene.collection)
            assert(col is not scene.collection)
            parent_col = context.scene.collection

            copy(scene.collection, col)
            # and linked copy
            #copy(scene.collection, col, linked=True)
                        
        return { 'FINISHED' } 

class OUTLINER_OT_duplicate_collections_linked(Operator):    
    bl_idname = "outliner.duplicate_collections_linked"
    bl_label = "Duplicate Linked Multi"
    bl_description = '''Duplicate multiple selected collections with linked object data'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context): 

        context = bpy.context
        scene = context.scene

        collection_names = [col.name for col in bpy.context.selected_ids]

        for col_name in collection_names:
            col = bpy.data.collections.get(col_name)
            print(col, scene.collection)
            assert(col is not scene.collection)
            parent_col = context.scene.collection

            copy(scene.collection, col, linked=True)
                        
        return { 'FINISHED' }         
###########################################################################################
#####################################    UI    ############################################
########################################################################################### 

def duplicate_menu_outliner(self, context):
    layout = self.layout
    layout.separator()
    layout.operator(OUTLINER_OT_duplicate.bl_idname)
    layout.operator(OUTLINER_OT_duplicate_hierarchy.bl_idname)
    layout.separator()
    layout.operator(OUTLINER_OT_duplicate_instance.bl_idname)
    layout.operator(OUTLINER_OT_duplicate_instance_hierarchy.bl_idname)
    layout.separator()
    layout.operator(OUTLINER_OT_select_hierarchy.bl_idname)

def select_colection_obects_menu_outliner(self, context):
    layout = self.layout
    layout.separator()
    layout.operator(OUTLINER_OT_duplicate_collections.bl_idname)
    layout.operator(OUTLINER_OT_duplicate_collections_linked.bl_idname)
    layout.separator()
    layout.operator(OUTLINER_OT_select_collection_objects.bl_idname)
   
###########################################################################################
##################################### Register ############################################
########################################################################################### 	

def register():
    bpy.utils.register_class(OUTLINER_OT_duplicate)
    bpy.utils.register_class(OUTLINER_OT_duplicate_hierarchy)
    bpy.utils.register_class(OUTLINER_OT_duplicate_instance)
    bpy.utils.register_class(OUTLINER_OT_duplicate_instance_hierarchy)
    bpy.utils.register_class(OUTLINER_OT_select_hierarchy)
    bpy.types.OUTLINER_MT_object.append(duplicate_menu_outliner)
    
    bpy.utils.register_class(OUTLINER_OT_select_collection_objects)
    bpy.utils.register_class(OUTLINER_OT_duplicate_collections)
    bpy.utils.register_class(OUTLINER_OT_duplicate_collections_linked)
    bpy.types.OUTLINER_MT_collection.append(select_colection_obects_menu_outliner)
    
def unregister():
    bpy.utils.unregister_class(OUTLINER_OT_duplicate)
    bpy.utils.unregister_class(OUTLINER_OT_duplicate_hierarchy)
    bpy.utils.unregister_class(OUTLINER_OT_duplicate_instance)
    bpy.utils.unregister_class(OUTLINER_OT_duplicate_instance_hierarchy)
    bpy.utils.unregister_class(OUTLINER_OT_select_hierarchy)
    bpy.types.OUTLINER_MT_object.remove(duplicate_menu_outliner)
    
    bpy.utils.unregister_class(OUTLINER_OT_select_collection_objects)
    bpy.utils.unregister_class(OUTLINER_OT_duplicate_collections)
    bpy.utils.unregister_class(OUTLINER_OT_duplicate_collections_linked)
    bpy.types.OUTLINER_MT_collection.remove(select_colection_obects_menu_outliner)
    
if __name__ == "__main__":
    register()
7 Likes

Have anyone real examples where no need sync selected (highlighted) objects in Outliner?

I’m confused because if I have multiple Outliners and select something in 3D View it automatically highlights in multiple Outliners identically,
but if I select objects in one Outliner area, then in other, it leaves highlighted but my current selection is chenged in last selected Outliner.
Example:


It also affect selecting in Outliner in different workspaces.