1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Designer of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "tabordereditor.h"
30
31#include <metadatabase_p.h>
32#include <qdesigner_command_p.h>
33#include <qdesigner_utils_p.h>
34#include <qlayout_widget_p.h>
35#include <orderdialog_p.h>
36
37#include <QtDesigner/qextensionmanager.h>
38#include <QtDesigner/abstractformwindow.h>
39#include <QtDesigner/abstractformwindowcursor.h>
40#include <QtDesigner/abstractformeditor.h>
41#include <QtDesigner/abstractwidgetfactory.h>
42#include <QtDesigner/propertysheet.h>
43
44#include <QtGui/qpainter.h>
45#include <QtGui/qevent.h>
46#include <QtWidgets/qmenu.h>
47#include <QtWidgets/qapplication.h>
48
49Q_DECLARE_METATYPE(QWidgetList)
50
51QT_BEGIN_NAMESPACE
52
53namespace {
54 enum { VBOX_MARGIN = 1, HBOX_MARGIN = 4, BG_ALPHA = 32 };
55}
56
57static QRect fixRect(const QRect &r)
58{
59 return QRect(r.x(), r.y(), r.width() - 1, r.height() - 1);
60}
61
62namespace qdesigner_internal {
63
64TabOrderEditor::TabOrderEditor(QDesignerFormWindowInterface *form, QWidget *parent) :
65 QWidget(parent),
66 m_form_window(form),
67 m_bg_widget(nullptr),
68 m_undo_stack(form->commandHistory()),
69 m_font_metrics(font()),
70 m_current_index(0),
71 m_beginning(true)
72{
73 connect(sender: form, signal: &QDesignerFormWindowInterface::widgetRemoved, receiver: this, slot: &TabOrderEditor::widgetRemoved);
74
75 QFont tabFont = font();
76 tabFont.setPointSize(tabFont.pointSize()*2);
77 tabFont.setBold(true);
78 setFont(tabFont);
79 m_font_metrics = QFontMetrics(tabFont);
80 setAttribute(Qt::WA_MouseTracking, on: true);
81}
82
83QDesignerFormWindowInterface *TabOrderEditor::formWindow() const
84{
85 return m_form_window;
86}
87
88void TabOrderEditor::setBackground(QWidget *background)
89{
90 if (background == m_bg_widget) {
91 return;
92 }
93
94 m_bg_widget = background;
95 updateBackground();
96}
97
98void TabOrderEditor::updateBackground()
99{
100 if (m_bg_widget == nullptr) {
101 // nothing to do
102 return;
103 }
104
105 initTabOrder();
106 update();
107}
108
109void TabOrderEditor::widgetRemoved(QWidget*)
110{
111 initTabOrder();
112}
113
114void TabOrderEditor::showEvent(QShowEvent *e)
115{
116 QWidget::showEvent(event: e);
117 updateBackground();
118}
119
120QRect TabOrderEditor::indicatorRect(int index) const
121{
122 if (index < 0 || index >= m_tab_order_list.size())
123 return QRect();
124
125 const QWidget *w = m_tab_order_list.at(i: index);
126 const QString text = QString::number(index + 1);
127
128 const QPoint tl = mapFromGlobal(w->mapToGlobal(w->rect().topLeft()));
129 const QSize size = m_font_metrics.size(flags: Qt::TextSingleLine, str: text);
130 QRect r(tl - QPoint(size.width(), size.height())/2, size);
131 r = QRect(r.left() - HBOX_MARGIN, r.top() - VBOX_MARGIN,
132 r.width() + HBOX_MARGIN*2, r.height() + VBOX_MARGIN*2);
133
134 return r;
135}
136
137static bool isWidgetVisible(QWidget *widget)
138{
139 while (widget && widget->parentWidget()) {
140 if (!widget->isVisibleTo(widget->parentWidget()))
141 return false;
142
143 widget = widget->parentWidget();
144 }
145
146 return true;
147}
148
149void TabOrderEditor::paintEvent(QPaintEvent *e)
150{
151 QPainter p(this);
152 p.setClipRegion(e->region());
153
154 int cur = m_current_index - 1;
155 if (!m_beginning && cur < 0)
156 cur = m_tab_order_list.size() - 1;
157
158 for (int i = 0; i < m_tab_order_list.size(); ++i) {
159 QWidget *widget = m_tab_order_list.at(i);
160 if (!isWidgetVisible(widget))
161 continue;
162
163 const QRect r = indicatorRect(index: i);
164
165 QColor c = Qt::darkGreen;
166 if (i == cur)
167 c = Qt::red;
168 else if (i > cur)
169 c = Qt::blue;
170 p.setPen(c);
171 c.setAlpha(BG_ALPHA);
172 p.setBrush(c);
173 p.drawRect(r: fixRect(r));
174
175 p.setPen(Qt::white);
176 p.drawText(r, text: QString::number(i + 1), o: QTextOption(Qt::AlignCenter));
177 }
178}
179
180bool TabOrderEditor::skipWidget(QWidget *w) const
181{
182 if (qobject_cast<QLayoutWidget*>(object: w)
183 || w == formWindow()->mainContainer()
184 || w->isHidden())
185 return true;
186
187 if (!formWindow()->isManaged(widget: w)) {
188 return true;
189 }
190
191 QExtensionManager *ext = formWindow()->core()->extensionManager();
192 if (const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(manager: ext, object: w)) {
193 const int index = sheet->indexOf(QStringLiteral("focusPolicy"));
194 if (index != -1) {
195 bool ok = false;
196 Qt::FocusPolicy q = (Qt::FocusPolicy) Utils::valueOf(value: sheet->property(index), ok: &ok);
197 return !ok || !(q & Qt::TabFocus);
198 }
199 }
200
201 return true;
202}
203
204void TabOrderEditor::initTabOrder()
205{
206 m_tab_order_list.clear();
207
208 QDesignerFormEditorInterface *core = formWindow()->core();
209
210 if (const QDesignerMetaDataBaseItemInterface *item = core->metaDataBase()->item(object: formWindow())) {
211 m_tab_order_list = item->tabOrder();
212 }
213
214 // Remove any widgets that have been removed form the form
215 for (int i = 0; i < m_tab_order_list.size(); ) {
216 QWidget *w = m_tab_order_list.at(i);
217 if (!formWindow()->mainContainer()->isAncestorOf(child: w) || skipWidget(w))
218 m_tab_order_list.removeAt(i);
219 else
220 ++i;
221 }
222
223 // Append any widgets that are in the form but are not in the tab order
224 QWidgetList childQueue;
225 childQueue.append(t: formWindow()->mainContainer());
226 while (!childQueue.isEmpty()) {
227 QWidget *child = childQueue.takeFirst();
228 childQueue += qvariant_cast<QWidgetList>(v: child->property(name: "_q_widgetOrder"));
229
230 if (skipWidget(w: child))
231 continue;
232
233 if (!m_tab_order_list.contains(t: child))
234 m_tab_order_list.append(t: child);
235 }
236
237 // Just in case we missed some widgets
238 QDesignerFormWindowCursorInterface *cursor = formWindow()->cursor();
239 for (int i = 0; i < cursor->widgetCount(); ++i) {
240
241 QWidget *widget = cursor->widget(index: i);
242 if (skipWidget(w: widget))
243 continue;
244
245 if (!m_tab_order_list.contains(t: widget))
246 m_tab_order_list.append(t: widget);
247 }
248
249 m_indicator_region = QRegion();
250 for (int i = 0; i < m_tab_order_list.size(); ++i) {
251 if (m_tab_order_list.at(i)->isVisible())
252 m_indicator_region |= indicatorRect(index: i);
253 }
254
255 if (m_current_index >= m_tab_order_list.size())
256 m_current_index = m_tab_order_list.size() - 1;
257 if (m_current_index < 0)
258 m_current_index = 0;
259}
260
261void TabOrderEditor::mouseMoveEvent(QMouseEvent *e)
262{
263 e->accept();
264#if QT_CONFIG(cursor)
265 if (m_indicator_region.contains(p: e->pos()))
266 setCursor(Qt::PointingHandCursor);
267 else
268 setCursor(QCursor());
269#endif
270}
271
272int TabOrderEditor::widgetIndexAt(const QPoint &pos) const
273{
274 int target_index = -1;
275 for (int i = 0; i < m_tab_order_list.size(); ++i) {
276 if (!m_tab_order_list.at(i)->isVisible())
277 continue;
278 if (indicatorRect(index: i).contains(p: pos)) {
279 target_index = i;
280 break;
281 }
282 }
283
284 return target_index;
285}
286
287void TabOrderEditor::mousePressEvent(QMouseEvent *e)
288{
289 e->accept();
290
291 if (!m_indicator_region.contains(p: e->pos())) {
292 if (QWidget *child = m_bg_widget->childAt(p: e->pos())) {
293 QDesignerFormEditorInterface *core = m_form_window->core();
294 if (core->widgetFactory()->isPassiveInteractor(widget: child)) {
295
296 QMouseEvent event(QEvent::MouseButtonPress,
297 child->mapFromGlobal(e->globalPos()),
298 e->button(), e->buttons(), e->modifiers());
299
300 qApp->sendEvent(receiver: child, event: &event);
301
302 QMouseEvent event2(QEvent::MouseButtonRelease,
303 child->mapFromGlobal(e->globalPos()),
304 e->button(), e->buttons(), e->modifiers());
305
306 qApp->sendEvent(receiver: child, event: &event2);
307
308 updateBackground();
309 }
310 }
311 return;
312 }
313
314 if (e->button() != Qt::LeftButton)
315 return;
316
317 const int target_index = widgetIndexAt(pos: e->pos());
318 if (target_index == -1)
319 return;
320
321 m_beginning = false;
322
323 if (e->modifiers() & Qt::ControlModifier) {
324 m_current_index = target_index + 1;
325 if (m_current_index >= m_tab_order_list.size())
326 m_current_index = 0;
327 update();
328 return;
329 }
330
331 if (m_current_index == -1)
332 return;
333
334 m_tab_order_list.swapItemsAt(i: target_index, j: m_current_index);
335
336 ++m_current_index;
337 if (m_current_index == m_tab_order_list.size())
338 m_current_index = 0;
339
340 TabOrderCommand *cmd = new TabOrderCommand(formWindow());
341 cmd->init(newTabOrder: m_tab_order_list);
342 formWindow()->commandHistory()->push(cmd);
343}
344
345void TabOrderEditor::contextMenuEvent(QContextMenuEvent *e)
346{
347 QMenu menu(this);
348 const int target_index = widgetIndexAt(pos: e->pos());
349 QAction *setIndex = menu.addAction(text: tr(s: "Start from Here"));
350 setIndex->setEnabled(target_index >= 0);
351
352 QAction *resetIndex = menu.addAction(text: tr(s: "Restart"));
353 menu.addSeparator();
354 QAction *showDialog = menu.addAction(text: tr(s: "Tab Order List..."));
355 showDialog->setEnabled(m_tab_order_list.size() > 1);
356
357 QAction *result = menu.exec(pos: e->globalPos());
358 if (result == resetIndex) {
359 m_current_index = 0;
360 m_beginning = true;
361 update();
362 } else if (result == setIndex) {
363 m_beginning = false;
364 m_current_index = target_index + 1;
365 if (m_current_index >= m_tab_order_list.size())
366 m_current_index = 0;
367 update();
368 } else if (result == showDialog) {
369 showTabOrderDialog();
370 }
371}
372
373void TabOrderEditor::mouseDoubleClickEvent(QMouseEvent *e)
374{
375 if (e->button() != Qt::LeftButton)
376 return;
377
378 const int target_index = widgetIndexAt(pos: e->pos());
379 if (target_index >= 0)
380 return;
381
382 m_beginning = true;
383 m_current_index = 0;
384 update();
385}
386
387void TabOrderEditor::resizeEvent(QResizeEvent *e)
388{
389 updateBackground();
390 QWidget::resizeEvent(event: e);
391}
392
393void TabOrderEditor::showTabOrderDialog()
394{
395 if (m_tab_order_list.size() < 2)
396 return;
397 OrderDialog dlg(this);
398 dlg.setWindowTitle(tr(s: "Tab Order List"));
399 dlg.setDescription(tr(s: "Tab Order"));
400 dlg.setFormat(OrderDialog::TabOrderFormat);
401 dlg.setPageList(m_tab_order_list);
402
403 if (dlg.exec() == QDialog::Rejected)
404 return;
405
406 const QWidgetList newOrder = dlg.pageList();
407 if (newOrder == m_tab_order_list)
408 return;
409
410 m_tab_order_list = newOrder;
411 TabOrderCommand *cmd = new TabOrderCommand(formWindow());
412 cmd->init(newTabOrder: m_tab_order_list);
413 formWindow()->commandHistory()->push(cmd);
414 update();
415}
416
417}
418
419QT_END_NAMESPACE
420

source code of qttools/src/designer/src/components/tabordereditor/tabordereditor.cpp