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.
- Supervertaler.py +47886 -0
- modules/__init__.py +10 -0
- modules/ai_actions.py +964 -0
- modules/ai_attachment_manager.py +343 -0
- modules/ai_file_viewer_dialog.py +210 -0
- modules/autofingers_engine.py +466 -0
- modules/cafetran_docx_handler.py +379 -0
- modules/config_manager.py +469 -0
- modules/database_manager.py +1878 -0
- modules/database_migrations.py +417 -0
- modules/dejavurtf_handler.py +779 -0
- modules/document_analyzer.py +427 -0
- modules/docx_handler.py +689 -0
- modules/encoding_repair.py +319 -0
- modules/encoding_repair_Qt.py +393 -0
- modules/encoding_repair_ui.py +481 -0
- modules/feature_manager.py +350 -0
- modules/figure_context_manager.py +340 -0
- modules/file_dialog_helper.py +148 -0
- modules/find_replace.py +164 -0
- modules/find_replace_qt.py +457 -0
- modules/glossary_manager.py +433 -0
- modules/image_extractor.py +188 -0
- modules/keyboard_shortcuts_widget.py +571 -0
- modules/llm_clients.py +1211 -0
- modules/llm_leaderboard.py +737 -0
- modules/llm_superbench_ui.py +1401 -0
- modules/local_llm_setup.py +1104 -0
- modules/model_update_dialog.py +381 -0
- modules/model_version_checker.py +373 -0
- modules/mqxliff_handler.py +638 -0
- modules/non_translatables_manager.py +743 -0
- modules/pdf_rescue_Qt.py +1822 -0
- modules/pdf_rescue_tkinter.py +909 -0
- modules/phrase_docx_handler.py +516 -0
- modules/project_home_panel.py +209 -0
- modules/prompt_assistant.py +357 -0
- modules/prompt_library.py +689 -0
- modules/prompt_library_migration.py +447 -0
- modules/quick_access_sidebar.py +282 -0
- modules/ribbon_widget.py +597 -0
- modules/sdlppx_handler.py +874 -0
- modules/setup_wizard.py +353 -0
- modules/shortcut_manager.py +932 -0
- modules/simple_segmenter.py +128 -0
- modules/spellcheck_manager.py +727 -0
- modules/statuses.py +207 -0
- modules/style_guide_manager.py +315 -0
- modules/superbench_ui.py +1319 -0
- modules/superbrowser.py +329 -0
- modules/supercleaner.py +600 -0
- modules/supercleaner_ui.py +444 -0
- modules/superdocs.py +19 -0
- modules/superdocs_viewer_qt.py +382 -0
- modules/superlookup.py +252 -0
- modules/tag_cleaner.py +260 -0
- modules/tag_manager.py +333 -0
- modules/term_extractor.py +270 -0
- modules/termbase_entry_editor.py +842 -0
- modules/termbase_import_export.py +488 -0
- modules/termbase_manager.py +1060 -0
- modules/termview_widget.py +1172 -0
- modules/theme_manager.py +499 -0
- modules/tm_editor_dialog.py +99 -0
- modules/tm_manager_qt.py +1280 -0
- modules/tm_metadata_manager.py +545 -0
- modules/tmx_editor.py +1461 -0
- modules/tmx_editor_qt.py +2784 -0
- modules/tmx_generator.py +284 -0
- modules/tracked_changes.py +900 -0
- modules/trados_docx_handler.py +430 -0
- modules/translation_memory.py +715 -0
- modules/translation_results_panel.py +2134 -0
- modules/translation_services.py +282 -0
- modules/unified_prompt_library.py +659 -0
- modules/unified_prompt_manager_qt.py +3951 -0
- modules/voice_commands.py +920 -0
- modules/voice_dictation.py +477 -0
- modules/voice_dictation_lite.py +249 -0
- supervertaler-1.9.153.dist-info/METADATA +896 -0
- supervertaler-1.9.153.dist-info/RECORD +85 -0
- supervertaler-1.9.153.dist-info/WHEEL +5 -0
- supervertaler-1.9.153.dist-info/entry_points.txt +2 -0
- supervertaler-1.9.153.dist-info/licenses/LICENSE +21 -0
- supervertaler-1.9.153.dist-info/top_level.txt +2 -0
modules/ribbon_widget.py
ADDED
|
@@ -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
|