~ubuntu-sdk-team/ubuntu-ui-toolkit/escapeLittleAmpersand

« back to all changes in this revision

Viewing changes to src/Ubuntu/UbuntuToolkit/exclusivegroup.cpp

  • Committer: Zoltán Balogh
  • Date: 2016-08-23 10:39:12 UTC
  • mfrom: (1000.105.31 uitk.actionlist)
  • Revision ID: zoltan@bakter.hu-20160823103912-m4g13qqajjzir8cf
Introduced Action states & ExclusiveGroup action list / https://code.launchpad.net/~nick-dedekind/ubuntu-ui-toolkit/exclusiveGroup/+merge/297921

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2016 Canonical Ltd.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify
 
5
 * it under the terms of the GNU Lesser General Public License as published by
 
6
 * the Free Software Foundation; version 3.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful,
 
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 * GNU Lesser General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU Lesser General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 */
 
17
 
 
18
#include "exclusivegroup_p.h"
 
19
#include "ucaction_p.h"
 
20
 
 
21
#include <QSignalMapper>
 
22
 
 
23
#define CHECKED_PROPERTY "checked"
 
24
 
 
25
UT_NAMESPACE_BEGIN
 
26
 
 
27
static const char *checkableSignals[] = {
 
28
    "toggled(bool)",
 
29
    0
 
30
};
 
31
 
 
32
static bool isChecked(const QObject *o)
 
33
{
 
34
    if (!o) return false;
 
35
    QVariant checkedVariant = o->property(CHECKED_PROPERTY);
 
36
    return checkedVariant.isValid() && checkedVariant.toBool();
 
37
}
 
38
 
 
39
/*!
 
40
 * \qmltype ExclusiveGroup
 
41
 * \inqmlmodule Ubuntu.Components
 
42
 * \since Ubuntu.Components 1.3
 
43
 * \ingroup ubuntu
 
44
 * \inherits ActionList
 
45
 * \brief ExclusiveGroup provides a way to declare several checkable controls as mutually exclusive.
 
46
 *
 
47
 * The ExclusiveGroup will only allow a single object to have it's checkable property set to "true"
 
48
 * at any one time. The exclusive group accepts child Actions, but objects other than Actions can be
 
49
 * used by using the \l bindCheckable function as long as they support one of the required signals,
 
50
 * and a "checked" property.
 
51
 * \qml
 
52
 * ExclusiveGroup {
 
53
 *     Action {
 
54
 *         parameterType: Action.Bool
 
55
 *         state: true
 
56
 *     }
 
57
 *     Action {
 
58
 *         parameterType: Action.Bool
 
59
 *         state: false
 
60
 *     }
 
61
 * }
 
62
 * \endqml
 
63
 */
 
64
ExclusiveGroup::ExclusiveGroup(QObject *parent)
 
65
    : ActionList(parent)
 
66
    , m_signalMapper(new QSignalMapper(this))
 
67
    , m_entranceGuard(false)
 
68
{
 
69
    connect(this, &ActionList::added, this, &ExclusiveGroup::onActionAdded);
 
70
    connect(this, &ActionList::removed, this, &ExclusiveGroup::onActionRemoved);
 
71
 
 
72
    int index = m_signalMapper->metaObject()->indexOfMethod("map()");
 
73
    m_updateCurrentMethod = m_signalMapper->metaObject()->method(index);
 
74
    connect(m_signalMapper, static_cast<void(QSignalMapper::*)(QObject *)>(&QSignalMapper::mapped), this, [this](QObject *object) {
 
75
        if (isChecked(object)) {
 
76
            setCurrent(object);
 
77
        }
 
78
    });
 
79
}
 
80
 
 
81
void ExclusiveGroup::onActionAdded(UCAction *action)
 
82
{
 
83
    action->setExclusiveGroup(this);
 
84
}
 
85
 
 
86
void ExclusiveGroup::onActionRemoved(UCAction *action)
 
87
{
 
88
    action->setExclusiveGroup(nullptr);
 
89
}
 
90
 
 
91
/*!
 
92
 * \qmlproperty Action ExclusiveGroup::current
 
93
 * Returns the currently checked action
 
94
 */
 
95
void ExclusiveGroup::setCurrent(QObject *object)
 
96
{
 
97
    if (m_current == object)
 
98
        return;
 
99
 
 
100
    if (m_current)
 
101
        m_current->setProperty(CHECKED_PROPERTY, QVariant(false));
 
102
    m_current = object;
 
103
    if (m_current)
 
104
        m_current->setProperty(CHECKED_PROPERTY, QVariant(true));
 
105
    Q_EMIT currentChanged();
 
106
}
 
107
 
 
108
QObject *ExclusiveGroup::current() const
 
109
{
 
110
    return m_current.data();
 
111
}
 
112
 
 
113
/*!
 
114
 * \qmlmethod void ExclusiveGroup::bindCheckable(object object)
 
115
 * Explicitly bind an objects checkability to this exclusive group.
 
116
 * \note This only works with objects which support the following signals signals:
 
117
 * \list
 
118
 *  \li \b toggled(bool)
 
119
 * \endlist
 
120
 * \qml
 
121
 * Item {
 
122
 *     ExclusiveGroup {
 
123
 *         id: exclusiveGroup
 
124
 *     }
 
125
 *     Instantiator {
 
126
 *         model: 4
 
127
 *         onObjectAdded: exclusiveGroup.bindCheckable(object)
 
128
 *         onObjectRemoved: exclusiveGroup.unbindCheckable(object)
 
129
 *
 
130
 *         Action {
 
131
 *             checkable: true
 
132
 *         }
 
133
 *     }
 
134
 * }
 
135
 * \endqml
 
136
 * \sa ExclusiveGroup::unbindCheckable
 
137
 */
 
138
void ExclusiveGroup::bindCheckable(QObject *object)
 
139
{
 
140
    for (const char **signalName = checkableSignals; *signalName; signalName++) {
 
141
        int signalIndex = object->metaObject()->indexOfSignal(*signalName);
 
142
        if (signalIndex != -1) {
 
143
            QMetaMethod signalMethod = object->metaObject()->method(signalIndex);
 
144
            connect(object, signalMethod, m_signalMapper, m_updateCurrentMethod, Qt::UniqueConnection);
 
145
            m_signalMapper->setMapping(object, object);
 
146
            connect(object, SIGNAL(destroyed(QObject*)), this, SLOT(unbindCheckable(QObject*)), Qt::UniqueConnection);
 
147
            if (!m_current && isChecked(object))
 
148
                setCurrent(object);
 
149
            break;
 
150
        }
 
151
    }
 
152
}
 
153
 
 
154
/*!
 
155
 * \qmlmethod void ExclusiveGroup::unbindCheckable(object object)
 
156
 * Explicitly unbind an objects checkability from this exclusive group.
 
157
 * \sa ExclusiveGroup::bindCheckable
 
158
 */
 
159
void ExclusiveGroup::unbindCheckable(QObject *object)
 
160
{
 
161
    if (m_current == object)
 
162
        setCurrent(0);
 
163
 
 
164
    disconnect(object, 0, m_signalMapper, 0);
 
165
    disconnect(object, SIGNAL(destroyed(QObject*)), this, SLOT(unbindCheckable(QObject*)));
 
166
}
 
167
 
 
168
UT_NAMESPACE_END