1554 lines
62 KiB
Python
1554 lines
62 KiB
Python
"""
|
|
Text Texture Generator - UI Panels Module
|
|
Contains all UI panel classes for different editor contexts.
|
|
"""
|
|
|
|
import bpy
|
|
from bpy.types import Panel
|
|
from ..core.generation_engine import generate_preview
|
|
from ..utils.constants import (
|
|
is_free_version, get_version_badge_text, get_version_badge_icon,
|
|
get_upgrade_hint_text, should_show_feature_lock,
|
|
get_feature_availability_text, get_version_info_details,
|
|
has_image_overlays, has_basic_utilities, has_presets,
|
|
has_normal_maps, has_batch_processing, has_advanced_utilities,
|
|
has_advanced_styling
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# SHARED UI COMPONENTS
|
|
# ============================================================================
|
|
|
|
def is_operator_available(operator_id):
|
|
"""Check if an operator is available/registered in Blender"""
|
|
try:
|
|
import bpy
|
|
# Try to get operator class - if it doesn't exist, this will fail
|
|
return hasattr(bpy.ops, operator_id.split('.')[0]) and hasattr(getattr(bpy.ops, operator_id.split('.')[0]), operator_id.split('.')[1])
|
|
except:
|
|
return False
|
|
|
|
def create_premium_lock_function(feature_name=None):
|
|
"""Create a standard premium lock function for a specific feature
|
|
|
|
Args:
|
|
feature_name: Optional feature name for context-specific hints
|
|
|
|
Returns:
|
|
A function that can be used as fallback_content_func in safe_operator_call
|
|
"""
|
|
def show_premium_lock(fallback_layout):
|
|
fallback_layout.enabled = False
|
|
fallback_layout.label(text="🔒 Pro Feature", icon='LOCKED')
|
|
return show_premium_lock
|
|
|
|
def safe_operator_call(layout, operator_id, fallback_content_func=None, **kwargs):
|
|
"""Safely call an operator, showing premium content if operator doesn't exist
|
|
|
|
Args:
|
|
layout: Blender UI layout object
|
|
operator_id: String ID of operator (e.g., 'text_texture.add_overlay')
|
|
fallback_content_func: Function to call if operator doesn't exist
|
|
**kwargs: Arguments to pass to layout.operator()
|
|
|
|
Returns:
|
|
Operator reference if successful, None if operator doesn't exist
|
|
"""
|
|
if is_operator_available(operator_id):
|
|
return layout.operator(operator_id, **kwargs)
|
|
else:
|
|
# Operator doesn't exist - show premium lock content instead
|
|
if fallback_content_func:
|
|
fallback_content_func(layout)
|
|
else:
|
|
# Default fallback - show generic premium lock
|
|
lock_row = layout.row()
|
|
lock_row.enabled = False
|
|
lock_row.label(text="🔒 Pro Feature", icon='LOCKED')
|
|
return None
|
|
|
|
def draw_collapsible_header(layout, prop_name, text, icon='TRIA_DOWN'):
|
|
pass
|
|
"""Draw a collapsible section header with expand/collapse arrow"""
|
|
props = bpy.context.scene.text_texture_props
|
|
expanded = getattr(props, prop_name)
|
|
|
|
box = layout.box()
|
|
header_row = box.row()
|
|
|
|
# Draw arrow icon and toggle expanded state
|
|
arrow_icon = 'TRIA_DOWN' if expanded else 'TRIA_RIGHT'
|
|
header_row.prop(props, prop_name, text="", icon=arrow_icon, emboss=False)
|
|
header_row.label(text=text, icon=icon)
|
|
|
|
return box if expanded else None
|
|
|
|
def draw_version_badge(layout):
|
|
pass
|
|
"""Draw version badge in panel header"""
|
|
badge_row = layout.row(align=True)
|
|
badge_row.scale_x = 0.7
|
|
badge_row.scale_y = 0.8
|
|
badge_row.alignment = 'RIGHT'
|
|
|
|
# Simple label-based badge (more reliable than operator)
|
|
badge_label = badge_row.label(text=get_version_badge_text())
|
|
if is_free_version():
|
|
pass
|
|
badge_row.label(text="", icon='GIFT')
|
|
else:
|
|
pass
|
|
badge_row.label(text="", icon='STAR')
|
|
|
|
def draw_feature_lock_indicator(layout, feature_name, show_hint=True):
|
|
"""Draw feature lock indicator for premium features"""
|
|
if should_show_feature_lock(feature_name):
|
|
# Create a box for better visual separation
|
|
lock_box = layout.box()
|
|
|
|
# Main lock indicator with proper vertical centering using row alignment
|
|
lock_row = lock_box.row(align=True)
|
|
lock_row.scale_y = 1.2 # Slightly larger for better visual balance
|
|
lock_row.alignment = 'CENTER'
|
|
|
|
# Use a single row with proper alignment instead of split for better centering
|
|
# Add icon with proper spacing
|
|
icon_col = lock_row.column(align=True)
|
|
icon_col.scale_x = 0.8
|
|
icon_col.alignment = 'CENTER'
|
|
icon_col.label(text="", icon='LOCKED')
|
|
|
|
# Add text with proper alignment
|
|
text_col = lock_row.column(align=True)
|
|
text_col.alignment = 'CENTER'
|
|
text_col.label(text="🔒 Pro Feature")
|
|
|
|
if show_hint:
|
|
hint_row = lock_box.row()
|
|
hint_row.scale_y = 0.8
|
|
hint_row.alignment = 'CENTER'
|
|
hint_row.label(text=get_upgrade_hint_text(feature_name))
|
|
|
|
def draw_version_info_section(layout, props):
|
|
pass
|
|
"""Draw version information display section"""
|
|
info_box = layout.box()
|
|
info_box.label(text="Version Information", icon='INFO')
|
|
|
|
# Version details
|
|
version_info = get_version_info_details()
|
|
|
|
col = info_box.column(align=True)
|
|
col.scale_y = 0.8
|
|
|
|
# Basic version info
|
|
col.label(text=f"Version: {version_info['version_type']}")
|
|
col.label(text=get_feature_availability_text())
|
|
|
|
# Current limits
|
|
limits_col = col.column(align=True)
|
|
limits_col.separator()
|
|
limits_col.label(text="Current Limits:")
|
|
limits_col.label(text=f"• Max texture size: {version_info['max_texture_size']}px")
|
|
limits_col.label(text=f"• Active features: {version_info['features_enabled']}")
|
|
|
|
# Feature availability status
|
|
col.separator()
|
|
availability_col = col.column(align=True)
|
|
availability_col.label(text="Feature Status:")
|
|
|
|
if has_presets():
|
|
pass
|
|
availability_col.label(text="• Presets: ✓ Available", icon='CHECKMARK')
|
|
else:
|
|
pass
|
|
availability_col.label(text="• Presets: 🔒 Pro Only", icon='LOCKED')
|
|
|
|
if has_image_overlays():
|
|
pass
|
|
availability_col.label(text="• Image Overlays: ✓ Available", icon='CHECKMARK')
|
|
else:
|
|
pass
|
|
availability_col.label(text="• Image Overlays: 🔒 Pro Only", icon='LOCKED')
|
|
|
|
if has_basic_utilities():
|
|
pass
|
|
availability_col.label(text="• Basic Tools: ✓ Available", icon='CHECKMARK')
|
|
else:
|
|
pass
|
|
availability_col.label(text="• Basic Tools: 🔒 Pro Only", icon='LOCKED')
|
|
|
|
if has_advanced_utilities():
|
|
pass
|
|
availability_col.label(text="• Advanced Tools: ✓ Available", icon='CHECKMARK')
|
|
else:
|
|
pass
|
|
availability_col.label(text="• Advanced Tools: 🔒 Pro Only", icon='LOCKED')
|
|
|
|
# Upgrade hint for free version
|
|
if version_info['upgrade_available']:
|
|
pass
|
|
col.separator()
|
|
upgrade_box = info_box.box()
|
|
upgrade_box.scale_y = 0.9
|
|
upgrade_row = upgrade_box.row()
|
|
upgrade_row.alignment = 'CENTER'
|
|
upgrade_row.label(text="💡 Upgrade to Pro for all features", icon='STAR')
|
|
|
|
def draw_text_input_section(layout, props):
|
|
pass
|
|
"""Draw the main text input field"""
|
|
layout.prop(props, "text", text="")
|
|
|
|
def draw_font_dropdown_section(layout, props):
|
|
pass
|
|
"""Draw basic font dropdown for top level"""
|
|
row = layout.row(align=True)
|
|
row.prop(props, "font_path", text="")
|
|
row.operator("text_texture.refresh_fonts", text="", icon='FILE_REFRESH')
|
|
|
|
def draw_generate_button(layout):
|
|
pass
|
|
"""Draw the main generate button"""
|
|
row = layout.row()
|
|
row.scale_y = 1.5
|
|
row.operator("text_texture.generate", text="Generate Text Texture", icon='IMAGE_DATA')
|
|
|
|
def draw_image_overlays_section(layout, props):
|
|
pass
|
|
"""Draw image overlays controls with collapsible individual overlays"""
|
|
# Check if image overlays are available
|
|
if not has_image_overlays():
|
|
pass
|
|
draw_feature_lock_indicator(layout, 'image_overlays')
|
|
return
|
|
|
|
# Add overlay button - use safe operator call
|
|
row = layout.row()
|
|
safe_operator_call(row, "text_texture.add_overlay",
|
|
lambda layout: draw_feature_lock_indicator(layout, 'image_overlays'),
|
|
text="Add Image Overlay", icon='ADD')
|
|
|
|
# Individual overlay controls
|
|
if props.image_overlays:
|
|
pass
|
|
layout.separator()
|
|
|
|
for i, overlay in enumerate(props.image_overlays):
|
|
pass
|
|
# Create collapsible box for each overlay
|
|
box = layout.box()
|
|
|
|
# Header row with controls
|
|
header_row = box.row(align=True)
|
|
|
|
# Enable/Disable checkbox with clear label
|
|
header_row.prop(overlay, "enabled", text="", icon='CHECKBOX_HLT' if overlay.enabled else 'CHECKBOX_DEHLT')
|
|
|
|
# Overlay name
|
|
name_row = header_row.row()
|
|
name_row.enabled = overlay.enabled # Gray out name if disabled
|
|
name_row.prop(overlay, "name", text="")
|
|
|
|
# Action buttons
|
|
# Duplicate button - use safe operator call
|
|
premium_lock = create_premium_lock_function()
|
|
dup_op = safe_operator_call(header_row, "text_texture.duplicate_overlay", premium_lock,
|
|
text="", icon='DUPLICATE')
|
|
if dup_op:
|
|
dup_op.overlay_index = i
|
|
|
|
# Delete button - use safe operator call
|
|
del_op = safe_operator_call(header_row, "text_texture.delete_overlay", premium_lock,
|
|
text="", icon='TRASH')
|
|
if del_op:
|
|
del_op.overlay_index = i
|
|
|
|
# Move buttons for reordering - use safe operator calls
|
|
if i > 0:
|
|
move_up_op = safe_operator_call(header_row, "text_texture.move_overlay", premium_lock,
|
|
text="", icon='TRIA_UP')
|
|
if move_up_op:
|
|
move_up_op.overlay_index = i
|
|
move_up_op.direction = 'UP'
|
|
|
|
if i < len(props.image_overlays) - 1:
|
|
move_down_op = safe_operator_call(header_row, "text_texture.move_overlay", premium_lock,
|
|
text="", icon='TRIA_DOWN')
|
|
if move_down_op:
|
|
move_down_op.overlay_index = i
|
|
move_down_op.direction = 'DOWN'
|
|
|
|
# Show details section with enable/disable state
|
|
details_col = box.column()
|
|
details_col.enabled = overlay.enabled # Gray out all controls if disabled
|
|
|
|
# Status indicator
|
|
if not overlay.enabled:
|
|
pass
|
|
status_row = details_col.row()
|
|
status_row.label(text="⚠️ Overlay Disabled", icon='INFO')
|
|
details_col.separator()
|
|
|
|
# Image file selection
|
|
details_col.prop(overlay, "image_path", text="Image File")
|
|
|
|
# Positioning mode
|
|
details_col.prop(overlay, "positioning_mode", text="Position Mode")
|
|
|
|
if overlay.positioning_mode == 'ABSOLUTE':
|
|
pass
|
|
# Position controls for absolute mode
|
|
pos_row = details_col.row(align=True)
|
|
pos_row.prop(overlay, "x_position", text="X Position")
|
|
pos_row.prop(overlay, "y_position", text="Y Position")
|
|
elif overlay.positioning_mode in ['APPEND', 'PREPEND']:
|
|
# Spacing controls for append/prepend modes with percentage labels
|
|
details_col.prop(overlay, "text_spacing", text="Text Spacing (%)")
|
|
details_col.prop(overlay, "image_spacing", text="Image Spacing (%)")
|
|
|
|
# Transform controls
|
|
trans_row = details_col.row(align=True)
|
|
trans_row.prop(overlay, "scale", text="Scale")
|
|
trans_row.prop(overlay, "rotation", text="Rotation")
|
|
trans_row.prop(overlay, "z_index", text="Z-Level")
|
|
|
|
def draw_positioning_section(layout, props):
|
|
pass
|
|
"""Draw positioning and alignment controls"""
|
|
from ..utils.system import get_anchor_point_matrix
|
|
|
|
# Dimensions moved here from top level
|
|
layout.label(text="Dimensions:", icon='SETTINGS')
|
|
row = layout.row(align=True)
|
|
row.prop(props, "texture_width", text="Width")
|
|
row.prop(props, "texture_height", text="Height")
|
|
|
|
layout.separator()
|
|
|
|
# Prepend/Append Text Section
|
|
layout.label(text="Additional Text:", icon='TEXT')
|
|
|
|
# Compact layout selector
|
|
layout.prop(props, "prepend_append_layout", text="Layout")
|
|
|
|
# Minimal prepend/append controls
|
|
row = layout.row(align=True)
|
|
row.prop(props, "prepend_text", text="Prepend")
|
|
if props.prepend_text.strip():
|
|
pass
|
|
row.prop(props, "prepend_margin", text="", slider=True)
|
|
|
|
row = layout.row(align=True)
|
|
row.prop(props, "append_text", text="Append")
|
|
if props.append_text.strip():
|
|
pass
|
|
row.prop(props, "append_margin", text="", slider=True)
|
|
|
|
layout.separator()
|
|
layout.prop(props, "position_mode", text="Mode")
|
|
|
|
if props.position_mode == 'PRESET':
|
|
pass
|
|
layout.separator()
|
|
layout.label(text="Anchor Point:", icon='ANCHOR_TOP')
|
|
|
|
anchor_matrix = get_anchor_point_matrix()
|
|
for row_idx, row in enumerate(anchor_matrix):
|
|
pass
|
|
grid_row = layout.row(align=True)
|
|
grid_row.scale_y = 1.2
|
|
for col_idx, anchor in enumerate(row):
|
|
pass
|
|
if anchor == props.anchor_point:
|
|
premium_lock = create_premium_lock_function()
|
|
op = safe_operator_call(grid_row, "text_texture.set_anchor_point", premium_lock,
|
|
text="●", depress=True)
|
|
else:
|
|
premium_lock = create_premium_lock_function()
|
|
op = safe_operator_call(grid_row, "text_texture.set_anchor_point", premium_lock,
|
|
text="○")
|
|
if op:
|
|
op.anchor_point = anchor
|
|
|
|
# Margins
|
|
layout.separator()
|
|
margin_row = layout.row(align=True)
|
|
margin_row.label(text="Margins:")
|
|
margin_row.prop(props, "margins_linked", text="", icon='LINKED' if props.margins_linked else 'UNLINKED', toggle=True)
|
|
|
|
margins_row = layout.row(align=True)
|
|
margins_row.prop(props, "margin_x", text="X")
|
|
if not props.margins_linked:
|
|
pass
|
|
margins_row.prop(props, "margin_y", text="Y")
|
|
|
|
# Offsets
|
|
layout.separator()
|
|
layout.label(text="Fine-tune (pixels):")
|
|
offset_row = layout.row(align=True)
|
|
offset_row.prop(props, "offset_x", text="X")
|
|
offset_row.prop(props, "offset_y", text="Y")
|
|
|
|
else: # MANUAL mode
|
|
layout.separator()
|
|
layout.prop(props, "manual_position_unit", text="Unit")
|
|
manual_row = layout.row(align=True)
|
|
manual_row.prop(props, "manual_position_x", text="X")
|
|
manual_row.prop(props, "manual_position_y", text="Y")
|
|
|
|
layout.separator()
|
|
layout.prop(props, "constrain_to_canvas")
|
|
|
|
def draw_font_settings_section(layout, props):
|
|
pass
|
|
"""Draw extended font settings"""
|
|
# Font size moved here from top level
|
|
layout.prop(props, "font_size")
|
|
|
|
layout.separator()
|
|
layout.label(text="Font Selection:")
|
|
# Default font dropdown moved here from top level
|
|
row = layout.row(align=True)
|
|
row.prop(props, "font_path", text="")
|
|
row.operator("text_texture.refresh_fonts", text="", icon='FILE_REFRESH')
|
|
|
|
layout.separator()
|
|
# Custom font controls moved here from top level
|
|
layout.prop(props, "use_custom_font")
|
|
if props.use_custom_font:
|
|
pass
|
|
layout.prop(props, "custom_font_path", text="")
|
|
|
|
layout.separator()
|
|
row = layout.row(align=True)
|
|
row.prop(props, "padding")
|
|
row.prop(props, "text_align")
|
|
|
|
def draw_colors_section(layout, props):
|
|
pass
|
|
"""Draw color controls for text and background"""
|
|
layout.label(text="Text Color:", icon='COLOR')
|
|
layout.prop(props, "text_color", text="")
|
|
|
|
layout.separator()
|
|
layout.label(text="Background Color:", icon='IMAGE_PLANE')
|
|
layout.prop(props, "background_transparent")
|
|
if not props.background_transparent:
|
|
pass
|
|
layout.prop(props, "background_color", text="")
|
|
|
|
def draw_preview_options_section(layout, props):
|
|
pass
|
|
"""Draw preview options (preview-specific settings only)"""
|
|
layout.prop(props, "preview_size", text="Preview Size")
|
|
layout.separator()
|
|
layout.label(text="Preview Background Display:", icon='SCENE')
|
|
layout.prop(props, "preview_bg_type", text="Background")
|
|
if props.preview_bg_type == 'color':
|
|
pass
|
|
layout.prop(props, "preview_bg_color", text="Preview Display Color")
|
|
elif props.preview_bg_type == 'gradient':
|
|
layout.prop(props, "preview_bg_color", text="Color 1")
|
|
layout.prop(props, "preview_bg_color2", text="Color 2")
|
|
|
|
def draw_presets_section(layout, props):
|
|
pass
|
|
"""Draw unified presets save/load controls"""
|
|
# Check if presets are available, show lock if not
|
|
if not has_presets():
|
|
pass
|
|
draw_feature_lock_indicator(layout, 'presets')
|
|
return
|
|
|
|
# Save preset section - use safe operator calls
|
|
save_row = layout.row(align=True)
|
|
save_row.prop(props, "new_preset_name", text="", placeholder="Preset Name")
|
|
|
|
premium_lock = create_premium_lock_function()
|
|
safe_operator_call(save_row, "text_texture.save_preset", premium_lock,
|
|
text="Save", icon='FILE_NEW')
|
|
|
|
# Save with blend file checkbox
|
|
layout.prop(props, "save_with_blend_file", text="Project-specific (only available in this .blend file)")
|
|
|
|
layout.separator()
|
|
|
|
# Import/Export and refresh buttons - use safe operator calls
|
|
backup_row = layout.row(align=True)
|
|
|
|
export_lock = create_premium_lock_function()
|
|
import_lock = create_premium_lock_function()
|
|
|
|
safe_operator_call(backup_row, "text_texture.export_presets", export_lock,
|
|
text="Export Backup", icon='EXPORT')
|
|
safe_operator_call(backup_row, "text_texture.import_presets", import_lock,
|
|
text="Import Backup", icon='IMPORT')
|
|
|
|
utility_row = layout.row(align=True)
|
|
|
|
refresh_lock = create_premium_lock_function()
|
|
|
|
safe_operator_call(utility_row, "text_texture.refresh_presets", refresh_lock,
|
|
text="Refresh", icon='FILE_REFRESH')
|
|
|
|
layout.separator()
|
|
|
|
# Load presets section - unified list
|
|
if props.presets:
|
|
pass
|
|
layout.label(text="Presets", icon='PRESET')
|
|
|
|
# Sort presets alphabetically for consistent display
|
|
sorted_presets = sorted(props.presets, key=lambda p: p.name.lower())
|
|
|
|
for preset in sorted_presets:
|
|
pass
|
|
row = layout.row(align=True)
|
|
row.scale_y = 0.9
|
|
|
|
# Load button - use safe operator call
|
|
load_lock = create_premium_lock_function()
|
|
load_btn = safe_operator_call(row, "text_texture.load_preset", load_lock,
|
|
text=preset.name)
|
|
if load_btn:
|
|
load_btn.preset_name = preset.name
|
|
|
|
# Quick overwrite button - use safe operator call
|
|
overwrite_lock = create_premium_lock_function()
|
|
overwrite_btn = safe_operator_call(row, "text_texture.save_preset", overwrite_lock,
|
|
text="", icon='FILE_REFRESH')
|
|
if overwrite_btn:
|
|
overwrite_btn.overwrite = True
|
|
overwrite_btn.preset_name = preset.name
|
|
|
|
# Delete button - use safe operator call
|
|
delete_lock = create_premium_lock_function()
|
|
delete_btn = safe_operator_call(row, "text_texture.delete_preset", delete_lock,
|
|
text="", icon='TRASH')
|
|
if delete_btn:
|
|
delete_btn.preset_name = preset.name
|
|
|
|
else:
|
|
pass
|
|
layout.label(text="No presets yet")
|
|
layout.label(text="Save your settings above to create your first preset.")
|
|
|
|
def draw_shader_section(layout, props):
|
|
pass
|
|
"""Draw shader generation controls"""
|
|
# Check if shader generation is available
|
|
from ..utils.constants import has_shader_generation
|
|
if not has_shader_generation():
|
|
draw_feature_lock_indicator(layout, 'shader_generation')
|
|
return
|
|
|
|
# Create Shader Button (as requested)
|
|
row = layout.row()
|
|
row.scale_y = 1.5
|
|
row.operator("text_texture.generate_shader", text="Create Shader", icon='MATERIAL')
|
|
|
|
layout.separator()
|
|
|
|
# Clean minimalist controls
|
|
col = layout.column(align=True)
|
|
col.prop(props, "shader_type", text="Type")
|
|
|
|
# Show relevant options based on shader type
|
|
if props.shader_type == 'PRINCIPLED':
|
|
pass
|
|
col.prop(props, "shader_connection", text="Connect")
|
|
if props.shader_connection == 'EMISSION':
|
|
pass
|
|
col.prop(props, "emission_strength", text="Strength")
|
|
elif props.shader_type == 'EMISSION':
|
|
col.prop(props, "emission_strength", text="Strength")
|
|
|
|
layout.separator()
|
|
|
|
# Simple toggles
|
|
col = layout.column(align=True)
|
|
col.prop(props, "use_alpha", text="Use Alpha Channel")
|
|
col.prop(props, "auto_assign_material", text="Auto-Assign to Object")
|
|
|
|
# Clean status info
|
|
if props.auto_assign_material:
|
|
pass
|
|
layout.separator()
|
|
info_row = layout.row()
|
|
info_row.scale_y = 0.8
|
|
try:
|
|
pass
|
|
context = bpy.context
|
|
if context and context.active_object and context.active_object.type in ['MESH', 'CURVE']:
|
|
pass
|
|
info_row.label(text=f"→ {context.active_object.name}", icon='CHECKMARK')
|
|
else:
|
|
pass
|
|
info_row.label(text="Select mesh/curve object", icon='INFO')
|
|
except:
|
|
pass
|
|
info_row.label(text="Select mesh/curve object", icon='INFO')
|
|
|
|
def draw_shader_options_only(layout, props):
|
|
pass
|
|
"""Draw only shader options without the Create button - for collapsible section"""
|
|
# Check if shader generation is available
|
|
from ..utils.constants import has_shader_generation
|
|
if not has_shader_generation():
|
|
draw_feature_lock_indicator(layout, 'shader_generation')
|
|
return
|
|
|
|
# Clean grid layout for shader options
|
|
grid = layout.grid_flow(row_major=True, columns=2, align=True)
|
|
|
|
grid.prop(props, "shader_type", text="Type")
|
|
|
|
if props.shader_type == 'PRINCIPLED':
|
|
pass
|
|
grid.prop(props, "shader_connection", text="Connect")
|
|
if props.shader_connection == 'EMISSION':
|
|
pass
|
|
grid.prop(props, "emission_strength", text="Strength")
|
|
elif props.shader_type == 'EMISSION':
|
|
grid.prop(props, "emission_strength", text="Strength")
|
|
|
|
# Simple row for toggles
|
|
layout.separator()
|
|
row = layout.row(align=True)
|
|
row.prop(props, "use_alpha", toggle=True)
|
|
row.prop(props, "auto_assign_material", toggle=True)
|
|
|
|
# Shadow, reflection, and backfacing toggles
|
|
row = layout.row(align=True)
|
|
row.prop(props, "disable_shadows", toggle=True)
|
|
row.prop(props, "disable_reflections", toggle=True)
|
|
row.prop(props, "disable_backfacing", toggle=True)
|
|
|
|
def draw_normal_maps_section(layout, props):
|
|
pass
|
|
"""Draw normal map generation controls"""
|
|
# Main toggle for normal map generation
|
|
layout.prop(props, "generate_normal_map", text="Generate Normal Map", icon='TEXTURE')
|
|
|
|
# Show controls only if normal map generation is enabled
|
|
if props.generate_normal_map:
|
|
pass
|
|
layout.separator()
|
|
|
|
# Normal map settings in a clean layout
|
|
col = layout.column(align=True)
|
|
col.prop(props, "normal_map_strength", text="Strength", slider=True)
|
|
col.prop(props, "normal_map_blur_radius", text="Blur Radius", slider=True)
|
|
|
|
layout.separator()
|
|
layout.prop(props, "normal_map_invert", text="Invert (Emboss ↔ Deboss)")
|
|
|
|
# Info box with usage tips
|
|
layout.separator()
|
|
info_box = layout.box()
|
|
info_box.scale_y = 0.8
|
|
info_box.label(text="💡 Normal maps add depth and detail", icon='INFO')
|
|
info_box.label(text="• Higher strength = more pronounced effect")
|
|
info_box.label(text="• Blur smooths sharp edges")
|
|
info_box.label(text="• Invert changes raised/recessed effect")
|
|
|
|
def draw_text_fitting_section(layout, props):
|
|
pass
|
|
"""Draw text fitting controls"""
|
|
# Main toggle for text fitting
|
|
layout.prop(props, "enable_text_fitting", text="Enable Text Fitting", icon='FULLSCREEN_ENTER')
|
|
|
|
# Show controls only if text fitting is enabled
|
|
if props.enable_text_fitting:
|
|
pass
|
|
layout.separator()
|
|
|
|
# Fitting mode selection
|
|
layout.prop(props, "text_fitting_mode", text="Fitting Mode")
|
|
|
|
layout.separator()
|
|
|
|
# Margin setting
|
|
layout.prop(props, "text_fitting_margin", text="Margin", slider=True)
|
|
|
|
layout.separator()
|
|
|
|
# Font size limits
|
|
col = layout.column(align=True)
|
|
col.label(text="Font Size Limits:", icon='RESTRICT_SELECT_ON')
|
|
row = col.row(align=True)
|
|
row.prop(props, "min_font_size", text="Min")
|
|
row.prop(props, "max_font_size", text="Max")
|
|
|
|
# Info box with usage tips
|
|
layout.separator()
|
|
info_box = layout.box()
|
|
info_box.scale_y = 0.8
|
|
info_box.label(text="💡 Text fitting automatically sizes text", icon='INFO')
|
|
|
|
if props.text_fitting_mode == 'FIT_WIDTH':
|
|
pass
|
|
info_box.label(text="• Fits text width to canvas width")
|
|
elif props.text_fitting_mode == 'FIT_HEIGHT':
|
|
info_box.label(text="• Fits text height to canvas height")
|
|
elif props.text_fitting_mode == 'FIT_BOTH':
|
|
info_box.label(text="• Fits both width and height (may distort)")
|
|
elif props.text_fitting_mode == 'FIT_CONTAIN':
|
|
info_box.label(text="• Fits entirely within canvas (preserves ratio)")
|
|
|
|
info_box.label(text="• Respects min/max font size limits")
|
|
|
|
def draw_multiline_section(layout, props):
|
|
pass
|
|
"""Draw multiline text controls"""
|
|
# Main toggle for multiline text
|
|
layout.prop(props, "enable_multiline", text="Enable Multiline", icon='TEXT')
|
|
|
|
# Show controls only if multiline is enabled
|
|
if props.enable_multiline:
|
|
pass
|
|
layout.separator()
|
|
|
|
# Line height and alignment settings
|
|
col = layout.column(align=True)
|
|
col.prop(props, "line_height", text="Line Height", slider=True)
|
|
col.prop(props, "text_alignment", text="Alignment")
|
|
|
|
layout.separator()
|
|
|
|
# Auto wrap settings
|
|
layout.prop(props, "auto_wrap", text="Auto Wrap")
|
|
if props.auto_wrap:
|
|
pass
|
|
layout.prop(props, "wrap_width", text="Wrap Width (0 = texture width)")
|
|
|
|
layout.separator()
|
|
|
|
# Line limits and spacing
|
|
col = layout.column(align=True)
|
|
col.prop(props, "max_lines", text="Max Lines (0 = unlimited)")
|
|
col.prop(props, "line_spacing_pixels", text="Line Spacing (px)")
|
|
|
|
# Info box with usage tips
|
|
layout.separator()
|
|
info_box = layout.box()
|
|
info_box.scale_y = 0.8
|
|
info_box.label(text="💡 Multiline text supports:", icon='INFO')
|
|
info_box.label(text="• Use \\n for manual line breaks")
|
|
info_box.label(text="• Auto-wrap fits text to width")
|
|
info_box.label(text="• Alignment works per line")
|
|
info_box.label(text="• Line height controls spacing")
|
|
|
|
def draw_stroke_section(layout, props):
|
|
pass
|
|
"""Draw stroke and outline controls"""
|
|
# Check if stroke effects are available
|
|
from ..utils.constants import has_stroke_effects
|
|
if not has_stroke_effects():
|
|
draw_feature_lock_indicator(layout, 'stroke_effects')
|
|
return
|
|
|
|
# Main toggle for stroke
|
|
layout.prop(props, "enable_stroke", text="Enable Stroke", icon='MOD_OUTLINE')
|
|
|
|
# Show controls only if stroke is enabled
|
|
if props.enable_stroke:
|
|
pass
|
|
layout.separator()
|
|
|
|
# Stroke width and color
|
|
col = layout.column(align=True)
|
|
col.prop(props, "stroke_width", text="Width", slider=True)
|
|
col.prop(props, "stroke_color", text="Color")
|
|
|
|
layout.separator()
|
|
|
|
# Stroke position
|
|
layout.prop(props, "stroke_position", text="Position")
|
|
|
|
# Info box with usage tips
|
|
layout.separator()
|
|
info_box = layout.box()
|
|
info_box.scale_y = 0.8
|
|
info_box.label(text="💡 Stroke adds outline to text:", icon='INFO')
|
|
|
|
if props.stroke_position == 'OUTER':
|
|
pass
|
|
info_box.label(text="• Outer: Expands text bounds outward")
|
|
elif props.stroke_position == 'INNER':
|
|
info_box.label(text="• Inner: Shrinks text, stroke inside")
|
|
elif props.stroke_position == 'CENTER':
|
|
info_box.label(text="• Center: Stroke on text edge")
|
|
|
|
info_box.label(text="• Width 0 = no stroke")
|
|
info_box.label(text="• Works with multiline text")
|
|
|
|
def draw_shadow_glow_section(layout, props):
|
|
pass
|
|
"""Draw shadow and glow effects controls"""
|
|
# Check if shadow/glow effects are available
|
|
from ..utils.constants import has_shadow_glow_effects
|
|
if not has_shadow_glow_effects():
|
|
draw_feature_lock_indicator(layout, 'shadow_glow_effects')
|
|
return
|
|
|
|
# Drop shadow section
|
|
layout.prop(props, "enable_shadow", text="Enable Drop Shadow", icon='SHADERFX')
|
|
|
|
if props.enable_shadow:
|
|
pass
|
|
layout.separator()
|
|
|
|
# Shadow offset controls in a row
|
|
offset_row = layout.row(align=True)
|
|
offset_row.prop(props, "shadow_offset_x", text="X Offset")
|
|
offset_row.prop(props, "shadow_offset_y", text="Y Offset")
|
|
|
|
# Shadow properties
|
|
col = layout.column(align=True)
|
|
col.prop(props, "shadow_blur", text="Shadow Blur", slider=True)
|
|
col.prop(props, "shadow_spread", text="Shadow Spread", slider=True)
|
|
col.prop(props, "shadow_color", text="Shadow Color")
|
|
|
|
layout.separator()
|
|
|
|
# Glow section
|
|
layout.prop(props, "enable_glow", text="Enable Glow", icon='LIGHT_SUN')
|
|
|
|
if props.enable_glow:
|
|
pass
|
|
layout.separator()
|
|
|
|
# Glow properties
|
|
col = layout.column(align=True)
|
|
col.prop(props, "glow_blur", text="Glow Blur", slider=True)
|
|
col.prop(props, "glow_color", text="Glow Color")
|
|
|
|
# Info box with usage tips
|
|
if props.enable_shadow or props.enable_glow:
|
|
pass
|
|
layout.separator()
|
|
info_box = layout.box()
|
|
info_box.scale_y = 0.8
|
|
info_box.label(text="💡 Shadow & Glow effects:", icon='INFO')
|
|
|
|
if props.enable_shadow:
|
|
pass
|
|
info_box.label(text="• Drop shadow: offset + blur for depth")
|
|
if props.enable_glow:
|
|
pass
|
|
info_box.label(text="• Glow: blur without offset for aura effect")
|
|
|
|
info_box.label(text="• Higher blur = softer effect")
|
|
info_box.label(text="• Alpha controls effect intensity")
|
|
|
|
def draw_blur_section(layout, props):
|
|
pass
|
|
"""Draw blur effects controls"""
|
|
# Check if blur effects are available
|
|
from ..utils.constants import has_blur_effects
|
|
if not has_blur_effects():
|
|
draw_feature_lock_indicator(layout, 'blur_effects')
|
|
return
|
|
|
|
# Main toggle for blur
|
|
layout.prop(props, "enable_blur", text="Enable Blur", icon='MOD_SMOOTH')
|
|
|
|
# Show controls only if blur is enabled
|
|
if props.enable_blur:
|
|
pass
|
|
layout.separator()
|
|
|
|
# Blur radius setting
|
|
layout.prop(props, "blur_radius", text="Blur Radius", slider=True)
|
|
|
|
# Info box with usage tips
|
|
layout.separator()
|
|
info_box = layout.box()
|
|
info_box.scale_y = 0.8
|
|
info_box.label(text="💡 Blur effect softens text edges:", icon='INFO')
|
|
info_box.label(text="• Applied to main text content only")
|
|
info_box.label(text="• Higher radius = softer/more blurred")
|
|
info_box.label(text="• Works with all other effects")
|
|
info_box.label(text="• Radius 0 = no blur effect")
|
|
|
|
|
|
# ============================================================================
|
|
# UI PANELS
|
|
# ============================================================================
|
|
|
|
class TEXT_TEXTURE_PT_panel(Panel):
|
|
pass
|
|
"""Main Panel in Shader Editor"""
|
|
bl_label = "Text Texture Generator"
|
|
bl_idname = "TEXT_TEXTURE_PT_panel"
|
|
bl_space_type = 'NODE_EDITOR'
|
|
bl_region_type = 'UI'
|
|
bl_category = "Tool"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
pass
|
|
# DEBUG: Log poll method calls
|
|
if hasattr(context.space_data, 'tree_type'):
|
|
pass
|
|
else:
|
|
pass
|
|
|
|
# More permissive poll condition to ensure panel shows up
|
|
if context.space_data.type == 'NODE_EDITOR':
|
|
pass
|
|
if hasattr(context.space_data, 'tree_type'):
|
|
pass
|
|
result = context.space_data.tree_type == 'ShaderNodeTree'
|
|
return result
|
|
else:
|
|
pass
|
|
# Allow panel even without tree_type (sometimes happens)
|
|
return True
|
|
|
|
return False
|
|
|
|
def draw(self, context):
|
|
pass
|
|
layout = self.layout
|
|
props = context.scene.text_texture_props
|
|
|
|
# TOP LEVEL - Always Visible Controls
|
|
|
|
# Text Input
|
|
box = layout.box()
|
|
box.label(text="Text:", icon='TEXT')
|
|
draw_text_input_section(box, props)
|
|
|
|
|
|
# Generate Button
|
|
layout.separator(factor=0.5)
|
|
draw_generate_button(layout)
|
|
|
|
# CREATE SHADER BUTTON - WITH PROPER RESTRICTION
|
|
layout.separator(factor=0.5)
|
|
create_shader_row = layout.row()
|
|
create_shader_row.scale_y = 1.5
|
|
|
|
# Check if shader generation is available
|
|
from ..utils.constants import has_shader_generation
|
|
if has_shader_generation():
|
|
create_shader_row.operator("text_texture.generate_shader", text="Create Shader", icon='MATERIAL')
|
|
else:
|
|
# Show locked shader button for free version
|
|
lock_row = create_shader_row.row()
|
|
lock_row.enabled = False
|
|
lock_row.operator("text_texture.generate_shader", text="🔒 Create Shader (Pro Only)", icon='LOCKED')
|
|
|
|
# Preview Button - ALWAYS available (core functionality for all users)
|
|
layout.separator(factor=0.5)
|
|
preview_row = layout.row()
|
|
preview_row.scale_y = 1.5
|
|
preview_row.operator("text_texture.preview", text="Preview Texture", icon='HIDE_OFF')
|
|
|
|
layout.separator(factor=1.2)
|
|
|
|
# COLLAPSIBLE SECTIONS - FREE FEATURES FIRST
|
|
|
|
# === FREE FEATURES ===
|
|
|
|
# Multiline Section (collapsed by default)
|
|
multiline_section = draw_collapsible_header(layout, "expand_multiline", "Multiline Text", 'TEXT')
|
|
if multiline_section:
|
|
pass
|
|
draw_multiline_section(multiline_section, props)
|
|
|
|
# Font Settings Section (collapsed by default)
|
|
font_section = draw_collapsible_header(layout, "expand_font_settings", "Font Settings", 'FONT_DATA')
|
|
if font_section:
|
|
pass
|
|
draw_font_settings_section(font_section, props)
|
|
|
|
# Positioning & Layout Section (collapsed by default) - FREE FEATURE moved to free group
|
|
position_section = draw_collapsible_header(layout, "expand_positioning", "Positioning & Layout", 'ANCHOR_CENTER')
|
|
if position_section:
|
|
draw_positioning_section(position_section, props)
|
|
|
|
# Text Fitting Section (collapsed by default)
|
|
text_fitting_section = draw_collapsible_header(layout, "expand_text_fitting", "Text Fitting", 'FULLSCREEN_ENTER')
|
|
if text_fitting_section:
|
|
pass
|
|
draw_text_fitting_section(text_fitting_section, props)
|
|
|
|
# Colors Section (collapsed by default)
|
|
colors_section = draw_collapsible_header(layout, "expand_colors", "Colors", 'COLOR')
|
|
if colors_section:
|
|
pass
|
|
draw_colors_section(colors_section, props)
|
|
|
|
# === PREMIUM FEATURES ===
|
|
|
|
# Shader Section (collapsed by default) - ONLY OPTIONS, NOT THE BUTTON
|
|
shader_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_shader_generation
|
|
if not has_shader_generation():
|
|
shader_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
shader_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = shader_header_row.row()
|
|
lock_header.label(text="Shader Options", icon='SETTINGS')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
shader_expanded = draw_collapsible_header(shader_header_row, "expand_shader", "Shader Options", 'SETTINGS')
|
|
|
|
# Only show content if available and expanded
|
|
if shader_expanded:
|
|
draw_shader_options_only(shader_expanded, props)
|
|
|
|
# Image Overlays Section (collapsed by default) - Show with premium indicator
|
|
overlay_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_image_overlays
|
|
if not has_image_overlays():
|
|
overlay_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
overlay_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = overlay_header_row.row()
|
|
lock_header.label(text="Image Overlays", icon='IMAGE_DATA')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
overlay_expanded = draw_collapsible_header(overlay_header_row, "expand_image_overlays", "Image Overlays", 'IMAGE_DATA')
|
|
|
|
# Only show content if available and expanded
|
|
if overlay_expanded:
|
|
draw_image_overlays_section(overlay_expanded, props)
|
|
|
|
|
|
# Stroke Section (collapsed by default) - Show with premium indicator
|
|
stroke_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_stroke_effects
|
|
if not has_stroke_effects():
|
|
stroke_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
stroke_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = stroke_header_row.row()
|
|
lock_header.label(text="Stroke & Outlines", icon='MOD_OUTLINE')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
stroke_expanded = draw_collapsible_header(stroke_header_row, "expand_stroke", "Stroke & Outlines", 'MOD_OUTLINE')
|
|
|
|
# Only show content if available and expanded
|
|
if stroke_expanded:
|
|
draw_stroke_section(stroke_expanded, props)
|
|
|
|
# Shadow & Glow Section (collapsed by default) - Show with premium indicator
|
|
shadow_glow_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_shadow_glow_effects
|
|
if not has_shadow_glow_effects():
|
|
shadow_glow_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
shadow_glow_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = shadow_glow_header_row.row()
|
|
lock_header.label(text="Shadow & Glow Effects", icon='SHADERFX')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
shadow_glow_expanded = draw_collapsible_header(shadow_glow_header_row, "expand_shadow_glow", "Shadow & Glow Effects", 'SHADERFX')
|
|
|
|
# Only show content if available and expanded
|
|
if shadow_glow_expanded:
|
|
draw_shadow_glow_section(shadow_glow_expanded, props)
|
|
|
|
# Blur Section (collapsed by default) - Show with premium indicator
|
|
blur_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_blur_effects
|
|
if not has_blur_effects():
|
|
blur_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
blur_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = blur_header_row.row()
|
|
lock_header.label(text="Blur Effects", icon='MOD_SMOOTH')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
blur_expanded = draw_collapsible_header(blur_header_row, "expand_blur", "Blur Effects", 'MOD_SMOOTH')
|
|
|
|
# Only show content if available and expanded
|
|
if blur_expanded:
|
|
draw_blur_section(blur_expanded, props)
|
|
|
|
# Normal Maps Section (collapsed by default) - Show with premium indicator
|
|
normal_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
if not has_normal_maps():
|
|
normal_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
normal_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = normal_header_row.row()
|
|
lock_header.label(text="Normal Maps", icon='TEXTURE')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
normal_expanded = draw_collapsible_header(normal_header_row, "expand_normal_maps", "Normal Maps", 'TEXTURE')
|
|
|
|
# Only show content if available and expanded
|
|
if normal_expanded:
|
|
draw_normal_maps_section(normal_expanded, props)
|
|
|
|
# Presets Section (collapsed by default) - Show with premium indicator
|
|
presets_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
if not has_presets():
|
|
presets_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
presets_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = presets_header_row.row()
|
|
lock_header.label(text="Presets", icon='PRESET')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
presets_expanded = draw_collapsible_header(presets_header_row, "expand_presets", "Presets", 'PRESET')
|
|
|
|
# Only show content if available and expanded
|
|
if presets_expanded:
|
|
draw_presets_section(presets_expanded, props)
|
|
|
|
class TEXT_TEXTURE_PT_panel_3d(Panel):
|
|
pass
|
|
"""3D View Panel - Access from sidebar (N key)"""
|
|
bl_label = "Text Texture Generator"
|
|
bl_idname = "TEXT_TEXTURE_PT_panel_3d"
|
|
bl_space_type = 'VIEW_3D'
|
|
bl_region_type = 'UI'
|
|
bl_category = "Tool"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
def draw(self, context):
|
|
pass
|
|
layout = self.layout
|
|
props = context.scene.text_texture_props
|
|
|
|
# TOP LEVEL - Always Visible Controls
|
|
|
|
# Text Input
|
|
box = layout.box()
|
|
box.label(text="Text:", icon='TEXT')
|
|
draw_text_input_section(box, props)
|
|
|
|
|
|
# Generate Button
|
|
layout.separator(factor=0.5)
|
|
draw_generate_button(layout)
|
|
|
|
# CREATE SHADER BUTTON - WITH PROPER RESTRICTION
|
|
layout.separator(factor=0.5)
|
|
create_shader_row = layout.row()
|
|
create_shader_row.scale_y = 1.5
|
|
|
|
# Check if shader generation is available
|
|
from ..utils.constants import has_shader_generation
|
|
if has_shader_generation():
|
|
create_shader_row.operator("text_texture.generate_shader", text="Create Shader", icon='MATERIAL')
|
|
else:
|
|
# Show locked shader button for free version
|
|
lock_row = create_shader_row.row()
|
|
lock_row.enabled = False
|
|
lock_row.operator("text_texture.generate_shader", text="🔒 Create Shader (Pro Only)", icon='LOCKED')
|
|
|
|
# Preview Button - ALWAYS available (core functionality for all users)
|
|
layout.separator(factor=0.5)
|
|
preview_row = layout.row()
|
|
preview_row.scale_y = 1.5
|
|
preview_row.operator("text_texture.preview", text="Preview Texture", icon='HIDE_OFF')
|
|
|
|
layout.separator(factor=1.2)
|
|
|
|
# COLLAPSIBLE SECTIONS - FREE FEATURES FIRST
|
|
|
|
# === FREE FEATURES ===
|
|
|
|
# Multiline Section (collapsed by default)
|
|
multiline_section = draw_collapsible_header(layout, "expand_multiline", "Multiline Text", 'TEXT')
|
|
if multiline_section:
|
|
pass
|
|
draw_multiline_section(multiline_section, props)
|
|
|
|
# Font Settings Section (collapsed by default)
|
|
font_section = draw_collapsible_header(layout, "expand_font_settings", "Font Settings", 'FONT_DATA')
|
|
if font_section:
|
|
pass
|
|
draw_font_settings_section(font_section, props)
|
|
|
|
# Positioning & Layout Section (collapsed by default) - FREE FEATURE moved to free group
|
|
position_section = draw_collapsible_header(layout, "expand_positioning", "Positioning & Layout", 'ANCHOR_CENTER')
|
|
if position_section:
|
|
draw_positioning_section(position_section, props)
|
|
|
|
# Text Fitting Section (collapsed by default)
|
|
text_fitting_section = draw_collapsible_header(layout, "expand_text_fitting", "Text Fitting", 'FULLSCREEN_ENTER')
|
|
if text_fitting_section:
|
|
pass
|
|
draw_text_fitting_section(text_fitting_section, props)
|
|
|
|
# Colors Section (collapsed by default)
|
|
colors_section = draw_collapsible_header(layout, "expand_colors", "Colors", 'COLOR')
|
|
if colors_section:
|
|
pass
|
|
draw_colors_section(colors_section, props)
|
|
|
|
# === PREMIUM FEATURES ===
|
|
|
|
# Shader Section (collapsed by default) - ONLY OPTIONS, NOT THE BUTTON
|
|
shader_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_shader_generation
|
|
if not has_shader_generation():
|
|
shader_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
shader_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = shader_header_row.row()
|
|
lock_header.label(text="Shader Options", icon='SETTINGS')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
shader_expanded = draw_collapsible_header(shader_header_row, "expand_shader", "Shader Options", 'SETTINGS')
|
|
|
|
# Only show content if available and expanded
|
|
if shader_expanded:
|
|
draw_shader_options_only(shader_expanded, props)
|
|
|
|
# Image Overlays Section (collapsed by default) - Show with premium indicator
|
|
overlay_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_image_overlays
|
|
if not has_image_overlays():
|
|
overlay_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
overlay_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = overlay_header_row.row()
|
|
lock_header.label(text="Image Overlays", icon='IMAGE_DATA')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
overlay_expanded = draw_collapsible_header(overlay_header_row, "expand_image_overlays", "Image Overlays", 'IMAGE_DATA')
|
|
|
|
# Only show content if available and expanded
|
|
if overlay_expanded:
|
|
draw_image_overlays_section(overlay_expanded, props)
|
|
|
|
|
|
# Stroke Section (collapsed by default) - Show with premium indicator
|
|
stroke_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_stroke_effects
|
|
if not has_stroke_effects():
|
|
stroke_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
stroke_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = stroke_header_row.row()
|
|
lock_header.label(text="Stroke & Outlines", icon='MOD_OUTLINE')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
stroke_expanded = draw_collapsible_header(stroke_header_row, "expand_stroke", "Stroke & Outlines", 'MOD_OUTLINE')
|
|
|
|
# Only show content if available and expanded
|
|
if stroke_expanded:
|
|
draw_stroke_section(stroke_expanded, props)
|
|
|
|
# Shadow & Glow Section (collapsed by default) - Show with premium indicator
|
|
shadow_glow_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_shadow_glow_effects
|
|
if not has_shadow_glow_effects():
|
|
shadow_glow_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
shadow_glow_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = shadow_glow_header_row.row()
|
|
lock_header.label(text="Shadow & Glow Effects", icon='SHADERFX')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
shadow_glow_expanded = draw_collapsible_header(shadow_glow_header_row, "expand_shadow_glow", "Shadow & Glow Effects", 'SHADERFX')
|
|
|
|
# Only show content if available and expanded
|
|
if shadow_glow_expanded:
|
|
draw_shadow_glow_section(shadow_glow_expanded, props)
|
|
|
|
# Blur Section (collapsed by default) - Show with premium indicator
|
|
blur_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_blur_effects
|
|
if not has_blur_effects():
|
|
blur_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
blur_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = blur_header_row.row()
|
|
lock_header.label(text="Blur Effects", icon='MOD_SMOOTH')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
blur_expanded = draw_collapsible_header(blur_header_row, "expand_blur", "Blur Effects", 'MOD_SMOOTH')
|
|
|
|
# Only show content if available and expanded
|
|
if blur_expanded:
|
|
draw_blur_section(blur_expanded, props)
|
|
|
|
# Normal Maps Section (collapsed by default) - Show with premium indicator
|
|
normal_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
if not has_normal_maps():
|
|
normal_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
normal_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = normal_header_row.row()
|
|
lock_header.label(text="Normal Maps", icon='TEXTURE')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
normal_expanded = draw_collapsible_header(normal_header_row, "expand_normal_maps", "Normal Maps", 'TEXTURE')
|
|
|
|
# Only show content if available and expanded
|
|
if normal_expanded:
|
|
draw_normal_maps_section(normal_expanded, props)
|
|
|
|
# Presets Section (collapsed by default) - Show with premium indicator
|
|
presets_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
if not has_presets():
|
|
presets_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
presets_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = presets_header_row.row()
|
|
lock_header.label(text="Presets", icon='PRESET')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
presets_expanded = draw_collapsible_header(presets_header_row, "expand_presets", "Presets", 'PRESET')
|
|
|
|
# Only show content if available and expanded
|
|
if presets_expanded:
|
|
draw_presets_section(presets_expanded, props)
|
|
|
|
class TEXT_TEXTURE_PT_panel_properties(Panel):
|
|
pass
|
|
"""Properties Panel"""
|
|
bl_label = "Text Texture Generator"
|
|
bl_idname = "TEXT_TEXTURE_PT_panel_properties"
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "material"
|
|
bl_category = "Tool"
|
|
|
|
def draw(self, context):
|
|
pass
|
|
layout = self.layout
|
|
props = context.scene.text_texture_props
|
|
|
|
# TOP LEVEL - Always Visible Controls
|
|
|
|
# Text Input
|
|
box = layout.box()
|
|
box.label(text="Text:", icon='TEXT')
|
|
draw_text_input_section(box, props)
|
|
|
|
|
|
# Generate Button
|
|
layout.separator(factor=0.5)
|
|
draw_generate_button(layout)
|
|
|
|
# CREATE SHADER BUTTON - WITH PROPER RESTRICTION
|
|
layout.separator(factor=0.5)
|
|
create_shader_row = layout.row()
|
|
create_shader_row.scale_y = 1.5
|
|
|
|
# Check if shader generation is available
|
|
from ..utils.constants import has_shader_generation
|
|
if has_shader_generation():
|
|
create_shader_row.operator("text_texture.generate_shader", text="Create Shader", icon='MATERIAL')
|
|
else:
|
|
# Show locked shader button for free version
|
|
lock_row = create_shader_row.row()
|
|
lock_row.enabled = False
|
|
lock_row.operator("text_texture.generate_shader", text="🔒 Create Shader (Pro Only)", icon='LOCKED')
|
|
|
|
# Preview Button - ALWAYS available (core functionality for all users)
|
|
layout.separator(factor=0.5)
|
|
preview_row = layout.row()
|
|
preview_row.scale_y = 1.5
|
|
preview_row.operator("text_texture.preview", text="Preview Texture", icon='HIDE_OFF')
|
|
|
|
layout.separator(factor=1.2)
|
|
|
|
# COLLAPSIBLE SECTIONS - FREE FEATURES FIRST
|
|
|
|
# === FREE FEATURES ===
|
|
|
|
# Multiline Section (collapsed by default)
|
|
multiline_section = draw_collapsible_header(layout, "expand_multiline", "Multiline Text", 'TEXT')
|
|
if multiline_section:
|
|
pass
|
|
draw_multiline_section(multiline_section, props)
|
|
|
|
# Font Settings Section (collapsed by default)
|
|
font_section = draw_collapsible_header(layout, "expand_font_settings", "Font Settings", 'FONT_DATA')
|
|
if font_section:
|
|
pass
|
|
draw_font_settings_section(font_section, props)
|
|
|
|
# Positioning & Layout Section (collapsed by default) - FREE FEATURE moved to free group
|
|
position_section = draw_collapsible_header(layout, "expand_positioning", "Positioning & Layout", 'ANCHOR_CENTER')
|
|
if position_section:
|
|
draw_positioning_section(position_section, props)
|
|
|
|
# Text Fitting Section (collapsed by default)
|
|
text_fitting_section = draw_collapsible_header(layout, "expand_text_fitting", "Text Fitting", 'FULLSCREEN_ENTER')
|
|
if text_fitting_section:
|
|
pass
|
|
draw_text_fitting_section(text_fitting_section, props)
|
|
|
|
# Colors Section (collapsed by default)
|
|
colors_section = draw_collapsible_header(layout, "expand_colors", "Colors", 'COLOR')
|
|
if colors_section:
|
|
pass
|
|
draw_colors_section(colors_section, props)
|
|
|
|
# === PREMIUM FEATURES ===
|
|
|
|
# Image Overlays Section (collapsed by default) - Show with premium indicator
|
|
overlay_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_image_overlays
|
|
if not has_image_overlays():
|
|
overlay_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
overlay_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = overlay_header_row.row()
|
|
lock_header.label(text="Image Overlays", icon='IMAGE_DATA')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
overlay_expanded = draw_collapsible_header(overlay_header_row, "expand_image_overlays", "Image Overlays", 'IMAGE_DATA')
|
|
|
|
# Only show content if available and expanded
|
|
if overlay_expanded:
|
|
draw_image_overlays_section(overlay_expanded, props)
|
|
|
|
|
|
# Stroke Section (collapsed by default) - Show with premium indicator
|
|
stroke_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_stroke_effects
|
|
if not has_stroke_effects():
|
|
stroke_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
stroke_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = stroke_header_row.row()
|
|
lock_header.label(text="Stroke & Outlines", icon='MOD_OUTLINE')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
stroke_expanded = draw_collapsible_header(stroke_header_row, "expand_stroke", "Stroke & Outlines", 'MOD_OUTLINE')
|
|
|
|
# Only show content if available and expanded
|
|
if stroke_expanded:
|
|
draw_stroke_section(stroke_expanded, props)
|
|
|
|
# Shadow & Glow Section (collapsed by default) - Show with premium indicator
|
|
shadow_glow_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_shadow_glow_effects
|
|
if not has_shadow_glow_effects():
|
|
shadow_glow_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
shadow_glow_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = shadow_glow_header_row.row()
|
|
lock_header.label(text="Shadow & Glow Effects", icon='SHADERFX')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
shadow_glow_expanded = draw_collapsible_header(shadow_glow_header_row, "expand_shadow_glow", "Shadow & Glow Effects", 'SHADERFX')
|
|
|
|
# Only show content if available and expanded
|
|
if shadow_glow_expanded:
|
|
draw_shadow_glow_section(shadow_glow_expanded, props)
|
|
|
|
# Blur Section (collapsed by default) - Show with premium indicator
|
|
blur_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
from ..utils.constants import has_blur_effects
|
|
if not has_blur_effects():
|
|
blur_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
blur_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = blur_header_row.row()
|
|
lock_header.label(text="Blur Effects", icon='MOD_SMOOTH')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
blur_expanded = draw_collapsible_header(blur_header_row, "expand_blur", "Blur Effects", 'MOD_SMOOTH')
|
|
|
|
# Only show content if available and expanded
|
|
if blur_expanded:
|
|
draw_blur_section(blur_expanded, props)
|
|
|
|
# Normal Maps Section (collapsed by default) - Show with premium indicator
|
|
normal_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
if not has_normal_maps():
|
|
normal_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
normal_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = normal_header_row.row()
|
|
lock_header.label(text="Normal Maps", icon='TEXTURE')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
normal_expanded = draw_collapsible_header(normal_header_row, "expand_normal_maps", "Normal Maps", 'TEXTURE')
|
|
|
|
# Only show content if available and expanded
|
|
if normal_expanded:
|
|
draw_normal_maps_section(normal_expanded, props)
|
|
|
|
# Presets Section (collapsed by default) - Show with premium indicator
|
|
presets_header_row = layout.row(align=True)
|
|
|
|
# Gray out header if locked and prevent expansion
|
|
if not has_presets():
|
|
presets_header_row.enabled = False
|
|
# Don't allow expansion for locked features
|
|
presets_expanded = None
|
|
# Show lock indicator instead of expandable header
|
|
lock_header = presets_header_row.row()
|
|
lock_header.label(text="Presets", icon='PRESET')
|
|
lock_header.label(text="🔒 PRO", icon='LOCKED')
|
|
else:
|
|
presets_expanded = draw_collapsible_header(presets_header_row, "expand_presets", "Presets", 'PRESET')
|
|
|
|
# Only show content if available and expanded
|
|
if presets_expanded:
|
|
draw_presets_section(presets_expanded, props) |