added close dialogs on escape
This commit is contained in:
parent
dc026e68db
commit
1be98d4125
16 changed files with 86 additions and 71 deletions
|
@ -31,7 +31,7 @@ KeyNavDevControl::KeyNavDevControl(INavigationControl* control)
|
|||
|
||||
void KeyNavDevControl::requestActive()
|
||||
{
|
||||
m_control->activeRequested().send(m_control);
|
||||
m_control->requestActive();
|
||||
}
|
||||
|
||||
void KeyNavDevControl::trigger()
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <tuple>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QVariantMap>
|
||||
|
@ -38,9 +39,7 @@ class INavigationSection;
|
|||
class INavigationPanel;
|
||||
class INavigationControl;
|
||||
|
||||
using PanelControlChannel = async::Channel<INavigationPanel*, INavigationControl*>;
|
||||
using SectionPanelControlChannel = async::Channel<INavigationSection*, INavigationPanel*, INavigationControl*>;
|
||||
|
||||
using OnActiveRequested = std::function<void (INavigationSection* sec, INavigationPanel* panel, INavigationControl* ctrl)>;
|
||||
class INavigation
|
||||
{
|
||||
public:
|
||||
|
@ -110,7 +109,7 @@ public:
|
|||
virtual INavigationPanel* panel() const = 0;
|
||||
|
||||
virtual void trigger() = 0;
|
||||
virtual async::Channel<INavigationControl*> activeRequested() const = 0;
|
||||
virtual void requestActive() = 0;
|
||||
};
|
||||
|
||||
class INavigationSection;
|
||||
|
@ -130,7 +129,7 @@ public:
|
|||
virtual Direction direction() const = 0;
|
||||
virtual const std::set<INavigationControl*>& controls() const = 0;
|
||||
virtual async::Notification controlsListChanged() const = 0;
|
||||
virtual PanelControlChannel activeRequested() const = 0;
|
||||
virtual void requestActive(INavigationControl* control = nullptr) = 0;
|
||||
};
|
||||
|
||||
class INavigationSection : public INavigation
|
||||
|
@ -149,7 +148,9 @@ public:
|
|||
virtual Type type() const = 0;
|
||||
virtual const std::set<INavigationPanel*>& panels() const = 0;
|
||||
virtual async::Notification panelsListChanged() const = 0;
|
||||
virtual SectionPanelControlChannel activeRequested() const = 0;
|
||||
|
||||
virtual void setOnActiveRequested(const OnActiveRequested& func) = 0;
|
||||
virtual void requestActive(INavigationPanel* panel = nullptr, INavigationControl* control = nullptr) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ public:
|
|||
|
||||
virtual async::Notification navigationChanged() const = 0;
|
||||
|
||||
virtual bool isHighlight() const = 0;
|
||||
virtual void setIsResetOnMousePress(bool arg) = 0;
|
||||
|
||||
virtual void dump() const = 0;
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
//#define NAVIGATION_LOGGING_ENABLED
|
||||
// #define NAVIGATION_LOGGING_ENABLED
|
||||
|
||||
#ifdef NAVIGATION_LOGGING_ENABLED
|
||||
#define MYLOG() LOGI()
|
||||
|
@ -284,7 +284,7 @@ void NavigationController::init()
|
|||
dispatcher()->reg(this, "nav-left", [this]() { navigateTo(NavigationType::Left); });
|
||||
dispatcher()->reg(this, "nav-up", [this]() { navigateTo(NavigationType::Up); });
|
||||
dispatcher()->reg(this, "nav-down", [this]() { navigateTo(NavigationType::Down); });
|
||||
dispatcher()->reg(this, "nav-escape", [this]() { navigateTo(NavigationType::Escape); });
|
||||
dispatcher()->reg(this, "nav-escape", [this]() { onEscape(); });
|
||||
|
||||
dispatcher()->reg(this, "nav-first-control", [this]() { navigateTo(NavigationType::FirstControl); }); // typically Home key
|
||||
dispatcher()->reg(this, "nav-last-control", [this]() { navigateTo(NavigationType::LastControl); }); // typically End key
|
||||
|
@ -299,8 +299,7 @@ void NavigationController::reg(INavigationSection* section)
|
|||
//! TODO add check on valid state
|
||||
TRACEFUNC;
|
||||
m_sections.insert(section);
|
||||
|
||||
section->activeRequested().onReceive(this, [this](INavigationSection* section, INavigationPanel* panel, INavigationControl* control) {
|
||||
section->setOnActiveRequested([this](INavigationSection* section, INavigationPanel* panel, INavigationControl* control) {
|
||||
onActiveRequested(section, panel, control);
|
||||
});
|
||||
}
|
||||
|
@ -309,7 +308,7 @@ void NavigationController::unreg(INavigationSection* section)
|
|||
{
|
||||
TRACEFUNC;
|
||||
m_sections.erase(section);
|
||||
section->activeRequested().resetOnReceive(this);
|
||||
section->setOnActiveRequested(nullptr);
|
||||
}
|
||||
|
||||
const std::set<INavigationSection*>& NavigationController::sections() const
|
||||
|
@ -317,12 +316,17 @@ const std::set<INavigationSection*>& NavigationController::sections() const
|
|||
return m_sections;
|
||||
}
|
||||
|
||||
bool NavigationController::isHighlight() const
|
||||
{
|
||||
return m_isNavigatedByKeyboard;
|
||||
}
|
||||
|
||||
void NavigationController::setIsResetOnMousePress(bool arg)
|
||||
{
|
||||
m_isResetOnMousePress = arg;
|
||||
}
|
||||
|
||||
void NavigationController::resetActiveIfNeed(QObject* watched)
|
||||
void NavigationController::resetIfNeed(QObject* watched)
|
||||
{
|
||||
if (!m_isResetOnMousePress) {
|
||||
return;
|
||||
|
@ -334,13 +338,13 @@ void NavigationController::resetActiveIfNeed(QObject* watched)
|
|||
}
|
||||
#endif
|
||||
|
||||
resetActive();
|
||||
m_isNavigatedByKeyboard = false;
|
||||
}
|
||||
|
||||
bool NavigationController::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
if (event->type() == QEvent::MouseButtonPress) {
|
||||
resetActiveIfNeed(watched);
|
||||
resetIfNeed(watched);
|
||||
}
|
||||
|
||||
return QObject::eventFilter(watched, event);
|
||||
|
@ -348,6 +352,16 @@ bool NavigationController::eventFilter(QObject* watched, QEvent* event)
|
|||
|
||||
void NavigationController::navigateTo(NavigationController::NavigationType type)
|
||||
{
|
||||
//! NOTE We will assume that if a action was sent, then it was sent using the keyboard
|
||||
//! (instead of an explicit request to activate the by clicking the mouse)
|
||||
if (!m_isNavigatedByKeyboard) {
|
||||
m_isNavigatedByKeyboard = true;
|
||||
INavigationSection* activeSec = findActive(m_sections);
|
||||
if (activeSec) {
|
||||
doDeactivateSection(activeSec);
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case NavigationType::NextSection:
|
||||
goToNextSection();
|
||||
|
@ -376,9 +390,6 @@ void NavigationController::navigateTo(NavigationController::NavigationType type)
|
|||
case NavigationType::Down:
|
||||
onDown();
|
||||
break;
|
||||
case NavigationType::Escape:
|
||||
onEscape();
|
||||
break;
|
||||
case NavigationType::TriggerControl:
|
||||
doTriggerControl();
|
||||
break;
|
||||
|
@ -1177,6 +1188,7 @@ bool NavigationController::requestActivateByIndex(const std::string& sectName, c
|
|||
void NavigationController::onActiveRequested(INavigationSection* sect, INavigationPanel* panel, INavigationControl* ctrl, bool force)
|
||||
{
|
||||
TRACEFUNC;
|
||||
UNUSED(force);
|
||||
|
||||
if (m_sections.empty()) {
|
||||
return;
|
||||
|
@ -1184,12 +1196,6 @@ void NavigationController::onActiveRequested(INavigationSection* sect, INavigati
|
|||
|
||||
INavigationSection* activeSec = findActive(m_sections);
|
||||
|
||||
//! NOTE If there is no active section,
|
||||
//! we may not be using keyboard navigation, so ignore the request.
|
||||
if (!force && !activeSec) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool isChanged = false;
|
||||
|
||||
if (activeSec && activeSec != sect) {
|
||||
|
@ -1207,18 +1213,18 @@ void NavigationController::onActiveRequested(INavigationSection* sect, INavigati
|
|||
doDeactivatePanel(activePanel);
|
||||
}
|
||||
|
||||
if (!panel->active()) {
|
||||
if (panel && !panel->active()) {
|
||||
panel->setActive(true);
|
||||
isChanged = true;
|
||||
MYLOG() << "activated panel: " << panel->name() << ", order: " << panel->index().order();
|
||||
}
|
||||
|
||||
INavigationControl* activeCtrl = findActive(panel->controls());
|
||||
INavigationControl* activeCtrl = panel ? findActive(panel->controls()) : nullptr;
|
||||
if (activeCtrl && activeCtrl != ctrl) {
|
||||
activeCtrl->setActive(false);
|
||||
}
|
||||
|
||||
if (!ctrl->active()) {
|
||||
if (ctrl && !ctrl->active()) {
|
||||
ctrl->setActive(true);
|
||||
isChanged = true;
|
||||
MYLOG() << "activated control: " << ctrl->name() << ", row: " << ctrl->index().row << ", column: " << ctrl->index().column;
|
||||
|
|
|
@ -64,6 +64,7 @@ public:
|
|||
|
||||
async::Notification navigationChanged() const override;
|
||||
|
||||
bool isHighlight() const override;
|
||||
void setIsResetOnMousePress(bool arg) override;
|
||||
|
||||
void dump() const override;
|
||||
|
@ -82,7 +83,6 @@ private:
|
|||
Right,
|
||||
Up,
|
||||
Down,
|
||||
Escape,
|
||||
TriggerControl,
|
||||
FirstControl,
|
||||
LastControl,
|
||||
|
@ -125,11 +125,12 @@ private:
|
|||
void doActivateFirst();
|
||||
void doActivateLast();
|
||||
|
||||
void resetActiveIfNeed(QObject* watched);
|
||||
void resetIfNeed(QObject* watched);
|
||||
void resetActive();
|
||||
|
||||
std::set<INavigationSection*> m_sections;
|
||||
async::Notification m_navigationChanged;
|
||||
bool m_isNavigatedByKeyboard = false;
|
||||
bool m_isResetOnMousePress = true;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -48,7 +48,9 @@ public:
|
|||
|
||||
MOCK_METHOD(const std::set<INavigationPanel*>&, panels, (), (const, override));
|
||||
MOCK_METHOD(async::Notification, panelsListChanged, (), (const, override));
|
||||
MOCK_METHOD(SectionPanelControlChannel, activeRequested, (), (const, override));
|
||||
|
||||
MOCK_METHOD(void, setOnActiveRequested, (const OnActiveRequested& func), (override));
|
||||
MOCK_METHOD(void, requestActive, (INavigationPanel*, INavigationControl*), (override));
|
||||
};
|
||||
|
||||
class NavigationPanelMock : public INavigationPanel
|
||||
|
@ -73,7 +75,8 @@ public:
|
|||
MOCK_METHOD(Direction, direction, (), (const, override));
|
||||
MOCK_METHOD(const std::set<INavigationControl*>&, controls, (), (const, override));
|
||||
MOCK_METHOD(async::Notification, controlsListChanged, (), (const, override));
|
||||
MOCK_METHOD(PanelControlChannel, activeRequested, (), (const, override));
|
||||
|
||||
MOCK_METHOD(void, requestActive, (INavigationControl*), (override));
|
||||
};
|
||||
|
||||
class NavigationControlMock : public INavigationControl
|
||||
|
@ -97,7 +100,7 @@ public:
|
|||
MOCK_METHOD(INavigationPanel*, panel, (), (const, override));
|
||||
|
||||
MOCK_METHOD(void, trigger, (), (override));
|
||||
MOCK_METHOD(async::Channel<INavigationControl*>, activeRequested, (), (const, override));
|
||||
MOCK_METHOD(void, requestActive, (), (override));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,6 @@ public:
|
|||
c->control = new NiceMock<NavigationControlMock>();
|
||||
ON_CALL(*c->control, enabled()).WillByDefault(Return(true));
|
||||
ON_CALL(*c->control, active()).WillByDefault(Return(false));
|
||||
ON_CALL(*c->control, activeRequested()).WillByDefault(Return(async::Channel<INavigationControl*>()));
|
||||
ON_CALL(*c->control, index()).WillByDefault(ReturnRef(idx));
|
||||
|
||||
return c;
|
||||
|
@ -139,7 +138,6 @@ public:
|
|||
p->panel = new NiceMock<NavigationPanelMock>();
|
||||
ON_CALL(*p->panel, enabled()).WillByDefault(Return(true));
|
||||
ON_CALL(*p->panel, active()).WillByDefault(Return(false));
|
||||
ON_CALL(*p->panel, activeRequested()).WillByDefault(Return(PanelControlChannel()));
|
||||
ON_CALL(*p->panel, controls()).WillByDefault(ReturnRef(p->icontrols));
|
||||
|
||||
INavigation::Index& idx = make_idx(env);
|
||||
|
@ -163,7 +161,6 @@ public:
|
|||
ON_CALL(*s->section, type()).WillByDefault(Return(INavigationSection::Type::Regular));
|
||||
ON_CALL(*s->section, enabled()).WillByDefault(Return(true));
|
||||
ON_CALL(*s->section, active()).WillByDefault(Return(false));
|
||||
ON_CALL(*s->section, activeRequested()).WillByDefault(Return(SectionPanelControlChannel()));
|
||||
ON_CALL(*s->section, panels()).WillByDefault(ReturnRef(s->ipanels));
|
||||
|
||||
INavigation::Index& idx = make_idx(env);
|
||||
|
|
|
@ -226,5 +226,5 @@ void AbstractNavigation::setAccessibleParent(AccessibleItem* p)
|
|||
|
||||
bool AbstractNavigation::highlight() const
|
||||
{
|
||||
return active();
|
||||
return active() && navigationController()->isHighlight();
|
||||
}
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
#include "qmlaccessible.h"
|
||||
#include "navigationevent.h"
|
||||
|
||||
#include "modularity/ioc.h"
|
||||
#include "../inavigationcontroller.h"
|
||||
|
||||
namespace mu::ui {
|
||||
class AbstractNavigation : public QObject, public QQmlParserStatus
|
||||
{
|
||||
|
@ -49,6 +52,8 @@ class AbstractNavigation : public QObject, public QQmlParserStatus
|
|||
|
||||
Q_INTERFACES(QQmlParserStatus)
|
||||
|
||||
INJECT(ui, INavigationController, navigationController)
|
||||
|
||||
public:
|
||||
explicit AbstractNavigation(QObject* parent = nullptr);
|
||||
|
||||
|
|
|
@ -94,14 +94,11 @@ void NavigationControl::trigger()
|
|||
emit triggered();
|
||||
}
|
||||
|
||||
mu::async::Channel<INavigationControl*> NavigationControl::activeRequested() const
|
||||
{
|
||||
return m_forceActiveRequested;
|
||||
}
|
||||
|
||||
void NavigationControl::requestActive()
|
||||
{
|
||||
m_forceActiveRequested.send(this);
|
||||
if (m_panel) {
|
||||
m_panel->requestActive(this);
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationControl::setPanel(NavigationPanel* panel)
|
||||
|
|
|
@ -57,9 +57,8 @@ public:
|
|||
void onEvent(EventPtr e) override;
|
||||
|
||||
void trigger() override;
|
||||
async::Channel<INavigationControl*> activeRequested() const override;
|
||||
|
||||
Q_INVOKABLE void requestActive();
|
||||
Q_INVOKABLE void requestActive() override;
|
||||
|
||||
public slots:
|
||||
void setPanel(NavigationPanel* panel);
|
||||
|
@ -74,7 +73,6 @@ private slots:
|
|||
private:
|
||||
|
||||
NavigationPanel* m_panel = nullptr;
|
||||
async::Channel<INavigationControl*> m_forceActiveRequested;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -143,11 +143,6 @@ mu::async::Notification NavigationPanel::controlsListChanged() const
|
|||
return m_controlsListChanged;
|
||||
}
|
||||
|
||||
PanelControlChannel NavigationPanel::activeRequested() const
|
||||
{
|
||||
return m_forceActiveRequested;
|
||||
}
|
||||
|
||||
INavigationSection* NavigationPanel::section() const
|
||||
{
|
||||
return m_section;
|
||||
|
@ -199,10 +194,6 @@ void NavigationPanel::addControl(NavigationControl* control)
|
|||
|
||||
m_controls.insert(control);
|
||||
|
||||
control->activeRequested().onReceive(this, [this](INavigationControl* control) {
|
||||
m_forceActiveRequested.send(this, control);
|
||||
});
|
||||
|
||||
if (m_controlsListChanged.isConnected()) {
|
||||
m_controlsListChanged.notify();
|
||||
}
|
||||
|
@ -216,9 +207,15 @@ void NavigationPanel::removeControl(NavigationControl* control)
|
|||
}
|
||||
|
||||
m_controls.erase(control);
|
||||
control->activeRequested().resetOnReceive(this);
|
||||
|
||||
if (m_controlsListChanged.isConnected()) {
|
||||
m_controlsListChanged.notify();
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationPanel::requestActive(INavigationControl* control)
|
||||
{
|
||||
if (m_section) {
|
||||
m_section->requestActive(this, control);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,14 +71,15 @@ public:
|
|||
const std::set<INavigationControl*>& controls() const override;
|
||||
async::Notification controlsListChanged() const override;
|
||||
|
||||
PanelControlChannel activeRequested() const override;
|
||||
|
||||
INavigationSection* section() const override;
|
||||
NavigationSection* section_property() const;
|
||||
|
||||
void addControl(NavigationControl* control);
|
||||
void removeControl(NavigationControl* control);
|
||||
|
||||
//! NOTE Can be called from QML without args
|
||||
Q_INVOKABLE void requestActive(INavigationControl* control = nullptr) override;
|
||||
|
||||
public slots:
|
||||
void setSection_property(NavigationSection* section);
|
||||
void setSection(INavigationSection* section);
|
||||
|
@ -95,7 +96,6 @@ private:
|
|||
NavigationSection* m_section = nullptr;
|
||||
std::set<INavigationControl*> m_controls;
|
||||
async::Notification m_controlsListChanged;
|
||||
PanelControlChannel m_forceActiveRequested;
|
||||
QmlDirection m_direction = QmlDirection::Horizontal;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -125,20 +125,11 @@ void NavigationSection::addPanel(NavigationPanel* panel)
|
|||
|
||||
m_panels.insert(panel);
|
||||
|
||||
panel->activeRequested().onReceive(this, [this](INavigationPanel* panel, INavigationControl* control) {
|
||||
m_forceActiveRequested.send(this, panel, control);
|
||||
});
|
||||
|
||||
if (m_panelsListChanged.isConnected()) {
|
||||
m_panelsListChanged.notify();
|
||||
}
|
||||
}
|
||||
|
||||
SectionPanelControlChannel NavigationSection::activeRequested() const
|
||||
{
|
||||
return m_forceActiveRequested;
|
||||
}
|
||||
|
||||
void NavigationSection::removePanel(NavigationPanel* panel)
|
||||
{
|
||||
TRACEFUNC;
|
||||
|
@ -147,13 +138,24 @@ void NavigationSection::removePanel(NavigationPanel* panel)
|
|||
}
|
||||
|
||||
m_panels.erase(panel);
|
||||
panel->activeRequested().resetOnReceive(this);
|
||||
|
||||
if (m_panelsListChanged.isConnected()) {
|
||||
m_panelsListChanged.notify();
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationSection::setOnActiveRequested(const OnActiveRequested& func)
|
||||
{
|
||||
m_onActiveRequested = func;
|
||||
}
|
||||
|
||||
void NavigationSection::requestActive(INavigationPanel* panel, INavigationControl* control)
|
||||
{
|
||||
if (m_onActiveRequested) {
|
||||
m_onActiveRequested(this, panel, control);
|
||||
}
|
||||
}
|
||||
|
||||
INavigationSection::Type NavigationSection::type() const
|
||||
{
|
||||
return static_cast<INavigationSection::Type>(m_type);
|
||||
|
|
|
@ -72,13 +72,16 @@ public:
|
|||
const std::set<INavigationPanel*>& panels() const override;
|
||||
async::Notification panelsListChanged() const override;
|
||||
|
||||
SectionPanelControlChannel activeRequested() const override;
|
||||
|
||||
void componentComplete() override;
|
||||
|
||||
void addPanel(NavigationPanel* panel);
|
||||
void removePanel(NavigationPanel* panel);
|
||||
|
||||
void setOnActiveRequested(const OnActiveRequested& func) override;
|
||||
|
||||
//! NOTE Can be called from QML without args
|
||||
Q_INVOKABLE void requestActive(INavigationPanel* panel = nullptr, INavigationControl* control = nullptr) override;
|
||||
|
||||
public slots:
|
||||
void setType(QmlType type);
|
||||
|
||||
|
@ -90,8 +93,8 @@ private:
|
|||
|
||||
std::set<INavigationPanel*> m_panels;
|
||||
async::Notification m_panelsListChanged;
|
||||
SectionPanelControlChannel m_forceActiveRequested;
|
||||
QmlType m_type = Regular;
|
||||
OnActiveRequested m_onActiveRequested;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,10 @@ DialogView {
|
|||
property bool isDoActiveParentOnClose: true
|
||||
property alias navigationSection: navSec
|
||||
|
||||
onOpened: {
|
||||
navSec.requestActive()
|
||||
}
|
||||
|
||||
onClosed: {
|
||||
if (root.isDoActiveParentOnClose && root.navigationParentControl) {
|
||||
Qt.callLater(root.navigationParentControl.requestActive)
|
||||
|
|
Loading…
Reference in a new issue