* 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 <http://www.gnu.org/licenses/>.
*/
/* 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 <math.h>
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);
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 ();
}
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 *