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