[Libreoffice-commits] core.git: compilerplugins/clang include/sfx2 include/vcl sc/source sfx2/source sfx2/uiconfig sfx2/UIConfig_sfx.mk solenv/sanitizers vcl/source

Caolán McNamara (via logerrit) logerrit at kemper.freedesktop.org
Mon Sep 21 18:10:47 UTC 2020


 compilerplugins/clang/constantparam.numbers.results |    4 
 include/sfx2/infobar.hxx                            |   57 +--
 include/sfx2/objsh.hxx                              |    4 
 include/sfx2/sfxbasecontroller.hxx                  |    4 
 include/sfx2/viewfrm.hxx                            |   12 
 include/vcl/weldutils.hxx                           |   29 +
 sc/source/ui/docshell/docsh4.cxx                    |   10 
 sc/source/ui/inc/docsh.hxx                          |    2 
 sfx2/UIConfig_sfx.mk                                |    2 
 sfx2/source/dialog/infobar.cxx                      |  380 ++++++++++----------
 sfx2/source/doc/objserv.cxx                         |   10 
 sfx2/source/view/sfxbasecontroller.cxx              |   17 
 sfx2/source/view/viewfrm.cxx                        |   56 +-
 sfx2/uiconfig/ui/extrabutton.ui                     |   24 +
 sfx2/uiconfig/ui/infobar.ui                         |  187 +++++++++
 solenv/sanitizers/ui/sfx.suppr                      |    1 
 vcl/source/app/weldutils.cxx                        |   58 +++
 17 files changed, 584 insertions(+), 273 deletions(-)

New commits:
commit af90b8089405d6f042207f5639e750f08798ae92
Author:     Caolán McNamara <caolanm at redhat.com>
AuthorDate: Tue Sep 8 09:30:35 2020 +0100
Commit:     Caolán McNamara <caolanm at redhat.com>
CommitDate: Mon Sep 21 20:10:06 2020 +0200

    weld infobars
    
    note: "pushed" status listener case dropped. Doesn't seem to be an expectation
    for it to something in infobars, and there doesn't seem to be a working case
    anyway.
    
    Change-Id: I7869cc05de9918f0dd70e28b0087205db6c9506c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/101945
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>

diff --git a/compilerplugins/clang/constantparam.numbers.results b/compilerplugins/clang/constantparam.numbers.results
index 0a808e5f2924..add43943e088 100644
--- a/compilerplugins/clang/constantparam.numbers.results
+++ b/compilerplugins/clang/constantparam.numbers.results
@@ -806,10 +806,6 @@ include/sfx2/frame.hxx:192
     void SfxUnoFrameItem::SfxUnoFrameItem(unsigned short,const class com::sun::star::uno::Reference<class com::sun::star::frame::XFrame> &)
     unsigned short nWhich
     6516
-include/sfx2/infobar.hxx:111
-    class VclPtr<class SfxInfoBarWindow> SfxInfoBarContainerWindow::appendInfoBar(const class rtl::OUString &,const class rtl::OUString &,const class rtl::OUString &,enum InfobarType,long,_Bool)
-    long nMessageStyle
-    278528
 include/sfx2/linkmgr.hxx:63
     _Bool sfx2::LinkManager::InsertLink(class sfx2::SvBaseLink *,enum sfx2::SvBaseLinkObjectType,enum SfxLinkUpdateMode,const class rtl::OUString *)
     enum SfxLinkUpdateMode nUpdateType
diff --git a/include/sfx2/infobar.hxx b/include/sfx2/infobar.hxx
index 40a1b1ff9fbf..a03c833b957d 100644
--- a/include/sfx2/infobar.hxx
+++ b/include/sfx2/infobar.hxx
@@ -15,11 +15,7 @@
 
 #include <sfx2/childwin.hxx>
 #include <sfx2/dllapi.h>
-
-class FixedImage;
-class FixedText;
-class Button;
-class PushButton;
+#include <vcl/InterimItemWindow.hxx>
 
 // These must match the values in offapi/com/sun/star/frame/InfobarType.idl
 enum class InfobarType
@@ -57,44 +53,59 @@ public:
     void Update();
 };
 
+class ExtraButton;
+
 /** Class representing a single InfoBar to be added in a SfxInfoBarContainerWindow.
   */
-class SFX2_DLLPUBLIC SfxInfoBarWindow final : public vcl::Window
+class SFX2_DLLPUBLIC SfxInfoBarWindow final : public InterimItemWindow
 {
 private:
     OUString m_sId;
     InfobarType m_eType;
-    VclPtr<FixedImage> m_pImage;
-    VclPtr<FixedText> m_pPrimaryMessage;
-    VclPtr<FixedText> m_pSecondaryMessage;
-    VclPtr<Button> m_pCloseBtn;
-    std::vector<VclPtr<PushButton>> m_aActionBtns;
+    Size m_aSize;
+    Size m_aMessageSize;
+    Size m_aOrigMessageSize;
+    bool m_bLayingOut;
+    std::unique_ptr<weld::Image> m_xImage;
+    std::unique_ptr<weld::Label> m_xPrimaryMessage;
+    std::unique_ptr<weld::TextView> m_xSecondaryMessage;
+    std::unique_ptr<weld::Container> m_xButtonBox;
+    std::unique_ptr<weld::Toolbar> m_xCloseBtn;
+    std::vector<std::unique_ptr<ExtraButton>> m_aActionBtns;
+
+    DECL_LINK(SizeAllocHdl, const Size&, void);
 
     void SetForeAndBackgroundColors(InfobarType eType);
+    void SetCloseButtonImage();
 
 public:
     SfxInfoBarWindow(vcl::Window* parent, const OUString& sId, const OUString& sPrimaryMessage,
                      const OUString& sSecondaryMessage, InfobarType InfobarType,
-                     WinBits nMessageStyle, bool bShowCloseButton);
+                     bool bShowCloseButton);
+    Size DoLayout();
+    virtual void Layout() override;
     virtual ~SfxInfoBarWindow() override;
     virtual void dispose() override;
 
     const OUString& getId() const { return m_sId; }
-    virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override;
-    virtual void Resize() override;
     void Update(const OUString& sPrimaryMessage, const OUString& sSecondaryMessage,
                 InfobarType eType);
     basegfx::BColor m_aBackgroundColor;
     basegfx::BColor m_aForegroundColor;
 
     /** Add button to Infobar.
-         * Infobar takes ownership of the button so the button is
-         * destroyed when the infobar gets destroyed.
-         */
-    void addButton(PushButton* pButton);
+      * Infobar takes ownership of the button so the button is
+      * destroyed when the infobar gets destroyed.
+      *
+      * The optional "pCommand" is used by extensions, via XInfobarProvider, to
+      * dispatch pCommand on click.
+      */
+    weld::Button& addButton(const OUString* pCommand = nullptr);
+
+    void SetCommandHandler(weld::Button& rBtn, const OUString& aCommand);
 
 private:
-    DECL_LINK(CloseHandler, Button*, void);
+    DECL_LINK(CloseHandler, const OString&, void);
 };
 
 class SfxInfoBarContainerWindow final : public vcl::Window
@@ -102,6 +113,10 @@ class SfxInfoBarContainerWindow final : public vcl::Window
 private:
     SfxInfoBarContainerChild* m_pChildWin;
     std::vector<VclPtr<SfxInfoBarWindow>> m_pInfoBars;
+    Idle m_aLayoutIdle;
+    bool m_bResizing;
+
+    DECL_LINK(DoUpdateLayout, Timer*, void);
 
 public:
     SfxInfoBarContainerWindow(SfxInfoBarContainerChild* pChildWin);
@@ -110,12 +125,14 @@ public:
 
     VclPtr<SfxInfoBarWindow> appendInfoBar(const OUString& sId, const OUString& sPrimaryMessage,
                                            const OUString& sSecondaryMessage, InfobarType ibType,
-                                           WinBits nMessageStyle, bool bShowCloseButton);
+                                           bool bShowCloseButton);
     VclPtr<SfxInfoBarWindow> getInfoBar(const OUString& sId);
     bool hasInfoBarWithID(const OUString& sId);
     void removeInfoBar(VclPtr<SfxInfoBarWindow> const& pInfoBar);
     static bool isInfobarEnabled(const OUString& sId);
 
+    void TriggerUpdateLayout();
+
     virtual void Resize() override;
 };
 
diff --git a/include/sfx2/objsh.hxx b/include/sfx2/objsh.hxx
index 8da1f2bb7f82..e60139348c26 100644
--- a/include/sfx2/objsh.hxx
+++ b/include/sfx2/objsh.hxx
@@ -41,6 +41,7 @@
 #include <functional>
 #include <sfx2/AccessibilityIssue.hxx>
 
+namespace weld {class Button; }
 class SbxValue;
 class SbxArray;
 class BasicManager;
@@ -58,7 +59,6 @@ class GDIMetaFile;
 class INetURLObject;
 class IndexBitSet;
 class JobSetup;
-class Button;
 class OutputDevice;
 class Color;
 class Fraction;
@@ -368,7 +368,7 @@ public:
                            const OUString& aComment);
     SignatureState              GetScriptingSignatureState();
     void                        SignScriptingContent(weld::Window* pDialogParent);
-    DECL_LINK(SignDocumentHandler, Button*, void);
+    DECL_LINK(SignDocumentHandler, weld::Button&, void);
 
     virtual std::shared_ptr<SfxDocumentInfoDialog> CreateDocumentInfoDialog(weld::Window* pParent, const SfxItemSet& rItemSet);
 
diff --git a/include/sfx2/sfxbasecontroller.hxx b/include/sfx2/sfxbasecontroller.hxx
index 7fd536a14ef7..2adae2ec6cdc 100644
--- a/include/sfx2/sfxbasecontroller.hxx
+++ b/include/sfx2/sfxbasecontroller.hxx
@@ -50,10 +50,10 @@ namespace com::sun::star::frame { struct DispatchDescriptor; }
 namespace com::sun::star::ui { class XContextMenuInterceptor; }
 namespace com::sun::star::ui { class XSidebarProvider; }
 namespace com::sun::star::util { struct URL; }
+namespace weld { class Button; }
 
 struct  IMPL_SfxBaseController_DataContainer    ;   // impl. struct to hold member of class SfxBaseController
 
-class Button;
 class NotifyEvent;
 class SfxViewFrame;
 class SfxViewShell;
@@ -207,7 +207,7 @@ private:
     SAL_DLLPRIVATE SfxViewFrame& GetViewFrame_Impl() const;
     SAL_DLLPRIVATE void ShowInfoBars( );
 
-    DECL_LINK( CheckOutHandler, Button*, void );
+    DECL_LINK( CheckOutHandler, weld::Button&, void );
 
     std::unique_ptr<IMPL_SfxBaseController_DataContainer>   m_pData ;
 
diff --git a/include/sfx2/viewfrm.hxx b/include/sfx2/viewfrm.hxx
index 07d1360c57c7..aedd362f8781 100644
--- a/include/sfx2/viewfrm.hxx
+++ b/include/sfx2/viewfrm.hxx
@@ -30,7 +30,7 @@
 #include <svl/poolitem.hxx>
 #include <vcl/svapp.hxx>
 
-class Button;
+namespace weld { class Button; }
 class SvBorder;
 class SfxDispatcher;
 class SfxBindings;
@@ -59,11 +59,11 @@ private:
 protected:
     virtual void            Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
 
-    DECL_LINK(GetInvolvedHandler, Button*, void);
-    DECL_LINK(DonationHandler, Button*, void);
-    DECL_LINK(WhatsNewHandler, Button*, void);
-    DECL_LINK(SwitchReadOnlyHandler, Button*, void);
-    DECL_LINK(SignDocumentHandler, Button*, void);
+    DECL_LINK(GetInvolvedHandler, weld::Button&, void);
+    DECL_LINK(DonationHandler, weld::Button&, void);
+    DECL_LINK(WhatsNewHandler, weld::Button&, void);
+    DECL_LINK(SwitchReadOnlyHandler, weld::Button&, void);
+    DECL_LINK(SignDocumentHandler, weld::Button&, void);
     SAL_DLLPRIVATE void KillDispatcher_Impl();
 
     virtual                 ~SfxViewFrame() override;
diff --git a/include/vcl/weldutils.hxx b/include/vcl/weldutils.hxx
index 7eb4334e0156..7d22a456119e 100644
--- a/include/vcl/weldutils.hxx
+++ b/include/vcl/weldutils.hxx
@@ -11,6 +11,9 @@
 #define INCLUDED_VCL_WELDUTILS_HXX
 
 #include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
 #include <com/sun/star/uno/Reference.hxx>
 #include <comphelper/interfacecontainer2.hxx>
 #include <cppuhelper/compbase.hxx>
@@ -156,6 +159,32 @@ public:
     }
 };
 
+class VCL_DLLPUBLIC WidgetStatusListener final
+    : public cppu::WeakImplHelper<css::frame::XStatusListener>
+{
+public:
+    WidgetStatusListener(weld::Widget* widget, const OUString& rCommand);
+
+private:
+    weld::Widget* mWidget; /** The widget on which actions are performed */
+
+    /** Dispatcher. Need to keep a reference to it as long as this StatusListener exists. */
+    css::uno::Reference<css::frame::XDispatch> mxDispatch;
+    css::util::URL maCommandURL;
+    css::uno::Reference<css::frame::XFrame> mxFrame;
+
+public:
+    void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& rEvent) override;
+
+    void SAL_CALL disposing(const css::lang::EventObject& /*Source*/) override;
+
+    const css::uno::Reference<css::frame::XFrame>& getFrame() { return mxFrame; }
+
+    void startListening();
+
+    void dispose();
+};
+
 class VCL_DLLPUBLIC EntryFormatter : public Formatter
 {
 public:
diff --git a/sc/source/ui/docshell/docsh4.cxx b/sc/source/ui/docshell/docsh4.cxx
index ecce1c5da91f..a63a8b49619b 100644
--- a/sc/source/ui/docshell/docsh4.cxx
+++ b/sc/source/ui/docshell/docsh4.cxx
@@ -135,7 +135,7 @@ void ScDocShell::ReloadAllLinks()
     m_aDocument.UpdateAreaLinks();
 }
 
-IMPL_LINK_NOARG( ScDocShell, ReloadAllLinksHdl, Button*, void )
+IMPL_LINK_NOARG( ScDocShell, ReloadAllLinksHdl, weld::Button&, void )
 {
     ReloadAllLinks();
 
@@ -500,11 +500,9 @@ void ScDocShell::Execute( SfxRequest& rReq )
                         auto pInfoBar = pViewFrame->AppendInfoBar("enablecontent", "", ScResId(STR_RELOAD_TABLES), InfobarType::WARNING);
                         if (pInfoBar)
                         {
-                            VclPtrInstance<PushButton> xBtn(&pViewFrame->GetWindow());
-                            xBtn->SetText(ScResId(STR_ENABLE_CONTENT));
-                            xBtn->SetSizePixel(xBtn->GetOptimalSize());
-                            xBtn->SetClickHdl(LINK(this, ScDocShell, ReloadAllLinksHdl));
-                            pInfoBar->addButton(xBtn);
+                            weld::Button& rBtn = pInfoBar->addButton();
+                            rBtn.set_label(ScResId(STR_ENABLE_CONTENT));
+                            rBtn.connect_clicked(LINK(this, ScDocShell, ReloadAllLinksHdl));
                         }
                     }
                     rReq.Done();
diff --git a/sc/source/ui/inc/docsh.hxx b/sc/source/ui/inc/docsh.hxx
index e23067503bae..8a7fc31c1364 100644
--- a/sc/source/ui/inc/docsh.hxx
+++ b/sc/source/ui/inc/docsh.hxx
@@ -337,7 +337,7 @@ public:
     void            UnlockDocument();
 
     DECL_LINK( DialogClosedHdl, sfx2::FileDialogHelper*, void );
-    DECL_LINK( ReloadAllLinksHdl, Button*, void );
+    DECL_LINK( ReloadAllLinksHdl, weld::Button&, void );
 
     virtual SfxStyleSheetBasePool*  GetStyleSheetPool() override;
 
diff --git a/sfx2/UIConfig_sfx.mk b/sfx2/UIConfig_sfx.mk
index 5e79a8399cbf..5bb91d7ac6f6 100644
--- a/sfx2/UIConfig_sfx.mk
+++ b/sfx2/UIConfig_sfx.mk
@@ -30,6 +30,7 @@ $(eval $(call gb_UIConfig_add_uifiles,sfx,\
 	sfx2/uiconfig/ui/editdocumentdialog \
 	sfx2/uiconfig/ui/editdurationdialog \
 	sfx2/uiconfig/ui/emojicontrol \
+	sfx2/uiconfig/ui/extrabutton \
 	sfx2/uiconfig/ui/errorfindemaildialog \
 	sfx2/uiconfig/ui/floatingrecord \
 	sfx2/uiconfig/ui/helpbookmarkpage \
@@ -39,6 +40,7 @@ $(eval $(call gb_UIConfig_add_uifiles,sfx,\
 	sfx2/uiconfig/ui/helpmanual \
 	sfx2/uiconfig/ui/helpsearchpage \
 	sfx2/uiconfig/ui/helpwindow \
+	sfx2/uiconfig/ui/infobar \
 	sfx2/uiconfig/ui/inputdialog \
 	sfx2/uiconfig/ui/licensedialog \
 	sfx2/uiconfig/ui/linefragment \
diff --git a/sfx2/source/dialog/infobar.cxx b/sfx2/source/dialog/infobar.cxx
index fd64691ffa99..580bd6685801 100644
--- a/sfx2/source/dialog/infobar.cxx
+++ b/sfx2/source/dialog/infobar.cxx
@@ -8,6 +8,7 @@
  */
 
 #include <basegfx/polygon/b2dpolygon.hxx>
+#include <comphelper/dispatchcommand.hxx>
 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
 #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
@@ -21,11 +22,13 @@
 #include <sfx2/objface.hxx>
 #include <sfx2/sfxsids.hrc>
 #include <sfx2/viewfrm.hxx>
-#include <vcl/button.hxx>
-#include <vcl/fixed.hxx>
 #include <vcl/decoview.hxx>
+#include <vcl/image.hxx>
 #include <vcl/settings.hxx>
 #include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weldutils.hxx>
+#include <bitmaps.hlst>
 
 using namespace std;
 using namespace drawinglayer::geometry;
@@ -37,8 +40,6 @@ using namespace css::frame;
 
 namespace
 {
-const long INFO_BAR_BASE_HEIGHT = 40;
-
 void GetInfoBarColors(InfobarType ibType, BColor& rBackgroundColor, BColor& rForegroundColor,
                       BColor& rMessageColor)
 {
@@ -79,54 +80,39 @@ OUString GetInfoBarIconName(InfobarType ibType)
     switch (ibType)
     {
         case InfobarType::INFO:
-            aRet = "vcl/res/infobox.svg";
+            aRet = "vcl/res/infobox.png";
             break;
         case InfobarType::SUCCESS:
-            aRet = "vcl/res/successbox.svg";
+            aRet = "vcl/res/successbox.png";
             break;
         case InfobarType::WARNING:
-            aRet = "vcl/res/warningbox.svg";
+            aRet = "vcl/res/warningbox.png";
             break;
         case InfobarType::DANGER:
-            aRet = "vcl/res/errorbox.svg";
+            aRet = "vcl/res/errorbox.png";
             break;
     }
 
     return aRet;
 }
 
-class SfxCloseButton : public PushButton
-{
-    basegfx::BColor m_aBackgroundColor;
-    basegfx::BColor m_aForegroundColor;
-
-public:
-    explicit SfxCloseButton(vcl::Window* pParent)
-        : PushButton(pParent, 0)
-    {
-        basegfx::BColor aMessageColor;
-        GetInfoBarColors(InfobarType::WARNING, m_aBackgroundColor, m_aForegroundColor,
-                         aMessageColor);
-    }
+} // anonymous namespace
 
-    virtual void Paint(vcl::RenderContext& rRenderContext,
-                       const ::tools::Rectangle& rRect) override;
+void SfxInfoBarWindow::SetCloseButtonImage()
+{
+    Size aSize = Image(StockImage::Yes, CLOSEDOC).GetSizePixel();
+    aSize = Size(aSize.Width() * 1.5, aSize.Height() * 1.5);
 
-    void setBackgroundColor(const basegfx::BColor& rColor);
-    void setForegroundColor(const basegfx::BColor& rColor);
-};
+    VclPtr<VirtualDevice> xDevice(m_xCloseBtn->create_virtual_device());
+    xDevice->SetOutputSizePixel(aSize);
 
-void SfxCloseButton::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle&)
-{
     Point aBtnPos(0, 0);
-    if (GetButtonState() & DrawButtonFlags::Pressed)
-        aBtnPos.Move(Size(1, 1));
 
     const ViewInformation2D aNewViewInfos;
     const unique_ptr<BaseProcessor2D> pProcessor(
-        createBaseProcessor2DFromOutputDevice(rRenderContext, aNewViewInfos));
+        createBaseProcessor2DFromOutputDevice(*xDevice, aNewViewInfos));
 
-    const ::tools::Rectangle aRect(aBtnPos, PixelToLogic(GetSizePixel()));
+    const ::tools::Rectangle aRect(aBtnPos, xDevice->PixelToLogic(aSize));
 
     drawinglayer::primitive2d::Primitive2DContainer aSeq(2);
 
@@ -138,12 +124,8 @@ void SfxCloseButton::Paint(vcl::RenderContext& rRenderContext, const ::tools::Re
     aPolygon.append(B2DPoint(aRect.Left(), aRect.Bottom()));
     aPolygon.setClosed(true);
 
-    Color aBackgroundColor(m_aBackgroundColor);
-    if (IsMouseOver() || HasFocus())
-        aBackgroundColor.ApplyTintOrShade(-2000);
-
     PolyPolygonColorPrimitive2D* pBack
-        = new PolyPolygonColorPrimitive2D(B2DPolyPolygon(aPolygon), aBackgroundColor.getBColor());
+        = new PolyPolygonColorPrimitive2D(B2DPolyPolygon(aPolygon), m_aBackgroundColor);
     aSeq[0] = pBack;
 
     LineAttribute aLineAttribute(m_aForegroundColor, 2.0);
@@ -167,187 +149,186 @@ void SfxCloseButton::Paint(vcl::RenderContext& rRenderContext, const ::tools::Re
     aSeq[1] = pCross;
 
     pProcessor->process(aSeq);
+
+    m_xCloseBtn->set_item_image("close", xDevice);
 }
 
-void SfxCloseButton::setBackgroundColor(const basegfx::BColor& rColor)
+class ExtraButton
 {
-    m_aBackgroundColor = rColor;
-}
+private:
+    std::unique_ptr<weld::Builder> m_xBuilder;
+    std::unique_ptr<weld::Container> m_xContainer;
+    std::unique_ptr<weld::Button> m_xButton;
+    /** StatusListener. Updates the button as the slot state changes */
+    rtl::Reference<weld::WidgetStatusListener> m_xStatusListener;
+    OUString m_aCommand;
+
+    DECL_LINK(CommandHdl, weld::Button&, void);
+
+public:
+    ExtraButton(weld::Container* pContainer, const OUString* pCommand)
+        : m_xBuilder(Application::CreateBuilder(pContainer, "sfx/ui/extrabutton.ui"))
+        , m_xContainer(m_xBuilder->weld_container("ExtraButton"))
+        , m_xButton(m_xBuilder->weld_button("button"))
+    {
+        if (pCommand)
+        {
+            m_aCommand = *pCommand;
+            m_xButton->connect_clicked(LINK(this, ExtraButton, CommandHdl));
+            m_xStatusListener.set(new weld::WidgetStatusListener(m_xButton.get(), m_aCommand));
+            m_xStatusListener->startListening();
+        }
+    }
+
+    ~ExtraButton()
+    {
+        if (m_xStatusListener.is())
+            m_xStatusListener->dispose();
+    }
+
+    weld::Button& get_widget() { return *m_xButton; }
+};
 
-void SfxCloseButton::setForegroundColor(const basegfx::BColor& rColor)
+IMPL_LINK_NOARG(ExtraButton, CommandHdl, weld::Button&, void)
 {
-    m_aForegroundColor = rColor;
+    comphelper::dispatchCommand(m_aCommand, css::uno::Sequence<css::beans::PropertyValue>());
 }
 
-} // anonymous namespace
-
 SfxInfoBarWindow::SfxInfoBarWindow(vcl::Window* pParent, const OUString& sId,
                                    const OUString& sPrimaryMessage,
                                    const OUString& sSecondaryMessage, InfobarType ibType,
-                                   WinBits nMessageStyle, bool bShowCloseButton)
-    : Window(pParent, WB_DIALOGCONTROL)
+                                   bool bShowCloseButton)
+    : InterimItemWindow(pParent, "sfx/ui/infobar.ui", "InfoBar")
     , m_sId(sId)
     , m_eType(ibType)
-    , m_pImage(VclPtr<FixedImage>::Create(this, nMessageStyle))
-    , m_pPrimaryMessage(VclPtr<FixedText>::Create(this, nMessageStyle | WB_WORDBREAK))
-    , m_pSecondaryMessage(VclPtr<FixedText>::Create(this, nMessageStyle | WB_WORDBREAK))
-    , m_pCloseBtn(VclPtr<SfxCloseButton>::Create(this))
+    , m_bLayingOut(false)
+    , m_xImage(m_xBuilder->weld_image("image"))
+    , m_xPrimaryMessage(m_xBuilder->weld_label("primary"))
+    , m_xSecondaryMessage(m_xBuilder->weld_text_view("secondary"))
+    , m_xButtonBox(m_xBuilder->weld_container("buttonbox"))
+    , m_xCloseBtn(m_xBuilder->weld_toolbar("closebar"))
     , m_aActionBtns()
 {
-    m_pCloseBtn->SetStyle(WB_DEFBUTTON | WB_TABSTOP);
-    SetForeAndBackgroundColors(m_eType);
-    float fScaleFactor = GetDPIScaleFactor();
-    long nWidth = pParent->GetSizePixel().getWidth();
-    SetPosSizePixel(Point(0, 0), Size(nWidth, INFO_BAR_BASE_HEIGHT * fScaleFactor));
+    SetStyle(GetStyle() | WB_DIALOGCONTROL);
 
-    m_pImage->SetImage(Image(StockImage::Yes, GetInfoBarIconName(ibType)));
-    m_pImage->SetPaintTransparent(true);
-    m_pImage->Show();
+    InitControlBase(m_xCloseBtn.get());
+
+    m_xImage->set_from_icon_name(GetInfoBarIconName(ibType));
+    m_xSecondaryMessage->set_margin_top(m_xImage->get_preferred_size().Height() / 4);
 
-    vcl::Font aFont(m_pPrimaryMessage->GetControlFont());
-    aFont.SetWeight(WEIGHT_BOLD);
-    m_pPrimaryMessage->SetControlFont(aFont);
     if (!sPrimaryMessage.isEmpty())
     {
-        m_pPrimaryMessage->SetText(sPrimaryMessage);
-        m_pPrimaryMessage->Show();
+        m_xPrimaryMessage->set_label(sPrimaryMessage);
+        m_xPrimaryMessage->show();
     }
 
-    m_pSecondaryMessage->SetText(sSecondaryMessage);
-    m_pSecondaryMessage->Show();
+    m_xSecondaryMessage->set_text(sSecondaryMessage);
+    m_aOrigMessageSize = m_xSecondaryMessage->get_preferred_size();
+    m_aMessageSize = m_aOrigMessageSize;
+    m_xSecondaryMessage->connect_size_allocate(LINK(this, SfxInfoBarWindow, SizeAllocHdl));
 
     if (bShowCloseButton)
     {
-        m_pCloseBtn->SetClickHdl(LINK(this, SfxInfoBarWindow, CloseHandler));
-        m_pCloseBtn->Show();
+        m_xCloseBtn->connect_clicked(LINK(this, SfxInfoBarWindow, CloseHandler));
+        m_xCloseBtn->show();
     }
 
     EnableChildTransparentMode();
 
-    Resize();
-}
+    SetForeAndBackgroundColors(m_eType);
+
+    auto nWidth = pParent->GetSizePixel().getWidth();
+    auto nHeight = get_preferred_size().Height();
+    SetSizePixel(Size(nWidth, nHeight + 2));
 
-void SfxInfoBarWindow::addButton(PushButton* pButton)
-{
-    pButton->SetParent(this);
-    pButton->Show();
-    m_aActionBtns.emplace_back(pButton);
     Resize();
 }
 
-SfxInfoBarWindow::~SfxInfoBarWindow() { disposeOnce(); }
-
-void SfxInfoBarWindow::SetForeAndBackgroundColors(InfobarType eType)
+IMPL_LINK(SfxInfoBarWindow, SizeAllocHdl, const Size&, rSize, void)
 {
-    basegfx::BColor aMessageColor;
-    GetInfoBarColors(eType, m_aBackgroundColor, m_aForegroundColor, aMessageColor);
-
-    static_cast<SfxCloseButton*>(m_pCloseBtn.get())->setBackgroundColor(m_aBackgroundColor);
-    static_cast<SfxCloseButton*>(m_pCloseBtn.get())->setForegroundColor(m_aForegroundColor);
-    m_pPrimaryMessage->SetControlForeground(Color(aMessageColor));
-    m_pSecondaryMessage->SetControlForeground(Color(aMessageColor));
+    if (m_aMessageSize != rSize)
+    {
+        m_aMessageSize = rSize;
+        static_cast<SfxInfoBarContainerWindow*>(GetParent())->TriggerUpdateLayout();
+    }
 }
 
-void SfxInfoBarWindow::dispose()
+Size SfxInfoBarWindow::DoLayout()
 {
-    for (auto& rxBtn : m_aActionBtns)
-        rxBtn.disposeAndClear();
-
-    m_pImage.disposeAndClear();
-    m_pPrimaryMessage.disposeAndClear();
-    m_pSecondaryMessage.disposeAndClear();
-    m_pCloseBtn.disposeAndClear();
-    m_aActionBtns.clear();
-    vcl::Window::dispose();
+    Size aGivenSize(GetSizePixel());
+
+    // disconnect SizeAllocHdl because we don't care about the size change
+    // during layout
+    m_xSecondaryMessage->connect_size_allocate(Link<const Size&, void>());
+
+    // blow away size cache in case m_aMessageSize.Width() is already the width request
+    // and we would get the cached preferred size instead of the recalc we want to force
+    m_xSecondaryMessage->set_size_request(-1, -1);
+    // make the width we were detected as set to by SizeAllocHdl as our desired width
+    m_xSecondaryMessage->set_size_request(m_aMessageSize.Width(), -1);
+    // get our preferred size with that message width
+    Size aSizeForWidth(aGivenSize.Width(), m_xContainer->get_preferred_size().Height());
+    // restore the message preferred size so we can freely resize, and get a new
+    // m_aMessageSize and repeat the process if we do
+    m_xSecondaryMessage->set_size_request(m_aOrigMessageSize.Width(), -1);
+
+    // connect SizeAllocHdl so changes outside of this layout will trigger a new layout
+    m_xSecondaryMessage->connect_size_allocate(LINK(this, SfxInfoBarWindow, SizeAllocHdl));
+
+    return aSizeForWidth;
 }
 
-void SfxInfoBarWindow::Paint(vcl::RenderContext& rRenderContext,
-                             const ::tools::Rectangle& rPaintRect)
+void SfxInfoBarWindow::Layout()
 {
-    const ViewInformation2D aNewViewInfos;
-    const unique_ptr<BaseProcessor2D> pProcessor(
-        createBaseProcessor2DFromOutputDevice(rRenderContext, aNewViewInfos));
+    if (m_bLayingOut)
+        return;
+    m_bLayingOut = true;
 
-    const ::tools::Rectangle aRect(Point(0, 0), PixelToLogic(GetSizePixel()));
+    InterimItemWindow::Layout();
 
-    drawinglayer::primitive2d::Primitive2DContainer aSeq(2);
-
-    // Light background
-    B2DPolygon aPolygon;
-    aPolygon.append(B2DPoint(aRect.Left(), aRect.Top()));
-    aPolygon.append(B2DPoint(aRect.Right(), aRect.Top()));
-    aPolygon.append(B2DPoint(aRect.Right(), aRect.Bottom()));
-    aPolygon.append(B2DPoint(aRect.Left(), aRect.Bottom()));
-    aPolygon.setClosed(true);
-
-    PolyPolygonColorPrimitive2D* pBack
-        = new PolyPolygonColorPrimitive2D(B2DPolyPolygon(aPolygon), m_aBackgroundColor);
-    aSeq[0] = pBack;
-
-    LineAttribute aLineAttribute(m_aForegroundColor, 1.0);
-
-    // Bottom dark line
-    B2DPolygon aPolygonBottom;
-    aPolygonBottom.append(B2DPoint(aRect.Left(), aRect.Bottom()));
-    aPolygonBottom.append(B2DPoint(aRect.Right(), aRect.Bottom()));
-
-    PolygonStrokePrimitive2D* pLineBottom
-        = new PolygonStrokePrimitive2D(aPolygonBottom, aLineAttribute);
-
-    aSeq[1] = pLineBottom;
-
-    pProcessor->process(aSeq);
-
-    Window::Paint(rRenderContext, rPaintRect);
+    m_bLayingOut = false;
 }
 
-void SfxInfoBarWindow::Resize()
+weld::Button& SfxInfoBarWindow::addButton(const OUString* pCommand)
 {
-    float fScaleFactor = GetDPIScaleFactor();
+    m_aActionBtns.emplace_back(std::make_unique<ExtraButton>(m_xButtonBox.get(), pCommand));
 
-    long nWidth = GetSizePixel().getWidth();
-    m_pCloseBtn->SetPosSizePixel(Point(nWidth - 25 * fScaleFactor, 15 * fScaleFactor),
-                                 Size(10 * fScaleFactor, 10 * fScaleFactor));
-
-    // Reparent the buttons and place them on the right of the bar
-    long nX = m_pCloseBtn->GetPosPixel().getX() - 15 * fScaleFactor;
-    long nButtonGap = 5 * fScaleFactor;
-
-    for (auto const& actionBtn : m_aActionBtns)
-    {
-        long nButtonWidth = actionBtn->GetSizePixel().getWidth();
-        nX -= nButtonWidth;
-        actionBtn->SetPosSizePixel(Point(nX, 5 * fScaleFactor),
-                                   Size(nButtonWidth, 30 * fScaleFactor));
-        nX -= nButtonGap;
-    }
+    return m_aActionBtns.back()->get_widget();
+}
 
-    Point aPrimaryMessagePosition(32 * fScaleFactor + 10 * fScaleFactor, 10 * fScaleFactor);
-    Point aSecondaryMessagePosition(aPrimaryMessagePosition);
-    Size aMessageSize(nX - 35 * fScaleFactor, 20 * fScaleFactor);
-    Size aPrimaryTextSize = m_pPrimaryMessage->CalcMinimumSize(aMessageSize.getWidth());
-    Size aSecondaryTextSize = m_pSecondaryMessage->CalcMinimumSize(aMessageSize.getWidth()
-                                                                   - aPrimaryTextSize.getWidth());
-    if (!m_pPrimaryMessage->GetText().isEmpty())
-        aSecondaryMessagePosition.AdjustX(aPrimaryTextSize.getWidth() + 6 * fScaleFactor);
+SfxInfoBarWindow::~SfxInfoBarWindow() { disposeOnce(); }
 
-    long aMinimumHeight = std::max(m_pPrimaryMessage->CalcMinimumSize().getHeight(),
-                                   m_pSecondaryMessage->CalcMinimumSize().getHeight());
+void SfxInfoBarWindow::SetForeAndBackgroundColors(InfobarType eType)
+{
+    basegfx::BColor aMessageColor;
+    GetInfoBarColors(eType, m_aBackgroundColor, m_aForegroundColor, aMessageColor);
 
-    long aExtraHeight = aSecondaryTextSize.getHeight() - aMinimumHeight;
+    m_xPrimaryMessage->set_font_color(Color(aMessageColor));
+    m_xSecondaryMessage->set_font_color(Color(aMessageColor));
 
-    // The message won't be legible and the window will get too high
-    if (aMessageSize.getWidth() < 30)
+    Color aBackgroundColor(m_aBackgroundColor);
+    m_xPrimaryMessage->set_background(aBackgroundColor);
+    m_xSecondaryMessage->set_background(aBackgroundColor);
+    m_xContainer->set_background(aBackgroundColor);
+    if (m_xCloseBtn->get_visible())
     {
-        aExtraHeight = 0;
+        m_xCloseBtn->set_background(aBackgroundColor);
+        SetCloseButtonImage();
     }
+}
 
-    m_pPrimaryMessage->SetPosSizePixel(aPrimaryMessagePosition, aPrimaryTextSize);
-    m_pSecondaryMessage->SetPosSizePixel(aSecondaryMessagePosition, aSecondaryTextSize);
-    m_pImage->SetPosSizePixel(Point(4, 4), Size(32 * fScaleFactor, 32 * fScaleFactor));
+void SfxInfoBarWindow::dispose()
+{
+    for (auto& rxBtn : m_aActionBtns)
+        rxBtn.reset();
 
-    SetPosSizePixel(GetPosPixel(), Size(nWidth, INFO_BAR_BASE_HEIGHT * fScaleFactor
-                                                    + aExtraHeight * fScaleFactor));
+    m_xImage.reset();
+    m_xPrimaryMessage.reset();
+    m_xSecondaryMessage.reset();
+    m_xButtonBox.reset();
+    m_xCloseBtn.reset();
+    m_aActionBtns.clear();
+    InterimItemWindow::dispose();
 }
 
 void SfxInfoBarWindow::Update(const OUString& sPrimaryMessage, const OUString& sSecondaryMessage,
@@ -357,16 +338,16 @@ void SfxInfoBarWindow::Update(const OUString& sPrimaryMessage, const OUString& s
     {
         m_eType = eType;
         SetForeAndBackgroundColors(m_eType);
-        m_pImage->SetImage(Image(StockImage::Yes, GetInfoBarIconName(eType)));
+        m_xImage->set_from_icon_name(GetInfoBarIconName(eType));
     }
 
-    m_pPrimaryMessage->SetText(sPrimaryMessage);
-    m_pSecondaryMessage->SetText(sSecondaryMessage);
+    m_xPrimaryMessage->set_label(sPrimaryMessage);
+    m_xSecondaryMessage->set_text(sSecondaryMessage);
     Resize();
     Invalidate();
 }
 
-IMPL_LINK_NOARG(SfxInfoBarWindow, CloseHandler, Button*, void)
+IMPL_LINK_NOARG(SfxInfoBarWindow, CloseHandler, const OString&, void)
 {
     static_cast<SfxInfoBarContainerWindow*>(GetParent())->removeInfoBar(this);
 }
@@ -375,9 +356,15 @@ SfxInfoBarContainerWindow::SfxInfoBarContainerWindow(SfxInfoBarContainerChild* p
     : Window(pChildWin->GetParent(), WB_DIALOGCONTROL)
     , m_pChildWin(pChildWin)
     , m_pInfoBars()
+    , m_bResizing(false)
 {
+    m_aLayoutIdle.SetPriority(TaskPriority::HIGHEST);
+    m_aLayoutIdle.SetInvokeHandler(LINK(this, SfxInfoBarContainerWindow, DoUpdateLayout));
+    m_aLayoutIdle.SetDebugName("SfxInfoBarContainerWindow m_aLayoutIdle");
 }
 
+IMPL_LINK_NOARG(SfxInfoBarContainerWindow, DoUpdateLayout, Timer*, void) { m_pChildWin->Update(); }
+
 SfxInfoBarContainerWindow::~SfxInfoBarContainerWindow() { disposeOnce(); }
 
 void SfxInfoBarContainerWindow::dispose()
@@ -388,16 +375,17 @@ void SfxInfoBarContainerWindow::dispose()
     Window::dispose();
 }
 
-VclPtr<SfxInfoBarWindow>
-SfxInfoBarContainerWindow::appendInfoBar(const OUString& sId, const OUString& sPrimaryMessage,
-                                         const OUString& sSecondaryMessage, InfobarType ibType,
-                                         WinBits nMessageStyle, bool bShowCloseButton)
+VclPtr<SfxInfoBarWindow> SfxInfoBarContainerWindow::appendInfoBar(const OUString& sId,
+                                                                  const OUString& sPrimaryMessage,
+                                                                  const OUString& sSecondaryMessage,
+                                                                  InfobarType ibType,
+                                                                  bool bShowCloseButton)
 {
     if (!isInfobarEnabled(sId))
         return nullptr;
 
     auto pInfoBar = VclPtr<SfxInfoBarWindow>::Create(this, sId, sPrimaryMessage, sSecondaryMessage,
-                                                     ibType, nMessageStyle, bShowCloseButton);
+                                                     ibType, bShowCloseButton);
 
     basegfx::BColor aBackgroundColor;
     basegfx::BColor aForegroundColor;
@@ -436,8 +424,6 @@ void SfxInfoBarContainerWindow::removeInfoBar(VclPtr<SfxInfoBarWindow> const& pI
         m_pInfoBars.erase(it);
     }
 
-    Resize();
-
     m_pChildWin->Update();
 }
 
@@ -457,25 +443,46 @@ bool SfxInfoBarContainerWindow::isInfobarEnabled(const OUString& sId)
     return true;
 }
 
+// This triggers the SfxFrame to re-layout its childwindows
+void SfxInfoBarContainerWindow::TriggerUpdateLayout() { m_aLayoutIdle.Start(); }
+
 void SfxInfoBarContainerWindow::Resize()
 {
-    long nWidth = GetSizePixel().getWidth();
+    if (m_bResizing)
+        return;
+    m_bResizing = true;
+    const Size& rOrigSize = GetSizePixel();
+    auto nOrigWidth = rOrigSize.getWidth();
+    auto nOrigHeight = rOrigSize.getHeight();
+
     long nHeight = 0;
 
     for (auto& rxInfoBar : m_pInfoBars)
     {
-        Size aSize = rxInfoBar->GetSizePixel();
-        aSize.setWidth(nWidth);
+        Size aOrigSize = rxInfoBar->GetSizePixel();
+        Size aSize(nOrigWidth, aOrigSize.Height());
+
         Point aPos(0, nHeight);
+        // stage 1: provisionally size the infobar,
+        rxInfoBar->SetPosSizePixel(aPos, aSize);
+
+        // stage 2: perhaps allow height to stretch to fit
+        // the stage 1 width
+        aSize = rxInfoBar->DoLayout();
         rxInfoBar->SetPosSizePixel(aPos, aSize);
-        rxInfoBar->Resize();
         rxInfoBar->Show();
 
         // Stretch to fit the infobar(s)
         nHeight += aSize.getHeight();
     }
 
-    SetSizePixel(Size(nWidth, nHeight));
+    if (nOrigHeight != nHeight)
+    {
+        SetSizePixel(Size(nOrigWidth, nHeight));
+        TriggerUpdateLayout();
+    }
+
+    m_bResizing = false;
 }
 
 SFX_IMPL_POS_CHILDWINDOW_WITHID(SfxInfoBarContainerChild, SID_INFOBAR, SFX_OBJECTBAR_OBJECT);
@@ -502,6 +509,15 @@ SfxChildWinInfo SfxInfoBarContainerChild::GetInfo() const
 
 void SfxInfoBarContainerChild::Update()
 {
+    // Layout to current width, this may change the height
+    if (vcl::Window* pChild = GetWindow())
+    {
+        Size aSize(pChild->GetSizePixel());
+        pChild->Resize();
+        if (aSize == pChild->GetSizePixel())
+            return;
+    }
+
     // Refresh the frame to take the infobars container height change into account
     const sal_uInt16 nId = GetChildWindowId();
     SfxViewFrame* pVFrame = m_pBindings->GetDispatcher()->GetFrame();
diff --git a/sfx2/source/doc/objserv.cxx b/sfx2/source/doc/objserv.cxx
index 8ab9ea898e9a..47c25433ee51 100644
--- a/sfx2/source/doc/objserv.cxx
+++ b/sfx2/source/doc/objserv.cxx
@@ -1434,11 +1434,9 @@ void SfxObjectShell::GetState_Impl(SfxItemSet &rSet)
                             auto pInfoBar = pFrame->AppendInfoBar("signature", "", sMessage, aInfobarType);
                             if (pInfoBar == nullptr || pInfoBar->IsDisposed())
                                 return;
-                            VclPtrInstance<PushButton> xBtn(&(pFrame->GetWindow()));
-                            xBtn->SetText(SfxResId(STR_SIGNATURE_SHOW));
-                            xBtn->SetSizePixel(xBtn->GetOptimalSize());
-                            xBtn->SetClickHdl(LINK(this, SfxObjectShell, SignDocumentHandler));
-                            pInfoBar->addButton(xBtn);
+                            weld::Button& rBtn = pInfoBar->addButton();
+                            rBtn.set_label(SfxResId(STR_SIGNATURE_SHOW));
+                            rBtn.connect_clicked(LINK(this, SfxObjectShell, SignDocumentHandler));
                         }
                     }
                     else // info bar exists already
@@ -1475,7 +1473,7 @@ void SfxObjectShell::GetState_Impl(SfxItemSet &rSet)
     }
 }
 
-IMPL_LINK_NOARG(SfxObjectShell, SignDocumentHandler, Button*, void)
+IMPL_LINK_NOARG(SfxObjectShell, SignDocumentHandler, weld::Button&, void)
 {
     GetDispatcher()->Execute(SID_SIGNATURE);
 }
diff --git a/sfx2/source/view/sfxbasecontroller.cxx b/sfx2/source/view/sfxbasecontroller.cxx
index a4706277e869..8f4ba77305db 100644
--- a/sfx2/source/view/sfxbasecontroller.cxx
+++ b/sfx2/source/view/sfxbasecontroller.cxx
@@ -1398,15 +1398,13 @@ void SfxBaseController::ShowInfoBars( )
                                               InfobarType::WARNING);
     if (pInfoBar)
     {
-        VclPtrInstance<PushButton> xBtn(&pViewFrame->GetWindow());
-        xBtn->SetText(SfxResId(STR_CHECKOUT));
-        xBtn->SetSizePixel(xBtn->GetOptimalSize());
-        xBtn->SetClickHdl(LINK(this, SfxBaseController, CheckOutHandler));
-        pInfoBar->addButton(xBtn);
+        weld::Button &rBtn = pInfoBar->addButton();
+        rBtn.set_label(SfxResId(STR_CHECKOUT));
+        rBtn.connect_clicked(LINK(this, SfxBaseController, CheckOutHandler));
     }
 }
 
-IMPL_LINK_NOARG ( SfxBaseController, CheckOutHandler, Button*, void )
+IMPL_LINK_NOARG ( SfxBaseController, CheckOutHandler, weld::Button&, void )
 {
     if ( m_pData->m_pViewShell )
         m_pData->m_pViewShell->GetObjectShell()->CheckOut( );
@@ -1498,11 +1496,8 @@ void SAL_CALL SfxBaseController::appendInfobar(const OUString& sId, const OUStri
     {
         if (actionButton.First.isEmpty() || actionButton.Second.isEmpty())
             continue;
-        VclPtrInstance<PushButton> xBtn(&pViewFrame->GetWindow());
-        xBtn->SetText(actionButton.First);
-        xBtn->SetSizePixel(xBtn->GetOptimalSize());
-        xBtn->SetCommandHandler(actionButton.Second);
-        pInfoBar->addButton(xBtn);
+        weld::Button& rBtn = pInfoBar->addButton(&actionButton.Second);
+        rBtn.set_label(actionButton.First);
     }
 }
 
diff --git a/sfx2/source/view/viewfrm.cxx b/sfx2/source/view/viewfrm.cxx
index 9e4b4b280c59..ccb39350f223 100644
--- a/sfx2/source/view/viewfrm.cxx
+++ b/sfx2/source/view/viewfrm.cxx
@@ -1258,19 +1258,17 @@ void SfxViewFrame::AppendReadOnlyInfobar()
     {
         // SID_SIGNPDF opened a read-write PDF
         // read-only for signing purposes.
-        VclPtrInstance<PushButton> xSignButton(&GetWindow());
+        weld::Button& rSignButton = pInfoBar->addButton();
         if (bSignWithCert)
         {
-            xSignButton->SetText(SfxResId(STR_READONLY_FINISH_SIGN));
+            rSignButton.set_label(SfxResId(STR_READONLY_FINISH_SIGN));
         }
         else
         {
-            xSignButton->SetText(SfxResId(STR_READONLY_SIGN));
+            rSignButton.set_label(SfxResId(STR_READONLY_SIGN));
         }
 
-        xSignButton->SetSizePixel(xSignButton->GetOptimalSize());
-        xSignButton->SetClickHdl(LINK(this, SfxViewFrame, SignDocumentHandler));
-        pInfoBar->addButton(xSignButton);
+        rSignButton.connect_clicked(LINK(this, SfxViewFrame, SignDocumentHandler));
     }
 
     bool showEditDocumentButton = true;
@@ -1279,11 +1277,9 @@ void SfxViewFrame::AppendReadOnlyInfobar()
 
     if (showEditDocumentButton)
     {
-        VclPtrInstance<PushButton> xBtn(&GetWindow());
-        xBtn->SetText(SfxResId(STR_READONLY_EDIT));
-        xBtn->SetSizePixel(xBtn->GetOptimalSize());
-        xBtn->SetClickHdl(LINK(this, SfxViewFrame, SwitchReadOnlyHandler));
-        pInfoBar->addButton(xBtn);
+        weld::Button& rBtn = pInfoBar->addButton();
+        rBtn.set_label(SfxResId(STR_READONLY_EDIT));
+        rBtn.connect_clicked(LINK(this, SfxViewFrame, SwitchReadOnlyHandler));
     }
 }
 
@@ -1341,11 +1337,9 @@ void SfxViewFrame::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
                         VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("whatsnew", "", SfxResId(STR_WHATSNEW_TEXT), InfobarType::INFO);
                         if (pInfoBar)
                         {
-                            VclPtrInstance<PushButton> xWhatsNewButton(&GetWindow());
-                            xWhatsNewButton->SetText(SfxResId(STR_WHATSNEW_BUTTON));
-                            xWhatsNewButton->SetSizePixel(xWhatsNewButton->GetOptimalSize());
-                            xWhatsNewButton->SetClickHdl(LINK(this, SfxViewFrame, WhatsNewHandler));
-                            pInfoBar->addButton(xWhatsNewButton);
+                            weld::Button& rWhatsNewButton = pInfoBar->addButton();
+                            rWhatsNewButton.set_label(SfxResId(STR_WHATSNEW_BUTTON));
+                            rWhatsNewButton.connect_clicked(LINK(this, SfxViewFrame, WhatsNewHandler));
 
                             //update lastversion
                             std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
@@ -1384,11 +1378,9 @@ void SfxViewFrame::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
 
                     if (pInfoBar)
                     {
-                        VclPtrInstance<PushButton> xGetInvolvedButton(&GetWindow());
-                        xGetInvolvedButton->SetText(SfxResId(STR_GET_INVOLVED_BUTTON));
-                        xGetInvolvedButton->SetSizePixel(xGetInvolvedButton->GetOptimalSize());
-                        xGetInvolvedButton->SetClickHdl(LINK(this, SfxViewFrame, GetInvolvedHandler));
-                        pInfoBar->addButton(xGetInvolvedButton);
+                        weld::Button& rGetInvolvedButton = pInfoBar->addButton();
+                        rGetInvolvedButton.set_label(SfxResId(STR_GET_INVOLVED_BUTTON));
+                        rGetInvolvedButton.connect_clicked(LINK(this, SfxViewFrame, GetInvolvedHandler));
                     }
                 }
 
@@ -1413,11 +1405,9 @@ void SfxViewFrame::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
                     VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("donate", "", SfxResId(STR_DONATE_TEXT), InfobarType::INFO);
                     if (pInfoBar)
                     {
-                        VclPtrInstance<PushButton> xDonateButton(&GetWindow());
-                        xDonateButton->SetText(SfxResId(STR_DONATE_BUTTON));
-                        xDonateButton->SetSizePixel(xDonateButton->GetOptimalSize());
-                        xDonateButton->SetClickHdl(LINK(this, SfxViewFrame, DonationHandler));
-                        pInfoBar->addButton(xDonateButton);
+                        weld::Button& rDonateButton = pInfoBar->addButton();
+                        rDonateButton.set_label(SfxResId(STR_DONATE_BUTTON));
+                        rDonateButton.connect_clicked(LINK(this, SfxViewFrame, DonationHandler));
                     }
                 }
 
@@ -1546,33 +1536,33 @@ void SfxViewFrame::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
     }
 }
 
-IMPL_LINK_NOARG(SfxViewFrame, WhatsNewHandler, Button*, void)
+IMPL_LINK_NOARG(SfxViewFrame, WhatsNewHandler, weld::Button&, void)
 {
     GetDispatcher()->Execute(SID_WHATSNEW);
 }
 
-IMPL_LINK_NOARG(SfxViewFrame, GetInvolvedHandler, Button*, void)
+IMPL_LINK_NOARG(SfxViewFrame, GetInvolvedHandler, weld::Button&, void)
 {
     GetDispatcher()->Execute(SID_GETINVOLVED);
 }
 
-IMPL_LINK_NOARG(SfxViewFrame, DonationHandler, Button*, void)
+IMPL_LINK_NOARG(SfxViewFrame, DonationHandler, weld::Button&, void)
 {
     GetDispatcher()->Execute(SID_DONATION);
 }
 
-IMPL_LINK(SfxViewFrame, SwitchReadOnlyHandler, Button*, pButton, void)
+IMPL_LINK(SfxViewFrame, SwitchReadOnlyHandler, weld::Button&, rButton, void)
 {
     if (m_xObjSh.is() && m_xObjSh->IsSignPDF())
     {
-        SfxEditDocumentDialog aDialog(pButton->GetFrameWeld());
+        SfxEditDocumentDialog aDialog(&rButton);
         if (aDialog.run() != RET_OK)
             return;
     }
     GetDispatcher()->Execute(SID_EDITDOC);
 }
 
-IMPL_LINK_NOARG(SfxViewFrame, SignDocumentHandler, Button*, void)
+IMPL_LINK_NOARG(SfxViewFrame, SignDocumentHandler, weld::Button&, void)
 {
     GetDispatcher()->Execute(SID_SIGNATURE);
 }
@@ -3340,7 +3330,7 @@ VclPtr<SfxInfoBarWindow> SfxViewFrame::AppendInfoBar(const OUString& sId,
 
     SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow());
     auto pInfoBar = pInfoBarContainer->appendInfoBar(sId, sPrimaryMessage, sSecondaryMessage,
-                                                     aInfobarType, WB_LEFT | WB_VCENTER, bShowCloseButton);
+                                                     aInfobarType, bShowCloseButton);
     ShowChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
     return pInfoBar;
 }
diff --git a/sfx2/uiconfig/ui/extrabutton.ui b/sfx2/uiconfig/ui/extrabutton.ui
new file mode 100644
index 000000000000..69619f5d4ed0
--- /dev/null
+++ b/sfx2/uiconfig/ui/extrabutton.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="svt">
+  <requires lib="gtk+" version="3.18"/>
+  <object class="GtkBox" id="ExtraButton">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkButton" id="button">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="can_default">True</property>
+        <property name="receives_default">True</property>
+        <property name="always_show_image">True</property>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/infobar.ui b/sfx2/uiconfig/ui/infobar.ui
new file mode 100644
index 000000000000..73fcd6b1122a
--- /dev/null
+++ b/sfx2/uiconfig/ui/infobar.ui
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="svt">
+  <requires lib="gtk+" version="3.18"/>
+  <object class="GtkBox" id="InfoBar">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="hexpand">True</property>
+    <property name="vexpand">True</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkScrolledWindow">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="hexpand">True</property>
+        <property name="vexpand">True</property>
+        <property name="hscrollbar_policy">never</property>
+        <property name="vscrollbar_policy">never</property>
+        <property name="shadow_type">in</property>
+        <child>
+          <object class="GtkViewport">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkGrid">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hexpand">True</property>
+                <property name="border_width">2</property>
+                <property name="column_spacing">6</property>
+                <child>
+                  <object class="GtkBox" id="right">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="valign">start</property>
+                    <property name="spacing">3</property>
+                    <child>
+                      <object class="GtkBox" id="buttonbox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="valign">center</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkToolbar" id="closebar">
+                        <property name="can_focus">False</property>
+                        <property name="no_show_all">True</property>
+                        <property name="valign">center</property>
+                        <property name="toolbar_style">icons</property>
+                        <property name="show_arrow">False</property>
+                        <property name="icon_size">1</property>
+                        <child>
+                          <object class="GtkToolButton" id="close">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="tooltip_text" translatable="yes" context="infobar|close|tooltip_text">Close Infobar</property>
+                            <property name="icon_name">sfx2/res/closedoc.png</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="homogeneous">True</property>
+                          </packing>
+                        </child>
+                        <style>
+                          <class name="small-button"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkBox" id="left">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="valign">start</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkImage" id="image">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="valign">center</property>
+                        <property name="stock">gtk-missing-image</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="primary">
+                        <property name="can_focus">False</property>
+                        <property name="no_show_all">True</property>
+                        <property name="valign">center</property>
+                        <property name="vexpand">True</property>
+                        <property name="label">label</property>
+                        <property name="xalign">0</property>
+                        <property name="yalign">0</property>
+                        <attributes>
+                          <attribute name="weight" value="bold"/>
+                        </attributes>
+                        <child internal-child="accessible">
+                          <object class="AtkObject" id="primary-atkobject">
+                            <property name="AtkObject::accessible-role">static</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow">
+                    <property name="width_request">60</property>
+                    <property name="height_request">6</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hexpand">True</property>
+                    <property name="vexpand">True</property>
+                    <property name="hscrollbar_policy">never</property>
+                    <property name="vscrollbar_policy">never</property>
+                    <child>
+                      <object class="GtkTextView" id="secondary">
+                        <property name="width_request">60</property>
+                        <property name="height_request">6</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hexpand">True</property>
+                        <property name="vexpand">True</property>
+                        <property name="editable">False</property>
+                        <property name="wrap_mode">word-char</property>
+                        <property name="cursor_visible">False</property>
+                        <property name="accepts_tab">False</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+  </object>
+  <object class="GtkSizeGroup" id="sizegroup1">
+    <property name="mode">vertical</property>
+    <widgets>
+      <widget name="right"/>
+      <widget name="left"/>
+    </widgets>
+  </object>
+</interface>
diff --git a/solenv/sanitizers/ui/sfx.suppr b/solenv/sanitizers/ui/sfx.suppr
index b34b687c0128..4edcdb02a842 100644
--- a/solenv/sanitizers/ui/sfx.suppr
+++ b/solenv/sanitizers/ui/sfx.suppr
@@ -22,6 +22,7 @@ sfx2/uiconfig/ui/documentinfopage.ui://GtkLabel[@id='showtype'] orphan-label
 sfx2/uiconfig/ui/documentinfopage.ui://GtkLabel[@id='showtemplate'] orphan-label
 sfx2/uiconfig/ui/documentinfopage.ui://GtkImage[@id='icon'] no-labelled-by
 sfx2/uiconfig/ui/documentinfopage.ui://GtkLabel[@id='nameed'] orphan-label
+sfx2/uiconfig/ui/extrabutton.ui://GtkButton[@id='button'] button-no-label
 sfx2/uiconfig/ui/helpindexpage.ui://GtkEntry[@id='termentry'] no-labelled-by
 sfx2/uiconfig/ui/helpwindow.ui://GtkCheckButton[@id='checkbutton'] button-no-label
 sfx2/uiconfig/ui/helpwindow.ui://GtkToolButton[@id='index'] button-no-label
diff --git a/vcl/source/app/weldutils.cxx b/vcl/source/app/weldutils.cxx
index ee074df63d93..237645076330 100644
--- a/vcl/source/app/weldutils.cxx
+++ b/vcl/source/app/weldutils.cxx
@@ -7,6 +7,9 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <comphelper/processfactory.hxx>
 #include <svl/zforlist.hxx>
 #include <svl/zformat.hxx>
 #include <vcl/builderpage.hxx>
@@ -488,6 +491,61 @@ int GetMinimumEditHeight()
     std::unique_ptr<weld::Entry> xEntry(xBuilder->weld_entry("name_entry"));
     return xEntry->get_preferred_size().Height();
 }
+
+WidgetStatusListener::WidgetStatusListener(weld::Widget* widget, const OUString& aCommand)
+    : mWidget(widget)
+{
+    css::uno::Reference<css::uno::XComponentContext> xContext
+        = ::comphelper::getProcessComponentContext();
+    css::uno::Reference<css::frame::XDesktop2> xDesktop = css::frame::Desktop::create(xContext);
+
+    css::uno::Reference<css::frame::XFrame> xFrame(xDesktop->getActiveFrame());
+    if (!xFrame.is())
+        xFrame = xDesktop;
+
+    mxFrame = xFrame;
+
+    maCommandURL.Complete = aCommand;
+    css::uno::Reference<css::util::XURLTransformer> xParser
+        = css::util::URLTransformer::create(xContext);
+    xParser->parseStrict(maCommandURL);
+}
+
+void WidgetStatusListener::startListening()
+{
+    if (mxDispatch.is())
+        mxDispatch->removeStatusListener(this, maCommandURL);
+
+    css::uno::Reference<css::frame::XDispatchProvider> xDispatchProvider(mxFrame,
+                                                                         css::uno::UNO_QUERY);
+    if (!xDispatchProvider.is())
+        return;
+
+    mxDispatch = xDispatchProvider->queryDispatch(maCommandURL, "", 0);
+    if (mxDispatch.is())
+        mxDispatch->addStatusListener(this, maCommandURL);
+}
+
+void WidgetStatusListener::statusChanged(const css::frame::FeatureStateEvent& rEvent)
+{
+    mWidget->set_sensitive(rEvent.IsEnabled);
+}
+
+void WidgetStatusListener::disposing(const css::lang::EventObject& /*Source*/)
+{
+    mxDispatch.clear();
+}
+
+void WidgetStatusListener::dispose()
+{
+    if (mxDispatch.is())
+    {
+        mxDispatch->removeStatusListener(this, maCommandURL);
+        mxDispatch.clear();
+    }
+    mxFrame.clear();
+    mWidget = nullptr;
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */


More information about the Libreoffice-commits mailing list