diff --git a/src/ui/dialog/color-item.cpp b/src/ui/dialog/color-item.cpp index 912253fc250d142b23a734ecbc8810f2a30a5348..c8f7790ed0a050bbbb9f599ec82bb5451feda330 100644 --- a/src/ui/dialog/color-item.cpp +++ b/src/ui/dialog/color-item.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include "colors/dragndrop.h" @@ -56,6 +57,7 @@ #include "ui/util.h" #include "util/value-utils.h" #include "util/variant-visitor.h" +#include "object/sp-stop.h" namespace Inkscape::UI::Dialog { namespace { @@ -152,8 +154,6 @@ void ColorItem::common_setup() set_name("ColorItem"); set_tooltip_text(description + (tooltip.empty() ? tooltip : "\n" + tooltip)); - set_draw_func(sigc::mem_fun(*this, &ColorItem::draw_func)); - auto const drag = Gtk::DragSource::create(); drag->set_button(1); // left drag->set_actions(Gdk::DragAction::MOVE | Gdk::DragAction::COPY); @@ -219,71 +219,148 @@ void ColorItem::draw_color(Cairo::RefPtr const &cr, int w, int h auto grad = graddata.gradient; if (!grad) return; - auto pat_checkerboard = Cairo::RefPtr(new Cairo::Pattern(ink_cairo_pattern_create_checkerboard(), true)); - auto pat_gradient = Cairo::RefPtr(new Cairo::Pattern(grad->create_preview_pattern(w), true)); + auto pat_checkerboard = Cairo::RefPtr( + new Cairo::Pattern(ink_cairo_pattern_create_checkerboard(), true)); + auto pat_gradient = Cairo::RefPtr( + new Cairo::Pattern(grad->create_preview_pattern(w), true)); cr->set_source(pat_checkerboard); cr->paint(); cr->set_source(pat_gradient); cr->paint(); + + // Draw + auto const fg = get_color(); + cr->rectangle(0.5, 0.5, w - 1, h - 1); + cr->set_source_rgba(fg.get_red(), fg.get_green(), fg.get_blue(), 0.07); + cr->set_line_width(1); + cr->stroke(); }}, data); } -void ColorItem::draw_func(Cairo::RefPtr const &cr, int const w, int const h) +void ColorItem::snapshot_vfunc(const Glib::RefPtr& snapshot) { - // Only using caching for none and gradients. None is included because the image is huge. - bool const use_cache = std::holds_alternative(data) || std::holds_alternative(data); - - if (use_cache) { - auto scale = get_scale_factor(); - // Ensure cache exists and has correct size. - if (!cache || cache->get_width() != w * scale || cache->get_height() != h * scale) { - cache = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, w * scale, h * scale); - cairo_surface_set_device_scale(cache->cobj(), scale, scale); - cache_dirty = true; + std::visit(VariantVisitor{ + [&] (Undefined) { + // there's no color to paint; indicate clearly that there is nothing to select: + + // Calculate the position and size for the horizontal rectangle + auto const y = get_allocated_height() / 2 + 0.5; + auto const w = get_allocated_width() / 4; + auto const x = (get_allocated_width() - w) / 2 - 0.5; + auto const h = get_allocated_height(); + + // Define the color for the line and border + auto const fg = get_color(); + auto const color = Gdk::RGBA(fg.get_red(), fg.get_green(), fg.get_blue(), 0.5); + + // Append a horizontal line to the snapshot + snapshot->append_color(color, Gdk::Graphene::Rect(x, y, w, 1)); + + // Define the outer rectangle (border) + graphene_rect_t outer = GRAPHENE_RECT_INIT( + 0.5f, 0.5f, + static_cast(w - 1), + static_cast(h - 1) + ); + + // Draw the border as a filled rectangle + snapshot->append_color(color, &outer); + + // Set the thickness of the border + constexpr float thickness = 1.0f; + + // Define the inner rectangle (the part that is transparent inside the border) + graphene_rect_t inner = GRAPHENE_RECT_INIT( + outer.origin.x + thickness, + outer.origin.y + thickness, + outer.size.width - 2 * thickness, + outer.size.height - 2 * thickness + ); + + // Draw the interior with transparency (or background color) + if (inner.size.width > 0 && inner.size.height > 0) { + auto bg = Gdk::RGBA(0, 0, 0, 0); + snapshot->append_color(bg, &inner); } - // Ensure cache contents is up-to-date. - if (cache_dirty) { - draw_color(Cairo::Context::create(cache), w * scale, h * scale); - cache_dirty = false; + }, + [&] (PaintNone) { + if (auto const pixbuf = get_removecolor()) { + auto texture = Gdk::Texture::create_for_pixbuf(pixbuf); + Gdk::Graphene::Rect rect (0, 0, get_allocated_width(), get_allocated_height()); + snapshot->append_texture(texture, rect); } - // Paint from cache. - cr->set_source(cache, 0, 0); - cr->paint(); - } else { - // Paint directly. - draw_color(cr, w, h); - } + }, + [&] (Colors::Color const &color) { + // Convert color to Gdk::RGBA + auto const gdk_color = color_to_rgba(color); + + // Create GTK Graphene Rect with the widget's size + Gdk::Graphene::Rect rect (0, 0, get_allocated_width(), get_allocated_height()); - // Draw fill/stroke indicators. - if (is_fill || is_stroke) { - double const lightness = Colors::get_perceptual_lightness(getColor()); - auto [gray, alpha] = Colors::get_contrasting_color(lightness); - cr->set_source_rgba(gray, gray, gray, alpha); - - // Scale so that the square -1...1 is the biggest possible square centred in the widget. - auto minwh = std::min(w, h); - cr->translate((w - minwh) / 2.0, (h - minwh) / 2.0); - cr->scale(minwh / 2.0, minwh / 2.0); - cr->translate(1.0, 1.0); - - if (is_fill) { - cr->arc(0.0, 0.0, 0.35, 0.0, 2 * M_PI); - cr->fill(); - } + snapshot->append_color(gdk_color, rect); + }, + [&] (GradientData graddata) { + // Gradient pointer may be null if the gradient was destroyed. + auto grad = graddata.gradient; + if (!grad) return; - if (is_stroke) { - cr->set_fill_rule(Cairo::Context::FillRule::EVEN_ODD); - cr->arc(0.0, 0.0, 0.65, 0.0, 2 * M_PI); - cr->arc(0.0, 0.0, 0.5, 0.0, 2 * M_PI); - cr->fill(); + // Create the rectangle of the widget's size + Gdk::Graphene::Rect rect(0, 0, get_allocated_width(), get_allocated_height()); + + auto const y = get_allocated_height() / 2 + 0.5; + auto const w = get_allocated_width() / 4; + auto const x = (get_allocated_width() - w) / 2 - 0.5; + auto const h = get_allocated_height(); + + // Starting point of the gradient + auto const start = Gdk::Graphene::Point(x, y); + + // Ending point of the gradient + auto const end = Gdk::Graphene::Point(x + w, y); + + // Create the gradient pattern + auto pat_gradient = Cairo::RefPtr(new Cairo::Pattern(grad->create_preview_pattern(w), true)); + + // Define the gradient stops + int stop_count = grad->getStopCount(); + std::vector stops(stop_count); + for (int i = 0; i < stop_count; ++i) { + auto const stop = grad->getFirstStop(); + if (stop) { + stops[i].offset = stop->offset; + stops[i].color.red = stop->getColor().get(0); + stops[i].color.green = stop->getColor().get(1); + stops[i].color.blue = stop->getColor().get(2); + stops[i].color.alpha = stop->getColor().get(3); + } else { + // If there are no stops, use a Black color + stops[i].offset = 0.0f; + stops[i].color.red = 0.0f; + stops[i].color.green = 0.0f; + stops[i].color.blue = 0.0f; + stops[i].color.alpha = 1.0f; + } } - } + + // Initialize the rectangle for the gradient + graphene_rect_t graphene_rect = GRAPHENE_RECT_INIT(0.5f, 0.5f, static_cast(w - 1), static_cast(h - 1)); + + // Add the linear gradient to the snapshot + gtk_snapshot_append_linear_gradient( + snapshot->gobj(), + &graphene_rect, + start.gobj(), + end.gobj(), + stops.data(), + G_N_ELEMENTS(stops) + ); + }}, data); } void ColorItem::size_allocate_vfunc(int width, int height, int baseline) { - Gtk::DrawingArea::size_allocate_vfunc(width, height, baseline); + Gtk::Widget::size_allocate_vfunc(width, height, baseline); cache_dirty = true; } diff --git a/src/ui/dialog/color-item.h b/src/ui/dialog/color-item.h index 3983e17670c9c527be0c5a0386157351bbd54c6a..382ebeb01e90ac3e2e8fd2391648de7d8efa1442 100644 --- a/src/ui/dialog/color-item.h +++ b/src/ui/dialog/color-item.h @@ -48,7 +48,7 @@ class DialogBase; * * Note: This widget must be outlived by its parent dialog, passed in the constructor. */ -class ColorItem : public Gtk::DrawingArea +class ColorItem : public Gtk::Widget { public: // No fill option @@ -88,7 +88,7 @@ public: sigc::signal& signal_pinned() { return _signal_pinned; }; private: - void draw_func(Cairo::RefPtr const&, int width, int height); + void snapshot_vfunc(const Glib::RefPtr& snapshot) override; void size_allocate_vfunc(int width, int height, int baseline) override; Glib::RefPtr on_drag_prepare();