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 "widgetbox.h" |
30 | #include "widgetboxtreewidget.h" |
31 | #include "widgetbox_dnditem.h" |
32 | |
33 | #include <QtDesigner/abstractformeditor.h> |
34 | #include <QtDesigner/abstractformwindowmanager.h> |
35 | |
36 | #include <iconloader_p.h> |
37 | #include <qdesigner_utils_p.h> |
38 | |
39 | #include <QtGui/qevent.h> |
40 | #include <QtWidgets/qboxlayout.h> |
41 | #include <QtWidgets/qapplication.h> |
42 | #include <QtWidgets/qtoolbar.h> |
43 | #include <QtWidgets/qlineedit.h> |
44 | #include <QtGui/qicon.h> |
45 | |
46 | QT_BEGIN_NAMESPACE |
47 | |
48 | namespace qdesigner_internal { |
49 | |
50 | /* WidgetBoxFilterLineEdit: This widget should never have initial focus |
51 | * (ie, be the first widget of a dialog, else, the hint cannot be displayed. |
52 | * As it is the only focusable control in the widget box, it clears the focus |
53 | * policy and focusses explicitly on click only (note that setting Qt::ClickFocus |
54 | * is not sufficient for that as an ActivationFocus will occur). */ |
55 | |
56 | class WidgetBoxFilterLineEdit : public QLineEdit { |
57 | public: |
58 | explicit WidgetBoxFilterLineEdit(QWidget *parent = nullptr) : QLineEdit(parent), m_defaultFocusPolicy(focusPolicy()) |
59 | { setFocusPolicy(Qt::NoFocus); } |
60 | |
61 | protected: |
62 | void mousePressEvent(QMouseEvent *event) override; |
63 | void focusInEvent(QFocusEvent *e) override; |
64 | |
65 | private: |
66 | const Qt::FocusPolicy m_defaultFocusPolicy; |
67 | }; |
68 | |
69 | void WidgetBoxFilterLineEdit::mousePressEvent(QMouseEvent *e) |
70 | { |
71 | if (!hasFocus()) // Explicitly focus on click. |
72 | setFocus(Qt::OtherFocusReason); |
73 | QLineEdit::mousePressEvent(e); |
74 | } |
75 | |
76 | void WidgetBoxFilterLineEdit::focusInEvent(QFocusEvent *e) |
77 | { |
78 | // Refuse the focus if the mouse it outside. In addition to the mouse |
79 | // press logic, this prevents a re-focussing which occurs once |
80 | // we actually had focus |
81 | const Qt::FocusReason reason = e->reason(); |
82 | if (reason == Qt::ActiveWindowFocusReason || reason == Qt::PopupFocusReason) { |
83 | const QPoint mousePos = mapFromGlobal(QCursor::pos()); |
84 | const bool refuse = !geometry().contains(p: mousePos); |
85 | if (refuse) { |
86 | e->ignore(); |
87 | return; |
88 | } |
89 | } |
90 | QLineEdit::focusInEvent(e); |
91 | } |
92 | |
93 | WidgetBox::WidgetBox(QDesignerFormEditorInterface *core, QWidget *parent, Qt::WindowFlags flags) |
94 | : QDesignerWidgetBox(parent, flags), |
95 | m_core(core), |
96 | m_view(new WidgetBoxTreeWidget(m_core)) |
97 | { |
98 | |
99 | QVBoxLayout *l = new QVBoxLayout(this); |
100 | l->setContentsMargins(QMargins()); |
101 | l->setSpacing(0); |
102 | |
103 | // Prevent the filter from grabbing focus since Our view has Qt::NoFocus |
104 | QToolBar *toolBar = new QToolBar(this); |
105 | QLineEdit *filterWidget = new WidgetBoxFilterLineEdit(toolBar); |
106 | filterWidget->setPlaceholderText(tr(s: "Filter" )); |
107 | filterWidget->setClearButtonEnabled(true); |
108 | connect(sender: filterWidget, signal: &QLineEdit::textChanged, receiver: m_view, slot: &WidgetBoxTreeWidget::filter); |
109 | toolBar->addWidget(widget: filterWidget); |
110 | l->addWidget(toolBar); |
111 | |
112 | // View |
113 | connect(sender: m_view, signal: &WidgetBoxTreeWidget::pressed, |
114 | receiver: this, slot: &WidgetBox::handleMousePress); |
115 | l->addWidget(m_view); |
116 | |
117 | setAcceptDrops (true); |
118 | } |
119 | |
120 | WidgetBox::~WidgetBox() = default; |
121 | |
122 | QDesignerFormEditorInterface *WidgetBox::core() const |
123 | { |
124 | return m_core; |
125 | } |
126 | |
127 | void WidgetBox::handleMousePress(const QString &name, const QString &xml, const QPoint &global_mouse_pos) |
128 | { |
129 | if (QApplication::mouseButtons() != Qt::LeftButton) |
130 | return; |
131 | |
132 | DomUI *ui = xmlToUi(name, xml, insertFakeTopLevel: true); |
133 | if (ui == nullptr) |
134 | return; |
135 | QList<QDesignerDnDItemInterface*> item_list; |
136 | item_list.append(t: new WidgetBoxDnDItem(core(), ui, global_mouse_pos)); |
137 | m_core->formWindowManager()->dragItems(item_list); |
138 | } |
139 | |
140 | int WidgetBox::categoryCount() const |
141 | { |
142 | return m_view->categoryCount(); |
143 | } |
144 | |
145 | QDesignerWidgetBoxInterface::Category WidgetBox::category(int cat_idx) const |
146 | { |
147 | return m_view->category(cat_idx); |
148 | } |
149 | |
150 | void WidgetBox::addCategory(const Category &cat) |
151 | { |
152 | m_view->addCategory(cat); |
153 | } |
154 | |
155 | void WidgetBox::removeCategory(int cat_idx) |
156 | { |
157 | m_view->removeCategory(cat_idx); |
158 | } |
159 | |
160 | int WidgetBox::widgetCount(int cat_idx) const |
161 | { |
162 | return m_view->widgetCount(cat_idx); |
163 | } |
164 | |
165 | QDesignerWidgetBoxInterface::Widget WidgetBox::widget(int cat_idx, int wgt_idx) const |
166 | { |
167 | return m_view->widget(cat_idx, wgt_idx); |
168 | } |
169 | |
170 | void WidgetBox::addWidget(int cat_idx, const Widget &wgt) |
171 | { |
172 | m_view->addWidget(cat_idx, wgt); |
173 | } |
174 | |
175 | void WidgetBox::removeWidget(int cat_idx, int wgt_idx) |
176 | { |
177 | m_view->removeWidget(cat_idx, wgt_idx); |
178 | } |
179 | |
180 | void WidgetBox::dropWidgets(const QList<QDesignerDnDItemInterface*> &item_list, const QPoint&) |
181 | { |
182 | m_view->dropWidgets(item_list); |
183 | } |
184 | |
185 | void WidgetBox::setFileName(const QString &file_name) |
186 | { |
187 | m_view->setFileName(file_name); |
188 | } |
189 | |
190 | QString WidgetBox::fileName() const |
191 | { |
192 | return m_view->fileName(); |
193 | } |
194 | |
195 | bool WidgetBox::load() |
196 | { |
197 | return m_view->load(loadMode: loadMode()); |
198 | } |
199 | |
200 | bool WidgetBox::loadContents(const QString &contents) |
201 | { |
202 | return m_view->loadContents(contents); |
203 | } |
204 | |
205 | bool WidgetBox::save() |
206 | { |
207 | return m_view->save(); |
208 | } |
209 | |
210 | static const QDesignerMimeData *checkDragEvent(QDropEvent * event, |
211 | bool acceptEventsFromWidgetBox) |
212 | { |
213 | const QDesignerMimeData *mimeData = qobject_cast<const QDesignerMimeData *>(object: event->mimeData()); |
214 | if (!mimeData) { |
215 | event->ignore(); |
216 | return nullptr; |
217 | } |
218 | // If desired, ignore a widget box drag and drop, where widget==0. |
219 | if (!acceptEventsFromWidgetBox) { |
220 | const bool fromWidgetBox = !mimeData->items().first()->widget(); |
221 | if (fromWidgetBox) { |
222 | event->ignore(); |
223 | return nullptr; |
224 | } |
225 | } |
226 | |
227 | mimeData->acceptEvent(e: event); |
228 | return mimeData; |
229 | } |
230 | |
231 | void WidgetBox::dragEnterEvent (QDragEnterEvent * event) |
232 | { |
233 | // We accept event originating from the widget box also here, |
234 | // because otherwise Windows will not show the DnD pixmap. |
235 | checkDragEvent(event, acceptEventsFromWidgetBox: true); |
236 | } |
237 | |
238 | void WidgetBox::dragMoveEvent(QDragMoveEvent * event) |
239 | { |
240 | checkDragEvent(event, acceptEventsFromWidgetBox: true); |
241 | } |
242 | |
243 | void WidgetBox::dropEvent(QDropEvent * event) |
244 | { |
245 | const QDesignerMimeData *mimeData = checkDragEvent(event, acceptEventsFromWidgetBox: false); |
246 | if (!mimeData) |
247 | return; |
248 | |
249 | dropWidgets(item_list: mimeData->items(), event->pos()); |
250 | QDesignerMimeData::removeMovedWidgetsFromSourceForm(items: mimeData->items()); |
251 | } |
252 | |
253 | QIcon WidgetBox::iconForWidget(const QString &className, const QString &category) const |
254 | { |
255 | Widget widgetData; |
256 | if (!findWidget(wbox: this, className, category, widgetData: &widgetData)) |
257 | return QIcon(); |
258 | return m_view->iconForWidget(iconName: widgetData.iconName()); |
259 | } |
260 | |
261 | } // namespace qdesigner_internal |
262 | |
263 | QT_END_NAMESPACE |
264 | |