mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-01-21 05:00:06 -05:00
698 lines
23 KiB
C++
698 lines
23 KiB
C++
#include "FractoriumPch.h"
|
|
#include "QssDialog.h"
|
|
#include "ui_QssDialog.h"
|
|
#include "qcssscanner.h"
|
|
|
|
/// <summary>
|
|
/// The code in this file did not originate in Fractorium.
|
|
/// It was taken either in whole or in part from the source code
|
|
/// of Qt Creator. Their license applies.
|
|
/// </summary>
|
|
|
|
/// <summary>
|
|
/// Comparison for sorting object names.
|
|
/// Strings not starting with the letter 'Q' take precedence.
|
|
/// This has the effect of putting custom derived classes first before
|
|
/// all Q* classes.
|
|
/// </summary>
|
|
/// <param name="s1">The first string to compare</param>
|
|
/// <param name="s2">The second string to compare</param>
|
|
/// <returns>True if s1 < s2 with special rules for 'Q' taken into account.</returns>
|
|
bool CaseInsensitiveLessThanQ(const QString& s1, const QString& s2)
|
|
{
|
|
if (s1.length() && s2.length())
|
|
{
|
|
if (s1[0] == 'Q' && s2[0] == 'Q')
|
|
return s1.toLower() < s2.toLower();
|
|
else if (s1[0] == 'Q')
|
|
return false;
|
|
else if (s2[0] == 'Q')
|
|
return true;
|
|
else
|
|
return s1.toLower() < s2.toLower();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Construct a QssDialog.
|
|
/// This manually constructs much of the menu GUI via code rather
|
|
/// than in the designer.
|
|
/// </summary>
|
|
/// <param name="parent">The main Fractorium window.</param>
|
|
QssDialog::QssDialog(Fractorium* parent) :
|
|
QDialog(parent),
|
|
ui(new Ui::QssDialog),
|
|
m_Parent(parent),
|
|
m_AddColorAction(new QAction(tr("Add Color"), this)),
|
|
m_AddGeomAction(new QAction(tr("Add Geometry"), this)),
|
|
m_AddBorderAction(new QAction(tr("Add Border"), this)),
|
|
m_AddFontAction(new QAction(tr("Add Font..."), this)),
|
|
m_AddStyleAction(new QAction(tr("Set Theme"), this))
|
|
{
|
|
ui->setupUi(this);
|
|
m_LastStyle = m_Parent->styleSheet();
|
|
setWindowTitle("QSS Editor - default.qss");
|
|
connect(ui->QssEdit, SIGNAL(textChanged()), this, SLOT(SlotTextChanged()));
|
|
QToolBar* toolBar = new QToolBar(this);
|
|
QMenu* colorActionMenu = new QMenu(this);
|
|
QMenu* geomActionMenu = new QMenu(this);
|
|
QMenu* borderActionMenu = new QMenu(this);
|
|
QMenu* styleActionMenu = new QMenu(this);
|
|
(m_ColorActionMapper = new QSignalMapper(this))->setMapping(m_AddColorAction, QString());
|
|
(m_GeomActionMapper = new QSignalMapper(this))->setMapping(m_AddGeomAction, QString());
|
|
(m_BorderActionMapper = new QSignalMapper(this))->setMapping(m_AddBorderAction, QString());
|
|
(m_StyleActionMapper = new QSignalMapper(this))->setMapping(m_AddStyleAction, QString());
|
|
connect(ui->QssLoadButton, SIGNAL(clicked()), this, SLOT(LoadButton_clicked()), Qt::QueuedConnection);
|
|
connect(ui->QssSaveButton, SIGNAL(clicked()), this, SLOT(SaveButton_clicked()), Qt::QueuedConnection);
|
|
connect(ui->QssBasicButton, SIGNAL(clicked()), this, SLOT(BasicButton_clicked()), Qt::QueuedConnection);
|
|
connect(ui->QssMediumButton, SIGNAL(clicked()), this, SLOT(MediumButton_clicked()), Qt::QueuedConnection);
|
|
connect(ui->QssAdvancedButton, SIGNAL(clicked()), this, SLOT(AdvancedButton_clicked()), Qt::QueuedConnection);
|
|
connect(m_AddFontAction, SIGNAL(triggered()), this, SLOT(SlotAddFont()));
|
|
QVector<QPair<QString, QString>> colorVec;
|
|
colorVec.reserve(12);
|
|
colorVec.push_back(QPair<QString, QString>("color", ""));
|
|
colorVec.push_back(QPair<QString, QString>("background-color", ""));
|
|
colorVec.push_back(QPair<QString, QString>("alternate-background-color", ""));
|
|
colorVec.push_back(QPair<QString, QString>("border-color", ""));
|
|
colorVec.push_back(QPair<QString, QString>("border-top-color", ""));
|
|
colorVec.push_back(QPair<QString, QString>("border-right-color", ""));
|
|
colorVec.push_back(QPair<QString, QString>("border-bottom-color", ""));
|
|
colorVec.push_back(QPair<QString, QString>("border-left-color", ""));
|
|
colorVec.push_back(QPair<QString, QString>("gridline-color", ""));
|
|
colorVec.push_back(QPair<QString, QString>("selection-color", ""));
|
|
colorVec.push_back(QPair<QString, QString>("selection-background-color", ""));
|
|
|
|
for (auto& c : colorVec)
|
|
{
|
|
auto colorAction = colorActionMenu->addAction(c.first);
|
|
m_ColorMap[c.first] = c.second;
|
|
connect(colorAction, SIGNAL(triggered()), m_ColorActionMapper, SLOT(map()));
|
|
m_ColorActionMapper->setMapping(colorAction, c.first);
|
|
}
|
|
|
|
QVector<QPair<QString, QString>> geomVec;
|
|
geomVec.reserve(12);
|
|
geomVec.push_back(QPair<QString, QString>("width", "100px"));
|
|
geomVec.push_back(QPair<QString, QString>("height", "50px"));
|
|
geomVec.push_back(QPair<QString, QString>("spacing", "10"));
|
|
geomVec.push_back(QPair<QString, QString>("padding", "3px"));
|
|
geomVec.push_back(QPair<QString, QString>("padding-top", "3px"));
|
|
geomVec.push_back(QPair<QString, QString>("padding-right", "3px"));
|
|
geomVec.push_back(QPair<QString, QString>("padding-bottom", "3px"));
|
|
geomVec.push_back(QPair<QString, QString>("padding-left", "3px"));
|
|
geomVec.push_back(QPair<QString, QString>("margin", "3px"));
|
|
geomVec.push_back(QPair<QString, QString>("margin-top", "3px"));
|
|
geomVec.push_back(QPair<QString, QString>("margin-right", "3px"));
|
|
geomVec.push_back(QPair<QString, QString>("margin-bottom", "3px"));
|
|
geomVec.push_back(QPair<QString, QString>("margin-left", "3px"));
|
|
|
|
for (auto& g : geomVec)
|
|
{
|
|
auto geomAction = geomActionMenu->addAction(g.first);
|
|
m_GeomMap[g.first] = g.second;
|
|
connect(geomAction, SIGNAL(triggered()), m_GeomActionMapper, SLOT(map()));
|
|
m_GeomActionMapper->setMapping(geomAction, g.first);
|
|
}
|
|
|
|
QVector<QPair<QString, QString>> borderVec;
|
|
borderVec.reserve(8);
|
|
borderVec.push_back(QPair<QString, QString>("border", "1px solid black"));
|
|
borderVec.push_back(QPair<QString, QString>("border-top", "1px inset black"));
|
|
borderVec.push_back(QPair<QString, QString>("border-right", "1px outset black"));
|
|
borderVec.push_back(QPair<QString, QString>("border-bottom", "1px ridge black"));
|
|
borderVec.push_back(QPair<QString, QString>("border-left", "1px groove black"));
|
|
borderVec.push_back(QPair<QString, QString>("border-style", "double"));
|
|
borderVec.push_back(QPair<QString, QString>("border-width", "1px"));
|
|
borderVec.push_back(QPair<QString, QString>("border-radius", "10px"));
|
|
|
|
for (auto& b : borderVec)
|
|
{
|
|
auto borderAction = borderActionMenu->addAction(b.first);
|
|
m_BorderMap[b.first] = b.second;
|
|
connect(borderAction, SIGNAL(triggered()), m_BorderActionMapper, SLOT(map()));
|
|
m_BorderActionMapper->setMapping(borderAction, b.first);
|
|
}
|
|
|
|
auto styles = QStyleFactory::keys();
|
|
|
|
for (auto& s : styles)
|
|
{
|
|
auto styleAction = styleActionMenu->addAction(s);
|
|
m_StyleMap[s] = s;
|
|
connect(styleAction, SIGNAL(triggered()), m_StyleActionMapper, SLOT(map()));
|
|
m_StyleActionMapper->setMapping(styleAction, s);
|
|
}
|
|
|
|
connect(m_ColorActionMapper, SIGNAL(mappedString(const QString&)), this, SLOT(SlotAddColor(const QString&)));
|
|
connect(m_GeomActionMapper, SIGNAL(mappedString(const QString&)), this, SLOT(SlotAddGeom(const QString&)));
|
|
connect(m_BorderActionMapper, SIGNAL(mappedString(const QString&)), this, SLOT(SlotAddBorder(const QString&)));
|
|
connect(m_StyleActionMapper, SIGNAL(mappedString(const QString&)), this, SLOT(SlotSetTheme(const QString&)));
|
|
m_AddColorAction->setMenu(colorActionMenu);
|
|
m_AddGeomAction->setMenu(geomActionMenu);
|
|
m_AddBorderAction->setMenu(borderActionMenu);
|
|
m_AddStyleAction->setMenu(styleActionMenu);
|
|
toolBar->addAction(m_AddColorAction);
|
|
toolBar->addAction(m_AddGeomAction);
|
|
toolBar->addAction(m_AddBorderAction);
|
|
toolBar->addAction(m_AddFontAction);
|
|
toolBar->addAction(m_AddStyleAction);
|
|
ui->verticalLayout->insertWidget(0, toolBar);
|
|
ui->QssEdit->setFocus();
|
|
m_ApplyTimer = new QTimer(this);
|
|
m_ApplyTimer->setSingleShot(true);
|
|
m_ApplyTimer->setInterval(1000);
|
|
connect(m_ApplyTimer, SIGNAL(timeout()), this, SLOT(SlotApplyCss()));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Destructor that stops the apply timer and deletes the ui.
|
|
/// </summary>
|
|
QssDialog::~QssDialog()
|
|
{
|
|
m_ApplyTimer->stop();
|
|
delete ui;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Thin wrapper around getting the text from the main text box as plain text.
|
|
/// </summary>
|
|
/// <returns>The plain text of the main text box</returns>
|
|
QString QssDialog::Text() const
|
|
{
|
|
return ui->QssEdit->toPlainText();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Thin wrapper around setting the text of the main text box.
|
|
/// </summary>
|
|
/// <param name="t">The text to set</param>
|
|
void QssDialog::SetText(const QString& t)
|
|
{
|
|
ui->QssEdit->setText(t);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the class names of all objects in the application.
|
|
/// This only makes one entry for each class type.
|
|
/// It will also optionally return the object names as well for advanced QSS editing.
|
|
/// </summary>
|
|
/// <param name="includeObjectNames">Whether to get the individual object names as well</param>
|
|
/// <returns>A list of all class names with optional entries for each individual object</returns>
|
|
QList<QString> QssDialog::GetClassNames(bool includeObjectNames)
|
|
{
|
|
QSet<QString> classNames;
|
|
QList<QList<QString>> dialogClassNames;
|
|
auto widgetList = m_Parent->findChildren<QWidget*>();
|
|
|
|
for (int i = 0; i < widgetList.size(); i++)
|
|
{
|
|
auto classAndName = QString(widgetList[i]->metaObject()->className());
|
|
|
|
if (!includeObjectNames)
|
|
{
|
|
classNames.insert(classAndName);
|
|
}
|
|
else
|
|
{
|
|
auto dlg = qobject_cast<QDialog*>(widgetList[i]);
|
|
|
|
if (dlg)//Dialogs only nest one level deep, so no need for generalized recursion.
|
|
{
|
|
QSet<QString> dlgSet;
|
|
auto dlgWidgetList = dlg->findChildren<QWidget*>();//Find all children of the dialog.
|
|
dlgSet.insert(classAndName);//Add the basic dialog class name, opening curly brace will be added later.
|
|
classAndName += " ";
|
|
|
|
for (int i = 0; i < dlgWidgetList.size(); i++)
|
|
{
|
|
auto dlgClassAndName = classAndName + QString(dlgWidgetList[i]->metaObject()->className());
|
|
dlgSet.insert(dlgClassAndName);
|
|
|
|
if (!dlgWidgetList[i]->objectName().isEmpty())//Add the class with object name for individual control customization.
|
|
{
|
|
dlgClassAndName += "#" + dlgWidgetList[i]->objectName();
|
|
dlgSet.insert(dlgClassAndName);
|
|
}
|
|
}
|
|
|
|
QList<QString> dlgList(dlgSet.begin(), dlgSet.end());//Convert set to list and sort.
|
|
std::sort(dlgList.begin(), dlgList.end(), CaseInsensitiveLessThanQ);
|
|
dialogClassNames.push_back(dlgList);//Add this to the full list after sorting at the end.
|
|
}
|
|
else if (GetAllParents<QDialog*>(widgetList[i]).empty())//Skip widgets on dialogs, they are added above.
|
|
{
|
|
classNames.insert(classAndName);//Add the basic class name.
|
|
|
|
if (!widgetList[i]->objectName().isEmpty())//Add the class with object name for individual control customization.
|
|
{
|
|
classAndName += "#" + widgetList[i]->objectName();
|
|
classNames.insert(classAndName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QList<QString> l(classNames.begin(), classNames.end());//Convert set to list and sort.
|
|
std::sort(l.begin(), l.end(), CaseInsensitiveLessThanQ);
|
|
|
|
for (auto& d : dialogClassNames)
|
|
l.append(d);
|
|
|
|
return l;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether the passed in stylesheet text is valid.
|
|
/// If the initial parse fails, a second attempt is made by wrapping the entire
|
|
/// text in curly braces.
|
|
/// </summary>
|
|
/// <param name="styleSheet">The stylesheet text to analyze.</param>
|
|
/// <returns>True if valid, else false.</returns>
|
|
bool QssDialog::IsStyleSheetValid(const QString& styleSheet)
|
|
{
|
|
QCss::Parser parser(styleSheet);
|
|
QCss::StyleSheet sheet;
|
|
|
|
if (parser.parse(&sheet))
|
|
return true;
|
|
|
|
QString fullSheet = QStringLiteral("* { ");
|
|
fullSheet += styleSheet;
|
|
fullSheet += QLatin1Char('}');
|
|
QCss::Parser parser2(fullSheet);
|
|
return parser2.parse(&sheet);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Save the current stylesheet text to default.qss.
|
|
/// Also save the selected theme to the settings.
|
|
/// Called when the user clicks ok.
|
|
/// Not called if cancelled or closed with the X.
|
|
/// </summary>
|
|
void QssDialog::accept()
|
|
{
|
|
if (m_Theme)
|
|
m_Parent->m_Settings->Theme(m_Theme->objectName());
|
|
|
|
SaveAsDefault();
|
|
QDialog::accept();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restore the stylesheet and theme to what it was when the dialog was opened.
|
|
/// Called when the user clicks cancel or closes with the X.
|
|
/// </summary>
|
|
void QssDialog::reject()
|
|
{
|
|
if (!m_LastStyle.isEmpty())
|
|
m_Parent->setStyleSheet(m_LastStyle);
|
|
|
|
if (m_LastTheme)
|
|
{
|
|
m_Parent->setStyle(m_LastTheme);
|
|
m_Parent->m_Settings->Theme(m_LastTheme->objectName());
|
|
}
|
|
|
|
m_Parent->m_Controller->FillVariationTreeWithCurrentXform();
|
|
QDialog::reject();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shows the event.
|
|
/// </summary>
|
|
/// <param name="e">The e.</param>
|
|
void QssDialog::showEvent(QShowEvent* e)
|
|
{
|
|
if (m_Parent)
|
|
{
|
|
m_LastStyle = m_Parent->styleSheet();
|
|
m_LastTheme = m_Parent->m_Theme;//The style() member cannot be relied upon, it is *not* the same object passed to setStyle();
|
|
SetText(m_LastStyle);
|
|
}
|
|
|
|
QDialog::showEvent(e);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start the timer which will analyze and apply the current stylesheet text.
|
|
/// Each successive keystroke will reset the timer if it has not timed out yet.
|
|
/// This is only called when the dialog is visible because it seems to be spurriously
|
|
/// called on startup.
|
|
/// Called when the user changes the text in main text box.
|
|
/// </summary>
|
|
void QssDialog::SlotTextChanged()
|
|
{
|
|
if (isVisible())//Sometimes this fires even though the window is not shown yet.
|
|
m_ApplyTimer->start();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a color string to the stylesheet text.
|
|
/// Called when the user clicks the add color menu.
|
|
/// </summary>
|
|
/// <param name="s">The color string selector to add</param>
|
|
void QssDialog::SlotAddColor(const QString& s)
|
|
{
|
|
const QColor color = QColorDialog::getColor(0xffffffff, this, QString(), QColorDialog::ShowAlphaChannel);
|
|
|
|
if (!color.isValid())
|
|
return;
|
|
|
|
QString colorStr;
|
|
|
|
if (color.alpha() == 255)
|
|
{
|
|
colorStr = QString(QStringLiteral("rgb(%1, %2, %3)")).arg(
|
|
color.red()).arg(color.green()).arg(color.blue());
|
|
}
|
|
else
|
|
{
|
|
colorStr = QString(QStringLiteral("rgba(%1, %2, %3, %4)")).arg(
|
|
color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha());
|
|
}
|
|
|
|
InsertCssProperty(s, colorStr);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a geometry string to the stylesheet text.
|
|
/// </summary>
|
|
/// <param name="s">The geometry string to add</param>
|
|
void QssDialog::SlotAddGeom(const QString& s)
|
|
{
|
|
auto val = m_GeomMap[s];
|
|
InsertCssProperty(s, val);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a border string to the stylesheet text.
|
|
/// </summary>
|
|
/// <param name="s">The border string to add</param>
|
|
void QssDialog::SlotAddBorder(const QString& s)
|
|
{
|
|
auto val = m_BorderMap[s];
|
|
InsertCssProperty(s, val);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the theme to the user selection.
|
|
/// Called when the user selects an item on the theme combo box.
|
|
/// </summary>
|
|
/// <param name="s">The s.</param>
|
|
void QssDialog::SlotSetTheme(const QString& s)
|
|
{
|
|
if (auto theme = QStyleFactory::create(s))
|
|
{
|
|
m_Theme = theme;
|
|
m_Parent->setStyle(m_Theme);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a font string.
|
|
/// Called when the user clicks the add font menu button.
|
|
/// </summary>
|
|
void QssDialog::SlotAddFont()
|
|
{
|
|
bool ok;
|
|
auto font = QFontDialog::getFont(&ok, this);
|
|
|
|
if (ok)
|
|
{
|
|
QString fontStr;
|
|
|
|
if (font.weight() != QFont::Normal)
|
|
{
|
|
fontStr += QString::number(font.weight());
|
|
fontStr += QLatin1Char(' ');
|
|
}
|
|
|
|
switch (font.style())
|
|
{
|
|
case QFont::StyleItalic:
|
|
fontStr += QStringLiteral("italic ");
|
|
break;
|
|
|
|
case QFont::StyleOblique:
|
|
fontStr += QStringLiteral("oblique ");
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
fontStr += QString::number(font.pointSize());
|
|
fontStr += QStringLiteral("pt \"");
|
|
fontStr += font.family();
|
|
fontStr += QLatin1Char('"');
|
|
InsertCssProperty(QStringLiteral("font"), fontStr);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if the current stylesheet is valid and apply it if so.
|
|
/// Also indicate via label whether it was valid.
|
|
/// </summary>
|
|
void QssDialog::SlotApplyCss()
|
|
{
|
|
auto label = ui->QssValidityLabel;
|
|
auto style = Text();
|
|
const bool valid = IsStyleSheetValid(style);
|
|
ui->QssButtonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);
|
|
|
|
if (valid)
|
|
{
|
|
label->setText(tr("Valid Style Sheet"));
|
|
label->setStyleSheet(QStringLiteral("color: green"));
|
|
m_Parent->setStyleSheet(style);
|
|
m_Parent->m_Controller->FillVariationTreeWithCurrentXform();
|
|
}
|
|
else
|
|
{
|
|
label->setText(tr("Invalid Style Sheet"));
|
|
label->setStyleSheet(QStringLiteral("color: red"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load a stylesheet from disk.
|
|
/// Called when the user clicks the load button.
|
|
/// </summary>
|
|
void QssDialog::LoadButton_clicked()
|
|
{
|
|
string s;
|
|
auto f = OpenFile();
|
|
|
|
if (!f.isEmpty() && ReadFile(f.toStdString().c_str(), s) && !s.empty())
|
|
SetText(QString::fromStdString(s));
|
|
|
|
setWindowTitle("QSS Editor - " + f);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Save the stylesheet to disk.
|
|
/// Called when the user clicks the save button.
|
|
/// The user cannot save to default.qss, as it's a special placeholder.
|
|
/// When they exit the dialog by clicking OK, the currently displayed stylesheet
|
|
/// will be saved to default.qss.
|
|
/// </summary>
|
|
void QssDialog::SaveButton_clicked()
|
|
{
|
|
auto path = SaveFile();
|
|
|
|
if (path.toLower().endsWith("default.qss"))
|
|
{
|
|
QMessageBox::critical(this, "File save error", "Stylesheet cannot be saved to default.qss. Save it to a different file name, then exit the dialog by clicking OK which will set it as the default.");
|
|
return;
|
|
}
|
|
|
|
if (!path.isEmpty())
|
|
{
|
|
ofstream of(path.toStdString());
|
|
string s = Text().toStdString();
|
|
|
|
if (of.is_open())
|
|
of << s;
|
|
else
|
|
QMessageBox::critical(this, "File save error", "Failed to save " + path + ", style will not be set as default");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Save the stylesheet to the default.qss on disk.
|
|
/// This will be loaded the next time Fractorium runs.
|
|
/// Called when the user clicks ok.
|
|
/// </summary>
|
|
void QssDialog::SaveAsDefault()
|
|
{
|
|
auto path = m_Parent->m_SettingsPath + "/default.qss";
|
|
ofstream of(path.toStdString());
|
|
auto s = Text().toStdString();
|
|
|
|
if (of.is_open())
|
|
of << s;
|
|
else
|
|
QMessageBox::critical(this, "File save error", "Failed to save " + path + ", style will not be set as default");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fill the main text box with the most basic style.
|
|
/// Called when the Basic button is clicked.
|
|
/// </summary>
|
|
void QssDialog::BasicButton_clicked()
|
|
{
|
|
SetText(BaseStyle());
|
|
setWindowTitle("QSS Editor");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fill the main text box with a medium specificity style.
|
|
/// This will expose all control types in the application.
|
|
/// Called when the Medium button is clicked.
|
|
/// </summary>
|
|
void QssDialog::MediumButton_clicked()
|
|
{
|
|
QString str = BaseStyle();
|
|
auto names = GetClassNames(false);
|
|
|
|
for (auto& it : names)
|
|
str += it + QString("\n{\n\t\n}\n\n");
|
|
|
|
SetText(str);
|
|
setWindowTitle("QSS Editor");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fill the main text box with the most advanced style.
|
|
/// This will expose all control types in the application as well as their named instances.
|
|
/// Called when the Advanced button is clicked.
|
|
/// </summary>
|
|
void QssDialog::AdvancedButton_clicked()
|
|
{
|
|
QString str = BaseStyle();
|
|
auto names = GetClassNames(true);
|
|
|
|
for (auto& it : names)
|
|
str += it + QString("\n{\n\t\n}\n\n");
|
|
|
|
SetText(str);
|
|
setWindowTitle("QSS Editor");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Insert a CSS property.
|
|
/// This is called whenever the user inserts a value via the menus.
|
|
/// </summary>
|
|
/// <param name="name">The name of the property to insert</param>
|
|
/// <param name="value">The value of the property to insert</param>
|
|
void QssDialog::InsertCssProperty(const QString& name, const QString& value)
|
|
{
|
|
auto editor = ui->QssEdit;
|
|
auto cursor = editor->textCursor();
|
|
|
|
if (!name.isEmpty())
|
|
{
|
|
cursor.beginEditBlock();
|
|
cursor.removeSelectedText();
|
|
cursor.movePosition(QTextCursor::EndOfLine);
|
|
//Simple check to see if we're in a selector scope.
|
|
const QTextDocument* doc = editor->document();
|
|
const QTextCursor closing = doc->find(QStringLiteral("}"), cursor, QTextDocument::FindBackward);
|
|
const QTextCursor opening = doc->find(QStringLiteral("{"), cursor, QTextDocument::FindBackward);
|
|
const bool inSelector = !opening.isNull() && (closing.isNull() ||
|
|
closing.position() < opening.position());
|
|
QString insertion;
|
|
|
|
//Reasonable attempt at positioning things correctly. This can and often is wrong, but is sufficient for our purposes.
|
|
if (editor->textCursor().block().length() != 1 && !editor->textCursor().block().text().isEmpty())
|
|
insertion += QLatin1Char('\n');
|
|
|
|
if (inSelector && editor->textCursor().block().text() != "\t")
|
|
insertion += QLatin1Char('\t');
|
|
|
|
insertion += name;
|
|
insertion += QStringLiteral(": ");
|
|
insertion += value;
|
|
insertion += QLatin1Char(';');
|
|
cursor.insertText(insertion);
|
|
cursor.endEditBlock();
|
|
}
|
|
else
|
|
{
|
|
cursor.insertText(value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initial file dialog creation.
|
|
/// This will perform lazy instantiation since it takes a long time.
|
|
/// </summary>
|
|
void QssDialog::SetupFileDialog()
|
|
{
|
|
#ifndef __APPLE__
|
|
|
|
if (!m_FileDialog)
|
|
{
|
|
m_FileDialog = new QFileDialog(this);
|
|
m_FileDialog->setViewMode(QFileDialog::List);
|
|
//m_FileDialog->setDirectory(m_Parent->m_SettingsPath);
|
|
m_FileDialog->setDirectory("./");
|
|
m_FileDialog->setOption(QFileDialog::DontUseNativeDialog, true);
|
|
m_FileDialog->setSidebarUrls(dynamic_cast<Fractorium*>(parent())->Urls());
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Present a file open dialog and retun the file selected.
|
|
/// </summary>
|
|
/// <returns>The file selected if any, else empty string.</returns>
|
|
QString QssDialog::OpenFile()
|
|
{
|
|
#ifndef __APPLE__
|
|
QStringList filenames;
|
|
SetupFileDialog();
|
|
m_FileDialog->setFileMode(QFileDialog::ExistingFile);
|
|
m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen);
|
|
m_FileDialog->setNameFilter("Qss (*.qss)");
|
|
m_FileDialog->setWindowTitle("Open Stylesheet");
|
|
m_FileDialog->selectNameFilter("*.qss");
|
|
|
|
if (m_FileDialog->exec() == QDialog::Accepted)
|
|
filenames = m_FileDialog->selectedFiles();
|
|
|
|
return !filenames.empty() ? filenames[0] : "";
|
|
#else
|
|
auto filename = QFileDialog::getOpenFileName(this, tr("Open Stylesheet"), m_Parent->m_SettingsPath, tr("Qss (*.qss)"));
|
|
return filename.size() > 0 ? filename : "";
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Present a file save dialog and retun the file selected.
|
|
/// </summary>
|
|
/// <returns>The file selected for saving if any, else empty string.</returns>
|
|
QString QssDialog::SaveFile()
|
|
{
|
|
#ifndef __APPLE__
|
|
QStringList filenames;
|
|
SetupFileDialog();
|
|
m_FileDialog->setFileMode(QFileDialog::AnyFile);
|
|
m_FileDialog->setAcceptMode(QFileDialog::AcceptSave);
|
|
m_FileDialog->setNameFilter("Qss (*.qss)");
|
|
m_FileDialog->setWindowTitle("Save Stylesheet");
|
|
m_FileDialog->selectNameFilter("*.qss");
|
|
|
|
if (m_FileDialog->exec() == QDialog::Accepted)
|
|
filenames = m_FileDialog->selectedFiles();
|
|
|
|
return !filenames.empty() ? filenames[0] : "";
|
|
#else
|
|
auto filename = QFileDialog::getSaveFileName(this, tr("Save Stylesheet"), m_Parent->m_SettingsPath, tr("Qss (*.qss)"));
|
|
return filename.size() > 0 ? filename : "";
|
|
#endif
|
|
}
|