loginwindow.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. #include "loginwindow.h"
  2. #include "qapplication.h"
  3. #include "qcolor.h"
  4. #include "qgroupbox.h"
  5. #include <QCheckBox>
  6. #include <QComboBox>
  7. #include <QDesktopServices>
  8. #include <QHBoxLayout>
  9. #include <QLabel>
  10. #include <QLineEdit>
  11. #include <QMap>
  12. #include <QMenu>
  13. #include <QMessageBox>
  14. #include <QPushButton>
  15. #include <QResizeEvent>
  16. #include <QSettings>
  17. #include <QTimer>
  18. #include <QTranslator>
  19. #include <QUrl>
  20. #include <QVBoxLayout>
  21. #include <thememanager.h>
  22. #include <widgets/maskoverlay.h>
  23. class QLabel;
  24. class QLineEdit;
  25. class QCheckBox;
  26. class QPushButton;
  27. class QComboBox;
  28. class LoginWidgetPrivate
  29. {
  30. public:
  31. explicit LoginWidgetPrivate(LoginWidget *q);
  32. ~LoginWidgetPrivate();
  33. void initialize();
  34. void setupMainLayout();
  35. void createFormContainer();
  36. void createBackgroundContainer();
  37. void createControlPanel();
  38. void updateLayoutPresentation();
  39. void applyCurrentTheme();
  40. void loadSettings();
  41. void saveSettings();
  42. void retranslateUi();
  43. LoginWidget *q_ptr;
  44. Q_DECLARE_PUBLIC(LoginWidget)
  45. // 配置和状态
  46. LoginWidget::Config config;
  47. LoginWidget::LayoutMode currentMode;
  48. bool rememberPassword;
  49. // UI组件
  50. QWidget *controlPanel;
  51. QWidget *formContainer;
  52. QWidget *backgroundContainer;
  53. QWidget *customFormWidget = nullptr;
  54. QHBoxLayout *mainContentLayout;
  55. // 表单控件
  56. QLineEdit *usernameEdit;
  57. QLineEdit *passwordEdit;
  58. QCheckBox *rememberCheck;
  59. QPushButton *loginBtn;
  60. // 控制面板组件
  61. QPushButton *layoutToggleBtn;
  62. QPushButton *darkModeBtn;
  63. QComboBox *languageCombo;
  64. QSettings settings;
  65. // 布局相关常量
  66. static constexpr int LAYOUT_SWITCH_THRESHOLD = 1024;
  67. static constexpr int FORM_RATIO = 34;
  68. static constexpr int BACKGROUND_RATIO = 66;
  69. private:
  70. QMenu *layoutMenu;
  71. void setupLayoutMenu();
  72. private:
  73. QPushButton *createSocialButton(const QString &iconPath);
  74. void setupSocialButtons(QVBoxLayout *layout);
  75. void setupDefaultForm(QVBoxLayout *layout);
  76. void handleWindowResize(int newWidth);
  77. QPushButton *createControlButton(const QString &text, const QString &objectName);
  78. };
  79. LoginWidgetPrivate::LoginWidgetPrivate(LoginWidget *q)
  80. : q_ptr(q)
  81. , currentMode(LoginWidget::RightForm)
  82. , rememberPassword(false)
  83. , controlPanel(nullptr)
  84. , formContainer(nullptr)
  85. , backgroundContainer(nullptr)
  86. , customFormWidget(nullptr)
  87. , mainContentLayout(nullptr)
  88. , usernameEdit(nullptr)
  89. , passwordEdit(nullptr)
  90. , rememberCheck(nullptr)
  91. , loginBtn(nullptr)
  92. , layoutToggleBtn(nullptr)
  93. , darkModeBtn(nullptr)
  94. , languageCombo(nullptr)
  95. {}
  96. LoginWidgetPrivate::~LoginWidgetPrivate()
  97. {
  98. if (customFormWidget)
  99. customFormWidget->deleteLater();
  100. }
  101. void LoginWidgetPrivate::initialize()
  102. {
  103. createFormContainer();
  104. createBackgroundContainer();
  105. createControlPanel();
  106. setupMainLayout();
  107. loadSettings();
  108. applyCurrentTheme();
  109. retranslateUi();
  110. }
  111. void LoginWidgetPrivate::setupMainLayout()
  112. {
  113. Q_Q(LoginWidget);
  114. QVBoxLayout *mainLayout = new QVBoxLayout(q);
  115. mainLayout->setContentsMargins(0, 0, 0, 0);
  116. mainLayout->setSpacing(0);
  117. // QHBoxLayout *controlLayout = new QHBoxLayout();
  118. // controlLayout->addStretch();
  119. // controlLayout->addWidget(controlPanel);
  120. // controlLayout->setContentsMargins(0, 10, 20, 0);
  121. mainContentLayout = new QHBoxLayout();
  122. mainContentLayout->setContentsMargins(0, 0, 0, 0);
  123. mainContentLayout->setSpacing(0);
  124. // mainLayout->addLayout(controlLayout);
  125. mainLayout->addLayout(mainContentLayout);
  126. // 将控制面板设为顶层窗口的子部件
  127. controlPanel->setParent(q);
  128. controlPanel->raise(); // 确保在最上层
  129. updateLayoutPresentation();
  130. }
  131. void LoginWidgetPrivate::createFormContainer()
  132. {
  133. Q_Q(LoginWidget);
  134. if (formContainer) {
  135. formContainer->deleteLater();
  136. formContainer = nullptr;
  137. }
  138. formContainer = new QWidget(q);
  139. formContainer->setObjectName("formContainer");
  140. QVBoxLayout *formLayout = new QVBoxLayout(formContainer);
  141. if (config.allowFormCustomization && customFormWidget) {
  142. formLayout->addWidget(customFormWidget);
  143. return;
  144. }
  145. formLayout->addStretch();
  146. setupDefaultForm(formLayout);
  147. if (config.showSocialButtons) {
  148. setupSocialButtons(formLayout);
  149. }
  150. formLayout->addStretch();
  151. }
  152. void LoginWidgetPrivate::setupDefaultForm(QVBoxLayout *layout)
  153. {
  154. usernameEdit = new QLineEdit(formContainer);
  155. passwordEdit = new QLineEdit(formContainer);
  156. passwordEdit->setEchoMode(QLineEdit::Password);
  157. rememberCheck = new QCheckBox(formContainer);
  158. loginBtn = new QPushButton(formContainer);
  159. layout->addWidget(new QLabel(("Username"), formContainer));
  160. layout->addWidget(usernameEdit);
  161. layout->addSpacing(15);
  162. layout->addWidget(new QLabel(("Password"), formContainer));
  163. layout->addWidget(passwordEdit);
  164. layout->addSpacing(10);
  165. layout->addWidget(rememberCheck);
  166. layout->addSpacing(15);
  167. layout->addWidget(loginBtn);
  168. layout->addSpacing(20);
  169. QObject::connect(loginBtn, &QPushButton::clicked, q_ptr, [this]() {
  170. Q_Q(LoginWidget);
  171. if (usernameEdit->text().isEmpty() || passwordEdit->text().isEmpty()) {
  172. QMessageBox::warning(q, ("Error"), ("Please fill all fields"));
  173. return;
  174. }
  175. emit q->loginRequested(usernameEdit->text(), passwordEdit->text());
  176. saveSettings();
  177. });
  178. }
  179. void LoginWidgetPrivate::setupSocialButtons(QVBoxLayout *layout)
  180. {
  181. QHBoxLayout *socialLayout = new QHBoxLayout();
  182. socialLayout->addStretch();
  183. QPushButton *wechatBtn = createSocialButton(":/icons/wechat.png");
  184. QPushButton *githubBtn = createSocialButton(":/icons/github.png");
  185. QObject::connect(wechatBtn, &QPushButton::clicked, q_ptr, &LoginWidget::wechatLoginRequested);
  186. QObject::connect(githubBtn, &QPushButton::clicked, q_ptr, &LoginWidget::githubLoginRequested);
  187. socialLayout->addWidget(wechatBtn);
  188. socialLayout->addWidget(githubBtn);
  189. socialLayout->addStretch();
  190. layout->addLayout(socialLayout);
  191. }
  192. QPushButton *LoginWidgetPrivate::createSocialButton(const QString &iconPath)
  193. {
  194. QPushButton *btn = new QPushButton(formContainer);
  195. btn->setIcon(QIcon(iconPath));
  196. btn->setIconSize(QSize(32, 32));
  197. btn->setFixedSize(48, 48);
  198. btn->setFlat(true);
  199. return btn;
  200. }
  201. void LoginWidgetPrivate::createBackgroundContainer()
  202. {
  203. Q_Q(LoginWidget);
  204. backgroundContainer = new QWidget(q);
  205. QLabel *description = new QLabel(("<h1>Secure Login</h1>"
  206. "<p>Version 2.0</p>"
  207. "<p>Advanced Security System</p>"),
  208. backgroundContainer);
  209. description->setAlignment(Qt::AlignCenter);
  210. QVBoxLayout *bgLayout = new QVBoxLayout(backgroundContainer);
  211. bgLayout->addWidget(description);
  212. }
  213. void LoginWidgetPrivate::createControlPanel()
  214. {
  215. Q_Q(LoginWidget);
  216. controlPanel = new QLabel(q);
  217. controlPanel->setObjectName("controlPanel");
  218. controlPanel->setFixedHeight(42);
  219. controlPanel->setMaximumHeight(42);
  220. QHBoxLayout *panelLayout = new QHBoxLayout(controlPanel);
  221. panelLayout->setSpacing(15); // 增加控件间距
  222. panelLayout->setContentsMargins(10, 0, 10, 0);
  223. panelLayout->setAlignment(Qt::AlignVCenter); // 居中对齐
  224. languageCombo = new QComboBox(controlPanel);
  225. languageCombo->addItem("中文", "zh_CN");
  226. languageCombo->addItem("English", "en_US");
  227. layoutToggleBtn = createControlButton("", "layoutToggle");
  228. layoutToggleBtn->setFixedSize(32, 32);
  229. layoutToggleBtn->setMaximumSize(32, 32);
  230. layoutToggleBtn->setStyleSheet(R"(
  231. QPushButton#layoutToggle {
  232. border: none;
  233. padding: 0px;
  234. margin: 0px;
  235. background-color: transparent;
  236. width: 32px;
  237. height: 32px;
  238. min-width: 32px;
  239. min-height: 32px;
  240. max-width: 32px;
  241. max-height: 32px;
  242. }
  243. )");
  244. // 创建并设置菜单
  245. setupLayoutMenu();
  246. darkModeBtn = createControlButton("", "darkToggle");
  247. darkModeBtn->setFixedSize(32, 32);
  248. darkModeBtn->setMaximumSize(32, 32);
  249. darkModeBtn->setCursor(Qt::PointingHandCursor);
  250. darkModeBtn->setStyleSheet(R"(
  251. QPushButton#darkToggle {
  252. border: none;
  253. padding: 0px;
  254. margin: 0px;
  255. background-color: transparent;
  256. width: 32px;
  257. height: 32px;
  258. min-width: 32px;
  259. min-height: 32px;
  260. max-width: 32px;
  261. max-height: 32px;
  262. }
  263. )");
  264. darkModeBtn->setIcon(QIcon(":/theme/dark_mode.svg"));
  265. darkModeBtn->setIconSize(QSize(24, 24));
  266. darkModeBtn->setToolTip("切换暗黑模式");
  267. panelLayout->addStretch();
  268. panelLayout->addWidget(languageCombo);
  269. panelLayout->addWidget(layoutToggleBtn);
  270. panelLayout->addWidget(darkModeBtn);
  271. panelLayout->addStretch();
  272. panelLayout->setSizeConstraint(QLayout::SetFixedSize);
  273. QObject::connect(layoutToggleBtn, &QPushButton::clicked, q, [this]() {
  274. if (q_ptr->width() < LAYOUT_SWITCH_THRESHOLD)
  275. return;
  276. // 在按钮下方显示菜单
  277. QPoint pos = layoutToggleBtn->mapToGlobal(layoutToggleBtn->rect().bottomLeft());
  278. layoutMenu->exec(pos);
  279. //currentMode = static_cast<LoginWidget::LayoutMode>((currentMode + 1) % 3);
  280. //updateLayoutPresentation();
  281. });
  282. QObject::connect(darkModeBtn, &QPushButton::clicked, q, [this]() {
  283. ThemeManager::instance().toggleThemeMode();
  284. applyCurrentTheme();
  285. });
  286. QObject::connect(&ThemeManager::instance(), &ThemeManager::themeChanged, q, [this]() {
  287. applyCurrentTheme();
  288. });
  289. QObject::connect(languageCombo,
  290. QOverload<int>::of(&QComboBox::currentIndexChanged),
  291. q,
  292. [this](int index) {
  293. QString locale = languageCombo->itemData(index).toString();
  294. QTranslator translator;
  295. if (translator.load(":/i18n/" + locale + ".qm")) {
  296. qApp->installTranslator(&translator);
  297. retranslateUi();
  298. }
  299. });
  300. }
  301. QPushButton *LoginWidgetPrivate::createControlButton(const QString &text, const QString &objectName)
  302. {
  303. QPushButton *btn = new QPushButton(text, controlPanel);
  304. btn->setObjectName(objectName);
  305. btn->setFixedSize(80, 30);
  306. return btn;
  307. }
  308. void LoginWidgetPrivate::updateLayoutPresentation()
  309. {
  310. QLayoutItem *item;
  311. while ((item = mainContentLayout->takeAt(0)) != nullptr) {
  312. delete item;
  313. }
  314. switch (currentMode) {
  315. case LoginWidget::CenterForm:
  316. mainContentLayout->addStretch();
  317. mainContentLayout->addWidget(formContainer);
  318. mainContentLayout->addStretch();
  319. backgroundContainer->setGeometry(q_ptr->rect());
  320. backgroundContainer->lower();
  321. break;
  322. case LoginWidget::LeftForm:
  323. mainContentLayout->addWidget(formContainer, FORM_RATIO);
  324. mainContentLayout->addWidget(backgroundContainer, BACKGROUND_RATIO);
  325. break;
  326. case LoginWidget::RightForm:
  327. mainContentLayout->addWidget(backgroundContainer, BACKGROUND_RATIO);
  328. mainContentLayout->addWidget(formContainer, FORM_RATIO);
  329. break;
  330. }
  331. //backgroundContainer->setVisible(currentMode != LoginWidget::CenterForm);
  332. applyCurrentTheme();
  333. }
  334. void LoginWidgetPrivate::applyCurrentTheme()
  335. {
  336. const ThemeManager &themeManager = ThemeManager::instance();
  337. QColor primary = themeManager.color("colorPrimary");
  338. primary.setAlphaF(0.48);
  339. const QString primaryColor = QString("rgba(%1, %2, %3, %4)")
  340. .arg(primary.red())
  341. .arg(primary.green())
  342. .arg(primary.blue())
  343. .arg(primary.alpha());
  344. const QString bg = QString(R"Raw(QWidget {
  345. background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
  346. stop:0.3 rgba(7, 7, 9, %1),
  347. stop:0.48 %2,
  348. stop:0.64 rgba(7, 7, 9, %1));
  349. })Raw")
  350. .arg(themeManager.themeMode() == ThemeManager::ThemeMode::Dark ? 30 : 20)
  351. .arg(primaryColor);
  352. if (themeManager.themeMode() == ThemeManager::ThemeMode::Dark) {
  353. formContainer->setStyleSheet(R"Raw(QWidget#formContainer{background-color: #FF14161A;})Raw");
  354. darkModeBtn->setIcon(QIcon(":/theme/light_mode.svg"));
  355. darkModeBtn->setToolTip("切换亮色模式");
  356. } else {
  357. formContainer->setStyleSheet(R"Raw(QWidget#formContainer{background-color: #FFFFFFFF;})Raw");
  358. darkModeBtn->setIcon(QIcon(":/theme/dark_mode.svg"));
  359. darkModeBtn->setToolTip("切换暗黑模式");
  360. }
  361. backgroundContainer->setStyleSheet(bg);
  362. controlPanel->setStyleSheet(QString(R"RAW(
  363. QWidget#controlPanel {
  364. border-radius: 21px;
  365. color: %1;
  366. background-color: %2;
  367. padding: 5px;
  368. height: 42px;
  369. min-height: 42px;
  370. max-height: 42px;
  371. }
  372. )RAW")
  373. .arg(themeManager.color("colorText").name(QColor::HexArgb))
  374. .arg(themeManager.color("colorFill").name(QColor::HexArgb)));
  375. }
  376. void LoginWidgetPrivate::loadSettings()
  377. {
  378. usernameEdit->setText(settings.value("username", "admin").toString());
  379. passwordEdit->setText((settings.value("password", "6MLAoh$#xR#0").toByteArray()));
  380. rememberCheck->setChecked(settings.value("rememberPassword", false).toBool());
  381. }
  382. void LoginWidgetPrivate::saveSettings()
  383. {
  384. if (rememberCheck->isChecked()) {
  385. settings.setValue("username", usernameEdit->text());
  386. settings.setValue("password", QByteArray(passwordEdit->text().toUtf8()).toBase64());
  387. } else {
  388. settings.remove("username");
  389. settings.remove("password");
  390. }
  391. settings.setValue("rememberPassword", rememberCheck->isChecked());
  392. }
  393. void LoginWidgetPrivate::retranslateUi()
  394. {
  395. usernameEdit->setPlaceholderText(("Enter username"));
  396. passwordEdit->setPlaceholderText(("Enter password"));
  397. rememberCheck->setText(("Remember password"));
  398. loginBtn->setText(("Login"));
  399. }
  400. void LoginWidgetPrivate::setupLayoutMenu()
  401. {
  402. layoutMenu = new QMenu(q_ptr);
  403. layoutMenu->setObjectName("layoutMenu");
  404. QAction *centerAction = new QAction(QIcon(":/icons/layout_center.svg"), "居中布局", layoutMenu);
  405. QAction *leftAction = new QAction(QIcon(":/icons/layout_left.svg"), "左侧布局", layoutMenu);
  406. QAction *rightAction = new QAction(QIcon(":/icons/layout_right.svg"), "右侧布局", layoutMenu);
  407. layoutMenu->addAction(centerAction);
  408. layoutMenu->addAction(leftAction);
  409. layoutMenu->addAction(rightAction);
  410. // 设置当前布局图标和文本
  411. layoutToggleBtn->setIcon(QIcon(":/icons/layout_right.svg"));
  412. layoutToggleBtn->setIconSize(QSize(24, 24));
  413. // 连接菜单动作信号
  414. QObject::connect(centerAction, &QAction::triggered, q_ptr, [this, centerAction]() {
  415. currentMode = LoginWidget::CenterForm;
  416. layoutToggleBtn->setIcon(centerAction->icon());
  417. updateLayoutPresentation();
  418. });
  419. QObject::connect(leftAction, &QAction::triggered, q_ptr, [this, leftAction]() {
  420. currentMode = LoginWidget::LeftForm;
  421. layoutToggleBtn->setIcon(leftAction->icon());
  422. updateLayoutPresentation();
  423. });
  424. QObject::connect(rightAction, &QAction::triggered, q_ptr, [this, rightAction]() {
  425. currentMode = LoginWidget::RightForm;
  426. layoutToggleBtn->setIcon(rightAction->icon());
  427. updateLayoutPresentation();
  428. });
  429. }
  430. void LoginWidgetPrivate::handleWindowResize(int newWidth)
  431. {
  432. LoginWidget::LayoutMode detectedMode = currentMode;
  433. if (newWidth < LAYOUT_SWITCH_THRESHOLD) {
  434. detectedMode = LoginWidget::CenterForm;
  435. }
  436. if (detectedMode != currentMode) {
  437. currentMode = detectedMode;
  438. updateLayoutPresentation();
  439. }
  440. layoutToggleBtn->setVisible(newWidth >= LAYOUT_SWITCH_THRESHOLD);
  441. if (currentMode == LoginWidget::CenterForm) {
  442. backgroundContainer->setGeometry(q_ptr->rect());
  443. }
  444. if (!controlPanel)
  445. return;
  446. const int margin = 10;
  447. controlPanel->move(newWidth - controlPanel->width() - margin, margin);
  448. }
  449. // Public class implementation
  450. LoginWidget::LoginWidget(QWidget *parent)
  451. : QWidget(parent)
  452. , d_ptr(new LoginWidgetPrivate(this))
  453. {
  454. Q_D(LoginWidget);
  455. d->initialize();
  456. }
  457. LoginWidget::~LoginWidget() = default;
  458. void LoginWidget::setConfig(const Config &config)
  459. {
  460. Q_D(LoginWidget);
  461. if (d->config.showSocialButtons != config.showSocialButtons
  462. || d->config.allowFormCustomization != config.allowFormCustomization) {
  463. d->config = config;
  464. d->createFormContainer();
  465. d->updateLayoutPresentation();
  466. }
  467. d->config = config;
  468. }
  469. LoginWidget::Config LoginWidget::config() const
  470. {
  471. Q_D(const LoginWidget);
  472. return d->config;
  473. }
  474. void LoginWidget::setCustomFormWidget(QWidget *widget)
  475. {
  476. Q_D(LoginWidget);
  477. if (d->customFormWidget) {
  478. d->customFormWidget->deleteLater();
  479. }
  480. d->customFormWidget = widget;
  481. if (d->config.allowFormCustomization) {
  482. d->createFormContainer();
  483. d->updateLayoutPresentation();
  484. }
  485. }
  486. QWidget *LoginWidget::takeCustomFormWidget()
  487. {
  488. Q_D(LoginWidget);
  489. QWidget *widget = d->customFormWidget;
  490. d->customFormWidget = nullptr;
  491. return widget;
  492. }
  493. QWidget *LoginWidget::formWidget() const
  494. {
  495. Q_D(const LoginWidget);
  496. return d->customFormWidget ? d->customFormWidget : d->formContainer;
  497. }
  498. void LoginWidget::resizeEvent(QResizeEvent *event)
  499. {
  500. Q_D(LoginWidget);
  501. QWidget::resizeEvent(event);
  502. d->handleWindowResize(event->size().width());
  503. }
  504. void LoginWidget::changeEvent(QEvent *event)
  505. {
  506. Q_D(LoginWidget);
  507. if (event->type() == QEvent::LanguageChange) {
  508. d->retranslateUi();
  509. }
  510. QWidget::changeEvent(event);
  511. }
  512. void LoginWidget::showEvent(QShowEvent *event)
  513. {
  514. Q_D(LoginWidget);
  515. QWidget::showEvent(event);
  516. d->handleWindowResize(width());
  517. }