From c9a404d63478c850c2478cd763edea0ad9b11406 Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Sun, 12 Oct 2025 12:11:59 +0200 Subject: [PATCH] remove multiline --- src/__init__.py | 6 +- src/__pycache__/__init__.cpython-311.pyc | Bin 13352 -> 13342 bytes src/operators/__init__.py | 4 +- .../__pycache__/__init__.cpython-311.pyc | Bin 2832 -> 2822 bytes src/operators/text_editor_ops.py | 42 ++--- src/ui/__pycache__/panels.cpython-311.pyc | Bin 57663 -> 57660 bytes src/ui/panels.py | 4 +- .../__pycache__/constants.cpython-311.pyc | Bin 7431 -> 7422 bytes src/utils/constants.py | 10 +- tests/unit/operators/test_text_editor_ops.py | 163 ++++-------------- 10 files changed, 55 insertions(+), 174 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index d1c9093..fcab370 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -139,7 +139,7 @@ else: # Conditionally import text editor operators (PRO feature) try: from .operators import ( - TEXT_TEXTURE_OT_edit_multiline_text, + TEXT_TEXTURE_OT_edit_text, ) TEXT_EDITOR_OPERATORS_AVAILABLE = True except ImportError as e: @@ -148,7 +148,7 @@ except ImportError as e: print(f"WARNING: text_editor_ops import failed: {e}", file=sys.stderr) traceback.print_exc() # Free version - use mock operator - TEXT_TEXTURE_OT_edit_multiline_text = MockOperator + TEXT_TEXTURE_OT_edit_text = MockOperator TEXT_EDITOR_OPERATORS_AVAILABLE = False # Import UI panels and functions @@ -182,7 +182,7 @@ classes = ( TEXT_TEXTURE_OT_duplicate_overlay, TEXT_TEXTURE_OT_delete_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_save_preset, TEXT_TEXTURE_OT_save_preset_enter, diff --git a/src/__pycache__/__init__.cpython-311.pyc b/src/__pycache__/__init__.cpython-311.pyc index e8799432443c87b81ea45eb47fd512f7db1037d3..6e78b2c3255a4d4ff71fb299a65c6c5773be5d51 100644 GIT binary patch delta 49 zcmZ3HF)xF6IWI340}!y(z0Nqck#`~=qvYiIe4?B8@%>O{RnWP>B0G7v(Go`4$qB|M E0EPt+VgLXD delta 59 zcmbQ2u_A+aIWI340}yl;zs`8Fk#`~=qw?hWe4?DWr8y;;IhlE>n>X|QRA$vOyTGD8 Od85%1M)k>I#wP$;?-c3) diff --git a/src/operators/__init__.py b/src/operators/__init__.py index 45befb3..3d2f75b 100644 --- a/src/operators/__init__.py +++ b/src/operators/__init__.py @@ -46,10 +46,10 @@ try: pass 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_TEXTURE_OT_edit_multiline_text', + 'TEXT_TEXTURE_OT_edit_text', ] except ImportError as e: import sys diff --git a/src/operators/__pycache__/__init__.cpython-311.pyc b/src/operators/__pycache__/__init__.cpython-311.pyc index 84625639f73c6e2ed648875b251d60fdfc48f4c6..6eff4670d1f58d5ce8ba77df39f1913ea1ca21f5 100644 GIT binary patch delta 56 zcmbOr)+WZgoR^o20SMmLzRozdk=KkxR5HXhA|xI}ga*0B`-jA*rev0EPGVWX!l<&D Io0F9Z0NJk)S^xk5 delta 66 zcmZn@n;^!!oR^o20SJDXzRb9_k=KkxUOB`yA|xI}ga*0B`-jA*rev1H=a%M_Waeb% SrEZR3S;4}nx0#WXl?ec)j}&VF diff --git a/src/operators/text_editor_ops.py b/src/operators/text_editor_ops.py index 6f930e8..39f7c84 100644 --- a/src/operators/text_editor_ops.py +++ b/src/operators/text_editor_ops.py @@ -1,19 +1,19 @@ import bpy from bpy.types import Operator 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): - """Open a text editor for single-line input""" - bl_idname = "text_texture.edit_multiline_text" +class TEXT_TEXTURE_OT_edit_text(Operator): + """Open a text editor for text input""" + bl_idname = "text_texture.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'} temp_text: StringProperty( name="Text", - description="Single line text only - multiline not supported", + description="Text input", default="", maxlen=4096 ) @@ -21,7 +21,7 @@ class TEXT_TEXTURE_OT_edit_multiline_text(Operator): @classmethod def poll(cls, context): """Only available in PRO version""" - return has_multiline_text() + return has_text_editor() def invoke(self, context, event): """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) def draw(self, context): - """Draw the dialog UI with single-line input""" + """Draw the dialog UI""" layout = self.layout max_chars = 4096 current_chars = len(self.temp_text) - # Main 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.scale_y = 2.0 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() stats_row = col.row() stats_row.label(text=f"📝 Characters: {current_chars}/{max_chars}", icon='FONT_DATA') - # Warnings if current_chars > max_chars: col.separator() 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') 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 - # 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 - - # Enforce character limit text = self.temp_text[:max_chars] - props.text = text return {'FINISHED'} -classes = (TEXT_TEXTURE_OT_edit_multiline_text,) +classes = (TEXT_TEXTURE_OT_edit_text,) def register(): diff --git a/src/ui/__pycache__/panels.cpython-311.pyc b/src/ui/__pycache__/panels.cpython-311.pyc index 48e27bb979834c7187f8d441cb46e9f5b06d8745..acf6d0210900b8f761de46adab82d8c6d2c91827 100644 GIT binary patch delta 59 zcmdmgh*t|uhdk+9?3KbFn delta 62 zcmdmUhO2LOk<6?gyu diff --git a/src/ui/panels.py b/src/ui/panels.py index a5ca162..ab25057 100644 --- a/src/ui/panels.py +++ b/src/ui/panels.py @@ -12,7 +12,7 @@ from ..utils.constants import ( 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, 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 row = layout.row() - if has_multiline_text(): + if has_text_editor(): row.operator("text_texture.edit_multiline_text", text="Edit Multiline Text", icon='TEXT') else: row.enabled = False diff --git a/src/utils/__pycache__/constants.cpython-311.pyc b/src/utils/__pycache__/constants.cpython-311.pyc index b68e05ad2da0711943a6faacaab9ad2d75a9727e..45361a9ed39b8079427a64b2b7c2ee53675ce1eb 100644 GIT binary patch delta 385 zcmZp-`e(_zoR^o20SGqKzRuXbk@qk&4|hpwMM->WN@huZ(c}l5Vw=A*Z{%S7Jb5|K zT1KzQp}a>JeJ4xt?dS3b>WT&8BJat5LaviF_(K>2C)e`NV+@`wBXEE*Z1P=!9gGo^ zrwOj(iUKMt1St&xDHYXn&PYwpR>(|K09mB~wQ9AHfi!98u%|BAh`)AczPC5n&)A0z`l;FN&FbQPhnwd9s91(VoR^o20SG#aUuT@$$a|QXpD(vGrzA5cGcPs1B(xDe(mfXtt~RLDS5C?l~r9@XBWXrQjr$#wivlUapNF~)DcAbgb3 z*b`)w4~PKSU*rHHoIykYhzJ4^p&%k0M1brrik^Hz)QvG|GQXH9la$V8H!(Lx#+1#y z;%6AeLC(L$oRgYYWCK#E0VFh;isB|0NE$HaPF^T^!4}Or+?Ig!$AgF@5Rn2Rawo5o Zl4UH|d`9XHBe&)U1|~)bF?q9$Hvo6aW@`Wd diff --git a/src/utils/constants.py b/src/utils/constants.py index 39b8bf9..37c49db 100644 --- a/src/utils/constants.py +++ b/src/utils/constants.py @@ -20,7 +20,7 @@ ENABLED_FEATURES = ["basic", "preview"] if VERSION_TYPE == "free" else [ "basic", "preview", "presets", "normal_maps", "batch_processing", "image_overlays", "basic_utilities", "advanced_utilities", "advanced_styling", "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) @@ -114,9 +114,9 @@ def has_text_fitting(): """Check if text fitting is available""" return has_feature("text_fitting") -def has_multiline_text(): - """Check if multiline text feature is available.""" - return has_feature("multiline_text") +def has_text_editor(): + """Check if text editor feature is available.""" + return has_feature("text_editor") def get_max_resolution(): """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, 'shader_generation': has_shader_generation, 'text_fitting': has_text_fitting, - 'multiline_text': has_multiline_text + 'text_editor': has_text_editor } if is_full_version(): diff --git a/tests/unit/operators/test_text_editor_ops.py b/tests/unit/operators/test_text_editor_ops.py index 49fdde4..de8c7ec 100644 --- a/tests/unit/operators/test_text_editor_ops.py +++ b/tests/unit/operators/test_text_editor_ops.py @@ -5,7 +5,6 @@ Tests verify: - Operator class exists and is properly structured - PRO-only access control via poll() - Text processing and limit enforcement -- Single-line validation (rejects newlines) - Proper Blender operator contract """ @@ -14,62 +13,62 @@ from unittest.mock import Mock, MagicMock, patch 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): """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 TEXT_TEXTURE_OT_edit_multiline_text.bl_idname == "text_texture.edit_multiline_text" - assert hasattr(TEXT_TEXTURE_OT_edit_multiline_text, 'bl_label') - assert hasattr(TEXT_TEXTURE_OT_edit_multiline_text, 'poll') - assert hasattr(TEXT_TEXTURE_OT_edit_multiline_text, 'invoke') - assert hasattr(TEXT_TEXTURE_OT_edit_multiline_text, 'execute') - assert hasattr(TEXT_TEXTURE_OT_edit_multiline_text, 'draw') + assert hasattr(TEXT_TEXTURE_OT_edit_text, 'bl_idname') + assert TEXT_TEXTURE_OT_edit_text.bl_idname == "text_texture.edit_text" + assert hasattr(TEXT_TEXTURE_OT_edit_text, 'bl_label') + assert hasattr(TEXT_TEXTURE_OT_edit_text, 'poll') + assert hasattr(TEXT_TEXTURE_OT_edit_text, 'invoke') + assert hasattr(TEXT_TEXTURE_OT_edit_text, 'execute') + assert hasattr(TEXT_TEXTURE_OT_edit_text, 'draw') def test_poll_denies_access_in_free_version(self): - """poll() must return False when has_multiline_text() is False (FREE version).""" - from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text + """poll() must return False when has_text_editor() is False (FREE version).""" + from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_text mock_context = Mock() - with patch('src.operators.text_editor_ops.has_multiline_text', return_value=False): - result = TEXT_TEXTURE_OT_edit_multiline_text.poll(mock_context) - assert result is False, "FREE version should not have access to multiline editor" + with patch('src.operators.text_editor_ops.has_text_editor', return_value=False): + result = TEXT_TEXTURE_OT_edit_text.poll(mock_context) + assert result is False, "FREE version should not have access to text editor" def test_poll_allows_access_in_pro_version(self): - """poll() must return True when has_multiline_text() is True (PRO version).""" - from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text + """poll() must return True when has_text_editor() is True (PRO version).""" + from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_text mock_context = Mock() - with patch('src.operators.text_editor_ops.has_multiline_text', return_value=True): - result = TEXT_TEXTURE_OT_edit_multiline_text.poll(mock_context) - assert result is True, "PRO version should have access to multiline editor" + with patch('src.operators.text_editor_ops.has_text_editor', return_value=True): + result = TEXT_TEXTURE_OT_edit_text.poll(mock_context) + assert result is True, "PRO version should have access to text editor" def test_invoke_copies_current_text_to_temp(self): """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.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_event = Mock() result = operator.invoke(mock_context, mock_event) - assert operator.temp_text == "Single line text" + assert operator.temp_text == "Test text" assert result == {'RUNNING_MODAL'} mock_context.window_manager.invoke_props_dialog.assert_called_once() def test_execute_saves_temp_text_back_to_property(self): """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.temp_text = "Edited single line" + operator = TEXT_TEXTURE_OT_edit_text() + operator.temp_text = "Edited text" mock_context = Mock() mock_context.scene.text_texture_props.text = "Old text" @@ -77,13 +76,13 @@ class TestModalTextEditorOperator: result = operator.execute(mock_context) 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): """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 operator.temp_text = "x" * 5000 @@ -95,63 +94,11 @@ class TestModalTextEditorOperator: saved_text = mock_context.scene.text_texture_props.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): """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 assert hasattr(operator, 'temp_text') # Should be able to set and get @@ -178,50 +125,4 @@ class TestModalTextEditorOperator: from src.operators import text_editor_ops assert hasattr(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 \ No newline at end of file + assert callable(text_editor_ops.unregister) \ No newline at end of file