From 26c558a2f5faead07f90b7913b26471793572eec Mon Sep 17 00:00:00 2001 From: Person Date: Mon, 30 Jul 2018 21:39:41 -0700 Subject: [PATCH] --User changes -Give tabs a height of 4px in the qss files. Looks a little large on 4k screens, but just right on HD screens which are much more common. -Allow for styling of zero and non-zero variation tree nodes via qss. -Allow for toggling whether to interpolate between colors in the palette editor, or to do hard cuts between colors. -Allow for adjusting spinner values with the + = or up arrow keys to increase, and - _ or down arrow keys to decrease. -Allow for responding to global presses of + = and - _ to cycle up or down to specify which xform is set as the current one. -Allow for adding "layers" via xaos which will add a user-specified number of xforms, and set certain xaos values to 0 or 1. -Add a new menu item under the Edit menu to copy the OpenCL iteration kernel source to the clipboard. -Show text on the status bar which indicates that an OpenCL kernel compilation is taking place. -Show xform name on xform combo box when expanded. Adjust size to fit all names. -Draw post affine circles using dashed lines. -Prevent QSS dialog from styling its editor, which makes it easier to see text when creating styles which have custom colors for text boxes. --Bug fixes -Fix up some table layouts which seemed to have regressed/decayed over time for reasons unknown. -Using undo/redo would create a new flame in the library every time. -Solo was not being preserved when using undo/redo. --Code changes -Make the solo flag be a part of the flame data now. -Fix some tabification in the OpenCL code for EllipticVariation. -Fix tabification in the varState code for OpenCL. -Add an event called m_CompileBegun to RendererCL that is called right before an OpenCL compile is begun. --This required making RendererCLBase not a pure virtual base class. Member functions just return defaults. -Filter key presses on main window to only process the third one. This is due to Qt triggering three events for every key press. -The palette preview table was installing an event filter for seemingly no reason. Remove it. -Mark certain virtual functions as override in SpinBox and DoubleSpinBox. --- .gitignore | 1 + Data/dark_linux.qss | 22 +++-- Data/dark_mac.qss | 23 +++-- Data/dark_windows.qss | 22 +++-- Source/Ember/Ember.h | 4 + Source/Ember/Variations01.h | 22 ++--- Source/Ember/Variations05.h | 12 +-- Source/Ember/Variations06.h | 8 +- Source/EmberCL/RendererCL.cpp | 2 + Source/EmberCL/RendererCL.h | 7 +- Source/Fractorium/DoubleSpinBox.cpp | 41 +++++++++ Source/Fractorium/DoubleSpinBox.h | 9 +- Source/Fractorium/Fractorium.cpp | 80 ++++++++++++++-- Source/Fractorium/Fractorium.h | 17 ++-- Source/Fractorium/Fractorium.ui | 83 +++++++++++++++-- Source/Fractorium/FractoriumCommon.h | 55 ++++++++++- .../Fractorium/FractoriumEmberController.cpp | 2 - Source/Fractorium/FractoriumEmberController.h | 12 ++- Source/Fractorium/FractoriumMenus.cpp | 26 ++++-- Source/Fractorium/FractoriumPalette.cpp | 2 +- Source/Fractorium/FractoriumRender.cpp | 15 ++- Source/Fractorium/FractoriumXaos.cpp | 44 +++++++++ Source/Fractorium/FractoriumXforms.cpp | 91 ++++++++++++------- Source/Fractorium/FractoriumXformsColor.cpp | 18 ++-- .../Fractorium/FractoriumXformsVariations.cpp | 30 ++++-- Source/Fractorium/GLWidget.cpp | 35 ++++--- Source/Fractorium/GLWidget.h | 4 +- Source/Fractorium/PaletteEditor.ui | 40 +++++--- .../PaletteEditor/GradientColorsView.cpp | 77 ++++++++++++---- .../PaletteEditor/GradientColorsView.h | 3 + .../PaletteEditor/PaletteEditor.cpp | 16 +++- .../Fractorium/PaletteEditor/PaletteEditor.h | 1 + Source/Fractorium/QssDialog.cpp | 2 + Source/Fractorium/SpinBox.cpp | 46 +++++++++- Source/Fractorium/SpinBox.h | 11 ++- Source/Fractorium/TwoButtonComboWidget.h | 24 ++--- Source/Fractorium/VariationsDialog.cpp | 2 - 37 files changed, 708 insertions(+), 201 deletions(-) diff --git a/.gitignore b/.gitignore index 72e77d5..2a06ed7 100644 --- a/.gitignore +++ b/.gitignore @@ -254,3 +254,4 @@ Builds/include/GL /Builds/MSVC/VS2017/zlib.props *last.flame /Source/Fractorium/Fractorium - Copy.ui +/.vs/slnx.sqlite diff --git a/Data/dark_linux.qss b/Data/dark_linux.qss index 56dd6f0..f224796 100644 --- a/Data/dark_linux.qss +++ b/Data/dark_linux.qss @@ -2,12 +2,19 @@ This is needed to deal with the large tabs in the fusion theme which is the default on Linux, and optional on Windows. It's not needed for other themes. You should keep this at the top of whatever custom style you make to ensure the tabs aren't unusually large.*/ -QTabBar::tab { height: 3ex; } +QTabBar::tab { height: 4ex; } /*This is needed to give the labels on the status bar some padding.*/ QStatusBar QLabel { padding-left: 2px; padding-right: 2px; } /*Specific styles below this line*/ +Fractorium +{ + qproperty-VariationTreeColorNonZero:black; + qproperty-VariationTreeColorZero:lightgray; + qproperty-VariationTreeBgColorNonZero:darkgray; + qproperty-VariationTreeBgColorZero:rgb(53, 53, 53); +} QObject { @@ -307,14 +314,12 @@ QTreeView { border: 1px solid gray; background-color: rgb(53, 53, 53); - font: 9pt "MS Shell Dlg";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/ + font: 9pt "MS Shell Dlg 2";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/ } -/*Setting this gives a more consistent look, but removes the ability to gray variations that are included in the xform*/ +/*Setting this gives a more consistent look. Also, by omitting color and background color, it allows us to set it above with VariationTreeColorNonZero etc...*/ QTreeView::item { - background-color: rgb(53, 53, 53); - color: darkgray; outline: none; margin-right: 1px; } @@ -365,7 +370,7 @@ QTableView color: darkgray; selection-color: darkgray; selection-background-color: rgb(53, 53, 53); - font: 9pt "MS Shell Dlg";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/ + font: 9pt "MS Shell Dlg 2";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/ } QTableView QTableCornerButton::section:enabled @@ -630,3 +635,8 @@ QTableView#FinalRenderParamsTable QPushButton margin-bottom: 2px; padding: 0px; } + +QssDialog QssTextEdit#QssEdit +{ + background-color: #FFFCE1; +} \ No newline at end of file diff --git a/Data/dark_mac.qss b/Data/dark_mac.qss index 56dd6f0..97385cf 100644 --- a/Data/dark_mac.qss +++ b/Data/dark_mac.qss @@ -2,12 +2,19 @@ This is needed to deal with the large tabs in the fusion theme which is the default on Linux, and optional on Windows. It's not needed for other themes. You should keep this at the top of whatever custom style you make to ensure the tabs aren't unusually large.*/ -QTabBar::tab { height: 3ex; } +QTabBar::tab { height: 4ex; } /*This is needed to give the labels on the status bar some padding.*/ QStatusBar QLabel { padding-left: 2px; padding-right: 2px; } /*Specific styles below this line*/ +Fractorium +{ + qproperty-VariationTreeColorNonZero:black; + qproperty-VariationTreeColorZero:lightgray; + qproperty-VariationTreeBgColorNonZero:darkgray; + qproperty-VariationTreeBgColorZero:rgb(53, 53, 53); +} QObject { @@ -307,14 +314,13 @@ QTreeView { border: 1px solid gray; background-color: rgb(53, 53, 53); - font: 9pt "MS Shell Dlg";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/ + font: 9pt "MS Shell Dlg 2";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/ } -/*Setting this gives a more consistent look, but removes the ability to gray variations that are included in the xform*/ + +/*Setting this gives a more consistent look. Also, by omitting color and background color, it allows us to set it above with VariationTreeColorNonZero etc...*/ QTreeView::item { - background-color: rgb(53, 53, 53); - color: darkgray; outline: none; margin-right: 1px; } @@ -365,7 +371,7 @@ QTableView color: darkgray; selection-color: darkgray; selection-background-color: rgb(53, 53, 53); - font: 9pt "MS Shell Dlg";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/ + font: 9pt "MS Shell Dlg 2";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/ } QTableView QTableCornerButton::section:enabled @@ -630,3 +636,8 @@ QTableView#FinalRenderParamsTable QPushButton margin-bottom: 2px; padding: 0px; } + +QssDialog QssTextEdit#QssEdit +{ + background-color: #FFFCE1; +} \ No newline at end of file diff --git a/Data/dark_windows.qss b/Data/dark_windows.qss index 1a46ab8..c6b260b 100644 --- a/Data/dark_windows.qss +++ b/Data/dark_windows.qss @@ -2,12 +2,19 @@ This is needed to deal with the large tabs in the fusion theme which is the default on Linux, and optional on Windows. It's not needed for other themes. You should keep this at the top of whatever custom style you make to ensure the tabs aren't unusually large.*/ -QTabBar::tab { height: 3ex; } +QTabBar::tab { height: 4ex; } /*This is needed to give the labels on the status bar some padding.*/ QStatusBar QLabel { padding-left: 2px; padding-right: 2px; } /*Specific styles below this line*/ +Fractorium +{ + qproperty-VariationTreeColorNonZero:black; + qproperty-VariationTreeColorZero:lightgray; + qproperty-VariationTreeBgColorNonZero:darkgray; + qproperty-VariationTreeBgColorZero:rgb(53, 53, 53); +} QObject { @@ -307,14 +314,12 @@ QTreeView { border: 1px solid gray; background-color: rgb(53, 53, 53); - font: 8pt "MS Shell Dlg";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/ + font: 8pt "MS Shell Dlg 2";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/ } -/*Setting this gives a more consistent look, but removes the ability to gray variations that are included in the xform*/ +/*Setting this gives a more consistent look. Also, by omitting color and background color, it allows us to set it above with VariationTreeColorNonZero etc...*/ QTreeView::item { - background-color: rgb(53, 53, 53); - color: darkgray; outline: none; margin-right: 1px; } @@ -365,7 +370,7 @@ QTableView color: darkgray; selection-color: darkgray; selection-background-color: rgb(53, 53, 53); - font: 8pt "MS Shell Dlg";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/ + font: 8pt "MS Shell Dlg 2";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/ } QTableView QTableCornerButton::section:enabled @@ -630,3 +635,8 @@ QTableView#FinalRenderParamsTable QPushButton margin-bottom: 2px; padding: 0px; } + +QssDialog QssTextEdit#QssEdit +{ + background-color: #FFFCE1; +} \ No newline at end of file diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index c2b010a..108e917 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -178,6 +178,7 @@ public: m_Edits = xmlCopyDoc(ember.m_Edits, 1); CopyCont(m_EmberMotionElements, ember.m_EmberMotionElements); + m_Solo = ember.m_Solo; return *this; } @@ -1723,6 +1724,9 @@ public: //The list of motion elements for the top-level flame params vector> m_EmberMotionElements; + //Index of xform to have non-zero opacity, while all others have zero. This is an interactive rendering parameter and is not saved to Xml. -1 means solo is not used. + intmax_t m_Solo = -1; + private: /// /// The type of scaling used when resizing. diff --git a/Source/Ember/Variations01.h b/Source/Ember/Variations01.h index e4cf94b..c5d1ad6 100644 --- a/Source/Ember/Variations01.h +++ b/Source/Ember/Variations01.h @@ -4009,18 +4009,18 @@ public: string weight = WeightDefineString(); string weightDivPiDiv2 = "parVars[" + ToUpper(m_Params[i++].Name()) + index; ss << "\t{\n" - << "double x2 = 2.0 * vIn.x;\n" - << "double u = precalcSumSquares + x2;\n" - << "double v = precalcSumSquares - x2;\n" - << "double xmaxm1 = 0.5 * (Sqrt1pm1(u) + Sqrt1pm1(v));\n" - << "double a = vIn.x / (1 + xmaxm1);\n" - << "double ssx = xmaxm1 < 0 ? 0.0 : sqrt(xmaxm1);\n" - << "vOut.x = (" << weightDivPiDiv2 << " * asin(clamp(a, (double)-1.0, (double)1.0)));\n" + << "\t\tdouble x2 = 2.0 * vIn.x;\n" + << "\t\tdouble u = precalcSumSquares + x2;\n" + << "\t\tdouble v = precalcSumSquares - x2;\n" + << "\t\tdouble xmaxm1 = 0.5 * (Sqrt1pm1(u) + Sqrt1pm1(v));\n" + << "\t\tdouble a = vIn.x / (1 + xmaxm1);\n" + << "\t\tdouble ssx = xmaxm1 < 0 ? 0.0 : sqrt(xmaxm1);\n" + << "\t\tvOut.x = (" << weightDivPiDiv2 << " * asin(clamp(a, (double)-1.0, (double)1.0)));\n" << "\n" - << "if (vIn.y > 0)\n" - << " vOut.y = " << weightDivPiDiv2 << " * log1p(xmaxm1 + ssx);\n" - << "else\n" - << " vOut.y = -(" << weightDivPiDiv2 << " * log1p(xmaxm1 + ssx));\n" + << "\t\tif (vIn.y > 0)\n" + << "\t\t\tvOut.y = " << weightDivPiDiv2 << " * log1p(xmaxm1 + ssx);\n" + << "\t\telse\n" + << "\t\t\tvOut.y = -(" << weightDivPiDiv2 << " * log1p(xmaxm1 + ssx));\n" << "\n" << "\t\tvOut.z = " << DefaultZCl() << "\t}\n"; diff --git a/Source/Ember/Variations05.h b/Source/Ember/Variations05.h index 94d33b5..ac7f677 100644 --- a/Source/Ember/Variations05.h +++ b/Source/Ember/Variations05.h @@ -3678,9 +3678,9 @@ public: string prefix = Prefix(); //CPU sets fycle and bcycle to 0 at the beginning in Precalc(). //Set to random in OpenCL since a value can't be set once and kept between kernel launches without writing it back to an OpenCL buffer. - ss << "\n\t\tvarState." << prefix << "hexaplay3D_rswtch" << stateIndex << " = trunc(MwcNext01(&mwc) * 3.0);"; - ss << "\n\t\tvarState." << prefix << "hexaplay3D_fcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 5.0);"; - ss << "\n\t\tvarState." << prefix << "hexaplay3D_bcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 2.0);"; + ss << "\n\tvarState." << prefix << "hexaplay3D_rswtch" << stateIndex << " = trunc(MwcNext01(&mwc) * 3.0);"; + ss << "\n\tvarState." << prefix << "hexaplay3D_fcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 5.0);"; + ss << "\n\tvarState." << prefix << "hexaplay3D_bcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 2.0);"; return ss.str(); } @@ -4068,9 +4068,9 @@ public: string prefix = Prefix(); //CPU sets fycle and bcycle to 0 at the beginning in Precalc(). //Set to random in OpenCL since a value can't be set once and kept between kernel launches without writing it back to an OpenCL buffer. - ss << "\n\t\tvarState." << prefix << "hexnix3D_rswtch" << stateIndex << " = trunc(MwcNext01(&mwc) * 3.0);"; - ss << "\n\t\tvarState." << prefix << "hexnix3D_fcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 5.0);"; - ss << "\n\t\tvarState." << prefix << "hexnix3D_bcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 2.0);"; + ss << "\n\tvarState." << prefix << "hexnix3D_rswtch" << stateIndex << " = trunc(MwcNext01(&mwc) * 3.0);"; + ss << "\n\tvarState." << prefix << "hexnix3D_fcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 5.0);"; + ss << "\n\tvarState." << prefix << "hexnix3D_bcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 2.0);"; return ss.str(); } diff --git a/Source/Ember/Variations06.h b/Source/Ember/Variations06.h index fc8c2b6..f53458d 100644 --- a/Source/Ember/Variations06.h +++ b/Source/Ember/Variations06.h @@ -4353,10 +4353,10 @@ public: ss2 << "_" << XformIndexInEmber(); string stateIndex = ss2.str(); string prefix = Prefix(); - ss << "\n\t\tvarState." << prefix << "smartcrop_x" << stateIndex << " = 0;"; - ss << "\n\t\tvarState." << prefix << "smartcrop_y" << stateIndex << " = 0;"; - ss << "\n\t\tvarState." << prefix << "smartcrop_z" << stateIndex << " = 0;"; - ss << "\n\t\tvarState." << prefix << "smartcrop_c" << stateIndex << " = 0;"; + ss << "\n\tvarState." << prefix << "smartcrop_x" << stateIndex << " = 0;"; + ss << "\n\tvarState." << prefix << "smartcrop_y" << stateIndex << " = 0;"; + ss << "\n\tvarState." << prefix << "smartcrop_z" << stateIndex << " = 0;"; + ss << "\n\tvarState." << prefix << "smartcrop_c" << stateIndex << " = 0;"; return ss.str(); } diff --git a/Source/EmberCL/RendererCL.cpp b/Source/EmberCL/RendererCL.cpp index b86aecd..0e88185 100644 --- a/Source/EmberCL/RendererCL.cpp +++ b/Source/EmberCL/RendererCL.cpp @@ -33,6 +33,7 @@ RendererCL::RendererCL(const vector>& devices, m_PaletteFormat.image_channel_data_type = CL_FLOAT; m_FinalFormat.image_channel_order = CL_RGBA; m_FinalFormat.image_channel_data_type = CL_FLOAT; + m_CompileBegun = [&]() { }; Init(devices, shared, outputTexID); } @@ -903,6 +904,7 @@ bool RendererCL::BuildIterProgramForEmber(bool doAccum) if (b) { + m_CompileBegun(); m_IterKernel = m_IterOpenCLKernelCreator.CreateIterKernelString(m_Ember, m_Params.first, m_GlobalShared.first, m_LockAccum, doAccum); //cout << "Building: " << "\n" << iterProgram << "\n"; vector threads; diff --git a/Source/EmberCL/RendererCL.h b/Source/EmberCL/RendererCL.h index 8dc5202..1ac5436 100644 --- a/Source/EmberCL/RendererCL.h +++ b/Source/EmberCL/RendererCL.h @@ -19,9 +19,10 @@ class EMBERCL_API RendererCLBase { public: virtual ~RendererCLBase() { } - virtual bool ReadFinal(v4F* pixels) = 0; - virtual bool ClearFinal() = 0; - virtual bool AnyNvidia() const = 0; + virtual bool ReadFinal(v4F* pixels) { return false; } + virtual bool ClearFinal() { return false; } + virtual bool AnyNvidia() const { return false; } + std::function m_CompileBegun; }; /// diff --git a/Source/Fractorium/DoubleSpinBox.cpp b/Source/Fractorium/DoubleSpinBox.cpp index bcc6305..c573af0 100644 --- a/Source/Fractorium/DoubleSpinBox.cpp +++ b/Source/Fractorium/DoubleSpinBox.cpp @@ -230,6 +230,47 @@ bool DoubleSpinBox::eventFilter(QObject* o, QEvent* e) return QDoubleSpinBox::eventFilter(o, e); } +/// +/// Override which is for handling specific key presses while this control is focused. +/// In particular, + = and up arrow increase the value, equivalent to scrolling the mouse wheel up, while also observing shift/ctrl modifiers. +/// Values are decreased in the same way by pressing - _ or down arrow. +/// +/// The key event +void DoubleSpinBox::keyPressEvent(QKeyEvent* ke) +{ + bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier); + bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier); + + if (ke->key() == Qt::Key_Plus || ke->key() == Qt::Key_Equal || ke->key() == Qt::Key_Up) + { + if (shift) + { + setSingleStep(m_SmallStep); + setValue(value() + m_SmallStep); + } + else + { + setSingleStep(m_Step); + setValue(value() + (ctrl ? m_Step * 10 : m_Step)); + } + } + else if (ke->key() == Qt::Key_Minus || ke->key() == Qt::Key_Underscore || ke->key() == Qt::Key_Down) + { + if (shift) + { + setSingleStep(m_SmallStep); + setValue(value() - m_SmallStep); + } + else + { + setSingleStep(m_Step); + setValue(value() - (ctrl ? m_Step * 10 : m_Step)); + } + } + else + QDoubleSpinBox::keyPressEvent(ke); +} + /// /// Called when focus enters the spinner. /// diff --git a/Source/Fractorium/DoubleSpinBox.h b/Source/Fractorium/DoubleSpinBox.h index 5552825..b2d1151 100644 --- a/Source/Fractorium/DoubleSpinBox.h +++ b/Source/Fractorium/DoubleSpinBox.h @@ -38,10 +38,11 @@ public slots: protected: virtual bool eventFilter(QObject* o, QEvent* e) override; - virtual void focusInEvent(QFocusEvent* e); - virtual void focusOutEvent(QFocusEvent* e); - virtual void enterEvent(QEvent* e); - virtual void leaveEvent(QEvent* e); + virtual void keyPressEvent(QKeyEvent* event) override; + virtual void focusInEvent(QFocusEvent* e) override; + virtual void focusOutEvent(QFocusEvent* e) override; + virtual void enterEvent(QEvent* e) override; + virtual void leaveEvent(QEvent* e) override; bool m_DoubleClick; shared_ptr m_Settings; diff --git a/Source/Fractorium/Fractorium.cpp b/Source/Fractorium/Fractorium.cpp index efb7282..e1b7086 100644 --- a/Source/Fractorium/Fractorium.cpp +++ b/Source/Fractorium/Fractorium.cpp @@ -89,10 +89,6 @@ Fractorium::Fractorium(QWidget* p) pixmap.fill(m_XformComboColors[i]); m_XformComboIcons[i] = QIcon(pixmap); } - - //Set Default VariationTreeBgColor - m_VariationTreeBgColorNoneZero=QColor(200,200,200); - m_VariationTreeBgColorZero=QColor(255,255,255); QPixmap pixmap(iconSize_, iconSize_); pixmap.fill(m_FinalXformComboColor); @@ -348,6 +344,11 @@ void Fractorium::dockLocationChanged(Qt::DockWidgetArea area) /// false bool Fractorium::eventFilter(QObject* o, QEvent* e) { + static int fcount = 0;//Qt seems to deliver three events for every key press. So a count must be kept to only respond to the third event. + static int libdelcount = 0;//Note that if anything ever changes under the hood with Qt, this will likely stop working, so adjust as accordingly. + static int xfupcount = 0; + static int xfdncount = 0; + if (o == ui.GLParentScrollArea && e->type() == QEvent::Resize) { m_WidthSpin->DoubleClickNonZero(ui.GLParentScrollArea->width() * ui.GLDisplay->devicePixelRatioF()); @@ -355,24 +356,79 @@ bool Fractorium::eventFilter(QObject* o, QEvent* e) } else if (auto ke = dynamic_cast(e)) { + auto combo = ui.CurrentXformCombo; bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier); if (ke->key() >= Qt::Key_F1 && ke->key() <= Qt::Key_F32) { - int val = ke->key() - (int)Qt::Key_F1; + fcount++; - if (val < ui.CurrentXformCombo->count()) - ui.CurrentXformCombo->setCurrentIndex(val); + if (fcount >= 3) + { + int val = ke->key() - (int)Qt::Key_F1; + + if (val < combo->count()) + combo->setCurrentIndex(val); + + fcount = 0; + qDebug() << "global function key press: " << ke->key() << " " << o->metaObject()->className() << " " << o->objectName(); + } + + return true; } else if (o == ui.LibraryTree) { //Require shift for deleting to prevent it from triggering when the user enters delete in the edit box. if (ke->key() == Qt::Key_Delete && e->type() == QEvent::KeyRelease && shift) { - auto v = GetCurrentEmberIndex(); + libdelcount++; - if (ui.LibraryTree->topLevelItem(0)->childCount() > 1) - OnDelete(v); + if (libdelcount >= 3) + { + auto v = GetCurrentEmberIndex(); + + if (ui.LibraryTree->topLevelItem(0)->childCount() > 1) + OnDelete(v); + + libdelcount = 0; + } + + return true; + } + } + else if (o == this) + { + unsigned int index = combo->currentIndex(); + + if (ke->key() == Qt::Key_Plus || ke->key() == Qt::Key_Equal) + { + xfupcount++; + + if (xfupcount >= 3) + { + xfupcount = 0; + combo->setCurrentIndex((index + 1) % combo->count()); + qDebug() << "global arrow plus key press: " << ke->key() << " " << o->metaObject()->className() << " " << o->objectName(); + } + + return true; + } + else if (ke->key() == Qt::Key_Minus || ke->key() == Qt::Key_Underscore) + { + xfdncount++; + + if (xfdncount >= 3) + { + xfdncount = 0; + + if (index == 0) + index = combo->count(); + + combo->setCurrentIndex((index - 1) % combo->count()); + qDebug() << "global arrow minus key press: " << ke->key() << " " << o->metaObject()->className() << " " << o->objectName(); + } + + return true; } } } @@ -866,6 +922,10 @@ void Fractorium::SetTabOrders() w = SetTabOrder(this, w, ui.WorldPivotRadio); w = SetTabOrder(this, ui.VariationsFilterLineEdit, ui.VariationsFilterClearButton);//Xforms variation. w = SetTabOrder(this, w, ui.VariationsTree); + w = SetTabOrder(this, w, ui.ClearXaosButton); + w = SetTabOrder(this, w, ui.RandomXaosButton); + w = SetTabOrder(this, w, ui.AddLayerButton); + w = SetTabOrder(this, w, ui.AddLayerSpinBox); //Xforms xaos is done dynamically every time. w = SetTabOrder(this, ui.PaletteFilenameCombo, m_PaletteHueSpin);//Palette. w = SetTabOrder(this, w, m_PaletteContrastSpin); diff --git a/Source/Fractorium/Fractorium.h b/Source/Fractorium/Fractorium.h index 4e70e26..f93804c 100644 --- a/Source/Fractorium/Fractorium.h +++ b/Source/Fractorium/Fractorium.h @@ -63,9 +63,11 @@ template class FinalRenderEmberController; class Fractorium : public QMainWindow { Q_OBJECT - Q_PROPERTY(QColor VariationTreeBgColorNoneZero MEMBER m_VariationTreeBgColorNoneZero) - Q_PROPERTY(QColor VariationTreeBgColorZero MEMBER m_VariationTreeBgColorZero) - + Q_PROPERTY(QColor VariationTreeColorNonZero MEMBER m_VariationTreeColorNonZero) + Q_PROPERTY(QColor VariationTreeColorZero MEMBER m_VariationTreeColorZero) + Q_PROPERTY(QColor VariationTreeBgColorNonZero MEMBER m_VariationTreeBgColorNonZero) + Q_PROPERTY(QColor VariationTreeBgColorZero MEMBER m_VariationTreeBgColorZero) + friend GLWidget; friend QssDialog; friend LibraryTreeWidget; @@ -140,6 +142,7 @@ public slots: void OnActionPasteXmlOver(bool checked); void OnActionCopySelectedXforms(bool checked); void OnActionPasteSelectedXforms(bool checked); + void OnActionCopyKernel(bool checked); void OnActionResetWorkspace(bool checked);//View void OnActionAlternateEditorImage(bool checked); @@ -319,6 +322,7 @@ public slots: void OnXaosChanged(double d); void OnClearXaosButtonClicked(bool checked); void OnRandomXaosButtonClicked(bool checked); + void OnAddLayerButtonClicked(bool checked); void OnXaosRowDoubleClicked(int logicalIndex); void OnXaosColDoubleClicked(int logicalIndex); void OnXaosTableModelDataChanged(const QModelIndex& indexA, const QModelIndex& indexB); @@ -561,7 +565,8 @@ private: char m_CoordinateString[128]; QColor m_XformComboColors[XFORM_COLOR_COUNT], m_FinalXformComboColor; QIcon m_XformComboIcons[XFORM_COLOR_COUNT], m_FinalXformComboIcon; - QColor m_VariationTreeBgColorNoneZero, m_VariationTreeBgColorZero; + QColor m_VariationTreeColorNonZero, m_VariationTreeColorZero; + QColor m_VariationTreeBgColorNonZero, m_VariationTreeBgColorZero; vector m_Docks; int m_FontSize; @@ -572,7 +577,7 @@ private: shared_ptr m_Info; unique_ptr m_Controller; Ui::FractoriumClass ui; - - + + }; diff --git a/Source/Fractorium/Fractorium.ui b/Source/Fractorium/Fractorium.ui index c5ec95c..edc14e8 100644 --- a/Source/Fractorium/Fractorium.ui +++ b/Source/Fractorium/Fractorium.ui @@ -41,6 +41,9 @@ 16 + + QTabWidget::Rounded + QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::AnimatedDocks|QMainWindow::GroupedDragging @@ -432,7 +435,7 @@ false - false + true 15 @@ -513,7 +516,7 @@ false - false + true 15 @@ -575,6 +578,9 @@ Qt::NoFocus + + false + QFrame::Panel @@ -600,7 +606,7 @@ false - false + true 15 @@ -697,7 +703,7 @@ false - false + true 15 @@ -1587,7 +1593,7 @@ 1 - false + true 15 @@ -1891,6 +1897,48 @@ + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Add N new xforms with all to/from xaos values to each other set to 1, and the remainder set to 0 + + + Add Layer + + + + + + + Qt::StrongFocus + + + Xforms + + + 1 + + + 9999999 + + + + + @@ -2805,6 +2853,9 @@ 12 + + QComboBox::AdjustToContents + 9 @@ -3096,7 +3147,7 @@ 2 - false + true 110 @@ -3179,7 +3230,7 @@ QTabWidget::Triangular - 2 + 3 @@ -6924,6 +6975,8 @@ + + @@ -8286,6 +8339,17 @@ Reset Scale + + + Copy &Kernel + + + Copy the OpenCL program to the clipboard for debugging. + + + Ctrl+K + + @@ -8310,6 +8374,11 @@ QTreeWidget
LibraryTreeWidget.h
+ + SpinBox + QSpinBox +
spinbox.h
+
LibraryDockWidget diff --git a/Source/Fractorium/FractoriumCommon.h b/Source/Fractorium/FractoriumCommon.h index 4b4ed6c..7bcba3c 100644 --- a/Source/Fractorium/FractoriumCommon.h +++ b/Source/Fractorium/FractoriumCommon.h @@ -495,12 +495,61 @@ static QString BaseStyle() "It's not needed for other themes." "You should keep this at the top of whatever custom style you make to ensure the tabs aren't unusually large.*/\n" #ifndef _WIN32 - "QTabBar::tab { height: 3ex; }\n\n" + "QTabBar::tab { height: 4ex; }\n\n" #else - "QTabBar::tab { height: 3ex; }\n\n" + "QTabBar::tab { height: 4ex; }\n\n" #endif "/*This is needed to give the labels on the status bar some padding.*/\n" - "QStatusBar QLabel { padding-left: 2px; padding-right: 2px; }\n\n" + "QStatusBar QLabel { padding-left: 2px; padding-right: 2px; }\n" + "\n" + "Fractorium\n" + "{\n" + "\tqproperty-VariationTreeColorNonZero:black;\n" + "\tqproperty-VariationTreeColorZero:black;\n" + "\tqproperty-VariationTreeBgColorNonZero:lightgray;\n" + "\tqproperty-VariationTreeBgColorZero:white;\n" + "}\n" + "\n" + "/*For some reason, Qt does not draw table headers correctly, so the style must always be manually specified.*/\n" + "QHeaderView::section::vertical:enabled\n" + "{\n" + "\tcolor: black;\n" + "\tbackground-color: lightgray;\n" + "\tborder: none;\n" + "\tborder-bottom: 1px solid gray;\n" + "\tpadding: 4px;\n" + "\tfont: 8pt \"MS Shell Dlg 2\";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/\n" + "}\n" + "\n" + "QHeaderView::section::horizontal:enabled\n" + "{\n" + "\tcolor: black;\n" + "\tbackground-color: lightgray;\n" + "\tborder: 0px solid darkgray;\n" + "\tborder-right: 1px solid gray;\n" + "\tpadding: 4px;\n" + "\tfont: 8pt \"MS Shell Dlg 2\";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/\n" + "}\n" + "\n" + "QHeaderView::section::vertical:disabled\n" + "{\n" + "\tcolor: rgb(35, 35, 35);\n" + "\tbackground-color: rgb(53, 53, 53);\n" + "\tborder: 0px none darkgray;\n" + "\tborder-bottom: 1px solid rgb(53, 53, 53);\n" + "\tpadding: 4px;\n" + "\tfont: 8pt \"MS Shell Dlg 2\";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/\n" + "}\n" + "\n" + "QHeaderView::section::horizontal:disabled\n" + "{\n" + "\tcolor:rgb(35, 35, 35);\n" + "\tbackground-color: rgb(53, 53, 53);\n" + "\tborder: 0px none darkgray;\n" + "\tborder-right: 0px solid rgb(53, 53, 53);\n" + "\tpadding: 4px;\n" + "\tfont: 8pt \"MS Shell Dlg 2\";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/\n" + "}\n" ; } diff --git a/Source/Fractorium/FractoriumEmberController.cpp b/Source/Fractorium/FractoriumEmberController.cpp index 085d5b0..e3eecf8 100644 --- a/Source/Fractorium/FractoriumEmberController.cpp +++ b/Source/Fractorium/FractoriumEmberController.cpp @@ -336,8 +336,6 @@ void FractoriumEmberController::SetEmberPrivate(const Ember& ember, bool v if (updatePointer && (typeid(T) == typeid(U))) m_EmberFilePointer = (Ember*)&ember; - else - m_EmberFilePointer = nullptr; if (!verbatim) { diff --git a/Source/Fractorium/FractoriumEmberController.h b/Source/Fractorium/FractoriumEmberController.h index d9f41fc..c88f314 100644 --- a/Source/Fractorium/FractoriumEmberController.h +++ b/Source/Fractorium/FractoriumEmberController.h @@ -109,6 +109,7 @@ public: virtual void PasteXmlOver() { } virtual void CopySelectedXforms() { } virtual void PasteSelectedXforms() { } + virtual void CopyKernel() { } virtual void AddReflectiveSymmetry() { }//Tools. virtual void AddRotationalSymmetry() { } virtual void AddBothSymmetry() { } @@ -184,7 +185,7 @@ public: virtual void XformNameChanged(int row, int col) { } virtual void XformAnimateChanged(int state) { } virtual void FillXforms(int index = 0) { } - virtual void UpdateXformName(int index) { } + virtual void UpdateXformName(int index) { } //Xforms Affine. virtual void AffineSetHelper(double d, int index, bool pre) { } @@ -210,6 +211,7 @@ public: virtual void XformColorSpeedChanged(double d) { } virtual void XformOpacityChanged(double d) { } virtual void XformDirectColorChanged(double d) { } + virtual void SoloXformCheckBoxStateChanged(int state, int index) { } virtual QColor ColorIndexToQColor(double d) { return QColor(); } //Xforms Variations. @@ -218,6 +220,7 @@ public: virtual void ClearVariationsTree() { } virtual void VariationSpinBoxValueChanged(double d) { } virtual void FilteredVariations() { } + virtual void FillVariationTreeWithCurrentXform() { } //Xforms Selection. @@ -226,6 +229,7 @@ public: virtual void XaosChanged(int x, int y, double val) { } virtual void ClearXaos() { } virtual void RandomXaos() { } + virtual void AddLayer(int xforms) { } //Palette. virtual size_t InitPaletteList(const QString& s) { return 0; } @@ -375,6 +379,7 @@ public: virtual void PasteXmlOver() override; virtual void CopySelectedXforms() override; virtual void PasteSelectedXforms() override; + virtual void CopyKernel() override; virtual void AddReflectiveSymmetry() override; virtual void AddRotationalSymmetry() override; virtual void AddBothSymmetry() override; @@ -451,7 +456,7 @@ public: virtual void XformNameChanged(int row, int col) override; virtual void XformAnimateChanged(int state) override; virtual void FillXforms(int index = 0) override; - virtual void UpdateXformName(int index) override; + virtual void UpdateXformName(int index) override; void FillWithXform(Xform* xform); Xform* CurrentXform(); void UpdateXform(std::function*, size_t, size_t)> func, eXformUpdate updateType = eXformUpdate::UPDATE_CURRENT, bool updateRender = true, eProcessAction action = eProcessAction::FULL_RENDER, size_t index = 0); @@ -480,6 +485,7 @@ public: virtual void XformColorSpeedChanged(double d) override; virtual void XformOpacityChanged(double d) override; virtual void XformDirectColorChanged(double d) override; + virtual void SoloXformCheckBoxStateChanged(int state, int index) override; virtual QColor ColorIndexToQColor(double d) override; void FillColorWithXform(Xform* xform); @@ -489,6 +495,7 @@ public: virtual void ClearVariationsTree() override; virtual void VariationSpinBoxValueChanged(double d) override; virtual void FilteredVariations() override; + virtual void FillVariationTreeWithCurrentXform() override; void FillVariationTreeWithXform(Xform* xform); //Xforms Xaos. @@ -496,6 +503,7 @@ public: virtual void XaosChanged(int x, int y, double val) override; virtual void ClearXaos() override; virtual void RandomXaos() override; + virtual void AddLayer(int xforms) override; //Xforms Selection. bool XformCheckboxAt(int i, std::function func); diff --git a/Source/Fractorium/FractoriumMenus.cpp b/Source/Fractorium/FractoriumMenus.cpp index 420cdfe..37a6e45 100644 --- a/Source/Fractorium/FractoriumMenus.cpp +++ b/Source/Fractorium/FractoriumMenus.cpp @@ -25,6 +25,7 @@ void Fractorium::InitMenusUI() connect(ui.ActionPasteXmlOver, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteXmlOver(bool)), Qt::QueuedConnection); connect(ui.ActionCopySelectedXforms, SIGNAL(triggered(bool)), this, SLOT(OnActionCopySelectedXforms(bool)), Qt::QueuedConnection); connect(ui.ActionPasteSelectedXforms, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteSelectedXforms(bool)), Qt::QueuedConnection); + connect(ui.ActionCopyKernel, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyKernel(bool)), Qt::QueuedConnection); ui.ActionPasteSelectedXforms->setEnabled(false); //View menu. connect(ui.ActionResetWorkspace, SIGNAL(triggered(bool)), this, SLOT(OnActionResetWorkspace(bool)), Qt::QueuedConnection); @@ -471,10 +472,10 @@ void FractoriumEmberController::Undo() int index = m_Ember.GetTotalXformIndex(current, forceFinal); m_LastEditWasUndoRedo = true; m_UndoIndex = std::max(0u, m_UndoIndex - 1u); - SetEmber(m_UndoList[m_UndoIndex], true, false);//Don't update pointer because it's coming from the undo list... + SetEmber(m_UndoList[m_UndoIndex], true, false);//Don't update pointer because it's coming from the undo list. m_EditState = eEditUndoState::UNDO_REDO; - if (index >= 0) + if (index >= 0 && index < m_Fractorium->ui.CurrentXformCombo->count()) m_Fractorium->CurrentXform(index); m_Fractorium->ui.ActionUndo->setEnabled(m_UndoList.size() > 1 && (m_UndoIndex > 0)); @@ -497,10 +498,10 @@ void FractoriumEmberController::Redo() int index = m_Ember.GetTotalXformIndex(current, forceFinal); m_LastEditWasUndoRedo = true; m_UndoIndex = std::min(m_UndoIndex + 1, m_UndoList.size() - 1); - SetEmber(m_UndoList[m_UndoIndex], true, false); + SetEmber(m_UndoList[m_UndoIndex], true, false);//Don't update pointer because it's coming from the undo list. m_EditState = eEditUndoState::UNDO_REDO; - if (index >= 0) + if (index >= 0 && index < m_Fractorium->ui.CurrentXformCombo->count()) m_Fractorium->CurrentXform(index); m_Fractorium->ui.ActionUndo->setEnabled(m_UndoList.size() > 1 && (m_UndoIndex > 0)); @@ -713,6 +714,19 @@ void Fractorium::OnActionPasteSelectedXforms(bool checked) m_Controller->PasteSelectedXforms(); } +/// +/// Copy the text of the OpenCL iteration kernel to the clipboard. +/// This performs no action if the renderer is not of type RendererCL. +/// +template +void FractoriumEmberController::CopyKernel() +{ + if (auto rendererCL = dynamic_cast*>(m_Renderer.get())) + QApplication::clipboard()->setText(QString::fromStdString(rendererCL->IterKernel())); +} + +void Fractorium::OnActionCopyKernel(bool checked) { m_Controller->CopyKernel(); } + /// /// Reset dock widgets and tabs to their default position. /// Note that there is a bug in Qt, where it will only move them all to the left side if at least @@ -837,7 +851,7 @@ void FractoriumEmberController::Flatten() { ember.Flatten(XmlToEmber::m_FlattenNames); }, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll()); - FillVariationTreeWithXform(CurrentXform()); + FillVariationTreeWithCurrentXform(); } void Fractorium::OnActionFlatten(bool checked) { m_Controller->Flatten(); } @@ -852,7 +866,7 @@ void FractoriumEmberController::Unflatten() { ember.Unflatten(); }, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll()); - FillVariationTreeWithXform(CurrentXform()); + FillVariationTreeWithCurrentXform(); } void Fractorium::OnActionUnflatten(bool checked) { m_Controller->Unflatten(); } diff --git a/Source/Fractorium/FractoriumPalette.cpp b/Source/Fractorium/FractoriumPalette.cpp index fefd61d..0db8054 100644 --- a/Source/Fractorium/FractoriumPalette.cpp +++ b/Source/Fractorium/FractoriumPalette.cpp @@ -38,7 +38,6 @@ void Fractorium::InitPaletteUI() palettePreviewTable->setItem(0, 0, previewNameCol); auto previewPaletteItem = new QTableWidgetItem(); palettePreviewTable->setItem(0, 1, previewPaletteItem); - palettePreviewTable->installEventFilter(this); connect(ui.PaletteFilterLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnPaletteFilterLineEditTextChanged(const QString&))); connect(ui.PaletteFilterClearButton, SIGNAL(clicked(bool)), this, SLOT(OnPaletteFilterClearButtonClicked(bool))); paletteTable->setColumnWidth(1, 260);//256 plus small margin on each side. @@ -392,6 +391,7 @@ void FractoriumEmberController::PaletteEditorButtonClicked() ed->SetColorIndices(colorIndices); ed->SetPaletteFile(m_CurrentPaletteFilePath); + //ed->setpal if (ed->exec() == QDialog::Accepted) { //Copy all just to be safe, because they may or may not have synced. diff --git a/Source/Fractorium/FractoriumRender.cpp b/Source/Fractorium/FractoriumRender.cpp index e7bfeb2..80bf81b 100644 --- a/Source/Fractorium/FractoriumRender.cpp +++ b/Source/Fractorium/FractoriumRender.cpp @@ -346,7 +346,7 @@ bool FractoriumEmberController::Render() if (action != eProcessAction::NOTHING) { size_t i = 0; - int solo = m_Fractorium->ui.CurrentXformCombo->property("soloxform").toInt(); + int solo = m_Ember.m_Solo; bool forceFinal = m_Fractorium->HaveFinal(); if (solo != -1) @@ -667,9 +667,11 @@ bool Fractorium::CreateRendererFromOptions(bool updatePreviews) bool ok = true; bool useOpenCL = m_Info->Ok() && m_Settings->OpenCL(); auto v = Devices(m_Settings->Devices()); + bool doOpenCL = useOpenCL && !v.empty(); + ui.ActionCopyKernel->setEnabled(doOpenCL); //The most important option to process is what kind of renderer is desired, so do it first. - if (!m_Controller->CreateRenderer((useOpenCL && !v.empty()) ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, v, updatePreviews, useOpenCL && m_Settings->SharedTexture())) + if (!m_Controller->CreateRenderer(doOpenCL ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, v, updatePreviews, doOpenCL && m_Settings->SharedTexture())) { //If using OpenCL, will only get here if creating RendererCL failed, but creating a backup CPU Renderer succeeded. ShowCritical("Renderer Creation Error", "Error creating renderer, most likely a GPU problem. Using CPU instead."); @@ -677,12 +679,21 @@ bool Fractorium::CreateRendererFromOptions(bool updatePreviews) m_Settings->SharedTexture(false); ui.ActionCpu->setChecked(true); ui.ActionCL->setChecked(false); + ui.ActionCopyKernel->setEnabled(false); m_OptionsDialog->ui.OpenCLCheckBox->setChecked(false); m_OptionsDialog->ui.SharedTextureCheckBox->setChecked(false); m_FinalRenderDialog->ui.FinalRenderOpenCLCheckBox->setChecked(false); ok = false; } + if (auto rendererCL = dynamic_cast(m_Controller->Renderer())) + rendererCL->m_CompileBegun = [&]() + { + m_RenderStatusLabel->setText("Compiling OpenCL kernel..."); + m_RenderStatusLabel->repaint(); + QApplication::processEvents(); + }; + return ok; } diff --git a/Source/Fractorium/FractoriumXaos.cpp b/Source/Fractorium/FractoriumXaos.cpp index a82e338..56f679d 100644 --- a/Source/Fractorium/FractoriumXaos.cpp +++ b/Source/Fractorium/FractoriumXaos.cpp @@ -22,6 +22,7 @@ void Fractorium::InitXaosUI() connect(m_XaosSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnXaosChanged(double)), Qt::QueuedConnection); connect(ui.ClearXaosButton, SIGNAL(clicked(bool)), this, SLOT(OnClearXaosButtonClicked(bool)), Qt::QueuedConnection); connect(ui.RandomXaosButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomXaosButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.AddLayerButton, SIGNAL(clicked(bool)), this, SLOT(OnAddLayerButtonClicked(bool)), Qt::QueuedConnection); connect(ui.XaosTableView->verticalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnXaosRowDoubleClicked(int)), Qt::QueuedConnection); connect(ui.XaosTableView->horizontalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnXaosColDoubleClicked(int)), Qt::QueuedConnection); } @@ -165,6 +166,49 @@ void FractoriumEmberController::RandomXaos() void Fractorium::OnRandomXaosButtonClicked(bool checked) { m_Controller->RandomXaos(); } +/// +/// Add a layer using the specified number of xforms. +/// A layer is defined as a new set of xforms whose xaos values are the following: +/// From existing to existing: unchanged. +/// From existing to new: 0. +/// From new to existing: 0. +/// From new to new: 1. +/// +/// The number of new xforms to add to create the layer +template +void FractoriumEmberController::AddLayer(int xforms) +{ + Update([&] + { + auto origXformCount = m_Ember.XformCount(); + m_Ember.AddXforms(xforms); + + for (auto i = 0; i < m_Ember.XformCount(); i++) + { + auto xf = m_Ember.GetXform(i); + + if (i < origXformCount) + { + for (auto j = 0; j < m_Ember.XformCount(); j++) + if (j >= origXformCount) + xf->SetXaos(j, 0); + } + else + { + for (auto j = 0; j < m_Ember.XformCount(); j++) + if (j < origXformCount) + xf->SetXaos(j, 0); + else + xf->SetXaos(j, 1); + } + } + }); + FillXforms(); + FillSummary(); +} + +void Fractorium::OnAddLayerButtonClicked(bool checked) { m_Controller->AddLayer(ui.AddLayerSpinBox->value()); } + /// /// Toggle all xaos values in one row. /// Resets the rendering process. diff --git a/Source/Fractorium/FractoriumXforms.cpp b/Source/Fractorium/FractoriumXforms.cpp index a8627b3..bcd9303 100644 --- a/Source/Fractorium/FractoriumXforms.cpp +++ b/Source/Fractorium/FractoriumXforms.cpp @@ -15,25 +15,28 @@ void Fractorium::InitXformsUI() connect(ui.AddFinalXformButton, SIGNAL(clicked(bool)), this, SLOT(OnAddFinalXformButtonClicked(bool)), Qt::QueuedConnection); connect(ui.CurrentXformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnCurrentXformComboChanged(int)), Qt::QueuedConnection); connect(ui.AnimateXformCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnXformAnimateCheckBoxStateChanged(int)), Qt::QueuedConnection); - SetFixedTableHeader(ui.XformWeightNameTable->horizontalHeader(),QHeaderView::ResizeToContents); + SetFixedTableHeader(ui.XformWeightNameTable->horizontalHeader(), QHeaderView::ResizeToContents); //Use SetupSpinner() just to create the spinner, but use col of -1 to prevent it from being added to the table. SetupSpinner(ui.XformWeightNameTable, this, row, -1, m_XformWeightSpin, spinHeight, 0, 1000, 0.05, SIGNAL(valueChanged(double)), SLOT(OnXformWeightChanged(double)), false, 0, 1, 0); m_XformWeightSpin->setDecimals(3); m_XformWeightSpin->SmallStep(0.001); - m_XformWeightSpin->setMinimumWidth(40); + m_XformWeightSpin->setMinimumWidth(40); m_XformWeightSpinnerButtonWidget = new SpinnerLabelButtonWidget(m_XformWeightSpin, "=", 20, 19, ui.XformWeightNameTable); - m_XformWeightSpinnerButtonWidget->m_SpinBox->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed); - m_XformWeightSpinnerButtonWidget->m_Label->setStyleSheet("border: 0px;"); - m_XformWeightSpinnerButtonWidget->m_Label->setAlignment(Qt::AlignRight| Qt::AlignVCenter); - m_XformWeightSpinnerButtonWidget->m_Label->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed); - m_XformWeightSpinnerButtonWidget->m_Button->setToolTip("Equalize weights"); + m_XformWeightSpinnerButtonWidget->m_SpinBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + m_XformWeightSpinnerButtonWidget->m_Label->setStyleSheet("border: 0px;"); + m_XformWeightSpinnerButtonWidget->m_Label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + m_XformWeightSpinnerButtonWidget->m_Label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + m_XformWeightSpinnerButtonWidget->m_Button->setToolTip("Equalize weights"); m_XformWeightSpinnerButtonWidget->m_Button->setStyleSheet("text-align: center center"); - m_XformWeightSpinnerButtonWidget->setMaximumWidth(130); + m_XformWeightSpinnerButtonWidget->setMaximumWidth(130); connect(m_XformWeightSpinnerButtonWidget->m_Button, SIGNAL(clicked(bool)), this, SLOT(OnEqualWeightButtonClicked(bool)), Qt::QueuedConnection); ui.XformWeightNameTable->setCellWidget(0, 0, m_XformWeightSpinnerButtonWidget); ui.XformWeightNameTable->setItem(0, 1, new QTableWidgetItem()); connect(ui.XformWeightNameTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnXformNameChanged(int, int)), Qt::QueuedConnection); - ui.CurrentXformCombo->setProperty("soloxform", -1); + ui.CurrentXformCombo->view()->setMinimumWidth(100); + ui.CurrentXformCombo->view()->setMaximumWidth(500); + //ui.CurrentXformCombo->view()->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + ui.CurrentXformCombo->view()->setSizeAdjustPolicy(QAbstractScrollArea::SizeAdjustPolicy::AdjustToContentsOnFirstShow); #ifndef _WIN32 //For some reason linux makes these 24x24, even though the designer explicitly says 16x16. ui.AddXformButton->setIconSize(QSize(16, 16)); @@ -63,7 +66,7 @@ Xform* FractoriumEmberController::CurrentXform() void Fractorium::CurrentXform(uint i) { if (i < uint(ui.CurrentXformCombo->count())) - ui.CurrentXformCombo->setCurrentIndex(i); + ui.CurrentXformCombo->setCurrentIndex(i); } /// /// Set the current xform and populate all GUI widgets. @@ -79,7 +82,7 @@ void FractoriumEmberController::CurrentXformComboChanged(int index) { FillWithXform(xform); m_GLController->SetSelectedXform(xform); - int solo = m_Fractorium->ui.CurrentXformCombo->property("soloxform").toInt(); + int solo = m_Ember.m_Solo; m_Fractorium->ui.SoloXformCheckBox->blockSignals(true); m_Fractorium->ui.SoloXformCheckBox->setChecked(solo == index); m_Fractorium->ui.SoloXformCheckBox->blockSignals(false); @@ -244,7 +247,7 @@ void FractoriumEmberController::ClearXform() { xform->ClearAndDeleteVariations();//Note xaos is left alone. }, eXformUpdate::UPDATE_SELECTED); - FillVariationTreeWithXform(CurrentXform()); + FillVariationTreeWithCurrentXform(); } void Fractorium::OnClearXformButtonClicked(bool checked) { m_Controller->ClearXform(); } @@ -393,11 +396,13 @@ void FractoriumEmberController::XformNameChanged(int row, int col) }, eXformUpdate::UPDATE_CURRENT, false); FillSummary();//Manually update because this does not trigger a render, which is where this would normally be called. } + void Fractorium::OnXformNameChanged(int row, int col) { - m_Controller->XformNameChanged(row, col); - m_Controller->UpdateXformName(ui.CurrentXformCombo->currentIndex()); + m_Controller->XformNameChanged(row, col); + m_Controller->UpdateXformName(ui.CurrentXformCombo->currentIndex()); } + /// /// Set the animate field of the selected xforms, this allows excluding current if it's not checked, but applies only to it if none are checked. /// This has no effect on interactive rendering, it only sets a value @@ -480,9 +485,8 @@ void FractoriumEmberController::SetNormalizedWeightText(Xform* xform) m_Ember.CalcNormalizedWeights(m_NormalizedWeights); if (index != -1 && index < m_NormalizedWeights.size()) - //m_Fractorium->m_XformWeightSpin->setSuffix(QString(" (") + QLocale::system().toString(double(m_NormalizedWeights[index]), 'g', 3) + ")"); - m_Fractorium->m_XformWeightSpinnerButtonWidget->m_Label->setText(QString(" (") + QLocale::system().toString(double(m_NormalizedWeights[index]), 'g', 3) + ")"); - } + m_Fractorium->m_XformWeightSpinnerButtonWidget->m_Label->setText(QString(" (") + QLocale::system().toString(double(m_NormalizedWeights[index]), 'g', 3) + ")"); + } } /// /// Determine whether the specified xform is the final xform in the ember. @@ -517,7 +521,7 @@ void FractoriumEmberController::FillXforms(int index) { combo->addItem(ToString(i + 1)); combo->setItemIcon(i, m_Fractorium->m_XformComboIcons[i % XFORM_COLOR_COUNT]); - UpdateXformName(i); + UpdateXformName(i); } i = 0; @@ -553,7 +557,7 @@ void FractoriumEmberController::FillXforms(int index) m_Fractorium->m_XformsSelectionLayout->addRow(cb, new QWidget(m_Fractorium)); combo->addItem("Final"); combo->setItemIcon(i, m_Fractorium->m_FinalXformComboIcon); - UpdateXformName(i); + UpdateXformName(i); } m_Fractorium->m_XformsSelectionLayout->blockSignals(false); @@ -562,31 +566,50 @@ void FractoriumEmberController::FillXforms(int index) if (index < combo->count()) combo->setCurrentIndex(index); + m_Fractorium->ui.SoloXformCheckBox->blockSignals(true); + + if (m_Ember.m_Solo == combo->currentIndex()) + m_Fractorium->ui.SoloXformCheckBox->setChecked(true); + else + m_Fractorium->ui.SoloXformCheckBox->setChecked(false); + + SoloXformCheckBoxStateChanged(m_Ember.m_Solo > -1 ? Qt::Checked : Qt::Unchecked, m_Ember.m_Solo); + m_Fractorium->ui.SoloXformCheckBox->blockSignals(false); m_Fractorium->FillXaosTable(); - m_Fractorium->OnSoloXformCheckBoxStateChanged(Qt::Unchecked); - m_Fractorium->OnCurrentXformComboChanged(index);//Make sure the event gets called, because it won't if the zero index is already selected. + m_Fractorium->OnCurrentXformComboChanged(index);//Make sure the event gets called, because it won't if the zero index is already selected. } + /// /// Update the text in xforms combo box to show the name of Xform. /// /// The index of the Xform to update. -/// +/// template void FractoriumEmberController::UpdateXformName(int index) { - bool forceFinal = m_Fractorium->HaveFinal(); - bool isFinal = m_Ember.FinalXform() == m_Ember.GetTotalXform(index, forceFinal); - QString name = isFinal ? "Final" : QString::number(index + 1); - - if (auto xform = m_Ember.GetTotalXform(index, forceFinal)) - { - if (!xform->m_Name.empty()) - { - name += " " + QString::fromStdString(xform->m_Name); - } - m_Fractorium->ui.CurrentXformCombo->setItemText(index,name); - } + bool forceFinal = m_Fractorium->HaveFinal(); + bool isFinal = m_Ember.FinalXform() == m_Ember.GetTotalXform(index, forceFinal); + QString name = isFinal ? "Final" : QString::number(index + 1); + + if (auto xform = m_Ember.GetTotalXform(index, forceFinal)) + { + if (!xform->m_Name.empty()) + name += " " + QString::fromStdString(xform->m_Name); + + m_Fractorium->ui.CurrentXformCombo->setItemText(index, name); + auto view = m_Fractorium->ui.CurrentXformCombo->view(); + auto fontMetrics1 = view->fontMetrics(); + auto textWidth = m_Fractorium->ui.CurrentXformCombo->width(); + auto ww = fontMetrics1.width("WW"); + + for (int i = 0; i < m_Fractorium->ui.CurrentXformCombo->count(); ++i) + textWidth = std::max(fontMetrics1.width(m_Fractorium->ui.CurrentXformCombo->itemText(i)) + ww, textWidth); + + view->setMinimumWidth(textWidth); + view->setMaximumWidth(textWidth); + } } + template class FractoriumEmberController; #ifdef DO_DOUBLE template class FractoriumEmberController; diff --git a/Source/Fractorium/FractoriumXformsColor.cpp b/Source/Fractorium/FractoriumXformsColor.cpp index f58d04a..cbe3533 100644 --- a/Source/Fractorium/FractoriumXformsColor.cpp +++ b/Source/Fractorium/FractoriumXformsColor.cpp @@ -181,28 +181,32 @@ void Fractorium::OnXformDirectColorChanged(double d) { m_Controller->XformDirect /// If checked, current is solo, if unchecked, none are solo. /// Solo means that all other xforms will have their opacity temporarily /// set to zero while rendering so that only the effect of current xform is visible. -/// This will not permanently alter the ember, as the temporary opacity values will be applied +/// This will not permanently alter the opacities of ember, as the temporary opacity values will be applied /// right before rendering and reset right after. /// Called when solo xform check box is checked. /// Resets the rendering process. /// /// The state of the checkbox -void Fractorium::OnSoloXformCheckBoxStateChanged(int state) +/// The index which has been specified as the solo xform, -1 to specify none. +template +void FractoriumEmberController::SoloXformCheckBoxStateChanged(int state, int index) { if (state == Qt::Checked) { - ui.CurrentXformCombo->setProperty("soloxform", ui.CurrentXformCombo->currentIndex()); - ui.SoloXformCheckBox->setText("Solo (" + ToString(ui.CurrentXformCombo->currentIndex() + 1) + ")"); + m_Ember.m_Solo = index; + m_Fractorium->ui.SoloXformCheckBox->setText("Solo (" + ToString(index + 1) + ")"); } else if (state == Qt::Unchecked) { - ui.CurrentXformCombo->setProperty("soloxform", -1); - ui.SoloXformCheckBox->setText("Solo"); + m_Ember.m_Solo = -1; + m_Fractorium->ui.SoloXformCheckBox->setText("Solo"); } - m_Controller->UpdateRender(); + UpdateRender(); } +void Fractorium::OnSoloXformCheckBoxStateChanged(int state) { m_Controller->SoloXformCheckBoxStateChanged(state, ui.CurrentXformCombo->currentIndex()); } + /// /// Redraw the palette ref table. /// Called on resize. diff --git a/Source/Fractorium/FractoriumXformsVariations.cpp b/Source/Fractorium/FractoriumXformsVariations.cpp index 77b609e..ca8826c 100644 --- a/Source/Fractorium/FractoriumXformsVariations.cpp +++ b/Source/Fractorium/FractoriumXformsVariations.cpp @@ -16,6 +16,11 @@ void Fractorium::InitXformsVariationsUI() //Setting dimensions in the designer with a layout is futile, so must hard code here. tree->setColumnWidth(0, 160); tree->setColumnWidth(1, 23); + //Set Default variation tree text and background colors for zero and non zero cases. + m_VariationTreeColorNonZero = Qt::black; + m_VariationTreeColorZero = Qt::black; + m_VariationTreeBgColorNonZero = Qt::lightGray; + m_VariationTreeBgColorZero = Qt::white; } /// @@ -226,8 +231,9 @@ void FractoriumEmberController::VariationSpinBoxValueChanged(double d)//Would if (xformVar) xform->DeleteVariationById(var->VariationId()); -// widgetItem->setBackgroundColor(0, QColor(255, 255, 255));//Ensure background is always white if weight goes to zero. - widgetItem->setBackgroundColor(0, m_Fractorium->m_VariationTreeBgColorZero); } + widgetItem->setTextColor(0, m_Fractorium->m_VariationTreeColorZero); + widgetItem->setBackgroundColor(0, m_Fractorium->m_VariationTreeBgColorZero); + } else { if (xformVar)//The xform already contained this variation, which means they just went from a non-zero weight to another non-zero weight (the simple case). @@ -241,8 +247,9 @@ void FractoriumEmberController::VariationSpinBoxValueChanged(double d)//Would auto newVar = var->Copy();//Create a new one with default values. newVar->m_Weight = d; xform->AddVariation(newVar); -// widgetItem->setBackgroundColor(0, QColor(200, 200, 200));//Set background to gray when a variation has non-zero weight in this xform. - widgetItem->setBackgroundColor(0, m_Fractorium->m_VariationTreeBgColorNoneZero); + widgetItem->setTextColor(0, m_Fractorium->m_VariationTreeColorNonZero); + widgetItem->setBackgroundColor(0, m_Fractorium->m_VariationTreeBgColorNonZero); + //If they've added a new parametric variation, then grab the values currently in the spinners //for the child parameters and assign them to the newly added variation. if (parVar) @@ -275,6 +282,15 @@ void FractoriumEmberController::VariationSpinBoxValueChanged(double d)//Would void Fractorium::OnVariationSpinBoxValueChanged(double d) { m_Controller->VariationSpinBoxValueChanged(d); } +/// +/// Fill in the variations tree with the values from the current xform. +/// +template +void FractoriumEmberController::FillVariationTreeWithCurrentXform() +{ + FillVariationTreeWithXform(CurrentXform()); +} + /// /// Fill the variation tree values from passed in xform and apply the current sorting mode. /// Called when the currently selected xform changes. @@ -300,9 +316,9 @@ void FractoriumEmberController::FillVariationTreeWithXform(Xform* xform) item->setHidden(false); spinBox->SetValueStealth(var ? var->m_Weight : 0);//If the variation was present, set the spin box to its weight, else zero. -// item->setBackgroundColor(0, var ? Qt::darkGray : Qt::lightGray);//Ensure background is always white if the value goes to zero, else gray if var present. -// item->setBackgroundColor(0, var ? QColor(200, 200, 200) : QColor(255, 255, 255));//Ensure background is always white if the value goes to zero, else gray if var present. - item->setBackgroundColor(0, var ? m_Fractorium->m_VariationTreeBgColorNoneZero : m_Fractorium->m_VariationTreeBgColorZero); + item->setTextColor(0, var ? m_Fractorium->m_VariationTreeColorNonZero : m_Fractorium->m_VariationTreeColorZero); + item->setBackgroundColor(0, var ? m_Fractorium->m_VariationTreeBgColorNonZero : m_Fractorium->m_VariationTreeBgColorZero); + for (int j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params if it was a parametric variation. { T* param = nullptr; diff --git a/Source/Fractorium/GLWidget.cpp b/Source/Fractorium/GLWidget.cpp index 6398382..0a0b663 100644 --- a/Source/Fractorium/GLWidget.cpp +++ b/Source/Fractorium/GLWidget.cpp @@ -685,7 +685,7 @@ void GLEmberController::DrawAffines(bool pre, bool post) GLfloat(m_DragHandlePos.x), GLfloat(m_DragHandlePos.y) }; QVector4D col(1.0f, 1.0f, 0.5f, 1.0f); - m_GL->DrawPointOrLine(col, vertices, 1, GL_POINTS, 6.0f); + m_GL->DrawPointOrLine(col, vertices, 1, GL_POINTS, false, 6.0f); #endif } else if (m_DragState == eDragState::DragSelect) @@ -733,7 +733,7 @@ void GLEmberController::DrawAffines(bool pre, bool post) GLfloat(m_HoverHandlePos.x), GLfloat(m_HoverHandlePos.y) }; QVector4D col(0.5f, 1.0f, 1.0f, 1.0f); - m_GL->DrawPointOrLine(col, vertices, 1, GL_POINTS, 6.0f); + m_GL->DrawPointOrLine(col, vertices, 1, GL_POINTS, false, 6.0f); #endif } } @@ -1091,10 +1091,11 @@ void GLWidget::wheelEvent(QWheelEvent* e) /// The color to draw with /// The vertices to use /// The type of primitive to draw, such as GL_POINT or GL_LINES +/// True to draw dashed lines, else solid /// The size in pixels of points, which is internally scaled by the device pixel ratio. -void GLWidget::DrawPointOrLine(const QVector4D& col, const std::vector& vertices, int drawType, GLfloat pointSize) +void GLWidget::DrawPointOrLine(const QVector4D& col, const std::vector& vertices, int drawType, bool dashed, GLfloat pointSize) { - DrawPointOrLine(col, vertices.data(), int(vertices.size() / 2), drawType, pointSize); + DrawPointOrLine(col, vertices.data(), int(vertices.size() / 2), drawType, dashed, pointSize); } /// @@ -1104,10 +1105,18 @@ void GLWidget::DrawPointOrLine(const QVector4D& col, const std::vector& v /// The vertices to use /// The number of verticies. This is usually the size of vertices / 2. /// The type of primitive to draw, such as GL_POINT or GL_LINES +/// True to draw dashed lines, else solid /// The size in pixels of points, which is internally scaled by the device pixel ratio. -void GLWidget::DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, int size, int drawType, GLfloat pointSize) +void GLWidget::DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, int size, int drawType, bool dashed, GLfloat pointSize) { #ifdef USE_GLSL + + if (dashed && drawType == GL_LINES) + { + glLineStipple(1, 0XFF00); + glEnable(GL_LINE_STIPPLE); + } + m_ModelViewProjectionMatrix = m_ProjMatrix * m_ModelViewMatrix; m_Program->setUniformValue(m_ColAttr, col); m_Program->setUniformValue(m_PointSizeUniform, pointSize * GLfloat(devicePixelRatioF())); @@ -1116,6 +1125,10 @@ void GLWidget::DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, in this->glEnableVertexAttribArray(0); this->glDrawArrays(drawType, 0, size); this->glDisableVertexAttribArray(0); + + if (dashed && drawType == GL_LINES) + glDisable(GL_LINE_STIPPLE); + #endif } @@ -1509,7 +1522,7 @@ void GLEmberController::DrawAffine(Xform* xform, bool pre, bool selected) m_Verts.push_back(0.0f); m_Verts.push_back(0.0f); m_Verts.push_back(1.0f); - m_GL->DrawPointOrLine(col, m_Verts, GL_POINTS, 5.0f);//Three black points, one in the center and two on the circle. Drawn big 5px first to give a black outline. + m_GL->DrawPointOrLine(col, m_Verts, GL_POINTS, !pre, 5.0f);//Three black points, one in the center and two on the circle. Drawn big 5px first to give a black outline. // m_GL->glLineWidth(2.0f * m_GL->devicePixelRatioF());//Draw lines again for y axis only, without drawing the circle, using the color of the selected xform. m_Verts.clear(); @@ -1518,7 +1531,7 @@ void GLEmberController::DrawAffine(Xform* xform, bool pre, bool selected) m_Verts.push_back(0.0f); m_Verts.push_back(1.0f); col = QVector4D(color.r, color.g, color.b, 1.0f); - m_GL->DrawPointOrLine(col, m_Verts, GL_LINES); + m_GL->DrawPointOrLine(col, m_Verts, GL_LINES, !pre); // m_Verts.clear(); m_Verts.push_back(0.0f); @@ -1528,7 +1541,7 @@ void GLEmberController::DrawAffine(Xform* xform, bool pre, bool selected) m_Verts.push_back(0.0f); m_Verts.push_back(1.0f); col = QVector4D(1.0f, 1.0f, 1.0f, selected ? 1.0f : 0.5f); - m_GL->DrawPointOrLine(col, m_Verts, GL_POINTS, 3.0f);//Draw smaller white points, to give a black outline effect. + m_GL->DrawPointOrLine(col, m_Verts, GL_POINTS, false, 3.0f);//Draw smaller white points, to give a black outline effect. m_GL->m_ModelViewMatrix.setToIdentity(); #endif } @@ -1628,7 +1641,7 @@ void GLWidget::DrawAffineHelper(int index, bool selected, bool pre, bool final, } } - DrawPointOrLine(color, m_Verts, GL_LINES); + DrawPointOrLine(color, m_Verts, GL_LINES, !pre); //Lines from center to circle. if (!background) @@ -1649,7 +1662,7 @@ void GLWidget::DrawAffineHelper(int index, bool selected, bool pre, bool final, m_Verts.push_back(0); m_Verts.push_back(1); m_Verts.push_back(0); - DrawPointOrLine(color, m_Verts, GL_LINES); + DrawPointOrLine(color, m_Verts, GL_LINES, !pre); if (background) color = QVector4D(0.0f, 0.0f, 0.0f, 1.0f); @@ -1659,7 +1672,7 @@ void GLWidget::DrawAffineHelper(int index, bool selected, bool pre, bool final, m_Verts.push_back(0); m_Verts.push_back(0); m_Verts.push_back(1); - DrawPointOrLine(color, m_Verts, GL_LINES); + DrawPointOrLine(color, m_Verts, GL_LINES, !pre); #endif } diff --git a/Source/Fractorium/GLWidget.h b/Source/Fractorium/GLWidget.h index d8d6009..b9157c9 100644 --- a/Source/Fractorium/GLWidget.h +++ b/Source/Fractorium/GLWidget.h @@ -66,8 +66,8 @@ protected: virtual void mouseMoveEvent(QMouseEvent* e) override; virtual void wheelEvent(QWheelEvent* e) override; - void DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, int size, int drawType, GLfloat pointSize = 1.0f); - void DrawPointOrLine(const QVector4D& col, const std::vector& vertices, int drawType, GLfloat pointSize = 1.0f); + void DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, int size, int drawType, bool dashed = false, GLfloat pointSize = 1.0f); + void DrawPointOrLine(const QVector4D& col, const std::vector& vertices, int drawType, bool dashed = false, GLfloat pointSize = 1.0f); private: void SetDimensions(int w, int h); diff --git a/Source/Fractorium/PaletteEditor.ui b/Source/Fractorium/PaletteEditor.ui index bc27a3a..bf4ea7c 100644 --- a/Source/Fractorium/PaletteEditor.ui +++ b/Source/Fractorium/PaletteEditor.ui @@ -70,11 +70,11 @@ 16777215 - - - true - - + + + true + + QFrame::Panel @@ -213,17 +213,17 @@ - - - - Qt::LeftToRight - + + - Sync + Auto Distribute + + + true - + @@ -251,10 +251,20 @@ - - + + + + Qt::LeftToRight + - Auto Distribute + Sync + + + + + + + Blend true diff --git a/Source/Fractorium/PaletteEditor/GradientColorsView.cpp b/Source/Fractorium/PaletteEditor/GradientColorsView.cpp index e30fea6..807cd3a 100644 --- a/Source/Fractorium/PaletteEditor/GradientColorsView.cpp +++ b/Source/Fractorium/PaletteEditor/GradientColorsView.cpp @@ -36,6 +36,24 @@ GradientColorsView::GradientColorsView(QWidget* p) ResetToDefault(); } +/// +/// Get whether to interpolate color keys. +/// +/// True to interpolate (blend), false to do hard cuts. +bool GradientColorsView::Blend() +{ + return m_Blend; +} + +/// +/// Set whether to interpolate color keys. +/// +/// rue to interpolate (blend), false to do hard cuts. +void GradientColorsView::Blend(bool blend) +{ + m_Blend = blend; +} + /// /// Set the focus to the arrow at the given normalized position. /// @@ -374,23 +392,37 @@ Palette& GradientColorsView::GetPalette(int size) { if (!m_Arrows.empty()) { - QPainter p; QSize imageSize(size, 1); QImage image(imageSize, QImage::Format_ARGB32_Premultiplied); - QLinearGradient grad(QPoint(0, 0), QPoint(imageSize.width(), imageSize.height())); m_Palette.m_SourceColors.clear(); + QPainter painter(&image); + float start = 0; - for (auto& it : m_Arrows) + if (Blend()) { - auto pos = it.first; - auto col = it.second.Color(); - m_Palette.m_SourceColors[pos] = v4F(col.red() / 255.0f, col.green() / 255.0f, col.blue() / 255.0f, 1.0f); - grad.setColorAt(pos, col); + QLinearGradient grad(QPoint(0, 0), QPoint(imageSize.width(), imageSize.height())); + + for (auto& it : m_Arrows) + { + auto col = it.second.Color(); + m_Palette.m_SourceColors[it.first] = v4F(col.red() / 255.0f, col.green() / 255.0f, col.blue() / 255.0f, 1.0f); + grad.setColorAt(it.first, col); + } + + painter.fillRect(image.rect(), grad); + } + else + { + for (auto& it : m_Arrows) + { + auto col = it.second.Color(); + m_Palette.m_SourceColors[it.first] = v4F(col.red() / 255.0f, col.green() / 255.0f, col.blue() / 255.0f, 1.0f); + painter.setBrush(col); + painter.fillRect(start, 0, imageSize.width(), imageSize.height(), col); + start = std::ceil(it.first * imageSize.width()); + } } - p.begin(&image); - p.fillRect(image.rect(), grad); - p.end(); m_Palette.m_Entries.reserve(image.width()); for (int i = 0; i < image.width(); i++) @@ -493,13 +525,25 @@ void GradientColorsView::paintEvent(QPaintEvent*) QPoint gradStart = QPoint(m_ViewRect.topLeft().x(), m_ViewRect.bottomLeft().y() / 2); QPoint gradStop = QPoint(m_ViewRect.topRight().x(), m_ViewRect.bottomRight().y() / 2); QLinearGradient grad(gradStart, gradStop); + float start = m_ViewRect.x(); for (auto& it : m_Arrows) { GradientArrow& arrow = it.second; - grad.setColorAt(it.first, arrow.Color()); + auto offset = std::ceil(it.first * RectWidth()); + + if (Blend()) + { + grad.setColorAt(it.first, arrow.Color()); + } + else + { + painter.fillRect(start, m_ViewRect.y(), m_ViewRect.right() - start, m_ViewRect.height(), arrow.Color()); + start = m_ViewRect.x() + offset; + } + QPolygon arrowPolygon = arrow.Area(); - int iPosX = it.first * RectWidth(), + int iPosX = offset, iPosY = m_ViewRect.height() + m_ViewRect.top() + 3; arrowPolygon.translate(iPosX, iPosY); QPainterPath paintPath; @@ -513,8 +557,11 @@ void GradientColorsView::paintEvent(QPaintEvent*) painter.setBrush(QBrush(Qt::NoBrush)); } - QBrush brush(grad); - painter.fillRect(m_ViewRect, brush); + if (Blend()) + { + painter.fillRect(m_ViewRect, grad); + } + painter.drawRect(m_ViewRect); } else @@ -534,8 +581,6 @@ void GradientColorsView::paintEvent(QPaintEvent*) //Draw text inside of the arrow. painter.drawText(topArrowRect.x() + (topArrowRect.width() - (topArrow.Width() - 5)) / 2.0, topArrowRect.y() + (topArrowRect.height() - 5), topArrow.Text()); } - - painter.end(); } /// diff --git a/Source/Fractorium/PaletteEditor/GradientColorsView.h b/Source/Fractorium/PaletteEditor/GradientColorsView.h index 576f3bd..2695379 100644 --- a/Source/Fractorium/PaletteEditor/GradientColorsView.h +++ b/Source/Fractorium/PaletteEditor/GradientColorsView.h @@ -35,6 +35,8 @@ class GradientColorsView : public QWidget public: explicit GradientColorsView(QWidget* p = nullptr); + bool Blend(); + void Blend(bool blend); void SetFocus(float position); void SetFocus(size_t position); void SetFocusColor(const QColor& col); @@ -74,6 +76,7 @@ private: int RectHeight(); bool m_ArrowMoving = false; bool m_ColorIndexArrowMoving = false; + bool m_Blend = true; QPoint m_ViewRectSize; QPoint m_ViewRectOffset = QPoint(5, 15); QPoint m_ViewRectTranslate = QPoint(5, 5); diff --git a/Source/Fractorium/PaletteEditor/PaletteEditor.cpp b/Source/Fractorium/PaletteEditor/PaletteEditor.cpp index 28569ca..0a430d1 100644 --- a/Source/Fractorium/PaletteEditor/PaletteEditor.cpp +++ b/Source/Fractorium/PaletteEditor/PaletteEditor.cpp @@ -34,9 +34,10 @@ PaletteEditor::PaletteEditor(QWidget* p) : connect(ui->RemoveColorButton, SIGNAL(clicked()), this, SLOT(OnRemoveColorButtonClicked())); connect(ui->InvertColorsButton, SIGNAL(clicked()), this, SLOT(OnInvertColorsButtonClicked())); connect(ui->ResetColorsButton, SIGNAL(clicked()), this, SLOT(OnResetToDefaultButtonClicked())); - connect(ui->DistributeColorsButton, SIGNAL(clicked()), this, SLOT(OnDistributeColorsButtonClicked())); connect(ui->RandomColorsButton, SIGNAL(clicked()), this, SLOT(OnRandomColorsButtonClicked())); + connect(ui->DistributeColorsButton, SIGNAL(clicked()), this, SLOT(OnDistributeColorsButtonClicked())); connect(ui->SyncCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSyncCheckBoxStateChanged(int)), Qt::QueuedConnection); + connect(ui->BlendCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnBlendCheckBoxStateChanged(int)), Qt::QueuedConnection); connect(ui->PaletteFilenameCombo, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(OnPaletteFilenameComboChanged(const QString&)), Qt::QueuedConnection); connect(ui->PaletteListTable, SIGNAL(cellClicked(int, int)), this, SLOT(OnPaletteCellClicked(int, int)), Qt::QueuedConnection); connect(ui->PaletteListTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnPaletteCellChanged(int, int)), Qt::QueuedConnection); @@ -290,6 +291,18 @@ void PaletteEditor::OnSyncCheckBoxStateChanged(int state) EmitColorIndexChanged(std::numeric_limits::max(), 0);//Pass special value to update all. } +/// +/// Change whether palette colors are blended between points, or instead do hard cuts. +/// Called when the Blend checkbox is checked/unchecked. +/// +/// Ignored +void PaletteEditor::OnBlendCheckBoxStateChanged(int state) +{ + m_GradientColorView->Blend((bool)state); + m_GradientColorView->update(); + EmitPaletteChanged(); +} + /// /// Load the palette file based on the currently selected index in the combo box. /// Called when the index of the palette filename combo box changes. @@ -607,6 +620,7 @@ void PaletteEditor::EnablePaletteControls() ui->AddColorButton->setEnabled(b); ui->DistributeColorsButton->setEnabled(b); ui->AutoDistributeCheckBox->setEnabled(b); + ui->BlendCheckBox->setEnabled(b); ui->RandomColorsButton->setEnabled(b); ui->RemoveColorButton->setEnabled(b); ui->ResetColorsButton->setEnabled(b); diff --git a/Source/Fractorium/PaletteEditor/PaletteEditor.h b/Source/Fractorium/PaletteEditor/PaletteEditor.h index 7835330..4893e30 100644 --- a/Source/Fractorium/PaletteEditor/PaletteEditor.h +++ b/Source/Fractorium/PaletteEditor/PaletteEditor.h @@ -54,6 +54,7 @@ private Q_SLOTS: void OnColorPickerColorChanged(const QColor& col); void OnArrowDoubleClicked(const GradientArrow& arrow); void OnSyncCheckBoxStateChanged(int state); + void OnBlendCheckBoxStateChanged(int state); void OnArrowMoved(qreal lastPos, const GradientArrow& arrow); void OnColorIndexMove(size_t index, float value); void OnPaletteFilenameComboChanged(const QString& text); diff --git a/Source/Fractorium/QssDialog.cpp b/Source/Fractorium/QssDialog.cpp index aea59ff..01ba933 100644 --- a/Source/Fractorium/QssDialog.cpp +++ b/Source/Fractorium/QssDialog.cpp @@ -315,6 +315,7 @@ void QssDialog::reject() m_Parent->m_Settings->Theme(m_LastTheme->objectName()); } + m_Parent->m_Controller->FillVariationTreeWithCurrentXform(); QDialog::reject(); } @@ -466,6 +467,7 @@ void QssDialog::SlotApplyCss() label->setText(tr("Valid Style Sheet")); label->setStyleSheet(QStringLiteral("color: green")); m_Parent->setStyleSheet(style); + m_Parent->m_Controller->FillVariationTreeWithCurrentXform(); } else { diff --git a/Source/Fractorium/SpinBox.cpp b/Source/Fractorium/SpinBox.cpp index 9af98d1..9f1eff2 100644 --- a/Source/Fractorium/SpinBox.cpp +++ b/Source/Fractorium/SpinBox.cpp @@ -161,9 +161,12 @@ void SpinBox::OnTimeout() /// false bool SpinBox::eventFilter(QObject* o, QEvent* e) { + if (!isEnabled()) + return QSpinBox::eventFilter(o, e); + auto me = dynamic_cast(e); - if (isEnabled() && me) + if (me) { if (!m_Settings->ToggleType() &&//Ensure double click toggles, not right click. me->type() == QMouseEvent::MouseButtonPress && @@ -222,6 +225,47 @@ bool SpinBox::eventFilter(QObject* o, QEvent* e) return QSpinBox::eventFilter(o, e); } +/// +/// Override which is for handling specific key presses while this control is focused. +/// In particular, + = and up arrow increase the value, equivalent to scrolling the mouse wheel up, while also observing shift/ctrl modifiers. +/// Values are decreased in the same way by pressing - _ or down arrow. +/// +/// The key event +void SpinBox::keyPressEvent(QKeyEvent* ke) +{ + bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier); + bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier); + + if (ke->key() == Qt::Key_Plus || ke->key() == Qt::Key_Equal || ke->key() == Qt::Key_Up) + { + if (shift) + { + setSingleStep(m_SmallStep); + setValue(value() + m_SmallStep); + } + else + { + setSingleStep(m_Step); + setValue(value() + (ctrl ? m_Step * 10 : m_Step)); + } + } + else if (ke->key() == Qt::Key_Minus || ke->key() == Qt::Key_Underscore || ke->key() == Qt::Key_Down) + { + if (shift) + { + setSingleStep(m_SmallStep); + setValue(value() - m_SmallStep); + } + else + { + setSingleStep(m_Step); + setValue(value() - (ctrl ? m_Step * 10 : m_Step)); + } + } + else + QSpinBox::keyPressEvent(ke); +} + /// /// Called when focus enters the spinner. /// diff --git a/Source/Fractorium/SpinBox.h b/Source/Fractorium/SpinBox.h index d62d973..63ef7d2 100644 --- a/Source/Fractorium/SpinBox.h +++ b/Source/Fractorium/SpinBox.h @@ -38,11 +38,12 @@ public slots: void OnTimeout(); protected: - bool eventFilter(QObject* o, QEvent* e); - virtual void focusInEvent(QFocusEvent* e); - virtual void focusOutEvent(QFocusEvent* e); - virtual void enterEvent(QEvent* e); - virtual void leaveEvent(QEvent* e); + virtual bool eventFilter(QObject* o, QEvent* e) override; + virtual void keyPressEvent(QKeyEvent* event) override; + virtual void focusInEvent(QFocusEvent* e) override; + virtual void focusOutEvent(QFocusEvent* e) override; + virtual void enterEvent(QEvent* e) override; + virtual void leaveEvent(QEvent* e) override; private: void StartTimer(); diff --git a/Source/Fractorium/TwoButtonComboWidget.h b/Source/Fractorium/TwoButtonComboWidget.h index 86ec269..4d46536 100644 --- a/Source/Fractorium/TwoButtonComboWidget.h +++ b/Source/Fractorium/TwoButtonComboWidget.h @@ -34,7 +34,6 @@ public: m_Button1 = new QPushButton(caption1, p); m_Button2 = new QPushButton(caption2, p); m_Combo = new QComboBox(p); - m_Combo->addItems(comboStrings); if (w1 != -1) @@ -55,14 +54,12 @@ public: m_Button2->setMaximumHeight(h); m_Combo->setMinimumHeight(h - 3); m_Combo->setMaximumHeight(h - 3); - l->addWidget(m_Combo); l->addWidget(m_Button1); l->addWidget(m_Button2); l->setAlignment(Qt::AlignLeft); l->setMargin(0); l->setSpacing(2); - setLayout(l); } @@ -105,13 +102,11 @@ public: m_Button->setMinimumHeight(h); m_Button->setMaximumHeight(h); - l->addWidget(spinBox); l->addWidget(m_Button); l->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); l->setMargin(0); l->setSpacing(0); - setLayout(l); } @@ -125,8 +120,8 @@ class SpinnerLabelButtonWidget : public QWidget public: /// - /// Constructor that passes the parent to the base, then creates a QLabel, - /// then creates a QPushButton and sets up its caption and dimensions, then + /// Constructor that passes the parent to the base, then creates a QLabel, + /// then creates a QPushButton and sets up its caption and dimensions, then /// assigns the DoubleSpinBox. /// /// The pre-created DoubleSpinBox @@ -139,12 +134,12 @@ public: { QHBoxLayout* l = new QHBoxLayout(this); m_Button = new QPushButton(buttonCaption, p); - m_SpinBox = spinBox; + m_SpinBox = spinBox; m_Label = new QLabel(p); - m_Label->setMinimumHeight(h); - m_Label->setMaximumHeight(h); - - if (w != -1) + m_Label->setMinimumHeight(h); + m_Label->setMaximumHeight(h); + + if (w != -1) { m_Button->setMinimumWidth(w); m_Button->setMaximumWidth(w); @@ -152,9 +147,8 @@ public: m_Button->setMinimumHeight(h); m_Button->setMaximumHeight(h); - l->addWidget(spinBox); - l->addWidget(m_Label); + l->addWidget(m_Label); l->addWidget(m_Button); l->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); l->setMargin(0); @@ -164,5 +158,5 @@ public: DoubleSpinBox* m_SpinBox; QPushButton* m_Button; - QLabel* m_Label; + QLabel* m_Label; }; diff --git a/Source/Fractorium/VariationsDialog.cpp b/Source/Fractorium/VariationsDialog.cpp index 2ef3496..7d39a37 100644 --- a/Source/Fractorium/VariationsDialog.cpp +++ b/Source/Fractorium/VariationsDialog.cpp @@ -194,10 +194,8 @@ void FractoriumVariationsDialog::OnSelectionCheckBoxStateChanged(int i) static vector dc{ "m_ColorX" }; static vector assign{ "outPoint->m_X =", "outPoint->m_Y =", "outPoint->m_Z =", "outPoint->m_X=", "outPoint->m_Y=", "outPoint->m_Z=" }; - //static vector*> excluded; static std::set excluded; excluded.clear(); - //excluded.reserve(size_t(eVariationId::LAST_VAR)); if (auto s = dynamic_cast(sender())) {