added close dialogs on escape

This commit is contained in:
Igor Korsukov 2021-12-13 17:32:21 +02:00
parent dc026e68db
commit 1be98d4125
16 changed files with 86 additions and 71 deletions

View file

@ -31,7 +31,7 @@ KeyNavDevControl::KeyNavDevControl(INavigationControl* control)
void KeyNavDevControl::requestActive()
{
m_control->activeRequested().send(m_control);
m_control->requestActive();
}
void KeyNavDevControl::trigger()

View file

@ -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;
};
}

View file

@ -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;

View file

@ -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;

View file

@ -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;
};
}

View file

@ -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));
};
}

View file

@ -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);

View file

@ -226,5 +226,5 @@ void AbstractNavigation::setAccessibleParent(AccessibleItem* p)
bool AbstractNavigation::highlight() const
{
return active();
return active() && navigationController()->isHighlight();
}

View file

@ -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);

View file

@ -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)

View file

@ -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;
};
}

View file

@ -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);
}
}

View file

@ -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;
};
}

View file

@ -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);

View file

@ -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;
};
}

View file

@ -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)