mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 02:22:26 +08:00
✨ Add auto-focus to input box when feedback window opens
• Automatically focus on the feedback input box when window appears • Improves user experience by allowing immediate text input • Added smart focus detection for both combined and separate modes • Implemented 300ms delay to ensure complete UI initialization • Enhanced debugging and error handling for focus functionality Resolves: Input box requires manual click before typing Feature: Auto-focus enhancement for better UX
This commit is contained in:
parent
462ed934a3
commit
747270b43a
@ -33,64 +33,70 @@ class FeedbackWindow(QMainWindow):
|
||||
self.result = None
|
||||
self.i18n = get_i18n_manager()
|
||||
self.mcp_timeout_seconds = timeout_seconds # MCP 傳入的超時時間
|
||||
|
||||
|
||||
# 初始化組件
|
||||
self.config_manager = ConfigManager()
|
||||
|
||||
|
||||
# 載入保存的語言設定
|
||||
saved_language = self.config_manager.get_language()
|
||||
if saved_language:
|
||||
self.i18n.set_language(saved_language)
|
||||
|
||||
|
||||
self.combined_mode = self.config_manager.get_layout_mode()
|
||||
self.layout_orientation = self.config_manager.get_layout_orientation()
|
||||
|
||||
|
||||
# 設置窗口狀態保存的防抖計時器
|
||||
self._save_timer = QTimer()
|
||||
self._save_timer.setSingleShot(True)
|
||||
self._save_timer.timeout.connect(self._delayed_save_window_position)
|
||||
self._save_delay = 500 # 500ms 延遲,避免過於頻繁的保存
|
||||
|
||||
|
||||
# 設置UI
|
||||
self._setup_ui()
|
||||
self._setup_shortcuts()
|
||||
self._connect_signals()
|
||||
|
||||
|
||||
debug_log("主窗口初始化完成")
|
||||
|
||||
# 如果啟用了超時,自動開始倒數計時
|
||||
self.start_timeout_if_enabled()
|
||||
|
||||
|
||||
# 設置定時器在窗口顯示後自動聚焦到輸入框
|
||||
self._focus_timer = QTimer()
|
||||
self._focus_timer.setSingleShot(True)
|
||||
self._focus_timer.timeout.connect(self._auto_focus_input)
|
||||
self._focus_timer.start(300) # 延遲300ms確保窗口和UI元素完全加載
|
||||
|
||||
def _setup_ui(self) -> None:
|
||||
"""設置用戶介面"""
|
||||
self.setWindowTitle(t('app.title'))
|
||||
self.setMinimumSize(400, 300) # 大幅降低最小窗口大小限制,允許用戶自由調整
|
||||
self.resize(1200, 900)
|
||||
|
||||
|
||||
# 智能視窗定位
|
||||
self._apply_window_positioning()
|
||||
|
||||
|
||||
# 中央元件
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
|
||||
# 主布局
|
||||
main_layout = QVBoxLayout(central_widget)
|
||||
main_layout.setSpacing(8)
|
||||
main_layout.setContentsMargins(16, 8, 16, 12)
|
||||
|
||||
|
||||
# 頂部專案目錄信息
|
||||
self._create_project_header(main_layout)
|
||||
|
||||
|
||||
# 分頁區域
|
||||
self._create_tab_area(main_layout)
|
||||
|
||||
|
||||
# 操作按鈕
|
||||
self._create_action_buttons(main_layout)
|
||||
|
||||
|
||||
# 應用深色主題
|
||||
self._apply_dark_style()
|
||||
|
||||
|
||||
def _create_project_header(self, layout: QVBoxLayout) -> None:
|
||||
"""創建專案目錄頭部信息"""
|
||||
# 創建水平布局來放置專案目錄和倒數計時器
|
||||
@ -159,7 +165,7 @@ class FeedbackWindow(QMainWindow):
|
||||
self._update_countdown_visibility()
|
||||
|
||||
|
||||
|
||||
|
||||
def _create_tab_area(self, layout: QVBoxLayout) -> None:
|
||||
"""創建分頁區域"""
|
||||
# 創建滾動區域來包裝整個分頁組件
|
||||
@ -220,21 +226,21 @@ class FeedbackWindow(QMainWindow):
|
||||
width: 0px;
|
||||
}
|
||||
""")
|
||||
|
||||
|
||||
self.tab_widget = QTabWidget()
|
||||
self.tab_widget.setMinimumHeight(150) # 降低分頁組件最小高度
|
||||
# 設置分頁組件的大小策略,確保能觸發滾動
|
||||
self.tab_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
|
||||
|
||||
# 初始化分頁管理器
|
||||
self.tab_manager = TabManager(
|
||||
self.tab_widget,
|
||||
self.project_dir,
|
||||
self.summary,
|
||||
self.tab_widget,
|
||||
self.project_dir,
|
||||
self.summary,
|
||||
self.combined_mode,
|
||||
self.layout_orientation
|
||||
)
|
||||
|
||||
|
||||
# 創建分頁
|
||||
self.tab_manager.create_tabs()
|
||||
|
||||
@ -243,21 +249,21 @@ class FeedbackWindow(QMainWindow):
|
||||
|
||||
# 將分頁組件放入滾動區域
|
||||
scroll_area.setWidget(self.tab_widget)
|
||||
|
||||
|
||||
layout.addWidget(scroll_area, 1)
|
||||
|
||||
|
||||
def _create_action_buttons(self, layout: QVBoxLayout) -> None:
|
||||
"""創建操作按鈕"""
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.addStretch()
|
||||
|
||||
|
||||
# 取消按鈕
|
||||
self.cancel_button = QPushButton(t('buttons.cancel'))
|
||||
self.cancel_button.clicked.connect(self._cancel_feedback)
|
||||
self.cancel_button.setFixedSize(130, 40)
|
||||
apply_widget_styles(self.cancel_button, "secondary_button")
|
||||
button_layout.addWidget(self.cancel_button)
|
||||
|
||||
|
||||
# 提交按鈕
|
||||
self.submit_button = QPushButton(t('buttons.submit'))
|
||||
self.submit_button.clicked.connect(self._submit_feedback)
|
||||
@ -265,19 +271,19 @@ class FeedbackWindow(QMainWindow):
|
||||
self.submit_button.setDefault(True)
|
||||
apply_widget_styles(self.submit_button, "success_button")
|
||||
button_layout.addWidget(self.submit_button)
|
||||
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
|
||||
def _setup_shortcuts(self) -> None:
|
||||
"""設置快捷鍵"""
|
||||
# Ctrl+Enter (主鍵盤) 提交回饋
|
||||
submit_shortcut_main = QShortcut(QKeySequence("Ctrl+Return"), self)
|
||||
submit_shortcut_main.activated.connect(self._submit_feedback)
|
||||
|
||||
|
||||
# Ctrl+Enter (數字鍵盤) 提交回饋
|
||||
submit_shortcut_keypad = QShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Enter), self)
|
||||
submit_shortcut_keypad.activated.connect(self._submit_feedback)
|
||||
|
||||
|
||||
# macOS 支援 Cmd+Return (主鍵盤)
|
||||
submit_shortcut_mac_main = QShortcut(QKeySequence("Meta+Return"), self)
|
||||
submit_shortcut_mac_main.activated.connect(self._submit_feedback)
|
||||
@ -285,19 +291,19 @@ class FeedbackWindow(QMainWindow):
|
||||
# macOS 支援 Cmd+Enter (數字鍵盤)
|
||||
submit_shortcut_mac_keypad = QShortcut(QKeySequence(Qt.Modifier.META | Qt.Key.Key_Enter), self)
|
||||
submit_shortcut_mac_keypad.activated.connect(self._submit_feedback)
|
||||
|
||||
|
||||
# Escape 取消回饋
|
||||
cancel_shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self)
|
||||
cancel_shortcut.activated.connect(self._cancel_feedback)
|
||||
|
||||
|
||||
def _connect_signals(self) -> None:
|
||||
"""連接信號"""
|
||||
# 連接語言變更信號
|
||||
self.language_changed.connect(self._refresh_ui_texts)
|
||||
|
||||
|
||||
# 連接分頁管理器的信號
|
||||
self.tab_manager.connect_signals(self)
|
||||
|
||||
|
||||
def _apply_dark_style(self) -> None:
|
||||
"""應用深色主題"""
|
||||
self.setStyleSheet("""
|
||||
@ -378,40 +384,40 @@ class FeedbackWindow(QMainWindow):
|
||||
border-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
|
||||
|
||||
def _on_layout_change_requested(self, combined_mode: bool, orientation: str) -> None:
|
||||
"""處理佈局變更請求(模式和方向同時變更)"""
|
||||
try:
|
||||
# 保存當前內容
|
||||
current_data = self.tab_manager.get_feedback_data()
|
||||
|
||||
|
||||
# 記住當前分頁索引
|
||||
current_tab_index = self.tab_widget.currentIndex()
|
||||
|
||||
|
||||
# 保存新設置
|
||||
self.combined_mode = combined_mode
|
||||
self.layout_orientation = orientation
|
||||
self.config_manager.set_layout_mode(combined_mode)
|
||||
self.config_manager.set_layout_orientation(orientation)
|
||||
|
||||
|
||||
# 重新創建分頁
|
||||
self.tab_manager.set_layout_mode(combined_mode)
|
||||
self.tab_manager.set_layout_orientation(orientation)
|
||||
self.tab_manager.create_tabs()
|
||||
|
||||
|
||||
# 恢復內容
|
||||
self.tab_manager.restore_content(
|
||||
current_data["interactive_feedback"],
|
||||
current_data["command_logs"],
|
||||
current_data["images"]
|
||||
)
|
||||
|
||||
|
||||
# 重新連接信號
|
||||
self.tab_manager.connect_signals(self)
|
||||
|
||||
|
||||
# 刷新UI文字
|
||||
self._refresh_ui_texts()
|
||||
|
||||
|
||||
# 恢復到設定頁面(通常是倒數第二個分頁)
|
||||
if self.combined_mode:
|
||||
# 合併模式:回饋、命令、設置、關於
|
||||
@ -419,107 +425,107 @@ class FeedbackWindow(QMainWindow):
|
||||
else:
|
||||
# 分離模式:回饋、摘要、命令、設置、關於
|
||||
settings_tab_index = 3
|
||||
|
||||
|
||||
# 確保索引在有效範圍內
|
||||
if settings_tab_index < self.tab_widget.count():
|
||||
self.tab_widget.setCurrentIndex(settings_tab_index)
|
||||
|
||||
|
||||
mode_text = "合併模式" if combined_mode else "分離模式"
|
||||
orientation_text = "(水平布局)" if orientation == "horizontal" else "(垂直布局)"
|
||||
if combined_mode:
|
||||
mode_text += orientation_text
|
||||
debug_log(f"佈局已切換到: {mode_text}")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
debug_log(f"佈局變更失敗: {e}")
|
||||
QMessageBox.warning(self, t('errors.title'), t('errors.interfaceReloadError', error=str(e)))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _on_reset_settings_requested(self) -> None:
|
||||
"""處理重置設定請求"""
|
||||
try:
|
||||
# 重置配置管理器的所有設定
|
||||
self.config_manager.reset_settings()
|
||||
|
||||
|
||||
# 重置應用程式狀態
|
||||
self.combined_mode = False # 重置為分離模式
|
||||
self.layout_orientation = 'vertical' # 重置為垂直布局
|
||||
|
||||
|
||||
# 重新設置語言為預設
|
||||
self.i18n.set_language('zh-TW')
|
||||
|
||||
|
||||
# 保存當前內容
|
||||
current_data = self.tab_manager.get_feedback_data()
|
||||
|
||||
|
||||
# 重新創建分頁
|
||||
self.tab_manager.set_layout_mode(self.combined_mode)
|
||||
self.tab_manager.set_layout_orientation(self.layout_orientation)
|
||||
self.tab_manager.create_tabs()
|
||||
|
||||
|
||||
# 恢復內容
|
||||
self.tab_manager.restore_content(
|
||||
current_data["interactive_feedback"],
|
||||
current_data["command_logs"],
|
||||
current_data["images"]
|
||||
)
|
||||
|
||||
|
||||
# 重新連接信號
|
||||
self.tab_manager.connect_signals(self)
|
||||
|
||||
|
||||
# 重新載入設定分頁的狀態
|
||||
if self.tab_manager.settings_tab:
|
||||
self.tab_manager.settings_tab.reload_settings_from_config()
|
||||
|
||||
|
||||
# 刷新UI文字
|
||||
self._refresh_ui_texts()
|
||||
|
||||
|
||||
# 重新應用視窗定位(使用重置後的設定)
|
||||
self._apply_window_positioning()
|
||||
|
||||
|
||||
# 切換到設定分頁顯示重置結果
|
||||
settings_tab_index = 3 # 分離模式下設定分頁是第4個(索引3)
|
||||
if settings_tab_index < self.tab_widget.count():
|
||||
self.tab_widget.setCurrentIndex(settings_tab_index)
|
||||
|
||||
|
||||
# 顯示成功訊息
|
||||
QMessageBox.information(
|
||||
self,
|
||||
self,
|
||||
t('settings.reset.successTitle'),
|
||||
t('settings.reset.successMessage'),
|
||||
QMessageBox.Ok
|
||||
)
|
||||
|
||||
|
||||
debug_log("設定重置成功")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
debug_log(f"重置設定失敗: {e}")
|
||||
QMessageBox.critical(
|
||||
self,
|
||||
self,
|
||||
t('errors.title'),
|
||||
t('settings.reset.error', error=str(e)),
|
||||
QMessageBox.Ok
|
||||
)
|
||||
|
||||
|
||||
def _submit_feedback(self) -> None:
|
||||
"""提交回饋"""
|
||||
# 獲取所有回饋數據
|
||||
data = self.tab_manager.get_feedback_data()
|
||||
|
||||
|
||||
self.result = data
|
||||
debug_log(f"回饋提交: 文字長度={len(data['interactive_feedback'])}, "
|
||||
f"命令日誌長度={len(data['command_logs'])}, "
|
||||
f"圖片數量={len(data['images'])}")
|
||||
|
||||
|
||||
# 關閉窗口
|
||||
self.close()
|
||||
|
||||
|
||||
def _cancel_feedback(self) -> None:
|
||||
"""取消回饋收集"""
|
||||
debug_log("取消回饋收集")
|
||||
self.result = ""
|
||||
self.close()
|
||||
|
||||
|
||||
def force_close(self) -> None:
|
||||
"""強制關閉視窗(用於超時處理)"""
|
||||
debug_log("強制關閉視窗(超時)")
|
||||
@ -644,7 +650,7 @@ class FeedbackWindow(QMainWindow):
|
||||
# 倒數計時器只在啟用超時時顯示
|
||||
self.countdown_label.setVisible(self.timeout_enabled)
|
||||
self.countdown_display.setVisible(self.timeout_enabled)
|
||||
|
||||
|
||||
def _refresh_ui_texts(self) -> None:
|
||||
"""刷新界面文字"""
|
||||
self.setWindowTitle(t('app.title'))
|
||||
@ -660,11 +666,11 @@ class FeedbackWindow(QMainWindow):
|
||||
|
||||
# 更新分頁文字
|
||||
self.tab_manager.update_tab_texts()
|
||||
|
||||
|
||||
def _apply_window_positioning(self) -> None:
|
||||
"""根據用戶設置應用視窗定位策略"""
|
||||
always_center = self.config_manager.get_always_center_window()
|
||||
|
||||
|
||||
if always_center:
|
||||
# 總是中心顯示模式:使用保存的大小(如果有的話),然後置中
|
||||
self._restore_window_size_only()
|
||||
@ -678,22 +684,22 @@ class FeedbackWindow(QMainWindow):
|
||||
else:
|
||||
# 沒有保存的位置,移到中心
|
||||
self._move_to_primary_screen_center()
|
||||
|
||||
|
||||
def _is_window_visible(self) -> bool:
|
||||
"""檢查視窗是否在任何螢幕的可見範圍內"""
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
|
||||
window_rect = self.frameGeometry()
|
||||
|
||||
|
||||
for screen in QApplication.screens():
|
||||
if screen.availableGeometry().intersects(window_rect):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _move_to_primary_screen_center(self) -> None:
|
||||
"""將視窗移到主螢幕中心"""
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
|
||||
screen = QApplication.primaryScreen()
|
||||
if screen:
|
||||
screen_geometry = screen.availableGeometry()
|
||||
@ -702,7 +708,7 @@ class FeedbackWindow(QMainWindow):
|
||||
window_geometry.moveCenter(center_point)
|
||||
self.move(window_geometry.topLeft())
|
||||
debug_log("視窗已移到主螢幕中心")
|
||||
|
||||
|
||||
def _restore_window_size_only(self) -> bool:
|
||||
"""只恢復視窗大小(不恢復位置)"""
|
||||
try:
|
||||
@ -714,7 +720,7 @@ class FeedbackWindow(QMainWindow):
|
||||
except Exception as e:
|
||||
debug_log(f"恢復視窗大小失敗: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def _restore_last_position(self) -> bool:
|
||||
"""嘗試恢復上次保存的視窗位置和大小"""
|
||||
try:
|
||||
@ -727,18 +733,18 @@ class FeedbackWindow(QMainWindow):
|
||||
except Exception as e:
|
||||
debug_log(f"恢復視窗位置失敗: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def _save_window_position(self) -> None:
|
||||
"""保存當前視窗位置和大小"""
|
||||
try:
|
||||
always_center = self.config_manager.get_always_center_window()
|
||||
|
||||
|
||||
# 獲取當前幾何信息
|
||||
current_geometry = {
|
||||
'width': self.width(),
|
||||
'height': self.height()
|
||||
}
|
||||
|
||||
|
||||
if not always_center:
|
||||
# 智能定位模式:同時保存位置
|
||||
current_geometry['x'] = self.x()
|
||||
@ -747,45 +753,86 @@ class FeedbackWindow(QMainWindow):
|
||||
else:
|
||||
# 總是中心顯示模式:只保存大小,不保存位置
|
||||
debug_log(f"已保存視窗大小: {current_geometry['width']}x{current_geometry['height']} (總是中心顯示模式)")
|
||||
|
||||
|
||||
# 獲取現有配置,只更新需要的部分
|
||||
saved_geometry = self.config_manager.get_window_geometry() or {}
|
||||
saved_geometry.update(current_geometry)
|
||||
|
||||
|
||||
self.config_manager.set_window_geometry(saved_geometry)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
debug_log(f"保存視窗狀態失敗: {e}")
|
||||
|
||||
|
||||
def resizeEvent(self, event) -> None:
|
||||
"""窗口大小變化事件"""
|
||||
super().resizeEvent(event)
|
||||
# 窗口大小變化時始終保存(無論是否設置為中心顯示)
|
||||
if hasattr(self, 'config_manager'):
|
||||
self._schedule_save_window_position()
|
||||
|
||||
|
||||
def moveEvent(self, event) -> None:
|
||||
"""窗口位置變化事件"""
|
||||
super().moveEvent(event)
|
||||
# 窗口位置變化只在智能定位模式下保存
|
||||
if hasattr(self, 'config_manager') and not self.config_manager.get_always_center_window():
|
||||
self._schedule_save_window_position()
|
||||
|
||||
|
||||
def _schedule_save_window_position(self) -> None:
|
||||
"""調度窗口位置保存(防抖機制)"""
|
||||
if hasattr(self, '_save_timer'):
|
||||
self._save_timer.start(self._save_delay)
|
||||
|
||||
|
||||
def _delayed_save_window_position(self) -> None:
|
||||
"""延遲保存窗口位置(防抖機制的實際執行)"""
|
||||
self._save_window_position()
|
||||
|
||||
|
||||
def _auto_focus_input(self) -> None:
|
||||
"""自動聚焦到輸入框"""
|
||||
try:
|
||||
# 確保窗口已經顯示並激活
|
||||
self.raise_()
|
||||
self.activateWindow()
|
||||
|
||||
# 獲取回饋輸入框(修正邏輯)
|
||||
feedback_input = None
|
||||
|
||||
# 檢查是否有tab_manager
|
||||
if not hasattr(self, 'tab_manager'):
|
||||
debug_log("tab_manager 不存在")
|
||||
return
|
||||
|
||||
# 檢查 feedback_tab(無論是合併模式還是分離模式)
|
||||
if hasattr(self.tab_manager, 'feedback_tab') and self.tab_manager.feedback_tab:
|
||||
if hasattr(self.tab_manager.feedback_tab, 'feedback_input'):
|
||||
feedback_input = self.tab_manager.feedback_tab.feedback_input
|
||||
debug_log("找到feedback_tab中的輸入框")
|
||||
else:
|
||||
debug_log("feedback_tab存在但沒有feedback_input屬性")
|
||||
else:
|
||||
debug_log("沒有找到feedback_tab")
|
||||
|
||||
# 設置焦點到輸入框
|
||||
if feedback_input:
|
||||
feedback_input.setFocus()
|
||||
feedback_input.raise_() # 確保輸入框可見
|
||||
debug_log("已自動聚焦到輸入框")
|
||||
else:
|
||||
debug_log("未找到回饋輸入框,無法自動聚焦")
|
||||
# 打印調試信息
|
||||
if hasattr(self, 'tab_manager'):
|
||||
debug_log(f"tab_manager 屬性: {dir(self.tab_manager)}")
|
||||
|
||||
except Exception as e:
|
||||
debug_log(f"自動聚焦失敗: {e}")
|
||||
import traceback
|
||||
debug_log(f"詳細錯誤: {traceback.format_exc()}")
|
||||
|
||||
def closeEvent(self, event) -> None:
|
||||
"""窗口關閉事件"""
|
||||
# 最終保存視窗狀態(大小始終保存,位置根據設置決定)
|
||||
self._save_window_position()
|
||||
|
||||
|
||||
# 清理分頁管理器
|
||||
self.tab_manager.cleanup()
|
||||
event.accept()
|
||||
debug_log("主窗口已關閉")
|
||||
debug_log("主窗口已關閉")
|
||||
|
Loading…
x
Reference in New Issue
Block a user