Merge branch '218-new-ui-ux-implementation' into 'develop'

New UI/UX implementation

See merge request slumber/multi-user!140
This commit is contained in:
Swann Martinez 2021-07-26 15:52:19 +00:00
commit 4c0d4cb1c7
13 changed files with 803 additions and 438 deletions

View File

@ -61,6 +61,7 @@ def register():
from . import operators
from . import handlers
from . import ui
from . import icons
from . import preferences
from . import addon_updater_ops
@ -70,6 +71,7 @@ def register():
operators.register()
handlers.register()
ui.register()
icons.register()
except ModuleNotFoundError as e:
raise Exception(module_error_msg)
logging.error(module_error_msg)
@ -83,7 +85,9 @@ def register():
type=preferences.SessionUser
)
bpy.types.WindowManager.user_index = bpy.props.IntProperty()
bpy.types.WindowManager.server_index = bpy.props.IntProperty()
bpy.types.TOPBAR_MT_file_import.append(operators.menu_func_import)
bpy.types.TOPBAR_MT_file_export.append(operators.menu_func_export)
def unregister():
@ -91,14 +95,17 @@ def unregister():
from . import operators
from . import handlers
from . import ui
from . import icons
from . import preferences
from . import addon_updater_ops
bpy.types.TOPBAR_MT_file_import.remove(operators.menu_func_import)
bpy.types.TOPBAR_MT_file_export.remove(operators.menu_func_export)
presence.unregister()
addon_updater_ops.unregister()
ui.unregister()
icons.unregister()
handlers.unregister()
operators.unregister()
preferences.unregister()
@ -107,5 +114,6 @@ def unregister():
del bpy.types.ID.uuid
del bpy.types.WindowManager.online_users
del bpy.types.WindowManager.user_index
del bpy.types.WindowManager.server_index
environment.unregister()

View File

@ -0,0 +1,45 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####
import bpy
import os
from pathlib import Path
import bpy.utils.previews
def register():
global icons_col
pcoll = bpy.utils.previews.new()
icons_dir = os.path.join(os.path.dirname(__file__), ".")
for png in Path(icons_dir).rglob("*.png"):
pcoll.load(png.stem, str(png), "IMAGE")
icons_col = pcoll
def unregister():
global icons_col
try:
bpy.utils.previews.remove(icons_col)
except Exception:
pass
icons_col = None

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -1 +1 @@
Subproject commit 15dc5c62c07a3e0025a82d87022f5073c019e7e9
Subproject commit d283d39e9cac6a5a8dcedf049220c764063ac8a9

View File

@ -20,6 +20,7 @@ import asyncio
import copy
import gzip
import logging
from multi_user.preferences import ServerPreset
import os
import queue
import random
@ -28,6 +29,7 @@ import string
import sys
import time
import traceback
from uuid import uuid4
from datetime import datetime
from operator import itemgetter
from pathlib import Path
@ -56,7 +58,7 @@ from replication.repository import Repository
from . import bl_types, environment, shared_data, timers, ui, utils
from .handlers import on_scene_update, sanitize_deps_graph
from .presence import SessionStatusWidget, renderer, view3d_find
from .presence import SessionStatusWidget, renderer, view3d_find, refresh_sidebar_view
from .timers import registry
background_execution_queue = Queue()
@ -81,7 +83,6 @@ def session_callback(name):
def initialize_session():
"""Session connection init hander
"""
settings = utils.get_preferences()
runtime_settings = bpy.context.window_manager.session
if not runtime_settings.is_host:
@ -108,7 +109,6 @@ def initialize_session():
for d in deleyables:
d.register()
# Step 5: Clearing history
utils.flush_history()
@ -142,16 +142,143 @@ def on_connection_end(reason="none"):
if isinstance(handler, logging.FileHandler):
logger.removeHandler(handler)
if reason != "user":
bpy.ops.session.notify('INVOKE_DEFAULT', message=f"Disconnected from session. Reason: {reason}. ")
bpy.ops.session.notify('INVOKE_DEFAULT', message=f"Disconnected from session. Reason: {reason}. ") #TODO: change op session.notify to add ui + change reason (in replication->interface)
def setup_logging():
""" Session setup logging (host/connect)
"""
settings = utils.get_preferences()
logger = logging.getLogger()
if len(logger.handlers) == 1:
formatter = logging.Formatter(
fmt='%(asctime)s CLIENT %(levelname)-8s %(message)s',
datefmt='%H:%M:%S'
)
start_time = datetime.now().strftime('%Y_%m_%d_%H-%M-%S')
log_directory = os.path.join(
settings.cache_directory,
f"multiuser_{start_time}.log")
os.makedirs(settings.cache_directory, exist_ok=True)
handler = logging.FileHandler(log_directory, mode='w')
logger.addHandler(handler)
for handler in logger.handlers:
if isinstance(handler, logging.NullHandler):
continue
handler.setFormatter(formatter)
def setup_timer():
""" Session setup timer (host/connect)
"""
settings = utils.get_preferences()
deleyables.append(timers.ClientUpdate())
deleyables.append(timers.DynamicRightSelectTimer())
deleyables.append(timers.ApplyTimer(timeout=settings.depsgraph_update_rate))
session_update = timers.SessionStatusUpdate()
session_user_sync = timers.SessionUserSync()
session_background_executor = timers.MainThreadExecutor(execution_queue=background_execution_queue)
session_listen = timers.SessionListenTimer(timeout=0.001)
session_listen.register()
session_update.register()
session_user_sync.register()
session_background_executor.register()
deleyables.append(session_background_executor)
deleyables.append(session_update)
deleyables.append(session_user_sync)
deleyables.append(session_listen)
deleyables.append(timers.AnnotationUpdates())
def get_active_server_preset(context):
active_index = context.window_manager.server_index
server_presets = utils.get_preferences().server_preset
active_index = active_index if active_index <= len(server_presets)-1 else 0
return server_presets[active_index]
# OPERATORS
class SessionStartOperator(bpy.types.Operator):
bl_idname = "session.start"
bl_label = "start"
class SessionConnectOperator(bpy.types.Operator):
bl_idname = "session.connect"
bl_label = "connect"
bl_description = "connect to a net server"
host: bpy.props.BoolProperty(default=False)
@classmethod
def poll(cls, context):
return True
def execute(self, context):
global deleyables
settings = utils.get_preferences()
users = bpy.data.window_managers['WinMan'].online_users
active_server = get_active_server_preset()
admin_pass = active_server.admin_password if active_server.use_admin_password else None
server_pass = active_server.server_password if active_server.use_server_password else None
users.clear()
deleyables.clear()
setup_logging()
bpy_protocol = bl_types.get_data_translation_protocol()
# Check if supported_datablocks are up to date before starting the
# the session
for dcc_type_id in bpy_protocol.implementations.keys():
if dcc_type_id not in settings.supported_datablocks:
logging.info(f"{dcc_type_id} not found, \
regenerate type settings...")
settings.generate_supported_types()
if bpy.app.version[1] >= 91:
python_binary_path = sys.executable
else:
python_binary_path = bpy.app.binary_path_python
repo = Repository(
rdp=bpy_protocol,
username=settings.username)
# Join a session
if not active_server.use_admin_password:
utils.clean_scene()
try:
porcelain.remote_add(
repo,
'origin',
active_server.ip,
active_server.port,
server_password=server_pass,
admin_password=admin_pass)
session.connect(
repository= repo,
timeout=settings.connection_timeout,
server_password=server_pass,
admin_password=admin_pass
)
except Exception as e:
self.report({'ERROR'}, str(e))
logging.error(str(e))
# Background client updates service
setup_timer()
return {"FINISHED"}
class SessionHostOperator(bpy.types.Operator):
bl_idname = "session.host"
bl_label = "host"
bl_description = "host server"
@classmethod
def poll(cls, context):
@ -163,33 +290,13 @@ class SessionStartOperator(bpy.types.Operator):
settings = utils.get_preferences()
runtime_settings = context.window_manager.session
users = bpy.data.window_managers['WinMan'].online_users
admin_pass = settings.password
admin_pass = settings.host_admin_password if settings.host_use_admin_password else None
server_pass = settings.host_server_password if settings.host_use_server_password else None
users.clear()
deleyables.clear()
logger = logging.getLogger()
if len(logger.handlers) == 1:
formatter = logging.Formatter(
fmt='%(asctime)s CLIENT %(levelname)-8s %(message)s',
datefmt='%H:%M:%S'
)
start_time = datetime.now().strftime('%Y_%m_%d_%H-%M-%S')
log_directory = os.path.join(
settings.cache_directory,
f"multiuser_{start_time}.log")
os.makedirs(settings.cache_directory, exist_ok=True)
handler = logging.FileHandler(log_directory, mode='w')
logger.addHandler(handler)
for handler in logger.handlers:
if isinstance(handler, logging.NullHandler):
continue
handler.setFormatter(formatter)
setup_logging()
bpy_protocol = bl_types.get_data_translation_protocol()
@ -212,80 +319,38 @@ class SessionStartOperator(bpy.types.Operator):
username=settings.username)
# Host a session
if self.host:
if settings.init_method == 'EMPTY':
utils.clean_scene()
if settings.init_method == 'EMPTY':
utils.clean_scene()
runtime_settings.is_host = True
runtime_settings.internet_ip = environment.get_ip()
try:
# Init repository
for scene in bpy.data.scenes:
porcelain.add(repo, scene)
try:
# Init repository
for scene in bpy.data.scenes:
porcelain.add(repo, scene)
porcelain.remote_add(
repo,
'origin',
'127.0.0.1',
settings.port,
admin_password=admin_pass)
session.host(
repository= repo,
remote='origin',
timeout=settings.connection_timeout,
password=admin_pass,
cache_directory=settings.cache_directory,
server_log_level=logging.getLevelName(
logging.getLogger().level),
)
except Exception as e:
self.report({'ERROR'}, repr(e))
logging.error(f"Error: {e}")
traceback.print_exc()
# Join a session
else:
if not runtime_settings.admin:
utils.clean_scene()
# regular session, no password needed
admin_pass = None
try:
porcelain.remote_add(
repo,
'origin',
settings.ip,
settings.port,
admin_password=admin_pass)
session.connect(
repository= repo,
timeout=settings.connection_timeout,
password=admin_pass
)
except Exception as e:
self.report({'ERROR'}, str(e))
logging.error(str(e))
porcelain.remote_add(
repo,
'origin',
'127.0.0.1',
settings.host_port,
server_password=server_pass,
admin_password=admin_pass)
session.host(
repository= repo,
remote='origin',
timeout=settings.connection_timeout,
server_password=server_pass,
admin_password=admin_pass,
cache_directory=settings.cache_directory,
server_log_level=logging.getLevelName(
logging.getLogger().level),
)
except Exception as e:
self.report({'ERROR'}, repr(e))
logging.error(f"Error: {e}")
traceback.print_exc()
# Background client updates service
deleyables.append(timers.ClientUpdate())
deleyables.append(timers.DynamicRightSelectTimer())
deleyables.append(timers.ApplyTimer(timeout=settings.depsgraph_update_rate))
session_update = timers.SessionStatusUpdate()
session_user_sync = timers.SessionUserSync()
session_background_executor = timers.MainThreadExecutor(execution_queue=background_execution_queue)
session_listen = timers.SessionListenTimer(timeout=0.001)
session_listen.register()
session_update.register()
session_user_sync.register()
session_background_executor.register()
deleyables.append(session_background_executor)
deleyables.append(session_update)
deleyables.append(session_user_sync)
deleyables.append(session_listen)
deleyables.append(timers.AnnotationUpdates())
setup_timer()
return {"FINISHED"}
@ -783,7 +848,6 @@ class SessionStopAutoSaveOperator(bpy.types.Operator):
return {'FINISHED'}
class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper):
bl_idname = "session.load"
bl_label = "Load session save"
@ -831,12 +895,90 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper):
class SessionPresetServerAdd(bpy.types.Operator):
"""Add a server to the server list preset"""
bl_idname = "session.preset_server_add"
bl_label = "add server preset"
bl_description = "add the current server to the server preset list"
bl_label = "Add server preset"
bl_description = "add a server to the server preset list"
bl_options = {"REGISTER"}
name : bpy.props.StringProperty(default="server_preset")
server_name: bpy.props.StringProperty(default="")
ip: bpy.props.StringProperty(default="127.0.0.1")
port: bpy.props.IntProperty(default=5555)
use_server_password: bpy.props.BoolProperty(default=False)
server_password: bpy.props.StringProperty(default="", subtype = "PASSWORD")
use_admin_password: bpy.props.BoolProperty(default=False)
admin_password: bpy.props.StringProperty(default="", subtype = "PASSWORD")
@classmethod
def poll(cls, context):
return True
def invoke(self, context, event):
self.server_name = ""
self.ip = "127.0.0.1"
self.port = 5555
self.use_server_password = False
self.server_password = ""
self.use_admin_password = False
self.admin_password = ""
assert(context)
return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
layout = self.layout
row = layout.row()
row.prop(self, "server_name", text="Server name")
row = layout.row(align = True)
row.prop(self, "ip", text="IP+port")
row.prop(self, "port", text="")
row = layout.row()
col = row.column()
col.prop(self, "use_server_password", text="Server password:")
col = row.column()
col.enabled = True if self.use_server_password else False
col.prop(self, "server_password", text="")
row = layout.row()
col = row.column()
col.prop(self, "use_admin_password", text="Admin password:")
col = row.column()
col.enabled = True if self.use_admin_password else False
col.prop(self, "admin_password", text="")
def execute(self, context):
assert(context)
settings = utils.get_preferences()
existing_preset = settings.get_server_preset(self.server_name)
new_server = existing_preset if existing_preset else settings.server_preset.add()
new_server.name = str(uuid4())
new_server.server_name = self.server_name
new_server.ip = self.ip
new_server.port = self.port
new_server.use_server_password = self.use_server_password
new_server.server_password = self.server_password
new_server.use_admin_password = self.use_admin_password
new_server.admin_password = self.admin_password
refresh_sidebar_view()
if new_server == existing_preset :
self.report({'INFO'}, "Server '" + self.server_name + "' edited")
else :
self.report({'INFO'}, "New '" + self.server_name + "' server preset")
return {'FINISHED'}
class SessionPresetServerEdit(bpy.types.Operator): # TODO : use preset, not settings
"""Edit a server to the server list preset"""
bl_idname = "session.preset_server_edit"
bl_label = "Edit server preset"
bl_description = "Edit a server from the server preset list"
bl_options = {"REGISTER"}
target_server_name: bpy.props.StringProperty(default="None")
@classmethod
def poll(cls, context):
return True
@ -847,35 +989,39 @@ class SessionPresetServerAdd(bpy.types.Operator):
def draw(self, context):
layout = self.layout
col = layout.column()
settings = utils.get_preferences()
settings_active_server = settings.server_preset.get(self.target_server_name)
col.prop(settings, "server_name", text="server name")
row = layout.row()
row.prop(settings_active_server, "server_name", text="Server name")
row = layout.row(align = True)
row.prop(settings_active_server, "ip", text="IP+port")
row.prop(settings_active_server, "port", text="")
row = layout.row()
col = row.column()
col.prop(settings_active_server, "use_server_password", text="Server password:")
col = row.column()
col.enabled = True if settings_active_server.use_server_password else False
col.prop(settings_active_server, "server_password", text="")
row = layout.row()
col = row.column()
col.prop(settings_active_server, "use_admin_password", text="Admin password:")
col = row.column()
col.enabled = True if settings_active_server.use_admin_password else False
col.prop(settings_active_server, "admin_password", text="")
def execute(self, context):
assert(context)
settings = utils.get_preferences()
settings_active_server = settings.server_preset.get(self.target_server_name)
existing_preset = settings.server_preset.get(settings.server_name)
refresh_sidebar_view()
new_server = existing_preset if existing_preset else settings.server_preset.add()
new_server.name = settings.server_name
new_server.server_ip = settings.ip
new_server.server_port = settings.port
new_server.server_password = settings.password
settings.server_preset_interface = settings.server_name
if new_server == existing_preset :
self.report({'INFO'}, "Server '" + settings.server_name + "' override")
else :
self.report({'INFO'}, "New '" + settings.server_name + "' server preset")
self.report({'INFO'}, "Server '" + settings_active_server.server_name + "' edited")
return {'FINISHED'}
class SessionPresetServerRemove(bpy.types.Operator):
"""Remove a server to the server list preset"""
bl_idname = "session.preset_server_remove"
@ -883,6 +1029,8 @@ class SessionPresetServerRemove(bpy.types.Operator):
bl_description = "remove the current server from the server preset list"
bl_options = {"REGISTER"}
target_server_name: bpy.props.StringProperty(default="None")
@classmethod
def poll(cls, context):
return True
@ -891,19 +1039,77 @@ class SessionPresetServerRemove(bpy.types.Operator):
assert(context)
settings = utils.get_preferences()
settings.server_preset.remove(settings.server_preset.find(settings.server_preset_interface))
settings.server_preset.remove(settings.server_preset.find(self.target_server_name))
return {'FINISHED'}
class RefreshServerStatus(bpy.types.Operator):
bl_idname = "session.get_info"
bl_label = "Get session info"
bl_description = "Get session info"
target_server: bpy.props.StringProperty(default="127.0.0.1:5555")
@classmethod
def poll(cls, context):
return (session.state != STATE_ACTIVE)
def execute(self, context):
settings = utils.get_preferences()
for server in settings.server_preset:
infos = porcelain.request_session_info(f"{server.ip}:{server.port}", timeout=settings.ping_timeout)
server.is_online = True if infos else False
if server.is_online:
server.is_private = infos.get("private")
return {'FINISHED'}
class GetDoc(bpy.types.Operator):
"""Get the documentation of the addon"""
bl_idname = "doc.get"
bl_label = "Multi-user's doc"
bl_description = "Go to the doc of the addon"
@classmethod
def poll(cls, context):
return True
def execute(self, context):
assert(context)
bpy.ops.wm.url_open(url="https://slumber.gitlab.io/multi-user/index.html")
return {'FINISHED'}
class FirstLaunch(bpy.types.Operator):
"""First time lauching the addon"""
bl_idname = "firstlaunch.verify"
bl_label = "First launch"
bl_description = "First time lauching the addon"
@classmethod
def poll(cls, context):
return True
def execute(self, context):
assert(context)
settings = utils.get_preferences()
settings.is_first_launch = False
settings.server_preset.clear()
prefs = bpy.context.preferences.addons[__package__].preferences
prefs.generate_default_presets()
return {'FINISHED'}
def menu_func_import(self, context):
self.layout.operator(SessionLoadSaveOperator.bl_idname, text='Multi-user session snapshot (.db)')
def menu_func_export(self, context):
self.layout.operator(SessionSaveBackupOperator.bl_idname, text='Multi-user session snapshot (.db)')
classes = (
SessionStartOperator,
SessionConnectOperator,
SessionHostOperator,
SessionStopOperator,
SessionPropertyRemoveOperator,
SessionSnapUserOperator,
@ -920,7 +1126,11 @@ classes = (
SessionStopAutoSaveOperator,
SessionPurgeOperator,
SessionPresetServerAdd,
SessionPresetServerEdit,
SessionPresetServerRemove,
RefreshServerStatus,
GetDoc,
FirstLaunch,
)

View File

@ -17,6 +17,7 @@
import random
import logging
from uuid import uuid4
import bpy
import string
import re
@ -25,7 +26,7 @@ import os
from pathlib import Path
from . import bl_types, environment, addon_updater_ops, presence, ui
from .utils import get_preferences, get_expanded_icon
from .utils import get_preferences, get_expanded_icon, get_folder_size
from replication.constants import RP_COMMON
from replication.interface import session
@ -33,15 +34,21 @@ from replication.interface import session
IP_REGEX = re.compile("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$")
HOSTNAME_REGEX = re.compile("^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$")
#SERVER PRESETS AT LAUNCH
DEFAULT_PRESETS = {
"localhost" : {
"server_ip": "localhost",
"server_port": 5555,
"server_password": "admin"
"server_name": "localhost",
"ip": "localhost",
"port": 5555,
"use_admin_password": True,
"admin_password": "admin",
"server_password": ""
},
"public session" : {
"server_ip": "51.75.71.183",
"server_port": 5555,
"server_name": "public session",
"ip": "51.75.71.183",
"port": 5555,
"admin_password": "",
"server_password": ""
},
}
@ -78,11 +85,6 @@ def update_ip(self, context):
logging.error("Wrong IP format")
self['ip'] = "127.0.0.1"
def update_server_preset_interface(self, context):
self.server_name = self.server_preset.get(self.server_preset_interface).name
self.ip = self.server_preset.get(self.server_preset_interface).server_ip
self.port = self.server_preset.get(self.server_preset_interface).server_port
self.password = self.server_preset.get(self.server_preset_interface).server_password
def update_directory(self, context):
new_dir = Path(self.cache_directory)
@ -110,9 +112,15 @@ class ReplicatedDatablock(bpy.types.PropertyGroup):
icon: bpy.props.StringProperty()
class ServerPreset(bpy.types.PropertyGroup):
server_ip: bpy.props.StringProperty()
server_port: bpy.props.IntProperty(default=5555)
server_password: bpy.props.StringProperty(default="admin", subtype = "PASSWORD")
server_name: bpy.props.StringProperty(default="")
ip: bpy.props.StringProperty(default="127.0.0.1", update=update_ip)
port: bpy.props.IntProperty(default=5555)
use_server_password: bpy.props.BoolProperty(default=False)
server_password: bpy.props.StringProperty(default="", subtype = "PASSWORD")
use_admin_password: bpy.props.BoolProperty(default=False)
admin_password: bpy.props.StringProperty(default="", subtype = "PASSWORD")
is_online: bpy.props.BoolProperty(default=False)
is_private: bpy.props.BoolProperty(default=False)
def set_sync_render_settings(self, value):
self['sync_render_settings'] = value
@ -162,35 +170,60 @@ class ReplicationFlags(bpy.types.PropertyGroup):
class SessionPrefs(bpy.types.AddonPreferences):
bl_idname = __package__
ip: bpy.props.StringProperty(
name="ip",
description='Distant host ip',
default="localhost",
update=update_ip)
# User settings
username: bpy.props.StringProperty(
name="Username",
default=f"user_{random_string_digits()}"
)
client_color: bpy.props.FloatVectorProperty(
name="client_instance_color",
description='User color',
subtype='COLOR',
default=randomColor())
port: bpy.props.IntProperty(
name="port",
description='Distant host port',
default=5555
default=randomColor()
)
# Current server settings
server_name: bpy.props.StringProperty(
name="server_name",
description="Custom name of the server",
default='localhost',
)
password: bpy.props.StringProperty(
name="password",
default=random_string_digits(),
server_index: bpy.props.IntProperty(
name="server_index",
description="index of the server",
)
# User host session settings
host_port: bpy.props.IntProperty(
name="host_port",
description='Distant host port',
default=5555
)
host_use_server_password: bpy.props.BoolProperty(
name="use_server_password",
description='Use session password',
default=False
)
host_server_password: bpy.props.StringProperty(
name="server_password",
description='Session password',
subtype='PASSWORD'
)
host_use_admin_password: bpy.props.BoolProperty(
name="use_admin_password",
description='Use admin password',
default=True
)
host_admin_password: bpy.props.StringProperty(
name="admin_password",
description='Admin password',
subtype='PASSWORD',
default='admin'
)
# Other
is_first_launch: bpy.props.BoolProperty(
name="is_fnirst_launch",
description="First time lauching the addon",
default=True
)
sync_flags: bpy.props.PointerProperty(
type=ReplicationFlags
)
@ -214,6 +247,11 @@ class SessionPrefs(bpy.types.AddonPreferences):
description='connection timeout before disconnection',
default=5000
)
ping_timeout: bpy.props.IntProperty(
name='ping timeout',
description='check if servers are online',
default=500
)
# Replication update settings
depsgraph_update_rate: bpy.props.FloatProperty(
name='depsgraph update rate (s)',
@ -225,11 +263,12 @@ class SessionPrefs(bpy.types.AddonPreferences):
description="Remove filecache from memory",
default=False
)
# for UI
# For UI
category: bpy.props.EnumProperty(
name="Category",
description="Preferences Category",
items=[
('PREF', "Preferences", "Preferences of this add-on"),
('CONFIG', "Configuration", "Configuration of this add-on"),
('UPDATE', "Update", "Update this add-on"),
],
@ -273,38 +312,58 @@ class SessionPrefs(bpy.types.AddonPreferences):
step=1,
subtype='PERCENTAGE',
)
presence_mode_distance: bpy.props.FloatProperty(
name="Distance mode visibilty",
description="Adjust the distance visibilty of user's mode",
presence_text_distance: bpy.props.FloatProperty(
name="Distance text visibilty",
description="Adjust the distance visibilty of user's mode/name",
min=0.1,
max=1000,
max=10000,
default=100,
)
conf_session_identity_expanded: bpy.props.BoolProperty(
name="Identity",
description="Identity",
default=True
default=False
)
conf_session_net_expanded: bpy.props.BoolProperty(
name="Net",
description="net",
default=True
default=False
)
conf_session_hosting_expanded: bpy.props.BoolProperty(
name="Rights",
description="Rights",
default=False
)
conf_session_rep_expanded: bpy.props.BoolProperty(
name="Replication",
description="Replication",
default=False
)
conf_session_cache_expanded: bpy.props.BoolProperty(
name="Cache",
description="cache",
default=False
)
conf_session_log_expanded: bpy.props.BoolProperty(
name="conf_session_log_expanded",
description="conf_session_log_expanded",
default=False
)
conf_session_ui_expanded: bpy.props.BoolProperty(
name="Interface",
description="Interface",
default=False
)
sidebar_repository_shown: bpy.props.BoolProperty(
name="sidebar_repository_shown",
description="sidebar_repository_shown",
default=False
)
sidebar_advanced_shown: bpy.props.BoolProperty(
name="sidebar_advanced_shown",
description="sidebar_advanced_shown",
default=False
)
sidebar_advanced_rep_expanded: bpy.props.BoolProperty(
name="sidebar_advanced_rep_expanded",
description="sidebar_advanced_rep_expanded",
@ -315,6 +374,11 @@ class SessionPrefs(bpy.types.AddonPreferences):
description="sidebar_advanced_log_expanded",
default=False
)
sidebar_advanced_hosting_expanded: bpy.props.BoolProperty(
name="sidebar_advanced_hosting_expanded",
description="sidebar_advanced_hosting_expanded",
default=False
)
sidebar_advanced_net_expanded: bpy.props.BoolProperty(
name="sidebar_advanced_net_expanded",
description="sidebar_advanced_net_expanded",
@ -371,12 +435,6 @@ class SessionPrefs(bpy.types.AddonPreferences):
name="server preset",
type=ServerPreset,
)
server_preset_interface: bpy.props.EnumProperty(
name="servers",
description="servers enum",
items=server_list_callback,
update=update_server_preset_interface,
)
# Custom panel
panel_category: bpy.props.StringProperty(
@ -386,38 +444,28 @@ class SessionPrefs(bpy.types.AddonPreferences):
def draw(self, context):
layout = self.layout
layout.row().prop(self, "category", expand=True)
if self.category == 'PREF':
grid = layout.column()
box = grid.box()
row = box.row()
# USER SETTINGS
split = row.split(factor=0.7, align=True)
split.prop(self, "username", text="User")
split.prop(self, "client_color", text="")
row = box.row()
row.label(text="Hide settings:")
row = box.row()
row.prop(self, "sidebar_advanced_shown", text="Hide “Advanced” settings in side pannel (Not in session)")
row = box.row()
row.prop(self, "sidebar_repository_shown", text="Hide “Repository” settings in side pannel (In session)")
if self.category == 'CONFIG':
grid = layout.column()
# USER INFORMATIONS
box = grid.box()
box.prop(
self, "conf_session_identity_expanded", text="User information",
icon=get_expanded_icon(self.conf_session_identity_expanded),
emboss=False)
if self.conf_session_identity_expanded:
box.row().prop(self, "username", text="name")
box.row().prop(self, "client_color", text="color")
# NETWORK SETTINGS
box = grid.box()
box.prop(
self, "conf_session_net_expanded", text="Networking",
icon=get_expanded_icon(self.conf_session_net_expanded),
emboss=False)
if self.conf_session_net_expanded:
box.row().prop(self, "ip", text="Address")
row = box.row()
row.label(text="Port:")
row.prop(self, "port", text="")
row = box.row()
row.label(text="Init the session from:")
row.prop(self, "init_method", text="")
# HOST SETTINGS
box = grid.box()
box.prop(
@ -425,9 +473,57 @@ class SessionPrefs(bpy.types.AddonPreferences):
icon=get_expanded_icon(self.conf_session_hosting_expanded),
emboss=False)
if self.conf_session_hosting_expanded:
row = box.row()
row.prop(self, "host_port", text="Port: ")
row = box.row()
row.label(text="Init the session from:")
row.prop(self, "init_method", text="")
row = box.row()
col = row.column()
col.prop(self, "host_use_server_password", text="Server password:")
col = row.column()
col.enabled = True if self.host_use_server_password else False
col.prop(self, "host_server_password", text="")
row = box.row()
col = row.column()
col.prop(self, "host_use_admin_password", text="Admin password:")
col = row.column()
col.enabled = True if self.host_use_admin_password else False
col.prop(self, "host_admin_password", text="")
# NETWORKING
box = grid.box()
box.prop(
self, "conf_session_net_expanded", text="Network",
icon=get_expanded_icon(self.conf_session_net_expanded),
emboss=False)
if self.conf_session_net_expanded:
row = box.row()
row.label(text="Timeout (ms):")
row.prop(self, "connection_timeout", text="")
row = box.row()
row.label(text="Server ping (ms):")
row.prop(self, "ping_timeout", text="")
# REPLICATION
box = grid.box()
box.prop(
self, "conf_session_rep_expanded", text="Replication",
icon=get_expanded_icon(self.conf_session_rep_expanded),
emboss=False)
if self.conf_session_rep_expanded:
row = box.row()
row.prop(self.sync_flags, "sync_render_settings")
row = box.row()
row.prop(self.sync_flags, "sync_active_camera")
row = box.row()
row.prop(self.sync_flags, "sync_during_editmode")
row = box.row()
if self.sync_flags.sync_during_editmode:
warning = row.box()
warning.label(text="Don't use this with heavy meshes !", icon='ERROR')
row = box.row()
row.prop(self, "depsgraph_update_rate", text="Apply delay")
# CACHE SETTINGS
box = grid.box()
@ -438,25 +534,18 @@ class SessionPrefs(bpy.types.AddonPreferences):
if self.conf_session_cache_expanded:
box.row().prop(self, "cache_directory", text="Cache directory")
box.row().prop(self, "clear_memory_filecache", text="Clear memory filecache")
# INTERFACE SETTINGS
box.row().operator('session.clear_cache', text=f"Clear cache ({get_folder_size(self.cache_directory)})")
# LOGGING
box = grid.box()
box.prop(
self, "conf_session_ui_expanded", text="Interface",
icon=get_expanded_icon(self.conf_session_ui_expanded),
self, "conf_session_log_expanded", text="Logging",
icon=get_expanded_icon(self.conf_session_log_expanded),
emboss=False)
if self.conf_session_ui_expanded:
box.row().prop(self, "panel_category", text="Panel category", expand=True)
if self.conf_session_log_expanded:
row = box.row()
row.label(text="Session widget:")
col = box.column(align=True)
col.prop(self, "presence_hud_scale", expand=True)
col.prop(self, "presence_hud_hpos", expand=True)
col.prop(self, "presence_hud_vpos", expand=True)
col.prop(self, "presence_mode_distance", expand=True)
row.label(text="Log level:")
row.prop(self, 'logging_level', text="")
if self.category == 'UPDATE':
from . import addon_updater_ops
@ -477,18 +566,31 @@ class SessionPrefs(bpy.types.AddonPreferences):
new_db.icon = impl.bl_icon
new_db.bl_name = impl.bl_id
# Get a server preset through its name
def get_server_preset(self, name):
existing_preset = None
# custom at launch server preset
for server_preset in self.server_preset :
if server_preset.server_name == name :
existing_preset = server_preset
return existing_preset
# Custom at launch server preset
def generate_default_presets(self):
for preset_name, preset_data in DEFAULT_PRESETS.items():
existing_preset = self.server_preset.get(preset_name)
existing_preset = self.get_server_preset(preset_name)
if existing_preset :
continue
new_server = self.server_preset.add()
new_server.name = preset_name
new_server.server_ip = preset_data.get('server_ip')
new_server.server_port = preset_data.get('server_port')
new_server.name = str(uuid4())
new_server.server_name = preset_data.get('server_name')
new_server.ip = preset_data.get('ip')
new_server.port = preset_data.get('port')
new_server.use_server_password = preset_data.get('use_server_password',False)
new_server.server_password = preset_data.get('server_password',None)
new_server.use_admin_password = preset_data.get('use_admin_password',False)
new_server.admin_password = preset_data.get('admin_password',None)
def client_list_callback(scene, context):
@ -576,11 +678,6 @@ class SessionProps(bpy.types.PropertyGroup):
description='Connect as admin',
default=False
)
internet_ip: bpy.props.StringProperty(
name="internet ip",
default="no found",
description='Internet interface ip',
)
user_snap_running: bpy.props.BoolProperty(
default=False
)

View File

@ -16,7 +16,9 @@
# ##### END GPL LICENSE BLOCK #####
from logging import log
import bpy
import bpy.utils.previews
from .utils import get_preferences, get_expanded_icon, get_folder_size, get_state_str
from replication.constants import (ADDED, ERROR, FETCHED,
@ -71,157 +73,124 @@ class SESSION_PT_settings(bpy.types.Panel):
def draw_header(self, context):
layout = self.layout
settings = get_preferences()
from multi_user import icons
offline_icon = icons.icons_col["session_status_offline"]
waiting_icon = icons.icons_col["session_status_waiting"]
online_icon = icons.icons_col["session_status_online"]
if session and session.state != STATE_INITIAL:
cli_state = session.state
state = session.state
connection_icon = "KEYTYPE_MOVING_HOLD_VEC"
connection_icon = offline_icon
if state == STATE_ACTIVE:
connection_icon = 'PROP_ON'
connection_icon = online_icon
else:
connection_icon = 'PROP_CON'
connection_icon = waiting_icon
layout.label(text=f"Session - {get_state_str(cli_state)}", icon=connection_icon)
layout.label(text=f"{str(settings.server_name)} - {get_state_str(cli_state)}", icon_value=connection_icon.icon_id)
else:
layout.label(text=f"Session - v{__version__}",icon="PROP_OFF")
layout.label(text=f"Multi-user - v{__version__}", icon="ANTIALIASED")
def draw(self, context):
layout = self.layout
row = layout.row()
runtime_settings = context.window_manager.session
settings = get_preferences()
if hasattr(context.window_manager, 'session'):
# STATE INITIAL
if not session \
or (session and session.state == STATE_INITIAL):
pass
else:
progress = session.state_progress
row = layout.row()
if settings.is_first_launch:
# USER SETTINGS
row = layout.row()
row.label(text="1. Enter your username and color:")
row = layout.row()
split = row.split(factor=0.7, align=True)
split.prop(settings, "username", text="")
split.prop(settings, "client_color", text="")
current_state = session.state
info_msg = None
# DOC
row = layout.row()
row.label(text="2. New here ? See the doc:")
row = layout.row()
row.operator("doc.get", text="Documentation", icon="HELP")
# START
row = layout.row()
row.label(text="3: Start the Multi-user:")
row = layout.row()
row.scale_y = 2
row.operator("firstlaunch.verify", text="Continue")
if not settings.is_first_launch:
if hasattr(context.window_manager, 'session'):
# STATE INITIAL
if not session \
or (session and session.state == STATE_INITIAL):
layout = self.layout
settings = get_preferences()
server_preset = settings.server_preset
selected_server = context.window_manager.server_index if context.window_manager.server_index<=len(server_preset)-1 else 0
active_server_name = server_preset[selected_server].name if len(server_preset)>=1 else ""
is_server_selected = True if active_server_name else False
if current_state in [STATE_ACTIVE]:
row = row.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
row.prop(settings.sync_flags, "sync_render_settings",text="",icon_only=True, icon='SCENE')
row.prop(settings.sync_flags, "sync_during_editmode", text="",icon_only=True, icon='EDITMODE_HLT')
row.prop(settings.sync_flags, "sync_active_camera", text="",icon_only=True, icon='VIEW_CAMERA')
# SERVER LIST
row = layout.row()
box = row.box()
box.scale_y = 0.7
split = box.split(factor=0.7)
split.label(text="Server")
split.label(text="Online")
row= layout.row()
col = row.column(align=True)
col.operator("session.get_info", icon="FILE_REFRESH", text="")
if current_state in [STATE_ACTIVE] and runtime_settings.is_host:
info_msg = f"LAN: {runtime_settings.internet_ip}"
if current_state == STATE_LOBBY:
info_msg = "Waiting for the session to start."
row = layout.row()
col = row.column(align=True)
col.template_list("SESSION_UL_network", "", settings, "server_preset", context.window_manager, "server_index")
col.separator()
connectOp = col.row()
connectOp.operator("session.host", text="Host")
connectopcol = connectOp.column()
connectopcol.enabled =is_server_selected
connectopcol.operator("session.connect", text="Connect")
if info_msg:
info_box = row.box()
info_box.row().label(text=info_msg,icon='INFO')
col = row.column(align=True)
col.operator("session.preset_server_add", icon="ADD", text="") # TODO : add conditions (need a name, etc..)
row_visible = col.row(align=True)
col_visible = row_visible.column(align=True)
col_visible.enabled = is_server_selected
col_visible.operator("session.preset_server_remove", icon="REMOVE", text="").target_server_name = active_server_name
col_visible.separator()
col_visible.operator("session.preset_server_edit", icon="GREASEPENCIL", text="").target_server_name = active_server_name
# Progress bar
if current_state in [STATE_SYNCING, STATE_SRV_SYNC, STATE_WAITING]:
info_box = row.box()
info_box.row().label(text=printProgressBar(
progress['current'],
progress['total'],
length=16
))
else:
exitbutton = layout.row()
exitbutton.scale_y = 1.5
exitbutton.operator("session.stop", icon='QUIT', text="Disconnect")
layout.row().operator("session.stop", icon='QUIT', text="Exit")
progress = session.state_progress
current_state = session.state
info_msg = None
if current_state == STATE_LOBBY:
row= layout.row()
info_msg = "Waiting for the session to start."
class SESSION_PT_settings_network(bpy.types.Panel):
bl_idname = "MULTIUSER_SETTINGS_NETWORK_PT_panel"
bl_label = "Network"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
if info_msg:
info_box = row.box()
info_box.row().label(text=info_msg,icon='INFO')
@classmethod
def poll(cls, context):
return not session \
or (session and session.state == 0)
def draw_header(self, context):
self.layout.label(text="", icon='URL')
def draw(self, context):
layout = self.layout
runtime_settings = context.window_manager.session
settings = get_preferences()
# USER SETTINGS
row = layout.row()
row.prop(runtime_settings, "session_mode", expand=True)
row = layout.row()
col = row.row(align=True)
col.prop(settings, "server_preset_interface", text="")
col.operator("session.preset_server_add", icon='ADD', text="")
col.operator("session.preset_server_remove", icon='REMOVE', text="")
row = layout.row()
box = row.box()
if runtime_settings.session_mode == 'HOST':
row = box.row()
row.label(text="Port:")
row.prop(settings, "port", text="")
row = box.row()
row.label(text="Start from:")
row.prop(settings, "init_method", text="")
row = box.row()
row.label(text="Admin password:")
row.prop(settings, "password", text="")
row = box.row()
row.operator("session.start", text="HOST").host = True
else:
row = box.row()
row.prop(settings, "ip", text="IP")
row = box.row()
row.label(text="Port:")
row.prop(settings, "port", text="")
row = box.row()
row.prop(runtime_settings, "admin", text='Connect as admin', icon='DISCLOSURE_TRI_DOWN' if runtime_settings.admin
else 'DISCLOSURE_TRI_RIGHT')
if runtime_settings.admin:
row = box.row()
row.label(text="Password:")
row.prop(settings, "password", text="")
row = box.row()
row.operator("session.start", text="CONNECT").host = False
class SESSION_PT_settings_user(bpy.types.Panel):
bl_idname = "MULTIUSER_SETTINGS_USER_PT_panel"
bl_label = "User info"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
@classmethod
def poll(cls, context):
return not session \
or (session and session.state == 0)
def draw_header(self, context):
self.layout.label(text="", icon='USER')
def draw(self, context):
layout = self.layout
runtime_settings = context.window_manager.session
settings = get_preferences()
row = layout.row()
# USER SETTINGS
row.prop(settings, "username", text="name")
row = layout.row()
row.prop(settings, "client_color", text="color")
row = layout.row()
# PROGRESS BAR
if current_state in [STATE_SYNCING, STATE_SRV_SYNC, STATE_WAITING]:
row= layout.row()
row.label(text=f"Status: {get_state_str(current_state)}")
row= layout.row()
info_box = row.box()
info_box.label(text=printProgressBar(
progress['current'],
progress['total'],
length=16
))
class SESSION_PT_advanced_settings(bpy.types.Panel):
@ -234,19 +203,45 @@ class SESSION_PT_advanced_settings(bpy.types.Panel):
@classmethod
def poll(cls, context):
settings = get_preferences()
return not session \
or (session and session.state == 0)
or (session and session.state == 0) \
and not settings.sidebar_advanced_shown \
and not settings.is_first_launch
def draw_header(self, context):
self.layout.label(text="", icon='PREFERENCES')
def draw(self, context):
layout = self.layout
runtime_settings = context.window_manager.session
settings = get_preferences()
#ADVANCED HOST
host_selection = layout.row().box()
host_selection.prop(
settings, "sidebar_advanced_hosting_expanded", text="Hosting",
icon=get_expanded_icon(settings.sidebar_advanced_hosting_expanded),
emboss=False)
if settings.sidebar_advanced_hosting_expanded:
host_selection_row = host_selection.row()
host_selection_row.prop(settings, "host_port", text="Port:")
host_selection_row = host_selection.row()
host_selection_row.label(text="Init the session from:")
host_selection_row.prop(settings, "init_method", text="")
host_selection_row = host_selection.row()
host_selection_col = host_selection_row.column()
host_selection_col.prop(settings, "host_use_server_password", text="Server password:")
host_selection_col = host_selection_row.column()
host_selection_col.enabled = True if settings.host_use_server_password else False
host_selection_col.prop(settings, "host_server_password", text="")
host_selection_row = host_selection.row()
host_selection_col = host_selection_row.column()
host_selection_col.prop(settings, "host_use_admin_password", text="Admin password:")
host_selection_col = host_selection_row.column()
host_selection_col.enabled = True if settings.host_use_admin_password else False
host_selection_col.prop(settings, "host_admin_password", text="")
#ADVANCED NET
net_section = layout.row().box()
net_section.prop(
settings,
@ -254,12 +249,15 @@ class SESSION_PT_advanced_settings(bpy.types.Panel):
text="Network",
icon=get_expanded_icon(settings.sidebar_advanced_net_expanded),
emboss=False)
if settings.sidebar_advanced_net_expanded:
net_section_row = net_section.row()
net_section_row.label(text="Timeout (ms):")
net_section_row.prop(settings, "connection_timeout", text="")
net_section_row = net_section.row()
net_section_row.label(text="Server ping (ms):")
net_section_row.prop(settings, "ping_timeout", text="")
#ADVANCED REPLICATION
replication_section = layout.row().box()
replication_section.prop(
settings,
@ -267,16 +265,12 @@ class SESSION_PT_advanced_settings(bpy.types.Panel):
text="Replication",
icon=get_expanded_icon(settings.sidebar_advanced_rep_expanded),
emboss=False)
if settings.sidebar_advanced_rep_expanded:
replication_section_row = replication_section.row()
replication_section_row = replication_section.row()
replication_section_row.prop(settings.sync_flags, "sync_render_settings")
replication_section_row = replication_section.row()
replication_section_row.prop(settings.sync_flags, "sync_active_camera")
replication_section_row = replication_section.row()
replication_section_row.prop(settings.sync_flags, "sync_during_editmode")
replication_section_row = replication_section.row()
if settings.sync_flags.sync_during_editmode:
@ -285,7 +279,7 @@ class SESSION_PT_advanced_settings(bpy.types.Panel):
replication_section_row = replication_section.row()
replication_section_row.prop(settings, "depsgraph_update_rate", text="Apply delay")
#ADVANCED CACHE
cache_section = layout.row().box()
cache_section.prop(
settings,
@ -303,6 +297,8 @@ class SESSION_PT_advanced_settings(bpy.types.Panel):
cache_section_row.prop(settings, "clear_memory_filecache", text="")
cache_section_row = cache_section.row()
cache_section_row.operator('session.clear_cache', text=f"Clear cache ({get_folder_size(settings.cache_directory)})")
#ADVANCED LOG
log_section = layout.row().box()
log_section.prop(
settings,
@ -310,11 +306,11 @@ class SESSION_PT_advanced_settings(bpy.types.Panel):
text="Logging",
icon=get_expanded_icon(settings.sidebar_advanced_log_expanded),
emboss=False)
if settings.sidebar_advanced_log_expanded:
log_section_row = log_section.row()
log_section_row.label(text="Log level:")
log_section_row.prop(settings, 'logging_level', text="")
class SESSION_PT_user(bpy.types.Panel):
bl_idname = "MULTIUSER_USER_PT_panel"
bl_label = "Online users"
@ -324,7 +320,8 @@ class SESSION_PT_user(bpy.types.Panel):
@classmethod
def poll(cls, context):
return session and session.state in [STATE_ACTIVE, STATE_LOBBY]
return session \
and session.state in [STATE_ACTIVE, STATE_LOBBY]
def draw_header(self, context):
self.layout.label(text="", icon='USER')
@ -336,9 +333,8 @@ class SESSION_PT_user(bpy.types.Panel):
settings = get_preferences()
active_user = online_users[selected_user] if len(
online_users)-1 >= selected_user else 0
runtime_settings = context.window_manager.session
# Create a simple row.
#USER LIST
row = layout.row()
box = row.box()
split = box.split(factor=0.35)
@ -353,6 +349,7 @@ class SESSION_PT_user(bpy.types.Panel):
layout.template_list("SESSION_UL_users", "", context.window_manager,
"online_users", context.window_manager, "user_index")
#OPERATOR ON USER
if active_user != 0 and active_user.username != settings.username:
row = layout.row()
user_operations = row.split()
@ -436,57 +433,8 @@ class SESSION_UL_users(bpy.types.UIList):
split.label(text=scene_current)
split.label(text=ping)
class SESSION_PT_presence(bpy.types.Panel):
bl_idname = "MULTIUSER_MODULE_PT_panel"
bl_label = "Presence overlay"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
return not session \
or (session and session.state in [STATE_INITIAL, STATE_ACTIVE])
def draw_header(self, context):
self.layout.prop(context.window_manager.session,
"enable_presence", text="",icon='OVERLAY')
def draw(self, context):
layout = self.layout
settings = context.window_manager.session
pref = get_preferences()
layout.active = settings.enable_presence
row = layout.row()
row = row.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
row.prop(settings, "presence_show_selected",text="",icon_only=True, icon='CUBE')
row.prop(settings, "presence_show_user", text="",icon_only=True, icon='CAMERA_DATA')
row.prop(settings, "presence_show_mode", text="",icon_only=True, icon='OBJECT_DATAMODE')
row.prop(settings, "presence_show_far_user", text="",icon_only=True, icon='SCENE_DATA')
col = layout.column()
if settings.presence_show_mode :
row = col.column()
row.prop(pref, "presence_mode_distance", expand=True)
col.prop(settings, "presence_show_session_status")
if settings.presence_show_session_status :
row = col.column()
row.active = settings.presence_show_session_status
row.prop(pref, "presence_hud_scale", expand=True)
row = col.column(align=True)
row.active = settings.presence_show_session_status
row.prop(pref, "presence_hud_hpos", expand=True)
row.prop(pref, "presence_hud_vpos", expand=True)
def draw_property(context, parent, property_uuid, level=0):
settings = get_preferences()
runtime_settings = context.window_manager.session
item = session.repository.graph.get(property_uuid)
type_id = item.data.get('type_id')
area_msg = parent.row(align=True)
@ -506,15 +454,18 @@ def draw_property(context, parent, property_uuid, level=0):
detail_item_box.label(text=f"{name}")
# Operations
have_right_to_modify = (item.owner == settings.username or \
item.owner == RP_COMMON) and item.state != ERROR
from multi_user import icons
sync_status = icons.icons_col["repository_push"] #TODO: Link all icons to the right sync (push/merge/issue). For issue use "UNLINKED" for icon
# sync_status = icons.icons_col["repository_merge"]
if have_right_to_modify:
detail_item_box.operator(
"session.commit",
text="",
icon='TRIA_UP').target = item.uuid
icon_value=sync_status.icon_id).target = item.uuid
detail_item_box.separator()
if item.state in [FETCHED, UP]:
@ -546,12 +497,40 @@ def draw_property(context, parent, property_uuid, level=0):
else:
detail_item_box.label(text="", icon="DECORATE_LOCKED")
class SESSION_PT_sync(bpy.types.Panel):
bl_idname = "MULTIUSER_SYNC_PT_panel"
bl_label = "Synchronize"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
return session \
and session.state in [STATE_ACTIVE]
def draw_header(self, context):
self.layout.label(text="", icon='UV_SYNC_SELECT')
def draw(self, context):
layout = self.layout
settings = get_preferences()
row= layout.row()
row = row.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
row.prop(settings.sync_flags, "sync_render_settings",text="",icon_only=True, icon='SCENE')
row.prop(settings.sync_flags, "sync_during_editmode", text="",icon_only=True, icon='EDITMODE_HLT')
row.prop(settings.sync_flags, "sync_active_camera", text="",icon_only=True, icon='VIEW_CAMERA')
class SESSION_PT_repository(bpy.types.Panel):
bl_idname = "MULTIUSER_PROPERTIES_PT_panel"
bl_label = "Repository"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
@ -565,7 +544,8 @@ class SESSION_PT_repository(bpy.types.Panel):
return hasattr(context.window_manager, 'session') and \
session and \
(session.state == STATE_ACTIVE or \
session.state == STATE_LOBBY and admin)
session.state == STATE_LOBBY and admin) and \
not settings.sidebar_repository_shown
def draw_header(self, context):
self.layout.label(text="", icon='OUTLINER_OB_GROUP_INSTANCE')
@ -579,19 +559,18 @@ class SESSION_PT_repository(bpy.types.Panel):
usr = session.online_users.get(settings.username)
row = layout.row()
if session.state == STATE_ACTIVE:
if 'SessionBackupTimer' in registry:
row = layout.row()
row.alert = True
row.operator('session.cancel_autosave', icon="CANCEL")
row.alert = False
else:
row.operator('session.save', icon="FILE_TICK")
# else:
# row.operator('session.save', icon="FILE_TICK")
box = layout.box()
row = box.row()
row.prop(runtime_settings, "filter_owned", text="Show only owned Nodes", icon_only=True, icon="DECORATE_UNLOCKED")
row.prop(runtime_settings, "filter_owned", text="Only show owned data blocks", icon_only=True, icon="DECORATE_UNLOCKED")
row = box.row()
row.prop(runtime_settings, "filter_name", text="Filter")
row = box.row()
@ -612,8 +591,10 @@ class SESSION_PT_repository(bpy.types.Panel):
layout.row().label(text="Empty")
elif session.state == STATE_LOBBY and usr and usr['admin']:
row = layout.row()
row.operator("session.init", icon='TOOL_SETTINGS', text="Init")
else:
row = layout.row()
row.label(text="Waiting to start")
class VIEW3D_PT_overlay_session(bpy.types.Panel):
@ -634,41 +615,65 @@ class VIEW3D_PT_overlay_session(bpy.types.Panel):
layout.active = settings.enable_presence
row = layout.row()
row = row.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
row.prop(settings, "presence_show_selected",text="",icon_only=True, icon='CUBE')
row.prop(settings, "presence_show_user", text="",icon_only=True, icon='CAMERA_DATA')
row.prop(settings, "presence_show_mode", text="",icon_only=True, icon='OBJECT_DATAMODE')
row.prop(settings, "presence_show_far_user", text="",icon_only=True, icon='SCENE_DATA')
row.prop(settings, "presence_show_selected",text="Selected Objects")
row = layout.row(align=True)
row.prop(settings, "presence_show_user", text="Users camera")
row.prop(settings, "presence_show_mode", text="Users mode")
col = layout.column()
if settings.presence_show_mode :
if settings.presence_show_mode or settings.presence_show_user:
row = col.column()
row.prop(pref, "presence_mode_distance", expand=True)
row.prop(pref, "presence_text_distance", expand=True)
row = col.column()
row.prop(settings, "presence_show_far_user", text="Users on different scenes")
col.prop(settings, "presence_show_session_status")
if settings.presence_show_session_status :
row = col.column()
row.active = settings.presence_show_session_status
row.prop(pref, "presence_hud_scale", expand=True)
row = col.column(align=True)
row.active = settings.presence_show_session_status
row.prop(pref, "presence_hud_hpos", expand=True)
row.prop(pref, "presence_hud_vpos", expand=True)
split = layout.split()
text_pos = split.column(align=True)
text_pos.active = settings.presence_show_session_status
text_pos.prop(pref, "presence_hud_hpos", expand=True)
text_pos.prop(pref, "presence_hud_vpos", expand=True)
text_scale = split.column()
text_scale.active = settings.presence_show_session_status
text_scale.prop(pref, "presence_hud_scale", expand=True)
class SESSION_UL_network(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
settings = get_preferences()
server_name = '-'
server_status = 'BLANK1'
server_private = 'BLANK1'
server_name = item.server_name
split = layout.split(factor=0.7)
if item.is_private:
server_private = 'LOCKED'
split.label(text=server_name, icon=server_private)
else:
split.label(text=server_name)
from multi_user import icons
server_status = icons.icons_col["server_offline"]
if item.is_online:
server_status = icons.icons_col["server_online"]
split.label(icon_value=server_status.icon_id)
classes = (
SESSION_UL_users,
SESSION_UL_network,
SESSION_PT_settings,
SESSION_PT_settings_user,
SESSION_PT_settings_network,
SESSION_PT_presence,
SESSION_PT_advanced_settings,
SESSION_PT_user,
SESSION_PT_sync,
SESSION_PT_repository,
VIEW3D_PT_overlay_session,
)
register, unregister = bpy.utils.register_classes_factory(classes)
if __name__ == "__main__":