1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include <QtVirtualKeyboard/qvirtualkeyboardselectionlistmodel.h> |
5 | #include <QtVirtualKeyboard/qvirtualkeyboardabstractinputmethod.h> |
6 | #include <QtVirtualKeyboard/private/settings_p.h> |
7 | #include <QtCore/private/qabstractitemmodel_p.h> |
8 | #include <QtCore/qpointer.h> |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | using namespace QtVirtualKeyboard; |
13 | |
14 | class QVirtualKeyboardSelectionListModelPrivate : public QAbstractItemModelPrivate |
15 | { |
16 | public: |
17 | QVirtualKeyboardSelectionListModelPrivate() : |
18 | QAbstractItemModelPrivate(), |
19 | dataSource(nullptr), |
20 | type(QVirtualKeyboardSelectionListModel::Type::WordCandidateList), |
21 | rowCount(0), |
22 | wclAutoCommitWord(false) |
23 | { |
24 | } |
25 | |
26 | QHash<int, QByteArray> roles; |
27 | QPointer<QVirtualKeyboardAbstractInputMethod> dataSource; |
28 | QVirtualKeyboardSelectionListModel::Type type; |
29 | int rowCount; |
30 | bool wclAutoCommitWord; |
31 | }; |
32 | |
33 | /*! |
34 | \qmltype SelectionListModel |
35 | \instantiates QVirtualKeyboardSelectionListModel |
36 | \inqmlmodule QtQuick.VirtualKeyboard |
37 | \ingroup qtvirtualkeyboard-internal-qml |
38 | \brief Provides a data model for the selection lists. |
39 | |
40 | The SelectionListModel is a data model for word candidates |
41 | provided by the input method. |
42 | |
43 | An instance of a SelectionListModel cannot be created directly. |
44 | Instead, the InputEngine manages the instances and provides |
45 | access to the model by InputEngine::wordCandidateListModel |
46 | property. |
47 | |
48 | The model exposes the following data roles for the list delegate: |
49 | \list |
50 | \li \c display Display text for item. |
51 | \li \c wordCompletionLength Word completion length for item. |
52 | \li \c dictionaryType Dictionary type of the word, see QVirtualKeyboardSelectionListModel::DictionaryType. |
53 | \li \c canRemoveSuggestion A boolean indicating if the word can be removed from the dictionary. |
54 | \endlist |
55 | |
56 | The activeItemChanged signal indicates which item is currently |
57 | highlighted by the input method. The view should respond to this |
58 | signal by highlighting the corresponding item in the list. |
59 | |
60 | The user selection is handled by the selectItem() method. The view |
61 | should be invoke this method when the user selects an item from the |
62 | list. |
63 | */ |
64 | |
65 | /*! |
66 | \class QVirtualKeyboardSelectionListModel |
67 | |
68 | \inmodule QtVirtualKeyboard |
69 | \ingroup qtvirtualkeyboard-cpp-for-devs |
70 | |
71 | \brief List model for selection lists. |
72 | |
73 | This class acts as a bridge between the UI and the |
74 | input method that provides the data for selection |
75 | lists. |
76 | */ |
77 | |
78 | /*! |
79 | \enum QVirtualKeyboardSelectionListModel::Type |
80 | |
81 | This enum specifies the type of selection list. |
82 | |
83 | \value WordCandidateList |
84 | Shows list of word candidates. |
85 | */ |
86 | |
87 | /*! |
88 | \enum QVirtualKeyboardSelectionListModel::Role |
89 | |
90 | This enum specifies a role of the data requested. |
91 | |
92 | \value Display |
93 | The data to be rendered in form of text. |
94 | \value DisplayRole |
95 | \c obsolete Use Role::Display. |
96 | \value WordCompletionLength |
97 | An integer specifying the length of the word |
98 | the completion part expressed as the |
99 | number of characters counted from the |
100 | end of the string. |
101 | \value WordCompletionLengthRole |
102 | \c obsolete Use Role::WordCompletionLength. |
103 | \value Dictionary |
104 | An integer specifying \ l {QVirtualKeyboardSelectionListModel::DictionaryType}{dictionary type}. |
105 | \value CanRemoveSuggestion |
106 | A boolean value indicating if the word candidate |
107 | can be removed from the dictionary. |
108 | */ |
109 | |
110 | /*! |
111 | \enum QVirtualKeyboardSelectionListModel::DictionaryType |
112 | |
113 | This enum specifies the dictionary type of a word. |
114 | |
115 | \value Default |
116 | The word candidate is from the default dictionary. |
117 | \value User |
118 | The word candidate is from the user dictionary. |
119 | */ |
120 | |
121 | QVirtualKeyboardSelectionListModel::QVirtualKeyboardSelectionListModel(QObject *parent) : |
122 | QAbstractListModel(*new QVirtualKeyboardSelectionListModelPrivate(), parent) |
123 | { |
124 | Q_D(QVirtualKeyboardSelectionListModel); |
125 | d->roles = |
126 | {{static_cast<int>(Role::Display), "display" }, |
127 | {static_cast<int>(Role::WordCompletionLength), "wordCompletionLength" }, |
128 | {static_cast<int>(Role::Dictionary), "dictionary" }, |
129 | {static_cast<int>(Role::CanRemoveSuggestion), "canRemoveSuggestion" }}; |
130 | } |
131 | |
132 | /*! |
133 | \internal |
134 | */ |
135 | QVirtualKeyboardSelectionListModel::~QVirtualKeyboardSelectionListModel() |
136 | { |
137 | } |
138 | |
139 | /*! |
140 | \internal |
141 | */ |
142 | void QVirtualKeyboardSelectionListModel::setDataSource(QVirtualKeyboardAbstractInputMethod *dataSource, Type type) |
143 | { |
144 | Q_D(QVirtualKeyboardSelectionListModel); |
145 | if (d->dataSource) { |
146 | disconnect(receiver: this, SLOT(selectionListChanged(Type))); |
147 | disconnect(receiver: this, SLOT(selectionListActiveItemChanged(Type, int))); |
148 | disconnect(receiver: this, SLOT(dataSourceDestroyed())); |
149 | } |
150 | d->type = type; |
151 | if (d->dataSource) { |
152 | d->dataSource = nullptr; |
153 | selectionListChanged(type); |
154 | selectionListActiveItemChanged(type, index: -1); |
155 | } |
156 | d->dataSource = dataSource; |
157 | if (d->dataSource) { |
158 | QObject::connect(sender: d->dataSource.data(), signal: &QVirtualKeyboardAbstractInputMethod::selectionListChanged, context: this, slot: &QVirtualKeyboardSelectionListModel::selectionListChanged); |
159 | QObject::connect(sender: d->dataSource.data(), signal: &QVirtualKeyboardAbstractInputMethod::selectionListActiveItemChanged, context: this, slot: &QVirtualKeyboardSelectionListModel::selectionListActiveItemChanged); |
160 | QObject::connect(sender: d->dataSource.data(), signal: &QObject::destroyed, context: this, slot: &QVirtualKeyboardSelectionListModel::dataSourceDestroyed); |
161 | } |
162 | } |
163 | |
164 | /*! |
165 | \internal |
166 | */ |
167 | QVirtualKeyboardAbstractInputMethod *QVirtualKeyboardSelectionListModel::dataSource() const |
168 | { |
169 | Q_D(const QVirtualKeyboardSelectionListModel); |
170 | return d->dataSource; |
171 | } |
172 | |
173 | /*! |
174 | \internal |
175 | */ |
176 | int QVirtualKeyboardSelectionListModel::rowCount(const QModelIndex &parent) const |
177 | { |
178 | Q_D(const QVirtualKeyboardSelectionListModel); |
179 | Q_UNUSED(parent); |
180 | return d->rowCount; |
181 | } |
182 | |
183 | /*! |
184 | \internal |
185 | */ |
186 | QVariant QVirtualKeyboardSelectionListModel::data(const QModelIndex &index, int role) const |
187 | { |
188 | Q_D(const QVirtualKeyboardSelectionListModel); |
189 | |
190 | if (!d->dataSource) |
191 | return QVariant(); |
192 | |
193 | if (index.row() < 0 || index.row() >= d->rowCount) |
194 | return QVariant(); |
195 | |
196 | return d->dataSource->selectionListData(type: d->type, index: index.row(), role: static_cast<Role>(role)); |
197 | } |
198 | |
199 | /*! |
200 | \internal |
201 | */ |
202 | QHash<int,QByteArray> QVirtualKeyboardSelectionListModel::roleNames() const |
203 | { |
204 | Q_D(const QVirtualKeyboardSelectionListModel); |
205 | return d->roles; |
206 | } |
207 | |
208 | /*! |
209 | \property QVirtualKeyboardSelectionListModel::count |
210 | \internal |
211 | */ |
212 | /* |
213 | \internal |
214 | */ |
215 | int QVirtualKeyboardSelectionListModel::count() const |
216 | { |
217 | Q_D(const QVirtualKeyboardSelectionListModel); |
218 | return d->rowCount; |
219 | } |
220 | |
221 | /*! \qmlmethod void SelectionListModel::selectItem(int index) |
222 | |
223 | This method should be called when the user selects an item at position |
224 | \a index from the list. |
225 | The selection is forwarded to the input method for further processing. |
226 | */ |
227 | /*! |
228 | This method should be called when the user selects an item at position |
229 | \a index from the list. |
230 | The selection is forwarded to the input method for further processing. |
231 | */ |
232 | void QVirtualKeyboardSelectionListModel::selectItem(int index) |
233 | { |
234 | Q_D(QVirtualKeyboardSelectionListModel); |
235 | if (index >= 0 && index < d->rowCount && d->dataSource) { |
236 | emit itemSelected(index); |
237 | d->dataSource->selectionListItemSelected(type: d->type, index); |
238 | } |
239 | } |
240 | |
241 | /*! |
242 | \qmlmethod void SelectionListModel::removeItem(int index) |
243 | |
244 | This method should be called when the user removes an item at position |
245 | \a index from the list. |
246 | The removal is forwarded to the input method for further processing. |
247 | */ |
248 | /*! |
249 | This method should be called when the user removes an item at position |
250 | \a index from the list. |
251 | The removal is forwarded to the input method for further processing. |
252 | */ |
253 | void QVirtualKeyboardSelectionListModel::removeItem(int index) |
254 | { |
255 | Q_D(QVirtualKeyboardSelectionListModel); |
256 | if (index >= 0 && index < d->rowCount && d->dataSource) { |
257 | d->dataSource->selectionListRemoveItem(type: d->type, index); |
258 | } |
259 | } |
260 | |
261 | /*! |
262 | * \internal |
263 | */ |
264 | QVariant QVirtualKeyboardSelectionListModel::dataAt(int index, QVirtualKeyboardSelectionListModel::Role role) const |
265 | { |
266 | return data(index: this->index(row: index, column: 0), role: static_cast<int>(role)); |
267 | } |
268 | |
269 | /*! |
270 | \internal |
271 | */ |
272 | void QVirtualKeyboardSelectionListModel::selectionListChanged(QVirtualKeyboardSelectionListModel::Type type) |
273 | { |
274 | Q_D(QVirtualKeyboardSelectionListModel); |
275 | if (static_cast<Type>(type) == d->type) { |
276 | int oldCount = d->rowCount; |
277 | int newCount = d->dataSource ? d->dataSource->selectionListItemCount(type: d->type) : 0; |
278 | if (newCount) { |
279 | int changedCount = qMin(a: oldCount, b: newCount); |
280 | if (changedCount) |
281 | emit dataChanged(topLeft: index(row: 0), bottomRight: index(row: changedCount - 1)); |
282 | if (oldCount > newCount) { |
283 | beginRemoveRows(parent: QModelIndex(), first: newCount, last: oldCount - 1); |
284 | d->rowCount = newCount; |
285 | endRemoveRows(); |
286 | } else if (oldCount < newCount) { |
287 | beginInsertRows(parent: QModelIndex(), first: oldCount, last: newCount - 1); |
288 | d->rowCount = newCount; |
289 | endInsertRows(); |
290 | } |
291 | } else { |
292 | beginResetModel(); |
293 | d->rowCount = 0; |
294 | endResetModel(); |
295 | } |
296 | if (static_cast<QVirtualKeyboardSelectionListModel::Type>(type) == QVirtualKeyboardSelectionListModel::Type::WordCandidateList) |
297 | d->wclAutoCommitWord = ((oldCount > 1 || (oldCount == 1 && d->wclAutoCommitWord)) && newCount == 1 && |
298 | Settings::instance()->wclAutoCommitWord() && |
299 | dataAt(index: 0).toString().size() > 1); |
300 | if (d->rowCount != oldCount) |
301 | emit countChanged(); |
302 | } |
303 | } |
304 | |
305 | /*! |
306 | \internal |
307 | */ |
308 | void QVirtualKeyboardSelectionListModel::selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type type, int index) |
309 | { |
310 | Q_D(QVirtualKeyboardSelectionListModel); |
311 | if (static_cast<Type>(type) == d->type && index < d->rowCount) { |
312 | emit activeItemChanged(index); |
313 | if (index == 0 && d->wclAutoCommitWord) |
314 | selectItem(index: 0); |
315 | } |
316 | } |
317 | |
318 | /*! |
319 | \internal |
320 | */ |
321 | void QVirtualKeyboardSelectionListModel::dataSourceDestroyed() |
322 | { |
323 | Q_D(QVirtualKeyboardSelectionListModel); |
324 | selectionListChanged(type: d->type); |
325 | selectionListActiveItemChanged(type: d->type, index: -1); |
326 | } |
327 | |
328 | /*! |
329 | \qmlsignal void SelectionListModel::activeItemChanged(int index) |
330 | |
331 | This signal is emitted when the active item in the list changes. The |
332 | UI should react to this signal by highlighting the item at \a index in |
333 | the list. |
334 | */ |
335 | /*! |
336 | \fn void QVirtualKeyboardSelectionListModel::activeItemChanged(int index) |
337 | |
338 | This signal is emitted when the active item in the list changes. The |
339 | UI should react to this signal by highlighting the item at \a index in |
340 | the list. |
341 | */ |
342 | |
343 | /*! |
344 | \qmlsignal void SelectionListModel::itemSelected(int index) |
345 | |
346 | This signal is emitted when an item at \a index is selected by the user. |
347 | */ |
348 | /*! |
349 | \fn void QVirtualKeyboardSelectionListModel::itemSelected(int index) |
350 | |
351 | This signal is emitted when an item at \a index is selected by the user. |
352 | */ |
353 | |
354 | QT_END_NAMESPACE |
355 | |