diff options
author | Caolán McNamara <caolanm@redhat.com> | 2020-04-03 19:30:00 +0100 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2020-04-04 19:51:52 +0200 |
commit | 338403833ec43539728d7609e243ae6070aafb3f (patch) | |
tree | f2f779bfd2ebff714b74c953b76706fc46d40fd6 | |
parent | 6ff081689573217621a4ef012755fc5e351359af (diff) |
add ability to have custom renderer treeview rows
Change-Id: Ia085242dee0aaa19f9aefa2a3cf71bc827fcca73
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/91658
Tested-by: Jenkins
Tested-by: Caolán McNamara <caolanm@redhat.com>
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
-rw-r--r-- | include/vcl/svlbitm.hxx | 3 | ||||
-rw-r--r-- | include/vcl/treelistbox.hxx | 11 | ||||
-rw-r--r-- | include/vcl/weld.hxx | 26 | ||||
-rw-r--r-- | include/vcl/weldutils.hxx | 4 | ||||
-rw-r--r-- | vcl/source/app/salvtables.cxx | 52 | ||||
-rw-r--r-- | vcl/source/treelist/svlbitm.cxx | 23 | ||||
-rw-r--r-- | vcl/source/treelist/treelistbox.cxx | 10 | ||||
-rw-r--r-- | vcl/source/window/builder.cxx | 19 | ||||
-rw-r--r-- | vcl/unx/gtk3/gtk3gtkinst.cxx | 347 |
9 files changed, 487 insertions, 8 deletions
diff --git a/include/vcl/svlbitm.hxx b/include/vcl/svlbitm.hxx index dc8cde9bcb02..abe2446ade11 100644 --- a/include/vcl/svlbitm.hxx +++ b/include/vcl/svlbitm.hxx @@ -105,6 +105,7 @@ class VCL_DLLPUBLIC SvLBoxString : public SvLBoxItem { private: bool mbEmphasized; + bool mbCustom; double mfAlign; protected: OUString maText; @@ -126,6 +127,8 @@ public: void Emphasize(bool bEmphasize) { mbEmphasized = bEmphasize; } bool IsEmphasized() const { return mbEmphasized; } + void SetCustomRender() { mbCustom = true; } + const OUString& GetText() const { return maText; diff --git a/include/vcl/treelistbox.hxx b/include/vcl/treelistbox.hxx index 179f27fe4cb6..e56fd1081fb2 100644 --- a/include/vcl/treelistbox.hxx +++ b/include/vcl/treelistbox.hxx @@ -177,6 +177,9 @@ namespace o3tl struct SvTreeListBoxImpl; +typedef std::pair<vcl::RenderContext&, const SvTreeListEntry&> svtree_measure_args; +typedef std::tuple<vcl::RenderContext&, const tools::Rectangle&, const SvTreeListEntry&> svtree_render_args; + class VCL_DLLPUBLIC SvTreeListBox :public Control ,public SvListView @@ -186,6 +189,7 @@ class VCL_DLLPUBLIC SvTreeListBox ,public vcl::ISearchableStringList { friend class SvImpLBox; + friend class SvLBoxString; friend class IconViewImpl; friend class TreeControlPeer; friend class SalInstanceIconView; @@ -201,6 +205,8 @@ class VCL_DLLPUBLIC SvTreeListBox Link<SvTreeListBox*,void> aDeselectHdl; Link<const CommandEvent&, bool> aPopupMenuHdl; Link<const HelpEvent&, bool> aTooltipHdl; + Link<svtree_render_args, void> aCustomRenderHdl; + Link<svtree_measure_args, Size> aCustomMeasureHdl; Image aPrevInsertedExpBmp; Image aPrevInsertedColBmp; @@ -269,6 +275,9 @@ private: // autowidth for the 1st checkbox column VCL_DLLPRIVATE void CheckBoxInserted(SvTreeListEntry* pEntry); + VCL_DLLPRIVATE void DrawCustomEntry(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const SvTreeListEntry& rEntry); + VCL_DLLPRIVATE Size MeasureCustomEntry(vcl::RenderContext& rRenderContext, const SvTreeListEntry& rEntry); + protected: bool CheckDragAndDropMode( SvTreeListBox const * pSource, sal_Int8 ); @@ -434,6 +443,8 @@ public: void SetExpandedHdl(const Link<SvTreeListBox*,void>& rNewHdl){aExpandedHdl=rNewHdl;} void SetPopupMenuHdl(const Link<const CommandEvent&, bool>& rLink) { aPopupMenuHdl = rLink; } void SetTooltipHdl(const Link<const HelpEvent&, bool>& rLink) { aTooltipHdl = rLink; } + void SetCustomRenderHdl(const Link<svtree_render_args, void>& rLink) { aCustomRenderHdl = rLink; } + void SetCustomMeasureHdl(const Link<svtree_measure_args, Size>& rLink) { aCustomMeasureHdl = rLink; } virtual void ExpandedHdl(); virtual bool ExpandingHdl(); diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx index 15dd5ad91de8..eda59f01783f 100644 --- a/include/vcl/weld.hxx +++ b/include/vcl/weld.hxx @@ -729,6 +729,12 @@ protected: Link<bool&, bool> m_aDragBeginHdl; std::function<int(const weld::TreeIter&, const weld::TreeIter&)> m_aCustomSort; +public: + typedef std::pair<vcl::RenderContext&, const OUString&> get_size_args; + typedef std::tuple<vcl::RenderContext&, const tools::Rectangle&, bool, const OUString&> + render_args; + +protected: std::vector<int> m_aRadioIndexes; void signal_changed() { m_aChangeHdl.Call(*this); } @@ -758,6 +764,21 @@ protected: Link<const TreeIter&, OUString> m_aQueryTooltipHdl; OUString signal_query_tooltip(const TreeIter& rIter) { return m_aQueryTooltipHdl.Call(rIter); } + Link<render_args, void> m_aRenderHdl; + void signal_custom_render(vcl::RenderContext& rDevice, const tools::Rectangle& rRect, + bool bSelected, const OUString& rId) + { + m_aRenderHdl.Call( + std::tuple<vcl::RenderContext&, const tools::Rectangle, bool, const OUString&>( + rDevice, rRect, bSelected, rId)); + } + + Link<get_size_args, Size> m_aGetSizeHdl; + Size signal_custom_get_size(vcl::RenderContext& rDevice, const OUString& rId) + { + return m_aGetSizeHdl.Call(std::pair<vcl::RenderContext&, const OUString&>(rDevice, rId)); + } + public: virtual void connect_query_tooltip(const Link<const TreeIter&, OUString>& rLink) { @@ -1068,6 +1089,11 @@ public: OUString const& get_saved_value() const { return m_sSavedValue; } bool get_value_changed_from_saved() const { return m_sSavedValue != get_selected_text(); } + // for custom rendering a cell + virtual void set_column_custom_renderer(int nColumn) = 0; + void connect_custom_get_size(const Link<get_size_args, Size>& rLink) { m_aGetSizeHdl = rLink; } + void connect_custom_render(const Link<render_args, void>& rLink) { m_aRenderHdl = rLink; } + // for dnd virtual bool get_dest_row_at_pos(const Point& rPos, weld::TreeIter* pResult) = 0; virtual tools::Rectangle get_row_area(const weld::TreeIter& rIter) const = 0; diff --git a/include/vcl/weldutils.hxx b/include/vcl/weldutils.hxx index 077dab0fa414..cb04f2d74915 100644 --- a/include/vcl/weldutils.hxx +++ b/include/vcl/weldutils.hxx @@ -153,7 +153,11 @@ public: } }; +// get the row the iterator is on VCL_DLLPUBLIC size_t GetAbsPos(const weld::TreeView& rTreeView, const weld::TreeIter& rIter); + +// an entry is visible if all parents are expanded +VCL_DLLPUBLIC bool IsEntryVisible(const weld::TreeView& rTreeView, const weld::TreeIter& rIter); } #endif diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx index d43dda8ffec1..1ae61d3ee21a 100644 --- a/vcl/source/app/salvtables.cxx +++ b/vcl/source/app/salvtables.cxx @@ -3263,6 +3263,8 @@ private: // currently expanding parent that logically, but not currently physically, // contain placeholders o3tl::sorted_vector<SvTreeListEntry*> m_aExpandingPlaceHolderParents; + // which columns should be custom rendered + o3tl::sorted_vector<int> m_aCustomRenders; bool m_bDisableCheckBoxAutoWidth; int m_nSortColumn; @@ -3283,6 +3285,8 @@ private: DECL_LINK(CompareHdl, const SvSortData&, sal_Int32); DECL_LINK(PopupMenuHdl, const CommandEvent&, bool); DECL_LINK(TooltipHdl, const HelpEvent&, bool); + DECL_LINK(CustomRenderHdl, svtree_render_args, void); + DECL_LINK(CustomMeasureHdl, svtree_measure_args, Size); bool IsDummyEntry(SvTreeListEntry* pEntry) const { @@ -3309,6 +3313,14 @@ private: pEntry->SetTextColor(rColor); } + void AddStringItem(SvTreeListEntry* pEntry, const OUString& rStr, int nCol) + { + auto xCell = std::make_unique<SvLBoxString>(rStr); + if (m_aCustomRenders.count(nCol)) + xCell->SetCustomRender(); + pEntry->AddItem(std::move(xCell)); + } + public: SalInstanceTreeView(SvTabListBox* pTreeView, SalInstanceBuilder* pBuilder, bool bTakeOwnership) : SalInstanceContainer(pTreeView, pBuilder, bTakeOwnership) @@ -3325,6 +3337,8 @@ public: m_xTreeView->SetDoubleClickHdl(LINK(this, SalInstanceTreeView, DoubleClickHdl)); m_xTreeView->SetExpandingHdl(LINK(this, SalInstanceTreeView, ExpandingHdl)); m_xTreeView->SetPopupMenuHdl(LINK(this, SalInstanceTreeView, PopupMenuHdl)); + m_xTreeView->SetCustomRenderHdl(LINK(this, SalInstanceTreeView, CustomRenderHdl)); + m_xTreeView->SetCustomMeasureHdl(LINK(this, SalInstanceTreeView, CustomMeasureHdl)); const long aTabPositions[] = { 0 }; m_xTreeView->SetTabs(SAL_N_ELEMENTS(aTabPositions), aTabPositions); LclHeaderTabListBox* pHeaderBox = dynamic_cast<LclHeaderTabListBox*>(m_xTreeView.get()); @@ -3452,6 +3466,11 @@ public: } } + virtual void set_column_custom_renderer(int nColumn) override + { + m_aCustomRenders.insert(nColumn); + } + virtual void show() override { if (LclHeaderTabListBox* pHeaderBox = dynamic_cast<LclHeaderTabListBox*>(m_xTreeView.get())) @@ -3496,7 +3515,7 @@ public: pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(aDummy, aDummy, false)); } if (pStr) - pEntry->AddItem(std::make_unique<SvLBoxString>(*pStr)); + AddStringItem(pEntry, *pStr, 0); pEntry->SetUserData(pUserData); m_xTreeView->Insert(pEntry, iter, nInsertPos); @@ -3744,11 +3763,11 @@ public: // blank out missing entries for (int i = pEntry->ItemCount(); i < col; ++i) - pEntry->AddItem(std::make_unique<SvLBoxString>("")); + AddStringItem(pEntry, "", i - 1); if (static_cast<size_t>(col) == pEntry->ItemCount()) { - pEntry->AddItem(std::make_unique<SvLBoxString>(rText)); + AddStringItem(pEntry, rText, col - 1); SvViewDataEntry* pViewData = m_xTreeView->GetViewDataEntry(pEntry); m_xTreeView->InitViewData(pViewData, pEntry); } @@ -3833,7 +3852,7 @@ public: // blank out missing entries for (int i = pEntry->ItemCount(); i < col; ++i) - pEntry->AddItem(std::make_unique<SvLBoxString>("")); + AddStringItem(pEntry, "", i - 1); if (static_cast<size_t>(col) == pEntry->ItemCount()) { @@ -3989,7 +4008,7 @@ public: // blank out missing entries for (int i = pEntry->ItemCount(); i < col; ++i) - pEntry->AddItem(std::make_unique<SvLBoxString>("")); + AddStringItem(pEntry, "", i - 1); if (static_cast<size_t>(col) == pEntry->ItemCount()) { @@ -4598,6 +4617,8 @@ public: m_xTreeView->SetDeselectHdl(Link<SvTreeListBox*, void>()); m_xTreeView->SetScrolledHdl(Link<SvTreeListBox*, void>()); m_xTreeView->SetTooltipHdl(Link<const HelpEvent&, bool>()); + m_xTreeView->SetCustomRenderHdl(Link<svtree_render_args, void>()); + m_xTreeView->SetCustomMeasureHdl(Link<svtree_measure_args, Size>()); } }; @@ -4621,6 +4642,27 @@ IMPL_LINK(SalInstanceTreeView, TooltipHdl, const HelpEvent&, rHEvt, bool) return true; } +IMPL_LINK(SalInstanceTreeView, CustomRenderHdl, svtree_render_args, payload, void) +{ + vcl::RenderContext& rRenderDevice = std::get<0>(payload); + const tools::Rectangle& rRect = std::get<1>(payload); + const SvTreeListEntry& rEntry = std::get<2>(payload); + const OUString* pId = static_cast<const OUString*>(rEntry.GetUserData()); + if (!pId) + return; + signal_custom_render(rRenderDevice, rRect, m_xTreeView->IsSelected(&rEntry), *pId); +} + +IMPL_LINK(SalInstanceTreeView, CustomMeasureHdl, svtree_measure_args, payload, Size) +{ + vcl::RenderContext& rRenderDevice = payload.first; + const SvTreeListEntry& rEntry = payload.second; + const OUString* pId = static_cast<const OUString*>(rEntry.GetUserData()); + if (!pId) + return Size(); + return signal_custom_get_size(rRenderDevice, *pId); +} + IMPL_LINK(SalInstanceTreeView, CompareHdl, const SvSortData&, rSortData, sal_Int32) { const SvTreeListEntry* pLHS = rSortData.pLeft; diff --git a/vcl/source/treelist/svlbitm.cxx b/vcl/source/treelist/svlbitm.cxx index 20407f71af2b..8fff9071dcdb 100644 --- a/vcl/source/treelist/svlbitm.cxx +++ b/vcl/source/treelist/svlbitm.cxx @@ -174,6 +174,7 @@ bool SvLBoxButtonData::IsRadio() const { SvLBoxString::SvLBoxString(const OUString& rStr) : mbEmphasized(false) + , mbCustom(false) , mfAlign(0.0) , maText(rStr) { @@ -181,6 +182,7 @@ SvLBoxString::SvLBoxString(const OUString& rStr) SvLBoxString::SvLBoxString() : mbEmphasized(false) + , mbCustom(false) , mfAlign(0.0) { } @@ -235,7 +237,12 @@ void SvLBoxString::Paint( rRenderContext.SetFont(aFont); } - rRenderContext.DrawText(tools::Rectangle(rPos, aSize), maText, nStyle); + tools::Rectangle aRect(rPos, aSize); + + if (mbCustom) + rDev.DrawCustomEntry(rRenderContext, aRect, rEntry); + else + rRenderContext.DrawText(aRect, maText, nStyle); if (mbEmphasized) rRenderContext.Pop(); @@ -248,6 +255,7 @@ std::unique_ptr<SvLBoxItem> SvLBoxString::Clone(SvLBoxItem const * pSource) cons const SvLBoxString* pOther = static_cast<const SvLBoxString*>(pSource); pNew->maText = pOther->maText; pNew->mbEmphasized = pOther->mbEmphasized; + pNew->mbCustom = pOther->mbCustom; pNew->mfAlign = pOther->mfAlign; return std::unique_ptr<SvLBoxItem>(pNew.release()); @@ -267,8 +275,17 @@ void SvLBoxString::InitViewData( pView->Control::SetFont( aFont ); } - pViewData->mnWidth = -1; // calc on demand - pViewData->mnHeight = pView->GetTextHeight(); + if (mbCustom) + { + Size aSize = pView->MeasureCustomEntry(*pView, *pEntry); + pViewData->mnWidth = aSize.Width(); + pViewData->mnHeight = aSize.Height(); + } + else + { + pViewData->mnWidth = -1; // calc on demand + pViewData->mnHeight = pView->GetTextHeight(); + } if (mbEmphasized) pView->Pop(); diff --git a/vcl/source/treelist/treelistbox.cxx b/vcl/source/treelist/treelistbox.cxx index a57b0e45b24e..f54e184356d4 100644 --- a/vcl/source/treelist/treelistbox.cxx +++ b/vcl/source/treelist/treelistbox.cxx @@ -2868,6 +2868,16 @@ void SvTreeListBox::PreparePaint(vcl::RenderContext& /*rRenderContext*/, SvTreeL { } +void SvTreeListBox::DrawCustomEntry(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const SvTreeListEntry& rEntry) +{ + aCustomRenderHdl.Call(std::tuple<vcl::RenderContext&, const tools::Rectangle&, const SvTreeListEntry&>(rRenderContext, rRect, rEntry)); +} + +Size SvTreeListBox::MeasureCustomEntry(vcl::RenderContext& rRenderContext, const SvTreeListEntry& rEntry) +{ + return aCustomMeasureHdl.Call(std::pair<vcl::RenderContext&, const SvTreeListEntry&>(rRenderContext, rEntry)); +} + tools::Rectangle SvTreeListBox::GetFocusRect(const SvTreeListEntry* pEntry, long nLine ) { pImpl->UpdateContextBmpWidthMax( pEntry ); diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx index f3ed31051d94..a7ea94053e7c 100644 --- a/vcl/source/window/builder.cxx +++ b/vcl/source/window/builder.cxx @@ -441,6 +441,25 @@ namespace weld return nAbsPos; } + + bool IsEntryVisible(const weld::TreeView& rTreeView, const weld::TreeIter& rIter) + { + // short circuit for the common case + if (rTreeView.get_iter_depth(rIter) == 0) + return true; + + std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator(&rIter)); + bool bRetVal = false; + do + { + if (rTreeView.get_iter_depth(*xEntry) == 0) + { + bRetVal = true; + break; + } + } while (rTreeView.iter_parent(*xEntry) && rTreeView.get_row_expanded(*xEntry)); + return bRetVal; + } } VclBuilder::VclBuilder(vcl::Window* pParent, const OUString& sUIDir, const OUString& sUIFile, diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx index c804670dda7c..63f5e710f817 100644 --- a/vcl/unx/gtk3/gtk3gtkinst.cxx +++ b/vcl/unx/gtk3/gtk3gtkinst.cxx @@ -5298,6 +5298,218 @@ GType crippled_viewport_get_type() return type; } +#define CUSTOM_TYPE_CELL_RENDERER_SURFACE (custom_cell_renderer_surface_get_type()) +#define CUSTOM_CELL_RENDERER_SURFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), CUSTOM_TYPE_CELL_RENDERER_SURFACE, CustomCellRendererSurface)) + +namespace { + + struct CustomCellRendererSurface + { + GtkCellRendererText parent; + VclPtr<VirtualDevice> device; + gchar *id; + gpointer instance; + }; + + struct CustomCellRendererSurfaceClass + { + GtkCellRendererTextClass parent_class; + }; + + enum + { + PROP_ID = 10000, + PROP_INSTANCE_TREE_VIEW = 10001 + }; +} + +static gpointer custom_cell_renderer_surface_parent_class; + +static GType custom_cell_renderer_surface_get_type(); +static void custom_cell_renderer_surface_class_init(CustomCellRendererSurfaceClass *klass); + +GType custom_cell_renderer_surface_get_type() +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (CustomCellRendererSurfaceClass), + nullptr, /* base init */ + nullptr, /* base finalize */ + reinterpret_cast<GClassInitFunc>(custom_cell_renderer_surface_class_init), /* class init */ + nullptr, /* class finalize */ + nullptr, /* class data */ + sizeof (CustomCellRendererSurface), /* instance size */ + 0, /* nb preallocs */ + nullptr, /* instance init */ + nullptr /* value table */ + }; + + // inherit from GtkCellRendererText so we can set the "text" property and get a11y support for that + type = g_type_register_static(GTK_TYPE_CELL_RENDERER_TEXT, "CustomCellRendererSurface", + &tinfo, GTypeFlags(0)); + } + + return type; +} + +static void custom_cell_renderer_surface_get_property(GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + CustomCellRendererSurface *cellsurface = CUSTOM_CELL_RENDERER_SURFACE(object); + + switch (param_id) + { + case PROP_ID: + g_value_set_string(value, cellsurface->id); + break; + case PROP_INSTANCE_TREE_VIEW: + g_value_set_pointer(value, cellsurface->instance); + break; + default: + G_OBJECT_CLASS(custom_cell_renderer_surface_parent_class)->get_property(object, param_id, value, pspec); + break; + } +} + +static void custom_cell_renderer_surface_set_property(GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + CustomCellRendererSurface *cellsurface = CUSTOM_CELL_RENDERER_SURFACE(object); + + switch (param_id) + { + case PROP_ID: + g_free(cellsurface->id); + cellsurface->id = g_value_dup_string(value); + break; + case PROP_INSTANCE_TREE_VIEW: + cellsurface->instance = g_value_get_pointer(value); + break; + default: + G_OBJECT_CLASS(custom_cell_renderer_surface_parent_class)->set_property(object, param_id, value, pspec); + break; + } +} + +static bool custom_cell_renderer_surface_get_preferred_size(GtkCellRenderer *cell, + GtkOrientation orientation, + gint *minimum_size, + gint *natural_size); + +static void custom_cell_renderer_surface_render(GtkCellRenderer* cell, + cairo_t* cr, + GtkWidget* widget, + const GdkRectangle* background_area, + const GdkRectangle* cell_area, + GtkCellRendererState flags); + +static void custom_cell_renderer_surface_finalize(GObject *object) +{ + CustomCellRendererSurface *cellsurface = CUSTOM_CELL_RENDERER_SURFACE(object); + + g_free(cellsurface->id); + cellsurface->device.disposeAndClear(); + + G_OBJECT_CLASS(custom_cell_renderer_surface_parent_class)->finalize(object); +} + +static void custom_cell_renderer_surface_get_preferred_width(GtkCellRenderer *cell, + GtkWidget *widget, + gint *minimum_size, + gint *natural_size) +{ + if (!custom_cell_renderer_surface_get_preferred_size(cell, GTK_ORIENTATION_HORIZONTAL, + minimum_size, natural_size)) + { + // fallback to parent if we're empty + GTK_CELL_RENDERER_CLASS(custom_cell_renderer_surface_parent_class)->get_preferred_width(cell, + widget, minimum_size, natural_size); + } +} + +static void custom_cell_renderer_surface_get_preferred_height(GtkCellRenderer *cell, + GtkWidget *widget, + gint *minimum_size, + gint *natural_size) +{ + if (!custom_cell_renderer_surface_get_preferred_size(cell, GTK_ORIENTATION_VERTICAL, + minimum_size, natural_size)) + { + // fallback to parent if we're empty + GTK_CELL_RENDERER_CLASS(custom_cell_renderer_surface_parent_class)->get_preferred_height(cell, + widget, minimum_size, natural_size); + } + +} + +static void custom_cell_renderer_surface_get_preferred_height_for_width(GtkCellRenderer *cell, + GtkWidget *widget, + gint /*width*/, + gint *minimum_height, + gint *natural_height) +{ + gtk_cell_renderer_get_preferred_height(cell, widget, minimum_height, natural_height); +} + +static void custom_cell_renderer_surface_get_preferred_width_for_height(GtkCellRenderer *cell, + GtkWidget *widget, + gint /*height*/, + gint *minimum_width, + gint *natural_width) +{ + gtk_cell_renderer_get_preferred_width(cell, widget, minimum_width, natural_width); +} + +void custom_cell_renderer_surface_class_init(CustomCellRendererSurfaceClass *klass) +{ + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + /* Hook up functions to set and get our custom cell renderer properties */ + object_class->get_property = custom_cell_renderer_surface_get_property; + object_class->set_property = custom_cell_renderer_surface_set_property; + + custom_cell_renderer_surface_parent_class = g_type_class_peek_parent(klass); + object_class->finalize = custom_cell_renderer_surface_finalize; + + cell_class->get_preferred_width = custom_cell_renderer_surface_get_preferred_width; + cell_class->get_preferred_height = custom_cell_renderer_surface_get_preferred_height; + cell_class->get_preferred_width_for_height = custom_cell_renderer_surface_get_preferred_width_for_height; + cell_class->get_preferred_height_for_width = custom_cell_renderer_surface_get_preferred_height_for_width; + + cell_class->render = custom_cell_renderer_surface_render; + + g_object_class_install_property(object_class, + PROP_ID, + g_param_spec_string("id", + "ID", + "The ID of the custom data", + nullptr, + G_PARAM_READWRITE)); + + g_object_class_install_property(object_class, + PROP_INSTANCE_TREE_VIEW, + g_param_spec_pointer("instance", + "Instance", + "The GtkInstanceTreeView", + G_PARAM_READWRITE)); + + gtk_cell_renderer_class_set_accessible_type(cell_class, GTK_TYPE_TEXT_CELL_ACCESSIBLE); +} + +static GtkCellRenderer* custom_cell_renderer_surface_new() +{ + return GTK_CELL_RENDERER(g_object_new(CUSTOM_TYPE_CELL_RENDERER_SURFACE, nullptr)); +} + static VclPolicyType GtkToVcl(GtkPolicyType eType) { VclPolicyType eRet(VclPolicyType::NEVER); @@ -9455,6 +9667,21 @@ public: gtk_tree_view_column_set_title(pColumn, OUStringToOString(rTitle, RTL_TEXTENCODING_UTF8).getStr()); } + virtual void set_column_custom_renderer(int nColumn) override + { + GtkTreeViewColumn* pColumn = GTK_TREE_VIEW_COLUMN(g_list_nth_data(m_pColumns, nColumn)); + assert(pColumn && "wrong count"); + gtk_cell_layout_clear(GTK_CELL_LAYOUT(pColumn)); + GtkCellRenderer *pRenderer = custom_cell_renderer_surface_new(); + GValue value = G_VALUE_INIT; + g_value_init(&value, G_TYPE_POINTER); + g_value_set_pointer(&value, static_cast<gpointer>(this)); + g_object_set_property(G_OBJECT(pRenderer), "instance", &value); + gtk_tree_view_column_pack_start(pColumn, pRenderer, true); + gtk_tree_view_column_add_attribute(pColumn, pRenderer, "text", m_nTextCol); + gtk_tree_view_column_add_attribute(pColumn, pRenderer, "id", m_nIdCol); + } + virtual void insert(const weld::TreeIter* pParent, int pos, const OUString* pText, const OUString* pId, const OUString* pIconName, VirtualDevice* pImageSurface, const OUString* pExpanderName, bool bChildrenOnDemand, weld::TreeIter* pRet) override @@ -10897,6 +11124,16 @@ public: enable_notify_events(); } + void call_signal_custom_render(VirtualDevice& rOutput, const tools::Rectangle& rRect, bool bSelected, const OUString& rId) + { + signal_custom_render(rOutput, rRect, bSelected, rId); + } + + Size call_signal_custom_get_size(VirtualDevice& rOutput, const OUString& rId) + { + return signal_custom_get_size(rOutput, rId); + } + virtual ~GtkInstanceTreeView() override { if (m_pChangeEvent) @@ -10930,6 +11167,116 @@ public: } }; +void ensure_device(CustomCellRendererSurface *cellsurface, weld::TreeView* pTreeView) +{ + if (!cellsurface->device) + { + cellsurface->device = VclPtr<VirtualDevice>::Create(); + cellsurface->device->SetBackground(COL_TRANSPARENT); + // expand the point size of the desired font to the equivalent pixel size + if (vcl::Window* pDefaultDevice = dynamic_cast<vcl::Window*>(Application::GetDefaultDevice())) + pDefaultDevice->SetPointFont(*cellsurface->device, pTreeView->get_font()); + } +} + +} + +bool custom_cell_renderer_surface_get_preferred_size(GtkCellRenderer *cell, + GtkOrientation orientation, + gint *minimum_size, + gint *natural_size) +{ + GValue value = G_VALUE_INIT; + g_value_init(&value, G_TYPE_STRING); + g_object_get_property(G_OBJECT(cell), "id", &value); + + const char* pStr = g_value_get_string(&value); + + if (!pStr) + { + // this happens if we're empty + return false; + } + + OUString sId(pStr, strlen(pStr), RTL_TEXTENCODING_UTF8); + + value = G_VALUE_INIT; + g_value_init(&value, G_TYPE_POINTER); + g_object_get_property(G_OBJECT(cell), "instance", &value); + + CustomCellRendererSurface *cellsurface = CUSTOM_CELL_RENDERER_SURFACE(cell); + + GtkInstanceTreeView* pTreeView = static_cast<GtkInstanceTreeView*>(g_value_get_pointer(&value)); + + ensure_device(cellsurface, pTreeView); + + Size aSize = pTreeView->call_signal_custom_get_size(*cellsurface->device, sId); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (minimum_size) + *minimum_size = aSize.Width(); + + if (natural_size) + *natural_size = aSize.Width(); + } + else + { + if (minimum_size) + *minimum_size = aSize.Height(); + + if (natural_size) + *natural_size = aSize.Height(); + } + + return true; +} + +void custom_cell_renderer_surface_render(GtkCellRenderer* cell, + cairo_t* cr, + GtkWidget* /*widget*/, + const GdkRectangle* /*background_area*/, + const GdkRectangle* cell_area, + GtkCellRendererState flags) +{ + GValue value = G_VALUE_INIT; + g_value_init(&value, G_TYPE_STRING); + g_object_get_property(G_OBJECT(cell), "id", &value); + + const char* pStr = g_value_get_string(&value); + OUString sId(pStr, pStr ? strlen(pStr) : 0, RTL_TEXTENCODING_UTF8); + + value = G_VALUE_INIT; + g_value_init(&value, G_TYPE_POINTER); + g_object_get_property(G_OBJECT(cell), "instance", &value); + + CustomCellRendererSurface *cellsurface = CUSTOM_CELL_RENDERER_SURFACE(cell); + + GtkInstanceTreeView* pTreeView = static_cast<GtkInstanceTreeView*>(g_value_get_pointer(&value)); + + ensure_device(cellsurface, pTreeView); + + Size aSize(cell_area->width, cell_area->height); + // false to not bother setting the bg on resize as we'll do that + // ourself via cairo + cellsurface->device->SetOutputSizePixel(aSize, false); + + cairo_surface_t* pSurface = get_underlying_cairo_surface(*cellsurface->device); + + // fill surface as transparent so it can be blended with the potentially + // selected background + cairo_t* tempcr = cairo_create(pSurface); + cairo_set_source_rgba(tempcr, 0, 0, 0, 0); + cairo_set_operator(tempcr, CAIRO_OPERATOR_SOURCE); + cairo_paint(tempcr); + cairo_destroy(tempcr); + cairo_surface_flush(pSurface); + + pTreeView->call_signal_custom_render(*cellsurface->device, tools::Rectangle(Point(0, 0), aSize), flags & GTK_CELL_RENDERER_SELECTED, sId); + cairo_surface_mark_dirty(pSurface); + + cairo_set_source_surface(cr, pSurface, cell_area->x, cell_area->y); + cairo_paint(cr); } IMPL_LINK_NOARG(GtkInstanceTreeView, async_signal_changed, void*, void) |