Page MenuHome

Make Contextual Menus More Contextual
Needs ReviewPublic

Authored by William Reynish (billreynish) on Thu, Dec 6, 9:15 PM.

Details

Summary

In Blender 2.8 we introduced the concept of contextual menus that depend on the current context. We show different things depending on which Editor is used and what mode we are in, as well as what type of object we have selected.

However, these menus could be a lot more contextually aware still.

For example, many commands only apply when you have a certain number of items selected. It makes no sense to join objects when there is only one selected. It makes no sense to unparent when there is no parent. It makes no sense to convert a mesh to a mesh, and so on. We could make these menus a lot smarter by looking at:

  1. Which Editor is used
  2. Which Mode is engaged
  3. If something is selected at all or not
  4. Which Item or object type is selected
  5. How many of them are selected
  6. Extra info, such as relationships (is item parented or not, or is item in a group or not)

Also, I think we can make the contextual manus a lot more useful, simply by including more commands here. When modeling, it should be possible to find basically all the main modeling commands using this menu system, as a more contextual alternative from using the header menus.

Here I've put together a patch that starts to add some more contextual logic to these menus.

What this does is:

  • If no objects are selected, display different options (Add, Paste etc)
  • More contextual items depending on the object selected
  • In Node Editor display different items if nothing is selected, something is selected, or multiple nodes are selected (Group, Connect etc)
  • In Edit Mode, added more contextual items
  • In Edit Mode, make some items only appear under correct circumstances, such as:
    • Bridge Faces only appears if you have 2 or more faces selected
    • Merge, Connect Vertices and others only appears if more than 1 element is selected

This is just a beginning. This kind of contextual awareness should also be added to the UV Editor and in the other edit modes.

I'm submitting this patch mainly to get feedback if this is the right way to do it technically.

Diff Detail

Event Timeline

In general, am not against this, noting some issues though.

  • Limiting 'Add' or 'Reveal' to only show when there is no selection seems a bit odd.

    There might even be good reasons to keep the existing selection, add new items - then group them for eg (nodes or objects).
  • Current code checks selection count, while this seems reasonable. Would we consider it an *improvement* to do more comprehensive checks?
    • 'triangulate' could check if all faces are already triangles.
    • 'clear parent' could check if any objects have parents.
    • 'set origin' could check if there is any geometry selected and hide menu items which depend on geometry.
    • Many operators could be hidden if for example the mesh has no faces or no edges.

      The issue with this is, there is a limit to what we should reasonably check for when when showing a menu. Even if it's just the limit of Python to be able to detect these cases on heavy scenes.

      If we wanted we could have a different kind of poll function where operators can perform more extensive checks, but this seems like it could be over-kill.
  • Items showing in menus should not seem 'random' from a user perspective.

    When a user follows a tutorial for example, and a menu item referenced in the tutorial doesn't show up - it could be very confusing. In some cases it could be better to show the menu item, then give a message about why the tool doesn't work (especially if the reason isn't straightforward ~ needs custom-normals layer, needs at least one other mesh object selected...).

    Keeping this checks to vert/edge/face selection seem safe, just noting that more comprehensive checks might seem clever, but end up being confusing from users perspective since they wont get any hints to why the operation is missing.
  • A down side to having items come and go too much in a menu is the key accelerators change.

    If checks are too fine grained, activating the menu might show a set of options that seem unfamiliar (making access require more thinking each time).
release/scripts/startup/bl_ui/space_node.py
331

Best assign the length to a variable, then access again later (saves creating and throwing away the information multiple times).

Same goes for objects too.

release/scripts/startup/bl_ui/space_view3d.py
2808–2811

This will get slow on high poly meshes, we could have some C method to access this information.

2822

It's fairly common (even useful) to un-hide when you have an existing selection.

Since you might want to act on a mix of existing selection + revealed.

2826

This could check for edges/faces, since selected verts cant be subdivided.

Note, am not sure if we should be so spesific, just noting it as possibility.

2927–2929

Bridge works for isolated face regions too.

Detecting supported cases is likely too intensive, see: https://docs.blender.org/manual/en/latest/modeling/meshes/editing/edges.html?highlight=bridge#examples

In general, am not against this, noting some issues though.

  • Limiting 'Add' or 'Reveal' to only show when there is no selection seems a bit odd.

Yes, we could add more here - it looks a bit bare with just the two, I agree.

There might even be good reasons to keep the existing selection, add new items - then group them for eg (nodes or objects).
  • Current code checks selection count, while this seems reasonable. Would we consider it an *improvement* to do more comprehensive checks?
    • 'triangulate' could check if all faces are already triangles.
    • 'clear parent' could check if any objects have parents.
    • 'set origin' could check if there is any geometry selected and hide menu items which depend on geometry.
    • Many operators could be hidden if for example the mesh has no faces or no edges.

      The issue with this is, there is a limit to what we should reasonably check for when when showing a menu. Even if it's just the limit of Python to be able to detect these cases on heavy scenes.

Agreed - there is probably a limit to how far we can reasonably go in terms of cleverness.

Mainly I think the big thing to improve is better context in relation to selections. In Curve Edit Mode, you can only create segments from curve endpoints. In Mesh Edit Mode, you can only bridge faces if two are selected. Those kinds of things would be nice to add.

But, yes, checking for the set origin thing and probably the triangulated faces thing is probably not necessary.

If we wanted we could have a different kind of `poll` function where operators can perform more extensive checks, but this seems like it could be over-kill.
  • Items showing in menus should not seem 'random' from a user perspective.

    When a user follows a tutorial for example, and a menu item referenced in the tutorial doesn't show up - it could be very confusing. In some cases it could be better to show the menu item, then give a message about why the tool doesn't work (especially if the reason isn't straightforward ~ needs custom-normals layer, needs at least one other mesh object selected...).

    Keeping this checks to vert/edge/face selection seem safe, just noting that more comprehensive checks might seem clever, but end up being confusing from users perspective since they wont get any hints to why the operation is missing.

Yes, so the way I think about this, is that this is the contextual menu. It's already quite contextual. We have alternative menus, such as Ctrl-V, Ctrl-E, Ctrl-F which spawn the vertex, edge and face menus with a static list of commands that don't change.

The role of the contextual menu is different - it's to be as contextually intelligent as reasonably possible.

Re. your code comments, I obviously agree because by default I defer to you completely in this area.

Hi. While this might be good, this is also very dangerous for the discoverability of features. I'd rather have the commands there either greyed out or giving a warning when they can't be used. IMHO.

Updated the diff ontop of 2.8 branch, also avoid indenting large blocks of code - causes conflicts and is hard to review.

Assign item length to variables

Also correct use of unassigned 'with_freestyle'

Looks good to me.

After discussing this with Julien Kaspar, I think we can add a few more commands still, so that all the main mesh modeling commands are available. I can easily do this.

@Campbell Barton (campbellbarton) question:

How can we add these kinds of checks to Curve Edit Mode and UV Editor? If you can make a quick template or example, I can fill it out more.

This should check all edit-mesh objects, not just the active one.

I like the idea a lot.
But couldnt we rely on operators poll() more here?
I guess this is what you meant by saying

If we wanted we could have a different kind of poll function where operators can perform more extensive checks, but this seems like it could be over-kill

but I am not sure why this would be a differnt kind of poll? Couldnt all these checks be done in that one existing poll?

If I understand correctly poll() is executed anyways [greying menu entries out when failing], so instead of doing decoupled checks in the menus themselves, should we pay more attention to putting these checks into the poll() of the corresponding operators?

  • we could then still decide if a failing poll would only grey out the menu entry or remove it entirely [this could be a user preference even]
  • downside: if there are performance costly checks, these would be executed multiple times [e.g. a checking verts_len would be done for both mesh.merge and mesh.remove_doubles in their poll (instead of just once in the menu code)]
  • upside: most operators polls are C [so more performant than py I guess]
  • upside: there is only one place in the code that is responsible for decission of whether or not this operator makes sense to be executed
  • downside: could lead to a lot of duplicated code in many operators poll functions (but I guess utility functions could be written for all operators to use)
  • upside: the operator search popup would also respect that [looks like this filters out operators for that poll fails?]

+1 for putting more operators in the [context] menus btw.

@Philipp Oeser (lichtwerk): Forgive me if I'm wrong, but isn't it much less efficient to perform the check for each individual menu item, as opposed to checking once for a section of commands in the menu?

@William Reynish (billreynish) : yes, thats what I was also worried about

downside: if there are performance costly checks, these would be executed multiple times [e.g. a checking verts_len would be done for both mesh.merge and mesh.remove_doubles in their poll (instead of just once in the menu code)]

I could do a quick check and see how it goes for e.g. checking how many vertices are selected say for 10-15 operators on a fairly highpoly mesh?

Yes I suppose that would be very useful, although perhaps @Campbell Barton (campbellbarton) would already know the relative difference?

In any case, such a test wouldn’t hurt.

Also could use CTX_wm_operator_poll_msg_set to our advantage here (so lets say when we just grey out the menu entries, we could add this hint to the tooltip, explaining why the menu entry is greyed out...)

@Philipp Oeser (lichtwerk): Well, for these contextual menus, we should show/hide things and not just grey out. Otherwise the menu may get 200 items long, and besides, the role of this is to be contextual, not to show everything.

Using operator poll function vs a poll in the UI: I have no preference, although the operator poll functions requires me to do it in C which I can't do :) But if someone else was to help with that part I also don't mind - my only goal with this is to make it so that the contextual menus are more contextual.

I love you guys.
I believe the level of the work has reached this level:

I could do a quick check and see how it goes for e.g. checking how many vertices are selected say for 10-15 operators on a fairly highpoly mesh?

It is too slow, as a rule we do not do such expensive checks in poll functions. For example the search operator runs thousands of poll functions, and any toolbar with these operators would run it on every UI redraw.

It's possible to make these fast if information about number of selected elements is stored in the edit mesh data structure, that might be some work to track though.

just wanted to point out that converting mesh to mesh is in fact really useful as a way to apply multiple modifiers... btw convert mesh to curve will only work if you have some edges with no faces connected, should you check that too? but it's a great move anyway, I'm all for it

It is too slow, as a rule we do not do such expensive checks in poll functions. For example the search operator runs thousands of poll functions, and any toolbar with these operators would run it on every UI redraw.

oki, thx for clarifying

I could do a quick check and see how it goes for e.g. checking how many vertices are selected say for 10-15 operators on a fairly highpoly mesh?

It is too slow, as a rule we do not do such expensive checks in poll functions. For example the search operator runs thousands of poll functions, and any toolbar with these operators would run it on every UI redraw.

It's possible to make these fast if information about number of selected elements is stored in the edit mesh data structure, that might be some work to track though.

Even in this case, you need to loop over all edit-meshes for every poll call.
We could do it by storing selection count information (the kind of information thats already in the status bar), but this complicates operator execution since we'd need to keep this valid between operator calls.

For a operators that call other operators, macro or Python scripts for eg, we never know which poll will need to check selection, so we would need to keep it valid, or invalidate it and lazy initialize it when the poll function requests it via an API call.

Even though all this is possible, there are further complications...

Operator macros for eg, check if the operators poll functions succeed, only running if all of them do. If a single operator is a nop. it's harmless at the moment, if poll functions are inspecting data, it could cause annoying corner cases where macros fail.
Worse, a macro may fail because the initial operators haven't yet been run to setup a valid state which other operators test.

If a Python script calls remove-doubles, an exception will be raised (if the only have one vertex selected or similar kinds of cases).
This makes script authors need to be aware of the situations where an operator might do nothing, and check for it in their code.

Since the use case for optionally showing menu items is limited to context menus, I'd rather limit it to checking in the menus, or have a separate kind of poll function.

thanx again for detailed explanantion

Updated the patch with feedback from users, including Julien Kaspar:

  • Only show Mark/Clear Freestyle Edge if Freestyle is enabled
  • Added more modelling tools. Now basically all main mesh modeling tools are available in the contextual menus, contextual to vert, edge or face mode
  • Added more items in the Outliner contextual menus (Copy, Paste, Duplicate)