First commit

This commit is contained in:
Martchus 2015-04-22 18:57:44 +02:00
commit 26748b4923
44 changed files with 6230 additions and 0 deletions

View File

@ -0,0 +1,89 @@
#include "aboutdialog.h"
#include "ui_aboutdialog.h"
#include <QGraphicsPixmapItem>
#include <QApplication>
#include <QDesktopWidget>
#include <QStyle>
#include <QMessageBox>
/*!
\namespace Dialogs
\brief Provides common dialogs such as AboutDialog, EnterPasswordDialog and SettingsDialog.
*/
namespace Dialogs {
/*!
* \class Dialogs::AboutDialog
* \brief The AboutDialog class provides a simple about dialog.
*/
/*!
* \brief Constructs an about dialog with the provided information.
* \param parent Specifies the parent widget.
* \param applicationName Specifies the name of the application. If empty, QApplication::applicationName() will be used.
* \param creator Specifies the creator of the application. If empty, QApplication::organizationName() will be used.
* \param version Specifies the version of the application. If empty, QApplication::applicationVersion() will be used.
* \param description Specifies a short description about the application.
* \param website Specifies the URL to the website of the application. If empty, QApplication::organizationDomain() will be used.
* \param image Specifies the application icon. If the image is null, the standard information icon will be used.
*/
AboutDialog::AboutDialog(QWidget *parent, const QString &applicationName, const QString &creator, const QString &version, const QString &website, const QString &description, const QImage &image) :
QDialog(parent),
m_ui(new Ui::AboutDialog)
{
m_ui->setupUi(this);
#ifdef Q_OS_WIN32
setStyleSheet(QStringLiteral("* { font: 9pt \"Segoe UI\"; } #mainWidget { color: black; background-color: white; border: none; } #productNameLabel { font-size: 12pt; color: #003399; }"));
#else
setStyleSheet(QStringLiteral("#productNameLabel { font-weight: bold; }"));
#endif
setWindowFlags(Qt::Tool);
if(!applicationName.isEmpty()) {
m_ui->productNameLabel->setText(applicationName);
} else if(!QApplication::applicationDisplayName().isEmpty()) {
m_ui->productNameLabel->setText(QApplication::applicationDisplayName());
} else {
m_ui->productNameLabel->setText(QApplication::applicationName());
}
if(!creator.isEmpty()) {
m_ui->creatorLabel->setText(creator);
} else {
m_ui->creatorLabel->setText(QApplication::organizationName());
}
if(!version.isEmpty()) {
m_ui->versionLabel->setText(version);
} else {
m_ui->versionLabel->setText(QApplication::applicationVersion());
}
m_ui->descLabel->setText(description);
if(!website.isEmpty()) {
m_ui->websiteLabel->setText(tr("<a href=\"%1\">Website</a>").arg(website));
} else {
m_ui->websiteLabel->setText(tr("<a href=\"%1\">Website</a>").arg(QApplication::organizationDomain()));
}
m_iconScene = new QGraphicsScene(this);
if(!image.isNull()) {
m_iconScene->addItem(new QGraphicsPixmapItem(QPixmap::fromImage(image)));
} else {
m_iconScene->addItem(new QGraphicsPixmapItem(QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation, nullptr, this).pixmap(128)));
}
m_ui->graphicsView->setScene(m_iconScene);
setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, size(), parentWidget() ? parentWidget()->geometry() : QApplication::desktop()->availableGeometry()));
}
/*!
* \brief Constructs an about dialog with the specified \a parent, \a description and \a image.
*/
AboutDialog::AboutDialog(QWidget *parent, const QString &description, const QImage &image) :
AboutDialog(parent, QString(), QString(), QString(), QString(), description, image)
{}
/*!
* \brief Destroys the about dialog.
*/
AboutDialog::~AboutDialog()
{}
}

38
aboutdialog/aboutdialog.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef ABOUTDIALOG_H
#define ABOUTDIALOG_H
#include <c++utilities/application/global.h>
#include <QDialog>
#include <memory>
QT_BEGIN_NAMESPACE
class QGraphicsScene;
QT_END_NAMESPACE
namespace Dialogs
{
namespace Ui {
class AboutDialog;
}
class LIB_EXPORT AboutDialog : public QDialog
{
Q_OBJECT
public:
explicit AboutDialog(QWidget *parent, const QString &applicationName, const QString &creator, const QString &version, const QString &website = QString(), const QString &description = QString(), const QImage &image = QImage());
explicit AboutDialog(QWidget *parent, const QString &description = QString(), const QImage &image = QImage());
~AboutDialog();
private:
std::unique_ptr<Ui::AboutDialog> m_ui;
QGraphicsScene *m_iconScene;
};
}
#endif // ABOUTDIALOG_H

252
aboutdialog/aboutdialog.ui Normal file
View File

@ -0,0 +1,252 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialogs::AboutDialog</class>
<widget class="QDialog" name="Dialogs::AboutDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>43</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>300</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>400</width>
<height>300</height>
</size>
</property>
<property name="windowTitle">
<string>About</string>
</property>
<property name="whatsThis">
<string/>
</property>
<property name="locale">
<locale language="English" country="UnitedStates"/>
</property>
<property name="modal">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="mainWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QFrame" name="informationFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGraphicsView" name="graphicsView">
<property name="minimumSize">
<size>
<width>32</width>
<height>128</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>128</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background: transparent;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="foregroundBrush">
<brush brushstyle="NoBrush">
<color alpha="0">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</property>
<property name="interactive">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="productNameLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">font-size: 12pt;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="text">
<string>application name</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="versionLabel">
<property name="text">
<string>version</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="descLabel">
<property name="styleSheet">
<string notr="true">font-style: italic;</string>
</property>
<property name="text">
<string>description</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="websiteLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="mouseTracking">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true">text-decoration: underline;
color: palette(link)s;</string>
</property>
<property name="text">
<string>website link</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="creatorLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">font-size: 8pt;</string>
</property>
<property name="text">
<string>creators</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,332 @@
#include "enterpassworddialog.h"
#include "gui/ui_enterpassworddialog.h"
#include <QEvent>
#include <QGraphicsPixmapItem>
#include <QKeyEvent>
#include <QMessageBox>
#include <QGuiApplication>
#ifdef PLATFORM_SPECIFIC_CAPSLOCK_DETECTION
# if defined(Q_OS_WIN32)
# include <windows.h>
# elif defined(Q_OS_UNIX)
# include <X11/XKBlib.h>
# undef KeyPress
# undef KeyRelease
# undef FocusIn
# undef FocusOut
# endif
#endif
namespace Dialogs {
/*!
* \class Dialogs::EnterPasswordDialog
* \brief The EnterPasswordDialog class provides a simple dialog to ask the user for a password.
*/
/*!
* \brief Constructs a password dialog.
* \param parent Specifies the parent widget.
*/
EnterPasswordDialog::EnterPasswordDialog(QWidget *parent) :
QDialog(parent),
m_ui(new Ui::EnterPasswordDialog)
{
// setup ui
m_ui->setupUi(this);
#ifdef Q_OS_WIN32
setStyleSheet(QStringLiteral("* { font: 9pt \"Segoe UI\"; } #mainWidget { color: black; background-color: white; border: none; } #bottomWidget { background-color: #F0F0F0; border-top: 1px solid #DFDFDF; } QMessageBox QLabel, QInputDialog QLabel, #instructionLabel {font-size: 12pt; color: #003399; }"));
#else
setStyleSheet(QStringLiteral("#instructionLabel { font-weight: bold; }"));
#endif
setDescription();
setPromptForUserName(false);
setVerificationRequired(false);
setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
installEventFilter(this);
m_ui->userNameLineEdit->installEventFilter(this);
m_ui->password1LineEdit->installEventFilter(this);
m_ui->password2LineEdit->installEventFilter(this);
// capslock key detection
#ifdef PLATFORM_SPECIFIC_CAPSLOCK_DETECTION
m_capslockPressed = isCapslockPressed();
#else
m_capslockPressed = false;
#endif
m_ui->capslockWarningWidget->setVisible(m_capslockPressed);
// draw icon to capslock warning graphics view
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, this);
QGraphicsScene* scene = new QGraphicsScene();
QGraphicsPixmapItem* item = new QGraphicsPixmapItem(icon.pixmap(16, 16));
scene->addItem(item);
m_ui->capslockWarningGraphicsView->setScene(scene);
// connect signals and slots
connect(m_ui->showPasswordCheckBox, &QCheckBox::clicked, this, &EnterPasswordDialog::updateShowPassword);
connect(m_ui->noPwCheckBox, &QCheckBox::clicked, this, &EnterPasswordDialog::updateShowPassword);
connect(m_ui->confirmPushButton, &QPushButton::clicked, this, &EnterPasswordDialog::confirm);
connect(m_ui->abortPushButton, &QPushButton::clicked, this, &EnterPasswordDialog::abort);
// grab the keyboard
grabKeyboard();
}
/*!
* \brief Destroys the password dialog.
*/
EnterPasswordDialog::~EnterPasswordDialog()
{}
/*!
* \brief Returns the description. The description is shown under the instruction text.
* \sa setDescription()
*/
QString EnterPasswordDialog::description() const
{
return m_ui->descLabel->text();
}
/*!
* \brief Sets the description.
* \sa description()
*/
void EnterPasswordDialog::setDescription(const QString &description)
{
m_ui->descLabel->setText(description);
m_ui->descLabel->setHidden(description.isEmpty());
adjustSize();
}
/*!
* \brief Returns whether the dialogs prompts for a user name as well.
*
* The dialog does not prompt for a user name by default.
*
* \sa setPromptForUserName()
*/
bool EnterPasswordDialog::promtForUserName() const
{
return !m_ui->userNameLineEdit->isHidden();
}
/*!
* \brief Sets whethere the dialog prompts for a user name as well.
* \sa promptForUserName()
*/
void EnterPasswordDialog::setPromptForUserName(bool prompt)
{
m_ui->userNameLineEdit->setHidden(!prompt);
adjustSize();
}
/*!
* \brief Returns an indication whether a verification (password has to be entered twice) is required.
*
* \sa EnterPasswordDialog::setVerificationRequired()
*/
bool EnterPasswordDialog::isVerificationRequired() const
{
return !m_ui->password2LineEdit->isHidden();
}
/*!
* \brief Returns an indication whether the user is force to enter a password.
*
* If no password is required, the user is allowed to skip the dialog without entering
* a password.
*
* \sa EnterPasswordDialog::setPasswordRequired()
*/
bool EnterPasswordDialog::isPasswordRequired() const
{
return m_ui->noPwCheckBox->isHidden();
}
/*!
* \brief Sets whether the user is force to enter a password.
*
* If no password is required, the user is allowed to skip the dialog without entering
* a password.
*
* \sa EnterPasswordDialog::isPasswordRequired()
*/
void EnterPasswordDialog::setPasswordRequired(bool value)
{
m_ui->noPwCheckBox->setHidden(value);
m_ui->noPwCheckBox->setChecked(false);
adjustSize();
}
/*!
* \brief Updates the relevant controls to show entered characters or to mask them them.
*
* This private slot is called when m_ui->showPasswordCheckBox is clicked.
*/
void EnterPasswordDialog::updateShowPassword()
{
m_ui->password1LineEdit->setEchoMode(m_ui->showPasswordCheckBox->isChecked()
? QLineEdit::Normal
: QLineEdit::Password);
m_ui->password1LineEdit->setEnabled(!m_ui->noPwCheckBox->isChecked());
m_ui->password2LineEdit->setEnabled(!(m_ui->showPasswordCheckBox->isChecked() || m_ui->noPwCheckBox->isChecked()));
}
/*!
* \brief Sets whether a verification (password has to be entered twice) is required.
*
* \sa EnterPasswordDialog::isVerificationRequired()
*/
void EnterPasswordDialog::setVerificationRequired(bool value)
{
if(m_instruction.isEmpty()) {
m_ui->instructionLabel->setText(value ? tr("Enter the new password") : tr("Enter the password"));
}
m_ui->password2LineEdit->setHidden(!value);
adjustSize();
}
/*!
* \brief Sets the instruction text.
*
* \sa EnterPasswordDialog::instruction()
*/
void EnterPasswordDialog::setInstruction(const QString &value)
{
m_instruction = value;
if(m_instruction.isEmpty()) {
m_ui->instructionLabel->setText(isVerificationRequired() ? tr("Enter the new password") : tr("Enter the password"));
} else {
m_ui->instructionLabel->setText(value);
}
adjustSize();
}
bool EnterPasswordDialog::event(QEvent *event)
{
switch(event->type()) {
case QEvent::KeyPress: {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_CapsLock) {
m_capslockPressed = !m_capslockPressed;
}
m_ui->capslockWarningWidget->setVisible(m_capslockPressed);
break;
}
default:
;
}
return QDialog::event(event);
}
/*!
* \brief Internal method to notice when the capslock key is pressed by the user.
*
* Invocation of this method is done by installing the event filter in the constructor.
*/
bool EnterPasswordDialog::eventFilter(QObject *sender, QEvent *event)
{
switch(event->type()) {
case QEvent::KeyPress: {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_CapsLock) {
m_capslockPressed = !m_capslockPressed;
} else {
QString text = keyEvent->text();
if(text.length()) {
QChar firstChar = text.at(0);
bool shiftPressed = (keyEvent->modifiers() & Qt::ShiftModifier) != 0;
if((shiftPressed && firstChar.isLower()) || (!shiftPressed && firstChar.isUpper())) {
m_capslockPressed = true;
} else if(firstChar.isLetter()) {
m_capslockPressed = false;
}
}
}
m_ui->capslockWarningWidget->setVisible(m_capslockPressed);
}
break;
case QEvent::FocusIn:
if(sender == m_ui->userNameLineEdit || sender == m_ui->password1LineEdit || sender == m_ui->password2LineEdit) {
releaseKeyboard();
qobject_cast<QWidget *>(sender)->grabKeyboard();
}
break;
case QEvent::FocusOut:
if(sender == m_ui->userNameLineEdit || sender == m_ui->password1LineEdit || sender == m_ui->password2LineEdit) {
qobject_cast<QWidget *>(sender)->releaseKeyboard();
grabKeyboard();
}
break;
default:
;
}
return false;
}
/*!
* \brief Sets the dialog status to QDialog::Accepted if a valid password has been enterd.
* Displays an error message otherwise.
*
* This private slot is called when m_ui->confirmPushButton is clicked.
*/
void EnterPasswordDialog::confirm()
{
if(!isPasswordRequired() && m_ui->noPwCheckBox->isChecked()) {
m_password.clear();
done(QDialog::Accepted);
} else {
QString userName = m_ui->userNameLineEdit->text();
QString password = m_ui->password1LineEdit->text();
QString repeatedPassword = m_ui->password2LineEdit->text();
if(promtForUserName() && userName.isEmpty()) {
QMessageBox::warning(this, windowTitle(), tr("You didn't enter a user name."));
} else if(password.isEmpty()) {
QMessageBox::warning(this, windowTitle(), tr("You didn't enter a password."));
} else {
if(isVerificationRequired() && (password != repeatedPassword) && !m_ui->showPasswordCheckBox->isChecked()) {
if(repeatedPassword.isEmpty()) {
QMessageBox::warning(this, windowTitle(), tr("You have to enter the new password twice to ensure you enterd it correct."));
} else {
QMessageBox::warning(this, windowTitle(), tr("You mistyped the password."));
}
} else {
m_userName = userName;
m_password = password;
done(QDialog::Accepted);
}
}
}
}
/*!
* \brief Returns an indication whether the capslock key is pressed using platform specific functions.
*
* \remarks - Returns always false for unsupported platforms.
* - This method is only avialable if the library is built with
* PLATFORM_SPECIFIC_CAPSLOCK_DETECTION defined.
* - This static function will be used internally to detect whether the capslock key is pressed
* when initializing the dialog if available.
* - The function requires the application to be linked against X11 on Linux/Unix/Max OS X.
*/
#ifdef PLATFORM_SPECIFIC_CAPSLOCK_DETECTION
bool EnterPasswordDialog::isCapslockPressed()
{
// platform dependent method of determining if CAPS LOCK is pressed
# if defined(Q_OS_WIN32)
return GetKeyState(VK_CAPITAL) == 1;
# elif defined(Q_OS_UNIX)
Display *d = XOpenDisplay((char*)0);
bool caps_state = false;
if (d) {
unsigned n;
XkbGetIndicatorState(d, XkbUseCoreKbd, &n);
caps_state = (n & 0x01) == 1;
}
return caps_state;
# else
return false;
# endif
}
#endif
}

View File

@ -0,0 +1,108 @@
#ifndef ENTERPASSWORDDIALOG_H
#define ENTERPASSWORDDIALOG_H
#include <c++utilities/application/global.h>
#include <QDialog>
#include <memory>
namespace Dialogs {
namespace Ui {
class EnterPasswordDialog;
}
class LIB_EXPORT EnterPasswordDialog : public QDialog
{
Q_OBJECT
Q_PROPERTY(QString userName READ userName)
Q_PROPERTY(QString password READ password)
Q_PROPERTY(QString description READ description WRITE setDescription)
Q_PROPERTY(bool promtForUserName READ promtForUserName WRITE setPromptForUserName)
Q_PROPERTY(bool isVerificationRequired READ isVerificationRequired WRITE setVerificationRequired)
Q_PROPERTY(bool isPasswordRequired READ isPasswordRequired WRITE setPasswordRequired)
Q_PROPERTY(QString instruction READ instruction WRITE setInstruction)
Q_PROPERTY(bool isCapslockPressed READ isCapslockPressed)
public:
explicit EnterPasswordDialog(QWidget *parent = nullptr);
~EnterPasswordDialog();
const QString &userName() const;
const QString &password() const;
QString description() const;
void setDescription(const QString &description = QString());
bool promtForUserName() const;
void setPromptForUserName(bool prompt);
bool isVerificationRequired() const;
void setVerificationRequired(bool value);
bool isPasswordRequired() const;
void setPasswordRequired(bool value);
const QString &instruction() const;
void setInstruction(const QString &value);
#ifdef PLATFORM_SPECIFIC_CAPSLOCK_DETECTION
static bool isCapslockPressed();
#endif
protected:
bool event(QEvent *event);
bool eventFilter(QObject *sender, QEvent *event);
private slots:
void updateShowPassword();
void confirm();
void abort();
private:
std::unique_ptr<Ui::EnterPasswordDialog> m_ui;
QString m_userName;
QString m_password;
QString m_instruction;
bool m_capslockPressed;
};
/*!
* \brief Returns the entered user name.
*/
inline const QString &EnterPasswordDialog::userName() const
{
return m_userName;
}
/*!
* \brief Returns the entered password.
*/
inline const QString &EnterPasswordDialog::password() const
{
return m_password;
}
/*!
* \brief Returns the instruction text.
*
* The instruction text is displayed at the top of the dialog.
* If the instruction text is empty the default text "Enter the new password"
* or "Enter the password" (depending on whether the verification is requried or
* not) displayed.
*
* \sa EnterPasswordDialog::setInstruction()
*/
inline const QString &EnterPasswordDialog::instruction() const
{
return m_instruction;
}
/*!
* \brief Clears all results and sets the dialog status to QDialog::Rejected.
*
* This private slot is called when m_ui->abortPushButton is clicked.
*/
inline void EnterPasswordDialog::abort()
{
m_password.clear();
done(QDialog::Rejected);
}
}
#endif // ENTERPASSWORDDIALOG_H

View File

@ -0,0 +1,283 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialogs::EnterPasswordDialog</class>
<widget class="QDialog" name="Dialogs::EnterPasswordDialog">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Enter the password</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="mainWidget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="instructionLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>500</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Enter the password</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="descLabel"/>
</item>
<item>
<widget class="Widgets::ClearLineEdit" name="userNameLineEdit">
<property name="placeholderText">
<string>user name</string>
</property>
</widget>
</item>
<item>
<widget class="Widgets::ClearLineEdit" name="password1LineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="maxLength">
<number>32</number>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
<property name="placeholderText">
<string>password</string>
</property>
</widget>
</item>
<item>
<widget class="Widgets::ClearLineEdit" name="password2LineEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maxLength">
<number>32</number>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
<property name="placeholderText">
<string>repeat password</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="checkboxesWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="showPasswordCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>show password</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="noPwCheckBox">
<property name="text">
<string>don't use a password</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="bottomWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QWidget" name="capslockWarningWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGraphicsView" name="capslockWarningGraphicsView">
<property name="maximumSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: transparent;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="backgroundBrush">
<brush brushstyle="NoBrush">
<color alpha="0">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</property>
<property name="sceneRect">
<rectf>
<x>0.000000000000000</x>
<y>0.000000000000000</y>
<width>16.000000000000000</width>
<height>16.000000000000000</height>
</rectf>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="capslockWarningLabel">
<property name="styleSheet">
<string notr="true">font-weight: bold;</string>
</property>
<property name="text">
<string>Capslock is active</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>168</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="abortPushButton">
<property name="styleSheet">
<string notr="true">background: none;</string>
</property>
<property name="text">
<string>Abort</string>
</property>
<property name="icon">
<iconset theme="window-close">
<normaloff/>
</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="confirmPushButton">
<property name="styleSheet">
<string notr="true">background: none;</string>
</property>
<property name="text">
<string>Confirm</string>
</property>
<property name="icon">
<iconset theme="go-next">
<normaloff/>
</iconset>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Widgets::ClearLineEdit</class>
<extends>QLineEdit</extends>
<header location="global">qtutilities/widgets/clearlineedit.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>password1LineEdit</tabstop>
<tabstop>password2LineEdit</tabstop>
<tabstop>confirmPushButton</tabstop>
<tabstop>abortPushButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

71
general.pri Normal file
View File

@ -0,0 +1,71 @@
# template
TEMPLATE = lib
#dirs
UI_DIR = ./gui
MOC_DIR = ./moc
OBJECTS_DIR = ./obj
RCC_DIR = ./res
# compiler flags
QMAKE_CXXFLAGS += -std=c++11
QMAKE_LFLAGS += -std=c++11
unix {
QMAKE_LFLAGS += "-Wl,--rpath=./"
}
# prefix
targetprefix = .
# target
CONFIG(debug, debug|release) {
TARGET = $$targetprefix/$${projectname}d
} else {
TARGET = $$targetprefix/$$projectname
}
# variables to check target architecture
win32-g++:QMAKE_TARGET.arch = $$QMAKE_HOST.arch
win32-g++-32:QMAKE_TARGET.arch = x86
win32-g++-64:QMAKE_TARGET.arch = x86_64
linux-g++:QMAKE_TARGET.arch = $$QMAKE_HOST.arch
linux-g++-32:QMAKE_TARGET.arch = x86
linux-g++-64:QMAKE_TARGET.arch = x86_64
# configuration
mobile {
DEFINES += CONFIG_MOBILE
} else:desktop {
DEFINES += CONFIG_DESKTOP
} else:android {
CONFIG += mobile
DEFINES += CONFIG_MOBILE
} else {
CONFIG += desktop
DEFINES += CONFIG_DESKTOP
}
no-gui {
QT -= gui
DEFINES += GUI_NONE
guiqtquick || guiqtwidgets {
error("Can not use no-gui with guiqtquick or guiqtwidgets.")
} else {
message("Configured for no GUI support.")
}
} else {
QT += gui
mobile {
CONFIG += guiqtquick
}
desktop {
CONFIG += guiqtwidgets
}
}
guiqtquick {
message("Configured for Qt Quick GUI support.")
greaterThan(QT_MAJOR_VERSION, 4): QT += quick
CONFIG(debug, debug|release) {
CONFIG += qml_debug
}
DEFINES += GUI_QTQUICK
}
guiqtwidgets {
message("Configured for Qt widgets GUI support.")
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
DEFINES += GUI_QTWIDGETS
DEFINES += MODEL_UNDO_SUPPORT
}

226
models/checklistmodel.cpp Normal file
View File

@ -0,0 +1,226 @@
#include "checklistmodel.h"
#include <QSettings>
/*!
\namespace Models
\brief Provides common models.
*/
namespace Models {
/*!
* \class Models::ChecklistItem
* \brief The ChecklistItem class provides an item for use with the ChecklistModel class.
*/
/*!
* \class Models::ChecklistModel
* \brief The ChecklistModel class provides a generic model for storing checkable items.
*/
/*!
* \brief Constructs a new checklist model.
*/
ChecklistModel::ChecklistModel(QObject *parent) :
QAbstractListModel(parent)
{}
int ChecklistModel::rowCount(const QModelIndex &parent) const
{
if(!parent.isValid()) {
return m_items.size();
}
return 0;
}
Qt::ItemFlags ChecklistModel::flags(const QModelIndex &index) const
{
if(!index.isValid() || index.row() >= m_items.count() || index.model() != this) {
return Qt::ItemIsDropEnabled; // allows drops outside the items
}
return QAbstractListModel::flags(index) | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled;
}
QVariant ChecklistModel::data(const QModelIndex &index, int role) const
{
if(index.isValid() && index.row() < m_items.size()) {
switch(role) {
case Qt::DisplayRole:
return m_items.at(index.row()).label();
case Qt::CheckStateRole:
return m_items.at(index.row()).checkState();
case idRole():
return m_items.at(index.row()).id();
default:
;
}
}
return QVariant();
}
QMap<int, QVariant> ChecklistModel::itemData(const QModelIndex &index) const
{
QMap<int, QVariant> roles;
roles.insert(Qt::DisplayRole, data(index, Qt::DisplayRole));
roles.insert(Qt::CheckStateRole, data(index, Qt::CheckStateRole));
roles.insert(idRole(), data(index, idRole()));
return roles;
}
bool ChecklistModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
bool success = false;
QVector<int> roles;
roles << role;
if(index.isValid() && index.row() < m_items.size()) {
switch(role) {
case Qt::DisplayRole:
m_items[index.row()].m_label = value.toString();
success = true;
break;
case Qt::CheckStateRole:
if(value.canConvert(QMetaType::Int)) {
m_items[index.row()].m_checkState = static_cast<Qt::CheckState>(value.toInt());
success = true;
}
break;
case idRole(): {
m_items[index.row()].m_id = value;
success = true;
QString label = labelForId(value);
if(!label.isEmpty()) {
m_items[index.row()].m_label = label;
roles << Qt::DisplayRole;
}
break;
} default:
;
}
}
if(success) {
dataChanged(index, index, roles);
}
return success;
}
bool ChecklistModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)
{
for(QMap<int, QVariant>::ConstIterator it = roles.constBegin(); it != roles.constEnd(); ++it) {
setData(index, it.value(), it.key());
}
return true;
}
/*!
* \brief Returns the label for the specified \a id.
*
* This method might be reimplemented when subclassing to provide labels
* for the item IDs.
*
* If an item's ID is set (using setData() and idRole()) this method is called
* to update the item's label as well. If this method returns an empty string
* (default behaviour) the item's label will not be updated.
*/
QString ChecklistModel::labelForId(const QVariant &) const
{
return QString();
}
Qt::DropActions ChecklistModel::supportedDropActions() const
{
return Qt::MoveAction;
}
bool ChecklistModel::insertRows(int row, int count, const QModelIndex &parent)
{
if (count < 1 || row < 0 || row > rowCount() || parent.isValid()) {
return false;
}
beginInsertRows(QModelIndex(), row, row + count - 1);
for(int index = row, end = row + count; index < end; ++index) {
m_items.insert(index, ChecklistItem());
}
endInsertRows();
return true;
}
bool ChecklistModel::removeRows(int row, int count, const QModelIndex &parent)
{
if (count < 1 || row < 0 || (row + count) > rowCount() || parent.isValid()) {
return false;
}
beginRemoveRows(QModelIndex(), row, row + count - 1);
for(int index = row, end = row + count; index < end; ++index) {
m_items.removeAt(index);
}
endRemoveRows();
return true;
}
/*!
* \brief Sets the items. Resets the model.
*/
void ChecklistModel::setItems(const QList<ChecklistItem> &items)
{
beginResetModel();
m_items = items;
endResetModel();
}
/*!
* \brief Restores the IDs and checkstates read from the specified \a settings object.
*
* The items will be read from the array with the specified \a name.
*
* Resets the model (current items are cleared).
*
* Does not restore any labels. Labels are meant to be restored from the ID.
*/
void ChecklistModel::restore(QSettings &settings, const QString &name)
{
beginResetModel();
auto currentItems = m_items;
QList<QVariant> restoredIds;
m_items.clear();
int rows = settings.beginReadArray(name);
m_items.reserve(rows);
for(int i = 0; i < rows; ++i) {
settings.setArrayIndex(i);
QVariant id = settings.value(QStringLiteral("id"));
QVariant selected = settings.value(QStringLiteral("selected"));
if(!id.isNull() && !selected.isNull() && selected.canConvert(QMetaType::Bool) && !restoredIds.contains(id)) {
m_items << ChecklistItem(id, labelForId(id), selected.toBool() ? Qt::Checked : Qt::Unchecked);
restoredIds << id;
}
}
settings.endArray();
for(const ChecklistItem &item : currentItems) {
if(!restoredIds.contains(item.id())) {
m_items << item;
}
}
endResetModel();
}
/*!
* \brief Saves the IDs and checkstates to the specified \a settings object.
*
* The items will be stored using an array with the specified \a name.
*
* Does not save any labels.
*/
void ChecklistModel::save(QSettings &settings, const QString &name) const
{
settings.beginWriteArray(name, m_items.size());
int index = 0;
for(const ChecklistItem &item : m_items) {
settings.setArrayIndex(index);
settings.setValue(QStringLiteral("id"), item.id());
settings.setValue(QStringLiteral("selected"), item.isChecked());
++index;
}
settings.endArray();
}
}

119
models/checklistmodel.h Normal file
View File

@ -0,0 +1,119 @@
#ifndef CHECKLISTMODEL_H
#define CHECKLISTMODEL_H
#include <c++utilities/application/global.h>
#include <QAbstractListModel>
#include <QList>
QT_BEGIN_NAMESPACE
class QSettings;
QT_END_NAMESPACE
namespace Models {
class ChecklistModel;
class LIB_EXPORT ChecklistItem
{
friend class ChecklistModel;
public:
ChecklistItem(const QVariant &id = QVariant(), const QString &label = QString(), Qt::CheckState checked = Qt::Unchecked);
const QVariant &id() const;
const QString &label() const;
Qt::CheckState checkState() const;
bool isChecked() const;
private:
QVariant m_id;
QString m_label;
Qt::CheckState m_checkState;
};
inline ChecklistItem::ChecklistItem(const QVariant &id, const QString &label, Qt::CheckState checkState) :
m_id(id),
m_label(label),
m_checkState(checkState)
{}
/*!
* \brief Returns the ID of the item.
*/
inline const QVariant &ChecklistItem::id() const
{
return m_id;
}
/*!
* \brief Returns the label.
*/
inline const QString &ChecklistItem::label() const
{
return m_label;
}
/*!
* \brief Returns the check state.
*/
inline Qt::CheckState ChecklistItem::checkState() const
{
return m_checkState;
}
/*!
* \brief Returns whether the item is checked.
*/
inline bool ChecklistItem::isChecked() const
{
return m_checkState == Qt::Checked;
}
class LIB_EXPORT ChecklistModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit ChecklistModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QMap<int, QVariant> itemData(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole);
bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles);
virtual QString labelForId(const QVariant &id) const;
Qt::DropActions supportedDropActions() const;
bool insertRows(int row, int count, const QModelIndex &parent);
bool removeRows(int row, int count, const QModelIndex &parent);
const QList<ChecklistItem> &items() const;
void setItems(const QList<ChecklistItem> &items);
void restore(QSettings &settings, const QString &name);
void save(QSettings &settings, const QString &name) const;
static inline constexpr int idRole();
private:
QList<ChecklistItem> m_items;
};
/*!
* \brief Returns the items.
*/
inline const QList<ChecklistItem> &ChecklistModel::items() const
{
return m_items;
}
/*!
* \brief Returns the role used to get or set the item ID.
*/
inline constexpr int ChecklistModel::idRole()
{
return Qt::UserRole + 1;
}
}
#endif // CHECKLISTMODEL_H

22
pkgbuild/default/PKGBUILD Normal file
View File

@ -0,0 +1,22 @@
pkgname=qtutilities
pkgver=1.0.4
pkgrel=1
arch=('i686' 'x86_64')
pkgdesc="Collection of Qt related utility classes and functions used by Tageditor, Video downloader and Password manager."
license=('GPL')
depends=('c++utilities')
makedepends=
url="http://martchus.netai.net/"
source=("$pkgname.tar.gz")
md5sums=('cee38afc5eb60b2da4eba64107628ed7')
# head end
build() {
cd $srcdir/$pkgname
INSTALL_ROOT=$pkgdir/usr/ qmake-qt5 "$pkgname.pro" -r -spec linux-g++
make
}
package() {
cd $srcdir/$pkgname
make install
}

View File

@ -0,0 +1,44 @@
_name=qtutilities
pkgname=mingw-w64-$_name
pkgver=1.0.4
pkgrel=1
arch=('any')
pkgdesc="Collection of Qt related utility classes and functions used by Tageditor, Video downloader and Password manager (mingw-w64)."
license=('GPL')
depends=('mingw-w64-crt' 'mingw-w64-qt5-base' 'mingw-w64-c++utilities')
makedepends=('mingw-w64-gcc')
url="http://martchus.netai.net/"
source=("${_name}.tar.gz")
md5sums=('4e94aff9225d8873f752995c1bcc5f15')
options=('staticlibs' '!strip')
_architectures="i686-w64-mingw32 x86_64-w64-mingw32"
# head end
build() {
cd $srcdir/$_name
# build utilities for each architecture
for _arch in ${_architectures}; do
mkdir -p build-${_arch} && pushd build-${_arch}
INSTALL_ROOT=$pkgdir/usr/ ${_arch}-qmake-qt5 -r ../${_name}.pro
make
popd
done
}
package() {
cd $srcdir/$_name
for _arch in ${_architectures}; do
# bin stuff
pushd build-${_arch}
${_arch}-strip --strip-unneeded ./release/${_name}.dll
${_arch}-strip --strip-unneeded ./release/lib${_name}.dll.a
install -m755 -D ./release/${_name}.dll $pkgdir/usr/${_arch}/bin/${_name}.dll
install -m755 -D ./release/lib${_name}.dll.a $pkgdir/usr/${_arch}/lib/lib${_name}.dll.a
popd
# include files
for dir in aboutdialog enterpassworddialog settingsdialog widgets resources models
do
mkdir -p $pkgdir/usr/${_arch}/include/${_name}/$dir
install -m644 -D ./$dir/*.h $pkgdir/usr/${_arch}/include/${_name}/$dir
done
done
}

2310
qtutilities.doxygen Normal file

File diff suppressed because it is too large Load Diff

95
qtutilities.pro Normal file
View File

@ -0,0 +1,95 @@
projectname = qtutilities
# include ../../common.pri when building as part of a subdirs project; otherwise include general.pri
!include(../../common.pri) {
!include(./general.pri) {
error("Couldn't find the common.pri or the general.pri file!")
}
}
QT += core gui
CONFIG(noplatformspecificcapslockdetection, noplatformspecificcapslockdetection|platformspecificcapslockdetection) {
DEFINES -= PLATFORM_SPECIFIC_CAPSLOCK_DETECTION
} else {
DEFINES += PLATFORM_SPECIFIC_CAPSLOCK_DETECTION
}
win32 {
CONFIG += dll
}
contains(DEFINES, PLATFORM_SPECIFIC_CAPSLOCK_DETECTION) {
x11 {
LIBS += -lX11
}
}
SOURCES += resources/resources.cpp \
models/checklistmodel.cpp \
resources/qtconfigarguments.cpp
contains(DEFINES, GUI_QTWIDGETS) {
SOURCES += aboutdialog/aboutdialog.cpp \
enterpassworddialog/enterpassworddialog.cpp \
settingsdialog/optioncategorymodel.cpp \
settingsdialog/settingsdialog.cpp \
settingsdialog/optionpage.cpp \
settingsdialog/optioncategory.cpp \
settingsdialog/optioncategoryfiltermodel.cpp \
widgets/clearlineedit.cpp \
widgets/iconbutton.cpp \
widgets/buttonoverlay.cpp \
widgets/clearcombobox.cpp \
widgets/clearspinbox.cpp \
widgets/clearplaintextedit.cpp
FORMS += aboutdialog/aboutdialog.ui \
enterpassworddialog/enterpassworddialog.ui \
settingsdialog/settingsdialog.ui
}
HEADERS += resources/resources.h \
models/checklistmodel.h \
resources/qtconfigarguments.h
contains(DEFINES, GUI_QTWIDGETS) {
HEADERS += aboutdialog/aboutdialog.h \
enterpassworddialog/enterpassworddialog.h \
settingsdialog/optioncategorymodel.h \
settingsdialog/settingsdialog.h \
settingsdialog/optioncategory.h \
settingsdialog/optionpage.h \
settingsdialog/optioncategoryfiltermodel.h \
widgets/clearlineedit.h \
widgets/iconbutton.h \
widgets/buttonoverlay.h \
widgets/clearcombobox.h \
widgets/clearspinbox.h \
widgets/clearplaintextedit.h
}
OTHER_FILES += \
pkgbuild/default/PKGBUILD \
pkgbuild/mingw-w64/PKGBUILD
# libs and includepath
CONFIG(debug, debug|release) {
LIBS += -L../../ -lc++utilitiesd
} else {
LIBS += -L../../ -lc++utilities
}
INCLUDEPATH += ../
RESOURCES += resources/qtutilsicons.qrc
# installs
target.path = $$(INSTALL_ROOT)/lib
INSTALLS += target
for(dir, $$list(aboutdialog enterpassworddialog models resources settingsdialog widgets)) {
eval(inc_$${dir} = $${dir})
inc_$${dir}.path = $$(INSTALL_ROOT)/include/$$projectname/$${dir}
inc_$${dir}.files = $${dir}/*.h
INSTALLS += inc_$${dir}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,92 @@
#include "qtconfigarguments.h"
#include <QString>
#include <QLocale>
#include <QFont>
#include <QIcon>
#ifdef GUI_QTWIDGETS
# include <QApplication>
# include <QStyleFactory>
#else
# include <QGuiApplication>
#endif
#include <initializer_list>
#include <iostream>
using namespace std;
namespace ApplicationUtilities {
QtConfigArguments::QtConfigArguments() :
m_qtWidgetsGuiArg("qt-widgets-gui", "g", "shows a Qt widgets based graphical user interface"),
m_qtQuickGuiArg("qt-quick-gui", "q", "shows a Qt quick based graphical user interface"),
m_lngArg("lang", "l", "sets the language for the Qt GUI"),
m_qmlDebuggerArg("qmljsdebugger", "qmljsdebugger", "enables QML debugging (see http://doc.qt.io/qt-5/qtquick-debugging.html)"),
m_styleArg("style", string(), "sets the Qt widgets style"),
m_iconThemeArg("icon-theme", string(), "sets the icon theme for the Qt GUI"),
m_fontArg("font", string(), "sets the font family and size (point) for the Qt GUI")
{
// language
m_lngArg.setValueNames({"language"});
m_lngArg.setRequiredValueCount(1);
m_lngArg.setRequired(false);
m_lngArg.setCallback([] (const StringVector &values) {
QLocale::setDefault(QLocale(QString::fromLocal8Bit(values.front().c_str())));
});
// qml debugger (handled by Qt, just to let the parser know of it)
m_qmlDebuggerArg.setValueNames({"port:<port_from>[,port_to][,host:<ip address>][,block]"});
m_qmlDebuggerArg.setRequiredValueCount(1);
// appearance
m_styleArg.setValueNames({"style name"});
m_styleArg.setRequiredValueCount(1);
m_styleArg.setCallback([] (const StringVector &values) {
#ifdef GUI_QTWIDGETS
if(QStyle *style = QStyleFactory::create(QString::fromLocal8Bit(values.front().c_str()))) {
QApplication::setStyle(style);
} else {
cout << "Warning: Can not find the specified style." << endl;
}
#endif
#ifdef GUI_QTQUICK
Q_UNUSED(values)
cout << "Warning: Can not set a style for the Qt Quick GUI." << endl;
#endif
});
m_iconThemeArg.setValueNames({"theme name"});
m_iconThemeArg.setRequiredValueCount(1);
m_iconThemeArg.setCallback([] (const StringVector &values) {
QIcon::setThemeName(QString::fromLocal8Bit(values.front().c_str()));
});
m_fontArg.setValueNames({"name", "size"});
m_fontArg.setRequiredValueCount(2);
m_fontArg.setDefault(true);
#ifdef Q_OS_WIN32
m_fontArg.setDefaultValues({"Segoe UI", "9"});
#else
m_fontArg.setDefaultValues({string(), string()});
#endif
m_fontArg.setCallback([] (const StringVector &values) {
if(!values.front().empty()) {
QFont font;
font.setFamily(QString::fromLocal8Bit(values.front().c_str()));
bool ok;
int size = QString::fromLocal8Bit(values.back().c_str()).toInt(&ok);
if(ok) {
font.setPointSize(size);
} else {
cout << "Warning: Can not parse specified font size. It will be ignored." << endl;
}
QGuiApplication::setFont(font);
}
});
m_qtWidgetsGuiArg.setSecondaryArguments({&m_lngArg, &m_qmlDebuggerArg, &m_styleArg, &m_iconThemeArg, &m_fontArg});
m_qtQuickGuiArg.setSecondaryArguments({&m_lngArg, &m_qmlDebuggerArg, &m_iconThemeArg, &m_fontArg});
#if defined GUI_QTWIDGETS
m_qtWidgetsGuiArg.setDefault(true);
#elif defined GUI_QTQUICK
m_qtQuickGuiArg.setDefault(true);
#endif
}
}

View File

@ -0,0 +1,57 @@
#ifndef QTCONFIGARGUMENTS_H
#define QTCONFIGARGUMENTS_H
#include <c++utilities/application/argumentparser.h>
namespace ApplicationUtilities {
class LIB_EXPORT QtConfigArguments
{
public:
QtConfigArguments();
Argument &qtWidgetsGuiArg();
Argument &qtQuickGuiArg();
Argument &languageArg();
bool areQtGuiArgsPresent() const;
private:
Argument m_qtWidgetsGuiArg;
Argument m_qtQuickGuiArg;
Argument m_lngArg;
Argument m_qmlDebuggerArg;
Argument m_styleArg;
Argument m_iconThemeArg;
Argument m_fontArg;
};
inline Argument &QtConfigArguments::qtWidgetsGuiArg()
{
return m_qtWidgetsGuiArg;
}
inline Argument &QtConfigArguments::qtQuickGuiArg()
{
return m_qtQuickGuiArg;
}
inline Argument &QtConfigArguments::languageArg()
{
return m_lngArg;
}
inline bool QtConfigArguments::areQtGuiArgsPresent() const
{
return m_qtWidgetsGuiArg.isPresent() || m_qtQuickGuiArg.isPresent();
}
}
#endif // QTCONFIGARGUMENTS_H
#ifdef QT_CONFIG_ARGUMENTS
#undef QT_CONFIG_ARGUMENTS
#endif
#define QT_CONFIG_ARGUMENTS ApplicationUtilities::QtConfigArguments

View File

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/qtutilities">
<file>icons/hicolor/16x16/actions/edit-clear.png</file>
<file>icons/hicolor/16x16/actions/edit-menu.png</file>
</qresource>
</RCC>

129
resources/resources.cpp Normal file
View File

@ -0,0 +1,129 @@
#include "resources.h"
#include <QString>
#include <QLocale>
#include <QTranslator>
#include <QLibraryInfo>
#if defined(GUI_QTWIDGETS)
# include <QApplication>
# include <QIcon>
# include <QFont>
# include <QStyleFactory>
#elif defined(GUI_QTQUICK)
# include <QGuiApplication>
# include <QIcon>
# include <QFont>
#else
# include <QCoreApplication>
#endif
#include <iostream>
using namespace std;
void qInitResources_qtutilsicons();
void qCleanupResources_qtutilsicons();
namespace QtUtilitiesResources {
/*!
* \brief Initiates the resources used and provided by this library.
*/
void init()
{
qInitResources_qtutilsicons();
}
/*!
* \brief Frees the resources used and provided by this library.
*/
void cleanup()
{
qCleanupResources_qtutilsicons();
}
}
namespace TranslationFiles {
/*!
* \brief Loads and installs the appropriate Qt translation file.
*/
void loadQtTranslationFile()
{
QLocale locale;
if(locale.language() != QLocale::English) {
QTranslator *qtTranslator = new QTranslator;
if(qtTranslator->load(QStringLiteral("qt_%1").arg(locale.name()),
QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
QCoreApplication::installTranslator(qtTranslator);
} else {
cout << "Unable to load Qt translation file for the language " << locale.name().toStdString() << "." << endl;
}
}
}
/*!
* \brief Loads and installs the appropriate application translation file.
* \param applicationName Specifies the name of the application.
* \remarks Translation files have to be placed in one of the following
* locations:
* - /usr/share/$application/translations
* - ./translations
* Translation files must be named using the following scheme:
* - $application_$language.qm
*/
void loadApplicationTranslationFile(const QString &applicationName)
{
QLocale locale;
QTranslator *appTranslator = new QTranslator;
QString fileName = QStringLiteral("%1_%2").arg(applicationName, locale.name());
if(appTranslator->load(fileName, QStringLiteral("./translations"))) {
QCoreApplication::installTranslator(appTranslator);
} else if(appTranslator->load(fileName, QStringLiteral("/usr/share/%1/translations").arg(applicationName))) {
QCoreApplication::installTranslator(appTranslator);
} else {
cout << "Unable to load application translation file for the language " << locale.name().toStdString() << "." << endl;
}
}
}
namespace Theme {
/*!
* \brief Sets the default icon theme.
*/
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
void setup()
{
if(QIcon::themeName().isEmpty()) {
QIcon::setThemeName(QStringLiteral("oxygen"));
}
}
#endif
}
namespace ApplicationInstances {
#if defined(GUI_QTWIDGETS)
bool hasWidgetsApp()
{
return qobject_cast<QApplication*>(QCoreApplication::instance()) != nullptr;
}
#endif
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
bool hasGuiApp()
{
return qobject_cast<QGuiApplication*>(QCoreApplication::instance()) != nullptr;
}
#endif
bool hasCoreApp()
{
return qobject_cast<QCoreApplication*>(QCoreApplication::instance()) != nullptr;
}
}

47
resources/resources.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef RESOURCES_H
#define RESOURCES_H
#include <c++utilities/application/global.h>
#include <QtGlobal>
QT_BEGIN_NAMESPACE
class QString;
class QStringList;
QT_END_NAMESPACE
namespace QtUtilitiesResources {
LIB_EXPORT void init();
LIB_EXPORT void cleanup();
}
namespace TranslationFiles {
LIB_EXPORT void loadQtTranslationFile();
LIB_EXPORT void loadApplicationTranslationFile(const QString &applicationName);
}
namespace Theme {
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
LIB_EXPORT void setup();
#endif
}
namespace ApplicationInstances {
#if defined(GUI_QTWIDGETS)
LIB_EXPORT bool hasWidgetsApp();
#endif
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
LIB_EXPORT bool hasGuiApp();
#endif
LIB_EXPORT bool hasCoreApp();
}
#endif // RESOURCES_H

View File

@ -0,0 +1,92 @@
#include "optioncategory.h"
#include "optionpage.h"
namespace Dialogs {
/*!
* \class Dialogs::OptionCategory
* \brief The OptionCategory class wraps associated option pages.
*/
/*!
* \brief Constructs a option category.
*/
OptionCategory::OptionCategory(QObject *parent) :
QObject(parent)
{}
/*!
* \brief Destroys the option category.
*/
OptionCategory::~OptionCategory()
{
qDeleteAll(m_pages);
}
/*!
* \brief Applies all pages.
* \sa OptionPage::apply()
*/
bool OptionCategory::applyAllPages()
{
foreach(OptionPage *page, m_pages) {
if(!page->apply()) {
return false;
}
}
return true;
}
/*!
* \brief Resets all pages.
* \sa OptionPage::reset()
*/
void OptionCategory::resetAllPages()
{
foreach(OptionPage *page, m_pages) {
page->reset();
}
}
/*!
* \brief Returns whether the option category matches the specified \a searchKeyWord.
*/
bool OptionCategory::matches(const QString &searchKeyWord) const
{
foreach(OptionPage *page, m_pages) {
if(page->matches(searchKeyWord)) {
return true;
}
}
return false;
}
/*!
* \brief Assigns the specified \a pages to the category.
*
* Previously assigned pages get deleted. The pagesChanged() signal is emitted.
* The category takes ownership over the given \a pages.
*/
void OptionCategory::assignPages(const QList<OptionPage *> pages)
{
qDeleteAll(m_pages);
m_pages = pages;
emit pagesChanged();
}
/*!
* \fn OptionCategory::displayNameChanged()
* \brief Emitted when the display name changed.
*/
/*!
* \fn OptionCategory::iconChanged()
* \brief Emitted when the icon changed.
*/
/*!
* \fn OptionCategory::pagesChanged()
* \brief Emitted when the pages changed.
*/
}

View File

@ -0,0 +1,91 @@
#ifndef OPTIONSCATEGORY_H
#define OPTIONSCATEGORY_H
#include <c++utilities/application/global.h>
#include <QObject>
#include <QIcon>
#include <QList>
namespace Dialogs {
class OptionPage;
class LIB_EXPORT OptionCategory : public QObject
{
Q_OBJECT
Q_PROPERTY(QString displayName READ displayName WRITE setDisplayName NOTIFY displayNameChanged)
Q_PROPERTY(QIcon icon READ icon WRITE setIcon NOTIFY iconChanged)
Q_PROPERTY(QList<OptionPage *> pages READ pages WRITE assignPages NOTIFY pagesChanged)
public:
explicit OptionCategory(QObject *parent = nullptr);
virtual ~OptionCategory();
const QString &displayName() const;
void setDisplayName(const QString &displayName);
const QIcon &icon() const;
void setIcon(const QIcon &icon);
const QList<OptionPage *> pages() const;
void assignPages(const QList<OptionPage *> pages);
bool applyAllPages();
void resetAllPages();
bool matches(const QString &searchKeyWord) const;
signals:
void displayNameChanged();
void iconChanged();
void pagesChanged();
private:
QString m_displayName;
QIcon m_icon;
QList<OptionPage *> m_pages;
};
/*!
* \brief Returns the display name of the category.
*/
inline const QString &OptionCategory::displayName() const
{
return m_displayName;
}
/*!
* \brief Sets the display name of the category.
*/
inline void OptionCategory::setDisplayName(const QString &displayName)
{
m_displayName = displayName;
emit displayNameChanged();
}
/*!
* \brief Returns the icon of the category.
*/
inline const QIcon &OptionCategory::icon() const
{
return m_icon;
}
/*!
* \brief Sets the icon of the category.
*/
inline void OptionCategory::setIcon(const QIcon &icon)
{
m_icon = icon;
emit iconChanged();
}
/*!
* \brief Returns the assigned pages.
*/
inline const QList<OptionPage *> OptionCategory::pages() const
{
return m_pages;
}
}
#endif // OPTIONSCATEGORY_H

View File

@ -0,0 +1,31 @@
#include "optioncategoryfiltermodel.h"
#include "optioncategorymodel.h"
#include "optioncategory.h"
namespace Dialogs {
/*!
* \class Dialogs::OptionCategoryFilterModel
* \brief The OptionCategoryFilterModel class is used by SettingsDialog to filter option categories.
*/
/*!
* \brief Constructs an option category filter model.
*/
OptionCategoryFilterModel::OptionCategoryFilterModel(QObject *parent) :
QSortFilterProxyModel(parent)
{}
bool OptionCategoryFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
if (QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent))
return true;
if(OptionCategoryModel *model = qobject_cast<OptionCategoryModel *>(sourceModel())) {
if(OptionCategory *category = model->category(sourceRow)) {
return category->matches(filterRegExp().pattern());
}
}
return false;
}
}

View File

@ -0,0 +1,22 @@
#ifndef OPTIONCATEGORYFILTERMODEL_H
#define OPTIONCATEGORYFILTERMODEL_H
#include <QSortFilterProxyModel>
namespace Dialogs {
class OptionCategoryFilterModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit OptionCategoryFilterModel(QObject *parent = nullptr);
protected:
virtual bool filterAcceptsRow(int source_row, const QModelIndex &sourceParent) const;
};
}
#endif // OPTIONCATEGORYFILTERMODEL_H

View File

@ -0,0 +1,108 @@
#include "optioncategorymodel.h"
#include "optioncategory.h"
namespace Dialogs {
/*!
* \class Dialogs::OptionCategoryModel
* \brief The OptionCategoryModel class is used by SettingsDialog to store and display option categories.
*/
/*!
* \brief Constructs an option category model.
*/
OptionCategoryModel::OptionCategoryModel(QObject *parent) :
QAbstractListModel(parent)
{}
/*!
* \brief Constructs an option category model with the specified \a categories.
*
* The model takes ownership over the given categories.
*/
OptionCategoryModel::OptionCategoryModel(const QList<Dialogs::OptionCategory *> &categories, QObject *parent) :
QAbstractListModel(parent),
m_categories(categories)
{
foreach(OptionCategory *category, m_categories) {
category->setParent(this);
}
}
/*!
* \brief Destroys the option category model.
*/
OptionCategoryModel::~OptionCategoryModel()
{}
/*!
* \brief Sets the \a categories for the model.
*
* The model takes ownership over the given \a categories.
*/
void OptionCategoryModel::setCategories(const QList<OptionCategory *> categories)
{
beginResetModel();
qDeleteAll(m_categories);
m_categories = categories;
foreach(OptionCategory *category, m_categories) {
category->setParent(this);
connect(category, &OptionCategory::displayNameChanged, this, &OptionCategoryModel::categoryChangedName);
connect(category, &OptionCategory::iconChanged, this, &OptionCategoryModel::categoryChangedIcon);
}
endResetModel();
}
int OptionCategoryModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_categories.size();
}
QVariant OptionCategoryModel::data(const QModelIndex &index, int role) const
{
if(index.isValid() && index.row() < m_categories.size()) {
switch (role) {
case Qt::DisplayRole:
return m_categories.at(index.row())->displayName();
case Qt::DecorationRole: {
QIcon icon = m_categories.at(index.row())->icon();
if(!icon.isNull()) {
return icon;
}
}
}
}
return QVariant();
}
/*!
* \brief Handles the change of name of a category.
*/
void OptionCategoryModel::categoryChangedName()
{
if(OptionCategory *senderCategory = qobject_cast<OptionCategory *>(QObject::sender())) {
for(int i = 0, end = m_categories.size(); i < end; ++i) {
if(senderCategory == m_categories.at(i)) {
QModelIndex index = this->index(i);
emit dataChanged(index, index, QVector<int>() << Qt::DisplayRole);
}
}
}
}
/*!
* \brief Handles the a changed icon of a category.
*/
void OptionCategoryModel::categoryChangedIcon()
{
if(OptionCategory *senderCategory = qobject_cast<OptionCategory *>(QObject::sender())) {
for(int i = 0, end = m_categories.size(); i < end; ++i) {
if(senderCategory == m_categories.at(i)) {
QModelIndex index = this->index(i);
emit dataChanged(index, index, QVector<int>() << Qt::DecorationRole);
}
}
}
}
}

View File

@ -0,0 +1,72 @@
#ifndef OPTIONCATEGORYMODEL_H
#define OPTIONCATEGORYMODEL_H
#include <c++utilities/application/global.h>
#include <QList>
#include <QAbstractListModel>
namespace Dialogs {
class OptionPage;
class OptionCategory;
class LIB_EXPORT OptionCategoryModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit OptionCategoryModel(QObject *parent = nullptr);
explicit OptionCategoryModel(const QList<OptionCategory *> &categories, QObject *parent = nullptr);
virtual ~OptionCategoryModel();
const QList<OptionCategory *> &categories() const;
OptionCategory *category(const QModelIndex &index) const;
OptionCategory *category(int row) const;
void setCategories(const QList<OptionCategory *> categories);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
private slots:
void categoryChangedName();
void categoryChangedIcon();
private:
QList<OptionCategory *> m_categories;
};
/*!
* \brief Returns the categories.
* \sa OptionCategoryModel::category()
* \sa OptionCategoryModel::setCategories()
*/
inline const QList<OptionCategory *> &OptionCategoryModel::categories() const
{
return m_categories;
}
/*!
* \brief Returns the category for the specified model \a index.
* \sa OptionCategoryModel::categories()
* \sa OptionCategoryModel::setCategories()
*/
inline OptionCategory *OptionCategoryModel::category(const QModelIndex &index) const
{
return (index.isValid())
? category(index.row())
: nullptr;
}
/*!
* \brief Returns the category for the specified \a row.
* \sa OptionCategoryModel::categories()
* \sa OptionCategoryModel::setCategories()
*/
inline OptionCategory *OptionCategoryModel::category(int row) const
{
return row < m_categories.size() ? m_categories.at(row) : nullptr;
}
}
#endif // OPTIONCATEGORYMODEL_H

View File

@ -0,0 +1,109 @@
#include "optionpage.h"
#include <QLabel>
#include <QCheckBox>
#include <QRadioButton>
#include <QPushButton>
#include <QGroupBox>
namespace Dialogs {
/*!
* \class Dialogs::OptionPage
* \brief The OptionPage class is the base class for SettingsDialog pages.
*
* The specified \a parentWindow might be used by some implementations as parent when showing dialogs.
*/
/*!
* \brief Constructs a option page.
*/
OptionPage::OptionPage(QWidget *parentWindow) :
m_parentWindow(parentWindow),
m_shown(false),
m_keywordsInitialized(false)
{}
/*!
* \brief Destroys the option page.
*/
OptionPage::~OptionPage()
{}
/*!
* \brief Returns the widget for the option page.
*
* If the widget has not been constructed yet, a new widget will be
* constructed using the OptionPage::setupWidget() method and the
* current configuration is applied.
*
* The option page keeps ownership over the returned widget.
*/
QWidget *OptionPage::widget()
{
if(!m_widget) {
m_widget.reset(setupWidget()); // ensure widget has been created
}
if(!m_shown) {
m_shown = true;
reset(); // show current configuration if not shown yet
}
return m_widget.get();
}
/*!
* \brief Returns whether the pages matches the specified
* \a searchKeyWord.
*/
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
}
// find common subwidgets
foreach (const QLabel *label, m_widget->findChildren<QLabel *>())
m_keywords << label->text();
foreach (const QCheckBox *checkbox, m_widget->findChildren<QCheckBox *>())
m_keywords << checkbox->text();
foreach (const QRadioButton *checkbox, m_widget->findChildren<QRadioButton *>())
m_keywords << checkbox->text();
foreach (const QPushButton *pushButton, m_widget->findChildren<QPushButton *>())
m_keywords << pushButton->text();
foreach (const QGroupBox *groupBox, m_widget->findChildren<QGroupBox *>())
m_keywords << groupBox->title();
m_keywordsInitialized = true;
}
foreach (const QString &keyword, m_keywords)
if (keyword.contains(searchKeyWord, Qt::CaseInsensitive))
return true;
return false;
}
/*!
* \fn OptionPage::displayName()
* \brief Returns the display name of the page.
*/
/*!
* \fn OptionPage::apply()
* \brief Applies altered settings.
*/
/*!
* \fn OptionPage::reset()
* \brief Discards altered settings and resets relevant widgets.
*/
/*!
* \fn OptionPage::setupWidget()
* \brief Creates the widget for the page. Called in the first invocation of widget().
*/
}

117
settingsdialog/optionpage.h Normal file
View File

@ -0,0 +1,117 @@
#ifndef OPTIONSPAGE_H
#define OPTIONSPAGE_H
#include <c++utilities/application/global.h>
#include <QObject>
#include <QWidget>
#include <memory>
namespace Dialogs {
class LIB_EXPORT OptionPage
{
public:
explicit OptionPage(QWidget *parentWindow = nullptr);
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);
protected:
virtual QWidget *setupWidget() = 0;
private:
std::unique_ptr<QWidget> m_widget;
QWidget *m_parentWindow;
bool m_shown;
bool m_keywordsInitialized;
QStringList m_keywords;
};
/*!
* \brief Returns the parent window of the option page.
*/
inline QWidget *OptionPage::parentWindow() const
{
return m_parentWindow;
}
/*!
* \brief Returns an indication whether the option page has been shown yet.
*/
inline bool OptionPage::hasBeenShown() const
{
return m_widget != nullptr && m_shown;
}
/*!
* \class Dialogs::UiFileBasedOptionPage
* \brief The UiFileBasedOptionPage class is the base class for SettingsDialog pages using UI files
* to describe the widget tree.
*
* \tparam UiClass Specifies the UI class generated by uic.
*/
template <class UiClass>
class LIB_EXPORT UiFileBasedOptionPage : public OptionPage
{
public:
explicit UiFileBasedOptionPage(QWidget *parentWindow = nullptr);
virtual ~UiFileBasedOptionPage();
virtual QString displayName() const = 0;
virtual bool apply() = 0;
virtual void reset() = 0;
protected:
virtual QWidget *setupWidget();
UiClass *ui();
private:
std::unique_ptr<UiClass> m_ui;
};
/*!
* \brief Constructs a new UI file based option page.
*/
template <class UiClass>
UiFileBasedOptionPage<UiClass>::UiFileBasedOptionPage(QWidget *parentWindow) :
OptionPage(parentWindow)
{}
/*!
* \brief Destroys the option page.
*/
template <class UiClass>
UiFileBasedOptionPage<UiClass>::~UiFileBasedOptionPage()
{}
/*!
* \brief Sets up the widget for the option page using the UI class.
*/
template <class UiClass>
QWidget *UiFileBasedOptionPage<UiClass>::setupWidget()
{
QWidget *widget = new QWidget();
if(!m_ui) {
m_ui.reset(new UiClass);
}
m_ui->setupUi(widget);
return widget;
}
template <class UiClass>
inline UiClass *UiFileBasedOptionPage<UiClass>::ui()
{
return m_ui.get();
}
}
#endif // OPTIONSPAGE_H

View File

@ -0,0 +1,214 @@
#include "settingsdialog.h"
#include "ui_settingsdialog.h"
#include "optioncategorymodel.h"
#include "optioncategoryfiltermodel.h"
#include "optioncategory.h"
#include "optionpage.h"
#include <QItemSelectionModel>
#include <QShowEvent>
#include <QScrollArea>
namespace Dialogs {
/*!
* \class Dialogs::SettingsDialog
* \brief The SettingsDialog class provides a framework for creating settings dialogs with different categories and subcategories.
*/
/*!
* \brief Constructs a settings dialog.
* \param parent Specifies the parent widget.
*/
SettingsDialog::SettingsDialog(QWidget *parent) :
QDialog(parent),
m_ui(new Ui::SettingsDialog),
m_categoryModel(new OptionCategoryModel(this)),
m_categoryFilterModel(new OptionCategoryFilterModel(this)),
m_currentCategory(nullptr),
m_tabBarAlwaysVisible(true)
{
m_ui->setupUi(this);
#ifdef Q_OS_WIN32
setStyleSheet(QStringLiteral("* { font: 9pt \"Segoe UI\"; } #mainWidget { color: black; background-color: white; border: none; } #bottomWidget { background-color: #F0F0F0; border-top: 1px solid #DFDFDF; } QMessageBox QLabel, QInputDialog QLabel, #instructionLabel {font-size: 12pt; color: #003399; }"));
#else
setStyleSheet(QStringLiteral("#instructionLabel { font-weight: bold; font-size: 12pt; }"));
#endif
// setup models
m_categoryFilterModel->setSourceModel(m_categoryModel);
m_ui->categoriesListView->setModel(m_categoryFilterModel);
// connect signals and slots
// selection models
connect(m_ui->categoriesListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &SettingsDialog::currentCategoryChanged);
// buttons
connect(m_ui->abortPushButton, &QPushButton::clicked, this, &SettingsDialog::reject);
connect(m_ui->applyPushButton, &QPushButton::clicked, this, &SettingsDialog::apply);
connect(m_ui->okPushButton, &QPushButton::clicked, this, &SettingsDialog::accept);
// dialog
connect(this, &SettingsDialog::accepted, this, &SettingsDialog::apply);
connect(this, &SettingsDialog::rejected, this, &SettingsDialog::reset);
// misc
connect(m_ui->filterLineEdit, &QLineEdit::textChanged, m_categoryFilterModel, &OptionCategoryFilterModel::setFilterFixedString);
connect(m_ui->filterLineEdit, &QLineEdit::textChanged, this, &SettingsDialog::updateTabWidget);
}
/*!
* \brief Destroys the settings dialog.
*/
SettingsDialog::~SettingsDialog()
{}
/*!
* \brief Sets whether the tab bar is always visible.
*
* \sa SettingsDialog::isTabBarAlwaysVisible()
*/
void SettingsDialog::setTabBarAlwaysVisible(bool value)
{
m_tabBarAlwaysVisible = value;
if(m_currentCategory) {
m_ui->pagesTabWidget->tabBar()->setHidden(!value && m_currentCategory->pages().size() == 1);
}
}
/*!
* \brief Returns the category for the specified \a categoryIndex.
*
* The settings dialog keeps ownership over the returned category.
* If no category exists for the specified index a null pointer is returned.
*/
OptionCategory *SettingsDialog::category(int categoryIndex) const
{
return m_categoryModel->category(categoryIndex);
}
/*!
* \brief Returns the page for the specified \a categoryIndex and the specified \a pageIndex.
*
* The settings dialog keeps ownership over the returned category.
* If no page for the specified indices a null pointer is returned.
*/
OptionPage *SettingsDialog::page(int categoryIndex, int pageIndex) const
{
if(OptionCategory *category = this->category(categoryIndex)) {
if(pageIndex < category->pages().length()) {
return category->pages()[pageIndex];
}
}
return nullptr;
}
/*!
* \brief Resets all pages before the dialog is shown by the application.
*/
void SettingsDialog::showEvent(QShowEvent *event)
{
if(!event->spontaneous()) {
foreach(OptionCategory *category, m_categoryModel->categories()) {
foreach(OptionPage *page, category->pages()) {
page->reset();
}
}
}
}
/*!
* \brief Shows the selected category specified by its model \a index in the category model.
*
* This private slot is called when m_ui->categoriesListView->selectionModel()->currentChanged() is emitted.
*/
void SettingsDialog::currentCategoryChanged(const QModelIndex &index)
{
showCategory(m_categoryModel->category(m_categoryFilterModel->mapToSource(index)));
}
/*!
* \brief Sets the current category to the specified \a category and updates the relevant widgets to show it.
*/
void SettingsDialog::showCategory(OptionCategory *category)
{
if(category) {
if(m_currentCategory != category) {
m_currentCategory = category;
m_ui->instructionLabel->setText(category->displayName());
}
} else {
m_currentCategory = nullptr;
m_ui->instructionLabel->setText(tr("No category selected"));
}
updateTabWidget();
}
/*!
* \brief Updates the tab widget to show the pages for the current category.
*/
void SettingsDialog::updateTabWidget()
{
if(m_currentCategory) {
m_ui->pagesTabWidget->setUpdatesEnabled(false);
QString searchKeyWord = m_ui->filterLineEdit->text();
int index = 0;
foreach(OptionPage *page, m_currentCategory->pages()) {
if(page->matches(searchKeyWord)) {
QScrollArea *scrollArea;
if(index < m_ui->pagesTabWidget->count()) {
scrollArea = qobject_cast<QScrollArea *>(m_ui->pagesTabWidget->widget(index));
scrollArea->takeWidget();
m_ui->pagesTabWidget->setTabText(index, page->displayName());
} 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());
}
if(page->widget()->layout()) {
page->widget()->layout()->setAlignment(Qt::AlignTop | Qt::AlignLeft);
}
scrollArea->setWidget(page->widget());
}
++index;
}
while(index < m_ui->pagesTabWidget->count()) {
QScrollArea *scrollArea = qobject_cast<QScrollArea *>(m_ui->pagesTabWidget->widget(index));
scrollArea->takeWidget();
m_ui->pagesTabWidget->removeTab(index);
delete scrollArea;
}
m_ui->pagesTabWidget->tabBar()->setHidden(!m_tabBarAlwaysVisible && m_ui->pagesTabWidget->count() == 1);
m_ui->pagesTabWidget->setUpdatesEnabled(true);
} else {
m_ui->pagesTabWidget->clear();
}
}
/*!
* \brief Applies all changes. Calls OptionCategory::applyAllPages() for each category.
*/
bool SettingsDialog::apply()
{
foreach(OptionCategory *category, m_categoryModel->categories()) {
foreach(OptionPage *page, category->pages()) {
if(!page->apply()) {
return false;
}
category->applyAllPages();
}
}
emit applied();
return true;
}
/*!
* \brief Resets all changes. Calls OptionCategory::resetAllPages() for each category.
*/
void SettingsDialog::reset()
{
foreach(OptionCategory *category, m_categoryModel->categories()) {
category->resetAllPages();
}
emit resetted();
}
}

View File

@ -0,0 +1,81 @@
#ifndef QT_UTILITIES_SETTINGSDIALOG_H
#define QT_UTILITIES_SETTINGSDIALOG_H
#include <c++utilities/application/global.h>
#include <QDialog>
#include <memory>
namespace Dialogs {
class OptionCategoryModel;
class OptionCategoryFilterModel;
class OptionCategory;
class OptionPage;
namespace Ui {
class SettingsDialog;
}
class LIB_EXPORT SettingsDialog : public QDialog
{
Q_OBJECT
Q_PROPERTY(bool isTabBarAlwaysVisible READ isTabBarAlwaysVisible WRITE setTabBarAlwaysVisible)
public:
explicit SettingsDialog(QWidget *parent = nullptr);
virtual ~SettingsDialog();
bool isTabBarAlwaysVisible() const;
void setTabBarAlwaysVisible(bool value);
OptionCategoryModel *categoryModel();
OptionCategory *category(int categoryIndex) const;
OptionPage *page(int categoryIndex, int pageIndex) const;
signals:
void applied();
void resetted();
protected:
void showEvent(QShowEvent *event);
private slots:
void currentCategoryChanged(const QModelIndex &index);
void updateTabWidget();
bool apply();
void reset();
private:
void showCategory(OptionCategory *category);
std::unique_ptr<Ui::SettingsDialog> m_ui;
OptionCategoryModel *m_categoryModel;
OptionCategoryFilterModel *m_categoryFilterModel;
OptionCategory *m_currentCategory;
bool m_tabBarAlwaysVisible;
};
/*!
* \brief Returns whether the tab bar is always visible.
*
* The tab bar is always visible by default.
*
* \sa SettingsDialog::setTabBarAlwaysVisible()
*/
inline bool SettingsDialog::isTabBarAlwaysVisible() const
{
return m_tabBarAlwaysVisible;
}
/*!
* \brief Returns the category model used by the settings dialog to manage the categories.
*/
inline OptionCategoryModel *SettingsDialog::categoryModel()
{
return m_categoryModel;
}
}
#endif // QT_UTILITIES_SETTINGSDIALOG_H

View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialogs::SettingsDialog</class>
<widget class="QDialog" name="Dialogs::SettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>515</width>
<height>311</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="mainWidget" native="true">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLabel" name="instructionLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>500</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>No category selected</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="Widgets::ClearLineEdit" name="filterLineEdit">
<property name="maximumSize">
<size>
<width>140</width>
<height>16777215</height>
</size>
</property>
<property name="placeholderText">
<string>Filter</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QTabWidget" name="pagesTabWidget">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QListView" name="categoriesListView">
<property name="maximumSize">
<size>
<width>140</width>
<height>16777215</height>
</size>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="bottomWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>168</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="abortPushButton">
<property name="styleSheet">
<string notr="true">background: none;</string>
</property>
<property name="text">
<string>Abort</string>
</property>
<property name="icon">
<iconset theme="dialog-cancel">
<normaloff/>
</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="applyPushButton">
<property name="styleSheet">
<string notr="true">background: none;</string>
</property>
<property name="text">
<string>Apply</string>
</property>
<property name="icon">
<iconset theme="dialog-ok-apply">
<normaloff/>
</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="okPushButton">
<property name="text">
<string>OK</string>
</property>
<property name="icon">
<iconset theme="dialog-ok">
<normaloff/>
</iconset>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Widgets::ClearLineEdit</class>
<extends>QLineEdit</extends>
<header location="global">qtutilities/widgets/clearlineedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

188
widgets/buttonoverlay.cpp Normal file
View File

@ -0,0 +1,188 @@
#include "buttonoverlay.h"
#include "iconbutton.h"
#include <QWidget>
#include <QHBoxLayout>
#include <QStyle>
#include <QStyleOption>
#include <QToolTip>
#include <QCursor>
#include <functional>
/*!
* \namespace Widgets
* \brief Provides a set of extended widgets such as ClearLineEdit and ClearComboBox.
*/
namespace Widgets {
/*!
* \class Widgets::ButtonOverlay
* \brief The ButtonOverlay class is used to display buttons on top of other widgets.
*
* The class creates a new layout manager and sets it to the widget which is specified
* when constructing an instance. Thus this widget must not already have a layout manager.
*
* The class is used to implement widget customization like ClearLineEidt and ClearComboBox.
*/
/*!
* \brief Constructs a button overlay for the specified \a widget.
* \param widget Specifies the widget to display the buttons on.
*/
ButtonOverlay::ButtonOverlay(QWidget *widget) :
m_widget(widget),
m_buttonWidget(new QWidget(widget)),
m_buttonLayout(new QHBoxLayout(m_buttonWidget)),
m_clearButton(nullptr),
m_infoButton(nullptr)
{
// setup button widget and layout
const QMargins margins = widget->contentsMargins();
QStyleOption opt;
opt.initFrom(m_widget);
const int frameWidth = widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, m_widget);
const int pad = 2;
m_buttonLayout->setContentsMargins(margins.left() + frameWidth + pad, margins.top() + frameWidth, margins.right() + frameWidth + pad, margins.bottom() + frameWidth);
m_buttonLayout->setAlignment(Qt::AlignCenter | Qt::AlignRight);
widget->setLayout(m_buttonLayout);
}
/*!
* \brief Destroys the button overlay.
*/
ButtonOverlay::~ButtonOverlay()
{}
/*!
* \brief Sets whether the clear button is enabled.
*/
void ButtonOverlay::setClearButtonEnabled(bool enabled)
{
if(isClearButtonEnabled() && !enabled) {
// disable clear button
m_buttonLayout->removeWidget(m_clearButton);
delete m_clearButton;
m_clearButton = nullptr;
} else if(!isClearButtonEnabled() && enabled) {
// enable clear button
m_clearButton = new IconButton;
m_clearButton->setHidden(isCleared());
m_clearButton->setPixmap(/*QIcon::fromTheme(QStringLiteral("edit-clear"), */QIcon(QStringLiteral(":/qtutilities/icons/hicolor/16x16/actions/edit-clear.png")/*)*/).pixmap(16));
m_clearButton->setGeometry(0, 0, 16, 16);
m_clearButton->setToolTip(QObject::tr("clear"));
QObject::connect(m_clearButton, &IconButton::clicked, std::bind(&ButtonOverlay::handleClearButtonClicked, this));
m_buttonLayout->addWidget(m_clearButton);
}
}
/*!
* \brief Shows an info button with the specified \a pixmap and \a infoText.
*
* If there is already an info button enabled, it gets replaced with the new button.
*
* \sa ButtonOverlay::disableInfoButton()
*/
void ButtonOverlay::enableInfoButton(const QPixmap &pixmap, const QString &infoText)
{
if(!m_infoButton) {
m_infoButton = new IconButton;
m_infoButton->setGeometry(0, 0, 16, 16);
QObject::connect(m_infoButton, &IconButton::clicked, std::bind(&ButtonOverlay::showInfo, this));
if(m_clearButton) {
m_buttonLayout->insertWidget(m_buttonLayout->count() - 2, m_infoButton);
} else {
m_buttonLayout->addWidget(m_infoButton);
}
}
m_infoButton->setPixmap(pixmap);
m_infoButton->setToolTip(infoText);
}
/*!
* \brief Hides an info button if one is shown.
* \sa ButtonOverlay::enableInfoButton()
*/
void ButtonOverlay::disableInfoButton()
{
if(m_infoButton) {
m_buttonLayout->removeWidget(m_infoButton);
delete m_infoButton;
m_infoButton = nullptr;
}
}
/*!
* \brief Adds a custom \a button.
*
* The button overlay takes ownership over the specified \a button.
*/
void ButtonOverlay::addCustomButton(QWidget *button)
{
m_buttonLayout->addWidget(button);
}
/*!
* \brief Inserts a custom \a button at the specified \a index.
*
* The button overlay takes ownership over the specified \a button.
*/
void ButtonOverlay::insertCustomButton(int index, QWidget *button)
{
m_buttonLayout->insertWidget(index, button);
}
/*!
* \brief Removes the specified custom \a button.
*
* The ownership of widget remains the same as when it was added.
*/
void ButtonOverlay::removeCustomButton(QWidget *button)
{
m_buttonLayout->removeWidget(button);
}
/*!
* \brief Updates the visibility of the clear button.
*
* This method is meant to be called when subclassing.
*/
void ButtonOverlay::updateClearButtonVisibility(bool visible)
{
if(m_clearButton) {
m_clearButton->setVisible(visible);
}
}
/*!
* \brief Clears the related widget.
*
* This method is meant to be implemented when subclassing.
*/
void ButtonOverlay::handleClearButtonClicked()
{}
/*!
* \brief Returns whether the related widget is cleared.
*
* This method is meant to be implemented when subclassing.
*/
bool ButtonOverlay::isCleared() const
{
return false;
}
/*!
* \brief Shows the info text using a tool tip.
*
* This method is called when the info button is clicked.
*/
void ButtonOverlay::showInfo()
{
if(m_infoButton) {
QToolTip::showText(QCursor::pos(), m_infoButton->toolTip(), m_infoButton);
}
}
}

76
widgets/buttonoverlay.h Normal file
View File

@ -0,0 +1,76 @@
#ifndef WIDGETS_CLEARABLEWIDGET_H
#define WIDGETS_CLEARABLEWIDGET_H
#include <c++utilities/application/global.h>
#include <QtGlobal>
QT_BEGIN_NAMESPACE
class QWidget;
class QHBoxLayout;
class QString;
class QPixmap;
QT_END_NAMESPACE
namespace Widgets {
class IconButton;
class LIB_EXPORT ButtonOverlay
{
public:
explicit ButtonOverlay(QWidget *widget);
virtual ~ButtonOverlay();
QHBoxLayout *buttonLayout();
bool isClearButtonEnabled() const;
void setClearButtonEnabled(bool enabled);
bool isInfoButtonEnabled() const;
void enableInfoButton(const QPixmap &pixmap, const QString &infoText);
void disableInfoButton();
void addCustomButton(QWidget *button);
void insertCustomButton(int index, QWidget *button);
void removeCustomButton(QWidget *button);
virtual bool isCleared() const;
protected:
void updateClearButtonVisibility(bool visible);
virtual void handleClearButtonClicked();
private:
void showInfo();
QWidget *m_widget;
QWidget *m_buttonWidget;
QHBoxLayout *m_buttonLayout;
IconButton *m_clearButton;
IconButton *m_infoButton;
};
/*!
* \brief Returns the layout manager holding the buttons.
*/
inline QHBoxLayout *ButtonOverlay::buttonLayout()
{
return m_buttonLayout;
}
/*!
* \brief Returns whether the clear button is enabled.
*/
inline bool ButtonOverlay::isClearButtonEnabled() const
{
return m_clearButton != nullptr;
}
/*!
* \brief Returns whether the info button is enabled.
*/
inline bool ButtonOverlay::isInfoButtonEnabled() const
{
return m_infoButton != nullptr;
}
}
#endif // WIDGETS_CLEARABLEWIDGET_H

56
widgets/clearcombobox.cpp Normal file
View File

@ -0,0 +1,56 @@
#include "clearcombobox.h"
#include <QHBoxLayout>
#include <QStyle>
#include <QStyleOptionComboBox>
namespace Widgets {
/*!
* \class Widgets::ClearComboBox
* \brief A QComboBox with an embedded button for clearing its contents.
*/
/*!
* \brief Constructs a clear combo box.
*/
ClearComboBox::ClearComboBox(QWidget *parent) :
QComboBox(parent),
ButtonOverlay(this)
{
const QMargins margins = contentsMargins();
QStyleOptionComboBox opt;
opt.initFrom(this);
const int frameWidth = style()->pixelMetric(QStyle::PM_ComboBoxFrameWidth, &opt, this);
const int pad = 2;
const int buttonWidth = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxArrow, this).width();
buttonLayout()->setContentsMargins(margins.left() + frameWidth + pad, margins.top() + frameWidth, margins.right() + frameWidth + pad + buttonWidth, margins.bottom() + frameWidth);
setClearButtonEnabled(isEditable());
connect(this, &ClearComboBox::currentTextChanged, this, &ClearComboBox::handleTextChanged);
}
/*!
* \brief Destroys the clear combo box.
*/
ClearComboBox::~ClearComboBox()
{}
/*!
* \brief Updates the visibility of the clear button.
*/
void ClearComboBox::handleTextChanged(const QString &text)
{
updateClearButtonVisibility(!text.isEmpty());
}
void ClearComboBox::handleClearButtonClicked()
{
clearEditText();
}
bool ClearComboBox::isCleared() const
{
return currentText().isEmpty();
}
}

28
widgets/clearcombobox.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef WIDGETS_CLEARCOMBOBOX_H
#define WIDGETS_CLEARCOMBOBOX_H
#include "buttonoverlay.h"
#include <c++utilities/application/global.h>
#include <QComboBox>
namespace Widgets {
class LIB_EXPORT ClearComboBox : public QComboBox, public ButtonOverlay
{
Q_OBJECT
public:
explicit ClearComboBox(QWidget *parent = nullptr);
~ClearComboBox();
bool isCleared() const;
private slots:
void handleTextChanged(const QString &text);
void handleClearButtonClicked();
};
} // namespace Widgets
#endif // WIDGETS_CLEARCOMBOBOX_H

45
widgets/clearlineedit.cpp Normal file
View File

@ -0,0 +1,45 @@
#include "clearlineedit.h"
namespace Widgets {
/*!
* \class Widgets::ClearLineEdit
* \brief A QLineEdit with an embedded button for clearing its contents.
*/
/*!
* \brief Constructs a clear line edit.
*/
ClearLineEdit::ClearLineEdit(QWidget *parent) :
QLineEdit(parent),
ButtonOverlay(this)
{
ButtonOverlay::setClearButtonEnabled(true);
connect(this, &ClearLineEdit::textChanged, this, &ClearLineEdit::handleTextChanged);
}
/*!
* \brief Destroys the clear combo box.
*/
ClearLineEdit::~ClearLineEdit()
{}
/*!
* \brief Updates the visibility of the clear button.
*/
void ClearLineEdit::handleTextChanged(const QString &text)
{
updateClearButtonVisibility(!text.isEmpty());
}
void ClearLineEdit::handleClearButtonClicked()
{
clear();
}
bool ClearLineEdit::isCleared() const
{
return text().isEmpty();
}
}

33
widgets/clearlineedit.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef WIDGETS_TAGFIELDLINEEDIT_H
#define WIDGETS_TAGFIELDLINEEDIT_H
#include "buttonoverlay.h"
#include <c++utilities/application/global.h>
#include <QLineEdit>
QT_BEGIN_NAMESPACE
class QHBoxLayout;
QT_END_NAMESPACE
namespace Widgets {
class IconButton;
class LIB_EXPORT ClearLineEdit : public QLineEdit, public ButtonOverlay
{
Q_OBJECT
public:
explicit ClearLineEdit(QWidget *parent = nullptr);
~ClearLineEdit();
bool isCleared() const;
private slots:
void handleTextChanged(const QString &text);
void handleClearButtonClicked();
};
}
#endif // WIDGETS_TAGFIELDLINEEDIT_H

View File

@ -0,0 +1,52 @@
#include "clearplaintextedit.h"
#include <QHBoxLayout>
namespace Widgets {
/*!
* \class Widgets::ClearPlainTextEdit
* \brief A QPlainTextEdit with an embedded button for clearing its contents.
*/
/*!
* \brief Constructs a clear plain text edit.
*/
ClearPlainTextEdit::ClearPlainTextEdit(QWidget *parent) :
QPlainTextEdit(parent),
ButtonOverlay(this)
{
// set alignment to show buttons in the bottom right corner
ButtonOverlay::buttonLayout()->setAlignment(Qt::AlignBottom | Qt::AlignRight);
ButtonOverlay::setClearButtonEnabled(true);
connect(document(), &QTextDocument::contentsChanged, this, &ClearPlainTextEdit::handleTextChanged);
}
/*!
* \brief Destroys the clear plain text edit.
*/
ClearPlainTextEdit::~ClearPlainTextEdit()
{}
/*!
* \brief Updates the visibility of the clear button.
*/
void ClearPlainTextEdit::handleTextChanged()
{
updateClearButtonVisibility(!document()->isEmpty());
}
void ClearPlainTextEdit::handleClearButtonClicked()
{
// do no call clear() here to prevent clearing of undo history
QTextCursor cursor(document());
cursor.select(QTextCursor::Document);
cursor.removeSelectedText();
}
bool ClearPlainTextEdit::isCleared() const
{
return document()->isEmpty();
}
} // namespace Widgets

View File

@ -0,0 +1,26 @@
#ifndef WIDGETS_CLEARPLAINTEXTEDIT_H
#define WIDGETS_CLEARPLAINTEXTEDIT_H
#include "buttonoverlay.h"
#include <QPlainTextEdit>
namespace Widgets {
class LIB_EXPORT ClearPlainTextEdit : public QPlainTextEdit, public ButtonOverlay
{
Q_OBJECT
public:
explicit ClearPlainTextEdit(QWidget *parent = nullptr);
~ClearPlainTextEdit();
bool isCleared() const;
private slots:
void handleTextChanged();
void handleClearButtonClicked();
};
} // namespace Widgets
#endif // WIDGETS_CLEARPLAINTEXTEDIT_H

76
widgets/clearspinbox.cpp Normal file
View File

@ -0,0 +1,76 @@
#include "clearspinbox.h"
#include <QHBoxLayout>
#include <QStyle>
#include <QStyleOptionSpinBox>
namespace Widgets {
/*!
* \class Widgets::ClearSpinBox
* \brief A QSpinBox with an embedded button for clearing its contents and the ability to hide
* the minimum value.
*/
/*!
* \brief Constructs a clear spin box.
*/
ClearSpinBox::ClearSpinBox(QWidget *parent) :
QSpinBox(parent),
ButtonOverlay(this),
m_minimumHidden(false)
{
const QMargins margins = contentsMargins();
QStyleOptionComboBox opt;
opt.initFrom(this);
const int frameWidth = style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth, &opt, this);
const int pad = 5;
const int buttonWidth = style()->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxUp, this).width() + 10;
buttonLayout()->setContentsMargins(margins.left() + frameWidth + pad, margins.top() + frameWidth, margins.right() + frameWidth + pad + buttonWidth, margins.bottom() + frameWidth);
setClearButtonEnabled(true);
connect(this, static_cast<void (ClearSpinBox::*)(int)>(&ClearSpinBox::valueChanged), this, &ClearSpinBox::handleValueChanged);
}
/*!
* \brief Destroys the clear spin box.
*/
ClearSpinBox::~ClearSpinBox()
{}
/*!
* \brief Updates the visibility of the clear button.
*/
void ClearSpinBox::handleValueChanged(int value)
{
updateClearButtonVisibility(value != minimum());
}
void ClearSpinBox::handleClearButtonClicked()
{
setValue(minimum());
}
bool ClearSpinBox::isCleared() const
{
return value() == minimum();
}
int ClearSpinBox::valueFromText(const QString &text) const
{
if(m_minimumHidden && text.isEmpty()) {
return minimum();
} else {
return QSpinBox::valueFromText(text);
}
}
QString ClearSpinBox::textFromValue(int val) const
{
if(m_minimumHidden && (val == minimum())) {
return QString();
} else {
return QSpinBox::textFromValue(val);
}
}
}

84
widgets/clearspinbox.h Normal file
View File

@ -0,0 +1,84 @@
#ifndef WIDGETS_CLEARSPINBOX_H
#define WIDGETS_CLEARSPINBOX_H
#include "buttonoverlay.h"
#include <c++utilities/application/global.h>
#include <QSpinBox>
#include <QLineEdit>
QT_BEGIN_NAMESPACE
class QHBoxLayout;
QT_END_NAMESPACE
namespace Widgets {
class IconButton;
class LIB_EXPORT ClearSpinBox : public QSpinBox, public ButtonOverlay
{
Q_OBJECT
Q_PROPERTY(bool minimumHidden READ minimumHidden WRITE setMinimumHidden)
Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText)
Q_PROPERTY(bool isCleared READ isCleared)
public:
explicit ClearSpinBox(QWidget *parent = nullptr);
~ClearSpinBox();
bool minimumHidden() const;
void setMinimumHidden(bool value);
QString placeholderText() const;
void setPlaceholderText(const QString &placeholderText);
bool isCleared() const;
protected:
int valueFromText(const QString &text) const;
QString textFromValue(int val) const;
private slots:
void handleValueChanged(int value);
void handleClearButtonClicked();
private:
bool m_minimumHidden;
};
/*!
* \brief Returns whether the minimum value will be hidden.
*/
inline bool ClearSpinBox::minimumHidden() const
{
return m_minimumHidden;
}
/*!
* \brief Sets whether the minimum value should be hidden.
*/
inline void ClearSpinBox::setMinimumHidden(bool value)
{
m_minimumHidden = value;
}
/*!
* \brief Returns the placeholder text.
* \sa QLineEdit::placeholderText()
*/
inline QString ClearSpinBox::placeholderText() const
{
return lineEdit()->placeholderText();
}
/*!
* \brief Sets the placeholder text.
* \sa QLineEdit::setPlaceholderText()
*/
inline void ClearSpinBox::setPlaceholderText(const QString &placeholderText)
{
lineEdit()->setPlaceholderText(placeholderText);
}
}
#endif // WIDGETS_CLEARSPINBOX_H

80
widgets/iconbutton.cpp Normal file
View File

@ -0,0 +1,80 @@
#include "iconbutton.h"
#include <QStylePainter>
#include <QStyleOptionFocusRect>
#include <QKeyEvent>
#include <QDebug>
namespace Widgets {
/*!
* \class Widgets::IconButton
* \brief A simple QAbstractButton implementation displaying a QPixmap.
*/
/*!
* \brief Constructs an icon button.
*/
IconButton::IconButton(QWidget *parent) :
QAbstractButton(parent)
{
setCursor(Qt::ArrowCursor);
setFocusPolicy(Qt::NoFocus);
}
/*!
* \brief Destroys the icon button.
*/
IconButton::~IconButton()
{}
QSize IconButton::sizeHint() const
{
#if QT_VERSION >= 0x050100
const qreal pixmapRatio = m_pixmap.devicePixelRatio();
#else
const qreal pixmapRatio = 1.0;
#endif
return QSize(m_pixmap.width() / pixmapRatio, m_pixmap.height() / pixmapRatio);
}
void IconButton::paintEvent(QPaintEvent *)
{
#if QT_VERSION >= 0x050100
const qreal pixmapRatio = m_pixmap.devicePixelRatio();
#else
const qreal pixmapRatio = 1.0;
#endif
QStylePainter painter(this);
QRect pixmapRect = QRect(0, 0, m_pixmap.width() / pixmapRatio, m_pixmap.height() / pixmapRatio);
pixmapRect.moveCenter(rect().center());
painter.drawPixmap(pixmapRect, m_pixmap);
if(hasFocus()) {
QStyleOptionFocusRect focusOption;
focusOption.initFrom(this);
focusOption.rect = pixmapRect;
#ifdef Q_OS_MAC
focusOption.rect.adjust(-4, -4, 4, 4);
painter.drawControl(QStyle::CE_FocusFrame, focusOption);
#else
painter.drawPrimitive(QStyle::PE_FrameFocusRect, focusOption);
#endif
}
}
void IconButton::keyPressEvent(QKeyEvent *event)
{
QAbstractButton::keyPressEvent(event);
if (!event->modifiers() && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)) {
click();
}
event->accept();
}
void IconButton::keyReleaseEvent(QKeyEvent *event)
{
QAbstractButton::keyReleaseEvent(event);
event->accept();
}
}

52
widgets/iconbutton.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef WIDGETS_ICONBUTTON_H
#define WIDGETS_ICONBUTTON_H
#include <c++utilities/application/global.h>
#include <QAbstractButton>
#include <QPixmap>
namespace Widgets {
class LIB_EXPORT IconButton : public QAbstractButton
{
Q_OBJECT
Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap)
public:
explicit IconButton(QWidget *parent = nullptr);
~IconButton();
const QPixmap &pixmap() const;
void setPixmap(const QPixmap &pixmap);
QSize sizeHint() const;
protected:
void paintEvent(QPaintEvent *event);
void keyPressEvent(QKeyEvent *event);
void keyReleaseEvent(QKeyEvent *event);
private:
QPixmap m_pixmap;
};
/*!
* \brief Returns the pixmap.
*/
inline const QPixmap &IconButton::pixmap() const
{
return m_pixmap;
}
/*!
* \brief Sets the pixmap.
*/
inline void IconButton::setPixmap(const QPixmap &pixmap)
{
m_pixmap = pixmap;
update();
}
}
#endif // WIDGETS_ICONBUTTON_H