remove multiline
This commit is contained in:
@@ -139,7 +139,7 @@ else:
|
|||||||
# Conditionally import text editor operators (PRO feature)
|
# Conditionally import text editor operators (PRO feature)
|
||||||
try:
|
try:
|
||||||
from .operators import (
|
from .operators import (
|
||||||
TEXT_TEXTURE_OT_edit_multiline_text,
|
TEXT_TEXTURE_OT_edit_text,
|
||||||
)
|
)
|
||||||
TEXT_EDITOR_OPERATORS_AVAILABLE = True
|
TEXT_EDITOR_OPERATORS_AVAILABLE = True
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
@@ -148,7 +148,7 @@ except ImportError as e:
|
|||||||
print(f"WARNING: text_editor_ops import failed: {e}", file=sys.stderr)
|
print(f"WARNING: text_editor_ops import failed: {e}", file=sys.stderr)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
# Free version - use mock operator
|
# Free version - use mock operator
|
||||||
TEXT_TEXTURE_OT_edit_multiline_text = MockOperator
|
TEXT_TEXTURE_OT_edit_text = MockOperator
|
||||||
TEXT_EDITOR_OPERATORS_AVAILABLE = False
|
TEXT_EDITOR_OPERATORS_AVAILABLE = False
|
||||||
|
|
||||||
# Import UI panels and functions
|
# Import UI panels and functions
|
||||||
@@ -182,7 +182,7 @@ classes = (
|
|||||||
TEXT_TEXTURE_OT_duplicate_overlay,
|
TEXT_TEXTURE_OT_duplicate_overlay,
|
||||||
TEXT_TEXTURE_OT_delete_overlay,
|
TEXT_TEXTURE_OT_delete_overlay,
|
||||||
TEXT_TEXTURE_OT_move_overlay,
|
TEXT_TEXTURE_OT_move_overlay,
|
||||||
TEXT_TEXTURE_OT_edit_multiline_text,
|
TEXT_TEXTURE_OT_edit_text,
|
||||||
TEXT_TEXTURE_OT_open_panel,
|
TEXT_TEXTURE_OT_open_panel,
|
||||||
TEXT_TEXTURE_OT_save_preset,
|
TEXT_TEXTURE_OT_save_preset,
|
||||||
TEXT_TEXTURE_OT_save_preset_enter,
|
TEXT_TEXTURE_OT_save_preset_enter,
|
||||||
|
|||||||
Binary file not shown.
@@ -46,10 +46,10 @@ try:
|
|||||||
pass
|
pass
|
||||||
from .text_editor_ops import *
|
from .text_editor_ops import *
|
||||||
from .text_editor_ops import (
|
from .text_editor_ops import (
|
||||||
TEXT_TEXTURE_OT_edit_multiline_text,
|
TEXT_TEXTURE_OT_edit_text,
|
||||||
)
|
)
|
||||||
TEXT_EDITOR_OPERATORS = [
|
TEXT_EDITOR_OPERATORS = [
|
||||||
'TEXT_TEXTURE_OT_edit_multiline_text',
|
'TEXT_TEXTURE_OT_edit_text',
|
||||||
]
|
]
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
Binary file not shown.
@@ -1,19 +1,19 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from bpy.types import Operator
|
from bpy.types import Operator
|
||||||
from bpy.props import StringProperty
|
from bpy.props import StringProperty
|
||||||
from ..utils.constants import has_multiline_text
|
from ..utils.constants import has_text_editor
|
||||||
|
|
||||||
|
|
||||||
class TEXT_TEXTURE_OT_edit_multiline_text(Operator):
|
class TEXT_TEXTURE_OT_edit_text(Operator):
|
||||||
"""Open a text editor for single-line input"""
|
"""Open a text editor for text input"""
|
||||||
bl_idname = "text_texture.edit_multiline_text"
|
bl_idname = "text_texture.edit_text"
|
||||||
bl_label = "Edit Text"
|
bl_label = "Edit Text"
|
||||||
bl_description = "Open a text editor for single-line input"
|
bl_description = "Open a text editor for text input"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
temp_text: StringProperty(
|
temp_text: StringProperty(
|
||||||
name="Text",
|
name="Text",
|
||||||
description="Single line text only - multiline not supported",
|
description="Text input",
|
||||||
default="",
|
default="",
|
||||||
maxlen=4096
|
maxlen=4096
|
||||||
)
|
)
|
||||||
@@ -21,7 +21,7 @@ class TEXT_TEXTURE_OT_edit_multiline_text(Operator):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
"""Only available in PRO version"""
|
"""Only available in PRO version"""
|
||||||
return has_multiline_text()
|
return has_text_editor()
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
"""Copy current text to temp storage and open dialog"""
|
"""Copy current text to temp storage and open dialog"""
|
||||||
@@ -30,36 +30,24 @@ class TEXT_TEXTURE_OT_edit_multiline_text(Operator):
|
|||||||
return context.window_manager.invoke_props_dialog(self, width=700)
|
return context.window_manager.invoke_props_dialog(self, width=700)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
"""Draw the dialog UI with single-line input"""
|
"""Draw the dialog UI"""
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
|
||||||
max_chars = 4096
|
max_chars = 4096
|
||||||
current_chars = len(self.temp_text)
|
current_chars = len(self.temp_text)
|
||||||
|
|
||||||
# Main column
|
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
|
|
||||||
col.label(text="Edit Text (Single Line Only):", icon='EDITMODE_HLT')
|
col.label(text="Edit Text:", icon='EDITMODE_HLT')
|
||||||
|
|
||||||
# Make the text input larger
|
|
||||||
text_row = col.row()
|
text_row = col.row()
|
||||||
text_row.scale_y = 2.0
|
text_row.scale_y = 2.0
|
||||||
text_row.prop(self, "temp_text", text="")
|
text_row.prop(self, "temp_text", text="")
|
||||||
|
|
||||||
# Warning box
|
|
||||||
col.separator()
|
|
||||||
info_box = col.box()
|
|
||||||
info_box.scale_y = 0.8
|
|
||||||
info_box.label(text="⚠️ Single Line Only:", icon='ERROR')
|
|
||||||
row = info_box.row()
|
|
||||||
row.label(text="• Multiline text is not supported")
|
|
||||||
|
|
||||||
# Statistics
|
|
||||||
col.separator()
|
col.separator()
|
||||||
stats_row = col.row()
|
stats_row = col.row()
|
||||||
stats_row.label(text=f"📝 Characters: {current_chars}/{max_chars}", icon='FONT_DATA')
|
stats_row.label(text=f"📝 Characters: {current_chars}/{max_chars}", icon='FONT_DATA')
|
||||||
|
|
||||||
# Warnings
|
|
||||||
if current_chars > max_chars:
|
if current_chars > max_chars:
|
||||||
col.separator()
|
col.separator()
|
||||||
warning_box = col.box()
|
warning_box = col.box()
|
||||||
@@ -67,25 +55,17 @@ class TEXT_TEXTURE_OT_edit_multiline_text(Operator):
|
|||||||
warning_box.label(text=f"⚠ Exceeds character limit! Will be trimmed to {max_chars} chars", icon='ERROR')
|
warning_box.label(text=f"⚠ Exceeds character limit! Will be trimmed to {max_chars} chars", icon='ERROR')
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
"""Save temp text back to main property - reject if contains newlines"""
|
"""Save temp text back to main property"""
|
||||||
props = context.scene.text_texture_props
|
props = context.scene.text_texture_props
|
||||||
|
|
||||||
# Check for newlines and reject
|
|
||||||
if '\n' in self.temp_text:
|
|
||||||
self.report({'ERROR'}, "Multiline text not supported. Please use single line only.")
|
|
||||||
return {'CANCELLED'}
|
|
||||||
|
|
||||||
max_chars = 4096
|
max_chars = 4096
|
||||||
|
|
||||||
# Enforce character limit
|
|
||||||
text = self.temp_text[:max_chars]
|
text = self.temp_text[:max_chars]
|
||||||
|
|
||||||
props.text = text
|
props.text = text
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
classes = (TEXT_TEXTURE_OT_edit_multiline_text,)
|
classes = (TEXT_TEXTURE_OT_edit_text,)
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
|
|||||||
Binary file not shown.
@@ -12,7 +12,7 @@ from ..utils.constants import (
|
|||||||
get_feature_availability_text, get_version_info_details,
|
get_feature_availability_text, get_version_info_details,
|
||||||
has_image_overlays, has_basic_utilities, has_presets,
|
has_image_overlays, has_basic_utilities, has_presets,
|
||||||
has_normal_maps, has_batch_processing, has_advanced_utilities,
|
has_normal_maps, has_batch_processing, has_advanced_utilities,
|
||||||
has_advanced_styling, has_text_fitting, has_multiline_text
|
has_advanced_styling, has_text_fitting, has_text_editor
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ def draw_text_input_section(layout, props):
|
|||||||
|
|
||||||
# Add multiline text editor button
|
# Add multiline text editor button
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
if has_multiline_text():
|
if has_text_editor():
|
||||||
row.operator("text_texture.edit_multiline_text", text="Edit Multiline Text", icon='TEXT')
|
row.operator("text_texture.edit_multiline_text", text="Edit Multiline Text", icon='TEXT')
|
||||||
else:
|
else:
|
||||||
row.enabled = False
|
row.enabled = False
|
||||||
|
|||||||
Binary file not shown.
@@ -20,7 +20,7 @@ ENABLED_FEATURES = ["basic", "preview"] if VERSION_TYPE == "free" else [
|
|||||||
"basic", "preview", "presets", "normal_maps", "batch_processing",
|
"basic", "preview", "presets", "normal_maps", "batch_processing",
|
||||||
"image_overlays", "basic_utilities", "advanced_utilities", "advanced_styling",
|
"image_overlays", "basic_utilities", "advanced_utilities", "advanced_styling",
|
||||||
"stroke_effects", "blur_effects", "shadow_glow_effects", "shader_generation",
|
"stroke_effects", "blur_effects", "shadow_glow_effects", "shader_generation",
|
||||||
"text_fitting", "multiline_text"
|
"text_fitting", "text_editor"
|
||||||
]
|
]
|
||||||
|
|
||||||
# Version-specific limits (applied at build time through file exclusions)
|
# Version-specific limits (applied at build time through file exclusions)
|
||||||
@@ -114,9 +114,9 @@ def has_text_fitting():
|
|||||||
"""Check if text fitting is available"""
|
"""Check if text fitting is available"""
|
||||||
return has_feature("text_fitting")
|
return has_feature("text_fitting")
|
||||||
|
|
||||||
def has_multiline_text():
|
def has_text_editor():
|
||||||
"""Check if multiline text feature is available."""
|
"""Check if text editor feature is available."""
|
||||||
return has_feature("multiline_text")
|
return has_feature("text_editor")
|
||||||
|
|
||||||
def get_max_resolution():
|
def get_max_resolution():
|
||||||
"""Get maximum texture resolution based on version"""
|
"""Get maximum texture resolution based on version"""
|
||||||
@@ -139,7 +139,7 @@ def should_show_feature_lock(feature_name):
|
|||||||
'shadow_glow_effects': has_shadow_glow_effects,
|
'shadow_glow_effects': has_shadow_glow_effects,
|
||||||
'shader_generation': has_shader_generation,
|
'shader_generation': has_shader_generation,
|
||||||
'text_fitting': has_text_fitting,
|
'text_fitting': has_text_fitting,
|
||||||
'multiline_text': has_multiline_text
|
'text_editor': has_text_editor
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_full_version():
|
if is_full_version():
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ Tests verify:
|
|||||||
- Operator class exists and is properly structured
|
- Operator class exists and is properly structured
|
||||||
- PRO-only access control via poll()
|
- PRO-only access control via poll()
|
||||||
- Text processing and limit enforcement
|
- Text processing and limit enforcement
|
||||||
- Single-line validation (rejects newlines)
|
|
||||||
- Proper Blender operator contract
|
- Proper Blender operator contract
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -14,62 +13,62 @@ from unittest.mock import Mock, MagicMock, patch
|
|||||||
|
|
||||||
|
|
||||||
class TestModalTextEditorOperator:
|
class TestModalTextEditorOperator:
|
||||||
"""Test suite for TEXT_TEXTURE_OT_edit_multiline_text operator."""
|
"""Test suite for TEXT_TEXTURE_OT_edit_text operator."""
|
||||||
|
|
||||||
def test_operator_class_exists(self):
|
def test_operator_class_exists(self):
|
||||||
"""Operator class must be importable and follow Blender conventions."""
|
"""Operator class must be importable and follow Blender conventions."""
|
||||||
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
|
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_text
|
||||||
|
|
||||||
assert hasattr(TEXT_TEXTURE_OT_edit_multiline_text, 'bl_idname')
|
assert hasattr(TEXT_TEXTURE_OT_edit_text, 'bl_idname')
|
||||||
assert TEXT_TEXTURE_OT_edit_multiline_text.bl_idname == "text_texture.edit_multiline_text"
|
assert TEXT_TEXTURE_OT_edit_text.bl_idname == "text_texture.edit_text"
|
||||||
assert hasattr(TEXT_TEXTURE_OT_edit_multiline_text, 'bl_label')
|
assert hasattr(TEXT_TEXTURE_OT_edit_text, 'bl_label')
|
||||||
assert hasattr(TEXT_TEXTURE_OT_edit_multiline_text, 'poll')
|
assert hasattr(TEXT_TEXTURE_OT_edit_text, 'poll')
|
||||||
assert hasattr(TEXT_TEXTURE_OT_edit_multiline_text, 'invoke')
|
assert hasattr(TEXT_TEXTURE_OT_edit_text, 'invoke')
|
||||||
assert hasattr(TEXT_TEXTURE_OT_edit_multiline_text, 'execute')
|
assert hasattr(TEXT_TEXTURE_OT_edit_text, 'execute')
|
||||||
assert hasattr(TEXT_TEXTURE_OT_edit_multiline_text, 'draw')
|
assert hasattr(TEXT_TEXTURE_OT_edit_text, 'draw')
|
||||||
|
|
||||||
def test_poll_denies_access_in_free_version(self):
|
def test_poll_denies_access_in_free_version(self):
|
||||||
"""poll() must return False when has_multiline_text() is False (FREE version)."""
|
"""poll() must return False when has_text_editor() is False (FREE version)."""
|
||||||
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
|
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_text
|
||||||
|
|
||||||
mock_context = Mock()
|
mock_context = Mock()
|
||||||
|
|
||||||
with patch('src.operators.text_editor_ops.has_multiline_text', return_value=False):
|
with patch('src.operators.text_editor_ops.has_text_editor', return_value=False):
|
||||||
result = TEXT_TEXTURE_OT_edit_multiline_text.poll(mock_context)
|
result = TEXT_TEXTURE_OT_edit_text.poll(mock_context)
|
||||||
assert result is False, "FREE version should not have access to multiline editor"
|
assert result is False, "FREE version should not have access to text editor"
|
||||||
|
|
||||||
def test_poll_allows_access_in_pro_version(self):
|
def test_poll_allows_access_in_pro_version(self):
|
||||||
"""poll() must return True when has_multiline_text() is True (PRO version)."""
|
"""poll() must return True when has_text_editor() is True (PRO version)."""
|
||||||
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
|
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_text
|
||||||
|
|
||||||
mock_context = Mock()
|
mock_context = Mock()
|
||||||
|
|
||||||
with patch('src.operators.text_editor_ops.has_multiline_text', return_value=True):
|
with patch('src.operators.text_editor_ops.has_text_editor', return_value=True):
|
||||||
result = TEXT_TEXTURE_OT_edit_multiline_text.poll(mock_context)
|
result = TEXT_TEXTURE_OT_edit_text.poll(mock_context)
|
||||||
assert result is True, "PRO version should have access to multiline editor"
|
assert result is True, "PRO version should have access to text editor"
|
||||||
|
|
||||||
def test_invoke_copies_current_text_to_temp(self):
|
def test_invoke_copies_current_text_to_temp(self):
|
||||||
"""invoke() must copy scene text property to temp_text for editing."""
|
"""invoke() must copy scene text property to temp_text for editing."""
|
||||||
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
|
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_text
|
||||||
|
|
||||||
operator = TEXT_TEXTURE_OT_edit_multiline_text()
|
operator = TEXT_TEXTURE_OT_edit_text()
|
||||||
mock_context = Mock()
|
mock_context = Mock()
|
||||||
mock_context.scene.text_texture_props.text = "Single line text"
|
mock_context.scene.text_texture_props.text = "Test text"
|
||||||
mock_context.window_manager.invoke_props_dialog.return_value = {'RUNNING_MODAL'}
|
mock_context.window_manager.invoke_props_dialog.return_value = {'RUNNING_MODAL'}
|
||||||
mock_event = Mock()
|
mock_event = Mock()
|
||||||
|
|
||||||
result = operator.invoke(mock_context, mock_event)
|
result = operator.invoke(mock_context, mock_event)
|
||||||
|
|
||||||
assert operator.temp_text == "Single line text"
|
assert operator.temp_text == "Test text"
|
||||||
assert result == {'RUNNING_MODAL'}
|
assert result == {'RUNNING_MODAL'}
|
||||||
mock_context.window_manager.invoke_props_dialog.assert_called_once()
|
mock_context.window_manager.invoke_props_dialog.assert_called_once()
|
||||||
|
|
||||||
def test_execute_saves_temp_text_back_to_property(self):
|
def test_execute_saves_temp_text_back_to_property(self):
|
||||||
"""execute() must save edited temp_text back to scene property."""
|
"""execute() must save edited temp_text back to scene property."""
|
||||||
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
|
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_text
|
||||||
|
|
||||||
operator = TEXT_TEXTURE_OT_edit_multiline_text()
|
operator = TEXT_TEXTURE_OT_edit_text()
|
||||||
operator.temp_text = "Edited single line"
|
operator.temp_text = "Edited text"
|
||||||
|
|
||||||
mock_context = Mock()
|
mock_context = Mock()
|
||||||
mock_context.scene.text_texture_props.text = "Old text"
|
mock_context.scene.text_texture_props.text = "Old text"
|
||||||
@@ -77,13 +76,13 @@ class TestModalTextEditorOperator:
|
|||||||
result = operator.execute(mock_context)
|
result = operator.execute(mock_context)
|
||||||
|
|
||||||
assert result == {'FINISHED'}
|
assert result == {'FINISHED'}
|
||||||
assert mock_context.scene.text_texture_props.text == "Edited single line"
|
assert mock_context.scene.text_texture_props.text == "Edited text"
|
||||||
|
|
||||||
def test_execute_enforces_character_limit(self):
|
def test_execute_enforces_character_limit(self):
|
||||||
"""execute() must truncate text exceeding max_text_length."""
|
"""execute() must truncate text exceeding max_text_length."""
|
||||||
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
|
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_text
|
||||||
|
|
||||||
operator = TEXT_TEXTURE_OT_edit_multiline_text()
|
operator = TEXT_TEXTURE_OT_edit_text()
|
||||||
# Create text exceeding 4096 char limit
|
# Create text exceeding 4096 char limit
|
||||||
operator.temp_text = "x" * 5000
|
operator.temp_text = "x" * 5000
|
||||||
|
|
||||||
@@ -95,63 +94,11 @@ class TestModalTextEditorOperator:
|
|||||||
saved_text = mock_context.scene.text_texture_props.text
|
saved_text = mock_context.scene.text_texture_props.text
|
||||||
assert len(saved_text) == 4096, f"Expected 4096 chars, got {len(saved_text)}"
|
assert len(saved_text) == 4096, f"Expected 4096 chars, got {len(saved_text)}"
|
||||||
|
|
||||||
def test_execute_rejects_text_with_newlines(self):
|
|
||||||
"""execute() must reject text containing newline characters."""
|
|
||||||
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
|
|
||||||
|
|
||||||
operator = TEXT_TEXTURE_OT_edit_multiline_text()
|
|
||||||
operator.temp_text = "Line 1\nLine 2"
|
|
||||||
|
|
||||||
mock_context = Mock()
|
|
||||||
mock_context.scene.text_texture_props.text = "Old text"
|
|
||||||
|
|
||||||
result = operator.execute(mock_context)
|
|
||||||
|
|
||||||
# Should return CANCELLED
|
|
||||||
assert result == {'CANCELLED'}
|
|
||||||
# Should not modify the original text
|
|
||||||
assert mock_context.scene.text_texture_props.text == "Old text"
|
|
||||||
# Should report an error
|
|
||||||
operator.report.assert_called_once_with(
|
|
||||||
{'ERROR'},
|
|
||||||
"Multiline text not supported. Please use single line only."
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_execute_rejects_multiple_newlines(self):
|
|
||||||
"""execute() must reject text with multiple newline characters."""
|
|
||||||
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
|
|
||||||
|
|
||||||
operator = TEXT_TEXTURE_OT_edit_multiline_text()
|
|
||||||
operator.temp_text = "Line 1\nLine 2\nLine 3\nLine 4"
|
|
||||||
|
|
||||||
mock_context = Mock()
|
|
||||||
mock_context.scene.text_texture_props.text = "Old text"
|
|
||||||
|
|
||||||
result = operator.execute(mock_context)
|
|
||||||
|
|
||||||
assert result == {'CANCELLED'}
|
|
||||||
assert mock_context.scene.text_texture_props.text == "Old text"
|
|
||||||
|
|
||||||
def test_execute_accepts_text_without_newlines(self):
|
|
||||||
"""execute() must accept and save text without newlines."""
|
|
||||||
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
|
|
||||||
|
|
||||||
operator = TEXT_TEXTURE_OT_edit_multiline_text()
|
|
||||||
operator.temp_text = "This is a single line with no newlines"
|
|
||||||
|
|
||||||
mock_context = Mock()
|
|
||||||
mock_context.scene.text_texture_props.text = "Old text"
|
|
||||||
|
|
||||||
result = operator.execute(mock_context)
|
|
||||||
|
|
||||||
assert result == {'FINISHED'}
|
|
||||||
assert mock_context.scene.text_texture_props.text == "This is a single line with no newlines"
|
|
||||||
|
|
||||||
def test_operator_has_temp_text_property(self):
|
def test_operator_has_temp_text_property(self):
|
||||||
"""Operator must have StringProperty named temp_text."""
|
"""Operator must have StringProperty named temp_text."""
|
||||||
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
|
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_text
|
||||||
|
|
||||||
operator = TEXT_TEXTURE_OT_edit_multiline_text()
|
operator = TEXT_TEXTURE_OT_edit_text()
|
||||||
# Blender properties are accessible as attributes
|
# Blender properties are accessible as attributes
|
||||||
assert hasattr(operator, 'temp_text')
|
assert hasattr(operator, 'temp_text')
|
||||||
# Should be able to set and get
|
# Should be able to set and get
|
||||||
@@ -178,50 +125,4 @@ class TestModalTextEditorOperator:
|
|||||||
from src.operators import text_editor_ops
|
from src.operators import text_editor_ops
|
||||||
|
|
||||||
assert hasattr(text_editor_ops, 'unregister')
|
assert hasattr(text_editor_ops, 'unregister')
|
||||||
assert callable(text_editor_ops.unregister)
|
assert callable(text_editor_ops.unregister)
|
||||||
|
|
||||||
|
|
||||||
class TestSingleLineUIBehavior:
|
|
||||||
"""Test suite for single-line text UI display and statistics."""
|
|
||||||
|
|
||||||
def test_draw_calculates_character_count(self):
|
|
||||||
"""draw() must count characters correctly."""
|
|
||||||
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
|
|
||||||
|
|
||||||
operator = TEXT_TEXTURE_OT_edit_multiline_text()
|
|
||||||
mock_context = Mock()
|
|
||||||
|
|
||||||
operator.temp_text = "Single line text"
|
|
||||||
operator.draw(mock_context)
|
|
||||||
|
|
||||||
actual_chars = len(operator.temp_text)
|
|
||||||
expected_chars = 16
|
|
||||||
assert actual_chars == expected_chars, f"Expected {expected_chars} chars, got {actual_chars}"
|
|
||||||
|
|
||||||
def test_draw_handles_empty_text(self):
|
|
||||||
"""draw() must handle empty text without errors."""
|
|
||||||
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
|
|
||||||
|
|
||||||
operator = TEXT_TEXTURE_OT_edit_multiline_text()
|
|
||||||
mock_context = Mock()
|
|
||||||
|
|
||||||
operator.temp_text = ""
|
|
||||||
operator.draw(mock_context)
|
|
||||||
|
|
||||||
char_count = len(operator.temp_text)
|
|
||||||
assert char_count == 0, f"Empty text should have 0 characters, got {char_count}"
|
|
||||||
|
|
||||||
def test_draw_handles_long_single_line(self):
|
|
||||||
"""draw() must handle long single-line text correctly."""
|
|
||||||
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
|
|
||||||
|
|
||||||
operator = TEXT_TEXTURE_OT_edit_multiline_text()
|
|
||||||
mock_context = Mock()
|
|
||||||
|
|
||||||
# Create a very long single line
|
|
||||||
long_text = "x" * 5000
|
|
||||||
operator.temp_text = long_text
|
|
||||||
operator.draw(mock_context)
|
|
||||||
|
|
||||||
# Should track all characters
|
|
||||||
assert len(operator.temp_text) == 5000
|
|
||||||
Reference in New Issue
Block a user