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 "LayersWidget.h"
18 
19 #include <cmath>
20 
21 #include <QCheckBox>
22 #include <QHBoxLayout>
23 #include <QLabel>
24 #include <QLineEdit>
25 #include <QPushButton>
26 #include <QScrollArea>
27 #include <QVBoxLayout>
28 
29 #include "Layer.h"
30 #include "Scene.h"
31 
32 namespace impl_
33 {
34 
LayerWidget(int index)35 LayerWidget::LayerWidget(int index) :
36     index_(index),
37     isActive_(false)
38 {
39     visibilityCheckBox_ = new QCheckBox();
40     visibilityCheckBox_->setCheckState(Qt::Checked);
41     visibilityCheckBox_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
42     connect(visibilityCheckBox_, SIGNAL(stateChanged(int)), this, SLOT(onVisibilityCheckBoxStateChanged_(int)));
43     connect(visibilityCheckBox_, SIGNAL(clicked(bool)), this, SLOT(onVisibilityCheckBoxClicked_(bool)));
44 
45     nameLabel_ = new QLabel();
46     nameLabel_->setMinimumHeight(30);
47 
48     nameLineEdit_ = new QLineEdit();
49     nameLineEdit_->setMinimumHeight(30);
50     nameLineEdit_->hide();
51     connect(nameLineEdit_, &QLineEdit::editingFinished, this, &LayerWidget::onNameLineEditEditingFinished_);
52 
53     QHBoxLayout * layout = new QHBoxLayout();
54     layout->addWidget(visibilityCheckBox_);
55     layout->addWidget(nameLabel_);
56     layout->addWidget(nameLineEdit_);
57     setLayout(layout);
58 
59     setAutoFillBackground(true);
60     updateBackground_();
61 }
62 
~LayerWidget()63 LayerWidget::~LayerWidget()
64 {
65 
66 }
67 
index() const68 int LayerWidget::index() const
69 {
70     return index_;
71 }
72 
isActive() const73 bool LayerWidget::isActive() const
74 {
75     return isActive_;
76 }
77 
setActive(bool b)78 void LayerWidget::setActive(bool b)
79 {
80     if (b != isActive_)
81     {
82         isActive_ = b;
83         updateBackground_();
84         if (isActive_)
85         {
86             emit activated(index());
87         }
88     }
89 }
90 
visibility() const91 bool LayerWidget::visibility() const
92 {
93     return visibilityCheckBox_->isChecked();
94 }
95 
setVisibility(bool b)96 void LayerWidget::setVisibility(bool b)
97 {
98     if (b != visibility())
99     {
100         visibilityCheckBox_->setChecked(b);
101         // Note: we don't emit a signal here, as it will be emitted
102         // in onVisibilityCheckBoxStateChanged_.
103     }
104 }
105 
name() const106 QString LayerWidget::name() const
107 {
108     return nameLabel_->text();
109 }
110 
setName(const QString & newName)111 bool LayerWidget::setName(const QString& newName)
112 {
113     // Abord name editing if any
114     abortNameEditing_();
115 
116     // Set new name if different form current name
117     if (newName != name())
118     {
119         nameLabel_->setText(newName);
120         emit nameChanged(index());
121         return true;
122     }
123     else
124     {
125         return false;
126     }
127 }
128 
startNameEditing()129 void LayerWidget::startNameEditing()
130 {
131     startNameEditing_(NameEditingReason_::ExternalRequest);
132 }
133 
mousePressEvent(QMouseEvent *)134 void LayerWidget::mousePressEvent(QMouseEvent*)
135 {
136     if (!isActive_)
137     {
138         setActive(true);
139         emit checkpoint();
140     }
141 }
142 
mouseDoubleClickEvent(QMouseEvent *)143 void LayerWidget::mouseDoubleClickEvent(QMouseEvent*)
144 {
145     startNameEditing_(NameEditingReason_::DoubleClick);
146 }
147 
onVisibilityCheckBoxClicked_(bool)148 void LayerWidget::onVisibilityCheckBoxClicked_(bool)
149 {
150     emit checkpoint();
151 }
152 
onVisibilityCheckBoxStateChanged_(int)153 void LayerWidget::onVisibilityCheckBoxStateChanged_(int)
154 {
155     emit visibilityChanged(index());
156 }
157 
onNameLineEditEditingFinished_()158 void LayerWidget::onNameLineEditEditingFinished_()
159 {
160     finishNameEditing_();
161 }
162 
startNameEditing_(NameEditingReason_ reason)163 void LayerWidget::startNameEditing_(NameEditingReason_ reason)
164 {
165     if (!nameLineEdit_->isVisible())
166     {
167         nameEditingReason_ = reason;
168         nameLineEdit_->setText(name());
169         nameLabel_->hide();
170         nameLineEdit_->show();
171         nameLineEdit_->selectAll();
172         nameLineEdit_->setFocus();
173     }
174 }
175 
abortNameEditing_()176 void LayerWidget::abortNameEditing_()
177 {
178     if (nameLineEdit_->isVisible())
179     {
180         nameLineEdit_->hide();
181         nameLabel_->show();
182     }
183 }
184 
finishNameEditing_()185 void LayerWidget::finishNameEditing_()
186 {
187     if (nameLineEdit_->isVisible())
188     {
189         QString newName = nameLineEdit_->text();
190 
191         nameLineEdit_->hide();
192         nameLabel_->show();
193 
194         bool changed = setName(newName);
195         if (changed && nameEditingReason_ == NameEditingReason_::DoubleClick)
196         {
197             // We only emit checkpoint if the user action causing the scene to
198             // change is initiated from this LayerWidget. In other words, the
199             // widget responsible for starting a user action is the widget
200             // responsible for calling checkpoint.
201             emit checkpoint();
202         }
203 
204         if (nameEditingReason_ == NameEditingReason_::ExternalRequest)
205         {
206             emit nameEditingFinished(index());
207         }
208     }
209 }
210 
updateBackground_()211 void LayerWidget::updateBackground_()
212 {
213     QPalette::ColorRole backgroundRole = isActive() ? QPalette::Highlight : QPalette::Base;
214     setBackgroundRole(backgroundRole);
215 }
216 
217 } // namespace impl_
218 
LayersWidget(Scene * scene)219 LayersWidget::LayersWidget(Scene * scene) :
220     scene_(scene),
221     numVisibleLayerWidgets_(0),
222     activeLayerWidget_(nullptr)
223 {
224     // VBoxLayout with all the individual LayerWidget instances
225     layerListLayout_ = new QVBoxLayout();
226     layerListLayout_->setContentsMargins(0,0,0,0);
227     layerListLayout_->setSpacing(0);
228 
229     // Create one LayerWidget right now. It will be hidden shortly after if the
230     // scene has in fact no layers.
231     //
232     // This is required because for some reason, LayerWidgets won't show up if
233     // none exist before layerListLayout_ is added to the scrollArea. I suspect
234     // this to be a bug of Qt.
235     //
236     createNewLayerWidget_();
237 
238     // Wrap the layerListLayout_ into yet another VBoxLayout.
239     // We need this because:
240     // 1. We need scrollArea->setWidgetResizable(true) to enable horizontal stretching
241     //    of the LayerWidget items, so that the background color takes all the
242     //    horizontal space when selected.
243     // 2. Unfortunately, as a side effect, this enables vertical stretching too, which results
244     //    in ugly vertical stretching of all the LayerWidget items.
245     // 3. So we add a QSpacerItem to "eat" all the remaining space below layerListLayout_.
246     QVBoxLayout * layerListLayout2 = new QVBoxLayout();
247     layerListLayout2->setContentsMargins(0,0,0,0);
248     layerListLayout2->setSpacing(0);
249     layerListLayout2->addLayout(layerListLayout_);
250     layerListLayout2->addStretch();
251 
252     // Put the vbox layout in a scrollarea
253     QScrollArea * scrollArea = new QScrollArea();
254     scrollArea->setWidgetResizable(true);
255     QWidget * layerList = new QWidget();
256     layerList->setLayout(layerListLayout2);
257     scrollArea->setWidget(layerList);
258 
259     // Set background color for scrollarea
260     scrollArea->setBackgroundRole(QPalette::Base);
261     scrollArea->setAutoFillBackground(true);
262 
263     // Create buttons
264     QPushButton * newLayerButton = new QPushButton(tr("New"));
265     QPushButton * moveLayerUpButton = new QPushButton(tr("Move Up"));
266     QPushButton * moveLayerDownButton = new QPushButton(tr("Move Down"));
267     QPushButton * deleteLayerButton = new QPushButton(tr("Delete"));
268     connect(newLayerButton, &QPushButton::clicked, this, &LayersWidget::onNewLayerClicked_);
269     connect(moveLayerUpButton, &QPushButton::clicked, this, &LayersWidget::onMoveLayerUpClicked_);
270     connect(moveLayerDownButton, &QPushButton::clicked, this, &LayersWidget::onMoveLayerDownClicked_);
271     connect(deleteLayerButton, &QPushButton::clicked, this, &LayersWidget::onDeleteLayerClicked_);
272     QHBoxLayout * buttonsLayout = new QHBoxLayout();
273     buttonsLayout->addWidget(newLayerButton);
274     buttonsLayout->addWidget(moveLayerUpButton);
275     buttonsLayout->addWidget(moveLayerDownButton);
276     buttonsLayout->addStretch();
277     buttonsLayout->addWidget(deleteLayerButton);
278 
279     // Add scrollarea to this widget
280     QVBoxLayout * layout = new QVBoxLayout();
281     layout->addWidget(scrollArea);
282     layout->addLayout(buttonsLayout);
283     setLayout(layout);
284 
285     // Connect to scene
286     updateUiFromScene_();
287     connect(scene_, SIGNAL(layerAttributesChanged()), this, SLOT(onSceneLayerAttributesChanged_()));
288 }
289 
~LayersWidget()290 LayersWidget::~LayersWidget()
291 {
292 
293 }
294 
scene() const295 Scene * LayersWidget::scene() const
296 {
297     return scene_;
298 }
299 
onLayerWidgetActivated_(int index)300 void LayersWidget::onLayerWidgetActivated_(int index)
301 {
302     scene()->setActiveLayer(numVisibleLayerWidgets_ - 1 - index);
303 }
304 
onLayerWidgetVisibilityChanged_(int index)305 void LayersWidget::onLayerWidgetVisibilityChanged_(int index)
306 {
307     if (0 <= index && index < numVisibleLayerWidgets_) {
308         int j = numVisibleLayerWidgets_ - 1 - index;
309         bool visibility = layerWidgets_[index]->visibility();
310         scene()->layer(j)->setVisible(visibility);
311     }
312 }
313 
onLayerWidgetNameChanged_(int index)314 void LayersWidget::onLayerWidgetNameChanged_(int index)
315 {
316     if (0 <= index && index < numVisibleLayerWidgets_) {
317         int j = numVisibleLayerWidgets_ - 1 - index;
318         QString name = layerWidgets_[index]->name();
319         scene()->layer(j)->setName(name);
320     }
321 }
322 
onLayerWidgetNameEditingFinished_(int)323 void LayersWidget::onLayerWidgetNameEditingFinished_(int)
324 {
325     scene()->emitCheckpoint();
326 }
327 
onLayerWidgetCheckpoint_()328 void LayersWidget::onLayerWidgetCheckpoint_()
329 {
330     scene()->emitCheckpoint();
331 }
332 
onNewLayerClicked_()333 void LayersWidget::onNewLayerClicked_()
334 {
335     // Create layer. This should indirectly create the corresponding
336     // LayerWidget, unless using asynchronous signals/slots.
337     Layer * layer = scene()->createLayer(tr("New Layer"));
338 
339     // Enter name editing mode. We need to check in case of asynchronous
340     // signals/slots.
341     //
342     if (activeLayerWidget_)
343     {
344         int j = numVisibleLayerWidgets_ - 1 - activeLayerWidget_->index();
345         if (scene()->layer(j) == layer)
346         {
347             activeLayerWidget_->startNameEditing();
348             // Checkpoint will be emitted in onLayerWidgetNameEditingFinished_
349         }
350     }
351     else
352     {
353         // This is not supposed to happen
354         scene()->emitCheckpoint();
355     }
356 }
357 
onDeleteLayerClicked_()358 void LayersWidget::onDeleteLayerClicked_()
359 {
360     scene()->destroyActiveLayer();
361     scene()->emitCheckpoint();
362 }
363 
onMoveLayerUpClicked_()364 void LayersWidget::onMoveLayerUpClicked_()
365 {
366     scene()->moveActiveLayerUp();
367     scene()->emitCheckpoint();
368 }
369 
onMoveLayerDownClicked_()370 void LayersWidget::onMoveLayerDownClicked_()
371 {
372     scene()->moveActiveLayerDown();
373     scene()->emitCheckpoint();
374 }
375 
onSceneLayerAttributesChanged_()376 void LayersWidget::onSceneLayerAttributesChanged_()
377 {
378     updateUiFromScene_();
379 }
380 
updateUiFromScene_()381 void LayersWidget::updateUiFromScene_()
382 {
383     // Show as many existing LayerWidgets as necessary
384     int numLayers = scene()->numLayers();
385     int numLayerWidgets = layerWidgets_.size();
386     int newNumVisibleLayerWidgets = std::min(numLayers, numLayerWidgets);
387     for (int i = numVisibleLayerWidgets_; i < newNumVisibleLayerWidgets; ++i) {
388         layerWidgets_[i]->show();
389         ++numVisibleLayerWidgets_;
390     }
391 
392     // Create as many new LayerWidgets as necessary
393     for (int i = numVisibleLayerWidgets_; i < numLayers; ++i) {
394         createNewLayerWidget_();
395     }
396 
397     // Hide superfluous LayerWidgets
398     for (int i = numLayers; i < numVisibleLayerWidgets_; ++i) {
399         layerWidgets_[i]->hide();
400     }
401     numVisibleLayerWidgets_ = numLayers;
402 
403     // Set LayerWidgets names and visibility
404     for (int i = 0; i < numVisibleLayerWidgets_; ++i) {
405         int j = numVisibleLayerWidgets_ - 1 - i;
406         bool visibility= scene()->layer(j)->isVisible();
407         QString name = scene()->layer(j)->name();
408         layerWidgets_[i]->setVisibility(visibility);
409         layerWidgets_[i]->setName(name);
410     }
411 
412     // Set active LayerWidget
413     int jActive = scene()->activeLayerIndex();
414     int iActive = numVisibleLayerWidgets_ - 1 - jActive;
415     if (activeLayerWidget_ && activeLayerWidget_->index() != iActive) {
416         activeLayerWidget_->setActive(false);
417         activeLayerWidget_ = nullptr;
418     }
419     if (0 <= iActive && iActive < numVisibleLayerWidgets_) {
420         activeLayerWidget_ = layerWidgets_[iActive];
421         activeLayerWidget_->setActive(true);
422     }
423 }
424 
425 // Precondition: all LayerWidgets are visible
createNewLayerWidget_()426 void LayersWidget::createNewLayerWidget_()
427 {
428     impl_::LayerWidget * layerWidget = new impl_::LayerWidget(layerWidgets_.size());
429     ++numVisibleLayerWidgets_;
430     layerWidgets_.push_back(layerWidget);
431     layerListLayout_->addWidget(layerWidget);
432     connect(layerWidget, &impl_::LayerWidget::activated, this, &LayersWidget::onLayerWidgetActivated_);
433     connect(layerWidget, &impl_::LayerWidget::visibilityChanged, this, &LayersWidget::onLayerWidgetVisibilityChanged_);
434     connect(layerWidget, &impl_::LayerWidget::nameChanged, this, &LayersWidget::onLayerWidgetNameChanged_);
435     connect(layerWidget, &impl_::LayerWidget::nameEditingFinished, this, &LayersWidget::onLayerWidgetNameEditingFinished_);
436     connect(layerWidget, &impl_::LayerWidget::checkpoint, this, &LayersWidget::onLayerWidgetCheckpoint_);
437 }
438