2025-12-01

This commit is contained in:
2026-03-17 14:58:51 -06:00
parent 183e865f8b
commit 4b82b57113
6846 changed files with 954887 additions and 162606 deletions
@@ -28,20 +28,24 @@ def _build_section_free_user(cTB) -> None:
box_free = cTB.vBase.box()
col = box_free.column()
msg = _t("Access 3,000+ studio quality assets")
wrapped_label(cTB, w_label, msg, col, add_padding=False)
msg = _t("Unused asset balance rolls over each month")
wrapped_label(cTB, w_label, msg, col, add_padding=False, icon="CHECKMARK")
msg = _t("Commercial & personal use license")
wrapped_label(cTB, w_label, msg, col, add_padding=False, icon="CHECKMARK")
msg = _t("Redownload even if your subscription ends")
wrapped_label(cTB, w_label, msg, col, add_padding=False, icon="CHECKMARK")
msg = _t("Cancel or pause at any time in a few clicks")
wrapped_label(cTB, w_label, msg, col, add_padding=False, icon="CHECKMARK")
msg = _t("50% discount for students and teachers")
wrapped_label(cTB, w_label, msg, col, add_padding=False, icon="CHECKMARK")
col.label(text=_t("Get more with Pro"),
icon_value=cTB.ui_icons["ICON_checkmark_verified"].icon_id)
spacer = col.row()
spacer.scale_y = 0.5
spacer.label(text="")
op = col.operator("poliigon.poliigon_link", text=_t("View Pricing"))
msg = _t("Access the full resolution downloads of 5,000+ assets")
wrapped_label(cTB, w_label, msg, col, add_padding=False, add_padding_bottom=True, icon="CHECKMARK")
msg = _t("Commercial & personal use license")
wrapped_label(cTB, w_label, msg, col, add_padding=False, add_padding_bottom=True, icon="CHECKMARK")
msg = _t("Cancel or pause at any time in a few clicks")
wrapped_label(cTB, w_label, msg, col, add_padding=False, add_padding_bottom=True, icon="CHECKMARK")
msg = _t("50% discount for students and teachers")
wrapped_label(cTB, w_label, msg, col, add_padding=False, add_padding_bottom=True, icon="CHECKMARK")
button_row = col.row()
button_row.scale_y = 1.3
op = button_row.operator("poliigon.poliigon_link", text=_t("View Pricing"), depress=True)
op.mode = "subscribe"
# TODO(Andreas): Figma did not contain any tooltips...
op.tooltip = _t("View Poliigon Pricing Online")
@@ -53,27 +57,35 @@ def _build_section_paid_plan(cTB) -> None:
box_free = cTB.vBase.box()
col = box_free.column()
col.label(text=_t("My Account"),
icon="COMMUNITY")
spacer = col.row()
spacer.scale_y = 0.5
spacer.label(text="")
name_plan = cTB.user.plan.plan_name
wrapped_label(cTB, w_label, name_plan, col, add_padding=False)
wrapped_label(cTB, w_label, name_plan, col, add_padding=False, add_padding_bottom=True)
if not cTB.is_unlimited_user():
credits = cTB.user.plan.plan_credit
msg = _t("Assets per month: {0}").format(credits)
wrapped_label(cTB, w_label, msg, col, add_padding=False)
wrapped_label(cTB, w_label, msg, col, add_padding=False, add_padding_bottom=True)
next_renew = cTB.user.plan.next_subscription_renewal_date
msg = _t("Renewal Date: {0}").format(next_renew)
wrapped_label(cTB, w_label, msg, col, add_padding=False)
wrapped_label(cTB, w_label, msg, col, add_padding=False, add_padding_bottom=True)
is_paused = cTB.is_paused_subscription()
status = _t("Paused") if is_paused else _t("Active")
msg = _t("Status: {0}").format(status)
wrapped_label(cTB, w_label, msg, col, add_padding=False)
wrapped_label(cTB, w_label, msg, col, add_padding=False, add_padding_bottom=True)
op = col.operator("poliigon.poliigon_link", text=_t("View Details"))
button_row = col.row()
button_row.scale_y = 1.3
op = button_row.operator("poliigon.poliigon_link", text=_t("Manage Account"))
op.mode = "credits"
# TODO(Andreas): Figma did not contain any tooltips...
op.tooltip = _t("View Details of Your Plan Online")
op.tooltip = _t("View details of your plan online")
def _build_still_loading(cTB) -> None:
@@ -86,14 +98,13 @@ def _build_still_loading(cTB) -> None:
def build_user(cTB) -> None:
cTB.logger_ui.debug("build_user")
cTB.vBase.label(text=_t("Your Plan"))
free_account = cTB.is_free_user() or cTB.user.plan.plan_name is None
if cTB.fetching_user_data:
_build_still_loading(cTB)
return
if cTB.is_free_user() or cTB.user.plan.plan_name is None:
if free_account:
_build_section_free_user(cTB)
else:
_build_section_paid_plan(cTB)
@@ -18,6 +18,7 @@
from ..modules.poliigon_core.api_remote_control_params import (
CATEGORY_ALL,
CATEGORY_FREE,
KEY_TAB_ONLINE)
from ..modules.poliigon_core.assets import (
AssetType,
@@ -68,15 +69,15 @@ def build_categories(cTB):
row_sub_cat = row_categories.row(align=True)
type_hdri = ASSET_TYPE_TO_CATEGORY_NAME[AssetType.HDRI]
type_hdri = "HDRIS" # TODO(SOFT-2733): Cleanup HDRIS mapping
type_model = ASSET_TYPE_TO_CATEGORY_NAME[AssetType.MODEL]
type_tex = ASSET_TYPE_TO_CATEGORY_NAME[AssetType.TEXTURE]
list_types = [CATEGORY_ALL, type_tex, type_model, type_hdri]
area = cTB.settings["area"]
if cTB.search_free and area == KEY_TAB_ONLINE:
lbl_button_cat = _t("Free")
elif cTB.vAssetType == CATEGORY_ALL:
if area == KEY_TAB_ONLINE:
list_types.append(CATEGORY_FREE)
if cTB.vAssetType == CATEGORY_ALL:
lbl_button_cat = _t("Select Category")
else:
lbl_button_cat = cTB.vAssetType
@@ -31,31 +31,6 @@ def build_mode(url, action, id_notice):
return "notify@{}@{}@{}".format(url, action, id_notice)
def _draw_notification_open_url_single_row(cTB, notice, first_row, icon) -> None:
# Single row with text + button.
# TODO: generalize this for notification message and length,
# and if dismiss is included.
# During SOFT-780 this has been changed for POPUP_MESSAGE in a
# very simplistic way
# (commit: https://github.com/poliigon/poliigon-addon-blender/pull/278/commits/00296ab70288893a023a6705d52eb4505ce36897).
# When addressing this properly,
# make sure to address it for all notification types.
first_row.alert = True
first_row.label(text=notice.title)
first_row.alert = False
op = first_row.operator(
"poliigon.poliigon_link",
icon=icon,
text=notice.label,
)
if notice.tooltip != "":
op.tooltip = notice.tooltip
op.mode = build_mode(
notice.url,
notice.label,
notice.id_notice)
def _draw_notification_open_url_two_rows(
cTB, notice, first_row, main_col, icon) -> None:
# Two rows (or more, if text wrapping).
@@ -88,13 +63,7 @@ def _draw_notification_open_url_two_rows(
def _draw_notification_open_url(
cTB, notice, first_row, main_col, panel_width, icon) -> None:
# Empirical for width for "Beta addon: [Take survey]" specifically.
single_row_width = 250
if panel_width > single_row_width:
_draw_notification_open_url_single_row(cTB, notice, first_row, icon)
else:
_draw_notification_open_url_two_rows(
cTB, notice, first_row, main_col, icon)
_draw_notification_open_url_two_rows(cTB, notice, first_row, main_col, icon)
def _draw_notification_update_ready_single_row(cTB, notice, first_row, icon) -> None:
@@ -231,6 +200,25 @@ def _draw_notification_run_operator(cTB, notice, first_row, icon) -> None:
op.tooltip = notice.tooltip
def _draw_notification_none_action(cTB, notice, first_row, icon) -> None:
"""Draws a notification with no clickable action - just text with dismiss button."""
# Create a row with icon on the left and text in center (X button handled by main function)
row = first_row.row(align=True)
# Icon column on the left - single row for compact height
icon_col = row.column(align=True)
icon_col.alignment = "CENTER"
icon_col.label(text="", icon=icon)
# Text column in center - fills remaining space
text_col = row.column()
text_col.alignment = "CENTER"
# Using full available width for text
wrapped_label(
cTB, cTB.width_draw_ui, notice.title, text_col)
text_col.alert = False
# TODO(Andreas): deactivated reporting here, as I needed a third parameter and
# was not able to quickly make handle_draw() work
# @reporting.handle_draw()
@@ -270,6 +258,8 @@ def notification_banner(cTB, layout):
cTB, notice, first_row, main_col, panel_width, icon)
elif notice.action == ActionType.RUN_OPERATOR:
_draw_notification_run_operator(cTB, notice, first_row, icon)
elif notice.action == ActionType.NONE:
_draw_notification_none_action(cTB, notice, first_row, icon)
else:
main_col.label(text=notice.title)
cTB.logger_ui.error("Invalid notifcation type")
@@ -18,10 +18,6 @@
import bpy
from ..modules.poliigon_core.api_remote_control_params import (
KEY_TAB_IMPORTED,
KEY_TAB_MY_ASSETS,
KEY_TAB_ONLINE)
from ..modules.poliigon_core.multilingual import _t
@@ -88,52 +84,60 @@ def build_areas(cTB):
cTB.logger_ui.debug("build_areas")
cTB.initial_view_screen()
row = cTB.vBase.row(align=True)
row.scale_x = 1.1
row.scale_y = 1.1
row = cTB.vBase.row()
label_column = row.column(align=True)
label_column.alignment = "LEFT"
_add_asset_tab(
cTB,
row,
tab=KEY_TAB_ONLINE,
mode="area_poliigon",
icon="HOME",
tooltip=_t("Show Poliigon Assets"))
_add_asset_tab(
cTB,
row,
tab=KEY_TAB_MY_ASSETS,
mode="area_my_assets",
icon_value=cTB.ui_icons["ICON_myassets"].icon_id,
tooltip=_t("Show My Assets"))
_add_asset_tab(
cTB,
row,
tab=KEY_TAB_IMPORTED,
mode="area_imported",
icon="OUTLINER_OB_GROUP_INSTANCE",
tooltip=_t("Show Imported Assets"))
icon = 0
label_txt = _t("Browse Assets")
if cTB.is_unlimited_user():
icon = cTB.ui_icons["LOGO_unlimited"].icon_id
label_txt = _t("Pro Unlimited")
elif not cTB.is_free_user():
icon = cTB.ui_icons["ICON_asset_balance"].icon_id
if cTB.is_paused_subscription() and cTB.get_user_credits() <= 0:
icon = cTB.ui_icons["ICON_subscription_paused"].icon_id
label_txt = _t("Downloads")
label_txt = f"{cTB.get_user_credits()} {label_txt}"
op = row.operator(
if not cTB.settings["show_user"]:
label_column.label(text=label_txt, icon_value=icon)
else:
back_button = label_column.operator(
"poliigon.poliigon_setting",
text=_t("Back"),
icon="BACK"
)
back_button.mode = "show_user"
row_right = row.row(align=True)
row_right.alignment = "RIGHT"
op = row_right.operator(
"poliigon.poliigon_setting",
text="",
icon_value=cTB.ui_icons["ICON_poliigon"].icon_id,
icon="COMMUNITY",
depress=cTB.settings["show_user"],
)
op.mode = "my_account"
op.mode = "show_user"
op.tooltip = _t("Show Your Account Details")
row.separator()
op = row_right.operator(
"poliigon.poliigon_link",
text="",
icon="HELP"
)
op.confirm_popup = True
op.mode = "help"
op.tooltip = _t("Addon help")
row_prefs = row.row(align=True)
row_prefs.alignment = "RIGHT"
_draw_asset_balance(cTB, row=row_prefs)
_ = row_prefs.operator(
_ = row_right.operator(
"poliigon.open_preferences",
text="",
icon="PREFERENCES",
).set_focus = "all"
)
# TODO(Joao): Check if this operator should be moved somewhere else
# _draw_asset_balance(cTB, row=row_prefs)
cTB.vBase.separator()
@@ -61,6 +61,7 @@ def _draw_banner(cTB, upgrade_content: UpgradeContent) -> None:
text=label,
icon_value=cTB.ui_icons[key_icon].icon_id)
op.mode = "subscribe_banner"
op.tooltip = "Opens the website to view available plans"
def _draw_banner_in_progress(cTB, upgrade_content: UpgradeContent) -> None:
@@ -31,9 +31,9 @@ from ..modules.poliigon_core.api_remote_control_params import (
CATEGORY_ALL,
KEY_TAB_IMPORTED,
KEY_TAB_MY_ASSETS,
KEY_TAB_RECENT_DOWNLOADS,
KEY_TAB_ONLINE,
IDX_PAGE_ACCUMULATED,
PAGE_SIZE_ACCUMULATED)
KEY_TAB_LOCAL)
from ..modules.poliigon_core.multilingual import _t
from .utils_dlg import (
check_convention,
@@ -43,6 +43,23 @@ from .utils_dlg import (
safe_size_apply,
wrapped_label)
from ..operators.operator_material import set_op_mat_disp_strength
from ..operators.operator_asset_filter import FilterOptions
from .. import reporting
class POLIIGON_MT_asset_display_options(bpy.types.Menu):
bl_label = "Display Options"
bl_idname = "POLIIGON_MT_asset_display_options"
def draw(self, context):
layout = self.layout
props = context.window_manager.poliigon_props
layout.label(text="Thumbnail Size:")
layout.prop(props, "thumbnail_size", expand=True)
layout.separator()
layout.label(text="Assets Per Page:")
layout.prop(props, "assets_per_page", expand=True)
THUMB_SIZE_FACTOR = {"Tiny": 0.5,
@@ -82,6 +99,10 @@ def _build_assets_no_assets(cTB, area: str, category: str) -> None:
label = _t("No Poliigon {0} found in the Scene").format(category)
elif area == KEY_TAB_ONLINE:
label = _t("No Poliigon {0} found Online").format(category)
elif area == KEY_TAB_LOCAL:
label = _t("No Poliigon {0} found locally").format(category)
elif area == KEY_TAB_RECENT_DOWNLOADS:
label = _t("No {0} previously downloaded").format(category)
width = cTB.width_draw_ui - 20 * get_ui_scale(cTB)
wrapped_label(cTB, width, label, box_not_found, add_padding=True)
@@ -347,9 +368,8 @@ def _draw_button_quick_preview(
do_show = False
# TODO(Andreas): confused about backplate handling...
has_thumb_urls = len(asset_data.thumb_urls) > 0
has_cf_thumb_urls = len(asset_data.cloudflare_thumb_urls) > 0
if is_backplate and (has_thumb_urls or has_cf_thumb_urls):
if is_backplate and has_cf_thumb_urls:
do_show = True
elif len(asset_type_data.get_watermark_preview_url_list()):
do_show = True
@@ -392,31 +412,72 @@ def _draw_button_quick_preview(
"Preview {0} on a plane created during import").format(asset_name)
def _draw_new_asset_icon(cTB, layout_row: bpy.types.UILayout, days: int) -> None:
"""Draws the new asset icon."""
icon_value = cTB.ui_icons["ICON_new_asset"].icon_id
op = layout_row.operator(
"poliigon.poliigon_setting",
text="",
emboss=False,
depress=False,
icon_value=icon_value
)
op.mode = "none" # No action needed
op.tooltip = _t("Released {x} days ago").format(x=days)
def _draw_free_asset_icon(cTB, layout_row: bpy.types.UILayout) -> None:
"""Draws the free asset icon."""
icon_val = cTB.ui_icons["ICON_free_asset"].icon_id
op = layout_row.operator(
"poliigon.poliigon_setting",
text="",
emboss=False,
depress=False,
icon_value=icon_val
)
op.mode = "none" # No action needed
op.tooltip = _t("Free asset")
def _draw_checkmark_purchased(cTB, layout_row: bpy.types.UILayout) -> None:
col_checkmark = layout_row.column(align=True)
col_checkmark.enabled = False
icon_val = cTB.ui_icons["ICON_acquired_check"].icon_id
op = col_checkmark.operator(
icon_val = cTB.ui_icons["ICON_local"].icon_id
op = layout_row.operator(
"poliigon.poliigon_setting",
text="",
icon_value=icon_val,
depress=False,
emboss=True
emboss=False
)
op.tooltip = _t("Asset already acquired")
op.mode = "none" # No action needed
op.tooltip = _t("Asset Downloaded")
def _draw_checkmark_purchased_not_local(cTB,
layout_row: bpy.types.UILayout,
tooltip: str) -> None:
icon_val = cTB.ui_icons["LOGO_downloads_recent"].icon_id
op = layout_row.operator(
"poliigon.poliigon_setting",
text="",
icon_value=icon_val,
depress=False,
emboss=False
)
op.mode = "none" # No action needed
op.tooltip = tooltip
def _draw_checkmark_unlimited(cTB, layout_row: bpy.types.UILayout) -> None:
col_checkmark = layout_row.column(align=True)
col_checkmark.enabled = False
icon_val = cTB.ui_icons["ICON_unlimited_local"].icon_id
op = col_checkmark.operator(
icon_val = cTB.ui_icons["ICON_local"].icon_id
op = layout_row.operator(
"poliigon.poliigon_setting",
text="",
icon_value=icon_val,
depress=False,
emboss=True
emboss=False
)
op.mode = "none" # No action needed
op.tooltip = _t("Asset found locally")
@@ -503,7 +564,9 @@ def _draw_button_texture_local(cTB,
text="Retry",
icon="ERROR",
)
op.tooltip = error
# str(): Error may also be an exception class,
# which we can not assign to tooltip property.
op.tooltip = str(error)
else:
op = row_button.operator(
"poliigon.poliigon_material",
@@ -592,14 +655,23 @@ def determine_hdri_sizes(asset_data: AssetData,
size_light = find_closest_size(size_light_default, sizes_jpg)
else:
# Just get any best fit
size_light = asset_type_data.get_size(
size=size_light_default,
local_only=True,
addon_convention=addon_convention,
local_convention=asset_data.get_convention(local=True))
try:
size_light = asset_type_data.get_size(
size=size_light_default,
local_only=True,
addon_convention=addon_convention,
local_convention=asset_data.get_convention(local=True))
except KeyError:
size_light = None
size_bg = size_bg_default
if not use_jpg_bg:
if size_bg is None or size_light is None:
# If either of these sizes is None, we won't be able to set the
# size property. Yet, in order to not complicate any None checks,
# we need to let these pass, instead of converting them to
# strings.
pass
elif not use_jpg_bg:
size_bg = f"{size_light}_EXR"
elif len(sizes_jpg) > 0:
size_bg = find_closest_size(size_bg_default, sizes_jpg)
@@ -629,7 +701,9 @@ def _draw_button_hdri_local(cTB,
text="Retry",
icon="ERROR",
)
op.tooltip = error.description
# str(): Error may also be an exception class,
# which we can not assign to tooltip property.
op.tooltip = str(error)
else:
op = layout_row.operator(
"poliigon.poliigon_hdri",
@@ -646,7 +720,16 @@ def _draw_button_hdri_local(cTB,
use_jpg_bg=cTB.settings["hdri_use_jpg_bg"],
addon_convention=cTB.addon_convention)
safe_size_apply(cTB, op, size_light, asset_name)
op.size_bg = size_bg
if size_bg is None:
return
try:
op.size_bg = size_bg
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_bg} size for {asset_name}: {e}"
cTB.logger_ui.error(msg)
def _draw_button_hdri_imported(cTB,
@@ -696,7 +779,9 @@ def _draw_button_download(cTB,
text=label,
icon="ERROR",
)
op.tooltip = error
# str(): Error may also be an exception class,
# which we can not assign to tooltip property.
op.tooltip = str(error)
else:
op = layout_row.operator(
"poliigon.poliigon_download",
@@ -718,54 +803,58 @@ def _draw_button_purchase(cTB,
) -> None:
asset_id = asset_data.asset_id
asset_name = asset_data.asset_name
num_credits = asset_data.credits
is_free = num_credits == 0
is_free_asset = asset_data.credits == 0
thumb_size = THUMB_SIZE_FACTOR[cTB.settings["thumbsize"]]
name_op = "poliigon.poliigon_download"
if cTB.is_free_user() and not is_free_asset:
name_op = "poliigon.poliigon_setting"
mode_purchase = "my_account"
label = _t("Download with Pro")
tooltip = _t("Learn more about Poliigon plans.")
elif is_free_asset or cTB.is_unlimited_user():
# While it will still be a purchase button,
# for free assets it will lead to an implicit auto-download
label = _t("Download {0}").format(size_default)
tooltip = _t("Download {0}").format(asset_name)
mode_purchase = "download"
elif not cTB.is_legacy_limited_user() and cTB.settings["auto_download"]:
# If not-legacy pro user, we are still using purchase endpoint but with
# Download label if auto-download option is set as true;
label = _t("Download {0}").format(size_default)
tooltip = _t("Download {0}").format(asset_name)
mode_purchase = "purchase"
else:
# If legacy pro user, then we are still using Purchase label and
# purchase endpoint;
if thumb_size >= 0.75:
label = _t("Purchase")
else:
label = _t("Buy")
tooltip = _t("Purchase {0}").format(asset_name)
mode_purchase = "purchase"
if error == ERR_NOT_ENOUGH_CREDITS:
label = "Balance"
elif error is not None:
# str(): Error may also be an exception class,
# which we can not assign to tooltip property.
tooltip = str(error)
label = "Retry"
elif is_free or cTB.is_unlimited_user():
# While it will still be a purchase button,
# for free assets it will lead to an implicit auto-download
label = _t("Download {0}").format(size_default)
elif thumb_size >= 0.75:
label = _t("Purchase")
else:
label = _t("Buy")
name_op = "poliigon.poliigon_download"
mode_purchase = "purchase"
tooltip = _t("Purchase {0}").format(asset_name)
if not is_free:
if cTB.is_free_user():
name_op = "poliigon.poliigon_setting"
mode_purchase = "my_account"
label = _t("Learn More")
tooltip = _t("Switch to your account overview.")
elif cTB.is_unlimited_user():
mode_purchase = "download"
tooltip = _t("Download {0}").format(asset_name)
elif not cTB.settings["one_click_purchase"]:
name_op = "poliigon.popup_purchase"
else:
tooltip = _t("Download {0}").format(asset_name)
if mode_purchase == "purchase" and not cTB.settings["one_click_purchase"]:
name_op = "poliigon.popup_purchase"
icon = "ERROR" if error is not None else "NONE"
op = layout_row.operator(
name_op, text=label,
icon=icon
)
op = layout_row.operator(name_op, text=label, icon=icon)
op.mode = mode_purchase
op.tooltip = tooltip
if mode_purchase == "purchase" or mode_purchase == "download":
op.asset_id = asset_id
safe_size_apply(cTB, op, size_default, asset_name)
if error:
op.tooltip = error
else:
op.tooltip = tooltip
def _draw_button_quick_menu(layout_row: bpy.types.UILayout,
@@ -808,15 +897,22 @@ def _draw_missing_grid_dummies(cTB,
layout_grid.column(align=1)
def _draw_view_more_my_assets(
cTB, layout_box_not_found: bpy.types.UILayout) -> None:
def _draw_reset_filters(cTB,
area: str,
layout_box_not_found: bpy.types.UILayout
) -> None:
if layout_box_not_found is None:
return
row = layout_box_not_found.row(align=True)
row.scale_y = 1.5
label = _t("View more online")
if cTB.vSearch[area] != "" and area == KEY_TAB_ONLINE:
label = _t("Clear Search")
mode = f"clear_search_{area}"
else:
label = _t("Reset Filters")
mode = f"area_{KEY_TAB_ONLINE}"
use_padding = 500
if cTB.width_draw_ui >= use_padding * get_ui_scale(cTB):
@@ -824,46 +920,14 @@ def _draw_view_more_my_assets(
op = row.operator(
"poliigon.poliigon_setting",
text=label,
icon_value=cTB.ui_icons["ICON_poliigon"].icon_id
text=label
)
op.mode = "view_more"
op.mode = mode
if cTB.width_draw_ui >= use_padding * get_ui_scale(cTB):
row.label(text="")
def _draw_view_more_imported(cTB, sorted_assets: List[Dict]) -> None:
if len(sorted_assets) != 0:
return
cTB.vBase.separator()
cTB.vBase.separator()
asset_ids_my_assets = cTB._asset_index.query(
"my_assets/All Assets",
chunk=IDX_PAGE_ACCUMULATED,
chunk_size=PAGE_SIZE_ACCUMULATED)
if asset_ids_my_assets is not None and len(asset_ids_my_assets) > 0:
row = cTB.vBase.row(align=True)
op = row.operator(
"poliigon.poliigon_setting",
text=_t("Explore Your Assets"),
icon_value=cTB.ui_icons["ICON_myassets"].icon_id,
)
op.mode = "area_my_assets"
op.tooltip = _t("Show My Assets")
else:
row = cTB.vBase.row(align=True)
op = row.operator(
"poliigon.poliigon_setting",
text=_t("Explore Poliigon Assets"),
icon_value=cTB.ui_icons["ICON_poliigon"].icon_id,
)
op.mode = "area_poliigon"
op.tooltip = _t("Show Poliigon Assets")
def _draw_button_unsupported_convention(row: bpy.types.UILayout) -> None:
_ = row.operator(
"poliigon.unsupported_convention",
@@ -960,7 +1024,7 @@ def _draw_page_buttons(
col = row_middle.column(align=True)
categories = cTB.settings["category"][area]
categories = cTB.settings["category"]
search = cTB.vSearch[area]
key_fetch = (tuple(categories), search)
enabled = key_fetch not in cTB.fetching_asset_data[area]
@@ -998,12 +1062,17 @@ def _build_unlimited_banner(cTB, layout: bpy.types.UILayout) -> None:
if area != KEY_TAB_MY_ASSETS:
return
col_grid = layout.column()
col_grid = layout.column(align=True)
box_unlimited = col_grid.box()
box_unlimited.alignment = "CENTER"
col_unlimited = box_unlimited.column()
# Spacer to ensure the box is given the full width
_wide_row = box_unlimited.row()
_wide_row.scale_y = 0.001
_wide_row.label(text="")
col_unlimited = box_unlimited.row()
col_unlimited.alignment = "CENTER"
text_info = _t("Unlimited Downloads - "
@@ -1013,16 +1082,20 @@ def _build_unlimited_banner(cTB, layout: bpy.types.UILayout) -> None:
w_info = cTB.width_draw_ui - 20 * get_ui_scale(cTB)
wrapped_label(cTB, w_info, text_info, col_unlimited, add_padding=False)
col_link = col_unlimited.column()
col_link.alignment = "LEFT"
# Spacer to ensure the box is given the full width
_wide_row = box_unlimited.row()
_wide_row.scale_y = 0.001
_wide_row.label(text="")
col_unlimited = box_unlimited.row()
icon_value = cTB.ui_icons["LOGO_unlimited"].icon_id
op_link = col_link.operator(
op_link = col_unlimited.operator(
"poliigon.poliigon_link", text=_t("Learn More"), emboss=True, icon_value=icon_value)
op_link.tooltip = _t("Learn more online about unlimited plans")
op_link.mode = "unlimited"
op_link.mode = "unlimited_plan_help"
def _build_tab_title(cTB) -> None:
def _build_tab_title(cTB, dummy_assets_grid: bool = False) -> None:
row = cTB.vBase.row()
area = cTB.settings["area"]
@@ -1033,69 +1106,44 @@ def _build_tab_title(cTB) -> None:
is_category_all = categories == [CATEGORY_ALL]
is_all = is_category_all and not has_search
if is_all and area != KEY_TAB_IMPORTED:
if is_all and area not in [KEY_TAB_IMPORTED, KEY_TAB_LOCAL]:
num_assets = cTB.num_assets[area]
else:
num_assets = cTB.num_assets_current_query
# We do not want the "All" removed, if search comes from our
# virtual "free category" (which only exists on Online tab), alone.
# TODO(Andreas): This will change again, once we do the "virtual free"
# category via API RC.
if area == KEY_TAB_ONLINE:
if has_search and cTB.vSearch[area] != "free ":
prefix_top_level = ""
else:
# Leads to deliberately replacing "All " with "All " (-> no change)
prefix_top_level = "All "
elif has_search:
prefix_top_level = ""
elif area == KEY_TAB_MY_ASSETS:
prefix_top_level = "My "
elif area == KEY_TAB_IMPORTED:
prefix_top_level = "Imported "
category = categories[-1]
if cTB.settings["show_settings"]:
area_title = _t("Settings")
elif cTB.settings["show_user"]:
area_title = _t("My Account")
elif len(categories) == 1:
category = categories[0]
if category in ["HDRIs", "Models", "Textures"]:
category = f"{prefix_top_level}{category}"
elif category == "All Assets" and area == KEY_TAB_ONLINE:
if "free" in cTB.vSearch[area]:
category = "All Free Assets"
category = category.replace("All ", prefix_top_level)
area_title = category
if has_search:
area_title = _t("Assets")
else:
area_title = categories[-1].title()
area_title = category
if area_title == KEY_TAB_ONLINE:
area_title = _t("Online")
area_title = f"{area_title} ({num_assets})"
if not dummy_assets_grid:
area_title = f"{area_title} ({num_assets})"
row.label(text=area_title)
row_right = row.row()
row_right = row.row(align=True)
row_right.alignment = "RIGHT"
curr_filter = FilterOptions.get_from_query(cTB.settings["area"])
set_pressed = curr_filter != FilterOptions.ALL_ASSETS
row_right.operator("poliigon.asset_filter",
text=curr_filter.value if set_pressed else _t("Filter"),
icon="FILTER",
depress=set_pressed)
if set_pressed:
reset_filters = row_right.operator("poliigon.poliigon_setting",
icon="PANEL_CLOSE",
depress=set_pressed)
reset_filters.mode = f"asset_filter_{FilterOptions.ALL_ASSETS.map_to_query()}"
row_right.separator()
col = row_right.column()
is_fetching_my_assets = len(cTB.fetching_asset_data[KEY_TAB_MY_ASSETS]) > 0
is_fetching_online = len(cTB.fetching_asset_data[KEY_TAB_ONLINE]) > 0
is_fetching = is_fetching_my_assets or is_fetching_online
op = col.operator(
"poliigon.refresh_data",
text="",
icon="FILE_REFRESH"
)
if is_fetching:
op.tooltip = _t("Fetching of asset data in progress")
col.enabled = not is_fetching
col = row_right.column(align=True)
col.operator("poliigon.refresh_data", text="", icon="FILE_REFRESH")
col.enabled = cTB.all_assets_fetched
row_right.menu("POLIIGON_MT_asset_display_options", icon="IMGDISPLAY", text="")
def _asset_is_local(cTB, asset_data: AssetData) -> bool:
@@ -1123,6 +1171,38 @@ def _asset_is_local(cTB, asset_data: AssetData) -> bool:
return is_local
def _create_icon_asset_layout(
cTB, row: bpy.types.UILayout, asset_data: Optional[AssetData]) -> None:
"""Creates the layout for new assets with icon and label."""
# Create a special row for the name with icon to ensure proper alignment
name_with_icon_row = row.row(align=True)
name_with_icon_row.alignment = "LEFT"
name_with_icon_row.separator(factor=0.8)
# # Draw the icon first - match text height
icon_col = name_with_icon_row.column(align=True)
# icon_col = row.column(align=True)
icon_col.separator(factor=0.4)
icon_col.scale_y = 0.6
icon_col.scale_x = 0.5
if asset_data.is_purchased and asset_data.is_local:
_draw_checkmark_purchased(cTB, icon_col)
elif asset_data.is_recent_downloaded:
_draw_checkmark_purchased_not_local(cTB, icon_col, _t("Previously Downloaded"))
elif asset_data.is_purchased:
_draw_checkmark_purchased_not_local(cTB, icon_col, _t("Asset acquired"))
elif cTB.is_unlimited_user() and asset_data.is_local:
_draw_checkmark_unlimited(cTB, icon_col)
elif asset_data.credits == 0:
_draw_free_asset_icon(cTB, icon_col)
elif asset_data.is_new():
days_ago = 1
_draw_new_asset_icon(cTB, icon_col, days_ago)
# Add more space between icon and text
row.separator(factor=1.5)
# @timer
def build_assets(cTB):
cTB.logger_ui.debug("build_assets")
@@ -1135,14 +1215,15 @@ def build_assets(cTB):
sorted_assets = cTB.f_GetAssetsSorted(idx_page_current)
cTB.logger_ui.debug(f"build_assets: sorted_assets {len(sorted_assets)}")
_draw_page_buttons(cTB, area, idx_page_current, at_top=True)
# If asset_id in sorted_assets is 0, we consider as dummy asset
# (for populating placeholder on the grid - check f_GetAssetsSorted);
dummy_assets = sorted_assets and sorted_assets[0] == 0
_build_tab_title(cTB, dummy_assets)
if cTB.is_unlimited_user():
row = cTB.vBase.row(align=False)
row = cTB.vBase.row(align=True)
_build_unlimited_banner(cTB, row)
_build_tab_title(cTB)
thumb_size_factor = THUMB_SIZE_FACTOR[cTB.settings["thumbsize"]]
category = cTB.vActiveCat[0].replace("All ", "")
@@ -1162,6 +1243,22 @@ def build_assets(cTB):
for _asset_id in sorted_assets:
if _asset_id != 0:
asset_data = cTB._asset_index.get_asset(_asset_id)
if asset_data is None:
# Asset could have been removed or failed to load for some reason.
# Skip this entry instead of raising an exception that breaks the UI.
# See historical errors at: SOFT-2849's linked report
msg = (f"Asset id {_asset_id} not found in index skipping.\n"
f"\tall_assets_fetched:{cTB.all_assets_fetched}\n"
f"\tpage {idx_page_current} of {category}\n"
f"\tAPI status: {getattr(cTB._api.status, 'name', cTB._api.status)}")
reporting.capture_message(
"asset_id_missing_during_draw",
msg,
"error",
max_reports=1
)
asset_data = AssetData(
0, AssetType.TEXTURE, "dummy", api_convention=0)
else:
asset_data = AssetData(
0, AssetType.TEXTURE, "dummy", api_convention=0)
@@ -1220,22 +1317,29 @@ def build_assets(cTB):
cell = grid.column(align=True)
box_thumb = cell.box().column()
# Add the name row
name_row = box_thumb.row(align=True)
ui_label = "" if "dummy" in asset_name.lower() else asset_name_display
name_row.label(text=ui_label)
name_row.scale_y = 0.8
name_row.alignment = "CENTER"
name_row.enabled = False # To fade label for less contrast.
# Icon element
is_dummy = asset_name == "dummy"
if not is_dummy and asset_data is not None:
# name_row.enabled = False
_create_icon_asset_layout(cTB, name_row, asset_data)
name_row.alignment = "LEFT"
# Label element
name_label_row = name_row.row(align=True)
ui_label = "" if is_dummy else asset_name_display
name_label_row.label(text=ui_label)
name_label_row.scale_y = 0.7
name_label_row.alignment = "LEFT"
name_label_row.enabled = False
# Draw the thumbnail normally
_draw_thumbnail(cTB, asset_data, thumb_size_factor, box_thumb)
# See if there's any errors associated with this asset,
# such as after or during purchase/download failure.
error = None
if asset_data.state.dl.has_error():
error = asset_data.state.dl.error
if asset_data.state.purchase.has_error():
error = asset_data.state.purchase.error
error = asset_data.state.get_any_error()
row = cell.row(align=True)
@@ -1248,19 +1352,15 @@ def build_assets(cTB):
elif asset_data.state.dl.is_in_progress():
_draw_thumb_state_asset_downloading(
row, asset_data, thumb_width)
elif area in [KEY_TAB_ONLINE, KEY_TAB_MY_ASSETS]:
elif area in [KEY_TAB_ONLINE,
KEY_TAB_MY_ASSETS,
KEY_TAB_LOCAL,
KEY_TAB_RECENT_DOWNLOADS]:
is_tex = asset_type == AssetType.TEXTURE
is_local = asset_data.is_local
if cTB.is_unlimited_user() and is_local:
_draw_checkmark_unlimited(cTB, row)
elif cTB.is_unlimited_user():
# No need for green checkmark or wm preview if unlimited
pass
elif is_tex and not is_purchased:
if is_tex and not is_purchased:
_draw_button_quick_preview(
cTB, row, asset_data, is_selection)
elif is_purchased and area == KEY_TAB_ONLINE:
_draw_checkmark_purchased(cTB, row)
if is_purchased or cTB.is_unlimited_user():
if is_downloaded:
@@ -1323,7 +1423,4 @@ def build_assets(cTB):
_draw_page_buttons(cTB, area, idx_page_current)
if area == KEY_TAB_MY_ASSETS:
_draw_view_more_my_assets(cTB, box_not_found)
elif area == KEY_TAB_IMPORTED:
_draw_view_more_imported(cTB, sorted_assets)
_draw_reset_filters(cTB, area, box_not_found)
@@ -36,11 +36,13 @@ def build_library(cTB):
cTB.vBase.separator()
disabled_row = cTB.vBase.row()
disabled_row.enabled = False
wrapped_label(
cTB,
cTB.width_draw_ui,
_t("Select where you will store Poliigon assets."),
cTB.vBase
disabled_row
)
cTB.vBase.separator()
@@ -25,10 +25,12 @@ from .utils_dlg import (
from ..toolbox import c_Toolbox
ERR_CREDS_FORMAT = _t("Invalid email format/password length.")
# TODO(Andreas): Currently not sure about this error
ERR_LOGIN_TIMEOUT = _t("Login with website timed out, please try again")
# Base threshold for responsive layout adjustment (unscaled)
RESPONSIVE_WIDTH_BASE = 250
def _draw_welcome_or_error(cTB: c_Toolbox, layout: bpy.types.UILayout) -> None:
if cTB.user_invalidated() and not cTB.login_in_progress:
@@ -66,9 +68,8 @@ def _draw_welcome_or_error(cTB: c_Toolbox, layout: bpy.types.UILayout) -> None:
def _draw_share_addon_errors(cTB: c_Toolbox,
layout: bpy.types.UILayout,
enabled: bool = True) -> None:
# Show terms of service, optin/out.
row_opt = layout.row()
row_opt.alignment = "LEFT"
row_opt.alignment = "CENTER"
row_opt.enabled = enabled
# __spec__.parent since __package__ got deprecated
# Since this module moved into dialogs,
@@ -76,237 +77,176 @@ def _draw_share_addon_errors(cTB: c_Toolbox,
spec_parent = __spec__.parent
spec_parent = spec_parent.split(".")[0]
prefs = bpy.context.preferences.addons.get(spec_parent, None)
row_opt.prop(prefs.preferences, "reporting_opt_in", text="")
twidth = cTB.width_draw_ui - 42 * get_ui_scale(cTB)
wrapped_label(cTB, twidth, _t("Share addon errors / usage"), row_opt)
def _draw_switch_email_login(col: bpy.types.UILayout,
enabled: bool = True) -> None:
row_login_email = col.row()
row_login_email.enabled = enabled
op_login_email = row_login_email.operator("poliigon.poliigon_user",
text=_t("Login via email"),
emboss=False)
op_login_email.mode = "login_switch_to_email"
op_login_email.tooltip = _t("Login via email")
def _draw_browser_login(cTB: c_Toolbox, col: bpy.types.UILayout) -> None:
if cTB.login_in_progress:
_draw_share_addon_errors(cTB, col, enabled=False)
row_buttons = col.row(align=True)
row_buttons.scale_y = 1.25
col1 = row_buttons.column(align=True)
op_login_website = col1.operator("poliigon.poliigon_user",
text=_t("Opening browser..."),
depress=True)
op_login_website.mode = "none"
op_login_website.tooltip = _t("Complete login via opened webpage")
col1.enabled = False
col2 = row_buttons.column(align=True)
op_login_cancel = col2.operator("poliigon.poliigon_user",
text="",
icon="X")
op_login_cancel.mode = "login_cancel"
op_login_cancel.tooltip = _t("Cancel Log In")
col.separator()
_draw_switch_email_login(col, enabled=False)
else:
_draw_share_addon_errors(cTB, col)
row_button = col.row()
row_button.scale_y = 1.25
op_login_website = row_button.operator("poliigon.poliigon_user",
text=_t("Login via Browser"))
op_login_website.mode = "login_with_website"
op_login_website.tooltip = _t("Login via Browser")
col.separator()
_draw_switch_email_login(col)
def _draw_email_login(cTB: c_Toolbox, col: bpy.types.UILayout) -> None:
vProps = bpy.context.window_manager.poliigon_props
col.label(text="Email")
row = col.row(align=True)
row.prop(vProps, "vEmail")
col_x = row.column(align=True)
op = col_x.operator("poliigon.poliigon_setting",
text="",
icon="X")
op.tooltip = _t("Clear Email")
op.mode = "clear_email"
error_credentials = False
has_login_error = cTB.last_login_error is not None
error_login = has_login_error and cTB.last_login_error != ERR_LOGIN_TIMEOUT
if error_login and "@" not in vProps.vEmail:
error_credentials = True
col.separator()
if prefs and prefs.preferences:
row_opt.prop(prefs.preferences, "reporting_opt_in", text="")
twidth = int(cTB.width_draw_ui - 42 * get_ui_scale(cTB))
wrapped_label(
cTB,
cTB.width_draw_ui - 40 * get_ui_scale(cTB),
_t("Email format is invalid e.g. john@example.org"),
col,
icon="ERROR")
col.separator()
col.label(text=_t("Password"))
row = col.row(align=True)
if cTB.settings["show_pass"]:
row.prop(vProps, "vPassShow")
vPass = vProps.vPassShow
else:
row.prop(vProps, "vPassHide")
vPass = vProps.vPassHide
col_x = row.column(align=True)
op = col_x.operator("poliigon.poliigon_setting",
text="",
icon="X")
op.tooltip = _t("Clear Password")
op.mode = "clear_pass"
if error_login and len(vPass) < 6:
error_credentials = True
col.separator()
wrapped_label(
cTB,
cTB.width_draw_ui - 40 * get_ui_scale(cTB),
_t("Password should be at least 6 characters."),
col,
icon="ERROR")
col.separator()
_draw_share_addon_errors(cTB, col)
enable_login_button = len(vProps.vEmail) > 0 and len(vPass) > 0
row = col.row()
row.scale_y = 1.25
if cTB.login_in_progress:
op_login = row.operator("poliigon.poliigon_setting",
text=_t("Logging In..."),
depress=enable_login_button)
op_login.mode = "none"
op_login.tooltip = _t("Logging In...")
row.enabled = False
else:
op_login = row.operator("poliigon.poliigon_user",
text=_t("Login via email"))
op_login.mode = "login"
op_login.tooltip = _t("Login via email")
row.enabled = enable_login_button
if cTB.last_login_error == ERR_CREDS_FORMAT:
# Will draw above with more specific messages if condition true, like
# invalid email format or password length.
pass
elif error_login and not error_credentials:
col.separator()
wrapped_label(
cTB,
cTB.width_draw_ui - 40 * get_ui_scale(cTB),
cTB.last_login_error,
col,
icon="ERROR",
twidth,
_t("Share addon errors / usage"),
row_opt
)
col.separator()
op_forgot = col.operator("poliigon.poliigon_link",
text=_t("Forgot Password?"),
emboss=False)
op_forgot.mode = "forgot"
op_forgot.tooltip = _t("Reset your Poliigon password")
op_login_website = col.operator("poliigon.poliigon_user",
text=_t("Login via Browser"),
emboss=False)
op_login_website.mode = "login_switch_to_browser"
op_login_website.tooltip = _t("Login via Browser")
def _draw_login(cTB, layout: bpy.types.UILayout) -> None:
spc = 1.0 / cTB.width_draw_ui
"""Draw the simplified login UI with only web login option."""
# Calculate the responsive width threshold with proper scaling
responsive_width_threshold = RESPONSIVE_WIDTH_BASE * get_ui_scale(cTB)
box = layout.box()
row = box.row()
row.separator(factor=spc)
col = row.column()
row.separator(factor=spc)
twidth = cTB.width_draw_ui - 42 * get_ui_scale(cTB)
wrapped_label(cTB, twidth, _t("Login"), col)
col.separator()
if cTB.login_mode_browser:
_draw_browser_login(cTB, col)
else:
_draw_email_login(cTB, col)
def _draw_signup(cTB, layout: bpy.types.UILayout) -> None:
row_welcome = layout.row()
row_welcome.alignment = 'CENTER'
wrapped_label(
cTB,
cTB.width_draw_ui,
_t("Don't have an account?"),
layout,
int(cTB.width_draw_ui * 0.9),
_t("Welcome to the Poliigon Addon!"),
row_welcome
)
op_signup = layout.operator("poliigon.poliigon_link",
text=_t("Sign Up"))
op_signup.mode = "signup"
op_signup.tooltip = _t("Create a Poliigon account")
row_subtitle = layout.row()
row_subtitle.alignment = 'CENTER'
row_subtitle.enabled = False # Darker shading for subtitle
wrapped_label(
cTB,
int(cTB.width_draw_ui * 0.9),
_t("Login or Sign Up below in your browser to get started"),
row_subtitle,
alignment='CENTER'
)
layout.separator()
# Content with padding for buttons and other elements
col = layout.column(align=True)
col.separator(factor=0.5)
if cTB.login_in_progress:
# Create a row for the main button
main_row = col.row(align=True)
main_row.scale_y = 1.25
# Left side: "Opening browser..." button (90% of width)
openingcol = main_row.column(align=True)
openingcol.enabled = False
main_button = openingcol.operator(
"poliigon.poliigon_user",
text=_t("Opening browser..."),
depress=True
)
main_button.mode = "none"
main_button.tooltip = _t("Complete authentication via opened webpage")
# Right side: X button (10% of width)
cancelcol = main_row.column(align=True)
cancel_button = cancelcol.operator(
"poliigon.poliigon_user",
text="",
icon="X"
)
cancel_button.mode = "login_cancel"
cancel_button.tooltip = _t("Cancel Authentication")
# Disable only the main button, not the whole row
main_row.enabled = True
# Add space where the second button would be
col.separator()
else:
# Login button
row_login = col.row()
row_login.scale_y = 1.25
op_login = row_login.operator(
"poliigon.poliigon_user",
text=_t("Login"),
depress=True
)
op_login.mode = "login_with_website"
op_login.tooltip = _t("Login via Browser")
col.separator()
# Pass enabled=False to _draw_share_addon_errors when login is in progress
_draw_share_addon_errors(cTB, col, enabled=not cTB.login_in_progress)
# Help and legal links - responsive layout
col.separator()
# Get current region width and log it for reference
region_width = bpy.context.region.width if hasattr(bpy.context, "region") else 0
cTB.logger_ui.debug(f"Current region width: {region_width}")
# Determine layout based on width threshold
if region_width > 0 and region_width < responsive_width_threshold:
# Narrow layout - display links in three separate rows in new order
# 1. Help
row_help = col.row()
row_help.alignment = 'CENTER'
row_help.enabled = not cTB.login_in_progress
op_help = row_help.operator(
"poliigon.poliigon_link",
text=_t("Need Help?"),
emboss=False
)
op_help.mode = "help"
op_help.tooltip = _t("Get help with the Poliigon addon")
# 2. Privacy Policy
row_privacy = col.row()
row_privacy.alignment = 'CENTER'
row_privacy.enabled = not cTB.login_in_progress
op_privacy = row_privacy.operator(
"poliigon.poliigon_link",
text=_t("Privacy Policy"),
emboss=False
)
op_privacy.mode = "privacy"
op_privacy.tooltip = _t("View the Privacy Policy")
# 3. Terms & Conditions
row_terms = col.row()
row_terms.alignment = 'CENTER'
row_terms.enabled = not cTB.login_in_progress
op_terms = row_terms.operator(
"poliigon.poliigon_link",
text=_t("Terms & Conditions"),
emboss=False
)
op_terms.mode = "terms"
op_terms.tooltip = _t("View the terms and conditions page")
else:
# Wide layout - two rows but with reordered links
# First row: Help and Privacy Policy
row_first = col.row(align=True)
row_first.enabled = not cTB.login_in_progress
op_help = row_first.operator(
"poliigon.poliigon_link",
text=_t("Need Help?"),
emboss=False
)
op_help.mode = "help"
op_help.tooltip = _t("Get help with the Poliigon addon")
op_privacy = row_first.operator(
"poliigon.poliigon_link",
text=_t("Privacy Policy"),
emboss=False
)
op_privacy.mode = "privacy"
op_privacy.tooltip = _t("View the Privacy Policy")
# Second row: Terms & Conditions
row_second = col.row()
row_second.alignment = 'CENTER'
row_second.enabled = not cTB.login_in_progress
op_terms = row_second.operator(
"poliigon.poliigon_link",
text=_t("Terms & Conditions"),
emboss=False
)
op_terms.mode = "terms"
op_terms.tooltip = _t("View the terms and conditions page")
def _draw_legal(layout: bpy.types.UILayout) -> None:
row = layout.row()
col = row.column(align=True)
op_terms = col.operator("poliigon.poliigon_link",
text=_t("Terms & Conditions"),
emboss=False)
op_terms.tooltip = _t("View the terms and conditions page")
op_terms.mode = "terms"
op_privacy = col.operator("poliigon.poliigon_link",
text=_t("Privacy Policy"),
emboss=False)
op_privacy.tooltip = _t("View the Privacy Policy ")
op_privacy.mode = "privacy"
# @timer
def build_login(cTB):
cTB.logger_ui.debug("build_login")
if cTB.last_login_error is not None:
cTB.login_in_progress = 0
_draw_welcome_or_error(cTB, cTB.vBase)
_draw_login(cTB, cTB.vBase)
cTB.vBase.separator()
_draw_signup(cTB, cTB.vBase)
cTB.vBase.separator()
_draw_legal(cTB.vBase)
@@ -0,0 +1,128 @@
# #### 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 #####
import bpy
from ..modules.poliigon_core.multilingual import _t
from ..modules.poliigon_core.user import PoliigonUserProfiles
from .utils_dlg import (
get_ui_scale,
wrapped_label)
from ..toolbox import c_Toolbox
def _draw_welcome_message(cTB: c_Toolbox, layout: bpy.types.UILayout) -> None:
"""Draw the welcome message and setup prompt."""
row = layout.row()
row.enabled = False
row.alignment = "CENTER"
wrapped_label(
cTB,
int(cTB.width_draw_ui * 0.9),
_t("Success! You are now logged in."),
row,
alignment='CENTER'
)
layout.separator()
wrapped_label(
cTB,
int(cTB.width_draw_ui * 0.9),
_t("Please complete your account setup below to continue"),
layout,
alignment='CENTER'
)
layout.separator()
def _draw_user_type_selection(cTB: c_Toolbox, layout: bpy.types.UILayout) -> None:
"""Draw the user type selection radio buttons."""
twidth = cTB.width_draw_ui - 42 * get_ui_scale(cTB)
wrapped_label(
cTB,
int(twidth),
_t("Which best describes you?"),
layout,
alignment="CENTER"
)
layout.separator()
if not cTB.signal_for_profile_survey_emitted:
cTB.signal_for_profile_survey_emitted = True
cTB.signal_view_screen("update_user")
for user_type in PoliigonUserProfiles.get_list():
row = layout.row()
row.scale_y = 1.2
display_type = PoliigonUserProfiles.from_string(user_type).to_ui_display()
op = row.operator(
"poliigon.poliigon_onboarding_survey",
text=_t(display_type))
op.user_type = user_type
layout.separator()
def _draw_email_preference(cTB: c_Toolbox, layout: bpy.types.UILayout) -> None:
"""Draw the email preference checkbox."""
vProps = bpy.context.window_manager.poliigon_props
row_opt = layout.row()
row_opt.alignment = "LEFT"
row_opt.prop(vProps, "onboarding_email_preference", text="")
twidth = cTB.width_draw_ui - 28 * get_ui_scale(cTB)
wrapped_label(
cTB,
int(twidth),
_t("Send me emails about new assets and other product updates"),
row_opt
)
def _draw_onboarding_survey(cTB: c_Toolbox, layout: bpy.types.UILayout) -> None:
"""Draw the main onboarding survey form."""
spc = 1.0 / cTB.width_draw_ui
_draw_welcome_message(cTB, layout)
# User type selection in darker box
box = layout.box()
row = box.row()
row.separator(factor=spc)
col = row.column()
row.separator(factor=spc)
_draw_user_type_selection(cTB, col)
# Email preference outside the box
layout.separator()
layout.separator()
_draw_email_preference(cTB, layout)
def build_onboarding_survey(cTB: c_Toolbox):
"""Main function to build the onboarding survey dialog."""
vProps = bpy.context.window_manager.poliigon_props
# Initialize properties if they don't exist
vProps.onboarding_user_type = ""
_draw_onboarding_survey(cTB, cTB.vBase)
@@ -118,7 +118,9 @@ def show_quick_menu(
_imported_model_extras(context, layout)
# List the different resolution sizes to provide.
if asset_data.is_purchased or is_free or cTB.is_unlimited_user():
have_size_opts = asset_data.is_purchased or is_free or cTB.is_unlimited_user()
have_size_opts &= not asset_data.state.has_error
if have_size_opts:
for size in sizes:
if asset_type == AssetType.TEXTURE:
draw_material_sizes(context, size, layout)
@@ -152,22 +154,20 @@ def show_quick_menu(
# Always show view online and high res previews.
if not hide_detail_view:
# new detail viewer design is unstable on OSX and fails for awhile
# for the remainder of the session. For consistency, we disable it
# outright for all OSX users until a better solution is found.
is_osx = bpy.app.build_platform.lower() == b"darwin"
if bpy.app.version >= (4, 2) and not is_osx:
op_name = "poliigon.detail_view_open"
op_text = _t("View Asset Details")
if bpy.app.version >= (4, 2):
row_detail_view = layout.row()
row_detail_view.operator_context = 'INVOKE_DEFAULT'
op = row_detail_view.operator(
"poliigon.detail_view",
text=_t("View Asset Details"),
icon="OUTLINER_OB_IMAGE",
)
else:
op_name = "poliigon.view_thumbnail"
op_text = _t("View Large Preview")
op = layout.operator(
op_name,
text=op_text,
icon="OUTLINER_OB_IMAGE",
)
op = layout.operator(
"poliigon.view_thumbnail",
text=_t("View Large Preview"),
icon="OUTLINER_OB_IMAGE",
)
op.asset_id = asset_data.asset_id
op = layout.operator(
@@ -197,15 +197,26 @@ def show_quick_menu(
if not is_feature_avail or missing_local_model:
return
# Asset browser sync option
client_starting = cTB.lock_client_start.locked()
owned = cTB.check_if_purchased(asset_data.asset_id)
layout.separator()
row = layout.row()
op = row.operator(
"poliigon.update_asset_browser",
text=_t("Synchronize with Asset Browser"),
icon="FILE_REFRESH")
op.asset_id = asset_id
row.enabled = not in_asset_browser and not client_starting
msg = _t("Synchronize with Asset Browser")
if not owned:
msg = _t("Must own to sync with Asset Browser")
elif in_asset_browser:
msg = _t("Already synced with Asset Browser")
elif client_starting:
msg = _t("Starting Asset Browser sync...")
if cTB.user_legacy_own_assets():
op = row.operator(
"poliigon.update_asset_browser",
text=msg,
icon="FILE_REFRESH")
op.asset_id = asset_id
row.enabled = not in_asset_browser and not client_starting and owned
def draw_material_sizes(
context, size: str, layout: bpy.types.UILayout) -> None:
@@ -99,6 +99,9 @@ def safe_size_apply(cTB,
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:
@@ -157,16 +160,18 @@ def wrapped_label(cTB,
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}")
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:
@@ -183,16 +188,36 @@ def wrapped_label(cTB,
if width_line > width:
if first:
if icon is None:
parent.label(text=line)
if alignment == 'LEFT':
parent.label(text=line)
else:
label_row = parent.row()
label_row.alignment = alignment
label_row.label(text=line)
else:
parent.label(text=line, icon=icon)
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:
parent.label(text=line)
if alignment == 'LEFT':
parent.label(text=line)
else:
label_row = parent.row()
label_row.alignment = alignment
label_row.label(text=line)
else:
parent.label(text=line, icon="BLANK1")
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 + " "
@@ -201,15 +226,32 @@ def wrapped_label(cTB,
if line != "":
if icon is None:
parent.label(text=line)
if alignment == 'LEFT':
parent.label(text=line)
else:
label_row = parent.row()
label_row.alignment = alignment
label_row.label(text=line)
else:
if first:
parent.label(text=line, icon=icon)
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:
parent.label(text=line, icon="BLANK1")
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:
parent.label(text="")
spacer = parent.row()
spacer.scale_y = 0.3
spacer.label(text="")
def separator_p4b(