[utilities/konsole] src: Add thumbnails for certain file types on mouse hover
Kurt Hindenburg
null at kde.org
Sat Jun 6 02:51:21 BST 2020
Git commit 877fd0caf4a12b85902a029bf0f127b5e8ad5e9e by Kurt Hindenburg, on behalf of Tomaz Canabrava.
Committed on 06/06/2020 at 01:49.
Pushed by hindenburg into branch 'master'.
Add thumbnails for certain file types on mouse hover
This allows for a user configured thumbnail image to be displayed
when the mouse is hovering over a file link. Any file that KIO can
transform into a thumbnail (image, video, folder) will be displayed.
Simply move the mouse to a file while holding an user selected keypress
(Alt, Shift, Control or a combination of them). The default
requires no key press. The profile setting 'Underline files' much be
enabled for this to work.
https://invent.kde.org/utilities/konsole/-/merge_requests/93
FIXED-IN: 20.08
FEATURE:
GUI:
CHANGELOG: Add thumbnails for certain file types on mouse hover
M +5 -2 src/CMakeLists.txt
M +101 -0 src/Filter.cpp
M +26 -0 src/Filter.h
M +6 -0 src/MainWindow.cpp
M +21 -0 src/TerminalDisplay.cpp
M +2 -0 src/TerminalDisplay.h
M +3 -0 src/settings/ConfigurationDialog.h
A +9 -0 src/settings/ThumbnailsSettings.cpp *
A +38 -0 src/settings/ThumbnailsSettings.h [License: GPL (v2/3)]
A +73 -0 src/settings/ThumbnailsSettings.ui
M +22 -0 src/settings/konsole.kcfg
The files marked with a * at the end have a non valid license. Please read: https://community.kde.org/Policies/Licensing_Policy and use the headers which are listed at that page.
https://invent.kde.org/utilities/konsole/commit/877fd0caf4a12b85902a029bf0f127b5e8ad5e9e
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index eda6ea82c..eef7f82a4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -158,7 +158,8 @@ ki18n_wrap_ui(konsoleprivate_SRCS ColorSchemeEditor.ui
settings/GeneralSettings.ui
settings/PartInfo.ui
settings/ProfileSettings.ui
- settings/TabBarSettings.ui)
+ settings/TabBarSettings.ui
+ settings/ThumbnailsSettings.ui)
# add the resource files for the ui files
qt5_add_resources( konsoleprivate_SRCS ../desktop/konsole.qrc)
@@ -182,7 +183,9 @@ set(konsole_KDEINIT_SRCS
settings/TemporaryFilesSettings.cpp
settings/GeneralSettings.cpp
settings/ProfileSettings.cpp
- settings/TabBarSettings.cpp)
+ settings/TabBarSettings.cpp
+ settings/ThumbnailsSettings.cpp
+)
# Sets the icon on Windows and OSX
diff --git a/src/Filter.cpp b/src/Filter.cpp
index 21a00fd88..fece98ead 100644
--- a/src/Filter.cpp
+++ b/src/Filter.cpp
@@ -21,6 +21,8 @@
#include "Filter.h"
#include "konsoledebug.h"
+#include "KonsoleSettings.h"
+
#include <algorithm>
// Qt
@@ -33,6 +35,11 @@
#include <QTextStream>
#include <QUrl>
#include <QMenu>
+#include <QMouseEvent>
+#include <QToolTip>
+#include <QBuffer>
+#include <QToolTip>
+#include <QTimer>
// KDE
#include <KLocalizedString>
@@ -40,6 +47,7 @@
#include <KFileItem>
#include <KFileItemListProperties>
#include <KFileItemActions>
+#include <KIO/PreviewJob>
// Konsole
#include "Session.h"
@@ -599,3 +607,96 @@ void FileFilter::HotSpot::setupMenu(QMenu *menu)
_menuActions.setItemListProperties(itemProperties);
_menuActions.addOpenWithActionsTo(menu);
}
+
+// Static variables for the HotSpot
+qintptr FileFilter::HotSpot::currentThumbnailHotspot = 0;
+bool FileFilter::HotSpot::_canGenerateThumbnail = false;
+QPointer<KIO::PreviewJob> FileFilter::HotSpot::_previewJob;
+
+void FileFilter::HotSpot::requestThumbnail(Qt::KeyboardModifiers modifiers, const QPoint &pos) {
+ _canGenerateThumbnail = true;
+ currentThumbnailHotspot = reinterpret_cast<qintptr>(this);
+ _eventModifiers = modifiers;
+ _eventPos = pos;
+
+ // Defer the real creation of the thumbnail by a few msec.
+ QTimer::singleShot(250, this, [this]{
+ if (currentThumbnailHotspot != reinterpret_cast<qintptr>(this)) {
+ return;
+ }
+
+ thumbnailRequested();
+ });
+}
+
+void FileFilter::HotSpot::stopThumbnailGeneration()
+{
+ _canGenerateThumbnail = false;
+ if (_previewJob) {
+ _previewJob->deleteLater();
+ QToolTip::hideText();
+ }
+}
+
+void Konsole::FileFilter::HotSpot::showThumbnail(const KFileItem& item, const QPixmap& preview)
+{
+ if (!_canGenerateThumbnail) {
+ return;
+ }
+ _thumbnailFinished = true;
+ Q_UNUSED(item)
+ QByteArray data;
+ QBuffer buffer(&data);
+ preview.save(&buffer, "PNG", 100);
+
+ const auto tooltipString = QStringLiteral("<img src='data:image/png;base64, %0'>")
+ .arg(QString::fromLocal8Bit(data.toBase64()));
+
+ QToolTip::showText(_thumbnailPos, tooltipString, qApp->focusWidget());
+}
+
+void FileFilter::HotSpot::thumbnailRequested() {
+ if (!_canGenerateThumbnail) {
+ return;
+ }
+
+ auto *settings = KonsoleSettings::self();
+
+ Qt::KeyboardModifiers modifiers = settings->thumbnailCtrl() ? Qt::ControlModifier : Qt::NoModifier;
+ modifiers |= settings->thumbnailAlt() ? Qt::AltModifier : Qt::NoModifier;
+ modifiers |= settings->thumbnailShift() ? Qt::ShiftModifier : Qt::NoModifier;
+
+ if (_eventModifiers != modifiers) {
+ return;
+ }
+
+ _thumbnailPos = QPoint(_eventPos.x() + 100, _eventPos.y() - settings->thumbnailSize() / 2);
+
+ const int size = KonsoleSettings::thumbnailSize();
+ if (_previewJob) {
+ _previewJob->deleteLater();
+ }
+
+ _thumbnailFinished = false;
+
+ // Show a "Loading" if Preview takes a long time.
+ QTimer::singleShot(10, this, [this]{
+ if (!_previewJob) {
+ return;
+ }
+ if (!_thumbnailFinished) {
+ QToolTip::showText(_thumbnailPos, i18n("Generating Thumbnail"), qApp->focusWidget());
+ }
+ });
+
+ _previewJob = new KIO::PreviewJob(KFileItemList({fileItem()}), QSize(size, size));
+ connect(_previewJob, &KIO::PreviewJob::gotPreview, this, &FileFilter::HotSpot::showThumbnail);
+ connect(_previewJob, &KIO::PreviewJob::failed, this, []{ QToolTip::hideText(); });
+ _previewJob->setAutoDelete(true);
+ _previewJob->start();
+}
+
+KFileItem Konsole::FileFilter::HotSpot::fileItem() const
+{
+ return KFileItem(QUrl::fromLocalFile(_filePath));
+}
diff --git a/src/Filter.h b/src/Filter.h
index 7b2bc3b8a..1b39ed3d7 100644
--- a/src/Filter.h
+++ b/src/Filter.h
@@ -28,9 +28,13 @@
#include <QStringList>
#include <QRegularExpression>
#include <QMultiHash>
+#include <QRect>
+#include <QPoint>
// KDE
#include <KFileItemActions>
+#include <KFileItem>
+#include <KIO/PreviewJob>
#include <memory>
@@ -39,6 +43,8 @@
class QAction;
class QMenu;
+class QMouseEvent;
+class KFileItem;
namespace Konsole {
class Session;
@@ -316,9 +322,29 @@ public:
*/
void activate(QObject *object = nullptr) override;
void setupMenu(QMenu *menu) override;
+
+ KFileItem fileItem() const;
+ void requestThumbnail(Qt::KeyboardModifiers modifiers, const QPoint &pos);
+ void thumbnailRequested();
+
+ static void stopThumbnailGeneration();
private:
+ void showThumbnail(const KFileItem& item, const QPixmap& preview);
QString _filePath;
KFileItemActions _menuActions;
+
+ QPoint _eventPos;
+ QPoint _thumbnailPos;
+ Qt::KeyboardModifiers _eventModifiers;
+ bool _thumbnailFinished;
+
+ /* This variable stores the pointer of the active HotSpot that
+ * is generating the thumbnail now, so we can bail out early.
+ * it's not used for pointer access.
+ */
+ static qintptr currentThumbnailHotspot;
+ static bool _canGenerateThumbnail;
+ static QPointer<KIO::PreviewJob> _previewJob;
};
explicit FileFilter(Session *session);
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index 0bc87112e..5b6e441ad 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -58,6 +58,7 @@
#include "settings/GeneralSettings.h"
#include "settings/ProfileSettings.h"
#include "settings/TabBarSettings.h"
+#include "settings/ThumbnailsSettings.h"
using namespace Konsole;
@@ -773,6 +774,11 @@ void MainWindow::showSettingsDialog(const bool showProfilePage)
temporaryFilesPage->setIcon(QIcon::fromTheme(QStringLiteral("folder-temp")));
confDialog->addPage(temporaryFilesPage, true);
+ const QString thumbnailPageName = i18nc("@title Preferences page name", "Thumbnails");
+ auto thumbnailPage = new KPageWidgetItem(new ThumbnailsSettings(confDialog), thumbnailPageName);
+ thumbnailPage->setIcon(QIcon::fromTheme(QStringLiteral("image-jpeg")));
+ confDialog->addPage(thumbnailPage, true);
+
if (showProfilePage) {
confDialog->setCurrentPage(profilePage);
}
diff --git a/src/TerminalDisplay.cpp b/src/TerminalDisplay.cpp
index 55b793ad1..1a68c8df4 100644
--- a/src/TerminalDisplay.cpp
+++ b/src/TerminalDisplay.cpp
@@ -1835,6 +1835,7 @@ void TerminalDisplay::focusOutEvent(QFocusEvent*)
Q_ASSERT(!_textBlinking);
_showUrlHint = false;
+ FileFilter::HotSpot::stopThumbnailGeneration();
}
void TerminalDisplay::focusInEvent(QFocusEvent*)
@@ -2262,6 +2263,7 @@ void TerminalDisplay::mouseMoveEvent(QMouseEvent* ev)
{
int charLine = 0;
int charColumn = 0;
+
getCharacterPosition(ev->pos(), charLine, charColumn, !_usesMouseTracking);
processFilters();
@@ -2302,6 +2304,15 @@ void TerminalDisplay::mouseMoveEvent(QMouseEvent* ev)
setCursor(Qt::PointingHandCursor);
}
+ /* can't use qobject_cast because moc is broken for inner classes */
+ auto fileSpot = spot.dynamicCast<FileFilter::HotSpot>();
+ if (fileSpot != _currentlyHoveredHotspot) {
+ _currentlyHoveredHotspot = fileSpot;
+ if (fileSpot) {
+ fileSpot->requestThumbnail(ev->modifiers(), ev->globalPos());
+ }
+ }
+
update(_mouseOverHotspotArea | previousHotspotArea);
} else if (!_mouseOverHotspotArea.isEmpty()) {
if ((_openLinksByDirectClick || ((ev->modifiers() & Qt::ControlModifier) != 0u)) || (cursor().shape() == Qt::PointingHandCursor)) {
@@ -2311,6 +2322,8 @@ void TerminalDisplay::mouseMoveEvent(QMouseEvent* ev)
update(_mouseOverHotspotArea);
// set hotspot area to an invalid rectangle
_mouseOverHotspotArea = QRegion();
+ FileFilter::HotSpot::stopThumbnailGeneration();
+ _currentlyHoveredHotspot.clear();
}
// for auto-hiding the cursor, we need mouseTracking
@@ -3564,6 +3577,14 @@ void TerminalDisplay::keyPressEvent(QKeyEvent* event)
}
}
+ if (_currentlyHoveredHotspot) {
+ auto fileHotspot = _currentlyHoveredHotspot.dynamicCast<FileFilter::HotSpot>();
+ if (!fileHotspot) {
+ return;
+ }
+ fileHotspot->requestThumbnail(event->modifiers(), QCursor::pos());
+ }
+
_screenWindow->screen()->setCurrentTerminalDisplay(this);
if (!_readOnly) {
diff --git a/src/TerminalDisplay.h b/src/TerminalDisplay.h
index ce217d771..7587bd1a0 100644
--- a/src/TerminalDisplay.h
+++ b/src/TerminalDisplay.h
@@ -886,6 +886,8 @@ private:
Qt::Edge _overlayEdge;
bool _hasCompositeFocus;
+
+ QSharedPointer<Filter::HotSpot> _currentlyHoveredHotspot;
};
class AutoScrollHandler : public QObject
diff --git a/src/settings/ConfigurationDialog.h b/src/settings/ConfigurationDialog.h
index 9d092198f..9cc95e8dd 100644
--- a/src/settings/ConfigurationDialog.h
+++ b/src/settings/ConfigurationDialog.h
@@ -118,6 +118,9 @@ public:
bool hasChanged() const {
for(const QButtonGroup *group: qAsConst(_groups)) {
+ if (!group->checkedButton()) {
+ continue;
+ }
int value = buttonToEnumValue(group->checkedButton());
const auto *enumItem = groupToConfigItemEnum(group);
diff --git a/src/settings/ThumbnailsSettings.cpp b/src/settings/ThumbnailsSettings.cpp
new file mode 100644
index 000000000..da2c5faf0
--- /dev/null
+++ b/src/settings/ThumbnailsSettings.cpp
@@ -0,0 +1,9 @@
+#include "ThumbnailsSettings.h"
+
+using namespace Konsole;
+
+ThumbnailsSettings::ThumbnailsSettings(QWidget *aParent)
+: QWidget(aParent)
+{
+ setupUi(this);
+}
diff --git a/src/settings/ThumbnailsSettings.h b/src/settings/ThumbnailsSettings.h
new file mode 100644
index 000000000..44b122954
--- /dev/null
+++ b/src/settings/ThumbnailsSettings.h
@@ -0,0 +1,38 @@
+/*
+ Copyright 2020 Tomaz Canabrava <tcanabrava at kde.org>
+
+ 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) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor appro-
+ ved by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ 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, see http://www.gnu.org/licenses/.
+*/
+
+#ifndef THUMBNAILSSETTINGS_H
+#define THUMBNAILSSETTINGS_H
+
+#include "ui_ThumbnailsSettings.h"
+
+namespace Konsole {
+class ThumbnailsSettings : public QWidget, private Ui::ThumbnailsSettings
+{
+ Q_OBJECT
+
+public:
+ explicit ThumbnailsSettings(QWidget *aParent = nullptr);
+ ~ThumbnailsSettings() override = default;
+};
+
+}
+
+#endif
diff --git a/src/settings/ThumbnailsSettings.ui b/src/settings/ThumbnailsSettings.ui
new file mode 100644
index 000000000..06ab42a1b
--- /dev/null
+++ b/src/settings/ThumbnailsSettings.ui
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ThumbnailsSettings</class>
+ <widget class="QWidget" name="ThumbnailsSettings">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>347</width>
+ <height>347</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Size:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="kcfg_ThumbnailSize">
+ <property name="suffix">
+ <string>px</string>
+ </property>
+ <property name="maximum">
+ <number>1024</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Activation:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Mouse hover plus</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="kcfg_ThumbnailShift">
+ <property name="text">
+ <string>Shift</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QCheckBox" name="kcfg_ThumbnailAlt">
+ <property name="text">
+ <string>Alt</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QCheckBox" name="kcfg_ThumbnailCtrl">
+ <property name="text">
+ <string>Ctrl</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/settings/konsole.kcfg b/src/settings/konsole.kcfg
index e878ede62..1340763bc 100644
--- a/src/settings/konsole.kcfg
+++ b/src/settings/konsole.kcfg
@@ -40,6 +40,28 @@
<default>false</default>
</entry>
</group>
+ <group name="ThumbnailsSettings">
+ <entry name="ThumbnailSize" type="Int">
+ <label> Thumbnail Width </label>
+ <tooltip> Sets the width of the thumbnail </tooltip>
+ <default> 250 </default>
+ </entry>
+ <entry name="ThumbnailShift" type="bool">
+ <label> Use shift to display a thumbnail </label>
+ <tooltip> Use shift to display a thumbnail </tooltip>
+ <default> false </default>
+ </entry>
+ <entry name="ThumbnailAlt" type="bool">
+ <label> Use alt to display a thumbnail </label>
+ <tooltip> Use alt to display a thumbnail </tooltip>
+ <default> false </default>
+ </entry>
+ <entry name="ThumbnailCtrl" type="bool">
+ <label> Use ctrl to display a thumbnail </label>
+ <tooltip> Use ctrl to display a thumbnail </tooltip>
+ <default> false </default>
+ </entry>
+ </group>
<group name="SearchSettings">
<entry name="SearchCaseSensitive" type="Bool">
<label>Search is case sensitive</label>
More information about the kde-doc-english
mailing list