diff options
author | Marc Deop <damnshock@gmail.com> | 2012-01-12 23:46:03 +0100 |
---|---|---|
committer | Andrea Diamantini <adjam7@gmail.com> | 2012-01-12 23:46:03 +0100 |
commit | c89d16a2e9eba8514e263c679faa355b90c59d2e (patch) | |
tree | 0e0469c9a1d70bd2ed9c8a0087d90789675daef9 | |
parent | Let keys autoscroll work also when middle click use is disabled (diff) | |
download | rekonq-c89d16a2e9eba8514e263c679faa355b90c59d2e.tar.xz |
Access Keys navigation
Ported access keys navigation system from Arora and adapted
to rekonq code.
Also (by adjam), get sure access keys are removed on loadStarted
REVIEW:103601
REVIEWED-BY: adjam
-rw-r--r-- | src/rekonq.kcfg | 3 | ||||
-rw-r--r-- | src/settings/settings_general.ui | 7 | ||||
-rw-r--r-- | src/urlbar/bookmarkwidget.cpp | 3 | ||||
-rw-r--r-- | src/urlbar/bookmarkwidget.h | 1 | ||||
-rw-r--r-- | src/webview.cpp | 215 | ||||
-rw-r--r-- | src/webview.h | 17 |
6 files changed, 239 insertions, 7 deletions
diff --git a/src/rekonq.kcfg b/src/rekonq.kcfg index bfaece5f..4c3dec40 100644 --- a/src/rekonq.kcfg +++ b/src/rekonq.kcfg @@ -79,6 +79,9 @@ <entry name="enableViShortcuts" type="Bool"> <default>false</default> </entry> + <entry name="accessKeysEnabled" type="Bool"> + <default>false</default> + </entry> </group> diff --git a/src/settings/settings_general.ui b/src/settings/settings_general.ui index 7e250c47..908f1c27 100644 --- a/src/settings/settings_general.ui +++ b/src/settings/settings_general.ui @@ -257,6 +257,13 @@ </property> </widget> </item> + <item> + <widget class="QCheckBox" name="kcfg_accessKeysEnabled"> + <property name="text"> + <string>Enable keyboard navigation using the Ctrl key</string> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/src/urlbar/bookmarkwidget.cpp b/src/urlbar/bookmarkwidget.cpp index 5ac8d2e6..b36af2bc 100644 --- a/src/urlbar/bookmarkwidget.cpp +++ b/src/urlbar/bookmarkwidget.cpp @@ -3,6 +3,7 @@ * This file is a part of the rekonq project * * Copyright (C) 2010-2011 by Yoann Laissus <yoann dot laissus at gmail dot com> +* Copyright (C) 2012 by Andrea Diamantini <adjam7 at gmail dot com> * * * This program is free software; you can redistribute it and/or @@ -168,7 +169,7 @@ void BookmarkWidget::setupFolderComboBox() for (KBookmark bookmark = root.first(); !bookmark.isNull(); bookmark = root.next(bookmark)) { - if(bookmark.isGroup()) + if (bookmark.isGroup()) { m_folder->addItem(bookmark.text(), bookmark.address()); } diff --git a/src/urlbar/bookmarkwidget.h b/src/urlbar/bookmarkwidget.h index 025077c7..f70fc750 100644 --- a/src/urlbar/bookmarkwidget.h +++ b/src/urlbar/bookmarkwidget.h @@ -3,6 +3,7 @@ * This file is a part of the rekonq project * * Copyright (C) 2010-2011 by Yoann Laissus <yoann dot laissus at gmail dot com> +* Copyright (C) 2012 by Andrea Diamantini <adjam7 at gmail dot com> * * * This program is free software; you can redistribute it and/or diff --git a/src/webview.cpp b/src/webview.cpp index 8a4cc4ba..da7f802a 100644 --- a/src/webview.cpp +++ b/src/webview.cpp @@ -76,6 +76,7 @@ WebView::WebView(QWidget* parent) , m_smoothScrolling(false) , m_dy(0) , m_smoothScrollSteps(0) + , m_accessKeysPressed(false) { WebPage *page = new WebPage(this); setPage(page); @@ -99,6 +100,7 @@ WebView::WebView(QWidget* parent) m_smoothScrollTimer->setInterval(16); connect(this, SIGNAL(iconChanged()), this, SLOT(changeWindowIcon())); + connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted())); } @@ -109,6 +111,12 @@ WebView::~WebView() } +void WebView::loadStarted() +{ + hideAccessKeys(); +} + + void WebView::changeWindowIcon() { if (ReKonfig::useFavicon()) @@ -159,7 +167,7 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) if (result.isContentSelected()) resultHit = WebView::TextSelection; - // -------------------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- // Ok, let's start filling up the menu... // is content editable? Add PASTE @@ -169,7 +177,7 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) menu.addSeparator(); } - // EMPTY PAGE ACTIONS ------------------------------------------------------------------------- + // EMPTY PAGE ACTIONS ------------------------------------------------------------- if (resultHit == WebView::EmptySelection) { // send by mail: page url @@ -226,7 +234,7 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) } } - // LINK ACTIONS ------------------------------------------------------------------------------- + // LINK ACTIONS ------------------------------------------------------------------- if (resultHit & WebView::LinkSelection) { // send by mail: link url @@ -248,7 +256,7 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) menu.addAction(pageAction(KWebPage::CopyLinkToClipboard)); } - // IMAGE ACTIONS ------------------------------------------------------------------------------ + // IMAGE ACTIONS ------------------------------------------------------------------ if (resultHit & WebView::ImageSelection) { // send by mail: image url @@ -271,7 +279,7 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) menu.addAction(a); } - // ACTIONS FOR TEXT SELECTION ----------------------------------------------------------------- + // ACTIONS FOR TEXT SELECTION ----------------------------------------------------- if (resultHit & WebView::TextSelection) { // send by mail: text @@ -355,7 +363,7 @@ void WebView::contextMenuEvent(QContextMenuEvent *event) } } - // DEFAULT ACTIONs (on the bottom) --------------------------------------------------- + // DEFAULT ACTIONs (on the bottom) ------------------------------------------------ menu.addSeparator(); if (resultHit & WebView::LinkSelection) { @@ -561,6 +569,7 @@ void WebView::viewImage(Qt::MouseButtons buttons, Qt::KeyboardModifiers modifier } } + void WebView::slotCopyImageLocation() { KAction *a = qobject_cast<KAction*>(sender()); @@ -609,6 +618,26 @@ void WebView::bookmarkLink() void WebView::keyPressEvent(QKeyEvent *event) { + if (ReKonfig::accessKeysEnabled()) + { + m_accessKeysPressed = (event->modifiers() == Qt::ControlModifier + && event->key() == Qt::Key_Control); + if (!m_accessKeysPressed) + { + if (checkForAccessKey(event)) + { + hideAccessKeys(); + event->accept(); + return; + } + hideAccessKeys(); + } + else + { + QTimer::singleShot(200, this, SLOT(accessKeyShortcut())); + } + } + if (event->modifiers() == Qt::ControlModifier) { if (event->key() == Qt::Key_C) @@ -898,6 +927,179 @@ void WebView::dragMoveEvent(QDragMoveEvent *event) } +void WebView::hideAccessKeys() +{ + if (!m_accessKeyLabels.isEmpty()) + { + for (int i = 0; i < m_accessKeyLabels.count(); ++i) + { + QLabel *label = m_accessKeyLabels[i]; + label->hide(); + label->deleteLater(); + } + m_accessKeyLabels.clear(); + m_accessKeyNodes.clear(); + update(); + } +} + + +void WebView::showAccessKeys() +{ + QStringList supportedElement; + supportedElement << QLatin1String("a") + << QLatin1String("input") + << QLatin1String("area") + << QLatin1String("button") + << QLatin1String("label") + << QLatin1String("legend") + << QLatin1String("textarea"); + + QList<QChar> unusedKeys; + for (char c = 'A'; c <= 'Z'; ++c) + unusedKeys << QLatin1Char(c); + for (char c = '0'; c <= '9'; ++c) + unusedKeys << QLatin1Char(c); + + QRect viewport = QRect(page()->mainFrame()->scrollPosition(), page()->viewportSize()); + // Priority first goes to elements with accesskey attributes + QList<QWebElement> alreadyLabeled; + Q_FOREACH(const QString & elementType, supportedElement) + { + QList<QWebElement> result = page()->mainFrame()->findAllElements(elementType).toList(); + Q_FOREACH(const QWebElement & element, result) + { + const QRect geometry = element.geometry(); + if (geometry.size().isEmpty() + || !viewport.contains(geometry.topLeft())) + { + continue; + } + QString accessKeyAttribute = element.attribute(QLatin1String("accesskey")).toUpper(); + if (accessKeyAttribute.isEmpty()) + continue; + QChar accessKey; + for (int i = 0; i < accessKeyAttribute.count(); i += 2) + { + const QChar &possibleAccessKey = accessKeyAttribute[i]; + if (unusedKeys.contains(possibleAccessKey)) + { + accessKey = possibleAccessKey; + break; + } + } + if (accessKey.isNull()) + { + continue; + } + unusedKeys.removeOne(accessKey); + makeAccessKeyLabel(accessKey, element); + alreadyLabeled.append(element); + } + } + + // Pick an access key first from the letters in the text and then from the + // list of unused access keys + Q_FOREACH(const QString & elementType, supportedElement) + { + QWebElementCollection result = page()->mainFrame()->findAllElements(elementType); + Q_FOREACH(const QWebElement & element, result) + { + const QRect geometry = element.geometry(); + if (unusedKeys.isEmpty() + || alreadyLabeled.contains(element) + || geometry.size().isEmpty()) + { + continue; + } + QChar accessKey; + QString text = element.toPlainText().toUpper(); + for (int i = 0; i < text.count(); ++i) + { + const QChar &c = text.at(i); + if (unusedKeys.contains(c)) + { + accessKey = c; + break; + } + } + if (accessKey.isNull()) + accessKey = unusedKeys.takeFirst(); + unusedKeys.removeOne(accessKey); + makeAccessKeyLabel(accessKey, element); + } + } +} + + +void WebView::makeAccessKeyLabel(const QChar &accessKey, const QWebElement &element) +{ + QLabel *label = new QLabel(this); + label->setText(QString(QLatin1String("<qt><b>%1</b>")).arg(accessKey)); + + label->setAutoFillBackground(true); + label->setFrameStyle(QFrame::Box | QFrame::Plain); + QPoint point = element.geometry().center(); + point -= page()->mainFrame()->scrollPosition(); + label->move(point); + label->show(); + point.setX(point.x() - label->width() / 2); + label->move(point); + m_accessKeyLabels.append(label); + m_accessKeyNodes[accessKey] = element; +} + + +bool WebView::checkForAccessKey(QKeyEvent *event) +{ + if (m_accessKeyLabels.isEmpty()) + return false; + + QString text = event->text(); + if (text.isEmpty()) + return false; + QChar key = text.at(0).toUpper(); + bool handled = false; + if (m_accessKeyNodes.contains(key)) + { + QWebElement element = m_accessKeyNodes[key]; + QPoint p = element.geometry().center(); + QWebFrame *frame = element.webFrame(); + Q_ASSERT(frame); + do + { + p -= frame->scrollPosition(); + frame = frame->parentFrame(); + } + while (frame && frame != page()->mainFrame()); + QMouseEvent pevent(QEvent::MouseButtonPress, p, Qt::LeftButton, 0, 0); + rApp->sendEvent(this, &pevent); + QMouseEvent revent(QEvent::MouseButtonRelease, p, Qt::LeftButton, 0, 0); + rApp->sendEvent(this, &revent); + handled = true; + } + return handled; +} + + +void WebView::accessKeyShortcut() +{ + if (!hasFocus() + || !m_accessKeysPressed + || !ReKonfig::accessKeysEnabled()) + return; + if (m_accessKeyLabels.isEmpty()) + { + showAccessKeys(); + } + else + { + hideAccessKeys(); + } + m_accessKeysPressed = false; +} + + void WebView::sendByMail() { KAction *a = qobject_cast<KAction*>(sender()); @@ -906,3 +1108,4 @@ void WebView::sendByMail() KToolInvocation::invokeMailer("", "", "", "", url); } + diff --git a/src/webview.h b/src/webview.h index 95f27f17..0d94aaf6 100644 --- a/src/webview.h +++ b/src/webview.h @@ -37,6 +37,8 @@ //Qt Includes #include <QtCore/QTime> +#include <QLabel> +#include <QToolTip> // Forward Declarations class WebPage; @@ -100,6 +102,11 @@ private Q_SLOTS: void stopScrolling(); void changeWindowIcon(); + void accessKeyShortcut(); + void hideAccessKeys(); + + void loadStarted(); + Q_SIGNALS: void loadUrl(const KUrl &, const Rekonq::OpenType &); void zoomChanged(int); @@ -107,6 +114,11 @@ Q_SIGNALS: void openNextInHistory(); private: + bool checkForAccessKey(QKeyEvent *event); + void showAccessKeys(); + void makeAccessKeyLabel(const QChar &accessKey, const QWebElement &element); + +private: QPoint m_mousePos; QPoint m_clickPos; @@ -125,6 +137,11 @@ private: bool m_smoothScrolling; int m_dy; int m_smoothScrollSteps; + + // Access Keys + QList<QLabel*> m_accessKeyLabels; + QHash<QChar, QWebElement> m_accessKeyNodes; + bool m_accessKeysPressed; }; #endif |