From 1fbd271d6b1ebdb203eb7286c7743717a6ded261 Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Sat, 29 Aug 2020 19:16:23 +0200 Subject: [PATCH 01/15] port color picker widget from https://github.com/RonnyDo/ColorPicker --- src/Lib/ColorPicker.vala | 321 +++++++++++++++++++++++++++++++++++++++ src/meson.build | 2 + 2 files changed, 323 insertions(+) create mode 100644 src/Lib/ColorPicker.vala diff --git a/src/Lib/ColorPicker.vala b/src/Lib/ColorPicker.vala new file mode 100644 index 000000000..cef6dab08 --- /dev/null +++ b/src/Lib/ColorPicker.vala @@ -0,0 +1,321 @@ +/* +* Copyright (c) 2019-2020 Alecaddd (https://alecaddd.com) +* +* This file is part of Akira. +* +* Akira is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. + +* Akira is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with Akira. If not, see . +* +* Authored by: Ivan "isneezy" Vilanculo +* Ported from: https://github.com/ColorPicker/RonnyDo +*/ + +public class Akira.Lib.ColorPicker : Gtk.Window { + public signal void picked (Gdk.RGBA color); + public signal void cancelled (); + public signal void moved (Gdk.RGBA color); + + const string dark_border_color_string = "#333333"; + private Gdk.RGBA dark_border_color = Gdk.RGBA(); + + const string bright_border_color_string = "#FFFFFF"; + private Gdk.RGBA bright_border_color = Gdk.RGBA(); + + + // 1. Snapsize is the amount of pixel going to be magnified by the zoomlevel. + // 2. The snapsize must be odd to have a 1px magnifier center. + // 3. Asure that snapsize*max_zoomlevel+shadow_width*2 is smaller than 2 * get_screen ().get_display ().get_maximal_cursor_size() + // Valid: snapsize = 31, max_zoomlevel = 7, shadow_width = 15 --> 247px + // get_maximal_cursor_size = 128 --> 256px + // Otherwise the cursor starts to flicker. See https://github.com/stuartlangridge/ColourPicker/issues/6#issuecomment-277972290 + // and https://github.com/RonnyDo/ColorPicker/issues/19 + int snapsize = 31; + int min_zoomlevel = 2; + int max_zoomlevel = 14; + //int max_zoomlevel = 7; + int zoomlevel = 3; + int shadow_width = 15; + + private Gdk.Cursor magnifier = null; + + construct { + app_paintable = true; + decorated = false; + resizable = false; + set_visual (get_screen ().get_rgba_visual ()); + type = Gtk.WindowType.POPUP; + } + + + public ColorPicker () { + stick (); + set_resizable (true); + set_deletable (false); + set_skip_taskbar_hint (true); + set_skip_pager_hint (true); + set_keep_above (true); + + + dark_border_color.parse (dark_border_color_string); + bright_border_color.parse (bright_border_color_string); + + // Todo remove + // restore zoomlevel + // if (settings.zoomlevel >= min_zoomlevel && settings.zoomlevel <= max_zoomlevel) { + // zoomlevel = settings.zoomlevel; + // } + + var display = Gdk.Display.get_default (); + Gdk.Monitor monitor = display.get_primary_monitor (); + Gdk.Rectangle geom = monitor.get_geometry (); + set_default_size (geom.width, geom.height); + } + + + public override bool button_release_event (Gdk.EventButton e) { + // button_1 is left mouse button + if (e.button == 1) { + Gdk.RGBA color = get_color_at ((int) e.x_root, (int) e.y_root); + picked (color); + // button_3 is right mouse button + } else if (e.button == 3) { + cancelled (); + } + + return true; + } + + + public override bool draw (Cairo.Context cr) { + return false; + } + + + public override bool motion_notify_event (Gdk.EventMotion e) { + Gdk.RGBA color = get_color_at ((int) e.x_root, (int) e.y_root); + + moved (color); + + set_magnifier_cursor (); + + return true; + } + + + public override bool scroll_event (Gdk.EventScroll e) { + switch (e.direction) { + case Gdk.ScrollDirection.UP: + if (zoomlevel < max_zoomlevel) { + zoomlevel++; + } + set_magnifier_cursor (); + break; + case Gdk.ScrollDirection.DOWN: + if (zoomlevel > min_zoomlevel) { + zoomlevel--; + } + set_magnifier_cursor (); + break; + default: + break; + } + + return true; + } + + + public void set_magnifier_cursor () { + var manager = Gdk.Display.get_default ().get_default_seat (); + + // get cursor position + int px, py; + get_window ().get_device_position (manager.get_pointer (), out px, out py, null); + + var radius = snapsize * zoomlevel / 2; + + // get a small area (snap) meant to be zoomed + var snapped_pixbuf = snap (px - snapsize / 2, py - snapsize / 2, snapsize, snapsize); + + // Zoom that screenshot up, and grab a snapsize-sized piece from the middle + var scaled_pb = snapped_pixbuf.scale_simple (snapsize * zoomlevel + shadow_width * 2 , snapsize * zoomlevel + shadow_width * 2 , Gdk.InterpType.NEAREST); + + + // Create the base surface for our cursor + var base_surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, snapsize * zoomlevel + shadow_width * 2 , snapsize * zoomlevel + shadow_width * 2); + var base_context = new Cairo.Context (base_surface); + + + // Create the circular path on our base surface + base_context.arc (radius + shadow_width, radius + shadow_width, radius, 0, 2 * Math.PI); + + // Paste in the screenshot + Gdk.cairo_set_source_pixbuf (base_context, scaled_pb, 0, 0); + + // Clip to that circular path, keeping the path around for later, and paint the pasted screenshot + base_context.save (); + base_context.clip_preserve (); + base_context.paint (); + base_context.restore (); + + + // Draw a shadow as outside magnifier border + double shadow_alpha = 0.6; + base_context.set_line_width (1); + + for (int i = 0; i <= shadow_width; i++) { + base_context.arc (radius + shadow_width, radius + shadow_width, radius + shadow_width- i, 0, 2 * Math.PI); + Gdk.RGBA shadow_color = Gdk.RGBA(); + shadow_color.parse(dark_border_color_string); + shadow_color.alpha = shadow_alpha / ((shadow_width - i + 1)*(shadow_width - i + 1)); + Gdk.cairo_set_source_rgba (base_context, shadow_color); + base_context.stroke (); + } + + + // Draw an outside bright magnifier border + Gdk.cairo_set_source_rgba (base_context, bright_border_color); + base_context.arc (radius + shadow_width, radius + shadow_width, radius - 1, 0, 2 * Math.PI); + base_context.stroke(); + + + // Draw inside square + base_context.set_line_width (1); + + Gdk.cairo_set_source_rgba (base_context, dark_border_color); + base_context.move_to (radius + shadow_width - zoomlevel, radius + shadow_width - zoomlevel); + base_context.line_to (radius + shadow_width + zoomlevel, radius + shadow_width - zoomlevel); + base_context.line_to (radius + shadow_width + zoomlevel, radius + shadow_width + zoomlevel); + base_context.line_to (radius + shadow_width - zoomlevel, radius + shadow_width + zoomlevel); + base_context.close_path (); + base_context.stroke (); + + Gdk.cairo_set_source_rgba (base_context, bright_border_color); + base_context.move_to (radius + shadow_width - zoomlevel + 1, radius + shadow_width - zoomlevel + 1); + base_context.line_to (radius + shadow_width + zoomlevel - 1, radius + shadow_width - zoomlevel + 1); + base_context.line_to (radius + shadow_width + zoomlevel - 1, radius + shadow_width + zoomlevel - 1); + base_context.line_to (radius + shadow_width - zoomlevel + 1, radius + shadow_width + zoomlevel - 1); + base_context.close_path (); + base_context.stroke (); + + + magnifier = new Gdk.Cursor.from_surface( + get_screen ().get_display (), + base_surface, + base_surface.get_width () / 2, + base_surface.get_height () / 2); + + // Set the cursor + manager.grab ( + get_window (), + Gdk.SeatCapabilities.ALL, + true, + magnifier, + new Gdk.Event (Gdk.EventType.BUTTON_PRESS | Gdk.EventType.MOTION_NOTIFY | Gdk.EventType.SCROLL), + null); + + } + + + public Gdk.Pixbuf? snap (int x, int y, int w, int h) { + var root = Gdk.get_default_root_window (); + + var screenshot = Gdk.pixbuf_get_from_window (root, x, y, w, h); + return screenshot; + } + + + public override bool key_press_event (Gdk.EventKey e) { + var manager = Gdk.Display.get_default ().get_default_seat (); + int px, py; + get_window ().get_device_position (manager.get_pointer (), out px, out py, null); + + switch (e.keyval) { + case Gdk.Key.Escape: + cancelled (); + break; + case Gdk.Key.Return: + Gdk.RGBA color = get_color_at (px, py); + picked (color); + break; + case Gdk.Key.Up: + manager.get_pointer ().warp (get_screen (), px, py - 1); + break; + case Gdk.Key.Down: + manager.get_pointer ().warp (get_screen (), px, py + 1); + break; + case Gdk.Key.Left: + manager.get_pointer ().warp (get_screen (), px - 1, py); + break; + case Gdk.Key.Right: + manager.get_pointer ().warp (get_screen (), px + 1, py); + break; + } + + return true; + } + + public Gdk.RGBA get_color_at (int x, int y) { + var root = Gdk.get_default_root_window (); + Gdk.Pixbuf? pixbuf = Gdk.pixbuf_get_from_window (root, x, y, 1, 1); + + if (pixbuf != null) { + // see https://hackage.haskell.org/package/gtk3-0.14.6/docs/Graphics-UI-Gtk-Gdk-Pixbuf.html + uint8 red = pixbuf.get_pixels()[0]; + uint8 green = pixbuf.get_pixels()[1]; + uint8 blue = pixbuf.get_pixels()[2]; + + Gdk.RGBA color = Gdk.RGBA(); + string spec = "rgb(" + red.to_string() + "," + green.to_string() + "," + blue.to_string() + ")"; + if (color.parse (spec)) { + return color; + } else { + stdout.printf("ERROR: Parse pixel rgb values failed."); + } + } + + // fallback: default RGBA color + stdout.printf("ERROR: Gdk.pixbuf_get_from_window failed"); + return Gdk.RGBA (); + } + + + public override void show_all () { + base.show_all (); + + var manager = Gdk.Display.get_default ().get_default_seat (); + var window = get_window (); + + var status = manager.grab (window, + Gdk.SeatCapabilities.ALL, + false, + new Gdk.Cursor.for_display (window.get_display (), Gdk.CursorType.CROSSHAIR), + new Gdk.Event (Gdk.EventType.BUTTON_PRESS | Gdk.EventType.BUTTON_RELEASE | Gdk.EventType.MOTION_NOTIFY), + null); + + if (status != Gdk.GrabStatus.SUCCESS) { + manager.ungrab (); + } + + // show magnifier + set_magnifier_cursor (); + } + + public new void close () { + // Todo remove + // save zoomlevel + // settings.zoomlevel = zoomlevel; + + get_window ().set_cursor (null); + base.close (); + } +} diff --git a/src/meson.build b/src/meson.build index bd5bece13..7484254a8 100644 --- a/src/meson.build +++ b/src/meson.build @@ -94,6 +94,8 @@ sources = files( 'Lib/Models/CanvasArtboard.vala', 'Lib/Selection/Nob.vala', + + 'Lib/ColorPicker.vala', ) deps = [ From f269f7fb19766b94a94d898ae18848070b9476cb Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Sun, 30 Aug 2020 11:42:45 +0200 Subject: [PATCH 02/15] prevent cursor flickering by reducing the max zoom level --- src/Lib/ColorPicker.vala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Lib/ColorPicker.vala b/src/Lib/ColorPicker.vala index cef6dab08..1945b618b 100644 --- a/src/Lib/ColorPicker.vala +++ b/src/Lib/ColorPicker.vala @@ -41,9 +41,8 @@ public class Akira.Lib.ColorPicker : Gtk.Window { // and https://github.com/RonnyDo/ColorPicker/issues/19 int snapsize = 31; int min_zoomlevel = 2; - int max_zoomlevel = 14; - //int max_zoomlevel = 7; - int zoomlevel = 3; + int max_zoomlevel = 7; + int zoomlevel = 6; int shadow_width = 15; private Gdk.Cursor magnifier = null; From bc1c4955eb570410b8103a96992cc961b7782998 Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Sun, 30 Aug 2020 11:53:33 +0200 Subject: [PATCH 03/15] add eye dropper to FillItem --- src/Layouts/Partials/FillItem.vala | 32 ++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Layouts/Partials/FillItem.vala b/src/Layouts/Partials/FillItem.vala index 7a8cb2f0b..11d697261 100644 --- a/src/Layouts/Partials/FillItem.vala +++ b/src/Layouts/Partials/FillItem.vala @@ -24,6 +24,7 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { public weak Akira.Window window { get; construct; } private Gtk.Grid fill_chooser; + private Gtk.Button eye_dropper_button; private Gtk.Button hidden_button; private Gtk.Button delete_button; private Gtk.Image hidden_button_icon; @@ -33,6 +34,7 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { private Gtk.Popover color_popover; private Gtk.Grid color_picker; private Gtk.ColorChooserWidget color_chooser_widget; + private Akira.Lib.ColorPicker eye_dropper; public Akira.Models.FillsItemModel model { get; construct; } @@ -166,6 +168,14 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { fill_chooser.attach (color_container, 1, 0, 1, 1); fill_chooser.attach (opacity_container, 2, 0, 1, 1); + eye_dropper_button = new Gtk.Button (); + eye_dropper_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + eye_dropper_button.get_style_context ().add_class ("button-rounded"); + eye_dropper_button.can_focus = false; + eye_dropper_button.valign = Gtk.Align.CENTER; + eye_dropper_button.add (new Gtk.Image.from_icon_name ("user-trash-symbolic", + Gtk.IconSize.SMALL_TOOLBAR)); + hidden_button = new Gtk.Button (); hidden_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); hidden_button.get_style_context ().add_class ("button-rounded"); @@ -191,20 +201,38 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { color_popover.add (color_picker); attach (fill_chooser, 0, 0, 1, 1); - attach (hidden_button, 1, 0, 1, 1); - attach (delete_button, 2, 0, 1, 1); + attach (eye_dropper_button, 1, 0, 1, 1); + attach (hidden_button, 2, 0, 1, 1); + attach (delete_button, 3, 0, 1, 1); set_color_chooser_color (); set_button_color (); } private void create_event_bindings () { + eye_dropper_button.clicked.connect(on_eye_dropper_click); delete_button.clicked.connect (on_delete_item); hidden_button.clicked.connect (toggle_visibility); model.notify.connect (on_model_changed); color_chooser_widget.notify["rgba"].connect (on_color_changed); } + private void on_eye_dropper_click () { + eye_dropper = new Akira.Lib.ColorPicker (); + eye_dropper.show_all(); + + eye_dropper.picked.connect((picked_color) => { + alpha = ((int)(picked_color.alpha * 255)); + color_chooser_widget.set_rgba (picked_color); + on_color_changed(); + eye_dropper.close (); + }); + + eye_dropper.cancelled.connect(() => { + eye_dropper.close (); + }); + } + private void on_model_changed () { model.item.reset_colors (); set_button_color (); From 9c8c5a430e1f72f12c3a81b293f7499572d60777 Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Sun, 30 Aug 2020 12:33:29 +0200 Subject: [PATCH 04/15] fix linting issues --- src/Layouts/Partials/FillItem.vala | 12 +-- src/Lib/ColorPicker.vala | 138 ++++++++++++++++------------- 2 files changed, 81 insertions(+), 69 deletions(-) diff --git a/src/Layouts/Partials/FillItem.vala b/src/Layouts/Partials/FillItem.vala index 11d697261..9a90545c4 100644 --- a/src/Layouts/Partials/FillItem.vala +++ b/src/Layouts/Partials/FillItem.vala @@ -210,7 +210,7 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { } private void create_event_bindings () { - eye_dropper_button.clicked.connect(on_eye_dropper_click); + eye_dropper_button.clicked.connect (on_eye_dropper_click); delete_button.clicked.connect (on_delete_item); hidden_button.clicked.connect (toggle_visibility); model.notify.connect (on_model_changed); @@ -219,16 +219,16 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { private void on_eye_dropper_click () { eye_dropper = new Akira.Lib.ColorPicker (); - eye_dropper.show_all(); + eye_dropper.show_all (); - eye_dropper.picked.connect((picked_color) => { + eye_dropper.picked.connect ((picked_color) => { alpha = ((int)(picked_color.alpha * 255)); color_chooser_widget.set_rgba (picked_color); - on_color_changed(); + on_color_changed (); eye_dropper.close (); }); - - eye_dropper.cancelled.connect(() => { + + eye_dropper.cancelled.connect (() => { eye_dropper.close (); }); } diff --git a/src/Lib/ColorPicker.vala b/src/Lib/ColorPicker.vala index 1945b618b..3127ed23a 100644 --- a/src/Lib/ColorPicker.vala +++ b/src/Lib/ColorPicker.vala @@ -25,11 +25,11 @@ public class Akira.Lib.ColorPicker : Gtk.Window { public signal void cancelled (); public signal void moved (Gdk.RGBA color); - const string dark_border_color_string = "#333333"; - private Gdk.RGBA dark_border_color = Gdk.RGBA(); + const string DARK_BORDER_COLOR_STRING = "#333333"; + private Gdk.RGBA dark_border_color = Gdk.RGBA (); - const string bright_border_color_string = "#FFFFFF"; - private Gdk.RGBA bright_border_color = Gdk.RGBA(); + const string BRIGHT_BORDER_COLOR_STRING = "#FFFFFF"; + private Gdk.RGBA bright_border_color = Gdk.RGBA (); // 1. Snapsize is the amount of pixel going to be magnified by the zoomlevel. @@ -65,10 +65,10 @@ public class Akira.Lib.ColorPicker : Gtk.Window { set_keep_above (true); - dark_border_color.parse (dark_border_color_string); - bright_border_color.parse (bright_border_color_string); + dark_border_color.parse (DARK_BORDER_COLOR_STRING); + bright_border_color.parse (BRIGHT_BORDER_COLOR_STRING); - // Todo remove + // TODO remove the zoom level restauration if we do not need it // restore zoomlevel // if (settings.zoomlevel >= min_zoomlevel && settings.zoomlevel <= max_zoomlevel) { // zoomlevel = settings.zoomlevel; @@ -91,7 +91,7 @@ public class Akira.Lib.ColorPicker : Gtk.Window { cancelled (); } - return true; + return true; } @@ -100,7 +100,7 @@ public class Akira.Lib.ColorPicker : Gtk.Window { } - public override bool motion_notify_event (Gdk.EventMotion e) { + public override bool motion_notify_event (Gdk.EventMotion e) { Gdk.RGBA color = get_color_at ((int) e.x_root, (int) e.y_root); moved (color); @@ -111,7 +111,7 @@ public class Akira.Lib.ColorPicker : Gtk.Window { } - public override bool scroll_event (Gdk.EventScroll e) { + public override bool scroll_event (Gdk.EventScroll e) { switch (e.direction) { case Gdk.ScrollDirection.UP: if (zoomlevel < max_zoomlevel) { @@ -128,11 +128,10 @@ public class Akira.Lib.ColorPicker : Gtk.Window { default: break; } - + return true; - } - - + } + public void set_magnifier_cursor () { var manager = Gdk.Display.get_default ().get_default_seat (); @@ -146,20 +145,29 @@ public class Akira.Lib.ColorPicker : Gtk.Window { var snapped_pixbuf = snap (px - snapsize / 2, py - snapsize / 2, snapsize, snapsize); // Zoom that screenshot up, and grab a snapsize-sized piece from the middle - var scaled_pb = snapped_pixbuf.scale_simple (snapsize * zoomlevel + shadow_width * 2 , snapsize * zoomlevel + shadow_width * 2 , Gdk.InterpType.NEAREST); + var scaled_pb = snapped_pixbuf.scale_simple ( + snapsize * zoomlevel + shadow_width * 2 , + snapsize * zoomlevel + shadow_width * 2 , + Gdk.InterpType.NEAREST + ); // Create the base surface for our cursor - var base_surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, snapsize * zoomlevel + shadow_width * 2 , snapsize * zoomlevel + shadow_width * 2); + var base_surface = new Cairo.ImageSurface ( + Cairo.Format.ARGB32, + snapsize * zoomlevel + shadow_width * 2 , + snapsize * zoomlevel + shadow_width * 2 + ); + var base_context = new Cairo.Context (base_surface); // Create the circular path on our base surface base_context.arc (radius + shadow_width, radius + shadow_width, radius, 0, 2 * Math.PI); - + // Paste in the screenshot Gdk.cairo_set_source_pixbuf (base_context, scaled_pb, 0, 0); - + // Clip to that circular path, keeping the path around for later, and paint the pasted screenshot base_context.save (); base_context.clip_preserve (); @@ -168,37 +176,40 @@ public class Akira.Lib.ColorPicker : Gtk.Window { // Draw a shadow as outside magnifier border - double shadow_alpha = 0.6; + double shadow_alpha = 0.6; base_context.set_line_width (1); - + for (int i = 0; i <= shadow_width; i++) { - base_context.arc (radius + shadow_width, radius + shadow_width, radius + shadow_width- i, 0, 2 * Math.PI); - Gdk.RGBA shadow_color = Gdk.RGBA(); - shadow_color.parse(dark_border_color_string); - shadow_color.alpha = shadow_alpha / ((shadow_width - i + 1)*(shadow_width - i + 1)); - Gdk.cairo_set_source_rgba (base_context, shadow_color); + base_context.arc ( + radius + shadow_width, radius + shadow_width, + radius + shadow_width - i, 0, 2 * Math.PI + ); + Gdk.RGBA shadow_color = Gdk.RGBA (); + shadow_color.parse (DARK_BORDER_COLOR_STRING); + shadow_color.alpha = shadow_alpha / ((shadow_width - i + 1) * (shadow_width - i + 1)); + Gdk.cairo_set_source_rgba (base_context, shadow_color); base_context.stroke (); } - - + + // Draw an outside bright magnifier border Gdk.cairo_set_source_rgba (base_context, bright_border_color); base_context.arc (radius + shadow_width, radius + shadow_width, radius - 1, 0, 2 * Math.PI); - base_context.stroke(); + base_context.stroke (); // Draw inside square base_context.set_line_width (1); - - Gdk.cairo_set_source_rgba (base_context, dark_border_color); + + Gdk.cairo_set_source_rgba (base_context, dark_border_color); base_context.move_to (radius + shadow_width - zoomlevel, radius + shadow_width - zoomlevel); base_context.line_to (radius + shadow_width + zoomlevel, radius + shadow_width - zoomlevel); base_context.line_to (radius + shadow_width + zoomlevel, radius + shadow_width + zoomlevel); base_context.line_to (radius + shadow_width - zoomlevel, radius + shadow_width + zoomlevel); base_context.close_path (); - base_context.stroke (); + base_context.stroke (); - Gdk.cairo_set_source_rgba (base_context, bright_border_color); + Gdk.cairo_set_source_rgba (base_context, bright_border_color); base_context.move_to (radius + shadow_width - zoomlevel + 1, radius + shadow_width - zoomlevel + 1); base_context.line_to (radius + shadow_width + zoomlevel - 1, radius + shadow_width - zoomlevel + 1); base_context.line_to (radius + shadow_width + zoomlevel - 1, radius + shadow_width + zoomlevel - 1); @@ -207,10 +218,10 @@ public class Akira.Lib.ColorPicker : Gtk.Window { base_context.stroke (); - magnifier = new Gdk.Cursor.from_surface( - get_screen ().get_display (), + magnifier = new Gdk.Cursor.from_surface ( + get_screen ().get_display (), base_surface, - base_surface.get_width () / 2, + base_surface.get_width () / 2, base_surface.get_height () / 2); // Set the cursor @@ -222,13 +233,13 @@ public class Akira.Lib.ColorPicker : Gtk.Window { new Gdk.Event (Gdk.EventType.BUTTON_PRESS | Gdk.EventType.MOTION_NOTIFY | Gdk.EventType.SCROLL), null); - } - - + } + + public Gdk.Pixbuf? snap (int x, int y, int w, int h) { var root = Gdk.get_default_root_window (); - - var screenshot = Gdk.pixbuf_get_from_window (root, x, y, w, h); + + var screenshot = Gdk.pixbuf_get_from_window (root, x, y, w, h); return screenshot; } @@ -259,47 +270,48 @@ public class Akira.Lib.ColorPicker : Gtk.Window { manager.get_pointer ().warp (get_screen (), px + 1, py); break; } - - return true; + + return true; } public Gdk.RGBA get_color_at (int x, int y) { var root = Gdk.get_default_root_window (); Gdk.Pixbuf? pixbuf = Gdk.pixbuf_get_from_window (root, x, y, 1, 1); - if (pixbuf != null) { + if (pixbuf != null) { // see https://hackage.haskell.org/package/gtk3-0.14.6/docs/Graphics-UI-Gtk-Gdk-Pixbuf.html - uint8 red = pixbuf.get_pixels()[0]; - uint8 green = pixbuf.get_pixels()[1]; - uint8 blue = pixbuf.get_pixels()[2]; - - Gdk.RGBA color = Gdk.RGBA(); - string spec = "rgb(" + red.to_string() + "," + green.to_string() + "," + blue.to_string() + ")"; + uint8 red = pixbuf.get_pixels ()[0]; + uint8 green = pixbuf.get_pixels ()[1]; + uint8 blue = pixbuf.get_pixels ()[2]; + + Gdk.RGBA color = Gdk.RGBA (); + string spec = "rgb(" + red.to_string () + "," + green.to_string () + "," + blue.to_string () + ")"; if (color.parse (spec)) { - return color; + return color; } else { - stdout.printf("ERROR: Parse pixel rgb values failed."); - } + stdout.printf ("ERROR: Parse pixel rgb values failed."); + } } - + // fallback: default RGBA color - stdout.printf("ERROR: Gdk.pixbuf_get_from_window failed"); + stdout.printf ("ERROR: Gdk.pixbuf_get_from_window failed"); return Gdk.RGBA (); } public override void show_all () { - base.show_all (); + base.show_all (); var manager = Gdk.Display.get_default ().get_default_seat (); var window = get_window (); - var status = manager.grab (window, - Gdk.SeatCapabilities.ALL, - false, - new Gdk.Cursor.for_display (window.get_display (), Gdk.CursorType.CROSSHAIR), - new Gdk.Event (Gdk.EventType.BUTTON_PRESS | Gdk.EventType.BUTTON_RELEASE | Gdk.EventType.MOTION_NOTIFY), - null); + var status = manager.grab ( + window, + Gdk.SeatCapabilities.ALL, + false, + new Gdk.Cursor.for_display (window.get_display (), Gdk.CursorType.CROSSHAIR), + new Gdk.Event (Gdk.EventType.BUTTON_PRESS | Gdk.EventType.BUTTON_RELEASE | Gdk.EventType.MOTION_NOTIFY), + null); if (status != Gdk.GrabStatus.SUCCESS) { manager.ungrab (); @@ -307,10 +319,10 @@ public class Akira.Lib.ColorPicker : Gtk.Window { // show magnifier set_magnifier_cursor (); - } + } public new void close () { - // Todo remove + // TODO remove the zoom level saving if we do not need it // save zoomlevel // settings.zoomlevel = zoomlevel; From ddbda16a3fc55e7f44bd7880dea22b1359da61e9 Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Mon, 31 Aug 2020 10:19:37 +0200 Subject: [PATCH 05/15] update eyedropper button icon and add a tooltip --- src/Layouts/Partials/FillItem.vala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Layouts/Partials/FillItem.vala b/src/Layouts/Partials/FillItem.vala index 9a90545c4..1cb31bbcd 100644 --- a/src/Layouts/Partials/FillItem.vala +++ b/src/Layouts/Partials/FillItem.vala @@ -173,7 +173,8 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { eye_dropper_button.get_style_context ().add_class ("button-rounded"); eye_dropper_button.can_focus = false; eye_dropper_button.valign = Gtk.Align.CENTER; - eye_dropper_button.add (new Gtk.Image.from_icon_name ("user-trash-symbolic", + eye_dropper_button.set_tooltip_text (_("Pick color")); + eye_dropper_button.add (new Gtk.Image.from_icon_name ("preferences-color-symbolic", Gtk.IconSize.SMALL_TOOLBAR)); hidden_button = new Gtk.Button (); From 43ced46240cca0bee9713c4d104563a98dff0af5 Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Mon, 31 Aug 2020 10:20:09 +0200 Subject: [PATCH 06/15] fix typo --- src/Layouts/Partials/FillItem.vala | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Layouts/Partials/FillItem.vala b/src/Layouts/Partials/FillItem.vala index 1cb31bbcd..3d0a7e3a6 100644 --- a/src/Layouts/Partials/FillItem.vala +++ b/src/Layouts/Partials/FillItem.vala @@ -24,7 +24,7 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { public weak Akira.Window window { get; construct; } private Gtk.Grid fill_chooser; - private Gtk.Button eye_dropper_button; + private Gtk.Button eyedropper_button; private Gtk.Button hidden_button; private Gtk.Button delete_button; private Gtk.Image hidden_button_icon; @@ -34,7 +34,7 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { private Gtk.Popover color_popover; private Gtk.Grid color_picker; private Gtk.ColorChooserWidget color_chooser_widget; - private Akira.Lib.ColorPicker eye_dropper; + private Akira.Lib.ColorPicker eyedropper; public Akira.Models.FillsItemModel model { get; construct; } @@ -168,13 +168,13 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { fill_chooser.attach (color_container, 1, 0, 1, 1); fill_chooser.attach (opacity_container, 2, 0, 1, 1); - eye_dropper_button = new Gtk.Button (); - eye_dropper_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); - eye_dropper_button.get_style_context ().add_class ("button-rounded"); - eye_dropper_button.can_focus = false; - eye_dropper_button.valign = Gtk.Align.CENTER; - eye_dropper_button.set_tooltip_text (_("Pick color")); - eye_dropper_button.add (new Gtk.Image.from_icon_name ("preferences-color-symbolic", + eyedropper_button = new Gtk.Button (); + eyedropper_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + eyedropper_button.get_style_context ().add_class ("button-rounded"); + eyedropper_button.can_focus = false; + eyedropper_button.valign = Gtk.Align.CENTER; + eyedropper_button.set_tooltip_text (_("Pick color")); + eyedropper_button.add (new Gtk.Image.from_icon_name ("preferences-color-symbolic", Gtk.IconSize.SMALL_TOOLBAR)); hidden_button = new Gtk.Button (); @@ -202,7 +202,7 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { color_popover.add (color_picker); attach (fill_chooser, 0, 0, 1, 1); - attach (eye_dropper_button, 1, 0, 1, 1); + attach (eyedropper_button, 1, 0, 1, 1); attach (hidden_button, 2, 0, 1, 1); attach (delete_button, 3, 0, 1, 1); @@ -211,26 +211,26 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { } private void create_event_bindings () { - eye_dropper_button.clicked.connect (on_eye_dropper_click); + eyedropper_button.clicked.connect (on_eyedropper_click); delete_button.clicked.connect (on_delete_item); hidden_button.clicked.connect (toggle_visibility); model.notify.connect (on_model_changed); color_chooser_widget.notify["rgba"].connect (on_color_changed); } - private void on_eye_dropper_click () { - eye_dropper = new Akira.Lib.ColorPicker (); - eye_dropper.show_all (); + private void on_eyedropper_click () { + eyedropper = new Akira.Lib.ColorPicker (); + eyedropper.show_all (); - eye_dropper.picked.connect ((picked_color) => { + eyedropper.picked.connect ((picked_color) => { alpha = ((int)(picked_color.alpha * 255)); color_chooser_widget.set_rgba (picked_color); on_color_changed (); - eye_dropper.close (); + eyedropper.close (); }); - eye_dropper.cancelled.connect (() => { - eye_dropper.close (); + eyedropper.cancelled.connect (() => { + eyedropper.close (); }); } From aba5adc382b966a12156030cd1a6e31229bc4976 Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Mon, 31 Aug 2020 10:30:45 +0200 Subject: [PATCH 07/15] move CorlorPicker to utils --- src/Layouts/Partials/FillItem.vala | 4 ++-- src/{Lib => Utils}/ColorPicker.vala | 2 +- src/meson.build | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/{Lib => Utils}/ColorPicker.vala (99%) diff --git a/src/Layouts/Partials/FillItem.vala b/src/Layouts/Partials/FillItem.vala index 3d0a7e3a6..7b181f9a5 100644 --- a/src/Layouts/Partials/FillItem.vala +++ b/src/Layouts/Partials/FillItem.vala @@ -34,7 +34,7 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { private Gtk.Popover color_popover; private Gtk.Grid color_picker; private Gtk.ColorChooserWidget color_chooser_widget; - private Akira.Lib.ColorPicker eyedropper; + private Akira.Utils.ColorPicker eyedropper; public Akira.Models.FillsItemModel model { get; construct; } @@ -219,7 +219,7 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { } private void on_eyedropper_click () { - eyedropper = new Akira.Lib.ColorPicker (); + eyedropper = new Akira.Utils.ColorPicker (); eyedropper.show_all (); eyedropper.picked.connect ((picked_color) => { diff --git a/src/Lib/ColorPicker.vala b/src/Utils/ColorPicker.vala similarity index 99% rename from src/Lib/ColorPicker.vala rename to src/Utils/ColorPicker.vala index 3127ed23a..90165bc65 100644 --- a/src/Lib/ColorPicker.vala +++ b/src/Utils/ColorPicker.vala @@ -20,7 +20,7 @@ * Ported from: https://github.com/ColorPicker/RonnyDo */ -public class Akira.Lib.ColorPicker : Gtk.Window { +public class Akira.Utils.ColorPicker : Gtk.Window { public signal void picked (Gdk.RGBA color); public signal void cancelled (); public signal void moved (Gdk.RGBA color); diff --git a/src/meson.build b/src/meson.build index 7484254a8..f247a5672 100644 --- a/src/meson.build +++ b/src/meson.build @@ -94,8 +94,8 @@ sources = files( 'Lib/Models/CanvasArtboard.vala', 'Lib/Selection/Nob.vala', - - 'Lib/ColorPicker.vala', + + 'Utils/ColorPicker.vala', ) deps = [ From e0063c553c12b5d3805150ac026234d6c17047c4 Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Mon, 31 Aug 2020 11:18:56 +0200 Subject: [PATCH 08/15] add eyedropper to border item --- src/Layouts/Partials/BorderItem.vala | 34 ++++++++++++++++++++++++++-- src/Layouts/Partials/FillItem.vala | 1 + 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/Layouts/Partials/BorderItem.vala b/src/Layouts/Partials/BorderItem.vala index 388c59b4f..2f1460c68 100644 --- a/src/Layouts/Partials/BorderItem.vala +++ b/src/Layouts/Partials/BorderItem.vala @@ -17,12 +17,14 @@ * along with Akira. If not, see . * * Authored by: Alessandro "alecaddd" Castellani + * Authored by: Ivan "isneezy" Vilanculo */ public class Akira.Layouts.Partials.BorderItem : Gtk.Grid { public weak Akira.Window window { get; construct; } private Gtk.Grid color_chooser; + private Gtk.Button eyedropper_button; private Gtk.Button hidden_button; private Gtk.Button delete_button; private Gtk.Image hidden_button_icon; @@ -31,6 +33,7 @@ public class Akira.Layouts.Partials.BorderItem : Gtk.Grid { public Akira.Partials.ColorField color_container; private Gtk.Popover color_popover; private Gtk.Grid color_picker; + private Akira.Utils.ColorPicker eyedropper; private Gtk.ColorChooserWidget color_chooser_widget; public Akira.Models.BordersItemModel model { get; construct; } @@ -164,6 +167,15 @@ public class Akira.Layouts.Partials.BorderItem : Gtk.Grid { color_chooser.attach (color_container, 1, 0, 1, 1); color_chooser.attach (tickness_container, 2, 0, 1, 1); + eyedropper_button = new Gtk.Button (); + eyedropper_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + eyedropper_button.get_style_context ().add_class ("button-rounded"); + eyedropper_button.can_focus = false; + eyedropper_button.valign = Gtk.Align.CENTER; + eyedropper_button.set_tooltip_text (_("Pick color")); + eyedropper_button.add (new Gtk.Image.from_icon_name ("preferences-color-symbolic", + Gtk.IconSize.SMALL_TOOLBAR)); + hidden_button = new Gtk.Button (); hidden_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); hidden_button.get_style_context ().add_class ("button-rounded"); @@ -189,19 +201,37 @@ public class Akira.Layouts.Partials.BorderItem : Gtk.Grid { color_popover.add (color_picker); attach (color_chooser, 0, 0, 1, 1); - attach (hidden_button, 1, 0, 1, 1); - attach (delete_button, 2, 0, 1, 1); + attach (eyedropper_button, 1, 0, 1, 1); + attach (hidden_button, 2, 0, 1, 1); + attach (delete_button, 3, 0, 1, 1); set_color_chooser_color (); set_button_color (); } private void create_event_bindings () { + eyedropper_button.clicked.connect (on_eyedropper_click); delete_button.clicked.connect (on_delete_item); hidden_button.clicked.connect (toggle_visibility); model.notify.connect (on_model_changed); color_chooser_widget.notify["rgba"].connect (on_color_changed); } + + private void on_eyedropper_click () { + eyedropper = new Akira.Utils.ColorPicker (); + eyedropper.show_all (); + + eyedropper.picked.connect ((picked_color) => { + alpha = ((int)(picked_color.alpha * 255)); + color_chooser_widget.set_rgba (picked_color); + on_color_changed (); + eyedropper.close (); + }); + + eyedropper.cancelled.connect (() => { + eyedropper.close (); + }); + } private void on_model_changed () { model.item.reset_colors (); diff --git a/src/Layouts/Partials/FillItem.vala b/src/Layouts/Partials/FillItem.vala index 7b181f9a5..71388c5ae 100644 --- a/src/Layouts/Partials/FillItem.vala +++ b/src/Layouts/Partials/FillItem.vala @@ -18,6 +18,7 @@ * * Authored by: Giacomo "giacomoalbe" Alberini * Authored by: Alessandro "alecaddd" Castellani + * Authored by: Ivan "isneezy" Vilanculo */ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { From 6818ffa896f7af358c8bab208342b58377659494 Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Mon, 31 Aug 2020 11:22:07 +0200 Subject: [PATCH 09/15] fix lint issues --- src/Layouts/Partials/BorderItem.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Layouts/Partials/BorderItem.vala b/src/Layouts/Partials/BorderItem.vala index 2f1460c68..5a7158ffe 100644 --- a/src/Layouts/Partials/BorderItem.vala +++ b/src/Layouts/Partials/BorderItem.vala @@ -216,7 +216,7 @@ public class Akira.Layouts.Partials.BorderItem : Gtk.Grid { model.notify.connect (on_model_changed); color_chooser_widget.notify["rgba"].connect (on_color_changed); } - + private void on_eyedropper_click () { eyedropper = new Akira.Utils.ColorPicker (); eyedropper.show_all (); From adc343d9324286e228c2285076e8201c04c7e488 Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Tue, 1 Sep 2020 09:56:01 +0200 Subject: [PATCH 10/15] [WIP] color picker global action --- src/Services/ActionManager.vala | 43 +++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/Services/ActionManager.vala b/src/Services/ActionManager.vala index bf0b50290..53519f0a1 100644 --- a/src/Services/ActionManager.vala +++ b/src/Services/ActionManager.vala @@ -17,6 +17,7 @@ * along with Akira. If not, see . * * Authored by: Alessandro "Alecaddd" Castellani +* Authored by: Ivan "isneezy" Vilanculo */ public class Akira.Services.ActionManager : Object { @@ -64,6 +65,7 @@ public class Akira.Services.ActionManager : Object { public const string ACTION_FLIP_V = "action_flip_v"; public const string ACTION_ESCAPE = "action_escape"; public const string ACTION_SHORTCUTS = "action_shortcuts"; + public const string ACTION_PICK_COLOR = "action_pick_color"; public static Gee.MultiMap action_accelerators = new Gee.HashMultiMap (); public static Gee.MultiMap typing_accelerators = new Gee.HashMultiMap (); @@ -101,6 +103,7 @@ public class Akira.Services.ActionManager : Object { { ACTION_FLIP_V, action_flip_v }, { ACTION_ESCAPE, action_escape }, { ACTION_SHORTCUTS, action_shortcuts }, + { ACTION_PICK_COLOR, action_pick_color }, }; public ActionManager (Akira.Application akira_app, Akira.Window window) { @@ -137,6 +140,7 @@ public class Akira.Services.ActionManager : Object { action_accelerators.set (ACTION_FLIP_H, "bracketleft"); action_accelerators.set (ACTION_FLIP_V, "bracketright"); action_accelerators.set (ACTION_SHORTCUTS, "F1"); + action_accelerators.set (ACTION_PICK_COLOR, "c"); typing_accelerators.set (ACTION_ESCAPE, "Escape"); typing_accelerators.set (ACTION_ARTBOARD_TOOL, "a"); @@ -452,6 +456,45 @@ public class Akira.Services.ActionManager : Object { dialog.present (); } + private void action_pick_color () { + if (window.main_window.main_canvas.canvas.selected_bound_manager.selected_items.length() == 0) { + return; + } + bool is_holding_shift = false; + var color_picker = new Akira.Utils.ColorPicker (); + color_picker.show_all(); + color_picker.key_press_event.connect((e) => { + if (e.keyval == Gdk.Key.Shift_R) { + is_holding_shift = true; + } + return true; + }); + color_picker.key_release_event.connect((e) => { + if (e.keyval == Gdk.Key.Shift_R) { + is_holding_shift = false; + } + return true; + }); + color_picker.cancelled.connect(() => { + color_picker.close(); + }); + color_picker.picked.connect(color => { + if (!is_holding_shift) { + // TODO change current selected shape fill color + for (int i = 0; i <= window.main_window.main_canvas.canvas.selected_bound_manager.selected_items.length (); i ++) { + var item = window.main_window.main_canvas.canvas.selected_bound_manager.selected_items.nth_data(i); + if(item != null) { + item.color = color; + } + } + } else { + // TODO change current selected shape border color + // window.main_window.main_canvas.canvas.selected_bound_manager.delete_selection (); + } + color_picker.close(); + }); + } + public static void action_from_group (string action_name, ActionGroup? action_group) { action_group.activate_action (action_name, null); } From f6b1a1c7592985b99db43f7c90e24d470a1f3d95 Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Wed, 2 Sep 2020 17:14:37 +0200 Subject: [PATCH 11/15] remove unnecessary method calls --- src/Layouts/Partials/BorderItem.vala | 2 -- src/Layouts/Partials/FillItem.vala | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/Layouts/Partials/BorderItem.vala b/src/Layouts/Partials/BorderItem.vala index 5f33bbf38..d495f937e 100644 --- a/src/Layouts/Partials/BorderItem.vala +++ b/src/Layouts/Partials/BorderItem.vala @@ -224,9 +224,7 @@ public class Akira.Layouts.Partials.BorderItem : Gtk.Grid { eyedropper.show_all (); eyedropper.picked.connect ((picked_color) => { - alpha = ((int)(picked_color.alpha * 255)); color_chooser_widget.set_rgba (picked_color); - on_color_changed (); eyedropper.close (); }); diff --git a/src/Layouts/Partials/FillItem.vala b/src/Layouts/Partials/FillItem.vala index 52876db48..234537870 100644 --- a/src/Layouts/Partials/FillItem.vala +++ b/src/Layouts/Partials/FillItem.vala @@ -226,9 +226,7 @@ public class Akira.Layouts.Partials.FillItem : Gtk.Grid { eyedropper.show_all (); eyedropper.picked.connect ((picked_color) => { - alpha = ((int)(picked_color.alpha * 255)); color_chooser_widget.set_rgba (picked_color); - on_color_changed (); eyedropper.close (); }); From f798c7636c3c8c1cc13638f6e5bd9955ede2d767 Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Wed, 2 Sep 2020 17:16:38 +0200 Subject: [PATCH 12/15] implement color picker global action --- src/Services/ActionManager.vala | 46 ++++++++++++++++----------------- src/Utils/Color.vala | 4 +++ 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/Services/ActionManager.vala b/src/Services/ActionManager.vala index 53519f0a1..727a4a522 100644 --- a/src/Services/ActionManager.vala +++ b/src/Services/ActionManager.vala @@ -457,41 +457,39 @@ public class Akira.Services.ActionManager : Object { } private void action_pick_color () { - if (window.main_window.main_canvas.canvas.selected_bound_manager.selected_items.length() == 0) { + weak Akira.Lib.Canvas canvas = window.main_window.main_canvas.canvas; + // Interrupt if no item is selected. + if (canvas.selected_bound_manager.selected_items.length () == 0) { return; } + foreach (var item in canvas.selected_bound_manager.selected_items) { + // Hide the ghost bound manager. + item.bounds_manager.hide (); + } bool is_holding_shift = false; var color_picker = new Akira.Utils.ColorPicker (); - color_picker.show_all(); - color_picker.key_press_event.connect((e) => { - if (e.keyval == Gdk.Key.Shift_R) { - is_holding_shift = true; - } + color_picker.show_all (); + color_picker.key_press_event.connect (e => { + is_holding_shift = e.keyval == Gdk.Key.Shift_L; return true; }); - color_picker.key_release_event.connect((e) => { - if (e.keyval == Gdk.Key.Shift_R) { - is_holding_shift = false; - } + color_picker.key_release_event.connect (e => { + is_holding_shift = e.keyval == Gdk.Key.Shift_L; return true; }); - color_picker.cancelled.connect(() => { - color_picker.close(); + color_picker.cancelled.connect (() => { + color_picker.close (); }); - color_picker.picked.connect(color => { - if (!is_holding_shift) { - // TODO change current selected shape fill color - for (int i = 0; i <= window.main_window.main_canvas.canvas.selected_bound_manager.selected_items.length (); i ++) { - var item = window.main_window.main_canvas.canvas.selected_bound_manager.selected_items.nth_data(i); - if(item != null) { - item.color = color; - } + color_picker.picked.connect (color => { + foreach (var item in canvas.selected_bound_manager.selected_items) { + if (is_holding_shift) { + item.border_color_string = Utils.Color.rgba_to_hex_string (color); + } else { + item.color_string = Utils.Color.rgba_to_hex_string (color); } - } else { - // TODO change current selected shape border color - // window.main_window.main_canvas.canvas.selected_bound_manager.delete_selection (); + item.load_colors (); } - color_picker.close(); + color_picker.close (); }); } diff --git a/src/Utils/Color.vala b/src/Utils/Color.vala index becd7ed38..0250b55fb 100644 --- a/src/Utils/Color.vala +++ b/src/Utils/Color.vala @@ -24,6 +24,10 @@ public class Akira.Utils.Color : Object { var rgba = Gdk.RGBA (); rgba.parse (rgba_string); + return rgba_to_hex_string (rgba); + } + + public static string rgba_to_hex_string (Gdk.RGBA rgba) { return "#%02x%02x%02x".printf ( (int) (rgba.red * 255), (int) (rgba.green * 255), From d444ddd27e57f0a1ea1333d4176d61d57bd1e79b Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Wed, 2 Sep 2020 17:17:17 +0200 Subject: [PATCH 13/15] fix color picker licence and indentation --- src/Utils/ColorPicker.vala | 506 ++++++++++++++++++------------------- 1 file changed, 253 insertions(+), 253 deletions(-) diff --git a/src/Utils/ColorPicker.vala b/src/Utils/ColorPicker.vala index 90165bc65..282c0a0cc 100644 --- a/src/Utils/ColorPicker.vala +++ b/src/Utils/ColorPicker.vala @@ -1,5 +1,5 @@ /* -* Copyright (c) 2019-2020 Alecaddd (https://alecaddd.com) +* Copyright (c) 2020 Alecaddd (https://alecaddd.com) * * This file is part of Akira. * @@ -21,312 +21,312 @@ */ public class Akira.Utils.ColorPicker : Gtk.Window { - public signal void picked (Gdk.RGBA color); - public signal void cancelled (); - public signal void moved (Gdk.RGBA color); - - const string DARK_BORDER_COLOR_STRING = "#333333"; - private Gdk.RGBA dark_border_color = Gdk.RGBA (); - - const string BRIGHT_BORDER_COLOR_STRING = "#FFFFFF"; - private Gdk.RGBA bright_border_color = Gdk.RGBA (); - - - // 1. Snapsize is the amount of pixel going to be magnified by the zoomlevel. - // 2. The snapsize must be odd to have a 1px magnifier center. - // 3. Asure that snapsize*max_zoomlevel+shadow_width*2 is smaller than 2 * get_screen ().get_display ().get_maximal_cursor_size() - // Valid: snapsize = 31, max_zoomlevel = 7, shadow_width = 15 --> 247px - // get_maximal_cursor_size = 128 --> 256px - // Otherwise the cursor starts to flicker. See https://github.com/stuartlangridge/ColourPicker/issues/6#issuecomment-277972290 - // and https://github.com/RonnyDo/ColorPicker/issues/19 - int snapsize = 31; - int min_zoomlevel = 2; - int max_zoomlevel = 7; - int zoomlevel = 6; - int shadow_width = 15; - - private Gdk.Cursor magnifier = null; - - construct { - app_paintable = true; - decorated = false; - resizable = false; - set_visual (get_screen ().get_rgba_visual ()); - type = Gtk.WindowType.POPUP; - } - - - public ColorPicker () { - stick (); - set_resizable (true); - set_deletable (false); - set_skip_taskbar_hint (true); - set_skip_pager_hint (true); - set_keep_above (true); - - - dark_border_color.parse (DARK_BORDER_COLOR_STRING); - bright_border_color.parse (BRIGHT_BORDER_COLOR_STRING); - - // TODO remove the zoom level restauration if we do not need it - // restore zoomlevel - // if (settings.zoomlevel >= min_zoomlevel && settings.zoomlevel <= max_zoomlevel) { - // zoomlevel = settings.zoomlevel; - // } - - var display = Gdk.Display.get_default (); - Gdk.Monitor monitor = display.get_primary_monitor (); - Gdk.Rectangle geom = monitor.get_geometry (); - set_default_size (geom.width, geom.height); + public signal void picked (Gdk.RGBA color); + public signal void cancelled (); + public signal void moved (Gdk.RGBA color); + + const string DARK_BORDER_COLOR_STRING = "#333333"; + private Gdk.RGBA dark_border_color = Gdk.RGBA (); + + const string BRIGHT_BORDER_COLOR_STRING = "#FFFFFF"; + private Gdk.RGBA bright_border_color = Gdk.RGBA (); + + + // 1. Snapsize is the amount of pixel going to be magnified by the zoomlevel. + // 2. The snapsize must be odd to have a 1px magnifier center. + // 3. Asure that snapsize*max_zoomlevel+shadow_width*2 is smaller than 2 * get_screen ().get_display ().get_maximal_cursor_size() + // Valid: snapsize = 31, max_zoomlevel = 7, shadow_width = 15 --> 247px + // get_maximal_cursor_size = 128 --> 256px + // Otherwise the cursor starts to flicker. See https://github.com/stuartlangridge/ColourPicker/issues/6#issuecomment-277972290 + // and https://github.com/RonnyDo/ColorPicker/issues/19 + int snapsize = 31; + int min_zoomlevel = 2; + int max_zoomlevel = 7; + int zoomlevel = 6; + int shadow_width = 15; + + private Gdk.Cursor magnifier = null; + + construct { + app_paintable = true; + decorated = false; + resizable = false; + set_visual (get_screen ().get_rgba_visual ()); + type = Gtk.WindowType.POPUP; + } + + + public ColorPicker () { + stick (); + set_resizable (true); + set_deletable (false); + set_skip_taskbar_hint (true); + set_skip_pager_hint (true); + set_keep_above (true); + + + dark_border_color.parse (DARK_BORDER_COLOR_STRING); + bright_border_color.parse (BRIGHT_BORDER_COLOR_STRING); + + // TODO remove the zoom level restauration if we do not need it + // restore zoomlevel + // if (settings.zoomlevel >= min_zoomlevel && settings.zoomlevel <= max_zoomlevel) { + // zoomlevel = settings.zoomlevel; + // } + + var display = Gdk.Display.get_default (); + Gdk.Monitor monitor = display.get_primary_monitor (); + Gdk.Rectangle geom = monitor.get_geometry (); + set_default_size (geom.width, geom.height); + } + + + public override bool button_release_event (Gdk.EventButton e) { + // button_1 is left mouse button + if (e.button == 1) { + Gdk.RGBA color = get_color_at ((int) e.x_root, (int) e.y_root); + picked (color); + // button_3 is right mouse button + } else if (e.button == 3) { + cancelled (); } + return true; + } - public override bool button_release_event (Gdk.EventButton e) { - // button_1 is left mouse button - if (e.button == 1) { - Gdk.RGBA color = get_color_at ((int) e.x_root, (int) e.y_root); - picked (color); - // button_3 is right mouse button - } else if (e.button == 3) { - cancelled (); - } - - return true; - } + public override bool draw (Cairo.Context cr) { + return false; + } - public override bool draw (Cairo.Context cr) { - return false; - } + public override bool motion_notify_event (Gdk.EventMotion e) { + Gdk.RGBA color = get_color_at ((int) e.x_root, (int) e.y_root); - public override bool motion_notify_event (Gdk.EventMotion e) { - Gdk.RGBA color = get_color_at ((int) e.x_root, (int) e.y_root); + moved (color); - moved (color); + set_magnifier_cursor (); - set_magnifier_cursor (); + return true; + } - return true; - } + public override bool scroll_event (Gdk.EventScroll e) { + switch (e.direction) { + case Gdk.ScrollDirection.UP: + if (zoomlevel < max_zoomlevel) { + zoomlevel++; + } + set_magnifier_cursor (); + break; + case Gdk.ScrollDirection.DOWN: + if (zoomlevel > min_zoomlevel) { + zoomlevel--; + } + set_magnifier_cursor (); + break; + default: + break; + } - public override bool scroll_event (Gdk.EventScroll e) { - switch (e.direction) { - case Gdk.ScrollDirection.UP: - if (zoomlevel < max_zoomlevel) { - zoomlevel++; - } - set_magnifier_cursor (); - break; - case Gdk.ScrollDirection.DOWN: - if (zoomlevel > min_zoomlevel) { - zoomlevel--; - } - set_magnifier_cursor (); - break; - default: - break; - } - - return true; - } + return true; + } - public void set_magnifier_cursor () { - var manager = Gdk.Display.get_default ().get_default_seat (); + public void set_magnifier_cursor () { + var manager = Gdk.Display.get_default ().get_default_seat (); - // get cursor position - int px, py; - get_window ().get_device_position (manager.get_pointer (), out px, out py, null); + // get cursor position + int px, py; + get_window ().get_device_position (manager.get_pointer (), out px, out py, null); - var radius = snapsize * zoomlevel / 2; + var radius = snapsize * zoomlevel / 2; - // get a small area (snap) meant to be zoomed - var snapped_pixbuf = snap (px - snapsize / 2, py - snapsize / 2, snapsize, snapsize); + // get a small area (snap) meant to be zoomed + var snapped_pixbuf = snap (px - snapsize / 2, py - snapsize / 2, snapsize, snapsize); - // Zoom that screenshot up, and grab a snapsize-sized piece from the middle - var scaled_pb = snapped_pixbuf.scale_simple ( - snapsize * zoomlevel + shadow_width * 2 , - snapsize * zoomlevel + shadow_width * 2 , - Gdk.InterpType.NEAREST - ); + // Zoom that screenshot up, and grab a snapsize-sized piece from the middle + var scaled_pb = snapped_pixbuf.scale_simple ( + snapsize * zoomlevel + shadow_width * 2 , + snapsize * zoomlevel + shadow_width * 2 , + Gdk.InterpType.NEAREST + ); - // Create the base surface for our cursor - var base_surface = new Cairo.ImageSurface ( - Cairo.Format.ARGB32, - snapsize * zoomlevel + shadow_width * 2 , - snapsize * zoomlevel + shadow_width * 2 - ); + // Create the base surface for our cursor + var base_surface = new Cairo.ImageSurface ( + Cairo.Format.ARGB32, + snapsize * zoomlevel + shadow_width * 2 , + snapsize * zoomlevel + shadow_width * 2 + ); - var base_context = new Cairo.Context (base_surface); + var base_context = new Cairo.Context (base_surface); - // Create the circular path on our base surface - base_context.arc (radius + shadow_width, radius + shadow_width, radius, 0, 2 * Math.PI); + // Create the circular path on our base surface + base_context.arc (radius + shadow_width, radius + shadow_width, radius, 0, 2 * Math.PI); - // Paste in the screenshot - Gdk.cairo_set_source_pixbuf (base_context, scaled_pb, 0, 0); + // Paste in the screenshot + Gdk.cairo_set_source_pixbuf (base_context, scaled_pb, 0, 0); - // Clip to that circular path, keeping the path around for later, and paint the pasted screenshot - base_context.save (); - base_context.clip_preserve (); - base_context.paint (); - base_context.restore (); + // Clip to that circular path, keeping the path around for later, and paint the pasted screenshot + base_context.save (); + base_context.clip_preserve (); + base_context.paint (); + base_context.restore (); - // Draw a shadow as outside magnifier border - double shadow_alpha = 0.6; - base_context.set_line_width (1); + // Draw a shadow as outside magnifier border + double shadow_alpha = 0.6; + base_context.set_line_width (1); - for (int i = 0; i <= shadow_width; i++) { - base_context.arc ( - radius + shadow_width, radius + shadow_width, - radius + shadow_width - i, 0, 2 * Math.PI - ); - Gdk.RGBA shadow_color = Gdk.RGBA (); - shadow_color.parse (DARK_BORDER_COLOR_STRING); - shadow_color.alpha = shadow_alpha / ((shadow_width - i + 1) * (shadow_width - i + 1)); - Gdk.cairo_set_source_rgba (base_context, shadow_color); - base_context.stroke (); - } + for (int i = 0; i <= shadow_width; i++) { + base_context.arc ( + radius + shadow_width, radius + shadow_width, + radius + shadow_width - i, 0, 2 * Math.PI + ); + Gdk.RGBA shadow_color = Gdk.RGBA (); + shadow_color.parse (DARK_BORDER_COLOR_STRING); + shadow_color.alpha = shadow_alpha / ((shadow_width - i + 1) * (shadow_width - i + 1)); + Gdk.cairo_set_source_rgba (base_context, shadow_color); + base_context.stroke (); + } - // Draw an outside bright magnifier border - Gdk.cairo_set_source_rgba (base_context, bright_border_color); - base_context.arc (radius + shadow_width, radius + shadow_width, radius - 1, 0, 2 * Math.PI); - base_context.stroke (); + // Draw an outside bright magnifier border + Gdk.cairo_set_source_rgba (base_context, bright_border_color); + base_context.arc (radius + shadow_width, radius + shadow_width, radius - 1, 0, 2 * Math.PI); + base_context.stroke (); - // Draw inside square - base_context.set_line_width (1); + // Draw inside square + base_context.set_line_width (1); - Gdk.cairo_set_source_rgba (base_context, dark_border_color); - base_context.move_to (radius + shadow_width - zoomlevel, radius + shadow_width - zoomlevel); - base_context.line_to (radius + shadow_width + zoomlevel, radius + shadow_width - zoomlevel); - base_context.line_to (radius + shadow_width + zoomlevel, radius + shadow_width + zoomlevel); - base_context.line_to (radius + shadow_width - zoomlevel, radius + shadow_width + zoomlevel); - base_context.close_path (); - base_context.stroke (); + Gdk.cairo_set_source_rgba (base_context, dark_border_color); + base_context.move_to (radius + shadow_width - zoomlevel, radius + shadow_width - zoomlevel); + base_context.line_to (radius + shadow_width + zoomlevel, radius + shadow_width - zoomlevel); + base_context.line_to (radius + shadow_width + zoomlevel, radius + shadow_width + zoomlevel); + base_context.line_to (radius + shadow_width - zoomlevel, radius + shadow_width + zoomlevel); + base_context.close_path (); + base_context.stroke (); - Gdk.cairo_set_source_rgba (base_context, bright_border_color); - base_context.move_to (radius + shadow_width - zoomlevel + 1, radius + shadow_width - zoomlevel + 1); - base_context.line_to (radius + shadow_width + zoomlevel - 1, radius + shadow_width - zoomlevel + 1); - base_context.line_to (radius + shadow_width + zoomlevel - 1, radius + shadow_width + zoomlevel - 1); - base_context.line_to (radius + shadow_width - zoomlevel + 1, radius + shadow_width + zoomlevel - 1); - base_context.close_path (); - base_context.stroke (); + Gdk.cairo_set_source_rgba (base_context, bright_border_color); + base_context.move_to (radius + shadow_width - zoomlevel + 1, radius + shadow_width - zoomlevel + 1); + base_context.line_to (radius + shadow_width + zoomlevel - 1, radius + shadow_width - zoomlevel + 1); + base_context.line_to (radius + shadow_width + zoomlevel - 1, radius + shadow_width + zoomlevel - 1); + base_context.line_to (radius + shadow_width - zoomlevel + 1, radius + shadow_width + zoomlevel - 1); + base_context.close_path (); + base_context.stroke (); - magnifier = new Gdk.Cursor.from_surface ( - get_screen ().get_display (), - base_surface, - base_surface.get_width () / 2, - base_surface.get_height () / 2); + magnifier = new Gdk.Cursor.from_surface ( + get_screen ().get_display (), + base_surface, + base_surface.get_width () / 2, + base_surface.get_height () / 2); - // Set the cursor - manager.grab ( - get_window (), - Gdk.SeatCapabilities.ALL, - true, - magnifier, - new Gdk.Event (Gdk.EventType.BUTTON_PRESS | Gdk.EventType.MOTION_NOTIFY | Gdk.EventType.SCROLL), - null); + // Set the cursor + manager.grab ( + get_window (), + Gdk.SeatCapabilities.ALL, + true, + magnifier, + new Gdk.Event (Gdk.EventType.BUTTON_PRESS | Gdk.EventType.MOTION_NOTIFY | Gdk.EventType.SCROLL), + null); - } + } - public Gdk.Pixbuf? snap (int x, int y, int w, int h) { - var root = Gdk.get_default_root_window (); + public Gdk.Pixbuf? snap (int x, int y, int w, int h) { + var root = Gdk.get_default_root_window (); - var screenshot = Gdk.pixbuf_get_from_window (root, x, y, w, h); - return screenshot; - } + var screenshot = Gdk.pixbuf_get_from_window (root, x, y, w, h); + return screenshot; + } - public override bool key_press_event (Gdk.EventKey e) { - var manager = Gdk.Display.get_default ().get_default_seat (); - int px, py; - get_window ().get_device_position (manager.get_pointer (), out px, out py, null); - - switch (e.keyval) { - case Gdk.Key.Escape: - cancelled (); - break; - case Gdk.Key.Return: - Gdk.RGBA color = get_color_at (px, py); - picked (color); - break; - case Gdk.Key.Up: - manager.get_pointer ().warp (get_screen (), px, py - 1); - break; - case Gdk.Key.Down: - manager.get_pointer ().warp (get_screen (), px, py + 1); - break; - case Gdk.Key.Left: - manager.get_pointer ().warp (get_screen (), px - 1, py); - break; - case Gdk.Key.Right: - manager.get_pointer ().warp (get_screen (), px + 1, py); - break; - } + public override bool key_press_event (Gdk.EventKey e) { + var manager = Gdk.Display.get_default ().get_default_seat (); + int px, py; + get_window ().get_device_position (manager.get_pointer (), out px, out py, null); - return true; + switch (e.keyval) { + case Gdk.Key.Escape: + cancelled (); + break; + case Gdk.Key.Return: + Gdk.RGBA color = get_color_at (px, py); + picked (color); + break; + case Gdk.Key.Up: + manager.get_pointer ().warp (get_screen (), px, py - 1); + break; + case Gdk.Key.Down: + manager.get_pointer ().warp (get_screen (), px, py + 1); + break; + case Gdk.Key.Left: + manager.get_pointer ().warp (get_screen (), px - 1, py); + break; + case Gdk.Key.Right: + manager.get_pointer ().warp (get_screen (), px + 1, py); + break; } - public Gdk.RGBA get_color_at (int x, int y) { - var root = Gdk.get_default_root_window (); - Gdk.Pixbuf? pixbuf = Gdk.pixbuf_get_from_window (root, x, y, 1, 1); - - if (pixbuf != null) { - // see https://hackage.haskell.org/package/gtk3-0.14.6/docs/Graphics-UI-Gtk-Gdk-Pixbuf.html - uint8 red = pixbuf.get_pixels ()[0]; - uint8 green = pixbuf.get_pixels ()[1]; - uint8 blue = pixbuf.get_pixels ()[2]; - - Gdk.RGBA color = Gdk.RGBA (); - string spec = "rgb(" + red.to_string () + "," + green.to_string () + "," + blue.to_string () + ")"; - if (color.parse (spec)) { - return color; - } else { - stdout.printf ("ERROR: Parse pixel rgb values failed."); - } + return true; + } + + public Gdk.RGBA get_color_at (int x, int y) { + var root = Gdk.get_default_root_window (); + Gdk.Pixbuf? pixbuf = Gdk.pixbuf_get_from_window (root, x, y, 1, 1); + + if (pixbuf != null) { + // see https://hackage.haskell.org/package/gtk3-0.14.6/docs/Graphics-UI-Gtk-Gdk-Pixbuf.html + uint8 red = pixbuf.get_pixels ()[0]; + uint8 green = pixbuf.get_pixels ()[1]; + uint8 blue = pixbuf.get_pixels ()[2]; + + Gdk.RGBA color = Gdk.RGBA (); + string spec = "rgb(" + red.to_string () + "," + green.to_string () + "," + blue.to_string () + ")"; + if (color.parse (spec)) { + return color; + } else { + stdout.printf ("ERROR: Parse pixel rgb values failed."); } - - // fallback: default RGBA color - stdout.printf ("ERROR: Gdk.pixbuf_get_from_window failed"); - return Gdk.RGBA (); } + // fallback: default RGBA color + stdout.printf ("ERROR: Gdk.pixbuf_get_from_window failed"); + return Gdk.RGBA (); + } - public override void show_all () { - base.show_all (); - var manager = Gdk.Display.get_default ().get_default_seat (); - var window = get_window (); + public override void show_all () { + base.show_all (); - var status = manager.grab ( - window, - Gdk.SeatCapabilities.ALL, - false, - new Gdk.Cursor.for_display (window.get_display (), Gdk.CursorType.CROSSHAIR), - new Gdk.Event (Gdk.EventType.BUTTON_PRESS | Gdk.EventType.BUTTON_RELEASE | Gdk.EventType.MOTION_NOTIFY), - null); + var manager = Gdk.Display.get_default ().get_default_seat (); + var window = get_window (); - if (status != Gdk.GrabStatus.SUCCESS) { - manager.ungrab (); - } + var status = manager.grab ( + window, + Gdk.SeatCapabilities.ALL, + false, + new Gdk.Cursor.for_display (window.get_display (), Gdk.CursorType.CROSSHAIR), + new Gdk.Event (Gdk.EventType.BUTTON_PRESS | Gdk.EventType.BUTTON_RELEASE | Gdk.EventType.MOTION_NOTIFY), + null); - // show magnifier - set_magnifier_cursor (); + if (status != Gdk.GrabStatus.SUCCESS) { + manager.ungrab (); } - public new void close () { - // TODO remove the zoom level saving if we do not need it - // save zoomlevel - // settings.zoomlevel = zoomlevel; + // show magnifier + set_magnifier_cursor (); + } - get_window ().set_cursor (null); - base.close (); - } + public new void close () { + // TODO remove the zoom level saving if we do not need it + // save zoomlevel + // settings.zoomlevel = zoomlevel; + + get_window ().set_cursor (null); + base.close (); + } } From 4c379237172d4cee12cd004b885e62e9f61076bf Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Wed, 2 Sep 2020 17:18:25 +0200 Subject: [PATCH 14/15] update meson.build --- src/meson.build | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/meson.build b/src/meson.build index f247a5672..6d6c1310c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -35,6 +35,7 @@ sources = files( 'Utils/AffineTransform.vala', 'Utils/Color.vala', 'Utils/Image.vala', + 'Utils/ColorPicker.vala', 'Layouts/HeaderBar.vala', 'Layouts/LeftSideBar.vala', @@ -94,8 +95,6 @@ sources = files( 'Lib/Models/CanvasArtboard.vala', 'Lib/Selection/Nob.vala', - - 'Utils/ColorPicker.vala', ) deps = [ From c0d819ef592ed0ebaad1d558210fee993d8f7b14 Mon Sep 17 00:00:00 2001 From: Ivan Vilanculo Date: Wed, 2 Sep 2020 17:31:17 +0200 Subject: [PATCH 15/15] fix issue with some listeners not being triggered --- src/Services/ActionManager.vala | 6 ++---- src/Utils/ColorPicker.vala | 10 ++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Services/ActionManager.vala b/src/Services/ActionManager.vala index 727a4a522..d20dea1e4 100644 --- a/src/Services/ActionManager.vala +++ b/src/Services/ActionManager.vala @@ -469,13 +469,11 @@ public class Akira.Services.ActionManager : Object { bool is_holding_shift = false; var color_picker = new Akira.Utils.ColorPicker (); color_picker.show_all (); - color_picker.key_press_event.connect (e => { + color_picker.key_pressed.connect (e => { is_holding_shift = e.keyval == Gdk.Key.Shift_L; - return true; }); - color_picker.key_release_event.connect (e => { + color_picker.key_released.connect (e => { is_holding_shift = e.keyval == Gdk.Key.Shift_L; - return true; }); color_picker.cancelled.connect (() => { color_picker.close (); diff --git a/src/Utils/ColorPicker.vala b/src/Utils/ColorPicker.vala index 282c0a0cc..10c2e86f2 100644 --- a/src/Utils/ColorPicker.vala +++ b/src/Utils/ColorPicker.vala @@ -24,6 +24,8 @@ public class Akira.Utils.ColorPicker : Gtk.Window { public signal void picked (Gdk.RGBA color); public signal void cancelled (); public signal void moved (Gdk.RGBA color); + public signal void key_pressed (Gdk.EventKey e); + public signal void key_released (Gdk.EventKey e); const string DARK_BORDER_COLOR_STRING = "#333333"; private Gdk.RGBA dark_border_color = Gdk.RGBA (); @@ -271,6 +273,14 @@ public class Akira.Utils.ColorPicker : Gtk.Window { break; } + key_pressed (e); + + return true; + } + + public override bool key_release_event (Gdk.EventKey e) { + key_released (e); + return true; }