diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,8 @@ Gui/ExportMenu.cpp Gui/ProgressButton.cpp Gui/SmartSpinBox.cpp + Gui/CaptureAreaComboBox/CaptureModeModel.cpp + Gui/CaptureAreaComboBox/CaptureModeDelegate.cpp Gui/SettingsDialog/SettingsDialog.cpp Gui/SettingsDialog/SettingsPage.cpp Gui/SettingsDialog/SaveOptionsPage.cpp diff --git a/src/Gui/CaptureAreaComboBox/CaptureModeDelegate.h b/src/Gui/CaptureAreaComboBox/CaptureModeDelegate.h new file mode 100644 --- /dev/null +++ b/src/Gui/CaptureAreaComboBox/CaptureModeDelegate.h @@ -0,0 +1,18 @@ +#ifndef COMBO_BOX_SHORTCUT_DELEGATE_H +#define COMBO_BOX_SHORTCUT_DELEGATE_H + +#include + +class QPainter; + +class CaptureModeDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + using QStyledItemDelegate::QStyledItemDelegate; + + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; +}; + +#endif //COMBO_BOX_SHORTCUT_DELEGATE_H diff --git a/src/Gui/CaptureAreaComboBox/CaptureModeDelegate.cpp b/src/Gui/CaptureAreaComboBox/CaptureModeDelegate.cpp new file mode 100644 --- /dev/null +++ b/src/Gui/CaptureAreaComboBox/CaptureModeDelegate.cpp @@ -0,0 +1,27 @@ +#include "CaptureModeDelegate.h" + +#include +#include + +void CaptureModeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItem opt = option; + + QStyledItemDelegate::paint(painter, opt, index); + + const QWidget* widget = opt.widget; + QStyle *style = widget ? widget->style() : QApplication::style(); + + QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &opt); + if (opt.state & QStyle::State_Selected) { + painter->setPen(option.palette.color(QPalette::HighlightedText)); + } else { + painter->setPen(option.palette.color(QPalette::Text)); + } + + int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, &opt, widget) + 1; + textRect.adjust(textMargin, 0, -textMargin, 0); + + QString rightText = index.sibling(index.row(), 1).data(Qt::DisplayRole).toString(); + painter->drawText(textRect, Qt::AlignRight | Qt::AlignVCenter, rightText); +} diff --git a/src/Gui/CaptureAreaComboBox/CaptureModeModel.h b/src/Gui/CaptureAreaComboBox/CaptureModeModel.h new file mode 100644 --- /dev/null +++ b/src/Gui/CaptureAreaComboBox/CaptureModeModel.h @@ -0,0 +1,37 @@ +#ifndef CAPTURE_MODE_MODEL_H +#define CAPTURE_MODE_MODEL_H + +#include "Platforms/Platform.h" +#include "SpectacleCommon.h" + +#include +#include +#include + +#include + +class QAction; + +class CaptureModeModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit CaptureModeModel(const Platform::GrabModes &theGrabModes, QObject* parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +public Q_SLOT: + void changeShortcut(QAction *action, const QKeySequence &seq); + +private: + struct ModelEntry { + QString captureModeLabel; + QKeySequence captureModeShortcut; + Spectacle::CaptureMode captureMode; + }; + + std::vector modelData; +}; +#endif //COMBO_BOX_MODEL_H diff --git a/src/Gui/CaptureAreaComboBox/CaptureModeModel.cpp b/src/Gui/CaptureAreaComboBox/CaptureModeModel.cpp new file mode 100644 --- /dev/null +++ b/src/Gui/CaptureAreaComboBox/CaptureModeModel.cpp @@ -0,0 +1,77 @@ +#include "CaptureModeModel.h" +#include "SpectacleConfig.h" + +#include + +#include +#include + +#include + +CaptureModeModel::CaptureModeModel(const Platform::GrabModes &theGrabModes, QObject* parent) : + QAbstractTableModel(parent), + modelData() +{ + SpectacleConfig *lConfigMgr = SpectacleConfig::instance(); + KGlobalAccel *accelerator = KGlobalAccel::self(); + + if(theGrabModes.testFlag(Platform::GrabMode::AllScreens)) { + QAction *fullScreenAction = lConfigMgr->shortCutActions->action(QStringLiteral("FullScreenScreenShot")); + QString fullScreenLabel = QApplication::screens().count() == 1 ? i18n("Full Screen") : i18n("Full Screen (All Monitors)"); + QKeySequence shortcut = accelerator->hasShortcut(fullScreenAction) ? accelerator->shortcut(fullScreenAction)[0] : QKeySequence(); + modelData.push_back({fullScreenLabel, shortcut, Spectacle::CaptureMode::AllScreens}); + } + if(theGrabModes.testFlag(Platform::GrabMode::CurrentScreen)) { + QAction *currentScreenAction = lConfigMgr->shortCutActions->action(QStringLiteral("CurrentMonitorScreenShot")); + QKeySequence shortcut = accelerator->hasShortcut(currentScreenAction) ? accelerator->shortcut(currentScreenAction)[0]: QKeySequence(); + modelData.push_back({currentScreenAction->text(), shortcut, Spectacle::CaptureMode::CurrentScreen}); + } + if (theGrabModes.testFlag(Platform::GrabMode::ActiveWindow)) { + QAction *activeWindowAction = lConfigMgr->shortCutActions->action(QStringLiteral("ActiveWindowScreenShot")); + QKeySequence shortcut = accelerator->hasShortcut(activeWindowAction) ? accelerator->shortcut(activeWindowAction)[0] : QKeySequence(); + modelData.push_back({activeWindowAction->text(), shortcut, Spectacle::CaptureMode::ActiveWindow}); + } + if (theGrabModes.testFlag(Platform::GrabMode::WindowUnderCursor)) { + QAction *windowUnderCursorAction = lConfigMgr->shortCutActions->action(QStringLiteral("WindowUnderCursorScreenShot")); + QKeySequence shortcut = accelerator->hasShortcut(windowUnderCursorAction) ? accelerator->shortcut(windowUnderCursorAction)[0] : QKeySequence(); + modelData.push_back({windowUnderCursorAction->text(), shortcut, Spectacle::CaptureMode::WindowUnderCursor}); + } + QAction *regionAction = lConfigMgr->shortCutActions->action(QStringLiteral("RectangularRegionScreenShot")); + QKeySequence shortcut = accelerator->hasShortcut(regionAction) ? accelerator->shortcut(regionAction)[0] : QKeySequence(); + modelData.push_back({regionAction->text(), shortcut, Spectacle::CaptureMode::RectangularRegion}); + + connect(accelerator, &KGlobalAccel::globalShortcutChanged, this, &CaptureModeModel::changeShortcut); +} + +int CaptureModeModel::rowCount(const QModelIndex &parent) const { + Q_UNUSED(parent); + return modelData.size(); +} + +int CaptureModeModel::columnCount(const QModelIndex &parent) const { + Q_UNUSED(parent); + //Model Entry has three members (=columns) + return 3; +} + +QVariant CaptureModeModel::data(const QModelIndex &index, int role) const { + if(role == Qt::DisplayRole) { + switch(index.column()) { + case 0: return modelData[index.row()].captureModeLabel; + case 1: return modelData[index.row()].captureModeShortcut; + } + } else if(role == Qt::UserRole) { + return modelData[index.row()].captureMode; + } + return QVariant {}; +} + +void CaptureModeModel::changeShortcut(QAction *action, const QKeySequence &seq) { + //TO DO: captureModeLabel does not work for fullscreen as the label is changed there + auto modelEntryIterator = std::find_if(modelData.begin(), modelData.end(), [action](const ModelEntry& entry) { + return (entry.captureModeLabel == action->text()); + }); + if(modelEntryIterator != modelData.end()) { + (*modelEntryIterator).captureModeShortcut = seq; + } +} diff --git a/src/Gui/KSWidget.h b/src/Gui/KSWidget.h --- a/src/Gui/KSWidget.h +++ b/src/Gui/KSWidget.h @@ -78,7 +78,7 @@ void captureModeChanged(int theIndex); private: - + QGridLayout *mMainLayout { nullptr }; QHBoxLayout *mDelayLayout { nullptr }; QVBoxLayout *mRightLayout { nullptr }; diff --git a/src/Gui/KSWidget.cpp b/src/Gui/KSWidget.cpp --- a/src/Gui/KSWidget.cpp +++ b/src/Gui/KSWidget.cpp @@ -24,14 +24,18 @@ #include "SmartSpinBox.h" #include "SpectacleConfig.h" #include "ProgressButton.h" +#include "CaptureAreaComboBox/CaptureModeModel.h" +#include "CaptureAreaComboBox/CaptureModeDelegate.h" +#include #include #include #include #include #include #include #include +#include #include #include @@ -50,24 +54,31 @@ // the capture mode options first mCaptureModeLabel = new QLabel(i18n("Capture Mode"), this); + mCaptureArea = new QComboBox(this); - QString lFullScreenLabel = QApplication::screens().count() == 1 - ? i18n("Full Screen") - : i18n("Full Screen (All Monitors)"); - - if (theGrabModes.testFlag(Platform::GrabMode::AllScreens)) - mCaptureArea->insertItem(1, lFullScreenLabel, Spectacle::CaptureMode::AllScreens); - if (theGrabModes.testFlag(Platform::GrabMode::CurrentScreen)) - mCaptureArea->insertItem(2, i18n("Current Screen"), Spectacle::CaptureMode::CurrentScreen); - if (theGrabModes.testFlag(Platform::GrabMode::ActiveWindow)) - mCaptureArea->insertItem(3, i18n("Active Window"), Spectacle::CaptureMode::ActiveWindow); - if (theGrabModes.testFlag(Platform::GrabMode::WindowUnderCursor)) - mCaptureArea->insertItem(4, i18n("Window Under Cursor"), Spectacle::CaptureMode::WindowUnderCursor); - if (theGrabModes.testFlag(Platform::GrabMode::TransientWithParent)) { - mTransientWithParentAvailable = true; - } - mCaptureArea->insertItem(5, i18n("Rectangular Region"), Spectacle::CaptureMode::RectangularRegion); mCaptureArea->setMinimumWidth(240); + mCaptureArea->setModel(new CaptureModeModel {theGrabModes}); + //Custom Delegate for comboBox with support for displaying shortcuts + mCaptureArea->setItemDelegate(new CaptureModeDelegate {}); + + //Calculate the mim width needed to display action + shortcut + spacing in the delegates + QFontMetrics fontMetrics {QGuiApplication::font()}; + QAbstractItemModel *model = mCaptureArea->model(); + int maxWidthLeftText = 0; + int maxWidthRightText = 0; + for (int i = 0; i < model->rowCount(); ++i) { + QString leftText = model->data(model->index(i, 0)).toString(); + QString rightText = model->data(model->index(i, 1)).toString(); + + int leftWidth = fontMetrics.horizontalAdvance(leftText); + maxWidthLeftText = qMax(maxWidthLeftText, leftWidth); + int rightWidth = fontMetrics.horizontalAdvance(rightText); + maxWidthRightText = qMax(maxWidthRightText, rightWidth); + } + //spacing between longest left text and longest right text in the popup of the QComboBox + int spacingComboBox = 30; + mCaptureArea->view()->setMinimumWidth(maxWidthLeftText + maxWidthRightText + spacingComboBox); + connect(mCaptureArea, qOverload(&QComboBox::currentIndexChanged), this, &KSWidget::captureModeChanged); mDelayMsec = new SmartSpinBox(this); @@ -106,6 +117,10 @@ mWindowDecorations->setEnabled(false); connect(mWindowDecorations, &QCheckBox::clicked, lConfigMgr, &SpectacleConfig::setIncludeDecorationsChecked); + if (theGrabModes.testFlag(Platform::GrabMode::TransientWithParent)) { + mTransientWithParentAvailable = true; + } + mCaptureTransientOnly = new QCheckBox(i18n("Capture the current pop-up only"), this); mCaptureTransientOnly->setToolTip(i18n("Capture only the current pop-up window (like a menu, tooltip etc).\n" "If disabled, the pop-up is captured along with the parent window")); diff --git a/src/SpectacleConfig.cpp b/src/SpectacleConfig.cpp --- a/src/SpectacleConfig.cpp +++ b/src/SpectacleConfig.cpp @@ -44,26 +44,37 @@ { QAction *action = new QAction(i18n("Launch Spectacle")); action->setObjectName(QStringLiteral("_launch")); + action->setProperty("isConfigurationAction", true); shortCutActions->addAction(action->objectName(), action); } { QAction *action = new QAction(i18n("Capture Entire Desktop")); action->setObjectName(QStringLiteral("FullScreenScreenShot")); + action->setProperty("isConfigurationAction", true); shortCutActions->addAction(action->objectName(), action); } { QAction *action = new QAction(i18n("Capture Current Monitor")); action->setObjectName(QStringLiteral("CurrentMonitorScreenShot")); + action->setProperty("isConfigurationAction", true); shortCutActions->addAction(action->objectName(), action); } { QAction *action = new QAction(i18n("Capture Active Window")); action->setObjectName(QStringLiteral("ActiveWindowScreenShot")); + action->setProperty("isConfigurationAction", true); + shortCutActions->addAction(action->objectName(), action); + } + { + QAction *action = new QAction(i18n("Window Under Cursor")); + action->setObjectName(QStringLiteral("WindowUnderCursorScreenShot")); + action->setProperty("isConfigurationAction", true); shortCutActions->addAction(action->objectName(), action); } { QAction *action = new QAction(i18n("Capture Rectangular Region")); action->setObjectName(QStringLiteral("RectangularRegionScreenShot")); + action->setProperty("isConfigurationAction", true); shortCutActions->addAction(action->objectName(), action); } } @@ -119,7 +130,7 @@ mGeneralConfig.sync(); } -QUrl SpectacleConfig::lastSaveLocation() const +QUrl SpectacleConfig::lastSaveLocation() const { return this->lastSaveFile().adjusted(QUrl::RemoveFilename); } diff --git a/src/SpectacleCore.cpp b/src/SpectacleCore.cpp --- a/src/SpectacleCore.cpp +++ b/src/SpectacleCore.cpp @@ -88,6 +88,8 @@ connect(lExportManager, &ExportManager::forceNotify, this, &SpectacleCore::doNotify); connect(mPlatform.get(), &Platform::windowTitleChanged, lExportManager, &ExportManager::setWindowTitle); + setUpShortcuts(); + switch (theStartMode) { case StartMode::DBus: break; @@ -106,7 +108,6 @@ initGui(lGuiConfig.readEntry("includePointer", true), lGuiConfig.readEntry("includeDecorations", true)); break; } - setUpShortcuts(); } void SpectacleCore::setUpShortcuts()