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
10 changed files with 56 additions and 131 deletions
Showing only changes of commit cbdb3e14a3 - Show all commits

View File

@ -445,6 +445,7 @@ class SVN_OT_cleanup(SVN_Operator, Operator):
Processes.restart('Status') Processes.restart('Status')
Processes.restart('Log') Processes.restart('Log')
Processes.restart('Redraw Viewport')
self.report({'INFO'}, "SVN Cleanup complete.") self.report({'INFO'}, "SVN Cleanup complete.")

View File

@ -28,7 +28,6 @@ class SVN_OT_checkout_initiate(Operator):
def execute(self, context): def execute(self, context):
prefs = get_addon_prefs(context) prefs = get_addon_prefs(context)
prefs.active_repo_mode = 'SELECTED_REPO'
if self.create: if self.create:
prefs.repositories.add() prefs.repositories.add()
prefs.active_repo_idx = len(prefs.repositories)-1 prefs.active_repo_idx = len(prefs.repositories)-1

View File

@ -46,39 +46,29 @@ class SVN_addon_preferences(AddonPreferences):
repo = self.repositories.add() repo = self.repositories.add()
repo.initialize(root_dir, base_url) repo.initialize(root_dir, base_url)
self.active_repo_idx = len(self.repositories)-1
return repo return repo
def update_active_repo_idx(self, context): def update_active_repo_idx(self, context):
if self.idx_updating or len(self.repositories) == 0: if len(self.repositories) == 0:
return return
self.idx_updating = True
active_repo = self.active_repo active_repo = self.active_repo
if self.active_repo_mode == 'CURRENT_BLEND':
scene_svn = context.scene.svn
scene_svn_idx = self.repositories.find(scene_svn.svn_directory)
if scene_svn_idx == -1:
self.idx_updating = False
return
self.active_repo_idx = scene_svn_idx
self.idx_updating = False
return
# Authenticate when switching repos.
if ( if (
active_repo and active_repo and
not active_repo.authenticated and
not active_repo.auth_failed and not active_repo.auth_failed and
active_repo.is_cred_entered active_repo.is_cred_entered
): ):
Processes.start('Redraw Viewport')
if active_repo.authenticated:
Processes.restart('Status')
else:
active_repo.authenticate(context) active_repo.authenticate(context)
else:
Processes.kill('Status')
self.idx_updating = False
def update_active_repo_mode(self, context):
if self.active_repo_mode == 'CURRENT_BLEND':
scene_svn = context.scene.svn
scene_svn_idx = self.repositories.find(scene_svn.svn_directory)
self.active_repo_idx = scene_svn_idx
checkout_mode: BoolProperty( checkout_mode: BoolProperty(
name="Checkout In Progress", name="Checkout In Progress",
@ -86,30 +76,16 @@ class SVN_addon_preferences(AddonPreferences):
default=False default=False
) )
active_repo_mode: EnumProperty(
name="Choose Repository",
description="Whether the add-on should communicate with the repository of the currently opened .blend file, or the repository selected in the list below",
items=[
('CURRENT_BLEND', "Current Blend", "Check if the current .blend file is in an SVN repository, and communicate with that if that is the case. The file list will display only the files of the repository of the current .blend file. If the current .blend is not in a repository, do nothing"),
('SELECTED_REPO', "Selected Repo",
"Communicate with the selected repository")
],
default='CURRENT_BLEND',
update=update_active_repo_mode
)
active_repo_idx: IntProperty( active_repo_idx: IntProperty(
name="SVN Repositories", name="SVN Repositories",
options=set(), options=set(),
update=update_active_repo_idx update=update_active_repo_idx
) )
idx_updating: BoolProperty(
name="Index is Updating",
description="Helper flag to avoid infinite looping update callbacks",
)
@property @property
def active_repo(self) -> SVN_repository: def active_repo(self) -> Optional[SVN_repository]:
if not self.is_svn_installed:
return
if 0 <= self.active_repo_idx <= len(self.repositories)-1: if 0 <= self.active_repo_idx <= len(self.repositories)-1:
return self.repositories[self.active_repo_idx] return self.repositories[self.active_repo_idx]

View File

@ -28,33 +28,10 @@ class SVN_scene_properties(PropertyGroup):
) )
def get_repo(self, context) -> Optional['SVN_repository']: def get_repo(self, context) -> Optional['SVN_repository']:
"""Return the current repository. """Return the active repository."""
Depending on preferences, this is either the repo the current .blend file is in,
or whatever repo is selected in the preferences UI.
"""
prefs = get_addon_prefs(context) prefs = get_addon_prefs(context)
if not prefs.is_svn_installed:
return
if prefs.active_repo_mode == 'CURRENT_BLEND':
return self.get_scene_repo(context)
else:
return prefs.active_repo return prefs.active_repo
def get_scene_repo(self, context) -> Optional['SVN_repository']:
"""Return the repository of the current file, even if the add-on is
configured to another repository.
"""
prefs = get_addon_prefs(context)
if not prefs.is_svn_installed:
return
if not self.svn_url or not self.svn_directory:
return
for repo in prefs.repositories:
if (repo.url == self.svn_url) and (Path(repo.directory) == Path(self.svn_directory)):
return repo
registry = [ registry = [
SVN_scene_properties, SVN_scene_properties,

View File

@ -305,9 +305,9 @@ class SVN_repository(PropertyGroup):
) )
@property @property
def is_cred_entered(self): def is_cred_entered(self) -> bool:
"""Check if there's a username and password entered at all.""" """Check if there's a username and password entered at all."""
return self.username and self.password return bool(self.username and self.password)
authenticated: BoolProperty( authenticated: BoolProperty(
name="Authenticated", name="Authenticated",

View File

@ -44,7 +44,7 @@ class BackgroundProcess:
# Displayed in the tooltip on mouse-hover in the error message when an error occurs. # Displayed in the tooltip on mouse-hover in the error message when an error occurs.
error_description = "SVN Error:" error_description = "SVN Error:"
debug = True debug = False
def debug_print(self, msg: str): def debug_print(self, msg: str):
if self.debug: if self.debug:
@ -82,7 +82,7 @@ class BackgroundProcess:
def handle_error(self, context, error): def handle_error(self, context, error):
self.output = "" self.output = ""
self.error = error.stderr.decode() self.error = error.stderr.decode()
self.is_running = False self.stop()
def process_output(self, context, prefs): def process_output(self, context, prefs):
""" """
@ -113,7 +113,7 @@ class BackgroundProcess:
repo = context.scene.svn.get_repo(context) repo = context.scene.svn.get_repo(context)
if not repo: if not repo:
self.debug_print("Shutdown: Not in repo.") self.debug_print("Shutdown: Not in repo.")
self.is_running = False self.stop()
return return
prefs = get_addon_prefs(context) prefs = get_addon_prefs(context)
@ -127,7 +127,7 @@ class BackgroundProcess:
if self.needs_authentication and not repo.authenticated: if self.needs_authentication and not repo.authenticated:
self.debug_print("Shutdown: Authentication needed.") self.debug_print("Shutdown: Authentication needed.")
self.is_running = False self.stop()
return return
if not self.thread or not self.thread.is_alive() and not self.output and not self.error: if not self.thread or not self.thread.is_alive() and not self.output and not self.error:
@ -146,15 +146,14 @@ class BackgroundProcess:
self.output = "" self.output = ""
redraw_viewport() redraw_viewport()
if self.repeat_delay == 0: if self.repeat_delay == 0:
self.debug_print( self.debug_print("Shutdown: Output was processed, repeat_delay==0.")
"Shutdown: Output was processed, repeat_delay==0.") self.stop()
self.is_running = False
return return
self.debug_print(f"Processed output. Waiting {self.repeat_delay}") self.debug_print(f"Processed output. Waiting {self.repeat_delay}")
return self.repeat_delay return self.repeat_delay
elif not self.thread and not self.thread.is_alive() and self.repeat_delay == 0: elif not self.thread and not self.thread.is_alive() and self.repeat_delay == 0:
self.debug_print("Shutdown: Finished.\n") self.debug_print("Shutdown: Finished.\n")
self.is_running = False self.stop()
return return
self.debug_print(f"Tick delay: {self.tick_delay}") self.debug_print(f"Tick delay: {self.tick_delay}")
@ -188,6 +187,7 @@ class BackgroundProcess:
def stop(self): def stop(self):
"""Stop the process if it isn't running, by unregistering its timer function""" """Stop the process if it isn't running, by unregistering its timer function"""
self.debug_print("stop() function was called.")
self.is_running = False self.is_running = False
if bpy.app.timers.is_registered(self.timer_function): if bpy.app.timers.is_registered(self.timer_function):
# This won't work if the timer has returned None at any point, as that # This won't work if the timer has returned None at any point, as that

View File

@ -7,6 +7,7 @@ class BGP_SVN_Redraw_Viewport(BackgroundProcess):
repeat_delay = 1 repeat_delay = 1
debug = False debug = False
tick_delay = 1 tick_delay = 1
needs_authentication = False
def tick(self, context, prefs): def tick(self, context, prefs):
redraw_viewport() redraw_viewport()

View File

@ -58,11 +58,10 @@ class SVN_OT_explain_status(Operator):
def ensure_svn_of_current_file(_scene=None): def ensure_svn_of_current_file(_scene=None):
"""When opening or saving a .blend file, it's possible that the new .blend """When opening or saving a .blend file, it's possible that the new .blend
is part of an SVN repository. If this is the case, do the following: is part of an SVN repository. If this is the case, do the following:
- Initialize SVN Scene info - Check if this file's repository is already in our database
- Initialize Repository - If not, create it
- Try to authenticate - Switch to that repo
""" """
context = bpy.context context = bpy.context
prefs = get_addon_prefs(context) prefs = get_addon_prefs(context)
prefs.is_svn_installed = check_svn_installed() prefs.is_svn_installed = check_svn_installed()
@ -71,52 +70,42 @@ def ensure_svn_of_current_file(_scene=None):
scene_svn = context.scene.svn scene_svn = context.scene.svn
# If we have any repository entries, make sure at least one is active.
prefs.sync_repo_info_file() prefs.sync_repo_info_file()
if prefs.active_repo_idx == -1 and len(prefs.repositories) > 0: if prefs.active_repo_idx == -1 and len(prefs.repositories) > 0:
prefs.active_repo_idx = 0 prefs.active_repo_idx = 0
elif prefs.active_repo_idx > len(prefs.repositories)-1:
prefs.active_repo_idx = 0
else:
prefs.active_repo_idx = prefs.active_repo_idx
repo = prefs.active_repo # If the file is unsaved, nothing more to do.
if repo and repo.is_cred_entered and repo.authenticated: if not bpy.data.filepath:
status = Processes.get('Status') scene_svn.svn_url = ""
if status and time.time() - status.timestamp_last_update < status.repeat_delay + 5:
# If the SVN Status background process has recently processed data,
# there is no need to re-initialize everything.
# This happens when a file that was already saved is saved again,
# or a file from the same repository as the previous one is loaded.
Processes.restart('Status')
return return
# If file is not in a repo, nothing more to do.
is_in_repo = set_scene_svn_info(context)
if not is_in_repo:
return
# If we switched repos, reset auth flags and authenticate the new repo.
for repo in prefs.repositories: for repo in prefs.repositories:
# This would ideally only run when opening Blender for the first # This would ideally only run when opening Blender for the first
# time, but there is no app handler for that, sadly. # time, but there is no app handler for that, sadly.
repo.authenticated = False repo.authenticated = False
repo.auth_failed = False repo.auth_failed = False
if prefs.active_repo_mode == 'CURRENT_BLEND': # If file is in an existing repo, we should switch over to that repo.
if not bpy.data.filepath: for i, existing_repo in enumerate(prefs.repositories):
scene_svn.svn_url = "" if ( existing_repo.url == scene_svn.svn_url and
return existing_repo.directory == scene_svn.svn_directory
):
is_in_repo = set_scene_svn_info(context)
if not is_in_repo:
return
repo = scene_svn.get_scene_repo(context)
if not repo:
repo = prefs.init_repo(context, scene_svn.svn_directory)
for i, other_repo in enumerate(prefs.repositories):
if other_repo == repo:
prefs.active_repo_idx = i prefs.active_repo_idx = i
break
else: else:
repo = prefs.active_repo # If file is in a non-existing repo, initialize that repo.
if not repo: repo = prefs.init_repo(context, scene_svn.svn_directory)
return
if repo.is_cred_entered:
repo.authenticate(context)
def set_scene_svn_info(context) -> bool: def set_scene_svn_info(context) -> bool:

View File

@ -18,9 +18,6 @@ class SVN_UL_repositories(UIList):
repo = item repo = item
row = layout.row() row = layout.row()
prefs = get_addon_prefs(context)
if prefs.active_repo_mode == 'CURRENT_BLEND' and repo != context.scene.svn.get_repo(context):
row.enabled = False
row.label(text=repo.display_name) row.label(text=repo.display_name)
if not repo.dir_exists: if not repo.dir_exists:
@ -198,10 +195,6 @@ def draw_prefs_checkout(self, context):
def draw_prefs_repos(self, context) -> None: def draw_prefs_repos(self, context) -> None:
layout = self.layout layout = self.layout
row = layout.row()
row.use_property_split = True
row.prop(self, 'active_repo_mode', expand=True)
auth_in_progress = False auth_in_progress = False
auth_error = False auth_error = False
auth_proc = Processes.get('Authenticate') auth_proc = Processes.get('Authenticate')
@ -209,12 +202,6 @@ def draw_prefs_repos(self, context) -> None:
auth_in_progress = auth_proc.is_running auth_in_progress = auth_proc.is_running
auth_error = auth_proc.error auth_error = auth_proc.error
if self.active_repo_mode == 'CURRENT_BLEND' and not context.scene.svn.get_repo(context):
split = layout.split(factor=0.4)
split.row()
split.row().label(text="Current file is not in a repository.")
return
repo_col = layout.column() repo_col = layout.column()
split = repo_col.row().split() split = repo_col.row().split()
split.row().label(text="SVN Repositories:") split.row().label(text="SVN Repositories:")

View File

@ -16,12 +16,7 @@ class VIEW3D_PT_svn_credentials(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
prefs = get_addon_prefs(context)
if prefs.active_repo_mode == 'CURRENT_BLEND':
repo = context.scene.svn.get_scene_repo(context)
else:
repo = context.scene.svn.get_repo(context) repo = context.scene.svn.get_repo(context)
return repo and not repo.authenticated return repo and not repo.authenticated
def draw(self, context): def draw(self, context):