From 87fd94a2e02d076cca9f9be66226328f6bb87c48 Mon Sep 17 00:00:00 2001 From: Martchus Date: Mon, 4 Apr 2016 14:49:40 +0200 Subject: [PATCH] improved settings dialog API --- CMakeLists.txt | 9 + paletteeditor/colorbutton.cpp | 233 ++++++++++ paletteeditor/colorbutton.h | 53 +++ paletteeditor/paletteeditor.cpp | 561 +++++++++++++++++++++++ paletteeditor/paletteeditor.h | 178 +++++++ paletteeditor/paletteeditor.ui | 175 +++++++ settingsdialog/optionpage.cpp | 6 +- settingsdialog/optionpage.h | 65 ++- settingsdialog/optionpage.ui.template | 22 + settingsdialog/qtappearanceoptionpage.ui | 277 +++++++++++ settingsdialog/qtlanguageoptionpage.ui | 67 +++ settingsdialog/qtsettings.cpp | 145 ++++++ settingsdialog/qtsettings.h | 28 ++ settingsdialog/settingsdialog.cpp | 55 ++- settingsdialog/settingsdialog.h | 20 +- 15 files changed, 1880 insertions(+), 14 deletions(-) create mode 100644 paletteeditor/colorbutton.cpp create mode 100644 paletteeditor/colorbutton.h create mode 100644 paletteeditor/paletteeditor.cpp create mode 100644 paletteeditor/paletteeditor.h create mode 100644 paletteeditor/paletteeditor.ui create mode 100644 settingsdialog/optionpage.ui.template create mode 100644 settingsdialog/qtappearanceoptionpage.ui create mode 100644 settingsdialog/qtlanguageoptionpage.ui create mode 100644 settingsdialog/qtsettings.cpp create mode 100644 settingsdialog/qtsettings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f1daa48..d780f03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,12 +18,15 @@ set(HEADER_FILES settingsdialog/optioncategorymodel.h settingsdialog/optionpage.h settingsdialog/settingsdialog.h + settingsdialog/qtsettings.h widgets/buttonoverlay.h widgets/clearcombobox.h widgets/clearlineedit.h widgets/clearplaintextedit.h widgets/clearspinbox.h widgets/iconbutton.h + paletteeditor/paletteeditor.h + paletteeditor/colorbutton.h ) set(SRC_FILES aboutdialog/aboutdialog.cpp @@ -38,18 +41,24 @@ set(SRC_FILES settingsdialog/optioncategorymodel.cpp settingsdialog/optionpage.cpp settingsdialog/settingsdialog.cpp + settingsdialog/qtsettings.cpp widgets/buttonoverlay.cpp widgets/clearcombobox.cpp widgets/clearlineedit.cpp widgets/clearplaintextedit.cpp widgets/clearspinbox.cpp widgets/iconbutton.cpp + paletteeditor/paletteeditor.cpp + paletteeditor/colorbutton.cpp resources/qtutilsicons.qrc ) set(WIDGETS_UI_FILES aboutdialog/aboutdialog.ui enterpassworddialog/enterpassworddialog.ui settingsdialog/settingsdialog.ui + settingsdialog/qtappearanceoptionpage.ui + settingsdialog/qtlanguageoptionpage.ui + paletteeditor/paletteeditor.ui ) # meta data diff --git a/paletteeditor/colorbutton.cpp b/paletteeditor/colorbutton.cpp new file mode 100644 index 0000000..9e0c198 --- /dev/null +++ b/paletteeditor/colorbutton.cpp @@ -0,0 +1,233 @@ +#include "./colorbutton.h" + +#include +#include +#include +#include +#include +#include + +namespace Widgets { + +class ColorButtonPrivate +{ + ColorButton *q_ptr; + Q_DECLARE_PUBLIC(ColorButton) +public: + QColor m_color; +#ifndef QT_NO_DRAGANDDROP + QColor m_dragColor; + QPoint m_dragStart; + bool m_dragging; +#endif + bool m_backgroundCheckered; + + void slotEditColor(); + QColor shownColor() const; + QPixmap generatePixmap() const; +}; + +void ColorButtonPrivate::slotEditColor() +{ + const QColor newColor = QColorDialog::getColor(m_color, q_ptr, QString(), QColorDialog::ShowAlphaChannel); + if (!newColor.isValid() || newColor == q_ptr->color()) + return; + q_ptr->setColor(newColor); + emit q_ptr->colorChanged(m_color); +} + +QColor ColorButtonPrivate::shownColor() const +{ +#ifndef QT_NO_DRAGANDDROP + if (m_dragging) + return m_dragColor; +#endif + return m_color; +} + +QPixmap ColorButtonPrivate::generatePixmap() const +{ + QPixmap pix(24, 24); + + int pixSize = 20; + QBrush br(shownColor()); + + QPixmap pm(2 * pixSize, 2 * pixSize); + QPainter pmp(&pm); + pmp.fillRect(0, 0, pixSize, pixSize, Qt::lightGray); + pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::lightGray); + pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::darkGray); + pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::darkGray); + pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, shownColor()); + br = QBrush(pm); + + QPainter p(&pix); + int corr = 1; + QRect r = pix.rect().adjusted(corr, corr, -corr, -corr); + p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr); + p.fillRect(r, br); + + p.fillRect(r.width() / 4 + corr, r.height() / 4 + corr, + r.width() / 2, r.height() / 2, + QColor(shownColor().rgb())); + p.drawRect(pix.rect().adjusted(0, 0, -1, -1)); + + return pix; +} + +/////////////// + +ColorButton::ColorButton(QWidget *parent) + : QToolButton(parent), d_ptr(new ColorButtonPrivate) +{ + d_ptr->q_ptr = this; + d_ptr->m_dragging = false; + d_ptr->m_backgroundCheckered = true; + + setAcceptDrops(true); + + connect(this, SIGNAL(clicked()), this, SLOT(slotEditColor())); + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred)); +} + +ColorButton::~ColorButton() +{ +} + +void ColorButton::setColor(const QColor &color) +{ + if (d_ptr->m_color == color) + return; + d_ptr->m_color = color; + update(); +} + +QColor ColorButton::color() const +{ + return d_ptr->m_color; +} + +void ColorButton::setBackgroundCheckered(bool checkered) +{ + if (d_ptr->m_backgroundCheckered == checkered) + return; + d_ptr->m_backgroundCheckered = checkered; + update(); +} + +bool ColorButton::isBackgroundCheckered() const +{ + return d_ptr->m_backgroundCheckered; +} + +void ColorButton::paintEvent(QPaintEvent *event) +{ + QToolButton::paintEvent(event); + if (!isEnabled()) + return; + + const int pixSize = 10; + QBrush br(d_ptr->shownColor()); + if (d_ptr->m_backgroundCheckered) { + QPixmap pm(2 * pixSize, 2 * pixSize); + QPainter pmp(&pm); + pmp.fillRect(0, 0, pixSize, pixSize, Qt::white); + pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::white); + pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::black); + pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::black); + pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, d_ptr->shownColor()); + br = QBrush(pm); + } + + QPainter p(this); + const int corr = 4; + QRect r = rect().adjusted(corr, corr, -corr, -corr); + p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr); + p.fillRect(r, br); + + //const int adjX = qRound(r.width() / 4.0); + //const int adjY = qRound(r.height() / 4.0); + //p.fillRect(r.adjusted(adjX, adjY, -adjX, -adjY), + // QColor(d_ptr->shownColor().rgb())); + /* + p.fillRect(r.adjusted(0, r.height() * 3 / 4, 0, 0), + QColor(d_ptr->shownColor().rgb())); + p.fillRect(r.adjusted(0, 0, 0, -r.height() * 3 / 4), + QColor(d_ptr->shownColor().rgb())); + */ + /* + const QColor frameColor0(0, 0, 0, qRound(0.2 * (0xFF - d_ptr->shownColor().alpha()))); + p.setPen(frameColor0); + p.drawRect(r.adjusted(adjX, adjY, -adjX - 1, -adjY - 1)); + */ + + const QColor frameColor1(0, 0, 0, 26); + p.setPen(frameColor1); + p.drawRect(r.adjusted(1, 1, -2, -2)); + const QColor frameColor2(0, 0, 0, 51); + p.setPen(frameColor2); + p.drawRect(r.adjusted(0, 0, -1, -1)); +} + +void ColorButton::mousePressEvent(QMouseEvent *event) +{ +#ifndef QT_NO_DRAGANDDROP + if (event->button() == Qt::LeftButton) + d_ptr->m_dragStart = event->pos(); +#endif + QToolButton::mousePressEvent(event); +} + +void ColorButton::mouseMoveEvent(QMouseEvent *event) +{ +#ifndef QT_NO_DRAGANDDROP + if (event->buttons() & Qt::LeftButton && + (d_ptr->m_dragStart - event->pos()).manhattanLength() > QApplication::startDragDistance()) { + QMimeData *mime = new QMimeData; + mime->setColorData(color()); + QDrag *drg = new QDrag(this); + drg->setMimeData(mime); + drg->setPixmap(d_ptr->generatePixmap()); + setDown(false); + event->accept(); + drg->start(); + return; + } +#endif + QToolButton::mouseMoveEvent(event); +} + +#ifndef QT_NO_DRAGANDDROP +void ColorButton::dragEnterEvent(QDragEnterEvent *event) +{ + const QMimeData *mime = event->mimeData(); + if (!mime->hasColor()) + return; + + event->accept(); + d_ptr->m_dragColor = qvariant_cast(mime->colorData()); + d_ptr->m_dragging = true; + update(); +} + +void ColorButton::dragLeaveEvent(QDragLeaveEvent *event) +{ + event->accept(); + d_ptr->m_dragging = false; + update(); +} + +void ColorButton::dropEvent(QDropEvent *event) +{ + event->accept(); + d_ptr->m_dragging = false; + if (d_ptr->m_dragColor == color()) + return; + setColor(d_ptr->m_dragColor); + emit colorChanged(color()); +} +#endif + +} + +#include "moc_colorbutton.cpp" diff --git a/paletteeditor/colorbutton.h b/paletteeditor/colorbutton.h new file mode 100644 index 0000000..e6df392 --- /dev/null +++ b/paletteeditor/colorbutton.h @@ -0,0 +1,53 @@ +#ifndef COLORBUTTON_H +#define COLORBUTTON_H + +#include + +#include + +namespace Widgets { + +/*! + * \brief The ColorButton class is used by PaletteEditor. + * + * This is taken from qttools/src/shared/qtgradienteditor/qtcolorbutton.h. + */ +class LIB_EXPORT ColorButton : public QToolButton +{ + Q_OBJECT + Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered) +public: + ColorButton(QWidget *parent = nullptr); + ~ColorButton(); + + bool isBackgroundCheckered() const; + void setBackgroundCheckered(bool checkered); + + QColor color() const; + +public slots: + void setColor(const QColor &color); + +signals: + void colorChanged(const QColor &color); + +protected: + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); +#ifndef QT_NO_DRAGANDDROP + void dragEnterEvent(QDragEnterEvent *event); + void dragLeaveEvent(QDragLeaveEvent *event); + void dropEvent(QDropEvent *event); +#endif + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(ColorButton) + Q_DISABLE_COPY(ColorButton) + Q_PRIVATE_SLOT(d_func(), void slotEditColor()) +}; + +} + +#endif diff --git a/paletteeditor/paletteeditor.cpp b/paletteeditor/paletteeditor.cpp new file mode 100644 index 0000000..f0bfe99 --- /dev/null +++ b/paletteeditor/paletteeditor.cpp @@ -0,0 +1,561 @@ +#include "./paletteeditor.h" +#include "./colorbutton.h" + +#include +#include +#include +#include +#include + +using namespace Widgets; + +namespace Dialogs { + +enum { BrushRole = 33 }; + +PaletteEditor::PaletteEditor(QWidget *parent) : + QDialog(parent), + m_currentColorGroup(QPalette::Active), + m_paletteModel(new PaletteModel(this)), + m_modelUpdated(false), + m_paletteUpdated(false), + m_compute(true) +{ + m_ui.setupUi(this); + m_ui.paletteView->setModel(m_paletteModel); + updatePreviewPalette(); + updateStyledButton(); + m_ui.paletteView->setModel(m_paletteModel); + ColorDelegate *delegate = new ColorDelegate(this); + m_ui.paletteView->setItemDelegate(delegate); + m_ui.paletteView->setEditTriggers(QAbstractItemView::AllEditTriggers); + connect(m_paletteModel, &PaletteModel::paletteChanged, + this, &PaletteEditor::paletteChanged); + m_ui.paletteView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_ui.paletteView->setDragEnabled(true); + m_ui.paletteView->setDropIndicatorShown(true); + m_ui.paletteView->setRootIsDecorated(false); + m_ui.paletteView->setColumnHidden(2, true); + m_ui.paletteView->setColumnHidden(3, true); +} + +PaletteEditor::~PaletteEditor() +{ +} + +QPalette PaletteEditor::palette() const +{ + return m_editPalette; +} + +void PaletteEditor::setPalette(const QPalette &palette) +{ + m_editPalette = palette; + const uint mask = palette.resolve(); + for (int i = 0; i < (int)QPalette::NColorRoles; i++) { + if (!(mask & (1 << i))) { + m_editPalette.setBrush(QPalette::Active, static_cast(i), + m_parentPalette.brush(QPalette::Active, static_cast(i))); + m_editPalette.setBrush(QPalette::Inactive, static_cast(i), + m_parentPalette.brush(QPalette::Inactive, static_cast(i))); + m_editPalette.setBrush(QPalette::Disabled, static_cast(i), + m_parentPalette.brush(QPalette::Disabled, static_cast(i))); + } + } + m_editPalette.resolve(mask); + updatePreviewPalette(); + updateStyledButton(); + m_paletteUpdated = true; + if (!m_modelUpdated) + m_paletteModel->setPalette(m_editPalette, m_parentPalette); + m_paletteUpdated = false; +} + +void PaletteEditor::setPalette(const QPalette &palette, const QPalette &parentPalette) +{ + m_parentPalette = parentPalette; + setPalette(palette); +} + +void PaletteEditor::on_buildButton_colorChanged(const QColor &) +{ + buildPalette(); +} + +void PaletteEditor::on_activeRadio_clicked() +{ + m_currentColorGroup = QPalette::Active; + updatePreviewPalette(); +} + +void PaletteEditor::on_inactiveRadio_clicked() +{ + m_currentColorGroup = QPalette::Inactive; + updatePreviewPalette(); +} + +void PaletteEditor::on_disabledRadio_clicked() +{ + m_currentColorGroup = QPalette::Disabled; + updatePreviewPalette(); +} + +void PaletteEditor::on_computeRadio_clicked() +{ + if (m_compute) + return; + m_ui.paletteView->setColumnHidden(2, true); + m_ui.paletteView->setColumnHidden(3, true); + m_compute = true; + m_paletteModel->setCompute(true); +} + +void PaletteEditor::on_detailsRadio_clicked() +{ + if (!m_compute) + return; + const int w = m_ui.paletteView->columnWidth(1); + m_ui.paletteView->setColumnHidden(2, false); + m_ui.paletteView->setColumnHidden(3, false); + QHeaderView *header = m_ui.paletteView->header(); + header->resizeSection(1, w / 3); + header->resizeSection(2, w / 3); + header->resizeSection(3, w / 3); + m_compute = false; + m_paletteModel->setCompute(false); +} + +void PaletteEditor::paletteChanged(const QPalette &palette) +{ + m_modelUpdated = true; + if (!m_paletteUpdated) + setPalette(palette); + m_modelUpdated = false; +} + +void PaletteEditor::buildPalette() +{ + const QColor btn = m_ui.buildButton->color(); + const QPalette temp = QPalette(btn); + setPalette(temp); +} + +void PaletteEditor::updatePreviewPalette() +{ + const QPalette::ColorGroup g = currentColorGroup(); + // build the preview palette + const QPalette currentPalette = palette(); + QPalette previewPalette; + for(int i = QPalette::WindowText; i < QPalette::NColorRoles; ++i) { + const QPalette::ColorRole r = static_cast(i); + const QBrush br = currentPalette.brush(g, r); + previewPalette.setBrush(QPalette::Active, r, br); + previewPalette.setBrush(QPalette::Inactive, r, br); + previewPalette.setBrush(QPalette::Disabled, r, br); + } +} + +void PaletteEditor::updateStyledButton() +{ + m_ui.buildButton->setColor(palette().color(QPalette::Active, QPalette::Button)); +} + +QPalette PaletteEditor::getPalette(QWidget *parent, const QPalette &init, + const QPalette &parentPal, int *ok) +{ + PaletteEditor dlg(parent); + QPalette parentPalette(parentPal); + uint mask = init.resolve(); + for (int i = 0; i < (int)QPalette::NColorRoles; ++i) { + if (!(mask & (1 << i))) { + parentPalette.setBrush(QPalette::Active, static_cast(i), + init.brush(QPalette::Active, static_cast(i))); + parentPalette.setBrush(QPalette::Inactive, static_cast(i), + init.brush(QPalette::Inactive, static_cast(i))); + parentPalette.setBrush(QPalette::Disabled, static_cast(i), + init.brush(QPalette::Disabled, static_cast(i))); + } + } + dlg.setPalette(init, parentPalette); + + const int result = dlg.exec(); + if (ok) *ok = result; + + return result == QDialog::Accepted ? dlg.palette() : init; +} + +////////////////////// + +PaletteModel::PaletteModel(QObject *parent) : + QAbstractTableModel(parent), + m_compute(true) +{ + const QMetaObject *meta = metaObject(); + const int index = meta->indexOfProperty("colorRole"); + const QMetaProperty p = meta->property(index); + const QMetaEnum e = p.enumerator(); + for (int r = QPalette::WindowText; r < QPalette::NColorRoles; r++) { + m_roleNames[static_cast(r)] = QLatin1String(e.key(r)); + } +} + +int PaletteModel::rowCount(const QModelIndex &) const +{ + return m_roleNames.count(); +} + +int PaletteModel::columnCount(const QModelIndex &) const +{ + return 4; +} + +QVariant PaletteModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + if (index.row() < 0 || index.row() >= QPalette::NColorRoles) + return QVariant(); + if (index.column() < 0 || index.column() >= 4) + return QVariant(); + + if (index.column() == 0) { + if (role == Qt::DisplayRole) + return m_roleNames[static_cast(index.row())]; + if (role == Qt::EditRole) { + const uint mask = m_palette.resolve(); + if (mask & (1 << index.row())) + return true; + return false; + } + return QVariant(); + } + if (role == BrushRole) + return m_palette.brush(columnToGroup(index.column()), + static_cast(index.row())); + return QVariant(); +} + +bool PaletteModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid()) + return false; + + if (index.column() != 0 && role == BrushRole) { + const QBrush br = qvariant_cast(value); + const QPalette::ColorRole r = static_cast(index.row()); + const QPalette::ColorGroup g = columnToGroup(index.column()); + m_palette.setBrush(g, r, br); + + QModelIndex idxBegin = PaletteModel::index(r, 0); + QModelIndex idxEnd = PaletteModel::index(r, 3); + if (m_compute) { + m_palette.setBrush(QPalette::Inactive, r, br); + switch (r) { + case QPalette::WindowText: + case QPalette::Text: + case QPalette::ButtonText: + case QPalette::Base: + break; + case QPalette::Dark: + m_palette.setBrush(QPalette::Disabled, QPalette::WindowText, br); + m_palette.setBrush(QPalette::Disabled, QPalette::Dark, br); + m_palette.setBrush(QPalette::Disabled, QPalette::Text, br); + m_palette.setBrush(QPalette::Disabled, QPalette::ButtonText, br); + idxBegin = PaletteModel::index(0, 0); + idxEnd = PaletteModel::index(m_roleNames.count() - 1, 3); + break; + case QPalette::Window: + m_palette.setBrush(QPalette::Disabled, QPalette::Base, br); + m_palette.setBrush(QPalette::Disabled, QPalette::Window, br); + idxBegin = PaletteModel::index(QPalette::Base, 0); + break; + case QPalette::Highlight: + //m_palette.setBrush(QPalette::Disabled, QPalette::Highlight, c.dark(120)); + break; + default: + m_palette.setBrush(QPalette::Disabled, r, br); + break; + } + } + emit paletteChanged(m_palette); + emit dataChanged(idxBegin, idxEnd); + return true; + } + if (index.column() == 0 && role == Qt::EditRole) { + uint mask = m_palette.resolve(); + const bool isMask = qvariant_cast(value); + const int r = index.row(); + if (isMask) + mask |= (1 << r); + else { + m_palette.setBrush(QPalette::Active, static_cast(r), + m_parentPalette.brush(QPalette::Active, static_cast(r))); + m_palette.setBrush(QPalette::Inactive, static_cast(r), + m_parentPalette.brush(QPalette::Inactive, static_cast(r))); + m_palette.setBrush(QPalette::Disabled, static_cast(r), + m_parentPalette.brush(QPalette::Disabled, static_cast(r))); + + mask &= ~(1 << index.row()); + } + m_palette.resolve(mask); + emit paletteChanged(m_palette); + const QModelIndex idxEnd = PaletteModel::index(r, 3); + emit dataChanged(index, idxEnd); + return true; + } + return false; +} + +Qt::ItemFlags PaletteModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemIsEnabled; + return Qt::ItemIsEditable | Qt::ItemIsEnabled; +} + +QVariant PaletteModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + if (section == 0) + return tr("Color Role"); + if (section == groupToColumn(QPalette::Active)) + return tr("Active"); + if (section == groupToColumn(QPalette::Inactive)) + return tr("Inactive"); + if (section == groupToColumn(QPalette::Disabled)) + return tr("Disabled"); + } + return QVariant(); +} + +QPalette PaletteModel::getPalette() const +{ + return m_palette; +} + +void PaletteModel::setPalette(const QPalette &palette, const QPalette &parentPalette) +{ + m_parentPalette = parentPalette; + m_palette = palette; + const QModelIndex idxBegin = index(0, 0); + const QModelIndex idxEnd = index(m_roleNames.count() - 1, 3); + emit dataChanged(idxBegin, idxEnd); +} + +QPalette::ColorGroup PaletteModel::columnToGroup(int index) const +{ + if (index == 1) + return QPalette::Active; + if (index == 2) + return QPalette::Inactive; + return QPalette::Disabled; +} + +int PaletteModel::groupToColumn(QPalette::ColorGroup group) const +{ + if (group == QPalette::Active) + return 1; + if (group == QPalette::Inactive) + return 2; + return 3; +} + +////////////////////////// + +BrushEditor::BrushEditor(QWidget *parent) : + QWidget(parent), + m_button(new ColorButton(this)), + m_changed(false) +{ + QLayout *layout = new QHBoxLayout(this); + layout->setMargin(0); + layout->addWidget(m_button); + connect(m_button, &ColorButton::colorChanged, this, &BrushEditor::brushChanged); + setFocusProxy(m_button); +} + +void BrushEditor::setBrush(const QBrush &brush) +{ + m_button->setColor(brush.color()); + m_changed = false; +} + +QBrush BrushEditor::brush() const +{ + return QBrush(m_button->color()); +} + +void BrushEditor::brushChanged() +{ + m_changed = true; + emit changed(this); +} + +bool BrushEditor::changed() const +{ + return m_changed; +} + +////////////////////////// + +RoleEditor::RoleEditor(QWidget *parent) : + QWidget(parent), + m_label(new QLabel(this)), + m_edited(false) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + + layout->addWidget(m_label); + m_label->setAutoFillBackground(true); + m_label->setIndent(3); // ### hardcode it should have the same value of textMargin in QItemDelegate + setFocusProxy(m_label); + + QToolButton *button = new QToolButton(this); + button->setToolButtonStyle(Qt::ToolButtonIconOnly); + button->setIcon(QIcon(QStringLiteral(":/qtutilities/icons/hicolor/16x16/actions/edit-clear.png"))); + button->setIconSize(QSize(8,8)); + button->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding)); + layout->addWidget(button); + connect(button, &QAbstractButton::clicked, this, &RoleEditor::emitResetProperty); +} + +void RoleEditor::setLabel(const QString &label) +{ + m_label->setText(label); +} + +void RoleEditor::setEdited(bool on) +{ + QFont font; + if (on == true) { + font.setBold(on); + } + m_label->setFont(font); + m_edited = on; +} + +bool RoleEditor::edited() const +{ + return m_edited; +} + +void RoleEditor::emitResetProperty() +{ + setEdited(false); + emit changed(this); +} + +////////////////////////// +ColorDelegate::ColorDelegate(QObject *parent) : + QItemDelegate(parent) +{ +} + +QWidget *ColorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const +{ + QWidget *ed = nullptr; + if (index.column() == 0) { + RoleEditor *editor = new RoleEditor(parent); + connect(editor, &RoleEditor::changed, this, &ColorDelegate::commitData); + //editor->setFocusPolicy(Qt::NoFocus); + //editor->installEventFilter(const_cast(this)); + ed = editor; + } else { + typedef void (BrushEditor::*BrushEditorWidgetSignal)(QWidget *); + + BrushEditor *editor = new BrushEditor(parent); + connect(editor, static_cast(&BrushEditor::changed), + this, &ColorDelegate::commitData); + editor->setFocusPolicy(Qt::NoFocus); + editor->installEventFilter(const_cast(this)); + ed = editor; + } + return ed; +} + +void ColorDelegate::setEditorData(QWidget *ed, const QModelIndex &index) const +{ + if (index.column() == 0) { + const bool mask = qvariant_cast(index.model()->data(index, Qt::EditRole)); + RoleEditor *editor = static_cast(ed); + editor->setEdited(mask); + const QString colorName = qvariant_cast(index.model()->data(index, Qt::DisplayRole)); + editor->setLabel(colorName); + } else { + const QBrush br = qvariant_cast(index.model()->data(index, BrushRole)); + BrushEditor *editor = static_cast(ed); + editor->setBrush(br); + } +} + +void ColorDelegate::setModelData(QWidget *ed, QAbstractItemModel *model, + const QModelIndex &index) const +{ + if (index.column() == 0) { + RoleEditor *editor = static_cast(ed); + const bool mask = editor->edited(); + model->setData(index, mask, Qt::EditRole); + } else { + BrushEditor *editor = static_cast(ed); + if (editor->changed()) { + QBrush br = editor->brush(); + model->setData(index, br, BrushRole); + } + } +} + +void ColorDelegate::updateEditorGeometry(QWidget *ed, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QItemDelegate::updateEditorGeometry(ed, option, index); + ed->setGeometry(ed->geometry().adjusted(0, 0, -1, -1)); +} + +void ColorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const +{ + QStyleOptionViewItem option = opt; + const bool mask = qvariant_cast(index.model()->data(index, Qt::EditRole)); + if (index.column() == 0 && mask) { + option.font.setBold(true); + } + QBrush br = qvariant_cast(index.model()->data(index, BrushRole)); + if (br.style() == Qt::LinearGradientPattern || + br.style() == Qt::RadialGradientPattern || + br.style() == Qt::ConicalGradientPattern) { + painter->save(); + painter->translate(option.rect.x(), option.rect.y()); + painter->scale(option.rect.width(), option.rect.height()); + QGradient gr = *(br.gradient()); + gr.setCoordinateMode(QGradient::LogicalMode); + br = QBrush(gr); + painter->fillRect(0, 0, 1, 1, br); + painter->restore(); + } else { + painter->save(); + painter->setBrushOrigin(option.rect.x(), option.rect.y()); + painter->fillRect(option.rect, br); + painter->restore(); + } + QItemDelegate::paint(painter, option, index); + + + const QColor color = static_cast(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &option)); + const QPen oldPen = painter->pen(); + painter->setPen(QPen(color)); + + painter->drawLine(option.rect.right(), option.rect.y(), + option.rect.right(), option.rect.bottom()); + painter->drawLine(option.rect.x(), option.rect.bottom(), + option.rect.right(), option.rect.bottom()); + painter->setPen(oldPen); +} + +QSize ColorDelegate::sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const +{ + return QItemDelegate::sizeHint(opt, index) + QSize(4, 4); +} + +} diff --git a/paletteeditor/paletteeditor.h b/paletteeditor/paletteeditor.h new file mode 100644 index 0000000..94676a6 --- /dev/null +++ b/paletteeditor/paletteeditor.h @@ -0,0 +1,178 @@ +#ifndef PALETTEEDITOR_H +#define PALETTEEDITOR_H + +#include "ui_paletteeditor.h" + +#include + +#include + +QT_FORWARD_DECLARE_CLASS(QListView) +QT_FORWARD_DECLARE_CLASS(QLabel) + +namespace Widgets { +class ColorButton; +} + +namespace Dialogs { + +/*! + * \brief The PaletteEditor class provides a dialog to customize a QPalette. + * + * This is taken from qttools/src/designer/src/components/propertyeditor/paletteeditor.cpp. + * In contrast to the original version this version doesn't provide a preview. + */ +class LIB_EXPORT PaletteEditor : public QDialog +{ + Q_OBJECT +public: + PaletteEditor(QWidget *parent); + ~PaletteEditor(); + + static QPalette getPalette(QWidget *parent, const QPalette &init = QPalette(), + const QPalette &parentPal = QPalette(), int *result = nullptr); + + QPalette palette() const; + void setPalette(const QPalette &palette); + void setPalette(const QPalette &palette, const QPalette &parentPalette); + +private slots: + void on_buildButton_colorChanged(const QColor &); + void on_activeRadio_clicked(); + void on_inactiveRadio_clicked(); + void on_disabledRadio_clicked(); + void on_computeRadio_clicked(); + void on_detailsRadio_clicked(); + + void paletteChanged(const QPalette &palette); + +private: + void buildPalette(); + + void updatePreviewPalette(); + void updateStyledButton(); + + QPalette::ColorGroup currentColorGroup() const + { + return m_currentColorGroup; + } + + Ui::PaletteEditor m_ui; + QPalette m_editPalette; + QPalette m_parentPalette; + QPalette::ColorGroup m_currentColorGroup; + class PaletteModel *m_paletteModel; + bool m_modelUpdated; + bool m_paletteUpdated; + bool m_compute; +}; + +/*! + * \brief The PaletteModel class is used by PaletteEditor. + */ +class LIB_EXPORT PaletteModel : public QAbstractTableModel +{ + Q_OBJECT + Q_PROPERTY(QPalette::ColorRole colorRole READ colorRole) +public: + explicit PaletteModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role); + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + QPalette getPalette() const; + void setPalette(const QPalette &palette, const QPalette &parentPalette); + + QPalette::ColorRole colorRole() const { return QPalette::NoRole; } + void setCompute(bool on) { m_compute = on; } +signals: + void paletteChanged(const QPalette &palette); +private: + + QPalette::ColorGroup columnToGroup(int index) const; + int groupToColumn(QPalette::ColorGroup group) const; + + QPalette m_palette; + QPalette m_parentPalette; + QMap m_roleNames; + bool m_compute; +}; + +/*! + * \brief The BrushEditor class is used by PaletteEditor. + */ +class LIB_EXPORT BrushEditor : public QWidget +{ + Q_OBJECT +public: + explicit BrushEditor(QWidget *parent = nullptr); + + void setBrush(const QBrush &brush); + QBrush brush() const; + bool changed() const; +signals: + void changed(QWidget *widget); +private slots: + void brushChanged(); +private: + Widgets::ColorButton *m_button; + bool m_changed; +}; + +/*! + * \brief The RoleEditor class is used by PaletteEditor. + */ +class LIB_EXPORT RoleEditor : public QWidget +{ + Q_OBJECT +public: + explicit RoleEditor(QWidget *parent = nullptr); + + void setLabel(const QString &label); + void setEdited(bool on); + bool edited() const; + +signals: + void changed(QWidget *widget); + +private slots: + void emitResetProperty(); + +private: + QLabel *m_label; + bool m_edited; +}; + +/*! + * \brief The ColorDelegate class is used by PaletteEditor. + */ +class LIB_EXPORT ColorDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + explicit ColorDelegate(QObject *parent = nullptr); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *ed, const QModelIndex &index) const; + void setModelData(QWidget *ed, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *ed, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &opt, + const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const Q_DECL_OVERRIDE; +}; + +} + +#endif // PALETTEEDITOR_H diff --git a/paletteeditor/paletteeditor.ui b/paletteeditor/paletteeditor.ui new file mode 100644 index 0000000..3f5adb2 --- /dev/null +++ b/paletteeditor/paletteeditor.ui @@ -0,0 +1,175 @@ + + + Dialogs::PaletteEditor + + + + 0 + 0 + 365 + 409 + + + + + 0 + 0 + + + + Edit Palette + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Tune Palette + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + + 0 + 200 + + + + + + + + Show Detai&ls + + + + + + + &Compute Details + + + true + + + + + + + Quick + + + + + + + + 0 + 0 + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + Widgets::ColorButton + QToolButton +
paletteeditor/colorbutton.h
+
+
+ + + + buttonBox + accepted() + Dialogs::PaletteEditor + accept() + + + 180 + 331 + + + 134 + 341 + + + + + buttonBox + rejected() + Dialogs::PaletteEditor + reject() + + + 287 + 329 + + + 302 + 342 + + + + +
diff --git a/settingsdialog/optionpage.cpp b/settingsdialog/optionpage.cpp index 3a2862c..54955bf 100644 --- a/settingsdialog/optionpage.cpp +++ b/settingsdialog/optionpage.cpp @@ -60,13 +60,11 @@ bool OptionPage::matches(const QString &searchKeyWord) if(searchKeyWord.isEmpty()) { return true; } - if(displayName().contains(searchKeyWord, Qt::CaseInsensitive)) { - return true; - } if(!m_keywordsInitialized) { if(!m_widget) { m_widget.reset(setupWidget()); // ensure widget has been created } + m_keywords << m_widget->windowTitle(); // find common subwidgets for(const QLabel *label : m_widget->findChildren()) m_keywords << label->text(); @@ -81,7 +79,7 @@ bool OptionPage::matches(const QString &searchKeyWord) m_keywordsInitialized = true; } for(const QString &keyword : m_keywords) - if (keyword.contains(searchKeyWord, Qt::CaseInsensitive)) + if(keyword.contains(searchKeyWord, Qt::CaseInsensitive)) return true; return false; } diff --git a/settingsdialog/optionpage.h b/settingsdialog/optionpage.h index f77956c..56d5c39 100644 --- a/settingsdialog/optionpage.h +++ b/settingsdialog/optionpage.h @@ -17,15 +17,16 @@ public: virtual ~OptionPage(); QWidget *parentWindow() const; - virtual QString displayName() const = 0; QWidget *widget(); bool hasBeenShown() const; virtual bool apply() = 0; virtual void reset() = 0; bool matches(const QString &searchKeyWord); + const QStringList &errors() const; protected: virtual QWidget *setupWidget() = 0; + QStringList &errors(); private: std::unique_ptr m_widget; @@ -33,6 +34,7 @@ private: bool m_shown; bool m_keywordsInitialized; QStringList m_keywords; + QStringList m_errors; }; /*! @@ -51,6 +53,25 @@ inline bool OptionPage::hasBeenShown() const return m_widget != nullptr && m_shown; } +/*! + * \brief Returns the errors which haven been occurred when applying the changes. + */ +inline const QStringList &OptionPage::errors() const +{ + return m_errors; +} + +/*! + * \brief Returns the errors which haven been occurred when applying the changes. + * + * Error messages should be added when implementing apply() and something goes wrong. + * In this case, apply() should return false. + */ +inline QStringList &OptionPage::errors() +{ + return m_errors; +} + /*! * \class Dialogs::UiFileBasedOptionPage * \brief The UiFileBasedOptionPage class is the base class for SettingsDialog pages using UI files @@ -65,7 +86,6 @@ public: explicit UiFileBasedOptionPage(QWidget *parentWindow = nullptr); virtual ~UiFileBasedOptionPage(); - virtual QString displayName() const = 0; virtual bool apply() = 0; virtual void reset() = 0; @@ -114,4 +134,45 @@ inline UiClass *UiFileBasedOptionPage::ui() } +#define BEGIN_DECLARE_OPTION_PAGE(SomeClass) \ + class SomeClass : public ::Dialogs::OptionPage \ + { \ + public: \ + explicit SomeClass(QWidget *parentWidget = nullptr); \ + ~SomeClass(); \ + bool apply(); \ + void reset(); \ + private: + +#define BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE(SomeClass) \ + namespace Ui { \ + class SomeClass; \ + } \ + typedef ::Dialogs::UiFileBasedOptionPage SomeClass ## Base; \ + class SomeClass : public ::Dialogs::UiFileBasedOptionPage \ + { \ + public: \ + explicit SomeClass(QWidget *parentWidget = nullptr); \ + ~SomeClass(); \ + bool apply(); \ + void reset(); \ + private: + +#define END_DECLARE_OPTION_PAGE \ + }; + +#define DECLARE_SETUP_WIDGETS \ + protected: \ + QWidget *setupWidget(); \ + private: + +#define DECLARE_UI_FILE_BASED_OPTION_PAGE(SomeClass) \ + BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE(SomeClass) \ + END_DECLARE_OPTION_PAGE + +#define DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_SETUP(SomeClass) \ + BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE(SomeClass) \ + DECLARE_SETUP_WIDGETS \ + END_DECLARE_OPTION_PAGE + #endif // OPTIONSPAGE_H diff --git a/settingsdialog/optionpage.ui.template b/settingsdialog/optionpage.ui.template new file mode 100644 index 0000000..cb9a928 --- /dev/null +++ b/settingsdialog/optionpage.ui.template @@ -0,0 +1,22 @@ + + + NS::Class + + + + 0 + 0 + 345 + 146 + + + + Window title + + + QGroupBox { font-weight: bold }; + + + + + diff --git a/settingsdialog/qtappearanceoptionpage.ui b/settingsdialog/qtappearanceoptionpage.ui new file mode 100644 index 0000000..ea0888b --- /dev/null +++ b/settingsdialog/qtappearanceoptionpage.ui @@ -0,0 +1,277 @@ + + + Dialogs::QtAppearanceOptionPage + + + + 0 + 0 + 514 + 158 + + + + Appearance + + + QGroupBox { font-weight: bold }; + + + + + + Style sheet + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + false + + + true + + + + + + + false + + + select ... + + + + + + + Widget style + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 4 + + + + + false + + + + 0 + 0 + + + + + + + + false + + + More options ... + + + + + + + + + Palette + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 4 + + + + + false + + + + + + + false + + + Select file ... + + + + + + + + + Font + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Use system default + + + + + + + true + + + + + + + true + + + + + + + true + + + + + + + + Widgets::ClearLineEdit + QLineEdit +
qtutilities/widgets/clearlineedit.h
+
+
+ + + + fontCheckBox + toggled(bool) + fontComboBox + setDisabled(bool) + + + 462 + 38 + + + 193 + 39 + + + + + fontCheckBox + toggled(bool) + fontPushButton + setDisabled(bool) + + + 462 + 38 + + + 365 + 39 + + + + + widgetStyleCheckBox + toggled(bool) + widgetStyleComboBox + setDisabled(bool) + + + 462 + 71 + + + 240 + 72 + + + + + styleSheetCheckBox + toggled(bool) + styleSheetLineEdit + setDisabled(bool) + + + 462 + 104 + + + 196 + 105 + + + + + styleSheetCheckBox + toggled(bool) + styleSheetPushButton + setDisabled(bool) + + + 462 + 104 + + + 368 + 105 + + + + + paletteCheckBox + toggled(bool) + paletteToolButton + setDisabled(bool) + + + 462 + 137 + + + 99 + 138 + + + + +
diff --git a/settingsdialog/qtlanguageoptionpage.ui b/settingsdialog/qtlanguageoptionpage.ui new file mode 100644 index 0000000..05ed8a6 --- /dev/null +++ b/settingsdialog/qtlanguageoptionpage.ui @@ -0,0 +1,67 @@ + + + Dialogs::QtLanguageOptionPage + + + Localization + + + QGroupBox { font-weight: bold }; + + + + + + Locale + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + true + + + + + + + true + + + + + + + Use system default + + + + + + + + + localeCheckBox + toggled(bool) + localeComboBox + setDisabled(bool) + + + 159 + 39 + + + 75 + 39 + + + + + diff --git a/settingsdialog/qtsettings.cpp b/settingsdialog/qtsettings.cpp new file mode 100644 index 0000000..832f605 --- /dev/null +++ b/settingsdialog/qtsettings.cpp @@ -0,0 +1,145 @@ +#include "./qtsettings.h" + +#include "./optioncategorymodel.h" +#include "./optioncategoryfiltermodel.h" +#include "./optioncategory.h" +#include "./optionpage.h" + +#include "../paletteeditor/paletteeditor.h" + +#include "ui_qtappearanceoptionpage.h" +#include "ui_qtlanguageoptionpage.h" + +#include +#include +#include +#include + +#include + +using namespace std; + +namespace Dialogs { + +/*! + * \brief Returns a new OptionCatecory containing all Qt related + * option pages. + */ +OptionCategory *qtOptionCategory(QObject *parent) +{ + auto *category = new OptionCategory(parent); + category->setDisplayName(QCoreApplication::translate("QtGui::QtOptionCategory", "Qt")); + category->setIcon(QIcon::fromTheme(QStringLiteral("qtcreator"))); + category->assignPages(QList() + << new QtAppearanceOptionPage + << new QtLanguageOptionPage); + return category; +} + +QtAppearanceOptionPage::QtAppearanceOptionPage(QWidget *parentWidget) : + QtAppearanceOptionPageBase(parentWidget), + m_fontDialog(nullptr) +{} + +QtAppearanceOptionPage::~QtAppearanceOptionPage() +{} + +bool QtAppearanceOptionPage::apply() +{ + if(hasBeenShown()) { + // read style sheet + QString styleSheet; + if(!ui()->styleSheetLineEdit->text().isEmpty()) { + QFile file(ui()->styleSheetLineEdit->text()); + if(!file.open(QFile::ReadOnly)) { + return false; + } + styleSheet.append(file.readAll()); + if(file.error() != QFile::NoError) { + return false; + } + } + + // apply config + const QFont font = ui()->fontComboBox->font(); + const QPalette palette = ui()->paletteToolButton->palette(); + QGuiApplication::setFont(font); + QGuiApplication::setPalette(palette); + if(auto *qapp = qobject_cast(QApplication::instance())) { + qapp->setStyleSheet(styleSheet); + } else { + return false; + } + QApplication::setStyle(ui()->widgetStyleComboBox->currentText()); + } + return true; +} + +void QtAppearanceOptionPage::reset() +{ + if(hasBeenShown()) { + ui()->widgetStyleComboBox->setCurrentText(QApplication::style() ? QApplication::style()->objectName() : QString()); + ui()->styleSheetLineEdit->setText(QString() /* TODO */); + ui()->fontComboBox->setCurrentFont(QGuiApplication::font()); + ui()->fontPushButton->setPalette(QGuiApplication::palette()); + } +} + +QWidget *QtAppearanceOptionPage::setupWidget() +{ + // call base implementation first, so ui() is available + auto *widget = QtAppearanceOptionPageBase::setupWidget(); + + // setup widget style selection + ui()->widgetStyleComboBox->addItems(QStyleFactory::keys()); + + // setup style sheet selection + QObject::connect(ui()->styleSheetPushButton, &QPushButton::clicked, [this] { + QString styleSheetPath = QFileDialog::getOpenFileName(this->widget()); + if(!styleSheetPath.isEmpty()) { + ui()->styleSheetLineEdit->setText(styleSheetPath); + } + }); + + // setup font selection + QObject::connect(ui()->fontPushButton, &QPushButton::clicked, [this] { + if(!m_fontDialog) { + m_fontDialog = new QFontDialog(this->widget()); + m_fontDialog->setCurrentFont(ui()->fontComboBox->font()); + QObject::connect(m_fontDialog, &QFontDialog::fontSelected, ui()->fontComboBox, &QFontComboBox::setCurrentFont); + QObject::connect(ui()->fontComboBox, &QFontComboBox::currentFontChanged, m_fontDialog, &QFontDialog::setCurrentFont); + } + m_fontDialog->show(); + }); + + // setup palette selection + QObject::connect(ui()->paletteToolButton, &QToolButton::clicked, [this] { + ui()->paletteToolButton->setPalette(PaletteEditor::getPalette(this->widget(), ui()->paletteToolButton->palette())); + }); + + return widget; +} + +QtLanguageOptionPage::QtLanguageOptionPage(QWidget *parentWidget) : + QtLanguageOptionPageBase(parentWidget) +{} + +QtLanguageOptionPage::~QtLanguageOptionPage() +{} + +bool QtLanguageOptionPage::apply() +{ + if(hasBeenShown()) { + QLocale::setDefault(ui()->localeComboBox->currentText()); + } + return true; +} + +void QtLanguageOptionPage::reset() +{ + if(hasBeenShown()) { + ui()->localeComboBox->setCurrentText(QLocale().name()); + } +} + +} diff --git a/settingsdialog/qtsettings.h b/settingsdialog/qtsettings.h new file mode 100644 index 0000000..b0302dc --- /dev/null +++ b/settingsdialog/qtsettings.h @@ -0,0 +1,28 @@ +// The functions and classes declared in this header are experimental. +// API/ABI might change in minor release! + +#ifndef QT_UTILITIES_QTSETTINGS_H +#define QT_UTILITIES_QTSETTINGS_H + +#include "./optionpage.h" + +#include + +QT_FORWARD_DECLARE_CLASS(QFontDialog) + +namespace Dialogs { + +class OptionCategory; + +OptionCategory LIB_EXPORT *qtOptionCategory(QObject *parent = nullptr); + +BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE(QtAppearanceOptionPage) + DECLARE_SETUP_WIDGETS + QFontDialog *m_fontDialog; +END_DECLARE_OPTION_PAGE + +DECLARE_UI_FILE_BASED_OPTION_PAGE(QtLanguageOptionPage) + +} + +#endif // QT_UTILITIES_QTSETTINGS_H diff --git a/settingsdialog/settingsdialog.cpp b/settingsdialog/settingsdialog.cpp index d83f7dd..f261ded 100644 --- a/settingsdialog/settingsdialog.cpp +++ b/settingsdialog/settingsdialog.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include namespace Dialogs { @@ -30,7 +32,8 @@ SettingsDialog::SettingsDialog(QWidget *parent) : m_categoryModel(new OptionCategoryModel(this)), m_categoryFilterModel(new OptionCategoryFilterModel(this)), m_currentCategory(nullptr), - m_tabBarAlwaysVisible(true) + m_tabBarAlwaysVisible(true), + m_categoriesAlwaysVisible(true) { m_ui->setupUi(this); makeHeading(m_ui->headingLabel); @@ -61,7 +64,6 @@ SettingsDialog::~SettingsDialog() /*! * \brief Sets whether the tab bar is always visible. - * * \sa SettingsDialog::isTabBarAlwaysVisible() */ void SettingsDialog::setTabBarAlwaysVisible(bool value) @@ -72,6 +74,21 @@ void SettingsDialog::setTabBarAlwaysVisible(bool value) } } +/*! + * \brief Sets whether the category selection is always visible. + * \sa SettingsDialog::areCategoriesAlwaysVisible() + */ +void SettingsDialog::setCategoriesAlwaysVisible(bool value) +{ + m_categoriesAlwaysVisible = value; + bool visible = value || m_categoryModel->rowCount(); + m_ui->filterLineEdit->setVisible(visible); + m_ui->categoriesListView->setVisible(visible); + if(!visible) { + m_ui->filterLineEdit->clear(); + } +} + /*! * \brief Returns the category for the specified \a categoryIndex. * @@ -155,14 +172,14 @@ void SettingsDialog::updateTabWidget() if(index < m_ui->pagesTabWidget->count()) { scrollArea = qobject_cast(m_ui->pagesTabWidget->widget(index)); scrollArea->takeWidget(); - m_ui->pagesTabWidget->setTabText(index, page->displayName()); + m_ui->pagesTabWidget->setTabText(index, page->widget()->windowTitle()); } else { scrollArea = new QScrollArea(m_ui->pagesTabWidget); scrollArea->setFrameStyle(QFrame::NoFrame); scrollArea->setBackgroundRole(QPalette::Base); scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); scrollArea->setWidgetResizable(true); - m_ui->pagesTabWidget->addTab(scrollArea, page->displayName()); + m_ui->pagesTabWidget->addTab(scrollArea, page->widget()->windowTitle()); } if(page->widget()->layout()) { page->widget()->layout()->setAlignment(Qt::AlignTop | Qt::AlignLeft); @@ -189,15 +206,41 @@ void SettingsDialog::updateTabWidget() */ bool SettingsDialog::apply() { + QString errorMessage; for(OptionCategory *category : m_categoryModel->categories()) { for(OptionPage *page : category->pages()) { if(!page->apply()) { - return false; + if(errorMessage.isEmpty()) { + errorMessage = tr("

Errors occured when applying changes.

    "); + } + if(const_cast(page)->errors().isEmpty()) { + errorMessage.append(QStringLiteral("
  • ") + % category->displayName() + % QLatin1Char('/') + % page->widget()->windowTitle() + % QStringLiteral(": ") + % tr("unknonw error") + % QStringLiteral("
  • ")); + } else { + for(const QString &error : const_cast(page)->errors()) { + errorMessage.append(QStringLiteral("
  • ") + % category->displayName() + % QLatin1Char('/') + % page->widget()->windowTitle() + % QStringLiteral(": ") + % error + % QStringLiteral("
  • ")); + } + } } } } + if(!errorMessage.isEmpty()) { + errorMessage.append(QStringLiteral("
")); + QMessageBox::warning(this, windowTitle(), errorMessage); + } emit applied(); - return true; + return errorMessage.isEmpty(); } /*! diff --git a/settingsdialog/settingsdialog.h b/settingsdialog/settingsdialog.h index 6670747..b380fcc 100644 --- a/settingsdialog/settingsdialog.h +++ b/settingsdialog/settingsdialog.h @@ -21,13 +21,16 @@ class SettingsDialog; class LIB_EXPORT SettingsDialog : public QDialog { Q_OBJECT - Q_PROPERTY(bool isTabBarAlwaysVisible READ isTabBarAlwaysVisible WRITE setTabBarAlwaysVisible) + Q_PROPERTY(bool tabBarAlwaysVisible READ isTabBarAlwaysVisible WRITE setTabBarAlwaysVisible) + Q_PROPERTY(bool categoriesAlwaysVisible READ areCategoriesAlwaysVisible WRITE setCategoriesAlwaysVisible) public: explicit SettingsDialog(QWidget *parent = nullptr); - virtual ~SettingsDialog(); + ~SettingsDialog(); bool isTabBarAlwaysVisible() const; void setTabBarAlwaysVisible(bool value); + bool areCategoriesAlwaysVisible() const; + void setCategoriesAlwaysVisible(bool value); OptionCategoryModel *categoryModel(); OptionCategory *category(int categoryIndex) const; OptionPage *page(int categoryIndex, int pageIndex) const; @@ -54,6 +57,7 @@ private: OptionCategoryFilterModel *m_categoryFilterModel; OptionCategory *m_currentCategory; bool m_tabBarAlwaysVisible; + bool m_categoriesAlwaysVisible; }; /*! @@ -68,6 +72,18 @@ inline bool SettingsDialog::isTabBarAlwaysVisible() const return m_tabBarAlwaysVisible; } +/*! + * \brief Returns whether the category selection is always visible. + * + * The category selection is always visible by default. + * + * \sa SettingsDialog::setCategoriesAlwaysVisible() + */ +inline bool SettingsDialog::areCategoriesAlwaysVisible() const +{ + return m_categoriesAlwaysVisible; +} + /*! * \brief Returns the category model used by the settings dialog to manage the categories. */