X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkcoloreditor.c;h=8cbbf21541c48e7141fa7c7253da69d7653243ef;hb=a41b73fbc71f613e4ca90ae8b9dd3bd317d026d6;hp=d10dc31f44caa67149cbc418d8bb275484a3cdac;hpb=021f5e0365e11b1ca7d2a22d10fb0183e8b922c3;p=~andy%2Fgtk diff --git a/gtk/gtkcoloreditor.c b/gtk/gtkcoloreditor.c index d10dc31f4..8cbbf2154 100644 --- a/gtk/gtkcoloreditor.c +++ b/gtk/gtkcoloreditor.c @@ -12,56 +12,70 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* TODO - * - split out sv-plane - * - focus indication - * - custom sliders - * - pop-up entries + * - touch + * - accessible relations for popups + * - saving per-application (?) + * - better popup theming */ #include "config.h" +#include "gtkcoloreditorprivate.h" + #include "gtkcolorchooserprivate.h" -#include "gtkcoloreditor.h" +#include "gtkcolorplaneprivate.h" +#include "gtkcolorscaleprivate.h" +#include "gtkcolorswatchprivate.h" +#include "gtkcolorutils.h" #include "gtkgrid.h" -#include "gtkscale.h" -#include "gtkaspectframe.h" -#include "gtkdrawingarea.h" +#include "gtkorientable.h" #include "gtkentry.h" -#include "gtkhsv.h" +#include "gtkoverlay.h" #include "gtkadjustment.h" +#include "gtklabel.h" +#include "gtkspinbutton.h" #include "gtkintl.h" +#include "gtkstylecontext.h" #include struct _GtkColorEditorPrivate { + GtkWidget *overlay; GtkWidget *grid; GtkWidget *swatch; GtkWidget *entry; GtkWidget *h_slider; - GtkWidget *sv_plane; + GtkWidget *h_popup; + GtkWidget *h_entry; GtkWidget *a_slider; + GtkWidget *a_popup; + GtkWidget *a_entry; + GtkWidget *sv_plane; + GtkWidget *sv_popup; + GtkWidget *s_entry; + GtkWidget *v_entry; + GtkWidget *current_popup; + GtkWidget *popdown_focus; GtkAdjustment *h_adj; + GtkAdjustment *s_adj; + GtkAdjustment *v_adj; GtkAdjustment *a_adj; - cairo_surface_t *surface; - GdkRGBA color; - gdouble h, s, v; - gint x, y; - gboolean in_drag; - gboolean text_changed; + + guint text_changed : 1; + guint use_alpha : 1; }; enum { PROP_ZERO, - PROP_COLOR + PROP_RGBA, + PROP_USE_ALPHA }; static void gtk_color_editor_iface_init (GtkColorChooserInterface *iface); @@ -70,529 +84,434 @@ G_DEFINE_TYPE_WITH_CODE (GtkColorEditor, gtk_color_editor, GTK_TYPE_BOX, G_IMPLEMENT_INTERFACE (GTK_TYPE_COLOR_CHOOSER, gtk_color_editor_iface_init)) -static gboolean -sv_draw (GtkWidget *widget, - cairo_t *cr, - GtkColorEditor *editor) +static guint +scale_round (gdouble value, gdouble scale) { - gint x, y; - gint width, height; - - cairo_set_source_surface (cr, editor->priv->surface, 0, 0); - cairo_paint (cr); - - x = editor->priv->x; - y = editor->priv->y; - width = gtk_widget_get_allocated_width (widget); - height = gtk_widget_get_allocated_height (widget); - - cairo_move_to (cr, 0, y + 0.5); - cairo_line_to (cr, width, y + 0.5); - - cairo_move_to (cr, x + 0.5, 0); - cairo_line_to (cr, x + 0.5, height); - - cairo_set_line_width (cr, 3.0); - cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6); - cairo_stroke_preserve (cr); - - cairo_set_line_width (cr, 1.0); - cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8); - cairo_stroke (cr); - - return FALSE; + value = floor (value * scale + 0.5); + value = MAX (value, 0); + value = MIN (value, scale); + return (guint)value; } static void -create_sv_surface (GtkColorEditor *editor) +entry_set_rgba (GtkColorEditor *editor, + const GdkRGBA *color) { - GtkWidget *sv_plane; - cairo_t *cr; - cairo_surface_t *surface; - gint width, height, stride; - cairo_surface_t *tmp; - guint red, green, blue; - guint32 *data, *p; - gdouble h, s, v; - gdouble r, g, b; - gdouble sf, vf; - gint x, y; - - sv_plane = editor->priv->sv_plane; - - if (!gtk_widget_get_realized (sv_plane)) - return; - - width = gtk_widget_get_allocated_width (sv_plane); - height = gtk_widget_get_allocated_height (sv_plane); + gchar *text; - surface = gdk_window_create_similar_surface (gtk_widget_get_window (sv_plane), - CAIRO_CONTENT_COLOR, - width, height); + text = g_strdup_printf ("#%02X%02X%02X", + scale_round (color->red, 255), + scale_round (color->green, 255), + scale_round (color->blue, 255)); + gtk_entry_set_text (GTK_ENTRY (editor->priv->entry), text); + editor->priv->text_changed = FALSE; + g_free (text); +} - if (editor->priv->surface) - cairo_surface_destroy (editor->priv->surface); - editor->priv->surface = surface; +static void +entry_apply (GtkWidget *entry, + GtkColorEditor *editor) +{ + GdkRGBA color; + gchar *text; - if (width == 1 || height == 1) + if (!editor->priv->text_changed) return; - stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width); - - data = g_malloc (4 * height * stride); - - h = editor->priv->h; - sf = 1.0 / (height - 1); - vf = 1.0 / (width - 1); - for (y = 0; y < height; y++) + text = gtk_editable_get_chars (GTK_EDITABLE (editor->priv->entry), 0, -1); + if (gdk_rgba_parse (&color, text)) { - s = CLAMP (1.0 - y * sf, 0.0, 1.0); - p = data + y * (stride / 4); - for (x = 0; x < width; x++) - { - v = x * vf; - gtk_hsv_to_rgb (h, s, v, &r, &g, &b); - red = CLAMP (r * 255, 0, 255); - green = CLAMP (g * 255, 0, 255); - blue = CLAMP (b * 255, 0, 255); - p[x] = (red << 16) | (green << 8) | blue; - } + color.alpha = gtk_adjustment_get_value (editor->priv->a_adj); + gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (editor), &color); } - tmp = cairo_image_surface_create_for_data ((guchar *)data, CAIRO_FORMAT_RGB24, - width, height, stride); - cr = cairo_create (surface); - - cairo_set_source_surface (cr, tmp, 0, 0); - cairo_paint (cr); - - cairo_destroy (cr); - cairo_surface_destroy (tmp); - g_free (data); -} - -static void -hsv_to_xy (GtkColorEditor *editor) -{ - GtkWidget *sv_plane; - gint width, height; - - sv_plane = editor->priv->sv_plane; - - width = gtk_widget_get_allocated_width (GTK_WIDGET (sv_plane)); - height = gtk_widget_get_allocated_height (GTK_WIDGET (sv_plane)); + editor->priv->text_changed = FALSE; - editor->priv->x = CLAMP (width * editor->priv->v, 0, width - 1); - editor->priv->y = CLAMP (height * (1 - editor->priv->s), 0, height - 1); + g_free (text); } static gboolean -sv_configure (GtkWidget *widget, - GdkEventConfigure *event, - GtkColorEditor *editor) +entry_focus_out (GtkWidget *entry, + GdkEventFocus *event, + GtkColorEditor *editor) { - create_sv_surface (editor); - hsv_to_xy (editor); - return TRUE; + entry_apply (entry, editor); + return FALSE; } static void -set_cross_grab (GtkWidget *widget, - GdkDevice *device, - guint32 time) +entry_text_changed (GtkWidget *entry, + GParamSpec *pspec, + GtkColorEditor *editor) { - GdkCursor *cursor; - - cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (widget)), - GDK_CROSSHAIR); - gdk_device_grab (device, - gtk_widget_get_window (widget), - GDK_OWNERSHIP_NONE, - FALSE, - GDK_POINTER_MOTION_MASK - | GDK_POINTER_MOTION_HINT_MASK - | GDK_BUTTON_RELEASE_MASK, - cursor, - time); - g_object_unref (cursor); + editor->priv->text_changed = TRUE; } -static gboolean -sv_grab_broken (GtkWidget *widget, - GdkEventGrabBroken *event, - GtkColorEditor *editor) +static void +hsv_changed (GtkColorEditor *editor) { - editor->priv->in_drag = FALSE; + GdkRGBA color; + gdouble h, s, v, a; - return TRUE; -} + h = gtk_adjustment_get_value (editor->priv->h_adj); + s = gtk_adjustment_get_value (editor->priv->s_adj); + v = gtk_adjustment_get_value (editor->priv->v_adj); + a = gtk_adjustment_get_value (editor->priv->a_adj); -static guint -scale_round (gdouble value, gdouble scale) -{ - value = floor (value * scale + 0.5); - value = MAX (value, 0); - value = MIN (value, scale); - return (guint)value; -} + gtk_hsv_to_rgb (h, s, v, &color.red, &color.green, &color.blue); + color.alpha = a; -static void -update_entry (GtkColorEditor *editor) -{ - gchar *text; + gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (editor->priv->swatch), &color); + gtk_color_scale_set_rgba (GTK_COLOR_SCALE (editor->priv->a_slider), &color); + entry_set_rgba (editor, &color); - text = g_strdup_printf ("#%02X%02X%02X", - scale_round (editor->priv->color.red, 255), - scale_round (editor->priv->color.green, 255), - scale_round (editor->priv->color.blue, 255)); - gtk_entry_set_text (GTK_ENTRY (editor->priv->entry), text); - editor->priv->text_changed = FALSE; - g_free (text); + g_object_notify (G_OBJECT (editor), "rgba"); } static void -sv_update_color (GtkColorEditor *editor, - gint x, - gint y) +dismiss_current_popup (GtkColorEditor *editor) { - GtkWidget *sv_plane; - - sv_plane = editor->priv->sv_plane; - - editor->priv->x = x; - editor->priv->y = y; - - editor->priv->s = CLAMP (1 - y * (1.0 / gtk_widget_get_allocated_height (sv_plane)), 0, 1); - editor->priv->v = CLAMP (x * (1.0 / gtk_widget_get_allocated_width (sv_plane)), 0, 1); - gtk_hsv_to_rgb (editor->priv->h, editor->priv->s, editor->priv->v, - &editor->priv->color.red, - &editor->priv->color.green, - &editor->priv->color.blue); - update_entry (editor); - gtk_adjustment_set_value (editor->priv->h_adj, editor->priv->h); - gtk_widget_queue_draw (editor->priv->swatch); - gtk_widget_queue_draw (sv_plane); + if (editor->priv->current_popup) + { + gtk_widget_hide (editor->priv->current_popup); + editor->priv->current_popup = NULL; + if (editor->priv->popdown_focus) + { + gtk_widget_grab_focus (editor->priv->popdown_focus); + editor->priv->popdown_focus = NULL; + } + } } -static gboolean -sv_button_press (GtkWidget *widget, - GdkEventButton *event, - GtkColorEditor *editor) +static void +popup_edit (GtkWidget *widget, + GtkColorEditor *editor) { - if (editor->priv->in_drag || event->button != GDK_BUTTON_PRIMARY) - return FALSE; + GtkWidget *popup = NULL; + GtkWidget *toplevel; + GtkWidget *focus; - editor->priv->in_drag = TRUE; - set_cross_grab (widget, gdk_event_get_device ((GdkEvent*)event), event->time); - sv_update_color (editor, event->x, event->y); - gtk_widget_grab_focus (widget); + if (widget == editor->priv->sv_plane) + { + popup = editor->priv->sv_popup; + focus = editor->priv->s_entry; + } + else if (widget == editor->priv->h_slider) + { + popup = editor->priv->h_popup; + focus = editor->priv->h_entry; + } + else if (widget == editor->priv->a_slider) + { + popup = editor->priv->a_popup; + focus = editor->priv->a_entry; + } - return TRUE; + if (popup) + { + dismiss_current_popup (editor); + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (editor)); + editor->priv->popdown_focus = gtk_window_get_focus (GTK_WINDOW (toplevel)); + editor->priv->current_popup = popup; + gtk_widget_show (popup); + gtk_widget_grab_focus (focus); + } } static gboolean -sv_button_release (GtkWidget *widget, - GdkEventButton *event, - GtkColorEditor *editor) +popup_key_press (GtkWidget *popup, + GdkEventKey *event, + GtkColorEditor *editor) { - if (!editor->priv->in_drag || event->button != GDK_BUTTON_PRIMARY) - return FALSE; - - editor->priv->in_drag = FALSE; - - sv_update_color (editor, event->x, event->y); - gdk_device_ungrab (gdk_event_get_device ((GdkEvent *) event), event->time); + if (event->keyval == GDK_KEY_Escape) + { + dismiss_current_popup (editor); + return TRUE; + } - return TRUE; + return FALSE; } static gboolean -sv_motion (GtkWidget *widget, - GdkEventMotion *event, - GtkColorEditor *editor) +get_child_position (GtkOverlay *overlay, + GtkWidget *widget, + GtkAllocation *allocation, + GtkColorEditor *editor) { - if (!editor->priv->in_drag) - return FALSE; + GtkRequisition req; + GtkAllocation alloc; + gint s, e; - gdk_event_request_motions (event); - sv_update_color (editor, event->x, event->y); + gtk_widget_get_preferred_size (widget, &req, NULL); - return TRUE; -} + allocation->width = req.width; + allocation->height = req.height; -static void -sv_move (GtkColorEditor *editor, - gdouble ds, - gdouble dv) -{ - if (editor->priv->s + ds > 1) + if (widget == editor->priv->sv_popup) { - if (editor->priv->s < 1) - editor->priv->s = 1; + if (gtk_widget_get_direction (GTK_WIDGET (overlay)) == GTK_TEXT_DIR_RTL) + allocation->x = 0; else - goto error; + allocation->x = gtk_widget_get_allocated_width (GTK_WIDGET (overlay)) - req.width; + allocation->y = req.height / 3; } - else if (editor->priv->s + ds < 0) + else if (widget == editor->priv->h_popup) { - if (editor->priv->s > 0) - editor->priv->s = 0; + gtk_widget_get_allocation (editor->priv->h_slider, &alloc); + gtk_range_get_slider_range (GTK_RANGE (editor->priv->h_slider), &s, &e); + + if (gtk_widget_get_direction (GTK_WIDGET (overlay)) == GTK_TEXT_DIR_RTL) + gtk_widget_translate_coordinates (editor->priv->h_slider, + gtk_widget_get_parent (editor->priv->grid), + - req.width, (s + e - req.height) / 2, + &allocation->x, &allocation->y); else - goto error; + gtk_widget_translate_coordinates (editor->priv->h_slider, + gtk_widget_get_parent (editor->priv->grid), + alloc.width, (s + e - req.height) / 2, + &allocation->x, &allocation->y); } - else + else if (widget == editor->priv->a_popup) { - editor->priv->s += ds; - } + gtk_widget_get_allocation (editor->priv->a_slider, &alloc); + gtk_range_get_slider_range (GTK_RANGE (editor->priv->a_slider), &s, &e); - if (editor->priv->v + dv > 1) - { - if (editor->priv->v < 1) - editor->priv->v = 1; - else - goto error; - } - else if (editor->priv->v + dv < 0) - { - if (editor->priv->v > 0) - editor->priv->v = 0; - else - goto error; + gtk_widget_translate_coordinates (editor->priv->a_slider, + gtk_widget_get_parent (editor->priv->grid), + (s + e - req.width) / 2, - req.height, + &allocation->x, &allocation->y); } else - { - editor->priv->v += dv; - } - - gtk_hsv_to_rgb (editor->priv->h, editor->priv->s, editor->priv->v, - &editor->priv->color.red, - &editor->priv->color.green, - &editor->priv->color.blue); - - hsv_to_xy (editor); + return FALSE; - update_entry (editor); - gtk_adjustment_set_value (editor->priv->h_adj, editor->priv->h); - gtk_widget_queue_draw (editor->priv->swatch); - gtk_widget_queue_draw (editor->priv->sv_plane); - return; + allocation->x = CLAMP (allocation->x, 0, gtk_widget_get_allocated_width (GTK_WIDGET (overlay)) - req.width); + allocation->y = CLAMP (allocation->y, 0, gtk_widget_get_allocated_height (GTK_WIDGET (overlay)) - req.height); -error: - gtk_widget_error_bell (editor->priv->sv_plane); + return TRUE; } -static gboolean -sv_key_press (GtkWidget *widget, - GdkEventKey *event, - GtkColorEditor *editor) +static void +value_changed (GtkAdjustment *a, + GtkAdjustment *as) { - gdouble step; + gdouble scale; - /* FIXME: turn into bindings */ - if ((event->state & GDK_MOD1_MASK) != 0) - step = 0.1; - else - step = 0.01; - - if (event->keyval == GDK_KEY_Up || - event->keyval == GDK_KEY_KP_Up) - sv_move (editor, step, 0); - else if (event->keyval == GDK_KEY_Down || - event->keyval == GDK_KEY_KP_Down) - sv_move (editor, -step, 0); - else if (event->keyval == GDK_KEY_Left || - event->keyval == GDK_KEY_KP_Left) - sv_move (editor, 0, -step); - else if (event->keyval == GDK_KEY_Right || - event->keyval == GDK_KEY_KP_Right) - sv_move (editor, 0, step); - else - return FALSE; - - return TRUE; + scale = gtk_adjustment_get_upper (as) / gtk_adjustment_get_upper (a); + g_signal_handlers_block_by_func (as, value_changed, a); + gtk_adjustment_set_value (as, gtk_adjustment_get_value (a) * scale); + g_signal_handlers_unblock_by_func (as, value_changed, a); } -static void -entry_apply (GtkWidget *entry, - GtkColorEditor *editor) +static GtkAdjustment * +scaled_adjustment (GtkAdjustment *a, + gdouble scale) { - GdkRGBA color; - gchar *text; + GtkAdjustment *as; - if (!editor->priv->text_changed) - return; - - text = gtk_editable_get_chars (GTK_EDITABLE (editor->priv->entry), 0, -1); - if (gdk_rgba_parse (&color, text)) - { - editor->priv->color.red = color.red; - editor->priv->color.green = color.green; - editor->priv->color.blue = color.blue; - gtk_rgb_to_hsv (editor->priv->color.red, - editor->priv->color.green, - editor->priv->color.blue, - &editor->priv->h, &editor->priv->s, &editor->priv->v); - hsv_to_xy (editor); - gtk_adjustment_set_value (editor->priv->h_adj, editor->priv->h); - gtk_widget_queue_draw (GTK_WIDGET (editor)); - g_object_notify (G_OBJECT (editor), "color"); - } + as = gtk_adjustment_new (gtk_adjustment_get_value (a) * scale, + gtk_adjustment_get_lower (a) * scale, + gtk_adjustment_get_upper (a) * scale, + gtk_adjustment_get_step_increment (a) * scale, + gtk_adjustment_get_page_increment (a) * scale, + gtk_adjustment_get_page_size (a) * scale); - editor->priv->text_changed = FALSE; + g_signal_connect (a, "value-changed", G_CALLBACK (value_changed), as); + g_signal_connect (as, "value-changed", G_CALLBACK (value_changed), a); - g_free (text); + return as; } static gboolean -entry_focus_out (GtkWidget *entry, - GdkEventFocus *event, - GtkColorEditor *editor) +popup_draw (GtkWidget *popup, + cairo_t *cr, + GtkColorEditor *editor) { - entry_apply (entry, editor); - return FALSE; -} + GtkStyleContext *context; + gint width, height; -static void -entry_text_changed (GtkWidget *entry, - GParamSpec *pspec, - GtkColorEditor *editor) -{ - editor->priv->text_changed = TRUE; -} + context = gtk_widget_get_style_context (popup); + width = gtk_widget_get_allocated_width (popup); + height = gtk_widget_get_allocated_height (popup); -static void -h_changed (GtkAdjustment *adj, - GtkColorEditor *editor) -{ - editor->priv->h = gtk_adjustment_get_value (adj); - - gtk_hsv_to_rgb (editor->priv->h, editor->priv->s, editor->priv->v, - &editor->priv->color.red, - &editor->priv->color.green, - &editor->priv->color.blue); - create_sv_surface (editor); - gtk_widget_queue_draw (editor->priv->sv_plane); - gtk_widget_queue_draw (editor->priv->swatch); - update_entry (editor); - g_object_notify (G_OBJECT (editor), "color"); -} + gtk_render_background (context, cr, 0, 0, width, height); + gtk_render_frame (context, cr, 0, 0, width, height); -static void -a_changed (GtkAdjustment *adj, - GtkColorEditor *editor) -{ - editor->priv->color.alpha = gtk_adjustment_get_value (adj); - gtk_widget_queue_draw (editor->priv->swatch); - g_object_notify (G_OBJECT (editor), "color"); + return FALSE; } -static cairo_pattern_t * -get_checkered_pattern (void) +static GtkWidget * +create_popup (GtkColorEditor *editor, + GtkWidget *attach, + GtkWidget *contents) { - /* need to respect pixman's stride being a multiple of 4 */ - static unsigned char data[8] = { 0xFF, 0x00, 0x00, 0x00, - 0x00, 0xFF, 0x00, 0x00 }; - static cairo_surface_t *checkered = NULL; - cairo_pattern_t *pattern; - - if (checkered == NULL) - checkered = cairo_image_surface_create_for_data (data, - CAIRO_FORMAT_A8, - 2, 2, 4); - - pattern = cairo_pattern_create_for_surface (checkered); - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); - cairo_pattern_set_filter (pattern, CAIRO_FILTER_NEAREST); - - return pattern; -} + GtkWidget *popup; -static gboolean -swatch_draw (GtkWidget *swatch, - cairo_t *cr, - GtkColorEditor *editor) -{ - cairo_pattern_t *checkered; + g_object_set (contents, "margin", 12, NULL); - cairo_set_source_rgb (cr, 0.33, 0.33, 0.33); - cairo_paint (cr); + popup = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_style_context_add_class (gtk_widget_get_style_context (popup), GTK_STYLE_CLASS_TOOLTIP); + gtk_container_add (GTK_CONTAINER (popup), contents); - cairo_set_source_rgb (cr, 0.66, 0.66, 0.66); - cairo_scale (cr, 8, 8); + gtk_widget_show_all (contents); + gtk_widget_set_no_show_all (popup, TRUE); - checkered = get_checkered_pattern (); - cairo_mask (cr, checkered); - cairo_pattern_destroy (checkered); + g_signal_connect (popup, "draw", G_CALLBACK (popup_draw), editor); - gdk_cairo_set_source_rgba (cr, &editor->priv->color); - cairo_paint (cr); + gtk_overlay_add_overlay (GTK_OVERLAY (editor->priv->overlay), popup); + g_signal_connect (attach, "popup-menu", G_CALLBACK (popup_edit), editor); - return TRUE; + return popup; } static void gtk_color_editor_init (GtkColorEditor *editor) { GtkWidget *grid; - GtkAdjustment *adj; + GtkWidget *slider; + GtkWidget *entry; + GtkWidget *swatch; + GtkAdjustment *h_adj, *s_adj, *v_adj, *a_adj; + AtkObject *atk_obj; + GdkRGBA transparent = { 0, 0, 0, 0 }; editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (editor, GTK_TYPE_COLOR_EDITOR, GtkColorEditorPrivate); + editor->priv->use_alpha = TRUE; + + editor->priv->h_adj = h_adj = gtk_adjustment_new (0, 0, 1, 0.01, 0.1, 0); + editor->priv->s_adj = s_adj = gtk_adjustment_new (0, 0, 1, 0.01, 0.1, 0); + editor->priv->v_adj = v_adj = gtk_adjustment_new (0, 0, 1, 0.01, 0.1, 0); + editor->priv->a_adj = a_adj = gtk_adjustment_new (0, 0, 1, 0.01, 0.1, 0); + + g_object_ref_sink (h_adj); + g_object_ref_sink (s_adj); + g_object_ref_sink (v_adj); + g_object_ref_sink (a_adj); + + g_signal_connect_swapped (h_adj, "value-changed", G_CALLBACK (hsv_changed), editor); + g_signal_connect_swapped (s_adj, "value-changed", G_CALLBACK (hsv_changed), editor); + g_signal_connect_swapped (v_adj, "value-changed", G_CALLBACK (hsv_changed), editor); + g_signal_connect_swapped (a_adj, "value-changed", G_CALLBACK (hsv_changed), editor); + gtk_widget_push_composite_child (); + /* Construct the main UI */ + editor->priv->swatch = swatch = gtk_color_swatch_new (); + gtk_color_swatch_set_selectable (GTK_COLOR_SWATCH (editor->priv->swatch), FALSE); + gtk_widget_set_events (swatch, gtk_widget_get_events (swatch) + & ~(GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_KEY_PRESS_MASK + | GDK_KEY_RELEASE_MASK)); + gtk_widget_set_can_focus (swatch, FALSE); + + editor->priv->entry = entry = gtk_entry_new (); + atk_obj = gtk_widget_get_accessible (entry); + atk_object_set_role (atk_obj, ATK_ROLE_ENTRY); + atk_object_set_name (atk_obj, _("Color Name")); + g_signal_connect (entry, "activate", G_CALLBACK (entry_apply), editor); + g_signal_connect (entry, "notify::text", G_CALLBACK (entry_text_changed), editor); + g_signal_connect (entry, "focus-out-event", G_CALLBACK (entry_focus_out), editor); + + editor->priv->h_slider = slider = gtk_color_scale_new (h_adj, GTK_COLOR_SCALE_HUE); + gtk_orientable_set_orientation (GTK_ORIENTABLE (slider), GTK_ORIENTATION_VERTICAL); + if (gtk_widget_get_direction (slider) == GTK_TEXT_DIR_RTL) + gtk_style_context_add_class (gtk_widget_get_style_context (slider), + GTK_STYLE_CLASS_SCALE_HAS_MARKS_ABOVE); + else + gtk_style_context_add_class (gtk_widget_get_style_context (slider), + GTK_STYLE_CLASS_SCALE_HAS_MARKS_BELOW); + + editor->priv->sv_plane = gtk_color_plane_new (h_adj, s_adj, v_adj); + gtk_widget_set_size_request (editor->priv->sv_plane, 300, 300); + + editor->priv->a_slider = slider = gtk_color_scale_new (a_adj, GTK_COLOR_SCALE_ALPHA); + gtk_orientable_set_orientation (GTK_ORIENTABLE (slider), GTK_ORIENTATION_HORIZONTAL); + gtk_style_context_add_class (gtk_widget_get_style_context (slider), + GTK_STYLE_CLASS_SCALE_HAS_MARKS_ABOVE); + editor->priv->grid = grid = gtk_grid_new (); gtk_grid_set_row_spacing (GTK_GRID (grid), 12); gtk_grid_set_column_spacing (GTK_GRID (grid), 12); - editor->priv->swatch = gtk_drawing_area_new (); - g_signal_connect (editor->priv->swatch, "draw", G_CALLBACK (swatch_draw), editor); - editor->priv->entry = gtk_entry_new (); - g_signal_connect (editor->priv->entry, "activate", - G_CALLBACK (entry_apply), editor); - g_signal_connect (editor->priv->entry, "notify::text", - G_CALLBACK (entry_text_changed), editor); - g_signal_connect (editor->priv->entry, "focus-out-event", - G_CALLBACK (entry_focus_out), editor); - - adj = gtk_adjustment_new (0, 0, 1, 0.01, 0.1, 0); - g_signal_connect (adj, "value-changed", G_CALLBACK (h_changed), editor); - editor->priv->h_slider = gtk_scale_new (GTK_ORIENTATION_VERTICAL, adj); - editor->priv->h_adj = adj; - - gtk_scale_set_draw_value (GTK_SCALE (editor->priv->h_slider), FALSE); - editor->priv->sv_plane = gtk_drawing_area_new (); - gtk_widget_set_size_request (editor->priv->sv_plane, 300, 300); - gtk_widget_set_hexpand (editor->priv->sv_plane, TRUE); - gtk_widget_set_vexpand (editor->priv->sv_plane, TRUE); - - adj = gtk_adjustment_new (1, 0, 1, 0.01, 0.1, 0); - editor->priv->a_slider = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adj); - g_signal_connect (adj, "value-changed", G_CALLBACK (a_changed), editor); - gtk_scale_set_draw_value (GTK_SCALE (editor->priv->a_slider), FALSE); - editor->priv->a_adj = adj; - - gtk_widget_set_can_focus (editor->priv->sv_plane, TRUE); - gtk_widget_set_events (editor->priv->sv_plane, GDK_KEY_PRESS_MASK - | GDK_BUTTON_PRESS_MASK - | GDK_BUTTON_RELEASE_MASK - | GDK_POINTER_MOTION_MASK); - - g_signal_connect (editor->priv->sv_plane, "draw", G_CALLBACK (sv_draw), editor); - g_signal_connect (editor->priv->sv_plane, "configure-event", G_CALLBACK (sv_configure), editor); - g_signal_connect (editor->priv->sv_plane, "button-press-event", G_CALLBACK (sv_button_press), editor); - g_signal_connect (editor->priv->sv_plane, "button-release-event", G_CALLBACK (sv_button_release), editor); - g_signal_connect (editor->priv->sv_plane, "motion-notify-event", G_CALLBACK (sv_motion), editor); - g_signal_connect (editor->priv->sv_plane, "grab-broken-event", G_CALLBACK (sv_grab_broken), editor); - g_signal_connect (editor->priv->sv_plane, "key-press-event", G_CALLBACK (sv_key_press), editor); - gtk_grid_attach (GTK_GRID (grid), editor->priv->swatch, 1, 0, 1, 1); gtk_grid_attach (GTK_GRID (grid), editor->priv->entry, 2, 0, 1, 1); gtk_grid_attach (GTK_GRID (grid), editor->priv->h_slider, 0, 1, 1, 1); gtk_grid_attach (GTK_GRID (grid), editor->priv->sv_plane, 1, 1, 2, 1); gtk_grid_attach (GTK_GRID (grid), editor->priv->a_slider, 1, 2, 2, 1); - gtk_widget_show_all (grid); - gtk_container_add (GTK_CONTAINER (editor), grid); + /* This extra margin is necessary so we have room to the sides + * to place the popups as desired + */ + gtk_widget_set_margin_left (grid, 30); + gtk_widget_set_margin_right (grid, 30); + + editor->priv->overlay = gtk_overlay_new (); + gtk_widget_override_background_color (editor->priv->overlay, 0, &transparent); + gtk_container_add (GTK_CONTAINER (editor->priv->overlay), grid); + + /* Construct the sv popup */ + editor->priv->s_entry = entry = gtk_spin_button_new (scaled_adjustment (s_adj, 100), 1, 0); + atk_obj = gtk_widget_get_accessible (entry); + atk_object_set_name (atk_obj, C_("Color channel", "Saturation")); + atk_object_set_role (atk_obj, ATK_ROLE_ENTRY); + g_signal_connect (entry, "key-press-event", G_CALLBACK (popup_key_press), editor); + + editor->priv->v_entry = entry = gtk_spin_button_new (scaled_adjustment (v_adj, 100), 1, 0); + atk_obj = gtk_widget_get_accessible (entry); + atk_object_set_name (atk_obj, C_("Color channel", "Value")); + atk_object_set_role (atk_obj, ATK_ROLE_ENTRY); + g_signal_connect (entry, "key-press-event", G_CALLBACK (popup_key_press), editor); + + grid = gtk_grid_new (); + gtk_grid_set_row_spacing (GTK_GRID (grid), 6); + gtk_grid_set_column_spacing (GTK_GRID (grid), 6); + + gtk_grid_attach (GTK_GRID (grid), gtk_label_new (C_("Color channel", "S")), 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), editor->priv->s_entry, 1, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), gtk_label_new (C_("Color channel", "V")), 0, 1, 1, 1); + gtk_grid_attach (GTK_GRID (grid), editor->priv->v_entry, 1, 1, 1, 1); + + editor->priv->sv_popup = create_popup (editor, editor->priv->sv_plane, grid); + + /* Construct the h popup */ + editor->priv->h_entry = entry = gtk_spin_button_new (scaled_adjustment (h_adj, 100), 1, 0); + atk_obj = gtk_widget_get_accessible (entry); + atk_object_set_name (atk_obj, C_("Color channel", "Hue")); + atk_object_set_role (atk_obj, ATK_ROLE_ENTRY); + g_signal_connect (entry, "key-press-event", G_CALLBACK (popup_key_press), editor); + + grid = gtk_grid_new (); + gtk_grid_set_column_spacing (GTK_GRID (grid), 6); + + gtk_grid_attach (GTK_GRID (grid), gtk_label_new (C_("Color channel", "H")), 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), editor->priv->h_entry, 1, 0, 1, 1); + + editor->priv->h_popup = create_popup (editor, editor->priv->h_slider, grid); + + /* Construct the a popup */ + editor->priv->a_entry = entry = gtk_spin_button_new (scaled_adjustment (a_adj, 100), 1, 0); + atk_obj = gtk_widget_get_accessible (entry); + atk_object_set_name (atk_obj, C_("Color channel", "Alpha")); + atk_object_set_role (atk_obj, ATK_ROLE_ENTRY); + g_signal_connect (entry, "key-press-event", G_CALLBACK (popup_key_press), editor); + + grid = gtk_grid_new (); + gtk_grid_set_column_spacing (GTK_GRID (grid), 6); + + gtk_grid_attach (GTK_GRID (grid), gtk_label_new (C_("Color channel", "A")), 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), editor->priv->a_entry, 1, 0, 1, 1); + + editor->priv->a_popup = create_popup (editor, editor->priv->a_slider, grid); + + /* Hook up popup positioning */ + g_signal_connect (editor->priv->overlay, "get-child-position", G_CALLBACK (get_child_position), editor); + g_signal_connect (editor, "notify::visible", G_CALLBACK (dismiss_current_popup), NULL); + + gtk_widget_show_all (editor->priv->overlay); + gtk_container_add (GTK_CONTAINER (editor), editor->priv->overlay); + gtk_widget_pop_composite_child (); } @@ -602,97 +521,130 @@ gtk_color_editor_get_property (GObject *object, GValue *value, GParamSpec *pspec) { + GtkColorEditor *ce = GTK_COLOR_EDITOR (object); GtkColorChooser *cc = GTK_COLOR_CHOOSER (object); switch (prop_id) { - case PROP_COLOR: + case PROP_RGBA: { GdkRGBA color; - gtk_color_chooser_get_color (cc, &color); + gtk_color_chooser_get_rgba (cc, &color); g_value_set_boxed (value, &color); } - break; + break; + case PROP_USE_ALPHA: + g_value_set_boolean (value, gtk_widget_get_visible (ce->priv->a_slider)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +static void +gtk_color_editor_set_use_alpha (GtkColorEditor *editor, + gboolean use_alpha) +{ + if (editor->priv->use_alpha != use_alpha) + { + editor->priv->use_alpha = use_alpha; + gtk_widget_set_visible (editor->priv->a_slider, use_alpha); + gtk_color_swatch_set_use_alpha (GTK_COLOR_SWATCH (editor->priv->swatch), use_alpha); + } +} + static void gtk_color_editor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { + GtkColorEditor *ce = GTK_COLOR_EDITOR (object); GtkColorChooser *cc = GTK_COLOR_CHOOSER (object); switch (prop_id) { - case PROP_COLOR: - gtk_color_chooser_set_color (cc, g_value_get_boxed (value)); - break; + case PROP_RGBA: + gtk_color_chooser_set_rgba (cc, g_value_get_boxed (value)); + break; + case PROP_USE_ALPHA: + gtk_color_editor_set_use_alpha (ce, g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +static void +gtk_color_editor_finalize (GObject *object) +{ + GtkColorEditor *editor = GTK_COLOR_EDITOR (object); + + g_clear_object (&editor->priv->h_adj); + g_clear_object (&editor->priv->s_adj); + g_clear_object (&editor->priv->v_adj); + g_clear_object (&editor->priv->a_adj); + + G_OBJECT_CLASS (gtk_color_editor_parent_class)->finalize (object); +} + static void gtk_color_editor_class_init (GtkColorEditorClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); + object_class->finalize = gtk_color_editor_finalize; object_class->get_property = gtk_color_editor_get_property; object_class->set_property = gtk_color_editor_set_property; - g_object_class_override_property (object_class, PROP_COLOR, "color"); + g_object_class_override_property (object_class, PROP_RGBA, "rgba"); + g_object_class_override_property (object_class, PROP_USE_ALPHA, "use-alpha"); g_type_class_add_private (class, sizeof (GtkColorEditorPrivate)); } static void -gtk_color_editor_get_color (GtkColorChooser *chooser, - GdkRGBA *color) +gtk_color_editor_get_rgba (GtkColorChooser *chooser, + GdkRGBA *color) { GtkColorEditor *editor = GTK_COLOR_EDITOR (chooser); + gdouble h, s, v; - color->red = editor->priv->color.red; - color->green = editor->priv->color.green; - color->blue = editor->priv->color.blue; - color->alpha = editor->priv->color.alpha; + h = gtk_adjustment_get_value (editor->priv->h_adj); + s = gtk_adjustment_get_value (editor->priv->s_adj); + v = gtk_adjustment_get_value (editor->priv->v_adj); + gtk_hsv_to_rgb (h, s, v, &color->red, &color->green, &color->blue); + color->alpha = gtk_adjustment_get_value (editor->priv->a_adj); } static void -gtk_color_editor_set_color (GtkColorChooser *chooser, - const GdkRGBA *color) +gtk_color_editor_set_rgba (GtkColorChooser *chooser, + const GdkRGBA *color) { GtkColorEditor *editor = GTK_COLOR_EDITOR (chooser); + gdouble h, s, v; - editor->priv->color.red = color->red; - editor->priv->color.green = color->green; - editor->priv->color.blue = color->blue; - gtk_rgb_to_hsv (editor->priv->color.red, - editor->priv->color.green, - editor->priv->color.blue, - &editor->priv->h, &editor->priv->s, &editor->priv->v); - hsv_to_xy (editor); - gtk_adjustment_set_value (editor->priv->h_adj, editor->priv->h); - update_entry (editor); + gtk_rgb_to_hsv (color->red, color->green, color->blue, &h, &s, &v); - editor->priv->color.alpha = color->alpha; - gtk_adjustment_set_value (editor->priv->a_adj, editor->priv->color.alpha); + gtk_adjustment_set_value (editor->priv->h_adj, h); + gtk_adjustment_set_value (editor->priv->s_adj, s); + gtk_adjustment_set_value (editor->priv->v_adj, v); + gtk_adjustment_set_value (editor->priv->a_adj, color->alpha); - gtk_widget_queue_draw (GTK_WIDGET (editor)); + gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (editor->priv->swatch), color); + gtk_color_scale_set_rgba (GTK_COLOR_SCALE (editor->priv->a_slider), color); + entry_set_rgba (editor, color); - g_object_notify (G_OBJECT (editor), "color"); + g_object_notify (G_OBJECT (editor), "rgba"); } static void gtk_color_editor_iface_init (GtkColorChooserInterface *iface) { - iface->get_color = gtk_color_editor_get_color; - iface->set_color = gtk_color_editor_set_color; + iface->get_rgba = gtk_color_editor_get_rgba; + iface->set_rgba = gtk_color_editor_set_rgba; } GtkWidget *