cui/Library_cui.mk | 1 cui/source/dialogs/toolbartabpage.cxx | 175 ++++++++++++++-- cui/source/inc/toolbartabpage.hxx | 14 + cui/uiconfig/ui/toolbartabpage.ui | 123 +++++++++-- framework/source/layoutmanager/layoutmanager.cxx | 18 + framework/source/layoutmanager/toolbarlayoutmanager.cxx | 23 ++ framework/source/layoutmanager/toolbarlayoutmanager.hxx | 1 include/framework/layoutmanager.hxx | 1 8 files changed, 324 insertions(+), 32 deletions(-)
New commits: commit a4341164a1390e42af76e612da9c36ce87a5b78f Author: Heiko Tietze <[email protected]> AuthorDate: Thu Dec 18 12:16:02 2025 +0100 Commit: Heiko Tietze <[email protected]> CommitDate: Sat Feb 14 08:36:52 2026 +0100 Resolves tdf#38850 - Allow configuration of toolbars Toggle on/off for visibility, locked state, and sensitivity, the latter behind ExperimentalMode as it works only to suppress the toolbar but not to make it always visible Change-Id: Ia65a27045472d42fc013970bf9de64320f2dbcdc Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195836 Reviewed-by: Heiko Tietze <[email protected]> Tested-by: Jenkins diff --git a/cui/Library_cui.mk b/cui/Library_cui.mk index 7972a211b085..7899e3826cd1 100644 --- a/cui/Library_cui.mk +++ b/cui/Library_cui.mk @@ -40,6 +40,7 @@ $(eval $(call gb_Library_use_libraries,cui,\ docmodel \ drawinglayer \ editeng \ + fwk \ i18nlangtag \ i18nutil \ $(if $(ENABLE_JAVA), \ diff --git a/cui/source/dialogs/toolbartabpage.cxx b/cui/source/dialogs/toolbartabpage.cxx index 643128ca966f..416d7b7fb1f8 100644 --- a/cui/source/dialogs/toolbartabpage.cxx +++ b/cui/source/dialogs/toolbartabpage.cxx @@ -16,9 +16,11 @@ #include <com/sun/star/ui/theWindowStateConfiguration.hpp> #include <comphelper/processfactory.hxx> +#include <officecfg/Office/Common.hxx> #include <sfx2/viewfrm.hxx> #include <vcl/commandinfoprovider.hxx> #include <vcl/weld/Builder.hxx> +#include <vcl/settings.hxx> std::unique_ptr<SfxTabPage> ToolbarTabPage::Create(weld::Container* pPage, weld::DialogController* pController, @@ -32,9 +34,30 @@ ToolbarTabPage::ToolbarTabPage(weld::Container* pPage, weld::DialogController* p : SfxTabPage(pPage, pController, u"cui/ui/toolbartabpage.ui"_ustr, u"ToolbarTabPage"_ustr, &rAttr) , m_pToolbarList(m_xBuilder->weld_tree_view(u"tvToolbarList"_ustr)) + , m_pLockAllLabel(m_xBuilder->weld_label(u"lbLockAll"_ustr)) + , m_pLockAll(m_xBuilder->weld_check_button(u"cbLockAll"_ustr)) { - m_pToolbarList->enable_toggle_buttons(weld::ColumnToggleType::Check); m_pToolbarList->connect_toggled(LINK(this, ToolbarTabPage, ToggleHdl)); + m_pLockAll->connect_toggled(LINK(this, ToolbarTabPage, CheckBoxHdl)); +} + +void ToolbarTabPage::UpdateAllLocked() +{ + std::unique_ptr<weld::TreeIter> pIter = m_pToolbarList->make_iterator(); + bool bLoop = m_pToolbarList->get_iter_first(*pIter); + TriState bAllLocked = m_pToolbarList->get_toggle(*pIter, 2); + bLoop = m_pToolbarList->iter_next(*pIter); + while (bLoop) + { + if (bAllLocked == m_pToolbarList->get_toggle(*pIter, 2)) + bLoop = m_pToolbarList->iter_next(*pIter); + else + { + bAllLocked = TRISTATE_INDET; + bLoop = false; + } + } + m_pLockAll->set_state(bAllLocked); } void ToolbarTabPage::Reset(const SfxItemSet* /* rSet*/) @@ -68,11 +91,19 @@ void ToolbarTabPage::Reset(const SfxItemSet* /* rSet*/) return; css::uno::Reference<css::beans::XPropertySet> xPropSet(xFrame, css::uno::UNO_QUERY); - xPropSet->getPropertyValue(u"LayoutManager"_ustr) >>= m_xLayoutManager; + + css::uno::Reference<css::frame::XLayoutManager> xLayoutManager; + xPropSet->getPropertyValue(u"LayoutManager"_ustr) >>= xLayoutManager; + m_xLayoutManager = dynamic_cast<framework::LayoutManager*>(xLayoutManager.get()); css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>> aSeqDocToolBars; aSeqDocToolBars = xUIConfigMgr->getUIElementsInfo(css::ui::UIElementType::TOOLBAR); + tools::Long nLongestLabel = 0; + const bool bIsExperimentalMode = officecfg::Office::Common::Misc::ExperimentalMode::get(); + + m_pToolbarList->freeze(); + m_pToolbarList->clear(); for (css::uno::Sequence<css::beans::PropertyValue> const& props : aSeqDocToolBars) { for (css::beans::PropertyValue const& prop : props) @@ -82,7 +113,10 @@ void ToolbarTabPage::Reset(const SfxItemSet* /* rSet*/) css::uno::Sequence<css::beans::PropertyValue> aCmdProps; OUString sResourceURL; OUString sUIName; - bool bHide = true; + bool bHideFromMenu = true; + bool bLocked = false; + bool bSensitive = false; + // bool bVisible = false; prop.Value >>= sResourceURL; @@ -94,38 +128,149 @@ void ToolbarTabPage::Reset(const SfxItemSet* /* rSet*/) if (aCmdProp.Name == u"UIName"_ustr) aCmdProp.Value >>= sUIName; else if (aCmdProp.Name == u"HideFromToolbarMenu"_ustr) - aCmdProp.Value >>= bHide; + aCmdProp.Value >>= bHideFromMenu; + // else if (aCmdProp.Name == u"Visible"_ustr) + // aCmdProp.Value >>= bVisible; + else if (aCmdProp.Name == u"ContextSensitive"_ustr) + aCmdProp.Value >>= bSensitive; + else if (aCmdProp.Name == u"Locked"_ustr) + aCmdProp.Value >>= bLocked; } - if (!bHide) + + if (!bHideFromMenu) { + // prop:"Visible" is also true if the toolbar is contextually hidden + const bool bVisible = m_xLayoutManager->isElementVisible(sResourceURL); + const int nRow = m_pToolbarList->n_children(); + TriState aState; m_pToolbarList->append(); - const int nRow = m_pToolbarList->n_children() - 1; m_pToolbarList->set_id(nRow, sResourceURL); m_pToolbarList->set_text(nRow, sUIName, 0); - const bool bShow = m_xLayoutManager->isElementVisible(sResourceURL); - m_pToolbarList->set_toggle(nRow, bShow ? TRISTATE_TRUE : TRISTATE_FALSE); + aState = bVisible ? TRISTATE_TRUE : TRISTATE_FALSE; + m_pToolbarList->set_toggle(nRow, aState, 1); + aState = bLocked ? TRISTATE_TRUE : TRISTATE_FALSE; + m_pToolbarList->set_toggle(nRow, aState, 2); + if (bIsExperimentalMode) + { + aState = bSensitive ? TRISTATE_TRUE : TRISTATE_FALSE; + m_pToolbarList->set_toggle(nRow, aState, 3); + } + + tools::Long nWidth = m_pToolbarList->get_pixel_size(sUIName).Width(); + nLongestLabel = std::max(nLongestLabel, nWidth); } } } } } + m_pToolbarList->thaw(); m_pToolbarList->make_sorted(); + + // column widths + std::vector<int> aWidths; + aWidths.push_back(nLongestLabel + 50); // first col has some indention on kf6 + for (sal_uInt16 i = 1; i < 3; i++) + { + OUString sTitle = m_pToolbarList->get_column_title(i); + aWidths.push_back(m_pToolbarList->get_pixel_size(sTitle).Width() + 12); // some padding + } + if (!bIsExperimentalMode) + aWidths.push_back(0); // zero width for context_sensitive column, otherwise available space + m_pToolbarList->set_column_fixed_widths(aWidths); + + // treeview height + // auto nWidth = std::accumulate(aWidths.begin(), aWidths.end(), + // Application::GetSettings().GetStyleSettings().GetScrollBarSize()); + m_pToolbarList->set_size_request(-1, m_pToolbarList->get_height_rows(20)); + + // place Lock All checkbox underneath the Locked column + OUString aLabel = m_pLockAllLabel->get_label(); + const int nLockLabelSize = m_pLockAllLabel->get_pixel_size(aLabel).Width(); + m_pLockAllLabel->set_margin_start(aWidths[0] + aWidths[1] - nLockLabelSize); + + UpdateAllLocked(); +} + +void ToolbarTabPage::ShowToolbar(const OUString& sName, const bool bShow) +{ + if (bShow) + { + m_xLayoutManager->createElement(sName); + m_xLayoutManager->showElement(sName); + } + else + { + m_xLayoutManager->hideElement(sName); + m_xLayoutManager->destroyElement(sName); + } +} + +void ToolbarTabPage::LockToolbar(const OUString& sName, const bool bLock) +{ + const bool bIsVisible = m_xLayoutManager->isElementVisible(sName); + // changing the locked state is possible only for visible toolbars + if (!bIsVisible) + m_xLayoutManager->createElement(sName); + if (bLock) + { + m_xLayoutManager->dockWindow(sName, css::ui::DockingArea_DOCKINGAREA_DEFAULT, + css::awt::Point(SAL_MAX_INT32, SAL_MAX_INT32)); + m_xLayoutManager->lockWindow(sName); + } + else + m_xLayoutManager->unlockWindow(sName); + if (!bIsVisible) + m_xLayoutManager->destroyElement(sName); +} + +void ToolbarTabPage::SensitiveToolbar(const OUString& sName, const bool bSensitive) +{ + const bool bIsVisible = m_xLayoutManager->isElementVisible(sName); + if (!bIsVisible) + m_xLayoutManager->createElement(sName); + + m_xLayoutManager->makeContextSensitive(sName, bSensitive); + + if (!bIsVisible) + m_xLayoutManager->destroyElement(sName); +} + +IMPL_LINK(ToolbarTabPage, CheckBoxHdl, weld::Toggleable&, rCheckBox, void) +{ + const bool bLock = rCheckBox.get_state() == TRISTATE_TRUE; + + std::unique_ptr<weld::TreeIter> pIter = m_pToolbarList->make_iterator(); + bool bLoop = m_pToolbarList->get_iter_first(*pIter); + while (bLoop) + { + m_pToolbarList->set_toggle(*pIter, bLock ? TRISTATE_TRUE : TRISTATE_FALSE, 2); + LockToolbar(m_pToolbarList->get_id(*pIter), bLock); + bLoop = m_pToolbarList->iter_next(*pIter); + } + // gtk3 does not auto-set the checkbox state + m_pLockAll->set_state(bLock ? TRISTATE_TRUE : TRISTATE_FALSE); } IMPL_LINK(ToolbarTabPage, ToggleHdl, const weld::TreeView::iter_col&, rRowCol, void) { const int nRow(m_pToolbarList->get_iter_index_in_parent(rRowCol.first)); m_pToolbarList->select(nRow); - OUString sToolbarUrl(m_pToolbarList->get_id(nRow)); - if (m_pToolbarList->get_toggle(nRow) == TRISTATE_TRUE) + const OUString sToolbarUrl(m_pToolbarList->get_id(nRow)); + if (rRowCol.second == 1) // visible { - m_xLayoutManager->createElement(sToolbarUrl); - m_xLayoutManager->showElement(sToolbarUrl); + const bool bShow = m_pToolbarList->get_toggle(nRow, 1) == TRISTATE_TRUE; + ShowToolbar(sToolbarUrl, bShow); } - else + else if (rRowCol.second == 2) // locked + { + const bool bLocked = m_pToolbarList->get_toggle(nRow, 2) == TRISTATE_TRUE; + LockToolbar(sToolbarUrl, bLocked); + UpdateAllLocked(); + } + else if (rRowCol.second == 3) // sensitive { - m_xLayoutManager->hideElement(sToolbarUrl); - m_xLayoutManager->destroyElement(sToolbarUrl); + const bool bSensitive = m_pToolbarList->get_toggle(nRow, 3) == TRISTATE_TRUE; + SensitiveToolbar(sToolbarUrl, bSensitive); } } diff --git a/cui/source/inc/toolbartabpage.hxx b/cui/source/inc/toolbartabpage.hxx index 4aea5ba5c1c6..29d4f5683975 100644 --- a/cui/source/inc/toolbartabpage.hxx +++ b/cui/source/inc/toolbartabpage.hxx @@ -10,18 +10,28 @@ #include <sal/config.h> -#include <com/sun/star/frame/XLayoutManager.hpp> +#include <framework/layoutmanager.hxx> #include <sfx2/tabdlg.hxx> +#include <vcl/weld/weld.hxx> #include <vcl/weld/TreeView.hxx> class ToolbarTabPage : public SfxTabPage { private: - css::uno::Reference<css::frame::XLayoutManager> m_xLayoutManager; + rtl::Reference<framework::LayoutManager> m_xLayoutManager; std::unique_ptr<weld::TreeView> m_pToolbarList; + std::unique_ptr<weld::Label> m_pLockAllLabel; + std::unique_ptr<weld::CheckButton> m_pLockAll; virtual void Reset(const SfxItemSet* /* rSet*/) override; + + void ShowToolbar(const OUString& sName, const bool bShow); + void LockToolbar(const OUString& sName, const bool bLock); + void SensitiveToolbar(const OUString& sName, const bool bSensitive); + void UpdateAllLocked(); + DECL_LINK(ToggleHdl, const weld::TreeView::iter_col&, void); + DECL_LINK(CheckBoxHdl, weld::Toggleable&, void); public: ToolbarTabPage(weld::Container* pPage, weld::DialogController* pController, diff --git a/cui/uiconfig/ui/toolbartabpage.ui b/cui/uiconfig/ui/toolbartabpage.ui index 7d6cb9b0380f..cba80f58bb48 100644 --- a/cui/uiconfig/ui/toolbartabpage.ui +++ b/cui/uiconfig/ui/toolbartabpage.ui @@ -1,19 +1,32 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.40.0 --> +<!-- Created with Cambalache 0.96.3 --> <interface domain="cui"> <requires lib="gtk+" version="3.24"/> - <object class="GtkTreeStore" id="listStore"> + <object class="GtkTreeStore" id="modelToolbarList"> <columns> - <!-- column-name checkbox --> - <column type="gboolean"/> - <!-- column-name text --> + <!-- column-name Name --> <column type="gchararray"/> + <!-- column-name Visible --> + <column type="gboolean"/> + <!-- column-name Locked --> + <column type="gboolean"/> + <!-- column-name Sensitive --> + <column type="gboolean"/> <!-- column-name id --> <column type="gchararray"/> <!-- column-name checkvis1 --> <column type="gboolean"/> + <!-- column-name checkvis2 --> + <column type="gboolean"/> + <!-- column-name checkvis3 --> + <column type="gboolean"/> <!-- column-name checktri1 --> <column type="gboolean"/> + <!-- column-name checktri2 --> + <column type="gboolean"/> + <!-- column-name checktri3 --> + <column type="gboolean"/> </columns> </object> <object class="GtkBox" id="ToolbarTabPage"> @@ -39,11 +52,9 @@ <object class="GtkTreeView" id="tvToolbarList"> <property name="visible">True</property> <property name="can-focus">True</property> - <property name="receives-default">True</property> <property name="hexpand">True</property> <property name="vexpand">True</property> - <property name="model">listStore</property> - <property name="headers-visible">False</property> + <property name="model">modelToolbarList</property> <property name="headers-clickable">False</property> <property name="enable-search">False</property> <property name="show-expanders">False</property> @@ -52,24 +63,55 @@ </child> <child> <object class="GtkTreeViewColumn" id="column1"> - <property name="resizable">True</property> + <property name="title" translatable="yes" context="toolbartabpage|header|Name">Name</property> <child> - <object class="GtkCellRendererToggle" id="cbRender"/> + <object class="GtkCellRendererText" id="lbName"> + <property name="xpad">6</property> + </object> <attributes> - <attribute name="visible">3</attribute> - <attribute name="active">0</attribute> + <attribute name="text">0</attribute> </attributes> </child> </object> </child> <child> <object class="GtkTreeViewColumn" id="column2"> - <property name="resizable">True</property> - <property name="expand">True</property> + <property name="title" translatable="yes" context="toolbartabpage|header|Visible">Visible</property> + <child> + <object class="GtkCellRendererToggle" id="cbVisible"> + <property name="xpad">6</property> + <property name="xalign">0</property> + </object> + <attributes> + <attribute name="active">1</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="column3"> + <property name="title" translatable="yes" context="toolbartabpage|header|Locked">Locked</property> + <child> + <object class="GtkCellRendererToggle" id="cbLocked"> + <property name="xpad">6</property> + <property name="xalign">0</property> + </object> + <attributes> + <attribute name="active">2</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="column4"> + <property name="title" translatable="yes" context="toolbartabpage|header|Sensitive">Context-sensitive</property> <child> - <object class="GtkCellRendererText" id="lbRender"/> + <object class="GtkCellRendererToggle" id="cbSensitive"> + <property name="xpad">6</property> + <property name="xalign">0</property> + </object> <attributes> - <attribute name="text">1</attribute> + <attribute name="active">3</attribute> </attributes> </child> </object> @@ -83,5 +125,56 @@ <property name="position">0</property> </packing> </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="hexpand">True</property> + <child> + <object class="GtkLabel" id="lbLockAll"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="halign">end</property> + <property name="margin-end">3</property> + <property name="label" translatable="yes" context="toolbartabpage|lbLockAll">_Lock all:</property> + <property name="use-underline">True</property> + <accessibility> + <relation type="label-for" target="cbLockAll"/> + </accessibility> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="cbLockAll"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="margin-start">3</property> + <property name="inconsistent">True</property> + <property name="draw-indicator">True</property> + <child> + <placeholder/> + </child> + <accessibility> + <relation type="labelled-by" target="lbLockAll"/> + </accessibility> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> </object> </interface> diff --git a/framework/source/layoutmanager/layoutmanager.cxx b/framework/source/layoutmanager/layoutmanager.cxx index c28d40e8f079..ba6b95136c59 100644 --- a/framework/source/layoutmanager/layoutmanager.cxx +++ b/framework/source/layoutmanager/layoutmanager.cxx @@ -1957,6 +1957,24 @@ sal_Bool SAL_CALL LayoutManager::unlockWindow( const OUString& aName ) return bResult; } +bool LayoutManager::makeContextSensitive(std::u16string_view sName, bool bSensitive ) +{ + bool bResult( false ); + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( sName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + if ( pToolbarManager ) + { + bResult = pToolbarManager->makeContextSensitive(sName, bSensitive ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } + } + return bResult; +} + void SAL_CALL LayoutManager::setElementSize( const OUString& aName, const awt::Size& aSize ) { if ( !o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) diff --git a/framework/source/layoutmanager/toolbarlayoutmanager.cxx b/framework/source/layoutmanager/toolbarlayoutmanager.cxx index 22f7ce12057d..1033c34ec5c0 100644 --- a/framework/source/layoutmanager/toolbarlayoutmanager.cxx +++ b/framework/source/layoutmanager/toolbarlayoutmanager.cxx @@ -1580,6 +1580,8 @@ void ToolbarLayoutManager::implts_writeWindowStateData( const UIElement& rElemen comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_UINAME, rElementData.m_aUIName), comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_LOCKED, rElementData.m_aDockedData.m_bLocked), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_CONTEXT, + rElementData.m_bContextSensitive), comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_STYLE, static_cast<sal_uInt16>(rElementData.m_nStyle)) }; @@ -3982,6 +3984,27 @@ bool ToolbarLayoutManager::floatToolbar( std::u16string_view rResourceURL ) return false; } +bool ToolbarLayoutManager::makeContextSensitive( std::u16string_view rResourceURL, const bool bSensitive ) +{ + UIElement aUIElement = implts_findToolbar( rResourceURL ); + if ( !aUIElement.m_xUIElement.is() ) + return false; + + try + { + aUIElement.m_bContextSensitive = bSensitive; + implts_writeWindowStateData(aUIElement); + + return true; + } + catch (const lang::DisposedException&) + { + } + + return false; +} + + bool ToolbarLayoutManager::lockToolbar( std::u16string_view rResourceURL ) { UIElement aUIElement = implts_findToolbar( rResourceURL ); diff --git a/framework/source/layoutmanager/toolbarlayoutmanager.hxx b/framework/source/layoutmanager/toolbarlayoutmanager.hxx index b37908f6087b..681ad67ad740 100644 --- a/framework/source/layoutmanager/toolbarlayoutmanager.hxx +++ b/framework/source/layoutmanager/toolbarlayoutmanager.hxx @@ -105,6 +105,7 @@ class ToolbarLayoutManager : public ::cppu::WeakImplHelper< css::awt::XDockableW bool floatToolbar( std::u16string_view rResourceURL ); bool lockToolbar( std::u16string_view rResourceURL ); bool unlockToolbar( std::u16string_view rResourceURL ); + bool makeContextSensitive( std::u16string_view sResourceURL, bool bSensitive ); void setToolbarPos( std::u16string_view rResourceURL, const css::awt::Point& aPos ); void setToolbarSize( std::u16string_view rResourceURL, const css::awt::Size& aSize ); void setToolbarPosSize( std::u16string_view rResourceURL, const css::awt::Point& aPos, const css::awt::Size& aSize ); diff --git a/include/framework/layoutmanager.hxx b/include/framework/layoutmanager.hxx index 41a08717125a..c1aae772bf5f 100644 --- a/include/framework/layoutmanager.hxx +++ b/include/framework/layoutmanager.hxx @@ -112,6 +112,7 @@ public: virtual sal_Bool SAL_CALL floatWindow(const OUString& aName) override; virtual sal_Bool SAL_CALL lockWindow(const OUString& ResourceURL) override; virtual sal_Bool SAL_CALL unlockWindow(const OUString& ResourceURL) override; + bool makeContextSensitive(std::u16string_view sResourceURL, bool bSensitive); virtual void SAL_CALL setElementSize(const OUString& aName, const css::awt::Size& aSize) override; virtual void SAL_CALL setElementPos(const OUString& aName,
