diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -41,6 +41,7 @@ Gui/SettingsDialog/GeneralOptionsPage.cpp Gui/SettingsDialog/ShortcutsOptionsPage.cpp QuickEditor/QuickEditor.cpp + QuickEditor/BottomHelpTextWidget.cpp ) ecm_qt_declare_logging_category(SPECTACLE_SRCS_DEFAULT HEADER spectacle_core_debug.h IDENTIFIER SPECTACLE_CORE_LOG CATEGORY_NAME org.kde.spectacle.core) diff --git a/src/QuickEditor/BottomHelpTextWidget.h b/src/QuickEditor/BottomHelpTextWidget.h new file mode 100644 --- /dev/null +++ b/src/QuickEditor/BottomHelpTextWidget.h @@ -0,0 +1,54 @@ +/* This file is part of Spectacle, the KDE screenshot utility + * Copyright (C) 2019 Leon De Andrade leondeandrade@hotmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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 Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef BOTTOM_HELP_TEXT_H +#define BOTTOM_HELP_TEXT_H + +#include +#include +#include +#include +#include + +using BottomHelpTextModel = QVector>>; + +class BottomHelpTextWidget : public QFrame +{ + Q_OBJECT + +public: + explicit BottomHelpTextWidget(bool isSelectionEmpty, QWidget* parent = nullptr); + ~BottomHelpTextWidget() = default; + +public Q_SLOTS: + void updateStyle(); + +Q_SIGNALS: + void textModelChanged(); + +private: + void createTextModel(bool isSelectionEmpty); + void createLayout(); + + constexpr static int marginX = 12; + constexpr static int marginY = 8; + constexpr static int spacingX = 6; + + BottomHelpTextModel mTextModel = BottomHelpTextModel(); + QGridLayout* mLayout = new QGridLayout(); +}; +#endif //BOTTOM_HELP_TEXT_H diff --git a/src/QuickEditor/BottomHelpTextWidget.cpp b/src/QuickEditor/BottomHelpTextWidget.cpp new file mode 100644 --- /dev/null +++ b/src/QuickEditor/BottomHelpTextWidget.cpp @@ -0,0 +1,156 @@ +/* This file is part of Spectacle, the KDE screenshot utility + * Copyright (C) 2019 Leon De Andrade leondeandrade@hotmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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 Lesser General Public + * License along with this library. If not, see . + */ + +#include "BottomHelpTextWidget.h" + +#include +#include + +#include + +#include "SpectacleConfig.h" + +BottomHelpTextWidget::BottomHelpTextWidget(bool isSelectionEmpty, QWidget* parent) : + QFrame(parent) +{ + setFrameStyle(QFrame::Panel); + setAutoFillBackground(true); + + updateStyle(); + + connect(this, &BottomHelpTextWidget::textModelChanged, this, &BottomHelpTextWidget::createLayout); + connect(qGuiApp, &QGuiApplication::paletteChanged, this, &BottomHelpTextWidget::updateStyle); + + createTextModel(isSelectionEmpty); +} + +void BottomHelpTextWidget::updateStyle() { + QPalette palette = this->palette(); + QColor backgroundColor = palette.color(QPalette::Light); + backgroundColor.setAlpha(216); + palette.setColor(QPalette::Window, backgroundColor); + setPalette(palette); +} + +void BottomHelpTextWidget::createTextModel(bool isSelectionEmpty) +{ + SpectacleConfig* config = SpectacleConfig::instance(); + if(config->useReleaseToCapture()) { + if((config->alwaysRememberRegion() || config->rememberLastRectangularRegion()) && !isSelectionEmpty) { + //Release to capture enabled and saved region available + mTextModel.append({ + QString(i18nc("Mouse action", "Click and drag,")), + { QString(i18n(" ")) } + }); + mTextModel.append({ + QString(i18nc("Keyboard/mouse action", "Enter, double-click:")), + { QString(i18n("Take screenshot")) } + }); + mTextModel.append({ + QString(i18nc("Keyboard action", "Shift:")), + { QString(i18nc("Shift key action first half", "Hold to toggle magnifier")), + QString(i18nc("Shift key action second half", "while dragging selection handles")) } + }); + mTextModel.append({ + QString(i18nc("Keyboard action", "Arrow keys:")), + { QString(i18nc("Shift key action first line", "Move selection rectangle")), + QString(i18nc("Shift key action second line", "Hold Alt to resize, Shift to fine‑tune")) } + }); + mTextModel.append({ + QString(i18nc("Mouse action", "Right-click:")), + { QString(i18n("Reset selection")) } + }); + mTextModel.append({ + QString(i18nc("Keyboard action", "Esc:")), + { QString(i18n("Cancel")) } + }); + } else { + //Release to capture enabled and NO saved region available + mTextModel.append({ + QString(i18nc("Keyboard/mouse action", "Release left-click, Enter:")), + { QString(i18n("Take Screenshot")) } + }); + mTextModel.append({ + QString(i18nc("Keyboard action", "Shift:")), + { QString(i18nc("Shift key action first half", "Hold to toggle magnifier")) } + }); + mTextModel.append({ + QString(i18nc("Mouse action", "Right-click:")), + { QString(i18n("Reset selection")) } + }); + mTextModel.append({ + QString(i18nc("Keyboard action", "Esc:")), + { QString(i18n("Cancel")) } + }); + } + } else { + //Default text, Release to capture option disabled + mTextModel.append({ + QString(i18nc("Keyboard/mouse action", "Enter, double-click:")), + { QString(i18n("Take screenshot")) } + }); + + mTextModel.append({ + QString(i18nc("Keyboard action", "Shift:")), + { QString(i18nc("Shift key action first half", "Hold to toggle magnifier")), + QString(i18nc("Shift key action second half", "while dragging selection handles")) } + }); + + mTextModel.append({ + QString(i18nc("Keyboard action", "Arrow keys:")), + { QString(i18nc("Shift key action first line", "Move selection rectangle")), + QString(i18nc("Shift key action second line", "Hold Alt to resize, Shift to fine‑tune")) } + }); + + mTextModel.append({ + QString(i18nc("Mouse action", "Right-click:")), + { QString(i18n("Reset selection")) } + }); + + mTextModel.append({ + QString(i18nc("Keyboard action", "Esc:")), + { QString(i18n("Cancel")) } + }); + } + emit textModelChanged(); +} + +void BottomHelpTextWidget::createLayout() +{ + mLayout->setHorizontalSpacing(spacingX); + mLayout->setVerticalSpacing(0); + mLayout->setContentsMargins(marginX, marginY, marginX, marginY); + + //Create QLabels from the textModel and layout them in a Grid, j counts number of columns in the grid + for(int i = 0, j = 0; i < mTextModel.size(); ++i, ++j) { + const auto& helpTextRow = mTextModel[i]; + const QString& leftHelpText = helpTextRow.first; + const QVector& rightHelpTexts = helpTextRow.second; + + mLayout->addWidget(new QLabel(leftHelpText), j, 0, Qt::AlignRight); + + for(const auto& rightHelpText: rightHelpTexts) { + mLayout->addWidget(new QLabel(rightHelpText), j, 2); + ++j; + } + //Add some vertical spacing only between the helpTextRows + if(i != mTextModel.size() - 1) { + mLayout->addItem(new QSpacerItem(0, 5), j, 0, 1, 3); + } + } + this->setLayout(mLayout); +} diff --git a/src/QuickEditor/QuickEditor.h b/src/QuickEditor/QuickEditor.h --- a/src/QuickEditor/QuickEditor.h +++ b/src/QuickEditor/QuickEditor.h @@ -25,17 +25,19 @@ #include #include #include -#include + +#include "BottomHelpTextWidget.h" class QMouseEvent; +class QScreen; class QuickEditor: public QWidget { Q_OBJECT public: - explicit QuickEditor(const QPixmap &thePixmap, QWidget *parent = nullptr); + explicit QuickEditor(const QPixmap &thePixmap, const QScreen *activeScreen, QWidget *parent = nullptr); virtual ~QuickEditor() = default; private: @@ -70,13 +72,10 @@ void mouseReleaseEvent(QMouseEvent* event) override; void mouseDoubleClickEvent(QMouseEvent* event) override; void paintEvent(QPaintEvent*) override; - void drawBottomHelpText(QPainter& painter); void drawDragHandles(QPainter& painter); void drawMagnifier(QPainter& painter); void drawMidHelpText(QPainter& painter); void drawSelectionSizeTooltip(QPainter& painter); - void setBottomHelpText(); - void layoutBottomHelpText(); void setMouseCursor(const QPointF& pos); MouseState mouseLocation(const QPointF& pos); @@ -89,12 +88,6 @@ static const int selectionBoxPaddingY; static const int selectionBoxMarginY; - static const int bottomHelpMaxLength = 6; - static bool bottomHelpTextPrepared; - static const int bottomHelpBoxPaddingX; - static const int bottomHelpBoxPaddingY; - static const int bottomHelpBoxPairSpacing; - static const int bottomHelpBoxMarginBottom; static const int midHelpTextFontSize; static const int magnifierLargeStep; @@ -113,11 +106,6 @@ QPointF mInitialTopLeft; QString mMidHelpText; QFont mMidHelpTextFont; - std::pair> mBottomHelpText[bottomHelpMaxLength]; - QFont mBottomHelpTextFont; - QRect mBottomHelpBorderBox; - QPoint mBottomHelpContentPos; - int mBottomHelpGridLeftWidth; MouseState mMouseDragState; QPixmap mPixmap; qreal dprI; @@ -128,8 +116,8 @@ bool mReleaseToCapture; bool mRememberRegion; bool mDisableArrowKeys; - QRect mPrimaryScreenGeo; - int mbottomHelpLength; + + BottomHelpTextWidget* mBottomHelpText; Q_SIGNALS: diff --git a/src/QuickEditor/QuickEditor.cpp b/src/QuickEditor/QuickEditor.cpp --- a/src/QuickEditor/QuickEditor.cpp +++ b/src/QuickEditor/QuickEditor.cpp @@ -33,20 +33,15 @@ const int QuickEditor::selectionBoxPaddingY = 4; const int QuickEditor::selectionBoxMarginY = 2; -bool QuickEditor::bottomHelpTextPrepared = false; -const int QuickEditor::bottomHelpBoxPaddingX = 12; -const int QuickEditor::bottomHelpBoxPaddingY = 8; -const int QuickEditor::bottomHelpBoxPairSpacing = 6; -const int QuickEditor::bottomHelpBoxMarginBottom = 5; const int QuickEditor::midHelpTextFontSize = 12; const int QuickEditor::magnifierLargeStep = 15; const int QuickEditor::magZoom = 5; const int QuickEditor::magPixels = 16; const int QuickEditor::magOffset = 32; -QuickEditor::QuickEditor(const QPixmap& thePixmap, QWidget *parent) : +QuickEditor::QuickEditor(const QPixmap& thePixmap, const QScreen *activeScreen, QWidget *parent) : QWidget(parent), mMaskColor(QColor::fromRgbF(0, 0, 0, 0.15)), mStrokeColor(palette().highlight().color()), @@ -58,20 +53,16 @@ 0.85 )), mLabelForegroundColor(palette().windowText().color()), + mSelection(static_cast(QRect(activeScreen->geometry().topLeft(), QSize(0,0)))), mMidHelpText(i18n("Click and drag to draw a selection rectangle,\nor press Esc to quit")), mMidHelpTextFont(font()), - mBottomHelpTextFont(font()), - mBottomHelpGridLeftWidth(0), mMouseDragState(MouseState::None), mPixmap(thePixmap), mMagnifierAllowed(false), mShowMagnifier(SpectacleConfig::instance()->showMagnifierChecked()), mToggleMagnifier(false), - mReleaseToCapture(SpectacleConfig::instance()->useReleaseToCapture()), mRememberRegion(SpectacleConfig::instance()->alwaysRememberRegion() || SpectacleConfig::instance()->rememberLastRectangularRegion()), - mDisableArrowKeys(false), - mPrimaryScreenGeo(QGuiApplication::primaryScreen()->geometry()), - mbottomHelpLength(bottomHelpMaxLength) + mDisableArrowKeys(false) { SpectacleConfig *config = SpectacleConfig::instance(); if (config->useLightRegionMaskColour()) { @@ -99,24 +90,8 @@ } else { setCursor(Qt::CrossCursor); } - - setBottomHelpText(); + mBottomHelpText = new BottomHelpTextWidget(mSelection.size().isEmpty(), this); mMidHelpTextFont.setPointSize(midHelpTextFontSize); - if (!bottomHelpTextPrepared) { - bottomHelpTextPrepared = true; - const auto prepare = [this](QStaticText& item) { - item.prepare(QTransform(), mBottomHelpTextFont); - item.setPerformanceHint(QStaticText::AggressiveCaching); - }; - for (auto& pair : mBottomHelpText) { - prepare(pair.first); - for (auto &item : pair.second) { - prepare(item); - } - } - } - layoutBottomHelpText(); - update(); } @@ -288,7 +263,7 @@ void QuickEditor::mousePressEvent(QMouseEvent* event) { if (event->button() & Qt::LeftButton) { - /* NOTE Workaround for Bug 407843 + /* NOTE Workaround for Bug 407843 * If we show the selection Widget when a right click menu is open we lose focus on X. * When the user clicks we get the mouse back. We can only grab the keyboard if we already * have mouse focus. So just grab it undconditionally here. @@ -436,7 +411,7 @@ if(mMouseDragState == MouseState::Inside) { setCursor(Qt::OpenHandCursor); } - else if(mMouseDragState == MouseState::Outside && mReleaseToCapture) { + else if(mMouseDragState == MouseState::Outside && SpectacleConfig::instance()->useReleaseToCapture()) { event->accept(); mMouseDragState = MouseState::None; return acceptSelection(); @@ -490,120 +465,18 @@ } else if (mMagnifierAllowed && (mShowMagnifier ^ mToggleMagnifier)) { drawMagnifier(painter); } - drawBottomHelpText(painter); - } else { - drawMidHelpText(painter); - } -} - -void QuickEditor::layoutBottomHelpText() -{ - int maxRightWidth = 0; - int contentWidth = 0; - int contentHeight = 0; - mBottomHelpGridLeftWidth = 0; - for (int i = 0; i < mbottomHelpLength; i++) { - const auto& item = mBottomHelpText[i]; - const auto& left = item.first; - const auto& right = item.second; - const auto leftSize = left.size().toSize(); - mBottomHelpGridLeftWidth = qMax(mBottomHelpGridLeftWidth, leftSize.width()); - for (const auto& item : right) { - const auto rightItemSize = item.size().toSize(); - maxRightWidth = qMax(maxRightWidth, rightItemSize.width()); - contentHeight += rightItemSize.height(); - } - contentWidth = qMax(contentWidth, mBottomHelpGridLeftWidth + maxRightWidth + bottomHelpBoxPairSpacing); - contentHeight += (i != bottomHelpMaxLength ? bottomHelpBoxMarginBottom : 0); - } - mBottomHelpContentPos.setX((mPrimaryScreenGeo.width() - contentWidth) / 2 + mPrimaryScreenGeo.x()); - mBottomHelpContentPos.setY(height() - contentHeight - 8); - mBottomHelpGridLeftWidth += mBottomHelpContentPos.x(); - mBottomHelpBorderBox.setRect( - mBottomHelpContentPos.x() - bottomHelpBoxPaddingX, - mBottomHelpContentPos.y() - bottomHelpBoxPaddingY, - contentWidth + bottomHelpBoxPaddingX * 2, - contentHeight + bottomHelpBoxPaddingY * 2 - 1 - ); -} - -void QuickEditor::setBottomHelpText() { - if (mReleaseToCapture) { - if(mRememberRegion && !mSelection.size().isEmpty()) { - //Release to capture enabled and saved region available - mBottomHelpText[0] = {QStaticText(i18nc("Mouse action", "Click and drag,")),{QStaticText(i18n(" "))}}; - mBottomHelpText[1] = {QStaticText(i18nc("Keyboard/mouse action", "Enter, double-click:")), - {QStaticText(i18n("Take screenshot"))}}; - mBottomHelpText[2] = {QStaticText(i18nc("Keyboard action", "Shift:")), { - QStaticText(i18nc("Shift key action first half", "Hold to toggle magnifier")), - QStaticText(i18nc("Shift key action second half", "while dragging selection handles")) - }}; - mBottomHelpText[3] = {QStaticText(i18nc("Keyboard action", "Arrow keys:")), { - QStaticText(i18nc("Shift key action first line", "Move selection rectangle")), - QStaticText(i18nc("Shift key action second line", "Hold Alt to resize, Shift to fine‑tune")) - }}; - mBottomHelpText[4] = {QStaticText(i18nc("Mouse action", "Right-click:")), - {QStaticText(i18n("Reset selection"))}}; - mBottomHelpText[5] = {QStaticText(i18nc("Keyboard action", "Esc:")), {QStaticText(i18n("Cancel"))}}; - + QRect activeScreenGeo = QGuiApplication::screenAt(mSelection.center().toPoint())->geometry(); + mBottomHelpText->move((activeScreenGeo.width() - mBottomHelpText->width())/2 + activeScreenGeo.x(), + height() - mBottomHelpText->height()); + //Only draw bottom help text, if possible (no selection rectangle in that area) + if(!mSelection.intersects(mBottomHelpText->geometry())) { + mBottomHelpText->setVisible(true); } else { - //Release to capture enabled and NO saved region available - mbottomHelpLength = 4; - mBottomHelpText[0] = {QStaticText(i18nc("Keyboard/mouse action", "Release left-click, Enter:")), - {QStaticText(i18n("Take Screenshot"))}}; - mBottomHelpText[1] = {QStaticText(i18nc("Keyboard action", "Shift:")), { - QStaticText(i18nc("Shift key action first half", "Hold to toggle magnifier"))}}; - mBottomHelpText[2] = {QStaticText(i18nc("Mouse action", "Right-click:")), - {QStaticText(i18n("Reset selection"))}}; - mBottomHelpText[3] = {QStaticText(i18nc("Keyboard action", "Esc:")), {QStaticText(i18n("Cancel"))}}; - } - }else { - //Default text, Release to capture option disabled - mbottomHelpLength = 5; - mBottomHelpText[0] = {QStaticText(i18nc("Keyboard/mouse action", "Enter, double-click:")), - {QStaticText(i18n("Take screenshot"))}}; - mBottomHelpText[1] = {QStaticText(i18nc("Keyboard action", "Shift:")), { - QStaticText(i18nc("Shift key action first half", "Hold to toggle magnifier")), - QStaticText(i18nc("Shift key action second half", "while dragging selection handles")) - }}; - mBottomHelpText[2] = {QStaticText(i18nc("Keyboard action", "Arrow keys:")), { - QStaticText(i18nc("Shift key action first line", "Move selection rectangle")), - QStaticText(i18nc("Shift key action second line", "Hold Alt to resize, Shift to fine‑tune")) - }}; - mBottomHelpText[3] = {QStaticText(i18nc("Mouse action", "Right-click:")), - {QStaticText(i18n("Reset selection"))}}; - mBottomHelpText[4] = {QStaticText(i18nc("Keyboard action", "Esc:")), {QStaticText(i18n("Cancel"))}}; - } -} - -void QuickEditor::drawBottomHelpText(QPainter &painter) -{ - if (mSelection.intersects(mBottomHelpBorderBox)) { - return; - } - - painter.setBrush(mLabelBackgroundColor); - painter.setPen(mLabelForegroundColor); - painter.setFont(mBottomHelpTextFont); - painter.setRenderHint(QPainter::Antialiasing, false); - painter.drawRect(mBottomHelpBorderBox); - painter.setRenderHint(QPainter::Antialiasing, true); - - int topOffset = mBottomHelpContentPos.y(); - for (int i = 0; i < mbottomHelpLength; i++) { - const auto& item = mBottomHelpText[i]; - const auto& left = item.first; - const auto& right = item.second; - const auto leftSize = left.size().toSize(); - painter.drawStaticText(mBottomHelpGridLeftWidth - leftSize.width(), topOffset, left); - for (const auto& item : right) { - const auto rightItemSize = item.size().toSize(); - painter.drawStaticText(mBottomHelpGridLeftWidth + bottomHelpBoxPairSpacing, topOffset, item); - topOffset += rightItemSize.height(); - } - if (i != bottomHelpMaxLength) { - topOffset += bottomHelpBoxMarginBottom; + mBottomHelpText->setVisible(false); } + } else { + mBottomHelpText->setVisible(false); + drawMidHelpText(painter); } } @@ -727,7 +600,8 @@ painter.fillRect(geometry(), mMaskColor); painter.setFont(mMidHelpTextFont); QRect textSize = painter.boundingRect(QRect(), Qt::AlignCenter, mMidHelpText); - QPoint pos((mPrimaryScreenGeo.width() - textSize.width()) / 2 + mPrimaryScreenGeo.x(), (height() - textSize.height()) / 2); + QRect activeScreenGeo = QGuiApplication::screenAt(mSelection.center().toPoint())->geometry(); + QPoint pos((activeScreenGeo.width() - textSize.width()) / 2 + activeScreenGeo.x(), (height() - textSize.height()) / 2); painter.setBrush(mLabelBackgroundColor); QPen pen(mLabelForegroundColor); diff --git a/src/SpectacleCore.cpp b/src/SpectacleCore.cpp --- a/src/SpectacleCore.cpp +++ b/src/SpectacleCore.cpp @@ -37,6 +37,7 @@ #include #include #include +#include SpectacleCore::SpectacleCore(StartMode theStartMode, Spectacle::CaptureMode theCaptureMode, @@ -223,7 +224,7 @@ if (lExportManager->captureMode() == Spectacle::CaptureMode::RectangularRegion) { if(!mQuickEditor) { - mQuickEditor = std::make_unique(thePixmap); + mQuickEditor = std::make_unique(thePixmap, mMainWindow->window()->windowHandle()->screen()); connect(mQuickEditor.get(), &QuickEditor::grabDone, this, &SpectacleCore::screenshotUpdated); connect(mQuickEditor.get(), &QuickEditor::grabCancelled, this, &SpectacleCore::screenshotFailed); mQuickEditor->showFullScreen();