[Libreoffice-commits] core.git: compilerplugins/clang cui/source include/svtools include/vcl sd/source svtools/inc svtools/source svx/source svx/uiconfig svx/UIConfig_svx.mk sw/inc vcl/source vcl/unx

Caolán McNamara (via logerrit) logerrit at kemper.freedesktop.org
Tue Apr 21 08:20:22 UTC 2020


 compilerplugins/clang/badstatics.cxx            |    4 
 cui/source/tabpages/chardlg.cxx                 |    2 
 include/svtools/ctrlbox.hxx                     |   61 +-
 include/vcl/combobox.hxx                        |    3 
 include/vcl/weld.hxx                            |   39 +
 sd/source/ui/animations/CustomAnimationList.cxx |    5 
 svtools/inc/pch/precompiled_svt.hxx             |    6 
 svtools/source/control/ctrlbox.cxx              |  503 ++++++++++------
 svx/UIConfig_svx.mk                             |    1 
 svx/source/tbxctrls/tbcontrl.cxx                |  296 +++++----
 svx/source/tbxctrls/tbunocontroller.cxx         |    8 
 svx/uiconfig/ui/fontnamebox.ui                  |   30 +
 sw/inc/pch/precompiled_swui.hxx                 |    8 
 vcl/source/app/salvtables.cxx                   |  120 ++++
 vcl/source/control/combobox.cxx                 |   12 
 vcl/source/control/imp_listbox.cxx              |    5 
 vcl/unx/gtk3/gtk3gtkinst.cxx                    |  718 +++++++++++++++++-------
 17 files changed, 1271 insertions(+), 550 deletions(-)

New commits:
commit 2e0a32b51681fb356699b4a722f461f55a46b890
Author:     Caolán McNamara <caolanm at redhat.com>
AuthorDate: Tue Apr 7 12:21:47 2020 +0100
Commit:     Caolán McNamara <caolanm at redhat.com>
CommitDate: Tue Apr 21 10:19:41 2020 +0200

    weld FontNameBox
    
    with custom row rendering
    
    Change-Id: Ia909b5b9ad56b6ea4611e9ea0a1e2cb0064a8cd4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/91841
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>

diff --git a/compilerplugins/clang/badstatics.cxx b/compilerplugins/clang/badstatics.cxx
index 60abc11c222b..e2a48f3462cc 100644
--- a/compilerplugins/clang/badstatics.cxx
+++ b/compilerplugins/clang/badstatics.cxx
@@ -197,7 +197,9 @@ public:
                     // sc/source/core/tool/adiasync.cxx, would leak
                     // ScAddInAsync* keys if that set is not empty at exit
                 || name == "g_aWindowList"
-                    //vcl/unx/gtk/a11y/atkutil.cxx, asserted empty at exit
+                    //vcl/unx/gtk3/a11y/gtk3atkutil.cxx, asserted empty at exit
+                || name == "gFontPreviewVirDevs"
+                    //svtools/source/control/ctrlbox.cxx, empty at exit
                 || name == "aLogger" // FormulaLogger& FormulaLogger::get() in sc/source/core/tool/formulalogger.cxx
                 || name == "m_aUncommittedRegistrations" // sw/source/uibase/dbui/dbmgr.cxx
                 || (loplugin::DeclCheck(pVarDecl).Var("aAllListeners")
diff --git a/cui/source/tabpages/chardlg.cxx b/cui/source/tabpages/chardlg.cxx
index 68eca4fb4d48..11aaf87aa0fc 100644
--- a/cui/source/tabpages/chardlg.cxx
+++ b/cui/source/tabpages/chardlg.cxx
@@ -766,7 +766,7 @@ void SvxCharNamePage::Reset_Impl( const SfxItemSet& rSet, LanguageGroup eLangGrp
     }
     else
     {
-        pSizeBox->set_entry_text( OUString() );
+        pSizeBox->set_active_or_entry_text(OUString());
         if ( eState <= SfxItemState::READONLY )
         {
             pSizeBox->set_sensitive(false);
diff --git a/include/svtools/ctrlbox.hxx b/include/svtools/ctrlbox.hxx
index a86722a7a603..3dd48f9dd985 100644
--- a/include/svtools/ctrlbox.hxx
+++ b/include/svtools/ctrlbox.hxx
@@ -21,10 +21,8 @@
 #define INCLUDED_SVTOOLS_CTRLBOX_HXX
 
 #include <svtools/svtdllapi.h>
-
 #include <editeng/borderline.hxx>
-
-#include <vcl/combobox.hxx>
+#include <vcl/idle.hxx>
 #include <vcl/metric.hxx>
 #include <vcl/weld.hxx>
 
@@ -33,6 +31,7 @@
 namespace weld { class CustomWeld; }
 
 class VirtualDevice;
+class BitmapEx;
 class BorderWidthImpl;
 class FontList;
 
@@ -327,30 +326,66 @@ private:
     void set_label_from_date();
 };
 
-class SVT_DLLPUBLIC FontNameBox : public ComboBox
+class SVT_DLLPUBLIC FontNameBox
 {
 private:
+    std::unique_ptr<weld::ComboBox> m_xComboBox;
     std::unique_ptr<ImplFontList> mpFontList;
+    size_t          mnPreviewProgress;
     bool            mbWYSIWYG;
     OUString        maFontMRUEntriesFile;
+    Idle            maUpdateIdle;
 
     SVT_DLLPRIVATE void         ImplCalcUserItemSize();
     SVT_DLLPRIVATE void         ImplDestroyFontList();
 
-protected:
+    DECL_LINK(CustomRenderHdl, weld::ComboBox::render_args, void);
+    DECL_STATIC_LINK(FontNameBox, CustomGetSizeHdl, weld::ComboBox::get_size_args, Size);
+    DECL_LINK(UpdateHdl, Timer*, void);
+
     void            LoadMRUEntries( const OUString& aFontMRUEntriesFile );
     void            SaveMRUEntries( const OUString& aFontMRUEntriesFile ) const;
-public:
-                    FontNameBox( vcl::Window* pParent,
-                                 WinBits nWinStyle );
-    virtual         ~FontNameBox() override;
-    virtual void    dispose() override;
 
-    virtual void    UserDraw( const UserDrawEvent& rUDEvt ) override;
+    OutputDevice&   CachePreview(size_t nIndex, Point* pTopLeft);
+
+public:
+    FontNameBox(std::unique_ptr<weld::ComboBox> p);
+    ~FontNameBox();
 
     void            Fill( const FontList* pList );
 
-    void            EnableWYSIWYG( bool bEnable );
+    void            EnableWYSIWYG();
+
+    void connect_changed(const Link<weld::ComboBox&, void>& rLink) { m_xComboBox->connect_changed(rLink); }
+    void connect_focus_in(const Link<weld::Widget&, void>& rLink) { m_xComboBox->connect_focus_in(rLink); }
+    void connect_focus_out(const Link<weld::Widget&, void>& rLink) { m_xComboBox->connect_focus_out(rLink); }
+    void connect_key_press(const Link<const KeyEvent&, bool>& rLink) { m_xComboBox->connect_key_press(rLink); }
+    int get_active() const { return m_xComboBox->get_active(); }
+    OUString get_active_text() const { return m_xComboBox->get_active_text(); }
+    void set_active_or_entry_text(const OUString& rText);
+    int get_count() const { return m_xComboBox->get_count(); }
+    OUString get_text(int nIndex) const { return m_xComboBox->get_text(nIndex); }
+    void set_sensitive(bool bSensitive) { m_xComboBox->set_sensitive(bSensitive); }
+    void save_value() { m_xComboBox->save_value(); }
+    OUString const& get_saved_value() const { return m_xComboBox->get_saved_value(); }
+    void select_entry_region(int nStartPos, int nEndPos) { m_xComboBox->select_entry_region(nStartPos, nEndPos); }
+    bool get_entry_selection_bounds(int& rStartPos, int& rEndPos) { return m_xComboBox->get_entry_selection_bounds(rStartPos, rEndPos); }
+    void clear() { m_xComboBox->clear(); }
+    void grab_focus() { m_xComboBox->grab_focus(); }
+    bool has_focus() const { return m_xComboBox->has_focus(); }
+    void connect_entry_activate(const Link<weld::ComboBox&, bool>& rLink) { m_xComboBox->connect_entry_activate(rLink); }
+    void connect_get_property_tree(const Link<boost::property_tree::ptree&, void>& rLink) { m_xComboBox->connect_get_property_tree(rLink); }
+    void set_entry_width_chars(int nWidth) { m_xComboBox->set_entry_width_chars(nWidth); }
+    void set_size_request(int nWidth, int nHeight) { m_xComboBox->set_size_request(nWidth, nHeight); }
+    int get_max_mru_count() { return m_xComboBox->get_max_mru_count(); }
+    void set_max_mru_count(int nCount) { m_xComboBox->set_max_mru_count(nCount); }
+
+    // font size is in points, not pixels, e.g. see Window::[G]etPointFont
+    vcl::Font get_font() { return m_xComboBox->get_font(); }
+    void set_entry_font(const vcl::Font& rFont) { m_xComboBox->set_entry_font(rFont); }
+    vcl::Font get_entry_font() { return m_xComboBox->get_entry_font(); }
+
+    void set_tooltip_text(const OUString& rTip) { m_xComboBox->set_tooltip_text(rTip); }
 
 private:
     void            InitFontMRUEntriesFile();
@@ -438,7 +473,7 @@ public:
     void connect_focus_out(const Link<weld::Widget&, void>& rLink) { m_aFocusOutHdl = rLink; }
     void connect_key_press(const Link<const KeyEvent&, bool>& rLink) { m_xComboBox->connect_key_press(rLink); }
     OUString get_active_text() const { return m_xComboBox->get_active_text(); }
-    void set_entry_text(const OUString& rText);
+    void set_active_or_entry_text(const OUString& rText);
     void set_sensitive(bool bSensitive) { m_xComboBox->set_sensitive(bSensitive); }
     int get_active() const { return m_xComboBox->get_active(); }
     int get_value() const;
diff --git a/include/vcl/combobox.hxx b/include/vcl/combobox.hxx
index 562072baa9ef..ef04fff46330 100644
--- a/include/vcl/combobox.hxx
+++ b/include/vcl/combobox.hxx
@@ -120,6 +120,7 @@ public:
     void            SetDoubleClickHdl(const Link<ComboBox&,void>& rLink);
     const Link<ComboBox&,void>&   GetDoubleClickHdl() const;
     void            SetEntryActivateHdl(const Link<Edit&,bool>& rLink);
+    void            SetUserDrawHdl(const Link<UserDrawEvent*, void>& rLink);
 
     Size            CalcMinimumSize() const override;
     virtual Size    GetOptimalSize() const override;
@@ -183,6 +184,8 @@ public:
 
     void SetWidthInChars(sal_Int32 nWidthInChars);
 
+    long GetDropDownEntryHeight() const;
+
     virtual bool set_property(const OString &rKey, const OUString &rValue) override;
 
     virtual FactoryFunction GetUITestFactory() const override;
diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx
index 951b591f654b..2d94323d6434 100644
--- a/include/vcl/weld.hxx
+++ b/include/vcl/weld.hxx
@@ -591,6 +591,12 @@ class VCL_DLLPUBLIC ComboBox : virtual public Container
 private:
     OUString m_sSavedValue;
 
+public:
+    // OUString is the id of the row, it may be null to measure the height of a generic line
+    typedef std::pair<vcl::RenderContext&, const OUString&> get_size_args;
+    typedef std::tuple<vcl::RenderContext&, const tools::Rectangle&, bool, const OUString&>
+        render_args;
+
 protected:
     Link<ComboBox&, void> m_aChangeHdl;
     Link<ComboBox&, void> m_aPopupToggledHdl;
@@ -600,6 +606,21 @@ protected:
     void signal_changed() { m_aChangeHdl.Call(*this); }
     virtual void signal_popup_toggled() { m_aPopupToggledHdl.Call(*this); }
 
+    Link<render_args, void> m_aRenderHdl;
+    void signal_custom_render(vcl::RenderContext& rDevice, const tools::Rectangle& rRect,
+                              bool bSelected, const OUString& rId)
+    {
+        m_aRenderHdl.Call(
+            std::tuple<vcl::RenderContext&, const tools::Rectangle, bool, const OUString&>(
+                rDevice, rRect, bSelected, rId));
+    }
+
+    Link<get_size_args, Size> m_aGetSizeHdl;
+    Size signal_custom_get_size(vcl::RenderContext& rDevice, const OUString& rId)
+    {
+        return m_aGetSizeHdl.Call(std::pair<vcl::RenderContext&, const OUString&>(rDevice, rId));
+    }
+
 public:
     virtual void insert(int pos, const OUString& rStr, const OUString* pId,
                         const OUString* pIconName, VirtualDevice* pImageSurface)
@@ -694,6 +715,20 @@ public:
     void save_value() { m_sSavedValue = get_active_text(); }
     OUString const& get_saved_value() const { return m_sSavedValue; }
     bool get_value_changed_from_saved() const { return m_sSavedValue != get_active_text(); }
+
+    // for custom rendering a row
+    void connect_custom_get_size(const Link<get_size_args, Size>& rLink) { m_aGetSizeHdl = rLink; }
+    void connect_custom_render(const Link<render_args, void>& rLink) { m_aRenderHdl = rLink; }
+    // call set_custom_renderer after setting custom callbacks
+    virtual void set_custom_renderer() = 0;
+    // create a virtual device compatible with the device passed in render_args wrt alpha
+    virtual VclPtr<VirtualDevice> create_render_virtual_device() const = 0;
+
+    // for mru support
+    virtual int get_max_mru_count() const = 0;
+    virtual void set_max_mru_count(int nCount) = 0;
+    virtual OUString get_mru_entries() const = 0;
+    virtual void set_mru_entries(const OUString& rEntries) = 0;
 };
 
 class VCL_DLLPUBLIC TreeIter
@@ -734,6 +769,7 @@ protected:
     std::function<int(const weld::TreeIter&, const weld::TreeIter&)> m_aCustomSort;
 
 public:
+    // OUString is the id of the row, it may be null to measure the height of a generic line
     typedef std::pair<vcl::RenderContext&, const OUString&> get_size_args;
     typedef std::tuple<vcl::RenderContext&, const tools::Rectangle&, bool, const OUString&>
         render_args;
@@ -1094,9 +1130,10 @@ public:
     bool get_value_changed_from_saved() const { return m_sSavedValue != get_selected_text(); }
 
     // for custom rendering a cell
-    virtual void set_column_custom_renderer(int nColumn) = 0;
     void connect_custom_get_size(const Link<get_size_args, Size>& rLink) { m_aGetSizeHdl = rLink; }
     void connect_custom_render(const Link<render_args, void>& rLink) { m_aRenderHdl = rLink; }
+    // call set_column_custom_renderer after setting custom callbacks
+    virtual void set_column_custom_renderer(int nColumn) = 0;
 
     // for dnd
     virtual bool get_dest_row_at_pos(const Point& rPos, weld::TreeIter* pResult) = 0;
diff --git a/sd/source/ui/animations/CustomAnimationList.cxx b/sd/source/ui/animations/CustomAnimationList.cxx
index 68ecf1362ce9..3b4c3b3afaa6 100644
--- a/sd/source/ui/animations/CustomAnimationList.cxx
+++ b/sd/source/ui/animations/CustomAnimationList.cxx
@@ -230,6 +230,7 @@ private:
     OUString        msEffectName;
     CustomAnimationEffectPtr mpEffect;
 
+public:
     static const long nIconWidth = 19;
     static const long nItemMinHeight = 38;
 };
@@ -275,6 +276,8 @@ IMPL_STATIC_LINK(CustomAnimationList, CustomGetSizeHdl, weld::TreeView::get_size
     const OUString& rId = aPayload.second;
 
     CustomAnimationListEntryItem* pItem = reinterpret_cast<CustomAnimationListEntryItem*>(rId.toInt64());
+    if (!pItem)
+        return Size(CustomAnimationListEntryItem::nIconWidth, CustomAnimationListEntryItem::nItemMinHeight);
     return pItem->GetSize(rRenderContext);
 }
 
@@ -426,7 +429,6 @@ CustomAnimationList::CustomAnimationList(std::unique_ptr<weld::TreeView> xTreeVi
     mxEmptyLabel->set_stack_background();
 
     mxTreeView->set_selection_mode(SelectionMode::Multiple);
-    mxTreeView->set_column_custom_renderer(0);
     mxTreeView->connect_changed(LINK(this, CustomAnimationList, SelectHdl));
     mxTreeView->connect_key_press(LINK(this, CustomAnimationList, KeyInputHdl));
     mxTreeView->connect_popup_menu(LINK(this, CustomAnimationList, CommandHdl));
@@ -436,6 +438,7 @@ CustomAnimationList::CustomAnimationList(std::unique_ptr<weld::TreeView> xTreeVi
     mxTreeView->connect_drag_begin(LINK(this, CustomAnimationList, DragBeginHdl));
     mxTreeView->connect_custom_get_size(LINK(this, CustomAnimationList, CustomGetSizeHdl));
     mxTreeView->connect_custom_render(LINK(this, CustomAnimationList, CustomRenderHdl));
+    mxTreeView->set_column_custom_renderer(0);
 }
 
 CustomAnimationListDropTarget::CustomAnimationListDropTarget(CustomAnimationList& rTreeView)
diff --git a/svtools/inc/pch/precompiled_svt.hxx b/svtools/inc/pch/precompiled_svt.hxx
index 2797de08da3a..40cfe76421db 100644
--- a/svtools/inc/pch/precompiled_svt.hxx
+++ b/svtools/inc/pch/precompiled_svt.hxx
@@ -13,7 +13,7 @@
  manual changes will be rewritten by the next run of update_pch.sh (which presumably
  also fixes all possible problems, so it's usually better to use it).
 
- Generated on 2020-02-19 12:45:36 using:
+ Generated on 2020-04-07 17:26:37 using:
  ./bin/update_pch svtools svt --cutoff=4 --exclude:system --include:module --exclude:local
 
  If after updating build fails, use the following command to locate conflicting headers:
@@ -37,6 +37,7 @@
 #include <math.h>
 #include <memory>
 #include <new>
+#include <optional>
 #include <ostream>
 #include <set>
 #include <stddef.h>
@@ -102,7 +103,6 @@
 #include <vcl/builder.hxx>
 #include <vcl/button.hxx>
 #include <vcl/checksum.hxx>
-#include <vcl/combobox.hxx>
 #include <vcl/commandevent.hxx>
 #include <vcl/ctrl.hxx>
 #include <vcl/dllapi.h>
@@ -162,6 +162,7 @@
 #include <basegfx/tuple/b2dtuple.hxx>
 #include <basegfx/tuple/b2ituple.hxx>
 #include <basegfx/tuple/b3dtuple.hxx>
+#include <basegfx/utils/common.hxx>
 #include <basegfx/vector/b2dsize.hxx>
 #include <basegfx/vector/b2dvector.hxx>
 #include <basegfx/vector/b2enums.hxx>
@@ -314,7 +315,6 @@
 #include <i18nutil/transliteration.hxx>
 #include <o3tl/cow_wrapper.hxx>
 #include <o3tl/deleter.hxx>
-#include <optional>
 #include <o3tl/safeint.hxx>
 #include <o3tl/strong_int.hxx>
 #include <o3tl/typed_flags_set.hxx>
diff --git a/svtools/source/control/ctrlbox.cxx b/svtools/source/control/ctrlbox.cxx
index 51895051551f..d1b9943dfd2c 100644
--- a/svtools/source/control/ctrlbox.cxx
+++ b/svtools/source/control/ctrlbox.cxx
@@ -61,7 +61,6 @@
 #define IMGOUTERTEXTSPACE 5
 #define EXTRAFONTSIZE 5
 #define GAPTOEXTRAPREVIEW 10
-#define MAXPREVIEWWIDTH 120
 #define MINGAPWIDTH 2
 
 #define FONTNAMEBOXMRUENTRIESFILE "/user/config/fontnameboxmruentries"
@@ -329,31 +328,43 @@ void DrawLine( OutputDevice& rDev, const basegfx::B2DPoint& rP1, const basegfx::
 
 }
 
-FontNameBox::FontNameBox( vcl::Window* pParent, WinBits nWinStyle ) :
-    ComboBox( pParent, nWinStyle )
+static Size gUserItemSz;
+static int gFontNameBoxes;
+static size_t gPreviewsPerDevice;
+static std::vector<VclPtr<VirtualDevice>> gFontPreviewVirDevs;
+static std::vector<OUString> gRenderedFontNames;
+
+FontNameBox::FontNameBox(std::unique_ptr<weld::ComboBox> p)
+    : m_xComboBox(std::move(p))
+    , mnPreviewProgress(0)
+    , mbWYSIWYG(false)
+    , maUpdateIdle("FontNameBox Preview Update")
 {
-    mbWYSIWYG = false;
+    ++gFontNameBoxes;
     InitFontMRUEntriesFile();
-}
 
-FontNameBox::~FontNameBox()
-{
-    disposeOnce();
+    maUpdateIdle.SetPriority(TaskPriority::LOWEST);
+    maUpdateIdle.SetInvokeHandler(LINK(this, FontNameBox, UpdateHdl));
 }
 
-void FontNameBox::dispose()
+FontNameBox::~FontNameBox()
 {
     if (mpFontList)
     {
         SaveMRUEntries (maFontMRUEntriesFile);
         ImplDestroyFontList();
     }
-    ComboBox::dispose();
+    --gFontNameBoxes;
+    if (!gFontNameBoxes)
+    {
+        gFontPreviewVirDevs.clear();
+        gRenderedFontNames.clear();
+    }
 }
 
-void FontNameBox::SaveMRUEntries( const OUString& aFontMRUEntriesFile ) const
+void FontNameBox::SaveMRUEntries(const OUString& aFontMRUEntriesFile) const
 {
-    OString aEntries(OUStringToOString(GetMRUEntries(),
+    OString aEntries(OUStringToOString(m_xComboBox->get_mru_entries(),
         RTL_TEXTENCODING_UTF8));
 
     if (aEntries.isEmpty() || aFontMRUEntriesFile.isEmpty())
@@ -392,7 +403,7 @@ void FontNameBox::LoadMRUEntries( const OUString& aFontMRUEntriesFile )
     aStream.ReadLine( aLine );
     OUString aEntries = OStringToOUString(aLine,
         RTL_TEXTENCODING_UTF8);
-    SetMRUEntries( aEntries );
+    m_xComboBox->set_mru_entries(aEntries);
 }
 
 void FontNameBox::InitFontMRUEntriesFile()
@@ -415,61 +426,71 @@ void FontNameBox::ImplDestroyFontList()
 void FontNameBox::Fill( const FontList* pList )
 {
     // store old text and clear box
-    OUString aOldText = GetText();
-    OUString rEntries = GetMRUEntries();
+    OUString aOldText = m_xComboBox->get_active_text();
+    OUString rEntries = m_xComboBox->get_mru_entries();
     bool bLoadFromFile = rEntries.isEmpty();
-    Clear();
+    m_xComboBox->freeze();
+    m_xComboBox->clear();
 
     ImplDestroyFontList();
     mpFontList.reset(new ImplFontList);
 
     // insert fonts
-    sal_uInt16 nFontCount = pList->GetFontNameCount();
-    for ( sal_uInt16 i = 0; i < nFontCount; i++ )
+    size_t nFontCount = pList->GetFontNameCount();
+    for (size_t i = 0; i < nFontCount; ++i)
     {
-        const FontMetric& rFontMetric = pList->GetFontName( i );
-        sal_Int32 nIndex = InsertEntry( rFontMetric.GetFamilyName() );
-        if ( nIndex < static_cast<sal_Int32>(mpFontList->size()) ) {
-            ImplFontList::iterator it = mpFontList->begin();
-            ::std::advance( it, nIndex );
-            mpFontList->insert( it, rFontMetric );
-        } else {
-            mpFontList->push_back( rFontMetric );
-        }
+        const FontMetric& rFontMetric = pList->GetFontName(i);
+        m_xComboBox->append(OUString::number(i), rFontMetric.GetFamilyName());
+        mpFontList->push_back(rFontMetric);
     }
 
-    if ( bLoadFromFile )
-        LoadMRUEntries (maFontMRUEntriesFile);
+    if (bLoadFromFile)
+        LoadMRUEntries(maFontMRUEntriesFile);
     else
-        SetMRUEntries( rEntries );
+        m_xComboBox->set_mru_entries(rEntries);
 
-    ImplCalcUserItemSize();
+    m_xComboBox->thaw();
+
+    if (mbWYSIWYG)
+    {
+        mnPreviewProgress = 0;
+        maUpdateIdle.Start();
+    }
 
     // restore text
     if (!aOldText.isEmpty())
-        SetText( aOldText );
+        set_active_or_entry_text(aOldText);
 }
 
-void FontNameBox::EnableWYSIWYG( bool bEnable )
+void FontNameBox::EnableWYSIWYG()
 {
-    if ( bEnable != mbWYSIWYG )
+    if (mbWYSIWYG)
+        return;
+    mbWYSIWYG = true;
+
+    static bool bGlobalsInited;
+    if (!bGlobalsInited)
     {
-        mbWYSIWYG = bEnable;
-        EnableUserDraw( mbWYSIWYG );
-        ImplCalcUserItemSize();
+        gUserItemSz = Size(m_xComboBox->get_approximate_digit_width() * 52, m_xComboBox->get_text_height());
+        gUserItemSz.setHeight(gUserItemSz.Height() * 16);
+        gUserItemSz.setHeight(gUserItemSz.Height() / 10);
+
+        size_t nMaxDeviceHeight = SAL_MAX_INT16 / 2; // see limitXCreatePixmap
+        gPreviewsPerDevice = nMaxDeviceHeight / gUserItemSz.Height();
+
+        bGlobalsInited = true;
     }
+
+    m_xComboBox->connect_custom_get_size(LINK(this, FontNameBox, CustomGetSizeHdl));
+    m_xComboBox->connect_custom_render(LINK(this, FontNameBox, CustomRenderHdl));
+    m_xComboBox->set_custom_renderer();
+
+    mbWYSIWYG = true;
 }
 
-void FontNameBox::ImplCalcUserItemSize()
+IMPL_STATIC_LINK_NOARG(FontNameBox, CustomGetSizeHdl, weld::ComboBox::get_size_args, Size)
 {
-    Size aUserItemSz;
-    if ( mbWYSIWYG && mpFontList )
-    {
-        aUserItemSz = Size(MAXPREVIEWWIDTH, GetTextHeight() );
-        aUserItemSz.setHeight( aUserItemSz.Height() * 16 );
-        aUserItemSz.setHeight( aUserItemSz.Height() / 10 );
-    }
-    SetUserItemSize( aUserItemSz );
+    return gUserItemSz;
 }
 
 namespace
@@ -500,192 +521,275 @@ namespace
     }
 }
 
-void FontNameBox::UserDraw( const UserDrawEvent& rUDEvt )
+IMPL_LINK_NOARG(FontNameBox, UpdateHdl, Timer*, void)
 {
-    assert( mpFontList );
+    CachePreview(mnPreviewProgress++, nullptr);
+    if (mnPreviewProgress < mpFontList->size())
+        maUpdateIdle.Start();
+}
 
-    FontMetric& rFontMetric = (*mpFontList)[ rUDEvt.GetItemId() ];
-    Point aTopLeft = rUDEvt.GetRect().TopLeft();
-    long nX = aTopLeft.X();
-    long nH = rUDEvt.GetRect().GetHeight();
+static void DrawPreview(const FontMetric& rFontMetric, const Point& rTopLeft, OutputDevice& rDevice, bool bSelected)
+{
+    rDevice.Push(PushFlags::TEXTCOLOR);
 
-    if ( mbWYSIWYG )
-    {
-        nX += IMGOUTERTEXTSPACE;
+    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+    if (bSelected)
+        rDevice.SetTextColor(rStyleSettings.GetHighlightTextColor());
+    else
+        rDevice.SetTextColor(rStyleSettings.GetDialogTextColor());
 
-        const bool bSymbolFont = isSymbolFont(rFontMetric);
-        vcl::RenderContext* pRenderContext = rUDEvt.GetRenderContext();
+    long nX = rTopLeft.X();
+    long nH = gUserItemSz.Height();
 
-        Color aTextColor = pRenderContext->GetTextColor();
-        vcl::Font aOldFont(pRenderContext->GetFont());
-        Size aSize( aOldFont.GetFontSize() );
-        aSize.AdjustHeight(EXTRAFONTSIZE );
-        vcl::Font aFont( rFontMetric );
-        aFont.SetFontSize( aSize );
-        pRenderContext->SetFont(aFont);
-        pRenderContext->SetTextColor(aTextColor);
+    nX += IMGOUTERTEXTSPACE;
 
-        bool bUsingCorrectFont = true;
-        tools::Rectangle aTextRect;
+    const bool bSymbolFont = isSymbolFont(rFontMetric);
 
-        // Preview the font name
-        const OUString& sFontName = rFontMetric.GetFamilyName();
+    vcl::Font aOldFont(rDevice.GetFont());
+    Size aSize( aOldFont.GetFontSize() );
+    aSize.AdjustHeight(EXTRAFONTSIZE );
+    vcl::Font aFont( rFontMetric );
+    aFont.SetFontSize( aSize );
+    rDevice.SetFont(aFont);
 
-        //If it shouldn't or can't draw its own name because it doesn't have the glyphs
-        if (!canRenderNameOfSelectedFont(*pRenderContext))
-            bUsingCorrectFont = false;
-        else
-        {
-            //Make sure it fits in the available height, shrinking the font if necessary
-            bUsingCorrectFont = shrinkFontToFit(sFontName, nH, aFont, *pRenderContext, aTextRect) != 0;
-        }
+    bool bUsingCorrectFont = true;
+    tools::Rectangle aTextRect;
 
-        if (!bUsingCorrectFont)
-        {
-            pRenderContext->SetFont(aOldFont);
-            pRenderContext->GetTextBoundRect(aTextRect, sFontName);
-        }
+    // Preview the font name
+    const OUString& sFontName = rFontMetric.GetFamilyName();
 
-        long nTextHeight = aTextRect.GetHeight();
-        long nDesiredGap = (nH-nTextHeight)/2;
-        long nVertAdjust = nDesiredGap - aTextRect.Top();
-        Point aPos( nX, aTopLeft.Y() + nVertAdjust );
-        pRenderContext->DrawText(aPos, sFontName);
-        long nTextX = aPos.X() + aTextRect.GetWidth() + GAPTOEXTRAPREVIEW;
+    //If it shouldn't or can't draw its own name because it doesn't have the glyphs
+    if (!canRenderNameOfSelectedFont(rDevice))
+        bUsingCorrectFont = false;
+    else
+    {
+        //Make sure it fits in the available height, shrinking the font if necessary
+        bUsingCorrectFont = shrinkFontToFit(sFontName, nH, aFont, rDevice, aTextRect) != 0;
+    }
 
-        if (!bUsingCorrectFont)
-            pRenderContext->SetFont(aFont);
+    if (!bUsingCorrectFont)
+    {
+        rDevice.SetFont(aOldFont);
+        rDevice.GetTextBoundRect(aTextRect, sFontName);
+    }
 
-        OUString sSampleText;
+    long nTextHeight = aTextRect.GetHeight();
+    long nDesiredGap = (nH-nTextHeight)/2;
+    long nVertAdjust = nDesiredGap - aTextRect.Top();
+    Point aPos( nX, rTopLeft.Y() + nVertAdjust );
+    rDevice.DrawText(aPos, sFontName);
+    long nTextX = aPos.X() + aTextRect.GetWidth() + GAPTOEXTRAPREVIEW;
 
-        if (!bSymbolFont)
-        {
-            const bool bNameBeginsWithLatinText = rFontMetric.GetFamilyName()[0] <= 'z';
+    if (!bUsingCorrectFont)
+        rDevice.SetFont(aFont);
 
-            if (bNameBeginsWithLatinText || !bUsingCorrectFont)
-                sSampleText = makeShortRepresentativeTextForSelectedFont(*pRenderContext);
-        }
+    OUString sSampleText;
+
+    if (!bSymbolFont)
+    {
+        const bool bNameBeginsWithLatinText = rFontMetric.GetFamilyName()[0] <= 'z';
 
-        //If we're not a symbol font, but could neither render our own name and
-        //we can't determine what script it would like to render, then try a
-        //few well known scripts
-        if (sSampleText.isEmpty() && !bUsingCorrectFont)
+        if (bNameBeginsWithLatinText || !bUsingCorrectFont)
+            sSampleText = makeShortRepresentativeTextForSelectedFont(rDevice);
+    }
+
+    //If we're not a symbol font, but could neither render our own name and
+    //we can't determine what script it would like to render, then try a
+    //few well known scripts
+    if (sSampleText.isEmpty() && !bUsingCorrectFont)
+    {
+        static const UScriptCode aScripts[] =
         {
-            static const UScriptCode aScripts[] =
-            {
-                USCRIPT_ARABIC,
-                USCRIPT_HEBREW,
-
-                USCRIPT_BENGALI,
-                USCRIPT_GURMUKHI,
-                USCRIPT_GUJARATI,
-                USCRIPT_ORIYA,
-                USCRIPT_TAMIL,
-                USCRIPT_TELUGU,
-                USCRIPT_KANNADA,
-                USCRIPT_MALAYALAM,
-                USCRIPT_SINHALA,
-                USCRIPT_DEVANAGARI,
-
-                USCRIPT_THAI,
-                USCRIPT_LAO,
-                USCRIPT_GEORGIAN,
-                USCRIPT_TIBETAN,
-                USCRIPT_SYRIAC,
-                USCRIPT_MYANMAR,
-                USCRIPT_ETHIOPIC,
-                USCRIPT_KHMER,
-                USCRIPT_MONGOLIAN,
-
-                USCRIPT_KOREAN,
-                USCRIPT_JAPANESE,
-                USCRIPT_HAN,
-                USCRIPT_SIMPLIFIED_HAN,
-                USCRIPT_TRADITIONAL_HAN,
-
-                USCRIPT_GREEK
-            };
-
-            for (const UScriptCode& rScript : aScripts)
+            USCRIPT_ARABIC,
+            USCRIPT_HEBREW,
+
+            USCRIPT_BENGALI,
+            USCRIPT_GURMUKHI,
+            USCRIPT_GUJARATI,
+            USCRIPT_ORIYA,
+            USCRIPT_TAMIL,
+            USCRIPT_TELUGU,
+            USCRIPT_KANNADA,
+            USCRIPT_MALAYALAM,
+            USCRIPT_SINHALA,
+            USCRIPT_DEVANAGARI,
+
+            USCRIPT_THAI,
+            USCRIPT_LAO,
+            USCRIPT_GEORGIAN,
+            USCRIPT_TIBETAN,
+            USCRIPT_SYRIAC,
+            USCRIPT_MYANMAR,
+            USCRIPT_ETHIOPIC,
+            USCRIPT_KHMER,
+            USCRIPT_MONGOLIAN,
+
+            USCRIPT_KOREAN,
+            USCRIPT_JAPANESE,
+            USCRIPT_HAN,
+            USCRIPT_SIMPLIFIED_HAN,
+            USCRIPT_TRADITIONAL_HAN,
+
+            USCRIPT_GREEK
+        };
+
+        for (const UScriptCode& rScript : aScripts)
+        {
+            OUString sText = makeShortRepresentativeTextForScript(rScript);
+            if (!sText.isEmpty())
             {
-                OUString sText = makeShortRepresentativeTextForScript(rScript);
-                if (!sText.isEmpty())
+                bool bHasSampleTextGlyphs = (-1 == rDevice.HasGlyphs(aFont, sText));
+                if (bHasSampleTextGlyphs)
                 {
-                    bool bHasSampleTextGlyphs = (-1 == pRenderContext->HasGlyphs(aFont, sText));
-                    if (bHasSampleTextGlyphs)
-                    {
-                        sSampleText = sText;
-                        break;
-                    }
+                    sSampleText = sText;
+                    break;
                 }
             }
+        }
 
-            static const UScriptCode aMinimalScripts[] =
-            {
-                USCRIPT_HEBREW, //e.g. biblical hebrew
-                USCRIPT_GREEK
-            };
+        static const UScriptCode aMinimalScripts[] =
+        {
+            USCRIPT_HEBREW, //e.g. biblical hebrew
+            USCRIPT_GREEK
+        };
 
-            for (const UScriptCode& rMinimalScript : aMinimalScripts)
+        for (const UScriptCode& rMinimalScript : aMinimalScripts)
+        {
+            OUString sText = makeShortMinimalTextForScript(rMinimalScript);
+            if (!sText.isEmpty())
             {
-                OUString sText = makeShortMinimalTextForScript(rMinimalScript);
-                if (!sText.isEmpty())
+                bool bHasSampleTextGlyphs = (-1 == rDevice.HasGlyphs(aFont, sText));
+                if (bHasSampleTextGlyphs)
                 {
-                    bool bHasSampleTextGlyphs = (-1 == pRenderContext->HasGlyphs(aFont, sText));
-                    if (bHasSampleTextGlyphs)
-                    {
-                        sSampleText = sText;
-                        break;
-                    }
+                    sSampleText = sText;
+                    break;
                 }
             }
         }
+    }
 
-        //If we're a symbol font, or for some reason the font still couldn't
-        //render something representative of what it would like to render then
-        //make up some semi-random text that it *can* display
-        if (bSymbolFont || (!bUsingCorrectFont && sSampleText.isEmpty()))
-            sSampleText = makeShortRepresentativeSymbolTextForSelectedFont(*pRenderContext);
+    //If we're a symbol font, or for some reason the font still couldn't
+    //render something representative of what it would like to render then
+    //make up some semi-random text that it *can* display
+    if (bSymbolFont || (!bUsingCorrectFont && sSampleText.isEmpty()))
+        sSampleText = makeShortRepresentativeSymbolTextForSelectedFont(rDevice);
 
-        if (!sSampleText.isEmpty())
-        {
-            const Size &rItemSize = rUDEvt.GetWindow()->GetOutputSize();
+    if (!sSampleText.isEmpty())
+    {
+        const Size &rItemSize = gUserItemSz;
 
-            //leave a little border at the edge
-            long nSpace = rItemSize.Width() - nTextX - IMGOUTERTEXTSPACE;
-            if (nSpace >= 0)
+        //leave a little border at the edge
+        long nSpace = rItemSize.Width() - nTextX - IMGOUTERTEXTSPACE;
+        if (nSpace >= 0)
+        {
+            //Make sure it fits in the available height, and get how wide that would be
+            long nWidth = shrinkFontToFit(sSampleText, nH, aFont, rDevice, aTextRect);
+            //Chop letters off until it fits in the available width
+            while (nWidth > nSpace || nWidth > gUserItemSz.Width())
             {
-                //Make sure it fits in the available height, and get how wide that would be
-                long nWidth = shrinkFontToFit(sSampleText, nH, aFont, *pRenderContext, aTextRect);
-                //Chop letters off until it fits in the available width
-                while (nWidth > nSpace || nWidth > MAXPREVIEWWIDTH)
-                {
-                    sSampleText = sSampleText.copy(0, sSampleText.getLength()-1);
-                    nWidth = pRenderContext->GetTextBoundRect(aTextRect, sSampleText) ?
-                             aTextRect.GetWidth() : 0;
-                }
+                sSampleText = sSampleText.copy(0, sSampleText.getLength()-1);
+                nWidth = rDevice.GetTextBoundRect(aTextRect, sSampleText) ?
+                         aTextRect.GetWidth() : 0;
+            }
 
-                //center the text on the line
-                if (!sSampleText.isEmpty() && nWidth)
-                {
-                    nTextHeight = aTextRect.GetHeight();
-                    nDesiredGap = (nH-nTextHeight)/2;
-                    nVertAdjust = nDesiredGap - aTextRect.Top();
-                    aPos = Point(nTextX + nSpace - nWidth, aTopLeft.Y() + nVertAdjust);
-                    pRenderContext->DrawText(aPos, sSampleText);
-                }
+            //center the text on the line
+            if (!sSampleText.isEmpty() && nWidth)
+            {
+                nTextHeight = aTextRect.GetHeight();
+                nDesiredGap = (nH-nTextHeight)/2;
+                nVertAdjust = nDesiredGap - aTextRect.Top();
+                aPos = Point(nTextX + nSpace - nWidth, rTopLeft.Y() + nVertAdjust);
+                rDevice.DrawText(aPos, sSampleText);
             }
         }
+    }
+
+    rDevice.SetFont(aOldFont);
+    rDevice.Pop();
+}
+
+OutputDevice& FontNameBox::CachePreview(size_t nIndex, Point* pTopLeft)
+{
+    SolarMutexGuard aGuard;
+    const FontMetric& rFontMetric = (*mpFontList)[nIndex];
+    const OUString& rFontName = rFontMetric.GetFamilyName();
+
+    size_t nPreviewIndex;
+    auto xFind = std::find(gRenderedFontNames.begin(), gRenderedFontNames.end(), rFontName);
+    bool bPreviewAvailable = xFind != gRenderedFontNames.end();
+    if (!bPreviewAvailable)
+    {
+        nPreviewIndex = gRenderedFontNames.size();
+        gRenderedFontNames.push_back(rFontName);
+    }
+    else
+        nPreviewIndex = std::distance(gRenderedFontNames.begin(), xFind);
+
+    size_t nPage = nPreviewIndex / gPreviewsPerDevice;
+    size_t nIndexInPage = nPreviewIndex - (nPage * gPreviewsPerDevice);
+
+    Point aTopLeft(0, gUserItemSz.Height() * nIndexInPage);
+
+    if (!bPreviewAvailable)
+    {
+        if (nPage >= gFontPreviewVirDevs.size())
+        {
+            gFontPreviewVirDevs.emplace_back(m_xComboBox->create_render_virtual_device());
+            VirtualDevice& rDevice = *gFontPreviewVirDevs.back();
+            rDevice.SetOutputSizePixel(Size(gUserItemSz.Width(), gUserItemSz.Height() * gPreviewsPerDevice));
+            if (vcl::Window* pDefaultDevice = dynamic_cast<vcl::Window*>(Application::GetDefaultDevice()))
+                pDefaultDevice->SetPointFont(rDevice, m_xComboBox->get_font());
+            assert(gFontPreviewVirDevs.size() == nPage + 1);
+        }
+
+        DrawPreview(rFontMetric, aTopLeft, *gFontPreviewVirDevs.back(), false);
+    }
+
+    if (pTopLeft)
+        *pTopLeft = aTopLeft;
+
+    return *gFontPreviewVirDevs[nPage];
+}
+
+IMPL_LINK(FontNameBox, CustomRenderHdl, weld::ComboBox::render_args, aPayload, void)
+{
+    vcl::RenderContext& rRenderContext = std::get<0>(aPayload);
+    const ::tools::Rectangle& rRect = std::get<1>(aPayload);
+    bool bSelected = std::get<2>(aPayload);
+    const OUString& rId = std::get<3>(aPayload);
+
+    sal_uInt32 nIndex = rId.toUInt32();
+
+    Point aDestPoint(rRect.TopLeft());
+    auto nMargin = (rRect.GetHeight() - gUserItemSz.Height()) / 2;
+    aDestPoint.AdjustY(nMargin);
 
-        pRenderContext->SetFont(aOldFont);
-        DrawEntry( rUDEvt, false, false);   // draw separator
+    if (bSelected)
+    {
+        const FontMetric& rFontMetric = (*mpFontList)[nIndex];
+        DrawPreview(rFontMetric, aDestPoint, rRenderContext, true);
     }
     else
     {
-        DrawEntry( rUDEvt, true, true );
+        // use cache of unselected entries
+        Point aTopLeft;
+        OutputDevice& rDevice = CachePreview(nIndex, &aTopLeft);
+
+        rRenderContext.DrawOutDev(aDestPoint, gUserItemSz,
+                                  aTopLeft, gUserItemSz,
+                                  rDevice);
     }
 }
 
+void FontNameBox::set_active_or_entry_text(const OUString& rText)
+{
+    const int nFound = m_xComboBox->find_text(rText);
+    if (nFound != -1)
+        m_xComboBox->set_active(nFound);
+    else
+        m_xComboBox->set_entry_text(rText);
+}
+
 FontStyleBox::FontStyleBox(std::unique_ptr<weld::ComboBox> p)
     : m_xComboBox(std::move(p))
 {
@@ -866,9 +970,13 @@ FontSizeBox::FontSizeBox(std::unique_ptr<weld::ComboBox> p)
     m_xComboBox->connect_changed(LINK(this, FontSizeBox, ModifyHdl));
 }
 
-void FontSizeBox::set_entry_text(const OUString& rText)
+void FontSizeBox::set_active_or_entry_text(const OUString& rText)
 {
-    m_xComboBox->set_entry_text(rText);
+    const int nFound = m_xComboBox->find_text(rText);
+    if (nFound != -1)
+        m_xComboBox->set_active(nFound);
+    else
+        m_xComboBox->set_entry_text(rText);
 }
 
 IMPL_LINK(FontSizeBox, ReformatHdl, weld::Widget&, rWidget, void)
@@ -1020,7 +1128,7 @@ void FontSizeBox::Fill( const FontMetric* pFontMetric, const FontList* pList )
         ++pTempAry;
     }
 
-    m_xComboBox->set_entry_text(aStr);
+    set_active_or_entry_text(aStr);
     m_xComboBox->select_entry_region(nSelectionStart, nSelectionEnd);
     m_xComboBox->thaw();
 }
@@ -1105,7 +1213,7 @@ void FontSizeBox::SetRelative( bool bNewRelative )
             Fill( &aFontMetric, pFontList );
     }
 
-    m_xComboBox->set_entry_text(aStr);
+    set_active_or_entry_text(aStr);
     m_xComboBox->select_entry_region(nSelectionStart, nSelectionEnd);
 }
 
@@ -1157,10 +1265,7 @@ void FontSizeBox::SetValue(int nNewValue, FieldUnit eInUnit)
         }
     }
     OUString aResult = format_number(nTempValue);
-    const int nFound = m_xComboBox->find_text(aResult);
-    if (nFound != -1)
-        m_xComboBox->set_active(nFound);
-    m_xComboBox->set_entry_text(aResult);
+    set_active_or_entry_text(aResult);
 }
 
 void FontSizeBox::set_value(int nNewValue)
diff --git a/svx/UIConfig_svx.mk b/svx/UIConfig_svx.mk
index 567464c49cb5..98ef36a88c6c 100644
--- a/svx/UIConfig_svx.mk
+++ b/svx/UIConfig_svx.mk
@@ -64,6 +64,7 @@ $(eval $(call gb_UIConfig_add_uifiles,svx,\
 	svx/uiconfig/ui/fontworkgallerydialog \
 	svx/uiconfig/ui/fontworkspacingdialog \
 	svx/uiconfig/ui/fontsizebox \
+	svx/uiconfig/ui/fontnamebox \
 	svx/uiconfig/ui/formdatamenu \
 	svx/uiconfig/ui/formfielddialog \
 	svx/uiconfig/ui/formlinkwarndialog \
diff --git a/svx/source/tbxctrls/tbcontrl.cxx b/svx/source/tbxctrls/tbcontrl.cxx
index 17a956579d88..9dd130a1dcc1 100644
--- a/svx/source/tbxctrls/tbcontrl.cxx
+++ b/svx/source/tbxctrls/tbcontrl.cxx
@@ -25,6 +25,7 @@
 #include <svl/poolitem.hxx>
 #include <svl/itemset.hxx>
 #include <vcl/commandinfoprovider.hxx>
+#include <vcl/combobox.hxx>
 #include <vcl/event.hxx>
 #include <vcl/menubtn.hxx>
 #include <vcl/toolbox.hxx>
@@ -35,6 +36,7 @@
 #include <svl/style.hxx>
 #include <svtools/ctrltool.hxx>
 #include <svtools/borderhelper.hxx>
+#include <sfx2/InterimItemWindow.hxx>
 #include <sfx2/tplpitem.hxx>
 #include <sfx2/sfxstatuslistener.hxx>
 #include <toolkit/helper/vclunohelper.hxx>
@@ -176,24 +178,25 @@ private:
 
 namespace {
 
-class SvxFontNameBox_Impl : public FontNameBox
+class SvxFontNameBox_Impl final : public InterimItemWindow
 {
 private:
+    std::unique_ptr<FontNameBox>   m_xWidget;
     const FontList*                pFontList;
     ::std::unique_ptr<FontList>    m_aOwnFontList;
     vcl::Font                      aCurFont;
-    Size                           aLogicalSize;
     OUString                       aCurText;
     sal_uInt16                     nFtCount;
     bool                           bRelease;
     Reference< XDispatchProvider > m_xDispatchProvider;
     Reference< XFrame >            m_xFrame;
-    bool            mbEndPreview;
     bool            mbCheckingUnknownFont;
 
     void            ReleaseFocus_Impl();
     void            EnableControls_Impl();
 
+    void            Select(bool bNonTravelSelect);
+
     void            EndPreview()
     {
         Sequence< PropertyValue > aArgs;
@@ -201,33 +204,44 @@ private:
                                          ".uno:CharEndPreviewFontName",
                                          aArgs );
     }
-    DECL_LINK( CheckAndMarkUnknownFont, VclWindowEvent&, void );
+    void            CheckAndMarkUnknownFont();
 
     void            SetOptimalSize();
 
-protected:
-    virtual void    Select() override;
     virtual void    DataChanged( const DataChangedEvent& rDCEvt ) override;
+    virtual void    GetFocus() override;
 
 public:
-    SvxFontNameBox_Impl( vcl::Window* pParent, const Reference< XDispatchProvider >& rDispatchProvider,const Reference< XFrame >& _xFrame
-        , WinBits nStyle
-        );
+    SvxFontNameBox_Impl(vcl::Window* pParent, const Reference<XDispatchProvider>& rDispatchProvider, const Reference<XFrame>& _xFrame);
     virtual ~SvxFontNameBox_Impl() override;
     virtual void dispose() override;
 
     void            FillList();
     void            Update( const css::awt::FontDescriptor* pFontDesc );
     sal_uInt16      GetListCount() const { return nFtCount; }
-    void            Clear() { FontNameBox::Clear(); nFtCount = 0; }
+    void            Clear() { m_xWidget->clear(); nFtCount = 0; }
     void            Fill( const FontList* pList )
-                        { FontNameBox::Fill( pList );
-                          nFtCount = pList->GetFontNameCount(); }
-    virtual bool    PreNotify( NotifyEvent& rNEvt ) override;
-    virtual bool    EventNotify( NotifyEvent& rNEvt ) override;
+    {
+        m_xWidget->Fill(pList);
+        nFtCount = pList->GetFontNameCount();
+    }
+
     virtual Reference< css::accessibility::XAccessible > CreateAccessible() override;
     void     SetOwnFontList(::std::unique_ptr<FontList> && _aOwnFontList) { m_aOwnFontList = std::move(_aOwnFontList); }
-    virtual boost::property_tree::ptree DumpAsPropertyTree() override;
+
+    void Enable() {m_xWidget->set_sensitive(true); InterimItemWindow::Enable();}
+    void Disable() {m_xWidget->set_sensitive(false); InterimItemWindow::Disable();}
+
+    void set_active_or_entry_text(const OUString& rText);
+
+    void statusChanged_Impl(const css::frame::FeatureStateEvent& rEvent);
+
+    DECL_LINK(SelectHdl, weld::ComboBox&, void);
+    DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+    DECL_LINK(ActivateHdl, weld::ComboBox&, bool);
+    DECL_LINK(FocusInHdl, weld::Widget&, void);
+    DECL_LINK(FocusOutHdl, weld::Widget&, void);
+    DECL_LINK(DumpAsPropertyTreeHdl, boost::property_tree::ptree&, void);
 };
 
 // SelectHdl needs the Modifiers, get them in MouseButtonUp
@@ -1304,22 +1318,34 @@ static bool lcl_GetDocFontList( const FontList** ppFontList, SvxFontNameBox_Impl
     return bChanged;
 }
 
-SvxFontNameBox_Impl::SvxFontNameBox_Impl( vcl::Window* pParent, const Reference< XDispatchProvider >& rDispatchProvider,const Reference< XFrame >& _xFrame, WinBits nStyle ) :
-
-    FontNameBox        ( pParent, nStyle | WinBits( WB_DROPDOWN | WB_AUTOHSCROLL ) ),
-    pFontList          ( nullptr ),
-    aLogicalSize       ( 60,160 ),
-    nFtCount           ( 0 ),
-    bRelease           ( true ),
-    m_xDispatchProvider( rDispatchProvider ),
-    m_xFrame (_xFrame),
-    mbEndPreview(false),
-    mbCheckingUnknownFont(false)
+SvxFontNameBox_Impl::SvxFontNameBox_Impl(vcl::Window* pParent, const Reference<XDispatchProvider>& rDispatchProvider,
+                                         const Reference<XFrame>& _xFrame)
+    : InterimItemWindow(pParent, "svx/ui/fontnamebox.ui", "FontNameBox")
+    , m_xWidget(new FontNameBox(m_xBuilder->weld_combo_box("fontnamecombobox")))
+    , pFontList(nullptr)
+    , nFtCount(0)
+    , bRelease(true)
+    , m_xDispatchProvider(rDispatchProvider)
+    , m_xFrame(_xFrame)
+    , mbCheckingUnknownFont(false)
 {
-    SetOptimalSize();
     EnableControls_Impl();
-    GetSubEdit()->AddEventListener( LINK( this, SvxFontNameBox_Impl, CheckAndMarkUnknownFont ));
     set_id("fontnamecombobox");
+
+    m_xWidget->connect_changed(LINK(this, SvxFontNameBox_Impl, SelectHdl));
+    m_xWidget->connect_key_press(LINK(this, SvxFontNameBox_Impl, KeyInputHdl));
+    m_xWidget->connect_entry_activate(LINK(this, SvxFontNameBox_Impl, ActivateHdl));
+    m_xWidget->connect_focus_in(LINK(this, SvxFontNameBox_Impl, FocusInHdl));
+    m_xWidget->connect_focus_out(LINK(this, SvxFontNameBox_Impl, FocusOutHdl));
+    m_xWidget->connect_get_property_tree(LINK(this, SvxFontNameBox_Impl, DumpAsPropertyTreeHdl));
+
+    const Size aLogicalSize(60, 0);
+    Size aSize(LogicToPixel(aLogicalSize, MapMode(MapUnit::MapAppFont)));
+    // set width in chars low so the size request will not be overridden
+    m_xWidget->set_entry_width_chars(1);
+    m_xWidget->set_size_request(aSize.Width(), -1);
+
+    SetOptimalSize();
 }
 
 SvxFontNameBox_Impl::~SvxFontNameBox_Impl()
@@ -1329,38 +1355,41 @@ SvxFontNameBox_Impl::~SvxFontNameBox_Impl()
 
 void SvxFontNameBox_Impl::dispose()
 {
-    GetSubEdit()->RemoveEventListener( LINK( this, SvxFontNameBox_Impl, CheckAndMarkUnknownFont ));
-    FontNameBox::dispose();
+    m_xWidget.reset();
+    InterimItemWindow::dispose();
 }
 
 void SvxFontNameBox_Impl::FillList()
 {
+    if (!m_xWidget) // e.g. disposed
+        return;
     // Save old Selection, set back in the end
-    Selection aOldSel = GetSelection();
+    int nStartPos, nEndPos;
+    m_xWidget->get_entry_selection_bounds(nStartPos, nEndPos);
+
     // Did Doc-Fontlist change?
-    lcl_GetDocFontList( &pFontList, this );
-    aCurText = GetText();
-    SetSelection( aOldSel );
+    lcl_GetDocFontList(&pFontList, this);
+
+    aCurText = m_xWidget->get_active_text();
+    m_xWidget->select_entry_region(nStartPos, nEndPos);
 }
 
-IMPL_LINK( SvxFontNameBox_Impl, CheckAndMarkUnknownFont, VclWindowEvent&, event, void )
+void SvxFontNameBox_Impl::CheckAndMarkUnknownFont()
 {
-    if( event.GetId() != VclEventId::EditModify )
-        return;
     if (mbCheckingUnknownFont) //tdf#117537 block rentry
         return;
     mbCheckingUnknownFont = true;
-    OUString fontname = GetSubEdit()->GetText();
+    OUString fontname = m_xWidget->get_active_text();
     lcl_GetDocFontList( &pFontList, this );
     // If the font is unknown, show it in italic.
-    vcl::Font font = GetControlFont();
+    vcl::Font font = m_xWidget->get_entry_font();
     if( pFontList != nullptr && pFontList->IsAvailable( fontname ))
     {
         if( font.GetItalic() != ITALIC_NONE )
         {
             font.SetItalic( ITALIC_NONE );
-            SetControlFont( font );
-            SetQuickHelpText( SvxResId( RID_SVXSTR_CHARFONTNAME ));
+            m_xWidget->set_entry_font(font);
+            m_xWidget->set_tooltip_text(SvxResId(RID_SVXSTR_CHARFONTNAME));
         }
     }
     else
@@ -1368,8 +1397,8 @@ IMPL_LINK( SvxFontNameBox_Impl, CheckAndMarkUnknownFont, VclWindowEvent&, event,
         if( font.GetItalic() != ITALIC_NORMAL )
         {
             font.SetItalic( ITALIC_NORMAL );
-            SetControlFont( font );
-            SetQuickHelpText( SvxResId( RID_SVXSTR_CHARFONTNAME_NOTAVAILABLE ));
+            m_xWidget->set_entry_font(font);
+            m_xWidget->set_tooltip_text(SvxResId(RID_SVXSTR_CHARFONTNAME_NOTAVAILABLE));
         }
     }
     mbCheckingUnknownFont = false;
@@ -1386,72 +1415,61 @@ void SvxFontNameBox_Impl::Update( const css::awt::FontDescriptor* pFontDesc )
         aCurFont.SetCharSet     ( rtl_TextEncoding( pFontDesc->CharSet ) );
     }
     OUString aCurName = aCurFont.GetFamilyName();
-    if ( GetText() != aCurName )
-        SetText( aCurName );
+    OUString aText = m_xWidget->get_active_text();
+    if (aText != aCurName)
+        set_active_or_entry_text(aCurName);
 }
 
-bool SvxFontNameBox_Impl::PreNotify( NotifyEvent& rNEvt )
+void SvxFontNameBox_Impl::set_active_or_entry_text(const OUString& rText)
 {
-    MouseNotifyEvent nType = rNEvt.GetType();
+    m_xWidget->set_active_or_entry_text(rText);
+    CheckAndMarkUnknownFont();
+}
 
-    if ( MouseNotifyEvent::MOUSEBUTTONDOWN == nType || MouseNotifyEvent::GETFOCUS == nType )
-    {
-        EnableControls_Impl();
-        FillList();
-    }
-    return FontNameBox::PreNotify( rNEvt );
+IMPL_LINK_NOARG(SvxFontNameBox_Impl, FocusInHdl, weld::Widget&, void)
+{
+    EnableControls_Impl();
+    FillList();
 }
 
-bool SvxFontNameBox_Impl::EventNotify( NotifyEvent& rNEvt )
+IMPL_LINK(SvxFontNameBox_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
 {
     bool bHandled = false;
-    mbEndPreview = false;
-    if ( rNEvt.GetType() == MouseNotifyEvent::KEYUP )
-        mbEndPreview = true;
 
-    if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
-    {
-        sal_uInt16 nCode = rNEvt.GetKeyEvent()->GetKeyCode().GetCode();
+    sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
 
-        switch ( nCode )
-        {
-            case KEY_RETURN:
-            case KEY_TAB:
-            {
-                if ( KEY_TAB == nCode )
-                    bRelease = false;
-                else
-                    bHandled = true;
-                Select();
-                break;
-            }
+    switch (nCode)
+    {
+        case KEY_TAB:
+            bRelease = false;
+            Select(true);
+            break;
 
-            case KEY_ESCAPE:
-                SetText( aCurText );
-                if ( typeid( *GetParent() ) != typeid( sfx2::sidebar::SidebarToolBox ) )
-                    ReleaseFocus_Impl();
-                EndPreview();
-                break;
-        }
+        case KEY_ESCAPE:
+            set_active_or_entry_text(aCurText);
+            if ( typeid( *GetParent() ) != typeid( sfx2::sidebar::SidebarToolBox ) )
+                ReleaseFocus_Impl();
+            EndPreview();
+            bHandled = true;
+            break;
     }
-    else if ( MouseNotifyEvent::LOSEFOCUS == rNEvt.GetType() )
+
+    return bHandled || ChildKeyInput(rKEvt);
+}
+
+IMPL_LINK_NOARG(SvxFontNameBox_Impl, FocusOutHdl, weld::Widget&, void)
+{
+    if (!m_xWidget->has_focus()) // a combobox can be comprised of different subwidget so double-check if none of those has focus
     {
-        vcl::Window* pFocusWin = Application::GetFocusWindow();
-        if ( !HasFocus() && GetSubEdit() != pFocusWin )
-            SetText( GetSavedValue() );
+        set_active_or_entry_text(m_xWidget->get_saved_value());
         // send EndPreview
         EndPreview();
     }
-
-    return bHandled || FontNameBox::EventNotify( rNEvt );
 }
 
 void SvxFontNameBox_Impl::SetOptimalSize()
 {
-    Size aSize(LogicToPixel(aLogicalSize, MapMode(MapUnit::MapAppFont)));
-    set_width_request(aSize.Width());
-    set_height_request(aSize.Height());
-    SetSizePixel(aSize);
+    SetSizePixel(get_preferred_size());
 }
 
 void SvxFontNameBox_Impl::DataChanged( const DataChangedEvent& rDCEvt )
@@ -1468,8 +1486,6 @@ void SvxFontNameBox_Impl::DataChanged( const DataChangedEvent& rDCEvt )
         // the new one before doing anything further.
         lcl_GetDocFontList( &pFontList, this );
     }
-
-    FontNameBox::DataChanged( rDCEvt );
 }
 
 void SvxFontNameBox_Impl::ReleaseFocus_Impl()
@@ -1488,27 +1504,36 @@ void SvxFontNameBox_Impl::EnableControls_Impl()
     SvtFontOptions aFontOpt;
     bool bEnable = aFontOpt.IsFontHistoryEnabled();
     sal_uInt16 nEntries = bEnable ? MAX_MRU_FONTNAME_ENTRIES : 0;
-    if ( GetMaxMRUCount() != nEntries )
+    if (m_xWidget->get_max_mru_count() != nEntries)
     {
         // refill in the next GetFocus-Handler
         pFontList = nullptr;
         Clear();
-        SetMaxMRUCount( nEntries );
+        m_xWidget->set_max_mru_count(nEntries);
     }
 
-    bEnable = aFontOpt.IsFontWYSIWYGEnabled();
-    EnableWYSIWYG( bEnable );
+    if (aFontOpt.IsFontWYSIWYGEnabled())
+        m_xWidget->EnableWYSIWYG();
 }
 
-void SvxFontNameBox_Impl::Select()
+IMPL_LINK(SvxFontNameBox_Impl, SelectHdl, weld::ComboBox&, rCombo, void)
 {
-    FontNameBox::Select();
+    Select(rCombo.changed_by_direct_pick()); // only when picked from the list
+}
 
+IMPL_LINK_NOARG(SvxFontNameBox_Impl, ActivateHdl, weld::ComboBox&, bool)
+{
+    Select(true);
+    return true;
+}
+
+void SvxFontNameBox_Impl::Select(bool bNonTravelSelect)
+{
     Sequence< PropertyValue > aArgs( 1 );
     std::unique_ptr<SvxFontItem> pFontItem;
     if ( pFontList )
     {
-        FontMetric aFontMetric( pFontList->Get( GetText(),
+        FontMetric aFontMetric( pFontList->Get(m_xWidget->get_active_text(),
             aCurFont.GetWeight(),
             aCurFont.GetItalic() ) );
         aCurFont = aFontMetric;
@@ -1524,8 +1549,10 @@ void SvxFontNameBox_Impl::Select()
         pFontItem->QueryValue( a );
         aArgs[0].Value  = a;
     }
-    if ( !IsTravelSelect() )
+
+    if (bNonTravelSelect)
     {
+        CheckAndMarkUnknownFont();
         //  #i33380# DR 2004-09-03 Moved the following line above the Dispatch() call.
         //  This instance may be deleted in the meantime (i.e. when a dialog is opened
         //  while in Dispatch()), accessing members will crash in this case.
@@ -1541,11 +1568,6 @@ void SvxFontNameBox_Impl::Select()
     }
     else
     {
-        if ( mbEndPreview )
-        {
-            EndPreview();
-            return;
-        }
         if (pFontItem)
         {
             aArgs[0].Name   = "CharPreviewFontName";
@@ -1556,35 +1578,39 @@ void SvxFontNameBox_Impl::Select()
     }
 }
 
-boost::property_tree::ptree SvxFontNameBox_Impl::DumpAsPropertyTree()
+void SvxFontNameBox_Impl::GetFocus()
 {
-    boost::property_tree::ptree aTree(FontNameBox::DumpAsPropertyTree());
+    if (m_xWidget)
+        m_xWidget->grab_focus();
+    InterimItemWindow::GetFocus();
+}
 
+IMPL_LINK(SvxFontNameBox_Impl, DumpAsPropertyTreeHdl, boost::property_tree::ptree&, rTree, void)
+{
     boost::property_tree::ptree aEntries;
 
-    for (int i = 0; i < GetEntryCount(); ++i)
+    for (int i = 0, nEntryCount = m_xWidget->get_count(); i < nEntryCount; ++i)
     {
         boost::property_tree::ptree aEntry;
-        aEntry.put("", GetEntry(i));
+        aEntry.put("", m_xWidget->get_text(i));
         aEntries.push_back(std::make_pair("", aEntry));
     }
 
-    aTree.add_child("entries", aEntries);
+    rTree.add_child("entries", aEntries);
 
     boost::property_tree::ptree aSelected;
 
-    for (int i = 0; i < GetSelectedEntryCount(); ++i)
+    int nSelectedEntry = m_xWidget->get_active();
+    if (nSelectedEntry != -1)
     {
         boost::property_tree::ptree aEntry;
-        aEntry.put("", GetSelectedEntryPos(i));
+        aEntry.put("", m_xWidget->get_text(nSelectedEntry));
         aSelected.push_back(std::make_pair("", aEntry));
     }
 
-    aTree.put("selectedCount", GetSelectedEntryCount());
-    aTree.add_child("selectedEntries", aSelected);
-    aTree.put("command", ".uno:CharFontName");
-
-    return aTree;
+    rTree.put("selectedCount", nSelectedEntry == -1 ? 0 : 1);
+    rTree.add_child("selectedEntries", aSelected);
+    rTree.put("command", ".uno:CharFontName");
 }
 
 ColorWindow::ColorWindow(const OUString& rCommand,
@@ -2846,40 +2872,44 @@ SvxFontNameToolBoxControl::SvxFontNameToolBoxControl()
 {
 }
 
-void SvxFontNameToolBoxControl::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+void SvxFontNameBox_Impl::statusChanged_Impl( const css::frame::FeatureStateEvent& rEvent )
 {
-    SolarMutexGuard aGuard;
-    ToolBox* pToolBox = nullptr;
-    sal_uInt16 nId = 0;
-    if ( !getToolboxId( nId, &pToolBox ) )
-        return;
-
     if ( !rEvent.IsEnabled )
     {
-        m_pBox->Disable();
-        m_pBox->Update( nullptr );
+        Disable();
+        Update( nullptr );
     }
     else
     {
-        m_pBox->Enable();
+        Enable();
 
         css::awt::FontDescriptor aFontDesc;
         if ( rEvent.State >>= aFontDesc )
-            m_pBox->Update( &aFontDesc );
+            Update(&aFontDesc);
         else
-            m_pBox->SetText( "" );
-        m_pBox->SaveValue();
+            set_active_or_entry_text("");
+        m_xWidget->save_value();
     }
+}
+
+void SvxFontNameToolBoxControl::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+    SolarMutexGuard aGuard;
+    ToolBox* pToolBox = nullptr;
+    sal_uInt16 nId = 0;
+    if ( !getToolboxId( nId, &pToolBox ) )
+        return;
 
+    m_pBox->statusChanged_Impl(rEvent);
     pToolBox->EnableItem( nId, rEvent.IsEnabled );
 }
 
 css::uno::Reference< css::awt::XWindow > SvxFontNameToolBoxControl::createItemWindow( const css::uno::Reference< css::awt::XWindow >& rParent )
 {
     SolarMutexGuard aGuard;
-    m_pBox = VclPtr<SvxFontNameBox_Impl>::Create( VCLUnoHelper::GetWindow( rParent ),
-                                                  Reference< XDispatchProvider >( m_xFrame->getController(), UNO_QUERY ),
-                                                  m_xFrame, 0);
+    m_pBox = VclPtr<SvxFontNameBox_Impl>::Create(VCLUnoHelper::GetWindow(rParent),
+                                                 Reference< XDispatchProvider >(m_xFrame->getController(), UNO_QUERY),
+                                                 m_xFrame);
     return VCLUnoHelper::GetInterface( m_pBox );
 }
 
@@ -3510,7 +3540,7 @@ com_sun_star_comp_svx_CurrencyToolBoxControl_get_implementation(
 Reference< css::accessibility::XAccessible > SvxFontNameBox_Impl::CreateAccessible()
 {
     FillList();
-    return FontNameBox::CreateAccessible();
+    return InterimItemWindow::CreateAccessible();
 }
 
 //static
diff --git a/svx/source/tbxctrls/tbunocontroller.cxx b/svx/source/tbxctrls/tbunocontroller.cxx
index 3e3d9ce2751b..cc638c794a1f 100644
--- a/svx/source/tbxctrls/tbunocontroller.cxx
+++ b/svx/source/tbxctrls/tbunocontroller.cxx
@@ -180,7 +180,7 @@ SvxFontSizeBox_Base::SvxFontSizeBox_Base(std::unique_ptr<weld::ComboBox> xWidget
     , m_xWidget(new FontSizeBox(std::move(xWidget)))
 {
     m_xWidget->set_value(0);
-    m_xWidget->set_entry_text("");
+    m_xWidget->set_active_or_entry_text("");
     m_xWidget->disable_entry_completion();
 
     m_xWidget->connect_changed(LINK(this, SvxFontSizeBox_Base, SelectHdl));
@@ -246,7 +246,7 @@ void SvxFontSizeBox_Base::statusChanged_Impl( long nPoint, bool bErase )
     {
         // delete value in the display
         m_xWidget->set_value(-1L);
-        m_xWidget->set_entry_text("");
+        m_xWidget->set_active_or_entry_text("");
     }
     m_aCurText = m_xWidget->get_active_text();
 }
@@ -292,7 +292,7 @@ bool SvxFontSizeBox_Base::DoKeyInput(const KeyEvent& rKEvt)
             break;
 
         case KEY_ESCAPE:
-            m_xWidget->set_entry_text(m_aCurText);
+            m_xWidget->set_active_or_entry_text(m_aCurText);
             if (!m_rCtrl.IsInSidebar())
             {
                 ReleaseFocus_Impl();
@@ -312,7 +312,7 @@ bool SvxFontSizeBox_Impl::DoKeyInput(const KeyEvent& rKEvt)
 IMPL_LINK_NOARG(SvxFontSizeBox_Base, FocusOutHdl, weld::Widget&, void)
 {
     if (!m_xWidget->has_focus()) // a combobox can be comprised of different subwidget so double-check if none of those has focus
-        m_xWidget->set_entry_text(m_aCurText);
+        m_xWidget->set_active_or_entry_text(m_aCurText);
 }
 
 void SvxFontSizeBox_Impl::SetOptimalSize()
diff --git a/svx/uiconfig/ui/fontnamebox.ui b/svx/uiconfig/ui/fontnamebox.ui
new file mode 100644
index 000000000000..ca1d3e71f570
--- /dev/null
+++ b/svx/uiconfig/ui/fontnamebox.ui
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface domain="svx">
+  <requires lib="gtk+" version="3.18"/>
+  <object class="GtkBox" id="FontNameBox">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="hexpand">True</property>
+    <property name="spacing">6</property>
+    <child>
+      <object class="GtkComboBoxText" id="fontnamecombobox">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="hexpand">True</property>
+        <property name="has_entry">True</property>
+        <property name="popup_fixed_width">False</property>
+        <child internal-child="entry">
+          <object class="GtkEntry">
+            <property name="can_focus">True</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/sw/inc/pch/precompiled_swui.hxx b/sw/inc/pch/precompiled_swui.hxx
index cc71c3c666e1..22537bd6e566 100644
--- a/sw/inc/pch/precompiled_swui.hxx
+++ b/sw/inc/pch/precompiled_swui.hxx
@@ -13,7 +13,7 @@
  manual changes will be rewritten by the next run of update_pch.sh (which presumably
  also fixes all possible problems, so it's usually better to use it).
 
- Generated on 2020-03-04 21:22:13 using:
+ Generated on 2020-04-07 17:26:59 using:
  ./bin/update_pch sw swui --cutoff=3 --exclude:system --include:module --include:local
 
  If after updating build fails, use the following command to locate conflicting headers:
@@ -106,10 +106,8 @@
 #include <vcl/bitmapex.hxx>
 #include <vcl/builder.hxx>
 #include <vcl/builderpage.hxx>
-#include <vcl/button.hxx>
 #include <vcl/cairo.hxx>
 #include <vcl/checksum.hxx>
-#include <vcl/combobox.hxx>
 #include <vcl/ctrl.hxx>
 #include <vcl/customweld.hxx>
 #include <vcl/devicecoordinate.hxx>
@@ -119,7 +117,6 @@
 #include <vcl/edit.hxx>
 #include <vcl/errcode.hxx>
 #include <vcl/event.hxx>
-#include <vcl/fixed.hxx>
 #include <vcl/floatwin.hxx>
 #include <vcl/fntstyle.hxx>
 #include <vcl/font.hxx>
@@ -131,10 +128,8 @@
 #include <vcl/image.hxx>
 #include <vcl/keycod.hxx>
 #include <vcl/keycodes.hxx>
-#include <vcl/lstbox.hxx>
 #include <vcl/mapmod.hxx>
 #include <vcl/menu.hxx>
-#include <vcl/menubtn.hxx>
 #include <vcl/metaactiontypes.hxx>
 #include <vcl/metric.hxx>
 #include <vcl/outdev.hxx>
@@ -178,6 +173,7 @@
 #include <basegfx/tuple/b2dtuple.hxx>
 #include <basegfx/tuple/b2ituple.hxx>
 #include <basegfx/tuple/b3dtuple.hxx>
+#include <basegfx/utils/common.hxx>
 #include <basegfx/vector/b2dsize.hxx>
 #include <basegfx/vector/b2dvector.hxx>
 #include <basegfx/vector/b2enums.hxx>
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx
index ed219871af24..7cc4d9ebbc50 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -5824,6 +5824,24 @@ public:
         ensure_event_listener();
     }
 
+    void call_signal_custom_render(UserDrawEvent* pEvent)
+    {
+        vcl::RenderContext* pRenderContext = pEvent->GetRenderContext();
+        auto nPos = pEvent->GetItemId();
+        signal_custom_render(*pRenderContext, pEvent->GetRect(), pEvent->IsSelected(), get_id(nPos));
+        m_xComboBox->DrawEntry(*pEvent, false, false);  // draw separator
+    }
+
+    Size call_signal_custom_get_size(VirtualDevice& rOutput, const OUString& rId)
+    {
+        return signal_custom_get_size(rOutput, rId);
+    }
+
+    VclPtr<VirtualDevice> create_render_virtual_device() const override
+    {
+        return VclPtr<VirtualDevice>::Create();
+    }
+
     virtual void HandleEventListener(VclWindowEvent& rEvent) override
     {
         if (rEvent.GetId() == VclEventId::DropdownPreOpen
@@ -5908,6 +5926,33 @@ public:
 
     virtual vcl::Font get_entry_font() override { assert(false); return vcl::Font(); }
 
+    virtual void set_custom_renderer() override
+    {
+        assert(false && "not implemented");
+    }
+
+    virtual int get_max_mru_count() const override
+    {
+        assert(false && "not implemented");
+        return 0;
+    }
+
+    virtual void set_max_mru_count(int) override
+    {
+        assert(false && "not implemented");
+    }
+
+    virtual OUString get_mru_entries() const override
+    {
+        assert(false && "not implemented");
+        return OUString();
+    }
+
+    virtual void set_mru_entries(const OUString&) override
+    {
+        assert(false && "not implemented");
+    }
+
     virtual ~SalInstanceComboBoxWithoutEdit() override
     {
         m_xComboBox->SetSelectHdl(Link<ListBox&, void>());
@@ -5929,6 +5974,7 @@ private:
     DECL_LINK(ChangeHdl, Edit&, void);
     DECL_LINK(EntryActivateHdl, Edit&, bool);
     DECL_LINK(SelectHdl, ::ComboBox&, void);
+    DECL_LINK(UserDrawHdl, UserDrawEvent*, void);
     WeldTextFilter m_aTextFilter;
     bool m_bInSelect;
 public:
@@ -6038,6 +6084,43 @@ public:
         return pEdit->GetPointFont(*pEdit);
     }
 
+    virtual void set_custom_renderer() override
+    {
+        auto nOldEntryHeight = m_xComboBox->GetDropDownEntryHeight();
+        auto nDropDownLineCount = m_xComboBox->GetDropDownLineCount();
+
+        Size aRowSize(signal_custom_get_size(*m_xComboBox, OUString()));
+        m_xComboBox->EnableUserDraw(true);
+        m_xComboBox->SetUserItemSize(aRowSize);
+        m_xComboBox->SetUserDrawHdl(LINK(this, SalInstanceComboBoxWithEdit, UserDrawHdl));
+
+        // adjust the line count to fit approx the height it would have been before
+        // using a custom renderer
+        auto nNewEntryHeight = m_xComboBox->GetDropDownEntryHeight();
+        double fRatio = nOldEntryHeight / static_cast<double>(nNewEntryHeight);
+        m_xComboBox->SetDropDownLineCount(nDropDownLineCount * fRatio);
+    }
+
+    virtual int get_max_mru_count() const override
+    {
+        return m_xComboBox->GetMaxMRUCount();
+    }
+
+    virtual void set_max_mru_count(int nCount) override
+    {
+        return m_xComboBox->SetMaxMRUCount(nCount);
+    }
+
+    virtual OUString get_mru_entries() const override
+    {
+        return m_xComboBox->GetMRUEntries();
+    }
+
+    virtual void set_mru_entries(const OUString& rEntries) override
+    {
+        m_xComboBox->SetMRUEntries(rEntries);
+    }
+
     virtual ~SalInstanceComboBoxWithEdit() override
     {
         m_xComboBox->SetTextFilter(nullptr);
@@ -6067,6 +6150,11 @@ IMPL_LINK_NOARG(SalInstanceComboBoxWithEdit, EntryActivateHdl, Edit&, bool)
     return m_aEntryActivateHdl.Call(*this);
 }
 
+IMPL_LINK(SalInstanceComboBoxWithEdit, UserDrawHdl, UserDrawEvent*, pEvent, void)
+{
+    call_signal_custom_render(pEvent);
+}
+
 class SalInstanceEntryTreeView : public SalInstanceContainer, public virtual weld::EntryTreeView
 {
 private:
@@ -6142,6 +6230,38 @@ public:
 
     virtual bool changed_by_direct_pick() const override { return m_bTreeChange; }
 
+    virtual void set_custom_renderer() override
+    {
+        assert(false && "not implemented");
+    }
+
+    virtual int get_max_mru_count() const override
+    {
+        assert(false && "not implemented");
+        return 0;
+    }
+
+    virtual void set_max_mru_count(int) override
+    {
+        assert(false && "not implemented");
+    }
+
+    virtual OUString get_mru_entries() const override
+    {
+        assert(false && "not implemented");
+        return OUString();
+    }
+
+    virtual void set_mru_entries(const OUString&) override
+    {
+        assert(false && "not implemented");
+    }
+
+    VclPtr<VirtualDevice> create_render_virtual_device() const override
+    {
+        return VclPtr<VirtualDevice>::Create();
+    }
+
     virtual ~SalInstanceEntryTreeView() override
     {
         Edit& rEntry = m_pEntry->getEntry();
diff --git a/vcl/source/control/combobox.cxx b/vcl/source/control/combobox.cxx
index 36f35704c4cf..ef1e3c1cebf5 100644
--- a/vcl/source/control/combobox.cxx
+++ b/vcl/source/control/combobox.cxx
@@ -1135,6 +1135,11 @@ Size ComboBox::CalcBlockSize( sal_uInt16 nColumns, sal_uInt16 nLines ) const
     return aSz;
 }
 
+long ComboBox::GetDropDownEntryHeight() const
+{
+    return m_pImpl->m_pImplLB->GetEntryHeight();
+}
+
 void ComboBox::GetMaxVisColumnsAndLines( sal_uInt16& rnCols, sal_uInt16& rnLines ) const
 {
     long nCharWidth = GetTextWidth(OUString(u'x'));
@@ -1142,7 +1147,7 @@ void ComboBox::GetMaxVisColumnsAndLines( sal_uInt16& rnCols, sal_uInt16& rnLines
     {
         Size aOutSz = m_pImpl->m_pImplLB->GetMainWindow()->GetOutputSizePixel();
         rnCols = (nCharWidth > 0) ? static_cast<sal_uInt16>(aOutSz.Width()/nCharWidth) : 1;
-        rnLines = static_cast<sal_uInt16>(aOutSz.Height()/m_pImpl->m_pImplLB->GetEntryHeight());
+        rnLines = static_cast<sal_uInt16>(aOutSz.Height()/GetDropDownEntryHeight());
     }
     else
     {
@@ -1264,6 +1269,11 @@ void ComboBox::UserDraw( const UserDrawEvent& )
 {
 }
 
+void ComboBox::SetUserDrawHdl(const Link<UserDrawEvent*, void>& rLink)
+{
+    m_pImpl->m_pImplLB->SetUserDrawHdl(rLink);
+}
+
 void ComboBox::SetUserItemSize( const Size& rSz )
 {
     m_pImpl->m_pImplLB->GetMainWindow()->SetUserItemSize( rSz );
diff --git a/vcl/source/control/imp_listbox.cxx b/vcl/source/control/imp_listbox.cxx
index a2a3a1615275..3fe90de21906 100644
--- a/vcl/source/control/imp_listbox.cxx
+++ b/vcl/source/control/imp_listbox.cxx
@@ -1714,7 +1714,8 @@ void ImplListBoxWindow::ImplPaint(vcl::RenderContext& rRenderContext, sal_Int32
     long nY = mpEntryList->GetAddedHeight(nPos, mnTop);
     tools::Rectangle aRect(Point(0, nY), Size(nWidth, pEntry->getHeightWithMargin()));
 
-    if (mpEntryList->IsEntryPosSelected(nPos))
+    bool bSelected = mpEntryList->IsEntryPosSelected(nPos);
+    if (bSelected)
     {
         rRenderContext.SetTextColor(!IsEnabled() ? rStyleSettings.GetDisableColor() : rStyleSettings.GetHighlightTextColor());
         rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
@@ -1738,7 +1739,7 @@ void ImplListBoxWindow::ImplPaint(vcl::RenderContext& rRenderContext, sal_Int32
             nPos = GetEntryList()->FindEntry(GetEntryList()->GetEntryText(nPos));
         nPos = nPos - GetEntryList()->GetMRUCount();
 
-        UserDrawEvent aUDEvt(this, &rRenderContext, aRect, nPos);
+        UserDrawEvent aUDEvt(this, &rRenderContext, aRect, nPos, bSelected);
         maUserDrawHdl.Call( &aUDEvt );
         mbInUserDraw = false;
     }
diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx
index ff07a983813d..4a0f975b2fd1 100644
--- a/vcl/unx/gtk3/gtk3gtkinst.cxx
+++ b/vcl/unx/gtk3/gtk3gtkinst.cxx
@@ -11237,7 +11237,7 @@ public:
     }
 };
 
-void ensure_device(CustomCellRendererSurface *cellsurface, weld::TreeView* pTreeView)
+void ensure_device(CustomCellRendererSurface *cellsurface, weld::Widget* pWidget)
 {
     if (!cellsurface->device)
     {
@@ -11245,110 +11245,12 @@ void ensure_device(CustomCellRendererSurface *cellsurface, weld::TreeView* pTree
         cellsurface->device->SetBackground(COL_TRANSPARENT);
         // expand the point size of the desired font to the equivalent pixel size
         if (vcl::Window* pDefaultDevice = dynamic_cast<vcl::Window*>(Application::GetDefaultDevice()))
-            pDefaultDevice->SetPointFont(*cellsurface->device, pTreeView->get_font());
+            pDefaultDevice->SetPointFont(*cellsurface->device, pWidget->get_font());
     }
 }
 
 }
 
-bool custom_cell_renderer_surface_get_preferred_size(GtkCellRenderer *cell,
-                                                     GtkOrientation orientation,
-                                                     gint *minimum_size,
-                                                     gint *natural_size)
-{
-    GValue value = G_VALUE_INIT;
-    g_value_init(&value, G_TYPE_STRING);
-    g_object_get_property(G_OBJECT(cell), "id", &value);
-
-    const char* pStr = g_value_get_string(&value);
-
-    if (!pStr)
-    {
-        // this happens if we're empty
-        return false;
-    }
-
-    OUString sId(pStr, strlen(pStr), RTL_TEXTENCODING_UTF8);
-
-    value = G_VALUE_INIT;
-    g_value_init(&value, G_TYPE_POINTER);
-    g_object_get_property(G_OBJECT(cell), "instance", &value);
-
-    CustomCellRendererSurface *cellsurface = CUSTOM_CELL_RENDERER_SURFACE(cell);
-
-    GtkInstanceTreeView* pTreeView = static_cast<GtkInstanceTreeView*>(g_value_get_pointer(&value));
-
-    ensure_device(cellsurface, pTreeView);
-
-    Size aSize = pTreeView->call_signal_custom_get_size(*cellsurface->device, sId);
-
-    if (orientation == GTK_ORIENTATION_HORIZONTAL)
-    {
-        if (minimum_size)
-            *minimum_size = aSize.Width();
-
-        if (natural_size)
-            *natural_size = aSize.Width();
-    }
-    else
-    {
-        if (minimum_size)
-            *minimum_size = aSize.Height();
-
-        if (natural_size)
-            *natural_size = aSize.Height();
-    }
-
-    return true;
-}
-
-void custom_cell_renderer_surface_render(GtkCellRenderer* cell,
-                                         cairo_t* cr,
-                                         GtkWidget* /*widget*/,
-                                         const GdkRectangle* /*background_area*/,
-                                         const GdkRectangle* cell_area,
-                                         GtkCellRendererState flags)
-{
-    GValue value = G_VALUE_INIT;
-    g_value_init(&value, G_TYPE_STRING);
-    g_object_get_property(G_OBJECT(cell), "id", &value);
-
-    const char* pStr = g_value_get_string(&value);
-    OUString sId(pStr, pStr ? strlen(pStr) : 0, RTL_TEXTENCODING_UTF8);
-
-    value = G_VALUE_INIT;
-    g_value_init(&value, G_TYPE_POINTER);
-    g_object_get_property(G_OBJECT(cell), "instance", &value);
-
-    CustomCellRendererSurface *cellsurface = CUSTOM_CELL_RENDERER_SURFACE(cell);
-
-    GtkInstanceTreeView* pTreeView = static_cast<GtkInstanceTreeView*>(g_value_get_pointer(&value));
-
-    ensure_device(cellsurface, pTreeView);
-
-    Size aSize(cell_area->width, cell_area->height);
-    // false to not bother setting the bg on resize as we'll do that
-    // ourself via cairo
-    cellsurface->device->SetOutputSizePixel(aSize, false);
-
-    cairo_surface_t* pSurface = get_underlying_cairo_surface(*cellsurface->device);
-
-    // fill surface as transparent so it can be blended with the potentially
-    // selected background
-    cairo_t* tempcr = cairo_create(pSurface);
-    cairo_set_source_rgba(tempcr, 0, 0, 0, 0);
-    cairo_set_operator(tempcr, CAIRO_OPERATOR_SOURCE);
-    cairo_paint(tempcr);
-    cairo_destroy(tempcr);
-    cairo_surface_flush(pSurface);
-
-    pTreeView->call_signal_custom_render(*cellsurface->device, tools::Rectangle(Point(0, 0), aSize), flags & GTK_CELL_RENDERER_SELECTED, sId);
-    cairo_surface_mark_dirty(pSurface);
-
-    cairo_set_source_surface(cr, pSurface, cell_area->x, cell_area->y);
-    cairo_paint(cr);
-}
-
 IMPL_LINK_NOARG(GtkInstanceTreeView, async_signal_changed, void*, void)
 {
     m_pChangeEvent = nullptr;
@@ -12619,6 +12521,14 @@ GtkBuilder* makeComboBoxBuilder()
     return gtk_builder_new_from_file(OUStringToOString(aPath, RTL_TEXTENCODING_UTF8).getStr());
 }
 
+struct GtkTreeRowReferenceDeleter
+{
+    void operator()(GtkTreeRowReference* p) const
+    {
+        gtk_tree_row_reference_free(p);
+    }
+};
+
 class GtkInstanceComboBox : public GtkInstanceContainer, public vcl::ISearchableStringList, public virtual weld::ComboBox
 {
 private:
@@ -12635,7 +12545,7 @@ private:
     std::unique_ptr<vcl::Font> m_xFont;
     std::unique_ptr<comphelper::string::NaturalStringSorter> m_xSorter;
     vcl::QuickSelectionEngine m_aQuickSelectionEngine;
-    std::vector<int> m_aSeparatorRows;
+    std::vector<std::unique_ptr<GtkTreeRowReference, GtkTreeRowReferenceDeleter>> m_aSeparatorRows;
     bool m_bHoverSelection;
     bool m_bPopupActive;
     bool m_bAutoComplete;
@@ -12658,6 +12568,8 @@ private:
     guint m_nAutoCompleteIdleId;
     gint m_nNonCustomLineHeight;
     gint m_nPrePopupCursorPos;
+    int m_nMRUCount;
+    int m_nMaxMRUCount;
 
     static gboolean idleAutoComplete(gpointer widget)
     {
@@ -12685,6 +12597,10 @@ private:
 
         int nPos = -1;
 
+        int nZeroRow = 0;
+        if (m_nMRUCount)
+            nZeroRow += (m_nMRUCount + 1);
+
         if (!m_bAutoCompleteCaseSensitive)
         {
             // Try match case insensitive from current position
@@ -12692,7 +12608,7 @@ private:
             if (nPos == -1 && nStart != 0)
             {
                 // Try match case insensitive, but from start
-                nPos = starts_with(m_pTreeModel, aStartText, 0, 0, false);
+                nPos = starts_with(m_pTreeModel, aStartText, 0, nZeroRow, false);
             }
         }
 
@@ -12703,13 +12619,13 @@ private:
             if (nPos == -1 && nStart != 0)
             {
                 // Try match case sensitive, but from start
-                nPos = starts_with(m_pTreeModel, aStartText, 0, 0, true);
+                nPos = starts_with(m_pTreeModel, aStartText, 0, nZeroRow, true);
             }
         }
 
         if (nPos != -1)
         {
-            OUString aText = get_text(nPos);
+            OUString aText = get_text_including_mru(nPos);
             if (aText != aStartText)
                 set_active_text(aText);
             select_entry_region(aText.getLength(), aStartText.getLength());
@@ -12769,10 +12685,18 @@ private:
         pThis->signal_popup_toggled();
     }
 
-    int get_popup_height()
+    int get_popup_height(gint& rPopupWidth)
     {
-        int nMaxRows = Application::GetSettings().GetStyleSettings().GetListBoxMaximumLineCount();
-        int nRows = std::min(nMaxRows, get_count());
+        const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+
+        int nMaxRows = rSettings.GetListBoxMaximumLineCount();
+        bool bAddScrollWidth = false;
+        int nRows = get_count_including_mru();
+        if (nMaxRows < nRows)
+        {
+            nRows = nMaxRows;
+            bAddScrollWidth = true;
+        }
 
         GList* pColumns = gtk_tree_view_get_columns(m_pTreeView);
         gint nRowHeight = get_height_row(m_pTreeView, pColumns);
@@ -12795,6 +12719,9 @@ private:
             }
         }
 
+        if (bAddScrollWidth)
+            rPopupWidth += rSettings.GetScrollBarSize();
+
         return nHeight;
     }
 
@@ -12817,8 +12744,10 @@ private:
             // so gdk_window_move_to_rect will work again the next time
             gtk_widget_unrealize(GTK_WIDGET(m_pMenuWindow));
 
+            gtk_widget_set_size_request(GTK_WIDGET(m_pMenuWindow), -1, -1);
+
             if (!m_bActivateCalled)
-                set_cursor(m_nPrePopupCursorPos);
+                tree_view_set_cursor(m_nPrePopupCursorPos);
 
             // undo show_menu tooltip blocking
             GtkWidget* pParent = gtk_widget_get_toplevel(m_pToggleButton);
@@ -12834,14 +12763,20 @@ private:
             GtkRequisition size;
             gtk_widget_get_preferred_size(GTK_WIDGET(m_pMenuWindow), nullptr, &size);
 
-            gint nPopupWidth = std::max(size.width, nComboWidth);
-            gint nPopupHeight = get_popup_height();
+            gint nPopupWidth = size.width;
+            gint nPopupHeight = get_popup_height(nPopupWidth);
+            nPopupWidth = std::max(nPopupWidth, nComboWidth);
 
             gtk_widget_set_size_request(GTK_WIDGET(m_pMenuWindow), nPopupWidth, nPopupHeight);
 
             m_nPrePopupCursorPos = get_active();
 
             m_bActivateCalled = false;
+
+            // if we are in mru mode always start with the cursor at the top of the menu
+            if (m_nMaxMRUCount)
+                tree_view_set_cursor(0);
+
             show_menu(pComboBox, m_pMenuWindow);
         }
     }
@@ -12913,6 +12848,7 @@ private:
             if (m_aEntryActivateHdl.Call(*this))
                 g_signal_stop_emission_by_name(m_pEntry, "activate");
         }
+        update_mru();
     }
 
     OUString get(int pos, int col) const
@@ -12939,14 +12875,22 @@ private:
         }
     }
 
-    int find(const OUString& rStr, int col) const
+    int find(const OUString& rStr, int col, bool bSearchMRUArea) const
     {
         GtkTreeIter iter;
         if (!gtk_tree_model_get_iter_first(m_pTreeModel, &iter))
             return -1;
 
-        OString aStr(OUStringToOString(rStr, RTL_TEXTENCODING_UTF8).getStr());
         int nRet = 0;
+
+        if (!bSearchMRUArea && m_nMRUCount)
+        {
+            if (!gtk_tree_model_iter_nth_child(m_pTreeModel, &iter, nullptr, m_nMRUCount + 1))
+                return -1;
+            nRet += (m_nMRUCount + 1);
+        }
+
+        OString aStr(OUStringToOString(rStr, RTL_TEXTENCODING_UTF8).getStr());
         do
         {
             gchar* pStr;
@@ -12961,22 +12905,38 @@ private:
         return -1;
     }
 
-    bool separator_function(int nIndex)
+    bool separator_function(GtkTreePath* path)
+    {
+        bool bFound = false;
+        for (auto& a : m_aSeparatorRows)
+        {
+            GtkTreePath* seppath = gtk_tree_row_reference_get_path(a.get());
+            if (seppath)
+            {
+                bFound = gtk_tree_path_compare(path, seppath) == 0;
+                gtk_tree_path_free(seppath);
+            }
+            if (bFound)
+                break;
+        }
+        return bFound;
+    }
+
+    bool separator_function(int pos)
     {
-        return std::find(m_aSeparatorRows.begin(), m_aSeparatorRows.end(), nIndex) != m_aSeparatorRows.end();
+        GtkTreePath* path = gtk_tree_path_new_from_indices(pos, -1);
+        bool bRet = separator_function(path);
+        gtk_tree_path_free(path);
+        return bRet;
     }
 
     static gboolean separatorFunction(GtkTreeModel* pTreeModel, GtkTreeIter* pIter, gpointer widget)
     {
         GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
         GtkTreePath* path = gtk_tree_model_get_path(pTreeModel, pIter);
-
-        gint depth;
-        gint* indices = gtk_tree_path_get_indices_with_depth(path, &depth);
-        int nIndex = indices[depth-1];
-
+        bool bRet = pThis->separator_function(path);
         gtk_tree_path_free(path);
-        return pThis->separator_function(nIndex);
+        return bRet;
     }
 
     // https://gitlab.gnome.org/GNOME/gtk/issues/310
@@ -13031,12 +12991,12 @@ private:
                 sal_uInt16 nKeyMod = aKeyCode.GetModifier();
                 if (!nKeyMod)
                 {
-                    int nCount = get_count();
-                    int nActive = get_active() + 1;
+                    int nCount = get_count_including_mru();
+                    int nActive = get_active_including_mru() + 1;
                     while (nActive < nCount && separator_function(nActive))
                         ++nActive;
                     if (nActive < nCount)
-                        set_active(nActive);
+                        set_active_including_mru(nActive);
                     bDone = true;
                 }
                 else if (nKeyMod == KEY_MOD2 && !m_bPopupActive)
@@ -13051,11 +13011,12 @@ private:
                 sal_uInt16 nKeyMod = aKeyCode.GetModifier();
                 if (!nKeyMod)
                 {
-                    int nActive = get_active() - 1;
-                    while (nActive >= 0 && separator_function(nActive))
+                    int nStartBound = m_bPopupActive ? 0 : (m_nMRUCount + 1);
+                    int nActive = get_active_including_mru() - 1;
+                    while (nActive >= nStartBound && separator_function(nActive))
                         --nActive;
-                    if (nActive >= 0)
-                        set_active(nActive);
+                    if (nActive >= nStartBound)
+                        set_active_including_mru(nActive);
                     bDone = true;
                 }
                 break;
@@ -13065,12 +13026,13 @@ private:
                 sal_uInt16 nKeyMod = aKeyCode.GetModifier();
                 if (!nKeyMod)
                 {
-                    int nCount = get_count();
-                    int nActive = 0;
+                    int nCount = get_count_including_mru();
+                    int nStartBound = m_bPopupActive ? 0 : (m_nMRUCount + 1);
+                    int nActive = nStartBound;
                     while (nActive < nCount && separator_function(nActive))
                         ++nActive;
                     if (nActive < nCount)
-                        set_active(nActive);
+                        set_active_including_mru(nActive);
                     bDone = true;
                 }
                 break;
@@ -13080,11 +13042,12 @@ private:
                 sal_uInt16 nKeyMod = aKeyCode.GetModifier();
                 if (!nKeyMod)
                 {
-                    int nActive = get_count() - 1;
-                    while (nActive >= 0 && separator_function(nActive))
+                    int nActive = get_count_including_mru() - 1;
+                    int nStartBound = m_bPopupActive ? 0 : (m_nMRUCount + 1);
+                    while (nActive >= nStartBound && separator_function(nActive))
                         --nActive;
-                    if (nActive >= 0)
-                        set_active(nActive);
+                    if (nActive >= nStartBound)
+                        set_active_including_mru(nActive);
                     bDone = true;
                 }
                 break;
@@ -13170,10 +13133,10 @@ private:
 
     vcl::StringEntryIdentifier typeahead_getEntry(int nPos, OUString& out_entryText) const
     {
-        int nEntryCount(get_count());
+        int nEntryCount(get_count_including_mru());
         if (nPos >= nEntryCount)
             nPos = 0;
-        out_entryText = get_text(nPos);
+        out_entryText = get_text_including_mru(nPos);
 
         // vcl::StringEntryIdentifier does not allow for 0 values, but our position is 0-based
         // => normalize
@@ -13186,7 +13149,7 @@ private:
         return reinterpret_cast<sal_Int64>(entry) - 1;
     }
 
-    void set_cursor(int pos)
+    void tree_view_set_cursor(int pos)
     {
         if (pos == -1)
         {
@@ -13228,15 +13191,15 @@ private:
         if (m_bPopupActive)
             return tree_view_get_cursor();
         else
-            return get_active();
+            return get_active_including_mru();
     }
 
     void set_selected_entry(int nSelect)
     {
         if (m_bPopupActive)
-            set_cursor(nSelect);
+            tree_view_set_cursor(nSelect);
         else
-            set_active(nSelect);
+            set_active_including_mru(nSelect);
     }
 
     virtual vcl::StringEntryIdentifier CurrentEntry(OUString& out_entryText) const override
@@ -13262,7 +13225,7 @@ private:
         }
 
         // normalize
-        int nCount = get_count();
+        int nCount = get_count_including_mru();
         if (nSelect >= nCount)
             nSelect = nCount ? nCount-1 : -1;
 
@@ -13355,10 +13318,175 @@ private:
         if (m_pEntry)
             gtk_entry_set_text(GTK_ENTRY(m_pEntry), OUStringToOString(get_text(nActive), RTL_TEXTENCODING_UTF8).getStr());
         else
-            set_cursor(nActive);
+            tree_view_set_cursor(nActive);
         enable_notify_events();
         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_pToggleButton), false);
         fire_signal_changed();
+        update_mru();
+    }
+
+    void do_clear()
+    {
+        disable_notify_events();
+        gtk_tree_view_set_row_separator_func(m_pTreeView, nullptr, nullptr, nullptr);
+        m_aSeparatorRows.clear();
+        gtk_list_store_clear(GTK_LIST_STORE(m_pTreeModel));
+        m_nMRUCount = 0;
+        enable_notify_events();
+    }
+
+    virtual int get_max_mru_count() const override
+    {
+        return m_nMaxMRUCount;
+    }
+
+    virtual void set_max_mru_count(int nMaxMRUCount) override
+    {
+        m_nMaxMRUCount = nMaxMRUCount;
+        update_mru();
+    }
+
+    void update_mru()
+    {
+        int nMRUCount = m_nMRUCount;
+
+        if (m_nMaxMRUCount)
+        {
+            OUString sActiveText = get_active_text();
+            OUString sActiveId = get_active_id();
+            insert_including_mru(0, sActiveText, &sActiveId, nullptr, nullptr);
+            ++m_nMRUCount;
+
+            for (int i = 1; i < m_nMRUCount - 1; ++i)
+            {
+                if (get_text_including_mru(i) == sActiveText)
+                {
+                    remove_including_mru(i);
+                    --m_nMRUCount;
+                    break;
+                }
+            }
+
+//TODO            set_active(0);
+        }
+
+        while (m_nMRUCount > m_nMaxMRUCount)
+        {
+            remove_including_mru(m_nMRUCount - 1);
+            --m_nMRUCount;
+        }
+
+        if (m_nMRUCount && !nMRUCount)
+            insert_separator_including_mru(m_nMRUCount, "separator");
+        else if (!m_nMRUCount && nMRUCount)
+            remove_including_mru(m_nMRUCount);  // remove separator
+    }
+
+    int get_count_including_mru() const
+    {
+        return gtk_tree_model_iter_n_children(m_pTreeModel, nullptr);
+    }
+
+    int get_active_including_mru() const
+    {
+        return tree_view_get_cursor();
+    }
+
+    void set_active_including_mru(int pos)
+    {
+        disable_notify_events();
+
+        tree_view_set_cursor(pos);
+
+        if (m_pEntry)
+        {
+            if (pos != -1)
+                gtk_entry_set_text(GTK_ENTRY(m_pEntry), OUStringToOString(get_text_including_mru(pos), RTL_TEXTENCODING_UTF8).getStr());
+            else
+                gtk_entry_set_text(GTK_ENTRY(m_pEntry), "");
+        }
+
+        m_bChangedByMenu = false;
+        enable_notify_events();
+    }
+
+    int find_text_including_mru(const OUString& rStr, bool bSearchMRU) const
+    {
+        return find(rStr, m_nTextCol, bSearchMRU);
+    }
+
+    int find_id_including_mru(const OUString& rId, bool bSearchMRU) const
+    {
+        return find(rId, m_nIdCol, bSearchMRU);
+    }
+
+    OUString get_text_including_mru(int pos) const
+    {
+        return get(pos, m_nTextCol);
+    }
+
+    OUString get_id_including_mru(int pos) const
+    {
+        return get(pos, m_nIdCol);
+    }
+
+    void set_id_including_mru(int pos, const OUString& rId)
+    {
+        set(pos, m_nIdCol, rId);
+    }
+
+    void remove_including_mru(int pos)
+    {
+        disable_notify_events();
+        GtkTreeIter iter;
+        gtk_tree_model_iter_nth_child(m_pTreeModel, &iter, nullptr, pos);
+        if (!m_aSeparatorRows.empty())
+        {
+            bool bFound = false;
+
+            GtkTreePath* pPath = gtk_tree_path_new_from_indices(pos, -1);
+
+            for (auto aIter = m_aSeparatorRows.begin(); aIter != m_aSeparatorRows.end(); ++aIter)
+            {
+                GtkTreePath* seppath = gtk_tree_row_reference_get_path(aIter->get());
+                if (seppath)
+                {
+                    if (gtk_tree_path_compare(pPath, seppath) == 0)
+                        bFound = true;
+                    gtk_tree_path_free(seppath);
+                }
+                if (bFound)
+                {
+                    m_aSeparatorRows.erase(aIter);
+                    break;
+                }
+            }
+
+            gtk_tree_path_free(pPath);
+        }
+        gtk_list_store_remove(GTK_LIST_STORE(m_pTreeModel), &iter);
+        enable_notify_events();
+    }
+
+    void insert_separator_including_mru(int pos, const OUString& rId)
+    {
+        disable_notify_events();
+        GtkTreeIter iter;
+        if (!gtk_tree_view_get_row_separator_func(m_pTreeView))
+            gtk_tree_view_set_row_separator_func(m_pTreeView, separatorFunction, this, nullptr);
+        insert_row(GTK_LIST_STORE(m_pTreeModel), iter, pos, &rId, "", nullptr, nullptr);
+        GtkTreePath* pPath = gtk_tree_path_new_from_indices(pos, -1);
+        m_aSeparatorRows.emplace_back(gtk_tree_row_reference_new(m_pTreeModel, pPath));
+        gtk_tree_path_free(pPath);
+        enable_notify_events();
+    }
+
+    void insert_including_mru(int pos, const OUString& rText, const OUString* pId, const OUString* pIconName, VirtualDevice* pImageSurface)
+    {
+        disable_notify_events();
+        GtkTreeIter iter;
+        insert_row(GTK_LIST_STORE(m_pTreeModel), iter, pos, pId, rText, pIconName, pImageSurface);
+        enable_notify_events();
     }
 
 public:
@@ -13390,6 +13518,8 @@ public:
         , m_nAutoCompleteIdleId(0)
         , m_nNonCustomLineHeight(-1)
         , m_nPrePopupCursorPos(-1)
+        , m_nMRUCount(0)
+        , m_nMaxMRUCount(0)
     {
         insertParent(GTK_WIDGET(m_pComboBox), GTK_WIDGET(getContainer()));
         gtk_widget_set_visible(GTK_WIDGET(m_pComboBox), false);
@@ -13489,7 +13619,19 @@ public:
 
     virtual int get_active() const override
     {
-        return tree_view_get_cursor();
+        int nActive = get_active_including_mru();
+        if (nActive == -1)
+            return -1;
+
+        if (m_nMRUCount)
+        {
+            if (nActive < m_nMRUCount)
+                nActive = find_text(get_text_including_mru(nActive));
+            else
+                nActive -= (m_nMRUCount + 1);
+        }
+
+        return nActive;
     }
 
     virtual OUString get_active_id() const override
@@ -13543,20 +13685,9 @@ public:
 
     virtual void set_active(int pos) override
     {
-        disable_notify_events();
-
-        set_cursor(pos);
-
-        if (m_pEntry)
-        {
-            if (pos != -1)
-                gtk_entry_set_text(GTK_ENTRY(m_pEntry), OUStringToOString(get_text(pos), RTL_TEXTENCODING_UTF8).getStr());
-            else
-                gtk_entry_set_text(GTK_ENTRY(m_pEntry), "");
-        }
-
-        m_bChangedByMenu = false;
-        enable_notify_events();
+        if (m_nMRUCount && pos != -1)
+            pos += (m_nMRUCount + 1);
+        set_active_including_mru(pos);
     }
 
     virtual OUString get_active_text() const override
@@ -13576,17 +13707,23 @@ public:
 
     virtual OUString get_text(int pos) const override
     {
-        return get(pos, m_nTextCol);
+        if (m_nMRUCount)
+            pos += (m_nMRUCount + 1);
+        return get_text_including_mru(pos);
     }
 
     virtual OUString get_id(int pos) const override
     {
-        return get(pos, m_nIdCol);
+        if (m_nMRUCount)
+            pos += (m_nMRUCount + 1);
+        return get_id_including_mru(pos);
     }
 
     virtual void set_id(int pos, const OUString& rId) override
     {
-        set(pos, m_nIdCol, rId);
+        if (m_nMRUCount)
+            pos += (m_nMRUCount + 1);
+        set_id_including_mru(pos, rId);
     }
 
     virtual void insert_vector(const std::vector<weld::ComboBoxEntry>& rItems, bool bKeepExisting) override
@@ -13605,56 +13742,53 @@ public:
 
     virtual void remove(int pos) override
     {
-        disable_notify_events();
-        GtkTreeIter iter;
-        gtk_tree_model_iter_nth_child(m_pTreeModel, &iter, nullptr, pos);
-        gtk_list_store_remove(GTK_LIST_STORE(m_pTreeModel), &iter);
-        m_aSeparatorRows.erase(std::remove(m_aSeparatorRows.begin(), m_aSeparatorRows.end(), pos), m_aSeparatorRows.end());
-        enable_notify_events();
+        if (m_nMRUCount)
+            pos += (m_nMRUCount + 1);
+        remove_including_mru(pos);
     }
 
     virtual void insert(int pos, const OUString& rText, const OUString* pId, const OUString* pIconName, VirtualDevice* pImageSurface) override
     {
-        disable_notify_events();
-        GtkTreeIter iter;
-        insert_row(GTK_LIST_STORE(m_pTreeModel), iter, pos, pId, rText, pIconName, pImageSurface);
-        enable_notify_events();
+        if (m_nMRUCount && pos != -1)
+            pos += (m_nMRUCount + 1);
+        insert_including_mru(pos, rText, pId, pIconName, pImageSurface);
     }
 
     virtual void insert_separator(int pos, const OUString& rId) override
     {
-        disable_notify_events();
-        GtkTreeIter iter;
         pos = pos == -1 ? get_count() : pos;
-        m_aSeparatorRows.push_back(pos);
-        if (!gtk_tree_view_get_row_separator_func(m_pTreeView))
-            gtk_tree_view_set_row_separator_func(m_pTreeView, separatorFunction, this, nullptr);
-        insert_row(GTK_LIST_STORE(m_pTreeModel), iter, pos, &rId, "", nullptr, nullptr);
-        enable_notify_events();
+        if (m_nMRUCount)
+            pos += (m_nMRUCount + 1);
+        insert_separator_including_mru(pos, rId);
     }
 
     virtual int get_count() const override
     {
-        return gtk_tree_model_iter_n_children(m_pTreeModel, nullptr);
+        int nCount = get_count_including_mru();
+        if (m_nMRUCount)
+            nCount -= (m_nMRUCount + 1);
+        return nCount;
     }
 
     virtual int find_text(const OUString& rStr) const override
     {
-        return find(rStr, m_nTextCol);
+        int nPos = find_text_including_mru(rStr, false);
+        if (m_nMRUCount)
+            nPos -= (m_nMRUCount + 1);
+        return nPos;
     }
 
     virtual int find_id(const OUString& rId) const override
     {
-        return find(rId, m_nIdCol);
+        int nPos = find_id_including_mru(rId, false);
+        if (m_nMRUCount)
+            nPos -= (m_nMRUCount + 1);
+        return nPos;
     }
 
     virtual void clear() override
     {
-        disable_notify_events();
-        gtk_list_store_clear(GTK_LIST_STORE(m_pTreeModel));
-        m_aSeparatorRows.clear();
-        gtk_combo_box_set_row_separator_func(m_pComboBox, nullptr, nullptr, nullptr);
-        enable_notify_events();
+        do_clear();
     }
 
     virtual void make_sorted() override
@@ -13860,8 +13994,88 @@ public:
         return m_bChangedByMenu;
     }
 
+    virtual void set_custom_renderer() override
+    {
+        GList* pColumns = gtk_tree_view_get_columns(m_pTreeView);
+        // keep the original height around for optimal popup height calculation
+        m_nNonCustomLineHeight = ::get_height_row(m_pTreeView, pColumns);
+        GtkTreeViewColumn* pColumn = GTK_TREE_VIEW_COLUMN(pColumns->data);
+        gtk_cell_layout_clear(GTK_CELL_LAYOUT(pColumn));
+        GtkCellRenderer *pRenderer = custom_cell_renderer_surface_new();
+        GValue value = G_VALUE_INIT;
+        g_value_init(&value, G_TYPE_POINTER);
+        g_value_set_pointer(&value, static_cast<gpointer>(this));
+        g_object_set_property(G_OBJECT(pRenderer), "instance", &value);
+        gtk_tree_view_column_pack_start(pColumn, pRenderer, true);
+        gtk_tree_view_column_add_attribute(pColumn, pRenderer, "text", m_nTextCol);
+        gtk_tree_view_column_add_attribute(pColumn, pRenderer, "id", m_nIdCol);
+        g_list_free(pColumns);
+    }
+
+    void call_signal_custom_render(VirtualDevice& rOutput, const tools::Rectangle& rRect, bool bSelected, const OUString& rId)
+    {
+        signal_custom_render(rOutput, rRect, bSelected, rId);
+    }
+
+    Size call_signal_custom_get_size(VirtualDevice& rOutput, const OUString& rId)
+    {
+        return signal_custom_get_size(rOutput, rId);
+    }
+
+    VclPtr<VirtualDevice> create_render_virtual_device() const override
+    {
+        return create_virtual_device();
+    }
+
+    OUString get_mru_entries() const override
+    {
+        const sal_Unicode cSep = ';';
+
+        OUStringBuffer aEntries;
+        for (sal_Int32 n = 0; n < m_nMRUCount; n++)
+        {
+            aEntries.append(get_text_including_mru(n));
+            if (n < m_nMRUCount - 1)
+                aEntries.append(cSep);
+        }
+        return aEntries.makeStringAndClear();
+    }
+
+    virtual void set_mru_entries(const OUString& rEntries) override
+    {
+        const sal_Unicode cSep = ';';
+

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list