(C++) QtKeyboardFriendlyGraphicsView
January 25, 2018 · View on GitHub
(C++) QtKeyboardFriendlyGraphicsView



QtKeyboardFriendlyGraphicsView is a Qt class for a keyboard friendly QGraphicsView.
Technical facts
./CppQtKeyboardFriendlyGraphicsView/CppQtKeyboardFriendlyGraphicsView.pri
INCLUDEPATH += \ ../../Classes/CppQtKeyboardFriendlyGraphicsView SOURCES += \ ../../Classes/CppQtKeyboardFriendlyGraphicsView/qtkeyboardfriendlygraphicsview.cpp HEADERS += \ ../../Classes/CppQtKeyboardFriendlyGraphicsView/qtkeyboardfriendlygraphicsview.h OTHER_FILES += \ ../../Classes/CppQtKeyboardFriendlyGraphicsView/Licence.txt
./CppQtKeyboardFriendlyGraphicsView/qtkeyboardfriendlygraphicsview.h
//--------------------------------------------------------------------------- /* QtKeyboardFriendlyGraphicsView, an keyboard friendly QGraphicsView Copyright 2012-2015 Richel Bilderbeek This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program.If not, see <http://www.gnu.org/licenses/>. */ //--------------------------------------------------------------------------- //From http://www.richelbilderbeek.nl/CppQtKeyboardFriendlyGraphicsView.htm //--------------------------------------------------------------------------- #ifndef QTKEYBOARDFRIENDLYGRAPHICSVIEW_H #define QTKEYBOARDFRIENDLYGRAPHICSVIEW_H #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #pragma GCC diagnostic ignored "-Wunused-local-typedefs" #include <boost/signals2.hpp> #include <QGraphicsView> #pragma GCC diagnostic pop struct QGraphicsItem; namespace ribi { ///The widget holding the items struct QtKeyboardFriendlyGraphicsView : public QGraphicsView { QtKeyboardFriendlyGraphicsView(QWidget* parent = 0); ///QtKeyboardFriendlyGraphicsView creates its own QGraphicsScene QtKeyboardFriendlyGraphicsView(QGraphicsScene* scene, QWidget* parent) = delete; virtual ~QtKeyboardFriendlyGraphicsView() noexcept {} ///Obtain the version of this class static std::string GetVersion() noexcept; ///Obtain the version history of this class static std::vector<std::string> GetVersionHistory() noexcept; ///Respond to a key press virtual void keyPressEvent(QKeyEvent *event) noexcept; ///Signals which QGraphicsItem has been moved by setpos boost::signals2::signal<void (QGraphicsItem*)> m_signal_update; private: ///Obtain the closest item in the collection ///Returns nullptr if there is no focusable item in the items QGraphicsItem* GetClosest( const QGraphicsItem* const focus_item, const std::vector<QGraphicsItem *>& items) const; ///Calculate the Euclidian distance between two points static double GetDistance(const QPointF& a, const QPointF& b); ///Obtain the items above the focus_item std::vector<QGraphicsItem *> GetItemsAbove(const QGraphicsItem* const focus_item) const; ///Obtain the items below the focus_item std::vector<QGraphicsItem *> GetItemsBelow(const QGraphicsItem* const focus_item) const; ///Obtain the items left of the focus_item std::vector<QGraphicsItem *> GetItemsLeft(const QGraphicsItem* const focus_item) const; ///Obtain the items right of the focus_item std::vector<QGraphicsItem *> GetItemsRight(const QGraphicsItem* const focus_item) const; ///Give focus to a random item void SetRandomFocus(); }; } //~namespace ribi #endif // QTKEYBOARDFRIENDLYGRAPHICSVIEW_H
./CppQtKeyboardFriendlyGraphicsView/qtkeyboardfriendlygraphicsview.cpp
//--------------------------------------------------------------------------- /* QtKeyboardFriendlyGraphicsView, an keyboard friendly QGraphicsView Copyright 2012-2015 Richel Bilderbeek This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program.If not, see <http://www.gnu.org/licenses/>. */ //--------------------------------------------------------------------------- //From http://www.richelbilderbeek.nl/CppQtKeyboardFriendlyGraphicsView.htm //--------------------------------------------------------------------------- #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #include "qtkeyboardfriendlygraphicsview.h" #include <cassert> #include <cmath> #include <iostream> #include <QGraphicsScene> #include <QKeyEvent> #include <QGraphicsSimpleTextItem> //#include "conceptmaphelper.h" #pragma GCC diagnostic pop ribi::QtKeyboardFriendlyGraphicsView::QtKeyboardFriendlyGraphicsView(QWidget* parent) : QGraphicsView(new QGraphicsScene,parent), m_signal_update{} { } QGraphicsItem* ribi::QtKeyboardFriendlyGraphicsView::GetClosest( const QGraphicsItem* const focus_item, const std::vector<QGraphicsItem *>& items) const { assert(!items.empty()); assert(std::count(items.begin(),items.end(),focus_item) == 0); QGraphicsItem* best = nullptr; double best_distance = std::numeric_limits<double>::max(); for (QGraphicsItem* const item:items) { if (!(item->flags() & QGraphicsItem::ItemIsFocusable)) continue; if (!(item->isVisible())) continue; assert(item != focus_item); const double distance = GetDistance(focus_item->pos(),item->pos()); if (distance < best_distance) { best_distance = distance; best = item; } } return best; //best can be nullptr } double ribi::QtKeyboardFriendlyGraphicsView::GetDistance(const QPointF& a, const QPointF& b) { const double dx = a.x() - b.x(); const double dy = a.y() - b.y(); return std::sqrt((dx * dx) + (dy * dy)); } std::vector<QGraphicsItem *> ribi::QtKeyboardFriendlyGraphicsView::GetItemsAbove(const QGraphicsItem* const focus_item) const { std::vector<QGraphicsItem *> v; const QList<QGraphicsItem *> items = this->items(); //Look for items between North-West and North-East first for(QGraphicsItem* const item: items) { const double dy = item->pos().y() - focus_item->pos().y(); if (dy < 0.0) //Use '<' (instead of '<=') to leave out focus_item { const double dx = item->pos().x() - focus_item->pos().x(); if (std::abs(dx) < std::abs(dy)) { assert(item != focus_item); v.push_back(item); } } } if (!v.empty()) return v; //Look for items North (from West, through North, through East) for(QGraphicsItem* const item: items) { const double dy = item->pos().y() - focus_item->pos().y(); if (dy < 0.0) //Use '<' (instead of '<=') to leave out focus_item { assert(item != focus_item); v.push_back(item); } } return v; } std::vector<QGraphicsItem *> ribi::QtKeyboardFriendlyGraphicsView::GetItemsBelow(const QGraphicsItem* const focus_item) const { std::vector<QGraphicsItem *> v; const QList<QGraphicsItem *> items = this->items(); //Look for items between South-East and South-West first for(QGraphicsItem* const item: items) { const double dy = item->pos().y() - focus_item->pos().y(); if (dy > 0.0) //Use '>' (instead of '>=') to leave out focus_item { const double dx = item->pos().x() - focus_item->pos().x(); if (std::abs(dx) < std::abs(dy)) { assert(item != focus_item); v.push_back(item); } } } if (!v.empty()) return v; //Look for items South for(QGraphicsItem* const item: items) { const double dy = item->pos().y() - focus_item->pos().y(); if (dy > 0.0) //Use '>' (instead of '>=') to leave out focus_item { assert(item != focus_item); v.push_back(item); } } return v; } std::vector<QGraphicsItem *> ribi::QtKeyboardFriendlyGraphicsView::GetItemsLeft(const QGraphicsItem* const focus_item) const { std::vector<QGraphicsItem *> v; const QList<QGraphicsItem *> items = this->items(); //Look for items between South-West and North-West first for(QGraphicsItem* const item: items) { const double dx = item->pos().x() - focus_item->pos().x(); if (dx < 0.0) //Use '<' (instead of '<=') to leave out focus_item { const double dy = item->pos().y() - focus_item->pos().y(); if (std::abs(dy) < std::abs(dx)) { assert(item != focus_item); v.push_back(item); } } } //Look for items Westwards for(QGraphicsItem* const item: items) { const double dx = item->pos().x() - focus_item->pos().x(); if (dx < 0.0) //Use '<' (instead of '<=') to leave out focus_item { assert(item != focus_item); v.push_back(item); } } return v; } std::vector<QGraphicsItem *> ribi::QtKeyboardFriendlyGraphicsView::GetItemsRight(const QGraphicsItem* const focus_item) const { std::vector<QGraphicsItem *> v; const QList<QGraphicsItem *> items = this->items(); //Look for items between North-East and South-East first for(QGraphicsItem* const item: items) { const double dx = item->pos().x() - focus_item->pos().x(); if (dx > 0.0) //Use '>' (instead of '>=') to leave out focus_item { const double dy = item->pos().y() - focus_item->pos().y(); if (std::abs(dy) < std::abs(dx)) { assert(item != focus_item); v.push_back(item); } } } //Look for items Eastwards for(QGraphicsItem* const item: items) { const double dx = item->pos().x() - focus_item->pos().x(); if (dx > 0.0) //Use '>' (instead of '>=') to leave out focus_item { assert(item != focus_item); v.push_back(item); } } return v; } std::string ribi::QtKeyboardFriendlyGraphicsView::GetVersion() noexcept { return "1.1"; } std::vector<std::string> ribi::QtKeyboardFriendlyGraphicsView::GetVersionHistory() noexcept { return { "2012-12-13: version 1.0: initial version", "2012-12-31: version 1.1: improved moving focus" }; } void ribi::QtKeyboardFriendlyGraphicsView::keyPressEvent(QKeyEvent *event) noexcept { if (event->modifiers() & Qt::ShiftModifier) { switch (event->key()) { case Qt::Key_Up: { QGraphicsItem* const focus_item = scene()->focusItem(); if (!focus_item) { return; } if (!(focus_item->flags() & QGraphicsItem::ItemIsMovable)) { return; } focus_item->setPos(focus_item->pos() + QPointF(0.0,-10.0)); m_signal_update(focus_item); scene()->update(); return; } case Qt::Key_Right: { QGraphicsItem* const focus_item = scene()->focusItem(); if (!focus_item) { return; } if (!(focus_item->flags() & QGraphicsItem::ItemIsMovable)) { return; } focus_item->setPos(focus_item->pos() + QPointF(10.0,0.0)); m_signal_update(focus_item); scene()->update(); return; } case Qt::Key_Down: { QGraphicsItem* const focus_item = scene()->focusItem(); if (!focus_item) { return; } if (!(focus_item->flags() & QGraphicsItem::ItemIsMovable)) { return; } focus_item->setPos(focus_item->pos() + QPointF(0.0,10.0)); m_signal_update(focus_item); scene()->update(); return; } case Qt::Key_Left: { QGraphicsItem* const focus_item = scene()->focusItem(); if (!focus_item) { return; } if (!(focus_item->flags() & QGraphicsItem::ItemIsMovable)) { return; } focus_item->setPos(focus_item->pos() + QPointF(-10.0,0.0)); m_signal_update(focus_item); scene()->update(); return; } } } switch (event->key()) { case Qt::Key_Up: { QGraphicsItem* const focus_item = scene()->focusItem(); if (!focus_item) { return; } const std::vector<QGraphicsItem *> items = GetItemsAbove(focus_item); if (items.empty()) { return; } QGraphicsItem* const new_focus_item = GetClosest(focus_item,items); if (!new_focus_item) return; focus_item->setEnabled(false); focus_item->clearFocus(); focus_item->setEnabled(true); new_focus_item->setFocus(); } break; case Qt::Key_Tab: case Qt::Key_Right: { QGraphicsItem* const focus_item = scene()->focusItem(); if (!focus_item) { return; } const std::vector<QGraphicsItem *> items = GetItemsRight(focus_item); if (items.empty()) { return; } QGraphicsItem* const new_focus_item = GetClosest(focus_item,items); if (!new_focus_item) return; focus_item->setEnabled(false); focus_item->clearFocus(); focus_item->setEnabled(true); new_focus_item->setFocus(); } break; case Qt::Key_Down: { QGraphicsItem* const focus_item = scene()->focusItem(); if (!focus_item) { return; } const std::vector<QGraphicsItem *> items = GetItemsBelow(focus_item); if (items.empty()) { return; } QGraphicsItem* const new_focus_item = GetClosest(focus_item,items); if (!new_focus_item) return; focus_item->setEnabled(false); focus_item->clearFocus(); focus_item->setEnabled(true); new_focus_item->setFocus(); } break; case Qt::Key_Left: case Qt::Key_Backtab: { QGraphicsItem* const focus_item = scene()->focusItem(); if (!focus_item) { return; } const std::vector<QGraphicsItem *> items = GetItemsLeft(focus_item); if (items.empty()) { return; } QGraphicsItem* const new_focus_item = GetClosest(focus_item,items); if (!new_focus_item) return; focus_item->setEnabled(false); focus_item->clearFocus(); focus_item->setEnabled(true); new_focus_item->setFocus(); } break; case Qt::Key_Space: { SetRandomFocus(); } break; case Qt::Key_Question: { if (const QGraphicsItem* const item = scene()->focusItem()) { std::clog << __func__ << ": QGraphicsItem selected at (" << item->pos().x() << "," << item->pos().y() << ")" << std::endl; } else { std::clog << __func__ << ": no QGraphicsItem selected" << std::endl; } } break; } //Let QGraphicsView do the rest... QGraphicsView::keyPressEvent(event); this->update(); } void ribi::QtKeyboardFriendlyGraphicsView::SetRandomFocus() { //Let existing item lose focus if (QGraphicsItem* const item = scene()->focusItem()) { //Really lose focus item->setEnabled(false); item->clearFocus(); //item->setSelected(false); // #239 item->setEnabled(true); } //Let a random item receive focus const QList<QGraphicsItem *> all_items = this->items(); QList<QGraphicsItem *> items; std::copy_if(all_items.begin(),all_items.end(),std::back_inserter(items), [](const QGraphicsItem* const item) { return (item->flags() & QGraphicsItem::ItemIsFocusable) && item->isVisible(); } ); if (!items.empty()) { const int i = std::rand() % items.size(); items.at(i)->setFocus(); //items.at(i)->setSelected(true); // #239 } }