SVN: UX improvements #136

Merged
Demeter Dzadik merged 15 commits from Mets/blender-studio-pipeline:svn_ux_improvements into main 2023-08-01 15:39:18 +02:00
5 changed files with 42 additions and 58 deletions
Showing only changes of commit 8a1059e925 - Show all commits

View File

@ -20,7 +20,7 @@ class SVN_Operator:
@staticmethod @staticmethod
def update_file_list(context): def update_file_list(context):
repo = context.scene.svn.get_repo(context) repo = context.scene.svn.get_repo(context)
repo.update_file_filter(context) repo.refresh_ui_lists(context)
def execute_svn_command(self, context, command: List[str], use_cred=False) -> str: def execute_svn_command(self, context, command: List[str], use_cred=False) -> str:
# Since a status update might already be being requested when an SVN operator is run, # Since a status update might already be being requested when an SVN operator is run,
@ -51,7 +51,6 @@ class SVN_Operator_Single_File(SVN_Operator):
ret = self._execute(context) ret = self._execute(context)
file = self.get_file(context) file = self.get_file(context)
if file:
Processes.start('Status') Processes.start('Status')
redraw_viewport() redraw_viewport()
@ -107,7 +106,7 @@ class May_Modifiy_Current_Blend(SVN_Operator_Single_File, Warning_Operator):
return current_blend and current_blend.svn_path == self.file_rel_path return current_blend and current_blend.svn_path == self.file_rel_path
reload_file: BoolProperty( reload_file: BoolProperty(
name="Reload File", name="Reload File (Keep UI)",
description="Reload the file after the operation is completed. The UI layout will be preserved", description="Reload the file after the operation is completed. The UI layout will be preserved",
default=False, default=False,
) )
@ -305,6 +304,7 @@ class SVN_OT_trash_file(SVN_Operator_Single_File, Warning_Operator, Operator):
bl_options = {'INTERNAL'} bl_options = {'INTERNAL'}
file_rel_path: StringProperty() file_rel_path: StringProperty()
missing_file_allowed = False
def get_warning_text(self, context): def get_warning_text(self, context):
return "Are you sure you want to move this file to the recycle bin?\n " + self.file_rel_path return "Are you sure you want to move this file to the recycle bin?\n " + self.file_rel_path

View File

@ -444,15 +444,15 @@ class SVN_repository(PropertyGroup):
"""When user clicks on a different file, the latest log entry of that file """When user clicks on a different file, the latest log entry of that file
should become the active log entry.""" should become the active log entry."""
latest_idx = self.get_latest_revision_of_file( latest_rev = self.get_latest_revision_of_file(
self.active_file.svn_path) self.active_file.svn_path)
# SVN Revisions are not 0-indexed, so we need to subtract 1. # SVN Revisions are not 0-indexed, so we need to subtract 1.
self.log_active_index = latest_idx-1 self.log_active_index = latest_rev-1
space = context.space_data space = context.space_data
if space and space.type == 'FILE_BROWSER': if space and space.type == 'FILE_BROWSER':
# Set the active file in the file browser to whatever was selected in the SVN Files panel. # Set the active file in the file browser to whatever was selected in the SVN Files panel.
self.log_active_index_filebrowser = latest_idx-1 self.log_active_index_filebrowser = latest_rev-1
space.params.directory = self.active_file.absolute_path.parent.as_posix().encode() space.params.directory = self.active_file.absolute_path.parent.as_posix().encode()
space.params.filename = self.active_file.name.encode() space.params.filename = self.active_file.name.encode()
@ -462,7 +462,7 @@ class SVN_repository(PropertyGroup):
relative_path=self.active_file.name) relative_path=self.active_file.name)
Processes.start('Activate File') Processes.start('Activate File')
# Filter out log entries that did not affect the selected file. # Set the filter flag of the log entries based on whether they affect the active file or not.
self.log.foreach_set( self.log.foreach_set(
'affects_active_file', 'affects_active_file',
[log_entry.changes_file(self.active_file) [log_entry.changes_file(self.active_file)
@ -513,28 +513,10 @@ class SVN_repository(PropertyGroup):
return self.get_file_by_absolute_path(bpy.data.filepath) return self.get_file_by_absolute_path(bpy.data.filepath)
### File List UIList filter properties ### ### File List UIList filter properties ###
# Filtering properties are normally stored on the UIList, def refresh_ui_lists(self, context):
# but then they cannot be accessed from anywhere else, """Refresh the file UI list based on filter settings.
# since template_list() does not return the UIList instance. Also triggers a refresh of the SVN UIList, through the update callback of
# We need to be able to access them outside of drawing code, to be able to external_files_active_index."""
# ensure that a filtered out entry can never be the active one.
def force_good_active_index(self, context) -> bool:
"""
We want to avoid having the active file entry be invisible due to filtering.
If the active element is being filtered out, set the active element to
something that is visible.
"""
if len(self.external_files) == 0:
return
if not self.active_file.show_in_filelist:
for i, file in enumerate(self.external_files):
if file.show_in_filelist:
self.external_files_active_index = i
return
def update_file_filter(self, context):
"""Should run when any of the SVN file list search filters are changed."""
UI_LIST = bpy.types.UI_UL_list UI_LIST = bpy.types.UI_UL_list
if self.file_search_filter: if self.file_search_filter:
@ -555,12 +537,24 @@ class SVN_repository(PropertyGroup):
file.show_in_filelist = not file.has_default_status file.show_in_filelist = not file.has_default_status
self.force_good_active_index(context) if len(self.external_files) == 0:
return
if self.active_file.show_in_filelist:
# Trigger the update callback, because that refreshes the SVN Log UI.
self.external_files_active_index = self.external_files_active_index
return
# Make sure the active file isn't now being filtered out.
# If it is, change the active file to the first visible one.
for i, file in enumerate(self.external_files):
if file.show_in_filelist:
self.external_files_active_index = i
return
file_search_filter: StringProperty( file_search_filter: StringProperty(
name="Search Filter", name="Search Filter",
description="Only show entries that contain this string", description="Only show entries that contain this string",
update=update_file_filter update=refresh_ui_lists
) )

View File

@ -277,8 +277,7 @@ def update_file_list(context, file_statuses: Dict[str, Tuple[str, str, int]]):
if file_entry.svn_path not in svn_paths: if file_entry.svn_path not in svn_paths:
repo.remove_file_entry(file_entry) repo.remove_file_entry(file_entry)
repo.update_file_filter(context) repo.refresh_ui_lists(context)
repo.force_good_active_index(context)
def get_repo_file_statuses(svn_status_str: str) -> Dict[str, Tuple[str, str, int]]: def get_repo_file_statuses(svn_status_str: str) -> Dict[str, Tuple[str, str, int]]:

View File

@ -3,26 +3,10 @@
import bpy import bpy
from bpy.types import Context, UIList, Operator from bpy.types import Context, UIList, Operator
from bpy.props import StringProperty from bpy.props import StringProperty, BoolProperty
from pathlib import Path from pathlib import Path
class SVN_OT_open_blend_file(Operator):
# This is needed because drawing a button for wm.open_mainfile in the UI
# directly simply does not work; Blender just opens a full-screen filebrowser,
# instead of opening the .blend file. Probably a bug.
bl_idname = "svn.open_blend_file"
bl_label = "Open Blend File"
bl_description = "Open Blend File"
bl_options = {'INTERNAL'}
filepath: StringProperty()
def execute(self, context):
bpy.ops.wm.open_mainfile(filepath=self.filepath, load_ui=False)
return {'FINISHED'}
def check_context_match(context: Context, uilayout_type: str, bl_idname: str) -> bool: def check_context_match(context: Context, uilayout_type: str, bl_idname: str) -> bool:
"""For example, when right-clicking on a UIList, the uilayout_type will """For example, when right-clicking on a UIList, the uilayout_type will
be `ui_list` and the bl_idname is that of the UIList being right-clicked. be `ui_list` and the bl_idname is that of the UIList being right-clicked.
@ -39,8 +23,17 @@ def svn_file_list_context_menu(self: UIList, context: Context) -> None:
layout.separator() layout.separator()
active_file = context.scene.svn.get_repo(context).active_file active_file = context.scene.svn.get_repo(context).active_file
if active_file.name.endswith("blend"): if active_file.name.endswith("blend"):
layout.operator("svn.open_blend_file", op = layout.operator("wm.open_mainfile",
text=f"Open {active_file.name}").filepath = active_file.absolute_path text=f"Open {active_file.name}")
op.filepath = active_file.absolute_path
op.display_file_selector = False
op.load_ui = True
op = layout.operator("wm.open_mainfile",
text=f"Open {active_file.name} (Keep UI)")
op.filepath = active_file.absolute_path
op.display_file_selector = False
op.load_ui = False
else: else:
layout.operator("wm.path_open", layout.operator("wm.path_open",
text=f"Open {active_file.name}").filepath = str(Path(active_file.absolute_path)) text=f"Open {active_file.name}").filepath = str(Path(active_file.absolute_path))
@ -73,5 +66,3 @@ def unregister():
bpy.types.UI_MT_list_item_context_menu.remove(svn_file_list_context_menu) bpy.types.UI_MT_list_item_context_menu.remove(svn_file_list_context_menu)
bpy.types.UI_MT_list_item_context_menu.remove(svn_log_list_context_menu) bpy.types.UI_MT_list_item_context_menu.remove(svn_log_list_context_menu)
registry = [SVN_OT_open_blend_file]

View File

@ -34,8 +34,8 @@ def draw_outdated_file_warning(self, context):
def register(): def register():
bpy.types.VIEW3D_HT_header.prepend(draw_outdated_file_warning) bpy.types.TOPBAR_MT_editor_menus.append(draw_outdated_file_warning)
def unregister(): def unregister():
bpy.types.VIEW3D_HT_header.remove(draw_outdated_file_warning) bpy.types.TOPBAR_MT_editor_menus.remove(draw_outdated_file_warning)