loginwindow.cpp 18 KB

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