Files
Text-Texture-Generator-for-…/ui/panels.py
2025-09-11 12:12:58 +03:00

940 lines
39 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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
# ============================================================================
# SHARED UI COMPONENTS
# ============================================================================
def draw_collapsible_header(layout, prop_name, text, icon='TRIA_DOWN'):
"""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_text_input_section(layout, props):
"""Draw the main text input field"""
layout.prop(props, "text", text="")
def draw_font_dropdown_section(layout, props):
"""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):
"""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):
"""Draw image overlays controls with collapsible individual overlays"""
# Add overlay button
row = layout.row()
row.operator("text_texture.add_overlay", text="Add Image Overlay", icon='ADD')
# Individual overlay controls
if props.image_overlays:
layout.separator()
for i, overlay in enumerate(props.image_overlays):
# 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
dup_op = header_row.operator("text_texture.duplicate_overlay", text="", icon='DUPLICATE')
dup_op.overlay_index = i
# Delete button
del_op = header_row.operator("text_texture.delete_overlay", text="", icon='TRASH')
del_op.overlay_index = i
# Move buttons for reordering
if i > 0:
move_up_op = header_row.operator("text_texture.move_overlay", text="", icon='TRIA_UP')
move_up_op.overlay_index = i
move_up_op.direction = 'UP'
if i < len(props.image_overlays) - 1:
move_down_op = header_row.operator("text_texture.move_overlay", text="", icon='TRIA_DOWN')
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:
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':
# 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):
"""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():
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():
row.prop(props, "append_margin", text="", slider=True)
layout.separator()
layout.prop(props, "position_mode", text="Mode")
if props.position_mode == 'PRESET':
layout.separator()
layout.label(text="Anchor Point:", icon='ANCHOR_TOP')
anchor_matrix = get_anchor_point_matrix()
for row_idx, row in enumerate(anchor_matrix):
grid_row = layout.row(align=True)
grid_row.scale_y = 1.2
for col_idx, anchor in enumerate(row):
if anchor == props.anchor_point:
op = grid_row.operator("text_texture.set_anchor_point", text="", depress=True)
else:
op = grid_row.operator("text_texture.set_anchor_point", text="")
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:
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):
"""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:
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):
"""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:
layout.prop(props, "background_color", text="")
def draw_preview_options_section(layout, props):
"""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':
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):
"""Draw presets save/load controls"""
# Save preset section
save_row = layout.row(align=True)
save_row.prop(props, "new_preset_name", text="", placeholder="Preset Name")
save_row.operator("text_texture.save_preset", text="Save", icon='FILE_NEW')
layout.separator()
# Import/Export and refresh buttons
backup_row = layout.row(align=True)
backup_row.operator("text_texture.export_presets", text="Export Backup", icon='EXPORT')
backup_row.operator("text_texture.import_presets", text="Import Backup", icon='IMPORT')
utility_row = layout.row(align=True)
utility_row.operator("text_texture.refresh_presets", text="Refresh", icon='FILE_REFRESH')
utility_row.operator("text_texture.show_migration_report", text="Migration Report", icon='INFO')
layout.separator()
# Load presets section
if props.presets:
layout.label(text=f"📁 Saved Presets ({len(props.presets)}):", icon='PRESET')
# Separate presets by source
blend_presets = [p for p in props.presets if p.source == 'BLEND_FILE']
persistent_presets = [p for p in props.presets if p.source == 'PERSISTENT_FILE']
local_presets = [p for p in props.presets if p.source == 'LOCAL_FILE']
# Show blend file presets first
if blend_presets:
layout.label(text="🎯 Blend File Presets (travel with file):", icon='FILE_BLEND')
for preset in blend_presets:
row = layout.row()
load_btn = row.operator("text_texture.load_preset", text=f"📋 {preset.name}")
load_btn.preset_name = preset.name
# Quick overwrite button
overwrite_btn = row.operator("text_texture.save_preset", text="", icon='FILE_REFRESH')
overwrite_btn.overwrite = True
# Set the preset name so it can be overwritten
overwrite_btn.preset_name = preset.name
delete_btn = row.operator("text_texture.delete_preset", text="", icon='TRASH')
delete_btn.preset_name = preset.name
# Show persistent presets second
if persistent_presets:
if blend_presets:
layout.separator()
layout.label(text="🛡️ Persistent Presets (survive addon updates):", icon='PREFERENCES')
for preset in persistent_presets:
row = layout.row()
load_btn = row.operator("text_texture.load_preset", text=f"📋 {preset.name}")
load_btn.preset_name = preset.name
# Quick overwrite button
overwrite_btn = row.operator("text_texture.save_preset", text="", icon='FILE_REFRESH')
overwrite_btn.overwrite = True
# Set the preset name so it can be overwritten
overwrite_btn.preset_name = preset.name
delete_btn = row.operator("text_texture.delete_preset", text="", icon='TRASH')
delete_btn.preset_name = preset.name
# Show local presets third
if local_presets:
if blend_presets or persistent_presets:
layout.separator()
layout.label(text="💻 Local Presets (this machine only):", icon='DISK_DRIVE')
for preset in local_presets:
row = layout.row()
load_btn = row.operator("text_texture.load_preset", text=f"📋 {preset.name}")
load_btn.preset_name = preset.name
# Quick overwrite button
overwrite_btn = row.operator("text_texture.save_preset", text="", icon='FILE_REFRESH')
overwrite_btn.overwrite = True
# Set the preset name so it can be overwritten
overwrite_btn.preset_name = preset.name
delete_btn = row.operator("text_texture.delete_preset", text="", icon='TRASH')
delete_btn.preset_name = preset.name
else:
layout.label(text="📂 No presets saved yet", icon='INFO')
layout.label(text="💡 Save your first preset above!", icon='LIGHT_DATA')
layout.separator()
info_box = layout.box()
info_box.label(text=" Presets are now saved in the blend file", icon='INFO')
info_box.label(text="They will travel with your .blend file!")
def draw_shader_section(layout, props):
"""Draw shader generation controls"""
# 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':
col.prop(props, "shader_connection", text="Connect")
if props.shader_connection == 'EMISSION':
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:
layout.separator()
info_row = layout.row()
info_row.scale_y = 0.8
try:
context = bpy.context
if context and context.active_object and context.active_object.type in ['MESH', 'CURVE']:
info_row.label(text=f"{context.active_object.name}", icon='CHECKMARK')
else:
info_row.label(text="Select mesh/curve object", icon='INFO')
except:
info_row.label(text="Select mesh/curve object", icon='INFO')
def draw_shader_options_only(layout, props):
"""Draw only shader options without the Create button - for collapsible section"""
# 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':
grid.prop(props, "shader_connection", text="Connect")
if props.shader_connection == 'EMISSION':
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):
"""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:
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):
"""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:
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':
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):
"""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:
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:
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):
"""Draw stroke and outline controls"""
# 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:
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':
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):
"""Draw shadow and glow effects controls"""
# Drop shadow section
layout.prop(props, "enable_shadow", text="Enable Drop Shadow", icon='SHADERFX')
if props.enable_shadow:
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:
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:
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:
info_box.label(text="• Drop shadow: offset + blur for depth")
if props.enable_glow:
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):
"""Draw blur effects controls"""
# 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:
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):
"""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):
# DEBUG: Log poll method calls
print(f"[TTG DEBUG] Panel poll called - space_data: {context.space_data}")
print(f"[TTG DEBUG] space_data.type: {context.space_data.type if context.space_data else 'None'}")
if hasattr(context.space_data, 'tree_type'):
print(f"[TTG DEBUG] tree_type: {context.space_data.tree_type}")
else:
print("[TTG DEBUG] No tree_type attribute")
# More permissive poll condition to ensure panel shows up
if context.space_data.type == 'NODE_EDITOR':
if hasattr(context.space_data, 'tree_type'):
result = context.space_data.tree_type == 'ShaderNodeTree'
print(f"[TTG DEBUG] Poll result: {result}")
return result
else:
# Allow panel even without tree_type (sometimes happens)
print("[TTG DEBUG] Poll result: True (no tree_type but NODE_EDITOR)")
return True
print("[TTG DEBUG] Poll result: False (not NODE_EDITOR)")
return False
def draw(self, context):
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 AT TOP LEVEL - AS REQUESTED
layout.separator(factor=0.5)
create_shader_row = layout.row()
create_shader_row.scale_y = 1.5
create_shader_row.operator("text_texture.generate_shader", text="Create Shader", icon='MATERIAL')
# Preview Button at top level
layout.separator(factor=0.5)
preview_row = layout.row()
preview_row.scale_y = 1.5
preview_row.operator("text_texture.view_preview_zoom", text="Open Preview Window", icon='WINDOW')
layout.separator(factor=1.2)
# COLLAPSIBLE SECTIONS
# Multiline Section (collapsed by default)
multiline_section = draw_collapsible_header(layout, "expand_multiline", "Multiline Text", 'TEXT')
if multiline_section:
draw_multiline_section(multiline_section, props)
# Shader Section (collapsed by default) - ONLY OPTIONS, NOT THE BUTTON
shader_section = draw_collapsible_header(layout, "expand_shader", "Shader Options", 'SETTINGS')
if shader_section:
draw_shader_options_only(shader_section, props)
# Image Overlays Section (collapsed by default)
overlay_section = draw_collapsible_header(layout, "expand_image_overlays", "Image Overlays", 'IMAGE_DATA')
if overlay_section:
draw_image_overlays_section(overlay_section, props)
# Positioning & Layout Section (collapsed by default)
position_section = draw_collapsible_header(layout, "expand_positioning", "Positioning & Layout", 'ANCHOR_CENTER')
if position_section:
draw_positioning_section(position_section, props)
# Font Settings Section (collapsed by default)
font_section = draw_collapsible_header(layout, "expand_font_settings", "Font Settings", 'FONT_DATA')
if font_section:
draw_font_settings_section(font_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:
draw_text_fitting_section(text_fitting_section, props)
# Stroke Section (collapsed by default)
stroke_section = draw_collapsible_header(layout, "expand_stroke", "Stroke & Outlines", 'MOD_OUTLINE')
if stroke_section:
draw_stroke_section(stroke_section, props)
# Shadow & Glow Section (collapsed by default)
shadow_glow_section = draw_collapsible_header(layout, "expand_shadow_glow", "Shadow & Glow Effects", 'SHADERFX')
if shadow_glow_section:
draw_shadow_glow_section(shadow_glow_section, props)
# Blur Section (collapsed by default)
blur_section = draw_collapsible_header(layout, "expand_blur", "Blur Effects", 'MOD_SMOOTH')
if blur_section:
draw_blur_section(blur_section, props)
# Normal Maps Section (collapsed by default)
normal_maps_section = draw_collapsible_header(layout, "expand_normal_maps", "Normal Maps", 'TEXTURE')
if normal_maps_section:
draw_normal_maps_section(normal_maps_section, props)
# Colors Section (collapsed by default)
colors_section = draw_collapsible_header(layout, "expand_colors", "Colors", 'COLOR')
if colors_section:
draw_colors_section(colors_section, props)
# Presets Section (collapsed by default)
presets_section = draw_collapsible_header(layout, "expand_presets", "Presets", 'PRESET')
if presets_section:
draw_presets_section(presets_section, props)
class TEXT_TEXTURE_PT_panel_3d(Panel):
"""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):
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 AT TOP LEVEL - AS REQUESTED
layout.separator(factor=0.5)
create_shader_row = layout.row()
create_shader_row.scale_y = 1.5
create_shader_row.operator("text_texture.generate_shader", text="Create Shader", icon='MATERIAL')
# Preview Button at top level
layout.separator(factor=0.5)
preview_row = layout.row()
preview_row.scale_y = 1.5
preview_row.operator("text_texture.view_preview_zoom", text="Open Preview Window", icon='WINDOW')
layout.separator(factor=1.2)
# COLLAPSIBLE SECTIONS
# Multiline Section (collapsed by default)
multiline_section = draw_collapsible_header(layout, "expand_multiline", "Multiline Text", 'TEXT')
if multiline_section:
draw_multiline_section(multiline_section, props)
# Shader Section (collapsed by default) - ONLY OPTIONS, NOT THE BUTTON
shader_section = draw_collapsible_header(layout, "expand_shader", "Shader Options", 'SETTINGS')
if shader_section:
draw_shader_options_only(shader_section, props)
# Image Overlays Section (collapsed by default)
overlay_section = draw_collapsible_header(layout, "expand_image_overlays", "Image Overlays", 'IMAGE_DATA')
if overlay_section:
draw_image_overlays_section(overlay_section, props)
# Positioning & Layout Section (collapsed by default)
position_section = draw_collapsible_header(layout, "expand_positioning", "Positioning & Layout", 'ANCHOR_CENTER')
if position_section:
draw_positioning_section(position_section, props)
# Font Settings Section (collapsed by default)
font_section = draw_collapsible_header(layout, "expand_font_settings", "Font Settings", 'FONT_DATA')
if font_section:
draw_font_settings_section(font_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:
draw_text_fitting_section(text_fitting_section, props)
# Stroke Section (collapsed by default)
stroke_section = draw_collapsible_header(layout, "expand_stroke", "Stroke & Outlines", 'MOD_OUTLINE')
if stroke_section:
draw_stroke_section(stroke_section, props)
# Shadow & Glow Section (collapsed by default)
shadow_glow_section = draw_collapsible_header(layout, "expand_shadow_glow", "Shadow & Glow Effects", 'SHADERFX')
if shadow_glow_section:
draw_shadow_glow_section(shadow_glow_section, props)
# Tiling Section removed - feature was removed from addon
# Blur Section (collapsed by default)
blur_section = draw_collapsible_header(layout, "expand_blur", "Blur Effects", 'MOD_SMOOTH')
if blur_section:
draw_blur_section(blur_section, props)
# Batch Processing Section removed - feature was removed from addon
# Normal Maps Section (collapsed by default)
normal_maps_section = draw_collapsible_header(layout, "expand_normal_maps", "Normal Maps", 'TEXTURE')
if normal_maps_section:
draw_normal_maps_section(normal_maps_section, props)
# Colors Section (collapsed by default)
colors_section = draw_collapsible_header(layout, "expand_colors", "Colors", 'COLOR')
if colors_section:
draw_colors_section(colors_section, props)
# Presets Section (collapsed by default)
presets_section = draw_collapsible_header(layout, "expand_presets", "Presets", 'PRESET')
if presets_section:
draw_presets_section(presets_section, props)
class TEXT_TEXTURE_PT_panel_properties(Panel):
"""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):
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)
# Preview Button at top level
layout.separator(factor=0.5)
preview_row = layout.row()
preview_row.scale_y = 1.5
preview_row.operator("text_texture.view_preview_zoom", text="Open Preview Window", icon='WINDOW')
layout.separator(factor=1.2)
# COLLAPSIBLE SECTIONS
# Multiline Section (collapsed by default)
multiline_section = draw_collapsible_header(layout, "expand_multiline", "Multiline Text", 'TEXT')
if multiline_section:
draw_multiline_section(multiline_section, props)
# Image Overlays Section (collapsed by default)
overlay_section = draw_collapsible_header(layout, "expand_image_overlays", "Image Overlays", 'IMAGE_DATA')
if overlay_section:
draw_image_overlays_section(overlay_section, props)
# Positioning & Layout Section (collapsed by default)
position_section = draw_collapsible_header(layout, "expand_positioning", "Positioning & Layout", 'ANCHOR_CENTER')
if position_section:
draw_positioning_section(position_section, props)
# Font Settings Section (collapsed by default)
font_section = draw_collapsible_header(layout, "expand_font_settings", "Font Settings", 'FONT_DATA')
if font_section:
draw_font_settings_section(font_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:
draw_text_fitting_section(text_fitting_section, props)
# Stroke Section (collapsed by default)
stroke_section = draw_collapsible_header(layout, "expand_stroke", "Stroke & Outlines", 'MOD_OUTLINE')
if stroke_section:
draw_stroke_section(stroke_section, props)
# Shadow & Glow Section (collapsed by default)
shadow_glow_section = draw_collapsible_header(layout, "expand_shadow_glow", "Shadow & Glow Effects", 'SHADERFX')
if shadow_glow_section:
draw_shadow_glow_section(shadow_glow_section, props)
# Tiling Section removed - feature was removed from addon
# Blur Section (collapsed by default)
blur_section = draw_collapsible_header(layout, "expand_blur", "Blur Effects", 'MOD_SMOOTH')
if blur_section:
draw_blur_section(blur_section, props)
# Batch Processing Section removed - feature was removed from addon
# Normal Maps Section (collapsed by default)
normal_maps_section = draw_collapsible_header(layout, "expand_normal_maps", "Normal Maps", 'TEXTURE')
if normal_maps_section:
draw_normal_maps_section(normal_maps_section, props)
# Colors Section (collapsed by default)
colors_section = draw_collapsible_header(layout, "expand_colors", "Colors", 'COLOR')
if colors_section:
draw_colors_section(colors_section, props)
# Presets Section (collapsed by default)
presets_section = draw_collapsible_header(layout, "expand_presets", "Presets", 'PRESET')
if presets_section:
draw_presets_section(presets_section, props)