7#include <gdkmm/paintable.h>
8#include <gdkmm/texture.h>
9#include <giomm/liststore.h>
10#include <glibmm/objectbase.h>
11#include <gtkmm/boolfilter.h>
12#include <gtkmm/centerbox.h>
13#include <gtkmm/filter.h>
14#include <gtkmm/filterlistmodel.h>
15#include <gtkmm/fixed.h>
16#include <gtkmm/grid.h>
17#include <gtkmm/gridview.h>
18#include <gtkmm/listitemfactory.h>
19#include <gtkmm/object.h>
20#include <gtkmm/overlay.h>
21#include <gtkmm/picture.h>
22#include <gtkmm/scrollinfo.h>
23#include <gtkmm/signallistitemfactory.h>
24#include <gtkmm/singleselection.h>
25#include <gtkmm/tooltip.h>
26#include <gtkmm/widgetpaintable.h>
27#include <initializer_list>
38#include <glibmm/markup.h>
39#include <gtkmm/adjustment.h>
41#include <gtkmm/button.h>
42#include <gtkmm/enums.h>
43#include <gtkmm/iconview.h>
44#include <gtkmm/label.h>
45#include <gtkmm/liststore.h>
46#include <gtkmm/paned.h>
47#include <gtkmm/scale.h>
48#include <gtkmm/searchentry2.h>
49#include <gtkmm/togglebutton.h>
50#include <gtkmm/treemodelfilter.h>
51#include <gtkmm/treemodelsort.h>
52#include <gtkmm/treeview.h>
72struct EffectItem :
public Glib::Object {
75 Glib::ustring tooltip;
76 Glib::ustring description;
79 Glib::ustring category;
83 static Glib::RefPtr<EffectItem>
create(
86 Glib::ustring tooltip,
87 Glib::ustring description,
90 Glib::ustring category,
94 auto item = Glib::make_refptr_for_instance<EffectItem>(
new EffectItem());
97 item->tooltip = tooltip;
99 item->access = access;
101 item->category = category;
102 item->effect = effect;
108struct CategoriesColumns :
public Gtk::TreeModel::ColumnRecord {
109 Gtk::TreeModelColumn<Glib::ustring> id;
110 Gtk::TreeModelColumn<Glib::ustring>
name;
112 CategoriesColumns() {
120 if (!
image)
return {};
122 auto w = image_size.
x();
123 auto h = image_size.
y();
129 auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32,
width * device_scale,
height * device_scale);
130 cairo_surface_set_device_scale(
surface->cobj(), device_scale, device_scale);
131 auto ctx = Cairo::Context::create(
surface);
135 ctx->set_source_rgba(1, 1, 1, 0);
140 ctx->set_source_rgba(1, 1, 1, 1);
144 auto imgw = cairo_image_surface_get_width(
image->cobj()) / device_scale;
145 auto imgh = cairo_image_surface_get_height(
image->cobj()) / device_scale;
148 ctx->set_source(
image, cx, cy);
152 auto black = 0x000000;
159 std::vector<Inkscape::Extension::Effect*> out;
161 std::copy_if(effects.begin(), effects.end(), std::back_inserter(out), [=](
auto effect) {
162 if (effect->hidden_from_menu()) return false;
164 return effect->is_filter_effect() != get_effects;
171 if (menu.empty())
return {};
178 Cairo::RefPtr<Cairo::Surface>
image;
180 if (icon.empty() || !
IO::file_test(icon.c_str(), G_FILE_TEST_EXISTS)) {
182 image = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, icon_size.
x(), icon_size.
y());
183 cairo_surface_set_device_scale(
image->cobj(), device_scale, device_scale);
188 auto file = Gio::File::create_for_path(icon);
190 if (!doc)
return image;
192 if (
auto item = cast<SPItem>(doc->getObjectById(
"test-object"))) {
198 if (
w > 0 && h > 0) {
199 auto scale = std::max(
w / icon_size.
x(), h / icon_size.
y());
205 g_warning(
"Cannot render icon for effect %s", effect->
get_id());
215 for (
auto& effect : effects) {
220 auto pos =
name.find(
"...", 0);
221 if (pos != std::string::npos) {
224 pos =
name.find(
"…", 0);
225 if (pos != std::string::npos) {
228 pos =
name.find(
"_", 0);
229 if (pos != std::string::npos) {
233 std::ostringstream
order;
234 std::ostringstream access;
236 for (
auto& part : menu) {
237 order << part.raw() <<
'\n';
238 access << part.raw() <<
" \u25b8 ";
242 auto translated = [](
const char* text) {
return *text ? gettext(text) :
""; };
253 auto tooltip =
"<small>" + access.str() +
"</small>";
254 if (!description.empty()) {
256 tooltip += translated(description.c_str());
259 item_store.append(EffectItem::create(
263 translated(description.c_str()),
272 item_store.sort([](
auto& a,
auto& b) {
return a->order.compare(b->order); });
275std::set<std::string>
add_categories(Glib::RefPtr<Gtk::ListStore>&
store,
const std::vector<Inkscape::Extension::Effect*>& effects,
bool effect) {
276 std::set<std::string> categories;
279 for (
auto& effect : effects) {
280 auto menu = effect->get_menu_list();
282 if (!category.empty()) {
283 categories.insert(category);
287 auto row = *
store->append();
291 row = *
store->append();
294 for (
auto cat : categories) {
295 auto row = *
store->append();
275std::set<std::string>
add_categories(Glib::RefPtr<Gtk::ListStore>&
store,
const std::vector<Inkscape::Extension::Effect*>& effects,
bool effect) {
…}
304 DialogBase(type == Effects ?
"/dialogs/extensions-gallery/effects" :
"/dialogs/extensions-gallery/filters",
305 type == Effects ?
"ExtensionsGallery" :
"FilterGallery"),
310 _run_btn_label(
get_widget<
Gtk::Label>(_builder,
"run-label")),
317 C_(
"apply-filter",
"_Apply");
319 auto& header = get_widget<Gtk::Label>(
_builder,
"header");
321 _(
"Select extension to run:") :
322 _(
"Select filter to apply:"));
326 auto selected = prefs->getString(
_prefs_path +
"/selected");
329 auto show_list = prefs->getBool(
_prefs_path +
"/show-list",
true);
330 auto position = prefs->getIntLimited(
_prefs_path +
"/position", 120, 10, 1000);
332 auto paned = &get_widget<Gtk::Paned>(
_builder,
"paned");
333 auto show_categories_list = [=](
bool show){
334 paned->get_start_child()->set_visible(show);
336 paned->set_position(position);
337 paned->property_position().signal_changed().connect([paned, prefs,
this](){
338 if (
auto const w = paned->get_start_child()) {
339 if (
w->is_visible()) prefs->setInt(
_prefs_path +
"/position", paned->get_position());
344 auto toggle = &get_widget<Gtk::ToggleButton>(
_builder,
"toggle");
346 _(
"Toggle list of effect categories") :
347 _(
"Toggle list of filter categories"));
348 toggle->set_active(show_list);
349 toggle->signal_toggled().connect([=,
this](){
350 auto visible = toggle->get_active();
354 show_categories_list(show_list);
357 _selector.set_row_separator_func([=](
auto&,
auto& it) {
364 _filter = Gtk::BoolFilter::create({});
381 it->get_value(g_categories_columns.id.index(), id);
389 auto effect = std::dynamic_pointer_cast<EffectItem>(ptr);
390 if (!effect)
return {};
393 auto name = Glib::Markup::escape_text(effect->name);
394 return { .label_markup =
name, .image = tex, .tooltip = effect->tooltip };
405 _gridview.signal_activate().connect([
this](
auto pos){
406 _run.activate_action(
_run.get_action_name());
408 _gridview.set_single_click_activate(
false);
410 _search.signal_search_changed().connect([
this](){
414 _selection_model->signal_selection_changed().connect([
this](
auto first,
auto count) {
419 _categories->foreach([
this](
const auto& path,
const auto&it) {
431 auto adj = get_object<Gtk::Adjustment>(
_builder,
"adjustment-thumbnails");
433 auto scale = &get_widget<Gtk::Scale>(
_builder,
"thumb-size");
443 if (!selected.empty()) {
445 for (
int pos = 0; pos <
N; ++pos) {
449 if (
item->
id == selected.raw()) {
452 auto scroll = Gtk::ScrollInfo::create();
453 scroll->set_enable_vertical();
454 _gridview.scroll_to(pos, Gtk::ListScrollFlags::NONE, scroll);
462 scale->signal_value_changed().connect([
scale, prefs,
this](){
473 auto& info = get_widget<Gtk::Label>(
_builder,
"info");
476 if (
auto effect = std::dynamic_pointer_cast<EffectItem>(
item)) {
478 label.set_label(effect->access);
479 label.set_tooltip_text(effect->access);
482 _run.set_action_name(
"app." + effect->id);
483 _run.set_sensitive();
485 auto& effect_obj = *effect->
effect;
488 Glib::ustring desc = effect->description;
489 info.set_markup(
"<i>" + Glib::Markup::escape_text(desc) +
"</i>");
490 info.set_tooltip_text(desc);
493 prefs->setString(
_prefs_path +
"/selected", effect->id);
497 label.set_tooltip_text(
"");
499 info.set_tooltip_text(
"");
501 _run.set_sensitive(
false);
517 auto effect_ptr = std::dynamic_pointer_cast<EffectItem>(
item);
518 if (!effect_ptr)
return false;
520 const auto& effect = *effect_ptr;
528 auto str =
_search.get_text().lowercase();
529 if (str.empty())
return true;
531 Glib::ustring text = effect.access;
532 return text.lowercase().find(str) != Glib::ustring::npos;
540 _filter->set_expression(expression);
547 auto none = Gtk::ClosureExpression<bool>::create([
this](
auto&
item){
return false; });
556 int min_size = effects ? 35 : 50;
557 const double factor = std::pow(2.0, 1.0 / 6.0);
559 auto size = std::round(std::pow(factor,
index) * min_size);
Cartesian point / 2D vector and related operations.
void ink_cairo_draw_drop_shadow(const Cairo::RefPtr< Cairo::Context > &ctx, const Geom::Rect &rect, double size, guint32 color, double color_alpha)
Draw drop shadow around the 'rect' with given 'size' and 'color'; shadow extends to the right and bot...
Cairo integration helpers.
Cairo::RefPtr< Cairo::ImageSurface > surface
static CRect from_xywh(Coord x, Coord y, Coord w, Coord h)
Create rectangle from origin and dimensions.
Two-dimensional point that doubles as a vector.
constexpr Coord y() const noexcept
constexpr Coord x() const noexcept
Effects are extensions that take a document and do something to it in place.
std::string find_icon_file(const std::string &default_dir) const
const Glib::ustring & get_menu_tip() const
bool apply_filter(SPItem *item)
std::string get_sanitized_id() const
Sanitizes the id and returns.
std::list< Glib::ustring > get_menu_list() const
void effect(SPDesktop *desktop, SPDocument *document=nullptr)
The function that 'does' the effect itself.
char const * get_name() const
Get the name of this extension - not a copy don't delete!
char const * get_id() const
Get the ID of this extension - not a copy don't delete!
static Preferences * get()
Access the singleton Preferences object.
DialogBase is the base class for the dialog system.
Glib::ustring const _prefs_path
ExtensionsGallery(Type type)
Glib::RefPtr< Gtk::BoolFilter > _filter
Glib::RefPtr< Gtk::FilterListModel > _filtered_model
std::unique_ptr< IconViewItemFactory > _factory
boost::compute::detail::lru_cache< std::string, Glib::RefPtr< Gdk::Texture > > _image_cache
Glib::ustring _current_category
Glib::RefPtr< Gtk::SingleSelection > _selection_model
Gtk::GridView & _gridview
Glib::RefPtr< Gtk::ListStore > _categories
sigc::scoped_connection _selection_change
Glib::RefPtr< Gdk::Texture > get_image(const std::string &key, const std::string &icon, Extension::Effect *effect)
Glib::RefPtr< Gtk::Builder > _builder
bool is_item_visible(const Glib::RefPtr< Glib::ObjectBase > &item) const
void show_category(const Glib::ustring &id)
Glib::RefPtr< Gtk::TreeSelection > _page_selection
Gtk::SearchEntry2 & _search
Gtk::TreeView & _selector
Gtk::Label & _run_btn_label
static std::unique_ptr< IconViewItemFactory > create(std::function< ItemData(Glib::RefPtr< Glib::ObjectBase > &)> get_item)
double get_width_px() const
void set_scale(double scale)
double get_height_px() const
Cairo::RefPtr< Cairo::ImageSurface > render_surface(double scale)
virtual char * description() const
A base class for all dialogs.
auto floor(Geom::Rect const &rect)
std::unique_ptr< Magick::Image > image
std::unique_ptr< SPDocument > ink_file_open(std::span< char const > buffer)
Open a document from memory.
DB db
This is the actual database object.
Util::ptr_shared get_path(Domain domain, Type type, char const *filename, char const *extra)
std::string get_path_string(Domain domain, Type type, char const *filename, char const *extra)
bool file_test(char const *utf8name, GFileTest test)
Inkscape::UI::Dialog::CategoriesColumns g_categories_columns
Geom::Point get_thumbnail_size(int index, ExtensionsGallery::Type type)
Cairo::RefPtr< Cairo::Surface > add_shadow(Geom::Point image_size, Cairo::RefPtr< Cairo::Surface > image, int device_scale)
static const std::map< Inkscape::Filters::FilterPrimitiveType, EffectMetadata > & get_effects()
const std::vector< Inkscape::Extension::Effect * > prepare_effects(const std::vector< Inkscape::Extension::Effect * > &effects, bool get_effects)
Cairo::RefPtr< Cairo::Surface > render_icon(Extension::Effect *effect, std::string icon, Geom::Point icon_size, int device_scale)
Glib::ustring get_category(const std::list< Glib::ustring > &menu)
void add_effects(Gio::ListStore< EffectItem > &item_store, const std::vector< Inkscape::Extension::Effect * > &effects, bool root)
std::set< std::string > add_categories(Glib::RefPtr< Gtk::ListStore > &store, const std::vector< Inkscape::Extension::Effect * > &effects, bool effect)
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
static void append(std::vector< T > &target, std::vector< T > &&source)
static std::string _run(char const *command)
Wrapper around g_spawn_sync which captures STDOUT and strips trailing whitespace.
static cairo_user_data_key_t key
Singleton class to access the preferences file in a convenient way.
Inkscape::IO::Resource - simple resource API.
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
Glib::RefPtr< Gdk::Texture > to_texture(Cairo::RefPtr< Cairo::Surface > const &surface)
Convert an image surface in ARGB32 format to a texture.