2025-06-03 03:45:33 +08:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
"""
|
|
|
|
|
|
設置分頁組件
|
|
|
|
|
|
============
|
|
|
|
|
|
|
|
|
|
|
|
專門處理應用設置的分頁組件。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from PySide6.QtWidgets import (
|
|
|
|
|
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
2025-06-03 16:43:36 +08:00
|
|
|
|
QComboBox, QRadioButton, QButtonGroup, QMessageBox,
|
|
|
|
|
|
QCheckBox, QPushButton, QFrame
|
2025-06-03 03:45:33 +08:00
|
|
|
|
)
|
2025-06-03 16:43:36 +08:00
|
|
|
|
from PySide6.QtCore import Signal, Qt
|
|
|
|
|
|
from PySide6.QtGui import QFont
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
|
|
|
|
|
from ...i18n import t, get_i18n_manager
|
2025-06-03 16:43:36 +08:00
|
|
|
|
from ...debug import gui_debug_log as debug_log
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SettingsTab(QWidget):
|
|
|
|
|
|
"""設置分頁組件"""
|
|
|
|
|
|
language_changed = Signal()
|
2025-06-03 13:42:59 +08:00
|
|
|
|
layout_change_requested = Signal(bool, str) # 佈局變更請求信號 (combined_mode, orientation)
|
2025-06-03 16:43:36 +08:00
|
|
|
|
reset_requested = Signal() # 重置設定請求信號
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 12:39:03 +08:00
|
|
|
|
def __init__(self, combined_mode: bool, config_manager, parent=None):
|
2025-06-03 03:45:33 +08:00
|
|
|
|
super().__init__(parent)
|
|
|
|
|
|
self.combined_mode = combined_mode
|
2025-06-03 12:39:03 +08:00
|
|
|
|
self.config_manager = config_manager
|
2025-06-03 13:42:59 +08:00
|
|
|
|
self.layout_orientation = self.config_manager.get_layout_orientation()
|
2025-06-03 03:45:33 +08:00
|
|
|
|
self.i18n = get_i18n_manager()
|
2025-06-03 16:43:36 +08:00
|
|
|
|
|
|
|
|
|
|
# 保存需要更新的UI元素引用
|
|
|
|
|
|
self.ui_elements = {}
|
|
|
|
|
|
|
|
|
|
|
|
# 設置全域字體為微軟正黑體
|
|
|
|
|
|
self._setup_font()
|
2025-06-03 03:45:33 +08:00
|
|
|
|
self._setup_ui()
|
2025-06-03 13:42:59 +08:00
|
|
|
|
|
|
|
|
|
|
# 在UI設置完成後,確保正確設置初始狀態
|
|
|
|
|
|
self._set_initial_layout_state()
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
def _setup_font(self) -> None:
|
|
|
|
|
|
"""設置全域字體"""
|
|
|
|
|
|
font = QFont("Microsoft JhengHei", 9) # 微軟正黑體,調整為 9pt
|
|
|
|
|
|
self.setFont(font)
|
|
|
|
|
|
|
|
|
|
|
|
# 設置整個控件的樣式表,確保中文字體正確
|
|
|
|
|
|
self.setStyleSheet("""
|
|
|
|
|
|
QWidget {
|
|
|
|
|
|
font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif;
|
|
|
|
|
|
}
|
|
|
|
|
|
""")
|
|
|
|
|
|
|
2025-06-03 03:45:33 +08:00
|
|
|
|
def _setup_ui(self) -> None:
|
|
|
|
|
|
"""設置用戶介面"""
|
2025-06-03 16:43:36 +08:00
|
|
|
|
# 主容器
|
|
|
|
|
|
main_layout = QHBoxLayout(self)
|
|
|
|
|
|
main_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
|
main_layout.setSpacing(0)
|
|
|
|
|
|
|
|
|
|
|
|
# 左側內容區域
|
|
|
|
|
|
content_widget = QWidget()
|
|
|
|
|
|
content_widget.setMaximumWidth(600)
|
|
|
|
|
|
content_layout = QVBoxLayout(content_widget)
|
|
|
|
|
|
content_layout.setContentsMargins(20, 20, 20, 20)
|
|
|
|
|
|
content_layout.setSpacing(16)
|
|
|
|
|
|
|
|
|
|
|
|
# === 語言設置 ===
|
|
|
|
|
|
self._create_language_section(content_layout)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加分隔線
|
|
|
|
|
|
self._add_separator(content_layout)
|
|
|
|
|
|
|
|
|
|
|
|
# === 界面佈局 ===
|
|
|
|
|
|
self._create_layout_section(content_layout)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加分隔線
|
|
|
|
|
|
self._add_separator(content_layout)
|
|
|
|
|
|
|
|
|
|
|
|
# === 視窗設置 ===
|
|
|
|
|
|
self._create_window_section(content_layout)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加分隔線
|
|
|
|
|
|
self._add_separator(content_layout)
|
|
|
|
|
|
|
|
|
|
|
|
# === 重置設定 ===
|
|
|
|
|
|
self._create_reset_section(content_layout)
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
# 添加彈性空間
|
|
|
|
|
|
content_layout.addStretch()
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
# 添加到主布局
|
|
|
|
|
|
main_layout.addWidget(content_widget)
|
|
|
|
|
|
main_layout.addStretch() # 右側彈性空間
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
# 設定初始狀態
|
|
|
|
|
|
self._set_initial_layout_state()
|
|
|
|
|
|
|
|
|
|
|
|
def _add_separator(self, layout: QVBoxLayout) -> None:
|
|
|
|
|
|
"""添加分隔線"""
|
|
|
|
|
|
separator = QFrame()
|
|
|
|
|
|
separator.setFrameShape(QFrame.HLine)
|
|
|
|
|
|
separator.setStyleSheet("""
|
|
|
|
|
|
QFrame {
|
|
|
|
|
|
color: #444444;
|
|
|
|
|
|
background-color: #444444;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
height: 1px;
|
|
|
|
|
|
margin: 6px 0px;
|
|
|
|
|
|
}
|
|
|
|
|
|
""")
|
|
|
|
|
|
layout.addWidget(separator)
|
|
|
|
|
|
|
|
|
|
|
|
def _create_section_header(self, title: str, emoji: str = "") -> QLabel:
|
|
|
|
|
|
"""創建區塊標題"""
|
|
|
|
|
|
text = f"{emoji} {title}" if emoji else title
|
|
|
|
|
|
label = QLabel(text)
|
|
|
|
|
|
label.setStyleSheet("""
|
|
|
|
|
|
QLabel {
|
|
|
|
|
|
font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif;
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
|
margin-top: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
""")
|
|
|
|
|
|
return label
|
|
|
|
|
|
|
|
|
|
|
|
def _create_description(self, text: str) -> QLabel:
|
|
|
|
|
|
"""創建說明文字"""
|
|
|
|
|
|
label = QLabel(text)
|
|
|
|
|
|
label.setStyleSheet("""
|
|
|
|
|
|
QLabel {
|
|
|
|
|
|
font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif;
|
|
|
|
|
|
color: #aaaaaa;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
|
}
|
|
|
|
|
|
""")
|
|
|
|
|
|
label.setWordWrap(True)
|
|
|
|
|
|
return label
|
|
|
|
|
|
|
|
|
|
|
|
def _create_language_section(self, layout: QVBoxLayout) -> None:
|
|
|
|
|
|
"""創建語言設置區域"""
|
|
|
|
|
|
header = self._create_section_header(t('settings.language.title'), "🌐")
|
|
|
|
|
|
layout.addWidget(header)
|
|
|
|
|
|
# 保存引用以便更新
|
|
|
|
|
|
self.ui_elements['language_header'] = header
|
|
|
|
|
|
|
|
|
|
|
|
desc = self._create_description(t('settings.language.description'))
|
|
|
|
|
|
layout.addWidget(desc)
|
|
|
|
|
|
# 保存引用以便更新
|
|
|
|
|
|
self.ui_elements['language_desc'] = desc
|
|
|
|
|
|
|
|
|
|
|
|
# 語言選擇器容器
|
|
|
|
|
|
lang_container = QHBoxLayout()
|
|
|
|
|
|
lang_container.setContentsMargins(0, 0, 0, 0)
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
|
|
|
|
|
self.language_selector = QComboBox()
|
2025-06-03 16:43:36 +08:00
|
|
|
|
self.language_selector.setMinimumHeight(28)
|
|
|
|
|
|
self.language_selector.setMaximumWidth(140)
|
2025-06-03 03:45:33 +08:00
|
|
|
|
self.language_selector.setStyleSheet("""
|
|
|
|
|
|
QComboBox {
|
2025-06-03 16:43:36 +08:00
|
|
|
|
font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif;
|
|
|
|
|
|
background-color: #3a3a3a;
|
|
|
|
|
|
border: 1px solid #555555;
|
2025-06-03 03:45:33 +08:00
|
|
|
|
border-radius: 4px;
|
2025-06-03 16:43:36 +08:00
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
|
font-size: 12px;
|
2025-06-03 03:45:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
QComboBox:hover {
|
|
|
|
|
|
border-color: #0078d4;
|
|
|
|
|
|
}
|
|
|
|
|
|
QComboBox::drop-down {
|
|
|
|
|
|
border: none;
|
2025-06-03 16:43:36 +08:00
|
|
|
|
width: 20px;
|
2025-06-03 03:45:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
QComboBox::down-arrow {
|
2025-06-03 16:43:36 +08:00
|
|
|
|
image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIHZpZXdCb3g9IjAgMCAxMiAxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTMgNEw2IDdMOSA0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPg==);
|
|
|
|
|
|
width: 12px;
|
|
|
|
|
|
height: 12px;
|
2025-06-03 03:45:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
QComboBox QAbstractItemView {
|
2025-06-03 16:43:36 +08:00
|
|
|
|
background-color: #3a3a3a;
|
|
|
|
|
|
border: 1px solid #555555;
|
2025-06-03 03:45:33 +08:00
|
|
|
|
selection-background-color: #0078d4;
|
2025-06-03 16:43:36 +08:00
|
|
|
|
color: #ffffff;
|
|
|
|
|
|
font-size: 12px;
|
2025-06-03 03:45:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
""")
|
|
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
# 填充語言選項
|
2025-06-03 03:45:33 +08:00
|
|
|
|
self._populate_language_selector()
|
|
|
|
|
|
self.language_selector.currentIndexChanged.connect(self._on_language_changed)
|
|
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
lang_container.addWidget(self.language_selector)
|
|
|
|
|
|
lang_container.addStretch()
|
|
|
|
|
|
layout.addLayout(lang_container)
|
|
|
|
|
|
|
|
|
|
|
|
def _create_layout_section(self, layout: QVBoxLayout) -> None:
|
|
|
|
|
|
"""創建界面佈局區域"""
|
|
|
|
|
|
header = self._create_section_header(t('settings.layout.title'), "📐")
|
|
|
|
|
|
layout.addWidget(header)
|
|
|
|
|
|
# 保存引用以便更新
|
|
|
|
|
|
self.ui_elements['layout_header'] = header
|
|
|
|
|
|
|
|
|
|
|
|
desc = self._create_description(t('settings.layout.description'))
|
|
|
|
|
|
layout.addWidget(desc)
|
|
|
|
|
|
# 保存引用以便更新
|
|
|
|
|
|
self.ui_elements['layout_desc'] = desc
|
|
|
|
|
|
|
|
|
|
|
|
# 選項容器
|
|
|
|
|
|
options_layout = QVBoxLayout()
|
|
|
|
|
|
options_layout.setSpacing(2)
|
|
|
|
|
|
|
|
|
|
|
|
# 創建按鈕組
|
2025-06-03 03:45:33 +08:00
|
|
|
|
self.layout_button_group = QButtonGroup()
|
|
|
|
|
|
|
|
|
|
|
|
# 分離模式
|
|
|
|
|
|
self.separate_mode_radio = QRadioButton(t('settings.layout.separateMode'))
|
2025-06-03 16:43:36 +08:00
|
|
|
|
self.separate_mode_radio.setStyleSheet("""
|
|
|
|
|
|
QRadioButton {
|
|
|
|
|
|
font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
|
spacing: 8px;
|
|
|
|
|
|
padding: 2px 0px;
|
|
|
|
|
|
}
|
|
|
|
|
|
QRadioButton::indicator {
|
|
|
|
|
|
width: 16px;
|
|
|
|
|
|
height: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
QRadioButton::indicator:unchecked {
|
|
|
|
|
|
border: 2px solid #666666;
|
|
|
|
|
|
border-radius: 9px;
|
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
QRadioButton::indicator:checked {
|
|
|
|
|
|
border: 2px solid #0078d4;
|
|
|
|
|
|
border-radius: 9px;
|
|
|
|
|
|
background-color: #0078d4;
|
|
|
|
|
|
image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSI4IiB2aWV3Qm94PSIwIDAgOCA4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8Y2lyY2xlIGN4PSI0IiBjeT0iNCIgcj0iMiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+);
|
|
|
|
|
|
}
|
|
|
|
|
|
QRadioButton::indicator:hover {
|
|
|
|
|
|
border-color: #0078d4;
|
|
|
|
|
|
}
|
|
|
|
|
|
""")
|
2025-06-03 03:45:33 +08:00
|
|
|
|
self.layout_button_group.addButton(self.separate_mode_radio, 0)
|
2025-06-03 16:43:36 +08:00
|
|
|
|
options_layout.addWidget(self.separate_mode_radio)
|
|
|
|
|
|
|
|
|
|
|
|
separate_hint = QLabel(f" {t('settings.layout.separateModeDescription')}")
|
|
|
|
|
|
separate_hint.setStyleSheet("""
|
|
|
|
|
|
QLabel {
|
|
|
|
|
|
font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif;
|
|
|
|
|
|
color: #888888;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
margin-left: 20px;
|
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
""")
|
|
|
|
|
|
options_layout.addWidget(separate_hint)
|
|
|
|
|
|
# 保存引用以便更新
|
|
|
|
|
|
self.ui_elements['separate_hint'] = separate_hint
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
# 合併模式(垂直)
|
2025-06-03 13:42:59 +08:00
|
|
|
|
self.combined_vertical_radio = QRadioButton(t('settings.layout.combinedVertical'))
|
2025-06-03 16:43:36 +08:00
|
|
|
|
self.combined_vertical_radio.setStyleSheet(self.separate_mode_radio.styleSheet())
|
2025-06-03 13:42:59 +08:00
|
|
|
|
self.layout_button_group.addButton(self.combined_vertical_radio, 1)
|
2025-06-03 16:43:36 +08:00
|
|
|
|
options_layout.addWidget(self.combined_vertical_radio)
|
2025-06-03 13:42:59 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
vertical_hint = QLabel(f" {t('settings.layout.combinedVerticalDescription')}")
|
|
|
|
|
|
vertical_hint.setStyleSheet(separate_hint.styleSheet())
|
|
|
|
|
|
options_layout.addWidget(vertical_hint)
|
|
|
|
|
|
# 保存引用以便更新
|
|
|
|
|
|
self.ui_elements['vertical_hint'] = vertical_hint
|
2025-06-03 13:42:59 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
# 合併模式(水平)
|
2025-06-03 13:42:59 +08:00
|
|
|
|
self.combined_horizontal_radio = QRadioButton(t('settings.layout.combinedHorizontal'))
|
2025-06-03 16:43:36 +08:00
|
|
|
|
self.combined_horizontal_radio.setStyleSheet(self.separate_mode_radio.styleSheet())
|
2025-06-03 13:42:59 +08:00
|
|
|
|
self.layout_button_group.addButton(self.combined_horizontal_radio, 2)
|
2025-06-03 16:43:36 +08:00
|
|
|
|
options_layout.addWidget(self.combined_horizontal_radio)
|
|
|
|
|
|
|
|
|
|
|
|
horizontal_hint = QLabel(f" {t('settings.layout.combinedHorizontalDescription')}")
|
|
|
|
|
|
horizontal_hint.setStyleSheet(separate_hint.styleSheet())
|
|
|
|
|
|
options_layout.addWidget(horizontal_hint)
|
|
|
|
|
|
# 保存引用以便更新
|
|
|
|
|
|
self.ui_elements['horizontal_hint'] = horizontal_hint
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
layout.addLayout(options_layout)
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 13:42:59 +08:00
|
|
|
|
# 連接佈局變更信號
|
|
|
|
|
|
self.layout_button_group.buttonToggled.connect(self._on_layout_changed)
|
2025-06-03 16:43:36 +08:00
|
|
|
|
|
|
|
|
|
|
def _create_window_section(self, layout: QVBoxLayout) -> None:
|
|
|
|
|
|
"""創建視窗設置區域"""
|
|
|
|
|
|
header = self._create_section_header(t('settings.window.title'), "🖥️")
|
|
|
|
|
|
layout.addWidget(header)
|
|
|
|
|
|
# 保存引用以便更新
|
|
|
|
|
|
self.ui_elements['window_header'] = header
|
|
|
|
|
|
|
|
|
|
|
|
desc = self._create_description(t('settings.window.alwaysCenterDescription'))
|
|
|
|
|
|
layout.addWidget(desc)
|
|
|
|
|
|
# 保存引用以便更新
|
|
|
|
|
|
self.ui_elements['window_desc'] = desc
|
|
|
|
|
|
|
|
|
|
|
|
# 選項容器
|
|
|
|
|
|
options_layout = QVBoxLayout()
|
|
|
|
|
|
options_layout.setSpacing(2)
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 12:39:03 +08:00
|
|
|
|
self.always_center_checkbox = QCheckBox(t('settings.window.alwaysCenter'))
|
|
|
|
|
|
self.always_center_checkbox.setChecked(self.config_manager.get_always_center_window())
|
2025-06-03 16:43:36 +08:00
|
|
|
|
self.always_center_checkbox.setStyleSheet("""
|
|
|
|
|
|
QCheckBox {
|
|
|
|
|
|
font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
|
spacing: 8px;
|
|
|
|
|
|
padding: 2px 0px;
|
|
|
|
|
|
}
|
|
|
|
|
|
QCheckBox::indicator {
|
|
|
|
|
|
width: 16px;
|
|
|
|
|
|
height: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
QCheckBox::indicator:unchecked {
|
|
|
|
|
|
border: 2px solid #666666;
|
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
QCheckBox::indicator:checked {
|
|
|
|
|
|
border: 2px solid #0078d4;
|
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
|
background-color: #0078d4;
|
|
|
|
|
|
image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIHZpZXdCb3g9IjAgMCAxMiAxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEwIDNMNC41IDguNUwyIDYiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9zdmc+);
|
|
|
|
|
|
}
|
|
|
|
|
|
QCheckBox::indicator:hover {
|
|
|
|
|
|
border-color: #0078d4;
|
|
|
|
|
|
}
|
|
|
|
|
|
""")
|
2025-06-03 12:39:03 +08:00
|
|
|
|
self.always_center_checkbox.stateChanged.connect(self._on_always_center_changed)
|
2025-06-03 16:43:36 +08:00
|
|
|
|
options_layout.addWidget(self.always_center_checkbox)
|
|
|
|
|
|
|
|
|
|
|
|
center_hint = QLabel(f" {t('settings.window.alwaysCenterDescription')}")
|
|
|
|
|
|
center_hint.setStyleSheet("""
|
|
|
|
|
|
QLabel {
|
|
|
|
|
|
font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif;
|
|
|
|
|
|
color: #888888;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
margin-left: 20px;
|
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
""")
|
|
|
|
|
|
options_layout.addWidget(center_hint)
|
|
|
|
|
|
# 保存引用以便更新
|
|
|
|
|
|
self.ui_elements['center_hint'] = center_hint
|
2025-06-03 12:39:03 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
layout.addLayout(options_layout)
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
def _create_reset_section(self, layout: QVBoxLayout) -> None:
|
|
|
|
|
|
"""創建重置設定區域"""
|
|
|
|
|
|
header = self._create_section_header(t('settings.reset.title'), "🔄")
|
|
|
|
|
|
layout.addWidget(header)
|
|
|
|
|
|
# 保存引用以便更新
|
|
|
|
|
|
self.ui_elements['reset_header'] = header
|
|
|
|
|
|
|
|
|
|
|
|
desc = self._create_description(t('settings.reset.description'))
|
|
|
|
|
|
layout.addWidget(desc)
|
|
|
|
|
|
# 保存引用以便更新
|
|
|
|
|
|
self.ui_elements['reset_desc'] = desc
|
|
|
|
|
|
|
|
|
|
|
|
reset_container = QHBoxLayout()
|
|
|
|
|
|
reset_container.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
|
|
|
|
|
|
|
self.reset_button = QPushButton(t('settings.reset.button'))
|
|
|
|
|
|
self.reset_button.setMinimumHeight(32)
|
|
|
|
|
|
self.reset_button.setMaximumWidth(110)
|
|
|
|
|
|
self.reset_button.setStyleSheet("""
|
|
|
|
|
|
QPushButton {
|
|
|
|
|
|
font-family: "Microsoft JhengHei", "微軟正黑體", sans-serif;
|
|
|
|
|
|
background-color: #dc3545;
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
padding: 6px 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
QPushButton:hover {
|
|
|
|
|
|
background-color: #e55565;
|
|
|
|
|
|
}
|
|
|
|
|
|
QPushButton:pressed {
|
|
|
|
|
|
background-color: #c82333;
|
|
|
|
|
|
}
|
|
|
|
|
|
""")
|
|
|
|
|
|
self.reset_button.clicked.connect(self._on_reset_settings)
|
|
|
|
|
|
|
|
|
|
|
|
reset_container.addWidget(self.reset_button)
|
|
|
|
|
|
reset_container.addStretch()
|
|
|
|
|
|
layout.addLayout(reset_container)
|
|
|
|
|
|
|
2025-06-03 03:45:33 +08:00
|
|
|
|
def _populate_language_selector(self) -> None:
|
|
|
|
|
|
"""填充語言選擇器"""
|
2025-06-03 16:43:36 +08:00
|
|
|
|
languages = [
|
|
|
|
|
|
('zh-TW', '繁體中文'),
|
|
|
|
|
|
('zh-CN', '简体中文'),
|
|
|
|
|
|
('en', 'English')
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
current_language = self.i18n.get_current_language()
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
# 暫時斷開信號連接以避免觸發變更事件
|
|
|
|
|
|
self.language_selector.blockSignals(True)
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
# 先清空現有選項
|
2025-06-03 03:45:33 +08:00
|
|
|
|
self.language_selector.clear()
|
|
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
for i, (code, name) in enumerate(languages):
|
|
|
|
|
|
self.language_selector.addItem(name, code)
|
|
|
|
|
|
if code == current_language:
|
2025-06-03 03:45:33 +08:00
|
|
|
|
self.language_selector.setCurrentIndex(i)
|
|
|
|
|
|
|
|
|
|
|
|
# 重新連接信號
|
2025-06-03 16:43:36 +08:00
|
|
|
|
self.language_selector.blockSignals(False)
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
|
|
|
|
|
def _on_language_changed(self, index: int) -> None:
|
2025-06-03 16:43:36 +08:00
|
|
|
|
"""語言變更事件處理"""
|
|
|
|
|
|
if index < 0:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
language_code = self.language_selector.itemData(index)
|
|
|
|
|
|
if language_code and language_code != self.i18n.get_current_language():
|
|
|
|
|
|
# 先保存語言設定
|
|
|
|
|
|
self.config_manager.set_language(language_code)
|
|
|
|
|
|
# 再設定語言
|
|
|
|
|
|
self.i18n.set_language(language_code)
|
|
|
|
|
|
# 發出信號
|
2025-06-03 03:45:33 +08:00
|
|
|
|
self.language_changed.emit()
|
|
|
|
|
|
|
2025-06-03 13:42:59 +08:00
|
|
|
|
def _on_layout_changed(self, button, checked: bool) -> None:
|
2025-06-03 16:43:36 +08:00
|
|
|
|
"""佈局變更事件處理"""
|
|
|
|
|
|
if not checked:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
button_id = self.layout_button_group.id(button)
|
|
|
|
|
|
|
|
|
|
|
|
if button_id == 0: # 分離模式
|
|
|
|
|
|
new_combined_mode = False
|
|
|
|
|
|
new_orientation = 'vertical'
|
|
|
|
|
|
elif button_id == 1: # 合併模式(垂直)
|
|
|
|
|
|
new_combined_mode = True
|
|
|
|
|
|
new_orientation = 'vertical'
|
|
|
|
|
|
elif button_id == 2: # 合併模式(水平)
|
|
|
|
|
|
new_combined_mode = True
|
|
|
|
|
|
new_orientation = 'horizontal'
|
|
|
|
|
|
else:
|
2025-06-03 03:45:33 +08:00
|
|
|
|
return
|
|
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
# 檢查是否真的有變更
|
2025-06-03 13:42:59 +08:00
|
|
|
|
if new_combined_mode != self.combined_mode or new_orientation != self.layout_orientation:
|
2025-06-03 18:55:42 +08:00
|
|
|
|
# 批量保存配置(避免多次寫入文件)
|
|
|
|
|
|
self.config_manager.update_partial_config({
|
|
|
|
|
|
'combined_mode': new_combined_mode,
|
|
|
|
|
|
'layout_orientation': new_orientation
|
|
|
|
|
|
})
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
# 更新內部狀態
|
|
|
|
|
|
self.combined_mode = new_combined_mode
|
|
|
|
|
|
self.layout_orientation = new_orientation
|
|
|
|
|
|
|
|
|
|
|
|
# 發出佈局變更請求信號
|
|
|
|
|
|
self.layout_change_requested.emit(new_combined_mode, new_orientation)
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
2025-06-03 12:39:03 +08:00
|
|
|
|
def _on_always_center_changed(self, state: int) -> None:
|
2025-06-03 16:43:36 +08:00
|
|
|
|
"""視窗定位選項變更事件處理"""
|
|
|
|
|
|
always_center = state == Qt.CheckState.Checked.value
|
|
|
|
|
|
# 立即保存設定
|
2025-06-03 12:39:03 +08:00
|
|
|
|
self.config_manager.set_always_center_window(always_center)
|
2025-06-03 16:43:36 +08:00
|
|
|
|
debug_log(f"視窗定位設置已保存: {always_center}") # 調試輸出
|
|
|
|
|
|
|
|
|
|
|
|
def _on_reset_settings(self) -> None:
|
|
|
|
|
|
"""重置設定事件處理"""
|
|
|
|
|
|
reply = QMessageBox.question(
|
|
|
|
|
|
self,
|
|
|
|
|
|
t('settings.reset.confirmTitle'),
|
|
|
|
|
|
t('settings.reset.confirmMessage'),
|
|
|
|
|
|
QMessageBox.Yes | QMessageBox.No,
|
|
|
|
|
|
QMessageBox.No
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if reply == QMessageBox.Yes:
|
|
|
|
|
|
self.reset_requested.emit()
|
2025-06-03 12:39:03 +08:00
|
|
|
|
|
2025-06-03 03:45:33 +08:00
|
|
|
|
def update_texts(self) -> None:
|
2025-06-03 16:43:36 +08:00
|
|
|
|
"""更新界面文字(不重新創建界面)"""
|
|
|
|
|
|
# 更新區塊標題
|
|
|
|
|
|
if 'language_header' in self.ui_elements:
|
|
|
|
|
|
self.ui_elements['language_header'].setText(f"🌐 {t('settings.language.title')}")
|
|
|
|
|
|
if 'layout_header' in self.ui_elements:
|
|
|
|
|
|
self.ui_elements['layout_header'].setText(f"📐 {t('settings.layout.title')}")
|
|
|
|
|
|
if 'window_header' in self.ui_elements:
|
|
|
|
|
|
self.ui_elements['window_header'].setText(f"🖥️ {t('settings.window.title')}")
|
|
|
|
|
|
if 'reset_header' in self.ui_elements:
|
|
|
|
|
|
self.ui_elements['reset_header'].setText(f"🔄 {t('settings.reset.title')}")
|
|
|
|
|
|
|
|
|
|
|
|
# 更新描述文字
|
|
|
|
|
|
if 'language_desc' in self.ui_elements:
|
|
|
|
|
|
self.ui_elements['language_desc'].setText(t('settings.language.description'))
|
|
|
|
|
|
if 'layout_desc' in self.ui_elements:
|
|
|
|
|
|
self.ui_elements['layout_desc'].setText(t('settings.layout.description'))
|
|
|
|
|
|
if 'window_desc' in self.ui_elements:
|
|
|
|
|
|
self.ui_elements['window_desc'].setText(t('settings.window.alwaysCenterDescription'))
|
|
|
|
|
|
if 'reset_desc' in self.ui_elements:
|
|
|
|
|
|
self.ui_elements['reset_desc'].setText(t('settings.reset.description'))
|
|
|
|
|
|
|
|
|
|
|
|
# 更新提示文字
|
|
|
|
|
|
if 'separate_hint' in self.ui_elements:
|
|
|
|
|
|
self.ui_elements['separate_hint'].setText(f" {t('settings.layout.separateModeDescription')}")
|
|
|
|
|
|
if 'vertical_hint' in self.ui_elements:
|
|
|
|
|
|
self.ui_elements['vertical_hint'].setText(f" {t('settings.layout.combinedVerticalDescription')}")
|
|
|
|
|
|
if 'horizontal_hint' in self.ui_elements:
|
|
|
|
|
|
self.ui_elements['horizontal_hint'].setText(f" {t('settings.layout.combinedHorizontalDescription')}")
|
|
|
|
|
|
if 'center_hint' in self.ui_elements:
|
|
|
|
|
|
self.ui_elements['center_hint'].setText(f" {t('settings.window.alwaysCenterDescription')}")
|
|
|
|
|
|
|
|
|
|
|
|
# 更新按鈕文字
|
|
|
|
|
|
if hasattr(self, 'reset_button'):
|
|
|
|
|
|
self.reset_button.setText(t('settings.reset.button'))
|
|
|
|
|
|
|
|
|
|
|
|
# 更新複選框文字
|
|
|
|
|
|
if hasattr(self, 'always_center_checkbox'):
|
|
|
|
|
|
self.always_center_checkbox.setText(t('settings.window.alwaysCenter'))
|
|
|
|
|
|
|
|
|
|
|
|
# 更新單選按鈕文字
|
|
|
|
|
|
if hasattr(self, 'separate_mode_radio'):
|
|
|
|
|
|
self.separate_mode_radio.setText(t('settings.layout.separateMode'))
|
|
|
|
|
|
if hasattr(self, 'combined_vertical_radio'):
|
|
|
|
|
|
self.combined_vertical_radio.setText(t('settings.layout.combinedVertical'))
|
|
|
|
|
|
if hasattr(self, 'combined_horizontal_radio'):
|
|
|
|
|
|
self.combined_horizontal_radio.setText(t('settings.layout.combinedHorizontal'))
|
|
|
|
|
|
|
|
|
|
|
|
# 注意:不要重新填充語言選擇器,避免重複選項問題
|
|
|
|
|
|
|
|
|
|
|
|
def reload_settings_from_config(self) -> None:
|
|
|
|
|
|
"""從配置重新載入設定狀態"""
|
|
|
|
|
|
# 重新載入語言設定
|
|
|
|
|
|
if hasattr(self, 'language_selector'):
|
|
|
|
|
|
self._populate_language_selector()
|
|
|
|
|
|
|
|
|
|
|
|
# 重新載入佈局設定
|
|
|
|
|
|
self.combined_mode = self.config_manager.get_layout_mode()
|
|
|
|
|
|
self.layout_orientation = self.config_manager.get_layout_orientation()
|
|
|
|
|
|
self._set_initial_layout_state()
|
2025-06-03 12:39:03 +08:00
|
|
|
|
|
2025-06-03 16:43:36 +08:00
|
|
|
|
# 重新載入視窗設定
|
|
|
|
|
|
if hasattr(self, 'always_center_checkbox'):
|
|
|
|
|
|
always_center = self.config_manager.get_always_center_window()
|
|
|
|
|
|
self.always_center_checkbox.setChecked(always_center)
|
|
|
|
|
|
debug_log(f"重新載入視窗定位設置: {always_center}") # 調試輸出
|
2025-06-03 03:45:33 +08:00
|
|
|
|
|
|
|
|
|
|
def set_layout_mode(self, combined_mode: bool) -> None:
|
|
|
|
|
|
"""設置佈局模式"""
|
|
|
|
|
|
self.combined_mode = combined_mode
|
2025-06-03 16:43:36 +08:00
|
|
|
|
self._set_initial_layout_state()
|
2025-06-03 13:42:59 +08:00
|
|
|
|
|
|
|
|
|
|
def set_layout_orientation(self, orientation: str) -> None:
|
|
|
|
|
|
"""設置佈局方向"""
|
|
|
|
|
|
self.layout_orientation = orientation
|
2025-06-03 16:43:36 +08:00
|
|
|
|
self._set_initial_layout_state()
|
2025-06-03 13:42:59 +08:00
|
|
|
|
|
|
|
|
|
|
def _set_initial_layout_state(self) -> None:
|
|
|
|
|
|
"""設置初始佈局狀態"""
|
2025-06-03 16:43:36 +08:00
|
|
|
|
if hasattr(self, 'separate_mode_radio'):
|
|
|
|
|
|
# 暫時斷開信號連接以避免觸發變更事件
|
|
|
|
|
|
self.layout_button_group.blockSignals(True)
|
|
|
|
|
|
|
|
|
|
|
|
if not self.combined_mode:
|
|
|
|
|
|
self.separate_mode_radio.setChecked(True)
|
|
|
|
|
|
elif self.layout_orientation == 'vertical':
|
2025-06-03 13:42:59 +08:00
|
|
|
|
self.combined_vertical_radio.setChecked(True)
|
2025-06-03 16:43:36 +08:00
|
|
|
|
else:
|
2025-06-03 13:42:59 +08:00
|
|
|
|
self.combined_horizontal_radio.setChecked(True)
|
2025-06-03 16:43:36 +08:00
|
|
|
|
|
|
|
|
|
|
# 重新連接信號
|
|
|
|
|
|
self.layout_button_group.blockSignals(False)
|