mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-06 07:51:47 +00:00
Make autosave recovery prompt properly modal
The recovery popup used invoke_popup, which is dismissed the instant
the mouse leaves its bounds - closing the prompt without loading
either file, and with no visible feedback that anything happened.
Switches to invoke_props_dialog, which blocks the rest of the UI and
is only dismissed by an explicit action. Since Blender always renders
both a fixed "Cancel" button and one labelled by confirm_text on that
dialog type, the prompt is reframed as a direct Yes/Cancel question
("Do you want to load the autosaved version instead?") instead of
adding separate Load Original/Load Autosave buttons on top of those.
Folds the load logic directly into the popup's execute()/cancel(), so
the now-redundant LoadAutosavedRecovery operator is removed.
Generated with the assistance of an AI coding tool.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -61,7 +61,6 @@ classes = (
|
|||||||
operator.LoadLink,
|
operator.LoadLink,
|
||||||
operator.AutosavePrompt,
|
operator.AutosavePrompt,
|
||||||
operator.LoadAutosavedRecoveryPopup,
|
operator.LoadAutosavedRecoveryPopup,
|
||||||
operator.LoadAutosavedRecovery,
|
|
||||||
operator.LoadLinkedProject,
|
operator.LoadLinkedProject,
|
||||||
operator.LoadProject,
|
operator.LoadProject,
|
||||||
operator.LoadProjectElements,
|
operator.LoadProjectElements,
|
||||||
|
|||||||
@@ -2169,71 +2169,41 @@ class LoadAutosavedRecoveryPopup(bpy.types.Operator):
|
|||||||
layout.label(text="A newer autosaved copy was found:", icon="INFO")
|
layout.label(text="A newer autosaved copy was found:", icon="INFO")
|
||||||
layout.label(text=os.path.basename(self.autosaved_filepath))
|
layout.label(text=os.path.basename(self.autosaved_filepath))
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.label(text=f"Original: {os.path.basename(self.original_filepath)}")
|
layout.label(text="Do you want to load the autosaved version instead?")
|
||||||
layout.label(text="Which one do you want to load?")
|
layout.label(text="(Cancel will load the original)")
|
||||||
|
|
||||||
row = layout.row(align=True)
|
|
||||||
op = row.operator("bim.load_autosaved_recovery", text="Load Original", icon="LOOP_BACK")
|
|
||||||
op.file_type = "ORIGINAL"
|
|
||||||
self._pass_props(op)
|
|
||||||
|
|
||||||
op = row.operator("bim.load_autosaved_recovery", text="Load Autosave", icon="LOOP_FORWARDS")
|
|
||||||
op.file_type = "AUTOSAVE"
|
|
||||||
self._pass_props(op)
|
|
||||||
|
|
||||||
def _pass_props(self, op):
|
|
||||||
op.original_filepath = self.original_filepath
|
|
||||||
op.autosaved_filepath = self.autosaved_filepath
|
|
||||||
op.is_advanced = self.is_advanced
|
|
||||||
op.use_relative_path = self.use_relative_path
|
|
||||||
op.should_start_fresh_session = self.should_start_fresh_session
|
|
||||||
op.import_without_ifc_data = self.import_without_ifc_data
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
return context.window_manager.invoke_popup(self, width=420)
|
# invoke_props_dialog is modal - unlike invoke_popup/popup_menu, it
|
||||||
|
# isn't dismissed by the mouse simply leaving its bounds. It always
|
||||||
|
# renders both a fixed "Cancel" button and this confirm_text one, so
|
||||||
|
# the question is framed as Yes/Cancel rather than adding separate
|
||||||
|
# Load buttons on top.
|
||||||
|
return context.window_manager.invoke_props_dialog(
|
||||||
|
self, width=420, title="Recover Autosaved File", confirm_text="Yes"
|
||||||
|
)
|
||||||
|
|
||||||
def execute(self, context):
|
def _load(self, filepath: str, skip_recent: bool) -> set["rna_enums.OperatorReturnItems"]:
|
||||||
# This should almost never run
|
return bpy.ops.bim.load_project(
|
||||||
self.report({"INFO"}, "Popup closed without choosing")
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class LoadAutosavedRecovery(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.load_autosaved_recovery"
|
|
||||||
bl_label = "Recover Autosaved File"
|
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
|
||||||
|
|
||||||
file_type: bpy.props.StringProperty(default="AUTOSAVE")
|
|
||||||
original_filepath: bpy.props.StringProperty(options={"SKIP_SAVE"})
|
|
||||||
autosaved_filepath: bpy.props.StringProperty(options={"SKIP_SAVE"})
|
|
||||||
is_advanced: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"})
|
|
||||||
use_relative_path: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"})
|
|
||||||
should_start_fresh_session: bpy.props.BoolProperty(default=True, options={"SKIP_SAVE"})
|
|
||||||
import_without_ifc_data: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"})
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
if self.file_type == "ORIGINAL":
|
|
||||||
filepath = self.original_filepath
|
|
||||||
else:
|
|
||||||
filepath = self.autosaved_filepath
|
|
||||||
|
|
||||||
# Call the real loader
|
|
||||||
result = bpy.ops.bim.load_project(
|
|
||||||
filepath=filepath,
|
filepath=filepath,
|
||||||
skip_autosave_recovery=True, # Prevent infinite loop
|
skip_autosave_recovery=True, # Prevent infinite loop
|
||||||
is_advanced=self.is_advanced,
|
is_advanced=self.is_advanced,
|
||||||
use_relative_path=self.use_relative_path,
|
use_relative_path=self.use_relative_path,
|
||||||
should_start_fresh_session=self.should_start_fresh_session,
|
should_start_fresh_session=self.should_start_fresh_session,
|
||||||
import_without_ifc_data=self.import_without_ifc_data,
|
import_without_ifc_data=self.import_without_ifc_data,
|
||||||
skip_recent=(self.file_type == "AUTOSAVE")
|
skip_recent=skip_recent,
|
||||||
)
|
)
|
||||||
|
|
||||||
# If user chose autosave, override the stored path
|
def execute(self, context):
|
||||||
if self.file_type == "AUTOSAVE":
|
result = self._load(self.autosaved_filepath, skip_recent=True)
|
||||||
tool.Ifc.set_path(self.original_filepath)
|
# Re-point tracking at the original path so future saves write back
|
||||||
|
# to it, not "_autosaved.ifc".
|
||||||
|
tool.Ifc.set_path(self.original_filepath)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def cancel(self, context):
|
||||||
|
# Also reached via Escape or a click outside the dialog, not just Cancel.
|
||||||
|
self._load(self.original_filepath, skip_recent=False)
|
||||||
|
|
||||||
|
|
||||||
class AutosavePrompt(bpy.types.Operator):
|
class AutosavePrompt(bpy.types.Operator):
|
||||||
bl_idname = "bim.autosave_prompt"
|
bl_idname = "bim.autosave_prompt"
|
||||||
|
|||||||
Reference in New Issue
Block a user