Files
blender-portable-repo/scripts/addons/poliigon-addon-blender/dialogs/utils_dlg.py
T
2026-03-17 14:58:51 -06:00

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()