supervertaler 1.9.153__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of supervertaler might be problematic. Click here for more details.

Files changed (85) hide show
  1. Supervertaler.py +47886 -0
  2. modules/__init__.py +10 -0
  3. modules/ai_actions.py +964 -0
  4. modules/ai_attachment_manager.py +343 -0
  5. modules/ai_file_viewer_dialog.py +210 -0
  6. modules/autofingers_engine.py +466 -0
  7. modules/cafetran_docx_handler.py +379 -0
  8. modules/config_manager.py +469 -0
  9. modules/database_manager.py +1878 -0
  10. modules/database_migrations.py +417 -0
  11. modules/dejavurtf_handler.py +779 -0
  12. modules/document_analyzer.py +427 -0
  13. modules/docx_handler.py +689 -0
  14. modules/encoding_repair.py +319 -0
  15. modules/encoding_repair_Qt.py +393 -0
  16. modules/encoding_repair_ui.py +481 -0
  17. modules/feature_manager.py +350 -0
  18. modules/figure_context_manager.py +340 -0
  19. modules/file_dialog_helper.py +148 -0
  20. modules/find_replace.py +164 -0
  21. modules/find_replace_qt.py +457 -0
  22. modules/glossary_manager.py +433 -0
  23. modules/image_extractor.py +188 -0
  24. modules/keyboard_shortcuts_widget.py +571 -0
  25. modules/llm_clients.py +1211 -0
  26. modules/llm_leaderboard.py +737 -0
  27. modules/llm_superbench_ui.py +1401 -0
  28. modules/local_llm_setup.py +1104 -0
  29. modules/model_update_dialog.py +381 -0
  30. modules/model_version_checker.py +373 -0
  31. modules/mqxliff_handler.py +638 -0
  32. modules/non_translatables_manager.py +743 -0
  33. modules/pdf_rescue_Qt.py +1822 -0
  34. modules/pdf_rescue_tkinter.py +909 -0
  35. modules/phrase_docx_handler.py +516 -0
  36. modules/project_home_panel.py +209 -0
  37. modules/prompt_assistant.py +357 -0
  38. modules/prompt_library.py +689 -0
  39. modules/prompt_library_migration.py +447 -0
  40. modules/quick_access_sidebar.py +282 -0
  41. modules/ribbon_widget.py +597 -0
  42. modules/sdlppx_handler.py +874 -0
  43. modules/setup_wizard.py +353 -0
  44. modules/shortcut_manager.py +932 -0
  45. modules/simple_segmenter.py +128 -0
  46. modules/spellcheck_manager.py +727 -0
  47. modules/statuses.py +207 -0
  48. modules/style_guide_manager.py +315 -0
  49. modules/superbench_ui.py +1319 -0
  50. modules/superbrowser.py +329 -0
  51. modules/supercleaner.py +600 -0
  52. modules/supercleaner_ui.py +444 -0
  53. modules/superdocs.py +19 -0
  54. modules/superdocs_viewer_qt.py +382 -0
  55. modules/superlookup.py +252 -0
  56. modules/tag_cleaner.py +260 -0
  57. modules/tag_manager.py +333 -0
  58. modules/term_extractor.py +270 -0
  59. modules/termbase_entry_editor.py +842 -0
  60. modules/termbase_import_export.py +488 -0
  61. modules/termbase_manager.py +1060 -0
  62. modules/termview_widget.py +1172 -0
  63. modules/theme_manager.py +499 -0
  64. modules/tm_editor_dialog.py +99 -0
  65. modules/tm_manager_qt.py +1280 -0
  66. modules/tm_metadata_manager.py +545 -0
  67. modules/tmx_editor.py +1461 -0
  68. modules/tmx_editor_qt.py +2784 -0
  69. modules/tmx_generator.py +284 -0
  70. modules/tracked_changes.py +900 -0
  71. modules/trados_docx_handler.py +430 -0
  72. modules/translation_memory.py +715 -0
  73. modules/translation_results_panel.py +2134 -0
  74. modules/translation_services.py +282 -0
  75. modules/unified_prompt_library.py +659 -0
  76. modules/unified_prompt_manager_qt.py +3951 -0
  77. modules/voice_commands.py +920 -0
  78. modules/voice_dictation.py +477 -0
  79. modules/voice_dictation_lite.py +249 -0
  80. supervertaler-1.9.153.dist-info/METADATA +896 -0
  81. supervertaler-1.9.153.dist-info/RECORD +85 -0
  82. supervertaler-1.9.153.dist-info/WHEEL +5 -0
  83. supervertaler-1.9.153.dist-info/entry_points.txt +2 -0
  84. supervertaler-1.9.153.dist-info/licenses/LICENSE +21 -0
  85. supervertaler-1.9.153.dist-info/top_level.txt +2 -0
@@ -0,0 +1,597 @@
1
+ """
2
+ Ribbon Widget - Modern Office-style ribbon interface for Supervertaler Qt
3
+
4
+ Provides context-sensitive ribbon tabs with grouped tool buttons,
5
+ similar to memoQ, Trados Studio, and Microsoft Office applications.
6
+
7
+ Author: Michael Beijer
8
+ License: MIT
9
+ """
10
+
11
+ from PyQt6.QtWidgets import (
12
+ QWidget, QHBoxLayout, QVBoxLayout, QToolButton, QLabel,
13
+ QFrame, QSizePolicy, QTabWidget, QTabBar, QPushButton
14
+ )
15
+ from PyQt6.QtCore import Qt, QSize, pyqtSignal
16
+ from PyQt6.QtGui import QIcon, QFont, QPainter, QColor
17
+
18
+
19
+ class RibbonButton(QToolButton):
20
+ """A ribbon-style tool button with icon and text"""
21
+
22
+ def __init__(self, text: str, icon_text: str = "", parent=None):
23
+ super().__init__(parent)
24
+
25
+ # Store emoji for display
26
+ self.emoji = icon_text
27
+ self.button_text = text
28
+ self.group_color = "#F5F5F5" # Default
29
+
30
+ # Set button style
31
+ self.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
32
+
33
+ # Create display text with emoji
34
+ if icon_text:
35
+ display_text = f"{icon_text} {text}"
36
+ else:
37
+ display_text = text
38
+
39
+ self.setText(display_text)
40
+ self.setToolTip(text)
41
+
42
+ # Reduced sizing for more compact ribbon
43
+ self.setMinimumSize(QSize(80, 40))
44
+ self.setMaximumHeight(44)
45
+ self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
46
+
47
+ # Font for emoji + text
48
+ font = QFont()
49
+ font.setPointSize(9)
50
+ self.setFont(font)
51
+
52
+ # Make button look modern
53
+ self.setAutoRaise(True)
54
+ self._update_style()
55
+
56
+ def set_group_color(self, color: str):
57
+ """Set the group color for this button"""
58
+ self.group_color = color
59
+ self._update_style()
60
+
61
+ def _update_style(self):
62
+ """Update button styling with current group color"""
63
+ # Convert color to RGB for calculations
64
+ color_rgb = self.group_color.lstrip('#')
65
+ r, g, b = tuple(int(color_rgb[i:i+2], 16) for i in (0, 2, 4))
66
+
67
+ # Create hover color (slightly darker)
68
+ hover_r = min(255, r + 20)
69
+ hover_g = min(255, g + 20)
70
+ hover_b = min(255, b + 20)
71
+ hover_color = f"rgb({hover_r}, {hover_g}, {hover_b})"
72
+
73
+ # Create pressed color (darker)
74
+ pressed_r = max(0, r - 30)
75
+ pressed_g = max(0, g - 30)
76
+ pressed_b = max(0, b - 30)
77
+ pressed_color = f"rgb({pressed_r}, {pressed_g}, {pressed_b})"
78
+
79
+ # Border color (darker than background)
80
+ border_r = max(0, r - 40)
81
+ border_g = max(0, g - 40)
82
+ border_b = max(0, b - 40)
83
+ border_color = f"rgb({border_r}, {border_g}, {border_b})"
84
+
85
+ self.setStyleSheet(f"""
86
+ QToolButton {{
87
+ border: 1px solid {border_color};
88
+ border-radius: 3px;
89
+ padding: 3px 6px;
90
+ background-color: {self.group_color};
91
+ }}
92
+ QToolButton:hover {{
93
+ background-color: {hover_color};
94
+ border: 1px solid {border_color};
95
+ }}
96
+ QToolButton:pressed {{
97
+ background-color: {pressed_color};
98
+ border: 1px solid {border_color};
99
+ }}
100
+ """)
101
+
102
+
103
+ class RibbonGroup(QFrame):
104
+ """A group of related ribbon buttons with a title"""
105
+
106
+ def __init__(self, title: str, parent=None):
107
+ super().__init__(parent)
108
+
109
+ self.title = title
110
+ self.tab_color = "#F5F5F5" # Default
111
+ self.setFrameShape(QFrame.Shape.StyledPanel)
112
+ self.setFrameShadow(QFrame.Shadow.Raised)
113
+
114
+ # Layout (must be created before styling)
115
+ layout = QVBoxLayout(self)
116
+ layout.setSpacing(0)
117
+ layout.setContentsMargins(2, 2, 2, 2)
118
+
119
+ # Buttons area
120
+ self.buttons_layout = QHBoxLayout()
121
+ self.buttons_layout.setSpacing(3)
122
+ self.buttons_layout.setContentsMargins(0, 0, 0, 0)
123
+ layout.addLayout(self.buttons_layout)
124
+
125
+ # Group title at bottom (hidden by default for cleaner look)
126
+ self.title_label = QLabel(self.title)
127
+ self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
128
+ title_font = QFont()
129
+ title_font.setPointSize(7)
130
+ self.title_label.setFont(title_font)
131
+ self.title_label.setStyleSheet("color: rgba(0, 0, 0, 0.7); margin-top: 2px;")
132
+ self.title_label.hide() # Hide group titles for cleaner appearance
133
+ layout.addWidget(self.title_label)
134
+
135
+ self.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
136
+
137
+ # Initial styling (after layout is set up)
138
+ self._update_style()
139
+
140
+ def set_tab_color(self, color: str):
141
+ """Set the tab color for this group and update styling"""
142
+ self.tab_color = color
143
+ self._update_style()
144
+
145
+ def _update_style(self):
146
+ """Update group styling with current tab color"""
147
+ # Use tab color as subtle background, with very subtle or no border
148
+ color_rgb = self.tab_color.lstrip('#')
149
+ r, g, b = tuple(int(color_rgb[i:i+2], 16) for i in (0, 2, 4))
150
+ border_color = f"rgb({max(0, r-30)}, {max(0, g-30)}, {max(0, b-30)})"
151
+
152
+ self.setStyleSheet(f"""
153
+ RibbonGroup {{
154
+ border: none;
155
+ border-radius: 0px;
156
+ margin: 0px;
157
+ padding: 2px 4px;
158
+ background-color: transparent;
159
+ }}
160
+ """)
161
+
162
+ def add_button(self, button: RibbonButton):
163
+ """Add a button to this group and apply group color"""
164
+ # Apply tab color to button
165
+ button.set_group_color(self.tab_color)
166
+ self.buttons_layout.addWidget(button)
167
+
168
+ def add_buttons(self, buttons: list):
169
+ """Add multiple buttons to this group"""
170
+ for button in buttons:
171
+ self.add_button(button)
172
+
173
+
174
+ class RibbonTab(QWidget):
175
+ """A single ribbon tab containing multiple groups"""
176
+
177
+ def __init__(self, parent=None):
178
+ super().__init__(parent)
179
+
180
+ self.tab_color = "#F5F5F5" # Default light gray
181
+
182
+ layout = QHBoxLayout(self)
183
+ layout.setSpacing(2) # Reduced spacing between groups for cleaner look
184
+ layout.setContentsMargins(2, 2, 2, 2)
185
+ layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
186
+
187
+ self.groups = []
188
+
189
+ def set_tab_color(self, color: str):
190
+ """Set the color theme for this ribbon tab"""
191
+ self.tab_color = color
192
+ self.setStyleSheet(f"background-color: {color};")
193
+ # Update all groups and their buttons with the new color
194
+ self._update_all_groups()
195
+
196
+ def _update_all_groups(self):
197
+ """Update all groups and buttons with the current tab color"""
198
+ for group in self.groups:
199
+ group.set_tab_color(self.tab_color)
200
+ # Also update all buttons in the group
201
+ for i in range(group.buttons_layout.count()):
202
+ item = group.buttons_layout.itemAt(i)
203
+ if item and item.widget():
204
+ button = item.widget()
205
+ if isinstance(button, RibbonButton):
206
+ button.set_group_color(self.tab_color)
207
+
208
+ def add_group(self, group: RibbonGroup):
209
+ """Add a group to this ribbon tab"""
210
+ self.groups.append(group)
211
+ # Apply tab color to group (will propagate to buttons)
212
+ group.set_tab_color(self.tab_color)
213
+ self.layout().addWidget(group)
214
+
215
+ def add_stretch(self):
216
+ """Add stretch to push groups to the left"""
217
+ self.layout().addStretch()
218
+
219
+
220
+ class ColoredTabBar(QTabBar):
221
+ """Custom QTabBar that supports per-tab background colors"""
222
+
223
+ def __init__(self, parent=None):
224
+ super().__init__(parent)
225
+ self.tab_colors = {} # index -> color
226
+
227
+ def set_tab_color(self, index: int, color: str):
228
+ """Set the background color for a specific tab"""
229
+ self.tab_colors[index] = color
230
+ self.update()
231
+
232
+ def paintEvent(self, event):
233
+ """Override paint event to draw custom tab colors"""
234
+ # Draw colored backgrounds first
235
+ painter = QPainter(self)
236
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
237
+
238
+ for i in range(self.count()):
239
+ tab_rect = self.tabRect(i)
240
+ color_str = self.tab_colors.get(i, "#E0E0E0")
241
+
242
+ # Convert hex to QColor
243
+ color = QColor(color_str)
244
+
245
+ # If selected, use full color; otherwise use lighter version
246
+ if i == self.currentIndex():
247
+ painter.fillRect(tab_rect, color)
248
+ # Draw border bottom (thicker line for selected)
249
+ darker = QColor(color)
250
+ darker.setRed(max(0, darker.red() - 50))
251
+ darker.setGreen(max(0, darker.green() - 50))
252
+ darker.setBlue(max(0, darker.blue() - 50))
253
+ pen = painter.pen()
254
+ pen.setColor(darker)
255
+ pen.setWidth(3)
256
+ painter.setPen(pen)
257
+ painter.drawLine(tab_rect.left(), tab_rect.bottom() - 1, tab_rect.right(), tab_rect.bottom() - 1)
258
+ else:
259
+ # Lighter version for unselected tabs
260
+ lighter = QColor(color)
261
+ # Blend with gray (E0E0E0 = 224, 224, 224)
262
+ lighter.setRed(int((lighter.red() + 224) / 2))
263
+ lighter.setGreen(int((lighter.green() + 224) / 2))
264
+ lighter.setBlue(int((lighter.blue() + 224) / 2))
265
+ painter.fillRect(tab_rect, lighter)
266
+
267
+ # Now draw text on top of colored backgrounds
268
+ painter.setPen(QColor(Qt.GlobalColor.black))
269
+ font = self.font()
270
+ painter.setFont(font)
271
+
272
+ for i in range(self.count()):
273
+ tab_rect = self.tabRect(i)
274
+ text = self.tabText(i)
275
+ # Center text in tab
276
+ painter.drawText(tab_rect, Qt.AlignmentFlag.AlignCenter, text)
277
+
278
+ painter.end()
279
+
280
+ # Don't call super().paintEvent() as it would redraw default backgrounds
281
+ # We've already drawn everything we need
282
+
283
+
284
+ class RibbonWidget(QTabWidget):
285
+ """Main ribbon widget with multiple context-sensitive tabs"""
286
+
287
+ # Signals for button actions
288
+ action_triggered = pyqtSignal(str) # Emits action name
289
+
290
+ # Color scheme for ribbon tabs
291
+ TAB_COLORS = {
292
+ "Home": "#E3F2FD", # Light blue
293
+ "Translation": "#FFF3E0", # Light orange
294
+ "View": "#E8F5E9", # Light green
295
+ "Tools": "#F3E5F5", # Light purple
296
+ }
297
+
298
+ def __init__(self, parent=None):
299
+ super().__init__(parent)
300
+
301
+ # Replace default tab bar with custom colored one
302
+ custom_tab_bar = ColoredTabBar(self)
303
+ self.setTabBar(custom_tab_bar)
304
+
305
+ # Styling - compact ribbon with colored tabs
306
+ self.setDocumentMode(True)
307
+ self.setTabPosition(QTabWidget.TabPosition.North)
308
+ self.setMaximumHeight(90) # Reduced from 120
309
+ self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
310
+
311
+ # Store expanded/collapsed state
312
+ self.is_collapsed = False
313
+ self.expanded_height = 90
314
+ self.collapsed_height = 30 # Just tabs, no content
315
+
316
+ # Base styling - ribbon area has distinct background
317
+ self.setStyleSheet("""
318
+ QTabWidget::pane {
319
+ border: 1px solid #CCCCCC;
320
+ border-top: none;
321
+ background-color: #F5F5F5;
322
+ margin: 0px;
323
+ padding: 4px;
324
+ }
325
+ QTabBar {
326
+ background-color: #E0E0E0;
327
+ margin: 0px;
328
+ padding: 0px;
329
+ }
330
+ QTabBar::tab {
331
+ padding: 6px 12px;
332
+ margin: 0px 2px;
333
+ border-top-left-radius: 4px;
334
+ border-top-right-radius: 4px;
335
+ border: none;
336
+ background-color: transparent;
337
+ }
338
+ QTabBar::tab:selected {
339
+ background-color: transparent;
340
+ border: none;
341
+ }
342
+ """)
343
+
344
+ # Store tabs by name for easy access
345
+ self.ribbon_tabs = {}
346
+
347
+ # Connect to tab change to update colors
348
+ self.currentChanged.connect(self._on_tab_changed)
349
+
350
+ # Connect to tab click - if collapsed, temporarily expand
351
+ tab_bar = self.tabBar()
352
+ if tab_bar:
353
+ try:
354
+ tab_bar.tabBarClicked.connect(self._on_tab_clicked)
355
+ except AttributeError:
356
+ # Fallback if signal doesn't exist
357
+ pass
358
+
359
+ # Create collapse/expand button
360
+ self.create_collapse_button()
361
+
362
+ def add_ribbon_tab(self, name: str, tab: RibbonTab):
363
+ """Add a ribbon tab with color coding"""
364
+ self.ribbon_tabs[name] = tab
365
+
366
+ # Apply color to the tab
367
+ tab_color = self.TAB_COLORS.get(name, "#F5F5F5")
368
+ tab.set_tab_color(tab_color)
369
+
370
+ # Add tab
371
+ index = self.addTab(tab, name)
372
+
373
+ # Set color on custom tab bar
374
+ tab_bar = self.tabBar()
375
+ if isinstance(tab_bar, ColoredTabBar):
376
+ tab_bar.set_tab_color(index, tab_color)
377
+
378
+ def apply_initial_colors(self):
379
+ """Apply colors to all tabs after they're all added"""
380
+ # Trigger the tab change handler to apply colors to the first tab
381
+ if self.count() > 0:
382
+ self._on_tab_changed(0)
383
+
384
+ def _on_tab_changed(self, index: int):
385
+ """Update pane color when tab changes"""
386
+ if index < 0 or index >= self.count():
387
+ return
388
+
389
+ # If ribbon is collapsed and user clicked a tab, expand it
390
+ if self.is_collapsed:
391
+ self.is_collapsed = False
392
+ self.setMaximumHeight(self.expanded_height)
393
+ self.setMinimumHeight(0)
394
+ self.collapse_button.setText("▼ Hide")
395
+ self.collapse_button.setToolTip("Hide Ribbon (Ctrl+F1)")
396
+
397
+ tab_name = self.tabText(index)
398
+ tab_color = self.TAB_COLORS.get(tab_name, "#F5F5F5")
399
+
400
+ # Get the active tab widget
401
+ active_tab = self.widget(index)
402
+ if isinstance(active_tab, RibbonTab):
403
+ # Ensure all buttons in the active tab have the correct color
404
+ active_tab._update_all_groups()
405
+ # Show the tab content
406
+ active_tab.setVisible(True)
407
+
408
+ # Update custom tab bar (it will repaint automatically)
409
+ tab_bar = self.tabBar()
410
+ if isinstance(tab_bar, ColoredTabBar):
411
+ tab_bar.update()
412
+
413
+ # Update pane background to match selected tab
414
+ self.setStyleSheet(f"""
415
+ QTabWidget::pane {{
416
+ border: 1px solid #CCCCCC;
417
+ border-top: none;
418
+ background-color: {tab_color};
419
+ margin: 0px;
420
+ padding: 4px;
421
+ }}
422
+ """)
423
+
424
+ # Force update to ensure visibility
425
+ self.update()
426
+ if active_tab:
427
+ active_tab.update()
428
+
429
+ def _darker_color(self, color: str) -> str:
430
+ """Make a color darker for borders"""
431
+ color_rgb = color.lstrip('#')
432
+ r, g, b = tuple(int(color_rgb[i:i+2], 16) for i in (0, 2, 4))
433
+ return f"rgb({max(0, r-50)}, {max(0, g-50)}, {max(0, b-50)})"
434
+
435
+ def _lighter_color(self, color: str) -> str:
436
+ """Make a color lighter for unselected tabs"""
437
+ color_rgb = color.lstrip('#')
438
+ r, g, b = tuple(int(color_rgb[i:i+2], 16) for i in (0, 2, 4))
439
+ # Blend with gray background (E0E0E0 = 224, 224, 224)
440
+ r = int((r + 224) / 2)
441
+ g = int((g + 224) / 2)
442
+ b = int((b + 224) / 2)
443
+ return f"rgb({r}, {g}, {b})"
444
+
445
+ def get_tab(self, name: str) -> RibbonTab:
446
+ """Get a ribbon tab by name"""
447
+ return self.ribbon_tabs.get(name)
448
+
449
+ def create_collapse_button(self):
450
+ """Create a collapse/expand button - more visible and better positioned"""
451
+ # Create button widget - larger and more visible
452
+ self.collapse_button = QPushButton("▼ Hide")
453
+ self.collapse_button.setToolTip("Hide/Show Ribbon (Ctrl+F1)")
454
+ self.collapse_button.setFixedSize(60, 26) # Larger size with text
455
+ self.collapse_button.setStyleSheet("""
456
+ QPushButton {
457
+ border: 1px solid #999;
458
+ background-color: #E8E8E8;
459
+ font-size: 11px;
460
+ font-weight: normal;
461
+ color: #333;
462
+ border-radius: 3px;
463
+ padding: 2px 4px;
464
+ }
465
+ QPushButton:hover {
466
+ background-color: #D8D8D8;
467
+ border: 1px solid #777;
468
+ }
469
+ QPushButton:pressed {
470
+ background-color: #C8C8C8;
471
+ border: 1px solid #555;
472
+ }
473
+ """)
474
+ self.collapse_button.clicked.connect(self.toggle_collapse)
475
+
476
+ # Position it as corner widget but it will be more visible now
477
+ self.setCornerWidget(self.collapse_button, Qt.Corner.TopRightCorner)
478
+
479
+ def toggle_collapse(self):
480
+ """Toggle ribbon between expanded and collapsed states"""
481
+ self.is_collapsed = not self.is_collapsed
482
+
483
+ if self.is_collapsed:
484
+ # Collapse: hide content pane, show only tabs
485
+ self.setMaximumHeight(self.collapsed_height)
486
+ self.setMinimumHeight(self.collapsed_height)
487
+ # Hide the content area (pane)
488
+ for i in range(self.count()):
489
+ widget = self.widget(i)
490
+ if widget:
491
+ widget.setVisible(False)
492
+ self.collapse_button.setText("▲ Show")
493
+ self.collapse_button.setToolTip("Show Ribbon (Ctrl+F1)")
494
+ else:
495
+ # Expand: show full ribbon with content
496
+ self.setMaximumHeight(self.expanded_height)
497
+ self.setMinimumHeight(0)
498
+ # Show the content area for the current tab
499
+ current_index = self.currentIndex()
500
+ if current_index >= 0:
501
+ widget = self.widget(current_index)
502
+ if widget:
503
+ widget.setVisible(True)
504
+ self.collapse_button.setText("▼ Hide")
505
+ self.collapse_button.setToolTip("Hide Ribbon (Ctrl+F1)")
506
+
507
+ def _on_tab_clicked(self, index: int):
508
+ """Handle tab click - if collapsed, temporarily expand"""
509
+ if self.is_collapsed:
510
+ # Temporarily expand to show the selected tab's content
511
+ self.is_collapsed = False # Update state
512
+ self.setMaximumHeight(self.expanded_height)
513
+ self.setMinimumHeight(0)
514
+
515
+ # Show the clicked tab's content
516
+ widget = self.widget(index)
517
+ if widget:
518
+ widget.setVisible(True)
519
+
520
+ # Update button text
521
+ self.collapse_button.setText("▼ Hide")
522
+ self.collapse_button.setToolTip("Hide Ribbon (Ctrl+F1)")
523
+
524
+ # Update colors for the newly selected tab
525
+ if index >= 0 and index < self.count():
526
+ self._on_tab_changed(index)
527
+
528
+ def create_button(self, text: str, emoji: str, action_name: str, tooltip: str = "") -> RibbonButton:
529
+ """Helper to create a ribbon button with action connection"""
530
+ # Create button with emoji as large icon text
531
+ btn = RibbonButton(text, emoji)
532
+
533
+ if tooltip:
534
+ btn.setToolTip(tooltip)
535
+ else:
536
+ btn.setToolTip(text)
537
+
538
+ # Connect to action signal
539
+ btn.clicked.connect(lambda: self.action_triggered.emit(action_name))
540
+
541
+ return btn
542
+
543
+
544
+ class RibbonBuilder:
545
+ """Helper class to build ribbon interfaces declaratively"""
546
+
547
+ @staticmethod
548
+ def build_home_ribbon() -> RibbonTab:
549
+ """Build the Home ribbon tab"""
550
+ tab = RibbonTab()
551
+
552
+ # File group
553
+ file_group = RibbonGroup("File")
554
+ tab.add_group(file_group)
555
+
556
+ # Edit group
557
+ edit_group = RibbonGroup("Edit")
558
+ tab.add_group(edit_group)
559
+
560
+ # View group
561
+ view_group = RibbonGroup("View")
562
+ tab.add_group(view_group)
563
+
564
+ tab.add_stretch()
565
+ return tab
566
+
567
+ @staticmethod
568
+ def build_translation_ribbon() -> RibbonTab:
569
+ """Build the Translation ribbon tab"""
570
+ tab = RibbonTab()
571
+
572
+ # Translate group
573
+ translate_group = RibbonGroup("Translate")
574
+ tab.add_group(translate_group)
575
+
576
+ # Memory group
577
+ memory_group = RibbonGroup("Translation Memory")
578
+ tab.add_group(memory_group)
579
+
580
+ tab.add_stretch()
581
+ return tab
582
+
583
+ @staticmethod
584
+ def build_tools_ribbon() -> RibbonTab:
585
+ """Build the Tools ribbon tab"""
586
+ tab = RibbonTab()
587
+
588
+ # Automation group
589
+ automation_group = RibbonGroup("Automation")
590
+ tab.add_group(automation_group)
591
+
592
+ # Settings group
593
+ settings_group = RibbonGroup("Settings")
594
+ tab.add_group(settings_group)
595
+
596
+ tab.add_stretch()
597
+ return tab