Fix: Presets overriding drag-n-drop data

Currently, internal I/O operators can be invoked with drag-n-drop path
data, and when this happens a quick popup menu is shown to customize
import settings.

If these operators support operator presets, using a preset can
override path data given by drag-n-drop, and that can be unwanted
behavior.

While this can be fixed by setting path properties to SKIP_SAVE, doing
this would make these properties also to stop using ghost values. These
ghost values are used by the file select window to open operator last
import directory, and using this flag makes the file select windows
always open the home directory.

To fix that, add an explicit flag PROP_SKIP_PRESET that skips properties
writing to presets. Also clarify that PROP_HIDDEN and PROP_SKIP_SAVE
also avoid writing to presets.

Added a operator that can clean operator's specific property presets.
Importing presets from previous versions runs an automatic cleanup.

Co-authored-by: Brecht Van Lommel <brecht@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/117673
This commit is contained in:
Guillermo Venegas
2024-02-06 20:02:27 +01:00
committed by Brecht Van Lommel
parent 42561c2a1c
commit 0d7282e69b
7 changed files with 109 additions and 11 deletions

View File

@@ -6,10 +6,12 @@ import bpy
from bpy.types import (
Menu,
Operator,
OperatorFileListElement,
WindowManager,
)
from bpy.props import (
BoolProperty,
CollectionProperty,
StringProperty,
)
from bpy.app.translations import (
@@ -622,7 +624,7 @@ class AddPresetOperator(AddPresetBase, Operator):
ret = []
for prop_id, prop in operator_rna.properties.items():
if not (prop.is_hidden or prop.is_skip_save):
if not prop.is_skip_preset:
if prop_id not in properties_blacklist:
ret.append("op.%s" % prop_id)
@@ -655,6 +657,74 @@ class WM_MT_operator_presets(Menu):
preset_operator = "script.execute_preset"
class WM_OT_operator_presets_cleanup(Operator):
bl_idname = "wm.operator_presets_cleanup"
bl_label = "Clean Up Operator Presets"
bl_description = "Remove outdated operator properties from presets that may cause problems"
operator: StringProperty(name="operator")
properties: CollectionProperty(name="properties", type=OperatorFileListElement)
def cleanup_preset(self, filepath, properties):
from pathlib import Path
file = Path(filepath)
if not (file.is_file() and filepath.suffix == ".py"):
return
lines = file.read_text().splitlines(True)
if len(lines) == 0:
return
new_lines = []
for line in lines:
if not any(line.startswith(("op.%s" % prop)) for prop in properties):
new_lines.append(line)
file.write_text("".join(new_lines))
def cleanup_operators_presets(self, operators, properties):
base_preset_directory = bpy.utils.user_resource(
'SCRIPTS', path="presets", create=False)
for operator in operators:
from pathlib import Path
operator_path = AddPresetOperator.operator_path(operator)
directory = Path(base_preset_directory, operator_path)
if not directory.is_dir():
continue
for filepath in directory.iterdir():
self.cleanup_preset(filepath, properties)
def execute(self, context):
properties = []
operators = []
if self.operator:
operators.append(self.operator)
for prop in self.properties:
properties.append(prop.name)
else:
# Cleanup by default I/O Operators Presets
operators = ['WM_OT_alembic_export',
'WM_OT_alembic_import',
'WM_OT_collada_export',
'WM_OT_collada_import',
'WM_OT_gpencil_export_svg',
'WM_OT_gpencil_export_pdf',
'WM_OT_gpencil_export_svg',
'WM_OT_gpencil_import_svg',
'WM_OT_obj_export',
'WM_OT_obj_import',
'WM_OT_ply_export',
'WM_OT_ply_import',
'WM_OT_stl_export',
'WM_OT_stl_import',
'WM_OT_usd_export',
'WM_OT_usd_import',
]
properties = ["filepath", "directory", "files", "filename"]
self.cleanup_operators_presets(operators, properties)
return {'FINISHED'}
class AddPresetGpencilBrush(AddPresetBase, Operator):
"""Add or remove grease pencil brush preset"""
bl_idname = "scene.gpencil_brush_preset_add"
@@ -748,4 +818,5 @@ classes = (
AddPresetEEVEERaytracing,
ExecutePreset,
WM_MT_operator_presets,
WM_OT_operator_presets_cleanup,
)

View File

@@ -148,6 +148,8 @@ class PREFERENCES_OT_copy_prev(Operator):
# Reload preferences and `recent-files.txt`.
bpy.ops.wm.read_userpref()
bpy.ops.wm.read_history()
# Fix operator presets that have unwanted filepath properties
bpy.ops.wm.operator_presets_cleanup()
# don't loose users work if they open the splash later.
if bpy.data.is_saved is bpy.data.is_dirty is False:

View File

@@ -454,6 +454,7 @@ class TOPBAR_MT_blender_system(Menu):
layout.separator()
layout.operator("screen.spacedata_cleanup")
layout.operator("wm.operator_presets_cleanup")
class TOPBAR_MT_templates_more(Menu):

View File

@@ -179,7 +179,7 @@ enum PropertySubType {
/* Make sure enums are updated with these */
/* HIGHEST FLAG IN USE: 1 << 31
* FREE FLAGS: 11, 13, 14, 15. */
* FREE FLAGS: 13, 14, 15. */
enum PropertyFlag {
/**
* Editable means the property is editable in the user
@@ -212,9 +212,9 @@ enum PropertyFlag {
PROP_ICONS_CONSECUTIVE = (1 << 12),
PROP_ICONS_REVERSE = (1 << 8),
/** Hidden in the user interface. */
/** Hidden in the user interface. Inherits #ROP_SKIP_PRESET. */
PROP_HIDDEN = (1 << 19),
/** Do not write in presets. */
/** Do not use ghost values. Inherits #PROP_SKIP_PRESET. */
PROP_SKIP_SAVE = (1 << 28),
/* numbers */
@@ -311,6 +311,9 @@ enum PropertyFlag {
* as having the +/- operators available in the file browser.
*/
PROP_PATH_OUTPUT = (1 << 2),
/** Do not write in presets. */
PROP_SKIP_PRESET = (1 << 11),
};
ENUM_OPERATORS(PropertyFlag, PROP_TEXTEDIT_UPDATE)

View File

@@ -154,8 +154,13 @@ const EnumPropertyItem rna_enum_property_unit_items[] = {
};
const EnumPropertyItem rna_enum_property_flag_items[] = {
{PROP_HIDDEN, "HIDDEN", 0, "Hidden", ""},
{PROP_SKIP_SAVE, "SKIP_SAVE", 0, "Skip Save", ""},
{PROP_HIDDEN, "HIDDEN", 0, "Hidden", "Hidden in the user interface. Inherits 'SKIP_PRESET'"},
{PROP_SKIP_SAVE,
"SKIP_SAVE",
0,
"Skip Save",
"Do not use ghost values. Inherits 'SKIP_PRESET'"},
{PROP_SKIP_PRESET, "SKIP_PRESET", 0, "Skip Preset", "Do not write in presets"},
{PROP_ANIMATABLE, "ANIMATABLE", 0, "Animatable", ""},
{PROP_LIB_EXCEPTION, "LIBRARY_EDITABLE", 0, "Library Editable", ""},
{PROP_PROPORTIONAL, "PROPORTIONAL", 0, "Adjust values proportionally to each other", ""},
@@ -728,6 +733,12 @@ static bool rna_Property_is_skip_save_get(PointerRNA *ptr)
return (prop->flag & PROP_SKIP_SAVE) != 0;
}
static bool rna_Property_is_skip_preset_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
return (prop->flag & (PROP_SKIP_SAVE | PROP_HIDDEN | PROP_SKIP_PRESET)) != 0;
}
static bool rna_Property_is_enum_flag_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
@@ -3136,7 +3147,12 @@ static void rna_def_property(BlenderRNA *brna)
prop = RNA_def_property(srna, "is_skip_save", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_funcs(prop, "rna_Property_is_skip_save_get", nullptr);
RNA_def_property_ui_text(prop, "Skip Save", "True when the property is not saved in presets");
RNA_def_property_ui_text(prop, "Skip Save", "True when the property uses ghost values");
prop = RNA_def_property(srna, "is_skip_preset", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_funcs(prop, "rna_Property_is_skip_preset_get", nullptr);
RNA_def_property_ui_text(prop, "Skip Preset", "True when the property is not saved in presets");
prop = RNA_def_property(srna, "is_output", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);

View File

@@ -901,6 +901,7 @@ enum eFileSel_Flag {
WM_FILESEL_FILES = 1 << 4,
/** Show the properties sidebar by default. */
WM_FILESEL_SHOW_PROPS = 1 << 5,
WM_FILESEL_SKIP_PATHS_PRESETS = 1 << 5,
};
ENUM_OPERATORS(eFileSel_Flag, WM_FILESEL_SHOW_PROPS)

View File

@@ -93,23 +93,27 @@ void WM_operator_properties_filesel(wmOperatorType *ot,
};
if (flag & WM_FILESEL_FILEPATH) {
RNA_def_string_file_path(ot->srna, "filepath", nullptr, FILE_MAX, "File Path", "Path to file");
prop = RNA_def_string_file_path(
ot->srna, "filepath", nullptr, FILE_MAX, "File Path", "Path to file");
RNA_def_property_flag(prop, PROP_SKIP_PRESET);
}
if (flag & WM_FILESEL_DIRECTORY) {
RNA_def_string_dir_path(
prop = RNA_def_string_dir_path(
ot->srna, "directory", nullptr, FILE_MAX, "Directory", "Directory of the file");
RNA_def_property_flag(prop, PROP_SKIP_PRESET);
}
if (flag & WM_FILESEL_FILENAME) {
RNA_def_string_file_name(
prop = RNA_def_string_file_name(
ot->srna, "filename", nullptr, FILE_MAX, "File Name", "Name of the file");
RNA_def_property_flag(prop, PROP_SKIP_PRESET);
}
if (flag & WM_FILESEL_FILES) {
prop = RNA_def_collection_runtime(
ot->srna, "files", &RNA_OperatorFileListElement, "Files", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE | PROP_SKIP_PRESET);
}
if ((flag & WM_FILESEL_SHOW_PROPS) == 0) {