1 
2 
3 #include "toonzqt/intfield.h"
4 #include "toonzqt/dvdialog.h"
5 #include "toonzqt/gutil.h"
6 
7 #include <QIntValidator>
8 #include <QSlider>
9 #include <QHBoxLayout>
10 #include <QAction>
11 #include <QFocusEvent>
12 #include <QPainter>
13 
14 namespace {
15 const int NonLinearSliderPrecision = 2;
16 }
17 
18 using namespace DVGui;
19 
20 //=============================================================================
21 // RollerField
22 //-----------------------------------------------------------------------------
23 
RollerField(QWidget * parent)24 RollerField::RollerField(QWidget *parent)
25     : QWidget(parent)
26     , m_value(0)
27     , m_minValue(-100000.0)
28     , m_maxValue(100000.0)
29     , m_xPos(0)
30     , m_step(1.0) {
31   setMinimumSize(43, 7);
32 }
33 
34 //-----------------------------------------------------------------------------
35 
setValue(double value)36 void RollerField::setValue(double value) {
37   if (m_value == value) return;
38   if (value < m_minValue) m_value = m_minValue;
39   if (value > m_maxValue) m_value = m_maxValue;
40 
41   m_value = value;
42 }
43 
44 //-----------------------------------------------------------------------------
45 
getValue() const46 double RollerField::getValue() const { return m_value; }
47 
48 //-----------------------------------------------------------------------------
49 
setRange(double minValue,double maxValue)50 void RollerField::setRange(double minValue, double maxValue) {
51   m_minValue = minValue;
52   m_maxValue = maxValue;
53 }
54 
55 //-----------------------------------------------------------------------------
56 
getRange(double & minValue,double & maxValue)57 void RollerField::getRange(double &minValue, double &maxValue) {
58   minValue = m_minValue;
59   maxValue = m_maxValue;
60 }
61 
62 //-----------------------------------------------------------------------------
63 
paintEvent(QPaintEvent * e)64 void RollerField::paintEvent(QPaintEvent *e) {
65   QPainter p(this);
66 
67   int w = width();
68 
69   drawArrow(p, QPointF(3, 3), QPointF(5, 5), QPointF(5, 1), true, Qt::black,
70             Qt::black);
71   drawArrow(p, QPointF(w - 4, 3), QPointF(w - 6, 5), QPointF(w - 6, 1), true,
72             Qt::black, Qt::black);
73 
74   p.drawLine(QPoint(3, 3), QPoint(w - 4, 3));
75 }
76 
77 //-----------------------------------------------------------------------------
78 
mousePressEvent(QMouseEvent * e)79 void RollerField::mousePressEvent(QMouseEvent *e) {
80   if (e->buttons() == Qt::LeftButton) {
81     m_xPos = e->pos().x();
82     e->accept();
83   }
84 }
85 
86 //-----------------------------------------------------------------------------
87 
mouseMoveEvent(QMouseEvent * e)88 void RollerField::mouseMoveEvent(QMouseEvent *e) {
89   if (e->buttons() == Qt::LeftButton) {
90     if (m_xPos < e->pos().x())
91       addValue(true);
92     else if (m_xPos > e->pos().x())
93       removeValue(true);
94     m_xPos = e->pos().x();
95     e->accept();
96   }
97 }
98 
99 //-----------------------------------------------------------------------------
100 
mouseReleaseEvent(QMouseEvent * e)101 void RollerField::mouseReleaseEvent(QMouseEvent *e) {
102   e->accept();
103   emit valueChanged(false);
104 }
105 
106 //-----------------------------------------------------------------------------
107 
addValue(bool isDragging)108 void RollerField::addValue(bool isDragging) {
109   double newValue = tcrop(m_value + m_step, m_minValue, m_maxValue);
110   if (newValue == m_value) return;
111   m_value = newValue;
112   emit valueChanged(isDragging);
113 }
114 
115 //-----------------------------------------------------------------------------
116 
removeValue(bool isDragging)117 void RollerField::removeValue(bool isDragging) {
118   double newValue = tcrop(m_value - m_step, m_minValue, m_maxValue);
119   if (newValue == m_value) return;
120   m_value = newValue;
121   emit valueChanged(isDragging);
122 }
123 
124 //=============================================================================
125 // IntLineEdit
126 //-----------------------------------------------------------------------------
127 
IntLineEdit(QWidget * parent,int value,int minValue,int maxValue,int showedDigits)128 IntLineEdit::IntLineEdit(QWidget *parent, int value, int minValue, int maxValue,
129                          int showedDigits)
130     : LineEdit(parent), m_showedDigits(showedDigits) {
131   setFixedWidth(54);
132 
133   m_validator = new QIntValidator(this);
134   setValue(value);
135   setRange(minValue, maxValue);
136   setValidator(m_validator);
137 }
138 
139 //-----------------------------------------------------------------------------
140 
setValue(int value)141 void IntLineEdit::setValue(int value) {
142   int minValue, maxValue;
143   getRange(minValue, maxValue);
144   if (value < minValue) value = minValue;
145   if (value > maxValue) value = maxValue;
146   QString str;
147   str.setNum(value);
148   if (m_showedDigits > 0) {
149     while (str.length() < m_showedDigits) str.push_front("0");
150     while (str.length() > m_showedDigits) str.remove(0, 1);
151   }
152   setText(str);
153 
154   // Faccio in modo che il cursore sia sulla prima cifra, cosi' se la stringa da
155   // visualizzare
156   // e' piu' lunga del campo le cifre che vengono troncate sono le ultime e non
157   // le prime.
158   setCursorPosition(0);
159 }
160 
161 //-----------------------------------------------------------------------------
162 
getValue()163 int IntLineEdit::getValue() { return text().toInt(); }
164 
165 //-----------------------------------------------------------------------------
166 
setRange(int minValue,int maxValue)167 void IntLineEdit::setRange(int minValue, int maxValue) {
168   m_validator->setRange(minValue, maxValue);
169 }
170 
171 //-----------------------------------------------------------------------------
172 
getRange(int & minValue,int & maxValue)173 void IntLineEdit::getRange(int &minValue, int &maxValue) {
174   minValue = m_validator->bottom();
175   maxValue = m_validator->top();
176 }
177 
178 //-----------------------------------------------------------------------------
179 
setBottomRange(int minValue)180 void IntLineEdit::setBottomRange(int minValue) {
181   m_validator->setBottom(minValue);
182 }
183 
184 //-----------------------------------------------------------------------------
185 
setTopRange(int maxValue)186 void IntLineEdit::setTopRange(int maxValue) { m_validator->setTop(maxValue); }
187 
188 //-----------------------------------------------------------------------------
189 
focusOutEvent(QFocusEvent * e)190 void IntLineEdit::focusOutEvent(QFocusEvent *e) {
191   int value = getValue();
192   int minValue, maxValue;
193   getRange(minValue, maxValue);
194 
195   if (e->lostFocus()) setValue(value);
196 
197   QLineEdit::focusOutEvent(e);
198   m_isTyping = false;
199 }
200 
201 //-----------------------------------------------------------------------------
202 
203 // for fps edit in flip console
setLineEditBackgroundColor(QColor color)204 void IntLineEdit::setLineEditBackgroundColor(QColor color) {
205   // Set text color based on luminescence of bg color
206   int value           = 0;
207   double luminescence = ((0.299 * color.red()) + (0.587 * color.green()) +
208                          (0.114 * color.blue())) /
209                         255;
210   if (luminescence > 0.5)
211     value = 0;  // black
212   else
213     value = 255;  // white
214 
215   QString sheet =
216       QString("background-color: rgb(") + QString::number(color.red()) +
217       QString(",") + QString::number(color.green()) + QString(",") +
218       QString::number(color.blue()) + QString(",") +
219       QString::number(color.alpha()) +
220       QString(");" +
221               QString("color: rgb(" + QString::number(value) + QString(",") +
222                       QString::number(value) + QString(",") +
223                       QString::number(value) + QString(");")));
224   setStyleSheet(sheet);
225 }
226 
227 //-----------------------------------------------------------------------------
228 
mousePressEvent(QMouseEvent * e)229 void IntLineEdit::mousePressEvent(QMouseEvent *e) {
230   if (e->buttons() == Qt::MiddleButton) {
231     m_xMouse           = e->x();
232     m_mouseDragEditing = true;
233   } else {
234     QLineEdit::mousePressEvent(e);
235     if (!m_isTyping) {  // only the first click will select all
236       selectAll();
237       m_isTyping = true;
238     }
239   }
240 }
241 
242 //-----------------------------------------------------------------------------
243 
mouseMoveEvent(QMouseEvent * e)244 void IntLineEdit::mouseMoveEvent(QMouseEvent *e) {
245   if (e->buttons() == Qt::MiddleButton) {
246     setValue(getValue() + ((e->x() - m_xMouse) / 2));
247     m_xMouse = e->x();
248   } else
249     QLineEdit::mouseMoveEvent(e);
250 }
251 
252 //-----------------------------------------------------------------------------
253 
mouseReleaseEvent(QMouseEvent * e)254 void IntLineEdit::mouseReleaseEvent(QMouseEvent *e) {
255   if ((e->buttons() == Qt::NoButton && m_mouseDragEditing)) {
256     m_mouseDragEditing = false;
257     clearFocus();
258   } else
259     QLineEdit::mouseReleaseEvent(e);
260 }
261 
262 //=============================================================================
263 // IntField
264 //-----------------------------------------------------------------------------
265 
IntField(QWidget * parent,bool isMaxRangeLimited,bool isRollerHide)266 IntField::IntField(QWidget *parent, bool isMaxRangeLimited, bool isRollerHide)
267     : QWidget(parent)
268     , m_lineEdit(0)
269     , m_slider(0)
270     , m_roller(0)
271     , m_isMaxRangeLimited(isMaxRangeLimited)
272     , m_isLinearSlider(true) {
273   setObjectName("IntField");
274   QHBoxLayout *layout = new QHBoxLayout(this);
275   layout->setMargin(0);
276   layout->setSpacing(5);
277 
278   QWidget *field = new QWidget(this);
279   field->setMaximumWidth(43);
280   QVBoxLayout *vLayout = new QVBoxLayout(field);
281   vLayout->setMargin(0);
282   vLayout->setSpacing(0);
283 
284   m_lineEdit = new DVGui::IntLineEdit(field);
285   bool ret   = connect(m_lineEdit, SIGNAL(editingFinished()), this,
286                      SLOT(onEditingFinished()));
287   vLayout->addWidget(m_lineEdit);
288 
289   m_roller = new RollerField(field);
290   ret      = ret && connect(m_roller, SIGNAL(valueChanged(bool)), this,
291                        SLOT(onRollerValueChanged(bool)));
292   vLayout->addWidget(m_roller);
293 
294   if (isRollerHide) enableRoller(false);
295 
296   layout->addWidget(field);
297 
298   m_slider = new QSlider(Qt::Horizontal, this);
299   ret      = ret && connect(m_slider, SIGNAL(valueChanged(int)), this,
300                        SLOT(onSliderChanged(int)));
301   ret = ret && connect(m_slider, SIGNAL(sliderReleased()), this,
302                        SLOT(onSliderReleased()));
303 
304   ret = ret && connect(m_lineEdit, SIGNAL(editingFinished()), this,
305                        SIGNAL(valueEditedByHand()));
306   ret = ret && connect(m_slider, SIGNAL(sliderReleased()), this,
307                        SIGNAL(valueEditedByHand()));
308   layout->addWidget(m_slider);
309 
310   setValues(0, 0, 100);
311 
312   setLayout(layout);
313   assert(ret);
314 }
315 
316 //-----------------------------------------------------------------------------
317 
getRange(int & minValue,int & maxValue)318 void IntField::getRange(int &minValue, int &maxValue) {
319   double min, max;
320   m_roller->getRange(min, max);
321   minValue = tround(min);
322   maxValue = tround(max);
323 }
324 
325 //-----------------------------------------------------------------------------
326 
setRange(int minValue,int maxValue)327 void IntField::setRange(int minValue, int maxValue) {
328   m_lineEdit->setRange(minValue, m_isMaxRangeLimited
329                                      ? maxValue
330                                      : (std::numeric_limits<int>::max)());
331   if (m_isLinearSlider)
332     m_slider->setRange(minValue, maxValue);
333   else
334     m_slider->setRange(minValue * pow(10., NonLinearSliderPrecision),
335                        maxValue * pow(10., NonLinearSliderPrecision));
336   m_roller->setRange(minValue, maxValue);
337 }
338 
339 //-----------------------------------------------------------------------------
340 
setValue(int value)341 void IntField::setValue(int value) {
342   if (m_lineEdit->getValue() == value) return;
343   m_lineEdit->setValue(value);
344   m_slider->setSliderPosition(value2pos(value));
345   m_roller->setValue((double)value);
346 }
347 
348 //-----------------------------------------------------------------------------
349 
getValue()350 int IntField::getValue() { return (m_lineEdit->getValue()); }
351 
352 //-----------------------------------------------------------------------------
353 
setValues(int value,int minValue,int maxValue)354 void IntField::setValues(int value, int minValue, int maxValue) {
355   setRange(minValue, maxValue);
356   setValue(value);
357 }
358 
359 //-----------------------------------------------------------------------------
360 
enableSlider(bool enable)361 void IntField::enableSlider(bool enable) {
362   m_slider->setEnabled(enable);
363   if (enable)
364     m_slider->show();
365   else
366     m_slider->hide();
367 }
368 
369 //-----------------------------------------------------------------------------
370 
sliderIsEnabled()371 bool IntField::sliderIsEnabled() { return m_slider->isEnabled(); }
372 
373 //-----------------------------------------------------------------------------
374 
enableRoller(bool enable)375 void IntField::enableRoller(bool enable) {
376   m_roller->setEnabled(enable);
377   if (enable)
378     m_roller->show();
379   else
380     m_roller->hide();
381 }
382 
383 //-----------------------------------------------------------------------------
384 
rollerIsEnabled()385 bool IntField::rollerIsEnabled() { return m_roller->isEnabled(); }
386 
387 //-----------------------------------------------------------------------------
388 
setLineEditBackgroundColor(QColor color)389 void IntField::setLineEditBackgroundColor(QColor color) {
390   m_lineEdit->setLineEditBackgroundColor(color);
391 }
392 
393 //-----------------------------------------------------------------------------
394 
pos2value(int x) const395 int IntField::pos2value(int x) const {
396   if (m_isLinearSlider) return x;
397 
398   // nonlinear slider case
399   double rangeSize = (double)(m_slider->maximum() - m_slider->minimum());
400   double posRatio  = (double)(x - m_slider->minimum()) / rangeSize;
401   double t;
402   if (posRatio <= 0.5)
403     t = 0.04 * posRatio;
404   else if (posRatio <= 0.75)
405     t = -0.02 + 0.08 * posRatio;
406   else if (posRatio <= 0.9)
407     t = -0.26 + 0.4 * posRatio;
408   else
409     t              = -8.0 + 9.0 * posRatio;
410   double sliderVal = (double)m_slider->minimum() + rangeSize * t;
411   return (int)round(sliderVal * pow(0.1, NonLinearSliderPrecision));
412 }
413 
414 //-----------------------------------------------------------------------------
415 
value2pos(int v) const416 int IntField::value2pos(int v) const {
417   if (m_isLinearSlider) return v;
418 
419   // nonlinear slider case
420   double sliderVal  = (double)v * pow(10., NonLinearSliderPrecision);
421   double rangeSize  = (double)(m_slider->maximum() - m_slider->minimum());
422   double valueRatio = (double)(sliderVal - m_slider->minimum()) / rangeSize;
423   double t;
424   if (valueRatio <= 0.02)
425     t = valueRatio / 0.04;
426   else if (valueRatio <= 0.04)
427     t = (valueRatio + 0.02) / 0.08;
428   else if (valueRatio <= 0.1)
429     t = (valueRatio + 0.26) / 0.4;
430   else
431     t = (valueRatio + 8.0) / 9.0;
432   return m_slider->minimum() + (int)(t * rangeSize);
433 }
434 
435 //-----------------------------------------------------------------------------
436 
onSliderChanged(int sliderPos)437 void IntField::onSliderChanged(int sliderPos) {
438   int value = pos2value(sliderPos);
439   // Controllo necessario per evitare che il segnale di cambiamento venga emesso
440   // piu' volte.
441   if (m_lineEdit->getValue() == value ||
442       ((int)m_roller->getValue() == value && m_roller->isVisible()))
443     return;
444   m_lineEdit->setValue(value);
445   m_roller->setValue((double)value);
446   // Faccio in modo che il cursore sia sulla prima cifra, cosi' se la stringa
447   // da visualizzare e' piu' lunga del campo le cifre che vengono troncate sono
448   // le ultime e non le prime (dovrebbero essere quelle dopo la virgola).
449   m_lineEdit->setCursorPosition(0);
450   emit valueChanged(true);
451 }
452 
453 //-----------------------------------------------------------------------------
454 
onEditingFinished()455 void IntField::onEditingFinished() {
456   double value = m_lineEdit->getValue();
457   // Controllo necessario per evitare che il segnale di cambiamento venga emesso
458   // piu' volte.
459   if ((pos2value(m_slider->value()) == value && m_slider->isVisible()) ||
460       ((int)m_roller->getValue() == value && m_roller->isVisible()))
461     return;
462   m_slider->setValue(value2pos(value));
463   m_roller->setValue((double)value);
464   emit valueChanged(false);
465 }
466 
467 //-----------------------------------------------------------------------------
468 
onRollerValueChanged(bool isDragging)469 void IntField::onRollerValueChanged(bool isDragging) {
470   int value = m_roller->getValue();
471   if (value == m_lineEdit->getValue()) {
472     assert(pos2value(m_slider->value()) == value || !m_slider->isVisible());
473     // Se isDragging e' falso e' giusto che venga emessa la notifica di
474     // cambiamento.
475     if (!isDragging) emit valueChanged(isDragging);
476     return;
477   }
478   m_slider->setValue(value2pos(value));
479   m_lineEdit->setValue(value);
480 
481   // Faccio in modo che il cursore sia sulla prima cifra, cosi' se la stringa
482   // da visualizzare e' piu' lunga del campo le cifre che vengono troncate sono
483   // le ultime e non le prime (dovrebbero essere quelle dopo la virgola).
484   m_lineEdit->setCursorPosition(0);
485 
486   emit valueChanged(isDragging);
487 }
488