Merge lp:~nick-dedekind/ubuntu-ui-toolkit/actionItem-mnemonics into lp:ubuntu-ui-toolkit/staging

Proposed by Nick Dedekind
Status: Needs review
Proposed branch: lp:~nick-dedekind/ubuntu-ui-toolkit/actionItem-mnemonics
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 740 lines (+319/-97)
8 files modified
components.api (+8/-1)
src/UbuntuToolkit/ubuntutoolkitmodule.cpp (+2/-0)
src/UbuntuToolkit/ucaction.cpp (+1/-77)
src/UbuntuToolkit/ucaction_p.h (+2/-1)
src/UbuntuToolkit/ucactionitem.cpp (+249/-17)
src/UbuntuToolkit/ucactionitem_p.h (+44/-0)
src/UbuntuToolkit/ucactionitem_p_p.h (+4/-0)
tests/unit/visual/tst_actionitem.11.qml (+9/-1)
To merge this branch: bzr merge lp:~nick-dedekind/ubuntu-ui-toolkit/actionItem-mnemonics
Reviewer Review Type Date Requested Status
Zsombor Egri Needs Information
Review via email: mp+313115@code.launchpad.net

Commit message

Moved Action mnemonic logic to the ActionItem.

Description of the change

Moved Action mnemonic logic to the ActionItem.
Added ActionItem.mnemonic for altering properties of the mnemonic logic.

To post a comment you must log in.
1329. By Nick Dedekind

removed action check

1330. By Nick Dedekind

removed extra coment block end

1331. By Nick Dedekind

Added mnemonic type

Revision history for this message
Zsombor Egri (zsombi) wrote :

Mostly clarification I need. Also few more tests to cover the group properties please.

review: Needs Information

Unmerged revisions

1331. By Nick Dedekind

Added mnemonic type

1330. By Nick Dedekind

removed extra coment block end

1329. By Nick Dedekind

removed action check

1328. By Nick Dedekind

merged with staging

1327. By Nick Dedekind

merged with trunk

1326. By Nick Dedekind

added qdoc check

1325. By Nick Dedekind

fixed 1.3 version of ActionItem

1324. By Nick Dedekind

merged with staging

1323. By Nick Dedekind

API fixes

1322. By Nick Dedekind

Added mnemonic test

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'components.api'
--- components.api 2016-09-17 05:48:25 +0000
+++ components.api 2016-12-13 14:24:00 +0000
@@ -45,13 +45,14 @@
45 property bool active45 property bool active
46 function addAction(Action action)46 function addAction(Action action)
47 function removeAction(Action action)47 function removeAction(Action action)
48Ubuntu.Components.ActionItem 1.0 0.1 UCActionItem: StyledItem48Ubuntu.Components.ActionItem 1.3 1.0 0.1 UCActionItem: StyledItem
49 property Action action49 property Action action
50 property string iconName50 property string iconName
51 property url iconSource51 property url iconSource
52 signal triggered(var value)52 signal triggered(var value)
53 function trigger(var value)53 function trigger(var value)
54 function trigger()54 function trigger()
55 readonly property ActionMnemonic mnemonic 1.3
55 property string text56 property string text
56Ubuntu.Components.Styles.ActionItemProperties 1.3: QtObject57Ubuntu.Components.Styles.ActionItemProperties 1.3: QtObject
57 property color backgroundColor58 property color backgroundColor
@@ -79,6 +80,10 @@
79 function removeAction(Action action)80 function removeAction(Action action)
80 function addLocalContext(ActionContext context)81 function addLocalContext(ActionContext context)
81 function removeLocalContext(ActionContext context)82 function removeLocalContext(ActionContext context)
83Ubuntu.Components.ActionMnemonic 1.3: QtObject
84 property int modifier
85 readonly property StandardKey sequence
86 property bool visible
82Ubuntu.Components.Popups.ActionSelectionPopover 1.0 0.1: Popover87Ubuntu.Components.Popups.ActionSelectionPopover 1.0 0.1: Popover
83 property var actions88 property var actions
84 property Component delegate89 property Component delegate
@@ -1597,6 +1602,7 @@
1597 signal triggered(var value)1602 signal triggered(var value)
1598 function trigger(var value)1603 function trigger(var value)
1599 function trigger()1604 function trigger()
1605 readonly property ActionMnemonic mnemonic
1600 property string text1606 property string text
1601Ubuntu.Components.ToolbarButton 1.3: StyledItem1607Ubuntu.Components.ToolbarButton 1.3: StyledItem
1602 property Action action1608 property Action action
@@ -1605,6 +1611,7 @@
1605 signal triggered(var value)1611 signal triggered(var value)
1606 function trigger(var value)1612 function trigger(var value)
1607 function trigger()1613 function trigger()
1614 readonly property ActionMnemonic mnemonic
1608 property string text1615 property string text
1609Ubuntu.Components.ToolbarItems 1.0 0.1: Item1616Ubuntu.Components.ToolbarItems 1.0 0.1: Item
1610 property Item back1617 property Item back
16111618
=== modified file 'src/UbuntuToolkit/ubuntutoolkitmodule.cpp'
--- src/UbuntuToolkit/ubuntutoolkitmodule.cpp 2016-09-29 10:19:06 +0000
+++ src/UbuntuToolkit/ubuntutoolkitmodule.cpp 2016-12-13 14:24:00 +0000
@@ -398,6 +398,8 @@
398 qmlRegisterType<UCMainViewBase>(uri, 1, 3, "MainViewBase");398 qmlRegisterType<UCMainViewBase>(uri, 1, 3, "MainViewBase");
399 qmlRegisterType<ActionList>(uri, 1, 3, "ActionList");399 qmlRegisterType<ActionList>(uri, 1, 3, "ActionList");
400 qmlRegisterType<ExclusiveGroup>(uri, 1, 3, "ExclusiveGroup");400 qmlRegisterType<ExclusiveGroup>(uri, 1, 3, "ExclusiveGroup");
401 qmlRegisterType<UCActionItem, 1>(uri, 1, 3, "ActionItem");
402 qmlRegisterType<UCActionMnemonic>();
401}403}
402404
403void UbuntuToolkitModule::undefineModule()405void UbuntuToolkitModule::undefineModule()
404406
=== modified file 'src/UbuntuToolkit/ucaction.cpp'
--- src/UbuntuToolkit/ucaction.cpp 2016-09-20 08:23:40 +0000
+++ src/UbuntuToolkit/ucaction.cpp 2016-12-13 14:24:00 +0000
@@ -138,20 +138,6 @@
138 *138 *
139 * Examples: See \l Page139 * Examples: See \l Page
140 *140 *
141 * \section2 Mnemonics
142 * Since Ubuntu.Components 1.3 Action supports mnemonics. Mnemonics are shortcuts
143 * defined in the \l text property, prefixed the shortcut letter with \&. For instance
144 * \c "\&Call" will bint the \c "Alt-C" shortcut to the action. When a mnemonic
145 * is detected on the Action and a keyboard is attached to the device, the \l text
146 * property will provide a formatted text having the mnemonic letter underscored.
147 * \qml
148 * Action {
149 * id: call
150 * iconName: "call"
151 * text: "&Call"
152 * }
153 * \endqml
154 *
155 * \section2 Checkable property141 * \section2 Checkable property
156 * Since Ubuntu.Components 1.3 Action supports the checkable/checked properties.142 * Since Ubuntu.Components 1.3 Action supports the checkable/checked properties.
157 * \qml143 * \qml
@@ -191,40 +177,10 @@
191/*!177/*!
192 * \qmlproperty string Action::text178 * \qmlproperty string Action::text
193 * The user visible primary label of the action.179 * The user visible primary label of the action.
194 *
195 * Mnemonics are shortcuts prefixed in the text with \&. If the text has multiple
196 * occurences of the \& character, the first one will be considered for the shortcut.
197 * However \&\& can be used for a single \& in the text, not as a mnemonic.
198 * The \& character cannot be used as shortcut.
199 */180 */
200QString UCAction::text()181QString UCAction::text()
201{182{
202 QString displayText(m_text);183 return m_text;
203 // if we have a mnemonic, underscore it
204 if (!m_mnemonic.isEmpty()) {
205
206 QString mnemonic = "&" + m_mnemonic.toString().remove(QStringLiteral("Alt+"));
207 // patch special cases
208 mnemonic.replace(QStringLiteral("Space"), QStringLiteral(" "));
209 int mnemonicIndex = m_text.indexOf(mnemonic);
210 if (mnemonicIndex < 0) {
211 // try lower case
212 mnemonic = mnemonic.toLower();
213 mnemonicIndex = m_text.indexOf(mnemonic);
214 }
215 ACT_TRACE("MNEM" << mnemonic);
216 // FIXME: we need QInputDeviceInfo to detect the keyboard attechment
217 // https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1276808
218 if (QuickUtils::instance()->keyboardAttached()) {
219 // underscore the character
220 displayText.replace(mnemonicIndex, mnemonic.length(), "<u>" + mnemonic[1] + "</u>");
221 } else {
222 displayText.remove(mnemonicIndex, 1);
223 }
224 }
225 // Escape ampersands
226 displayText.replace(QStringLiteral("&&"), QStringLiteral("&amp;"));
227 return displayText;
228}184}
229void UCAction::setText(const QString &text)185void UCAction::setText(const QString &text)
230{186{
@@ -232,7 +188,6 @@
232 return;188 return;
233 }189 }
234 m_text = text;190 m_text = text;
235 setMnemonicFromText(m_text);
236 Q_EMIT textChanged();191 Q_EMIT textChanged();
237}192}
238void UCAction::resetText()193void UCAction::resetText()
@@ -240,25 +195,6 @@
240 setText(QString());195 setText(QString());
241}196}
242197
243void UCAction::setMnemonicFromText(const QString &text)
244{
245 QKeySequence sequence = QKeySequence::mnemonic(text);
246 if (sequence == m_mnemonic) {
247 return;
248 }
249 if (!m_mnemonic.isEmpty()) {
250 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(0, this, m_mnemonic);
251 }
252
253 m_mnemonic = sequence;
254
255 if (!m_mnemonic.isEmpty()) {
256 ACT_TRACE("MNEMONIC SET" << m_mnemonic.toString());
257 Qt::ShortcutContext context = Qt::WindowShortcut;
258 QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(this, m_mnemonic, context, shortcutContextMatcher);
259 }
260}
261
262/*!198/*!
263 * \qmlproperty string Action::keywords199 * \qmlproperty string Action::keywords
264 * Additional user visible keywords for the action.200 * Additional user visible keywords for the action.
@@ -287,10 +223,6 @@
287 , m_checked(false)223 , m_checked(false)
288{224{
289 generateName();225 generateName();
290 // FIXME: we need QInputDeviceInfo to detect the keyboard attechment
291 // https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1276808
292 connect(QuickUtils::instance(), &QuickUtils::keyboardAttachedChanged,
293 this, &UCAction::onKeyboardAttached);
294}226}
295227
296UCAction::~UCAction()228UCAction::~UCAction()
@@ -609,14 +541,6 @@
609 return true;541 return true;
610}542}
611543
612// trigger text changes whenever HW keyboad is attached/detached
613void UCAction::onKeyboardAttached()
614{
615 if (!m_mnemonic.isEmpty()) {
616 Q_EMIT textChanged();
617 }
618}
619
620/*!544/*!
621 * \qmlmethod Action::trigger(var value)545 * \qmlmethod Action::trigger(var value)
622 * Checks the \c value against the action \l parameterType and triggers the action.546 * Checks the \c value against the action \l parameterType and triggers the action.
623547
=== modified file 'src/UbuntuToolkit/ucaction_p.h'
--- src/UbuntuToolkit/ucaction_p.h 2016-09-09 17:49:07 +0000
+++ src/UbuntuToolkit/ucaction_p.h 2016-12-13 14:24:00 +0000
@@ -32,6 +32,8 @@
3232
33UT_NAMESPACE_BEGIN33UT_NAMESPACE_BEGIN
3434
35Q_DECLARE_LOGGING_CATEGORY(ucAction)
36
35// the function detects whether QML has an overridden trigger() slot available37// the function detects whether QML has an overridden trigger() slot available
36// and invokes the one with the appropriate signature38// and invokes the one with the appropriate signature
37template<class T>39template<class T>
@@ -185,7 +187,6 @@
185 void generateName();187 void generateName();
186 void setMnemonicFromText(const QString &text);188 void setMnemonicFromText(const QString &text);
187 bool event(QEvent *event) override;189 bool event(QEvent *event) override;
188 void onKeyboardAttached();
189};190};
190191
191UT_NAMESPACE_END192UT_NAMESPACE_END
192193
=== modified file 'src/UbuntuToolkit/ucactionitem.cpp'
--- src/UbuntuToolkit/ucactionitem.cpp 2016-09-12 09:03:50 +0000
+++ src/UbuntuToolkit/ucactionitem.cpp 2016-12-13 14:24:00 +0000
@@ -15,16 +15,65 @@
15 */15 */
1616
17#include "ucactionitem_p_p.h"17#include "ucactionitem_p_p.h"
18
19#define foreach Q_FOREACH
20#include <QtQml/private/qqmlbinding_p.h>
21#undef foreach
22
23#include "ucaction_p.h"18#include "ucaction_p.h"
19#include "ucactioncontext_p.h"
24#include "ucstyleditembase_p_p.h"20#include "ucstyleditembase_p_p.h"
21#include "quickutils_p.h"
22
23#include <QtQuick/qquickwindow.h>
24#include <private/qguiapplication_p.h>
2525
26UT_NAMESPACE_BEGIN26UT_NAMESPACE_BEGIN
2727
28#define ACT_TRACE(params) qCDebug(ucAction) << params
29
30bool itemShortcutContextMatcher(QObject* object, Qt::ShortcutContext context)
31{
32 UCActionItem* actionItem = static_cast<UCActionItem*>(object);
33 UCAction* action = actionItem->action();
34 if (!action || !action->isEnabled()) {
35 return false;
36 }
37
38 switch (context) {
39 case Qt::ApplicationShortcut:
40 return true;
41 case Qt::WindowShortcut: {
42 QObject* window = actionItem->window();
43 bool activatable = window && window == QGuiApplication::focusWindow();
44
45 if (activatable) {
46 QQuickItem *pl = actionItem;
47 activatable = false;
48 while (pl) {
49 UCActionContextAttached *attached = static_cast<UCActionContextAttached*>(
50 qmlAttachedPropertiesObject<UCActionContext>(pl, false));
51 if (attached) {
52 activatable = attached->context()->active();
53 if (!activatable) {
54 ACT_TRACE(action << "Inactive context found" << attached->context());
55 break;
56 }
57 }
58 pl = pl->parentItem();
59 }
60 if (!activatable) {
61 // check if the action is in an active context
62 UCActionContext *context = qobject_cast<UCActionContext*>(action->parent());
63 activatable = context && context->active();
64 }
65 }
66 if (activatable) {
67 ACT_TRACE("SELECTED ACTION" << action);
68 }
69
70 return activatable;
71 }
72 default: break;
73 }
74 return false;
75}
76
28UCActionItemPrivate::UCActionItemPrivate()77UCActionItemPrivate::UCActionItemPrivate()
29 : action(Q_NULLPTR)78 : action(Q_NULLPTR)
30 , flags(0)79 , flags(0)
@@ -36,6 +85,9 @@
36 Q_Q(UCActionItem);85 Q_Q(UCActionItem);
37 QObject::connect(q, &UCActionItem::enabledChanged, q, &UCActionItem::enabledChanged2);86 QObject::connect(q, &UCActionItem::enabledChanged, q, &UCActionItem::enabledChanged2);
38 QObject::connect(q, &UCActionItem::visibleChanged, q, &UCActionItem::visibleChanged2);87 QObject::connect(q, &UCActionItem::visibleChanged, q, &UCActionItem::visibleChanged2);
88
89 QObject::connect(&mnemonic, SIGNAL(visibleChanged()), q, SLOT(_q_textBinding()));
90 QObject::connect(&mnemonic, SIGNAL(modifierChanged()), q, SLOT(_q_updateMnemonic()));
39}91}
4092
41/*!93/*!
@@ -50,6 +102,20 @@
50 * If \l action is set, the values of the other properties will by default102 * If \l action is set, the values of the other properties will by default
51 * be identical to the \l Action's property values. Setting the other properties103 * be identical to the \l Action's property values. Setting the other properties
52 * will override the properties copied from the \l Action.104 * will override the properties copied from the \l Action.
105 *
106 * \section2 Mnemonics
107 * Since Ubuntu.Components 1.3 ActionItem supports mnemonics. Mnemonics are shortcuts
108 * defined in the \l text property, prefixed the shortcut letter with \&. For instance
109 * \c "\&Call" will bint the \c "Alt-C" shortcut to the action. When a mnemonic
110 * is detected on the ActionItem and a keyboard is attached to the device, the \l text
111 * property will provide a formatted text having the mnemonic letter underscored.
112 * \qml
113 * ActionItem {
114 * id: call
115 * iconName: "call"
116 * text: "&Call"
117 * }
118 * \endqml
53 */119 */
54120
55/*!121/*!
@@ -60,6 +126,11 @@
60 : UCStyledItemBase(*(new UCActionItemPrivate), parent)126 : UCStyledItemBase(*(new UCActionItemPrivate), parent)
61{127{
62 d_func()->init();128 d_func()->init();
129
130 // FIXME: we need QInputDeviceInfo to detect the keyboard attechment
131 // https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1276808
132 connect(QuickUtils::instance(), SIGNAL(keyboardAttachedChanged()),
133 this, SLOT(_q_onKeyboardAttached()), Qt::DirectConnection);
63}134}
64135
65UCActionItem::UCActionItem(UCActionItemPrivate &dd, QQuickItem *parent)136UCActionItem::UCActionItem(UCActionItemPrivate &dd, QQuickItem *parent)
@@ -68,6 +139,29 @@
68 d_func()->init();139 d_func()->init();
69}140}
70141
142bool UCActionItem::event(QEvent *e)
143{
144 Q_D(UCActionItem);
145 if (e->type() == QEvent::Shortcut) {
146 if (!d->action) {
147 return false;
148 }
149
150 // when we reach this point, we can be sure the Action is used
151 // by a component belonging to an active ActionContext.
152 QShortcutEvent *shortcut_event(static_cast<QShortcutEvent*>(e));
153 if (shortcut_event->isAmbiguous()) {
154 qmlInfo(this) << "Ambiguous shortcut: " << shortcut_event->key().toString();
155 return false;
156 }
157
158 // do not call trigger() directly but invoke, as it may get overridden in QML
159 invokeTrigger<UCAction>(d->action, QVariant());
160 return true;
161 }
162 return UCStyledItemBase::event(e);
163}
164
71bool UCActionItemPrivate::hasBindingOnProperty(const QString &name)165bool UCActionItemPrivate::hasBindingOnProperty(const QString &name)
72{166{
73 Q_Q(UCActionItem);167 Q_Q(UCActionItem);
@@ -109,6 +203,51 @@
109 invokeTrigger<UCAction>(action, value);203 invokeTrigger<UCAction>(action, value);
110}204}
111205
206// update the text property
207void UCActionItemPrivate::_q_textBinding()
208{
209 if (flags & CustomText) {
210 return;
211 }
212 _q_updateMnemonic();
213 Q_EMIT q_func()->textChanged();
214}
215
216// trigger text changes whenever HW keyboad is attached/detached
217void UCActionItemPrivate::_q_onKeyboardAttached()
218{
219 if (!mnemonic.sequence().isEmpty()) {
220 Q_EMIT q_func()->textChanged();
221 }
222}
223
224void UCActionItemPrivate::_q_updateMnemonic()
225{
226 Q_Q(UCActionItem);
227
228 const QString displayText = action ? action->text() : QString();
229
230 QKeySequence sequence = QKeySequence::mnemonic(displayText);
231 if (!sequence.isEmpty()) {
232 sequence = sequence[0] & ~Qt::ALT; // QKeySequence::mnemonic adds alt.
233 sequence = sequence[0] | mnemonic.modifier();
234 }
235
236 if (sequence == mnemonic.sequence()) {
237 return;
238 }
239 if (!mnemonic.sequence().isEmpty()) {
240 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(0, q, mnemonic.sequence());
241 }
242
243 mnemonic.setSequence(sequence);
244
245 if (!sequence.isEmpty()) {
246 Qt::ShortcutContext context = Qt::WindowShortcut;
247 QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(q, sequence, context, itemShortcutContextMatcher);
248 }
249}
250
112// setter called when bindings from QML set the value. Internal functions will251// setter called when bindings from QML set the value. Internal functions will
113// all use the setVisible setter, so initialization and (re)parenting related252// all use the setVisible setter, so initialization and (re)parenting related
114// visible alteration won't set the custom flag253// visible alteration won't set the custom flag
@@ -124,12 +263,15 @@
124 setEnabled(enabled);263 setEnabled(enabled);
125}264}
126265
266UCActionMnemonic *UCActionItem::mnemonic()
267{
268 Q_D(UCActionItem);
269 return &d->mnemonic;
270}
271
127void UCActionItemPrivate::updateProperties()272void UCActionItemPrivate::updateProperties()
128{273{
129 Q_Q(UCActionItem);274 Q_Q(UCActionItem);
130 if (!(flags & CustomText)) {
131 Q_EMIT q->textChanged();
132 }
133 if (!(flags & CustomIconSource)) {275 if (!(flags & CustomIconSource)) {
134 Q_EMIT q->iconSourceChanged();276 Q_EMIT q->iconSourceChanged();
135 }277 }
@@ -154,8 +296,8 @@
154 q, SLOT(_q_enabledBinding()), Qt::DirectConnection);296 q, SLOT(_q_enabledBinding()), Qt::DirectConnection);
155 }297 }
156 if (!(flags & CustomText)) {298 if (!(flags & CustomText)) {
157 QObject::connect(action, &UCAction::textChanged,299 QObject::connect(action, SIGNAL(textChanged()),
158 q, &UCActionItem::textChanged, Qt::DirectConnection);300 q, SLOT(_q_textBinding()), Qt::DirectConnection);
159 }301 }
160 if (!(flags & CustomIconSource)) {302 if (!(flags & CustomIconSource)) {
161 QObject::connect(action, &UCAction::iconSourceChanged,303 QObject::connect(action, &UCAction::iconSourceChanged,
@@ -178,8 +320,8 @@
178 q, SLOT(_q_enabledBinding()));320 q, SLOT(_q_enabledBinding()));
179 }321 }
180 if (!(flags & CustomText)) {322 if (!(flags & CustomText)) {
181 QObject::disconnect(action, &UCAction::textChanged,323 QObject::disconnect(action, SIGNAL(textChanged()),
182 q, &UCActionItem::textChanged);324 q, SLOT(_q_textBinding()));
183 }325 }
184 if (!(flags & CustomIconSource)) {326 if (!(flags & CustomIconSource)) {
185 QObject::disconnect(action, &UCAction::iconSourceChanged,327 QObject::disconnect(action, &UCAction::iconSourceChanged,
@@ -189,6 +331,11 @@
189 QObject::disconnect(action, &UCAction::iconNameChanged,331 QObject::disconnect(action, &UCAction::iconNameChanged,
190 q, &UCActionItem::iconNameChanged);332 q, &UCActionItem::iconNameChanged);
191 }333 }
334
335 if (!mnemonic.sequence().isEmpty()) {
336 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(0, q, mnemonic.sequence());
337 mnemonic.setSequence(QKeySequence());
338 }
192 }339 }
193}340}
194341
@@ -220,12 +367,18 @@
220 }367 }
221 d->_q_visibleBinding();368 d->_q_visibleBinding();
222 d->_q_enabledBinding();369 d->_q_enabledBinding();
370 d->_q_textBinding();
223 d->updateProperties();371 d->updateProperties();
224}372}
225373
226/*!374/*!
227 * \qmlproperty string ActionItem::text375 * \qmlproperty string ActionItem::text
228 * The title of the actionItem. Defaults to the \c action.text.376 * The title of the actionItem. Defaults to the \c action.text.
377 *
378 * Mnemonics are shortcuts prefixed in the text with \&. If the text has multiple
379 * occurences of the \& character, the first one will be considered for the shortcut.
380 * However \&\& can be used for a single \& in the text, not as a mnemonic.
381 * The \& character cannot be used as shortcut.
229 */382 */
230QString UCActionItem::text()383QString UCActionItem::text()
231{384{
@@ -233,7 +386,40 @@
233 if (d->flags & UCActionItemPrivate::CustomText) {386 if (d->flags & UCActionItemPrivate::CustomText) {
234 return d->text;387 return d->text;
235 }388 }
236 return d->action ? d->action->text() : QString();389
390 if (!d->action) {
391 return QString();
392 }
393
394 QString displayText(d->action->text());
395
396 // if we have a mnemonic, underscore it
397 if (!d->mnemonic.sequence().isEmpty()) {
398 const QString modifier = QKeySequence(d->mnemonic.modifier()).toString();
399
400 QString mnemonic = "&" + d->mnemonic.sequence().toString().remove(modifier);
401 // patch special cases
402 mnemonic.replace(QStringLiteral("Space"), QStringLiteral(" "));
403 int mnemonicIndex = displayText.indexOf(mnemonic);
404 if (mnemonicIndex < 0) {
405 // try lower case
406 mnemonic = mnemonic.toLower();
407 mnemonicIndex = displayText.indexOf(mnemonic);
408 }
409
410 // FIXME: we need QInputDeviceInfo to detect the keyboard attechment
411 // https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1276808
412 if (d->mnemonic.visible() && QuickUtils::instance()->keyboardAttached()) {
413 // underscore the character
414 displayText.replace(mnemonicIndex, mnemonic.length(), "<u>" + mnemonic[1] + "</u>");
415 } else {
416 displayText.remove(mnemonicIndex, 1);
417 }
418 }
419
420 // Escape ampersands
421 displayText.replace(QStringLiteral("&&"), QStringLiteral("&amp;"));
422 return displayText;
237}423}
238void UCActionItem::setText(const QString &text)424void UCActionItem::setText(const QString &text)
239{425{
@@ -241,8 +427,8 @@
241427
242 if (d->action && !(d->flags & UCActionItemPrivate::CustomText)) {428 if (d->action && !(d->flags & UCActionItemPrivate::CustomText)) {
243 // disconnect change signal from Action429 // disconnect change signal from Action
244 disconnect(d->action, &UCAction::textChanged,430 disconnect(d->action, SIGNAL(textChanged()),
245 this, &UCActionItem::textChanged);431 this, SLOT(_q_textBinding()));
246 }432 }
247 d->flags |= UCActionItemPrivate::CustomText;433 d->flags |= UCActionItemPrivate::CustomText;
248434
@@ -259,8 +445,8 @@
259 d->flags &= ~UCActionItemPrivate::CustomText;445 d->flags &= ~UCActionItemPrivate::CustomText;
260 if (d->action) {446 if (d->action) {
261 // re-connect change signal from Action447 // re-connect change signal from Action
262 connect(d->action, &UCAction::textChanged,448 connect(d->action, SIGNAL(textChanged()),
263 this, &UCActionItem::textChanged, Qt::DirectConnection);449 this, SLOT(_q_textBinding()), Qt::DirectConnection);
264 }450 }
265 Q_EMIT textChanged();451 Q_EMIT textChanged();
266}452}
@@ -382,6 +568,52 @@
382 }568 }
383}569}
384570
571UCActionMnemonic::UCActionMnemonic(QObject *parent)
572 : QObject(parent)
573 , m_visible(true)
574 , m_modifier(Qt::ALT)
575{
576}
577
578bool UCActionMnemonic::visible() const
579{
580 return m_visible;
581}
582
583void UCActionMnemonic::setVisible(bool visible)
584{
585 if (visible != m_visible) {
586 m_visible = visible;
587 Q_EMIT visibleChanged();
588 }
589}
590
591int UCActionMnemonic::modifier() const
592{
593 return m_modifier;
594}
595
596void UCActionMnemonic::setModifier(int modifier)
597{
598 if (modifier != m_modifier) {
599 m_modifier = modifier;
600 Q_EMIT modifierChanged();
601 }
602}
603
604const QKeySequence& UCActionMnemonic::sequence() const
605{
606 return m_sequence;
607}
608
609void UCActionMnemonic::setSequence(const QKeySequence &sequence)
610{
611 if (m_sequence != sequence) {
612 m_sequence = sequence;
613 Q_EMIT sequenceChanged();
614 }
615}
616
385UT_NAMESPACE_END617UT_NAMESPACE_END
386618
387#include "moc_ucactionitem_p.cpp"619#include "moc_ucactionitem_p.cpp"
388620
=== modified file 'src/UbuntuToolkit/ucactionitem_p.h'
--- src/UbuntuToolkit/ucactionitem_p.h 2016-09-09 17:49:07 +0000
+++ src/UbuntuToolkit/ucactionitem_p.h 2016-12-13 14:24:00 +0000
@@ -23,6 +23,7 @@
2323
24class UCAction;24class UCAction;
25class UCActionItemPrivate;25class UCActionItemPrivate;
26class UCActionMnemonic;
26class UBUNTUTOOLKIT_EXPORT UCActionItem : public UCStyledItemBase27class UBUNTUTOOLKIT_EXPORT UCActionItem : public UCStyledItemBase
27{28{
28 Q_OBJECT29 Q_OBJECT
@@ -35,6 +36,13 @@
35 Q_PROPERTY(QUrl iconSource READ iconSource WRITE setIconSource RESET resetIconSource NOTIFY iconSourceChanged)36 Q_PROPERTY(QUrl iconSource READ iconSource WRITE setIconSource RESET resetIconSource NOTIFY iconSourceChanged)
36 Q_PROPERTY(QString iconName READ iconName WRITE setIconName RESET resetIconName NOTIFY iconNameChanged)37 Q_PROPERTY(QString iconName READ iconName WRITE setIconName RESET resetIconName NOTIFY iconNameChanged)
3738
39 // 1.3
40#ifndef Q_QDOC
41 Q_PROPERTY(UT_PREPEND_NAMESPACE(UCActionMnemonic) *mnemonic READ mnemonic CONSTANT FINAL REVISION 1)
42#else
43 Q_PROPERTY(UCActionMnemonic *mnemonic READ mnemonic CONSTANT FINAL REVISION 1)
44#endif
45
38 // overrides46 // overrides
39 Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled2 NOTIFY enabledChanged2)47 Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled2 NOTIFY enabledChanged2)
40 Q_PROPERTY(bool visible READ isVisible WRITE setVisible2 NOTIFY visibleChanged2 FINAL)48 Q_PROPERTY(bool visible READ isVisible WRITE setVisible2 NOTIFY visibleChanged2 FINAL)
@@ -56,6 +64,8 @@
56 void setVisible2(bool visible);64 void setVisible2(bool visible);
57 void setEnabled2(bool enabled);65 void setEnabled2(bool enabled);
5866
67 UCActionMnemonic* mnemonic();
68
59Q_SIGNALS:69Q_SIGNALS:
60 void actionChanged();70 void actionChanged();
61 void textChanged();71 void textChanged();
@@ -72,10 +82,44 @@
72protected:82protected:
73 UCActionItem(UCActionItemPrivate &, QQuickItem *parent);83 UCActionItem(UCActionItemPrivate &, QQuickItem *parent);
7484
85 bool event(QEvent *event) override;
86
75 Q_DECLARE_PRIVATE(UCActionItem)87 Q_DECLARE_PRIVATE(UCActionItem)
76 Q_PRIVATE_SLOT(d_func(), void _q_visibleBinding())88 Q_PRIVATE_SLOT(d_func(), void _q_visibleBinding())
77 Q_PRIVATE_SLOT(d_func(), void _q_enabledBinding())89 Q_PRIVATE_SLOT(d_func(), void _q_enabledBinding())
78 Q_PRIVATE_SLOT(d_func(), void _q_invokeActionTrigger(const QVariant &value))90 Q_PRIVATE_SLOT(d_func(), void _q_invokeActionTrigger(const QVariant &value))
91 Q_PRIVATE_SLOT(d_func(), void _q_textBinding())
92 Q_PRIVATE_SLOT(d_func(), void _q_onKeyboardAttached())
93 Q_PRIVATE_SLOT(d_func(), void _q_updateMnemonic())
94};
95
96class UBUNTUTOOLKIT_EXPORT UCActionMnemonic : public QObject
97{
98 Q_OBJECT
99 Q_PROPERTY(bool visible READ visible WRITE setVisible NOTIFY visibleChanged)
100 Q_PROPERTY(int modifier READ modifier WRITE setModifier NOTIFY modifierChanged)
101 Q_PROPERTY(QKeySequence sequence READ sequence NOTIFY sequenceChanged)
102public:
103 UCActionMnemonic(QObject* parent = 0);
104
105 bool visible() const;
106 void setVisible(bool visible);
107
108 int modifier() const;
109 void setModifier(int modifier);
110
111 const QKeySequence& sequence() const;
112 void setSequence(const QKeySequence& sequence);
113
114Q_SIGNALS:
115 void visibleChanged();
116 void modifierChanged();
117 void sequenceChanged();
118
119private:
120 bool m_visible;
121 int m_modifier;
122 QKeySequence m_sequence;
79};123};
80124
81UT_NAMESPACE_END125UT_NAMESPACE_END
82126
=== modified file 'src/UbuntuToolkit/ucactionitem_p_p.h'
--- src/UbuntuToolkit/ucactionitem_p_p.h 2016-09-09 17:49:07 +0000
+++ src/UbuntuToolkit/ucactionitem_p_p.h 2016-12-13 14:24:00 +0000
@@ -45,6 +45,9 @@
45 void _q_visibleBinding();45 void _q_visibleBinding();
46 void _q_enabledBinding();46 void _q_enabledBinding();
47 void _q_invokeActionTrigger(const QVariant &value);47 void _q_invokeActionTrigger(const QVariant &value);
48 void _q_textBinding();
49 void _q_onKeyboardAttached();
50 void _q_updateMnemonic();
4851
49 enum {52 enum {
50 CustomText = 0x01,53 CustomText = 0x01,
@@ -58,6 +61,7 @@
58 QUrl iconSource;61 QUrl iconSource;
59 UCAction *action;62 UCAction *action;
60 quint8 flags;63 quint8 flags;
64 UCActionMnemonic mnemonic;
61};65};
6266
63UT_NAMESPACE_END67UT_NAMESPACE_END
6468
=== modified file 'tests/unit/visual/tst_actionitem.11.qml'
--- tests/unit/visual/tst_actionitem.11.qml 2016-06-15 13:46:51 +0000
+++ tests/unit/visual/tst_actionitem.11.qml 2016-12-13 14:24:00 +0000
@@ -16,7 +16,7 @@
1616
17import QtQuick 2.017import QtQuick 2.0
18import QtTest 1.018import QtTest 1.0
19import Ubuntu.Components 1.119import Ubuntu.Components 1.3
2020
21Item {21Item {
22 id: main22 id: main
@@ -58,6 +58,7 @@
58 Action {58 Action {
59 id: action259 id: action2
60 objectName: "action2"60 objectName: "action2"
61 text: "&mnemonicActionText"
61 }62 }
6263
63 Loader {64 Loader {
@@ -86,6 +87,7 @@
86 function cleanup() {87 function cleanup() {
87 loader.sourceComponent = null;88 loader.sourceComponent = null;
88 item1.action = null;89 item1.action = null;
90 item1.text = undefined;
89 action1.visible = true;91 action1.visible = true;
90 action1.enabled = true;92 action1.enabled = true;
91 action2.visible = true;93 action2.visible = true;
@@ -116,6 +118,12 @@
116 compare(item1.text, "", "text can be unset")118 compare(item1.text, "", "text can be unset")
117 }119 }
118120
121 function test_action_mnemonic() {
122 QuickUtils.keyboardAttached = true;
123 item1.action = action2;
124 compare(item1.text, "<u>m</u>nemonicActionText", "Text uses action text mnemonics");
125 }
126
119 // NOTE: This test must be run AFTER test_action(), otherwise setting the action will127 // NOTE: This test must be run AFTER test_action(), otherwise setting the action will
120 // will not update the iconSource128 // will not update the iconSource
121 function test_iconSource() {129 function test_iconSource() {

Subscribers

People subscribed via source and target branches