include/vcl/builder.hxx | 17 ++++++++++++++ include/vcl/toolkit/svtabbx.hxx | 13 +++++++++++ vcl/source/treelist/svtabbx.cxx | 20 +++++++++++++++++ vcl/source/window/builder.cxx | 47 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+)
New commits: commit aed17efe5e2a32899cdbfec8f145a29528a48d4a Author: Caolán McNamara <[email protected]> AuthorDate: Tue Feb 17 20:31:02 2026 +0000 Commit: Caolán McNamara <[email protected]> CommitDate: Thu Feb 19 12:12:45 2026 +0100 extract from .ui the TreeView structure, tree, treegrid, grid or listbox The renderer/column structure in .ui files for GtkTreeView is a set of compromise rules of expressing a layout in gtk terms which the vcl equivalent is also able to do. The ui files end up containing information as to what each TreeView is limited to, whether it will be used as a tree or a list and whether there are multiple columns or not, so here we extract enough of that information in the generic loader to determine what type of role this TreeView has. Change-Id: I7758a3cdab070c013301d519ab2ffbfb97010183 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199579 Reviewed-by: Miklos Vajna <[email protected]> Tested-by: Caolán McNamara <[email protected]> diff --git a/include/vcl/builder.hxx b/include/vcl/builder.hxx index ae7a25af7bd5..9c6299997426 100644 --- a/include/vcl/builder.hxx +++ b/include/vcl/builder.hxx @@ -207,6 +207,23 @@ private: sal_uInt16 m_nLastMenuItemId; + /* The renderer/column structure in .ui files for GtkTreeView is a set + of compromise rules of expressing a layout in gtk terms which the + vcl equivalent is also able to do. + + The GtkInstanceTreeView ctor has some more details of that. + + The ui files end up containing information as to what each TreeView + is limited to, whether it will be used as a tree or a list and + whether there are multiple columns or not, so here we extract enough + of that information in the generic loader to determine what type of + role this TreeView has. + */ + sal_uInt16 m_nTreeViewRenderers; + sal_uInt16 m_nTreeViewExpanders; + sal_uInt16 m_nTreeViewColumnCount; + bool m_bTreeViewSeenTextInColumn; + VclParserState(); }; diff --git a/include/vcl/toolkit/svtabbx.hxx b/include/vcl/toolkit/svtabbx.hxx index 94b0077f44fe..53ebd82926a3 100644 --- a/include/vcl/toolkit/svtabbx.hxx +++ b/include/vcl/toolkit/svtabbx.hxx @@ -40,11 +40,21 @@ enum class SvTabJustify AdjustCenter = static_cast<int>(SvLBoxTabFlags::ADJUST_CENTER) }; +enum class SvTabListBoxRole +{ + Unknown, + Tree, // hierarchical, single-column + TreeGrid, // hierarchical, multi-column + ListBox, // flat, single-column + Grid // flat, multi-column +}; + class UNLESS_MERGELIBS_MORE(VCL_DLLPUBLIC) SvTabListBox : public SvTreeListBox { private: std::vector<SvLBoxTab> mvTabList; OUString aCurEntry; + SvTabListBoxRole m_eRole; protected: static std::u16string_view GetToken( std::u16string_view sStr, sal_Int32 &nIndex ); @@ -81,6 +91,9 @@ public: void SetTabJustify( sal_uInt16 nTab, SvTabJustify ); void SetTabEditable( sal_uInt16 nTab, bool bEditable ); + void SetRole(SvTabListBoxRole e) { m_eRole = e; } + SvTabListBoxRole GetRole() const { return m_eRole; } + virtual void DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) override; }; diff --git a/vcl/source/treelist/svtabbx.cxx b/vcl/source/treelist/svtabbx.cxx index d03d24dcc3dc..a27d7592117c 100644 --- a/vcl/source/treelist/svtabbx.cxx +++ b/vcl/source/treelist/svtabbx.cxx @@ -197,6 +197,25 @@ void SvTabListBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) rJsonWriter.put("singleclickactivate", GetActivateOnSingleClick()); + switch (m_eRole) + { + case SvTabListBoxRole::Unknown: + assert(false && "this shouldn't be possible on load from .ui"); + break; + case SvTabListBoxRole::Tree: + rJsonWriter.put("role", "tree"); + break; + case SvTabListBoxRole::TreeGrid: + rJsonWriter.put("role", "treegrid"); + break; + case SvTabListBoxRole::ListBox: + rJsonWriter.put("role", "listbox"); + break; + case SvTabListBoxRole::Grid: + rJsonWriter.put("role", "grid"); + break; + } + bool bCheckButtons = static_cast<int>(nTreeFlags & SvTreeFlags::CHKBTN); bool isRadioButton = false; @@ -287,6 +306,7 @@ void SvTabListBox::InitEntry(SvTreeListEntry* pEntry, const OUString& rStr, SvTabListBox::SvTabListBox( vcl::Window* pParent, WinBits nBits ) : SvTreeListBox( pParent, nBits ) + , m_eRole(SvTabListBoxRole::Unknown) { SetHighlightRange(); // select full width } diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx index 01fbbc9c5fe1..7d961167d5ca 100644 --- a/vcl/source/window/builder.cxx +++ b/vcl/source/window/builder.cxx @@ -1828,6 +1828,11 @@ VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OUString } else if (name == "GtkTreeView") { + m_pVclParserState->m_nTreeViewRenderers = 0; + m_pVclParserState->m_nTreeViewExpanders = 0; + m_pVclParserState->m_nTreeViewColumnCount = 0; + m_pVclParserState->m_bTreeViewSeenTextInColumn = false; + if (!isLegacy()) { assert(rMap.find(u"model"_ustr) != rMap.end() && "GtkTreeView must have a model"); @@ -1900,6 +1905,9 @@ VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OUString } else if (name == "GtkTreeViewColumn") { + m_pVclParserState->m_nTreeViewColumnCount++; + m_pVclParserState->m_bTreeViewSeenTextInColumn = false; + if (!isLegacy()) { SvHeaderTabListBox* pTreeView = dynamic_cast<SvHeaderTabListBox*>(pParent); @@ -1923,6 +1931,25 @@ VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OUString } } } + // The somewhat convoluted GtkCellRenderer* rules here are intended to + // match those of the GtkInstanceTreeView so we can take advantage of the + // consistency of the .ui format to determine the role of a GtkTreeView in + // terms of tree/treegrid/grid/listbox + else if (name == "GtkCellRendererText") + { + m_pVclParserState->m_nTreeViewRenderers++; + m_pVclParserState->m_bTreeViewSeenTextInColumn = true; + } + else if (name == "GtkCellRendererPixbuf" || name == "GtkCellRendererToggle") + { + m_pVclParserState->m_nTreeViewRenderers++; + // leading non-text renderers in the first column are expander decorations + if (m_pVclParserState->m_nTreeViewColumnCount == 1 + && !m_pVclParserState->m_bTreeViewSeenTextInColumn) + { + m_pVclParserState->m_nTreeViewExpanders++; + } + } else if (name == "GtkLabel") { WinBits nWinStyle = WB_CENTER|WB_VCENTER|WB_3DLOOK; @@ -2597,6 +2624,22 @@ void VclBuilder::tweakInsertedChild(vcl::Window *pParent, vcl::Window* pCurrentC { assert(pCurrentChild); + if (SvTabListBox* pTabListBox = dynamic_cast<SvTabListBox*>(pCurrentChild)) + { + const bool bTree(pTabListBox->GetStyle() & (WB_HASBUTTONS | WB_HASBUTTONSATROOT)); + const sal_uInt16 nRealColumns = m_pVclParserState->m_nTreeViewRenderers - + m_pVclParserState->m_nTreeViewExpanders; + const bool bMultiColumn = nRealColumns > 1; + if (bTree && bMultiColumn) + pTabListBox->SetRole(SvTabListBoxRole::TreeGrid); + else if (bTree) + pTabListBox->SetRole(SvTabListBoxRole::Tree); + else if (bMultiColumn) + pTabListBox->SetRole(SvTabListBoxRole::Grid); + else + pTabListBox->SetRole(SvTabListBoxRole::ListBox); + } + //Select the first page if it's a notebook if (pCurrentChild->GetType() == WindowType::TABCONTROL) { @@ -4007,6 +4050,10 @@ void VclBuilder::mungeTextBuffer(VclMultiLineEdit &rTarget, const TextBuffer &rT VclBuilder::VclParserState::VclParserState() : m_nLastToolbarId(0) , m_nLastMenuItemId(0) + , m_nTreeViewRenderers(0) + , m_nTreeViewExpanders(0) + , m_nTreeViewColumnCount(0) + , m_bTreeViewSeenTextInColumn(false) {} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
