1 // Copyright (C) 2012-2019 The VPaint Developers.
2 // See the COPYRIGHT file at the top-level directory of this distribution
3 // and at https://github.com/dalboris/vpaint/blob/master/COPYRIGHT
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16
17 #include "ObjectPropertiesWidget.h"
18 #include "VectorAnimationComplex/VAC.h"
19 #include "VectorAnimationComplex/Cell.h"
20 #include "VectorAnimationComplex/KeyVertex.h"
21 #include "VectorAnimationComplex/KeyEdge.h"
22 #include "VectorAnimationComplex/KeyFace.h"
23 #include "VectorAnimationComplex/InbetweenVertex.h"
24 #include "VectorAnimationComplex/InbetweenEdge.h"
25 #include "VectorAnimationComplex/InbetweenFace.h"
26 #include "Global.h"
27 #include "MainWindow.h"
28
ObjectPropertiesWidget()29 ObjectPropertiesWidget::ObjectPropertiesWidget()
30 {
31 // ---- Type ----
32 QLabel * typeLabel = new QLabel(tr("Type:"));
33 type_ = new QLabel(tr("no objects"));
34 QHBoxLayout * typeLayout = new QHBoxLayout();
35 typeLayout->addWidget(typeLabel);
36 typeLayout->setAlignment(typeLabel, Qt::AlignTop);
37 typeLayout->addWidget(type_);
38 typeLayout->addStretch();
39
40 // ---- ID ----
41 moreText_ = tr("more");
42 lessText_ = tr("less");
43 QLabel * idLabel = new QLabel(tr("ID:"));
44 id_ = new QLabel("");
45 idMoreLessButton_ = new QPushButton(moreText_);
46 idMoreLessButton_->setMaximumHeight(15);
47 connect(idMoreLessButton_, SIGNAL(clicked()), this, SLOT(idMoreLessSlot()));
48 QHBoxLayout * idLayout = new QHBoxLayout();
49 idLayout->addWidget(idLabel);
50 idLayout->setAlignment(idLabel, Qt::AlignTop);
51 idLayout->addWidget(id_);
52 idLayout->setAlignment(id_, Qt::AlignTop);
53 idLayout->addWidget(idMoreLessButton_);
54 idLayout->setAlignment(idMoreLessButton_, Qt::AlignTop);
55 idLayout->addStretch();
56
57 // -- inbetween closed edge --
58 inbetweenClosedEdgeWidgets_ = new QWidget();
59 inbetweenClosedEdgeBeforeCycleSlider_ = new QSlider(Qt::Horizontal);
60 inbetweenClosedEdgeBeforeCycleSlider_->setRange(0,100);
61 connect(inbetweenClosedEdgeBeforeCycleSlider_, SIGNAL(sliderMoved(int)), this, SLOT(updateBeforeCycleStartingPoint(int)));
62 connect(inbetweenClosedEdgeBeforeCycleSlider_, SIGNAL(sliderReleased()), this, SLOT(sliderStartingPointReleased()));
63 inbetweenClosedEdgeAfterCycleSlider_ = new QSlider(Qt::Horizontal);
64 inbetweenClosedEdgeAfterCycleSlider_->setRange(0,100);
65 connect(inbetweenClosedEdgeAfterCycleSlider_, SIGNAL(sliderMoved(int)), this, SLOT(updateAfterCycleStartingPoint(int)));
66 connect(inbetweenClosedEdgeAfterCycleSlider_, SIGNAL(sliderReleased()), this, SLOT(sliderStartingPointReleased()));
67 QGridLayout * inbetweenClosedEdgeLayout = new QGridLayout();
68 //inbetweenClosedEdgeLayout->addWidget(new QLabel("before cycle starting point:"),0,0);
69 inbetweenClosedEdgeLayout->addWidget(new QLabel("Cycle offset:"),1,0);
70 //inbetweenClosedEdgeLayout->addWidget(inbetweenClosedEdgeBeforeCycleSlider_,0,1);
71 inbetweenClosedEdgeLayout->addWidget(inbetweenClosedEdgeAfterCycleSlider_,1,1);
72 inbetweenClosedEdgeWidgets_->setLayout(inbetweenClosedEdgeLayout);
73
74 // -- animated cycle --
75 animatedCycleComboBox_ = new QComboBox();
76 animatedCycleShowHide_ = new QPushButton("Show");
77 connect(animatedCycleShowHide_, SIGNAL(clicked()), this, SLOT(toggleAnimatedCycleShowHide()));
78 animatedCycleEdit_ = new QPushButton("Edit");
79 connect(animatedCycleEdit_, SIGNAL(clicked()), this, SLOT(animatedCycleEdit()));
80 animatedCycleNew_ = new QPushButton("New");
81 connect(animatedCycleNew_, SIGNAL(clicked()), this, SLOT(animatedCycleNew()));
82 animatedCycleDelete_ = new QPushButton("Delete");
83 connect(animatedCycleDelete_, SIGNAL(clicked()), this, SLOT(animatedCycleDelete()));
84 animatedCycleWidget_ = new AnimatedCycleWidget();
85 animatedCycleWidget_->hide();
86
87 animatedCycleWidgets_ = new QWidget();
88 QWidget * hStretch = new QWidget();
89 hStretch->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
90 QHBoxLayout * animatedCycleButtonsLayout = new QHBoxLayout();
91 animatedCycleButtonsLayout->addWidget(animatedCycleComboBox_);
92 animatedCycleComboBox_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum);
93 connect(animatedCycleComboBox_, SIGNAL(currentIndexChanged(int)), this, SLOT(setAnimatedCycle(int)));
94 animatedCycleButtonsLayout->addWidget(animatedCycleShowHide_);
95 animatedCycleButtonsLayout->addWidget(animatedCycleEdit_);
96 animatedCycleButtonsLayout->addWidget(animatedCycleNew_);
97 animatedCycleButtonsLayout->addWidget(animatedCycleDelete_);
98 animatedCycleButtonsLayout->addStretch();
99 QVBoxLayout * animatedCycleWidgetsLayout = new QVBoxLayout();
100 animatedCycleWidgetsLayout->addLayout(animatedCycleButtonsLayout);
101 animatedCycleWidgetsLayout->addWidget(animatedCycleWidget_);
102 animatedCycleWidgets_->setLayout(animatedCycleWidgetsLayout);
103
104 // -- Main layout --
105 mainLayout_ = new QVBoxLayout();
106 mainLayout_->addLayout(typeLayout);
107 mainLayout_->addLayout(idLayout);
108 mainLayout_->addWidget(inbetweenClosedEdgeWidgets_);
109 mainLayout_->addWidget(animatedCycleWidgets_);
110 bottomStretch_ = new QWidget();
111 bottomStretch_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
112 mainLayout_->addWidget(bottomStretch_);
113 //mainLayout_->addStretch();
114 setLayout(mainLayout_);
115
116 // -- Set that no object is selected --
117 setObjects(VectorAnimationComplex::CellSet());
118 }
119
~ObjectPropertiesWidget()120 ObjectPropertiesWidget::~ObjectPropertiesWidget()
121 {
122
123 }
124
hideAnimatedCycleWidget()125 void ObjectPropertiesWidget::hideAnimatedCycleWidget()
126 {
127 animatedCycleWidget_->hide();
128 animatedCycleWidget_->stop();
129 animatedCycleShowHide_->setText(tr("Show"));
130 bottomStretch_->show();
131 }
132
showAnimatedCycleWidget()133 void ObjectPropertiesWidget::showAnimatedCycleWidget()
134 {
135 animatedCycleWidget_->show();
136 animatedCycleWidget_->start();
137 animatedCycleShowHide_->setText(tr("Hide"));
138 bottomStretch_->hide();
139 }
140
toggleAnimatedCycleShowHide()141 void ObjectPropertiesWidget::toggleAnimatedCycleShowHide()
142 {
143 if(animatedCycleWidget_->isVisible())
144 {
145 hideAnimatedCycleWidget();
146 }
147 else
148 {
149 showAnimatedCycleWidget();
150 }
151 }
152
animatedCycleEdit()153 void ObjectPropertiesWidget::animatedCycleEdit()
154 {
155 global()->mainWindow()->editAnimatedCycle(inbetweenFace_,animatedCycleComboBox_->currentIndex());
156 hideAnimatedCycleWidget();
157 }
158
animatedCycleNew()159 void ObjectPropertiesWidget::animatedCycleNew()
160 {
161 inbetweenFace_->addAnimatedCycle();
162 updateAnimatedCycleComboBox();
163
164 animatedCycleComboBox_->setCurrentIndex(inbetweenFace_->numAnimatedCycles()-1);
165 animatedCycleEdit();
166
167 VectorAnimationComplex::VAC * vac = inbetweenFace_->vac();
168 emit vac->needUpdatePicking();
169 emit vac->changed();
170 emit vac->checkpoint();
171 }
172
animatedCycleDelete()173 void ObjectPropertiesWidget::animatedCycleDelete()
174 {
175 int i = animatedCycleComboBox_->currentIndex();
176 int n = inbetweenFace_->numAnimatedCycles();
177 if(0<=i && i<n)
178 inbetweenFace_->removeCycle(i);
179 updateAnimatedCycleComboBox();
180
181 VectorAnimationComplex::VAC * vac = inbetweenFace_->vac();
182 emit vac->needUpdatePicking();
183 emit vac->changed();
184 emit vac->checkpoint();
185 }
186
updateBeforeCycleStartingPoint(int i)187 void ObjectPropertiesWidget::updateBeforeCycleStartingPoint(int i)
188 {
189 if(inbetweenEdge_ && inbetweenEdge_->isClosed())
190 {
191 inbetweenEdge_->setBeforeCycleStartingPoint( (double)i / 100.0);
192
193 VectorAnimationComplex::VAC * vac = inbetweenEdge_->vac();
194 emit vac->needUpdatePicking();
195 emit vac->changed();
196 }
197 }
198
updateAfterCycleStartingPoint(int i)199 void ObjectPropertiesWidget::updateAfterCycleStartingPoint(int i)
200 {
201 if(inbetweenEdge_ && inbetweenEdge_->isClosed())
202 {
203 inbetweenEdge_->setAfterCycleStartingPoint( (double)i / 100.0);
204
205 VectorAnimationComplex::VAC * vac = inbetweenEdge_->vac();
206 emit vac->needUpdatePicking();
207 emit vac->changed();
208 }
209 }
210
sliderStartingPointReleased()211 void ObjectPropertiesWidget::sliderStartingPointReleased()
212 {
213
214 if(inbetweenEdge_ && inbetweenEdge_->isClosed())
215 {
216 VectorAnimationComplex::VAC * vac = inbetweenEdge_->vac();
217 emit vac->checkpoint();
218 }
219 }
220
hideInbetweenClosedEdgeWidgets()221 void ObjectPropertiesWidget::hideInbetweenClosedEdgeWidgets()
222 {
223 inbetweenClosedEdgeWidgets_->hide();
224 bottomStretch_->show();
225 }
226
showInbetweenClosedEdgeWidgets()227 void ObjectPropertiesWidget::showInbetweenClosedEdgeWidgets()
228 {
229 inbetweenClosedEdgeWidgets_->show();
230 bottomStretch_->show();
231 }
232
hideAnimatedCycleWidgets()233 void ObjectPropertiesWidget::hideAnimatedCycleWidgets()
234 {
235 animatedCycleWidgets_->hide();
236 animatedCycleWidget_->stop();
237 animatedCycleWidget_->setAnimatedCycle(AnimatedCycle());
238 bottomStretch_->show();
239 }
240
showAnimatedCycleWidgets()241 void ObjectPropertiesWidget::showAnimatedCycleWidgets()
242 {
243 animatedCycleWidgets_->show();
244 if(animatedCycleWidget_->isVisible())
245 {
246 animatedCycleWidget_->start();
247 bottomStretch_->hide();
248 }
249 }
250
setAnimatedCycle(int i)251 void ObjectPropertiesWidget::setAnimatedCycle(int i)
252 {
253 if(i>=0 && i<inbetweenFace_->numAnimatedCycles())
254 animatedCycleWidget_->setAnimatedCycle(inbetweenFace_, i);
255 }
256
updateAnimatedCycleComboBox()257 void ObjectPropertiesWidget::updateAnimatedCycleComboBox()
258 {
259 animatedCycleComboBox_->clear();
260 for(int i=1; i<=inbetweenFace_->numAnimatedCycles(); ++i)
261 {
262 animatedCycleComboBox_->addItem(tr("Animated Cycle %1").arg(i));
263 }
264 }
265
setObject(InbetweenEdge * inbetweenEdge)266 void ObjectPropertiesWidget::setObject(InbetweenEdge * inbetweenEdge)
267 {
268 inbetweenEdge_ = inbetweenEdge;
269
270 if(inbetweenEdge_->isClosed())
271 {
272 inbetweenClosedEdgeBeforeCycleSlider_->setValue((int) (inbetweenEdge_->beforeCycleStartingPoint() * 100) );
273 inbetweenClosedEdgeAfterCycleSlider_->setValue((int) (inbetweenEdge_->afterCycleStartingPoint() * 100) );
274 showInbetweenClosedEdgeWidgets();
275 }
276 else
277 {
278
279 }
280 }
281
setObject(InbetweenFace * inbetweenFace)282 void ObjectPropertiesWidget::setObject(InbetweenFace * inbetweenFace)
283 {
284 inbetweenFace_ = inbetweenFace;
285
286 showAnimatedCycleWidgets();
287 updateAnimatedCycleComboBox();
288
289 animatedCycleComboBox_->setCurrentIndex(0);
290 setAnimatedCycle(0);
291 }
292
setObjects(const VectorAnimationComplex::CellSet & cells)293 void ObjectPropertiesWidget::setObjects(const VectorAnimationComplex::CellSet & cells)
294 {
295 // Always visible info
296 setType(getStringType(cells));
297 setId(cells);
298
299 // Hide all other selection-dependent info
300 hideInbetweenClosedEdgeWidgets();
301 hideAnimatedCycleWidgets();
302
303 // set pointers to null
304 inbetweenEdge_ = 0;
305 inbetweenFace_ = 0;
306
307 // Show selection-dependent info
308 if(cells.size() == 1)
309 {
310 Cell * cell = *cells.begin();
311 InbetweenEdge * sedge = cell->toInbetweenEdge();
312 InbetweenFace * sface = cell->toInbetweenFace();
313 if(sedge)
314 {
315 setObject(sedge);
316 }
317 else if(sface)
318 {
319 setObject(sface);
320 }
321 }
322 }
323
324
getStringType(VectorAnimationComplex::Cell * cell)325 QString ObjectPropertiesWidget::getStringType(VectorAnimationComplex::Cell * cell)
326 {
327 if(cell->toKeyVertex())
328 return tr("key vertex");
329 else if(cell->toKeyEdge())
330 return tr("key edge");
331 else if(cell->toKeyFace())
332 return tr("key face");
333 else if(cell->toInbetweenVertex())
334 return tr("inbetween vertex");
335 else if(cell->toInbetweenEdge())
336 return tr("inbetween edge");
337 else if(cell->toInbetweenFace())
338 return tr("inbetween face");
339 else
340 return tr("unknown object");
341 }
342
getStringType(const VectorAnimationComplex::CellSet & cells)343 QString ObjectPropertiesWidget::getStringType(const VectorAnimationComplex::CellSet & cells)
344 {
345 //VectorAnimationComplex::KeyVertexSet kv = cells;
346 //VectorAnimationComplex::KeyEdgeSet ke = cells;
347 //VectorAnimationComplex::KeyFaceSet kf = cells;
348 //VectorAnimationComplex::InbetweenVertexSet sv = cells;
349 //VectorAnimationComplex::InbetweenEdgeSet se = cells;
350 //VectorAnimationComplex::InbetweenFaceSet sf = cells;
351
352 // Count cells
353 int nkv = 0;
354 int nkoe = 0;
355 int nkce = 0;
356 int nkf = 0;
357 int nsv = 0;
358 int nsoe = 0;
359 int nsce = 0;
360 int nsf = 0;
361 foreach(VectorAnimationComplex::Cell * cell, cells)
362 {
363 if(cell->toKeyVertex())
364 ++nkv;
365 else if(cell->toKeyEdge())
366 {
367 if(cell->toKeyEdge()->isClosed())
368 ++nkce;
369 else
370 ++nkoe;
371 }
372 else if(cell->toKeyFace())
373 ++nkf;
374 else if(cell->toInbetweenVertex())
375 ++nsv;
376 else if(cell->toInbetweenEdge())
377 {
378 if(cell->toInbetweenEdge()->isClosed())
379 ++nsce;
380 else
381 ++nsoe;
382 }
383 else if(cell->toInbetweenFace())
384 ++nsf;
385 }
386
387 // Set string according to count
388 QStringList stringList;
389 if(nkv == 1)
390 stringList << tr("1 key vertex");
391 if(nkv > 1)
392 stringList << QString().setNum(nkv) + tr(" key vertices");
393 if(nkce == 1)
394 stringList << tr("1 key closed edge");
395 if(nkce > 1)
396 stringList << QString().setNum(nkce) + tr(" key closed edges");
397 if(nkoe == 1)
398 stringList << tr("1 key open edge");
399 if(nkoe > 1)
400 stringList << QString().setNum(nkoe) + tr(" key open edges");
401 if(nkf == 1)
402 stringList << tr("1 key face");
403 if(nkf > 1)
404 stringList << QString().setNum(nkf) + tr(" key faces");
405 if(nsv == 1)
406 stringList << tr("1 inbetween vertex");
407 if(nsv > 1)
408 stringList << QString().setNum(nsv) + tr(" inbetween vertices");
409 if(nsce == 1)
410 stringList << tr("1 inbetween closed edge");
411 if(nsce > 1)
412 stringList << QString().setNum(nsce) + tr(" inbetween closed edges");
413 if(nsoe == 1)
414 stringList << tr("1 inbetween open edge");
415 if(nsoe > 1)
416 stringList << QString().setNum(nsoe) + tr(" inbetween open edges");
417 if(nsf == 1)
418 stringList << tr("1 inbetween face");
419 if(nsf > 1)
420 stringList << QString().setNum(nsf) + tr(" inbetween faces");
421
422 if(stringList.isEmpty())
423 return tr("no objects");
424 else
425 {
426 QString res = stringList[0];
427 for(int i=1; i<stringList.size(); ++i)
428 res += QString("\n") + stringList[i];
429 return res;
430 }
431 }
432
setType(const QString & type)433 void ObjectPropertiesWidget::setType(const QString & type)
434 {
435 type_->setText(type);
436 }
437
setId(const VectorAnimationComplex::CellSet & cells)438 void ObjectPropertiesWidget::setId(const VectorAnimationComplex::CellSet & cells)
439 {
440 int numIdPerLine = 5;
441
442 idLess_ = QString("");
443 idMore_ = QString("");
444 idMoreLessButton_->hide();
445
446 int i = 0; // count cells
447 int j = 0; // num on line
448 foreach(VectorAnimationComplex::Cell * cell, cells)
449 {
450 j = i % numIdPerLine;
451 if(i==0)
452 {
453 idMore_ += QString().setNum(cell->id());
454 idLess_ += QString().setNum(cell->id());
455 }
456 else if(j==0)
457 {
458 idMore_ += QString(",\n") + QString().setNum(cell->id());
459
460 if(i == numIdPerLine)
461 {
462 idLess_ += QString(",...");
463 idMoreLessButton_->show();
464 }
465 }
466 else
467 {
468 idMore_ += QString(", ") + QString().setNum(cell->id());
469
470 if(i < numIdPerLine)
471 {
472 idLess_ += QString(", ") + QString().setNum(cell->id());
473 }
474
475
476 }
477 ++i;
478 }
479
480 setIdFromString();
481 }
482
setIdFromString()483 void ObjectPropertiesWidget::setIdFromString()
484 {
485 if(idMoreLessButton_->text() == moreText_)
486 {
487 id_->setText(idLess_);
488 }
489 else
490 {
491 id_->setText(idMore_);
492 }
493 update();
494 }
495
idMoreLessSlot()496 void ObjectPropertiesWidget::idMoreLessSlot()
497 {
498 if(idMoreLessButton_->text() == moreText_)
499 {
500 idMoreLessButton_->setText(lessText_);
501 }
502 else
503 {
504 idMoreLessButton_->setText(moreText_);
505 }
506 setIdFromString();
507 }
508