263 lines
8.5 KiB
Python
263 lines
8.5 KiB
Python
# #### 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 2
|
|
# 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, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# ##### END GPL LICENSE BLOCK #####
|
|
|
|
from typing import Optional, Tuple
|
|
|
|
import bpy
|
|
|
|
from ..modules.poliigon_core.assets import (
|
|
AssetData,
|
|
ModelType)
|
|
from ..modules.poliigon_core.multilingual import _t
|
|
|
|
from ..constants import SUPPORTED_CONVENTION
|
|
from ..utils import construct_model_name
|
|
|
|
|
|
def check_convention(asset_data: AssetData, local: bool = False) -> bool:
|
|
asset_convention = asset_data.get_convention(local=local)
|
|
|
|
if asset_convention is None:
|
|
return False
|
|
elif asset_convention > SUPPORTED_CONVENTION:
|
|
return False
|
|
return True
|
|
|
|
|
|
def get_model_op_details(
|
|
cTB, asset_data: AssetData, size: str) -> Tuple[str, str, str]:
|
|
"""Get details to use in the ui for a given model and size."""
|
|
|
|
asset_type_data = asset_data.get_type_data()
|
|
asset_name = asset_data.asset_name
|
|
|
|
default_lod = cTB.settings["lod"]
|
|
downloaded = asset_type_data.get_size_list(
|
|
local_only=True,
|
|
addon_convention=cTB._asset_index.addon_convention,
|
|
local_convention=asset_data.get_convention(local=True))
|
|
|
|
lod = asset_type_data.get_lod(default_lod)
|
|
if lod is None:
|
|
lod = "NONE"
|
|
|
|
if not asset_type_data.has_mesh(ModelType.FBX):
|
|
lod = "NONE"
|
|
|
|
coll_name = construct_model_name(asset_name, size, lod)
|
|
|
|
coll = bpy.data.collections.get(coll_name)
|
|
if coll:
|
|
in_scene = True
|
|
else:
|
|
in_scene = False
|
|
|
|
label = ""
|
|
tip = ""
|
|
if size in downloaded:
|
|
if in_scene:
|
|
if lod:
|
|
label = _t("{0} {1} (import again)").format(size, lod)
|
|
tip = _t("Import {0} {1} again\n{2}").format(
|
|
size, lod, asset_name)
|
|
else:
|
|
label = _t("{0} (import again)").format(size)
|
|
tip = _t("Import {0} again\n{1}").format(size, asset_name)
|
|
else:
|
|
if lod:
|
|
label = _t("{0} {1} (import)").format(size, lod)
|
|
tip = _t("Import {0} {1}\n{2}").format(
|
|
size, lod, asset_name)
|
|
else:
|
|
label = _t("{0} (import)").format(size)
|
|
tip = _t("Import {0}\n{1}").format(size, asset_name)
|
|
|
|
return lod, label, tip
|
|
|
|
|
|
def safe_size_apply(cTB,
|
|
op_ref: bpy.types.OperatorProperties,
|
|
size_value: str,
|
|
asset_name: str) -> None:
|
|
"""Applies a size value to operator draw with a safe fallback.
|
|
|
|
If we try to apply a size which is not recognized as local, it will fail
|
|
and disrupt further drawing. This function mitigates this problem.
|
|
"""
|
|
|
|
if size_value is None:
|
|
return
|
|
try:
|
|
op_ref.size = size_value
|
|
except TypeError as e:
|
|
# Since this is a UI draw issue, there will be multiple of these
|
|
# these reports, but we have user-level debouncing for a max number
|
|
# per message type.
|
|
msg = f"Failed to assign {size_value} size for {asset_name}: {e}"
|
|
cTB.logger_ui.error(msg)
|
|
# TODO(SOFT-1303): Include in refactor to asset index, disabled
|
|
# overreporting for now.
|
|
# reporting.capture_message("failed_size_op_set", msg, "error")
|
|
|
|
|
|
def check_dpi(cTB, force: bool = True) -> None:
|
|
"""Checks the DPI of the screen to adjust the scale accordingly.
|
|
|
|
Used to ensure previews remain square and avoid text truncation.
|
|
"""
|
|
|
|
if not force and cTB.ui_scale_checked:
|
|
return
|
|
|
|
prefs = bpy.context.preferences
|
|
cTB.settings["win_scale"] = prefs.system.ui_scale
|
|
cTB.ui_scale_checked = True
|
|
|
|
|
|
def get_ui_scale(cTB) -> float:
|
|
"""Utility for fetching the ui scale, used in draw code."""
|
|
|
|
check_dpi(cTB)
|
|
return cTB.settings["win_scale"]
|
|
|
|
|
|
def _get_line_width(cTB, line: str) -> int:
|
|
"""Returns pixel width of a string."""
|
|
|
|
width_line = 15
|
|
for _char in line:
|
|
if _char in "ABCDEFGHKLMNOPQRSTUVWXYZmw":
|
|
width_line += 9
|
|
elif _char in "abcdeghknopqrstuvxyz0123456789":
|
|
width_line += 6
|
|
elif _char in "IJfijl .":
|
|
width_line += 3
|
|
|
|
width_line *= get_ui_scale(cTB)
|
|
return width_line
|
|
|
|
|
|
def wrapped_label(cTB,
|
|
width: int,
|
|
text: str,
|
|
container: bpy.types.UILayout,
|
|
icon: Optional[str] = None,
|
|
add_padding: bool = False,
|
|
add_padding_top: bool = False,
|
|
add_padding_bottom: bool = False,
|
|
alignment: str = 'LEFT'
|
|
) -> None:
|
|
"""Text wrap a label based on indicated width."""
|
|
|
|
cTB.logger_ui.debug(f"wrapped_label width={width}, text={text}, "
|
|
f"icon={icon}, add_padding={add_padding}, alignment={alignment}")
|
|
|
|
list_words = [_word.replace("!@#", " ") for _word in text.split(" ")]
|
|
|
|
row = container.row()
|
|
parent = row.column(align=True)
|
|
parent.alignment = alignment # Set alignment on the parent column
|
|
parent.scale_y = 0.8 # To make vertical height more natural for text.
|
|
|
|
if add_padding or add_padding_top:
|
|
parent.label(text="")
|
|
|
|
if icon is not None:
|
|
width -= 25 * get_ui_scale(cTB)
|
|
|
|
line = ""
|
|
first = True
|
|
|
|
for _word in list_words:
|
|
width_line = _get_line_width(cTB, line + _word + " ")
|
|
if width_line > width:
|
|
if first:
|
|
if icon is None:
|
|
if alignment == 'LEFT':
|
|
parent.label(text=line)
|
|
else:
|
|
label_row = parent.row()
|
|
label_row.alignment = alignment
|
|
label_row.label(text=line)
|
|
else:
|
|
if alignment == 'LEFT':
|
|
parent.label(text=line, icon=icon)
|
|
else:
|
|
label_row = parent.row()
|
|
label_row.alignment = alignment
|
|
label_row.label(text=line, icon=icon)
|
|
first = False
|
|
|
|
else:
|
|
if icon is None:
|
|
if alignment == 'LEFT':
|
|
parent.label(text=line)
|
|
else:
|
|
label_row = parent.row()
|
|
label_row.alignment = alignment
|
|
label_row.label(text=line)
|
|
else:
|
|
if alignment == 'LEFT':
|
|
parent.label(text=line, icon="BLANK1")
|
|
else:
|
|
label_row = parent.row()
|
|
label_row.alignment = alignment
|
|
label_row.label(text=line, icon="BLANK1")
|
|
|
|
line = _word + " "
|
|
|
|
else:
|
|
line += _word + " "
|
|
|
|
if line != "":
|
|
if icon is None:
|
|
if alignment == 'LEFT':
|
|
parent.label(text=line)
|
|
else:
|
|
label_row = parent.row()
|
|
label_row.alignment = alignment
|
|
label_row.label(text=line)
|
|
else:
|
|
if first:
|
|
if alignment == 'LEFT':
|
|
parent.label(text=line, icon=icon)
|
|
else:
|
|
label_row = parent.row()
|
|
label_row.alignment = alignment
|
|
label_row.label(text=line, icon=icon)
|
|
else:
|
|
if alignment == 'LEFT':
|
|
parent.label(text=line, icon="BLANK1")
|
|
else:
|
|
label_row = parent.row()
|
|
label_row.alignment = alignment
|
|
label_row.label(text=line, icon="BLANK1")
|
|
|
|
if add_padding or add_padding_bottom:
|
|
spacer = parent.row()
|
|
spacer.scale_y = 0.3
|
|
spacer.label(text="")
|
|
|
|
|
|
def separator_p4b(
|
|
container: bpy.types.UILayout, *, line: bool = False) -> None:
|
|
if line and bpy.app.version >= (4, 2):
|
|
container.separator(type='LINE')
|
|
else:
|
|
container.separator()
|