Krita Source Code Documentation
Loading...
Searching...
No Matches
KoToolBoxLayout_p.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2005-2009 Thomas Zander <zander@kde.org>
3 * SPDX-FileCopyrightText: 2009 Peter Simonsson <peter.simonsson@gmail.com>
4 * SPDX-FileCopyrightText: 2010 Cyrille Berger <cberger@cberger.net>
5 *
6 * SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8#ifndef _KO_TOOLBOX_LAYOUT_H_
9#define _KO_TOOLBOX_LAYOUT_H_
10
11#include <WidgetsDebug.h>
12#include <QLayout>
13#include <QMap>
14#include <QRect>
15#include <QAbstractButton>
16#include <QApplication>
17#include <QMouseEvent>
18
19class SectionLayout : public QLayout
20{
21public:
22 explicit SectionLayout(QWidget *parent)
23 : QLayout(parent)
24 , m_orientation(Qt::Vertical)
25 , m_onset(0)
26 {
27 }
28
29 ~SectionLayout() override
30 {
31 qDeleteAll( m_items );
32 m_items.clear();
33 }
34
35 void addButton(QAbstractButton *button, int priority)
36 {
37 addChildWidget(button);
38 if (m_priorities.values().contains(priority)) {
39 qWarning() << "Button" << button << "has a conflicting priority";
40 }
41
42 m_priorities.insert(button, priority);
43 int index = 1;
44 Q_FOREACH (QWidgetItem *item, m_items) {
45 if (m_priorities.value(static_cast<QAbstractButton*>(item->widget())) > priority)
46 break;
47 index++;
48 }
49 m_items.insert(index-1, new QWidgetItem(button));
50 }
51
52 QSize sizeHint() const override
53 {
54 // This is implemented just to not freak out GammaRay, in practice
55 // this doesn't have any effect on the layout.
56 if (m_orientation == Qt::Vertical) {
57 return QSize(m_buttonSize.width(), m_buttonSize.height() * count());
58 } else {
59 return QSize(m_buttonSize.width() * count(), m_buttonSize.height());
60 }
61 }
62
63 void addItem(QLayoutItem*) override { Q_ASSERT(0); }
64
65 QLayoutItem* itemAt(int i) const override
66 {
67 if (m_items.count() <= i)
68 return 0;
69 return m_items.at(i);
70 }
71
72 QLayoutItem* takeAt(int i) override { return m_items.takeAt(i); }
73
74 int count() const override { return m_items.count(); }
75
76 void setGeometry (const QRect &rect) override
77 {
78 // the names of the variables assume a vertical orientation,
79 // but all calculations are done based on the real orientation
80 const bool isVertical = m_orientation == Qt::Vertical;
81 const bool isLeftToRight = parentWidget()->isLeftToRight();
82
83 const QSize &size = buttonSize();
84 const int maxWidth = isVertical ? rect.width() : rect.height();
85 const int iconWidth = isVertical ? size.width() : size.height();
86 const int iconHeight = isVertical ? size.height() : size.width();
87
88 int x = iconWidth * m_onset;
89 int y = 0;
90 foreach (QWidgetItem* w, m_items) {
91 if (w->isEmpty()) {
92 continue;
93 }
94 if (isVertical) {
95 const int realX = isLeftToRight ? x : rect.width() - x - size.width();
96 w->widget()->setGeometry(QRect(realX, y, size.width(), size.height()));
97 } else {
98 const int realX = isLeftToRight ? y : rect.width() - y - size.width();
99 w->widget()->setGeometry(QRect(realX, x, size.width(), size.height()));
100 }
101 x += iconWidth;
102 if (x + iconWidth > maxWidth) {
103 x = 0;
104 y += iconHeight;
105 }
106 }
107 }
108
109 void setButtonSize(const QSize size)
110 {
111 m_buttonSize = size;
112 }
113
114 const QSize &buttonSize() const
115 {
116 return m_buttonSize;
117 }
118
119 void setOrientation (Qt::Orientation orientation)
120 {
121 m_orientation = orientation;
122 }
123
124 void setOnset(int onset)
125 {
126 m_onset = onset;
127 }
128
129private:
131 QMap<QAbstractButton*, int> m_priorities;
133 Qt::Orientation m_orientation;
135};
136
137class Section : public QWidget
138{
139public:
142 SeparatorTop = (1 << 0),
143 // SeparatorBottom = (1 << 1),
144 // SeparatorRight = (1 << 2),
145 SeparatorLeft = (1 << 3)
146 };
147 Q_DECLARE_FLAGS(Separators, SeparatorFlag)
148 explicit Section(QWidget *parent = 0)
149 : QWidget(parent),
150 m_layout(new SectionLayout(this))
151 {
152// Re-enable this when we need to debug the section layout again.
153// setAutoFillBackground(true);
154// static int i = 0;
155// switch(i) {
156// case 0:
157// setStyleSheet("background-color:red");
158// break;
159// case 1:
160// setStyleSheet("background-color:blue");
161// break;
162// case 2:
163// setStyleSheet("background-color:green");
164// break;
165// case 3:
166// setStyleSheet("background-color:yellow");
167// break;
168// case 4:
169// setStyleSheet("background-color:white");
170// break;
171// case 5:
172// setStyleSheet("background-color:gray");
173// break;
174// case 6:
175// setStyleSheet("background-color:lime");
176// break;
177// case 7:
178// setStyleSheet("background-color:silver");
179// break;
180// case 8:
181// setStyleSheet("background-color:purple");
182// break;
183// default:
184// setStyleSheet("background-color:maroon");
185// break;
186// }
187// i++;
188
189 }
190
191 void addButton(QAbstractButton *button, int priority)
192 {
193 m_layout->addButton(button, priority);
194 }
195
196 void setName(const QString &name)
197 {
198 setObjectName(name);
199 m_name = name;
200 }
201
202 QString name() const
203 {
204 return m_name;
205 }
206
207 void setButtonSize(QSize size)
208 {
209 m_layout->setButtonSize(size);
210 }
211
212 QSize iconSize() const
213 {
214 return m_layout->buttonSize();
215 }
216
218 {
219 int count = 0;
220 for(int i = m_layout->count()-1; i >= 0; --i) {
221 if (! static_cast<QWidgetItem*> (m_layout->itemAt(i))->isEmpty())
222 ++count;
223 }
224 return count;
225 }
226
227 void setSeparator(Separators separators)
228 {
230 }
231
232 Separators separators() const
233 {
234 return m_separators;
235 }
236
237 void setOrientation (Qt::Orientation orientation)
238 {
239 m_layout->setOrientation(orientation);
240 }
241
242 void setOnset(int onset)
243 {
244 m_layout->setOnset(onset);
245 }
246
247
248protected:
249private:
251 QString m_name;
252 Separators m_separators;
253};
254
255Q_DECLARE_OPERATORS_FOR_FLAGS(Section::Separators)
256
257class KoToolBoxLayout : public QLayout
258{
259public:
260 explicit KoToolBoxLayout(QWidget *parent)
261 : QLayout(parent)
262 , m_orientation(Qt::Vertical)
263 , m_compact(false)
264 {
265 setSpacing(6);
266 }
267
269 {
270 qDeleteAll( m_sections );
271 m_sections.clear();
272 }
273
274 QSize sizeHint() const override
275 {
276 // Prefer showing two rows/columns by default
277 QSize twoIcons = static_cast<Section*> (m_sections[0]->widget())->iconSize() * 2;
278 const int length = doLayout(QRect(QPoint(), twoIcons), false);
279 if (m_orientation == Qt::Vertical) {
280 return QSize(twoIcons.width(), length);
281 } else {
282 return QSize(length, twoIcons.height());
283 }
284 }
285
286 QSize minimumSize() const override
287 {
288 if (m_sections.isEmpty())
289 return QSize();
290 QSize oneIcon = static_cast<Section*> (m_sections[0]->widget())->iconSize();
291 return oneIcon;
292 }
293
294 void addSection(Section *section)
295 {
296 addChildWidget(section);
297
298 QList<QWidgetItem*>::iterator iterator = m_sections.begin();
299 int defaults = 2; // skip the first two as they are the 'main' and 'dynamic' sections.
300 while (iterator != m_sections.end()) {
301 if (--defaults < 0 && static_cast<Section*> ((*iterator)->widget())->name() > section->name())
302 break;
303 ++iterator;
304 }
305 m_sections.insert(iterator, new QWidgetItem(section));
306 }
307
308 void addItem(QLayoutItem*) override
309 {
310 Q_ASSERT(0); // don't let anything else be added. (code depends on this!)
311 }
312
313 QLayoutItem* itemAt(int i) const override
314 {
315 return m_sections.value(i);
316 }
317
318 QLayoutItem* takeAt(int i) override { return m_sections.takeAt(i); }
319
320 int count() const override { return m_sections.count(); }
321
322 void setGeometry (const QRect &rect) override
323 {
324 QLayout::setGeometry(rect);
325 doLayout(rect, true);
326 }
327
328 bool hasHeightForWidth() const override
329 {
330 // return true;
331 return m_orientation == Qt::Vertical;
332 }
333
334 int heightForWidth(int width) const override
335 {
336 if (m_orientation == Qt::Vertical) {
337 const int height = doLayout(QRect(0, 0, width, 0), false);
338 return height;
339 } else {
340 #if 0
341 const int iconHeight = static_cast<Section*> (m_sections[0]->widget())->iconSize().height();
342 for (int i = 1; i <= 10; i++) {
343 const int testWidth = doLayout(QRect(0, 0, 0, iconHeight * i), false);
344 if (testWidth <= width) {
345 return iconHeight * i;
346 }
347 }
348 // Return a huge height
349 return 65535;
350 #endif
351 return -1;
352 }
353 }
354
360 int widthForHeight(int height) const
361 {
362 if (m_orientation == Qt::Horizontal) {
363 const int width = doLayout(QRect(0, 0, 0, height), false);
364 return width;
365 } else {
366 return -1;
367 }
368 }
369
370 void setOrientation (Qt::Orientation orientation)
371 {
372 m_orientation = orientation;
373 invalidate();
374 }
375
376 void setCompact(bool state)
377 {
378 m_compact = state;
379 foreach (QWidgetItem *wi, m_sections) {
380 wi->widget()->layout()->invalidate();
381 }
382 }
383
385 {
386 return m_compact;
387 }
388
389private:
390 int doLayout(const QRect &rect, bool notDryRun) const
391 {
392 // nothing to do?
393 if (m_sections.isEmpty()) {
394 return 0;
395 }
396
397 // the names of the variables assume a vertical orientation,
398 // but all calculations are done based on the real orientation
399 const bool isVertical = m_orientation == Qt::Vertical;
400 const bool isLeftToRight = parentWidget()->isLeftToRight();
401
402 const QSize iconSize = static_cast<Section*> (m_sections.first()->widget())->iconSize();
403
404 const int maxWidth = isVertical ? rect.width() : rect.height();
405 // using min 1 as width to e.g. protect against div by 0 below
406 const int iconWidth = qMax(1, isVertical ? iconSize.width() : iconSize.height());
407 const int iconHeight = qMax(1, isVertical ? iconSize.height() : iconSize.width());
408
409 const Section::Separators separator = m_compact ?
411 : isVertical ?
413 :
415
416 const int maxColumns = qMax(1, (maxWidth / iconWidth));
417
418 int y = 0;
419 int offset = 0;
420 bool firstSection = true;
421 foreach (QWidgetItem *wi, m_sections) {
422 Section *section = static_cast<Section*> (wi->widget());
423 const int buttonCount = section->visibleButtonCount();
424 if (buttonCount == 0) {
425 // move out of view, not perfect TODO: better solution
426 if (notDryRun) {
427 section->setGeometry(1000, 1000, 0, 0);
428 }
429 continue;
430 }
431
432 // in compact mode, the previous section's offset is this section's onset
433 const int onset = m_compact ? offset : 0;
434 const int usedColumns = onset + buttonCount;
435 offset = usedColumns % maxColumns;
436
437 // rows needed for the buttons (calculation gets the ceiling value of the plain div)
438 const int neededRowCount = ((usedColumns - 1) / maxColumns) + 1;
439
440 if (firstSection) {
441 firstSection = false;
442 } else {
443 if (m_compact) {
444 // start on a new row if the current row is full
445 y += (onset == 0) ? iconHeight : 0;
446 } else {
447 // start on a new row, set separator
448 y += iconHeight + spacing();
449 }
450 if (notDryRun) {
451 section->setOnset(onset);
452 section->setSeparator(separator);
453 }
454 }
455
456 if (notDryRun) {
457 const int onW = onset * iconWidth;
458 const int offW = offset * iconWidth;
459 const int width = maxColumns * iconWidth;
460 const int height = neededRowCount * iconHeight;
461
462 QRect geometry;
463 QRegion mask;
464 if (isVertical) {
465 mask = QRegion(0, 0, width, height);
466 if (isLeftToRight) {
467 geometry = QRect(0, y, width, height);
468
469 // mask onset and offset regions
470 // so that they don't block mouse events to other sections
471 mask -= QRegion(0, 0, onW, iconHeight);
472 if (offset != 0) {
473 mask -= QRegion(offW, height - iconHeight, width - offW, iconHeight);
474 }
475 } else {
476 geometry = QRect(rect.width() - width, y, width, height);
477 mask -= QRegion(width - onW, 0, onW, iconHeight);
478 if (offset != 0) {
479 mask -= QRegion(0, height - iconHeight, width - offW, iconHeight);
480 }
481 }
482 } else {
483 mask = QRegion(0, 0, height, width);
484 if (isLeftToRight) {
485 geometry = QRect(y, 0, height, width);
486 mask -= QRegion(0, 0, iconHeight, onW);
487 if (offset != 0) {
488 mask -= QRegion(height - iconHeight, offW, iconHeight, width - offW);
489 }
490 } else {
491 geometry = QRect(rect.width() - y - height, 0, height, width);
492 mask -= QRegion(height - iconHeight, 0, iconHeight, onW);
493 if (offset != 0) {
494 mask -= QRegion(0, offW, iconHeight, width - offW);
495 }
496 }
497 }
498 section->setGeometry(geometry);
499 section->setMask(mask);
500 }
501
502 // advance by all but the last used row
503 y += (neededRowCount - 1) * iconHeight;
504 }
505
506 // cache total height (or width), adding the iconHeight for the current row
507 return y + iconHeight;
508 }
509
510 QList <QWidgetItem*> m_sections;
511 Qt::Orientation m_orientation;
513};
514
515#endif
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
Q_DECLARE_FLAGS(KisUpdaterContextSnapshotEx, KisUpdaterContextSnapshotExTag)
int iconSize(qreal width, qreal height)
void addItem(QLayoutItem *) override
int count() const override
QSize sizeHint() const override
QList< QWidgetItem * > m_sections
bool hasHeightForWidth() const override
int doLayout(const QRect &rect, bool notDryRun) const
void setCompact(bool state)
KoToolBoxLayout(QWidget *parent)
int heightForWidth(int width) const override
QLayoutItem * itemAt(int i) const override
void setOrientation(Qt::Orientation orientation)
void addSection(Section *section)
~KoToolBoxLayout() override
void setGeometry(const QRect &rect) override
QLayoutItem * takeAt(int i) override
Qt::Orientation m_orientation
int widthForHeight(int height) const
QSize minimumSize() const override
void setButtonSize(const QSize size)
QMap< QAbstractButton *, int > m_priorities
const QSize & buttonSize() const
SectionLayout(QWidget *parent)
QLayoutItem * itemAt(int i) const override
int count() const override
~SectionLayout() override
void addButton(QAbstractButton *button, int priority)
void setOrientation(Qt::Orientation orientation)
QList< QWidgetItem * > m_items
QSize sizeHint() const override
void setGeometry(const QRect &rect) override
QLayoutItem * takeAt(int i) override
Qt::Orientation m_orientation
void setOnset(int onset)
void addItem(QLayoutItem *) override
SectionLayout * m_layout
void addButton(QAbstractButton *button, int priority)
void setButtonSize(QSize size)
Separators separators() const
QString name() const
Separators m_separators
void setOnset(int onset)
void setSeparator(Separators separators)
void setOrientation(Qt::Orientation orientation)
int visibleButtonCount() const
void setName(const QString &name)
QSize iconSize() const
Q_DECLARE_OPERATORS_FOR_FLAGS(KisBaseRectsWalker::SubtreeVisitFlags)
QString button(const QWheelEvent &ev)