include/sfx2/tabdlg.hxx | 16 +++++++++++-- include/vcl/tabs.hrc | 38 ++++++++++++++++++++++++++++++ include/vcl/weld.hxx | 9 ++++--- sfx2/source/dialog/tabdlg.cxx | 25 +++++++++++++++++--- vcl/inc/jsdialog/jsdialogbuilder.hxx | 6 +++- vcl/inc/qt5/QtInstanceNotebook.hxx | 3 +- vcl/inc/salvtables.hxx | 6 +++- vcl/jsdialog/jsdialogbuilder.cxx | 10 ++++---- vcl/qt5/QtInstanceNotebook.cxx | 3 +- vcl/source/app/salvtables.cxx | 8 ++++-- vcl/unx/gtk3/gtkinst.cxx | 43 +++++++++++++++++++++++++++++++---- 11 files changed, 141 insertions(+), 26 deletions(-)
New commits: commit 05919359717b8b7b2fff6ad73088d34ee397e297 Author: Heiko Tietze <tietze.he...@gmail.com> AuthorDate: Thu Jul 3 12:36:28 2025 +0200 Commit: Heiko Tietze <heiko.tie...@documentfoundation.org> CommitDate: Wed Jul 9 10:50:14 2025 +0200 Show icons on tabs Simplifies the setup of SfxTabDialogs as tabs can be added per code; and vertical tabs should be accompanied by icons. One definition of tab label/icon makes the code more consistent and minimizes the effort for localization. Allows to switch between horizontal and vertical tabs via code. Change-Id: Ib631bcbd76a8e24ab15ff824a37e346b82756b82 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187307 Reviewed-by: Heiko Tietze <heiko.tie...@documentfoundation.org> Tested-by: Jenkins diff --git a/include/sfx2/tabdlg.hxx b/include/sfx2/tabdlg.hxx index 17d0dd035f78..71bad7aa6d59 100644 --- a/include/sfx2/tabdlg.hxx +++ b/include/sfx2/tabdlg.hxx @@ -129,11 +129,23 @@ public: void AddTabPage(const OUString& rName, // Name of the label for the new page to create const OUString& rLabel, // UI Label for the new page to create - CreateTabPage pCreateFunc); // != 0 + CreateTabPage pCreateFunc, // != 0 + const OUString* pIconName = nullptr); + + void AddTabPage(const OUString& rName, + const OUString& rLabel, + CreateTabPage pCreateFunc, + const OUString& rIconName); void AddTabPage(const OUString& rName, // Name of the label for the new page to create const OUString& rLabel, // UI Label for the new page to create - sal_uInt16 nPageCreateId); // Identifier of the Factory Method to create the page + sal_uInt16 nPageCreateId, // Identifier of the Factory Method to create the page + const OUString* pIconName = nullptr); + + void AddTabPage(const OUString& rName, + const OUString& rLabel, + sal_uInt16 nPageCreateId, + const OUString& rIconName); void RemoveTabPage( const OUString& rName ); // Name of the label for the page in the notebook .ui diff --git a/include/vcl/tabs.hrc b/include/vcl/tabs.hrc new file mode 100644 index 000000000000..a0bb3f7c185a --- /dev/null +++ b/include/vcl/tabs.hrc @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#define NC_(Context, String) TranslateId(Context, u8##String) + +#include <rtl/ustring.hxx> +#include <unotools/resmgr.hxx> + +struct TabData +{ + TranslateId aLabel; + OUString sIconName; +}; + +/* + * Tabs are used on dialogs with a few items, usually <4, where it should be + * accompanied with larger 32px icons and dialogs with more content using 24px icons; + * the effective icon is loaded per RID_M + RID_TAB*.sIconName +*/ + +static constexpr OUString RID_M = u"cmd/lc_"_ustr; +static constexpr OUString RID_L = u"cmd/32/"_ustr; + + +static OUString TabResId(TranslateId aId) +{ + return Translate::get(aId, Translate::Create("vcl")); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx index 2fa451f32bc4..564004be1905 100644 --- a/include/vcl/weld.hxx +++ b/include/vcl/weld.hxx @@ -525,10 +525,13 @@ public: virtual void set_current_page(int nPage) = 0; virtual void set_current_page(const OUString& rIdent) = 0; virtual void remove_page(const OUString& rIdent) = 0; - virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos) = 0; - void append_page(const OUString& rIdent, const OUString& rLabel) + virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos, + const OUString* pIconName = nullptr) + = 0; + void append_page(const OUString& rIdent, const OUString& rLabel, + const OUString* pIconName = nullptr) { - insert_page(rIdent, rLabel, -1); + insert_page(rIdent, rLabel, -1, pIconName); } virtual void set_tab_label_text(const OUString& rIdent, const OUString& rLabel) = 0; virtual OUString get_tab_label_text(const OUString& rIdent) const = 0; diff --git a/sfx2/source/dialog/tabdlg.cxx b/sfx2/source/dialog/tabdlg.cxx index 9320146c9fe6..9bdc433dab5f 100644 --- a/sfx2/source/dialog/tabdlg.cxx +++ b/sfx2/source/dialog/tabdlg.cxx @@ -880,21 +880,38 @@ void SfxTabDialogController::AddTabPage(const OUString &rName /* Page ID */, void SfxTabDialogController::AddTabPage(const OUString &rName, /* Page ID */ const OUString& rRiderText, - CreateTabPage pCreateFunc /* Pointer to the Factory Method */) + CreateTabPage pCreateFunc, /* Pointer to the Factory Method */ + const OUString* pIconName) { assert(!m_xTabCtrl->get_page(rName) && "Double Page-Ids in the Tabpage"); - m_xTabCtrl->append_page(rName, rRiderText); + m_xTabCtrl->append_page(rName, rRiderText, pIconName); AddTabPage(rName, pCreateFunc, nullptr); } +void SfxTabDialogController::AddTabPage(const OUString &rName, + const OUString& rRiderText, + CreateTabPage pCreateFunc, + const OUString& rIconName) +{ + AddTabPage(rName, rRiderText, pCreateFunc, &rIconName); +} + void SfxTabDialogController::AddTabPage(const OUString &rName, const OUString& rRiderText, - sal_uInt16 nPageCreateId /* Identifier of the Factory Method to create the page */) + sal_uInt16 nPageCreateId, /* Identifier of the Factory Method to create the page */ + const OUString* pIconName) { assert(!m_xTabCtrl->get_page(rName) && "Double Page-Ids in the Tabpage"); - m_xTabCtrl->append_page(rName, rRiderText); + m_xTabCtrl->append_page(rName, rRiderText, pIconName); AddTabPage(rName, nPageCreateId); } +void SfxTabDialogController::AddTabPage(const OUString &rName, const OUString& rRiderText, + sal_uInt16 nPageCreateId, + const OUString& rIconName) +{ + AddTabPage(rName, rRiderText, nPageCreateId, &rIconName); +} + /* [Description] Default implementation of the virtual Method. diff --git a/vcl/inc/jsdialog/jsdialogbuilder.hxx b/vcl/inc/jsdialog/jsdialogbuilder.hxx index bc2817dd317c..be76e9c54164 100644 --- a/vcl/inc/jsdialog/jsdialogbuilder.hxx +++ b/vcl/inc/jsdialog/jsdialogbuilder.hxx @@ -557,7 +557,8 @@ public: bool bTakeOwnership); virtual void remove_page(const OUString& rIdent) override; - virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos) override; + virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos, + const OUString* pIconName = nullptr) override; }; class JSVerticalNotebook final : public JSWidget<SalInstanceVerticalNotebook, ::VerticalTabControl> @@ -567,7 +568,8 @@ public: SalInstanceBuilder* pBuilder, bool bTakeOwnership); virtual void remove_page(const OUString& rIdent) override; - virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos) override; + virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos, + const OUString* pIconName = nullptr) override; }; class JSSpinButton final : public JSWidget<SalInstanceSpinButton, ::FormattedField> diff --git a/vcl/inc/qt5/QtInstanceNotebook.hxx b/vcl/inc/qt5/QtInstanceNotebook.hxx index 2a18a65784a0..0e6e2280dd55 100644 --- a/vcl/inc/qt5/QtInstanceNotebook.hxx +++ b/vcl/inc/qt5/QtInstanceNotebook.hxx @@ -36,7 +36,8 @@ public: virtual void set_current_page(int nPage) override; virtual void set_current_page(const OUString& rIdent) override; virtual void remove_page(const OUString& rIdent) override; - virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos) override; + virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos, + const OUString* pIconName = nullptr) override; virtual void set_tab_label_text(const OUString& rIdent, const OUString& rLabel) override; virtual OUString get_tab_label_text(const OUString& rIdent) const override; virtual void set_show_tabs(bool bShow) override; diff --git a/vcl/inc/salvtables.hxx b/vcl/inc/salvtables.hxx index 49ebd5bb0f23..0934d4d06309 100644 --- a/vcl/inc/salvtables.hxx +++ b/vcl/inc/salvtables.hxx @@ -1178,7 +1178,8 @@ public: virtual void remove_page(const OUString& rIdent) override; - virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos) override; + virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos, + const OUString* pIconName = nullptr) override; virtual int get_n_pages() const override; @@ -2333,7 +2334,8 @@ public: virtual void remove_page(const OUString& rIdent) override; - virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos) override; + virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos, + const OUString* pIconName = nullptr) override; virtual int get_n_pages() const override; diff --git a/vcl/jsdialog/jsdialogbuilder.cxx b/vcl/jsdialog/jsdialogbuilder.cxx index 7f9693a9d4cf..c5414e70a615 100644 --- a/vcl/jsdialog/jsdialogbuilder.cxx +++ b/vcl/jsdialog/jsdialogbuilder.cxx @@ -1359,9 +1359,10 @@ void JSNotebook::remove_page(const OUString& rIdent) sendFullUpdate(); } -void JSNotebook::insert_page(const OUString& rIdent, const OUString& rLabel, int nPos) +void JSNotebook::insert_page(const OUString& rIdent, const OUString& rLabel, int nPos, + const OUString* pIconName) { - SalInstanceNotebook::insert_page(rIdent, rLabel, nPos); + SalInstanceNotebook::insert_page(rIdent, rLabel, nPos, pIconName); sendFullUpdate(); } @@ -1378,9 +1379,10 @@ void JSVerticalNotebook::remove_page(const OUString& rIdent) sendFullUpdate(); } -void JSVerticalNotebook::insert_page(const OUString& rIdent, const OUString& rLabel, int nPos) +void JSVerticalNotebook::insert_page(const OUString& rIdent, const OUString& rLabel, int nPos, + const OUString* pIconName) { - SalInstanceVerticalNotebook::insert_page(rIdent, rLabel, nPos); + SalInstanceVerticalNotebook::insert_page(rIdent, rLabel, nPos, pIconName); sendFullUpdate(); } diff --git a/vcl/qt5/QtInstanceNotebook.cxx b/vcl/qt5/QtInstanceNotebook.cxx index 706ee6aa583d..8ff95b59f8bf 100644 --- a/vcl/qt5/QtInstanceNotebook.cxx +++ b/vcl/qt5/QtInstanceNotebook.cxx @@ -100,7 +100,8 @@ void QtInstanceNotebook::remove_page(const OUString& rIdent) GetQtInstance().RunInMainThread([&] { m_pTabWidget->removeTab(get_page_index(rIdent)); }); } -void QtInstanceNotebook::insert_page(const OUString& rIdent, const OUString& rLabel, int nPos) +void QtInstanceNotebook::insert_page(const OUString& rIdent, const OUString& rLabel, int nPos, + const OUString* /* pIconName */) { SolarMutexGuard g; GetQtInstance().RunInMainThread([&] { diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx index ebfce424463f..4035f772c5bd 100644 --- a/vcl/source/app/salvtables.cxx +++ b/vcl/source/app/salvtables.cxx @@ -2723,7 +2723,8 @@ void SalInstanceNotebook::remove_page(const OUString& rIdent) } } -void SalInstanceNotebook::insert_page(const OUString& rIdent, const OUString& rLabel, int nPos) +void SalInstanceNotebook::insert_page(const OUString& rIdent, const OUString& rLabel, int nPos, + const OUString* /* pIconName */) { sal_uInt16 nPageCount = m_xNotebook->GetPageCount(); sal_uInt16 nLastPageId = nPageCount ? m_xNotebook->GetPageId(nPageCount - 1) : 0; @@ -2847,12 +2848,13 @@ void SalInstanceVerticalNotebook::remove_page(const OUString& rIdent) } void SalInstanceVerticalNotebook::insert_page(const OUString& rIdent, const OUString& rLabel, - int nPos) + int nPos, const OUString* pIconName) { VclPtrInstance<VclGrid> xGrid(m_xNotebook->GetPageParent()); xGrid->set_hexpand(true); xGrid->set_vexpand(true); - m_xNotebook->InsertPage(rIdent, rLabel, Image(), u""_ustr, xGrid, nPos); + Image aImage = pIconName ? Image(StockImage::Yes, *pIconName) : Image(); + m_xNotebook->InsertPage(rIdent, rLabel, aImage, u""_ustr, xGrid, nPos); } int SalInstanceVerticalNotebook::get_n_pages() const { return m_xNotebook->GetPageCount(); } diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx index 4924183de978..72b4a7125d1d 100644 --- a/vcl/unx/gtk3/gtkinst.cxx +++ b/vcl/unx/gtk3/gtkinst.cxx @@ -9048,12 +9048,47 @@ private: enable_notify_events(); } - void insert_page(GtkNotebook *pNotebook, const OUString& rIdent, const OUString& rLabel, GtkWidget *pChild, int nPos) + void insert_page(GtkNotebook* pNotebook, const OUString& rIdent, const OUString& rLabel, + GtkWidget* pChild, int nPos, const OUString* pIconName = nullptr) { disable_notify_events(); - GtkWidget *pTabWidget = gtk_label_new_with_mnemonic(MapToGtkAccelerator(rLabel).getStr()); + GtkWidget* pLabel = gtk_label_new_with_mnemonic(MapToGtkAccelerator(rLabel).getStr()); + GtkWidget* pTabWidget = nullptr; + + GtkWidget* pImage = nullptr; + if (pIconName) + pImage = image_new_from_icon_name(*pIconName); + + if (pImage) + { + // image/label should be stacked vertically for only a few tabs with large icons + bool bLarge = false; +#if !GTK_CHECK_VERSION(4, 0, 0) + GdkPixbuf* pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(pImage)); + bLarge = gdk_pixbuf_get_height(pixbuf) > 24; +#endif + GtkBox* pBox = GTK_BOX(gtk_box_new(bLarge ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL, 6)); + gtk_label_set_xalign(GTK_LABEL(pLabel), bLarge ? 0.5 : 0.0); +#if !GTK_CHECK_VERSION(4, 0, 0) + gtk_box_pack_start(pBox, pImage, false, true, 0); + gtk_box_pack_start(pBox, pLabel, true, true, 0); +#else + gtk_box_insert_child_after(GTK_BOX(pBox), pImage, nullptr); + gtk_box_insert_child_after(GTK_BOX(pBox), pLabel, pImage); +#endif + pTabWidget = GTK_WIDGET(pBox); +#if !GTK_CHECK_VERSION(4, 0, 0) + gtk_widget_show_all(pTabWidget); +#endif + } + else + { + pTabWidget = pLabel; + } + ::set_buildable_id(GTK_BUILDABLE(pTabWidget), rIdent); + gtk_notebook_insert_page(pNotebook, pChild, pTabWidget, nPos); gtk_widget_set_visible(pChild, true); gtk_widget_set_visible(pTabWidget, true); @@ -9598,7 +9633,7 @@ public: m_aPages.erase(m_aPages.begin() + nPageIndex); } - virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos) override + virtual void insert_page(const OUString& rIdent, const OUString& rLabel, int nPos, const OUString* pIconName = nullptr) override { if (m_bOverFlowBoxActive) { @@ -9610,7 +9645,7 @@ public: gtk_widget_set_visible(GTK_WIDGET(m_pOverFlowNotebook), false); m_bOverFlowBoxActive = false; - insert_page(m_pNotebook, rIdent, rLabel, gtk_grid_new(), nPos); + insert_page(m_pNotebook, rIdent, rLabel, gtk_grid_new(), nPos, pIconName); } virtual ~GtkInstanceNotebook() override