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

Reply via email to