1 // SPDX-License-Identifier: GPL-2.0-or-later
3 // This is an item factory for a ColumnView container. It creates an item with picture and label.
4 // During "bind" phase it will ask for label markup and picture image, plus tooltip, and populate item.
6 #ifndef _ICONVIEWITEMFACTORY_H_
7 #define _ICONVIEWITEMFACTORY_H_
9 #include <gdkmm/texture.h>
10 #include <glibmm/objectbase.h>
11 #include <glibmm/refptr.h>
12 #include <glibmm/ustring.h>
13 #include <gtkmm/binlayout.h>
14 #include <gtkmm/centerbox.h>
15 #include <gtkmm/image.h>
16 #include <gtkmm/label.h>
17 #include <gtkmm/overlay.h>
18 #include <gtkmm/picture.h>
19 #include <gtkmm/signallistitemfactory.h>
20 #include <gtkmm/widget.h>
22 #include <unordered_map>
24 namespace Inkscape::UI
{
26 class IconViewItemFactory
{
29 Glib::ustring label_markup
;
30 Glib::RefPtr
<Gdk::Texture
> image
;
31 Glib::ustring tooltip
;
34 static std::unique_ptr
<IconViewItemFactory
> create(std::function
<ItemData (Glib::RefPtr
<Glib::ObjectBase
>&)> get_item
) {
35 return std::unique_ptr
<IconViewItemFactory
>(new IconViewItemFactory(std::move(get_item
)));
38 Glib::RefPtr
<Gtk::ListItemFactory
> get_factory() { return _factory
; }
40 // requests that labels are created (or not); gridview needs to be refreshed afterwards
41 void set_include_label(bool enable_labels
) { _enable_labels
= enable_labels
; }
43 // keep track of bound items, so we can query them
44 void set_track_bindings(bool track
) { _track_items
= track
; }
46 Glib::RefPtr
<Glib::ObjectBase
> find_item(Gtk::Widget
& item_container
) {
47 auto it
= _bound_items
.find(item_container
.get_first_child());
48 return it
!= _bound_items
.end() ? it
->second
: Glib::RefPtr
<Glib::ObjectBase
>();
51 void set_use_tooltip_markup(bool use_markup
= true) { _use_markup
= use_markup
; }
54 IconViewItemFactory(std::function
<ItemData (Glib::RefPtr
<Glib::ObjectBase
>&)> get_item
):
55 _get_item_data(std::move(get_item
)) {
57 _factory
= Gtk::SignalListItemFactory::create();
59 _connections
.emplace_back(_factory
->signal_setup().connect([this](const Glib::RefPtr
<Gtk::ListItem
>& list_item
) {
60 auto box
= Gtk::make_managed
<Gtk::CenterBox
>();
61 box
->add_css_class("item-box");
62 box
->set_orientation(Gtk::Orientation::VERTICAL
);
63 auto image
= Gtk::make_managed
<Gtk::Picture
>();
64 // add bin layout manager, so picture doesn't propagete its size to the parent container;
65 // that way picture widget can be freely resized to desired dimensions and it will not grow beyond them
66 image
->set_layout_manager(Gtk::BinLayout::create());
67 image
->set_halign(Gtk::Align::CENTER
);
68 image
->set_valign(Gtk::Align::CENTER
);
69 box
->set_start_widget(*image
);
70 // add a label below the picture
72 auto label
= Gtk::make_managed
<Gtk::Label
>();
74 label
->set_valign(Gtk::Align::START
);
75 box
->set_end_widget(*label
);
78 list_item
->set_child(*box
);
81 _connections
.emplace_back(_factory
->signal_bind().connect([this](const Glib::RefPtr
<Gtk::ListItem
>& list_item
) {
82 auto item
= list_item
->get_item();
84 auto box
= dynamic_cast<Gtk::CenterBox
*>(list_item
->get_child());
86 auto image
= dynamic_cast<Gtk::Picture
*>(box
->get_start_widget());
88 auto label
= dynamic_cast<Gtk::Label
*>(box
->get_end_widget());
90 auto item_data
= _get_item_data(item
);
92 image
->set_can_shrink(true);
93 image
->set_content_fit(Gtk::ContentFit::CONTAIN
);
94 auto tex
= item_data
.image
;
95 image
->set_paintable(tex
);
96 // poor man's high dpi support here:
97 auto scale
= box
->get_scale_factor();
98 auto width
= tex
? tex
->get_intrinsic_width() / scale
: 0;
99 auto height
= tex
? tex
->get_intrinsic_height() / scale
: 0;
100 image
->set_size_request(width
, height
);
103 label
->set_markup(item_data
.label_markup
);
104 label
->set_max_width_chars(std::min(5 + width
/ 10, 12));
106 label
->set_wrap_mode(Pango::WrapMode::WORD_CHAR
);
107 label
->set_natural_wrap_mode(Gtk::NaturalWrapMode::WORD
);
108 label
->set_justify(Gtk::Justification::CENTER
);
109 label
->set_valign(Gtk::Align::START
);
113 box
->set_tooltip_markup(item_data
.tooltip
);
115 box
->set_tooltip_text(item_data
.tooltip
);
118 if (_track_items
) _bound_items
[box
] = item
;
121 _connections
.emplace_back(_factory
->signal_unbind().connect([this](const Glib::RefPtr
<Gtk::ListItem
>& list_item
) {
123 auto box
= dynamic_cast<Gtk::CenterBox
*>(list_item
->get_child());
124 _bound_items
.erase(box
);
129 std::function
<ItemData (Glib::RefPtr
<Glib::ObjectBase
>&)> _get_item_data
;
130 Glib::RefPtr
<Gtk::SignalListItemFactory
> _factory
;
131 bool _use_markup
= false;
132 bool _enable_labels
= true;
133 bool _track_items
= false;
134 std::unordered_map
<Gtk::Widget
*, Glib::RefPtr
<Glib::ObjectBase
>> _bound_items
;
135 std::vector
<sigc::scoped_connection
> _connections
;