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 "MultiView.h"
18 #include "View.h"
19 #include "ViewWidget.h"
20 #include "Scene.h"
21 #include "Timeline.h"
22 #include "Global.h"
23
24 #include <QKeyEvent>
25 #include <QtDebug>
26 #include <QSplitter>
27 #include <QVBoxLayout>
28
29 // Splitter invariants:
30 // 1. For every View v, v->parentWidget() is a QSplitter, accessed via getParentSplitter(v)
31 // 2. For every View v, v is the only child of getParentSplitter(v)
32 // 3. For every QSplitter s, s has either:
33 // a. One child, in which case it is a View, or
34 // b. Two children, in which case both of them are QSplitters
35
36 namespace
37 {
38
viewWidgetFromView_(View * view)39 ViewWidget * viewWidgetFromView_(View * view)
40 {
41 return qobject_cast<ViewWidget*>(view->parentWidget());
42 }
43
viewFromViewWidget_(ViewWidget * viewWidget)44 View * viewFromViewWidget_(ViewWidget * viewWidget)
45 {
46 return viewWidget->view();
47 }
48
getParentSplitter_(QSplitter * w)49 QSplitter * getParentSplitter_(QSplitter * w)
50 {
51 return qobject_cast<QSplitter*>(w->parentWidget());
52 }
53
getParentSplitter_(ViewWidget * w)54 QSplitter * getParentSplitter_(ViewWidget * w)
55 {
56 return qobject_cast<QSplitter*>(w->parentWidget());
57 }
58
getParentSplitter_(View * w)59 QSplitter * getParentSplitter_(View * w)
60 {
61 return getParentSplitter_(viewWidgetFromView_(w));
62 }
63
64 }
65
MultiView(Scene * scene,QWidget * parent)66 MultiView::MultiView(Scene *scene, QWidget *parent) :
67 QWidget(parent),
68 views_(),
69 activeView_(0),
70 hoveredView_(0),
71 scene_(scene)
72 {
73 // create initial view and splitter
74 View * view = createView_();
75 QSplitter * splitter = new QSplitter();
76 splitter->addWidget(viewWidgetFromView_(view));
77
78 // set layout
79 QVBoxLayout * layout = new QVBoxLayout();
80 layout->addWidget(splitter);
81 layout->setContentsMargins(0,0,0,0);
82 setLayout(layout);
83
84 // Redraw views when necessary
85 // This line is necessary since some mouse cursor are drawn by VAC->draw()
86 // and depends on which view is hovered, if any
87 connect(this, SIGNAL(hoveredViewChanged()), this, SIGNAL(allViewsNeedToUpdate()));
88 }
89
createView_()90 View * MultiView::createView_()
91 {
92 ViewWidget * viewWidget = new ViewWidget(scene_, this);
93 View * view = viewFromViewWidget_(viewWidget);
94 views_ << viewWidget;
95 setActiveView(view);
96 hoveredView_ = 0;
97 Timeline * timeline = global()->timeline();
98 if(timeline)
99 {
100 timeline->addView(view);
101 }
102
103 connect(view, SIGNAL(allViewsNeedToUpdate()), this, SIGNAL(allViewsNeedToUpdate()));
104 connect(view, SIGNAL(allViewsNeedToUpdatePicking()), this, SIGNAL(allViewsNeedToUpdatePicking()));
105 connect(view, SIGNAL(mousePressed(GLWidget*)), this, SLOT(setActive(GLWidget*)));
106 connect(view, SIGNAL(mouseEntered(GLWidget*)), this, SLOT(setHovered(GLWidget*)));
107 connect(view, SIGNAL(mouseLeft(GLWidget*)), this, SLOT(unsetHovered(GLWidget*)));
108 connect(view, SIGNAL(mouseMoved(GLWidget*)), this, SLOT(setHovered(GLWidget*)));
109 connect(view, SIGNAL(viewIsBeingChanged(int,int)), this, SIGNAL(cameraChanged()));
110 connect(view, SIGNAL(viewChanged(int,int)), this, SIGNAL(cameraChanged()));
111 connect(view, SIGNAL(viewResized()), this, SIGNAL(cameraChanged()));
112 connect(view, SIGNAL(settingsChanged()), this, SIGNAL(settingsChanged()));
113
114 return view;
115 }
116
117
deleteView_(View * view)118 void MultiView::deleteView_(View * view)
119 {
120 views_.removeAll(viewWidgetFromView_(view));
121 Timeline * timeline = global()->timeline();
122 if(timeline)
123 {
124 timeline->removeView(view);
125 }
126 if(activeView_ == view)
127 {
128 if(views_.size() > 0)
129 setActiveView(viewFromViewWidget_(views_[0]));
130 else
131 setActiveView(0);
132 }
133 if(hoveredView_ == view)
134 {
135 hoveredView_ = 0;
136 }
137 }
138
setActiveView(View * view)139 void MultiView::setActiveView(View * view)
140 {
141 if(view && (activeView_!=view))
142 {
143 if(activeView_)
144 activeView_->setActive(false);
145
146 activeView_ = view;
147 activeView_->setActive(true);
148
149 emit activeViewChanged();
150 }
151 }
152
setActive(GLWidget * w)153 void MultiView::setActive(GLWidget * w)
154 {
155 View * view = qobject_cast<View*>(w);
156 setActiveView(view);
157 }
158
setHovered(GLWidget * w)159 void MultiView::setHovered(GLWidget * w)
160 {
161 View * oldHoveredView = hoveredView_;
162
163 View * view = qobject_cast<View*>(w);
164 if(view)
165 {
166 hoveredView_ = view;
167 }
168 else
169 {
170 hoveredView_ = 0;
171 }
172
173 if(hoveredView_ != oldHoveredView)
174 emit hoveredViewChanged();
175 }
176
unsetHovered(GLWidget *)177 void MultiView::unsetHovered(GLWidget * /*w*/)
178 {
179 View * oldHoveredView = hoveredView_;
180
181 hoveredView_ = 0;
182
183 if(hoveredView_ != oldHoveredView)
184 emit hoveredViewChanged();
185 }
186
187
split_(View * view,Qt::Orientation orientation)188 void MultiView::split_(View * view, Qt::Orientation orientation)
189 {
190 // Schematic view of what happens
191 // _________________________________
192 // | splitterParent |
193 // | _____________________________ |
194 // | | view | |
195 // | | | |
196 // | | | |
197 // | | | |
198 // | | | |
199 // | |_____________________________| |
200 // |_________________________________|
201 //
202 // []
203 // _[]_
204 // \ /
205 // \/
206 // _________________________________
207 // | splitterParent |
208 // | _____________ _____________ |
209 // | | splitter1 | | splitter2 | |
210 // | | _________ | | _________ | |
211 // | | | view | | | | view2 | | |
212 // | | | | | | | | | |
213 // | | |_________| | | |_________| | |
214 // | |_____________| |_____________| |
215 // |_________________________________|
216 //
217
218 // Get current splitter and view
219 QSplitter * splitterParent = getParentSplitter_(view);
220 assert(splitterParent);
221
222 // create view
223 View * view2 = createView_();
224
225 // Create two new splitters
226 QSplitter * splitter1 = new QSplitter;
227 QSplitter * splitter2 = new QSplitter;
228
229 // Change parent widgets
230 splitterParent->setOrientation(orientation);
231 splitterParent->addWidget(splitter1);
232 splitterParent->addWidget(splitter2);
233 splitter1->addWidget(viewWidgetFromView_(view)); // This also removes view from splitterParent
234 splitter2->addWidget(viewWidgetFromView_(view2));
235 }
236
splitClose_(View * view)237 void MultiView::splitClose_(View * view)
238 {
239 assert( numViews() >= 2);
240
241 // Schematic view of what happens
242 // Note: [*] is either 1 view, or 2 splitters
243 // _________________________________
244 // | splitterParent |
245 // | _____________ _____________ |
246 // | | splitter1 | | splitter2 | |
247 // | | _________ | | _________ | |
248 // | | | view | | | | * | | |
249 // | | | | | | | | | |
250 // | | |_________| | | |_________| | |
251 // | |_____________| |_____________| |
252 // |_________________________________|
253 //
254 // []
255 // _[]_
256 // \ /
257 // \/
258 // _________________________________
259 // | splitterParent |
260 // | _____________________________ |
261 // | | * | |
262 // | | | |
263 // | | | |
264 // | | | |
265 // | | | |
266 // | |_____________________________| |
267 // |_________________________________|
268 //
269
270 // Get splitters
271 QSplitter * splitter1 = getParentSplitter_(view);
272 QSplitter * splitterParent = getParentSplitter_(splitter1);
273 assert(splitterParent->count() == 2);
274 QSplitter * splitter2 = qobject_cast<QSplitter*>(splitterParent->widget(0));
275 if(splitter2 == splitter1)
276 splitter2 = qobject_cast<QSplitter*>(splitterParent->widget(1));
277
278 // Delete view and splitter1
279 // Caution: order is important, because deleteView_(view) does some
280 // management before actually deleting view, and deleting
281 // splitter1 would recursively delete view
282 deleteView_(view);
283 delete splitter1;
284
285 // Save layout of splitter2
286 Qt::Orientation orientation = splitter2->orientation();
287 QList<int> sizes = splitter2->sizes();
288
289 // Transfer children of splitter2 to splitterParent
290 QList<QWidget*> splitter2Children;
291 for(int i=0; i<splitter2->count(); ++i)
292 splitter2Children << splitter2->widget(i);
293 for(int i=0; i<splitter2Children.size(); ++i)
294 splitterParent->addWidget(splitter2Children[i]);
295
296 // Delete splitter2
297 delete splitter2;
298
299 // Transfer layout of splitter2 to splitterParent
300 splitterParent->setOrientation(orientation);
301 splitterParent->setSizes(sizes);
302 }
303
304
split_(Qt::Orientation orientation)305 void MultiView::split_(Qt::Orientation orientation)
306 {
307 // Get current splitter and view
308 View * view1 = activeView();
309 QSplitter * s = getParentSplitter_(view1);
310
311 if(s)
312 {
313 // create view
314 View * view2 = createView_();
315
316 // Create two new splitters
317 QSplitter * s1 = new QSplitter;
318 QSplitter * s2 = new QSplitter;
319
320 // Change parent widgets
321 s->setOrientation(orientation);
322 s->addWidget(s1);
323 s->addWidget(s2);
324 s1->addWidget(viewWidgetFromView_(view1)); // This also removes view1 from s
325 s2->addWidget(viewWidgetFromView_(view2));
326 }
327 }
328
splitVertical()329 void MultiView::splitVertical()
330 {
331 split_(Qt::Horizontal);
332 }
333
splitHorizontal()334 void MultiView::splitHorizontal()
335 {
336 split_(Qt::Vertical);
337 }
338
numViews() const339 int MultiView::numViews() const
340 {
341 return views_.size();
342 }
343
splitClose()344 void MultiView::splitClose()
345 {
346 View * view = activeView();
347 if(view && numViews() >= 2)
348 {
349 splitClose_(view);
350 }
351 }
352
splitOne()353 void MultiView::splitOne()
354 {
355 View * view = activeView();
356 if(view && numViews() >= 2)
357 {
358 QList<ViewWidget*> viewsToClose = views_;
359 viewsToClose.removeAll(viewWidgetFromView_(view));
360 for(int i=0; i<viewsToClose.size(); ++i)
361 {
362 splitClose_(viewFromViewWidget_(viewsToClose[i]));
363 }
364 }
365 }
366
367
368
activeView() const369 View * MultiView::activeView() const
370 {
371 return activeView_;
372 }
373
hoveredView() const374 View * MultiView::hoveredView() const
375 {
376 return hoveredView_;
377 }
378
~MultiView()379 MultiView::~MultiView()
380 {
381 }
382
update()383 void MultiView::update()
384 {
385 foreach(ViewWidget * viewWidget, views_)
386 {
387 View * view = viewFromViewWidget_(viewWidget);
388 if(view->isVisible())
389 view->update();
390 }
391 }
392
updatePicking()393 void MultiView::updatePicking()
394 {
395 foreach(ViewWidget * viewWidget, views_)
396 {
397 View * view = viewFromViewWidget_(viewWidget);
398 if(view->isVisible())
399 view->updatePicking();
400 }
401 }
402
zoomIn()403 void MultiView::zoomIn()
404 {
405 activeView()->zoomIn();
406 }
407
zoomOut()408 void MultiView::zoomOut()
409 {
410 activeView()->zoomOut();
411 }
412
fitAllInWindow()413 void MultiView::fitAllInWindow()
414 {
415 activeView()->fitAllInWindow();
416 }
417
fitSelectionInWindow()418 void MultiView::fitSelectionInWindow()
419 {
420 activeView()->fitSelectionInWindow();
421 }
422
keyPressEvent(QKeyEvent * event)423 void MultiView::keyPressEvent(QKeyEvent *event)
424 {
425 event->ignore();
426 }
427
keyReleaseEvent(QKeyEvent * event)428 void MultiView::keyReleaseEvent(QKeyEvent *event)
429 {
430 event->ignore();
431 }
432
433 // toChange
zoom() const434 double MultiView::zoom() const { return activeView()->camera2D().zoom(); }
435
toggleOutline()436 void MultiView::toggleOutline()
437 {
438 activeView()->toggleOutline();
439 }
440
toggleOutlineOnly()441 void MultiView::toggleOutlineOnly()
442 {
443 activeView()->toggleOutlineOnly();
444 }
setDisplayMode(ViewSettings::DisplayMode displayMode)445 void MultiView::setDisplayMode(ViewSettings::DisplayMode displayMode)
446 {
447 activeView()->setDisplayMode(displayMode);
448 }
449
setOnionSkinningEnabled(bool enabled)450 void MultiView::setOnionSkinningEnabled(bool enabled)
451 {
452 activeView()->setOnionSkinningEnabled(enabled);
453 }
454