sd/source/ui/slideshow/slideshowimpl.cxx       |    6 +
 slideshow/source/engine/slideoverlaybutton.cxx |   12 ++
 slideshow/source/engine/slideoverlaybutton.hxx |    2 
 slideshow/source/engine/slideshowimpl.cxx      |  147 ++++++++++++++++++++++++-
 4 files changed, 165 insertions(+), 2 deletions(-)

New commits:
commit b6bec207724d19bb04f7bf2e3deba3a1e70f895e
Author:     Vladislav Tarakanov <[email protected]>
AuthorDate: Wed Jan 7 14:37:24 2026 +0400
Commit:     Samuel Mehrbrodt <[email protected]>
CommitDate: Mon Jan 26 10:59:04 2026 +0100

    tdf#158394 Automatic scaling of icons for navigation bar
    
    Added handling for the viewAdded and
    viewChanged events, which cause navigation bar
    icons to change if they don't fit the current
    presentation area size.
    
    This handler is only applied when automatic
    scaling of the navigation bar is enabled, so it
    shouldn't impact performance when the bar isn't
    used or a fixed size is selected.
    
    Icon sizes:
    XLarge for UHD and above
    Large for FHD and up
    Small for anything smaller than FHD
    
    Change-Id: I9f6599291d57aca9991c8d2dd2969d1edb36080e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196157
    Reviewed-by: Heiko Tietze <[email protected]>
    Tested-by: Jenkins
    Reviewed-by: Samuel Mehrbrodt <[email protected]>

diff --git a/sd/source/ui/slideshow/slideshowimpl.cxx 
b/sd/source/ui/slideshow/slideshowimpl.cxx
index 0f18410a3ad3..92b8b605af5c 100644
--- a/sd/source/ui/slideshow/slideshowimpl.cxx
+++ b/sd/source/ui/slideshow/slideshowimpl.cxx
@@ -1303,6 +1303,12 @@ bool SlideshowImpl::startShowImpl( const Sequence< 
beans::PropertyValue >& aProp
                         break;
                     }
                     case NavbarButtonSize::Auto:
+                    {
+                        
mxShow->setProperty(beans::PropertyValue("NavigationBarAutoscale", -1,
+                                Any(true),
+                                PropertyState_DIRECT_VALUE));
+                    }
+                    [[fallthrough]]; // Preset default buttons size
                     case NavbarButtonSize::Small:
                     default:
                     {
diff --git a/slideshow/source/engine/slideoverlaybutton.cxx 
b/slideshow/source/engine/slideoverlaybutton.cxx
index 2aad5cd793cf..43b72dcd3332 100644
--- a/slideshow/source/engine/slideoverlaybutton.cxx
+++ b/slideshow/source/engine/slideoverlaybutton.cxx
@@ -83,6 +83,18 @@ void SlideOverlayButton::setVisible(const bool bVisible)
     mrScreenUpdater.requestImmediateUpdate();
 }
 
+void SlideOverlayButton::clear()
+{
+    mrEventMultiplexer.removeViewHandler(
+        std::dynamic_pointer_cast<ViewEventHandler>(shared_from_this()));
+    mrEventMultiplexer.removeClickHandler(
+        std::dynamic_pointer_cast<MouseEventHandler>(shared_from_this()));
+    for (auto& item : maViews)
+    {
+        item.second->getContentCanvas()->clear();
+    }
+}
+
 css::geometry::IntegerSize2D SlideOverlayButton::getSize() const { return 
mxIconBitmap->getSize(); }
 
 basegfx::B2DPoint SlideOverlayButton::calcSpritePos(UnoViewSharedPtr const& 
rView) const
diff --git a/slideshow/source/engine/slideoverlaybutton.hxx 
b/slideshow/source/engine/slideoverlaybutton.hxx
index ceec9eed7b59..ab4bb23d6f74 100644
--- a/slideshow/source/engine/slideoverlaybutton.hxx
+++ b/slideshow/source/engine/slideoverlaybutton.hxx
@@ -66,6 +66,8 @@ public:
     css::geometry::IntegerSize2D getSize() const;
     basegfx::B2DPoint calcSpritePos(UnoViewSharedPtr const& rView) const;
 
+    void clear();
+
 private:
     SlideOverlayButton(css::uno::Reference<css::rendering::XBitmap> 
xIconBitmap,
                        css::awt::Point pPosition,
diff --git a/slideshow/source/engine/slideshowimpl.cxx 
b/slideshow/source/engine/slideshowimpl.cxx
index c84cda86a3ca..71af70f54841 100644
--- a/slideshow/source/engine/slideshowimpl.cxx
+++ b/slideshow/source/engine/slideshowimpl.cxx
@@ -63,6 +63,9 @@
 #include <com/sun/star/uno/Reference.hxx>
 #include <com/sun/star/loader/CannotActivateFactoryException.hpp>
 
+#include <vcl/canvastools.hxx>
+#include <vcl/vclenum.hxx>
+
 #include <unoviewcontainer.hxx>
 #include <transitionfactory.hxx>
 #include <eventmultiplexer.hxx>
@@ -101,6 +104,18 @@ namespace box2d::utils { class box2DWorld;
 
 namespace {
 
+constexpr OUString BMP_PREV_SLIDE_SMALL = u"sd/res/prevslide_small.png"_ustr;
+constexpr OUString BMP_NEXT_SLIDE_SMALL = u"sd/res/nextslide_small.png"_ustr;
+constexpr OUString BMP_MENU_SLIDE_SMALL = 
u"sd/res/slideshowmenu_small.png"_ustr;
+
+constexpr OUString BMP_PREV_SLIDE_LARGE = u"sd/res/prevslide_large.png"_ustr;
+constexpr OUString BMP_NEXT_SLIDE_LARGE = u"sd/res/nextslide_large.png"_ustr;
+constexpr OUString BMP_MENU_SLIDE_LARGE = 
u"sd/res/slideshowmenu_large.png"_ustr;
+
+constexpr OUString BMP_PREV_SLIDE_EXTRALARGE = 
u"sd/res/prevslide_extralarge.png"_ustr;
+constexpr OUString BMP_NEXT_SLIDE_EXTRALARGE = 
u"sd/res/nextslide_extralarge.png"_ustr;
+constexpr OUString BMP_MENU_SLIDE_EXTRALARGE = 
u"sd/res/slideshowmenu_extralarge.png"_ustr;
+
 /** During animations the update() method tells its caller to call it as
     soon as possible.  This gives us more time to render the next frame and
     still maintain a steady frame rate.  This class is responsible for
@@ -266,6 +281,9 @@ public:
      */
     bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode );
 
+
+    void handleViewChangedEvent( const sal_Int32 width, const sal_Int32 height 
);
+
     /** Obtain a MediaTempFile for the specified url. */
     virtual std::shared_ptr<avmedia::MediaTempFile> getMediaTempFile(const 
OUString& aUrl) override;
 
@@ -406,6 +424,11 @@ private:
     */
     void rewindEffectToPreviousSlide();
 
+    void updateNavbarIcons(
+            const OUString& prevSlideIconPath,
+            const OUString& contextMenuIconPath,
+            const OUString& nextSlideIconPath );
+
     /// all registered views
     UnoViewContainer                        maViewContainer;
 
@@ -448,6 +471,8 @@ private:
     std::shared_ptr<SlideOverlayButton>  mpNavigationMenu;
     std::shared_ptr<SlideOverlayButton>  mpNavigationNext;
 
+    NavbarButtonSize                     maCurNavbarBtnSize;
+
     std::shared_ptr<PointerSymbol>        mpPointerSymbol;
 
     /// the current slide transition sound object:
@@ -498,7 +523,8 @@ private:
 struct SlideShowImpl::SeparateListenerImpl : public EventHandler,
                                              public ViewRepaintHandler,
                                              public HyperlinkHandler,
-                                             public AnimationEventHandler
+                                             public AnimationEventHandler,
+                                             public ViewEventHandler
 {
     SlideShowImpl& mrShow;
     ScreenUpdater& mrScreenUpdater;
@@ -550,6 +576,23 @@ struct SlideShowImpl::SeparateListenerImpl : public 
EventHandler,
     {
         return mrShow.handleAnimationEvent(rNode);
     }
+
+    // ViewEventHandler
+    virtual void viewAdded(const UnoViewSharedPtr& rView) override
+    {
+        auto size = rView->getUnoView()->getCanvasArea();
+        mrShow.handleViewChangedEvent(size.Width, size.Height);
+    }
+
+    virtual void viewRemoved(const UnoViewSharedPtr& /*rView*/) override { }
+
+    virtual void viewChanged(const UnoViewSharedPtr& rView) override
+    {
+        auto size = rView->getUnoView()->getCanvasArea();
+        mrShow.handleViewChangedEvent(size.Width, size.Height);
+    }
+
+    virtual void viewsChanged() override { }
 };
 
 SlideShowImpl::SlideShowImpl(
@@ -574,7 +617,7 @@ SlideShowImpl::SlideShowImpl(
       mpBox2DDummyPtr(),
       mpListener(),
       mpRehearseTimingsActivity(),
-      mpWaitSymbol(),
+      maCurNavbarBtnSize(),
       mpPointerSymbol(),
       mpCurrentSlideTransitionSound(),
       mxComponentContext(std::move( xContext )),
@@ -1280,6 +1323,52 @@ void SlideShowImpl::rewindEffectToPreviousSlide()
     maScreenUpdater.commitUpdates();
 }
 
+void SlideShowImpl::updateNavbarIcons(
+    const OUString& prevSlideIconPath,
+    const OUString& contextMenuIconPath,
+    const OUString& nextSlideIconPath )
+{
+    mpNavigationPrev->clear();
+    Bitmap prevSlideBm(prevSlideIconPath);
+    const uno::Reference<rendering::XBitmap> xPrevSBitmap( 
vcl::unotools::xBitmapFromBitmap(prevSlideBm) );
+    if (xPrevSBitmap.is())
+    {
+        mpNavigationPrev = SlideOverlayButton::create(
+            xPrevSBitmap, { 20, 10 }, [this](basegfx::B2DPoint) { 
notifySlideEnded(true); },
+            maScreenUpdater, maEventMultiplexer, maViewContainer);
+    }
+
+    mpNavigationMenu->clear();
+    Bitmap menuBm(contextMenuIconPath);
+    const uno::Reference<rendering::XBitmap> xMenuBitmap( 
vcl::unotools::xBitmapFromBitmap(menuBm) );
+    if (xMenuBitmap.is())
+    {
+        mpNavigationMenu = SlideOverlayButton::create(
+            xMenuBitmap, { xMenuBitmap->getSize().Width + 48, 10 }, 
[this](basegfx::B2DPoint pos) {
+                maListenerContainer.forEach(
+                    [pos](const 
uno::Reference<presentation::XSlideShowListener>& xListener) {
+                        
uno::Reference<presentation::XSlideShowNavigationListener> xNavListener(
+                            xListener, uno::UNO_QUERY);
+                        if (xNavListener.is())
+                            xNavListener->contextMenuShow(
+                                
css::awt::Point(static_cast<sal_Int32>(floor(pos.getX())),
+                                                
static_cast<sal_Int32>(floor(pos.getY()))));
+                    });
+            },
+            maScreenUpdater, maEventMultiplexer, maViewContainer);
+    }
+
+    mpNavigationNext->clear();
+    Bitmap nextSlideBm(nextSlideIconPath);
+    const uno::Reference<rendering::XBitmap> xNextSBitmap( 
vcl::unotools::xBitmapFromBitmap(nextSlideBm) );
+    if (xNextSBitmap.is())
+    {
+        mpNavigationNext = SlideOverlayButton::create(
+            xNextSBitmap, { 2 * xPrevSBitmap->getSize().Width + 76, 10 }, 
[this](basegfx::B2DPoint) { notifySlideEnded(false); },
+            maScreenUpdater, maEventMultiplexer, maViewContainer);
+    }
+}
+
 sal_Bool SlideShowImpl::startShapeActivity(
     uno::Reference<drawing::XShape> const& /*xShape*/ )
 {
@@ -1878,6 +1967,24 @@ sal_Bool SlideShowImpl::setProperty( 
beans::PropertyValue const& rProperty )
         return true;
     }
 
+    if (rProperty.Name == "NavigationBarAutoscale")
+    {
+        bool isAutoscale;
+        if (!(rProperty.Value >>= isAutoscale))
+            return false;
+
+        if (isAutoscale)
+        {
+            maEventMultiplexer.addViewHandler( mpListener );
+        }
+        else
+        {
+            maEventMultiplexer.removeViewHandler( mpListener );
+        }
+
+        return true;
+    }
+
     if ( rProperty.Name == "PointerSymbolBitmap" )
     {
         uno::Reference<rendering::XBitmap> xBitmap;
@@ -2501,6 +2608,42 @@ bool SlideShowImpl::handleAnimationEvent( const 
AnimationNodeSharedPtr& rNode )
     return true;
 }
 
+void SlideShowImpl::handleViewChangedEvent( const sal_Int32 width, const 
sal_Int32 height )
+{
+    NavbarButtonSize newSize = NavbarButtonSize::Small;
+    sal_Int32 screenArea = width * height;
+    if (screenArea >= 3840 * 2160 /* UHD and above */)
+    {
+        newSize = NavbarButtonSize::XLarge;
+    }
+    else if (screenArea >= 1920 * 1080 /* FHD and above */)
+    {
+        newSize = NavbarButtonSize::Large;
+    }
+
+    if (newSize != maCurNavbarBtnSize)
+    {
+        maCurNavbarBtnSize = newSize;
+        switch ( maCurNavbarBtnSize ) {
+            case NavbarButtonSize::Large:
+            {
+                updateNavbarIcons(BMP_PREV_SLIDE_LARGE, BMP_MENU_SLIDE_LARGE, 
BMP_NEXT_SLIDE_LARGE);
+            }
+            break;
+            case NavbarButtonSize::XLarge:
+            {
+                updateNavbarIcons(BMP_PREV_SLIDE_EXTRALARGE, 
BMP_MENU_SLIDE_EXTRALARGE, BMP_NEXT_SLIDE_EXTRALARGE);
+            }
+            break;
+            default:
+            {
+                updateNavbarIcons(BMP_PREV_SLIDE_SMALL, BMP_MENU_SLIDE_SMALL, 
BMP_NEXT_SLIDE_SMALL);
+            }
+            break;
+        }
+    }
+}
+
 std::shared_ptr<avmedia::MediaTempFile> SlideShowImpl::getMediaTempFile(const 
OUString& aUrl)
 {
     std::shared_ptr<avmedia::MediaTempFile> aRet;

Reply via email to