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 : #include "navigationcontroller.h"
23 :
24 : #include <algorithm>
25 : #include <limits>
26 : #include <utility>
27 :
28 : #include <QCoreApplication>
29 : #include <QWindow>
30 : #include <QTextStream>
31 :
32 : #include "global/containers.h"
33 : #include "global/defer.h"
34 :
35 : #include "../navigationcommands.h"
36 :
37 : #include "muse_framework_config.h"
38 : #include "types/ret.h"
39 :
40 : #ifdef MUSE_MODULE_DIAGNOSTICS
41 : #include "diagnostics/diagnosticutils.h"
42 : #endif
43 :
44 : #include "log.h"
45 :
46 : // #define NAVIGATION_LOGGING_ENABLED
47 :
48 : #ifdef NAVIGATION_LOGGING_ENABLED
49 : #define MYLOG() LOGI()
50 : #else
51 : #define MYLOG() LOGN()
52 : #endif
53 :
54 : using namespace muse;
55 : using namespace muse::ui;
56 :
57 : static const muse::UriQuery DEV_SHOW_CONTROLS_URI("muse://devtools/keynav/controls?modal=false");
58 :
59 : using MoveDirection = NavigationController::MoveDirection;
60 : using Event = INavigation::Event;
61 : using ActivationType = INavigation::ActivationType;
62 :
63 : // algorithms
64 : template<class T>
65 136 : static T* findNearestEnabled(const std::set<T*>& set, const INavigation::Index& currentIndex, MoveDirection direction)
66 : {
67 139 : TRACEFUNC;
68 136 : T* ret = nullptr;
69 476 : for (T* v : set) {
70 340 : if (!v->enabled()) {
71 3 : continue;
72 : }
73 :
74 337 : switch (direction) {
75 265 : case MoveDirection::First: {
76 265 : if (!ret) {
77 106 : ret = v;
78 106 : continue;
79 : }
80 :
81 159 : if (v->index().row <= ret->index().row) {
82 156 : if (v->index().column <= ret->index().column) {
83 67 : ret = v;
84 67 : continue;
85 : }
86 : }
87 92 : } break;
88 14 : case MoveDirection::Last: {
89 14 : if (!ret) {
90 6 : ret = v;
91 6 : continue;
92 : }
93 :
94 8 : if (v->index().row >= ret->index().row) {
95 4 : if (v->index().column >= ret->index().column) {
96 1 : ret = v;
97 1 : continue;
98 : }
99 : }
100 7 : } break;
101 39 : case MoveDirection::Right: {
102 39 : if (v->index().row != currentIndex.row) {
103 4 : continue;
104 : }
105 :
106 35 : if (v->index().column > currentIndex.column) {
107 7 : if (!ret) {
108 7 : ret = v;
109 7 : continue;
110 : }
111 :
112 0 : if (v->index().column < ret->index().column) {
113 0 : ret = v;
114 : }
115 : }
116 28 : } break;
117 7 : case MoveDirection::Left: {
118 7 : if (v->index().row != currentIndex.row) {
119 3 : continue;
120 : }
121 :
122 4 : if (v->index().column < currentIndex.column) {
123 1 : if (!ret) {
124 1 : ret = v;
125 1 : continue;
126 : }
127 :
128 0 : if (v->index().column > ret->index().column) {
129 0 : ret = v;
130 : }
131 : }
132 3 : } break;
133 6 : case MoveDirection::Down: {
134 6 : if (v->index().column != currentIndex.column) {
135 4 : continue;
136 : }
137 :
138 2 : if (v->index().row > currentIndex.row) {
139 0 : if (!ret) {
140 0 : ret = v;
141 0 : continue;
142 : }
143 :
144 0 : if (v->index().row < ret->index().row) {
145 0 : ret = v;
146 : }
147 : }
148 2 : } break;
149 6 : case MoveDirection::Up: {
150 6 : if (v->index().column != currentIndex.column) {
151 3 : continue;
152 : }
153 :
154 3 : if (v->index().row < currentIndex.row) {
155 0 : if (!ret) {
156 0 : ret = v;
157 0 : continue;
158 : }
159 :
160 0 : if (v->index().row > ret->index().row) {
161 0 : ret = v;
162 : }
163 : }
164 3 : } break;
165 : }
166 : }
167 :
168 136 : return ret;
169 136 : }
170 :
171 : template<class T>
172 0 : static T* firstEnabled(const std::set<T*>& set, const INavigation::Index& currentIndex, MoveDirection direction)
173 : {
174 0 : if (set.empty()) {
175 0 : return nullptr;
176 : }
177 :
178 0 : IF_ASSERT_FAILED(direction == MoveDirection::Right || direction == MoveDirection::Down) {
179 0 : return nullptr;
180 : }
181 :
182 0 : return findNearestEnabled<T>(set, currentIndex, direction);
183 : }
184 :
185 : template<class T>
186 107 : static T* firstEnabled(const std::set<T*>& set)
187 : {
188 107 : if (set.empty()) {
189 1 : return nullptr;
190 : }
191 :
192 106 : return findNearestEnabled<T>(set, INavigation::Index(), MoveDirection::First);
193 : }
194 :
195 : template<class T>
196 0 : static T* lastEnabled(const std::set<T*>& set, const INavigation::Index& currentIndex, MoveDirection direction)
197 : {
198 0 : if (set.empty()) {
199 0 : return nullptr;
200 : }
201 :
202 0 : IF_ASSERT_FAILED(direction == MoveDirection::Left || (direction == MoveDirection::Up)) {
203 0 : return nullptr;
204 : }
205 :
206 0 : return findNearestEnabled<T>(set, currentIndex, direction);
207 : }
208 :
209 : template<class T>
210 7 : static T* lastEnabled(const std::set<T*>& set)
211 : {
212 7 : if (set.empty()) {
213 1 : return nullptr;
214 : }
215 :
216 6 : return findNearestEnabled<T>(set, INavigation::Index(), MoveDirection::Last);
217 : }
218 :
219 : template<class T>
220 23 : static T* nextEnabled(const std::set<T*>& set, const INavigation::Index& currentIndex,
221 : MoveDirection direction = MoveDirection::Right)
222 : {
223 23 : if (set.empty()) {
224 0 : return nullptr;
225 : }
226 :
227 23 : return findNearestEnabled<T>(set, currentIndex, direction);
228 : }
229 :
230 : template<class T>
231 1 : static T* prevEnabled(const std::set<T*>& set, const INavigation::Index& currentIndex, MoveDirection direction = MoveDirection::Left)
232 : {
233 1 : if (set.empty()) {
234 0 : return nullptr;
235 : }
236 :
237 1 : return findNearestEnabled<T>(set, currentIndex, direction);
238 : }
239 :
240 : template<class T>
241 221 : static T* findActive(const std::set<T*>& set)
242 : {
243 633 : auto it = std::find_if(set.cbegin(), set.cend(), [](const T* s) {
244 412 : return s->active();
245 : });
246 :
247 221 : if (it != set.cend()) {
248 125 : return *it;
249 : }
250 :
251 96 : return nullptr;
252 : }
253 :
254 : template<class T>
255 0 : static T* findByName(const std::set<T*>& set, const QString& name)
256 : {
257 0 : auto it = std::find_if(set.cbegin(), set.cend(), [name](const T* s) {
258 0 : return s->name() == name;
259 : });
260 :
261 0 : if (it != set.cend()) {
262 0 : return *it;
263 : }
264 :
265 0 : return nullptr;
266 : }
267 :
268 : template<class T>
269 0 : static T* findByIndex(const std::set<T*>& set, const INavigation::Index& idx)
270 : {
271 0 : auto it = std::find_if(set.cbegin(), set.cend(), [idx](const T* s) {
272 0 : return s->index() == idx;
273 : });
274 :
275 0 : if (it != set.cend()) {
276 0 : return *it;
277 : }
278 :
279 0 : return nullptr;
280 : }
281 :
282 32 : void NavigationController::init()
283 : {
284 32 : auto d = dispatcher();
285 32 : d->onRequest(this, ESCAPE_COMMAND, [this]() { onEscape(); return muse::make_ok(); });
286 :
287 50 : d->onRequest(this, NEXT_SECTION_COMMAND, [this]() { navigateTo(NavigationType::NextSection); return muse::make_ok(); });
288 35 : d->onRequest(this, PREV_SECTION_COMMAND, [this]() { navigateTo(NavigationType::PrevSection); return muse::make_ok(); });
289 39 : d->onRequest(this, NEXT_PANEL_COMMAND, [this]() { navigateTo(NavigationType::NextPanel); return muse::make_ok(); });
290 35 : d->onRequest(this, PREV_PANEL_COMMAND, [this]() { navigateTo(NavigationType::PrevPanel); return muse::make_ok(); });
291 32 : d->onRequest(this, NEXT_TAB_COMMAND, [this]() { navigateTo(NavigationType::NextPanel); return muse::make_ok(); });
292 32 : d->onRequest(this, PREV_TAB_COMMAND, [this]() { navigateTo(NavigationType::PrevPanel); return muse::make_ok(); });
293 :
294 34 : d->onRequest(this, RIGHT_COMMAND, [this]() { navigateTo(NavigationType::Right); return muse::make_ok(); });
295 33 : d->onRequest(this, LEFT_COMMAND, [this]() { navigateTo(NavigationType::Left); return muse::make_ok(); });
296 33 : d->onRequest(this, UP_COMMAND, [this]() { navigateTo(NavigationType::Up); return muse::make_ok(); });
297 33 : d->onRequest(this, DOWN_COMMAND, [this]() { navigateTo(NavigationType::Down); return muse::make_ok(); });
298 :
299 32 : d->onRequest(this, FIRST_CONTROL_COMMAND, [this]() { navigateTo(NavigationType::FirstControl); return muse::make_ok(); });
300 32 : d->onRequest(this, LAST_CONTROL_COMMAND, [this]() { navigateTo(NavigationType::LastControl); return muse::make_ok(); });
301 32 : d->onRequest(this, NEXTROW_CONTROL_COMMAND, [this]() { navigateTo(NavigationType::NextRowControl); return muse::make_ok(); });
302 32 : d->onRequest(this, PREVROW_CONTROL_COMMAND, [this]() { navigateTo(NavigationType::PrevRowControl); return muse::make_ok(); });
303 :
304 32 : d->onRequest(this, TRIGGER_CONTROL_COMMAND, [this]() { doTriggerControl(); return muse::make_ok(); });
305 :
306 : // compat
307 : {
308 : static const std::map<std::string, rcommand::Command> compatActionToCommand = {
309 : { "nav-next-section", NEXT_SECTION_COMMAND },
310 : { "nav-prev-section", PREV_SECTION_COMMAND },
311 : { "nav-next-panel", NEXT_PANEL_COMMAND },
312 : { "nav-prev-panel", PREV_PANEL_COMMAND },
313 : { "nav-next-tab", NEXT_PANEL_COMMAND },
314 : { "nav-prev-tab", PREV_PANEL_COMMAND },
315 : { "nav-trigger-control", TRIGGER_CONTROL_COMMAND },
316 : { "nav-right", RIGHT_COMMAND },
317 : { "nav-left", LEFT_COMMAND },
318 : { "nav-up", UP_COMMAND },
319 : { "nav-down", DOWN_COMMAND },
320 : { "nav-escape", ESCAPE_COMMAND },
321 : { "nav-first-control", FIRST_CONTROL_COMMAND },
322 : { "nav-last-control", LAST_CONTROL_COMMAND },
323 : { "nav-nextrow-control", NEXTROW_CONTROL_COMMAND },
324 : { "nav-prevrow-control", PREVROW_CONTROL_COMMAND },
325 50 : };
326 :
327 32 : auto ad = actionsDispatcher();
328 544 : for (const auto& [action, command] : compatActionToCommand) {
329 512 : ad->reg(this, action, [d, command]() { d->dispatch(command); });
330 : }
331 32 : }
332 :
333 32 : qApp->installEventFilter(this);
334 33 : }
335 :
336 64 : void NavigationController::reg(INavigationSection* section)
337 : {
338 : //! TODO add check on valid state
339 65 : TRACEFUNC;
340 64 : m_sections.insert(section);
341 64 : section->setOnActiveRequested([this](INavigationSection* section, INavigationPanel* panel, INavigationControl* control,
342 : bool enableHighlight, ActivationType activationType) {
343 30 : if (control && activationType == ActivationType::ByMouse) {
344 2 : if (section->type() != INavigationSection::Type::Exclusive) {
345 1 : return;
346 : }
347 : }
348 :
349 29 : if (enableHighlight) {
350 0 : setIsHighlight(true);
351 : }
352 :
353 29 : onActiveRequested(section, panel, control);
354 : });
355 64 : }
356 :
357 66 : void NavigationController::unreg(INavigationSection* section)
358 : {
359 67 : TRACEFUNC;
360 66 : m_sections.erase(section);
361 66 : section->setOnActiveRequested(nullptr);
362 66 : section->enabledChanged().disconnect(this);
363 :
364 66 : if (m_prioritySection == section) {
365 2 : m_prioritySection = nullptr;
366 : }
367 66 : }
368 :
369 0 : const std::set<INavigationSection*>& NavigationController::sections() const
370 : {
371 0 : return m_sections;
372 : }
373 :
374 6 : bool NavigationController::isHighlight() const
375 : {
376 6 : return m_isHighlight;
377 : }
378 :
379 40 : void NavigationController::setIsHighlight(bool isHighlight)
380 : {
381 40 : if (m_isHighlight == isHighlight) {
382 14 : return;
383 : }
384 :
385 26 : m_isHighlight = isHighlight;
386 26 : m_highlightChanged.notify();
387 : }
388 :
389 0 : muse::async::Notification NavigationController::highlightChanged() const
390 : {
391 0 : return m_highlightChanged;
392 : }
393 :
394 0 : void NavigationController::setIsResetOnMousePress(bool arg)
395 : {
396 0 : m_isResetOnMousePress = arg;
397 0 : }
398 :
399 3 : void NavigationController::resetIfNeed(QObject* watched)
400 : {
401 3 : if (!m_isResetOnMousePress) {
402 0 : return;
403 : }
404 :
405 : #ifdef MUSE_MODULE_DIAGNOSTICS
406 3 : if (muse::diagnostics::isDiagnosticHierarchy(watched)) {
407 0 : return;
408 : }
409 : #endif
410 :
411 3 : auto activeCtrl = activeControl();
412 3 : if (activeCtrl && activeCtrl != m_defaultNavigationControl && watched == qApp) {
413 3 : resetNavigation();
414 : }
415 :
416 3 : setIsHighlight(false);
417 : }
418 :
419 2218 : bool NavigationController::eventFilter(QObject* watched, QEvent* event)
420 : {
421 2218 : if (event->type() == QEvent::MouseButtonPress) {
422 3 : resetIfNeed(watched);
423 : }
424 :
425 2218 : return QObject::eventFilter(watched, event);
426 : }
427 :
428 36 : void NavigationController::navigateTo(NavigationController::NavigationType type)
429 : {
430 36 : switch (type) {
431 18 : case NavigationType::NextSection:
432 18 : goToNextSection();
433 18 : break;
434 3 : case NavigationType::PrevSection:
435 3 : goToPrevSection(false);
436 3 : break;
437 0 : case NavigationType::PrevSectionActiveLastPanel:
438 0 : goToPrevSection(true);
439 0 : break;
440 7 : case NavigationType::NextPanel:
441 7 : goToNextPanel();
442 7 : break;
443 3 : case NavigationType::PrevPanel:
444 3 : goToPrevPanel();
445 3 : break;
446 1 : case NavigationType::Left:
447 1 : onLeft();
448 1 : break;
449 2 : case NavigationType::Right:
450 2 : onRight();
451 2 : break;
452 1 : case NavigationType::Up:
453 1 : onUp();
454 1 : break;
455 1 : case NavigationType::Down:
456 1 : onDown();
457 1 : break;
458 0 : case NavigationType::FirstControl:
459 0 : goToFirstControl();
460 0 : break;
461 0 : case NavigationType::LastControl:
462 0 : goToLastControl();
463 0 : break;
464 0 : case NavigationType::NextRowControl:
465 0 : goToNextRowControl();
466 0 : break;
467 0 : case NavigationType::PrevRowControl:
468 0 : goToPrevRowControl();
469 0 : break;
470 : }
471 :
472 36 : setIsHighlight(true);
473 36 : }
474 :
475 3 : void NavigationController::resetNavigation()
476 : {
477 : MYLOG() << "===";
478 3 : INavigationSection* activeSec = this->activeSection();
479 3 : if (!activeSec) {
480 0 : return;
481 : }
482 :
483 3 : INavigationPanel* activePnl = findActive(activeSec->panels());
484 3 : if (activePnl) {
485 3 : INavigationControl* activeCtrl = findActive(activePnl->controls());
486 3 : if (activeCtrl) {
487 3 : activeCtrl->setActive(false);
488 : }
489 :
490 3 : activePnl->setActive(false);
491 : }
492 :
493 3 : activeSec->setActive(false);
494 :
495 3 : if (m_defaultNavigationControl) {
496 2 : INavigationPanel* panel = m_defaultNavigationControl->panel();
497 2 : INavigationSection* section = panel ? panel->section() : nullptr;
498 2 : if (section->enabled()) {
499 1 : m_defaultNavigationControl->setActive(true);
500 1 : m_navigationChanged.notify();
501 : }
502 : } else {
503 1 : doActivateFirst();
504 : }
505 : }
506 :
507 25 : void NavigationController::doActivateSection(INavigationSection* sect, bool isActivateLastPanel)
508 : {
509 26 : TRACEFUNC;
510 25 : IF_ASSERT_FAILED(sect) {
511 0 : return;
512 : }
513 :
514 75 : for (INavigationPanel* panel : sect->panels()) {
515 50 : doDeactivatePanel(panel);
516 : }
517 :
518 25 : sect->setActive(true);
519 : MYLOG() << "activated section: " << sect->name() << ", order: " << sect->index().order();
520 :
521 25 : if (m_prioritySection == sect) {
522 0 : m_prioritySection = nullptr;
523 : }
524 :
525 25 : INavigationPanel* toActivatePanel = nullptr;
526 25 : if (isActivateLastPanel) {
527 1 : toActivatePanel = lastEnabled(sect->panels());
528 : } else {
529 24 : toActivatePanel = firstEnabled(sect->panels());
530 : }
531 :
532 25 : if (toActivatePanel) {
533 25 : doActivatePanel(toActivatePanel);
534 : }
535 25 : }
536 :
537 20 : void NavigationController::doDeactivateSection(INavigationSection* sect)
538 : {
539 21 : TRACEFUNC;
540 20 : IF_ASSERT_FAILED(sect) {
541 0 : return;
542 : }
543 :
544 20 : saveLastActiveControl(sect);
545 :
546 56 : for (INavigationPanel* panel : sect->panels()) {
547 36 : doDeactivatePanel(panel);
548 : }
549 :
550 20 : sect->setActive(false);
551 20 : }
552 :
553 32 : void NavigationController::doActivatePanel(INavigationPanel* panel)
554 : {
555 33 : TRACEFUNC;
556 32 : IF_ASSERT_FAILED(panel) {
557 0 : return;
558 : }
559 :
560 128 : for (INavigationControl* ctrl : panel->controls()) {
561 96 : doDeactivateControl(ctrl);
562 : }
563 :
564 32 : INavigation::EventPtr event = INavigation::Event::make(INavigation::Event::AboutActive);
565 32 : panel->onEvent(event);
566 :
567 32 : panel->setActive(true);
568 : MYLOG() << "activated panel: " << panel->name() << ", order: " << panel->index().order();
569 :
570 32 : INavigationControl* control = nullptr;
571 32 : QString ctrlName = event->data.value("controlName").toString();
572 32 : if (!ctrlName.isEmpty()) {
573 0 : control = findByName(panel->controls(), ctrlName);
574 0 : IF_ASSERT_FAILED(control) {
575 : }
576 : }
577 :
578 32 : if (!control) {
579 32 : QVariantList idxVal = event->data.value("controlIndex").toList();
580 32 : if (idxVal.count() == 2) {
581 0 : INavigation::Index ctrlIndex;
582 0 : ctrlIndex.row = idxVal.at(0).toInt();
583 0 : ctrlIndex.column = idxVal.at(1).toInt();
584 0 : control = findByIndex(panel->controls(), ctrlIndex);
585 0 : if (!control) {
586 0 : bool isOptional = event->data.value("controlOptional").toBool();
587 0 : if (!isOptional) {
588 0 : IF_ASSERT_FAILED(control) {
589 : }
590 : }
591 : }
592 : }
593 32 : }
594 :
595 32 : if (!control) {
596 32 : control = firstEnabled(panel->controls());
597 : }
598 :
599 32 : if (control) {
600 32 : doActivateControl(control);
601 : }
602 32 : }
603 :
604 91 : void NavigationController::doDeactivatePanel(INavigationPanel* panel)
605 : {
606 92 : TRACEFUNC;
607 91 : IF_ASSERT_FAILED(panel) {
608 0 : return;
609 : }
610 :
611 364 : for (INavigationControl* ctr : panel->controls()) {
612 273 : doDeactivateControl(ctr);
613 : }
614 :
615 91 : if (panel->active()) {
616 22 : panel->setActive(false);
617 : }
618 91 : }
619 :
620 37 : void NavigationController::doActivateControl(INavigationControl* ctrl)
621 : {
622 38 : TRACEFUNC;
623 37 : IF_ASSERT_FAILED(ctrl) {
624 0 : return;
625 : }
626 :
627 37 : ctrl->setActive(true);
628 : MYLOG() << "activated control: " << ctrl->name() << ", row: " << ctrl->index().row << ", column: " << ctrl->index().column;
629 37 : }
630 :
631 374 : void NavigationController::doDeactivateControl(INavigationControl* ctrl)
632 : {
633 375 : TRACEFUNC;
634 374 : IF_ASSERT_FAILED(ctrl) {
635 0 : return;
636 : }
637 :
638 374 : if (ctrl->active()) {
639 31 : ctrl->setActive(false);
640 : }
641 374 : }
642 :
643 4 : void NavigationController::doActivateFirst()
644 : {
645 4 : if (m_sections.empty()) {
646 0 : return;
647 : }
648 :
649 4 : INavigationSection* first = firstEnabled(m_sections);
650 4 : if (first) {
651 4 : doActivateSection(first);
652 : }
653 : }
654 :
655 3 : void NavigationController::doActivateLast()
656 : {
657 3 : if (m_sections.empty()) {
658 0 : return;
659 : }
660 :
661 3 : INavigationSection* last = lastEnabled(m_sections);
662 3 : if (last) {
663 3 : doActivateSection(last);
664 : }
665 : }
666 :
667 28 : INavigationSection* NavigationController::activeSection() const
668 : {
669 29 : TRACEFUNC;
670 28 : if (m_sections.empty()) {
671 0 : return nullptr;
672 : }
673 28 : return findActive(m_sections);
674 28 : }
675 :
676 15 : INavigationPanel* NavigationController::activePanel() const
677 : {
678 16 : TRACEFUNC;
679 15 : INavigationSection* activeSec = activeSection();
680 15 : if (!activeSec) {
681 1 : return nullptr;
682 : }
683 14 : return findActive(activeSec->panels());
684 15 : }
685 :
686 10 : INavigationControl* NavigationController::activeControl() const
687 : {
688 11 : TRACEFUNC;
689 10 : INavigationPanel* activePanel = this->activePanel();
690 10 : if (!activePanel) {
691 1 : return nullptr;
692 : }
693 9 : return findActive(activePanel->controls());
694 10 : }
695 :
696 0 : const INavigationSection* NavigationController::findSection(const std::string& sectionName) const
697 : {
698 0 : const INavigationSection* sec = findByName(m_sections, QString::fromStdString(sectionName));
699 0 : return sec;
700 : }
701 :
702 0 : const INavigationPanel* NavigationController::findPanel(const std::string& sectionName, const std::string& panelName) const
703 : {
704 0 : const INavigationSection* sec = findByName(m_sections, QString::fromStdString(sectionName));
705 0 : const INavigationPanel* pnl = sec ? findByName(sec->panels(), QString::fromStdString(panelName)) : nullptr;
706 0 : return pnl;
707 : }
708 :
709 0 : const INavigationControl* NavigationController::findControl(const std::string& sectionName, const std::string& panelName,
710 : const std::string& controlName) const
711 : {
712 0 : const INavigationSection* sec = findByName(m_sections, QString::fromStdString(sectionName));
713 0 : const INavigationPanel* pnl = sec ? findByName(sec->panels(), QString::fromStdString(panelName)) : nullptr;
714 0 : const INavigationControl* ctrl = pnl ? findByName(pnl->controls(), QString::fromStdString(controlName)) : nullptr;
715 :
716 0 : return ctrl;
717 : }
718 :
719 3 : void NavigationController::setDefaultNavigationControl(INavigationControl* control)
720 : {
721 3 : m_defaultNavigationControl = control;
722 3 : }
723 :
724 12 : void NavigationController::setPrioritySection(INavigationSection* section)
725 : {
726 12 : if (!section) {
727 0 : m_prioritySection = nullptr;
728 0 : return;
729 : }
730 :
731 12 : if (m_sections.find(section) == m_sections.end()) {
732 0 : LOGW() << "unable to set priority section, section is not registered, name: " << section->name();
733 0 : return;
734 : }
735 :
736 12 : m_prioritySection = section;
737 :
738 12 : section->enabledChanged().onReceive(this, [this, section](bool enabled) {
739 6 : if (!enabled && section->active()) {
740 3 : restoreLastActiveControl(section);
741 : }
742 6 : }, async::Asyncable::Mode::SetReplace);
743 : }
744 :
745 20 : INavigationSection* NavigationController::takePrioritySection(const INavigationSection* activeSec)
746 : {
747 20 : INavigationSection* prioritySec = m_prioritySection;
748 20 : m_prioritySection = nullptr;
749 :
750 20 : if (!prioritySec || prioritySec == activeSec || !prioritySec->enabled()) {
751 13 : return nullptr;
752 : }
753 :
754 7 : return prioritySec;
755 : }
756 :
757 24 : void NavigationController::saveLastActiveControl(INavigationSection* sect)
758 : {
759 24 : if (!sect->enabled() || sect == m_prioritySection) {
760 5 : return;
761 : }
762 :
763 19 : INavigationPanel* activePanel = findActive(sect->panels());
764 19 : INavigationControl* ctrl = activePanel ? findActive(activePanel->controls()) : nullptr;
765 19 : if (!ctrl) {
766 1 : return;
767 : }
768 :
769 18 : m_lastActiveControl = ctrl;
770 : }
771 :
772 3 : INavigationControl* NavigationController::takeLastActiveControl()
773 : {
774 3 : INavigationControl* ctrl = std::exchange(m_lastActiveControl, nullptr);
775 :
776 3 : const bool isAvailable = ctrl && std::any_of(m_sections.cbegin(), m_sections.cend(), [ctrl](const INavigationSection* sect) {
777 5 : if (!sect->enabled()) {
778 2 : return false;
779 : }
780 :
781 3 : const std::set<INavigationPanel*>& panels = sect->panels();
782 3 : return std::any_of(panels.cbegin(), panels.cend(), [ctrl](const INavigationPanel* panel) {
783 5 : return panel->enabled() && muse::contains(panel->controls(), ctrl);
784 3 : });
785 3 : });
786 :
787 3 : return isAvailable && ctrl->enabled() ? ctrl : m_defaultNavigationControl;
788 : }
789 :
790 3 : void NavigationController::restoreLastActiveControl(INavigationSection* sect)
791 : {
792 3 : INavigationControl* ctrl = takeLastActiveControl();
793 :
794 3 : doDeactivateSection(sect);
795 :
796 3 : if (ctrl) {
797 3 : ctrl->requestActive();
798 : }
799 :
800 3 : m_navigationChanged.notify();
801 3 : }
802 :
803 0 : muse::async::Notification NavigationController::navigationChanged() const
804 : {
805 0 : return m_navigationChanged;
806 : }
807 :
808 20 : void NavigationController::goToNextSection()
809 : {
810 21 : TRACEFUNC;
811 : MYLOG() << "====";
812 20 : if (m_sections.empty()) {
813 0 : return;
814 : }
815 :
816 20 : INavigationSection* activeSec = findActive(m_sections);
817 20 : if (activeSec && activeSec->type() == INavigationSection::Type::Exclusive) {
818 3 : INavigationPanel* first = firstEnabled(activeSec->panels());
819 3 : if (first) {
820 3 : doActivatePanel(first);
821 3 : m_navigationChanged.notify();
822 : }
823 3 : return;
824 : }
825 :
826 17 : if (INavigationSection* prioritySec = takePrioritySection(activeSec)) {
827 6 : if (activeSec) {
828 4 : doDeactivateSection(activeSec);
829 : }
830 6 : doActivateSection(prioritySec);
831 6 : m_navigationChanged.notify();
832 6 : return;
833 : }
834 :
835 11 : if (!activeSec) { // no any active
836 1 : doActivateFirst();
837 1 : return;
838 : }
839 :
840 10 : doDeactivateSection(activeSec);
841 :
842 10 : INavigationSection* nextSec = nextEnabled(m_sections, activeSec->index());
843 10 : if (!nextSec) { // active is last
844 6 : nextSec = firstEnabled(m_sections); // the first to be the next
845 : }
846 10 : if (!nextSec) {
847 0 : LOGI() << "no enabled sections!";
848 0 : return;
849 : }
850 :
851 10 : LOGI() << "nextSec: " << nextSec->name() << ", enabled: " << nextSec->enabled();
852 :
853 10 : doActivateSection(nextSec);
854 :
855 10 : m_navigationChanged.notify();
856 20 : }
857 :
858 4 : void NavigationController::goToPrevSection(bool isActivateLastPanel)
859 : {
860 5 : TRACEFUNC;
861 : MYLOG() << "====";
862 4 : if (m_sections.empty()) {
863 0 : return;
864 : }
865 :
866 4 : INavigationSection* activeSec = findActive(m_sections);
867 4 : if (activeSec && activeSec->type() == INavigationSection::Type::Exclusive) {
868 1 : INavigationPanel* first = firstEnabled(activeSec->panels());
869 1 : if (first) {
870 1 : doActivatePanel(first);
871 1 : m_navigationChanged.notify();
872 : }
873 1 : return;
874 : }
875 :
876 3 : if (INavigationSection* prioritySec = takePrioritySection(activeSec)) {
877 1 : if (activeSec) {
878 1 : doDeactivateSection(activeSec);
879 : }
880 1 : doActivateSection(prioritySec);
881 1 : m_navigationChanged.notify();
882 1 : return;
883 : }
884 :
885 2 : if (!activeSec) { // no any active
886 1 : doActivateLast();
887 1 : return;
888 : }
889 :
890 1 : doDeactivateSection(activeSec);
891 :
892 1 : INavigationSection* prevSec = prevEnabled(m_sections, activeSec->index());
893 1 : if (!prevSec) { // active is first
894 0 : prevSec = lastEnabled(m_sections); // the last to be the prev
895 : }
896 :
897 1 : doActivateSection(prevSec, isActivateLastPanel);
898 :
899 1 : m_navigationChanged.notify();
900 4 : }
901 :
902 7 : void NavigationController::goToNextPanel()
903 : {
904 8 : TRACEFUNC;
905 : MYLOG() << "====";
906 7 : INavigationSection* activeSec = activeSection();
907 7 : if (!activeSec) {
908 2 : doActivateFirst();
909 2 : m_navigationChanged.notify();
910 2 : return;
911 : }
912 :
913 5 : INavigationPanel* activePanel = findActive(activeSec->panels());
914 5 : if (!activePanel) { // no any active
915 1 : INavigationPanel* first = firstEnabled(activeSec->panels());
916 1 : if (first) {
917 0 : doActivatePanel(first);
918 0 : m_navigationChanged.notify();
919 0 : return;
920 : }
921 :
922 : MYLOG() << "the active section " << activeSec->name() << " has no enabled panels, go to the next section";
923 1 : goToNextSection();
924 1 : return;
925 : }
926 :
927 4 : saveLastActiveControl(activeSec);
928 4 : doDeactivatePanel(activePanel);
929 :
930 4 : INavigationPanel* nextPanel = nextEnabled(activeSec->panels(), activePanel->index());
931 4 : if (nextPanel) {
932 2 : doActivatePanel(nextPanel);
933 2 : m_navigationChanged.notify();
934 2 : return;
935 : }
936 :
937 2 : if (activeSec->type() == INavigationSection::Type::Exclusive) {
938 1 : INavigationPanel* first = firstEnabled(activeSec->panels());
939 1 : if (first) {
940 1 : doActivatePanel(first);
941 1 : m_navigationChanged.notify();
942 : }
943 1 : return;
944 : }
945 :
946 : // active is last panel, go to first panel in next section
947 1 : goToNextSection();
948 7 : }
949 :
950 3 : void NavigationController::goToPrevPanel()
951 : {
952 4 : TRACEFUNC;
953 : MYLOG() << "====";
954 3 : INavigationSection* activeSec = activeSection();
955 3 : if (!activeSec) {
956 2 : doActivateLast();
957 2 : m_navigationChanged.notify();
958 2 : return;
959 : }
960 :
961 1 : INavigationPanel* activePanel = findActive(activeSec->panels());
962 1 : if (!activePanel) { // no any active
963 1 : INavigationPanel* lastPanel = lastEnabled(activeSec->panels());
964 1 : if (lastPanel) {
965 0 : doActivatePanel(lastPanel);
966 0 : m_navigationChanged.notify();
967 0 : return;
968 : }
969 :
970 : MYLOG() << "the active section " << activeSec->name() << " has no enabled panels, go to the prev section";
971 1 : goToPrevSection(true);
972 1 : return;
973 : }
974 :
975 0 : saveLastActiveControl(activeSec);
976 0 : doDeactivatePanel(activePanel);
977 :
978 0 : INavigationPanel* prevPanel = prevEnabled(activeSec->panels(), activePanel->index());
979 0 : if (prevPanel) {
980 0 : doActivatePanel(prevPanel);
981 0 : m_navigationChanged.notify();
982 0 : return;
983 : }
984 :
985 0 : if (activeSec->type() == INavigationSection::Type::Exclusive) {
986 0 : INavigationPanel* lastPanel = lastEnabled(activeSec->panels());
987 0 : if (lastPanel) {
988 0 : doActivatePanel(lastPanel);
989 0 : m_navigationChanged.notify();
990 : }
991 0 : return;
992 : }
993 :
994 : // active is first, go to last panel in prev section
995 0 : goToPrevSection(true);
996 3 : }
997 :
998 2 : void NavigationController::onRight()
999 : {
1000 3 : TRACEFUNC;
1001 2 : INavigationPanel* activePanel = this->activePanel();
1002 2 : if (!activePanel) {
1003 0 : return;
1004 : }
1005 :
1006 2 : INavigation::EventPtr e = Event::make(Event::Right);
1007 2 : activePanel->onEvent(e);
1008 2 : if (e->accepted) {
1009 0 : return;
1010 : }
1011 :
1012 2 : INavigationControl* activeCtrl = findActive(activePanel->controls());
1013 2 : if (activeCtrl) {
1014 2 : activeCtrl->onEvent(e);
1015 2 : if (e->accepted) {
1016 0 : return;
1017 : }
1018 : }
1019 :
1020 2 : goToControl(MoveDirection::Right, activePanel);
1021 2 : }
1022 :
1023 1 : void NavigationController::onLeft()
1024 : {
1025 2 : TRACEFUNC;
1026 1 : INavigationPanel* activePanel = this->activePanel();
1027 1 : if (!activePanel) {
1028 0 : return;
1029 : }
1030 :
1031 1 : INavigation::EventPtr e = Event::make(Event::Left);
1032 1 : activePanel->onEvent(e);
1033 1 : if (e->accepted) {
1034 0 : return;
1035 : }
1036 :
1037 1 : INavigationControl* activeCtrl = findActive(activePanel->controls());
1038 1 : if (activeCtrl) {
1039 1 : activeCtrl->onEvent(e);
1040 1 : if (e->accepted) {
1041 0 : return;
1042 : }
1043 : }
1044 :
1045 1 : goToControl(MoveDirection::Left, activePanel);
1046 1 : }
1047 :
1048 1 : void NavigationController::onDown()
1049 : {
1050 2 : TRACEFUNC;
1051 1 : INavigationPanel* activePanel = this->activePanel();
1052 1 : if (!activePanel) {
1053 0 : return;
1054 : }
1055 :
1056 1 : INavigation::EventPtr e = Event::make(Event::Down);
1057 1 : activePanel->onEvent(e);
1058 1 : if (e->accepted) {
1059 0 : return;
1060 : }
1061 :
1062 1 : INavigationControl* activeCtrl = findActive(activePanel->controls());
1063 1 : if (activeCtrl) {
1064 1 : activeCtrl->onEvent(e);
1065 1 : if (e->accepted) {
1066 0 : return;
1067 : }
1068 : }
1069 :
1070 1 : goToControl(MoveDirection::Down, activePanel);
1071 1 : }
1072 :
1073 1 : void NavigationController::onUp()
1074 : {
1075 2 : TRACEFUNC;
1076 1 : INavigationPanel* activePanel = this->activePanel();
1077 1 : if (!activePanel) {
1078 0 : return;
1079 : }
1080 :
1081 1 : INavigation::EventPtr e = Event::make(Event::Up);
1082 1 : activePanel->onEvent(e);
1083 1 : if (e->accepted) {
1084 0 : return;
1085 : }
1086 :
1087 1 : INavigationControl* activeCtrl = findActive(activePanel->controls());
1088 1 : if (activeCtrl) {
1089 1 : activeCtrl->onEvent(e);
1090 1 : if (e->accepted) {
1091 0 : return;
1092 : }
1093 : }
1094 :
1095 1 : goToControl(MoveDirection::Up, activePanel);
1096 1 : }
1097 :
1098 0 : void NavigationController::onEscape()
1099 : {
1100 0 : TRACEFUNC;
1101 : MYLOG() << "====";
1102 0 : INavigationSection* activeSec = activeSection();
1103 0 : if (!activeSec) {
1104 0 : return;
1105 : }
1106 :
1107 0 : INavigation::EventPtr e = Event::make(Event::Escape);
1108 0 : activeSec->onEvent(e);
1109 0 : if (e->accepted) {
1110 0 : return;
1111 : }
1112 :
1113 0 : INavigationPanel* activePanel = findActive(activeSec->panels());
1114 0 : if (!activePanel) {
1115 0 : return;
1116 : }
1117 :
1118 0 : activePanel->onEvent(e);
1119 0 : if (e->accepted) {
1120 0 : return;
1121 : }
1122 :
1123 0 : INavigationControl* activeCtrl = findActive(activePanel->controls());
1124 0 : if (!activeCtrl) {
1125 0 : return;
1126 : }
1127 :
1128 0 : activeCtrl->onEvent(e);
1129 0 : if (e->accepted) {
1130 0 : return;
1131 : }
1132 :
1133 0 : activeCtrl->setActive(false);
1134 :
1135 0 : if (m_defaultNavigationControl) {
1136 0 : INavigationPanel* defaultPanel = m_defaultNavigationControl->panel();
1137 0 : if (!defaultPanel) {
1138 0 : return;
1139 : }
1140 :
1141 0 : INavigationSection* defaultSection = defaultPanel->section();
1142 0 : if (!defaultSection) {
1143 0 : return;
1144 : }
1145 :
1146 0 : onActiveRequested(defaultSection, defaultPanel, m_defaultNavigationControl, true);
1147 : } else {
1148 0 : m_navigationChanged.notify();
1149 : }
1150 0 : }
1151 :
1152 0 : void NavigationController::goToFirstControl()
1153 : {
1154 : MYLOG() << "====";
1155 0 : goToControl(MoveDirection::First);
1156 0 : }
1157 :
1158 0 : void NavigationController::goToLastControl()
1159 : {
1160 : MYLOG() << "====";
1161 0 : goToControl(MoveDirection::Last);
1162 0 : }
1163 :
1164 0 : void NavigationController::goToNextRowControl()
1165 : {
1166 0 : TRACEFUNC;
1167 : MYLOG() << "====";
1168 0 : INavigationPanel* activePanel = this->activePanel();
1169 0 : if (!activePanel) {
1170 0 : return;
1171 : }
1172 :
1173 0 : INavigationControl* activeControl = findActive(activePanel->controls());
1174 0 : if (activeControl) {
1175 0 : doDeactivateControl(activeControl);
1176 : }
1177 :
1178 0 : INavigationControl* toControl = nullptr;
1179 0 : if (activeControl) {
1180 0 : INavigation::Index index = activeControl->index();
1181 0 : index.column = 0;
1182 0 : toControl = nextEnabled(activePanel->controls(), index, MoveDirection::Down);
1183 0 : if (!toControl) { // last row
1184 0 : toControl = firstEnabled(activePanel->controls());
1185 : }
1186 : } else {
1187 0 : toControl = firstEnabled(activePanel->controls());
1188 : }
1189 :
1190 0 : if (toControl) {
1191 0 : doActivateControl(toControl);
1192 : }
1193 :
1194 0 : m_navigationChanged.notify();
1195 0 : }
1196 :
1197 0 : void NavigationController::goToPrevRowControl()
1198 : {
1199 0 : TRACEFUNC;
1200 : MYLOG() << "====";
1201 0 : INavigationPanel* activePanel = this->activePanel();
1202 0 : if (!activePanel) {
1203 0 : return;
1204 : }
1205 :
1206 0 : INavigationControl* activeControl = findActive(activePanel->controls());
1207 0 : if (activeControl) {
1208 0 : doDeactivateControl(activeControl);
1209 : }
1210 :
1211 0 : INavigationControl* toControl = nullptr;
1212 0 : if (activeControl) {
1213 0 : INavigation::Index index = activeControl->index();
1214 0 : index.column = 0;
1215 0 : toControl = prevEnabled(activePanel->controls(), index, MoveDirection::Up);
1216 0 : if (!toControl) { // first row
1217 0 : toControl = lastEnabled(activePanel->controls());
1218 : }
1219 : } else {
1220 0 : toControl = lastEnabled(activePanel->controls());
1221 : }
1222 :
1223 0 : if (toControl) {
1224 0 : doActivateControl(toControl);
1225 : }
1226 :
1227 0 : m_navigationChanged.notify();
1228 0 : }
1229 :
1230 5 : void NavigationController::goToControl(MoveDirection direction, INavigationPanel* activePanel)
1231 : {
1232 6 : TRACEFUNC;
1233 : MYLOG() << "direction: " << direction;
1234 5 : if (!activePanel) {
1235 0 : activePanel = this->activePanel();
1236 : }
1237 :
1238 5 : if (!activePanel) {
1239 0 : return;
1240 : }
1241 :
1242 5 : INavigationControl* activeControl = findActive(activePanel->controls());
1243 5 : INavigationControl* toControl = nullptr;
1244 :
1245 5 : bool tryOtherOrientation = activePanel->direction() == INavigationPanel::Direction::Horizontal
1246 5 : || activePanel->direction() == INavigationPanel::Direction::Vertical;
1247 :
1248 5 : switch (direction) {
1249 0 : case MoveDirection::First: {
1250 0 : toControl = firstEnabled(activePanel->controls());
1251 0 : } break;
1252 0 : case MoveDirection::Last: {
1253 0 : toControl = lastEnabled(activePanel->controls());
1254 0 : } break;
1255 2 : case MoveDirection::Right: {
1256 2 : if (!activeControl) { // no any active
1257 0 : toControl = firstEnabled(activePanel->controls(), INavigation::Index(), MoveDirection::Right);
1258 : } else {
1259 2 : toControl = nextEnabled(activePanel->controls(), activeControl->index(), MoveDirection::Right);
1260 2 : if (!toControl && tryOtherOrientation) {
1261 1 : toControl = nextEnabled(activePanel->controls(), activeControl->index(), MoveDirection::Down);
1262 : }
1263 2 : if (!toControl) { // active is last
1264 1 : if (activePanel->direction() == INavigationPanel::Direction::Horizontal) {
1265 1 : toControl = firstEnabled(activePanel->controls());
1266 1 : break;
1267 : }
1268 :
1269 0 : INavigation::Index index = activeControl->index();
1270 0 : index.column = -1;
1271 0 : toControl = firstEnabled(activePanel->controls(), index, MoveDirection::Right); // the first to be the next
1272 :
1273 0 : if (!toControl && tryOtherOrientation) {
1274 0 : index = activeControl->index();
1275 0 : index.row = -1;
1276 0 : toControl = nextEnabled(activePanel->controls(), index, MoveDirection::Down);
1277 : }
1278 : }
1279 : }
1280 1 : } break;
1281 1 : case MoveDirection::Down: {
1282 1 : if (!activeControl) { // no any active
1283 0 : toControl = firstEnabled(activePanel->controls(), INavigation::Index(), MoveDirection::Down);
1284 : } else {
1285 1 : toControl = nextEnabled(activePanel->controls(), activeControl->index(), MoveDirection::Down);
1286 1 : if (!toControl && tryOtherOrientation) {
1287 1 : toControl = nextEnabled(activePanel->controls(), activeControl->index(), MoveDirection::Right);
1288 : }
1289 1 : if (!toControl) { // active is last
1290 1 : if (activePanel->direction() == INavigationPanel::Direction::Vertical) {
1291 1 : toControl = firstEnabled(activePanel->controls());
1292 1 : break;
1293 : }
1294 :
1295 0 : INavigation::Index index = activeControl->index();
1296 0 : index.row = -1;
1297 0 : toControl = firstEnabled(activePanel->controls(), index, MoveDirection::Down); // the first to be the next
1298 :
1299 0 : if (!toControl && tryOtherOrientation) {
1300 0 : index = activeControl->index();
1301 0 : index.column = -1;
1302 0 : toControl = nextEnabled(activePanel->controls(), index, MoveDirection::Right);
1303 : }
1304 : }
1305 : }
1306 0 : } break;
1307 1 : case MoveDirection::Left: {
1308 1 : if (!activeControl) { // no any active
1309 0 : toControl = lastEnabled(activePanel->controls(), INavigation::Index(), MoveDirection::Left);
1310 : } else {
1311 1 : toControl = nextEnabled(activePanel->controls(), activeControl->index(), MoveDirection::Left);
1312 1 : if (!toControl && tryOtherOrientation) {
1313 1 : toControl = nextEnabled(activePanel->controls(), activeControl->index(), MoveDirection::Up);
1314 : }
1315 1 : if (!toControl) { // active is first
1316 1 : if (activePanel->direction() == INavigationPanel::Direction::Horizontal) {
1317 1 : toControl = lastEnabled(activePanel->controls());
1318 1 : break;
1319 : }
1320 :
1321 0 : INavigation::Index index = activeControl->index();
1322 0 : index.column = std::numeric_limits<int>::max();
1323 0 : toControl = lastEnabled(activePanel->controls(), index, MoveDirection::Left); // the last to be the next
1324 :
1325 0 : if (!toControl && tryOtherOrientation) {
1326 0 : index = activeControl->index();
1327 0 : index.row = std::numeric_limits<int>::max();
1328 0 : toControl = nextEnabled(activePanel->controls(), index, MoveDirection::Up);
1329 : }
1330 : }
1331 : }
1332 0 : } break;
1333 1 : case MoveDirection::Up: {
1334 1 : if (!activeControl) { // no any active
1335 0 : toControl = lastEnabled(activePanel->controls(), INavigation::Index(), MoveDirection::Up);
1336 : } else {
1337 1 : toControl = nextEnabled(activePanel->controls(), activeControl->index(), MoveDirection::Up);
1338 1 : if (!toControl && tryOtherOrientation) {
1339 1 : toControl = nextEnabled(activePanel->controls(), activeControl->index(), MoveDirection::Left);
1340 : }
1341 1 : if (!toControl) { // active is first
1342 1 : if (activePanel->direction() == INavigationPanel::Direction::Vertical) {
1343 1 : toControl = lastEnabled(activePanel->controls());
1344 1 : break;
1345 : }
1346 :
1347 0 : INavigation::Index index = activeControl->index();
1348 0 : index.row = std::numeric_limits<int>::max();
1349 0 : toControl = lastEnabled(activePanel->controls(), index, MoveDirection::Up); // the last to be the next
1350 :
1351 0 : if (!toControl && tryOtherOrientation) {
1352 0 : index = activeControl->index();
1353 0 : index.column = std::numeric_limits<int>::max();
1354 0 : toControl = nextEnabled(activePanel->controls(), index, MoveDirection::Left);
1355 : }
1356 : }
1357 : }
1358 0 : } break;
1359 : }
1360 :
1361 5 : if (!toControl) {
1362 0 : return;
1363 : }
1364 :
1365 : //! NOTE Maybe just one control (or just one enabled control)
1366 5 : if (toControl == activeControl) {
1367 0 : return;
1368 : }
1369 :
1370 5 : if (activeControl) {
1371 : MYLOG() << "current activated control: " << activeControl->name()
1372 : << ", row: " << activeControl->index().row
1373 : << ", column: " << activeControl->index().column;
1374 :
1375 5 : doDeactivateControl(activeControl);
1376 : }
1377 :
1378 5 : doActivateControl(toControl);
1379 :
1380 5 : m_navigationChanged.notify();
1381 5 : }
1382 :
1383 0 : void NavigationController::doTriggerControl()
1384 : {
1385 0 : TRACEFUNC;
1386 : MYLOG() << "====";
1387 0 : INavigationPanel* activePanel = this->activePanel();
1388 0 : if (!activePanel) {
1389 0 : return;
1390 : }
1391 :
1392 0 : INavigation::EventPtr e = Event::make(Event::Trigger);
1393 0 : activePanel->onEvent(e);
1394 0 : if (e->accepted) {
1395 0 : return;
1396 : }
1397 :
1398 0 : INavigationControl* activeCtrl = findActive(activePanel->controls());
1399 0 : if (!activeCtrl) {
1400 0 : return;
1401 : }
1402 :
1403 : MYLOG() << "triggered control: " << activeCtrl->name();
1404 0 : activeCtrl->onEvent(e);
1405 0 : if (e->accepted) {
1406 0 : return;
1407 : }
1408 :
1409 0 : activeCtrl->trigger();
1410 0 : }
1411 :
1412 0 : bool NavigationController::requestActivateByName(const std::string& sectName, const std::string& panelName, const std::string& controlName)
1413 : {
1414 0 : INavigationSection* section = findByName(m_sections, QString::fromStdString(sectName));
1415 0 : if (!section) {
1416 0 : LOGE() << "not found section with name: " << sectName;
1417 0 : return false;
1418 : }
1419 :
1420 0 : INavigationPanel* panel = findByName(section->panels(), QString::fromStdString(panelName));
1421 0 : if (!panel) {
1422 0 : LOGE() << "not found panel with name: " << panelName << ", section: " << sectName;
1423 0 : return false;
1424 : }
1425 :
1426 0 : INavigationControl* control = findByName(panel->controls(), QString::fromStdString(controlName));
1427 0 : if (!control) {
1428 0 : LOGE() << "not found control with name: " << controlName << ", panel: " << panelName << ", section: " << sectName;
1429 0 : QString has = "has:\n";
1430 0 : for (const INavigationControl* c : panel->controls()) {
1431 0 : has += c->name() + "\n";
1432 : }
1433 0 : LOGI() << has;
1434 0 : return false;
1435 0 : }
1436 :
1437 0 : onActiveRequested(section, panel, control, true);
1438 0 : return true;
1439 : }
1440 :
1441 0 : bool NavigationController::requestActivateByIndex(const std::string& sectName, const std::string& panelName,
1442 : const INavigation::Index& controlIndex)
1443 : {
1444 0 : INavigationSection* section = findByName(m_sections, QString::fromStdString(sectName));
1445 0 : if (!section) {
1446 0 : LOGE() << "not found section with name: " << sectName;
1447 0 : return false;
1448 : }
1449 :
1450 0 : INavigationPanel* panel = findByName(section->panels(), QString::fromStdString(panelName));
1451 0 : if (!panel) {
1452 0 : LOGE() << "not found panel with name: " << panelName << ", section: " << sectName;
1453 0 : return false;
1454 : }
1455 :
1456 0 : INavigationControl* control = findByIndex(panel->controls(), controlIndex);
1457 0 : if (!control) {
1458 0 : LOGE() << "not found control with index: " << controlIndex.to_string() << ", panel: " << panelName << ", section: " << sectName;
1459 0 : std::string has = "has:\n";
1460 0 : for (const INavigationControl* c : panel->controls()) {
1461 0 : has += c->index().to_string() + "\n";
1462 : }
1463 0 : LOGI() << has;
1464 0 : return false;
1465 0 : }
1466 :
1467 0 : onActiveRequested(section, panel, control, true);
1468 0 : return true;
1469 : }
1470 :
1471 29 : void NavigationController::onActiveRequested(INavigationSection* sect, INavigationPanel* panel, INavigationControl* ctrl, bool force)
1472 : {
1473 30 : TRACEFUNC;
1474 : UNUSED(force);
1475 :
1476 29 : if (m_sections.empty()) {
1477 0 : return;
1478 : }
1479 :
1480 29 : bool isChanged = false;
1481 :
1482 29 : DEFER {
1483 29 : if (isChanged) {
1484 29 : m_navigationChanged.notify();
1485 : }
1486 29 : };
1487 :
1488 29 : INavigationSection* activeSec = findActive(m_sections);
1489 29 : if (activeSec && activeSec != sect) {
1490 1 : doDeactivateSection(activeSec);
1491 : }
1492 :
1493 29 : if (!sect->active()) {
1494 28 : sect->setActive(true);
1495 28 : isChanged = true;
1496 : MYLOG() << "activated section: " << sect->name() << ", order: " << sect->index().order();
1497 : }
1498 :
1499 29 : if (m_prioritySection == sect) {
1500 1 : m_prioritySection = nullptr;
1501 : }
1502 :
1503 29 : if (!panel) {
1504 16 : panel = firstEnabled(sect->panels());
1505 : }
1506 :
1507 29 : INavigationPanel* activePanel = findActive(sect->panels());
1508 29 : if (activePanel && activePanel != panel) {
1509 1 : doDeactivatePanel(activePanel);
1510 : }
1511 :
1512 29 : if (!panel) {
1513 0 : return;
1514 : }
1515 :
1516 29 : if (!panel->active()) {
1517 29 : panel->setActive(true);
1518 29 : isChanged = true;
1519 : MYLOG() << "activated panel: " << panel->name() << ", order: " << panel->index().order();
1520 : }
1521 :
1522 29 : if (!ctrl) {
1523 17 : ctrl = firstEnabled(panel->controls());
1524 : }
1525 :
1526 29 : INavigationControl* activeCtrl = findActive(panel->controls());
1527 29 : if (activeCtrl && activeCtrl != ctrl) {
1528 0 : activeCtrl->setActive(false);
1529 : }
1530 :
1531 29 : if (!ctrl) {
1532 0 : return;
1533 : }
1534 :
1535 29 : if (!ctrl->active()) {
1536 29 : ctrl->setActive(true);
1537 29 : isChanged = true;
1538 : MYLOG() << "activated control: " << ctrl->name() << ", row: " << ctrl->index().row << ", column: " << ctrl->index().column;
1539 : }
1540 29 : }
1541 :
1542 0 : inline QTextStream& operator<<(QTextStream& s, const std::string& v)
1543 : {
1544 0 : s << v.c_str();
1545 0 : return s;
1546 : }
1547 :
1548 0 : void NavigationController::dump() const
1549 : {
1550 0 : QString str;
1551 0 : QTextStream stream(&str);
1552 0 : stream << "Navigation Dump:" << Qt::endl;
1553 0 : for (const INavigationSection* sect : m_sections) {
1554 0 : stream << "section: " << sect->name()
1555 0 : << ", index: " << sect->index().to_string()
1556 0 : << ", enabled: " << sect->enabled()
1557 0 : << ", active: " << sect->active()
1558 0 : << Qt::endl;
1559 :
1560 0 : for (const INavigationPanel* panel : sect->panels()) {
1561 0 : stream << " panel: " << panel->name()
1562 0 : << ", index: " << panel->index().to_string()
1563 0 : << ", enabled: " << panel->enabled()
1564 0 : << ", active: " << panel->active()
1565 0 : << Qt::endl;
1566 :
1567 0 : for (const INavigationControl* ctrl : panel->controls()) {
1568 0 : stream << " control: " << ctrl->name()
1569 0 : << ", index: " << ctrl->index().to_string()
1570 0 : << ", enabled: " << ctrl->enabled()
1571 0 : << ", active: " << ctrl->active()
1572 0 : << Qt::endl;
1573 : }
1574 : }
1575 : }
1576 :
1577 0 : stream << Qt::endl;
1578 :
1579 0 : std::cout << str.toStdString();
1580 0 : }
|