Enhance save as template and new from template dialogs
[inkscape.git] / src / ui / iconview-item-factory.h
blobbe620f59306f1505d8d09ab0cb6fe8c69ac6ba13
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 //
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>
21 #include <memory>
22 #include <unordered_map>
24 namespace Inkscape::UI {
26 class IconViewItemFactory {
27 public:
28 struct ItemData {
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; }
53 private:
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
71 if (_enable_labels) {
72 auto label = Gtk::make_managed<Gtk::Label>();
73 label->set_vexpand();
74 label->set_valign(Gtk::Align::START);
75 box->set_end_widget(*label);
78 list_item->set_child(*box);
79 }));
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());
85 if (!box) return;
86 auto image = dynamic_cast<Gtk::Picture*>(box->get_start_widget());
87 if (!image) return;
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);
102 if (label) {
103 label->set_markup(item_data.label_markup);
104 label->set_max_width_chars(std::min(5 + width / 10, 12));
105 label->set_wrap();
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);
112 if (_use_markup) {
113 box->set_tooltip_markup(item_data.tooltip);
114 } else {
115 box->set_tooltip_text(item_data.tooltip);
118 if (_track_items) _bound_items[box] = item;
119 }));
121 _connections.emplace_back(_factory->signal_unbind().connect([this](const Glib::RefPtr<Gtk::ListItem>& list_item) {
122 if (_track_items) {
123 auto box = dynamic_cast<Gtk::CenterBox*>(list_item->get_child());
124 _bound_items.erase(box);
126 }));
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;
138 } // namespace
140 #endif