Krita Source Code Documentation
Loading...
Searching...
No Matches
KoToolBox.cpp
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 * SPDX-FileCopyrightText: 2022 Alvin Wong <alvin@alvinhc.com>
6 *
7 * SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9
10#include "KoToolBox_p.h"
11#include "KoToolBoxLayout_p.h"
12#include "KoToolBoxButton_p.h"
13#include "kis_assert.h"
14
15#include <QButtonGroup>
16#include <QToolButton>
17#include <QStyleOption>
18#include <QActionGroup>
19#include <QPainter>
20#include <QHash>
21#include <QApplication>
22#include <QStyle>
23#include <QTimer>
24#include <QMenu>
25#include <QAction>
26#include <QScreen>
27
28#include <klocalizedstring.h>
29#include <WidgetsDebug.h>
30#include <kconfiggroup.h>
31#include <ksharedconfig.h>
32#include <KisPortingUtils.h>
33
34#include <kactioncollection.h>
35#include <KisViewManager.h>
36#include <KoCanvasController.h>
37#include <KoShapeLayer.h>
38
39#define BUTTON_MARGIN 10
40
41static int buttonSize(int screen)
42{
43 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(screen < QGuiApplication::screens().size() && screen >= 0, 16);
44
45 QRect rc = QGuiApplication::screens().at(screen)->availableGeometry();
46 if (rc.width() <= 1024) {
47 return 12;
48 }
49 else if (rc.width() <= 1377) {
50 return 14;
51 }
52 else if (rc.width() <= 1920 ) {
53 return 16;
54 }
55 else {
56 return 22;
57 }
58
59}
60
62{
63public:
64 void addSection(Section *section, const QString &name);
65
68 QHash<QString, KoToolBoxButton*> buttonsByToolId;
69 QMap<QString, Section*> sections;
71 QButtonGroup *buttonGroup {0};
72 QHash<QToolButton*, QString> visibilityCodes;
73 bool floating {false};
74 int iconSize {0};
75 QMap<QAction*,int> contextIconSizes;
77};
78
79void KoToolBox::Private::addSection(Section *section, const QString &name)
80{
81 section->setName(name);
82 layout->addSection(section);
83 sections.insert(name, section);
84}
85
87 : d(new Private)
88{
89 d->layout = new KoToolBoxLayout(this);
90 // add defaults
91 d->addSection(new Section(this), "main");
92 d->addSection(new Section(this), "dynamic");
93
94 d->buttonGroup = new QButtonGroup(this);
95
96 // Get screen the widget exists in, but fall back to primary screen if invalid.
97 const int widgetsScreen = KisPortingUtils::getScreenNumberForWidget(QApplication::activeWindow());
98 const int primaryScreen = 0; //In QT, primary screen should always be the first index of QGuiApplication::screens()
99 const int screen = (widgetsScreen >= 0 && widgetsScreen < QGuiApplication::screens().size()) ? widgetsScreen : primaryScreen;
100 const int toolbuttonSize = buttonSize(screen);
101 KConfigGroup cfg = KSharedConfig::openConfig()->group("KoToolBox");
102 d->iconSize = cfg.readEntry("iconSize", toolbuttonSize);
103
104 Q_FOREACH (KoToolAction *toolAction, KoToolManager::instance()->toolActionList()) {
105 addButton(toolAction);
106 }
107
109
110 // Update visibility of buttons
112
113 setCompact(cfg.readEntry<bool>("compact", false));
114
115 connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*)),
116 this, SLOT(setActiveTool(KoCanvasController*)));
117 connect(KoToolManager::instance(), SIGNAL(currentLayerChanged(const KoCanvasController*,const KoShapeLayer*)),
118 this, SLOT(setCurrentLayer(const KoCanvasController*,const KoShapeLayer*)));
119 connect(KoToolManager::instance(), SIGNAL(toolCodesSelected(QList<QString>)), this, SLOT(setButtonsVisible(QList<QString>)));
120 connect(KoToolManager::instance(),
121 SIGNAL(addedTool(KoToolAction*,KoCanvasController*)),
123
124}
125
127{
128 delete d;
129}
130
132{
133 Q_FOREACH (QToolButton *button, d->buttons) {
134 button->setIconSize(QSize(d->iconSize, d->iconSize));
135 }
136
137 Q_FOREACH (Section *section, d->sections.values()) {
139 }
140}
141
142
144{
145 KisKActionCollection *actionCollection = viewManager->actionCollection();
146 Q_FOREACH(KoToolAction *toolAction, KoToolManager::instance()->toolActionList()) {
147 QAction *toolQAction = actionCollection->action(toolAction->id());
148 auto button = d->buttonsByToolId.find(toolAction->id());
149 if (button == d->buttonsByToolId.end()) {
150 qWarning() << "Toolbox is missing button for tool" << toolAction->id();
151 continue;
152 }
153 (*button)->attachAction(toolQAction);
154 }
155}
156
158{
159 KoToolBoxButton *button = new KoToolBoxButton(toolAction, this);
160
161 d->buttons << button;
162
163 QString sectionToBeAddedTo;
164 const QString section = toolAction->section();
165 if (section.contains(qApp->applicationName())) {
166 sectionToBeAddedTo = "main";
167 } else if (section.contains("main")) {
168 sectionToBeAddedTo = "main";
169 } else if (section.contains("dynamic")) {
170 sectionToBeAddedTo = "dynamic";
171 } else {
172 sectionToBeAddedTo = section;
173 }
174
175 Section *sectionWidget = d->sections.value(sectionToBeAddedTo);
176 if (sectionWidget == 0) {
177 sectionWidget = new Section(this);
178 d->addSection(sectionWidget, sectionToBeAddedTo);
179 }
180 sectionWidget->addButton(button, toolAction->priority());
181
182 d->buttonGroup->addButton(button);
183 d->buttonsByToolId.insert(toolAction->id(), button);
184
185 d->visibilityCodes.insert(button, toolAction->visibilityCode());
186}
187
189{
190 Q_UNUSED(canvas);
191
192 QString id = KoToolManager::instance()->activeToolId();
194 if (button) {
195 button->setChecked(true);
196 button->setHighlightColor();
197 if (d->selectedButton) {
199 }
201 }
202 else {
203 warnWidgets << "KoToolBox::setActiveTool(" << id << "): no such button found";
204 }
205}
206
208{
209 Q_FOREACH (QToolButton *button, d->visibilityCodes.keys()) {
210 QString code = d->visibilityCodes.value(button);
211
212 if (code.startsWith(QLatin1String("flake/"))) {
213 continue;
214 }
215
216 if (code.endsWith( QLatin1String( "/always"))) {
217 button->setVisible(true);
218 button->setEnabled(true);
219 }
220 else if (code.isEmpty()) {
221 button->setVisible(true);
222 button->setEnabled( codes.count() != 0 );
223 }
224 else {
225 button->setVisible( codes.contains(code) );
226 }
227 }
228 layout()->invalidate();
229 update();
230}
231
233{
234 Q_UNUSED(canvas);
235 const bool enabled = layer == 0 || (layer->isShapeEditable() && layer->isVisible());
236 foreach (QToolButton *button, d->visibilityCodes.keys()) {
237 if (d->visibilityCodes[button].endsWith( QLatin1String( "/always") ) ) {
238 continue;
239 }
240 button->setEnabled(enabled);
241 }
242}
243
244void KoToolBox::paintEvent(QPaintEvent *)
245{
246 QPainter painter(this);
247
248 const QList<Section*> sections = d->sections.values();
249 int halfSpacing = layout()->spacing();
250 if (halfSpacing > 0) {
251 halfSpacing /= 2;
252 }
253 for(auto iterator = sections.begin(); iterator != sections.end(); ++iterator) {
254 Section *section = *iterator;
255 if (section->separators() == Section::SeparatorNone) {
256 continue;
257 }
258
259 QStyleOption styleoption;
260 styleoption.palette = palette();
261
262 if (section->separators() & Section::SeparatorTop) {
263 int y = section->y() - halfSpacing;
264 styleoption.state = QStyle::State_None;
265 styleoption.rect = QRect(section->x(), y-1, section->width(), 2);
266
267 style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &styleoption, &painter);
268 }
269
270 if (section->separators() & Section::SeparatorLeft && section->isLeftToRight()) {
271 int x = section->x() - halfSpacing;
272 styleoption.state = QStyle::State_Horizontal;
273 styleoption.rect = QRect(x-1, section->y(), 2, section->height());
274
275 style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &styleoption, &painter);
276 } else if (section->separators() & Section::SeparatorLeft && section->isRightToLeft()) {
277 int x = section->x() + section->width() + halfSpacing;
278 styleoption.state = QStyle::State_Horizontal;
279 styleoption.rect = QRect(x-1, section->y(), 2, section->height());
280
281 style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &styleoption, &painter);
282 }
283 }
284
285 painter.end();
286}
287
288void KoToolBox::changeEvent(QEvent *event)
289{
290 QWidget::changeEvent(event);
291 if (event->type() == QEvent::PaletteChange) {
292 Q_FOREACH (QToolButton *button, d->buttons) {
293 KoToolBoxButton* toolBoxButton = qobject_cast<KoToolBoxButton*>(button);
294 if (toolBoxButton) {
295 toolBoxButton->setHighlightColor();
296 }
297 }
298 }
299}
300
301void KoToolBox::setOrientation(Qt::Orientation orientation)
302{
303 d->layout->setOrientation(orientation);
304 QTimer::singleShot(0, this, SLOT(update()));
305 Q_FOREACH (Section* section, d->sections) {
306 section->setOrientation(orientation);
307 }
308}
309
310void KoToolBox::setCompact(bool state)
311{
312 d->layout->setCompact(state);
313}
314
316{
317 return d->layout->isCompact();
318}
319
321{
322 d->floating = v;
323}
324
326{
327 Q_UNUSED(canvas);
328 addButton(toolAction);
330
331}
332
334{
335 QAction* action = qobject_cast<QAction*>(sender());
336 if (action) {
337 int iconSize = -1;
338 if (action == d->defaultIconSizeAction) {
339 iconSize = buttonSize(KisPortingUtils::getScreenNumberForWidget(QApplication::activeWindow()));
340 QAction *action = d->contextIconSizes.key(iconSize);
341 if (action) {
342 action->setChecked(true);
343 }
344 } else if (d->contextIconSizes.contains(action)) {
345 iconSize = d->contextIconSizes.value(action);
346 }
348
349 KConfigGroup cfg = KSharedConfig::openConfig()->group("KoToolBox");
350 cfg.writeEntry("iconSize", iconSize);
352
354 }
355}
356
358{
359 if (d->contextIconSizes.isEmpty()) {
360 d->defaultIconSizeAction = menu->addAction(i18nc("@item:inmenu Icon size", "Default"),
361 this, SLOT(slotContextIconSize()));
362
363 QActionGroup *sizeGroup = new QActionGroup(menu);
364 QList<int> sizes;
365 sizes << 12 << 14 << 16 << 22 << 32 << 48 << 64; //<< 96 << 128 << 192 << 256;
366 Q_FOREACH (int i, sizes) {
367 QAction *action = menu->addAction(i18n("%1x%2", i, i), this, SLOT(slotContextIconSize()));
368 d->contextIconSizes.insert(action, i);
369 action->setActionGroup(sizeGroup);
370 action->setCheckable(true);
371 if (d->iconSize == i) {
372 action->setChecked(true);
373 }
374 }
375 }
376}
377
379{
380 return d->layout;
381}
382
383#include "moc_KoToolBoxScrollArea_p.cpp"
qreal v
#define BUTTON_MARGIN
Definition KoToolBox.cpp:39
static int buttonSize(int screen)
Definition KoToolBox.cpp:41
#define warnWidgets
int iconSize(qreal width, qreal height)
A container for a set of QAction objects.
QAction * action(int index) const
virtual KisKActionCollection * actionCollection() const
virtual bool isShapeEditable(bool recursive=true) const
checks recursively if the shape or one of its parents is not visible or locked
Definition KoShape.cpp:965
bool isVisible(bool recursive=true) const
Definition KoShape.cpp:797
QString section() const
The section the tool wants to be in.
QString id() const
The id of the tool.
int priority() const
Lower number (higher priority) means coming first in the section.
QString visibilityCode() const
This tool should become visible when we Q_EMIT this string in toolCodesSelected()
void setCompact(bool state)
void setOrientation(Qt::Orientation orientation)
void addSection(Section *section)
QHash< QString, KoToolBoxButton * > buttonsByToolId
Definition KoToolBox.cpp:68
QHash< QToolButton *, QString > visibilityCodes
Definition KoToolBox.cpp:72
QMap< QAction *, int > contextIconSizes
Definition KoToolBox.cpp:75
KoToolBoxLayout * layout
Definition KoToolBox.cpp:70
QList< QToolButton * > buttons
Definition KoToolBox.cpp:66
QAction * defaultIconSizeAction
Definition KoToolBox.cpp:76
void addSection(Section *section, const QString &name)
Definition KoToolBox.cpp:79
KoToolBoxButton * selectedButton
Definition KoToolBox.cpp:67
QMap< QString, Section * > sections
Definition KoToolBox.cpp:69
QButtonGroup * buttonGroup
Definition KoToolBox.cpp:71
void setButtonsVisible(const QList< QString > &codes)
void addButton(KoToolAction *toolAction)
void setupIconSizeMenu(QMenu *menu)
~KoToolBox() override
void paintEvent(QPaintEvent *event) override
void setCurrentLayer(const KoCanvasController *canvas, const KoShapeLayer *newLayer)
void setFloating(bool v)
void applyIconSize()
void toolAdded(KoToolAction *toolAction, KoCanvasController *canvas)
add a tool post-initialization. The tool will also be activated.
void setActiveTool(KoCanvasController *canvas)
KoToolBoxLayout * toolBoxLayout() const
void setViewManager(KisViewManager *viewManager)
void changeEvent(QEvent *event) override
bool isCompact()
void slotContextIconSize()
set the icon size for all the buttons
Private *const d
void setOrientation(Qt::Orientation orientation)
Set the orientation of the layout to orientation.
KoToolBox()
constructor
Definition KoToolBox.cpp:86
void setCompact(bool)
QString activeToolId() const
Returns the toolId of the currently active tool.
static KoToolManager * instance()
Return the toolmanager singleton.
void addButton(QAbstractButton *button, int priority)
void setButtonSize(QSize size)
Separators separators() const
void setOrientation(Qt::Orientation orientation)
void setName(const QString &name)
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
QString button(const QWheelEvent &ev)
int getScreenNumberForWidget(const QWidget *w)
rgba palette[MAX_PALETTE]
Definition palette.c:35