From 111e4504acb5f1575b00d60cba8552c70db45c08 Mon Sep 17 00:00:00 2001
From: Adasauce <adasauce@with.parts>
Date: Tue, 18 Feb 2020 11:08:19 -0400
Subject: [PATCH] Replace QHBoxLayout with QFormLayout in UserSettings

This allows form fields to wrap dynamically when they are too long for
the view. This should help mobile and small screen users to configure
the app without having to force it into portrait mode, or not be able to
reach the settings fields at all.
---
 src/UserSettingsPage.cpp | 210 ++++++++++++++-------------------------
 src/UserSettingsPage.h   |   2 +
 2 files changed, 74 insertions(+), 138 deletions(-)

diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp
index 87d802b..495e5ae 100644
--- a/src/UserSettingsPage.cpp
+++ b/src/UserSettingsPage.cpp
@@ -18,6 +18,7 @@
 #include <QApplication>
 #include <QComboBox>
 #include <QFileDialog>
+#include <QFormLayout>
 #include <QInputDialog>
 #include <QLabel>
 #include <QLineEdit>
@@ -173,81 +174,43 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
         topBarLayout_->addWidget(backBtn_, 1, Qt::AlignLeft | Qt::AlignVCenter);
         topBarLayout_->addStretch(1);
 
-        auto addSetting = [this, &font](QString labelText) {
-                auto layout = new QHBoxLayout;
-                layout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
+        formLayout_ = new QFormLayout;
 
-                auto label = new QLabel(labelText, this);
-                label->setFont(font);
-
-                auto toggle = new Toggle(this);
-
-                layout->addWidget(label);
-                layout->addWidget(toggle, 0, Qt::AlignRight);
+        formLayout_->setLabelAlignment(Qt::AlignLeft);
+        formLayout_->setFormAlignment(Qt::AlignRight);
+        formLayout_->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
+        formLayout_->setRowWrapPolicy(QFormLayout::WrapLongRows);
+        formLayout_->setHorizontalSpacing(0);
 
-                return std::pair{layout, toggle};
-        };
+        auto general_ = new QLabel(tr("GENERAL"), this);
+        general_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
+        general_->setFont(font);
 
-        QHBoxLayout *trayOptionLayout_           = nullptr;
-        std::tie(trayOptionLayout_, trayToggle_) = addSetting(tr("Minimize to tray"));
+        trayToggle_        = new Toggle(this);
+        startInTrayToggle_ = new Toggle(this);
 
-        QHBoxLayout *startInTrayOptionLayout_                  = nullptr;
-        std::tie(startInTrayOptionLayout_, startInTrayToggle_) = addSetting(tr("Start in tray"));
         if (!settings_->isTrayEnabled())
                 startInTrayToggle_->setDisabled(true);
 
-        QHBoxLayout *groupViewLayout                = nullptr;
-        std::tie(groupViewLayout, groupViewToggle_) = addSetting(tr("Group's sidebar"));
-
-        QHBoxLayout *avatarViewLayout              = nullptr;
-        std::tie(avatarViewLayout, avatarCircles_) = addSetting(tr("Circular Avatars"));
-
-        QHBoxLayout *typingLayout                    = nullptr;
-        std::tie(typingLayout, typingNotifications_) = addSetting(tr("Typing notifications"));
-
-        QHBoxLayout *receiptsLayout             = nullptr;
-        std::tie(receiptsLayout, readReceipts_) = addSetting(tr("Read receipts"));
-
-        QHBoxLayout *markdownLayout                = nullptr;
-        std::tie(markdownLayout, markdownEnabled_) = addSetting(tr("Send messages as Markdown"));
-
-        QHBoxLayout *desktopLayout                     = nullptr;
-        std::tie(desktopLayout, desktopNotifications_) = addSetting(tr("Desktop notifications"));
-
-        auto scaleFactorOptionLayout = new QHBoxLayout;
-        scaleFactorOptionLayout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
-        auto scaleFactorLabel = new QLabel(tr("Scale factor"), this);
-        scaleFactorLabel->setFont(font);
-        scaleFactorCombo_ = new QComboBox(this);
+        avatarCircles_        = new Toggle(this);
+        groupViewToggle_      = new Toggle(this);
+        typingNotifications_  = new Toggle(this);
+        readReceipts_         = new Toggle(this);
+        markdownEnabled_      = new Toggle(this);
+        desktopNotifications_ = new Toggle(this);
+        scaleFactorCombo_     = new QComboBox(this);
         for (double option = 1; option <= 3; option += 0.25)
                 scaleFactorCombo_->addItem(QString::number(option));
-
-        scaleFactorOptionLayout->addWidget(scaleFactorLabel);
-        scaleFactorOptionLayout->addWidget(scaleFactorCombo_, 0, Qt::AlignRight);
-
-        auto fontSizeOptionLayout = new QHBoxLayout;
-        fontSizeOptionLayout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
-        auto fontSizeLabel = new QLabel(tr("Font size"), this);
-        fontSizeLabel->setFont(font);
         fontSizeCombo_ = new QComboBox(this);
         for (double option = 10; option < 17; option += 0.5)
                 fontSizeCombo_->addItem(QString("%1 ").arg(QString::number(option)));
 
-        fontSizeOptionLayout->addWidget(fontSizeLabel);
-        fontSizeOptionLayout->addWidget(fontSizeCombo_, 0, Qt::AlignRight);
-
-        auto fontFamilyOptionLayout      = new QHBoxLayout;
-        auto emojiFontFamilyOptionLayout = new QHBoxLayout;
-        fontFamilyOptionLayout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
-        emojiFontFamilyOptionLayout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
-        auto fontFamilyLabel  = new QLabel(tr("Font Family"), this);
-        auto emojiFamilyLabel = new QLabel(tr("Emoji Font Famly"), this);
-        fontFamilyLabel->setFont(font);
-        emojiFamilyLabel->setFont(font);
         fontSelectionCombo_      = new QComboBox(this);
         emojiFontSelectionCombo_ = new QComboBox(this);
+
         QFontDatabase fontDb;
         auto fontFamilies = fontDb.families();
+
         // TODO: Is there a way to limit to just emojis, rather than
         // all emoji fonts?
         auto emojiFamilies = fontDb.families(QFontDatabase::Symbol);
@@ -266,16 +229,6 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
         fontIndex = emojiFontSelectionCombo_->findText(settings_->emojiFont());
         emojiFontSelectionCombo_->setCurrentIndex(fontIndex);
 
-        fontFamilyOptionLayout->addWidget(fontFamilyLabel);
-        fontFamilyOptionLayout->addWidget(fontSelectionCombo_, 0, Qt::AlignRight);
-
-        emojiFontFamilyOptionLayout->addWidget(emojiFamilyLabel);
-        emojiFontFamilyOptionLayout->addWidget(emojiFontSelectionCombo_, 0, Qt::AlignRight);
-
-        auto themeOptionLayout_ = new QHBoxLayout;
-        themeOptionLayout_->setContentsMargins(0, OptionMargin, 0, OptionMargin);
-        auto themeLabel_ = new QLabel(tr("Theme"), this);
-        themeLabel_->setFont(font);
         themeCombo_ = new QComboBox(this);
         themeCombo_->addItem("Light");
         themeCombo_->addItem("Dark");
@@ -286,112 +239,87 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
         int themeIndex = themeCombo_->findText(themeStr);
         themeCombo_->setCurrentIndex(themeIndex);
 
-        themeOptionLayout_->addWidget(themeLabel_);
-        themeOptionLayout_->addWidget(themeCombo_, 0, Qt::AlignRight);
-
-        auto encryptionLayout_ = new QVBoxLayout;
-        encryptionLayout_->setContentsMargins(0, OptionMargin, 0, OptionMargin);
-        encryptionLayout_->setAlignment(Qt::AlignVCenter);
+        auto encryptionLabel_ = new QLabel(tr("ENCRYPTION"), this);
+        encryptionLabel_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
+        encryptionLabel_->setFont(font);
 
         QFont monospaceFont;
         monospaceFont.setFamily("Monospace");
         monospaceFont.setStyleHint(QFont::Monospace);
         monospaceFont.setPointSizeF(monospaceFont.pointSizeF() * 0.9);
 
-        auto deviceIdLayout = new QHBoxLayout;
-        deviceIdLayout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
-
-        auto deviceIdLabel = new QLabel(tr("Device ID"), this);
-        deviceIdLabel->setFont(font);
-        deviceIdLabel->setMargin(0);
         deviceIdValue_ = new QLabel{this};
         deviceIdValue_->setTextInteractionFlags(Qt::TextSelectableByMouse);
         deviceIdValue_->setFont(monospaceFont);
-        deviceIdLayout->addWidget(deviceIdLabel, 1);
-        deviceIdLayout->addWidget(deviceIdValue_);
 
-        auto deviceFingerprintLayout = new QHBoxLayout;
-        deviceFingerprintLayout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
-
-        auto deviceFingerprintLabel = new QLabel(tr("Device Fingerprint"), this);
-        deviceFingerprintLabel->setFont(font);
-        deviceFingerprintLabel->setMargin(0);
         deviceFingerprintValue_ = new QLabel{this};
         deviceFingerprintValue_->setTextInteractionFlags(Qt::TextSelectableByMouse);
         deviceFingerprintValue_->setFont(monospaceFont);
-        deviceFingerprintLayout->addWidget(deviceFingerprintLabel, 1);
-        deviceFingerprintLayout->addWidget(deviceFingerprintValue_);
 
-        auto sessionKeysLayout = new QHBoxLayout;
-        sessionKeysLayout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
         auto sessionKeysLabel = new QLabel(tr("Session Keys"), this);
         sessionKeysLabel->setFont(font);
-        sessionKeysLayout->addWidget(sessionKeysLabel, 1);
+        sessionKeysLabel->setMargin(OptionMargin);
 
         auto sessionKeysImportBtn = new QPushButton{tr("IMPORT"), this};
-        connect(
-          sessionKeysImportBtn, &QPushButton::clicked, this, &UserSettingsPage::importSessionKeys);
         auto sessionKeysExportBtn = new QPushButton{tr("EXPORT"), this};
-        connect(
-          sessionKeysExportBtn, &QPushButton::clicked, this, &UserSettingsPage::exportSessionKeys);
+
+        auto sessionKeysLayout = new QHBoxLayout(this);
+        sessionKeysLayout->addWidget(new QLabel("", this), 1, Qt::AlignRight);
         sessionKeysLayout->addWidget(sessionKeysExportBtn, 0, Qt::AlignRight);
         sessionKeysLayout->addWidget(sessionKeysImportBtn, 0, Qt::AlignRight);
 
-        encryptionLayout_->addLayout(deviceIdLayout);
-        encryptionLayout_->addLayout(deviceFingerprintLayout);
-        encryptionLayout_->addWidget(new HorizontalLine{this});
-        encryptionLayout_->addLayout(sessionKeysLayout);
+        auto boxWrap = [this, &font](QString labelText, QWidget *field) {
+                auto label = new QLabel(labelText, this);
+                label->setFont(font);
+                label->setMargin(OptionMargin);
 
-        font.setWeight(QFont::Medium);
+                auto layout = new QHBoxLayout(this);
+                layout->addWidget(field, 0, Qt::AlignRight);
 
-        auto encryptionLabel_ = new QLabel(tr("ENCRYPTION"), this);
-        encryptionLabel_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
-        encryptionLabel_->setFont(font);
+                formLayout_->addRow(label, layout);
+        };
 
-        auto general_ = new QLabel(tr("GENERAL"), this);
-        general_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
-        general_->setFont(font);
+        formLayout_->addRow(general_);
+        formLayout_->addRow(new HorizontalLine(this));
+        boxWrap(tr("Minimize to tray"), trayToggle_);
+        boxWrap(tr("Start in tray"), startInTrayToggle_);
+        formLayout_->addRow(new HorizontalLine{this});
+        boxWrap(tr("Circular Avatars"), avatarCircles_);
+        boxWrap(tr("Group's sidebar"), groupViewToggle_);
+        boxWrap(tr("Typing notifications"), typingNotifications_);
+        formLayout_->addRow(new HorizontalLine{this});
+        boxWrap(tr("Read receipts"), readReceipts_);
+        boxWrap(tr("Send messages as Markdown"), markdownEnabled_);
+        boxWrap(tr("Desktop notifications"), desktopNotifications_);
+        formLayout_->addRow(new QLabel("", this));
+        formLayout_->addRow(new HorizontalLine{this});
+        boxWrap(tr("Scale factor"), scaleFactorCombo_);
+        boxWrap(tr("Font size"), fontSizeCombo_);
+        formLayout_->addRow(new HorizontalLine(this));
+        boxWrap(tr("Font Family"), fontSelectionCombo_);
+        boxWrap(tr("Emoji Font Family"), emojiFontSelectionCombo_);
+        boxWrap(tr("Theme"), themeCombo_);
+        formLayout_->addRow(new QLabel("", this));
+        formLayout_->addRow(encryptionLabel_);
+        formLayout_->addRow(new HorizontalLine(this));
+        boxWrap(tr("Device ID"), deviceIdValue_);
+        boxWrap(tr("Device Fingerprint"), deviceFingerprintValue_);
+        formLayout_->addRow(new HorizontalLine(this));
+        formLayout_->addRow(sessionKeysLabel, sessionKeysLayout);
 
         mainLayout_ = new QVBoxLayout;
         mainLayout_->setAlignment(Qt::AlignTop);
         mainLayout_->setSpacing(7);
         mainLayout_->setContentsMargins(
           sideMargin_, LayoutTopMargin, sideMargin_, LayoutBottomMargin);
-        mainLayout_->addWidget(general_, 1, Qt::AlignLeft | Qt::AlignBottom);
-        mainLayout_->addWidget(new HorizontalLine(this));
-        mainLayout_->addLayout(trayOptionLayout_);
-        mainLayout_->addLayout(startInTrayOptionLayout_);
-        mainLayout_->addWidget(new HorizontalLine(this));
-        mainLayout_->addLayout(groupViewLayout);
-        mainLayout_->addLayout(avatarViewLayout);
-        mainLayout_->addWidget(new HorizontalLine(this));
-        mainLayout_->addLayout(typingLayout);
-        mainLayout_->addLayout(receiptsLayout);
-        mainLayout_->addLayout(markdownLayout);
-        mainLayout_->addLayout(desktopLayout);
-        mainLayout_->addWidget(new HorizontalLine(this));
+        mainLayout_->addLayout(formLayout_);
 
 #if defined(Q_OS_MAC)
-        scaleFactorLabel->hide();
+        // TODO: hide these with formlayout
         scaleFactorCombo_->hide();
-        emojiFamilyLabel->hide();
         emojiFontSelectionCombo_->hide();
 #endif
 
-        mainLayout_->addLayout(scaleFactorOptionLayout);
-        mainLayout_->addLayout(fontSizeOptionLayout);
-        mainLayout_->addLayout(fontFamilyOptionLayout);
-        mainLayout_->addLayout(emojiFontFamilyOptionLayout);
-        mainLayout_->addWidget(new HorizontalLine(this));
-        mainLayout_->addLayout(themeOptionLayout_);
-        mainLayout_->addWidget(new HorizontalLine(this));
-
-        mainLayout_->addSpacing(50);
-
-        mainLayout_->addWidget(encryptionLabel_, 1, Qt::AlignLeft | Qt::AlignBottom);
-        mainLayout_->addWidget(new HorizontalLine(this));
-        mainLayout_->addLayout(encryptionLayout_);
-
         auto scrollArea_ = new QScrollArea(this);
         scrollArea_->setFrameShape(QFrame::NoFrame);
         scrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@@ -467,6 +395,12 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
                 settings_->setDesktopNotifications(!isDisabled);
         });
 
+        connect(
+          sessionKeysImportBtn, &QPushButton::clicked, this, &UserSettingsPage::importSessionKeys);
+
+        connect(
+          sessionKeysExportBtn, &QPushButton::clicked, this, &UserSettingsPage::exportSessionKeys);
+
         connect(backBtn_, &QPushButton::clicked, this, [this]() {
                 settings_->save();
                 emit moveBack();
@@ -507,7 +441,7 @@ UserSettingsPage::resizeEvent(QResizeEvent *event)
         else
                 sideMargin_ = static_cast<double>(event->size().width() - contentMinWidth) / 2.;
 
-        if (sideMargin_ < 40)
+        if (sideMargin_ < 70)
                 sideMargin_ = 0;
 
         mainLayout_->setContentsMargins(
diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h
index 299905a..7b29007 100644
--- a/src/UserSettingsPage.h
+++ b/src/UserSettingsPage.h
@@ -19,6 +19,7 @@
 
 #include <QComboBox>
 #include <QFontDatabase>
+#include <QFormLayout>
 #include <QFrame>
 #include <QLabel>
 #include <QLayout>
@@ -166,6 +167,7 @@ private slots:
 private:
         // Layouts
         QVBoxLayout *topLayout_;
+        QFormLayout *formLayout_;
         QVBoxLayout *mainLayout_;
         QHBoxLayout *topBarLayout_;