Hi Okular Team!
I've been using Okular for years. In my opinion it is the best PDF
viewer out there!
For my PhD presentation i created a beamer pdf, but missed a laser
pointer feature from Okular.
I've implemented it, see it as an attachment. It would be an honor to
have a commit in your codebase. What do you think about it?
I am looking forward to hearing from you!
Kind regards,
Adam
ps.: I am not subscribed to your list, please cc me.
From cce1654646ad70cd73cdf36401bfaf17c101d479 Mon Sep 17 00:00:00 2001
From: Adam Kiss <[email protected]>
Date: Sat, 17 Jan 2026 20:31:32 +0100
Subject: [PATCH] Presentation:
Implement optional laser pointer
---
conf/okular.kcfg | 3 +++
part/dlgpresentation.cpp | 6 ++++++
part/presentationwidget.cpp | 35 ++++++++++++++++++++++++++++++++---
part/presentationwidget.h | 2 ++
4 files changed, 43 insertions(+), 3 deletions(-)
diff --git a/conf/okular.kcfg b/conf/okular.kcfg
index b2301c39c..25c91d43a 100644
--- a/conf/okular.kcfg
+++ b/conf/okular.kcfg
@@ -261,6 +261,9 @@
<choice name="Hidden" />
</choices>
</entry>
+ <entry key="LaserPointer" type="Bool" >
+ <default>false</default>
+ </entry>
<entry key="SlidesShowProgress" type="Bool" >
<default>true</default>
</entry>
diff --git a/part/dlgpresentation.cpp b/part/dlgpresentation.cpp
index a263638c9..84ec76b48 100644
--- a/part/dlgpresentation.cpp
+++ b/part/dlgpresentation.cpp
@@ -48,6 +48,12 @@ DlgPresentation::DlgPresentation(QWidget *parent)
advanceTime->setEnabled(false);
connect(advanceAutomatically, &QCheckBox::toggled, advanceTime, &QSpinBox::setEnabled);
+ // Checkbox: Laser pointer on presentation
+ QCheckBox *laserPointer = new QCheckBox(this);
+ laserPointer->setText(i18nc("@option:check Config dialog, presentation page", "Show laser pointer instead of cursor"));
+ laserPointer->setObjectName(QStringLiteral("kcfg_LaserPointer"));
+ layout->addRow(QString(), laserPointer);
+
// Checkbox: Loop after last page
QCheckBox *loopAfterLastPage = new QCheckBox(this);
loopAfterLastPage->setText(i18nc("@option:check Config dialog, presentation page", "Loop after last page"));
diff --git a/part/presentationwidget.cpp b/part/presentationwidget.cpp
index 1b1af331c..c139477e2 100644
--- a/part/presentationwidget.cpp
+++ b/part/presentationwidget.cpp
@@ -163,6 +163,7 @@ PresentationWidget::PresentationWidget(QWidget *parent, Okular::Document *doc, D
, m_advanceSlides(Okular::SettingsCore::slidesAdvance())
, m_goToPreviousPageOnRelease(false)
, m_goToNextPageOnRelease(false)
+ , m_defaultPresentationCursor(Qt::ArrowCursor)
{
setAttribute(Qt::WA_DeleteOnClose);
setAttribute(Qt::WA_OpaquePaintEvent);
@@ -266,6 +267,30 @@ PresentationWidget::PresentationWidget(QWidget *parent, Okular::Document *doc, D
connect(m_document, &Okular::Document::processMovieAction, this, &PresentationWidget::slotProcessMovieAction);
connect(m_document, &Okular::Document::processRenditionAction, this, &PresentationWidget::slotProcessRenditionAction);
+ if (Okular::Settings::laserPointer() == true) {
+ QPixmap laserPixmap(32, 32);
+ laserPixmap.fill(Qt::transparent);
+
+ QPainter p(&laserPixmap);
+ p.setRenderHint(QPainter::Antialiasing, true);
+
+ // red dot
+ p.setBrush(QColor(255, 0, 0));
+ p.setPen(Qt::NoPen);
+ p.drawEllipse(QPoint(16, 16), 4, 4);
+
+ // optional glow
+ QColor glow(255, 0, 0, 120);
+ p.setBrush(glow);
+ p.drawEllipse(QPoint(16, 16), 7, 7);
+
+ p.end();
+
+ this->m_defaultPresentationCursor = QCursor(laserPixmap, 16, 16);
+
+ setCursor(this->m_defaultPresentationCursor);
+ }
+
// handle cursor appearance as specified in configuration
if (Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::HiddenDelay) {
KCursor::setAutoHideCursor(this, true);
@@ -531,7 +556,7 @@ bool PresentationWidget::eventFilter(QObject *o, QEvent *e)
if (e->type() == QTabletEvent::TabletEnterProximity) {
setCursor(QCursor(Qt::CrossCursor));
} else if (e->type() == QTabletEvent::TabletLeaveProximity) {
- setCursor(QCursor(Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::Hidden ? Qt::BlankCursor : Qt::ArrowCursor));
+ setCursor(QCursor(Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::Hidden ? Qt::BlankCursor : this->m_defaultPresentationCursor));
if (Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::HiddenDelay) {
// Trick KCursor to hide the cursor if needed by sending an "unknown" key press event
// Send also the key release to make the world happy even it's probably not needed
@@ -1050,7 +1075,7 @@ void PresentationWidget::testCursorOnLink(QPointF point)
if ((needsHandCursor && !m_handCursor) || (!needsHandCursor && m_handCursor)) {
// change cursor shape
m_handCursor = needsHandCursor;
- setCursor(QCursor(m_handCursor ? Qt::PointingHandCursor : Qt::ArrowCursor));
+ setCursor(QCursor(m_handCursor ? Qt::PointingHandCursor : this->m_defaultPresentationCursor));
}
}
@@ -1641,7 +1666,7 @@ void PresentationWidget::slotChangeDrawingToolEngine(const QDomElement &element)
delete m_drawingEngine;
m_drawingEngine = nullptr;
m_drawingRect = QRect();
- setCursor(Qt::ArrowCursor);
+ setCursor(this->m_defaultPresentationCursor); //TODO Hidden cursor option
} else {
m_drawingEngine = new SmoothPathEngine(element);
setCursor(QCursor(QPixmap(QStringLiteral("pencil")), Qt::ArrowCursor));
@@ -1790,6 +1815,10 @@ void PresentationWidget::showTopBar(bool show)
} else {
m_topBar->hide();
+ if (Okular::Settings::slidesCursor() != Okular::Settings::EnumSlidesCursor::Hidden) {
+ setCursor(this->m_defaultPresentationCursor);
+ }
+
// Reenable autohide if need be when leaving the toolbar
if (Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::HiddenDelay) {
KCursor::setAutoHideCursor(this, true);
diff --git a/part/presentationwidget.h b/part/presentationwidget.h
index c8fc744d3..31256a650 100644
--- a/part/presentationwidget.h
+++ b/part/presentationwidget.h
@@ -151,6 +151,8 @@ private:
bool m_goToPreviousPageOnRelease;
bool m_goToNextPageOnRelease;
+ QCursor m_defaultPresentationCursor;
+
/** TODO Qt6: Just use QWidget::screen() instead of this. */
static inline QScreen *oldQt_screenOf(const QWidget *widget)
{
--
2.52.0