Merge lp:~osomon/webbrowser-app/qquickshortcuts into lp:webbrowser-app

Proposed by Olivier Tilloy
Status: Merged
Approved by: Olivier Tilloy
Approved revision: 1391
Merged at revision: 1407
Proposed branch: lp:~osomon/webbrowser-app/qquickshortcuts
Merge into: lp:webbrowser-app
Diff against target: 1198 lines (+666/-337)
12 files modified
src/app/CMakeLists.txt (+3/-1)
src/app/FilteredKeyboardModel.qml (+33/-0)
src/app/browserapplication.cpp (+2/-0)
src/app/qquickshortcut.cpp (+283/-0)
src/app/qquickshortcut_p.h (+109/-0)
src/app/webbrowser/Browser.qml (+200/-258)
src/app/webbrowser/KeyboardShortcut.qml (+0/-25)
src/app/webbrowser/KeyboardShortcuts.qml (+0/-41)
src/app/webbrowser/ListViewHighlight.qml (+2/-3)
src/app/webbrowser/NavigationBar.qml (+10/-7)
tests/autopilot/webbrowser_app/tests/test_keyboard.py (+22/-1)
tests/autopilot/webbrowser_app/tests/test_private.py (+2/-1)
To merge this branch: bzr merge lp:~osomon/webbrowser-app/qquickshortcuts
Reviewer Review Type Date Requested Status
system-apps-ci-bot continuous-integration Needs Fixing
PS Jenkins bot continuous-integration Needs Fixing
Review via email: mp+290299@code.launchpad.net

Commit message

Import QQuickShortcut from Qt 5.5 to properly handle window-level keyboard shortcuts.
We cannot bump the dependency on Qt to 5.5 as the stable overlay PPA for devices currently has Qt 5.4.1.

To post a comment you must log in.
1389. By Olivier Tilloy

Remove an incorrect condition now that the QInputInfo API is available.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1390. By Olivier Tilloy

Fix failing autopilot test.

1391. By Olivier Tilloy

Filter out autopilot-emulated keyboards.

Revision history for this message
system-apps-ci-bot (system-apps-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
1392. By Olivier Tilloy

Do not change focus unnecessarily when switching tabs: clear the address bar instead if needed.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/app/CMakeLists.txt'
2--- src/app/CMakeLists.txt 2016-02-09 21:19:41 +0000
3+++ src/app/CMakeLists.txt 2016-04-08 16:47:47 +0000
4@@ -27,13 +27,15 @@
5 session-storage.cpp
6 single-instance-manager.cpp
7 webbrowser-window.cpp
8+ qquickshortcut.cpp
9 )
10
11 add_library(${COMMONLIB} STATIC ${COMMONLIB_SRC})
12
13 include_directories(${unity8_SOURCE_DIR}/libs/UbuntuGestures
14 ${unity8_SOURCE_DIR}/plugins
15- ${LIBAPPARMOR_INCLUDE_DIRS})
16+ ${LIBAPPARMOR_INCLUDE_DIRS}
17+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
18 target_link_libraries(${COMMONLIB}
19 Qt5::Core
20 Qt5::Gui
21
22=== added file 'src/app/FilteredKeyboardModel.qml'
23--- src/app/FilteredKeyboardModel.qml 1970-01-01 00:00:00 +0000
24+++ src/app/FilteredKeyboardModel.qml 2016-04-08 16:47:47 +0000
25@@ -0,0 +1,33 @@
26+/*
27+ * Copyright 2016 Canonical Ltd.
28+ *
29+ * This file is part of webbrowser-app.
30+ *
31+ * webbrowser-app is free software; you can redistribute it and/or modify
32+ * it under the terms of the GNU General Public License as published by
33+ * the Free Software Foundation; version 3.
34+ *
35+ * webbrowser-app is distributed in the hope that it will be useful,
36+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
37+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38+ * GNU General Public License for more details.
39+ *
40+ * You should have received a copy of the GNU General Public License
41+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
42+ */
43+
44+import QtQuick 2.4
45+import Ubuntu.Components 1.3
46+import Unity.InputInfo 0.1
47+
48+SortFilterModel {
49+ model: InputDeviceModel {
50+ deviceFilter: InputInfo.Keyboard
51+ }
52+ filter {
53+ // Filter out autopilot-emulated keyboards
54+ // (see https://launchpad.net/bugs/1542224).
55+ property: "name"
56+ pattern: /^(?!py-evdev-uinput).*$/
57+ }
58+}
59
60=== modified file 'src/app/browserapplication.cpp'
61--- src/app/browserapplication.cpp 2016-02-09 22:03:25 +0000
62+++ src/app/browserapplication.cpp 2016-04-08 16:47:47 +0000
63@@ -38,6 +38,7 @@
64 #include "favicon-fetcher.h"
65 #include "meminfo.h"
66 #include "mime-database.h"
67+#include "qquickshortcut_p.h"
68 #include "session-storage.h"
69 #include "webbrowser-window.h"
70
71@@ -179,6 +180,7 @@
72 qmlRegisterSingletonType<MemInfo>(uri, 0, 1, "MemInfo", MemInfo_singleton_factory);
73 qmlRegisterSingletonType<MimeDatabase>(uri, 0, 1, "MimeDatabase", MimeDatabase_singleton_factory);
74 qmlRegisterType<SessionStorage>(uri, 0, 1, "SessionStorage");
75+ qmlRegisterType<QQuickShortcut>(uri, 0, 1, "Shortcut");
76
77 const char* gesturesUri = "Ubuntu.Gestures";
78 qmlRegisterSingletonType<Direction>(gesturesUri, 0, 1, "Direction", Direction_singleton_factory);
79
80=== added file 'src/app/qquickshortcut.cpp'
81--- src/app/qquickshortcut.cpp 1970-01-01 00:00:00 +0000
82+++ src/app/qquickshortcut.cpp 2016-04-08 16:47:47 +0000
83@@ -0,0 +1,283 @@
84+/****************************************************************************
85+**
86+** Copyright (C) 2015 The Qt Company Ltd.
87+** Contact: http://www.qt.io/licensing/
88+**
89+** This file is part of the QtQuick module of the Qt Toolkit.
90+**
91+** $QT_BEGIN_LICENSE:LGPL21$
92+** Commercial License Usage
93+** Licensees holding valid commercial Qt licenses may use this file in
94+** accordance with the commercial license agreement provided with the
95+** Software or, alternatively, in accordance with the terms contained in
96+** a written agreement between you and The Qt Company. For licensing terms
97+** and conditions see http://www.qt.io/terms-conditions. For further
98+** information use the contact form at http://www.qt.io/contact-us.
99+**
100+** GNU Lesser General Public License Usage
101+** Alternatively, this file may be used under the terms of the GNU Lesser
102+** General Public License version 2.1 or version 3 as published by the Free
103+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
104+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
105+** following information to ensure the GNU Lesser General Public License
106+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
107+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
108+**
109+** As a special exception, The Qt Company gives you certain additional
110+** rights. These rights are described in The Qt Company LGPL Exception
111+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
112+**
113+** $QT_END_LICENSE$
114+**
115+****************************************************************************/
116+
117+#include "qquickshortcut_p.h"
118+
119+#include <QtQuick/qquickitem.h>
120+#include <QtQuick/qquickwindow.h>
121+#include <QtGui/private/qguiapplication_p.h>
122+
123+QT_BEGIN_NAMESPACE
124+
125+/*!
126+ \qmltype Shortcut
127+ \instantiates QQuickShortcut
128+ \inqmlmodule QtQuick
129+ \since 5.5
130+ \ingroup qtquick-input
131+ \brief Provides keyboard shortcuts
132+
133+ The Shortcut type provides a way of handling keyboard shortcuts. The shortcut can
134+ be set to one of the \l{QKeySequence::StandardKey}{standard keyboard shortcuts},
135+ or it can be described with a string containing a sequence of up to four key
136+ presses that are needed to \l{Shortcut::activated}{activate} the shortcut.
137+
138+ \qml
139+ Item {
140+ id: view
141+
142+ property int currentIndex
143+
144+ Shortcut {
145+ sequence: StandardKey.NextChild
146+ onActivated: view.currentIndex++
147+ }
148+ }
149+ \endqml
150+
151+ \sa Keys
152+*/
153+
154+/*! \qmlsignal QtQuick::Shortcut::activated()
155+
156+ This signal is emitted when the shortcut is activated.
157+
158+ The corresponding handler is \c onActivated.
159+*/
160+
161+/*! \qmlsignal QtQuick::Shortcut::activatedAmbiguously()
162+
163+ This signal is emitted when the shortcut is activated ambigously,
164+ meaning that it matches the start of more than one shortcut.
165+
166+ The corresponding handler is \c onActivatedAmbiguously.
167+*/
168+
169+QQuickShortcut::QQuickShortcut(QObject *parent) : QObject(parent), m_id(0),
170+ m_enabled(true), m_completed(false), m_autorepeat(true), m_context(Qt::WindowShortcut)
171+{
172+}
173+
174+QQuickShortcut::~QQuickShortcut()
175+{
176+ ungrabShortcut();
177+}
178+
179+/*!
180+ \qmlproperty keysequence QtQuick::Shortcut::sequence
181+
182+ This property holds the shortcut's key sequence. The key sequence can be set
183+ to one of the \l{QKeySequence::StandardKey}{standard keyboard shortcuts}, or
184+ it can be described with a string containing a sequence of up to four key
185+ presses that are needed to \l{Shortcut::activated}{activate} the shortcut.
186+
187+ The default value is an empty key sequence.
188+
189+ \qml
190+ Shortcut {
191+ sequence: "Ctrl+E,Ctrl+W"
192+ onActivated: edit.wrapMode = TextEdit.Wrap
193+ }
194+ \endqml
195+*/
196+QVariant QQuickShortcut::sequence() const
197+{
198+ return m_sequence;
199+}
200+
201+void QQuickShortcut::setSequence(const QVariant &sequence)
202+{
203+ if (sequence == m_sequence)
204+ return;
205+
206+ QKeySequence shortcut;
207+ if (sequence.type() == QVariant::Int)
208+ shortcut = QKeySequence(static_cast<QKeySequence::StandardKey>(sequence.toInt()));
209+ else
210+ shortcut = QKeySequence::fromString(sequence.toString());
211+
212+ grabShortcut(shortcut, m_context);
213+
214+ m_sequence = sequence;
215+ m_shortcut = shortcut;
216+ emit sequenceChanged();
217+}
218+
219+/*!
220+ \qmlproperty bool QtQuick::Shortcut::enabled
221+
222+ This property holds whether the shortcut is enabled.
223+
224+ The default value is \c true.
225+*/
226+bool QQuickShortcut::isEnabled() const
227+{
228+ return m_enabled;
229+}
230+
231+void QQuickShortcut::setEnabled(bool enabled)
232+{
233+ if (enabled == m_enabled)
234+ return;
235+
236+ if (m_id)
237+ QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enabled, m_id, this);
238+
239+ m_enabled = enabled;
240+ emit enabledChanged();
241+}
242+
243+/*!
244+ \qmlproperty bool QtQuick::Shortcut::autoRepeat
245+
246+ This property holds whether the shortcut can auto repeat.
247+
248+ The default value is \c true.
249+*/
250+bool QQuickShortcut::autoRepeat() const
251+{
252+ return m_autorepeat;
253+}
254+
255+void QQuickShortcut::setAutoRepeat(bool repeat)
256+{
257+ if (repeat == m_autorepeat)
258+ return;
259+
260+ if (m_id)
261+ QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(repeat, m_id, this);
262+
263+ m_autorepeat = repeat;
264+ emit autoRepeatChanged();
265+}
266+
267+/*!
268+ \qmlproperty enumeration QtQuick::Shortcut::context
269+
270+ This property holds the \l{Qt::ShortcutContext}{shortcut context}.
271+
272+ Supported values are:
273+ \list
274+ \li \c Qt.WindowShortcut (default) - The shortcut is active when its parent item is in an active top-level window.
275+ \li \c Qt.ApplicationShortcut - The shortcut is active when one of the application's windows are active.
276+ \endlist
277+
278+ \qml
279+ Shortcut {
280+ sequence: StandardKey.Quit
281+ context: Qt.ApplicationShortcut
282+ onActivated: Qt.quit()
283+ }
284+ \endqml
285+*/
286+Qt::ShortcutContext QQuickShortcut::context() const
287+{
288+ return m_context;
289+}
290+
291+void QQuickShortcut::setContext(Qt::ShortcutContext context)
292+{
293+ if (context == m_context)
294+ return;
295+
296+ grabShortcut(m_shortcut, context);
297+
298+ m_context = context;
299+ emit contextChanged();
300+}
301+
302+void QQuickShortcut::classBegin()
303+{
304+}
305+
306+void QQuickShortcut::componentComplete()
307+{
308+ m_completed = true;
309+ grabShortcut(m_shortcut, m_context);
310+}
311+
312+bool QQuickShortcut::event(QEvent *event)
313+{
314+ if (m_enabled && event->type() == QEvent::Shortcut) {
315+ QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
316+ if (se->shortcutId() == m_id && se->key() == m_shortcut){
317+ if (se->isAmbiguous())
318+ emit activatedAmbiguously();
319+ else
320+ emit activated();
321+ return true;
322+ }
323+ }
324+ return false;
325+}
326+
327+static bool qQuickShortcutContextMatcher(QObject *obj, Qt::ShortcutContext context)
328+{
329+ switch (context) {
330+ case Qt::ApplicationShortcut:
331+ return true;
332+ case Qt::WindowShortcut:
333+ while (obj && !obj->isWindowType()) {
334+ obj = obj->parent();
335+ if (QQuickItem *item = qobject_cast<QQuickItem *>(obj))
336+ obj = item->window();
337+ }
338+ return obj && obj == QGuiApplication::focusWindow();
339+ default:
340+ return false;
341+ }
342+}
343+
344+void QQuickShortcut::grabShortcut(const QKeySequence &sequence, Qt::ShortcutContext context)
345+{
346+ ungrabShortcut();
347+
348+ if (m_completed && !sequence.isEmpty()) {
349+ QGuiApplicationPrivate *pApp = QGuiApplicationPrivate::instance();
350+ m_id = pApp->shortcutMap.addShortcut(this, sequence, context, qQuickShortcutContextMatcher);
351+ if (!m_enabled)
352+ pApp->shortcutMap.setShortcutEnabled(false, m_id, this);
353+ if (!m_autorepeat)
354+ pApp->shortcutMap.setShortcutAutoRepeat(false, m_id, this);
355+ }
356+}
357+
358+void QQuickShortcut::ungrabShortcut()
359+{
360+ if (m_id) {
361+ QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(m_id, this);
362+ m_id = 0;
363+ }
364+}
365+
366+QT_END_NAMESPACE
367
368=== added file 'src/app/qquickshortcut_p.h'
369--- src/app/qquickshortcut_p.h 1970-01-01 00:00:00 +0000
370+++ src/app/qquickshortcut_p.h 2016-04-08 16:47:47 +0000
371@@ -0,0 +1,109 @@
372+/****************************************************************************
373+**
374+** Copyright (C) 2015 The Qt Company Ltd.
375+** Contact: http://www.qt.io/licensing/
376+**
377+** This file is part of the QtQuick module of the Qt Toolkit.
378+**
379+** $QT_BEGIN_LICENSE:LGPL21$
380+** Commercial License Usage
381+** Licensees holding valid commercial Qt licenses may use this file in
382+** accordance with the commercial license agreement provided with the
383+** Software or, alternatively, in accordance with the terms contained in
384+** a written agreement between you and The Qt Company. For licensing terms
385+** and conditions see http://www.qt.io/terms-conditions. For further
386+** information use the contact form at http://www.qt.io/contact-us.
387+**
388+** GNU Lesser General Public License Usage
389+** Alternatively, this file may be used under the terms of the GNU Lesser
390+** General Public License version 2.1 or version 3 as published by the Free
391+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
392+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
393+** following information to ensure the GNU Lesser General Public License
394+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
395+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
396+**
397+** As a special exception, The Qt Company gives you certain additional
398+** rights. These rights are described in The Qt Company LGPL Exception
399+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
400+**
401+** $QT_END_LICENSE$
402+**
403+****************************************************************************/
404+
405+#ifndef QQUICKSHORTCUT_P_H
406+#define QQUICKSHORTCUT_P_H
407+
408+//
409+// W A R N I N G
410+// -------------
411+//
412+// This file is not part of the Qt API. It exists purely as an
413+// implementation detail. This header file may change from version to
414+// version without notice, or even be removed.
415+//
416+// We mean it.
417+//
418+
419+#include <QtCore/qobject.h>
420+#include <QtCore/qvariant.h>
421+#include <QtGui/qkeysequence.h>
422+#include <QtQml/qqmlparserstatus.h>
423+
424+QT_BEGIN_NAMESPACE
425+
426+class QQuickShortcut : public QObject, public QQmlParserStatus
427+{
428+ Q_OBJECT
429+ Q_INTERFACES(QQmlParserStatus)
430+ Q_PROPERTY(QVariant sequence READ sequence WRITE setSequence NOTIFY sequenceChanged FINAL)
431+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged FINAL)
432+ Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat NOTIFY autoRepeatChanged FINAL)
433+ Q_PROPERTY(Qt::ShortcutContext context READ context WRITE setContext NOTIFY contextChanged FINAL)
434+
435+public:
436+ explicit QQuickShortcut(QObject *parent = Q_NULLPTR);
437+ ~QQuickShortcut();
438+
439+ QVariant sequence() const;
440+ void setSequence(const QVariant &sequence);
441+
442+ bool isEnabled() const;
443+ void setEnabled(bool enabled);
444+
445+ bool autoRepeat() const;
446+ void setAutoRepeat(bool repeat);
447+
448+ Qt::ShortcutContext context() const;
449+ void setContext(Qt::ShortcutContext context);
450+
451+Q_SIGNALS:
452+ void sequenceChanged();
453+ void enabledChanged();
454+ void autoRepeatChanged();
455+ void contextChanged();
456+
457+ void activated();
458+ void activatedAmbiguously();
459+
460+protected:
461+ void classBegin() Q_DECL_OVERRIDE;
462+ void componentComplete() Q_DECL_OVERRIDE;
463+ bool event(QEvent *event) Q_DECL_OVERRIDE;
464+
465+ void grabShortcut(const QKeySequence &sequence, Qt::ShortcutContext context);
466+ void ungrabShortcut();
467+
468+private:
469+ int m_id;
470+ bool m_enabled;
471+ bool m_completed;
472+ bool m_autorepeat;
473+ QKeySequence m_shortcut;
474+ Qt::ShortcutContext m_context;
475+ QVariant m_sequence;
476+};
477+
478+QT_END_NAMESPACE
479+
480+#endif // QQUICKSHORTCUT_P_H
481
482=== modified file 'src/app/webbrowser/Browser.qml'
483--- src/app/webbrowser/Browser.qml 2016-03-07 18:44:34 +0000
484+++ src/app/webbrowser/Browser.qml 2016-04-08 16:47:47 +0000
485@@ -63,21 +63,6 @@
486 }
487
488 Connections {
489- target: tabsModel
490- onCurrentIndexChanged: {
491- // Remove focus from the address bar when the current tab
492- // changes to ensure that its contents are updated.
493- contentsContainer.forceActiveFocus()
494-
495- // In narrow mode, the tabslist is a stack:
496- // the current tab is always at the top.
497- if (!browser.wide) {
498- tabsModel.move(tabsModel.currentIndex, 0)
499- }
500- }
501- }
502-
503- Connections {
504 target: currentWebview
505
506 /* Note that we are connecting the mediaAccessPermissionRequested signal
507@@ -111,6 +96,10 @@
508 deviceFilter: InputInfo.TouchScreen
509 }
510
511+ FilteredKeyboardModel {
512+ id: keyboardModel
513+ }
514+
515 Component {
516 id: mediaAccessDialogComponent
517 MediaAccessDialog { }
518@@ -1425,21 +1414,12 @@
519 Component {
520 id: bookmarkOptionsComponent
521 BookmarkOptions {
522- id: bookmarkOptions
523 folderModel: BookmarksFolderListModel {
524 sourceModel: BookmarksModel
525 }
526
527- Component.onCompleted: {
528- forceActiveFocus()
529- }
530+ Component.onCompleted: forceActiveFocus()
531
532- // Fragile workaround for https://launchpad.net/bugs/1546677.
533- // By destroying the popover, its visibility isn’t changed to
534- // false, and thus the bookmark is not removed.
535- function closeAndConfirm() {
536- destroy()
537- }
538 onVisibleChanged: {
539 if (!visible) {
540 BookmarksModel.remove(bookmarkUrl)
541@@ -1452,28 +1432,11 @@
542 }
543 }
544
545- Keys.onPressed: {
546- if (bookmarkOptionsShortcuts.processKey(event.key, event.modifiers)) {
547- event.accepted = true
548- }
549- }
550-
551- KeyboardShortcuts {
552- id: bookmarkOptionsShortcuts
553- KeyboardShortcut {
554- key: Qt.Key_Return
555- onTriggered: closeAndConfirm()
556- }
557-
558- KeyboardShortcut {
559- modifiers: Qt.ControlModifier
560- key: Qt.Key_D
561- onTriggered: {
562- BookmarksModel.remove(bookmarkUrl)
563- closeAndConfirm()
564- }
565- }
566- }
567+ // Fragile workaround for https://launchpad.net/bugs/1546677.
568+ // By destroying the popover, its visibility isn’t changed to
569+ // false, and thus the bookmark is not removed.
570+ Keys.onEnterPressed: destroy()
571+ Keys.onReturnPressed: destroy()
572 }
573 }
574
575@@ -1627,16 +1590,7 @@
576 }
577
578 function maybeFocusAddressBar() {
579- // XXX: this is not the right condition, but it is better than
580- // inferring a "desktop" form factor from various heuristics.
581- // The real fix is to detect whether there is a physical keyboard
582- // connected, for which there is currently no API yet (there will
583- // be a QInputInfo API in a future version of Qt).
584- // Wide mode might be in use on a device without a physical
585- // keyboard (e.g. a 10" tablet), and conversely the browser window
586- // might be shrinked to a narrow layout on a desktop setup with a
587- // physical keyboard and no OSK.
588- if (browser.wide) {
589+ if (keyboardModel.count > 0) {
590 focusAddressBar()
591 } else {
592 contentsContainer.forceActiveFocus()
593@@ -1682,14 +1636,14 @@
594 }
595 }
596
597+ property var currentBookmarkOptionsDialog: null
598 function addBookmark(url, title, icon, location) {
599 if (title == "") title = UrlUtils.removeScheme(url)
600 BookmarksModel.add(url, title, icon, "")
601 if (location === undefined) location = chrome.bookmarkTogglePlaceHolder
602- PopupUtils.open(bookmarkOptionsComponent,
603- location,
604- {"bookmarkUrl": url,
605- "bookmarkTitle": title})
606+ var properties = {"bookmarkUrl": url, "bookmarkTitle": title}
607+ currentBookmarkOptionsDialog = PopupUtils.open(bookmarkOptionsComponent,
608+ location, properties)
609 }
610 }
611
612@@ -1926,6 +1880,13 @@
613
614 Connections {
615 target: tabsModel
616+ onCurrentIndexChanged: {
617+ // In narrow mode, the tabslist is a stack:
618+ // the current tab is always at the top.
619+ if (!browser.wide) {
620+ tabsModel.move(tabsModel.currentIndex, 0)
621+ }
622+ }
623 onCurrentTabChanged: {
624 chrome.findInPageMode = false
625 var tab = tabsModel.currentTab
626@@ -1972,206 +1933,187 @@
627 }
628 }
629
630- Keys.onPressed: if (shortcuts.processKey(event.key, event.modifiers)) event.accepted = true
631- KeyboardShortcuts {
632- id: shortcuts
633-
634- // Ctrl+Tab or Ctrl+PageDown: cycle through open tabs
635- KeyboardShortcut {
636- modifiers: Qt.ControlModifier
637- key: Qt.Key_Tab
638- enabled: tabContainer.visible || recentView.visible
639- onTriggered: internal.switchToNextTab()
640- }
641- KeyboardShortcut {
642- modifiers: Qt.ControlModifier
643- key: Qt.Key_PageDown
644- enabled: tabContainer.visible || recentView.visible
645- onTriggered: internal.switchToNextTab()
646- }
647-
648- // Ctrl+Shift+Tab or Ctrl+PageUp: cycle through open tabs in reverse order
649- KeyboardShortcut {
650- modifiers: Qt.ControlModifier
651- key: Qt.Key_Backtab
652- enabled: tabContainer.visible || recentView.visible
653- onTriggered: internal.switchToPreviousTab()
654- }
655- KeyboardShortcut {
656- modifiers: Qt.ControlModifier
657- key: Qt.Key_PageUp
658- enabled: tabContainer.visible || recentView.visible
659- onTriggered: internal.switchToPreviousTab()
660- }
661-
662- // Ctrl+Shift+W or Ctrl+Shift+T: Undo close tab
663- KeyboardShortcut {
664- modifiers: Qt.ControlModifier | Qt.ShiftModifier
665- key: Qt.Key_W
666- enabled: tabContainer.visible || recentView.visible
667- onTriggered: internal.undoCloseTab()
668- }
669-
670- KeyboardShortcut {
671- modifiers: Qt.ControlModifier | Qt.ShiftModifier
672- key: Qt.Key_T
673- enabled: tabContainer.visible || recentView.visible
674- onTriggered: internal.undoCloseTab()
675- }
676-
677- // Ctrl+W or Ctrl+F4: Close the current tab
678- KeyboardShortcut {
679- modifiers: Qt.ControlModifier
680- key: Qt.Key_W
681- enabled: tabContainer.visible || recentView.visible
682- onTriggered: internal.closeCurrentTab()
683- }
684- KeyboardShortcut {
685- modifiers: Qt.ControlModifier
686- key: Qt.Key_F4
687- enabled: tabContainer.visible || recentView.visible
688- onTriggered: internal.closeCurrentTab()
689- }
690-
691- // Ctrl+T: Open a new Tab
692- KeyboardShortcut {
693- modifiers: Qt.ControlModifier
694- key: Qt.Key_T
695- enabled: tabContainer.visible || recentView.visible ||
696- bookmarksViewLoader.active || historyViewLoader.active
697- onTriggered: {
698- openUrlInNewTab("", true)
699- if (recentView.visible) recentView.reset()
700- bookmarksViewLoader.active = false
701- historyViewLoader.active = false
702- }
703- }
704-
705- // F6 or Ctrl+L or Alt+D: Select the content in the address bar
706- KeyboardShortcut {
707- modifiers: Qt.ControlModifier
708- key: Qt.Key_L
709- enabled: tabContainer.visible
710- onTriggered: internal.focusAddressBar(true)
711- }
712- KeyboardShortcut {
713- modifiers: Qt.AltModifier
714- key: Qt.Key_D
715- enabled: tabContainer.visible
716- onTriggered: internal.focusAddressBar(true)
717- }
718- KeyboardShortcut {
719- key: Qt.Key_F6
720- enabled: tabContainer.visible
721- onTriggered: internal.focusAddressBar(true)
722- }
723-
724- // Ctrl+D: Toggle bookmarked state on current Tab
725- KeyboardShortcut {
726- modifiers: Qt.ControlModifier
727- key: Qt.Key_D
728- enabled: tabContainer.visible
729- onTriggered: {
730- if (currentWebview) {
731- if (BookmarksModel.contains(currentWebview.url)) {
732- BookmarksModel.remove(currentWebview.url)
733- } else {
734- internal.addBookmark(currentWebview.url, currentWebview.title, currentWebview.icon)
735- }
736+ // TODO: internationalize non-standard key sequences?
737+
738+ // Ctrl+Tab or Ctrl+PageDown: cycle through open tabs
739+ Shortcut {
740+ sequence: StandardKey.NextChild
741+ enabled: tabContainer.visible || recentView.visible
742+ onActivated: internal.switchToNextTab()
743+ }
744+ Shortcut {
745+ sequence: "Ctrl+PgDown"
746+ enabled: tabContainer.visible || recentView.visible
747+ onActivated: internal.switchToNextTab()
748+ }
749+
750+ // Ctrl+Shift+Tab or Ctrl+PageUp: cycle through open tabs in reverse order
751+ Shortcut {
752+ sequence: StandardKey.PreviousChild
753+ enabled: tabContainer.visible || recentView.visible
754+ onActivated: internal.switchToPreviousTab()
755+ }
756+ Shortcut {
757+ sequence: "Ctrl+Shift+Tab"
758+ enabled: tabContainer.visible || recentView.visible
759+ onActivated: internal.switchToPreviousTab()
760+ }
761+ Shortcut {
762+ sequence: "Ctrl+PgUp"
763+ enabled: tabContainer.visible || recentView.visible
764+ onActivated: internal.switchToPreviousTab()
765+ }
766+
767+ // Ctrl+W or Ctrl+F4: Close the current tab
768+ Shortcut {
769+ sequence: StandardKey.Close
770+ enabled: tabContainer.visible || recentView.visible
771+ onActivated: internal.closeCurrentTab()
772+ }
773+ Shortcut {
774+ sequence: "Ctrl+F4"
775+ enabled: tabContainer.visible || recentView.visible
776+ onActivated: internal.closeCurrentTab()
777+ }
778+
779+ // Ctrl+Shift+W or Ctrl+Shift+T: Undo close tab
780+ Shortcut {
781+ sequence: "Ctrl+Shift+W"
782+ enabled: tabContainer.visible || recentView.visible
783+ onActivated: internal.undoCloseTab()
784+ }
785+ Shortcut {
786+ sequence: "Ctrl+Shift+T"
787+ enabled: tabContainer.visible || recentView.visible
788+ onActivated: internal.undoCloseTab()
789+ }
790+
791+ // Ctrl+T: Open a new Tab
792+ Shortcut {
793+ sequence: StandardKey.AddTab
794+ enabled: tabContainer.visible || recentView.visible ||
795+ bookmarksViewLoader.active || historyViewLoader.active
796+ onActivated: {
797+ openUrlInNewTab("", true)
798+ if (recentView.visible) recentView.reset()
799+ bookmarksViewLoader.active = false
800+ historyViewLoader.active = false
801+ }
802+ }
803+
804+ // F6 or Ctrl+L or Alt+D: Select the content in the address bar
805+ Shortcut {
806+ sequence: "F6"
807+ enabled: tabContainer.visible
808+ onActivated: internal.focusAddressBar(true)
809+ }
810+ Shortcut {
811+ sequence: "Ctrl+L"
812+ enabled: tabContainer.visible
813+ onActivated: internal.focusAddressBar(true)
814+ }
815+ Shortcut {
816+ sequence: "Alt+D"
817+ enabled: tabContainer.visible
818+ onActivated: internal.focusAddressBar(true)
819+ }
820+
821+ // Ctrl+D: Toggle bookmarked state on current Tab
822+ Shortcut {
823+ sequence: "Ctrl+D"
824+ enabled: tabContainer.visible
825+ onActivated: {
826+ if (internal.currentBookmarkOptionsDialog) {
827+ internal.currentBookmarkOptionsDialog.hide()
828+ } else if (currentWebview) {
829+ if (BookmarksModel.contains(currentWebview.url)) {
830+ BookmarksModel.remove(currentWebview.url)
831+ } else {
832+ internal.addBookmark(currentWebview.url, currentWebview.title, currentWebview.icon)
833 }
834 }
835 }
836-
837- // Ctrl+H: Show History
838- KeyboardShortcut {
839- modifiers: Qt.ControlModifier
840- key: Qt.Key_H
841- enabled: tabContainer.visible
842- onTriggered: historyViewLoader.active = true
843- }
844-
845- // Ctrl+Shift+O: Show Bookmarks
846- KeyboardShortcut {
847- modifiers: Qt.ControlModifier | Qt.ShiftModifier
848- key: Qt.Key_O
849- enabled: tabContainer.visible
850- onTriggered: bookmarksViewLoader.active = true
851- }
852-
853- // Alt+← or Backspace: Goes to the previous page in history
854- KeyboardShortcut {
855- modifiers: Qt.AltModifier
856- key: Qt.Key_Left
857- enabled: tabContainer.visible
858- onTriggered: internal.historyGoBack()
859- }
860- KeyboardShortcut {
861- key: Qt.Key_Backspace
862- enabled: tabContainer.visible
863- onTriggered: internal.historyGoBack()
864- }
865-
866- // Alt+→ or Shift+Backspace: Goes to the next page in history
867- KeyboardShortcut {
868- modifiers: Qt.AltModifier
869- key: Qt.Key_Right
870- enabled: tabContainer.visible
871- onTriggered: internal.historyGoForward()
872- }
873- KeyboardShortcut {
874- modifiers: Qt.ShiftModifier
875- key: Qt.Key_Backspace
876- enabled: tabContainer.visible
877- onTriggered: internal.historyGoForward()
878- }
879-
880- // F5 or Ctrl+R: Reload current Tab
881- KeyboardShortcut {
882- key: Qt.Key_F5
883- enabled: tabContainer.visible
884- onTriggered: if (currentWebview) currentWebview.reload()
885- }
886- KeyboardShortcut {
887- modifiers: Qt.ControlModifier
888- key: Qt.Key_R
889- enabled: tabContainer.visible
890- onTriggered: if (currentWebview) currentWebview.reload()
891- }
892-
893- // Ctrl+F: Find in Page
894- KeyboardShortcut {
895- modifiers: Qt.ControlModifier
896- key: Qt.Key_F
897- enabled: tabContainer.visible && !newTabViewLoader.active
898- onTriggered: chrome.findInPageMode = true
899- }
900-
901- // Ctrl+J: Show downloads page
902- KeyboardShortcut {
903- modifiers: Qt.ControlModifier
904- key: Qt.Key_J
905- enabled: chrome.visible &&
906- downloadHandlerLoader.status == Loader.Ready &&
907- contentHandlerLoader.status == Loader.Ready &&
908- !downloadsViewLoader.active
909- onTriggered: downloadsViewLoader.active = true
910- }
911-
912- // Ctrl+Shift+G: Find previous
913- KeyboardShortcut {
914- modifiers: Qt.ControlModifier | Qt.ShiftModifier
915- key: Qt.Key_G
916- enabled: currentWebview && chrome.findInPageMode
917- onTriggered: currentWebview.findController.previous()
918- }
919-
920- // Ctrl+G: Find next
921- KeyboardShortcut {
922- modifiers: Qt.ControlModifier
923- key: Qt.Key_G
924- enabled: currentWebview && chrome.findInPageMode
925- onTriggered: currentWebview.findController.next()
926- }
927+ }
928+
929+ // Ctrl+H: Show History
930+ Shortcut {
931+ sequence: "Ctrl+H"
932+ enabled: tabContainer.visible
933+ onActivated: historyViewLoader.active = true
934+ }
935+
936+ // Ctrl+Shift+O: Show Bookmarks
937+ Shortcut {
938+ sequence: "Ctrl+Shift+O"
939+ enabled: tabContainer.visible
940+ onActivated: bookmarksViewLoader.active = true
941+ }
942+
943+ // Alt+← or Backspace: Goes to the previous page in history
944+ Shortcut {
945+ sequence: StandardKey.Back
946+ enabled: tabContainer.visible
947+ onActivated: internal.historyGoBack()
948+ }
949+ Shortcut {
950+ sequence: "Backspace"
951+ enabled: tabContainer.visible
952+ onActivated: internal.historyGoBack()
953+ }
954+
955+ // Alt+→ or Shift+Backspace: Goes to the next page in history
956+ Shortcut {
957+ sequence: StandardKey.Forward
958+ enabled: tabContainer.visible
959+ onActivated: internal.historyGoForward()
960+ }
961+ Shortcut {
962+ sequence: "Shift+Backspace"
963+ enabled: tabContainer.visible
964+ onActivated: internal.historyGoForward()
965+ }
966+
967+ // F5 or Ctrl+R: Reload current Tab
968+ Shortcut {
969+ sequence: StandardKey.Refresh
970+ enabled: tabContainer.visible
971+ onActivated: if (currentWebview) currentWebview.reload()
972+ }
973+ Shortcut {
974+ sequence: "F5"
975+ enabled: tabContainer.visible
976+ onActivated: if (currentWebview) currentWebview.reload()
977+ }
978+
979+ // Ctrl+F: Find in Page
980+ Shortcut {
981+ sequence: StandardKey.Find
982+ enabled: tabContainer.visible && !newTabViewLoader.active
983+ onActivated: chrome.findInPageMode = true
984+ }
985+
986+ // Ctrl+J: Show downloads page
987+ Shortcut {
988+ sequence: "Ctrl+J"
989+ enabled: chrome.visible &&
990+ downloadHandlerLoader.status == Loader.Ready &&
991+ contentHandlerLoader.status == Loader.Ready &&
992+ !downloadsViewLoader.active
993+ onActivated: downloadsViewLoader.active = true
994+ }
995+
996+ // Ctrl+G: Find next
997+ Shortcut {
998+ sequence: StandardKey.FindNext
999+ enabled: currentWebview && chrome.findInPageMode
1000+ onActivated: currentWebview.findController.next()
1001+ }
1002+
1003+ // Ctrl+Shift+G: Find previous
1004+ Shortcut {
1005+ sequence: StandardKey.FindPrevious
1006+ enabled: currentWebview && chrome.findInPageMode
1007+ onActivated: currentWebview.findController.previous()
1008 }
1009
1010 Loader {
1011
1012=== removed file 'src/app/webbrowser/KeyboardShortcut.qml'
1013--- src/app/webbrowser/KeyboardShortcut.qml 2015-08-10 15:22:00 +0000
1014+++ src/app/webbrowser/KeyboardShortcut.qml 1970-01-01 00:00:00 +0000
1015@@ -1,25 +0,0 @@
1016-/*
1017- * Copyright 2015 Canonical Ltd.
1018- *
1019- * This file is part of webbrowser-app.
1020- *
1021- * webbrowser-app is free software; you can redistribute it and/or modify
1022- * it under the terms of the GNU General Public License as published by
1023- * the Free Software Foundation; version 3.
1024- *
1025- * webbrowser-app is distributed in the hope that it will be useful,
1026- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1027- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1028- * GNU General Public License for more details.
1029- *
1030- * You should have received a copy of the GNU General Public License
1031- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1032- */
1033-
1034-import QtQuick 2.4
1035-import Ubuntu.Components 1.3
1036-
1037-Action {
1038- property int key
1039- property int modifiers: Qt.NoModifier
1040-}
1041
1042=== removed file 'src/app/webbrowser/KeyboardShortcuts.qml'
1043--- src/app/webbrowser/KeyboardShortcuts.qml 2015-08-10 15:22:00 +0000
1044+++ src/app/webbrowser/KeyboardShortcuts.qml 1970-01-01 00:00:00 +0000
1045@@ -1,41 +0,0 @@
1046-/*
1047- * Copyright 2015 Canonical Ltd.
1048- *
1049- * This file is part of webbrowser-app.
1050- *
1051- * webbrowser-app is free software; you can redistribute it and/or modify
1052- * it under the terms of the GNU General Public License as published by
1053- * the Free Software Foundation; version 3.
1054- *
1055- * webbrowser-app is distributed in the hope that it will be useful,
1056- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1057- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1058- * GNU General Public License for more details.
1059- *
1060- * You should have received a copy of the GNU General Public License
1061- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1062- */
1063-
1064-import QtQuick 2.4
1065-
1066-Item {
1067- function processKey(key, modifiers) {
1068- for (var i = 0; i < data.length; i++) {
1069- var shortcut = data[i];
1070-
1071- if (!shortcut.enabled) continue
1072- if (key !== shortcut.key) continue
1073-
1074- if (shortcut.modifiers === Qt.NoModifier) {
1075- if (modifiers === Qt.NoModifier) {
1076- shortcut.trigger()
1077- return true
1078- }
1079- } else if ((modifiers & shortcut.modifiers) === shortcut.modifiers) {
1080- shortcut.trigger()
1081- return true
1082- }
1083- }
1084- return false
1085- }
1086-}
1087
1088=== modified file 'src/app/webbrowser/ListViewHighlight.qml'
1089--- src/app/webbrowser/ListViewHighlight.qml 2016-02-10 17:20:47 +0000
1090+++ src/app/webbrowser/ListViewHighlight.qml 2016-04-08 16:47:47 +0000
1091@@ -18,7 +18,7 @@
1092
1093 import QtQuick 2.4
1094 import Ubuntu.Components 1.3
1095-import Unity.InputInfo 0.1
1096+import ".."
1097
1098 Rectangle {
1099 color: "transparent"
1100@@ -33,8 +33,7 @@
1101
1102 readonly property bool hasKeyboard: keyboardModel.count > 0
1103
1104- InputDeviceModel {
1105+ FilteredKeyboardModel {
1106 id: keyboardModel
1107- deviceFilter: InputInfo.Keyboard
1108 }
1109 }
1110
1111=== modified file 'src/app/webbrowser/NavigationBar.qml'
1112--- src/app/webbrowser/NavigationBar.qml 2016-03-01 09:20:32 +0000
1113+++ src/app/webbrowser/NavigationBar.qml 2016-04-08 16:47:47 +0000
1114@@ -220,15 +220,18 @@
1115 id: internal
1116 property var openDrawer: null
1117 readonly property var webview: tab ? tab.webview : null
1118+ }
1119
1120- onWebviewChanged: {
1121- if (webview) {
1122- addressbar.actualUrl = webview.url
1123- addressbar.securityStatus = webview.securityStatus
1124- } else {
1125- addressbar.actualUrl = ""
1126- addressbar.securityStatus = null
1127+ onTabChanged: {
1128+ if (tab) {
1129+ addressbar.actualUrl = tab.url
1130+ addressbar.securityStatus = (tab.webview ? tab.webview.securityStatus : null)
1131+ if (!tab.url.toString() && editing) {
1132+ addressbar.text = ""
1133 }
1134+ } else {
1135+ addressbar.actualUrl = ""
1136+ addressbar.securityStatus = null
1137 }
1138 }
1139
1140
1141=== modified file 'tests/autopilot/webbrowser_app/tests/test_keyboard.py'
1142--- tests/autopilot/webbrowser_app/tests/test_keyboard.py 2016-01-21 10:29:17 +0000
1143+++ tests/autopilot/webbrowser_app/tests/test_keyboard.py 2016-04-08 16:47:47 +0000
1144@@ -103,7 +103,7 @@
1145 self.check_tab_number(2)
1146 self.main_window.press_key('Ctrl+Page_Down')
1147 self.check_tab_number(0)
1148- self.main_window.press_key('Shift+Ctrl+Tab')
1149+ self.main_window.press_key('Ctrl+Shift+Tab')
1150 if self.main_window.wide:
1151 self.check_tab_number(2)
1152 else:
1153@@ -493,3 +493,24 @@
1154
1155 self.main_window.press_key('Ctrl+w')
1156 self.assert_number_webviews_eventually(1)
1157+
1158+ def test_addressbar_cleared_when_opening_new_tab(self):
1159+ # Verify that when opening a new tab while the address bar was focused,
1160+ # the address bar is cleared.
1161+ self.main_window.press_key('Ctrl+l')
1162+ self.address_bar.activeFocus.wait_for(True)
1163+ self.assertThat(self.address_bar.text, Eventually(Equals(self.url)))
1164+ self.main_window.press_key('Ctrl+t')
1165+ self.address_bar.activeFocus.wait_for(True)
1166+ self.assertThat(self.address_bar.text, Eventually(Equals("")))
1167+
1168+ def test_addressbar_cleared_when_switching_between_new_tabs(self):
1169+ # Verify that when opening a new tab while a new tab was already open
1170+ # with text input in the address bar, the address bar is cleared.
1171+ self.main_window.press_key('Ctrl+t')
1172+ self.address_bar.activeFocus.wait_for(True)
1173+ self.assertThat(self.address_bar.text, Eventually(Equals("")))
1174+ self.address_bar.write("abc")
1175+ self.main_window.press_key('Ctrl+t')
1176+ self.address_bar.activeFocus.wait_for(True)
1177+ self.assertThat(self.address_bar.text, Eventually(Equals("")))
1178
1179=== modified file 'tests/autopilot/webbrowser_app/tests/test_private.py'
1180--- tests/autopilot/webbrowser_app/tests/test_private.py 2016-01-21 10:29:17 +0000
1181+++ tests/autopilot/webbrowser_app/tests/test_private.py 2016-04-08 16:47:47 +0000
1182@@ -16,6 +16,7 @@
1183
1184 from testtools.matchers import Equals, NotEquals
1185 from autopilot.matchers import Eventually
1186+from autopilot.platform import model
1187
1188 from webbrowser_app.tests import StartOpenRemotePageTestCaseBase
1189
1190@@ -30,7 +31,7 @@
1191 self.assert_number_incognito_webviews_eventually(1)
1192 self.assertTrue(self.main_window.is_new_private_tab_view_visible())
1193 self.assertThat(address_bar.activeFocus,
1194- Eventually(Equals(self.main_window.wide)))
1195+ Eventually(Equals(model() == 'Desktop')))
1196 self.assertThat(address_bar.text, Eventually(Equals("")))
1197
1198 self.main_window.leave_private_mode()

Subscribers

People subscribed via source and target branches

to status/vote changes: