add separators to TreeView

Change-Id: I5e49913dfac82c1243d16ed0a0267a3647f51311
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95270
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
This commit is contained in:
Caolán McNamara 2020-06-01 10:39:30 +01:00
parent b4f7a08ea5
commit 9b498e2b88
6 changed files with 171 additions and 71 deletions

View file

@ -39,13 +39,15 @@ enum class SvTLEntryFlags
DISABLE_DROP = 0x0002,
// is set if RequestingChildren has not set any children
NO_NODEBMP = 0x0004,
// is set if this is a separator line
IS_SEPARATOR = 0x0008,
// entry had or has children
HAD_CHILDREN = 0x0010,
SEMITRANSPARENT = 0x8000, // draw semi-transparent entry bitmaps
};
namespace o3tl
{
template<> struct typed_flags<SvTLEntryFlags> : is_typed_flags<SvTLEntryFlags, 0x8017> {};
template<> struct typed_flags<SvTLEntryFlags> : is_typed_flags<SvTLEntryFlags, 0x801f> {};
}
class VCL_DLLPUBLIC SvTreeListEntry

View file

@ -888,6 +888,9 @@ public:
insert(nullptr, -1, &rStr, &rId, nullptr, &rImage, nullptr, false, nullptr);
}
virtual void insert_separator(int pos, const OUString& rId) = 0;
void append_separator(const OUString& rId) { insert_separator(-1, rId); }
void connect_changed(const Link<TreeView&, void>& rLink) { m_aChangeHdl = rLink; }
/* A row is "activated" when the user double clicks a treeview row. It may

View file

@ -153,6 +153,8 @@
#define STR_WIZDLG_NEXT NC_("STR_WIZDLG_NEXT", "~Next >")
#define STR_WIZDLG_PREVIOUS NC_("STR_WIZDLG_PREVIOUS", "< Bac~k")
#define STR_SEPARATOR NC_("STR_SEPARATOR", "Separator")
#endif // INCLUDED_VCL_INC_STRINGS_HRC
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -3258,6 +3258,71 @@ private:
pEntry->AddItem(std::move(xCell));
}
void do_insert(const weld::TreeIter* pParent, int pos, const OUString* pStr,
const OUString* pId, const OUString* pIconName,
VirtualDevice* pImageSurface, const OUString* pExpanderName,
bool bChildrenOnDemand, weld::TreeIter* pRet, bool bIsSeparator)
{
disable_notify_events();
const SalInstanceTreeIter* pVclIter = static_cast<const SalInstanceTreeIter*>(pParent);
SvTreeListEntry* iter = pVclIter ? pVclIter->iter : nullptr;
auto nInsertPos = pos == -1 ? TREELIST_APPEND : pos;
void* pUserData;
if (pId)
{
m_aUserData.emplace_back(std::make_unique<OUString>(*pId));
pUserData = m_aUserData.back().get();
}
else
pUserData = nullptr;
SvTreeListEntry* pEntry = new SvTreeListEntry;
if (bIsSeparator)
pEntry->SetFlags(pEntry->GetFlags() | SvTLEntryFlags::IS_SEPARATOR);
if (pIconName || pImageSurface)
{
Image aImage(pIconName ? createImage(*pIconName) : createImage(*pImageSurface));
pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(aImage, aImage, false));
}
else
{
Image aDummy;
pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(aDummy, aDummy, false));
}
if (pStr)
AddStringItem(pEntry, *pStr, 0);
pEntry->SetUserData(pUserData);
m_xTreeView->Insert(pEntry, iter, nInsertPos);
if (pExpanderName)
{
Image aImage(createImage(*pExpanderName));
m_xTreeView->SetExpandedEntryBmp(pEntry, aImage);
m_xTreeView->SetCollapsedEntryBmp(pEntry, aImage);
}
if (pRet)
{
SalInstanceTreeIter* pVclRetIter = static_cast<SalInstanceTreeIter*>(pRet);
pVclRetIter->iter = pEntry;
}
if (bChildrenOnDemand)
{
SvTreeListEntry* pPlaceHolder = m_xTreeView->InsertEntry("<dummy>", pEntry, false, 0, nullptr);
SvViewDataEntry* pViewData = m_xTreeView->GetViewDataEntry(pPlaceHolder);
pViewData->SetSelectable(false);
}
if (bIsSeparator)
{
SvViewDataEntry* pViewData = m_xTreeView->GetViewDataEntry(pEntry);
pViewData->SetSelectable(false);
}
enable_notify_events();
}
public:
SalInstanceTreeView(SvTabListBox* pTreeView, SalInstanceBuilder* pBuilder, bool bTakeOwnership)
: SalInstanceContainer(pTreeView, pBuilder, bTakeOwnership)
@ -3433,55 +3498,15 @@ public:
VirtualDevice* pImageSurface, const OUString* pExpanderName,
bool bChildrenOnDemand, weld::TreeIter* pRet) override
{
disable_notify_events();
const SalInstanceTreeIter* pVclIter = static_cast<const SalInstanceTreeIter*>(pParent);
SvTreeListEntry* iter = pVclIter ? pVclIter->iter : nullptr;
auto nInsertPos = pos == -1 ? TREELIST_APPEND : pos;
void* pUserData;
if (pId)
{
m_aUserData.emplace_back(std::make_unique<OUString>(*pId));
pUserData = m_aUserData.back().get();
}
else
pUserData = nullptr;
do_insert(pParent, pos, pStr, pId, pIconName, pImageSurface, pExpanderName,
bChildrenOnDemand, pRet, false);
}
SvTreeListEntry* pEntry = new SvTreeListEntry;
if (pIconName || pImageSurface)
{
Image aImage(pIconName ? createImage(*pIconName) : createImage(*pImageSurface));
pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(aImage, aImage, false));
}
else
{
Image aDummy;
pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(aDummy, aDummy, false));
}
if (pStr)
AddStringItem(pEntry, *pStr, 0);
pEntry->SetUserData(pUserData);
m_xTreeView->Insert(pEntry, iter, nInsertPos);
if (pExpanderName)
{
Image aImage(createImage(*pExpanderName));
m_xTreeView->SetExpandedEntryBmp(pEntry, aImage);
m_xTreeView->SetCollapsedEntryBmp(pEntry, aImage);
}
if (pRet)
{
SalInstanceTreeIter* pVclRetIter = static_cast<SalInstanceTreeIter*>(pRet);
pVclRetIter->iter = pEntry;
}
if (bChildrenOnDemand)
{
SvTreeListEntry* pPlaceHolder = m_xTreeView->InsertEntry("<dummy>", pEntry, false, 0, nullptr);
SvViewDataEntry* pViewData = m_xTreeView->GetViewDataEntry(pPlaceHolder);
pViewData->SetSelectable(false);
}
enable_notify_events();
virtual void insert_separator(int pos, const OUString& /*rId*/) override
{
OUString sSep(VclResId(STR_SEPARATOR));
do_insert(nullptr, pos, &sSep, nullptr, nullptr, nullptr, nullptr,
false, nullptr, true);
}
virtual void

View file

@ -25,6 +25,7 @@
#include <vcl/toolkit/button.hxx>
#include <vcl/decoview.hxx>
#include <vcl/salnativewidgets.hxx>
#include <vcl/settings.hxx>
struct SvLBoxButtonData_Impl
{
@ -196,12 +197,35 @@ SvLBoxItemType SvLBoxString::GetType() const
return SvLBoxItemType::String;
}
namespace
{
void drawSeparator(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRegion)
{
Color aOldLineColor(rRenderContext.GetLineColor());
const StyleSettings& rStyle = rRenderContext.GetSettings().GetStyleSettings();
Point aTmpPos = rRegion.TopLeft();
Size aSize = rRegion.GetSize();
aTmpPos.AdjustY(aSize.Height() / 2 );
rRenderContext.SetLineColor(rStyle.GetShadowColor());
rRenderContext.DrawLine(aTmpPos, Point(aSize.Width() + aTmpPos.X(), aTmpPos.Y()));
rRenderContext.SetLineColor(aOldLineColor);
}
}
void SvLBoxString::Paint(
const Point& rPos, SvTreeListBox& rDev, vcl::RenderContext& rRenderContext,
const SvViewDataEntry* /*pView*/, const SvTreeListEntry& rEntry)
{
Size aSize;
DrawTextFlags nStyle = (rDev.IsEnabled() && !mbDisabled) ? DrawTextFlags::NONE : DrawTextFlags::Disable;
if (bool(rEntry.GetFlags() & SvTLEntryFlags::IS_SEPARATOR))
{
Point aStartPos(0, rPos.Y() - 2);
tools::Rectangle aRegion(aStartPos, Size(rDev.GetSizePixel().Width(), 4));
drawSeparator(rRenderContext, aRegion);
return;
}
Size aSize;
if (rDev.IsEntryMnemonicsEnabled())
nStyle |= DrawTextFlags::Mnemonic;
if (rDev.TextCenterAndClipEnabled())
@ -267,6 +291,13 @@ void SvLBoxString::InitViewData(
if( !pViewData )
pViewData = pView->GetViewDataItem( pEntry, this );
if (bool(pEntry->GetFlags() & SvTLEntryFlags::IS_SEPARATOR))
{
pViewData->mnWidth = -1;
pViewData->mnHeight = 0;
return;
}
if (mbEmphasized)
{
pView->Push();

View file

@ -9050,6 +9050,31 @@ tools::Rectangle get_row_area(GtkTreeView* pTreeView, GList* pColumns, GtkTreePa
return aRet;
}
struct GtkTreeRowReferenceDeleter
{
void operator()(GtkTreeRowReference* p) const
{
gtk_tree_row_reference_free(p);
}
};
bool separator_function(GtkTreePath* path, const std::vector<std::unique_ptr<GtkTreeRowReference, GtkTreeRowReferenceDeleter>>& rSeparatorRows)
{
bool bFound = false;
for (auto& a : rSeparatorRows)
{
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;
}
class GtkInstanceTreeView : public GtkInstanceContainer, public virtual weld::TreeView
{
private:
@ -9073,6 +9098,8 @@ private:
// currently expanding parent that logically, but not currently physically,
// contain placeholders
o3tl::sorted_vector<GtkTreePath*, CompareGtkTreePath> m_aExpandingPlaceHolderParents;
// which rows are separators (rare)
std::vector<std::unique_ptr<GtkTreeRowReference, GtkTreeRowReferenceDeleter>> m_aSeparatorRows;
std::vector<GtkSortType> m_aSavedSortTypes;
std::vector<int> m_aSavedSortColumns;
std::vector<int> m_aViewColToModelCol;
@ -9183,6 +9210,20 @@ private:
}
}
bool separator_function(GtkTreePath* path)
{
return ::separator_function(path, m_aSeparatorRows);
}
static gboolean separatorFunction(GtkTreeModel* pTreeModel, GtkTreeIter* pIter, gpointer widget)
{
GtkInstanceTreeView* pThis = static_cast<GtkInstanceTreeView*>(widget);
GtkTreePath* path = gtk_tree_model_get_path(pTreeModel, pIter);
bool bRet = pThis->separator_function(path);
gtk_tree_path_free(path);
return bRet;
}
OUString get(const GtkTreeIter& iter, int col) const
{
GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore);
@ -9942,6 +9983,20 @@ public:
enable_notify_events();
}
virtual void insert_separator(int pos, const OUString& rId) override
{
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(iter, nullptr, pos, &rId, nullptr, nullptr, nullptr, nullptr);
GtkTreeModel* pTreeModel = GTK_TREE_MODEL(m_pTreeStore);
GtkTreePath* pPath = gtk_tree_model_get_path(pTreeModel, &iter);
m_aSeparatorRows.emplace_back(gtk_tree_row_reference_new(pTreeModel, pPath));
gtk_tree_path_free(pPath);
enable_notify_events();
}
virtual void set_font_color(int pos, const Color& rColor) override
{
GtkTreeIter iter;
@ -10018,6 +10073,8 @@ public:
virtual void clear() override
{
disable_notify_events();
gtk_tree_view_set_row_separator_func(m_pTreeView, nullptr, nullptr, nullptr);
m_aSeparatorRows.clear();
gtk_tree_store_clear(m_pTreeStore);
enable_notify_events();
}
@ -12668,14 +12725,6 @@ 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);
}
};
// pop down the toplevel combobox menu when something is activated from a custom
// submenu, i.e. wysiwyg style menu
class CustomRenderMenuButtonHelper : public MenuHelper
@ -13077,19 +13126,7 @@ private:
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;
return ::separator_function(path, m_aSeparatorRows);
}
bool separator_function(int pos)