UI: Gray out Scene Auto-Masking option if equivalent Brush option is used #102971 #106126
No reviewers
Labels
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset System
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Code Documentation
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Viewport & EEVEE
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Asset Browser Project
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Module
Viewport & EEVEE
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Severity
High
Severity
Low
Severity
Normal
Severity
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
7 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: blender/blender#106126
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "Henry-Chang/blender:gray-out-automasking"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Hi, @JosephEagar and @JulienKaspar.
Completed Part:
Graying out Scene Auto-Masking if equivalent Brush option is straightforward.
The Following Matching Criteria is applied (Scene option to Brush option that would cause Gray out):
Topology to Topology
Face Sets to Face Sets
Mesh Boundary to Mesh Boundary
Face Sets Boundary to Face Sets Boundary
Propagation Steps to both Mesh Boundary and Face Sets Boundary
Cavity, Cavity (Inverted), Create Mask, Factor, Blur, Custom Curve to either Cavity or Cavity (Inverted)
View Normal, Occlusion, Limit, Falloff to View Normal
Area Normal, Limit, Falloff to Area Normal
WIP Part:
Showing Disabled Message was less straightforward. The current implementation does indeed work as shown in the attached picture, but relies on hard-coding and is hacky. Pointers that would lead to a more elegant implementation would be appreciated.
There were two original methods to display Disabled messages:
1, Poll Function return message from operators. However, most of the automasking buttons are properties, not operators, and even for the one operator, the poll method would not fail, as there is nothing preventing its successful completion.
2, but->disabled_info. However, there isn't an obvious way to set this disabled_info from Panel class.
Thank you.
On the surface this looks great! The only issue is that once a toggle is grayed out it cannot be toggled anymore. That should ideally still be possible.
For the technical UI implementation I'd like to get another opinion on this.
Maybe @HooglyBoogly @pablovazquez or @Harley could give some advice?
Nice one!
To solve that @Henry-Chang should use
.active
instead of.enabled
Dear @JulienKaspar, @pablovazquez, thank you for the recommendations. The code is updated as suggested. Thank you.
Apologies in advance in this answer is a bit incomplete.
Indeed the way to disable operators is with "poll" functions, and within those we can call
CTX_wm_operator_poll_msg_set
to add information about why it is disabled.For properties there is a separate mechanism. Where the properties are defined, for example in this case
source\blender\makesrna\intern\rna_sculpt_paint.c
, we can add a function that sets the enabled state withRNA_def_property_editable_func
.Most existing uses of that do not set a text description, but here is a great example:
In
blender\source\blender\makesrna\intern\rna_space.c
you will find a section of code like this:If you then look for "rna_FileSelectParams_filename_editable" in that same file you will see something like this:
So returning 0 if disabled, PROP_EDITABLE (1) if not. And giving that text description on why.
A very important thing to mention is that in the above case the "ptr->data" is pointing to data that is something useful in this context: the FileSelectParams. In your case, it would be different but also useful. You'd do
Sculpt *sd = (Sculpt *)ptr->data;
Note that from there you can get to both the automasking_flags in there and also the ->paint->brush->automasking_flags so easy to compare them.But... I think you will find that a reason why it might be difficult to do in your particular circumstance is because all these properties you are playing with are defined together in a loop (just search for "rna_enum_brush_automasking_flag_items") since they share the same getters, setters, etc. Fairly certainly you couldn't just define a single "rna_FileSelectParams_filename_editable" for all of them because you might not know which one in particular it is being called for. So you might have to break these up into separate definitions? Or do something special for your needs in a similar way that "BRUSH_AUTOMASKING_CAVITY_NORMAL" gets its own "set" function.
Hopefully the above all sounds interesting and not daunting and discouraging. But we can't really do the "interface_region_tooltip.cc" hack proposed here for the disabled message.
Don't hesitate to ask if you need more information.
Dear @Harley, thank you so much for the detailed reply. No, it does not sound discouraging at all; in fact rather interesting and exciting. I am very glad the hack cannot be accepted, as I am learning more about Blender this way than I would have.
Following along with your post, I was able to quickly set Disabled messages the right way. And it was great.
However, 3 unexpected problems expressed themselves:
1, For the loop part you mentioned, I used an if else check, and a separate function for each check. It doesn't seem ideal but I looked into the StructRNA struct in the PointerRNA struct, and there doesn't seem to be any useful info that could help with this situation.
2, The Scene panel for Automasking at the top and the Brush panel in the side tool menu generate their own UIs. However, there are a few things that are shared between them, namely the Propagation Steps scroll bar, Create Mask button, Limit and Falloff scroll bars of View Normal and Area Normal. By shared between them, I mean they access the same operator or PRNA in the back end. An obvious solution would be to do like what has been done for the situation in 1, and create separate items for Brush and Scene, but I am hoping that wouldn't be necessary.
3, Even if using custom functions, operators, PRNAs for 1 and 2, this is only solving it for the .enabled setting. The .active setting seems to be much more complicated. In the function assigned to prop->editable, if one does not return 0, rna_property_editable_do would return true, which means the but->disabled_info wouldn't be set. I wanted to create an inactive attribute under PropertyRNA, and make a duplicate of RNA_def_property_editable_func, UI_but_disable, rna_property_editable_do, and RNA_property_editable_indo for the inactive attribute. But this seems to entail some non-minor changes to the RNA module, so I wanted to confirm if this makes sense.
Thank you so much.
Again, sorry if I only manage to answer parts of your questions or get some bits wrong. But I'm hoping to help push you along further and perhaps look more closely later.
No, if it gets too messy you should probably just unroll that "do" loop, defining each Boolean property separately. It might seem like you would be adding complexity but you'd be removing some. And there's only seven so no big deal.
I probably don't understand, but if they are sharing the same operators or properties then you don't have the same problem as with automasking_flags. Couldn't you just allow their use in both places as that would be harmless? If you want the two areas to do these things separately and have distinct settings then you'd have to add new fields and copy operators, etc. But feels like a separate issue best deal with separately?
If I am reading you right then you are wanting to not only not allow those items to be changed but you want to make them show the other value? So if the brush as something turned on you want to show it checked but greyed out? That sounds like you just want to add a custom "getter". That area of code has some uses of
RNA_def_property_boolean_funcs
that set a custom "setter". You just want to add one that would define something for the second argument of that function. As with the "editable" you would get passed a pointer to the Sculpt object and from there you can get to the Brush to get its settings. You can have the getter function return whatever Boolean value you want.Dear @Harley, thank you for your clarification on the 1st issue. Sorry that I wasn't clear about the details, and @JulienKaspar please correct me if I am mistaken.
As I understand it, the task is about graying out the toggle, scroll bar, and button of the Scene Auto-Masking options when the corresponding toggles in the Brush panel is selected.
i.e. If Area Normal toggle is selected in Brush panel, in the Scene Panel, the Area Normal toggle and its corresponding Limit and Falloff scroll bar should be grayed out, but still editable. So, essentially, editable but grayed out. This can be done in the front end, where one can set the .active attribute instead of .enabled attribute in the Python script.
As such, I think the task essentially boils down into two parts.
1, In the Python script: Graying out the corresponding parts of the panel in the Python script but still allow them to be maneuverable. This is achieved by setting the .active attribute and can be done cleanly on the Scene Panel without affecting the Brush Panel.
2, Adding the Disabled message in the backend to items that are grayed out in the Scene Panel (though still editable). This I imagine would be achieved by calling a callback function similar to prop->editable, but only setting the r_info message and not affecting the editable state (still want them to editable). Such a callback function (maybe InactiveFunc inactive instead of EditableFunc editable), which I don't think yet exists, would solve both Problem 2 and 3.
Thank you very much.
You are welcome and no worries at all. Note that I was mostly giving you quite general advice and haven't looked much at your issue in particular. I don't sculpt and so haven't really thought through exactly what you are trying to do; I was mostly just telling how our code works.
Yes, it makes some sense to me that we might want to disable, grey-out, and/or warn users when we have conflicting options like the scene and brush Auto-Masking.
I think it would be unusual though to make options look disabled yet still editable and also give a warning message. But this could be a unique situation and Julien Kaspar is the best person to confirm this for you.
Off the top of my head I can't think of way of showing a "disabled" explanatory message for items that aren't actually disabled. Maybe you could show a message on the panel above those options (space_view3d.py) only while in this condition? So you grey them out by changing active and also show above them "The following items currently have conflicting brush settings" or similar (or better or nicer).
@Henry-Chang Sorry for the late response. I wasn't working over the course of March.
You got it right.
Warning messages in this way are not common but a welcome addition IMO, because it's not clear why the options are grayed out.
Usually it's due to a main toggle being disabled right next to it (like proportional editing in the header) or the context is made clear in the tooltip (grid overlays).
But in this case the cause is somewhere completely different.
An easier alternative could be to just write it plain in the tooltips instead of making it a contextual warning.
But @Henry-Chang if you can spend the time to implement the contextual warning, I'd like to propose this to the UI module as a general addition to the tooltips that can be used elsewhere too.
Dear @Harley and @JulienKaspar, thank you for your patience and guidance.
Above is my implementation, which is very close to the requirements, with one difference. The 'Create Mask' button isn't inactive, but actually disabled. The reason for that is that failing poll() function would automatically make the operator not editable. There could be some similar design as the inactive properties implemented above, where some sort of poll() function could enable "Disabled" message but keep the button editable. However, there is only one operator, and the overall difference between being able to click the 'Create Mask' button or not isn't that noticeable; therefore, I did not delve into it here, but it would also be an interesting avenue to explore further if needed.
For the modifications that would allow Disabled message to be set without forcing the button to be non-editable, I tried to make as little changes to the code as possible. However, if it would be more desirable for inactive to have its own workflow instead of borrowing parts of editable for its implementation, please let me know.
'Create Mask' Button and View Normal Limit and Falloff had to be separated between Scene and Brush Panel. Area Normal Limit and Falloff can still rely on the same PropertyRNA in the backend. However, since failing poll() function for 'Create Mask' operator would make the operator non-editable; without a different operator backend, the 'Create Mask' Button in the Brush panel would be grayed out as well. In View Normal Limit and Falloff's case, since turning on Occlusion, would make them inactive in the Brush Panel, if they rely on the same PropertyRNA backend, when they are inactive, they would show the Disabled message, which would not make sense.
For the Extra: Sync Values between View Normal Limit and Falloff between Scene and Brush Panel, I would imagine a callback like RNA_def_property_float_funcs should be used, but I'm not sure whether setting a new set function here would interfere with UI interface's ability to set the float value. It would be nice to be able to get a little guidance on this.
Also, sometimes I get the warning during make: unused function 'rna_Scene_automasking_view_normal_editable' in rna_sculpt_editable.c. It would be nice to not have to see that warning anymore. If there is also other recommendations, please let me know.
Thank you.
@Henry-Chang Sorry for getting back to you so late. I tried testing the PR but it doesn't grey out any options anymore. Perhaps there's somethign wrong and it needs an update?
@JulienKaspar Hi, thank you for your reply. This is weird. I just fetched and reset to the gray-out-automasking branch. Built with make lite and was able to get the correct response as shown in the screenshot below? Would like to solve this problem but I can't reproduce the problem at my end?
Ok I got it to work on my end!
The behaviour is working great imo and doesn't need any further changes.
@Harley do you have any more notes on the implementation? Otherwise I'd say the WIP tag can be removed and the PR put up for review any time 👍
Taking a quick look I see nothing obviously wrong so I might be a good time to remove "WIP", assuming it does everything it intends to. But I'd guess the review could be a longer process. This is mostly because it is not only solving a particular problem but it is introducing some new UI concepts that it needs - and that we probably need too - but needs some thought.
For example this introduces PROP_INACTIVE and UI_but_inactive, both that seem to do useful things. The state of being "inactive" seems like an obvious no-brainer but we actually (and wrongly) use the term "active" instead of "hover" all over the UI code. I know that is silly and unrelated to this but needs some thought so we don't make these confusing things worse.
"UI_but_inactive()" seems to mirror the behavior of existing "UI_but_disable()" but the latter sets the button disabled while for former does not set inactive, just sets the disabled_info text without doing the disabling. Perhaps "UI_but_inactive" should be something like "UI_but_set_disabled_hint" since that is all it does. And then "UI_but_disable" would call that after it disables the button.
WIP: UI: Gray out Scene Auto-Masking option if equivalent Brush option is used #102971to UI: Gray out Scene Auto-Masking option if equivalent Brush option is used #102971Dear @Harley and @JulienKaspar, thank you for your recommendations, confirmations, and help. I have made the changes to UI_but_set_disabled_hint and have UI_but_disable call it.
I understand that the review could take a while. That is not a problem.
Thank you.
@Harley Who from the UI module should be included in the review process?
Are there other modules that would have a stake in this?
@JulienKaspar - Who from the UI module should be included in the review process?
I added @JulianEisel as reviewer
I don't think so. There doesn't seem to be anything too contentious here. Outside of regular review this just seems to need special consideration of the UI module for these two changes:
disabled_info
for items that aren't actually disabledPROP_INACTIVE
flag to indicate the state that is editable yet looks disabled, within our already confusing state flags and our current use of term "active" for "hover".This seems like a good change. I agree that the disabled hint should be set here, otherwise it's hard to understand why a button is disabled.
What I'm unsure about is the use of the editable function for inactive state. Although allowing RNA properties to be inactive may be okay/useful, it seems what we actually lack is a way to do this on the Python layout definition level. I'd rather add a
layout.disabled_info
. E.g.@brecht what do you think? Should we allow setting inactive state in RNA or rather do this in the layout definition?
@ -2078,6 +2078,8 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene)
sd->automasking_start_normal_limit = 20.0f / 180.0f * M_PI;
sd->automasking_start_normal_falloff = 0.25f;
sd->automasking_scene_view_normal_limit = 90.0f / 180.0f * M_PI;
Why is this change done here, it seems unrelated to the patch?
Wait I see that these properties were introduced just now. Then my question is, what are these properties doing? That's not immediately clear to me.
This is due specifically to the Occlusion toggle in View Normal setting. When Occlusion is selected, it will make the Limit and Falloff scroll bars in Brush Panel inactive. If the disabled_info is set at the RNA level, a "Disabled:" message would erroneously show up when hovering over View Normal Limit and View Normal Falloff in the Brush Panel. This would not be needed if disabled_info is set at the Python layout definition level.
@ -900,2 +900,2 @@
/* Button is disabled, we may be able to tell user why. */
if ((but->flag & UI_BUT_DISABLED) || extra_icon) {
/* Button is disabled or set to inactive, we may be able to tell user why. */
if ((but->flag & UI_BUT_DISABLED) || (but->flag & UI_BUT_INACTIVE) || extra_icon) {
Can be shortened:
Understood, would update.
@ -4276,0 +4278,4 @@
ToolSettings *tool_settings = CTX_data_tool_settings(C);
int automasking_flags = tool_settings->sculpt->paint.brush->automasking_flags;
if (SCULPT_mode_poll(C) && !(automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL)) {
I find it generally more readable if error conditions are checked individually first. I.e.:
This minimizes the amount of information you have to keep in mind or understand when reading/changing code. (Also this would set the wrong poll message outside of sculpt mode, one more reason to keep error condition checks separate.)
Makes sense, would update.
@ -1704,0 +1810,4 @@
if (sd && (sd->paint.brush->automasking_flags & BRUSH_AUTOMASKING_VIEW_NORMAL)) {
*r_info = "The active brush already has the same auto-masking enabled.";
// TODO should probably utilize RNA_def_property_float_funcs to sync values between automasking_view and automasking_scene_view
sd->automasking_scene_view_normal_limit = sd->automasking_view_normal_limit;
Do not change properties inside of an editable callback, these are meant to be mere queries and not modify data.
Agreed. Will definitely revert them.
I think some support for this is useful at the UI level regardless, so might as well add something like this. I can imagine cases where doing it at the RNA level doesn't work well and would require too many assumptions about the particular UI layout.
Having support for active/inactive for RNA properties seems useful too, but I would imagine it for more "local" checks for relations between properties within a datablock, not something more complicated like relations between brushes and tool settings.
@Henry-Chang Hey, if you have some time and are interested in updating this PR that would be great! We were just looking over it in the sculpt module meeting, it seems like a nice change. If you have other questions let us know in chat too. Thanks!
Dear @Hans-Goudey, thank you for reaching out. I have not been happy with myself for this unfinished PR; unfortunately, work and life have not allowed me to make further headway. Nonetheless, I do still want to contribute. In terms of code issues, I distinctly remember not yet successfully finding the relevant parts related to expanding existent "Python layout definition" code. Any pointers or guidance on that front would undoutedly be of great help. Thank you very much.
Checkout
From your project repository, check out a new branch and test the changes.