This commit is contained in:
mfeemster 2015-05-15 18:45:15 -07:00
parent e005b4c20e
commit 2561708ae0
14 changed files with 463 additions and 152 deletions

View File

@ -231,6 +231,10 @@ public:
if (ember.UseFinalXform()) if (ember.UseFinalXform())
os << ToString(*ember.NonConstFinalXform(), ember.XformCount(), true, false);//Final, don't do motion. os << ToString(*ember.NonConstFinalXform(), ember.XformCount(), true, false);//Final, don't do motion.
//Note that only embedded palettes are saved. The old style of specifying a palette index to look up in a default palette file
//is no longer supported, as it makes no sense when using multiple palette files. The only way it could work is if the index was
//always meant to refer to the default file, or if the filename was embedded as well. It's easier, more straightforward and
//less error prone to just embed the palette.
if (hexPalette) if (hexPalette)
{ {
os << " <palette count=\"256\" format=\"RGB\">\n"; os << " <palette count=\"256\" format=\"RGB\">\n";

View File

@ -145,6 +145,7 @@ public:
{ {
m_Index = palette.m_Index; m_Index = palette.m_Index;
m_Name = palette.m_Name; m_Name = palette.m_Name;
m_Filename = palette.m_Filename;
CopyVec(m_Entries, palette.m_Entries); CopyVec(m_Entries, palette.m_Entries);
return *this; return *this;
@ -204,6 +205,7 @@ public:
{ {
palette.m_Index = m_Index; palette.m_Index = m_Index;
palette.m_Name = m_Name; palette.m_Name = m_Name;
palette.m_Filename = m_Filename;
palette.m_Entries.resize(Size()); palette.m_Entries.resize(Size());
for (uint i = 0; i < Size(); i++) for (uint i = 0; i < Size(); i++)
@ -263,6 +265,7 @@ public:
} }
palette.m_Name = m_Name; palette.m_Name = m_Name;
palette.m_Filename = m_Filename;
} }
else else
{ {
@ -340,6 +343,7 @@ public:
{ {
palette.m_Index = m_Index; palette.m_Index = m_Index;
palette.m_Name = m_Name; palette.m_Name = m_Name;
palette.m_Filename = m_Filename;
if (palette.Size() != Size()) if (palette.Size() != Size())
palette.m_Entries.resize(Size()); palette.m_Entries.resize(Size());
@ -574,6 +578,7 @@ public:
int m_Index;//Index in the xml palette file of this palette, use -1 for random. int m_Index;//Index in the xml palette file of this palette, use -1 for random.
string m_Name;//Name of this palette. string m_Name;//Name of this palette.
string m_Filename;//Name of the parent file this palette came from, can be empty.
vector<v4T> m_Entries;//Storage for the color values. vector<v4T> m_Entries;//Storage for the color values.
}; };
} }

View File

@ -55,7 +55,7 @@ public:
palettes.clear(); palettes.clear();
palettes.reserve(buf.size() / 2048);//Roughly what it takes per palette. palettes.reserve(buf.size() / 2048);//Roughly what it takes per palette.
ParsePalettes(rootNode, palettes); ParsePalettes(rootNode, filename, palettes);
xmlFreeDoc(doc); xmlFreeDoc(doc);
added = true; added = true;
} }
@ -81,6 +81,7 @@ public:
auto p = m_Palettes.begin(); auto p = m_Palettes.begin();
int i = 0, paletteFileIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Rand() % Size(); int i = 0, paletteFileIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Rand() % Size();
//Move p forward i elements.
while (i < paletteFileIndex && p != m_Palettes.end()) while (i < paletteFileIndex && p != m_Palettes.end())
{ {
++i; ++i;
@ -218,13 +219,14 @@ public:
private: private:
/// <summary> /// <summary>
/// Parses an Xml node for all palettes present and store in the passed in palette vector. /// Parses an Xml node for all palettes present and stores them in the passed in palette vector.
/// Note that although the Xml color values are expected to be 0-255, they are converted and /// Note that although the Xml color values are expected to be 0-255, they are converted and
/// stored as normalized colors, with values from 0-1. /// stored as normalized colors, with values from 0-1.
/// </summary> /// </summary>
/// <param name="node">The parent note of all palettes in the Xml file.</param> /// <param name="node">The parent note of all palettes in the Xml file.</param>
/// <param name="filename">The name of the Xml file.</param>
/// <param name="palettes">The vector to store the paresed palettes associated with this file in.</param> /// <param name="palettes">The vector to store the paresed palettes associated with this file in.</param>
void ParsePalettes(xmlNode* node, vector<Palette<T>>& palettes) void ParsePalettes(xmlNode* node, const string& filename, vector<Palette<T>>& palettes)
{ {
bool hexError = false; bool hexError = false;
char* val; char* val;
@ -287,12 +289,13 @@ private:
if (!hexError) if (!hexError)
{ {
palette.m_Filename = filename;
palettes.push_back(palette); palettes.push_back(palette);
} }
} }
else else
{ {
ParsePalettes(node->children, palettes); ParsePalettes(node->children, filename, palettes);
} }
node = node->next; node = node->next;

View File

@ -1,6 +1,8 @@
#include "FractoriumPch.h" #include "FractoriumPch.h"
#include "DoubleSpinBox.h" #include "DoubleSpinBox.h"
QTimer DoubleSpinBox::m_Timer;
/// <summary> /// <summary>
/// Constructor that passes parent to the base and sets up height and step. /// Constructor that passes parent to the base and sets up height and step.
/// Specific focus policy is used to allow the user to hover over the control /// Specific focus policy is used to allow the user to hover over the control
@ -21,14 +23,15 @@ DoubleSpinBox::DoubleSpinBox(QWidget* p, int h, double step)
m_SmallStep = step / 10.0; m_SmallStep = step / 10.0;
setSingleStep(step); setSingleStep(step);
setFrame(false); setFrame(false);
//setAttribute(Qt::WA_PaintOnScreen);
setButtonSymbols(QAbstractSpinBox::NoButtons); setButtonSymbols(QAbstractSpinBox::NoButtons);
setFocusPolicy(Qt::StrongFocus); setFocusPolicy(Qt::StrongFocus);
setMinimumHeight(h);//setGeometry() has no effect, so must set both of these instead. setMinimumHeight(h);//setGeometry() has no effect, so must set both of these instead.
setMaximumHeight(h); setMaximumHeight(h);
setContextMenuPolicy(Qt::PreventContextMenu);
lineEdit()->installEventFilter(this); lineEdit()->installEventFilter(this);
lineEdit()->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); lineEdit()->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
connect(this, SIGNAL(valueChanged(double)), this, SLOT(onSpinBoxValueChanged(double)), Qt::QueuedConnection); connect(this, SIGNAL(valueChanged(double)), this, SLOT(OnSpinBoxValueChanged(double)), Qt::QueuedConnection);
} }
/// <summary> /// <summary>
@ -118,11 +121,44 @@ QLineEdit* DoubleSpinBox::lineEdit()
/// <summary> /// <summary>
/// Another workaround for the persistent text selection bug in Qt. /// Another workaround for the persistent text selection bug in Qt.
/// </summary> /// </summary>
void DoubleSpinBox::onSpinBoxValueChanged(double d) void DoubleSpinBox::OnSpinBoxValueChanged(double d)
{ {
lineEdit()->deselect();//Gets rid of nasty "feature" that always has text selected. lineEdit()->deselect();//Gets rid of nasty "feature" that always has text selected.
} }
/// <summary>
/// Called while the timer is activated due to the right mouse button being held down.
/// </summary>
void DoubleSpinBox::OnTimeout()
{
int xdistance = m_MouseMovePoint.x() - m_MouseDownPoint.x();
int ydistance = m_MouseMovePoint.y() - m_MouseDownPoint.y();
int distance = abs(xdistance) > abs(ydistance) ? xdistance : ydistance;
double scale, val;
double d = value();
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
//bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
double amount = (m_SmallStep + m_Step) * 0.5;
if (shift)
{
//qDebug() << "Shift pressed";
scale = 0.0001;
}
/*else if (ctrl)
{
qDebug() << "Control pressed";
scale = 0.01;
}*/
else
scale = 0.001;
val = d + (distance * amount * scale);
setValue(val);
//qDebug() << "Timer on, orig val: " << d << ", new val: " << val << ", distance " << distance;
}
/// <summary> /// <summary>
/// Event filter for taking special action on double click events. /// Event filter for taking special action on double click events.
/// </summary> /// </summary>
@ -131,8 +167,16 @@ void DoubleSpinBox::onSpinBoxValueChanged(double d)
/// <returns>false</returns> /// <returns>false</returns>
bool DoubleSpinBox::eventFilter(QObject* o, QEvent* e) bool DoubleSpinBox::eventFilter(QObject* o, QEvent* e)
{ {
if (e->type() == QMouseEvent::MouseButtonPress && isEnabled()) QMouseEvent* me = dynamic_cast<QMouseEvent*>(e);
if (isEnabled() &&
me &&
me->type() == QMouseEvent::MouseButtonPress &&
me->button() == Qt::RightButton)
{ {
m_MouseDownPoint = m_MouseMovePoint = me->pos();
StartTimer();
//qDebug() << "Right mouse down";
// QPoint pt; // QPoint pt;
// //
// if (QMouseEvent* me = (QMouseEvent*)e) // if (QMouseEvent* me = (QMouseEvent*)e)
@ -154,6 +198,23 @@ bool DoubleSpinBox::eventFilter(QObject* o, QEvent* e)
// return true; // return true;
// } // }
} }
else if (isEnabled() &&
me &&
me->type() == QMouseEvent::MouseButtonRelease &&
me->button() == Qt::RightButton)
{
StopTimer();
m_MouseDownPoint = m_MouseMovePoint = me->pos();
//qDebug() << "Right mouse up";
}
else if (isEnabled() &&
me &&
me->type() == QMouseEvent::MouseMove &&
QGuiApplication::mouseButtons() & Qt::RightButton)
{
m_MouseMovePoint = me->pos();
qDebug() << "Mouse move while right down. Pt = " << me->pos() << ", global: " << mapToGlobal(me->pos());
}
else if (m_DoubleClick && e->type() == QMouseEvent::MouseButtonDblClick && isEnabled()) else if (m_DoubleClick && e->type() == QMouseEvent::MouseButtonDblClick && isEnabled())
{ {
if (IsNearZero(value())) if (IsNearZero(value()))
@ -189,6 +250,7 @@ bool DoubleSpinBox::eventFilter(QObject* o, QEvent* e)
void DoubleSpinBox::focusInEvent(QFocusEvent* e) void DoubleSpinBox::focusInEvent(QFocusEvent* e)
{ {
//lineEdit()->setReadOnly(false); //lineEdit()->setReadOnly(false);
StopTimer();
QDoubleSpinBox::focusInEvent(e); QDoubleSpinBox::focusInEvent(e);
} }
@ -203,7 +265,8 @@ void DoubleSpinBox::focusOutEvent(QFocusEvent* e)
{ {
//lineEdit()->deselect();//Clear selection when leaving. //lineEdit()->deselect();//Clear selection when leaving.
//lineEdit()->setReadOnly(true);//Clever hack to clear the cursor when leaving. //lineEdit()->setReadOnly(true);//Clever hack to clear the cursor when leaving.
QDoubleSpinBox::focusOutEvent(e); StopTimer();
QDoubleSpinBox::focusOutEvent(e);
} }
/// <summary> /// <summary>
@ -215,6 +278,7 @@ void DoubleSpinBox::enterEvent(QEvent* e)
{ {
//m_Select = true; //m_Select = true;
//setFocus(); //setFocus();
StopTimer();
QDoubleSpinBox::enterEvent(e); QDoubleSpinBox::enterEvent(e);
} }
@ -226,6 +290,26 @@ void DoubleSpinBox::enterEvent(QEvent* e)
void DoubleSpinBox::leaveEvent(QEvent* e) void DoubleSpinBox::leaveEvent(QEvent* e)
{ {
//m_Select = false; //m_Select = false;
//clearFocus(); //clearFocus();.
StopTimer();
QDoubleSpinBox::leaveEvent(e); QDoubleSpinBox::leaveEvent(e);
} }
/// <summary>
/// Start the timer in response to the right mouse button being pressed.
/// </summary>
void DoubleSpinBox::StartTimer()
{
m_Timer.stop();
connect(&m_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout()));
m_Timer.start(300);
}
/// <summary>
/// Stop the timer in response to the left mouse button being pressed.
/// </summary>
void DoubleSpinBox::StopTimer()
{
m_Timer.stop();
disconnect(&m_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout()));
}

View File

@ -29,7 +29,8 @@ public:
QLineEdit* lineEdit(); QLineEdit* lineEdit();
public slots: public slots:
void onSpinBoxValueChanged(double d); void OnSpinBoxValueChanged(double d);
void OnTimeout();
protected: protected:
virtual bool eventFilter(QObject* o, QEvent* e) override; virtual bool eventFilter(QObject* o, QEvent* e) override;
@ -39,12 +40,18 @@ protected:
virtual void leaveEvent(QEvent* e); virtual void leaveEvent(QEvent* e);
private: private:
void StartTimer();
void StopTimer();
bool m_Select; bool m_Select;
bool m_DoubleClick; bool m_DoubleClick;
double m_DoubleClickNonZero; double m_DoubleClickNonZero;
double m_DoubleClickZero; double m_DoubleClickZero;
double m_Step; double m_Step;
double m_SmallStep; double m_SmallStep;
QPoint m_MouseDownPoint;
QPoint m_MouseMovePoint;
static QTimer m_Timer;
}; };
/// <summary> /// <summary>

View File

@ -723,6 +723,78 @@ void Fractorium::SetTabOrders()
w = SetTabOrder(this, w, ui.InfoRenderingTextEdit); w = SetTabOrder(this, w, ui.InfoRenderingTextEdit);
} }
/// <summary>
/// Toggle all table spinner values in one row.
/// The logic is:
/// If any cell in the row is non zero, set all cells to zero, else 1.
/// If shift is held down, reverse the logic.
/// Resets the rendering process.
/// </summary>
/// <param name="logicalIndex">The index of the row that was double clicked</param>
void Fractorium::ToggleTableRow(TableWidget* table, int logicalIndex)
{
bool allZero = true;
int cols = table->columnCount();
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
for (int i = 0; i < cols; i++)
{
if (auto* spinBox = dynamic_cast<DoubleSpinBox*>(table->cellWidget(logicalIndex, i)))
{
if (!IsNearZero(spinBox->value()))
{
allZero = false;
break;
}
}
}
if (shift)
allZero = !allZero;
double val = allZero ? 1.0 : 0.0;
for (int i = 0; i < cols; i++)
if (auto* spinBox = dynamic_cast<DoubleSpinBox*>(table->cellWidget(logicalIndex, i)))
spinBox->setValue(val);
}
/// <summary>
/// Toggle all table spinner values in one column.
/// The logic is:
/// If any cell in the column is non zero, set all cells to zero, else 1.
/// If shift is held down, reverse the logic.
/// Resets the rendering process.
/// </summary>
/// <param name="logicalIndex">The index of the column that was double clicked</param>
void Fractorium::ToggleTableCol(TableWidget* table, int logicalIndex)
{
bool allZero = true;
int rows = table->rowCount();
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
for (int i = 0; i < rows; i++)
{
if (auto* spinBox = dynamic_cast<DoubleSpinBox*>(table->cellWidget(i, logicalIndex)))
{
if (!IsNearZero(spinBox->value()))
{
allZero = false;
break;
}
}
}
if (shift)
allZero = !allZero;
double val = allZero ? 1.0 : 0.0;
for (int i = 0; i < rows; i++)
if (auto* spinBox = dynamic_cast<DoubleSpinBox*>(table->cellWidget(i, logicalIndex)))
spinBox->setValue(val);
}
/// <summary> /// <summary>
/// This is no longer needed and was used to compensate for a different bug /// This is no longer needed and was used to compensate for a different bug
/// however the code is interesting, so keep it around for possible future use. /// however the code is interesting, so keep it around for possible future use.

View File

@ -194,6 +194,11 @@ public slots:
void OnXformNameChanged(int row, int col); void OnXformNameChanged(int row, int col);
//Xforms Affine. //Xforms Affine.
void OnPreAffineRowDoubleClicked(int logicalIndex);
void OnPreAffineColDoubleClicked(int logicalIndex);
void OnPostAffineRowDoubleClicked(int logicalIndex);
void OnPostAffineColDoubleClicked(int logicalIndex);
void OnX1Changed(double d); void OnX1Changed(double d);
void OnX2Changed(double d); void OnX2Changed(double d);
void OnY1Changed(double d); void OnY1Changed(double d);
@ -299,6 +304,9 @@ private:
void InitLibraryUI(); void InitLibraryUI();
void SetTabOrders(); void SetTabOrders();
void ToggleTableRow(TableWidget* table, int logicalIndex);
void ToggleTableCol(TableWidget* table, int logicalIndex);
//Embers. //Embers.
bool HaveFinal(); bool HaveFinal();
@ -324,6 +332,7 @@ private:
//Palette. //Palette.
void ResetPaletteControls(); void ResetPaletteControls();
void SetPaletteFileComboIndex(const string& filename);
//Info. //Info.
void UpdateHistogramBounds(); void UpdateHistogramBounds();

View File

@ -365,6 +365,9 @@
<attribute name="title"> <attribute name="title">
<string>Library</string> <string>Library</string>
</attribute> </attribute>
<attribute name="toolTip">
<string>All flames in the currently opened file or randomly generated flock</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_10"> <layout class="QGridLayout" name="gridLayout_10">
<property name="leftMargin"> <property name="leftMargin">
<number>5</number> <number>5</number>
@ -472,6 +475,9 @@
<attribute name="title"> <attribute name="title">
<string>Flame</string> <string>Flame</string>
</attribute> </attribute>
<attribute name="toolTip">
<string>General flame parameters</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>0</number>
@ -1884,6 +1890,9 @@
<attribute name="title"> <attribute name="title">
<string>Xforms</string> <string>Xforms</string>
</attribute> </attribute>
<attribute name="toolTip">
<string>Xforms in the current flame</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_13"> <layout class="QGridLayout" name="gridLayout_13">
<property name="leftMargin"> <property name="leftMargin">
<number>5</number> <number>5</number>
@ -1962,11 +1971,14 @@ SpinBox
</sizepolicy> </sizepolicy>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Color palette index for the current xform, and curve adjustment.</string> <string/>
</property> </property>
<attribute name="title"> <attribute name="title">
<string>Color</string> <string>Color</string>
</attribute> </attribute>
<attribute name="toolTip">
<string>Color palette index for the current xform, and curve adjustment</string>
</attribute>
<layout class="QFormLayout" name="formLayout_3"> <layout class="QFormLayout" name="formLayout_3">
<property name="fieldGrowthPolicy"> <property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum> <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
@ -2583,7 +2595,7 @@ SpinBox
</sizepolicy> </sizepolicy>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Affine transforms for the current xform.</string> <string/>
</property> </property>
<property name="autoFillBackground"> <property name="autoFillBackground">
<bool>true</bool> <bool>true</bool>
@ -2591,6 +2603,9 @@ SpinBox
<attribute name="title"> <attribute name="title">
<string>Affine</string> <string>Affine</string>
</attribute> </attribute>
<attribute name="toolTip">
<string>Affine transforms for the current xform</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_12"> <layout class="QVBoxLayout" name="verticalLayout_12">
<property name="spacing"> <property name="spacing">
<number>5</number> <number>5</number>
@ -3944,11 +3959,14 @@ SpinBox
</widget> </widget>
<widget class="QWidget" name="XformVariationsTab"> <widget class="QWidget" name="XformVariationsTab">
<property name="toolTip"> <property name="toolTip">
<string>Full list of available variations and their weights for the currently selected xform.</string> <string/>
</property> </property>
<attribute name="title"> <attribute name="title">
<string>Variations</string> <string>Variations</string>
</attribute> </attribute>
<attribute name="toolTip">
<string>Full list of available variations and their weights for the currently selected xform</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_15"> <layout class="QVBoxLayout" name="verticalLayout_15">
<property name="leftMargin"> <property name="leftMargin">
<number>6</number> <number>6</number>
@ -4175,7 +4193,7 @@ SpinBox
</widget> </widget>
<widget class="QWidget" name="XformSelectTab"> <widget class="QWidget" name="XformSelectTab">
<property name="toolTip"> <property name="toolTip">
<string>Select multiple xforms to apply operations to.</string> <string/>
</property> </property>
<property name="autoFillBackground"> <property name="autoFillBackground">
<bool>true</bool> <bool>true</bool>
@ -4183,6 +4201,9 @@ SpinBox
<attribute name="title"> <attribute name="title">
<string>Select</string> <string>Select</string>
</attribute> </attribute>
<attribute name="toolTip">
<string>Select multiple xforms to apply operations to</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing"> <property name="spacing">
<number>3</number> <number>3</number>
@ -4673,6 +4694,9 @@ SpinBox
<attribute name="title"> <attribute name="title">
<string>Xaos</string> <string>Xaos</string>
</attribute> </attribute>
<attribute name="toolTip">
<string>Xaos weights between xforms</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing"> <property name="spacing">
<number>4</number> <number>4</number>
@ -4886,6 +4910,9 @@ SpinBox
<attribute name="title"> <attribute name="title">
<string>Palette</string> <string>Palette</string>
</attribute> </attribute>
<attribute name="toolTip">
<string>List of available palette files and their palettes</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_16" rowstretch="0,0,0,0,0" columnstretch="0"> <layout class="QGridLayout" name="gridLayout_16" rowstretch="0,0,0,0,0" columnstretch="0">
<property name="sizeConstraint"> <property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum> <enum>QLayout::SetDefaultConstraint</enum>
@ -5272,6 +5299,9 @@ SpinBox
<attribute name="title"> <attribute name="title">
<string>Info</string> <string>Info</string>
</attribute> </attribute>
<attribute name="toolTip">
<string>Diagnostic information of engineering interest</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_17" stretch="0"> <layout class="QVBoxLayout" name="verticalLayout_17" stretch="0">
<property name="spacing"> <property name="spacing">
<number>6</number> <number>6</number>

View File

@ -83,55 +83,56 @@ int FractoriumEmberController<T>::InitPaletteList(const string& s)
template <typename T> template <typename T>
bool FractoriumEmberController<T>::FillPaletteTable(const string& s) bool FractoriumEmberController<T>::FillPaletteTable(const string& s)
{ {
QTableWidget* paletteTable = m_Fractorium->ui.PaletteListTable; if (!s.empty())//This occasionally seems to get called with an empty string for reasons unknown.
QTableWidget* palettePreviewTable = m_Fractorium->ui.PalettePreviewTable;
m_CurrentPaletteFilePath = m_Fractorium->ui.PaletteFilenameCombo->property("path").toString().toStdString() + s;
size_t paletteSize = m_PaletteList.Size(m_CurrentPaletteFilePath);
if (paletteSize)
{ {
paletteTable->clear(); QTableWidget* paletteTable = m_Fractorium->ui.PaletteListTable;
paletteTable->blockSignals(true); QTableWidget* palettePreviewTable = m_Fractorium->ui.PalettePreviewTable;
paletteTable->setRowCount(paletteSize);
//Headers get removed when clearing, so must re-create here.
QTableWidgetItem* nameHeader = new QTableWidgetItem("Name");
QTableWidgetItem* paletteHeader = new QTableWidgetItem("Palette");
nameHeader->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); m_CurrentPaletteFilePath = m_Fractorium->ui.PaletteFilenameCombo->property("path").toString().toStdString() + s;
paletteHeader->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
paletteTable->setHorizontalHeaderItem(0, nameHeader); if (size_t paletteSize = m_PaletteList.Size(m_CurrentPaletteFilePath))
paletteTable->setHorizontalHeaderItem(1, paletteHeader);
//Palette list table.
for (size_t i = 0; i < paletteSize; i++)
{ {
Palette<T>* p = m_PaletteList.GetPalette(m_CurrentPaletteFilePath, i); paletteTable->clear();
vector<byte> v = p->MakeRgbPaletteBlock(PALETTE_CELL_HEIGHT); paletteTable->blockSignals(true);
QTableWidgetItem* nameCol = new QTableWidgetItem(p->m_Name.c_str()); paletteTable->setRowCount(paletteSize);
nameCol->setToolTip(p->m_Name.c_str()); //Headers get removed when clearing, so must re-create here.
paletteTable->setItem(i, 0, nameCol); QTableWidgetItem* nameHeader = new QTableWidgetItem("Name");
QTableWidgetItem* paletteHeader = new QTableWidgetItem("Palette");
QImage image(v.data(), p->Size(), PALETTE_CELL_HEIGHT, QImage::Format_RGB888); nameHeader->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
QTableWidgetItem* paletteItem = new QTableWidgetItem(); paletteHeader->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
paletteItem->setData(Qt::DecorationRole, QPixmap::fromImage(image)); paletteTable->setHorizontalHeaderItem(0, nameHeader);
paletteTable->setItem(i, 1, paletteItem); paletteTable->setHorizontalHeaderItem(1, paletteHeader);
//Palette list table.
for (size_t i = 0; i < paletteSize; i++)
{
Palette<T>* p = m_PaletteList.GetPalette(m_CurrentPaletteFilePath, i);
vector<byte> v = p->MakeRgbPaletteBlock(PALETTE_CELL_HEIGHT);
QTableWidgetItem* nameCol = new QTableWidgetItem(p->m_Name.c_str());
nameCol->setToolTip(p->m_Name.c_str());
paletteTable->setItem(i, 0, nameCol);
QImage image(v.data(), p->Size(), PALETTE_CELL_HEIGHT, QImage::Format_RGB888);
QTableWidgetItem* paletteItem = new QTableWidgetItem();
paletteItem->setData(Qt::DecorationRole, QPixmap::fromImage(image));
paletteTable->setItem(i, 1, paletteItem);
}
paletteTable->blockSignals(false);
return true;
} }
else
{
vector<string> errors = m_PaletteList.ErrorReport();
paletteTable->blockSignals(false); m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoFileOpeningTextEdit);
m_Fractorium->OnPaletteRandomSelectButtonClicked(true); m_Fractorium->ShowCritical("Palette Read Error", "Could not load palette file, all images will be black. See info tab for details.");
return true; }
}
else
{
vector<string> errors = m_PaletteList.ErrorReport();
m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoFileOpeningTextEdit);
m_Fractorium->ShowCritical("Palette Read Error", "Could not load palette file, all images will be black. See info tab for details.");
} }
return false; return false;
@ -226,7 +227,6 @@ template <typename T>
void FractoriumEmberController<T>::PaletteCellClicked(int row, int col) void FractoriumEmberController<T>::PaletteCellClicked(int row, int col)
{ {
Palette<T>* palette = m_PaletteList.GetPalette(m_CurrentPaletteFilePath, row); Palette<T>* palette = m_PaletteList.GetPalette(m_CurrentPaletteFilePath, row);
QTableWidgetItem* nameItem = m_Fractorium->ui.PaletteListTable->item(row, 0);
if (palette) if (palette)
{ {
@ -266,7 +266,7 @@ void Fractorium::OnPaletteCellDoubleClicked(int row, int col)
/// Called when the Random Palette button is clicked. /// Called when the Random Palette button is clicked.
/// Resets the rendering process. /// Resets the rendering process.
/// </summary> /// </summary>
/// <param name="checked">True to clear the current adjustments, else leave current adjustments.</param> /// <param name="checked">True to clear the current adjustments, else leave current adjustments and apply them to the newly selected palette.</param>
void Fractorium::OnPaletteRandomSelectButtonClicked(bool checked) void Fractorium::OnPaletteRandomSelectButtonClicked(bool checked)
{ {
uint i = 0; uint i = 0;
@ -323,6 +323,18 @@ void Fractorium::ResetPaletteControls()
m_PaletteFrequencySpin->SetValueStealth(1); m_PaletteFrequencySpin->SetValueStealth(1);
} }
/// <summary>
/// Set the index of the palette file combo box.
/// This is for display purposes only so the user can see which file, if any,
/// the current palette came from.
/// </summary>
/// <param name="filename">The string to set the index to</param>
void Fractorium::SetPaletteFileComboIndex(const string& filename)
{
if (!filename.empty())
ui.PaletteFilenameCombo->setCurrentText(QFileInfo(QString::fromStdString(filename)).fileName());
}
template class FractoriumEmberController<float>; template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE #ifdef DO_DOUBLE

View File

@ -512,6 +512,7 @@ void FractoriumEmberController<T>::SetCenter(double x, double y)
/// <summary> /// <summary>
/// Fill the parameter tables and palette widgets with values from the current ember. /// Fill the parameter tables and palette widgets with values from the current ember.
/// This takes ~1-2ms.
/// </summary> /// </summary>
template <typename T> template <typename T>
void FractoriumEmberController<T>::FillParamTablesAndPalette() void FractoriumEmberController<T>::FillParamTablesAndPalette()
@ -551,26 +552,18 @@ void FractoriumEmberController<T>::FillParamTablesAndPalette()
m_Fractorium->m_AffineInterpTypeCombo->SetCurrentIndexStealth(m_Ember.m_AffineInterp); m_Fractorium->m_AffineInterpTypeCombo->SetCurrentIndexStealth(m_Ember.m_AffineInterp);
m_Fractorium->m_InterpTypeCombo->SetCurrentIndexStealth(m_Ember.m_Interp); m_Fractorium->m_InterpTypeCombo->SetCurrentIndexStealth(m_Ember.m_Interp);
//Xaos.
FillXaos();
//Palette. //Palette.
m_Fractorium->ResetPaletteControls(); m_Fractorium->ResetPaletteControls();
m_Fractorium->m_PaletteHueSpin->SetValueStealth(NormalizeDeg180<double>(m_Ember.m_Hue * 360.0));//Convert -0.5 to 0.5 range to -180 - 180. m_Fractorium->m_PaletteHueSpin->SetValueStealth(NormalizeDeg180<double>(m_Ember.m_Hue * 360.0));//Convert -0.5 to 0.5 range to -180 - 180.
//Use -1 as a placeholder to mean either generate a random palette from the list or //Use the ember's embedded palette, rather than one from the list, so assign it directly to the controls without applying adjustments.
//to just use the values "as-is" without looking them up in the list. //Normally, the temp palette is assigned whenever the user clicks on a palette cell. But since that is skipped here, must do it manually.
if (m_Ember.m_Palette.m_Index >= 0) m_TempPalette = m_Ember.m_Palette;
{ m_Fractorium->SetPaletteFileComboIndex(m_Ember.m_Palette.m_Filename);
m_Fractorium->OnPaletteCellClicked(Clamp<int>(m_Ember.m_Palette.m_Index, 0, m_Fractorium->ui.PaletteListTable->rowCount() - 1), 1); UpdateAdjustedPaletteGUI(m_Ember.m_Palette);//Setting the palette will trigger a full render.
}
else
{
//An ember with an embedded palette was loaded, rather than one from the list, so assign it directly to the controls without applying adjustments.
//Normally, temp palette is assigned whenever the user clicks on a palette cell. But since that is skipped here just make a copy of the ember's palette.
m_TempPalette = m_Ember.m_Palette;
UpdateAdjustedPaletteGUI(m_Ember.m_Palette);//Will clear name string since embedded palettes have no name. This will trigger a full render.
}
//Xaos.
FillXaos();
} }
/// <summary> /// <summary>

View File

@ -188,74 +188,22 @@ void Fractorium::OnRandomXaosButtonClicked(bool checked) { m_Controller->RandomX
/// <summary> /// <summary>
/// Toggle all xaos values in one row. /// Toggle all xaos values in one row.
/// The logic is:
/// If any cell in the row is non zero, set all cells to zero, else 1.
/// If shift is held down, reverse the logic.
/// Resets the rendering process. /// Resets the rendering process.
/// </summary> /// </summary>
/// <param name="logicalIndex">The index of the row that was double clicked</param> /// <param name="logicalIndex">The index of the row that was double clicked</param>
void Fractorium::OnXaosRowDoubleClicked(int logicalIndex) void Fractorium::OnXaosRowDoubleClicked(int logicalIndex)
{ {
bool allZero = true; ToggleTableRow(ui.XaosTable, logicalIndex);
int cols = ui.XaosTable->columnCount();
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
for (int i = 0; i < cols; i++)
{
if (auto* spinBox = dynamic_cast<DoubleSpinBox*>(ui.XaosTable->cellWidget(logicalIndex, i)))
{
if (!IsNearZero(spinBox->value()))
{
allZero = false;
break;
}
}
}
if (shift)
allZero = !allZero;
double val = allZero ? 1.0 : 0.0;
for (int i = 0; i < cols; i++)
if (auto* spinBox = dynamic_cast<DoubleSpinBox*>(ui.XaosTable->cellWidget(logicalIndex, i)))
spinBox->setValue(val);
} }
/// <summary> /// <summary>
/// Toggle all xaos values in one column. /// Toggle all xaos values in one column.
/// The logic is:
/// If any cell in the column is non zero, set all cells to zero, else 1.
/// If shift is held down, reverse the logic.
/// Resets the rendering process. /// Resets the rendering process.
/// </summary> /// </summary>
/// <param name="logicalIndex">The index of the column that was double clicked</param> /// <param name="logicalIndex">The index of the column that was double clicked</param>
void Fractorium::OnXaosColDoubleClicked(int logicalIndex) void Fractorium::OnXaosColDoubleClicked(int logicalIndex)
{ {
bool allZero = true; ToggleTableCol(ui.XaosTable, logicalIndex);
int rows = ui.XaosTable->rowCount();
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
for (int i = 0; i < rows; i++)
{
if (auto* spinBox = dynamic_cast<DoubleSpinBox*>(ui.XaosTable->cellWidget(i, logicalIndex)))
{
if (!IsNearZero(spinBox->value()))
{
allZero = false;
break;
}
}
}
if (shift)
allZero = !allZero;
double val = allZero ? 1.0 : 0.0;
for (int i = 0; i < rows; i++)
if (auto* spinBox = dynamic_cast<DoubleSpinBox*>(ui.XaosTable->cellWidget(i, logicalIndex)))
spinBox->setValue(val);
} }
template class FractoriumEmberController<float>; template class FractoriumEmberController<float>;

View File

@ -10,9 +10,16 @@ void Fractorium::InitXformsAffineUI()
double affineStep = 0.01, affineMin = std::numeric_limits<double>::lowest(), affineMax = std::numeric_limits<double>::max(); double affineStep = 0.01, affineMin = std::numeric_limits<double>::lowest(), affineMax = std::numeric_limits<double>::max();
QTableWidget* table = ui.PreAffineTable; QTableWidget* table = ui.PreAffineTable;
SetFixedTableHeader(table->horizontalHeader(), QHeaderView::Stretch);//The designer continually clobbers these values, so must manually set them here. table->verticalHeader()->setVisible(true);//The designer continually clobbers these values, so must manually set them here.
SetFixedTableHeader(table->verticalHeader()); table->horizontalHeader()->setVisible(true);
table->verticalHeader()->setSectionsClickable(true);
table->horizontalHeader()->setSectionsClickable(true);
table->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
connect(table->verticalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnPreAffineRowDoubleClicked(int)), Qt::QueuedConnection);
connect(table->horizontalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnPreAffineColDoubleClicked(int)), Qt::QueuedConnection);
//Pre affine spinners. //Pre affine spinners.
SetupAffineSpinner(table, this, 0, 0, m_PreX1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX1Changed(double))); SetupAffineSpinner(table, this, 0, 0, m_PreX1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX1Changed(double)));
SetupAffineSpinner(table, this, 0, 1, m_PreX2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX2Changed(double))); SetupAffineSpinner(table, this, 0, 1, m_PreX2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX2Changed(double)));
@ -22,9 +29,16 @@ void Fractorium::InitXformsAffineUI()
SetupAffineSpinner(table, this, 2, 1, m_PreO2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO2Changed(double))); SetupAffineSpinner(table, this, 2, 1, m_PreO2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO2Changed(double)));
table = ui.PostAffineTable; table = ui.PostAffineTable;
SetFixedTableHeader(table->horizontalHeader(), QHeaderView::Stretch);//The designer continually clobbers these values, so must manually set them here. table->verticalHeader()->setVisible(true);//The designer continually clobbers these values, so must manually set them here.
SetFixedTableHeader(table->verticalHeader()); table->horizontalHeader()->setVisible(true);
table->verticalHeader()->setSectionsClickable(true);
table->horizontalHeader()->setSectionsClickable(true);
table->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
connect(table->verticalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnPostAffineRowDoubleClicked(int)), Qt::QueuedConnection);
connect(table->horizontalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnPostAffineColDoubleClicked(int)), Qt::QueuedConnection);
//Post affine spinners. //Post affine spinners.
SetupAffineSpinner(table, this, 0, 0, m_PostX1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX1Changed(double))); SetupAffineSpinner(table, this, 0, 0, m_PostX1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX1Changed(double)));
SetupAffineSpinner(table, this, 0, 1, m_PostX2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX2Changed(double))); SetupAffineSpinner(table, this, 0, 1, m_PostX2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX2Changed(double)));
@ -159,6 +173,46 @@ void Fractorium::InitXformsAffineUI()
ui.PostAffineGroupBox->setChecked(false); ui.PostAffineGroupBox->setChecked(false);
} }
/// <summary>
/// Toggle all pre affine values in one row for the selected xforms.
/// Resets the rendering process.
/// </summary>
/// <param name="logicalIndex">The index of the row that was double clicked</param>
void Fractorium::OnPreAffineRowDoubleClicked(int logicalIndex)
{
ToggleTableRow(ui.PreAffineTable, logicalIndex);
}
/// <summary>
/// Toggle all pre affine values in one column for the selected xforms.
/// Resets the rendering process.
/// </summary>
/// <param name="logicalIndex">The index of the row that was double clicked</param>
void Fractorium::OnPreAffineColDoubleClicked(int logicalIndex)
{
ToggleTableCol(ui.PreAffineTable, logicalIndex);
}
/// <summary>
/// Toggle all post affine values in one row for the selected xforms.
/// Resets the rendering process.
/// </summary>
/// <param name="logicalIndex">The index of the row that was double clicked</param>
void Fractorium::OnPostAffineRowDoubleClicked(int logicalIndex)
{
ToggleTableRow(ui.PostAffineTable, logicalIndex);
}
/// <summary>
/// Toggle all post affine values in one column for the selected xforms.
/// Resets the rendering process.
/// </summary>
/// <param name="logicalIndex">The index of the row that was double clicked</param>
void Fractorium::OnPostAffineColDoubleClicked(int logicalIndex)
{
ToggleTableCol(ui.PostAffineTable, logicalIndex);
}
/// <summary> /// <summary>
/// Helper for setting the value of a single pre/post affine coefficient. /// Helper for setting the value of a single pre/post affine coefficient.
/// Resets the rendering process. /// Resets the rendering process.

View File

@ -1,6 +1,8 @@
#include "FractoriumPch.h" #include "FractoriumPch.h"
#include "SpinBox.h" #include "SpinBox.h"
QTimer SpinBox::m_Timer;
/// <summary> /// <summary>
/// Constructor that passes parent to the base and sets up height and step. /// Constructor that passes parent to the base and sets up height and step.
/// Specific focus policy is used to allow the user to hover over the control /// Specific focus policy is used to allow the user to hover over the control
@ -97,6 +99,39 @@ void SpinBox::onSpinBoxValueChanged(int i)
lineEdit()->deselect();//Gets rid of nasty "feature" that always has text selected. lineEdit()->deselect();//Gets rid of nasty "feature" that always has text selected.
} }
/// <summary>
/// Called while the timer is activated due to the right mouse button being held down.
/// </summary>
void SpinBox::OnTimeout()
{
int xdistance = m_MouseMovePoint.x() - m_MouseDownPoint.x();
int ydistance = m_MouseMovePoint.y() - m_MouseDownPoint.y();
int distance = abs(xdistance) > abs(ydistance) ? xdistance : ydistance;
double scale, val;
int d = value();
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
//bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
double amount = (m_SmallStep + m_Step) * 0.5;
if (shift)
{
//qDebug() << "Shift pressed";
scale = 0.001;
}
/*else if (ctrl)
{
qDebug() << "Control pressed";
scale = 0.01;
}*/
else
scale = 0.01;
val = d + (distance * amount * scale);
setValue(int(val));
//qDebug() << "Timer on, orig val: " << d << ", new val: " << val << ", distance " << distance;
}
/// <summary> /// <summary>
/// Event filter for taking special action on double click events. /// Event filter for taking special action on double click events.
/// </summary> /// </summary>
@ -105,28 +140,53 @@ void SpinBox::onSpinBoxValueChanged(int i)
/// <returns>false</returns> /// <returns>false</returns>
bool SpinBox::eventFilter(QObject* o, QEvent* e) bool SpinBox::eventFilter(QObject* o, QEvent* e)
{ {
if (e->type() == QMouseEvent::MouseButtonPress && isEnabled()) QMouseEvent* me = dynamic_cast<QMouseEvent*>(e);
if (isEnabled() &&
me &&
me->type() == QMouseEvent::MouseButtonPress &&
me->button() == Qt::RightButton)
{ {
//QPoint pt; m_MouseDownPoint = m_MouseMovePoint = me->pos();
StartTimer();
//qDebug() << "Right mouse down";
// QPoint pt;
// //
//if (QMouseEvent* me = (QMouseEvent*)e) // if (QMouseEvent* me = (QMouseEvent*)e)
// pt = me->localPos().toPoint(); // pt = me->localPos().toPoint();
// //
//int pos = lineEdit()->cursorPositionAt(pt); // int pos = lineEdit()->cursorPositionAt(pt);
// //
//if (lineEdit()->selectedText() != "") // if (lineEdit()->selectedText() != "")
//{ // {
// lineEdit()->deselect(); // lineEdit()->deselect();
// lineEdit()->setCursorPosition(pos); // lineEdit()->setCursorPosition(pos);
// return true; // return true;
//} // }
//else if (m_Select) // else if (m_Select)
//{ // {
// lineEdit()->setCursorPosition(pos); // lineEdit()->setCursorPosition(pos);
// selectAll(); // selectAll();
// m_Select = false; // m_Select = false;
// return true; // return true;
//} // }
}
else if (isEnabled() &&
me &&
me->type() == QMouseEvent::MouseButtonRelease &&
me->button() == Qt::RightButton)
{
StopTimer();
m_MouseDownPoint = m_MouseMovePoint = me->pos();
//qDebug() << "Right mouse up";
}
else if (isEnabled() &&
me &&
me->type() == QMouseEvent::MouseMove &&
QGuiApplication::mouseButtons() & Qt::RightButton)
{
m_MouseMovePoint = me->pos();
qDebug() << "Mouse move while right down. Pt = " << me->pos() << ", global: " << mapToGlobal(me->pos());
} }
else if (m_DoubleClick && e->type() == QMouseEvent::MouseButtonDblClick && isEnabled()) else if (m_DoubleClick && e->type() == QMouseEvent::MouseButtonDblClick && isEnabled())
{ {
@ -163,6 +223,7 @@ bool SpinBox::eventFilter(QObject* o, QEvent* e)
void SpinBox::focusInEvent(QFocusEvent* e) void SpinBox::focusInEvent(QFocusEvent* e)
{ {
//lineEdit()->setReadOnly(false); //lineEdit()->setReadOnly(false);
StopTimer();
QSpinBox::focusInEvent(e); QSpinBox::focusInEvent(e);
} }
@ -177,7 +238,8 @@ void SpinBox::focusOutEvent(QFocusEvent* e)
{ {
//lineEdit()->deselect();//Clear selection when leaving. //lineEdit()->deselect();//Clear selection when leaving.
//lineEdit()->setReadOnly(true);//Clever hack to clear the cursor when leaving. //lineEdit()->setReadOnly(true);//Clever hack to clear the cursor when leaving.
QSpinBox::focusOutEvent(e); StopTimer();
QSpinBox::focusOutEvent(e);
} }
/// <summary> /// <summary>
@ -189,6 +251,7 @@ void SpinBox::enterEvent(QEvent* e)
{ {
//m_Select = true; //m_Select = true;
//setFocus(); //setFocus();
StopTimer();
QSpinBox::enterEvent(e); QSpinBox::enterEvent(e);
} }
@ -201,5 +264,25 @@ void SpinBox::leaveEvent(QEvent* e)
{ {
//m_Select = false; //m_Select = false;
//clearFocus(); //clearFocus();
StopTimer();
QSpinBox::leaveEvent(e); QSpinBox::leaveEvent(e);
} }
/// <summary>
/// Start the timer in response to the right mouse button being pressed.
/// </summary>
void SpinBox::StartTimer()
{
m_Timer.stop();
connect(&m_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout()));
m_Timer.start(300);
}
/// <summary>
/// Stop the timer in response to the left mouse button being pressed.
/// </summary>
void SpinBox::StopTimer()
{
m_Timer.stop();
disconnect(&m_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout()));
}

View File

@ -27,6 +27,7 @@ public:
public slots: public slots:
void onSpinBoxValueChanged(int i); void onSpinBoxValueChanged(int i);
void OnTimeout();
protected: protected:
bool eventFilter(QObject* o, QEvent* e); bool eventFilter(QObject* o, QEvent* e);
@ -36,10 +37,16 @@ protected:
virtual void leaveEvent(QEvent* e); virtual void leaveEvent(QEvent* e);
private: private:
void StartTimer();
void StopTimer();
bool m_Select; bool m_Select;
bool m_DoubleClick; bool m_DoubleClick;
int m_DoubleClickNonZero; int m_DoubleClickNonZero;
int m_DoubleClickZero; int m_DoubleClickZero;
int m_Step; int m_Step;
int m_SmallStep; int m_SmallStep;
QPoint m_MouseDownPoint;
QPoint m_MouseMovePoint;
static QTimer m_Timer;
}; };