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:
parent
b4f7a08ea5
commit
9b498e2b88
6 changed files with 171 additions and 71 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue