Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "dockpageview.h"
24 :
25 : #include <QTimer>
26 :
27 : #include "docktoolbarview.h"
28 : #include "dockingholderview.h"
29 : #include "dockcentralview.h"
30 : #include "dockpanelview.h"
31 : #include "dockstatusbar.h"
32 :
33 : #include "ui/qml/Muse/Ui/navigationcontrol.h"
34 :
35 : #include "log.h"
36 :
37 : using namespace muse::dock;
38 : using namespace muse::ui;
39 :
40 24 : DockPageView::DockPageView(QQuickItem* parent)
41 24 : : QQuickItem(parent), muse::Contextable(muse::iocCtxForQmlObject(this)),
42 24 : m_mainToolBars(this),
43 24 : m_toolBars(this),
44 24 : m_toolBarsDockingHolders(this),
45 24 : m_panels(this),
46 72 : m_panelsDockingHolders(this)
47 : {
48 : //! NOTE: dockwindow controls the visible state
49 24 : setVisible(false);
50 24 : }
51 :
52 3 : void DockPageView::init()
53 : {
54 4 : TRACEFUNC;
55 :
56 12 : for (DockBase* dock : allDocks()) {
57 9 : dock->setParentItem(this);
58 9 : dock->init();
59 :
60 9 : connect(dock, &DockBase::floatingChanged, [this](){
61 0 : reorderSections();
62 0 : });
63 :
64 9 : connect(dock, &DockBase::visibleChanged, [this](){
65 9 : reorderSections();
66 9 : });
67 3 : }
68 :
69 3 : emit inited();
70 3 : }
71 :
72 0 : void DockPageView::deinit()
73 : {
74 0 : TRACEFUNC;
75 :
76 0 : for (DockBase* dock : allDocks()) {
77 0 : dock->disconnect(this);
78 0 : }
79 0 : }
80 :
81 3 : QString DockPageView::uri() const
82 : {
83 3 : return m_uri;
84 : }
85 :
86 3 : void DockPageView::setParams(const QVariantMap& params)
87 : {
88 3 : emit setParamsRequested(params);
89 3 : }
90 :
91 3 : QQmlListProperty<DockToolBarView> DockPageView::mainToolBarsProperty()
92 : {
93 3 : return m_mainToolBars.property();
94 : }
95 :
96 0 : QQmlListProperty<DockToolBarView> DockPageView::toolBarsProperty()
97 : {
98 0 : return m_toolBars.property();
99 : }
100 :
101 3 : QQmlListProperty<DockPanelView> DockPageView::panelsProperty()
102 : {
103 3 : return m_panels.property();
104 : }
105 :
106 0 : QQmlListProperty<DockingHolderView> DockPageView::toolBarsDockingHoldersProperty()
107 : {
108 0 : return m_toolBarsDockingHolders.property();
109 : }
110 :
111 0 : QQmlListProperty<DockingHolderView> DockPageView::panelsDockingHoldersProperty()
112 : {
113 0 : return m_panelsDockingHolders.property();
114 : }
115 :
116 37 : QList<DockToolBarView*> DockPageView::mainToolBars() const
117 : {
118 37 : return m_mainToolBars.list();
119 : }
120 :
121 25 : QList<DockToolBarView*> DockPageView::toolBars() const
122 : {
123 25 : return m_toolBars.list();
124 : }
125 :
126 0 : QList<DockingHolderView*> DockPageView::toolBarsHolders() const
127 : {
128 0 : return m_toolBarsDockingHolders.list();
129 : }
130 :
131 6 : DockCentralView* DockPageView::centralDock() const
132 : {
133 6 : return m_central;
134 : }
135 :
136 6 : DockStatusBar* DockPageView::statusBar() const
137 : {
138 6 : return m_statusBar;
139 : }
140 :
141 28 : QList<DockPanelView*> DockPageView::panels() const
142 : {
143 28 : return m_panels.list();
144 : }
145 :
146 0 : QList<DockingHolderView*> DockPageView::panelsHolders() const
147 : {
148 0 : return m_panelsDockingHolders.list();
149 : }
150 :
151 0 : DockBase* DockPageView::dockByName(const QString& dockName) const
152 : {
153 0 : for (DockBase* dock : allDocks()) {
154 0 : if (dock->objectName() == dockName) {
155 0 : return dock;
156 : }
157 0 : }
158 :
159 0 : return nullptr;
160 : }
161 :
162 24 : DockingHolderView* DockPageView::holder(DockType type, Location location) const
163 : {
164 24 : QList<DockingHolderView*> holders;
165 :
166 24 : if (type == DockType::ToolBar) {
167 12 : holders = m_toolBarsDockingHolders.list();
168 12 : } else if (type == DockType::Panel) {
169 12 : holders = m_panelsDockingHolders.list();
170 : }
171 :
172 24 : for (DockingHolderView* holder : holders) {
173 0 : if (holder->location() == location) {
174 0 : return holder;
175 : }
176 : }
177 :
178 24 : return nullptr;
179 24 : }
180 :
181 0 : QList<DockPanelView*> DockPageView::findPanelsForDropping(const DockPanelView* panel) const
182 : {
183 0 : QList<DockPanelView*> result;
184 :
185 0 : for (DockPanelView* destinationPanel : panels()) {
186 0 : if (destinationPanel->isTabAllowed(panel)) {
187 0 : result << destinationPanel;
188 : }
189 0 : }
190 :
191 0 : return result;
192 0 : }
193 :
194 3 : DockPanelView* DockPageView::findPanelForTab(const DockPanelView* tab) const
195 : {
196 6 : for (DockPanelView* destinationPanel: panels()) {
197 3 : if (destinationPanel->isTabAllowed(tab)
198 3 : && destinationPanel->location() == tab->location()) {
199 0 : return destinationPanel;
200 : }
201 3 : }
202 :
203 3 : return nullptr;
204 : }
205 :
206 0 : bool DockPageView::isDockOpen(const QString& dockName) const
207 : {
208 0 : const DockBase* dock = dockByName(dockName);
209 0 : return dock && dock->isOpen();
210 : }
211 :
212 0 : void DockPageView::toggleDock(const QString& dockName)
213 : {
214 0 : setDockOpen(dockName, !isDockOpen(dockName));
215 0 : }
216 :
217 0 : void DockPageView::setDockOpen(const QString& dockName, bool open)
218 : {
219 0 : DockBase* dock = dockByName(dockName);
220 0 : if (!dock) {
221 0 : return;
222 : }
223 :
224 0 : if (!open) {
225 0 : dock->close();
226 0 : return;
227 : }
228 :
229 0 : DockPanelView* panel = dynamic_cast<DockPanelView*>(dock);
230 0 : if (!panel) {
231 0 : dock->open();
232 0 : return;
233 : }
234 :
235 0 : DockPanelView* destinationPanel = findPanelForTab(panel);
236 0 : if (destinationPanel) {
237 0 : destinationPanel->addPanelAsTab(panel);
238 : } else {
239 0 : panel->open();
240 : }
241 : }
242 :
243 12 : void DockPageView::reorderSections()
244 : {
245 : //! NOTE: In some cases, such as setting visible true,
246 : //! it is necessary to give the UI time to render the content, so we will add a delay.
247 12 : QTimer::singleShot(2000, this, [this](){
248 0 : doReorderSections();
249 0 : });
250 12 : }
251 :
252 0 : void DockPageView::doReorderSections()
253 : {
254 0 : TRACEFUNC;
255 :
256 0 : if (!isVisible()) {
257 0 : return;
258 : }
259 :
260 0 : QList<DockBase*> docks = allDocks();
261 0 : QList<DockBase*> docksAvailableForNavigation;
262 :
263 0 : for (DockBase* dock: docks) {
264 0 : if (dock->navigationSection() && dock->isVisible()) {
265 0 : docksAvailableForNavigation.append(dock);
266 : }
267 : }
268 :
269 0 : reorderDocksNavigationSections(docksAvailableForNavigation);
270 0 : }
271 :
272 0 : void DockPageView::reorderDocksNavigationSections(QList<DockBase*>& docks)
273 : {
274 0 : QHash<muse::ui::INavigationSection*, QList<DockBase*> > orderedSections;
275 0 : for (DockBase* dock: docks) {
276 0 : if (ui::INavigationSection* section = dock->navigationSection()) {
277 0 : orderedSections[section] << dock;
278 : }
279 : }
280 :
281 0 : for (QList<DockBase*>& panels : orderedSections.values()) {
282 0 : reorderNavigationSectionPanels(panels);
283 0 : }
284 0 : }
285 :
286 0 : void DockPageView::reorderNavigationSectionPanels(QList<DockBase*>& sectionDocks)
287 : {
288 0 : std::sort(sectionDocks.begin(), sectionDocks.end(), [](DockBase* dock1, DockBase* dock2) {
289 0 : if (!dock1->navigationSection() || !dock2->navigationSection()) {
290 0 : return false;
291 : }
292 :
293 0 : if (dock1 == dock2) {
294 0 : return false;
295 : }
296 :
297 0 : QPoint dock1Pos = dock1->globalPosition();
298 0 : QPoint dock2Pos = dock2->globalPosition();
299 :
300 0 : if (dock1->floating() && dock2->floating()) {
301 0 : return dock1Pos.x() < dock2Pos.x();
302 0 : } else if (dock1->floating()) {
303 0 : return false;
304 0 : } else if (dock2->floating()) {
305 0 : return true;
306 : }
307 :
308 0 : if (dock1Pos.x() == dock2Pos.x()) {
309 0 : return dock1Pos.y() < dock2Pos.y();
310 : }
311 :
312 0 : return dock1Pos.x() < dock2Pos.x();
313 : });
314 :
315 : //!NOTE: It is possible that the dock does not contain all the panels.
316 : //! For example, MainToolBar is created for the entire window and is always visible.
317 : //! Reserve n panels for each dock.
318 0 : int i = 10;
319 0 : QHash<DockBase*, int> orderedDocks;
320 0 : for (DockBase* dock: sectionDocks) {
321 : //!NOTE: It is possible that the dock contains multiple panels.
322 : //! Reserve n panels for each dock.
323 0 : int order = i++ *1000;
324 :
325 : //! NOTE: If a panel is inside a frame with another panel,
326 : //! there is no need to set the order for the frame panel, as it is already set.
327 0 : bool exists = false;
328 0 : for (DockBase* orderedPanel: orderedDocks.keys()) {
329 0 : if (orderedPanel->isInSameFrame(dock)) {
330 0 : exists = true;
331 : }
332 0 : }
333 :
334 0 : if (!exists) {
335 0 : orderedDocks[dock] = order;
336 0 : dock->setFramePanelOrder(order);
337 : }
338 :
339 0 : dock->setContentNavigationPanelOrderStart(order + 1);
340 : }
341 0 : }
342 :
343 0 : bool DockPageView::isDockFloating(const QString& dockName) const
344 : {
345 0 : const DockBase* dock = dockByName(dockName);
346 0 : return dock ? dock->floating() : false;
347 : }
348 :
349 0 : void DockPageView::toggleDockFloating(const QString& dockName)
350 : {
351 0 : DockBase* dock = dockByName(dockName);
352 0 : if (!dock) {
353 0 : return;
354 : }
355 :
356 0 : dock->setFloating(!dock->floating());
357 : }
358 :
359 3 : void DockPageView::setUri(const QString& uri)
360 : {
361 3 : if (uri == m_uri) {
362 0 : return;
363 : }
364 :
365 3 : m_uri = uri;
366 3 : emit uriChanged(uri);
367 : }
368 :
369 3 : void DockPageView::setCentralDock(DockCentralView* central)
370 : {
371 3 : if (central == m_central) {
372 0 : return;
373 : }
374 :
375 3 : m_central = central;
376 3 : emit centralDockChanged(central);
377 : }
378 :
379 0 : void DockPageView::setStatusBar(DockStatusBar* statusBar)
380 : {
381 0 : if (statusBar == m_statusBar) {
382 0 : return;
383 : }
384 :
385 0 : m_statusBar = statusBar;
386 0 : emit statusBarChanged(statusBar);
387 : }
388 :
389 3 : void DockPageView::componentComplete()
390 : {
391 3 : QQuickItem::componentComplete();
392 :
393 3 : Q_ASSERT(!m_uri.isEmpty());
394 3 : Q_ASSERT(m_central != nullptr);
395 :
396 3 : connect(this, &DockPageView::visibleChanged, [this](){
397 6 : if (isVisible()) {
398 3 : reorderSections();
399 : }
400 6 : });
401 3 : }
402 :
403 19 : QList<DockBase*> DockPageView::allDocks() const
404 : {
405 19 : auto mainToolBars = this->mainToolBars();
406 19 : auto toolbars = this->toolBars();
407 19 : auto panels = this->panels();
408 :
409 19 : QList<DockBase*> docks;
410 19 : docks << QList<DockBase*>(mainToolBars.begin(), mainToolBars.end());
411 19 : docks << QList<DockBase*>(toolbars.begin(), toolbars.end());
412 19 : docks << QList<DockBase*>(panels.begin(), panels.end());
413 :
414 19 : docks << m_central;
415 :
416 19 : if (m_statusBar) {
417 0 : docks << m_statusBar;
418 : }
419 :
420 19 : return docks;
421 19 : }
422 :
423 0 : void DockPageView::setDefaultNavigationControl(muse::ui::NavigationControl* control)
424 : {
425 0 : muse::ui::INavigationControl* _control = dynamic_cast<muse::ui::INavigationControl*>(control);
426 0 : navigationController()->setDefaultNavigationControl(_control);
427 0 : }
428 :
429 0 : void DockPageView::forceLayout()
430 : {
431 0 : emit layoutRequested();
432 0 : }
433 :
434 0 : QVariant DockPageView::tours() const
435 : {
436 0 : return m_tours;
437 : }
438 :
439 0 : void DockPageView::setTours(const QVariant& newTours)
440 : {
441 0 : if (m_tours == newTours) {
442 0 : return;
443 : }
444 :
445 0 : for (const QVariant& tourVar: newTours.toList()) {
446 0 : QVariantMap tourMap = tourVar.toMap();
447 :
448 0 : String eventCode = tourMap.value("eventCode").toString();
449 :
450 0 : QVariantMap tourInfoMap = tourMap.value("tour").toMap();
451 :
452 0 : tours::Tour tour;
453 :
454 0 : tour.id = tourInfoMap.value("id").toString();
455 :
456 0 : for (const QVariant& stepVar: tourInfoMap.value("steps").toList()) {
457 0 : QVariantMap stepMap = stepVar.toMap();
458 :
459 0 : tours::TourStep step;
460 0 : step.title = stepMap.value("title").toString();
461 0 : step.description = stepMap.value("description").toString();
462 0 : step.previewImageOrGifUrl = stepMap.value("previewImageOrGifUrl").toString();
463 0 : step.videoExplanationUrl = stepMap.value("videoExplanationUrl").toString();
464 0 : step.controlUri = Uri(stepMap.value("controlUri").toString());
465 :
466 0 : tour.steps.emplace_back(step);
467 0 : }
468 :
469 0 : if (!tour.steps.empty()) {
470 0 : toursService()->registerTour(eventCode, tour);
471 : }
472 0 : }
473 :
474 0 : m_tours = newTours;
475 0 : emit toursChanged();
476 : }
|