Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_paintop_box.cc
Go to the documentation of this file.
1/*
2 * kis_paintop_box.cc - part of KImageShop/Krayon/Krita
3 *
4 * SPDX-FileCopyrightText: 2004 Boudewijn Rempt (boud@valdyas.org)
5 * SPDX-FileCopyrightText: 2009-2011 Sven Langkamp (sven.langkamp@gmail.com)
6 * SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
7 * SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>
8 * SPDX-FileCopyrightText: 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
9 * SPDX-FileCopyrightText: 2014 Mohit Goyal <mohit.bits2011@gmail.com>
10 *
11 * SPDX-License-Identifier: GPL-2.0-or-later
12 */
13
14#include "kis_paintop_box.h"
15
16#include <QHBoxLayout>
17#include <QToolButton>
18#include <QPixmap>
19#include <QWidgetAction>
20#include <QApplication>
21#include <QMenu>
22#include <QTime>
23
24#include <kis_debug.h>
25#include <kis_types.h>
26
27#include <kactioncollection.h>
28#include <kacceleratormanager.h>
29#include <QKeySequence>
30
31#include <kis_icon.h>
32#include <KoColorSpace.h>
34#include <KoToolManager.h>
36
37#include <KoResource.h>
38#include <KisDirtyStateSaver.h>
40
41#include <kis_paint_device.h>
46#include <kis_config_widget.h>
47#include <kis_image.h>
48#include <kis_node.h>
50#include <kis_action.h>
51
52#include "kis_canvas2.h"
53#include "kis_node_manager.h"
54#include "KisViewManager.h"
58#include "kis_config.h"
59#include "kis_image_config.h"
60
61#include "KisPopupButton.h"
68#include "kis_slider_spin_box.h"
69#include "KisAngleSelector.h"
73#include "tool/kis_tool.h"
74#include "kis_signals_blocker.h"
75#include "kis_action_manager.h"
78#include "KisResourceLoader.h"
81#include "KisMainWindow.h"
82
83
84KisPaintopBox::KisPaintopBox(KisViewManager *viewManager, QWidget *parent, const char *name)
85 : QWidget(parent)
86 , m_resourceProvider(viewManager->canvasResourceProvider())
87 , m_viewManager(viewManager)
88 , m_optionWidgetUpdateCompressor(100, KisSignalCompressor::FIRST_ACTIVE)
89{
90 Q_ASSERT(viewManager != 0);
91
92 setObjectName(name);
93 KisConfig cfg(true);
97
98 KAcceleratorManager::setNoAccel(this);
99
100 setWindowTitle(i18n("Painter's Toolchest"));
101
103
104 KConfigGroup grp = KSharedConfig::openConfig()->group("krita").group("Toolbar BrushesAndStuff");
105 int iconsize = grp.readEntry("IconSize", 22);
106 // NOTE: buttonsize should be the same value as the one used in ktoolbar for all QToolButton
107 int buttonsize = grp.readEntry("ButtonSize", 32);
108
109 if (!cfg.toolOptionsInDocker()) {
111 m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("view-choose"));
112 m_toolOptionsPopupButton->setToolTip(i18n("Tool Settings"));
113 m_toolOptionsPopupButton->setFixedSize(buttonsize, buttonsize);
114 m_toolOptionsPopupButton->setIconSize(QSize(iconsize, iconsize));
115 m_toolOptionsPopupButton->setAutoRaise(true);
116 }
117
119 m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02"));
120 m_brushEditorPopupButton->setToolTip(i18n("Edit brush settings"));
121 m_brushEditorPopupButton->setFixedSize(buttonsize, buttonsize);
122 m_brushEditorPopupButton->setIconSize(QSize(iconsize, iconsize));
123 m_brushEditorPopupButton->setAutoRaise(true);
124
126 m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01"));
127 m_presetSelectorPopupButton->setToolTip(i18n("Choose brush preset"));
128 m_presetSelectorPopupButton->setFixedSize(buttonsize, buttonsize);
129 m_presetSelectorPopupButton->setIconSize(QSize(iconsize, iconsize));
130 m_presetSelectorPopupButton->setAutoRaise(true);
132
134 m_eraseModeButton->setFixedSize(buttonsize, buttonsize);
135 m_eraseModeButton->setIconSize(QSize(iconsize, iconsize));
136 m_eraseModeButton->setCheckable(true);
137 m_eraseModeButton->setAutoRaise(true);
138
139
141 m_eraseModeButton->setDefaultAction(m_eraseAction);
142
143 // toggling eraser preset
145
146 // explicitly select eraser or brush preset
147 m_eraserSelectPresetAction = m_viewManager->actionManager()->createAction("eraser_select_preset_action");
148 m_brushSelectPresetAction = m_viewManager->actionManager()->createAction("brush_select_preset_action");
149
150 m_reloadButton = new QToolButton(this);
151 m_reloadButton->setFixedSize(buttonsize, buttonsize);
152 m_reloadButton->setIconSize(QSize(iconsize, iconsize));
153 m_reloadButton->setAutoRaise(true); // make button flat
154
155
156 m_reloadAction = m_viewManager->actionManager()->createAction("reload_preset_action");
157 m_reloadButton->setDefaultAction(m_reloadAction);
158
160 m_alphaLockButton->setFixedSize(buttonsize, buttonsize);
161 m_alphaLockButton->setIconSize(QSize(iconsize, iconsize));
162 m_alphaLockButton->setCheckable(true);
163 m_alphaLockButton->setAutoRaise(true);
164
165 KisAction* alphaLockAction = m_viewManager->actionManager()->createAction("preserve_alpha");
166 m_alphaLockButton->setDefaultAction(alphaLockAction);
167
168 // horizontal and vertical mirror toolbar buttons
169
170 // mirror tool options for the X Mirror
171 toolbarMenuXMirror = new QMenu();
172
173 hideCanvasDecorationsX = m_viewManager->actionManager()->createAction("mirrorX-hideDecorations");
175
178
179 moveToCenterActionX = m_viewManager->actionManager()->createAction("mirrorX-moveToCenter");
181
182
183 // mirror tool options for the Y Mirror
184 toolbarMenuYMirror = new QMenu();
185
186 hideCanvasDecorationsY = m_viewManager->actionManager()->createAction("mirrorY-hideDecorations");
188
189
192
193 moveToCenterActionY = m_viewManager->actionManager()->createAction("mirrorY-moveToCenter");
195
196 // create horizontal and vertical mirror buttons
197
199 int menuPadding = 20;
200 m_hMirrorButton->setFixedSize(buttonsize + menuPadding, buttonsize);
201 m_hMirrorButton->setIconSize(QSize(iconsize, iconsize));
202 m_hMirrorButton->setCheckable(true);
204 m_hMirrorButton->setDefaultAction(m_hMirrorAction);
206 m_hMirrorButton->setPopupMode(QToolButton::MenuButtonPopup);
207 m_hMirrorButton->setAutoRaise(true);
208
210 m_vMirrorButton->setFixedSize(buttonsize + menuPadding, buttonsize);
211 m_vMirrorButton->setIconSize(QSize(iconsize, iconsize));
212 m_vMirrorButton->setCheckable(true);
214 m_vMirrorButton->setDefaultAction(m_vMirrorAction);
216 m_vMirrorButton->setPopupMode(QToolButton::MenuButtonPopup);
217 m_vMirrorButton->setAutoRaise(true);
218
219 QAction *wrapAroundAction = m_viewManager->actionManager()->createAction("wrap_around_mode");
220
222 m_wrapAroundButton->setFixedSize(buttonsize, buttonsize);
223 m_wrapAroundButton->setIconSize(QSize(iconsize, iconsize));
224 m_wrapAroundButton->setDefaultAction(wrapAroundAction);
225 m_wrapAroundButton->setCheckable(true);
226 m_wrapAroundButton->setAutoRaise(true);
227
228 // add connections for horizontal and mirror buttons
229 connect(lockActionX, SIGNAL(toggled(bool)), this, SLOT(slotLockXMirrorToggle(bool)));
230 connect(lockActionY, SIGNAL(toggled(bool)), this, SLOT(slotLockYMirrorToggle(bool)));
231
232 connect(moveToCenterActionX, SIGNAL(triggered(bool)), this, SLOT(slotMoveToCenterMirrorX()));
233 connect(moveToCenterActionY, SIGNAL(triggered(bool)), this, SLOT(slotMoveToCenterMirrorY()));
234
235 connect(hideCanvasDecorationsX, SIGNAL(toggled(bool)), this, SLOT(slotHideDecorationMirrorX(bool)));
236 connect(hideCanvasDecorationsY, SIGNAL(toggled(bool)), this, SLOT(slotHideDecorationMirrorY(bool)));
237
238 const bool sliderLabels = cfg.sliderLabels();
239 int sliderWidth;
240
241 if (sliderLabels) {
242 sliderWidth = 150 * logicalDpiX() / 96;
243 }
244 else {
245 sliderWidth = 120 * logicalDpiX() / 96;
246 }
247
248 for (int i = 0; i < 5; ++i) {
249 m_sliderChooser[i] = new KisWidgetChooser(i + 1);
250
251 KisDoubleSliderSpinBox* slOpacity;
254 KisAngleSelector* slRotation;
256
257 if (sliderLabels) {
258 slOpacity = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("opacity");
261 slRotation = m_sliderChooser[i]->addWidget<KisAngleSelector>("rotation");
262 slPatternSize = m_sliderChooser[i]->addWidget<KisMultipliersDoubleSliderSpinBox>("patternsize");
263
265 i18nc("{n} is the number value, % is the percent sign", "Opacity: {n}%"));
267 i18nc("{n} is the number value, % is the percent sign", "Flow: {n}%"));
268 slSize->setPrefix(QString("%1 ").arg(i18n("Size:")));
269 slRotation->setPrefix(QString("%1 ").arg(i18n("Rotation:")));
270 slPatternSize->setPrefix(QString("%1 ").arg(i18n("Pattern Scale:")));
271 }
272 else {
273 slOpacity = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("opacity", i18n("Opacity:"));
274 slFlow = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("flow", i18n("Flow:"));
275 slSize = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("size", i18n("Size:"));
276 slRotation = m_sliderChooser[i]->addWidget<KisAngleSelector>("rotation", i18n("Rotation:"));
277 slPatternSize = m_sliderChooser[i]->addWidget<KisMultipliersDoubleSliderSpinBox>("patternsize", i18n("Pattern Scale:"));
278
279 KisSpinBoxI18nHelper::setText(slOpacity, i18nc("{n} is the number value, % is the percent sign", "{n}%"));
280 KisSpinBoxI18nHelper::setText(slFlow, i18nc("{n} is the number value, % is the percent sign", "{n}%"));
281 }
282
283 slOpacity->setRange(0, 100, 0);
284 slOpacity->setValue(100);
285 slOpacity->setSingleStep(5);
286 slOpacity->setMinimumWidth(qMax(sliderWidth, slOpacity->sizeHint().width()));
287 slOpacity->setFixedHeight(buttonsize);
288 slOpacity->setBlockUpdateSignalOnDrag(true);
289
290 slFlow->setRange(0, 100, 0);
291 slFlow->setValue(100);
292 slFlow->setSingleStep(5);
293 slFlow->setMinimumWidth(qMax(sliderWidth, slFlow->sizeHint().width()));
294 slFlow->setFixedHeight(buttonsize);
295 slFlow->setBlockUpdateSignalOnDrag(true);
296
297 slSize->setRange(0.01, KisImageConfig(true).maxBrushSize(), 2);
298 slSize->setValue(100);
299
300 slSize->setSingleStep(1);
301 slSize->setExponentRatio(3.0);
302 slSize->setSuffix(i18n(" px"));
303 slSize->setMinimumWidth(qMax(sliderWidth, slSize->sizeHint().width()));
304 slSize->setFixedHeight(buttonsize);
305 slSize->setBlockUpdateSignalOnDrag(true);
306
308 slRotation->setRange(-360.0, 360.0);
309 slRotation->setMinimumWidth(qMax(sliderWidth, slRotation->sizeHint().width()));
310 slRotation->setWidgetsHeight(buttonsize);
311
312 slPatternSize->setRange(0.0, 2.0, 2);
313 slPatternSize->setValue(1.0);
314 slPatternSize->addMultiplier(0.1);
315 slPatternSize->addMultiplier(2);
316 slPatternSize->addMultiplier(10);
317
318 slPatternSize->setSingleStep(0.01);
319 slPatternSize->setSuffix(i18n("x"));
320 slPatternSize->setMinimumWidth(qMax(sliderWidth, slPatternSize->sizeHint().width()));
321 slPatternSize->setFixedHeight(buttonsize);
322 slPatternSize->setBlockUpdateSignalOnDrag(true);
323
324 m_sliderChooser[i]->setMinimumWidth(qMax(sliderWidth, slPatternSize->sizeHint().width()));
325
327 }
328
330 m_cmbCompositeOp->setFixedHeight(buttonsize);
332
333 // Workspace Button
335 m_workspaceWidget->setIcon(KisIconUtils::loadIcon("workspace-chooser"));
336 m_workspaceWidget->setToolTip(i18n("Choose workspace"));
337 m_workspaceWidget->setFixedSize(buttonsize, buttonsize);
338 m_workspaceWidget->setIconSize(QSize(iconsize, iconsize));
339 KisWorkspaceChooser *workspacePopup = new KisWorkspaceChooser(viewManager);
340 m_workspaceWidget->setPopupWidget(workspacePopup);
341 m_workspaceWidget->setAutoRaise(true);
343
345 m_presetsChooserPopup->setMinimumHeight(550);
346 m_presetsChooserPopup->setMinimumWidth(450);
348
349 QHBoxLayout* baseLayout = new QHBoxLayout(this);
350 m_paintopWidget = new QWidget(this);
351 baseLayout->addWidget(m_paintopWidget);
352 baseLayout->setSpacing(4);
353 baseLayout->setContentsMargins(0, 0, 0, 0);
354
355 m_layout = new QHBoxLayout(m_paintopWidget);
356 if (!cfg.toolOptionsInDocker()) {
358 }
361 m_layout->setSpacing(4);
362 m_layout->setContentsMargins(0, 0, 0, 0);
363
364 QWidget* compositeActions = new QWidget(this);
365 QHBoxLayout* compositeLayout = new QHBoxLayout(compositeActions);
366 compositeLayout->addWidget(m_cmbCompositeOp);
367 compositeLayout->addWidget(m_eraseModeButton);
368 compositeLayout->addWidget(m_alphaLockButton);
369
370 compositeLayout->setSpacing(4);
371 compositeLayout->setContentsMargins(0, 0, 0, 0);
372
373 compositeLayout->addWidget(m_reloadButton);
374
375 QWidgetAction * action;
376
377 action = new QWidgetAction(this);
378 viewManager->actionCollection()->addAction("composite_actions", action);
379 action->setText(i18n("Brush composite"));
380 action->setDefaultWidget(compositeActions);
381
382 action = new QWidgetAction(this);
383 KisActionRegistry::instance()->propertizeAction("brushslider1", action);
384 viewManager->actionCollection()->addAction("brushslider1", action);
385 action->setDefaultWidget(m_sliderChooser[0]);
386 connect(action, SIGNAL(triggered()), m_sliderChooser[0], SLOT(showPopupWidget()));
387 connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[0], SLOT(updateThemedIcons()));
388
389 action = new QWidgetAction(this);
390 KisActionRegistry::instance()->propertizeAction("brushslider2", action);
391 viewManager->actionCollection()->addAction("brushslider2", action);
392 action->setDefaultWidget(m_sliderChooser[1]);
393 connect(action, SIGNAL(triggered()), m_sliderChooser[1], SLOT(showPopupWidget()));
394 connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[1], SLOT(updateThemedIcons()));
395
396 action = new QWidgetAction(this);
397 KisActionRegistry::instance()->propertizeAction("brushslider3", action);
398 viewManager->actionCollection()->addAction("brushslider3", action);
399 action->setDefaultWidget(m_sliderChooser[2]);
400 connect(action, SIGNAL(triggered()), m_sliderChooser[2], SLOT(showPopupWidget()));
401 connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[2], SLOT(updateThemedIcons()));
402
403 action = new QWidgetAction(this);
404 KisActionRegistry::instance()->propertizeAction("brushslider4", action);
405 viewManager->actionCollection()->addAction("brushslider4", action);
406 action->setDefaultWidget(m_sliderChooser[3]);
407 connect(action, SIGNAL(triggered()), m_sliderChooser[3], SLOT(showPopupWidget()));
408 connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[3], SLOT(updateThemedIcons()));
409
410 action = new QWidgetAction(this);
411 KisActionRegistry::instance()->propertizeAction("brushslider5", action);
412 viewManager->actionCollection()->addAction("brushslider5", action);
413 action->setDefaultWidget(m_sliderChooser[4]);
414 connect(action, SIGNAL(triggered()), m_sliderChooser[4], SLOT(showPopupWidget()));
415 connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[4], SLOT(updateThemedIcons()));
416
417 action = new QWidgetAction(this);
418 KisActionRegistry::instance()->propertizeAction("next_favorite_preset", action);
419 viewManager->actionCollection()->addAction("next_favorite_preset", action);
420 connect(action, SIGNAL(triggered()), this, SLOT(slotNextFavoritePreset()));
421
422 action = new QWidgetAction(this);
423 KisActionRegistry::instance()->propertizeAction("previous_favorite_preset", action);
424 viewManager->actionCollection()->addAction("previous_favorite_preset", action);
425 connect(action, SIGNAL(triggered()), this, SLOT(slotPreviousFavoritePreset()));
426
427 action = new QWidgetAction(this);
428 KisActionRegistry::instance()->propertizeAction("previous_preset", action);
429 viewManager->actionCollection()->addAction("previous_preset", action);
430 connect(action, SIGNAL(triggered()), this, SLOT(slotSwitchToPreviousPreset()));
431
432 if (!cfg.toolOptionsInDocker()) {
433 action = new QWidgetAction(this);
434 KisActionRegistry::instance()->propertizeAction("show_tool_options", action);
435 viewManager->actionCollection()->addAction("show_tool_options", action);
436 connect(action, SIGNAL(triggered()), m_toolOptionsPopupButton, SLOT(showPopupWidget()));
437 }
438
439
440 action = new QWidgetAction(this);
441 KisActionRegistry::instance()->propertizeAction("show_brush_presets", action);
442 viewManager->actionCollection()->addAction("show_brush_presets", action);
443 connect(action, SIGNAL(triggered()), m_presetSelectorPopupButton, SLOT(showPopupWidget()));
444 m_presetsChooserPopup->addAction(action);
445
446 QWidget* mirrorActions = new QWidget(this);
447 QHBoxLayout* mirrorLayout = new QHBoxLayout(mirrorActions);
448 mirrorLayout->addWidget(m_hMirrorButton);
449
450 mirrorLayout->addWidget(m_vMirrorButton);
451 mirrorLayout->addWidget(m_wrapAroundButton);
452 mirrorLayout->setSpacing(4);
453 mirrorLayout->setContentsMargins(0, 0, 0, 0);
454
455 action = new QWidgetAction(this);
456 KisActionRegistry::instance()->propertizeAction("mirror_actions", action);
457 action->setDefaultWidget(mirrorActions);
458 viewManager->actionCollection()->addAction("mirror_actions", action);
459
460 action = new QWidgetAction(this);
462 viewManager->actionCollection()->addAction(ResourceType::Workspaces, action);
463 action->setDefaultWidget(m_workspaceWidget);
464 connect(action, SIGNAL(triggered()), m_workspaceWidget, SLOT(showPopupWidget()));
465 workspacePopup->addAction(action);
466
467 if (!cfg.toolOptionsInDocker()) {
470 }
471
472
474
476 m_presetsEditor->setWindowTitle(i18n("Brush Editor"));
479
480 connect(m_presetsEditor, SIGNAL(brushEditorShown()), SLOT(slotUpdateOptionsWidgetPopup()));
481 connect(m_presetsEditor, SIGNAL(toggleDetachState(bool)), m_brushEditorPopupButton, SLOT(setPopupWidgetDetached(bool)));
482 connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_presetsEditor, SLOT(updateThemedIcons()));
483
484 action = new QWidgetAction(this);
485 KisActionRegistry::instance()->propertizeAction("show_brush_editor", action);
486 viewManager->actionCollection()->addAction("show_brush_editor", action);
487 connect(action, SIGNAL(toggled(bool)), this, SLOT(togglePresetEditor()));
488 m_presetsEditor->addAction(action);
489
491
492 slotNodeChanged(viewManager->activeNode());
493 // Get all the paintops
495 QList<KisPaintOpFactory*> factoryList;
496
497 Q_FOREACH (const QString & paintopId, keys) {
498 factoryList.append(KisPaintOpRegistry::instance()->get(paintopId));
499 }
500 m_presetsEditor->setPaintOpList(factoryList);
501
502 connect(m_presetsEditor , SIGNAL(paintopActivated(QString)) , SLOT(slotSetPaintop(QString)));
503 connect(m_presetsEditor , SIGNAL(defaultPresetClicked()) , SLOT(slotSetupDefaultPreset()));
504 connect(m_presetsEditor , SIGNAL(signalResourceSelected(KoResourceSP )), SLOT(resourceSelected(KoResourceSP )));
505 connect(m_presetsEditor , SIGNAL(reloadPresetClicked()) , SLOT(slotReloadPreset()));
506 connect(m_presetsEditor , SIGNAL(dirtyPresetToggled(bool)) , SLOT(slotDirtyPresetToggled(bool)));
507 connect(m_presetsEditor , SIGNAL(eraserBrushSizeToggled(bool)) , SLOT(slotEraserBrushSizeToggled(bool)));
508 connect(m_presetsEditor , SIGNAL(eraserBrushOpacityToggled(bool)) , SLOT(slotEraserBrushOpacityToggled(bool)));
509
510 connect(m_presetsEditor, SIGNAL(createPresetFromScratch(QString)), this, SLOT(slotCreatePresetFromScratch(QString)));
511
513 connect(m_presetsChooserPopup, SIGNAL(resourceClicked(KoResourceSP )) , SLOT(resourceSelected(KoResourceSP )));
514
515 connect(m_resourceProvider , SIGNAL(sigNodeChanged(KisNodeSP)) , SLOT(slotNodeChanged(KisNodeSP)));
516 connect(m_cmbCompositeOp , SIGNAL(currentIndexChanged(int)) , SLOT(slotSetCompositeMode(int)));
517 connect(m_eraseAction , SIGNAL(toggled(bool)) , SLOT(slotToggleEraseMode(bool)));
518 connect(m_eraserTogglePresetAction , SIGNAL(triggered(bool)) , SLOT(slotToggleEraserPreset(bool)));
519
520 connect(m_eraserSelectPresetAction , SIGNAL(triggered()) , SLOT(slotSelectEraserPreset()));
521 connect(m_brushSelectPresetAction , SIGNAL(triggered()) , SLOT(slotSelectBrushPreset()));
522
523 connect(alphaLockAction , SIGNAL(toggled(bool)) , SLOT(slotToggleAlphaLockMode(bool)));
524
526 connect(m_disablePressureAction , SIGNAL(toggled(bool)) , SLOT(slotDisablePressureMode(bool)));
527 m_disablePressureAction->setChecked(true);
528
529 connect(m_hMirrorAction , SIGNAL(toggled(bool)) , SLOT(slotHorizontalMirrorChanged(bool)));
530 connect(m_vMirrorAction , SIGNAL(toggled(bool)) , SLOT(slotVerticalMirrorChanged(bool)));
531 connect(m_reloadAction , SIGNAL(triggered()) , SLOT(slotReloadPreset()));
532
533 connect(m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("opacity") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
534 connect(m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
535 connect(m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
536 connect(m_sliderChooser[0]->getWidget<KisAngleSelector>("rotation") , SIGNAL(angleChanged(qreal)), SLOT(slotSlider1Changed()));
537 connect(m_sliderChooser[0]->getWidget<KisMultipliersDoubleSliderSpinBox>("patternsize"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
538 connect(m_sliderChooser[1]->getWidget<KisDoubleSliderSpinBox>("opacity") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
539 connect(m_sliderChooser[1]->getWidget<KisDoubleSliderSpinBox>("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
540 connect(m_sliderChooser[1]->getWidget<KisDoubleSliderSpinBox>("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
541 connect(m_sliderChooser[1]->getWidget<KisAngleSelector>("rotation") , SIGNAL(angleChanged(qreal)), SLOT(slotSlider2Changed()));
542 connect(m_sliderChooser[1]->getWidget<KisMultipliersDoubleSliderSpinBox>("patternsize"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
543 connect(m_sliderChooser[2]->getWidget<KisDoubleSliderSpinBox>("opacity") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
544 connect(m_sliderChooser[2]->getWidget<KisDoubleSliderSpinBox>("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
545 connect(m_sliderChooser[2]->getWidget<KisDoubleSliderSpinBox>("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
546 connect(m_sliderChooser[2]->getWidget<KisAngleSelector>("rotation") , SIGNAL(angleChanged(qreal)), SLOT(slotSlider3Changed()));
547 connect(m_sliderChooser[2]->getWidget<KisMultipliersDoubleSliderSpinBox>("patternsize"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
548 connect(m_sliderChooser[3]->getWidget<KisDoubleSliderSpinBox>("opacity") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider4Changed()));
549 connect(m_sliderChooser[3]->getWidget<KisDoubleSliderSpinBox>("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider4Changed()));
550 connect(m_sliderChooser[3]->getWidget<KisDoubleSliderSpinBox>("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider4Changed()));
551 connect(m_sliderChooser[3]->getWidget<KisAngleSelector>("rotation") , SIGNAL(angleChanged(qreal)), SLOT(slotSlider4Changed()));
552 connect(m_sliderChooser[3]->getWidget<KisMultipliersDoubleSliderSpinBox>("patternsize"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider4Changed()));
553 connect(m_sliderChooser[4]->getWidget<KisDoubleSliderSpinBox>("opacity") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider5Changed()));
554 connect(m_sliderChooser[4]->getWidget<KisDoubleSliderSpinBox>("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider5Changed()));
555 connect(m_sliderChooser[4]->getWidget<KisDoubleSliderSpinBox>("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider5Changed()));
556 connect(m_sliderChooser[4]->getWidget<KisAngleSelector>("rotation") , SIGNAL(angleChanged(qreal)), SLOT(slotSlider5Changed()));
557 connect(m_sliderChooser[4]->getWidget<KisMultipliersDoubleSliderSpinBox>("patternsize"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider5Changed()));
558
559 connect(m_resourceProvider, SIGNAL(sigFGColorUsed(KoColor)), m_favoriteResourceManager, SLOT(slotAddRecentColor(KoColor)));
560
561 connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotChangeFGColorSelector(KoColor)));
562 connect(m_resourceProvider, SIGNAL(sigBGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotSetBGColor(KoColor)));
563
564 // cold initialization
567
568 connect(m_favoriteResourceManager, SIGNAL(sigSetFGColor(KoColor)), m_resourceProvider, SLOT(slotSetFGColor(KoColor)));
569 connect(m_favoriteResourceManager, SIGNAL(sigSetBGColor(KoColor)), m_resourceProvider, SLOT(slotSetBGColor(KoColor)));
570
571 connect(viewManager->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateSelectionIcon()));
572 connect(m_resourceProvider->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
573 this, SLOT(slotCanvasResourceChanged(int,QVariant)));
574 connect(m_resourceProvider->resourceManager(), SIGNAL(canvasResourceChangeAttempted(int,QVariant)),
575 this, SLOT(slotCanvasResourceChangeAttempted(int,QVariant)));
576
577 connect(&m_optionWidgetUpdateCompressor, SIGNAL(timeout()), this, SLOT(slotUpdateOptionsWidgetPopup()));
578
579 slotInputDeviceChanged(KoToolManager::instance()->currentInputDevice());
580
581 m_brushSelectPresetAction->setChecked(true);
582}
583
584
586{
588
589 // Do not delete the widget, since it is global to the application, not owned by the view
591 qDeleteAll(m_paintopOptionWidgets);
593
594 delete toolbarMenuXMirror;
595 delete toolbarMenuYMirror;
596
597 for (int i = 0; i < 5; ++i)
598 {
599 delete m_sliderChooser[i];
600 }
601}
602
604{
605 KisPaintOpPresetSP preset = resource.dynamicCast<KisPaintOpPreset>();
606
607 if (preset) {
608 setCurrentPaintop(preset);
609
611 }
612}
613
615{
616 if (m_toolOptionsPopup) {
617 m_toolOptionsPopup->newOptionWidgets(optionWidgetList);
618 }
619}
620
622{
623 // This happens if no storages were available on startup
624 if (!m_optionWidget) {
625 KisPaintOpPresetSP preset = resource.dynamicCast<KisPaintOpPreset>();
626 setCurrentPaintop(preset);
627 return;
628 }
629
630 m_presetsEditor->setCreatingBrushFromScratch(false); // show normal UI elements when we are not creating
631
632 KisPaintOpPresetSP preset = resource.dynamicCast<KisPaintOpPreset>();
633
634 if (preset && preset->valid() && preset != m_resourceProvider->currentPreset()) {
637 Q_UNUSED(blocker);
638
640
641 if (!rserver->reloadResource(preset)) {
642 qWarning() << "failed to reload the preset.";
643 }
644 }
645
646 dbgResources << "resourceSelected: preset" << preset << (preset ? QString("%1").arg(preset->valid()) : "");
647 setCurrentPaintop(preset);
648
650 }
651}
652
654{
655 KisPaintOpPresetSP preset = activePreset(paintop);
656 setCurrentPaintop(preset);
657}
658
660{
661 if (preset == m_resourceProvider->currentPreset()) {
662 if (preset == m_tabletToolMap[m_currTabletToolID].preset) {
663 return;
664 }
665 }
666 Q_ASSERT(preset);
667 const KoID& paintop = preset->paintOp();
669
672
673 if (m_optionWidget) {
674 m_optionWidget->hide();
675 }
676
677 }
678
679 if (!m_paintopOptionWidgets.contains(paintop)) {
680 m_paintopOptionWidgets[paintop] =
682 ->createConfigWidget(this,
685 }
686
688
690
692
695
697 m_presetsEditor->readOptionSetting(preset->settings());
698
700
701
702
708 m_optionsWidgetConnections->connectForwardVoid(m_optionWidget, SIGNAL(sigConfigurationUpdated()), this, SLOT(slotGuiChangedCurrentPreset()));
709 m_optionsWidgetConnections->connectBackwardVoid(preset->updateProxy(), SIGNAL(sigSettingsChangedUncompressed()), &m_optionWidgetUpdateCompressor, SLOT(start()));
710
713
714 // load the current preset icon to the preset selector toolbar button
717
718
719 //o() << "\tsetting the new preset for" << m_currTabletToolID.uniqueID << "to" << preset->name();
721 m_tabletToolMap[m_currTabletToolID].preset = preset;
722 m_tabletToolMap[m_currTabletToolID].paintOpID = preset->paintOp();
723
724
725 if (m_presetsEditor->currentPaintOpId() != paintop.id()) {
726 // Must change the paintop as the current one is not supported
727 // by the new colorspace.
728 dbgKrita << "current paintop " << paintop.name() << " was not set, not supported by colorspace";
729 }
730
731 m_currCompositeOpID = preset->settings()->paintOpCompositeOp();
733
734 if (preset->settings()->hasPatternSettings()) {
735 setMultiplierSliderValue("patternsize", preset->settings()->paintOpPatternSize());
736 }
737
738 // MyPaint brushes don't support custom blend modes, they always perform "Normal and Erase" blending.
739 if (preset->paintOp().id() == "mypaintbrush") {
743 }
744 }
745 else {
747 }
748}
749
751{
753
754 // This happens when we have a new brush engine for which no default preset exists yet.
755 if (!preset) return;
756
757 // the connection to the preset is maintained even when the editor
758 // is hidden, so skip the updates
759 if (!m_presetsEditor->isVisible()) return;
760
763
764 m_presetsEditor->readOptionSetting(preset->settings());
767
768 // the m_viewManager->image() is set earlier, but the reference will be missing when the stamp button is pressed
769 // need to later do some research on how and when we should be using weak shared pointers (WSP) that creates this situation
771}
772
781
783{
784 QString path = ":/presets/" + paintOp.id() + ".kpp";
785
786 if (paintOp.id() == "mypaintbrush") {
787 path = ":/presets/" + paintOp.id() + ".myb";
788 }
789
790 dbgResources << "Getting default presets from qrc resources" << path;
791
792 KisPaintOpPresetSP preset(new KisPaintOpPreset(path));
793
794 if (!preset->load(KisGlobalResourcesInterface::instance())) {
795 bool success = false;
796
797 QFile file(path);
798
799 if (file.open(QIODevice::ReadOnly))
800 {
801 // this is needed specifically for MyPaint brushes
803 for (int i = 0; i < loaders.count(); i++) {
804 file.seek(0); // to ensure that one loader reading bytes won't interfere with another
805 KoResourceSP resource = loaders[i]->load(paintOp.id(), file, KisGlobalResourcesInterface::instance());
806 if (resource) {
807 preset = resource.dynamicCast<KisPaintOpPreset>();
808 success = !preset.isNull();
809 if (success) {
810 break;
811 }
812 }
813 }
814 file.close();
815 }
816
817 if (!success) {
819 }
820 }
821
822 Q_ASSERT(preset);
823 Q_ASSERT(preset->valid());
824
825 return preset;
826}
827
829{
830 if (m_paintOpPresetMap[paintOp] == 0) {
831 m_paintOpPresetMap[paintOp] = defaultPreset(paintOp);
832 }
833
834 return m_paintOpPresetMap[paintOp];
835}
836
837void KisPaintopBox::updateCompositeOp(QString compositeOpID)
838{
839 if (!m_optionWidget) return;
841
843
844 if (node && node->paintDevice()) {
845 if (!node->paintDevice()->compositionSourceColorSpace()->hasCompositeOp(compositeOpID)) {
847 }
848 }
849
850 {
852 m_cmbCompositeOp->selectCompositeOp(KoID(compositeOpID));
853 m_eraseModeButton->setChecked(compositeOpID == COMPOSITE_ERASE || m_resourceProvider->eraserMode());
854 }
855 m_currCompositeOpID = compositeOpID;
856}
857
859{
860 if (flags & (ENABLE_COMPOSITEOP | DISABLE_COMPOSITEOP)) {
861 m_cmbCompositeOp->setEnabled(flags & ENABLE_COMPOSITEOP);
862 }
863}
864
865void KisPaintopBox::setSliderValue(const QString& sliderID, qreal value)
866{
867 for (int i = 0; i < 5; ++i) {
869 KisSignalsBlocker b(slider);
870
871 if (sliderID == "opacity" || sliderID == "flow") { // opacity and flows UI stored at 0-100%
872 slider->setValue(value*100);
873 } else {
874 slider->setValue(value); // brush size
875 }
876
877
878 }
879}
880
881void KisPaintopBox::setMultiplierSliderValue(const QString& sliderID, qreal value)
882{
883 for (int i = 0; i < 5; ++i) {
885 if (!slider) continue;
886 KisSignalsBlocker b(slider);
887
888 slider->setValue(value); // brush pattern size
889 }
890}
891
892void KisPaintopBox::setAngleSliderValue(const QString& sliderID, qreal value)
893{
894 for (int i = 0; i < 5; ++i) {
896 if (!slider) continue;
897 KisSignalsBlocker b(slider);
898
899 slider->setAngle(value);
900 }
901}
902
903void KisPaintopBox::slotSetPaintop(const QString& paintOpId)
904{
905 if (KisPaintOpRegistry::instance()->get(paintOpId) != 0) {
906 KoID id(paintOpId, KisPaintOpRegistry::instance()->get(paintOpId)->name());
907 //qDebug() << "slotsetpaintop" << id;
909 }
910}
911
913{
914 TabletToolMap::iterator toolData = m_tabletToolMap.find(inputDevice);
915
916 //qDebug() << "slotInputDeviceChanged()" << inputDevice.device() << inputDevice.uniqueTabletId();
917
921
922 m_currTabletToolID = TabletToolID(inputDevice);
923
924 if (toolData == m_tabletToolMap.end()) {
925 KisConfig cfg(true);
927 KisPaintOpPresetSP preset;
928
930
931 if (inputDevice.pointer() == KoInputDevice::Pointer::Eraser) {
932 preset = rserver->resource("", "", cfg.readEntry<QString>(QString("LastEraser_%1").arg(inputDevice.uniqueTabletId()), m_eraserName));
933 }
934 else {
935 preset = rserver->resource("", "", cfg.readEntry<QString>(QString("LastPreset_%1").arg(inputDevice.uniqueTabletId()), m_defaultPresetName));
936// if (preset)
937// qDebug() << "found stored preset " << preset->name() << "for" << inputDevice.uniqueTabletId();
938// else
939// qDebug() << "no preset found for" << inputDevice.uniqueTabletId();
940 }
941 if (!preset) {
942 preset = rserver->resource("", "", m_defaultPresetName);
943 }
944 if (preset) {
945// qDebug() << "inputdevicechanged 1" << preset;
946 setCurrentPaintop(preset);
947 }
948 }
949 else {
950 if (toolData->preset) {
951 //qDebug() << "inputdevicechanged 2" << toolData->preset;
952 setCurrentPaintop(toolData->preset);
953 }
954 else {
955 //qDebug() << "inputdevicechanged 3" << toolData->paintOpID;
956 setCurrentPaintop(toolData->paintOpID);
957 }
958 }
959}
960
962{
965 qint64 id = -1;
966 const KoInputDevice inputDevice(dev, ptr, id);
967 slotInputDeviceChanged(inputDevice);
968}
969
971{
972 // automatically select freehand brush tool for these select actions
973 KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush");
974
977 qint64 id = -1;
978 const KoInputDevice inputDevice(dev, ptr, id);
979 slotInputDeviceChanged(inputDevice);
980}
981
982
984{
985 // automatically select freehand brush tool for these select actions
986 KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush");
987
990 qint64 id = -1;
991 const KoInputDevice inputDevice(dev, ptr, id);
992 slotInputDeviceChanged(inputDevice);
993}
994
995
997{
998 //First try to select an available default preset for that engine. If it doesn't exist, then
999 //manually set the engine to use a new preset.
1000 KoID id(paintop, KisPaintOpRegistry::instance()->get(paintop)->name());
1001 KisPaintOpPresetSP preset = defaultPreset(id);
1002
1003 slotSetPaintop(paintop); // change the paintop settings area and update the UI
1004
1005 if (!preset) {
1006 m_presetsEditor->setCreatingBrushFromScratch(true); // disable UI elements while creating from scratch
1008 } else {
1009 m_presetsEditor->readOptionSetting(preset->settings());
1011 }
1012 m_presetsEditor->resourceSelected(preset); // this helps update the UI on the brush editor
1013}
1014
1016{
1017 Q_UNUSED(value);
1018
1021 }
1022}
1023
1025{
1026 if (m_viewManager) {
1027 sender()->blockSignals(true);
1029 if (preset && m_resourceProvider->currentPreset()->name() != preset->name()) {
1030 QString compositeOp = preset->settings()->getString("CompositeOp");
1031 updateCompositeOp(compositeOp);
1032 resourceSelected(preset);
1033 }
1034
1041 }
1042
1046 }
1047 }
1048
1049 if (key == KoCanvasResource::Size) {
1051 }
1052
1055 }
1056
1057 if (key == KoCanvasResource::PatternSize) {
1059 }
1060
1061 if (key == KoCanvasResource::Opacity) {
1063 }
1064
1065 if (key == KoCanvasResource::Flow) {
1067 }
1068
1069 if (key == KoCanvasResource::EraserMode) {
1070 m_eraseAction->setChecked(value.toBool());
1071 }
1072
1074 m_disablePressureAction->setChecked(value.toBool());
1075 }
1076
1078 m_hMirrorAction->setChecked(value.toBool());
1079 }
1080
1082 m_vMirrorAction->setChecked(value.toBool());
1083 }
1084
1085 sender()->blockSignals(false);
1086 }
1087}
1088
1090{
1092
1093 m_presetsEditor->readOptionSetting(preset->settings());
1095
1096 // tell the brush editor that the resource has changed
1097 // so it can update everything
1099}
1100
1102{
1103 KisNodeSP previousNode(m_currentNode);
1104 if (previousNode && previousNode->paintDevice())
1105 disconnect(previousNode->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*)));
1106
1107 // Reconnect colorspace change of node
1108 if (node && node->paintDevice()) {
1109 connect(node->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*)));
1111 m_currentNode = node;
1113 }
1114
1115 if (m_optionWidget) {
1116 m_optionWidget->setNode(node);
1117 }
1118}
1119
1121{
1123 const KoColorSpace *compositionSpace = colorSpace;
1124 if (node && node->paintDevice()) {
1125 // Currently, composition source is enough to determine the available blending mode,
1126 // because either destination is the same (paint layers), or composition happens
1127 // in source space (masks).
1128 compositionSpace = node->paintDevice()->compositionSourceColorSpace();
1129 }
1130 m_cmbCompositeOp->validate(compositionSpace);
1131}
1132
1134{
1135 const bool oldEraserMode = m_resourceProvider->eraserMode();
1137
1138 if (oldEraserMode != checked && m_eraserBrushSizeEnabled) {
1139 const qreal currentSize = m_resourceProvider->size();
1140
1141 KisPaintOpSettingsSP settings = m_resourceProvider->currentPreset()->settings();
1142
1143 // remember brush size. set the eraser size to the normal brush size if not set
1144 if (checked) {
1145 settings->setSavedBrushSize(currentSize);
1146 if (qFuzzyIsNull(settings->savedEraserSize())) {
1147 settings->setSavedEraserSize(currentSize);
1148 }
1149 } else {
1150 settings->setSavedEraserSize(currentSize);
1151 if (qFuzzyIsNull(settings->savedBrushSize())) {
1152 settings->setSavedBrushSize(currentSize);
1153 }
1154 }
1155
1156 //update value in UI (this is the main place the value is 'stored' in memory)
1157 qreal newSize = checked ? settings->savedEraserSize() : settings->savedBrushSize();
1158 m_resourceProvider->setSize(newSize);
1159 }
1160 if (oldEraserMode != checked && m_eraserBrushOpacityEnabled) {
1161 const qreal currentOpacity = m_resourceProvider->opacity();
1162
1163 KisPaintOpSettingsSP settings = m_resourceProvider->currentPreset()->settings();
1164
1165 // remember brush opacity. set the eraser opacity to the normal brush opacity if not set
1166 if (checked) {
1167 settings->setSavedBrushOpacity(currentOpacity);
1168 if (qFuzzyIsNull(settings->savedEraserOpacity())) {
1169 settings->setSavedEraserOpacity(currentOpacity);
1170 }
1171 } else {
1172 settings->setSavedEraserOpacity(currentOpacity);
1173 if (qFuzzyIsNull(settings->savedBrushOpacity())) {
1174 settings->setSavedBrushOpacity(currentOpacity);
1175 }
1176 }
1177
1178 //update value in UI (this is the main place the value is 'stored' in memory)
1179 qreal newOpacity = checked ? settings->savedEraserOpacity() : settings->savedBrushOpacity();
1180 m_resourceProvider->setOpacity(newOpacity);
1181 }
1182}
1183
1185{
1186 Q_UNUSED(index);
1187 QString compositeOp = m_cmbCompositeOp->selectedCompositeOp().id();
1189}
1190
1195
1200
1202{
1203 if (!m_optionWidget) // widget will not exist if the are no documents open
1204 return;
1205
1206
1208
1209 // flow and opacity are shown as 0-100% on the UI, but their data is actually 0-1. Convert those two values
1210 // back for further work
1211 qreal opacity = m_sliderChooser[n]->getWidget<KisDoubleSliderSpinBox>("opacity")->value()/100;
1212 qreal flow = m_sliderChooser[n]->getWidget<KisDoubleSliderSpinBox>("flow")->value()/100;
1213 qreal size = m_sliderChooser[n]->getWidget<KisDoubleSliderSpinBox>("size")->value();
1214 qreal rotation = m_sliderChooser[n]->getWidget<KisAngleSelector>("rotation")->angle();
1215 qreal patternsize = m_sliderChooser[n]->getWidget<KisMultipliersDoubleSliderSpinBox>("patternsize")->value();
1216
1217
1218 setSliderValue("opacity", opacity);
1219 setSliderValue("flow" , flow);
1220 setSliderValue("size" , size);
1221 setAngleSliderValue("rotation", rotation);
1222 setMultiplierSliderValue("patternsize", patternsize);
1223
1224 if (m_presetsEnabled) {
1225 // IMPORTANT: set the PaintOp size before setting the other properties
1226 // it won't work the other way
1227 // TODO: why?!
1228
1231 m_resourceProvider->setPatternSize(patternsize);
1234
1235
1237 propertiesProxy->setProperty("FlowValue", flow);
1238 propertiesProxy->setProperty("Texture/Pattern/Scale", patternsize);
1240 } else {
1242 }
1243
1245}
1246
1251
1256
1261
1266
1271
1273{
1274 Q_UNUSED(canvas);
1275
1276 if (!m_viewManager->canvasBase()) return;
1277
1278 QString id = KoToolManager::instance()->activeToolId();
1279 KisTool* tool = dynamic_cast<KisTool*>(KoToolManager::instance()->toolById(m_viewManager->canvasBase(), id));
1280
1282
1283 if (tool) {
1284 int flags = tool->flags();
1285
1287 if (!m_resourceProvider->currentPreset()) return;
1288
1289 // block updates of avoid some over updating of the option widget
1290 m_blockUpdate = true;
1291
1294
1295 {
1296 setSliderValue("flow", m_resourceProvider->currentPreset()->settings()->paintOpFlow());
1297 }
1298
1299 {
1300 setMultiplierSliderValue("patternsize", m_resourceProvider->currentPreset()->settings()->paintOpPatternSize());
1301 }
1302
1303 // MyPaint brushes don't support custom blend modes, they always perform "Normal and Erase" blending.
1304 if (m_resourceProvider->currentPreset()->paintOp().id() == "mypaintbrush") {
1307 }
1308 } else {
1309 updateCompositeOp(m_resourceProvider->currentPreset()->settings()->paintOpCompositeOp());
1310 }
1311
1312 m_blockUpdate = false;
1313
1314 m_presetsEnabled = true;
1315 } else {
1316 m_presetsEnabled = false;
1317 }
1318 }
1319}
1320
1322{
1323 if (!m_favoriteResourceManager) return;
1324
1326 for (int i=0; i < presets.size(); ++i) {
1328 m_resourceProvider->currentPreset()->name() == presets[i]) {
1329 if (i > 0) {
1331 } else {
1333 }
1334 //floating message should have least 2 lines, otherwise
1335 //preset thumbnail will be too small to distinguish
1336 //(because size of image on floating message depends on amount of lines in msg)
1338 i18n("%1\nselected",
1340 QIcon(QPixmap::fromImage(m_resourceProvider->currentPreset()->image())));
1341
1342 return;
1343 }
1344 }
1345}
1346
1348{
1349 if (!m_favoriteResourceManager) return;
1350
1352 for(int i = 0; i < presets.size(); ++i) {
1353 if (m_resourceProvider->currentPreset()->name() == presets[i]) {
1354 if (i < m_favoriteResourceManager->numFavoritePresets() - 1) {
1356 } else {
1358 }
1360 i18n("%1\nselected",
1362 QIcon(QPixmap::fromImage(m_resourceProvider->currentPreset()->image())));
1363
1364 return;
1365 }
1366 }
1367}
1368
1370{
1374 i18n("%1\nselected",
1376 QIcon(QPixmap::fromImage(m_resourceProvider->currentPreset()->image())));
1377 }
1378}
1379
1381{
1382 m_eraseAction->setChecked(false);
1383}
1384
1386{
1387 if (checked) {
1388 m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("bar-transparency-locked"));
1389 } else {
1390 m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("bar-transparency-unlocked"));
1391 }
1393}
1394
1396{
1397 if (checked) {
1398 m_disablePressureAction->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
1399 } else {
1400 m_disablePressureAction->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure_locked"));
1401 }
1402
1404}
1405
1407{
1409
1412
1413 // Presets that just have been created cannot be reloaded.
1414 if (preset && preset->resourceId() > -1) {
1415 const bool result = rserver->reloadResource(preset);
1416 KIS_SAFE_ASSERT_RECOVER_NOOP(result && "couldn't reload preset");
1417 }
1418}
1419void KisPaintopBox::slotGuiChangedCurrentPreset() // Called only when UI is changed and not when preset is changed
1420{
1422
1423 {
1430 KisPaintOpPreset::UpdatedPostponer postponer(preset);
1431
1432 // clear all the properties before dumping the stuff into the preset,
1433 // some of the options add the values incrementally
1434 // (e.g. KisPaintOpUtils::RequiredBrushFilesListTag), therefore they
1435 // may add up if we pass the same preset multiple times
1436 preset->settings()->resetSettings();
1437
1438 m_presetsEditor->writeOptionSetting(const_cast<KisPaintOpSettings*>(preset->settings().data()));
1439 }
1440
1441 // we should also update the preset strip to update the status of the "dirty" mark
1443
1444 // TODO!!!!!!!!
1445 //m_presetsPopup->updateViewSettings();
1446}
1448{
1450 KisPaintOpPreset::UpdatedPostponer postponer(preset);
1451
1452
1453 QMapIterator<QString, QVariant> i(p->getProperties());
1454 while (i.hasNext()) {
1455 i.next();
1456
1457 preset->settings()->setProperty(i.key(), QVariant(i.value()));
1458 if (preset->settings()->hasProperty(i.key() + "_previous")) {
1459 preset->settings()->removeProperty(i.key() + "_previous");
1460 }
1461
1462 }
1463
1466 preset->setDirty(true);
1467}
1468
1470{
1473
1474 {
1475 KisPaintOpPreset::UpdatedPostponer postponer(preset);
1476 KisDirtyStateSaver<KisPaintOpPresetSP> dirtySaver(preset);
1477
1478 QMapIterator<QString, QVariant> i(p->getProperties());
1479 while (i.hasNext()) {
1480 i.next();
1481 if (preset->settings()->hasProperty(i.key() + "_previous")) {
1482 preset->settings()->setProperty(i.key(), preset->settings()->getProperty(i.key() + "_previous"));
1483 preset->settings()->removeProperty(i.key() + "_previous");
1484 }
1485
1486 }
1487 }
1488}
1500
1507
1514
1516{
1517 KisConfig cfg(true);
1519 m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("configure"));
1520 }
1521
1522 m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01"));
1523 m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02"));
1524 m_workspaceWidget->setIcon(KisIconUtils::loadIcon("workspace-chooser"));
1525
1526 if (m_disablePressureAction->isChecked()) {
1527 m_disablePressureAction->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
1528 } else {
1529 m_disablePressureAction->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure_locked"));
1530 }
1531}
1532
1536
1540
1544
1548
1552
1556
1558{
1559 m_eraserName = "a) Eraser Circle";
1560 m_defaultPresetName = "b) Basic-5 Size Opacity";
1561}
1562
1564{
1565 KisConfig cfg(false);
1566 QMapIterator<TabletToolID, TabletToolData> iter(m_tabletToolMap);
1567 while (iter.hasNext()) {
1568 iter.next();
1569 if ((iter.key().pointer) == KoInputDevice::Pointer::Eraser) {
1570 cfg.writeEntry(QString("LastEraser_%1").arg(iter.key().uniqueTabletId),
1571 iter.value().preset->name());
1572 }
1573 else {
1574 cfg.writeEntry(QString("LastPreset_%1").arg(iter.key().uniqueTabletId),
1575 iter.value().preset->name());
1576 }
1577 }
1578}
1579
float value(const T *src, size_t ch)
const Params2D p
VertexDescriptor get(PredecessorMap const &m, VertexDescriptor v)
const QString COMPOSITE_OVER
const QString COMPOSITE_ERASE
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
KisAction * createAction(const QString &name)
bool propertizeAction(const QString &name, QAction *a)
static KisActionRegistry * instance()
A widget with several options to select an angle.
@ FlipOptionsMode_MenuButton
The flip options are shown as a menu accessible via a options button.
void setPrefix(const QString &newPrefix)
Sets the prefix shown in the spin box.
void setFlipOptionsMode(FlipOptionsMode newMode)
Sets the mode in which the flip options should be shown.
void setAngle(qreal newAngle)
Sets the current angle.
void setRange(qreal newMinimum, qreal newMaximum)
Sets the minimum and maximum values for the angle.
void setWidgetsHeight(int newHeight)
Sets the common height of the widgets inside this angle selector. Use 0 to reset widgets to default h...
KoCanvasResourceProvider * resourceManager()
KisPaintOpPresetSP currentPreset() const
KisPaintOpPresetSP previousPreset() const
void setPreviousPaintOpPreset(const KisPaintOpPresetSP preset)
void setMirrorVertical(bool mirrorVertical)
void setMirrorHorizontal(bool mirrorHorizontal)
void setPaintOpPreset(const KisPaintOpPresetSP preset)
void setCurrentCompositeOp(const QString &compositeOp)
void validate(const KoColorSpace *cs)
void selectCompositeOp(const KoID &op)
void connectBlendmodeActions(KisActionManager *manager)
void setUseEraserBrushOpacity(bool value)
QString toolbarSlider(int sliderNumber, bool defaultValue=false) const
bool useDirtyPresets(bool defaultValue=false) const
void writeEntry(const QString &name, const T &value)
Definition kis_config.h:779
bool paintopPopupDetached(bool defaultValue=false) const
bool useEraserBrushOpacity(bool defaultValue=false) const
bool sliderLabels(bool defaultValue=false) const
bool toolOptionsInDocker(bool defaultValue=false) const
T readEntry(const QString &name, const T &defaultValue=T())
Definition kis_config.h:789
void setUseEraserBrushSize(bool value)
bool useEraserBrushSize(bool defaultValue=false) const
void setUseDirtyPresets(bool value)
This class is a spinbox in which you can click and drag to set the value. A slider like bar is displa...
void setValue(qreal newValue)
void setRange(qreal newMinimum, qreal newMaximum, int newNumberOfDecimals=0, bool computeNewFastSliderStep=true)
Set the minimum and the maximum values of the range.
void setBlockUpdateSignalOnDrag(bool newBlockUpdateSignalOnDrag)
void setExponentRatio(qreal newExponentRatio)
QSize sizeHint() const override
static KisResourcesInterfaceSP instance()
void setThumbnail(const QImage &thumbnail)
Q_INVOKABLE QAction * addAction(const QString &name, QAction *action)
static KisLockedPropertiesServer * instance()
KisLockedPropertiesProxySP createLockedPropertiesProxy(KisPropertiesConfiguration *settings)
void setRange(qreal minimum, qreal maximum, int decimals=0)
void setValue(qreal value)
Set the value, don't use setValue()
virtual const KoColorSpace * compositionSourceColorSpace() const
const KoColorSpace * colorSpace() const
virtual void setImage(KisImageWSP image)
virtual void setNode(KisNodeWSP node)
virtual KisPaintOpConfigWidget * createConfigWidget(QWidget *parent, KisResourcesInterfaceSP resourcesInterface, KoCanvasResourcesInterfaceSP canvasResourcesInterface)=0
void canvasResourceChanged(KisPaintOpPresetSP preset)
void setPaintOpList(const QList< KisPaintOpFactory * > &list)
void writeOptionSetting(KisPropertiesConfigurationSP setting) const
QString currentPaintOpId()
returns the internal ID for the paint op (brush engine)
void resourceSelected(KoResourceSP resource)
void setCurrentPaintOpId(const QString &paintOpId)
void currentPresetChanged(KisPaintOpPresetSP preset)
void readOptionSetting(const KisPropertiesConfigurationSP setting)
void setPaintOpSettingsWidget(QWidget *widget)
static KisPaintOpRegistry * instance()
KisPaintOpPresetSP defaultPreset(const KoID &id, KisResourcesInterfaceSP resourcesInterface) const
~KisPaintopBox() override
QWidget * m_paintopWidget
QToolButton * m_hMirrorButton
void slotNextFavoritePreset()
QToolButton * m_wrapAroundButton
KisPaintOpPresetsChooserPopup * m_presetsChooserPopup
void slotToolChanged(KoCanvasController *canvas)
QString m_defaultPresetName
KisAction * lockActionY
void slotToggleEraseMode(bool checked)
void setSliderValue(const QString &sliderID, qreal value)
void newOptionWidgets(const QList< QPointer< QWidget > > &optionWidgetList)
KisSignalAutoConnectionsStore m_presetConnections
void slotSwitchToPreviousPreset()
KisAction * m_vMirrorAction
KisPaintopBox(KisViewManager *view, QWidget *parent, const char *name)
QHBoxLayout * m_layout
void setAngleSliderValue(const QString &sliderID, qreal value)
void slotHideDecorationMirrorY(bool)
void slotCanvasResourceChangeAttempted(int key, const QVariant &value)
bool m_eraserBrushSizeEnabled
void slotColorSpaceChanged(const KoColorSpace *colorSpace)
QToolButton * m_reloadButton
void slotInputDeviceChanged(const KoInputDevice &inputDevice)
KisAction * lockActionX
void slotDisablePressureMode(bool)
KisAction * m_disablePressureAction
QMenu * toolbarMenuXMirror
KisAction * moveToCenterActionY
void updateCompositeOp(QString compositeOpID)
KisIconWidget * m_brushEditorPopupButton
KisSignalCompressor m_optionWidgetUpdateCompressor
KisAction * m_brushSelectPresetAction
KisAction * moveToCenterActionX
QMenu * toolbarMenuYMirror
void slotMoveToCenterMirrorX()
void restoreResource(KoResourceSP resource)
KisWidgetChooser * m_sliderChooser[5]
KisAction * m_eraserSelectPresetAction
TabletToolMap m_tabletToolMap
KisToolOptionsPopup * m_toolOptionsPopup
void slotMoveToCenterMirrorY()
void slotVerticalMirrorChanged(bool value)
KisCompositeOpComboBox * m_cmbCompositeOp
KisPaintOpPresetSP defaultPreset(const KoID &paintOp)
void slotPreviousFavoritePreset()
void slotSetupDefaultPreset()
void slotLockYMirrorToggle(bool)
void slotUpdateSelectionIcon()
KisAction * hideCanvasDecorationsX
KisPaintOpPresetsEditor * m_presetsEditor
void slotSetPaintop(const QString &paintOpId)
PaintOpPresetMap m_paintOpPresetMap
TabletToolID m_currTabletToolID
KisPaintOpConfigWidget * m_optionWidget
void slotUpdateOptionsWidgetPopup()
void slotEraserBrushOpacityToggled(bool)
QToolButton * m_eraseModeButton
QMap< KoID, KisPaintOpConfigWidget * > m_paintopOptionWidgets
QString m_currCompositeOpID
void resourceSelected(KoResourceSP resource)
KisAction * hideCanvasDecorationsY
void slotSaveLockedOptionToPreset(KisPropertiesConfigurationSP p)
void slotGuiChangedCurrentPreset()
void slotDropLockedOption(KisPropertiesConfigurationSP p)
void sliderChanged(int n)
KisNodeWSP m_currentNode
KisViewManager * m_viewManager
KisPaintOpPresetSP activePreset(const KoID &paintOp)
void slotNodeChanged(const KisNodeSP node)
KisFavoriteResourceManager * m_favoriteResourceManager
void slotCanvasResourceChanged(int key, const QVariant &v)
KisAction * m_eraserTogglePresetAction
KisPopupButton * m_workspaceWidget
void slotCreatePresetFromScratch(QString paintop)
KisCanvasResourceProvider * m_resourceProvider
KisIconWidget * m_presetSelectorPopupButton
QToolButton * m_alphaLockButton
void slotToggleAlphaLockMode(bool)
void setWidgetState(int flags)
void slotSetCompositeMode(int index)
void setCurrentPaintop(const KoID &paintop)
void slotHorizontalMirrorChanged(bool value)
void slotLockXMirrorToggle(bool)
QToolButton * m_vMirrorButton
KisAction * m_reloadAction
KisAction * m_hMirrorAction
void slotSelectBrushPreset()
void slotEraserBrushSizeToggled(bool)
KisPopupButton * m_toolOptionsPopupButton
QScopedPointer< KisAcyclicSignalConnector > m_optionsWidgetConnections
void slotToggleEraserPreset(bool usingEraser)
void setMultiplierSliderValue(const QString &sliderID, qreal value)
bool m_eraserBrushOpacityEnabled
KisPresetSaveWidget * m_savePresetWidget
KisAction * m_eraseAction
void slotSelectEraserPreset()
void slotDirtyPresetToggled(bool)
void slotHideDecorationMirrorX(bool)
void setPopupWidgetDetached(bool detach)
void setPopupWidget(QWidget *widget)
void setArrowVisible(bool v)
static KisResourceLoaderRegistry * instance()
QVector< KisResourceLoaderBase * > resourceTypeLoaders(const QString &resourceType) const
static KisResourceServerProvider * instance()
KisPaintOpPresetResourceServer * paintOpPresetServer()
void addConnection(Sender sender, Signal signal, Receiver receiver, Method method, Qt::ConnectionType type=Qt::AutoConnection)
void newOptionWidgets(const QList< QPointer< QWidget > > &optionWidgetList)
KisMainWindow * mainWindow() const
KisActionManager * actionManager() const
KisCanvas2 * canvasBase() const
Return the canvas base class.
KisNodeSP activeNode()
virtual KisKActionCollection * actionCollection() const
KisImageWSP image() const
Return the image this view is displaying.
KisCanvasResourceProvider * canvasResourceProvider()
void showFloatingMessage(const QString &message, const QIcon &icon, int timeout=4500, KisFloatingMessage::Priority priority=KisFloatingMessage::Medium, int alignment=Qt::AlignCenter|Qt::TextWordWrap)
shows a floating message in the top right corner of the canvas
TWidget * addWidget(const QString &id, const QString &label="")
QWidget * getWidget(const QString &id) const
QWidget * chooseWidget(const QString &id)
KoCanvasResourcesInterfaceSP canvasResourcesInterface() const
virtual bool hasCompositeOp(const QString &id, const KoColorSpace *srcSpace=nullptr) const
static const KoCompositeOpRegistry & instance()
T get(const QString &id) const
QList< QString > keys() const
Definition KoID.h:30
QString name() const
Definition KoID.cpp:68
QString id() const
Definition KoID.cpp:63
KoInputDevice::Pointer pointer
QSharedPointer< T > resource(const QString &md5, const QString &fileName, const QString &name)
resource retrieves a resource. If the md5sum is not empty, the resource will only be retrieved if a r...
bool reloadResource(QSharedPointer< T > resource)
KoToolBase * toolById(KoCanvasBase *canvas, const QString &id) const
void switchToolRequested(const QString &id)
QString activeToolId() const
Returns the toolId of the currently active tool.
static KoToolManager * instance()
Return the toolmanager singleton.
static bool qFuzzyIsNull(half h)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define dbgKrita
Definition kis_debug.h:45
#define dbgResources
Definition kis_debug.h:43
QIcon loadIcon(const QString &name)
void setText(QSpinBox *spinBox, const QStringView textTemplate)
@ ForegroundColor
The active foreground color selected for this canvas.
const QString Workspaces
const QString PaintOpPresets
virtual KisPaintDeviceSP paintDevice() const =0
virtual int flags() const
Definition kis_tool.h:52
@ FLAG_USES_CUSTOM_PRESET
Definition kis_tool.h:47