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 demonstration applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
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 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include "colorswatch.h" |
52 | |
53 | #include <QAction> |
54 | #include <QtEvents> |
55 | #include <QFrame> |
56 | #include <QMainWindow> |
57 | #include <QMenu> |
58 | #include <QPainter> |
59 | #include <QImage> |
60 | #include <QColor> |
61 | #include <QDialog> |
62 | #include <QDialogButtonBox> |
63 | #include <QGridLayout> |
64 | #include <QSignalBlocker> |
65 | #include <QSpinBox> |
66 | #include <QLabel> |
67 | #include <QPainterPath> |
68 | #include <QPushButton> |
69 | #include <QHBoxLayout> |
70 | #include <QBitmap> |
71 | #include <QtDebug> |
72 | |
73 | #undef DEBUG_SIZEHINTS |
74 | |
75 | QColor bgColorForName(const QString &name) |
76 | { |
77 | if (name == "Black" ) |
78 | return QColor("#D8D8D8" ); |
79 | if (name == "White" ) |
80 | return QColor("#F1F1F1" ); |
81 | if (name == "Red" ) |
82 | return QColor("#F1D8D8" ); |
83 | if (name == "Green" ) |
84 | return QColor("#D8E4D8" ); |
85 | if (name == "Blue" ) |
86 | return QColor("#D8D8F1" ); |
87 | if (name == "Yellow" ) |
88 | return QColor("#F1F0D8" ); |
89 | return QColor(name).lighter(f: 110); |
90 | } |
91 | |
92 | QColor fgColorForName(const QString &name) |
93 | { |
94 | if (name == "Black" ) |
95 | return QColor("#6C6C6C" ); |
96 | if (name == "White" ) |
97 | return QColor("#F8F8F8" ); |
98 | if (name == "Red" ) |
99 | return QColor("#F86C6C" ); |
100 | if (name == "Green" ) |
101 | return QColor("#6CB26C" ); |
102 | if (name == "Blue" ) |
103 | return QColor("#6C6CF8" ); |
104 | if (name == "Yellow" ) |
105 | return QColor("#F8F76C" ); |
106 | return QColor(name); |
107 | } |
108 | |
109 | class ColorDock : public QFrame |
110 | { |
111 | Q_OBJECT |
112 | public: |
113 | explicit ColorDock(const QString &c, QWidget *parent); |
114 | |
115 | QSize sizeHint() const override { return szHint; } |
116 | QSize minimumSizeHint() const override { return minSzHint; } |
117 | |
118 | void setCustomSizeHint(const QSize &size); |
119 | |
120 | public slots: |
121 | void changeSizeHints(); |
122 | |
123 | protected: |
124 | void paintEvent(QPaintEvent *) override; |
125 | |
126 | private: |
127 | const QString color; |
128 | QSize szHint; |
129 | QSize minSzHint; |
130 | }; |
131 | |
132 | ColorDock::ColorDock(const QString &c, QWidget *parent) |
133 | : QFrame(parent) |
134 | , color(c) |
135 | , szHint(-1, -1) |
136 | , minSzHint(125, 75) |
137 | { |
138 | QFont font = this->font(); |
139 | font.setPointSize(8); |
140 | setFont(font); |
141 | } |
142 | |
143 | void ColorDock::paintEvent(QPaintEvent *) |
144 | { |
145 | QPainter p(this); |
146 | p.setRenderHint(hint: QPainter::Antialiasing); |
147 | p.fillRect(rect(), color: bgColorForName(name: color)); |
148 | |
149 | p.save(); |
150 | |
151 | extern void render_qt_text(QPainter *, int, int, const QColor &); |
152 | render_qt_text(&p, width(), height(), fgColorForName(name: color)); |
153 | |
154 | p.restore(); |
155 | |
156 | #ifdef DEBUG_SIZEHINTS |
157 | p.setRenderHint(QPainter::Antialiasing, false); |
158 | |
159 | QSize sz = size(); |
160 | QSize szHint = sizeHint(); |
161 | QSize minSzHint = minimumSizeHint(); |
162 | QSize maxSz = maximumSize(); |
163 | QString text = QString::fromLatin1("sz: %1x%2\nszHint: %3x%4\nminSzHint: %5x%6\n" |
164 | "maxSz: %8x%9" ) |
165 | .arg(sz.width()).arg(sz.height()) |
166 | .arg(szHint.width()).arg(szHint.height()) |
167 | .arg(minSzHint.width()).arg(minSzHint.height()) |
168 | .arg(maxSz.width()).arg(maxSz.height()); |
169 | |
170 | QRect r = fontMetrics().boundingRect(rect(), Qt::AlignLeft|Qt::AlignTop, text); |
171 | r.adjust(-2, -2, 1, 1); |
172 | p.translate(4, 4); |
173 | QColor bg = Qt::yellow; |
174 | bg.setAlpha(120); |
175 | p.setBrush(bg); |
176 | p.setPen(Qt::black); |
177 | p.drawRect(r); |
178 | p.drawText(rect(), Qt::AlignLeft|Qt::AlignTop, text); |
179 | #endif // DEBUG_SIZEHINTS |
180 | } |
181 | |
182 | static QSpinBox *createSpinBox(int value, QWidget *parent, int max = 1000) |
183 | { |
184 | QSpinBox *result = new QSpinBox(parent); |
185 | result->setMinimum(-1); |
186 | result->setMaximum(max); |
187 | result->setValue(value); |
188 | return result; |
189 | } |
190 | |
191 | void ColorDock::changeSizeHints() |
192 | { |
193 | QDialog dialog(this); |
194 | dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); |
195 | dialog.setWindowTitle(color); |
196 | |
197 | QVBoxLayout *topLayout = new QVBoxLayout(&dialog); |
198 | |
199 | QGridLayout *inputLayout = new QGridLayout(); |
200 | topLayout->addLayout(layout: inputLayout); |
201 | |
202 | inputLayout->addWidget(new QLabel(tr(s: "Size Hint:" ), &dialog), row: 0, column: 0); |
203 | inputLayout->addWidget(new QLabel(tr(s: "Min Size Hint:" ), &dialog), row: 1, column: 0); |
204 | inputLayout->addWidget(new QLabel(tr(s: "Max Size:" ), &dialog), row: 2, column: 0); |
205 | inputLayout->addWidget(new QLabel(tr(s: "Dock Widget Max Size:" ), &dialog), row: 3, column: 0); |
206 | |
207 | QSpinBox *szHintW = createSpinBox(value: szHint.width(), parent: &dialog); |
208 | inputLayout->addWidget(szHintW, row: 0, column: 1); |
209 | QSpinBox *szHintH = createSpinBox(value: szHint.height(), parent: &dialog); |
210 | inputLayout->addWidget(szHintH, row: 0, column: 2); |
211 | |
212 | QSpinBox *minSzHintW = createSpinBox(value: minSzHint.width(), parent: &dialog); |
213 | inputLayout->addWidget(minSzHintW, row: 1, column: 1); |
214 | QSpinBox *minSzHintH = createSpinBox(value: minSzHint.height(), parent: &dialog); |
215 | inputLayout->addWidget(minSzHintH, row: 1, column: 2); |
216 | |
217 | QSize maxSz = maximumSize(); |
218 | QSpinBox *maxSzW = createSpinBox(value: maxSz.width(), parent: &dialog, QWIDGETSIZE_MAX); |
219 | inputLayout->addWidget(maxSzW, row: 2, column: 1); |
220 | QSpinBox *maxSzH = createSpinBox(value: maxSz.height(), parent: &dialog, QWIDGETSIZE_MAX); |
221 | inputLayout->addWidget(maxSzH, row: 2, column: 2); |
222 | |
223 | QSize dwMaxSz = parentWidget()->maximumSize(); |
224 | QSpinBox *dwMaxSzW = createSpinBox(value: dwMaxSz.width(), parent: &dialog, QWIDGETSIZE_MAX); |
225 | inputLayout->addWidget(dwMaxSzW, row: 3, column: 1); |
226 | QSpinBox *dwMaxSzH = createSpinBox(value: dwMaxSz.height(), parent: &dialog, QWIDGETSIZE_MAX); |
227 | inputLayout->addWidget(dwMaxSzH, row: 3, column: 2); |
228 | |
229 | inputLayout->setColumnStretch(column: 1, stretch: 1); |
230 | inputLayout->setColumnStretch(column: 2, stretch: 1); |
231 | |
232 | topLayout->addStretch(); |
233 | |
234 | QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); |
235 | connect(sender: buttonBox, signal: &QDialogButtonBox::rejected, receiver: &dialog, slot: &QDialog::reject); |
236 | connect(sender: buttonBox, signal: &QDialogButtonBox::accepted, receiver: &dialog, slot: &QDialog::accept); |
237 | |
238 | topLayout->addWidget(buttonBox); |
239 | |
240 | if (dialog.exec() != QDialog::Accepted) |
241 | return; |
242 | |
243 | szHint = QSize(szHintW->value(), szHintH->value()); |
244 | minSzHint = QSize(minSzHintW->value(), minSzHintH->value()); |
245 | maxSz = QSize(maxSzW->value(), maxSzH->value()); |
246 | setMaximumSize(maxSz); |
247 | dwMaxSz = QSize(dwMaxSzW->value(), dwMaxSzH->value()); |
248 | parentWidget()->setMaximumSize(dwMaxSz); |
249 | updateGeometry(); |
250 | update(); |
251 | } |
252 | |
253 | void ColorDock::setCustomSizeHint(const QSize &size) |
254 | { |
255 | if (szHint != size) { |
256 | szHint = size; |
257 | updateGeometry(); |
258 | } |
259 | } |
260 | |
261 | ColorSwatch::ColorSwatch(const QString &colorName, QMainWindow *parent, Qt::WindowFlags flags) |
262 | : QDockWidget(parent, flags), mainWindow(parent) |
263 | { |
264 | setObjectName(colorName + QLatin1String(" Dock Widget" )); |
265 | setWindowTitle(objectName() + QLatin1String(" [*]" )); |
266 | |
267 | ColorDock *swatch = new ColorDock(colorName, this); |
268 | swatch->setFrameStyle(QFrame::Box | QFrame::Sunken); |
269 | |
270 | setWidget(swatch); |
271 | |
272 | closableAction = new QAction(tr(s: "Closable" ), this); |
273 | closableAction->setCheckable(true); |
274 | connect(sender: closableAction, signal: &QAction::triggered, receiver: this, slot: &ColorSwatch::changeClosable); |
275 | |
276 | movableAction = new QAction(tr(s: "Movable" ), this); |
277 | movableAction->setCheckable(true); |
278 | connect(sender: movableAction, signal: &QAction::triggered, receiver: this, slot: &ColorSwatch::changeMovable); |
279 | |
280 | floatableAction = new QAction(tr(s: "Floatable" ), this); |
281 | floatableAction->setCheckable(true); |
282 | connect(sender: floatableAction, signal: &QAction::triggered, receiver: this, slot: &ColorSwatch::changeFloatable); |
283 | |
284 | verticalTitleBarAction = new QAction(tr(s: "Vertical title bar" ), this); |
285 | verticalTitleBarAction->setCheckable(true); |
286 | connect(sender: verticalTitleBarAction, signal: &QAction::triggered, |
287 | receiver: this, slot: &ColorSwatch::changeVerticalTitleBar); |
288 | |
289 | floatingAction = new QAction(tr(s: "Floating" ), this); |
290 | floatingAction->setCheckable(true); |
291 | connect(sender: floatingAction, signal: &QAction::triggered, receiver: this, slot: &ColorSwatch::changeFloating); |
292 | |
293 | allowedAreasActions = new QActionGroup(this); |
294 | allowedAreasActions->setExclusive(false); |
295 | |
296 | allowLeftAction = new QAction(tr(s: "Allow on Left" ), this); |
297 | allowLeftAction->setCheckable(true); |
298 | connect(sender: allowLeftAction, signal: &QAction::triggered, receiver: this, slot: &ColorSwatch::allowLeft); |
299 | |
300 | allowRightAction = new QAction(tr(s: "Allow on Right" ), this); |
301 | allowRightAction->setCheckable(true); |
302 | connect(sender: allowRightAction, signal: &QAction::triggered, receiver: this, slot: &ColorSwatch::allowRight); |
303 | |
304 | allowTopAction = new QAction(tr(s: "Allow on Top" ), this); |
305 | allowTopAction->setCheckable(true); |
306 | connect(sender: allowTopAction, signal: &QAction::triggered, receiver: this, slot: &ColorSwatch::allowTop); |
307 | |
308 | allowBottomAction = new QAction(tr(s: "Allow on Bottom" ), this); |
309 | allowBottomAction->setCheckable(true); |
310 | connect(sender: allowBottomAction, signal: &QAction::triggered, receiver: this, slot: &ColorSwatch::allowBottom); |
311 | |
312 | allowedAreasActions->addAction(a: allowLeftAction); |
313 | allowedAreasActions->addAction(a: allowRightAction); |
314 | allowedAreasActions->addAction(a: allowTopAction); |
315 | allowedAreasActions->addAction(a: allowBottomAction); |
316 | |
317 | areaActions = new QActionGroup(this); |
318 | areaActions->setExclusive(true); |
319 | |
320 | leftAction = new QAction(tr(s: "Place on Left" ) , this); |
321 | leftAction->setCheckable(true); |
322 | connect(sender: leftAction, signal: &QAction::triggered, receiver: this, slot: &ColorSwatch::placeLeft); |
323 | |
324 | rightAction = new QAction(tr(s: "Place on Right" ) , this); |
325 | rightAction->setCheckable(true); |
326 | connect(sender: rightAction, signal: &QAction::triggered, receiver: this, slot: &ColorSwatch::placeRight); |
327 | |
328 | topAction = new QAction(tr(s: "Place on Top" ) , this); |
329 | topAction->setCheckable(true); |
330 | connect(sender: topAction, signal: &QAction::triggered, receiver: this, slot: &ColorSwatch::placeTop); |
331 | |
332 | bottomAction = new QAction(tr(s: "Place on Bottom" ) , this); |
333 | bottomAction->setCheckable(true); |
334 | connect(sender: bottomAction, signal: &QAction::triggered, receiver: this, slot: &ColorSwatch::placeBottom); |
335 | |
336 | areaActions->addAction(a: leftAction); |
337 | areaActions->addAction(a: rightAction); |
338 | areaActions->addAction(a: topAction); |
339 | areaActions->addAction(a: bottomAction); |
340 | |
341 | connect(sender: movableAction, signal: &QAction::triggered, receiver: areaActions, slot: &QActionGroup::setEnabled); |
342 | |
343 | connect(sender: movableAction, signal: &QAction::triggered, receiver: allowedAreasActions, slot: &QActionGroup::setEnabled); |
344 | |
345 | connect(sender: floatableAction, signal: &QAction::triggered, receiver: floatingAction, slot: &QAction::setEnabled); |
346 | |
347 | connect(sender: floatingAction, signal: &QAction::triggered, receiver: floatableAction, slot: &QAction::setDisabled); |
348 | connect(sender: movableAction, signal: &QAction::triggered, receiver: floatableAction, slot: &QAction::setEnabled); |
349 | |
350 | tabMenu = new QMenu(this); |
351 | tabMenu->setTitle(tr(s: "Tab into" )); |
352 | connect(sender: tabMenu, signal: &QMenu::triggered, receiver: this, slot: &ColorSwatch::tabInto); |
353 | |
354 | splitHMenu = new QMenu(this); |
355 | splitHMenu->setTitle(tr(s: "Split horizontally into" )); |
356 | connect(sender: splitHMenu, signal: &QMenu::triggered, receiver: this, slot: &ColorSwatch::splitInto); |
357 | |
358 | splitVMenu = new QMenu(this); |
359 | splitVMenu->setTitle(tr(s: "Split vertically into" )); |
360 | connect(sender: splitVMenu, signal: &QMenu::triggered, receiver: this, slot: &ColorSwatch::splitInto); |
361 | |
362 | QAction *windowModifiedAction = new QAction(tr(s: "Modified" ), this); |
363 | windowModifiedAction->setCheckable(true); |
364 | windowModifiedAction->setChecked(false); |
365 | connect(sender: windowModifiedAction, signal: &QAction::toggled, receiver: this, slot: &QWidget::setWindowModified); |
366 | |
367 | menu = new QMenu(colorName, this); |
368 | menu->addAction(action: toggleViewAction()); |
369 | menu->addAction(text: tr(s: "Raise" ), object: this, slot: &QWidget::raise); |
370 | menu->addAction(text: tr(s: "Change Size Hints..." ), object: swatch, slot: &ColorDock::changeSizeHints); |
371 | |
372 | menu->addSeparator(); |
373 | menu->addAction(action: closableAction); |
374 | menu->addAction(action: movableAction); |
375 | menu->addAction(action: floatableAction); |
376 | menu->addAction(action: floatingAction); |
377 | menu->addAction(action: verticalTitleBarAction); |
378 | menu->addSeparator(); |
379 | menu->addActions(actions: allowedAreasActions->actions()); |
380 | menu->addSeparator(); |
381 | menu->addActions(actions: areaActions->actions()); |
382 | menu->addSeparator(); |
383 | menu->addMenu(menu: splitHMenu); |
384 | menu->addMenu(menu: splitVMenu); |
385 | menu->addMenu(menu: tabMenu); |
386 | menu->addSeparator(); |
387 | menu->addAction(action: windowModifiedAction); |
388 | |
389 | connect(sender: menu, signal: &QMenu::aboutToShow, receiver: this, slot: &ColorSwatch::updateContextMenu); |
390 | |
391 | if (colorName == QLatin1String("Black" )) { |
392 | leftAction->setShortcut(Qt::CTRL | Qt::Key_W); |
393 | rightAction->setShortcut(Qt::CTRL | Qt::Key_E); |
394 | toggleViewAction()->setShortcut(Qt::CTRL | Qt::Key_R); |
395 | } |
396 | } |
397 | |
398 | void ColorSwatch::() |
399 | { |
400 | const Qt::DockWidgetArea area = mainWindow->dockWidgetArea(dockwidget: this); |
401 | const Qt::DockWidgetAreas areas = allowedAreas(); |
402 | |
403 | closableAction->setChecked(features() & QDockWidget::DockWidgetClosable); |
404 | if (windowType() == Qt::Drawer) { |
405 | floatableAction->setEnabled(false); |
406 | floatingAction->setEnabled(false); |
407 | movableAction->setEnabled(false); |
408 | verticalTitleBarAction->setChecked(false); |
409 | } else { |
410 | floatableAction->setChecked(features() & QDockWidget::DockWidgetFloatable); |
411 | floatingAction->setChecked(isWindow()); |
412 | // done after floating, to get 'floatable' correctly initialized |
413 | movableAction->setChecked(features() & QDockWidget::DockWidgetMovable); |
414 | verticalTitleBarAction |
415 | ->setChecked(features() & QDockWidget::DockWidgetVerticalTitleBar); |
416 | } |
417 | |
418 | allowLeftAction->setChecked(isAreaAllowed(area: Qt::LeftDockWidgetArea)); |
419 | allowRightAction->setChecked(isAreaAllowed(area: Qt::RightDockWidgetArea)); |
420 | allowTopAction->setChecked(isAreaAllowed(area: Qt::TopDockWidgetArea)); |
421 | allowBottomAction->setChecked(isAreaAllowed(area: Qt::BottomDockWidgetArea)); |
422 | |
423 | if (allowedAreasActions->isEnabled()) { |
424 | allowLeftAction->setEnabled(area != Qt::LeftDockWidgetArea); |
425 | allowRightAction->setEnabled(area != Qt::RightDockWidgetArea); |
426 | allowTopAction->setEnabled(area != Qt::TopDockWidgetArea); |
427 | allowBottomAction->setEnabled(area != Qt::BottomDockWidgetArea); |
428 | } |
429 | |
430 | { |
431 | const QSignalBlocker blocker(leftAction); |
432 | leftAction->setChecked(area == Qt::LeftDockWidgetArea); |
433 | } |
434 | { |
435 | const QSignalBlocker blocker(rightAction); |
436 | rightAction->setChecked(area == Qt::RightDockWidgetArea); |
437 | } |
438 | { |
439 | const QSignalBlocker blocker(topAction); |
440 | topAction->setChecked(area == Qt::TopDockWidgetArea); |
441 | } |
442 | { |
443 | const QSignalBlocker blocker(bottomAction); |
444 | bottomAction->setChecked(area == Qt::BottomDockWidgetArea); |
445 | } |
446 | |
447 | if (areaActions->isEnabled()) { |
448 | leftAction->setEnabled(areas & Qt::LeftDockWidgetArea); |
449 | rightAction->setEnabled(areas & Qt::RightDockWidgetArea); |
450 | topAction->setEnabled(areas & Qt::TopDockWidgetArea); |
451 | bottomAction->setEnabled(areas & Qt::BottomDockWidgetArea); |
452 | } |
453 | |
454 | tabMenu->clear(); |
455 | splitHMenu->clear(); |
456 | splitVMenu->clear(); |
457 | const QList<ColorSwatch *> dockList = mainWindow->findChildren<ColorSwatch*>(); |
458 | for (const ColorSwatch *dock : dockList) { |
459 | tabMenu->addAction(text: dock->objectName()); |
460 | splitHMenu->addAction(text: dock->objectName()); |
461 | splitVMenu->addAction(text: dock->objectName()); |
462 | } |
463 | } |
464 | |
465 | static ColorSwatch *findByName(const QMainWindow *mainWindow, const QString &name) |
466 | { |
467 | const QList<ColorSwatch *> dockList = mainWindow->findChildren<ColorSwatch*>(); |
468 | for (ColorSwatch *dock : dockList) { |
469 | if (name == dock->objectName()) |
470 | return dock; |
471 | } |
472 | return nullptr; |
473 | } |
474 | |
475 | void ColorSwatch::splitInto(QAction *action) |
476 | { |
477 | ColorSwatch *target = findByName(mainWindow, name: action->text()); |
478 | if (!target) |
479 | return; |
480 | |
481 | const Qt::Orientation o = action->parent() == splitHMenu |
482 | ? Qt::Horizontal : Qt::Vertical; |
483 | mainWindow->splitDockWidget(after: target, dockwidget: this, orientation: o); |
484 | } |
485 | |
486 | void ColorSwatch::tabInto(QAction *action) |
487 | { |
488 | if (ColorSwatch *target = findByName(mainWindow, name: action->text())) |
489 | mainWindow->tabifyDockWidget(first: target, second: this); |
490 | } |
491 | |
492 | #ifndef QT_NO_CONTEXTMENU |
493 | void ColorSwatch::(QContextMenuEvent *event) |
494 | { |
495 | event->accept(); |
496 | menu->exec(pos: event->globalPos()); |
497 | } |
498 | #endif // QT_NO_CONTEXTMENU |
499 | |
500 | void ColorSwatch::resizeEvent(QResizeEvent *e) |
501 | { |
502 | if (BlueTitleBar *btb = qobject_cast<BlueTitleBar*>(object: titleBarWidget())) |
503 | btb->updateMask(); |
504 | |
505 | QDockWidget::resizeEvent(event: e); |
506 | } |
507 | |
508 | void ColorSwatch::allow(Qt::DockWidgetArea area, bool a) |
509 | { |
510 | Qt::DockWidgetAreas areas = allowedAreas(); |
511 | areas = a ? areas | area : areas & ~area; |
512 | setAllowedAreas(areas); |
513 | |
514 | if (areaActions->isEnabled()) { |
515 | leftAction->setEnabled(areas & Qt::LeftDockWidgetArea); |
516 | rightAction->setEnabled(areas & Qt::RightDockWidgetArea); |
517 | topAction->setEnabled(areas & Qt::TopDockWidgetArea); |
518 | bottomAction->setEnabled(areas & Qt::BottomDockWidgetArea); |
519 | } |
520 | } |
521 | |
522 | void ColorSwatch::place(Qt::DockWidgetArea area, bool p) |
523 | { |
524 | if (!p) |
525 | return; |
526 | |
527 | mainWindow->addDockWidget(area, dockwidget: this); |
528 | |
529 | if (allowedAreasActions->isEnabled()) { |
530 | allowLeftAction->setEnabled(area != Qt::LeftDockWidgetArea); |
531 | allowRightAction->setEnabled(area != Qt::RightDockWidgetArea); |
532 | allowTopAction->setEnabled(area != Qt::TopDockWidgetArea); |
533 | allowBottomAction->setEnabled(area != Qt::BottomDockWidgetArea); |
534 | } |
535 | } |
536 | |
537 | void ColorSwatch::setCustomSizeHint(const QSize &size) |
538 | { |
539 | if (ColorDock *dock = qobject_cast<ColorDock*>(object: widget())) |
540 | dock->setCustomSizeHint(size); |
541 | } |
542 | |
543 | void ColorSwatch::changeClosable(bool on) |
544 | { setFeatures(on ? features() | DockWidgetClosable : features() & ~DockWidgetClosable); } |
545 | |
546 | void ColorSwatch::changeMovable(bool on) |
547 | { setFeatures(on ? features() | DockWidgetMovable : features() & ~DockWidgetMovable); } |
548 | |
549 | void ColorSwatch::changeFloatable(bool on) |
550 | { setFeatures(on ? features() | DockWidgetFloatable : features() & ~DockWidgetFloatable); } |
551 | |
552 | void ColorSwatch::changeFloating(bool floating) |
553 | { setFloating(floating); } |
554 | |
555 | void ColorSwatch::allowLeft(bool a) |
556 | { allow(area: Qt::LeftDockWidgetArea, a); } |
557 | |
558 | void ColorSwatch::allowRight(bool a) |
559 | { allow(area: Qt::RightDockWidgetArea, a); } |
560 | |
561 | void ColorSwatch::allowTop(bool a) |
562 | { allow(area: Qt::TopDockWidgetArea, a); } |
563 | |
564 | void ColorSwatch::allowBottom(bool a) |
565 | { allow(area: Qt::BottomDockWidgetArea, a); } |
566 | |
567 | void ColorSwatch::placeLeft(bool p) |
568 | { place(area: Qt::LeftDockWidgetArea, p); } |
569 | |
570 | void ColorSwatch::placeRight(bool p) |
571 | { place(area: Qt::RightDockWidgetArea, p); } |
572 | |
573 | void ColorSwatch::placeTop(bool p) |
574 | { place(area: Qt::TopDockWidgetArea, p); } |
575 | |
576 | void ColorSwatch::placeBottom(bool p) |
577 | { place(area: Qt::BottomDockWidgetArea, p); } |
578 | |
579 | void ColorSwatch::changeVerticalTitleBar(bool on) |
580 | { |
581 | setFeatures(on ? features() | DockWidgetVerticalTitleBar |
582 | : features() & ~DockWidgetVerticalTitleBar); |
583 | } |
584 | |
585 | QSize BlueTitleBar::minimumSizeHint() const |
586 | { |
587 | QDockWidget *dw = qobject_cast<QDockWidget*>(object: parentWidget()); |
588 | Q_ASSERT(dw); |
589 | QSize result(leftPm.width() + rightPm.width(), centerPm.height()); |
590 | if (dw->features() & QDockWidget::DockWidgetVerticalTitleBar) |
591 | result.transpose(); |
592 | return result; |
593 | } |
594 | |
595 | BlueTitleBar::BlueTitleBar(QWidget *parent) |
596 | : QWidget(parent) |
597 | , leftPm(QPixmap(":/res/titlebarLeft.png" )) |
598 | , centerPm(QPixmap(":/res/titlebarCenter.png" )) |
599 | , rightPm(QPixmap(":/res/titlebarRight.png" )) |
600 | { |
601 | } |
602 | |
603 | void BlueTitleBar::paintEvent(QPaintEvent*) |
604 | { |
605 | QPainter painter(this); |
606 | QRect rect = this->rect(); |
607 | |
608 | QDockWidget *dw = qobject_cast<QDockWidget*>(object: parentWidget()); |
609 | Q_ASSERT(dw); |
610 | |
611 | if (dw->features() & QDockWidget::DockWidgetVerticalTitleBar) { |
612 | QSize s = rect.size(); |
613 | s.transpose(); |
614 | rect.setSize(s); |
615 | |
616 | painter.translate(dx: rect.left(), dy: rect.top() + rect.width()); |
617 | painter.rotate(a: -90); |
618 | painter.translate(dx: -rect.left(), dy: -rect.top()); |
619 | } |
620 | |
621 | painter.drawPixmap(p: rect.topLeft(), pm: leftPm); |
622 | painter.drawPixmap(p: rect.topRight() - QPoint(rightPm.width() - 1, 0), pm: rightPm); |
623 | QBrush brush(centerPm); |
624 | painter.fillRect(x: rect.left() + leftPm.width(), y: rect.top(), |
625 | w: rect.width() - leftPm.width() - rightPm.width(), |
626 | h: centerPm.height(), b: centerPm); |
627 | } |
628 | |
629 | void BlueTitleBar::mouseReleaseEvent(QMouseEvent *event) |
630 | { |
631 | QPoint pos = event->pos(); |
632 | |
633 | QRect rect = this->rect(); |
634 | |
635 | QDockWidget *dw = qobject_cast<QDockWidget*>(object: parentWidget()); |
636 | Q_ASSERT(dw); |
637 | |
638 | if (dw->features() & QDockWidget::DockWidgetVerticalTitleBar) { |
639 | QPoint p = pos; |
640 | pos.setX(rect.left() + rect.bottom() - p.y()); |
641 | pos.setY(rect.top() + p.x() - rect.left()); |
642 | |
643 | QSize s = rect.size(); |
644 | s.transpose(); |
645 | rect.setSize(s); |
646 | } |
647 | |
648 | const int buttonRight = 7; |
649 | const int buttonWidth = 20; |
650 | int right = rect.right() - pos.x(); |
651 | int button = (right - buttonRight)/buttonWidth; |
652 | switch (button) { |
653 | case 0: |
654 | event->accept(); |
655 | dw->close(); |
656 | break; |
657 | case 1: |
658 | event->accept(); |
659 | dw->setFloating(!dw->isFloating()); |
660 | break; |
661 | case 2: { |
662 | event->accept(); |
663 | QDockWidget::DockWidgetFeatures features = dw->features(); |
664 | if (features & QDockWidget::DockWidgetVerticalTitleBar) |
665 | features &= ~QDockWidget::DockWidgetVerticalTitleBar; |
666 | else |
667 | features |= QDockWidget::DockWidgetVerticalTitleBar; |
668 | dw->setFeatures(features); |
669 | break; |
670 | } |
671 | default: |
672 | event->ignore(); |
673 | break; |
674 | } |
675 | } |
676 | |
677 | void BlueTitleBar::updateMask() |
678 | { |
679 | QDockWidget *dw = qobject_cast<QDockWidget*>(object: parent()); |
680 | Q_ASSERT(dw); |
681 | |
682 | QRect rect = dw->rect(); |
683 | QPixmap bitmap(dw->size()); |
684 | |
685 | { |
686 | QPainter painter(&bitmap); |
687 | |
688 | // initialize to transparent |
689 | painter.fillRect(r: rect, c: Qt::color0); |
690 | |
691 | QRect contents = rect; |
692 | contents.setTopLeft(geometry().bottomLeft()); |
693 | contents.setRight(geometry().right()); |
694 | contents.setBottom(contents.bottom()-y()); |
695 | painter.fillRect(r: contents, c: Qt::color1); |
696 | |
697 | // let's paint the titlebar |
698 | QRect titleRect = this->geometry(); |
699 | |
700 | if (dw->features() & QDockWidget::DockWidgetVerticalTitleBar) { |
701 | QSize s = rect.size(); |
702 | s.transpose(); |
703 | rect.setSize(s); |
704 | |
705 | QSize s2 = size(); |
706 | s2.transpose(); |
707 | titleRect.setSize(s2); |
708 | |
709 | painter.translate(dx: rect.left(), dy: rect.top() + rect.width()); |
710 | painter.rotate(a: -90); |
711 | painter.translate(dx: -rect.left(), dy: -rect.top()); |
712 | } |
713 | |
714 | contents.setTopLeft(titleRect.bottomLeft()); |
715 | contents.setRight(titleRect.right()); |
716 | contents.setBottom(rect.bottom()-y()); |
717 | |
718 | QRect rect = titleRect; |
719 | |
720 | painter.drawPixmap(p: rect.topLeft(), pm: leftPm.mask()); |
721 | painter.fillRect(x: rect.left() + leftPm.width(), y: rect.top(), |
722 | w: rect.width() - leftPm.width() - rightPm.width(), |
723 | h: centerPm.height(), c: Qt::color1); |
724 | painter.drawPixmap(p: rect.topRight() - QPoint(rightPm.width() - 1, 0), pm: rightPm.mask()); |
725 | |
726 | painter.fillRect(r: contents, c: Qt::color1); |
727 | } |
728 | |
729 | dw->setMask(bitmap); |
730 | } |
731 | |
732 | #include "colorswatch.moc" |
733 | |