diff --git a/libs/widgets/KisDlgInternalColorSelector.cpp b/libs/widgets/KisDlgInternalColorSelector.cpp index 8e25dc6d4d..7ed2111267 100644 --- a/libs/widgets/KisDlgInternalColorSelector.cpp +++ b/libs/widgets/KisDlgInternalColorSelector.cpp @@ -1,359 +1,359 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "KoColorSpaceRegistry.h" #include #include #include #include #include #include #include "kis_signal_compressor.h" #include "KoColorDisplayRendererInterface.h" #include "kis_spinbox_color_selector.h" #include "KisDlgInternalColorSelector.h" #include "ui_WdgDlgInternalColorSelector.h" #include "kis_config_notifier.h" #include "kis_color_input.h" #include "kis_icon_utils.h" #include "KisSqueezedComboBox.h" #include "ui_WdgDlgInternalColorSelector.h" std::function KisDlgInternalColorSelector::s_screenColorPickerFactory = 0; struct KisDlgInternalColorSelector::Private { bool allowUpdates = true; KoColor currentColor; KoColor previousColor; KoColor sRGB = KoColor(KoColorSpaceRegistry::instance()->rgb8()); const KoColorSpace *currentColorSpace; bool lockUsedCS = false; bool chooseAlpha = false; KisSignalCompressor *compressColorChanges; const KoColorDisplayRendererInterface *displayRenderer; KisHexColorInput *hexColorInput = 0; KisPaletteModel *paletteModel = 0; KisPaletteListWidget *paletteChooser = 0; KisScreenColorPickerBase *screenColorPicker = 0; }; KisDlgInternalColorSelector::KisDlgInternalColorSelector(QWidget *parent, KoColor color, Config config, const QString &caption, const KoColorDisplayRendererInterface *displayRenderer) : KisPopover(parent, KisPopover::DOWN) , m_d(new Private) { setModal(config.modal); setFocusPolicy(Qt::ClickFocus); QWidget *panelContents = new QWidget(this); m_ui = new Ui_WdgDlgInternalColorSelector(); m_ui->setupUi(panelContents); this->layout()->addWidget(panelContents); + this->setAcceptable(false); setWindowTitle(caption); - setHeaderText(caption); m_d->currentColor = color; m_d->currentColorSpace = m_d->currentColor.colorSpace(); m_d->displayRenderer = displayRenderer; m_ui->spinboxselector->slotSetColor(color); connect(m_ui->spinboxselector, SIGNAL(sigNewColor(KoColor)), this, SLOT(slotColorUpdated(KoColor))); m_ui->visualSelector->slotSetColor(color); m_ui->visualSelector->setDisplayRenderer(displayRenderer); m_ui->visualSelector->setConfig(false, config.modal); if (config.visualColorSelector) { connect(m_ui->visualSelector, SIGNAL(sigNewColor(KoColor)), this, SLOT(slotColorUpdated(KoColor))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), m_ui->visualSelector, SLOT(configurationChanged())); } else { m_ui->visualSelector->hide(); } m_d->paletteChooser = new KisPaletteListWidget(panelContents); m_d->paletteModel = new KisPaletteModel(panelContents); m_ui->bnPaletteChooser->setIcon(KisIconUtils::loadIcon("hi16-palette_library")); m_ui->paletteBox->setPaletteModel(m_d->paletteModel); m_ui->paletteBox->setDisplayRenderer(displayRenderer); m_ui->cmbNameList->setCompanionView(m_ui->paletteBox); connect(m_d->paletteChooser, SIGNAL(sigPaletteSelected(KoColorSet*)), this, SLOT(slotChangePalette(KoColorSet*))); connect(m_ui->cmbNameList, SIGNAL(sigColorSelected(KoColor)), SLOT(slotColorUpdated(KoColor))); // For some bizare reason, the modal dialog doesn't like having the colorset set, so let's not. if (config.paletteBox) { //TODO: Add disable signal as well. Might be not necessary...? KConfigGroup cfg(KSharedConfig::openConfig()->group("")); QString paletteName = cfg.readEntry("internal_selector_active_color_set", QString()); KoResourceServer* rServer = KoResourceServerProvider::instance()->paletteServer(); KoColorSet *savedPal = rServer->resourceByName(paletteName); if (savedPal) { this->slotChangePalette(savedPal); } else { if (rServer->resources().count()) { savedPal = rServer->resources().first(); if (savedPal) { this->slotChangePalette(savedPal); } } } connect(m_ui->paletteBox, SIGNAL(sigColorSelected(KoColor)), this, SLOT(slotColorUpdated(KoColor))); m_ui->bnPaletteChooser->setPopupWidget(m_d->paletteChooser); } else { m_ui->paletteBox->setEnabled(false); m_ui->cmbNameList->setEnabled(false); m_ui->bnPaletteChooser->setEnabled(false); } if (config.prevNextButtons) { m_ui->currentColor->setColor(m_d->currentColor); m_ui->currentColor->setDisplayRenderer(displayRenderer); m_ui->previousColor->setColor(m_d->currentColor); m_ui->previousColor->setDisplayRenderer(displayRenderer); connect(m_ui->previousColor, SIGNAL(triggered(KoColorPatch*)), SLOT(slotSetColorFromPatch(KoColorPatch*))); } else { m_ui->currentColor->hide(); m_ui->previousColor->hide(); } if (config.hexInput) { m_d->sRGB.fromKoColor(m_d->currentColor); m_d->hexColorInput = new KisHexColorInput(panelContents, &m_d->sRGB); m_d->hexColorInput->update(); connect(m_d->hexColorInput, SIGNAL(updated()), SLOT(slotSetColorFromHex())); m_ui->rightPane->addWidget(m_d->hexColorInput); m_d->hexColorInput->setToolTip(i18n("This is a hexcode input, for webcolors. It can only get colors in the sRGB space.")); } // KisScreenColorPicker is in the kritaui module, so dependency inversion is used to access it. m_ui->screenColorPickerWidget->setLayout(new QHBoxLayout(m_ui->screenColorPickerWidget)); if (s_screenColorPickerFactory) { m_d->screenColorPicker = s_screenColorPickerFactory(m_ui->screenColorPickerWidget); m_ui->screenColorPickerWidget->layout()->addWidget(m_d->screenColorPicker); if (config.screenColorPicker) { connect(m_d->screenColorPicker, SIGNAL(sigNewColorPicked(KoColor)),this, SLOT(slotColorUpdated(KoColor))); } else { m_d->screenColorPicker->hide(); } } m_d->compressColorChanges = new KisSignalCompressor(100 /* ms */, KisSignalCompressor::POSTPONE, this); connect(m_d->compressColorChanges, SIGNAL(timeout()), this, SLOT(endUpdateWithNewColor())); connect(this, SIGNAL(finished(int)), SLOT(slotFinishUp())); } KisDlgInternalColorSelector::~KisDlgInternalColorSelector() { delete m_ui; } void KisDlgInternalColorSelector::slotColorUpdated(KoColor newColor) { // not-so-nice solution: if someone calls this slot directly and that code was // triggered by our compressor signal, our compressor is technically the sender()! if (sender() == m_d->compressColorChanges) { return; } // Do not accept external updates while a color update emit is pending; // Note: Assumes external updates only come from parent(), a separate slot might be better if (m_d->allowUpdates || (QObject::sender() && QObject::sender() != this->parent())) { // Enforce palette colors KConfigGroup group(KSharedConfig::openConfig(), ""); if (group.readEntry("colorsettings/forcepalettecolors", false)) { newColor = m_ui->paletteBox->closestColor(newColor); } if (m_d->lockUsedCS){ newColor.convertTo(m_d->currentColorSpace); m_d->currentColor = newColor; } else { m_d->currentColor = newColor; } updateAllElements(QObject::sender()); } } void KisDlgInternalColorSelector::slotSetColorFromPatch(KoColorPatch *patch) { slotColorUpdated(patch->color()); } void KisDlgInternalColorSelector::colorSpaceChanged(const KoColorSpace *cs) { if (cs == m_d->currentColorSpace) { return; } m_d->currentColorSpace = KoColorSpaceRegistry::instance()->colorSpace(cs->colorModelId().id(), cs->colorDepthId().id(), cs->profile()); m_ui->spinboxselector->slotSetColorSpace(m_d->currentColorSpace); m_ui->visualSelector->slotsetColorSpace(m_d->currentColorSpace); } void KisDlgInternalColorSelector::lockUsedColorSpace(const KoColorSpace *cs) { colorSpaceChanged(cs); if (m_d->currentColor.colorSpace() != m_d->currentColorSpace) { m_d->currentColor.convertTo(m_d->currentColorSpace); } m_d->lockUsedCS = true; } void KisDlgInternalColorSelector::setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer) { if (displayRenderer) { m_d->displayRenderer = displayRenderer; m_ui->visualSelector->setDisplayRenderer(displayRenderer); m_ui->currentColor->setDisplayRenderer(displayRenderer); m_ui->previousColor->setDisplayRenderer(displayRenderer); m_ui->paletteBox->setDisplayRenderer(displayRenderer); } else { m_d->displayRenderer = KoDumbColorDisplayRenderer::instance(); } } KoColor KisDlgInternalColorSelector::getModalColorDialog(const KoColor color, QWidget* parent, QString caption, QPoint position) { Config config = Config(); KisDlgInternalColorSelector dialog(parent, color, config, caption); dialog.setPreviousColor(color); dialog.setOrigin(position); dialog.exec(); return dialog.getCurrentColor(); } KoColor KisDlgInternalColorSelector::getCurrentColor() { return m_d->currentColor; } void KisDlgInternalColorSelector::chooseAlpha(bool chooseAlpha) { m_d->chooseAlpha = chooseAlpha; } void KisDlgInternalColorSelector::slotConfigurationChanged() { //m_d->canvas->displayColorConverter()-> //slotColorSpaceChanged(m_d->canvas->image()->colorSpace()); } //void KisDlgInternalColorSelector::slotLockSelector() //{ // m_d->allowUpdates = false; //} void KisDlgInternalColorSelector::setPreviousColor(KoColor c){ m_d->previousColor = c; } void KisDlgInternalColorSelector::reject(){ slotColorUpdated(m_d->previousColor); QDialog::reject(); } void KisDlgInternalColorSelector::updateAllElements(QObject *source) { if (source != m_ui->spinboxselector) { m_ui->spinboxselector->slotSetColor(m_d->currentColor); } if (source != m_ui->visualSelector) { m_ui->visualSelector->slotSetColor(m_d->currentColor); } if (source != m_d->hexColorInput) { m_d->sRGB.fromKoColor(m_d->currentColor); m_d->hexColorInput->update(); } if (source != m_ui->paletteBox) { m_ui->paletteBox->selectClosestColor(m_d->currentColor); } m_ui->previousColor->setColor(m_d->previousColor); m_ui->currentColor->setColor(m_d->currentColor); if (source && source != this->parent()) { m_d->allowUpdates = false; m_d->compressColorChanges->start(); } if (m_d->screenColorPicker) { m_d->screenColorPicker->updateIcons(); } } void KisDlgInternalColorSelector::endUpdateWithNewColor() { emit signalForegroundColorChosen(m_d->currentColor); m_d->allowUpdates = true; } void KisDlgInternalColorSelector::focusInEvent(QFocusEvent *) { //setPreviousColor(); } void KisDlgInternalColorSelector::slotFinishUp() { setPreviousColor(m_d->currentColor); KConfigGroup cfg(KSharedConfig::openConfig()->group("")); if (m_d->paletteModel) { if (m_d->paletteModel->colorSet()) { cfg.writeEntry("internal_selector_active_color_set", m_d->paletteModel->colorSet()->name()); } } } void KisDlgInternalColorSelector::slotSetColorFromHex() { slotColorUpdated(m_d->sRGB); } void KisDlgInternalColorSelector::slotChangePalette(KoColorSet *set) { if (!set) { return; } m_d->paletteModel->setPalette(set); } void KisDlgInternalColorSelector::showEvent(QShowEvent *event) { updateAllElements(0); QDialog::showEvent(event); } diff --git a/libs/widgets/kis_color_button.cpp b/libs/widgets/kis_color_button.cpp index f2fd0ec5c3..48fb17057d 100644 --- a/libs/widgets/kis_color_button.cpp +++ b/libs/widgets/kis_color_button.cpp @@ -1,382 +1,385 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Martin Jones (mjones@kde.org) Copyright (C) 1999 Cristian Tibirna (ctibirna@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_button.h" #include #include #include #include #include #include #include #include #include #include #include #include #include +#include "kis_debug.h" class KisColorButton::KisColorButtonPrivate { public: KisColorButtonPrivate(KisColorButton *q); void _k_chooseColor(); void _k_colorChosen(); KisColorButton *q; KoColor m_defaultColor; bool m_bdefaultColor : 1; bool m_alphaChannel : 1; bool m_palette : 1; KoColor col; QPoint mPos; #ifndef Q_OS_MACOS QPointer dialogPtr; #else QPointer dialogPtr; #endif void initStyleOption(QStyleOptionButton *opt) const; }; ///////////////////////////////////////////////////////////////////// // Functions duplicated from KColorMimeData // Should be kept in sync void _k_populateMimeData(QMimeData *mimeData, const KoColor &color) { mimeData->setColorData(color.toQColor()); mimeData->setText(color.toQColor().name()); } bool _k_canDecode(const QMimeData *mimeData) { if (mimeData->hasColor()) { return true; } if (mimeData->hasText()) { const QString colorName = mimeData->text(); if ((colorName.length() >= 4) && (colorName[0] == QLatin1Char('#'))) { return true; } } return false; } QColor _k_fromMimeData(const QMimeData *mimeData) { if (mimeData->hasColor()) { return mimeData->colorData().value(); } if (_k_canDecode(mimeData)) { return QColor(mimeData->text()); } return QColor(); } QDrag *_k_createDrag(const KoColor &color, QObject *dragsource) { QDrag *drag = new QDrag(dragsource); QMimeData *mime = new QMimeData; _k_populateMimeData(mime, color); drag->setMimeData(mime); QPixmap colorpix(25, 20); colorpix.fill(color.toQColor()); QPainter p(&colorpix); p.setPen(Qt::black); p.drawRect(0, 0, 24, 19); p.end(); drag->setPixmap(colorpix); drag->setHotSpot(QPoint(-5, -7)); return drag; } ///////////////////////////////////////////////////////////////////// KisColorButton::KisColorButtonPrivate::KisColorButtonPrivate(KisColorButton *q) : q(q) { m_bdefaultColor = false; m_alphaChannel = false; m_palette = true; q->setAcceptDrops(true); connect(q, SIGNAL(clicked()), q, SLOT(_k_chooseColor())); } KisColorButton::KisColorButton(QWidget *parent) : QPushButton(parent) , d(new KisColorButtonPrivate(this)) { } KisColorButton::KisColorButton(const KoColor &c, QWidget *parent) : QPushButton(parent) , d(new KisColorButtonPrivate(this)) { d->col = c; } KisColorButton::KisColorButton(const KoColor &c, const KoColor &defaultColor, QWidget *parent) : QPushButton(parent) , d(new KisColorButtonPrivate(this)) { d->col = c; setDefaultColor(defaultColor); } KisColorButton::~KisColorButton() { delete d; } KoColor KisColorButton::color() const { return d->col; } void KisColorButton::setColor(const KoColor &c) { d->col = c; update(); emit changed(d->col); } void KisColorButton::setAlphaChannelEnabled(bool alpha) { d->m_alphaChannel = alpha; } bool KisColorButton::isAlphaChannelEnabled() const { return d->m_alphaChannel; } void KisColorButton::setPaletteViewEnabled(bool enable) { d->m_palette = enable; } bool KisColorButton::paletteViewEnabled() const { return d->m_palette; } KoColor KisColorButton::defaultColor() const { return d->m_defaultColor; } void KisColorButton::setDefaultColor(const KoColor &c) { d->m_bdefaultColor = true; d->m_defaultColor = c; } void KisColorButton::KisColorButtonPrivate::initStyleOption(QStyleOptionButton *opt) const { opt->initFrom(q); opt->state |= q->isDown() ? QStyle::State_Sunken : QStyle::State_Raised; opt->features = QStyleOptionButton::None; if (q->isDefault()) { opt->features |= QStyleOptionButton::DefaultButton; } opt->text.clear(); opt->icon = QIcon(); } void KisColorButton::paintEvent(QPaintEvent *) { QPainter painter(this); QStyle *style = QWidget::style(); //First, we need to draw the bevel. QStyleOptionButton butOpt; d->initStyleOption(&butOpt); style->drawControl(QStyle::CE_PushButtonBevel, &butOpt, &painter, this); //OK, now we can muck around with drawing out pretty little color box //First, sort out where it goes QRect labelRect = style->subElementRect(QStyle::SE_PushButtonContents, &butOpt, this); int shift = style->pixelMetric(QStyle::PM_ButtonMargin, &butOpt, this) / 2; labelRect.adjust(shift, shift, -shift, -shift); int x, y, w, h; labelRect.getRect(&x, &y, &w, &h); if (isChecked() || isDown()) { x += style->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &butOpt, this); y += style->pixelMetric(QStyle::PM_ButtonShiftVertical, &butOpt, this); } QColor fillCol = isEnabled() ? d->col.toQColor() : palette().color(backgroundRole()); qDrawShadePanel(&painter, x, y, w, h, palette(), true, 1, NULL); if (fillCol.isValid()) { const QRect rect(x + 1, y + 1, w - 2, h - 2); if (fillCol.alpha() < 255) { QPixmap chessboardPattern(16, 16); QPainter patternPainter(&chessboardPattern); patternPainter.fillRect(0, 0, 8, 8, Qt::black); patternPainter.fillRect(8, 8, 8, 8, Qt::black); patternPainter.fillRect(0, 8, 8, 8, Qt::white); patternPainter.fillRect(8, 0, 8, 8, Qt::white); patternPainter.end(); painter.fillRect(rect, QBrush(chessboardPattern)); } painter.fillRect(rect, fillCol); } if (hasFocus()) { QRect focusRect = style->subElementRect(QStyle::SE_PushButtonFocusRect, &butOpt, this); QStyleOptionFocusRect focusOpt; focusOpt.init(this); focusOpt.rect = focusRect; focusOpt.backgroundColor = palette().background().color(); style->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpt, &painter, this); } } QSize KisColorButton::sizeHint() const { QStyleOptionButton opt; d->initStyleOption(&opt); return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(40, 15), this). expandedTo(QApplication::globalStrut()); } QSize KisColorButton::minimumSizeHint() const { QStyleOptionButton opt; d->initStyleOption(&opt); return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(3, 3), this). expandedTo(QApplication::globalStrut()); } void KisColorButton::dragEnterEvent(QDragEnterEvent *event) { event->setAccepted(_k_canDecode(event->mimeData()) && isEnabled()); } void KisColorButton::dropEvent(QDropEvent *event) { QColor c = _k_fromMimeData(event->mimeData()); if (c.isValid()) { KoColor col; col.fromQColor(c); setColor(col); } } void KisColorButton::keyPressEvent(QKeyEvent *e) { int key = e->key() | e->modifiers(); if (QKeySequence::keyBindings(QKeySequence::Copy).contains(key)) { QMimeData *mime = new QMimeData; _k_populateMimeData(mime, color()); QApplication::clipboard()->setMimeData(mime, QClipboard::Clipboard); } else if (QKeySequence::keyBindings(QKeySequence::Paste).contains(key)) { QColor color = _k_fromMimeData(QApplication::clipboard()->mimeData(QClipboard::Clipboard)); KoColor col; col.fromQColor(color); setColor(col); } else { QPushButton::keyPressEvent(e); } } void KisColorButton::mousePressEvent(QMouseEvent *e) { d->mPos = e->pos(); QPushButton::mousePressEvent(e); } void KisColorButton::mouseMoveEvent(QMouseEvent *e) { if ((e->buttons() & Qt::LeftButton) && (e->pos() - d->mPos).manhattanLength() > QApplication::startDragDistance()) { _k_createDrag(color(), this)->start(); setDown(false); } } void KisColorButton::KisColorButtonPrivate::_k_chooseColor() { #ifndef Q_OS_MACOS KisDlgInternalColorSelector *dialog = dialogPtr.data(); #else QColorDialog *dialog = dialogPtr.data(); #endif if (dialog) { #ifndef Q_OS_MACOS dialog->setPreviousColor(q->color()); #else dialog->setCurrentColor(q->color().toQColor()); #endif dialog->show(); + dialog->setOrigin(QCursor::pos()); dialog->raise(); dialog->activateWindow(); return; } KisDlgInternalColorSelector::Config cfg; cfg.paletteBox = q->paletteViewEnabled(); #ifndef Q_OS_MACOS dialog = new KisDlgInternalColorSelector(q, q->color(), cfg, i18n("Choose a color")); #else dialog = new QColorDialog(q); dialog->setOption(QColorDialog::ShowAlphaChannel, m_alphaChannel); #endif dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, SIGNAL(accepted()), q, SLOT(_k_colorChosen())); dialogPtr = dialog; #ifndef Q_OS_MACOS dialog->setPreviousColor(q->color()); #else dialog->setCurrentColor(q->color().toQColor()); #endif dialog->show(); + dialog->setOrigin(QCursor::pos()); } void KisColorButton::KisColorButtonPrivate::_k_colorChosen() { #ifndef Q_OS_MACOS KisDlgInternalColorSelector *dialog = dialogPtr.data(); #else QColorDialog *dialog = dialogPtr.data(); #endif if (!dialog) { return; } #ifndef Q_OS_MACOS q->setColor(dialog->getCurrentColor()); #else KoColor c; c.fromQColor(dialog->currentColor()); q->setColor(c); #endif } #include "moc_kis_color_button.cpp" diff --git a/libs/widgetutils/KisPopover.cpp b/libs/widgetutils/KisPopover.cpp index 0d2c8f4979..9ce29cb563 100644 --- a/libs/widgetutils/KisPopover.cpp +++ b/libs/widgetutils/KisPopover.cpp @@ -1,194 +1,271 @@ +/* KisPopover.cpp + * ==== + * Copyright (c) 2019 Eoin O'Neill (eoinoneill1991@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + #include "KisPopover.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_icon_utils.h" #include "kis_global.h" #include "kis_debug.h" KisPopover::KisPopover(QWidget *parent) : QDialog(parent, Qt::FramelessWindowHint | Qt::Popup) { setModal(false); setMinimumSize(250, 300); QVBoxLayout* layout = new QVBoxLayout; setLayout(layout); QToolBar* toolbar = new QToolBar; denyAction = toolbar->addAction(KisIconUtils::loadIcon("edit-undo"), "Revert"); denyAction->connect(denyAction, &QAction::triggered, this, &KisPopover::reject); header = new QLabel(""); header->setAlignment(Qt::AlignCenter); header->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); toolbar->addWidget(header); acceptAction = toolbar->addAction(KisIconUtils::loadIcon("go-next"), "Apply"); acceptAction->connect(acceptAction, &QAction::triggered, this, &KisPopover::accept); layout->addWidget(toolbar); //TEMP -- Color settings only necessary to visualize. There should be better ways to handle the visualization. QPalette pal = palette(); QColor color = pal.color(QPalette::Window); QBrush brush(color.lighter(120)); pal.setBrush(QPalette::Window, brush); setPalette(pal); connect(this, &KisPopover::originChanged, this, &KisPopover::recalculatePosition); connect(this, &KisPopover::unfoldDirectionChanged, this, &KisPopover::recalculatePosition); connect(this, &KisPopover::windowTitleChanged, this, &KisPopover::setHeaderText); } -KisPopover::KisPopover(QWidget *parent, UnfoldDirections directions) : KisPopover(parent){ +KisPopover::KisPopover(QWidget *parent, UnfoldDirection directions) : KisPopover(parent){ this->unfoldingDirection = directions; } KisPopover::~KisPopover() { } QPointer KisPopover::create(QWidget* parent, QPoint pos){ QSize windowSize = parent->size(); - UnfoldDirections bestUnfoldDirection = discernBestUnfoldDirection(windowSize, pos); + UnfoldDirection bestUnfoldDirection = discernBestUnfoldDirection(windowSize, pos); return KisPopover::create(parent, pos, bestUnfoldDirection); } -QPointer KisPopover::create(QWidget* parent, QPoint pos, UnfoldDirections directions = UnfoldDirection::NONE){ +QPointer KisPopover::create(QWidget* parent, QPoint pos, UnfoldDirection directions = UnfoldDirection::NONE){ KisPopover* popover = new KisPopover(parent); popover->setOrigin(pos); popover->setUnfoldDirection(directions); popover->setAttribute(Qt::WA_DeleteOnClose); //todo: perhaps use a smart pointer instead to try to have better auto clean situations? return popover; } -KisPopover::UnfoldDirections KisPopover::discernBestUnfoldDirection(QSize mainWindowSize, QPoint desiredOpenPosition){ +KisPopover::UnfoldDirection KisPopover::discernBestUnfoldDirection(QSize mainWindowSize, QPoint desiredOpenPosition){ return UnfoldDirection::DOWN; } void KisPopover::setAcceptable(bool state){ if (acceptAction != nullptr){ acceptAction->setVisible(state); } } void KisPopover::setCancelable(bool state){ if (denyAction != nullptr){ denyAction->setVisible(state); } } void KisPopover::setHeaderText(QString title){ if (header != nullptr){ header->setText(title); } } -void KisPopover::setUnfoldDirection(KisPopover::UnfoldDirections directions){ +void KisPopover::setUnfoldDirection(KisPopover::UnfoldDirection directions){ this->unfoldingDirection = directions; emit unfoldDirectionChanged(); } void KisPopover::setOrigin( QPoint newOrigin ){ this->origin = newOrigin; emit originChanged(); } -void KisPopover::adjustPosition(){ +QPoint KisPopover::calculateOriginOffset(KisPopover::UnfoldDirection direction) +{ + QPoint offset = QPoint(); + switch(direction) { + case UnfoldDirection::DOWN: + offset.setX( -1 * this->size().width() / 2 ); + break; + case UnfoldDirection::UP: + offset.setX(-1 * this->size().width() / 2); + offset.setY(-1 * this->size().height()); + break; + case UnfoldDirection::LEFT: + offset.setX(-1 * this->size().width()); + offset.setY( -1 * this->size().height() / 2); + break; + case UnfoldDirection::RIGHT: + offset.setY(-1 * this->size().height() / 2); + break; + default: + break; + } + return offset; +} + +QPoint KisPopover::adjustPosition(){ QDesktopWidget* desktopWidget = QApplication::desktop(); QRect screenRect = desktopWidget->availableGeometry(); QSize popSize = this->sizeHint(); QRect popupRect( this->pos(), popSize); popupRect = kisEnsureInRect(popupRect, screenRect); - //qDebug() << "Adjust Pos: " << ppVar(popupRect); + QPoint translation = popupRect.topLeft() - this->pos(); this->setGeometry(popupRect); + return translation; } void KisPopover::resizeEvent(QResizeEvent *revent){ - QRect windowSize = this->rect(); + emit recalculatePosition(); +} + +void KisPopover::closeEvent(QCloseEvent *cevent) +{ + if (this->acceptAction->isVisible()) { + cevent->ignore(); + } else { + cevent->accept(); + emit accept(); + } +} +QPolygon KisPopover::makeWindowFrame(QRect windowSize, UnfoldDirection direction, float stemOffset) +{ + QPolygon renderPolygon; const int stemHeight = 15; const int stemWidth = stemHeight * 2; - //qDebug() << "resize Event: " <unfoldingDirection) { + switch (direction) { case UnfoldDirection::DOWN: - //setStyleSheet(QString("KisPopover { margin-top: %1px; }").arg(stemHeight)); renderPolygon << QPoint(windowSize.x(), windowSize.y() + stemHeight) - << QPoint(windowSize.x() + windowSize.width()/2 - stemWidth/2, windowSize.y() + stemHeight) - << QPoint(windowSize.x() + windowSize.width()/2, windowSize.y()) - << QPoint(windowSize.x() + windowSize.width()/2 + stemWidth/2, windowSize.y() + stemHeight) + << QPoint(windowSize.x() + windowSize.width() * stemOffset - stemWidth/2, windowSize.y() + stemHeight) + << QPoint(windowSize.x() + windowSize.width() * stemOffset, windowSize.y()) + << QPoint(windowSize.x() + windowSize.width() * stemOffset + stemWidth/2, windowSize.y() + stemHeight) << QPoint(windowSize.x() + windowSize.width(), windowSize.y() + stemHeight) << QPoint(windowSize.x() + windowSize.width(), windowSize.y() + windowSize.height() ) << QPoint(windowSize.x(), windowSize.y() + windowSize.height()); break; case UnfoldDirection::UP: - //setStyleSheet(QString("KisPopover { margin-bottom: %1px; }").arg(stemHeight)); renderPolygon << QPoint(windowSize.x(), windowSize.y()) << QPoint(windowSize.x() + windowSize.width(), windowSize.y()) << QPoint(windowSize.x() + windowSize.width(), windowSize.y() + windowSize.height() - stemHeight ) - << QPoint(windowSize.x() + windowSize.width()/2 + stemWidth/2, windowSize.y() + windowSize.height() - stemHeight) - << QPoint(windowSize.x() + windowSize.width()/2, windowSize.y() + windowSize.height()) - << QPoint(windowSize.x() + windowSize.width()/2 - stemWidth/2, windowSize.y() + windowSize.height() - stemHeight) + << QPoint(windowSize.x() + windowSize.width()*stemOffset + stemWidth/2, windowSize.y() + windowSize.height() - stemHeight) + << QPoint(windowSize.x() + windowSize.width()*stemOffset, windowSize.y() + windowSize.height()) + << QPoint(windowSize.x() + windowSize.width()*stemOffset - stemWidth/2, windowSize.y() + windowSize.height() - stemHeight) << QPoint(windowSize.x(), windowSize.y() + windowSize.height() - stemHeight); break; case UnfoldDirection::LEFT: - //setStyleSheet(QString("KisPopover { margin-right: %1px; }").arg(stemHeight)); renderPolygon << QPoint(windowSize.x(), windowSize.y()) << QPoint(windowSize.x() + windowSize.width()-stemHeight, windowSize.y()) << QPoint(windowSize.x() + windowSize.width()-stemHeight, windowSize.y() + windowSize.height()/2 - stemWidth/2) << QPoint(windowSize.x() + windowSize.width(), windowSize.y() + windowSize.height()/2) << QPoint(windowSize.x() + windowSize.width()-stemHeight, windowSize.y() + windowSize.height()/2 + stemWidth/2) << QPoint(windowSize.x() + windowSize.width()-stemHeight, windowSize.y() + windowSize.height() ) << QPoint(windowSize.x(), windowSize.y() + windowSize.height()); break; case UnfoldDirection::RIGHT: renderPolygon << QPoint(windowSize.x() + stemHeight, windowSize.y()) << QPoint(windowSize.x() + windowSize.width(), windowSize.y()) << QPoint(windowSize.x() + windowSize.width(), windowSize.y() + windowSize.height()) << QPoint(windowSize.x() + stemHeight, windowSize.y() + windowSize.height()) << QPoint(windowSize.x() + stemHeight, windowSize.y() + windowSize.height()/2 + stemWidth/2) << QPoint(windowSize.x(), windowSize.y() + windowSize.height()/2) << QPoint(windowSize.x() + stemHeight, windowSize.y() + windowSize.height()/2 - stemWidth/2); break; default: renderPolygon = QPolygon(windowSize); break; } - QRegion mask(renderPolygon); - setMask(mask); - emit recalculatePosition(); + return renderPolygon; } void KisPopover::recalculatePosition(){ - QPoint offset = QPoint(); - switch(this->unfoldingDirection) { + QPoint offset = calculateOriginOffset(unfoldingDirection); + + this->move(this->origin + offset); + QPoint adjustment = this->adjustPosition(); + + UnfoldDirection adjustedUnfold = unfoldingDirection; + + /* If it has to adjust in the direction it is trying to unfold in, try to unfold the other direction... + * + * If that causes a collision, well, just correct it and move on. Covering up the origin should be avoided, but + * it's not worth doing a ton of loops to prevent it. Collision detection need not be perfect... */ + if (abs(adjustment.y()) > 0 && (adjustedUnfold == UnfoldDirection::DOWN || adjustedUnfold == UnfoldDirection::UP) ) { + adjustedUnfold = (unfoldingDirection == UnfoldDirection::DOWN) ? UnfoldDirection::UP : UnfoldDirection::DOWN; + this->move(this->origin + calculateOriginOffset(adjustedUnfold)); + this->adjustPosition(); + } else if (abs(adjustment.x()) > 0 && (unfoldingDirection == UnfoldDirection::LEFT || unfoldingDirection == UnfoldDirection::RIGHT) ) { + adjustedUnfold = (unfoldingDirection == UnfoldDirection::LEFT) ? UnfoldDirection::RIGHT : UnfoldDirection::LEFT; + this->move(this->origin + calculateOriginOffset(adjustedUnfold)); + this->adjustPosition(); + } + + QPolygon poly; + float correction = 0.5f; + switch(adjustedUnfold){ case UnfoldDirection::DOWN: - offset.setX( -1 * this->size().width() / 2 ); + correction = (float)(this->origin.x() - this->pos().x()) / (float)(this->sizeHint().width()); + poly = makeWindowFrame(this->rect(), adjustedUnfold, correction); break; case UnfoldDirection::UP: - offset.setX(-1 * this->size().width() / 2); - offset.setY(-1 * this->size().height()); + correction = (float)(this->origin.x() - this->pos().x()) / (float)(this->rect().width()); + poly = makeWindowFrame(this->rect(), adjustedUnfold, correction); + break; case UnfoldDirection::LEFT: - offset.setX(-1 * this->size().width()); - offset.setY( -1 * this->size().height() / 2); + correction = (float)(this->origin.y() - this->pos().y()) / (float)(this->rect().height()); + poly = makeWindowFrame(this->rect(), adjustedUnfold, correction); + break; case UnfoldDirection::RIGHT: - offset.setY(-1 * this->size().height() / 2); - default: + correction = (float)(this->origin.y() - this->pos().y()) / (float)(this->rect().height()); + poly = makeWindowFrame(this->rect(), adjustedUnfold, correction); break; } - this->move(this->origin + offset); - this->adjustPosition(); + + setMask(QRegion(poly)); } + + diff --git a/libs/widgetutils/KisPopover.h b/libs/widgetutils/KisPopover.h index 7e02563ad2..56650fefb4 100644 --- a/libs/widgetutils/KisPopover.h +++ b/libs/widgetutils/KisPopover.h @@ -1,64 +1,64 @@ #ifndef KPOPOVER_H #define KPOPOVER_H #include "kritawidgetutils_export.h" #include #include #include #include #include class KRITAWIDGETUTILS_EXPORT KisPopover : public QDialog { Q_OBJECT public: enum UnfoldDirection { NONE = 0, - UP = 1 << 0, - DOWN = 1 << 1, - LEFT = 1 << 2, - RIGHT = 1 << 3 + UP = 1, + DOWN = 2, + LEFT = 3, + RIGHT = 4 }; - Q_DECLARE_FLAGS(UnfoldDirections, UnfoldDirection); private: - UnfoldDirections unfoldingDirection = UnfoldDirection::UP; + UnfoldDirection unfoldingDirection = UnfoldDirection::UP; QPointer acceptAction; QPointer denyAction; QPointer header; QPoint origin; public: KisPopover(QWidget *parent); - KisPopover(QWidget *parent, UnfoldDirections directions); + KisPopover(QWidget *parent, UnfoldDirection directions); ~KisPopover(); static QPointer create(QWidget* mainWindow, QPoint pos); - static QPointer create(QWidget* mainWindow, QPoint pos, UnfoldDirections directions); - static UnfoldDirections discernBestUnfoldDirection(QSize mainWindowSize, QPoint desiredOpenPosition); + static QPointer create(QWidget* mainWindow, QPoint pos, UnfoldDirection directions); + static UnfoldDirection discernBestUnfoldDirection(QSize mainWindowSize, QPoint desiredOpenPosition); void setAcceptable(bool state); void setCancelable(bool state); void setHeaderText(QString description); - void setUnfoldDirection(UnfoldDirections directions); + void setUnfoldDirection(UnfoldDirection directions); void setOrigin( QPoint newOrigin ); - - void adjustPosition(); + QPoint calculateOriginOffset( UnfoldDirection direction ); + QPoint adjustPosition(); protected: void resizeEvent(QResizeEvent* revent) override; + void closeEvent(QCloseEvent* cevent) override; + + QPolygon makeWindowFrame(QRect rect, UnfoldDirection direction, float stemOffset = 0.5); Q_SIGNALS: void originChanged(); void unfoldDirectionChanged(); void deny(); protected Q_SLOTS: void recalculatePosition(); }; -Q_DECLARE_OPERATORS_FOR_FLAGS(KisPopover::UnfoldDirections) - #endif // KPOPOVER_H diff --git a/plugins/extensions/dockpopovers/dockpopovers.cpp b/plugins/extensions/dockpopovers/dockpopovers.cpp index 4a627131f6..3254b61ddb 100644 --- a/plugins/extensions/dockpopovers/dockpopovers.cpp +++ b/plugins/extensions/dockpopovers/dockpopovers.cpp @@ -1,69 +1,68 @@ -/* - * rotateimage.cc -- Part of Krita - * - * Copyright (c) 2004 Michael Thaler +/* dockpopovers.cpp + * ==== + * Copyright (c) 2019 Eoin O'Neill (eoinoneill1991@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dockpopovers.h" #include "KisPart.h" #include "KoDockRegistry.h" #include #include #include "kis_mainwindow_observer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(DockPopoversFactory, "kritadockpopovers.json", registerPlugin();) DockPopovers::DockPopovers(QObject *parent, const QVariantList &) : KisActionPlugin(parent) { KisAction *action = createAction("showtoolsdock"); connect(action, SIGNAL(triggered()), this, SLOT(slotShowToolsDock())); } DockPopovers::~DockPopovers() { } void DockPopovers::slotShowToolsDock() { toolPopover = KisPopover::create(viewManager()->mainWindow(), QCursor::pos(), KisPopover::DOWN); toolPopover->setAcceptable(true); toolPopover->setCancelable(true); toolPopover->setHeaderText("Tools Settings"); toolPopover->exec(); } #include "dockpopovers.moc"