feat: collapsible host sidebar (Ctrl+B / view menu / floating restore button)
- ◀ button in sidebar header + '视图→隐藏侧栏 (Ctrl+B)' menu item - collapsed state persisted to ~/.sshclient/settings.json, restored on startup - floating '主机' button (theme-aware dark/light) to re-show sidebar - fix: remove duplicate QShortcut — QShortcut + QAction shortcut on same key both fired, toggling twice per Ctrl+B press (net zero effect) - fix: floating button positioned with parent-local coords instead of mapToGlobal — button no longer drifts off the window edge when the window is not at screen origin; resizeEvent/moveEvent overrides removed - remove dead terminal_input_set_enabled (referenced nonexistent cmd_input) - test_sidebar.py: 7-step coverage incl. single-fire shortcut regression, splitter width restore, cross-restart persistence; settings isolated to tmpdir
This commit is contained in:
+136
@@ -0,0 +1,136 @@
|
||||
"""
|
||||
侧栏折叠测试:
|
||||
- 默认状态可见
|
||||
- _toggle_sidebar 后隐藏 + 浮动按钮显示
|
||||
- 再 toggle 恢复,splitter 宽度还原
|
||||
- 菜单 act_toggle_sidebar 同步状态
|
||||
- Ctrl+B 快捷键一次按键只触发一次(回归:曾经双绑定 toggle 两次=没反应)
|
||||
- 浮动按钮用父级局部坐标,贴主窗口左边缘
|
||||
- 折叠状态持久化到 settings,新窗口恢复
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
os.environ["QT_QPA_PLATFORM"] = "offscreen"
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
|
||||
# 隔离配置:不污染真实 ~/.sshclient/settings.json
|
||||
import ui.theme as theme
|
||||
_tmpdir = tempfile.mkdtemp(prefix="sshclient_sidebar_test_")
|
||||
theme.CONFIG_DIR = Path(_tmpdir)
|
||||
theme.SETTINGS_FILE = Path(_tmpdir) / "settings.json"
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtTest import QTest
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from ui.main_window import MainWindow
|
||||
|
||||
|
||||
def main():
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
print("[1/7] 默认状态:侧栏可见")
|
||||
w = MainWindow()
|
||||
w.show()
|
||||
w.activateWindow()
|
||||
app.processEvents()
|
||||
assert w._sidebar_collapsed is False
|
||||
assert w.left_panel.isVisible()
|
||||
assert w.btn_collapse_sidebar.text() == "◀"
|
||||
assert w.btn_show_sidebar.isHidden()
|
||||
print(" ✓ left_panel 可见, btn_collapse_sidebar='◀', btn_show_sidebar 隐藏")
|
||||
|
||||
print("[2/7] 折叠:侧栏隐藏 + 浮动按钮显示")
|
||||
w._toggle_sidebar()
|
||||
app.processEvents()
|
||||
assert w._sidebar_collapsed is True
|
||||
assert not w.left_panel.isVisible()
|
||||
assert w.btn_collapse_sidebar.text() == "▶"
|
||||
assert w.btn_show_sidebar.isVisible()
|
||||
# 菜单项同步
|
||||
assert w.act_toggle_sidebar.isChecked()
|
||||
assert "展开" in w.act_toggle_sidebar.text()
|
||||
print(" ✓ left_panel 隐藏, btn_collapse_sidebar='▶', btn_show_sidebar 可见, 菜单项 checked+text 已同步")
|
||||
|
||||
print("[3/7] 展开:恢复 + splitter 宽度还原")
|
||||
w._toggle_sidebar()
|
||||
app.processEvents()
|
||||
assert w._sidebar_collapsed is False
|
||||
assert w.left_panel.isVisible()
|
||||
assert w.btn_collapse_sidebar.text() == "◀"
|
||||
assert w.btn_show_sidebar.isHidden()
|
||||
assert not w.act_toggle_sidebar.isChecked()
|
||||
assert "隐藏" in w.act_toggle_sidebar.text()
|
||||
left_w = w.splitter.sizes()[0]
|
||||
assert left_w > 0, f"展开后侧栏宽度应 >0, 实际 {left_w}"
|
||||
print(f" ✓ 恢复原状, splitter 侧栏宽度={left_w}px")
|
||||
|
||||
print("[4/7] 连续折叠/展开多次")
|
||||
# 当前 collapsed=False;第 k 次 toggle 后 collapsed = (k 为奇数)
|
||||
for i in range(5):
|
||||
w._toggle_sidebar()
|
||||
app.processEvents()
|
||||
expected = (i % 2 == 0) # i=0 → 第1次 → True
|
||||
assert w._sidebar_collapsed == expected, \
|
||||
f"第 {i+1} 次切换后应为 {expected}, 实际 {w._sidebar_collapsed}"
|
||||
assert w.left_panel.isVisible() == (not expected)
|
||||
# 5 次后 collapsed=True,再 toggle 一次回到 False
|
||||
w._toggle_sidebar()
|
||||
app.processEvents()
|
||||
assert w._sidebar_collapsed is False
|
||||
print(" ✓ 6 次切换状态全部正确")
|
||||
|
||||
print("[5/7] Ctrl+B 快捷键:一次按键只触发一次")
|
||||
before = w._sidebar_collapsed
|
||||
QTest.keyClick(w, Qt.Key_B, Qt.ControlModifier)
|
||||
app.processEvents()
|
||||
assert w._sidebar_collapsed == (not before), \
|
||||
f"Ctrl+B 未生效(或双绑定触发了两次): before={before}, after={w._sidebar_collapsed}"
|
||||
QTest.keyClick(w, Qt.Key_B, Qt.ControlModifier)
|
||||
app.processEvents()
|
||||
assert w._sidebar_collapsed == before
|
||||
print(f" ✓ 按两次 Ctrl+B: {before} → {not before} → {before}")
|
||||
|
||||
print("[6/7] 浮动按钮位置:父级局部坐标 (4, 100)")
|
||||
if not w._sidebar_collapsed:
|
||||
w._toggle_sidebar()
|
||||
app.processEvents()
|
||||
pos = w.btn_show_sidebar.pos()
|
||||
assert (pos.x(), pos.y()) == (4, 100), \
|
||||
f"浮动按钮位置 ({pos.x()}, {pos.y()}) != (4, 100)"
|
||||
print(f" ✓ 浮动按钮局部坐标 ({pos.x()}, {pos.y()}),窗口移动/缩放都不会飘走")
|
||||
w._toggle_sidebar()
|
||||
app.processEvents()
|
||||
|
||||
print("[7/7] 状态持久化:重启后恢复折叠状态")
|
||||
# 折叠 → 新窗口应恢复为折叠
|
||||
if not w._sidebar_collapsed:
|
||||
w._toggle_sidebar()
|
||||
app.processEvents()
|
||||
assert theme.load_settings()["sidebar_collapsed"] is True
|
||||
w2 = MainWindow()
|
||||
w2.show()
|
||||
app.processEvents()
|
||||
assert w2._sidebar_collapsed is True
|
||||
assert not w2.left_panel.isVisible()
|
||||
assert w2.btn_show_sidebar.isVisible()
|
||||
assert w2.act_toggle_sidebar.isChecked()
|
||||
# 展开 → 再开新窗口应恢复为展开
|
||||
w2._toggle_sidebar()
|
||||
app.processEvents()
|
||||
w3 = MainWindow()
|
||||
w3.show()
|
||||
app.processEvents()
|
||||
assert w3._sidebar_collapsed is False
|
||||
assert w3.left_panel.isVisible()
|
||||
assert w3.btn_show_sidebar.isHidden()
|
||||
print(" ✓ 折叠/展开状态均能跨启动恢复(含菜单项勾选同步)")
|
||||
|
||||
print("\n侧栏折叠测试全部通过 ✓")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user